Comment synchroniser les horloges dans un jeu multijoueur?

8

J'ai 2 à 3 clients qui peuvent échanger des messages via Apple Game Center.

La seule synchronisation dont j'ai besoin est: démarrer le jeu au même moment.

Je suppose que cela implique une synchronisation d'horloge. Comment y parvenir?

GameCoder
la source
3
La réponse courte est «vous ne pouvez pas». Le retard inévitable (et incohérent) dans les communications entre les appareils signifie que même si un appareil dit «Je crois qu'il est maintenant 12 h 00: 00 h 00», il pourrait facilement se situer entre 12 h 00 h 00 et 10 h 00 min 00 s 05 heure à laquelle ce message est reçu ailleurs. Dans un modèle basé sur un serveur, vous ne pouvez pas faire mieux que de simplement envoyer votre serveur le message «Démarrer le jeu» aux clients en même temps.
Steven Stadnicki
Heureusement que je n'ai pas besoin de perfection. Comment faire mieux que rien? Peut-être: chaque joueur envoie un message "START" et commence dès qu'il reçoit le message START de tous les autres joueurs
GameCoder
1
@StevenStadnicki Les retards du réseau ne rendent pas cela impossible. Le problème général est appelé "synchronisation d'horloge" et il est bien étudié. (Cependant, si vous ne contrôlez que les points de terminaison, il n'est pas possible de tenir compte de l'asymétrie des retards.)
Praxeolitic
Je comprends peut-être mal votre problème, mais ne pouvez-vous pas simplement faire en sorte que le message que vous envoyez aux clients indique légèrement une heure de début dans le futur? Si l'heure est nowet que vous envoyez un message pour commencer exactement now + halfSecondalors tant qu'ils reçoivent tous le message dans la demi-seconde et tant que leurs horloges système sont correctement synchronisées, elles démarreront toutes en même temps.
Mark

Réponses:

12

Le commentaire de Steven est juste: c'est théoriquement impossible à faire.

Heureusement, dans la pratique, vous pouvez vous en approcher, c'est ainsi que fonctionnent des choses comme le NTP .

Par exemple, mieux que d'envoyer simplement un message à 3 clients disant "commencer maintenant", vous pouvez échanger quelques messages ping au préalable pour mesurer le temps nécessaire pour envoyer un message au client, et lorsque vous envoyez le message de démarrage, au lieu de "commencer maintenant", dites "démarrer dans X millisecondes" et ajustez X pour les différents temps pris pour qu'un message arrive.

par exemple.:

  • Vous envoyez un message au client 1 et obtenez une réponse 20 ms plus tard. Vous pensez qu'il faut environ 10 ms pour envoyer un message au client 1.
  • Vous faites de même pour le client 2 et obtenez une réponse 28 ms plus tard - le temps de transmission est donc susceptible d'être d'environ 14 ms.
  • Vous envoyez donc un message au client 1 disant "démarrer le jeu dans 50 ms" et vous en envoyez un au client 2 disant "commencez le jeu dans 46 ms". Le client 2 recevra le message environ 4 ms plus tard, mais attendra 4 ms de moins avant de démarrer le jeu.

Cela ne peut pas garantir la synchronisation car le temps nécessaire pour envoyer un message sur Internet varie et parce qu'il peut être différent dans chaque direction. Le premier, vous pouvez réduire les effets en effectuant la mesure plusieurs fois et en prenant une lecture médiane. La seconde est plus délicate et peut être théoriquement impossible à résoudre (bien que je ne me souvienne pas de la preuve pour le moment). La bonne nouvelle est que vous n'avez probablement pas besoin de beaucoup de précision.

Kylotan
la source
Je devrai probablement limiter l'activité de mes applications pendant la synchronisation - en ce moment, je donne du temps CPU au sous-système réseau à chaque cadre de dessin. Si la fréquence d'images est de 30 FPS, je répondrai quand même au ping en 1/30 = 33 ms. Je pourrais aussi ajouter les 33 ms, ou la différence entre "le temps de réception de la pile TCP" et mes MilisecondTicks () actuels.
GameCoder
2
Si vous espérez garder les systèmes synchronisés avec précision image par image pendant le jeu, je pense que c'est une partie perdante et vous devriez abandonner maintenant. Les horloges et la latence dériveront et les informations prendront toujours du temps à voyager. Quoi que vous essayiez de faire, vous n'avez probablement pas besoin de ce niveau de synchronisation.
Kylotan
Je pense que nous manquons de point. Ce n'est pas la perfection qui me dérange, c'est l'expérience utilisateur. Cela peut être bon ou meilleur, et j'aimerais mieux. C'est ça.
GameCoder
2
Une bonne expérience utilisateur ne nécessite pas de synchronisation précise. C'est pourquoi la plupart des jeux ne l'essaient pas.
Kylotan
1
Il est théoriquement impossible si chaque joueur contrôle sa propre chauve-souris, car cela prend un temps non nul pour que ces informations atteignent l'autre ordinateur, pendant lequel les 2 parties ont des informations différentes. Au lieu de cela, vous trouvez des moyens de faire face aux différences. Votre problème n'est pas vraiment différent de tout autre jeu en ligne rapide et il y a plusieurs autres questions sur la façon de gérer la mise en réseau pour eux sur ce site. L'un est ici: gamedev.stackexchange.com/questions/22444/…
Kylotan
3

Comme mentionné, c'est impossible, alors j'essaierais une autre approche:

Si vous n'avez pas de serveur dédié, élisez un client participant pour devenir l'hôte (cela peut être transféré si le besoin s'en fait sentir).

L'hôte exécutera désormais toutes les logiques de jeu importantes, telles que la détection des coups, les contrôles de l'IA, la gestion des stocks, etc., ainsi que le suivi du temps (c'est-à-dire la dictée du temps de jeu).

Les autres clients essaieront simplement de rester synchronisés avec l'hôte, en essayant d'estimer ou d'approximer la valeur attendue. Si le décalage augmente ou qu'il y a perte de paquets, les choses peuvent devenir saccadées, mais il est trivial de rattraper le retard, essentiellement en attendant la prochaine mise à jour.

La plupart des jeux (en particulier les FPS) cachent ce fait en faisant leur propre calcul local pour le propre mouvement du joueur, les coups de feu tirés, etc. pour éviter que le jeu ne se sente lent. Tout est toujours corrigé en fonction des données du serveur. Cela peut entraîner une certaine confusion, par exemple, vous vous voyez tirer sur l'ennemi, mais au même moment où vous tombez mort (sans que l'ennemi ne frappe), mais c'est toujours une bien meilleure approche que la synchronisation complète.


Si vous insistez toujours pour que tout soit synchronisé, vous voudrez probablement créer une sorte de compteur d'étapes ou de trames, de sorte que tous les clients ne traitent qu'une seule étape logique, puis synchronisent leurs données, etc. Gardez à l'esprit que cela peut être à la fois de la bande passante intensive ainsi que laggy, donc je ne recommanderais pas de le faire à moins d'avoir beaucoup de données autrement et votre gameplay est basé sur le tour (par exemple les jeux de style Artillerie / Worms).

Mario
la source
1

Je recommande de synchroniser les temporisateurs système sur tous les clients et le serveur au moyen du protocole NTP [Stratum 2], puis le serveur envoie une commande pour démarrer le jeu à l'heure spécifiée, disons, lorsque tous les temporisateurs atteignent 0:05:00. Cette approche devrait vous donner une synchronisation précise de 3 à 4 ms, je crois.

ivan866
la source