Journal Rendre un jeu jouable en réseau

Posté par  (site web personnel) .
Étiquettes : aucune
0
8
juil.
2006
Bonjour cher (bien que libre et gratuit) journal,
Aujourd'hui (en fait déjà hier), j'ai envie de m'atteler à rendre un jeu jouable en réseau.
Pour commencer j'aimerais essayer sur carterrain (jeu de 4x4 en très gros :) qui parait être le plus simple à faire (5 actions possibles, et un objet "CCar" représentant l'état de la voiture sur tous les aspects (position du chassis/orientation/pareil pour les roues/vitesse/état du moteur etc)
Seulement voilà j'ai de nombreux problèmes qui se posent, plus ou moins lié au jeu directement:
1.Le lag!
Comme je penses vaguement faire, c'est à dire d'envoyer les différentes commandes sur l'objet CCar, ça pose un énorme problème, dès que le lag n'est pas fixe, l'objet se retrouve à des positions différentes, par exemple si y a un lag de 100ms normal et pour 10 paquets on passe à 1s de lag(oui je sais c'est un cas extrême mais je suis presque sur que même avec 10ms de différence ça se ressentirait), si le client était entrain d'accélérer, il reste sur la pédale d'accélérateur 900ms de plus qu'à la normale, ce qui mène à atterrir dans le mur, alors que le client est toujours sur la piste (note que je crois avoir distingué ce problème avec le support expérimental (ce qu'il porte bien son nom) du réseau dans wormux))
Une idée qui me vient spontanément c'est UDP, seulement voilà il souffre de pertes de paquets, alors comment on fait pour savoir si l'information est erronée ou juste ?
Je fais un checksum ?
Et comment
2. Une autre solution qui me vient c'est , d'en plus d'envoyer les changements, que toutes les (1?10?) secondes je sérialise l'objet CCar, seulement voilà l'auteur n'a utilisé aucun pointeur donc ça devrait être excessivement simple, mais les objets sont décrit dans des objets ODE, alors je me demande si le mieux c'est de sérialiser l'objet (qui doit être relativement bordélique vu que j'ai l'impression qu'on a pas un accès directe aux données), ou alors les paramètres importants (position chassis/roues + vitesse juste ?), mais il faut encore pouvoir les récupérer et recréer un objet à partir de ces variables

Enfin un autre problème, qui rejoint les problèmes précédant est le choix du protocole, déjà UDP ou TCP?
Un format purement binaire ?
Un format textuel ?
Comment assurer la robustesse du protocole ?

Voilà merci d'avance pour toutes vos réponses constructives
  • # premiere idée

    Posté par  . Évalué à 7.

    Le client balance a intervalle régulier les infos vitesse, position en udp. Lorsque qu'on ne reçoit pas d'info, on continu a partir des infos précédentes (inertie).
    Lorsque les nouvelles infos arrivent, on synchronise.
    Si c'est vraiment trop long, c'est mort, on le déco.
    • [^] # Re: premiere idée

      Posté par  (site web personnel) . Évalué à 2.

      Il me semble bien que quake3 fait comme cela.
      En tout cas il présente une option pour activer ou non la prediction de la prochaine position du joueur
      • [^] # Re: premiere idée

        Posté par  (site web personnel, Mastodon) . Évalué à 10.

        Wi en fait, je n'ai jamais écrit de protocole réseau dans la pratique, mais c'est ce qui ressort de tous les articles. Quand la vitesse est bien plus importante que la précision de l'information, alors c'est l'udp qu'il te faut. Or c'est clairement le besoin dans la plupart des jeux, et un jeu de voiture en particulier.

        Pour l'udp, le truc, ce n'est pas que ça "souffre de perte de paquet", c'est juste que tout est à faire. C'est un protocole quasi vierge. Mais rien ne t'empêche de créer tes propres acquittements, et ton propre système d'ordonnancement des paquets (l'ordre des paquets et les acquittements donc l'assurance d'arrivée du paquet étant les 2 caractéristiques principales du tcp par rapport au udp). Le seul truc, c'est que toi tu n'en as pas besoin constamment.

        En effet, la plupart du temps, tu t'en fous de perdre des paquets. L'important, c'est qu'un nombre suffisant de paquet arrive (évidemment si trop de paquet n'arrivent pas à destination, c'est que la connexion client-serveur est vraiment trop mauvaise, et donc tu coupes. Or pour cela, le tcp n'y aurait rien changé, sinon encore plus de désagrément peut-être). Evidemment les paquets absents sont "remplacés" par des prédictions comme indiqué plus haut. D'ailleurs dans le cas de véhicule, je pense que ça doit même être plus facile et on doit pouvoir avoir des algos prédictifs plus robustes que -- par exemple -- des personnages, car y a bien plus d'inertie (alors qu'un bonhomme peut tourner à 180° et réaccélérer immédiatemment dans l'autre sens en un rien de temps).

        Ensuite, le point important de l'acquittement, disons qu'il n'est pas nécessaire constamment comme on vient de le voir, par contre dans certains cas ça peut être nécessaire. Par exemple, les trajs de ton jeu de 4x4, on a vu qu'il est juste important d'avoir un nombre de paquet suffisant. Donc pas d'acquittement. Par contre, si jamais le joueur veut envoyer un message à un autre joueur, là il faut de l'acquittement, parce qu'on veut être sûr que le serveur a reçu le message à transmettre (dans un jeu, on n'imaginerait pas un système de comm qui marche aléatoirement :p). C'est vrai aussi pour toute possibilité du jeu qui soit ponctuelle en fait, et donc qu'on ne puisse absolument pas prédire. Par exemple, si ton jeu incorporait des armes sur les véhicules, il serait évident -- je pense -- que les tirs soient acquittés (on veut être sûr que le serveur sait qu'on tire, il peut pas le "deviner")... à moins évidemment que ce soient des tirs en rafale (mitraillette). Là évidemment, c'est pas grave si le serveur reçoit des coupures dans le tir, il imaginera un temps de latence de l'arme suffisant pour que le tir soit continu en recevant des informations incomplètes, quoique suffisamment proche (y a alors acquittement partiel par exemple).
        Une autre idée d'acquittement est d'en faire, mais limitée en nombre de tentatives. Par exemple certains paquets peuvent demander un acquittement, mais pas une seconde fois (le tcp redemande tant qu'il n'a pas confirmation. Donc ça peut boucler très longtemps, alors que les infos reçus seraient obsolètes depuis longtemps et il aurait mieux valu les oublier pour la fluidité du jeu).

        Pour l'histoire de l'ordre des paquets, c'est pareil. Tout dépend vraiment de tes souhaits. Là l'exemple précédent serait inversé par ex, je pense. Par exemple, pour tout ce qui est mouvement de véhicule, l'ordre a quand même son importance. Par contre, un tir d'arme, j'imagine que ça va dépendre de l'effet qu'on souhaite en cas de léger lag.
        Idéalement évidemment les paquets sont reçus instantanément, donc l'ennemi nous voit tirer au moment où on le veut. Mais si jamais y a latence, soit avec de l'ordonnancement, on va dire que le tir est à un moment précis qui est déjà passé... dans ce cas, on fait quoi? Les joueurs en avance subissent un retour en arrière du jeu pour qu'ils puisse assister au tir?
        Ou alors sans ordonnancement, le tir est effectué au moment où il est reçu. A ce moment, c'est plus fluide, car les autres joueurs voient bien le tir sans saute dans le temps, mais ce dernier n'a pas forcément l'effet escompté par le joueur qui tire.
        Evidemment, ce sont des "pires-cas", mais c'est bien le principe de ces protocoles, essayer d'amoindrir l'effet des mauvais cas pour tous les joueurs (dans l'idéal, y aura pas besoin d'acquittement ni d'ordonnancement :p), et repérer les cas vraiment trop mauvais où on coupe la connexion.
        On peut aussi imaginer des ordonnancements "approximatifs". C'est à dire qu'il ne faut pas être trop précis, et ainsi imaginer des intervalles où on considère que l'info reçue est suffisamment ordonnée pour ne pas gêner le jeu, et d'autre où l'info doit soit être réordonnée, soit carrêment oubliée (par ex trop vieille).

        En fait, tout est imaginable selon ce que tu souhaites. C'est l'avantage du udp vis à vis du tcp. Le tcp est "trop parfait", et par conséquent, il est lent. Le udp est vierge, t'en fais ce que tu veux, et c'est donc à toi de décider ce qui doit absolument arriver à destination, ce qui serait bien d'arriver à destination mais pas indispensable, ce qui n'est pas une info primordiale du tout, ce qui doit arriver avec un minimum d'ordre ou pas, etc. Le tout, c'est de ne jamais bloquer le jeu. Et pour ça, le tcp c'est mauvais (boucle d'acquittement, attente d'un paquet manquant parce que tenant trop compte de l'ordre, et donc lag énorme). Mieux vaut faire ton protocole perso basé sur l'udp réglé selon les spécificités uniques de ton jeu.

        Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

        • [^] # Re: premiere idée

          Posté par  (site web personnel) . Évalué à 5.

          Merci d'avoir pris le temps de rédiger ce commentaire. Je le trouve intérressant, comme j'ai déjà rencontré ce problème (et renoncé, fautes de motivation) pour mettre en réseau un Pong très (trop ?) rapide.
        • [^] # Re: premiere idée

          Posté par  (site web personnel) . Évalué à 2.

          Il y a quand même u problème à prendre en compte pour l'udp.

          Durant un stage, j'ai travaillé sur du calcul scientifique parallèle.
          Vu qu'il fallait accélérer les communiation, on a essayé de mettre en place un système de comm avec perte de message en utilisant l'udp ...
          et on est tombé sur le probleme que l'udp n'est pas, est de loin prioritaire sur les routeurs ... donc il y a vraiment peu de comm qui passait.

          De plus, le protocoles tcp est beaucoup plus optimisé que l'udp.
          Donc si en théorie, l'udp n'avait que des avantages, en pratique, le tcp restait plus performant à cause de 2 points:
          - Son implémentation est beaucoup plus optimisée que celle de l'udp
          -les comm tcp sont prioritaires par rapport à l'udp

          Apres, ces tests ont été fait il y a deux, les stratégies de routages ont peut être évolué depuis.

          ++ Beleys
      • [^] # Re: premiere idée

        Posté par  . Évalué à 2.

        Il me semble bien que quake3 fait comme cela.
        En tout cas il présente une option pour activer ou non la prediction de la prochaine position du joueur

        Prédire la prochaine postion du joueur ? Dans Quake 3 ? C'est possible çà ? A moins que le perso soit mort et en chute libre, çà me parait un poil difficile quand même, chapeau ;-)
        • [^] # Re: premiere idée

          Posté par  (site web personnel) . Évalué à 3.

          On parle pas de beaucoup de secondes non plus ;)
          C'est tout à fait possible si le perso est en train d'avancer, si il est à l'arret, ou si il est en l'air. A moins qu'il ne strafe il est assez probable que la prediction soit bonne.
          Après il me semble effectivement que cette option de prédiction est rarement utilisée
  • # this is not a title

    Posté par  . Évalué à 10.

    Il me semble qu'UDP est en effet tout indiqué pour ce genre de protocole. La plupart des jeux l'utilisent, la plus notable exception étant il me semble WoW, que les spécialistes confirment. Son principal défaut (et également avantage au final) étant qu'il n'assure pas l'ordre d'arrivée des paquets, ni même le fait qu'ils arrivent tout court. À partir de là, deux approches s'imposent :

    - Gérer à la main (bas niveau) les paquets. Si le jeu envoie x mouvements du joueur par seconde, ajoute un horodatage au paquet : peu importe qu'ils arrivent (ou pas) dans l'ordre, le serveur ignore les paquets antérieurs au dernier reçu. Peu importe qu'il en manque, c'est l'avantage principal d'UDP face aux problèmes de lag qui font de TCP un protocole peu adapté. Par contre, pour les informations importantes qui ne peuvent être ignorées (discussion pendant une partie par exemple), tu peux mettre en place un système ou le client renvoie les données tant qu'il n'a pas reçu d'accusé de réception. Je crois me souvenir que Quake 2 (lu dans le code de Warsow) fonctionnait comme ça, avec des reliable commands. Tu peux peut-être t'inspirer de http://www.csse.monash.edu.au/~timf/bottim/q2net/q2network-0(...) . Sinon qui sait, tu pourrais peut-être coder le serveur en Erlang ;-).

    - La solution facile selon certains, sage pour d'autre : réutiliser une des multiples bibliothèques pré-existantes. Tu n'as que l'embarras du choix, du plus bas au plus haut niveau : http://jonatkins.org/SDL_net/ SDL_net, http://www.opentnl.org/ OpenTNL, http://www.hawksoft.com/hawknl/ HawkNL ou encore http://www.rakkarsoft.com/ RakNet. En moins orienté jeu, il y a aussi le mastodonte (intéressant, mais un peu overkill pour quelque chose de plus petit qu'un MMOG) : http://zeroc.com ICE. J'avais regardé les uns et les autres, et je pense que dans ton cas RakNet serait approprié.
    • [^] # Re: this is not a title

      Posté par  (site web personnel) . Évalué à 3.

      Moui effectivement je crois que c'est comme ca que ca va finir mais ca m'embete de rajouter une dépendance :/
      Enfin bon
      Sinon j'ai vu la librairie incluse dans cube et sauerbraten nommée "enet", qui a l'air d'utiliser les principe que tu décrit (avec la notion de reliable packets, mais je sais pas s'il connait l'horodatage par contre)
      Bon sinon je vais regarder RakNet merci
  • # C'est le probleme le plus complexe...

    Posté par  (site web personnel) . Évalué à 10.

    Etudier de près le source code "physic across network" dans les sources en base de cette page.
    http://www.gaffer.org/articles/
    Le code contient tout : "proxy, historique, client side prédication, important moves, serveur de simulation avec perte et lag, etc..."
    (l'auteur est celui qui a codé la physique et du réseau dans "Tribes")

    Si tu es anglophone, il faut lire absolument tout ces articles :
    http://www.ogre3d.org/wiki/index.php/External_Links#Articles
    (Sinon ca va vraiment être dur)

    Sinon, en gros:

    - Pas de robustesse de protocole. pas de sequencement. juste un timestamp par paquet, qui permet de décider ce que l'on en fait au niveau du jeu.
    - position chassis/roues + quaternion chassis plus roues (roues peut-etre en option et simulé du coté client très simplement). Vitesse et autres données physique si "client side prédiction"
    - UDP (une très bonne librairies UDP est ENET utilisé dans cube, sauerbraten) Attention au pièges des librairies (opentnl, hawkNL raknet, etc..). A part gérer les sockets et les threads elle ne sont pas forcement d'une grande aide, notemment pour le lag.
    - format binaire, voire compressé. (lzma) Chaque octets conmpte. (pas tellement pour la bande passante, c'est plus pour la surcharge du serveur et/ou la vitesse de traitement des paquets (au niveau OS))
    - Chaque envoir "input + dernier changements d'input" + timestamp ("important move") c'est contre les pertes de paquets. (tres frequent sur internet)
    - client side predication => il faut pouvoir retourner dans le passer et recommencer à partir de la première divergence avec le serveur. (pour le Lag)
    - 20-25Hz maximum pour les envoi réseau depuis le serveur.
    - Avoir un serveur de simulation pour les test (qui permet %perte de paquets, lag en ms. le source listé au-dessus a ca aussi.) et qui permet de differencier les erreurs de protocole, socket, os, des erreurs de design.
    - Du LOD sur le réseau. N'envoyer les position+quaternions que des voitures dans un champ rapproché limité (plus que la vision). Les autres seront justes un X,Y sur une map 2D de l'utlisateur qui peut se coder sur 16bits.
    - plusieurs type de paquet des lents () et des rapides (score, nouveau client, joueurs hors du champ rapproché, etc...). utiliser TCP pour les lents est une option
    - si client side prediction, beaucoup de boulot pour etre sur que les résultats seront les memes sur tous les ordis (ne pas oublier un appel à "srand()" par exemple). La mailing liste ODE a des archives complete sur ce sujet.
    - Pour le NAT voir la libNAT de john Wattte (le meme que sur la liste ode) et surtout toujours utiliser le meme port.
    - Reserver l'ajout des threads après le codage complet du client et serveur (pour debogger tranquille), ou mieux pouvoir le désactiver facilement (#define). les threads sont utiles dès que le nombre de clients devient important (>4) mais rende le deboggage bien plus complexe.
    -etc..
  • # Dead reckoning

    Posté par  . Évalué à 6.

    Cherche des infos sur les algos de dead reckoning.

    Ca consiste à faire des calculs simple de trajectoire physique pour anticiper le mouvement / compenser le lag / ou interpoler durablement une position probable lors d'une perte prolongée (enfin pas trop) de paquets.

    On peut dériver le concept pour bien des choses, et surtout l'adapter selon le cas particulier que représente le jeu (si dans un FPS on peut s'attendre à des demi tours brusques quasi instantanné, la probabilité qu'un telle chose se produise dans une course de voiture est nulle, sauf peut être si une collision frontale avec un mur se produit :p ).

    Evidemment on ne peut pas deviner à 100% ce que va faire le joueur (sinon le joueur ne sert plus a rien :P) donc ca marche bien sûr lors d'un ping faible (et plutôt constant)

    Effectivement l'UDP est tout indiqué pour faire ce qu'on peut qualifier de temps réel mou.
  • # Read the source ?

    Posté par  (site web personnel) . Évalué à 3.

    Je crois me rappeler que les sources de QIII sont libres depuis un petit moment... Ne serait-ce pas une bonne source d'infos, en complément des très intéressants commentaires ci-dessus ? Ou les sources d'autres jeux ? En priant pour que le code soit commenté...
  • # lien a regarder

    Posté par  (site web personnel) . Évalué à 2.

    Salut, j'ai pas le temps de lire tout ton journal la mais je te file un lien qui pourra surement t'interesser, ca decrit l'architecture reseau d'unreal
    http://unreal.epicgames.com/Network.htm
    Lecture interessante :-)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.