000ccae-t172 revient avec un nouveau document sur les protocoles réseau de VALVe. Cette fois-ci, il nous explique comment se déroulent les interactions entre le serveur et le client quand vous jouez à HL2 ou Counter Strike : Source.

  • Si vous voulez savoir comment certains joueurs se rendent plus difficilement touchables en modifiant leur fichier de configuration ;
  • si vous ne comprenez pas pourquoi vous avez du choke avec votre connexion ADSL 20Mb ;
  • si vous ne savez pas quoi utiliser comme rate/cmdrate/updaterate ;
  • si vous possédez un serveur et que vous voulez l’optimiser ;
  • si vous ne voyez pas l’intérêt de virer l’interpolation ;
  • si vous croyez malin de ne pas utiliser la prédiction ;
  • ou si vous êtes simplement curieux de savoir comment fonctionne un netcode moderne

C’est l’article qu’il faut lire ![–SUITE–]

Introduction

Half-Life 1 a été sujet à de nombreuses spéculations concernant son netcode et l’art d’optimiser ses paramètres. Ce « sac à rumeurs » n’a pas échappé à la deuxième version du moteur, à savoir le moteur Source.

Aujourd’hui sur le net, à propos de ces « boîtes noires », on trouve des explications le plus souvent totalement contradictoires (l’une recommandant tel paramètre et l’autre exactement le contraire), tellement qu’aujourd’hui personne ne sait qui croire.

Ce document est une N + Nième interprétation du fonctionnement et des paramètres de ce Netcode. Mais, là où d’autres n’indiquent que les paramètres à définir (« tu règles ton cmdrate sur X, ton updaterate sur Y, tu verras ça va te booster ton jeu »), ce document est une tentative d’explication en profondeur et avec le plus de détails possible des principes directeurs du Netcode, de son fonctionnement, des techniques mises en oeuvre et des paramètres associés.

Au lieu d’appliquer des paramètres et de les tester en jeu, une technique qui dépend énormément des circonstances et de l’humeur du jour du testeur et difficilement objective, ce document présente une approche différente, tentant de découvrir ce sur quoi un paramètre précis influe, non pas à l’extérieur, mais à l’intérieur, ceci pour mieux comprendre ses véritables effets (secondaires ou non) sur la qualité du jeu. Le principe étant de partir des documentations officielles (Valve SDK), des tests « formels » (c’est à dire montrant l’influence de telle situation sur une valeur chiffrée, sur le netgraph le plus souvent) et, dans une moindre mesure, des faits communément admis (c’est à dire reconnus par la grande majorité des joueurs) pour en déduire d’autres faits qui eux-mêmes auront des conséquences sur le comportement du serveur ou du client.

A la lumière de cette introduction il apparaît que vous devez lire ce document avec un esprit critique. Toutes les thèses avancées ici ne sont pas forcément vraies. Il s’agit juste de réfléxions personnelles basées sur la documentation officielle. Pour les mêmes raisons, il est possible que le contenu de ce document soit sujet à variations en fonction de l’évolution de mes théories (voir la section Historique). Il est également possible qu’une partie de ce document contredise ce que vous avez lu ailleurs : à vous de vous faire une opinion.

Tick tac fait le serveur

Afin de comprendre comment communique le client et le serveur, il m’a paru nécéssaire d’étudier le fonctionnement de ce dernier afin d’en déduire la nature des échanges avec le client.

Avant toute chose, rappellons que le serveur est le seul, parmi les parties mises en communication, à avoir la « vraie » vision du jeu. Il a priorité sur toute spéculation faite par le client. On dit que le serveur a autorité (server is authoritative en anglais) sur le jeu. Cela signifie que les différents clients ne communiquent jamais directement entre eux, il ne connaissent même pas l’adresse IP de leurs voisins : leur seul interlocuteur est le serveur.

Ticks et tickrate

Un tick est, dans une partie, une unité de temps élémentaire et indivisible. Il ne peut rien se passer entre deux ticks. Ce terme désigne une génération du monde par le serveur.

Au cours d’un tick, le serveur récupère et analyse les informations envoyées par les clients quant à leur état. En partie grâce à ces informations, il va générer le « monde » : il détermine la position et l’état des joueurs et des différentes entités telles que les objets physiques (bidons…). Cette tâche est particulièrement ardue du fait de la complexité des éléments à gérer, surtout dans le cas du moteur physique. Une fois le monde généré, il envoie cette nouvelle vision de la partie aux clients connectés.

Le rôle principal du serveur est de générer constamment des ticks pour réagir aux actions des clients et des entités. Le nombre de ticks générés par seconde est appellé tickrate. Exprimé en ticks par seconde, il est en outre paramétrable par l’administrateur du serveur via le paramètre -tickrate . Il est par défaut réglé à 33 ticks par seconde, ce qui signifie que le monde sera recalculé toutes les 30 millisecondes (1000 ÷ 33 ~= 30).

Augmenter le tickrate n’est pas sans contrepartie : en effet le serveur consommera plus de CPU et de mémoire vive. Pour éviter qu’une surconsommation n’aie des effets néfastes sur les joueurs, le tickrate est dynamique : ainsi, le serveur pourra passer d’un tickrate de 66 à un tickrate de 50 s’il il n’a plus assez de ressources pour tenir la charge imposée. Il convient donc de faire la différence entre le tickrate théorique, paramétré par l’administrateur, et le tickrate réel, qui est le tickrate sur lequel le serveur tourne en réalité à un moment précis. Nous verrons plus tard comment déterminer le tickrate réel d’un serveur.

Au niveau de la gestion des clients connectés, le serveur utilise des sockets dits « non bloquants », la boucle principale étant commandée par le tickrate. Pour cette raison il est absurde de dire qu’un joueur, de par sa connexion ou son ping, peut par sa présence avoir des conséquences négatives sur le jeu des autres joueurs. La consommation d’un serveur et donc son tickrate ne peuvent pas être affectés par la qualité de la connexion d’un joueur.

Les effets du tickrate

Nous allons prendre un exemple précis pour démontrer l’influence du tickrate sur la qualité du jeu.

