Przejdź do treści
Blog

Jak znaleźć zasób, przez który laguje serwer FiveM

Przycięcia serwera czy niski FPS? Jak je odróżnić, czytać profiler FXServera, liczyć komendy zasobów i znaleźć skrypt, który stoi za lagiem.

Autor: Termi

6 min czytania
  • Performance
  • Debugging
  • Lua
Na tej stronie

„Serwer laguje” to dwa różne problemy. Czasem staje wątek serwera i przycina wszystkich naraz, a czasem część graczy traci FPS, choć serwer ma się dobrze. Ten poradnik pokazuje, jak je odróżnić, jak czytać wbudowany profiler FXServera i jak znaleźć przyczynę, która chowa się przed oboma narzędziami: zasób rejestrujący tysiące komend. Na końcu to samo polowanie jedną komendą.

Dwa rodzaje laga

  • Staje wątek serwera. Wszystkich naraz cofa, auta skaczą, a konsola serwera wypisuje server thread hitch warning: timer interval of 212 milliseconds. Coś w skrypcie serwera trwa za długo w jednej klatce.
  • Spada FPS gracza. Serwer działa, jego konsola milczy, ale część graczy ma przycięcia, często w tych samych miejscach. Koszt siedzi w skryptach klienta, streamowanych assetach albo encjach wokół graczy.

Każdy wymaga innych narzędzi, więc najpierw sprawdź, który masz: czy w konsoli są hitch warningi i czy czują to wszyscy, czy tylko część graczy?

Czytaj profiler serwera

FXServer ma wbudowany profiler, który mierzy czas każdego zasobu, wątku i handlera eventów. W konsoli serwera:

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

Uruchom go, kiedy serwer laguje, zatrzymaj po mniej więcej dziesięciu sekundach i zapisz nagranie. lag.json to trace Chrome’a: otwórz DevTools w Chrome, przejdź do Performance i wczytaj plik. Każda klatka serwera to Resource Manager Tick, a w niej tick (nazwa) każdego zasobu, jego wątki jako thread @zasób/plik.lua[od..do], handlery eventów i eksporty.

Szukaj klatek, które trwają dużo dłużej niż reszta, i sprawdź, który zasób je wypełnia. Zakres linii mówi, który CreateThread albo handler to był.

Zasób nie uruchomi profilera sam. ExecuteCommand('profiler record start') ze skryptu zostanie odrzucone, chyba że pozwala na to server.cfg: add_ace resource.<nazwa> command.profiler allow i ta sama linijka dla command.record oraz command.saveJSON.

Zmierz FPS gracza

Po stronie gracza F8 ma dwa narzędzia. resmon 1 pokazuje na żywo czas CPU i pamięć każdego zasobu, co wystarcza, gdy jeden zasób wyraźnie odstaje. Żeby przyjrzeć się dokładniej, ten sam profiler działa w grze:

F8
resmon 1
profiler record 500
profiler saveJSON lag.json

To nagrywa 500 klatek. Wczytaj plik w DevTools tak samo i szukaj wątków klienta, które działają co klatkę. Pętla z Wait(0), która w każdej klatce robi prawdziwą pracę, np. liczy odległość do setek markerów, to zwykle pierwszy podejrzany.

Przyczyna, której nie pokaże żadne z nich

Jeden rodzaj laga chowa się przed oboma narzędziami. Serwer dodaje paczkę emotek i wkrótce zaczyna lagować. Profiler nie pokazuje żadnego ciężkiego wątku, a szukanie RegisterCommand w paczce znajduje kilka wywołań.

Wywołania siedzą w pętli, za lokalnym aliasem, po jednym na każdą emotkę:

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

Przy dwóch tysiącach emotek to dwa tysiące komend i dwa tysiące podpowiedzi czatu w grze każdego gracza. Wyszukiwanie tekstu ich nie policzy, ale gra tak: GetRegisteredCommands() zwraca każdą komendę razem z zasobem, który ją zarejestrował.

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

Uruchom to jako skrypt klienta i jako skrypt serwera. Zasób z setkami komend warto obejrzeć. Ten z tysiącami prawie zawsze rejestruje komendę na każdą emotkę albo przedmiot, choć wystarczyłaby jedna komenda z argumentem.

