Pular para o conteúdo
Blog

Servidor FiveM com lag: descubra o resource que causa

Servidor travando ou FPS baixo? Como diferenciar, ler o profiler do FXServer, contar os comandos de cada resource e achar o script por trás do lag.

Por Termi

7 min de leitura
  • Performance
  • Debugging
  • Lua
Nesta página

“O servidor está com lag” são dois problemas diferentes. Às vezes a thread do servidor trava e todo mundo engasga ao mesmo tempo; às vezes só alguns jogadores perdem FPS enquanto o servidor vai bem. Este guia mostra como diferenciar os dois, como ler o profiler do próprio FXServer e como achar a causa que se esconde dos dois: um resource que registra milhares de comandos. No fim, a mesma caçada com um único comando.

Dois tipos de lag

  • A thread do servidor trava. Todo mundo dá rubber-band no mesmo momento, os carros teleportam e o console do servidor imprime server thread hitch warning: timer interval of 212 milliseconds. Alguma coisa num script do servidor demora demais num frame.
  • O FPS de um jogador cai. O servidor está bem e o console fica quieto, mas alguns jogadores engasgam, muitas vezes nos mesmos lugares. O custo está nos scripts do client, em assets em streaming ou nas entidades ao redor.

Cada um pede ferramentas diferentes, então primeiro confira qual você tem: há hitch warnings, e todo mundo sente ou só alguns jogadores?

Leia o profiler do servidor

O FXServer vem com um profiler que mede cada resource, thread e handler de evento. No console do servidor:

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

Inicie enquanto o servidor está com lag, pare depois de uns dez segundos e salve a gravação. O lag.json é um trace do Chrome: abra o DevTools no Chrome, vá em Performance e carregue o arquivo. Cada frame do servidor é um Resource Manager Tick, e dentro dele você encontra o tick (name) de cada resource, as threads como thread @resource/file.lua[from..to], os handlers de evento e os exports.

Procure os frames que demoram bem mais que os outros e veja qual resource os enche. O intervalo de linhas diz qual CreateThread ou handler era.

Um resource não consegue iniciar o profiler sozinho. ExecuteCommand('profiler record start') num script é recusado, a menos que o server.cfg permita: add_ace resource.<name> command.profiler allow, mais a mesma linha para command.record e command.saveJSON.

Meça o FPS de um jogador

Do lado do jogador, o F8 tem duas ferramentas. O resmon 1 mostra ao vivo o tempo de CPU e a memória de cada resource, o que basta quando um deles se destaca. Para olhar mais de perto, o mesmo profiler roda dentro do jogo:

F8
resmon 1
profiler record 500
profiler saveJSON lag.json

Isso grava 500 frames. Carregue o arquivo no DevTools do mesmo jeito e procure threads do client que rodam a cada frame. Um loop com Wait(0) que faz trabalho de verdade a cada frame, como medir a distância até centenas de markers, costuma ser o primeiro suspeito.

A causa que nenhuma das duas ferramentas mostra

Um tipo de lag se esconde das duas. Um servidor adiciona um pacote de emotes e logo depois começa a ter lag. O profiler não mostra nenhuma thread pesada, e procurar RegisterCommand no pacote acha um punhado de chamadas.

As chamadas estão num loop, atrás de um alias local, uma para cada 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

Com dois mil emotes, são dois mil comandos e duas mil sugestões de chat no jogo de cada jogador. Uma busca por texto não consegue contá-los, mas o jogo consegue: GetRegisteredCommands() devolve cada comando com o resource que o registrou.

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

Rode como script de client e como script de servidor. Um resource com centenas de comandos merece uma olhada. Um com milhares quase sempre registra um comando para cada emote ou item, onde um comando com argumento resolveria.

Os suspeitos de sempre

  • Threads que esperam pouco. Wait(0) num loop roda a cada frame. Serve para desenhar um marker e sai caro quando cada volta percorre todos os jogadores ou todos os pontos.
  • Trabalho no tick do servidor. Reconstruir uma tabela grande ou ordenar milhares de linhas dentro de uma thread bloqueia todos os outros resources pelo tempo que durar.
  • Eventos para todo mundo. TriggerClientEvent(name, -1, data) num loop manda o payload inteiro a cada jogador, toda vez.
  • Entidades que ninguém apaga. Props e peds que se acumulam custam para todo jogador por perto. GetGamePool('CObject') conta quantos são.
  • Comandos aos milhares. Veja acima.

Perguntas frequentes

O que significa “server thread hitch warning” no FiveM?

A thread principal do FXServer deve dar 20 ticks por segundo, um a cada 50 ms. Quando passam mais de 150 ms entre dois ticks, o console imprime server thread hitch warning: timer interval of 212 milliseconds, e o número é a duração desse buraco. Nada mais roda na thread do servidor durante o buraco, então todos os jogadores sentem no mesmo momento. Algo no servidor segurou a thread por tempo demais, normalmente um resource fazendo muito trabalho num único frame.

Por que o meu servidor FiveM dá hitch a cada poucos segundos?

Um hitch com ritmo regular costuma vir de uma thread do servidor que dorme alguns segundos com Wait e depois faz trabalho pesado de uma vez, como percorrer todos os jogadores, veículos ou pontos de um job. O profiler do servidor mostra qual é a thread: os frames longos nomeiam o resource e as linhas da thread, por exemplo thread @jobs_garbage/server/main.lua[88..131].

Como descubro qual resource está dando lag no meu servidor FiveM?

Use o profiler embutido do FXServer. Rode profiler record start no console do servidor, profiler record stop uns dez segundos depois, depois profiler saveJSON lag.json, e carregue o arquivo na aba Performance do DevTools do Chrome. O tick, as threads e os handlers de evento de cada resource aparecem com seu tempo, então o que está por trás de um frame longo se destaca. Se só alguns jogadores têm lag, peça que digitem resmon 1 no F8 para ver ao vivo o tempo de CPU de cada resource do client.

O que o resmon mostra no FiveM?

resmon 1, digitado no console do F8, abre o monitor de resources: uma lista ao vivo de cada resource do client com o tempo de CPU que ele usa por frame e a memória que ocupa. Ele mede só o jogo de quem o abriu, então explica o FPS desse jogador e não diz nada sobre um hitch do servidor.

O que significa o aviso “took … ms to execute a query” do oxmysql?

O oxmysql imprime uma linha como [10.11.6-MariaDB] jobs_garbage took 312.4512ms to execute a query!, seguida da query, sempre que uma query demora mais que o convar mysql_slow_query_warning, 200 ms por padrão. O nome do resource diz qual script a enviou. A query roda de forma assíncrona, então sozinha ela não trava a thread do servidor, mas o script que espera o resultado atrasa, e os jogadores veem uma garagem ou um inventário que abre devagar. Uma causa comum é a falta de índice na coluna da cláusula WHERE; coloque EXPLAIN na frente da query para ver se o MySQL usa algum.

A mesma caçada com um comando

O agente do FiveDock faz tudo isso por você. Com as três linhas add_ace no server.cfg, o fivedock perf grava o profiler por dez segundos, conta os comandos de cada resource no servidor e no jogo de três jogadores e pede o FPS de cada jogador:

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.

A thread do emprego de lixeiro em server/main.lua:88-131 segura o servidor por uns 200 ms a cada poucos segundos, e o pacote de emotes tem 2.034 comandos no jogo dos jogadores. Quando a thread do servidor trava sem ninguém olhando, o agente grava sozinho os dez segundos seguintes, então o próximo relatório já diz o que aconteceu.

Descobrir o que causa lag cobre a configuração e cada parte do relatório.

Todos os artigos