Nous sommes dans la situation suivante, dans le jeu Counter-Strike Source : un contre-terroriste se déplace du côté gauche de la vision d’un terroriste vers le côté droit. Le terroriste tire sur le contre-terroriste lorsque celui-ci est à mi-chemin de son parcours. On considère qu’il a bien visé et on fait abstraction de la précision de l’arme. La distance de déplacement du contre-terroriste est relativement minime pour mieux constater les effets du tickrate.

Situation « réelle »
000cc7
Situation sur un serveur tickrate 33
000cc8

Vous pouvez constater que à t = 15 ms, le serveur n’a pas généré de nouveau tick (puisque l’intervalle de tick est à 30 ms), donc le tir du terroriste n’est pas encore inclus et le contre-terroriste est toujours à sa position initiale. En revanche, à t = 30 ms, le serveur génère un nouveau tick et place le contre-terroriste à se position finale tout en incluant le tir du terroriste. Malheureusement, la balle de celui-ci ne touchera pas son but car pour le serveur le contre-terroriste est positionné après la balle : la position intermédiaire n’a pas été calculée.

Lorsque cette situation se produit, les joueurs utilisent en général l’expression « le serveur ne touche pas ».

Situation sur un serveur tickrate 66
000cc9

Sur un serveur de tickrate 66, l’intervale entre deux ticks est de 1000 ÷ 66 ~= 15 ms. Un tick supplémentaire est donc généré à t = 15 ms : la position intermédiaire est calculée, l’impact est bien pris en compte.

Vous devez cependant relativiser cet exemple : la situation n’est pas aussi dramatique qu’elle en a l’air sur un serveur tickrate 33, du fait que le rapport taille des hitboxes/distance parcourue est complètement différent.

FPS Serveur : la grande mascarade

Le serveur possède en plus du paramètre -tickrate une CVAR nommée fps_max, qui est décrite par le moteur comme étant le « frame rate limiter », soit le « limiteur de cadence d’images par seconde ». Beaucoup de personnes pensent qu’augmenter ce paramètre améliore la qualité du serveur. Il n’en n’est rien. Explications.

Vous vous demandez peut-être : « Mais pourquoi les frames existent-elles sur un serveur alors que cette fonction est remplie par le tick ? ». Ce sujet du FPS a nécéssité l’ouverture d’un espace de travail dans lequel j’ai exposé durant quelques jours les données connues, mes hypothèses et les conclusions que je tirais. Cette recherche a porté ses fruits et je suis maintenant à même d’expliquer le concept de FPS serveur.

Pour bien comprendre, prenons un serveur non dédié, dit « listen ». La particularité de ce type de serveur est qu’il y a le serveur et un client dans le même processus. Du coup, les ticks doivent se faire entre deux générations d’images (frames). Les développeurs ont donc été contraints d’enfermer la boucle des ticks dans la boucle des images générées. En plus clair, à chaque itération de la boucle des images, le jeu vérifie avec la valeur du tickrate si il doit générer un tick. Si la réponse est positive il le génère, dans le cas contraire il génère juste l’image sans tick.

Passons au cas du serveur dédié. Sur un tel type de serveur, aucune image n’a besoin d’être générée : seuls les ticks comptent. La boucle des FPS devient donc inutile et devrait logiquement être remplacée par une boucle des ticks. Eh bien non. La boucle des FPS est toujours là et suit le même principe que pour un serveur listen, à la différence qu’elle ne génère plus d’images mais uniquement des ticks. Pourquoi ? Une explication plausible est que les développeurs, dans un souci de maintenabilité du code, ont voulu garder la même architecture sur les serveurs listen que sur les serveurs dédiés. Ils ont donc simplement désactivé la partie client pour passer d’un serveur listen à un serveur dédié.

Etant donné que la boucle des FPS sur un serveur dédié ne génère plus d’images, augmenter la fréquence des itérations de cette boucle via la CVAR serveur fps_max ne changera strictement rien à la qualité du serveur, de même que sa consommation. En revanche, si vous la baissez en dessous de la valeur du tickrate théorique, le tickrate réel descendra à la valeur de fps_max : cela est dû au fait que les ticks sont toujours encapsulés dans la boucle des FPS.

Communication client/serveur

La communication client/serveur dans le jeu doit permettre deux choses :

  • Envoyer l’état des commandes (clavier et souris), c’est à dire les commandes éxécutées par le client, au serveur.
  • Envoyer l’état actuel du jeu au client.

Le serveur ou le client va envoyer ces informations à une fréquence bien précise. Dans le cas des informations client, on appelle cette fréquence cmdrate (fréquence de commandes) et dans le cas des informations sur le jeu envoyées par le serveur, on l’appelle updaterate (fréquence de mises à jour).

Note : le cmdrate et le updaterate dont on parle ici sont les valeurs réelles. Il s’agit du nombre de mises à jour que vous envoyez/recevez en réalité, non de la valeur des CVARs correspondantes.

La bande passante utilisée pour les mises à jour provenant du serveur (donc dont la fréquence est caractérisée par l’updaterate) est auto-limitable grâce à une fonctionnalité appellée rate.

Influence de l’updaterate/cmdrate et facteurs limitants

Vous pouvez déterminer votre cmdrate en cours de jeu en lisant la donnée correspondante du netgraph. Qu’est-ce qui limite cette valeur ?

  • La connexion avec le serveur.
  • La CVAR client cl_cmdrate (par défaut 30) définissant le cmdrate maximum.
  • Le nombre de FPS affichées chez le client. Cette limitation qui peut paraître bizarre est dûe au fait que le client ne peut rien faire durant la génération d’une frame (donc ne peut pas envoyer d’informations); les commandes client sont donc envoyées entre les frames.

Plus votre cmdrate est élevé, plus le client enverra d’informations sur ce qu’il fait au serveur, donc plus vos actions seront prises en compte rapidement par le serveur. En effet, le fait que votre tir soit interprété par le serveur au moment du tick 5 au lieu du tick 6 peut réellement faire une différence (voir tickrate).

