Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Réseau et multijoueurs Discussion :

Politique de communication des positions entre client et serveur


Sujet :

Réseau et multijoueurs

  1. #1
    Futur Membre du Club
    Politique de communication des positions entre client et serveur
    Coucou,

    Cherchant à progresser, je me suis mis en tête de faire un tuto sur le client/serveur avec socket en java. Je me suis naturellement tourné vers celui-ci : http://defaut.developpez.com/tutorie...r/multithread/
    Fort de cet expérience, j'ai fait un chat en mode console, que j'ai enrichi au fir et à mesure (salon, whisper, kick, privilège, mp etc...)
    Puis de file en aiguille, une interface cliente, et une console d'admin, une authenfication avec appel JDBC, et ainsi de suite...


    Là, je me suis dit que je voulais faire plus, faire bouger des sprites sur un plan 2D (qui contient donc les coordonnée x/y de chaque sprite).
    Au moment de réfléchir sur l'implémentation du bousin. J'hésite.

    Voici les solutions qui s'offrent à moi :
    Solution 1 :
    Quand un client (A) bouge, il envoie au serveur.
    Après vérif du serveur, il envoie à tous les clients sur le plan 2D que A a bougé.
    Si B bouge, de même, et ainsi de suite.
    Inconvénient, si tout l'alphabet bougent chaque c'est chaque mouvement qui gènère un nombre important d'envois.
    Faudrait il alors implémenter coté serveur, pour ne pas envoyer les mouvements à tout le monde, mais seulement à ceux qui sont proches (et qui donc voient le mouvement !) ?

    Solution 2 :
    Quand un client (A) bouge, il envoie au serveur.
    Quand un client (B) bouge, il envoie au serveur.
    Après vérif du serveur, il n'envoie à tous les clients que tous les xxx Millisecondes l'état du plan2D. Pas après chaque mouvement, donc, mais à intervalle régulier.
    Dans les faits, je fais un thread coté serveur qui se réveille toutes les xxx Milliseconde, sérialise le plan, et va les envoyer aux clients.
    Inconvénient, comme coté serveur, j'ai un thread par client, je parcours tous les client, et envoie sur leur "outputStream"

    Solution 2(bis) :
    Idem que le la solution 2. Mais ici plutôt que de faire un thread unique qui se réveille toutes les XXX secondes, c'est chaque thread coté serveur qui envoie à intervalle régulier au client.

    Solution 3
    Un mix de la solution 1 et 2bis.
    A chaque mouvement on envoie à chaque client uniquement ce qui se passe autours de lui et celà uniquement à intervalle régulier.

    Pas sur que je sois bien clair...

    Merci d'avance pour vos conseils éclairés.

  2. #2
    Responsable 2D/3D/Jeux

    Bonjour,

    Pour éviter les mauvais effets du lags, il faudra créer un système d'interpolation (enfin, extrapolation) coté client.
    Ensuite, vous pouvez très bien faire un thread coté serveur qui envoie au client toutes les N millisecondes, les changements du plan (pas la position de tous les joueurs, mais que celles qui sont modifiées).
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  3. #3
    Futur Membre du Club
    Ce qui nous fait une solution 4 :
    Envoyer uniquement le Delta des modifications.
    4 bis : A tout le monde
    4 ter : Uniquement à ceux qui sont proches

    Et pour 4 bis et 4 ter, choix de l'envoyer soit :
    - dès qu'un mouvement a été effectué,
    - ou à intervalle régulier l'ensemble des mouvements depuis le dernier envoi...

  4. #4
    Responsable 2D/3D/Jeux

    Uniquement à ceux qui en ont besoin, cela évite la triche du coté client, car il pourrait afficher les unités qui sont dans le brouillard, ou autre cas de ce genre.
    Je pense, à un intervalle régulier, afin de regrouper les informations à envoyer. Il est aussi possible de mieux gérer l'interpolation/extrapolation avec des pas fixes, je pense. Peut être aussi mieux gérer les pertes de paquets.
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  5. #5
    Expert confirmé
    Solution 5
    * quand un client quelconque bouge, il envoie un timestamp + sa position de départ + son vecteur déplacement + sa vitesse
    * lorsque ce client change de direction ou de vitesse, il envoie un timestamp + sa position + son vecteur déplacement + sa vitesse
    * lorsque ce client s'arrête, il envoie un timestamp + sa position d'arrêt
    * Lorsque le serveur reçoit des données liées au déplacement d'un joueur, il envoie une copie de ces données (validées, bien sûr) aux client qui voient le joueur

    Cette solution porte un nom : c'est une technique appelée dead reckoning (http://en.wikipedia.org/wiki/Dead_reckoning). Ce sont les clients qui font l'interpolation nécessaire à l'affichage du mouvement. Le serveur fait lui aussi des calculs, notamment pour dire au joueur "non, tu t'arrête là parce que tu ne traverse pas les murs" (à ce moment, il avertit aussi les autres clients concernés).

    Le volume de données à transmettre est relativement réduit, parce que le joueur change peu de direction et/ou de vitesse lorsqu'il joue - même s'il le fait deux fois par seconde, on parle de 64 octets par seconde environ (en 3D : timestamp = 1 int + (position + vecteur = 6 float + vitesse = 1 float, le tout X2 pour les deux changements dans la même seconde). Au niveau de la BP upload du serveur, même avec 1000 clients connectés et ayant le joueur en visu, ça ne fait guère que 64K/s environ à envoyer - ça reste acceptable pour un jeu online. Si seulement 10% des joueurs le voient, on tombe à 6,4Ko/s (un modem 56Kbits le gère très bien).

    Le calcul coté serveur est lui-même relativement simple : il s'agit de savoir si le joueur traverse ou non quelque chose : c'est du lancer de rayon, qui permet avec les données reçue de calculer le moment exact où le joueur sera bloqué - et donc de prévoir le moment où il faudra lui envoyer un message "stop".

    On peut mixer cette solution avec une de tes solutions, en envoyant par exemple toutes les secondes le timestamp + position + vecteur + vitesse tant que le joueur ne change ni son vecteur de déplacement ni sa vitesse. Ca permet de s'assurer que le déplacement est toujours en cours et qu'aucun paramètre de mouvement n'a changé. Et ce ne coûte que 32 octets.
    Cette signature n'a pas pu être affichée car elle comporte des erreurs.

  6. #6
    Responsable 2D/3D/Jeux

    Juste pour information :

    on tombe à 6,4Ko/s (un modem 56Kbits le gère très bien).
    Dans Counter Strike, c'est comme ça que les gens utilisent le débit réseau, pour savoir s'il y a des joueurs dans la zone (astuce assez connue)
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  7. #7
    Futur Membre du Club
    Citation Envoyé par Emmanuel Deloget Voir le message
    Solution 5
    * quand un client quelconque bouge, il envoie un timestamp + sa position de départ + son vecteur déplacement + sa vitesse
    * lorsque ce client change de direction ou de vitesse, il envoie un timestamp + sa position + son vecteur déplacement + sa vitesse
    * lorsque ce client s'arrête, il envoie un timestamp + sa position d'arrêt
    * Lorsque le serveur reçoit des données liées au déplacement d'un joueur, il envoie une copie de ces données (validées, bien sûr) aux client qui voient le joueur

    Cette solution porte un nom : c'est une technique appelée dead reckoning (http://en.wikipedia.org/wiki/Dead_reckoning). Ce sont les clients qui font l'interpolation nécessaire à l'affichage du mouvement. Le serveur fait lui aussi des calculs, notamment pour dire au joueur "non, tu t'arrête là parce que tu ne traverse pas les murs" (à ce moment, il avertit aussi les autres clients concernés).
    Merci pour vos messages !
    Pour je vais lire tout ce que je peux sur la "navigation à l'estime" !
    Ça a l'air d'être la solution la plus utilisée.

    Avant ton message, j'avais commencé à reprendre mon archi coté serveur serveur.
    Pour l'instant,
    1 Classe (chez moi appelé Connectivity serveur), qui contient une map des "ThreadClients" (1 threadClient par client connectés).
    1 Classe GestionActionThread qui traite les actions (toute actions reçues d'un thread Client est transmis à cette classe dans une Pile qui les "résout" dès qu'elle peut). Je me suis un peu battu avec les "synchronized" . Une action résolue coté serveur va générer N réponses à dispatcher aux clients. Dans les faits chaque "ThreadClient" a lui une file de "Réponse".
    1 Thread qui tick toutes les N millisecondes (pour l'instant 100), qui demande aux ThreadClients de renvoyer la file complètes de réponses.

    En tout cas ça progresse bien.

    Par contre, je suis bien saoulé par le designer graphique de Netbeans, mais ce problème n'a rien à voire avec la partie client/server

    Merci à vous

    Vraiment top d'avoir ce genre de retour quand on se pose plein de questions comme moi !

    Krazey

  8. #8
    Futur Membre du Club
    ...
    Après une bonne partie de la matinée à lire. Une nouvelle question m’apparaît.
    l'idée du Dead Reckoning est de se baser sur le timestamp pour valider/invalider à posteriori le mouvement.
    Mais le timestamp client est modifiable par le client... non ?
    Vous synchroniser les clients grâce à NTP ?
    J'ai du rater un truc là, non ?

  9. #9
    Invité
    Invité(e)
    Moi je fais comme ça (dans mon jeux) :

    -J'envoie une demande de la position des personnages qui sont visible par le client au serveur toute les x secondes (appelons ce temps, T) pour ne pas surcharger la bande passante, et j'envoie en plus, le temps de latence (temps mis par le client pour recevoir la réponse à une requête envoyé par le client divisé par deux, appelons ce temps, L), pour que le serveur puisse estimé la position des joueurs lorsque le client recevra le message.
    Faire une moyenne des temps de latences serait sûrement mieux.

    Entre temps, le serveur et le client continuent à mettre à jour la position des personnages normalement en fonction du temps boucle.
    Ou bien tu peux aussi faire une interpolation côté client c'est à dire estimer la position du personnages après T + L temps et calculer la position du personnage en fonction du temps écoulé depuis la dernière requête. (je vais appeler ce temps, le temps D)
    Ce qui donne côté client : nouvellPosition = anciennePosition + dir * (vitesse / (T + L) * D)

    Je ne pense pas que tu aies besoin de NTP, mesurer le temps dans le programme devrait suffire.