Aller au contenu
Blog

Serveur FiveM qui lag : trouver la resource responsable

Serveur qui rame ou FPS en chute ? Les distinguer, lire le profiler de FXServer, compter les commandes par resource et trouver le script fautif.

Par Termi

7 min de lecture
  • Performance
  • Debugging
  • Lua
Sur cette page

« Le serveur lag » recouvre deux problèmes différents. Parfois le thread serveur cale et tout le monde saccade en même temps ; parfois seuls certains joueurs perdent des FPS alors que le serveur va bien. Ce guide montre comment les distinguer, comment lire le profiler intégré de FXServer, et comment trouver la cause qui échappe aux deux : une resource qui enregistre des milliers de commandes. À la fin, la même chasse en une seule commande.

Deux sortes de lag

  • Le thread serveur cale. Tout le monde fait du rubber-band au même moment, les voitures se téléportent, et la console du serveur affiche server thread hitch warning: timer interval of 212 milliseconds. Quelque chose dans un script serveur prend trop de temps sur une frame.
  • Les FPS d’un joueur chutent. Le serveur va bien et sa console reste muette, mais certains joueurs saccadent, souvent aux mêmes endroits. Le coût est dans les scripts client, les assets streamés ou les entités autour d’eux.

Il faut des outils différents : commencez donc par vérifier lequel vous avez. Y a-t-il des hitch warnings, et est-ce que tout le monde le ressent ou seulement certains joueurs ?

Lire le profiler serveur

FXServer embarque un profiler qui mesure chaque resource, chaque thread et chaque gestionnaire d’événement. Dans la console du serveur :

server console
profiler record start
profiler record stop
profiler saveJSON lag.json

Lancez-le pendant que le serveur lag, arrêtez-le au bout d’une dizaine de secondes et enregistrez le résultat. lag.json est une trace Chrome : ouvrez les DevTools dans Chrome, allez dans Performance et chargez le fichier. Chaque frame serveur est un Resource Manager Tick, et dedans vous trouvez le tick (name) de chaque resource, ses threads sous la forme thread @resource/file.lua[from..to], ses gestionnaires d’événements et ses exports.

Cherchez les frames bien plus longues que les autres et regardez quelle resource les remplit. La plage de lignes indique de quel CreateThread ou gestionnaire il s’agit.

Une resource ne peut pas lancer le profiler toute seule. ExecuteCommand('profiler record start') depuis un script est refusé tant que server.cfg ne l’autorise pas : add_ace resource.<name> command.profiler allow, plus la même ligne pour command.record et command.saveJSON.

Mesurer les FPS d’un joueur

Côté joueur, F8 offre deux outils. resmon 1 affiche en direct le temps CPU et la mémoire de chaque resource, ce qui suffit quand une resource se détache nettement. Pour regarder de plus près, le même profiler tourne dans le jeu :

F8
resmon 1
profiler record 500
profiler saveJSON lag.json

Cela enregistre 500 frames. Chargez le fichier dans les DevTools de la même façon et cherchez les threads client qui tournent à chaque frame. Une boucle Wait(0) qui fait un vrai travail à chaque frame, comme mesurer la distance à des centaines de markers, est en général le premier suspect.

La cause qu’aucun des deux outils ne montre

Un type de lag échappe aux deux. Un serveur ajoute un pack d’emotes et se met à laguer peu après. Le profiler ne montre aucun thread lourd, et chercher RegisterCommand dans le pack ne trouve qu’une poignée d’appels.

Ces appels sont dans une boucle, derrière un alias local, un pour chaque emote :

client.lua
local register = RegisterCommand

for _, emote in ipairs(Emotes) do
  register(emote.command, function() PlayEmote(emote) end, false)
  TriggerEvent('chat:addSuggestion', '/' .. emote.command, emote.label)
end

Avec deux mille emotes, cela fait deux mille commandes et deux mille suggestions de chat dans le jeu de chaque joueur. Une recherche de texte ne peut pas les compter, mais le jeu le peut : GetRegisteredCommands() renvoie chaque commande avec la resource qui l’a enregistrée.

count.lua
local counts = {}
for _, command in ipairs(GetRegisteredCommands()) do
  counts[command.resource] = (counts[command.resource] or 0) + 1
end

for resource, count in pairs(counts) do
  if count > 100 then print(resource, count) end
end

Exécutez-le comme script client puis comme script serveur. Une resource avec des centaines de commandes mérite un coup d’œil. Avec des milliers, elle enregistre presque toujours une commande par emote ou par item, là où une seule commande avec un argument suffirait.

Les suspects habituels

  • Des threads qui attendent trop peu. Wait(0) dans une boucle s’exécute à chaque frame. C’est correct pour dessiner un marker et coûteux quand chaque tour parcourt tous les joueurs ou tous les points.
  • Du travail sur le tick serveur. Reconstruire une grosse table ou trier des milliers de lignes dans un seul thread bloque toutes les autres resources aussi longtemps que ça dure.
  • Des événements pour tout le monde. TriggerClientEvent(name, -1, data) dans une boucle envoie toute la charge à chaque joueur, à chaque fois.
  • Des entités que personne ne supprime. Les props et les peds qui s’accumulent coûtent à chaque joueur proche. GetGamePool('CObject') les compte.
  • Des commandes par milliers. Voir plus haut.