L’updaterate est lui aussi lisible sur votre netgraph. Qu’est-ce qui limite cette valeur ?

  • La connexion avec le serveur. Ceci inclut le rate (voir plus loin). Notez que si ce facteur limite l’updaterate, du choke sera généré.
  • La CVAR client cl_updaterate (par défaut 20) définissant l’updaterate maximum
  • La CVAR serveur sv_maxupdaterate (par défaut 60) définissant l’updaterate maximum
  • La CVAR serveur sv_minupdaterate (par défaut 10) définissant l’updaterate minimum
  • Et bien sûr, le tickrate du serveur.

Plus votre updaterate est élevé, plus votre vision du jeu sera similaire à celle du serveur. L’updaterate est cependant considéré comme moins important du point de vue du jeu en lui-même, étant donné que l’intervalle entre deux mises à jour est négligeable comparé à d’autres valeurs telles que les réflexes humains. Néanmoins, un updaterate haut vous permettra peut-être de désactiver l’interpolation, ce qui fera ici une nette différence.

Le rate : auto-limitation de bande passante

Le rate, contrôlé par la CVAR client du même nom, limite la bande passante utilisée par le serveur pour envoyer les mises à jour au client (cela ne concerne pas le sens « client vers serveur »). Il est exprimé en octets par seconde.

On pourrait penser que le rate est un dispositif destiné à ajuster automatiquement l’updaterate maximal de manière à ne pas dépasser une certaine limite de bande passante. Force est de constater qu’il n’en est rien : l’updaterate maximal ne bouge pas, ce qui provoque forcément des problèmes de choke en cas de rate trop bas pour la taille des mises à jour. Limiter le rate revient donc à « simuler » une connexion plus lente – ainsi, régler le rate à 5000 revient à jouer avec un modem 56K.

De ce fait, le rate n’est utile que lorsque l’on veut, pour une raison ou pour une autre, s’assurer que la bande passante consommée en récéption chez le client ne dépasse jamais une certaine valeur. Ce qui restreint l’utilisation de ce paramètre qu’à des cas très spécifiques. Limiter le rate force le jeu à sous-utiliser la connexion, ce qui n’a strictement aucun intérêt pour le commun des mortels. Cela signifie également que, contrairement à ce que beaucoup de monde pense, placer le rate à une valeur plus grande que la bande passante de votre connexion ne peut pas provoquer de problèmes de choke supplémentaires. Il convient donc de toujours placer la CVAR rate à sa valeur maximale, à savoir 81920 (qui correspond à 80 ko/s). Ces 80 ko/s ne seront en réalité atteints que rarement, la bande passante utilisée étant dans ce cas contrôlée par l’updaterate.

Pour ajuster la bande passante consommée par le jeu à son type de connexion (RTC, ADSL…), il est donc plus judicieux d’utiliser les CVARs de limitation de fréquence (cl_updaterate et cl_cmdrate), qui vous éviteront le choke. Voir à ce sujet la partie optimisation.

Une autre utilité du rate réside dans la CVAR serveur sv_maxrate, qui limite la bande passante utilisable par chaque client connecté à une valeur maximale, au risque de causer du choke chez ceux-ci. Si vous voulez simplement diminuer les proportions prises par la bande passante de votre serveur sans risquer de problème de choke, penchez vous plutôt sur la CVAR sv_maxupdaterate.

Taille des mises à jour et compression

Une mise à jour totale indiquant la position et l’état de tous les joueurs et entités de la map « pèse » au minimum 1000 octets et peut facilement dépasser les 2000. Ces valeurs vous semblent ridicules ? Avec un updaterate de 66, donc en recevant 66 paquets de 2000 octets par seconde, on atteint 132 kilo-octets/seconde de bande passante consommée. Impensable.

Pour résoudre ce problème, Source applique une compression de type delta aux mises à jour. En clair, il n’envoie que ce qui a changé depuis la dernière mise à jour reçue par le client (ou le serveur). Avec cette technique et en situation normale de jeu, la taille moyenne des paquets descend à environ 150 octets, ce qui est beaucoup plus supportable.

Cela dit, lors d’affrontements violents impliquant plus d’une dizaine de joueurs, les informations envoyées par le serveur (les différents sons produits et le mouvement des objets suite au souffle d’une grenade notamment) atteignent une taille telle que le choke est souvent inévitable.

Une question se pose : baisser votre updaterate diminue votre consommation de bande passante (et donc les risques de choke) en situation normale de jeu, mais pas lors d’affrontements, où la bande passante consommée ne varie quasiment pas. Pourquoi ?

Pour répondre à cette question, considérons le contenu des mises à jour en situation normale de jeu et comparons-le à des mises à jour en situation d’affrontement.

En situation normale, le contenu des paquets se résume à des déplacements de joueurs et aux tirs éventuels. Imaginons qu’un joueur se trouve au point A et se dirige vers le point C en passant par le point B. Avec un updaterate haut, le serveur va envoyer au client un paquet disant que le joueur se trouve au point A, puis un autre rapportant qu’il se trouve au point B, et enfin un autre pour le point C. Trois paquets. Avec un updaterate bas, le serveur va seulement envoyer deux paquets au client : le point A et le point C, en faisant abstraction du point B (le serveur a en réalité bien calculé ce point – du moins si son tickrate est suffisant – mais ne l’envoie pas au client). Résultat : deux paquets au lieu de trois donc économie de bande passante.

Passons au cas d’affrontements. Ici les paquets sont plus de 10 fois plus lourds qu’en situation normale. Les déplacements et les tirs ne constituent plus qu’un petit coin de chaque paquet : les sons et les informations du moteur physique prennent tout le reste. Or le problème est que ces informations sont spontanées et indivisibles, donc non compressibles selon la technique présentée au paragraphe précédent. Baisser l’updaterate ne provoque donc que peu d’économie de bande passante dans ces situations. En réalité, le serveur enverra effectivement moins de mises à jour, mais celles-ci seront plus lourdes.

Les mises à jour et le ping affiché

