Zum Inhalt springen
Blog

FiveM Server laggt: die Resource finden, die den Lag verursacht

Server hängt oder FPS im Keller? So unterscheidest du beides, liest den FXServer-Profiler, zählst Commands pro Resource und findest das Script hinter dem Lag.

Von Termi

6 Min. Lesezeit
  • Performance
  • Debugging
  • Lua
Auf dieser Seite

„Der Server laggt“ meint zwei verschiedene Probleme. Mal hängt der Server-Thread, und alle ruckeln gleichzeitig. Mal verlieren nur einige Spieler FPS, während der Server sauber läuft. Diese Anleitung zeigt, wie du beides auseinanderhältst, wie du den Profiler von FXServer liest und wie du die Ursache findest, die sich vor beiden versteckt: eine Resource, die Tausende Commands registriert. Am Ende dieselbe Suche mit einem einzigen Befehl.

Zwei Arten von Lag

  • Der Server-Thread hängt. Alle rubberbanden im selben Moment, Autos teleportieren sich, und die Server-Konsole gibt server thread hitch warning: timer interval of 212 milliseconds aus. Irgendetwas in einem Server-Script braucht in einem Frame zu lange.
  • Die FPS eines Spielers fallen. Der Server ist in Ordnung und seine Konsole ruhig, aber einige Spieler ruckeln, oft an denselben Stellen. Die Kosten stecken in Client-Scripts, gestreamten Assets oder den Entities in der Umgebung.

Beide brauchen andere Werkzeuge. Prüfe also zuerst, welches Problem du hast: Gibt es Hitch-Warnungen, und spüren es alle oder nur einige Spieler?

Den Server-Profiler lesen

FXServer bringt einen Profiler mit, der jede Resource, jeden Thread und jeden Event-Handler misst. In der Server-Konsole:

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

Starte ihn, während der Server laggt, stoppe ihn nach etwa zehn Sekunden und speichere die Aufnahme. lag.json ist ein Chrome-Trace: Öffne die DevTools in Chrome, geh zu Performance und lade die Datei. Jeder Server-Frame ist ein Resource Manager Tick. Darin findest du den tick (name) jeder Resource, ihre Threads als thread @resource/file.lua[from..to], ihre Event-Handler und ihre Exports.

Suche Frames, die viel länger dauern als der Rest, und schau, welche Resource sie füllt. Der Zeilenbereich verrät, welcher CreateThread oder Handler es war.

Eine Resource kann den Profiler nicht selbst starten. ExecuteCommand('profiler record start') aus einem Script wird abgelehnt, solange die server.cfg es nicht erlaubt: add_ace resource.<name> command.profiler allow, dazu dieselbe Zeile für command.record und command.saveJSON.

Die FPS eines Spielers messen

Auf Spielerseite hat F8 zwei Werkzeuge. resmon 1 zeigt live die CPU-Zeit und den Speicher jeder Resource, was reicht, wenn eine Resource klar heraussticht. Für einen genaueren Blick läuft derselbe Profiler im Spiel:

F8
resmon 1
profiler record 500
profiler saveJSON lag.json

Das nimmt 500 Frames auf. Lade die Datei genauso in die DevTools und such nach Client-Threads, die jeden Frame laufen. Eine Wait(0)-Schleife, die in jedem Frame echte Arbeit leistet, etwa die Distanz zu Hunderten Markern misst, ist meist der erste Verdächtige.

Die Ursache, die keins der Tools zeigt

Eine Art von Lag versteckt sich vor beiden. Ein Server fügt ein Emote-Pack hinzu und kurz darauf laggt es. Der Profiler zeigt keinen schweren Thread, und die Suche nach RegisterCommand im Pack findet eine Handvoll Aufrufe.

Die Aufrufe stehen in einer Schleife, hinter einem lokalen Alias, einer pro 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

Bei zweitausend Emotes sind das zweitausend Commands und zweitausend Chat-Vorschläge im Spiel jedes Spielers. Eine Textsuche kann sie nicht zählen, das Spiel aber schon: GetRegisteredCommands() gibt jeden Command mit der Resource zurück, die ihn registriert hat.

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

Führ es als Client-Script und als Server-Script aus. Eine Resource mit Hunderten Commands ist einen Blick wert. Eine mit Tausenden registriert fast immer einen Command pro Emote oder Item, wo ein Command mit Argument reichen würde.