Questions fréquentes

Que signifie « server thread hitch warning » dans FiveM ?

Le thread principal de FXServer doit faire 20 ticks par seconde, soit un toutes les 50 ms. Quand plus de 150 ms s’écoulent entre deux ticks, la console affiche server thread hitch warning: timer interval of 212 milliseconds, et le nombre indique la durée de ce trou. Rien d’autre ne tourne sur le thread serveur pendant ce temps, donc tous les joueurs le ressentent au même moment. Quelque chose sur le serveur a retenu le thread trop longtemps, en général une resource qui fait beaucoup de travail sur une seule frame.

Pourquoi mon serveur FiveM a-t-il un hitch toutes les quelques secondes ?

Un hitch à intervalle régulier vient souvent d’un thread serveur qui dort quelques secondes avec Wait puis fait tout son travail d’un coup, par exemple parcourir tous les joueurs, véhicules ou points de job. Le profiler serveur montre de quel thread il s’agit : les longues frames nomment la resource et les lignes du thread, par exemple thread @jobs_garbage/server/main.lua[88..131].

Comment trouver quelle resource fait laguer mon serveur FiveM ?

Utilisez le profiler intégré de FXServer. Lancez profiler record start dans la console du serveur, profiler record stop une dizaine de secondes plus tard, puis profiler saveJSON lag.json, et chargez le fichier dans l’onglet Performance des DevTools de Chrome. Le tick, les threads et les gestionnaires d’événements de chaque resource apparaissent avec leur durée, et celle qui se cache derrière une longue frame ressort. Si seuls certains joueurs lagent, demandez-leur plutôt de taper resmon 1 dans F8 pour voir en direct le temps CPU de chaque resource client.

Que montre resmon dans FiveM ?

resmon 1, tapé dans la console F8, ouvre le moniteur de resources : une liste en direct de toutes les resources client avec le temps CPU qu’elles prennent à chaque frame et la mémoire qu’elles occupent. Il ne mesure que le jeu du joueur qui l’a ouvert : il explique donc les FPS de ce joueur et ne dit rien d’un hitch serveur.

Que signifie l’avertissement « took … ms to execute a query » d’oxmysql ?

oxmysql affiche une ligne comme [10.11.6-MariaDB] jobs_garbage took 312.4512ms to execute a query!, suivie de la requête, chaque fois qu’une requête dépasse le convar mysql_slow_query_warning, 200 ms par défaut. Le nom de la resource indique quel script l’a envoyée. La requête s’exécute en asynchrone, elle ne fait donc pas caler le thread serveur à elle seule, mais le script qui attend le résultat est en retard, et les joueurs voient un garage ou un inventaire qui s’ouvre lentement. Un index manquant sur la colonne de la clause WHERE est une cause fréquente ; mettez EXPLAIN devant la requête pour voir si MySQL en utilise un.

La même chasse en une commande

L’agent FiveDock fait tout cela pour vous. Avec les trois lignes add_ace dans server.cfg, fivedock perf enregistre le profiler pendant dix secondes, compte les commandes de chaque resource sur le serveur et dans le jeu de trois joueurs, et demande ses FPS à chaque joueur :

fivedock perf my-city
my-city  profiled 10 s · 214 players online
  server thread  late by 3.8 ms on average, 212 ms at worst in the last minute

Server  198 ticks, the slowest took 212 ms
  resource          total   per tick  worst tick
  jobs_garbage   1,236 ms     6.2 ms      205 ms
  hud              412 ms     2.1 ms      6.3 ms
  oxmysql           96 ms     0.5 ms       14 ms
  emotes            12 ms     0.1 ms      0.4 ms

Where exactly  the threads, events and exports that took longest
  jobs_garbage     611 ms  thread · server/main.lua:88-131 · 3× · worst 203 ms
  jobs_garbage     598 ms  thread · server/main.lua:12-40 · 198× · worst 6.1 ms
  hud              380 ms  event hud:sync · server.lua:52-77 · 1,420× · worst 4.2 ms
  oxmysql           88 ms  export · dist/build.js:310 · 402× · worst 14 ms

Commands  on the server, and in the game of 3 sampled players
  resource     server  players
  emotes            –    2,034
  admin_menu       61       12
  hud               9        4
  total           248    2,310
  A command per emote or item adds up fast. Check whether that resource needs them all.

Le thread du job éboueur dans server/main.lua:88-131 bloque le serveur environ 200 ms toutes les quelques secondes, et le pack d’emotes a 2 034 commandes dans le jeu des joueurs. Quand le thread serveur cale sans que personne ne regarde, l’agent enregistre lui-même les dix secondes suivantes, si bien que le rapport suivant dit déjà ce qui s’est passé.

Trouver ce qui fait laguer couvre la configuration et chaque partie du rapport.

Tous les articles