Chaque partie mise en communication, c’est à dire le serveur ou votre client, calcule la latence selon un procédé qui pour l’instant m’échappe. J’ai néanmoins réussi à découvrir que la partie qui détermine le ping le calcule sur les paquets qu’il reçoit : ainsi, le ping déterminé par le serveur (affiché par le tableau des scores) est calculé sur les mises à jour envoyées par le client, tandis que le ping déterminé par le client (affiché dans le netgraph) est calculé sur les mises à jour reçues par le client.

Il vous est peut-être déjà arrivé de constater que si vous mettiez votre CVAR cl_cmdrate à des valeurs très basses (telles que 5 par exemple), votre ping dans le tableau des scores tombait à un niveau ridiculement bas. En réalité, votre latence n’est pas diminuée. En aucun cas votre ping réel ne peut passer en dessous du ping que vous auriez en utilisant ICMP (c’est à dire la commande « ping »). Il est même possible que ce problème de calcul touche aussi le lag compensation, auquel cas vous n’auriez pas du tout intérêt à essayer.

Il semblerait en fait que, de par le fait que le ping est calculé sur les paquets reçus, le serveur donnerait un ping faux sur des valeurs de cl_cmdrate trop basses. Les calculs commenceraient à « dérailler » à partir de 30 pour cl_cmdrate (tableau des scores) et 20 pour cl_updaterate (netgraph).

L’explication précise de ce phénomène m’échappe encore.

Loss, choke et compagnie : non, tout n’est pas rose

Il arrive souvent que le netcode ne fonctionne pas de façon optimale. Cela peut être dû à de mauvais paramètres ou, plus souvent, à une connexion de qualité insuffisante. Deux problèmes peuvent se produire : l’apparition de loss ou de choke.

Le loss

Le loss n’est pas un phénomène compliqué : il signifie tout simplement que des paquets de données n’arrivent pas à destination. Il y a donc de la perte. Ce problème ne peut se produire qu’à cause d’une connexion instable, ou dans le cas d’un choke très élevé.

A priori, le calcul du loss s’effectue en vérifiant les numéros de séquence des mises à jour : si le jeu reçoit les mises à jour 1 et 3 mais pas la mise à jour 2, il y a un paquet perdu.

Un loss faible n’aura pas beaucoup d’impact sur votre jeu : perdre 5 mises à jour sur 100 par exemple n’est pas dramatique. Pour les mises à jour client vers serveur, chaque paquet envoyé contient un « historique » des commandes précédentes pour éviter que le serveur ne « loupe » votre tir par exemple.

Le choke

Nous avons vu dans la section sur les mises à jour que le serveur envoyait les informations sur le jeu en cours à une certaine fréquence appellée updaterate et exprimée en mises à jours par seconde.

Avec un updaterate de 66, les mises à jour sont envoyées par intervalles de 15 millisecondes. Il faut donc que chaque mise à jour soit transmise dans un délai maximum, une « fenêtre de transmission » de 15 millisecondes. Que ce passe-t-il si le délai de transmission d’un paquet dépasse cette fenêtre ?

000cbf

En situation normale, les mises à jour gardent toujours un « intervalle » entre elles. Mais si la transmission d’une mise à jour est trop longue, celle-ci « déborde » de sa fenêtre : c’est le choke. Et si le problème persiste, la situation dégénère en une sorte de « réaction en chaîne » que vous pouvez voir sur le schéma (la zone rouge augmente de taille). Le principal problème causé par ce phénomène est que l’updaterate baisse et, surtout, les mises à jour arrivent avec de plus en plus de retard. La partie peut rapidement devenir injouable.

Ce « débordement » est dû à un manque de bande passante pour transmettre les mises à jour, qui lui même peut avoir deux causes :

  • Votre connexion n’a pas une bande passante suffisante pour tenir le rythme du jeu (assez rare, sauf pour les 56K).
  • La taille des paquets est trop grande.

Cette dernière cause n’est quasiment jamais mentionnée, pourtant elle provoque 90% des problèmes de choke. Il vous est peut-être déjà arrivé de constater que vous avez du choke lors d’affrontements violents dans le jeu, alors que dans d’autres circonstances tout est normal. Le choke ne peut pas être provoqué par un manque de ressources système (CPU ou RAM) sur le serveur. L’explication est simple : alors qu’en situation normale de jeu la taille des paquets transmis reste dans les environs de 350 octets, il n’est pas rare en situation d’affrontements de se retrouver avec des paquets fragmentés pouvant atteindre des tailles totales de l’ordre de 5000 octets. Faites le calcul : avec un updaterate 66, ces données doivent être transmises en 15 millisecondes. Soit une bande passante nécéssaire de 330 ko/s.

Vous êtes en droit de vous demander comment votre connexion à 8 mbps (voire 25 mbps) pourrait avoir peur de 330 ko/s là où elle en supporte beaucoup plus. Malheureusement, le moteur Source souffre ici d’une limitation de taille. Pour rappel, le rate limite la bande passante utilisée par le serveur lors de l’envoi de ces mises à jour. Or, cette valeur a comme valeur maximale 81920, soit 80 ko/s… Conclusion, un paquet dont la taille dépasse 81920 ÷ updaterate (soit 1241 octets pour un updaterate 66) provoquera un choke inévitable : vous ne pouvez absolument rien faire pour résoudre le problème. La seule solution est d’attendre que Valve fasse sauter cette limitation, ce qui n’est pas Gagné d’avance. Cela dit, il est possible que le serveur limite votre rate (CVAR sv_maxrate); si c’est le cas, retirer cette limite devrait diminuer les problèmes de choke sur les clients.

Si vous avez du choke en situation normale de jeu, baisser votre updaterate (via la CVAR cl_updaterate) peut améliorer votre situation. En revanche, le seul moyen d’éviter ce problème lors des affrontements (rushs…) est d’améliorer votre connexion avec le serveur (voir le problème des données ponctuelles); si votre connexion supporte déjà plus de 80 ko/s (et côté serveur également), vous ne pouvez rien faire.

Les mécanismes de compensation réseau

Le jeu en réseau souffre de nombreuses contraintes dûes à la nature même de ces réseaux, peu adaptés à cette utilisation nécéssitant de délivrer des paquets de données à fréquence fixe avec une latence minimale. Cela est surtout vrai pour les connexions RTC et les connexions ADSL dénuées de la technologie FastPath.