Die üblichen Verdächtigen

  • Threads, die zu kurz warten. Wait(0) in einer Schleife läuft jeden Frame. Das ist okay, um einen Marker zu zeichnen, und teuer, wenn jede Runde alle Spieler oder alle Punkte durchläuft.
  • Arbeit auf dem Server-Tick. Eine große Tabelle neu aufzubauen oder Tausende Zeilen in einem Thread zu sortieren, blockiert jede andere Resource, solange es dauert.
  • Events an alle. TriggerClientEvent(name, -1, data) in einer Schleife schickt die ganze Payload jedes Mal an jeden Spieler.
  • Entities, die niemand löscht. Props und Peds, die sich ansammeln, kosten jeden Spieler in ihrer Nähe. GetGamePool('CObject') zählt sie.
  • Commands zu Tausenden. Siehe oben.

Häufige Fragen

Was bedeutet „server thread hitch warning“ in FiveM?

Der Haupt-Thread von FXServer soll 20-mal pro Sekunde ticken, also alle 50 ms. Vergehen zwischen zwei Ticks mehr als 150 ms, gibt die Konsole server thread hitch warning: timer interval of 212 milliseconds aus, und die Zahl ist die Dauer dieser Lücke. In der Lücke läuft auf dem Server-Thread nichts anderes, deshalb spürt sie jeder Spieler im selben Moment. Etwas auf dem Server hat den Thread zu lange festgehalten, meist eine Resource, die in einem einzigen Frame viel erledigt.

Warum hitcht mein FiveM-Server alle paar Sekunden?

Ein Hitch im festen Takt kommt meist von einem Server-Thread, der ein paar Sekunden per Wait schläft und dann auf einmal schwer arbeitet, etwa alle Spieler, Fahrzeuge oder Job-Punkte durchläuft. Der Server-Profiler zeigt, welcher Thread es ist: Die langen Frames nennen die Resource und die Zeilen des Threads, zum Beispiel thread @jobs_garbage/server/main.lua[88..131].

Wie finde ich heraus, welche Resource meinen FiveM-Server laggen lässt?

Nimm den eingebauten Profiler von FXServer. Gib profiler record start in der Server-Konsole ein, etwa zehn Sekunden später profiler record stop, dann profiler saveJSON lag.json, und lade die Datei im Performance-Tab der Chrome DevTools. Tick, Threads und Event-Handler jeder Resource erscheinen mit ihrer Zeit, die Resource hinter einem langen Frame fällt also auf. Laggen nur einige Spieler, lass sie stattdessen resmon 1 in F8 eingeben, um die CPU-Zeit jeder Client-Resource live zu sehen.

Was zeigt resmon in FiveM?

resmon 1, in die F8-Konsole getippt, öffnet den Resource-Monitor: eine Live-Liste aller Client-Resources mit der CPU-Zeit, die sie pro Frame brauchen, und dem Speicher, den sie belegen. Er misst nur das Spiel des Spielers, der ihn geöffnet hat. Er erklärt also dessen FPS und sagt nichts über einen Server-Hitch.

Was bedeutet die oxmysql-Warnung „took … ms to execute a query“?

oxmysql gibt eine Zeile wie [10.11.6-MariaDB] jobs_garbage took 312.4512ms to execute a query! aus, gefolgt von der Query, wenn eine Query länger läuft als die Convar mysql_slow_query_warning, standardmäßig 200 ms. Der Resource-Name verrät, welches Script sie geschickt hat. Die Query läuft asynchron und blockiert den Server-Thread daher nicht selbst, aber das Script, das auf das Ergebnis wartet, kommt zu spät, und Spieler erleben eine Garage oder ein Inventar, das langsam aufgeht. Ein fehlender Index auf der Spalte in der WHERE-Klausel ist eine häufige Ursache. Setz EXPLAIN vor die Query, um zu sehen, ob MySQL einen nutzt.

Dieselbe Suche mit einem Befehl

Der Agent von FiveDock übernimmt das alles. Mit den drei add_ace-Zeilen in der server.cfg nimmt fivedock perf zehn Sekunden lang den Profiler auf, zählt die Commands jeder Resource auf dem Server und im Spiel von drei Spielern und fragt jeden Spieler nach seinen FPS:

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.

Der Thread des Müllabfuhr-Jobs in server/main.lua:88-131 hält den Server alle paar Sekunden etwa 200 ms lang auf, und das Emote-Pack hat 2.034 Commands im Spiel der Spieler. Hängt der Server-Thread, während niemand hinschaut, nimmt der Agent die nächsten zehn Sekunden selbst auf, sodass der nächste Report schon sagt, was passiert ist.

Finden, was laggt behandelt die Einrichtung und jeden Teil des Reports.

Alle Artikel