Merci .
Etape 3: Gestion du snapshot
Note: Ce qui suit concernera uniquement l'architecture serveur (le client aura sa part du gâteau à la fin).
Avoir un snapshot tout beau tout propre ne suffit pas. Pour optimiser l'envoi des paquets sur le réseau, notre objectif est de réaliser un "Snapshot Delta": un snapshot qui ne contient que les changements d'états des entités, depuis le précédent envoi. Un peu comme une sauvegarde différentielle du jeu. L'idée est de ne pas transmettre des informations que les clients connaissent déjà.
Par exemple, si le joueur se déplace verticalement, seule sa position Y sera modifiée. Quand le serveur souhaite informer les clients de la nouvelle position du joueur, il est inutile de leur envoyer sa position X. Seulement sa nouvelle position Y.
Or, pour faire un delta (ou une différence) d'un objet, il faut pouvoir récupérer son état antérieur. Avant de créer notre "Snapshot Delta", il faut savoir comment stocker les précédentes créations. Mais un simple champ previousSnapshot dans la classe NetworkServer ne suffit pas.
Il faut considérer qu'un joueur ne possède pas le même contexte qu'un autre joueur et qu'il n'a pas besoin des mêmes informations que tous les autres participants de la partie. De plus les joueurs n'ont pas besoin d'avoir une vue sur tous les éléments du jeu, au même moment.
Du coup, il faut pouvoir gérer les snapshots par client ET par gameobjects.
(Merci quake3!)
Alors, je revois la méthode d'envoi du snapshot server - SnapshotWorld - aux clients:
Au départ, je génère le SnapshotWorld et ses SnapshotGameObject du jeu. Ce snapshot doit contenir toutes les informations de la partie à un temps T.
Ensuite, pour chaque client, je génère un nouveau objet SnapshotDelta. Je pense que vous aurez compris le but de ce snapshot.Spoiler Alert!
Pour l'instant, nous n'allons pas étudier ce qui sera dans ce SnapshotDelta. Tout ce que je peux dire, c'est qu'il contiendra une liste de SnapshotGameObjectDelta. Je traiterai cette partie dans la prochaine étape et nous allons supposer que j'ai produit par magie notre snapshot delta.Spoiler Alert!
Avant d'envoyer notre chef d'oeuvre au client, il faut l'historier pour les prochaines générations de snapshots delta.
Pour cela, je vais réutiliser l'objet ClientGameData - où j'enregistre toutes les informations de connexion du joueur, son identifiant et les gameobjects qu'il utilise. A cet objet, je peux lui ajouter un nouveau champ: une liste de snapshots par gameobject.
Après avoir produit un SnapshotGameObjectDelta, je le duplique pour sauvegarder sa copie dans la liste que je viens de créer. L'original sera inséré dans le conteneur SnapshotDelta.
Ainsi, je viens d’historier les snapshots par client et par gameobject. Youpi tralala!
Dans un même temps, je peux copier les instances de SnapshotWorld pour le serveur, dans une autre liste. Cela ne me sert à rien pour le moment... Mais peux-être dans les prochains chapitres :teaser:.
Nous pouvons peaufiner cette gestion de snapshots en ajoutant un paramètre: si le client a besoin d'un snapshot delta ou complète (= FULL). Ce paramètre est très important, surtout quand le joueur a besoin de réinitialiser son contexte de la partie après une perte de paquet (Packet loss) ou après une perte de connexion. Ou sur la demande du joueur.
Après avoir historié tout ca, je peux enfin transmettre mon super SnapshotDelta à mon client (après avoir sérialisé et encodé le tout en Base64, bien sûr).
Il ne reste plus qu'à reproduire tout ce traitement pour tous les autres clients de la partie, et ca sera finis .
Tout ca c'est bien... Mais je n'ai parlé que de l'architecture serveur. Et du côté client ?
Nous pouvons appliquer ce même procédé pour le client: Avant d'envoyer le SnapshotClient au serveur, nous faisons un delta avec le précédent envoi et nous historions chaque version du snapshot. C'est facile (le client ne gère qu'un seul snapshot et ne l'envoi qu'au serveur), ca fonctionne très bien et cela peut grandement optimiser la bande passante.
Sauf dans un cas spécifique.
Peux-être que je ne l'ai pas précisé plus tôt, mais il est fortement conseillé que le snapshot client ne stocke que des "états" de commandes: Le joueur appuie actuellement sur la touche "déplacement gauche", le joueur est en train de sauter, etc. Il est compliqué d'informer précisément le serveur que le joueur "tire une seule fois à un instant T", surtout quand le lag est présent. Il existe des solutions à ce problème, mais qui ne sont pas simples à mettre en place.Spoiler Alert!
Si ce genre d'information détaillée est importante pour le serveur et pour le jeu (ce qui n'est pas rare), rien n'empêche le développeur de le gérer. Par contre, il faudra que le snapshot delta puisse prendre en compte ce détail .
Allez, la prochaine étape, j'optimise tout ce bordel!