Pour cette raison, il a fallu développer des mécanismes « artificiels » pour compenser ces limitations. Pour le moteur Source, 3 de ces mécanismes sont implémentés : l’interpolation et la prédiction côté client, et le lag compensation côté serveur.

La prédiction

Il s’agit du mécanisme le plus simple. Il a pour objectif de rendre le jeu plus « réactif » aux actions du joueur et est implémenté côté client.

Sans la prédiction, le client affiche ce qu’il reçoit du serveur par les mises à jour et uniquement cela. Ce qui signifie que si vous tirez, le jeu ne déclenchera l’animation et les effets correspondants que lorsqu’il aura reçu confirmation de ce tir par le serveur. Cela nuit considérablement à la jouabilité car entraîne un décalage entre le moment où vous tirez et le moment où vous voyez ce tir sur l’écran.

Pour éviter ce problème, le jeu va anticiper la confirmation du serveur : c’est la prédiction. En clair, le jeu va tirer sans attendre la confirmation. Ce qui signifie que lorsque vous voyez l’animation de tir de votre arme, le tir a en réalité été pris en compte quelques millisecondes plus tard sur le serveur (votre latence), ce décalage étant compensé par le lag compensation que nous verrons plus tard.

La prédiction est surtout utile sur les actions de tir et de déplacement. Mais il peut arriver que cette « anticipation » ne soit pas correcte et que l’action ne se produise pas sur le serveur (ou d’une manière différente). Cela arrive notamment si vous tirez et que quelqu’un vous tue pendant le trajet de l’information de tir jusqu’au serveur : en réalité, votre tir n’a pas été pris en compte. Ces différences entre ce que le client s’attend à recevoir et ce qu’il reçoit réellement sont appellées erreurs de prédiction. Elles sont visibles grâce à la CVAR cl_showerror (uniquement si sv_cheats est à 1 sur le serveur). Le client se replacera alors sur l’état indiqué par le serveur (puisque celui-ci a autorité sur le jeu). Ces erreurs de prédiction arrivent en général peu fréquemment, et leurs conséquences sont souvent négligeables.

Lorsque l’erreur de prédiction est minime (une très légère différence de direction lors d’un déplacement par exemple), votre client va « smoother » c’est à dire « adoucir » la correction de l’erreur. Par exemple, si la prédiction d’un de vos mouvements s’est fait légèrement trop à droite par rapport à la confirmation du serveur, votre jeu vous replacera « doucement » vers la bonne position, de sorte que vous ne vous en rendrez même pas compte.

Si l’erreur est grande (c’est à dire lorsque le temps nécéssaire pour « l’adoucir » dépasse la valeur de la CVAR client cl_smoothtime), le client va vous replacer brutalement à la bonne position : c’est ce phénomène que vous pouvez voir quand vous revenez brusquement un pas en arrière au cours d’un rush par exemple (un ami appellait ça des phénomènes de « back-vidéo »).

La fréquence et la gravité des erreurs de prédiction dépend de votre latence et de la fréquence de vos mises à jour (dans les deux sens), ainsi que de la qualité du serveur.

La prédiction est désactivable avec la CVAR cl_predict. Cependant, cela ne présente aucun intérêt.

L’interpolation

Par défaut, votre updaterate ne peut pas dépasser 20 (cl_updaterate). Ce qui signifie que vous recevrez, pour un joueur précis, 20 fois sa position par seconde.

Si il n’y avait pas d’interpolation, ce joueur, lors de ses déplacements, changerait de position 20 fois par seconde sur la vue du client, ce qui équivaut à avoir 20 images par seconde de ce joueur. D’où des saccades d’affichage. Et augmenter l’updaterate ne corrige pas grand chose dans la plupart des cas, vu que ces saccades seront dans ce cas dûes au cmdrate trop faible du joueur que vous regardez.

L’interpolation va « inventer » des points du joueur situés entre deux positions. Ainsi, si vous avez 85 images par seconde lorsque vous jouez et que le joueur que vous regardez change 20 fois de position par seconde, 65 points seront « inventés » par l’interpolation pour fluidifier son mouvement.

L’interpolation s’effectue sur un intervalle de temps fixe réglable avec la CVAR cl_interp (en secondes, 0 pour désactiver l’interpolation). Par défaut, l’interpolation s’effectue sur 100 millisecondes (cl_interp 0.1).

L’interpolation induit donc forcément un décalage de temps entre la vue du client et les mises à jour que celui-ci reçoit, compensé par le lag compensation que nous verrons plus tard.

interpolation et cmdrate : le problème de la « touchabilité »

L’interpolation invente des points qui n’existent pas. Le fait que ces points n’existent pas peut être dû à votre updaterate trop bas, dans ce cas le serveur aura plus de points que vous sur le déplacement d’un joueur précis.

Mais imaginons que l’interpolation se fasse sur le mouvement d’un joueur dont le cmdrate est plus bas que le tickrate du serveur (par exemple un joueur avec le cmdrate par défaut 30 et un serveur de tickrate 66). Le client va inventer des points qui n’existent pas pour le serveur. Ce fait qui paraît logique (et qui l’est) peut avoir des conséquences néfastes sur la « touchabilité » d’un joueur, c’est à dire vos chances de l’abattre lorsque celui ci est en mouvement. En effet, si vous tirez sur une hitbox « inventée » par l’interpolation, c’est à dire ne correspondant pas avec sa position réelle sur le serveur, vous ne la toucherez pas car sa position réelle est différente.

Imaginons une situation similaire à celle prise comme exemple pour le tickrate : un contre-terroriste se déplace dans le champ de vision d’un terroriste. Celui-ci lui lui tire dessus à mi-chemin. On fait abstraction du lag et du décalage induit par l’interpolation, tous deux compensés par le lag compensation. On considère également que le tickrate du serveur est supérieur à 40 et que l’updaterate ainsi que le cmdrate du terroriste sont également supérieurs à 40.

Nous allons comparer plusieurs situations où on fait varier le cmdrate du contre-terroriste ainsi que l’interpolation.

