Les préparatifsCommençons par télécharger RakNet
Download RakNetCompilez le avec votre IDE favoris (les projets sont créent et configuré pour les IDE les plus courant, juste a lancer la compilation et c’est réglé). Récupérez la DLL pour vos projets.
On peut utiliser RakNet de 2 façons :
-Replica : Un système qui permet de répliquer la même chose sur tous les clients connectés au serveur automatiquement. (Le système replica utilise le système BitStream)
-BitStream : Une autre façon, celle de tout faire a la main. A nous d’écrire les données à envoyer, et de les lire. Il faut une bonne organisation pour ne pas lire n’importe quoi à la réception.
Nous allons nous utiliser le BlitStream. D’abord parce que je préfère bien contrôler les paquets, savoir ce que j’envoie et quand je l’envoie. Mais aussi, parce que je ne sais pas encore très bien utiliser le Replica
Ma vision de l’utilisation de RakNet dans mes applicationsPersonnellement, je préfère créer une application « serveur » et une application « client ».
On peut très bien faire une application qui fait les deux, mais sur une application assez lourde, on a vite fait de s’emmêler les pédales sur la gestion des paquets. Chacun son rôle !
Introduction rapide au réseauLes machines doivent être toutes reliées sur un réseau (qu’il soit local ou internet) évidement.
Pour que les machines communiquent entre elle, elle utilise une adresse IP et un numéro de port.
-L’adresse IP :Il faut donc impérativement connaitre l’adresse IP du serveur pour pouvoir si connecter.
Si on veut utiliser le même PC pour faire tourner à la fois le client et le serveur, on utilisera l’adresse 127.0.0.1, c’est une adresse réservé à l’ordinateur lui-même.
-Le port :Le port est un numéro utilisé par le PC pour en quelque sorte savoir sur quelle application on se connecte. Imaginer que vous avez 2 application serveur sur le même PC et qu’il passe tout les 2 sur le port 5000 (par exemple) Il y aurait vite un conflit entre le client et le/les serveurs.
On préféra donc lancer un serveur sur le port 5000 et l’autre sur le port 5001.
Un paquet !On peut imaginer un tube avec des cases. Case 1, case 2, case 3…
C’est à nous de mettre ce qu’on veut dans les cases, des variables de n’importe quels types.
Du moment qu’on sait les relire a l’autre bout.
Les fonctions principal que l’on va utiliserVoici en gros les chose que l’on va utiliser pendant le tuto.
Vous retrouverez tout cela bien en détail dans la doc de RakNet bien sûr.
Code c++ :
RakPeerInterface *client = RakNetworkFactory::GetRakPeerInterface();
Code c++ :
Packet *packet;
Code c++ :
Startup (unsigned short maxConnections,
int _threadSleepTimer,
SocketDescriptor *socketDescriptors,
unsigned socketDescriptorCount)
client->Startup( unsigned short maxConnections, int _threadSleepTimer, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount )
Code c++ :
SetMaximumIncomingConnections (unsigned short numberAllowed)
server->SetMaximumIncomingConnections(int nbClientMaxi);
Code c++ :
Connect (const char *host,
unsigned short remotePort,
const char *passwordData,
int passwordDataLength,
unsigned connectionSocketIndex=0,
unsigned sendConnectionAttemptCount=7,
unsigned timeBetweenSendConnectionAttemptsMS=500,
RakNetTime timeoutTime=0)
client->Connect(ipServer, portServer, 0,0);
Code c++ :
BitStream (unsigned char *_data,
const unsigned int lengthInBytes,
bool _copyData)
RakNet::BitStream dataStream(packet->data, packet->length, false);
Code c++ :
Write (templateType var)
int nombre = 10;
dataStream.Write(nombre);
Code c++ :
Read (templateType &var)
int nombre;
dataStream.Read(nombre);
Code c++ :
DeallocatePacket (Packet *packet)
client->DeallocatePacket(packet);
Code c++ :
Send (const RakNet::BitStream *bitStream,
PacketPriority priority,
PacketReliability reliability,
char orderingChannel,
SystemAddress systemAddress,
bool broadcast)=0
client->Send(&dataStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
Code c++ :
RakNetworkFactory::DestroyRakPeerInterface(client);
Avec ça, on devrait déjà en faire pas mal
Je vous conseil quand même d'allez faire un petit tour dans la doc pour voir ça de plus prêt documentation RakNet. 1er application : une petite console pour comprendreUne petite application pour tester la connexion a un serveur et l'envoie de paquet/réception de données.
Ce programme va simplement lancer un serveur, puis un client qui va envoyer sont nom au serveur si ça connexion est accepté.
Puis le serveur va avertir tous les clients déjà connecté qu'un nouveau client est arrivé.
LE SERVEURCréez une application console appelé "serveur"et linker les lib.
Code c++ :
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"
#include <BitStream.h>
#include <RakNetTypes.h>
#include <conio.h>
const unsigned char PACKET_ID_MESSAGE_DIT = 100;
void sendAMessage(RakPeerInterface *serveur, Packet* packet, char name[])
{
char message[100]; sprintf(message, "Le client <%s> vient de se connecter\n", name);
RakNet::BitStream data; data.Write(PACKET_ID_MESSAGE_DIT); data.Write(message); serveur->Send(&data, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, true);
}
int main(void)
{
int maxClient = 10;
int portServeur = 10000;
RakPeerInterface *serveur = RakNetworkFactory::GetRakPeerInterface();
Packet * packet = NULL;
serveur->Startup(maxClient, 10, &SocketDescriptor(portServeur,0), 1);
serveur->SetMaximumIncomingConnections(maxClient);
printf("Demarrage du serveur sur le port %d\n", portServeur);
printf("Avec %d client maxi\n", maxClient);
bool continuer = true;
while(continuer)
{
packet = serveur->Receive();
if(packet != NULL) {
unsigned char packetID;
RakNet::BitStream dataStream(packet->data, packet->length, false);
dataStream.Read(packetID);
char charTempo[50];
switch(packetID)
{
case ID_NEW_INCOMING_CONNECTION:
break;
case PACKET_ID_MESSAGE_DIT:
dataStream.Read(charTempo); printf("Bienvenue a %s .\n", charTempo);
sendAMessage(serveur, packet, charTempo);
break;
case ID_NO_FREE_INCOMING_CONNECTIONS:
printf("le serveur est plein.\n");
break;
case ID_CONNECTION_LOST:
printf("Un client a perdu la connection.\n");
break;
default:
printf("Reception d'un packet avec un ID inconnue: %i\n", int(packetID));
}
serveur->DeallocatePacket(packet);
}
if (kbhit())
{
int key = getch();
if (key == 27) {
continuer = false;
}
}
}
RakNetworkFactory::DestroyRakPeerInterface(serveur);
return 0;
}
LE CLIENTCréez une application console appelé "client"et linker les lib.
Code c++ :
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"
#include <BitStream.h>
#include <RakNetTypes.h>
#include <conio.h>
const unsigned char PACKET_ID_MESSAGE_DIT = 100;
void sendMyName(RakPeerInterface *client, char name[])
{
RakNet::BitStream data; data.Write(PACKET_ID_MESSAGE_DIT); data.Write(name); client->Send(&data, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
}
int main(void)
{
int portServeur = 10000;
char IP_serveur[20];
char myName[50];
printf("Le client <%s> vient de se connecter\n");
scanf("Demarrage du serveur sur le port %d\n", IP_serveur);
printf("Avec %d client maxi\n");
scanf("Bienvenue a %s .\n", myName);
RakPeerInterface *client = RakNetworkFactory::GetRakPeerInterface();
Packet * packet = NULL;
client->Startup(1,10,&SocketDescriptor(), 1);
client->Connect(IP_serveur, portServeur, 0,0);
bool continuer = true;
while(continuer)
{
packet = client->Receive();
if(packet != NULL) {
unsigned char packetID;
RakNet::BitStream dataStream(packet->data, packet->length, false);
dataStream.Read(packetID);
char charTempo[100];
switch(packetID)
{
case ID_CONNECTION_REQUEST_ACCEPTED:
printf("le serveur est plein.\n");
sendMyName(client, myName);
break;
case ID_CONNECTION_ATTEMPT_FAILED:
printf("Un client a perdu la connection.\n");
break;
case PACKET_ID_MESSAGE_DIT:
dataStream.Read(charTempo); printf("Reception d'un packet avec un ID inconnue: %i\n", charTempo);
break;
default:
printf("Entrez l'adresse IP su serveur :", int(packetID));
}
client->DeallocatePacket(packet);
}
if (kbhit())
{
int key = getch();
if (key == 27) {
continuer = false;
}
}
}
RakNetworkFactory::DestroyRakPeerInterface(client);
return 0;
}
COMPILEZ !
OK : Vous pouvez donc maintenant lancer le serveur, puis lancer plusieurs clients en même temps pour testé ça.
Comme vous avez pu le remarquez, un certain nombre de paquets avec un ID bien précis sont déjà envoyé automatiquement par RakNet. Vous retrouverez tous ces ID dans le fichier "MessageIdentifiers.h" des sourcesDonc pour résumer un peu, comme vous avez sans doute pu le remarquer, l'idée c'est quoi ?(pas la mienne hein, celle de RakNet
)
On créer un paquet, ( pour reprendre le schéma du paquet plus haut) dans la première case on écrit l'ID du paquet, ce qui va nous servir à savoir à quoi ce paquet va nous servir. Puis on écrit les valeurs (dans les cases) que l'on a besoin.
De l'autre côté, le client reçoit un paquet. Il commence par lire l'ID de ce paquet, (je me répète) ce qui va lui servir à savoir à quoi ce paquet va nous servir. Puis on lie les données ( dans les cases) qui suivent.
Cette première application est très juste une petite approche de ce qu'il possible de faire, c'est que ici, l'utilisateur ne rentre pas vraiment en jeu.
Mais attendez de voir la suite...
2e application : RakNet et Irrlicht – Ninja !On va ici, créer un serveur (toujours en console) et un client qui lui va être un peu plus jolie.
Le serveur:
-Doit être capable d'accepter 2 connections client.
-Il doit réceptionner la position des 2 joueurs et la renvoyer au client.
Le client :
-dans une application Irrlicht, création d'un sol, de 2 nodes animés (on va prendre le Ninja fournie par Irrlicht), une camera qui suit notre node.
-On doit envoyer notre position régulièrement au serveur.
-On doit être capable de réceptionner la position de l’autre joueur envoyé par le serveur.
-On va aussi joueur une animation lorsque les ninja se déplace. (Idle, lorsqu'il sont sur place et Walk quand il se déplace)
-On va utiliser les models et textures fournie par Irrlicht :
-le terrain : terrain-heightmap.bmp, terrain-texture.jpg, detailmap3.jpg.
-les modèles : ninja.b3d, nskinbl.jpg, nskinrd.jpg. (pour avoir 1 ninja rouge et un bleu)
Mais avant : Un problème de la vitesse de l'envoi/lecture des paquets.
Il existe un vrai problème sur la vitesse d'envoi et de réception des paquet suivant l'application que vous voulez mettre en réseau.
Exemple :
-Un jeu de morpion : le joueur 1 coche un case, il envoie directement au serveur quel case il a cocher, le serveur dit au joueur 2 quel case le joueur 1 a cocher, et tout roule pas de problème.
-Un FPS : Le joueur 1 se déplace, il dit au serveur ou il est, le serveur envoie la position du joueur 1 au joueur 2.
Mais ! A quel moment doit-on envoyer notre position ? quand est-ce que le serveur renvoie la position de tout les joueurs a tout le monde ?
Imaginons que à chaque frame on envoie notre position au serveur, cela est-il suffisant, sachant que l'on sait que pour une animation fluide, il faut au minimum 25 frames par seconde. Mais imaginons que sur notre premier client on tourne à 50 fps. On envoie donc 50 fois par seconde notre position au serveur, qui lui, dès qu'il la reçois, il la renvoie au autre client. Seulement, imaginons que le 2em client tourne a 30 fps. Ce client va donc vite se retrouver déborder, il va donc avoir plus de paquet à lire qu'il n'est capable de le faire (on part bien-sûr avec le code exemple de la console au dessus, ou notre client ne sait lire que 1 paquet par frame). Bien des solutions existe pour remédier a ce genre de problème :
1= On envoie notre position au serveur toutes les 40 millisecondes (ce qui correspond à 25 fois par seconde). Et le serveur renvoie la position à tout les client toutes les 40 ms à sont tour. En théorie ça marche bien, à condition que le client et le serveur tourne à plus de 25fps.(en général le serveur tourne largement plus vite, surtout si on ne l'utilise que en console)
Personnellement, j'ai pour habitude de gérer le temps avec clock(). exemple :
Code c++ :
#include <ctime>
int main(void)
{
clock_t tempsActuel = clock();
clock_t tempsEcouler = clock();
while(1)
{
tempsEcouler = clock();
if(tempsEcouler - tempsActuel > 40)
{
tempsActuel = clock(); }
}
}
Mais vous pouvez très bien utiliser ce qu'il vous plait.
2=On fait une boucle qui li tout les paquets reçus avant d'afficher le rendu. ça marche aussi pas mal, sauf si on reçois d'un coup énormément de paquets et là, on va ramer un gros coup.
3=On fait un mélange des 2 premières solution, on essaye d'optimiser au maximum pour envoyer le moins de paquet possible. On envoi notre position toutes les 40 ms mais seulement si on a bouger depuis la dernière fois.
4=Pour encore plus de rapidité pour les clients, on sait que bien souvent les perte de FPS sont en partie a cause des collisions (notamment avec Irrlicht) Alors, même si c'est un peut plus compliquer, on peut très bien ne gérer aucune collision sur le programme client, mais plutôt laisser le serveur s'en charger. Si on fait un serveur sans interface graphique, il ne perd quasiment rien en rapidité même avec une gestion des collisions. Cela implique que le personnage du client ne bouge pas directement pour ensuite envoyer sa position au serveur, mais fait une demande au serveur de bouger, et c'est le serveur qui place le personnage.
Il y a encore un bon nombre de chose pouvant être réaliser, avec un peu d'imagination, on peut réaliser de très belle chose...
NINJA !! c'est partie !
Bon, nous dans notre exemple du Ninja nous allons codé en utilisant la première solution, ce n'est pas la plus optimisée mais pour faire tourner une api composée seulement de 2 ninjas et 1 terrain, ça devrais suffire.
Je vais mettre un maximum de commentaire dans le code plutôt que de faire un long chapitre explicatif
depuis la version 1.5.1 de Irrlicht, il n'est plus possible de lancer 2 clients simultanément utilisant une caméra FPS, il vous faudra donc au moins 2 ordinateurs pour testé ce programme (1 serveur et client et 1 client)LE SERVEURCode c++ :
#include <irrlicht.h>
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"
#include <BitStream.h>
#include <RakNetTypes.h>
#include <conio.h>#include <ctime>
using namespace std
;using namespace irr;using namespace core;using namespace scene;using namespace video;using namespace gui;const unsigned char PACKET_ID_DEPLACEMENT
= 101;const unsigned char PACKET_ID_ANIMATION
= 102;const unsigned char PACKET_ID_ID_JOUEUR
= 103;void send_a_ID_joueur
(RakPeerInterface
*serveur
, int ID_joueur
){ RakNet
::BitStream data
; data
.Write
(PACKET_ID_ID_JOUEUR
); data
.Write
(ID_joueur
); serveur
->Send
(&data
, HIGH_PRIORITY
, RELIABLE_ORDERED
, 0, UNASSIGNED_SYSTEM_ADDRESS
, true);} void send_animation
(RakPeerInterface
*serveur
, Packet
*packet
, int ID_joueur
, bool il_marche
){ RakNet
::BitStream data
; data
.Write
(PACKET_ID_ANIMATION
); data
.Write
(ID_joueur
); data
.Write
(il_marche
); serveur
->Send
(&data
, MEDIUM_PRIORITY
, RELIABLE_ORDERED
, 0, packet
->systemAddress
, true);}int main
(void){ int maxClient
= 2; int portServeur
= 10000; RakPeerInterface
*serveur
= RakNetworkFactory
::GetRakPeerInterface
(); Packet
* packet
= NULL
; serveur
->Startup
(maxClient
, 10, &SocketDescriptor
(portServeur
,0), 1); serveur
->SetMaximumIncomingConnections
(maxClient
); printf("Le client <%s> vient de se connecter\n", portServeur
); printf("Demarrage du serveur sur le port %d\n", maxClient
); int ID_joueur
= 0; bool il_marche
= false; vector3df positionJoueur
[2]; vector3df rotationJoueur
[2]; positionJoueur
[0].X
= 0; positionJoueur
[0].Y
= 0; positionJoueur
[0].Z
= 0; positionJoueur
[1].X
= 0; positionJoueur
[1].Y
= 0; positionJoueur
[1].Z
= 0; clock_t tempsActuel
= clock
(); clock_t tempsEcouler
= clock
(); bool continuer
= true; while(continuer
) { packet
= serveur
->Receive
(); if(packet
!= NULL
) { unsigned char packetID
; RakNet
::BitStream dataStream
(packet
->data
, packet
->length
, false); dataStream
.Read
(packetID
); int un_ID_Joueur
; switch(packetID
) { case ID_NEW_INCOMING_CONNECTION
: send_a_ID_joueur
(serveur
, ID_joueur
); ID_joueur
++; break; case PACKET_ID_DEPLACEMENT
: dataStream
.Read
(un_ID_Joueur
); dataStream
.Read
(positionJoueur
[un_ID_Joueur
]); dataStream
.Read
(rotationJoueur
[un_ID_Joueur
]); break; case PACKET_ID_ANIMATION
: dataStream
.Read
(un_ID_Joueur
); dataStream
.Read
(il_marche
); send_animation
(serveur
, packet
, un_ID_Joueur
, il_marche
); break; case ID_NO_FREE_INCOMING_CONNECTIONS
: printf("Avec %d client maxi\n"); break; case ID_CONNECTION_LOST
: printf("Bienvenue a %s .\n"); break; default: printf("le serveur est plein.\n", int(packetID
)); } serveur
->DeallocatePacket
(packet
); } tempsEcouler
= clock
(); if(tempsEcouler
- tempsActuel
> 40) { for(int i
= 0; i
< 2; i
++) { RakNet
::BitStream data
; data
.Write
(PACKET_ID_DEPLACEMENT
); data
.Write
(i
); data
.Write
(positionJoueur
[i
]); data
.Write
(rotationJoueur
[i
]); serveur
->Send
(&data
, HIGH_PRIORITY
, RELIABLE_ORDERED
, 0, UNASSIGNED_SYSTEM_ADDRESS
, true); } tempsActuel
= clock
(); } if (kbhit
()) { int key
= getch
(); if (key
== 27) { continuer
= false; } } } RakNetworkFactory
::DestroyRakPeerInterface
(serveur
); return 0;} LE CLIENTCode c++ :
#include <irrlicht.h>
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"
#include <BitStream.h>
#include <RakNetTypes.h>
#include <iostream>
#include <conio.h>
using namespace std
;using namespace irr;using namespace core;using namespace scene;using namespace video;using namespace gui;const unsigned char PACKET_ID_DEPLACEMENT
= 101;const unsigned char PACKET_ID_ANIMATION
= 102;const unsigned char PACKET_ID_ID_JOUEUR
= 103;class MyEventReceiver
: public IEventReceiver{public: virtual bool OnEvent
(const SEvent& event
) { if (event
.EventType
== irr::EET_KEY_INPUT_EVENT
) KeyIsDown
[event
.KeyInput
.Key
] = event
.KeyInput
.PressedDown
; return false; } virtual bool IsKeyDown
(EKEY_CODE keyCode
) const { return KeyIsDown
[keyCode
]; } MyEventReceiver
() { for (u32 i
=0; i
<KEY_KEY_CODES_COUNT
; ++i
) KeyIsDown
[i
] = false; }private: bool KeyIsDown
[KEY_KEY_CODES_COUNT
];};int main
(void){ int portServeur
= 10000; char IP_serveur
[20]; printf("Le client <%s> vient de se connecter\n"); scanf
("Demarrage du serveur sur le port %d\n", IP_serveur
); RakPeerInterface
*client
= RakNetworkFactory
::GetRakPeerInterface
(); Packet
* packet
= NULL
; client
->Startup
(1,10,&SocketDescriptor
(), 1); client
->Connect
(IP_serveur
, portServeur
, 0,0); MyEventReceiver myReceiver
; IrrlichtDevice *device
= createDevice
(EDT_DIRECT3D9
, dimension2d<u32
>(800,600),32,false,false,false); IVideoDriver* driver
= device
->getVideoDriver
(); ISceneManager* sceneMgr
= device
->getSceneManager
(); device
->setEventReceiver
(&myReceiver
); device
->getCursorControl
()->setVisible
(false); ICameraSceneNode* myCamera
= sceneMgr
->addCameraSceneNodeFPS
(0, 100.0f, 0.50f); myCamera
->setPosition
(vector3df
(500,100,500)); scene::ITerrainSceneNode* terrain
= sceneMgr
->addTerrainSceneNode
( "Avec %d client maxi\n", 0, -1, core::vector3df
(0.f
, 0.f
, 0.f
), core::vector3df
(0.f
, 0.f
, 0.f
), core::vector3df
(10.f
, 0.3f, 10.f
), video::SColor ( 255, 255, 255, 255 ), 5, scene::ETPS_17
, 4 ); terrain
->setMaterialFlag
(video::EMF_LIGHTING
, false); terrain
->setMaterialTexture
(0,driver
->getTexture
("Bienvenue a %s .\n")); terrain
->setMaterialTexture
(1,driver
->getTexture
("le serveur est plein.\n")); terrain
->setMaterialType
(video::EMT_DETAIL_MAP
); terrain
->scaleTexture
(1.0f, 20.0f); ITriangleSelector* selector
= sceneMgr
->createTerrainTriangleSelector
(terrain
, 0); terrain
->setTriangleSelector
(selector
); ISceneNodeAnimator
* anim
= sceneMgr
->createCollisionResponseAnimator
( selector
, myCamera
, vector3df
(60,100,60), vector3df
(0,-5,0), vector3df
(0,100,0)); selector
->drop
(); myCamera
->addAnimator
(anim
); anim
->drop
(); IAnimatedMeshSceneNode* ninja
[2]; ninja
[0] = sceneMgr
->addAnimatedMeshSceneNode
(sceneMgr
->getMesh
("Un client a perdu la connection.\n")); ninja
[0]->setMaterialFlag
(video::EMF_LIGHTING
, false); ninja
[0]->setScale
(vector3df
(30,30,30)); ninja
[0]->setFrameLoop
(184,205); ninja
[0]->setAnimationSpeed
(10); ninja
[1] = sceneMgr
->addAnimatedMeshSceneNode
(sceneMgr
->getMesh
("Reception d'un packet avec un ID inconnue: %i\n")); ninja
[1]->setMaterialTexture
(0, driver
->getTexture
("Entrez l'adresse IP su serveur :")); ninja
[1]->setMaterialFlag
(video::EMF_LIGHTING
, false); ninja
[1]->setScale
(vector3df
(30,30,30)); ninja
[1]->setFrameLoop
(184,205); ninja
[1]->setAnimationSpeed
(10); int my_ID_Joueur
= -1; int un_ID_Joueur
= 0; vector3df une_position
; vector3df une_rotation
; bool je_marche
= false; bool il_marche
= false; clock_t tempsActuel
= clock
(); clock_t tempsEcouler
= clock
();do{ packet
= client
->Receive
(); if(packet
!= NULL
) { unsigned char packetID
; RakNet
::BitStream dataStream
(packet
->data
, packet
->length
, false); dataStream
.Read
(packetID
); switch(packetID
) { case ID_CONNECTION_REQUEST_ACCEPTED
: printf("%s"); break; case ID_CONNECTION_ATTEMPT_FAILED
: printf("Entrez votre nom :"); break; case PACKET_ID_ID_JOUEUR
: dataStream
.Read
(my_ID_Joueur
); printf("%s", my_ID_Joueur
); ninja
[my_ID_Joueur
]->setParent
(myCamera
); ninja
[my_ID_Joueur
]->setPosition
(vector3df
(000,-210,-30)); break; } }}while(my_ID_Joueur
< 0); while (device
->run
()) { packet
= client
->Receive
(); if(packet
!= NULL
) { unsigned char packetID
; RakNet
::BitStream dataStream
(packet
->data
, packet
->length
, false); dataStream
.Read
(packetID
); switch(packetID
) { case PACKET_ID_DEPLACEMENT
: dataStream
.Read
(un_ID_Joueur
); dataStream
.Read
(une_position
); dataStream
.Read
(une_rotation
); if(un_ID_Joueur
!= my_ID_Joueur
) { ninja
[un_ID_Joueur
]->setPosition
(une_position
); ninja
[un_ID_Joueur
]->setRotation
(une_rotation
); } break; case PACKET_ID_ANIMATION
: dataStream
.Read
(un_ID_Joueur
); dataStream
.Read
(il_marche
); if(il_marche
) { ninja
[un_ID_Joueur
]->setFrameLoop
(1,14); } else { ninja
[un_ID_Joueur
]->setFrameLoop
(184,205); } break; default: printf("La connection au serveur a bien ete accepter\n", int(packetID
)); } client
->DeallocatePacket
(packet
); } if(myReceiver
.IsKeyDown
(irr::KEY_ESCAPE
)) { device
->closeDevice
(); } if(myReceiver
.IsKeyDown
(irr::KEY_UP
) || myReceiver
.IsKeyDown
(irr::KEY_DOWN
) ||myReceiver
.IsKeyDown
(irr::KEY_LEFT
) || myReceiver
.IsKeyDown
(irr::KEY_RIGHT
)) { if(!je_marche
) { je_marche
= true; RakNet
::BitStream data
; data
.Write
(PACKET_ID_ANIMATION
); data
.Write
(my_ID_Joueur
); data
.Write
(je_marche
); client
->Send
(&data
, MEDIUM_PRIORITY
, RELIABLE_ORDERED
, 0, UNASSIGNED_SYSTEM_ADDRESS
, true); } } else { if(je_marche
) { je_marche
= false; RakNet
::BitStream data
; data
.Write
(PACKET_ID_ANIMATION
); data
.Write
(my_ID_Joueur
); data
.Write
(je_marche
); client
->Send
(&data
, MEDIUM_PRIORITY
, RELIABLE_ORDERED
, 0, UNASSIGNED_SYSTEM_ADDRESS
, true); } } tempsEcouler
= clock
(); if(tempsEcouler
- tempsActuel
> 30) { RakNet
::BitStream data
; data
.Write
(PACKET_ID_DEPLACEMENT
); data
.Write
(my_ID_Joueur
); une_position
= ninja
[my_ID_Joueur
]->getAbsolutePosition
(); data
.Write
(une_position
); une_rotation
= myCamera
->getRotation
(); une_rotation
.X
= 0; une_rotation
.Z
= 0; data
.Write
(une_rotation
); client
->Send
(&data
, HIGH_PRIORITY
, RELIABLE_ORDERED
, 0, UNASSIGNED_SYSTEM_ADDRESS
, true); tempsActuel
= clock
(); } driver
->beginScene
(true, true, video::SColor(0,200,200,200)); sceneMgr
->drawAll
(); driver
->endScene
(); } device
->drop
(); RakNetworkFactory
::DestroyRakPeerInterface
(client
); return 0;} Bien-sûr il y a énormément de chose qu'on pourrait rajouté ici pour rendre ce petit programme un peu prêt correct, je vous suggère même de le faire. Créer une class pour les personnages, enregistrer le nom des ninjas, leurs rajouter des animations.... lâchés-vous
Toutes les remarques sur ce tutoriel sont les bienvenue afin de le rendre le plus complet et compréhensible possible.