J'ai une question sur UDP. Pour le contexte, je travaille sur un jeu d'action en temps réel.
J'ai beaucoup lu sur les différences entre UDP et TCP et je sens que je les comprends assez bien, mais il y a un élément qui ne s'est jamais senti correct, et c'est la fiabilité , et en particulier les remerciements . Je comprends que UDP n'offre aucune fiabilité par défaut (c'est-à-dire que les paquets peuvent être abandonnés ou arriver en panne). Lorsqu'une certaine fiabilité est requise, la solution que j'ai vue (ce qui est logique sur le plan conceptuel) consiste à utiliser des accusés de réception (c'est-à-dire que le serveur envoie un paquet au client, et lorsque le client reçoit ce message, il renvoie un accusé de réception au serveur) .
Que se passe-t-il lorsque l'accusé de réception est abandonné?
Dans l'exemple ci-dessus (un serveur envoie un paquet à un client), le serveur gère la perte potentielle de paquets en renvoyant des paquets à chaque trame jusqu'à ce que des accusés de réception soient reçus pour ces paquets. Vous pouvez toujours rencontrer des problèmes de bande passante ou des messages hors service, mais uniquement du point de vue de la perte de paquets, le serveur est couvert.
Cependant, si le client envoie un accusé de réception qui n'arrive jamais, le serveur n'aurait d'autre choix que d'arrêter éventuellement d'envoyer ce message, ce qui pourrait interrompre le jeu si les informations contenues dans ce paquet étaient nécessaires. Vous pouvez adopter une approche similaire pour le serveur (c.-à-d. Continuer d'envoyer des accusés de réception jusqu'à ce que vous receviez un accusé de réception pour l'acquittement?), Mais cette approche vous obligerait à boucler d'avant en arrière pour toujours (puisque vous auriez besoin d'un accusé de réception pour l'accusé de réception pour l'accusé de réception etc).
Je pense que ma logique de base est correcte ici, ce qui me laisse deux options.
- Envoyez un seul paquet d'accusé de réception et espérez le meilleur.
- Envoyez une poignée de paquets d'accusé de réception (peut-être 3-4) et espérez le meilleur, en supposant qu'ils ne seront pas tous abandonnés.
Y a-t-il une réponse à ce problème? Suis-je fondamentalement incompréhensible quelque chose? Y a-t-il une garantie d'utiliser UDP dont je ne suis pas au courant? J'hésite à avancer avec trop de code de réseautage jusqu'à ce que je sois à l'aise que ma logique soit solide.
la source
Réponses:
C'est une forme du problème des deux généraux , et vous avez raison - aucun nombre de tentatives n'est suffisant pour garantir parfaitement la réception.
Dans la pratique des jeux, il y a généralement un horizon au-delà duquel les informations n'ont pas vraiment d'importance, même si elles arrivent techniquement de manière fiable. Comme découvrir que vous aviez un tir à la tête parfait il y a 2 secondes - il est trop tard pour que le joueur utilise cette information maintenant.
Si votre perte de paquets est si élevée que vous ne pouvez pas systématiquement obtenir les informations nécessaires à l'intérieur d'une fenêtre de réaction serrée, alors pour un jeu en temps réel, vous feriez mieux de donner un coup de pied au joueur et d'essayer de trouver une meilleure correspondance pour lui ailleurs, plutôt que de continuer à essayer d'envoyer le paquet pour émuler une connexion fiable.
Pour cette raison, certains systèmes de réplication de jeux ignorent complètement les accusés de réception et les tentatives et choisissent de spammer la plus récente mise à jour aussi souvent qu'ils le peuvent. Si l'un est tombé ou arrive en retard, tant pis, sautez-le, prenez le suivant et continuez, en vous appuyant sur les systèmes de prédiction et d'interpolation pour lisser l'écart et minimiser les hoquets visibles pour le joueur.
Je veux soudainement commencer à appeler cette "réplication Simba" pour la façon dont elle ignore les problèmes du passé et essaie de vivre dans le moment présent. ;)
Une solution hybride consiste à prendre de l'avance en envoyant la nouvelle mise à jour ET (puisque les mises à jour de l'état du jeu peuvent souvent être assez petites / compressibles ) également intégrer la dernière mise à jour, et peut-être celle d'avant ... Donc, juste au cas où le client les manquerait , vous n'avez pas besoin d'attendre un aller-retour complet pour le découvrir et le corriger. La plupart du temps, le client a déjà vu cela, il y a donc des données redondantes de cette façon, mais la latence pour corriger un message manqué est plus faible. Les mises à jour du client peuvent inclure le numéro d'index de la dernière mise à jour consécutive qu'ils ont vue, vous pouvez donc être minimalement conservateur avec le nombre d'anciennes mises à jour que vous incluez dans le prochain paquet de mise à jour.
Vous pouvez également implémenter un système à deux niveaux comme un autre type d'hybride, où l'état à courte durée de vie est répliqué de manière peu fiable et où l'état à long terme est synchronisé de manière fiable, en utilisant TCP ou votre propre implémentation de fiabilité avec une nouvelle tentative élevée compter. Cela devient cependant plus complexe à gérer, car vous avez deux systèmes de messagerie à gérer et les deux instantanés peuvent être désynchronisés, ajoutant une toute nouvelle classe de cas de périphérie.
la source
L'approche utilisée par TCP est que l'expéditeur continuera à renvoyer le paquet jusqu'à ce qu'il reçoive un accusé de réception. Le récepteur ignorera les paquets en double, mais enverra toujours des accusés de réception pour eux. L'expéditeur ignorera les accusés de réception en double.
Si un paquet est perdu, l'expéditeur le renvoie, comme vous le savez déjà.
Si un accusé de réception est perdu, l'expéditeur renvoie le paquet d'origine, ce qui oblige le destinataire à renvoyer l'accusé de réception.
Si un accusé de réception n'est pas reçu dans un certain délai (peut-être 60 secondes ou 20 tentatives), le joueur est considéré comme déconnecté du jeu. Vous devez implémenter une sorte de règle de temporisation, sinon un joueur qui débranche son câble réseau bloquera les ressources de votre serveur pour toujours.
la source
Si vous souhaitez réinventer TCP, il est judicieux d' examiner d' abord TCP , qui traite du problème exact que vous décrivez (une partie de la solution consiste à utiliser des valeurs définies par l'utilisateur pour les tentatives de tentative et les délais d'attente).
Les solutions qui utilisent 2 canaux, un canal TCP (pour une communication fiable) ainsi qu'un canal UDP (pour une communication à faible latence) ne sont pas rares.
Certaines solutions détectent quand un client manque des informations pendant trop longtemps et démarrent une resynchronisation, qui peut utiliser UDP ou TCP.
Une autre approche courante consiste à concevoir une communication telle qu'elle ne repose pas du tout sur des remerciements, mais cela sort du cadre de la question.
la source
Dans un RTS, vous ne pouvez vraiment pas utiliser un protocole comme TCP et vous ne pouvez pas non plus rendre UDP fiable. Si vous essayez, le jeu se fige chaque fois qu'il y a une coupure de réseau.
Au lieu de cela, vous concevez le protocole de sorte que les paquets manqués ne soient pas trop importants.
La version courte est que vous ne vous souciez pas de la dernière image des autres joueurs tant que vous savez où ils sont maintenant . La version longue est plus compliquée.
La question devient alors, que faites-vous quand un paquet est manquant? Et la réponse est ... vous devinez. Le joueur se déplace probablement en ligne droite, non? Déplacez-les simplement un peu plus loin sur cette ligne. ... Sauf qu'aucun joueur RTS ne se déplace jamais en ligne droite. Et puis il y a la détection de collision.
C'est dur. De nombreux jeux se trompent. On peut faire valoir qu'il n'y a pas de bonne réponse à cela, seulement divers torts qui peuvent être échangés.
La raison pour laquelle ces jeux fonctionnent plutôt bien est non seulement qu'ils ont longuement réfléchi à ces problèmes, mais aussi que l'Internet est devenu assez fiable. Presque tous les paquets UDP atteignent réellement leur destination en temps opportun. (Sauf s'il y a un problème permanent comme un pare-feu)
la source