Avec un cmdrate de 40 (intervalle : 25 ms), interpolation activée
000cc1
Avec un cmdrate de 20 (intervalle : 50 ms), interpolation activée
000cc0

Comme vous pouvez le voir, la position du contre-terroriste à 25 ms a été interpolée en raison de son cmdrate trop faible : le joueur a tiré sur un point qui n’existe pas sur le serveur, l’impact n’est pas pris en compte.

Avec un cmdrate de 20 (intervalle : 50 ms), interpolation désactivée
000cc2

L’interpolation étant désactivée, le terroriste voit le contre-terroriste à la même position à 0 comme à 25 ms, c’est à dire la position réelle du contre-terroriste : il tire sur cette position et l’impact est bien pris en compte.

NOTE : comme pour le tickrate, cet exemple doit être relativisé. En effet le rapport taille des hitboxes/distance parcourue présenté ici est totalement différent.

Nous sommes donc dans la situation suivante : si le tireur a l’interpolation activée, plus l’adversaire aura un cmdrate bas, moins celui-ci sera « touchable » par le tireur. Mais ce cmdrate bas peut aussi entraîner des effets néfastes :

  • Si le terroriste est immobile et que le contre-terroriste est en mouvement (notre exemple), le cmdrate bas du contre-terroriste lui sera profitable car il diminuera la « touchabilité » du terroriste.
  • Si le terroriste et le contre-terroriste sont immobiles, le cmdrate du contre-terroriste ne joue pas.
  • Si le terroriste est en mouvement et que le contre-terroriste est immobile, le contre-terroriste sera pénalisé par son cmdrate bas, car il enverra à une fréquence plus basse ses informations de tir pour toucher le terroriste en mouvement.

On peut donc en conclure que fixer un cmdrate bas et rester en mouvement vous rend moins vulnérable face à un adversaire qui a son interpolation activée. Au contraire, si vous restez immobile et que vous essayez d’abattre un adversaire en mouvement, le cmdrate bas vous pénalisera dans tous les cas. Cela explique le fait que certains joueurs ont de meilleurs scores avec un cmdrate de 10 qu’avec un cmdrate de 100.

La meilleure solution pour éviter de se faire avoir par un cmdrate bas est donc de désactiver l’interpolation (cl_interp 0). L’affichage des entités et des joueurs sera alors plus saccadé mais retranscrira exactement le contenu des mises à jour, donc tous les points affichés seront les mêmes que sur le serveur.

Le lag compensation

Il nous reste un problème à résoudre, fondamental dans les jeux en réseau : la latence. En effet, tirer sur un joueur en mouvement avec un ping de 80 ms sans mécanisme de compensation adapté oblige à viser au-devant de l’ennemi, ce qui n’est pas vraiment satisfaisant du point de vue de la crédibilité de la simulation.

Alors comment le serveur fait-il pour compenser cette latence ? Eh bien il remonte le temps. En effet, tout le principe du lag compensation consiste à estimer à quel moment le joueur a réellement tiré (ou plus précisément : le moment auquel l’image sur laquelle le joueur a tiré s’est affichée). Une fois ce moment déterminé, le serveur va remonter dans un « historique » des précédents ticks. Une fois le tick correspondant isolé, il va l’utiliser pour déterminer si, dans cette « image du jeu », le joueur a touché son adversaire ou pas.

Avant de continuer, rappellons que la latence (le ping) est exprimé dans un délai aller et retour. Quels éléments le serveur doit-il prendre en compte lors de l’estimation du décalage entre le serveur et le client ?

  • Le temps d’arrivée des mises à jour serveur vers client.
  • L’interpolation (si elle est activée), qui crée un décalage supplémentaire.
  • Le temps d’arrivée des commandes client.

Ce qui nous donne : temps d’arrivée des mises à jour + temps d’interpolation + temps d’arrivée des commandes client = lag à compenser, soit latence + temps d’interpolation = lag à compenser.

000cc3

Dans l’exemple ci-dessus (tickrate 66), le joueur a tiré alors qu’il voyait une mise à jour générée au tick 1. Au moment où il a « vu » ce tick, il était déjà en décalage avec le serveur de 140 ms (temps d’arrivée de la mise à jour + interpolation). Ajoutons à cela les 40 ms pour le parcours de la commande et on obtient 140 + 40 = 180 ms à remonter. Le décalage maximal compensable par le serveur est paramétrable avec la CVAR sv_maxunlag (en secondes, par défaut 1).

Le serveur sait qu’il y a 180 ms il en était au tick 1, donc il se sert de ce tick pour déterminer si le joueur a touché sa cible ou non. Si oui, l’impact est comptabilisé pour le tick 13.

Le lag compensation est désactivable avec la CVAR sv_unlag. Cependant le faire est très fortement déconseillé.

Le netgraph, une mine d’informations

Afin de tester la qualité de votre connexion et de vous fournir des infos utiles sur ce qui se passe, Source vous offre un outil très pratique : le netgraph.

Une fois activé, celui-ci s’affiche dans un coin de votre écran pendant votre jeu et vous renseigne en permanence sur l’état actuel du Netcode.

Les différents modes de Netgraph

Le netgraph s’active avec la CVAR net_graph, qui peut prendre 4 valeurs différentes. Pour toutes ces valeurs, le netgraph affichera vos FPS, votre ping, la taille des paquets que vous envoyez/recevez, la bande passante consommée dans les deux sens et votre updaterate/cmdrate.

  • net_graph 1 : affiche en plus un graphique en bas dont je n’arrive pas à saisir la signification. Il est tout le temps plat…
  • net_graph 2 : affiche en plus un graphique indiquant la taille des paquets que vous recevez en temps réel.
  • net_graph 3 : affiche en plus vos valeurs de loss/choke.

Interpréter le netgraph

000cc4
Il se lit de cette manière, de gauche à droite et de haut en bas :

Première ligne : votre nombre de FPS et votre latence actuelle.
Deuxième ligne : la taille du dernier paquet reçu, la bande passante consommée en récéption, et votre updaterate réel.
Troisième ligne : la taille du dernier paquet émis, la bande passante consommée en émission, et votre cmdrate réel.
Quatrième ligne (uniquement en net_graph 3) : votre valeur de loss (a priori exprimée en pourcentage de paquets perdus) et votre valeur de choke (a priori exprimée en unité arbitraire)