Zwykli podejrzani

  • Wątki, które za krótko czekają. Wait(0) w pętli działa co klatkę. Do rysowania markera to w porządku, ale drogo, gdy każdy obieg przechodzi po wszystkich graczach albo punktach.
  • Praca na ticku serwera. Przebudowa dużej tabeli albo sortowanie tysięcy wierszy w jednym wątku blokuje wszystkie inne zasoby tak długo, jak trwa.
  • Eventy do wszystkich. TriggerClientEvent(name, -1, data) w pętli wysyła cały payload każdemu graczowi, za każdym razem.
  • Encje, których nikt nie usuwa. Propy i pedy, które się zbierają, kosztują każdego gracza w pobliżu. GetGamePool('CObject') je policzy.
  • Komendy w tysiącach. Patrz wyżej.

Częste pytania

Co znaczy „server thread hitch warning” w FiveM?

Główny wątek FXServera ma tykać 20 razy na sekundę, czyli co 50 ms. Kiedy między dwoma tickami minie ponad 150 ms, konsola wypisuje server thread hitch warning: timer interval of 212 milliseconds, a liczba to długość tej przerwy. Przez ten czas nic innego na wątku serwera nie działa, więc przycina wszystkich graczy naraz. Coś na serwerze trzymało wątek za długo, zwykle jeden zasób, który robi dużo pracy w jednej klatce.

Dlaczego serwer FiveM przycina co kilka sekund?

Przycięcie, które wraca w równych odstępach, zwykle robi wątek serwera, który śpi kilka sekund na Wait, a potem za jednym zamachem wykonuje ciężką robotę, np. przechodzi po wszystkich graczach, pojazdach albo punktach pracy. Profiler serwera pokaże, który to wątek: długie klatki podają zasób i linie wątku, np. thread @jobs_garbage/server/main.lua[88..131].

Jak znaleźć zasób, który laguje serwer FiveM?

Użyj wbudowanego profilera FXServera. Wpisz w konsoli serwera profiler record start, po mniej więcej dziesięciu sekundach profiler record stop, potem profiler saveJSON lag.json i wczytaj plik w zakładce Performance w DevTools Chrome’a. Każdy tick zasobu, wątek i handler eventu ma tam swój czas, więc zasób stojący za długą klatką od razu widać. Jeśli laguje tylko część graczy, niech wpiszą w F8 resmon 1, żeby na żywo zobaczyć czas CPU każdego zasobu po stronie klienta.

Co pokazuje resmon w FiveM?

resmon 1 wpisane w konsoli F8 otwiera monitor zasobów: listę wszystkich zasobów klienta na żywo, z czasem CPU, który zajmują w każdej klatce, i pamięcią, którą trzymają. Mierzy tylko grę gracza, który go otworzył, więc tłumaczy jego FPS i nic nie mówi o przycięciach serwera.

Co znaczy ostrzeżenie oxmysql „took … ms to execute a query”?

oxmysql wypisuje linijkę w rodzaju [10.11.6-MariaDB] jobs_garbage took 312.4512ms to execute a query!, a pod nią samo zapytanie, za każdym razem, gdy zapytanie trwa dłużej niż convar mysql_slow_query_warning, domyślnie 200 ms. Nazwa zasobu mówi, który skrypt je wysłał. Zapytanie idzie asynchronicznie, więc samo nie zatrzymuje wątku serwera, ale skrypt, który czeka na wynik, się spóźnia i gracze widzą garaż albo ekwipunek otwierający się z opóźnieniem. Częsta przyczyna to brak indeksu na kolumnie z WHERE; dopisz EXPLAIN przed zapytaniem, żeby sprawdzić, czy MySQL jakiegoś używa.

To samo polowanie jedną komendą

Agent FiveDocka robi to wszystko za ciebie. Z trzema linijkami add_ace w server.cfg komenda fivedock perf nagrywa profiler przez dziesięć sekund, liczy komendy każdego zasobu na serwerze i w grze trzech graczy, i pyta każdego gracza o 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.

Wątek śmieciarza w server/main.lua:88-131 co kilka sekund zatrzymuje serwer na około 200 ms, a paczka emotek ma w grze graczy 2034 komendy. Kiedy wątek serwera stanie, a nikt nie patrzy, agent sam nagrywa kolejne dziesięć sekund, więc następny raport już mówi, co się stało.

Znajdź, co laguje opisuje konfigurację i każdą część raportu.

Wszystkie artykuły