Notez que en net_graph 1 et 3, deux valeurs supplémentaires apparaissent : en haut la valeur de la CVAR cl_updaterate et en bas celle de cl_cmdrate.

000cc5

Ce graphique disponible en net_graph 2 vous donne des informations sur les paquets que vous recevez. Une colonne du graphe signifie un paquet. La hauteur de la colonne symbolise la taille du paquet, et les différentes couleurs désignent le type des informations contenues dans le paquet (leurs significations m’échappent pour le moment).

La vitesse de défilement de ce graphe symbolise votre updaterate réel. Un « trou » signifie soit du loss, soit du choke.

Configurez votre netgraph

Le netgraph possède des paramètres modifiables dont je fais la liste ici :

  • net_graphpos : position horizontale du netgraph. 1 : gauche, 2 : droite et 3 : centre.
  • net_graphheight : position verticale du netgraph en pixels. Ne concerne pas le graphe inférieur qui, lui, restera toujours collé au bas de l’écran.
  • net_scale : échelle du graphe supérieur.

Optimisez !

Toutes les informations que vous venez d’ingurgiter à la lecture de ce document vont maintenant nous permettre de déterminer les meilleurs paramètres précis pour améliorer la qualité du jeu.

Nous avons ici trois cas de figure : vous êtes administrateur de serveur et vous cherchez à améliorer la qualité du jeu chez les joueurs, vous êtes vous même joueur et vous cherchez les meilleurs valeurs possibles, ou encore vous cherchez à déterminer la qualité d’un serveur précis.

Administrateurs

Le serveur est la pièce maîtresse de toute partie de jeu en réseau. Pour cette raison, une configuration optimale de ce serveur peut considérablement améliorer la qualité du jeu chez les clients connectés.

Le tickrate ne vous mordra pas

Comme nous l’avons dit, le tickrate est dynamique. Ce qui signifie que si votre serveur de jeu est la seule chose qui tourne sur la machine, vous avez tout intérêt à régler d’office le tickrate à 100. Il s’adaptera ensuite aux capacités réelles de la machine.

La situation est plus compliquée sur un serveur mutualisé, où vous devez aussi faire attention aux ressources des autres serveurs de jeux hébergés sur la même machine. Dans ce cas, vous allez devoir faire des tests.

Le tickrate est de loin la caractéristique la plus importante pour la qualité d’un serveur de jeu. Un serveur tenant un tickrate 100 à pleine charge peut être considéré comme quasi parfait.

Ne limitez rien si vous ne manquez de rien

Si vous vous contrefichez de la bande passante consommée par votre serveur, vous pouvez placer les CVARs sv_maxrate et sv_maxupdaterate à 0.

Si vous souhaitez simplement diminuer les proportions de bande passante prises par le serveur, utilisez en priorité la CVAR sv_maxupdaterate.

Enfin, si vous souhaitez vous assurer que la bande passante consommée pour chaque client ne dépasse jamais une certaine limite, utilisez la CVAR sv_maxrate. Il est conseillé de l’utiliser conjointement avec sv_maxupdaterate pour éviter les problèmes de choke avec les clients ayant un updaterate haut.

Contrecarrer les petits malins du cmdrate

J’ai expliqué dans la section sur l’interpolation qu’un cmdrate bas rendait un joueur moins touchable. Cet état de fait porte préjudice aux joueurs lambda qui ne sont pas forcément au courant qu’il vaut mieux désactiver l’interpolation pour éviter de tomber dans le panneau.

Le serveur ne dispose pas de CVAR sv_mincmdrate, et c’est bien dommage. Pour résoudre le problème, vous devrez utiliser des plugins serveur (Mani Admin Plugin par exemple) pour forcer la valeur de CVARs chez le joueur. Vous pouvez appliquer deux politiques :

  • Forcer cl_cmdrate à une valeur minimale. Atténue fortement le problème mais ne le fait pas disparaître complètement; de plus, peut causer des problèmes chez les joueurs ayant des connexions lentes.
  • Forcer cl_interp à 0 pour couper l’interpolation chez tout le monde. Plus souple mais possède l’inconvénient de choisir pour les joueurs entre touchabilité et fluidité graphique sans leur demander leur avis, et surtout pénalise tout le monde pour contrecarrer les agissements d’une minorité.

A vous de choisir.

Joueurs

Vous êtes joueur et souhaitez bénéficier des meilleurs paramètres possibles ? Suivez le guide !

interpolation et rate

Vous devriez désactiver l’interpolation en plaçant la CVAR cl_interp à 0 pour contrecarrer les « petits malins du cmdrate« .

Placez votre rate à 81920, c’est à dire la valeur maximale, même si votre connexion ne va pas jusque là : le placer plus bas n’a d’intérêt dans aucun cas.

cmdrate et updaterate

Là, c’est un petit peu plus compliqué. Vous allez devoir utiliser votre netgraph pour trouver les bonnes valeurs par tâtonnement.

Tout d’abord, placez votre cmdrate et votre updaterate tous deux à 100. Puis observez votre netgraph en situation normale de jeu (c’est à dire en dehors des affrontements tels que les rushs de début de partie) : si vous avez du choke, baissez votre updaterate et votre cmdrate progressivement jusqu’à ne plus en avoir, en essayant de les baisser séparément puis ensemble. L’objectif est d’obtenir les valeurs les plus hautes possible sans choke.

Notez qu’avec un serveur doté d’une connexion potable et d’une connexion ADSL 1 mbps, vous devriez pouvoir rester à 100 pour les deux CVARs sans aucun problème.

Il est possible que des valeurs optimales avec un serveur précis ne le soient pas avec un autre, voire même à certaines périodes et pas d’autres. Cela est dû au fait que la bande passante consommée augmente avec le nombre de joueurs présents sur le serveur et avec le tickrate (si votre updaterate/cmdrate est au dessus du tickrate pour ce dernier cas).

Notez également que la présence de choke lors d’affrontements violents est inévitable comme expliqué dans la section correspondante.

Déterminer la qualité d’un serveur

Il y a plusieurs raisons pour lesquelles on voudrait tester la qualité d’un serveur : pour un administrateur, cela permet d’apprécier la qualité du boulot, et pour un joueur, cela permet de choisir des serveurs de bonne qualité pour y jouer.

Il y a trois éléments pour déterminer la qualité d’un serveur : son ping, sa bande passante et son tickrate réel. Le premier est visualisable très facilement, le deuxième se teste en « tâtant » le choke pour des valeurs d’updaterate hautes, mais le troisième est un peu plus subtil. Notre objectif ici sera donc de chiffrer ce tickrate réel.

Pour cela, il vous faut vous connecter au serveur. Ensuite, essayez d’obtenir les réglages d’updaterate les plus hauts possibles sans choke (chose que vous avez peut-être déjà faite, voir l’optimisation joueur). Ensuite lisez l’indication d’updaterate réel sur le netgraph. Il y a trois cas possibles :

  • Votre updaterate plafonne à la valeur de cl_updaterate : votre connexion est trop lente pour tester la qualité du serveur. Vous pouvez néanmoins affirmer que le tickrate est supérieur ou égal à votre updaterate actuel.
  • Votre updaterate plafonne à une valeur précise située en dessous de votre cl_updaterate : la valeur affichée est soit la valeur de sv_maxupdaterate sur le serveur, soit le tickrate réel. Si vous ne connaissez pas la valeur de sv_maxupdaterate, vous pouvez affirmer que le tickrate réel est égal ou supérieur à la valeur affichée. Si sv_maxupdaterate est au-dessus de la valeur affichée ou est illimité, vous avez le tickrate réel.
  • Votre updaterate plafonne à 100 : le serveur est « quasi-parfait » (tickrate réel égal ou supérieur à 100).

Notez que les résultats seront faussés si vous avez du choke.

Le bug des hitboxes : mythe ou réalité ?

Aujourd’hui beaucoup de personnes ne veulent pas passer à Source. Ceci à cause d’un bug qui serait présent dans le moteur : le fameux bug des hitboxes. Concrètement, ce bug consiste en un décalage vers l’avant des hiboxes d’un joueur par rapport à la position réelle de ce dernier lorsque celui-ci est en déplacement.

J’ai visionné une vidéo qui montre le bug, et une autre expliquant pourquoi elle n’est pas crédible (dont j’ai malheureusement paumé le lien). Ce qui me rend initialement neutre. Voici les 3 réponses que j’ai pu voir des prétendants au « y’a pas de bug » :

  • Cette vidéo est très vieille, le bug a été corrigé depuis.
  • Les démos ont un bug de prédiction qui fausse les résultats.
  • Enfin, les hitboxes apparaissant décalées sont dûes au fait que la CVAR sv_showhitboxes montre les hitboxes du serveur et non du client.

Si les deux premières propositions sont difficilement vérifiables, en revanche la troisième l’est. Nous allons de ce pas vérifier la réalité du bug des hitboxes.

Nous allons tout d’abord lancer un serveur local, puis activer l’affichage des hitboxes avec la commande sv_showhitboxes 2.

Nous allons ensuite multiplier par 10 le temps d’interpolation (cl_interp 1) pour déterminer si les hitboxes affichées sont celles du serveur ou du client. Verdict :

000cc6

Les hiboxes sont très décalées : les hitboxes affichées sont celles du serveur, et non du client comme le supposait la vidéo. A quoi cela nous avance ? Eh bien le fait que ces hitboxes soient décalées est tout à fait normal : cela est dû au lag (latence + interpolation). Ce décalage est compensé par le lag compensation que nous avons vu plus haut.

Vérification : mettons cl_interp à 0 – on constate que les hitboxes ne sont plus décalées.

On peut aussi faire le même type de tests en simulant de la latence grâce à la CVAR net_fakelag : là aussi, les hitboxes sont plus décalées que la normale.

Nous allons maintenant « ralentir le temps ». Cela nous permettra de tester plus facilement les effets du tir lorsque l’on vise les hitboxes, le model ou juste devant le model. Pour cela, faites host_timescale 0.2.

Essayez ensuite, avec différentes valeurs de cl_interp ou de net_fakelag, de tirer sur les hitboxes serveur, sur le model ou devant le model : seul le model est touchable. Le lag compensation fait très bien son boulot.

Conclusion : le bug des hitboxes a probablement existé, mais n’est plus effectif aujourd’hui.

Questions sans réponse

Mon document n’est malheureusement pas exhaustif. En effet je n’ai pas trouvé le moyen de répondre à certaines des questions que je me suis posées. Lesquelles sont :

  • Que représente le graphique du bas dans le netgraph ?
  • La CVAR fps_max sur le serveur sert-elle vraiment à quelque chose ? Si oui, comment fonctionne-t-elle ? Comment un serveur peut-il avoir une notion de FPS alors que cette fonction est comparable au tickrate ?
  • Baisser son cmdrate avec la CVAR cl_cmdrate fait baisser le ping dans le tableau des scores, mais pas dans le netgraph. Quelles sont les différences entre le calcul du ping affiché dans le tableau des scores et le calcul du ping affiché sur le netgraph ? Pourquoi abaisser le cmdrate a-t-il cet effet secondaire alors que la véritable latence ne descend vraisemblablement pas ? Cette « soi-disant » descente de ping a-t-elle un impact sur le calcul du lag compensation ou celui-ci utilise-t-il l’algorythme de calcul du netgraph ?
  • Qu’est ce que la CVAR serveur sv_alternateticks ? Que désignent ces « alternate ticks » ? S’agirait-il d’une sorte d’interpolation côté serveur ?
  • Qu’est ce que la CVAR serveur sv_lagflushbonecache (« Flushes entity bone cache on lag compensation« ) ?

Il est possible que ces questions trouvent réponse dans une prochaine version de ce document.

Un grand merci à l’auteur qui nous a permis de recopier son document :

Pour voir les mises à jour, référez-vous au document original.

Article précédentCall of Preview 2
Article suivantMedal of Honor: Pacific Assault patché