FiveM CreateThread e Wait(0) explicados: por que seu script custa ms
Como CreateThread, Wait e SetTimeout funcionam no Lua FiveM, por que Wait(0) em cada loop aparece no resmon, e por que um while loop sem Wait congela o jogo.
Você escreve um loop para verificar algo, e o jogo para de responder, ou resmon mostra seu recurso em alguns ms enquanto você está parado:
while true do
-- no Wait here: the game never gets control back
local ped = PlayerPedId()
endEste artigo explica como as threads e Wait funcionam no Lua FiveM, o que Wait(0) realmente custa, e como escrever loops que permanecem baratos.
Como as threads funcionam no FiveM
Lua no FiveM é cooperativo. Seu script não é executado em paralelo com o jogo: ele é executado quando o tempo de execução o chama, e deve devolver o controle. Duas coisas tornam isso possível:
- Event handlers e callbacks são executados uma vez, do início ao fim, e depois retornam.
- Threads criadas com
CreateThreadsão pequenas coroutines que o agendador retoma novamente e novamente. Uma thread devolve o controle quando chamaWait.
CreateThread(function()
while true do
Wait(1000)
print('one second later')
end
end)CreateThread e Citizen.CreateThread são a mesma função, e assim são Wait e Citizen.Wait. Use qual você preferir, mas seja consistente em um script.
Um while loop sem Wait congela o jogo
Se uma thread nunca chama Wait, o agendador nunca recupera o controle. No cliente, o jogo para de desenhar quadros até o loop terminar, e no servidor, cada outro script aguarda o seu.
CreateThread(function()
while true do
-- missing Wait: this blocks the whole client
end
end)O mesmo acontece com um repeat … until ou um long for loop que faz trabalho pesado. Cada loop que pode executar por um tempo precisa de um Wait dentro dele. Se um script congela o jogo na inicialização, procure por um loop assim primeiro.
O que Wait(0) custa
Wait(0) significa: pause até o próximo quadro (cliente) ou o próximo tick (servidor), e depois continue. O loop é executado novamente em cada quadro, portanto a 60 fps seu corpo é pago 60 vezes por segundo.
CreateThread(function()
while true do
Wait(0)
local coords = GetEntityCoords(PlayerPedId())
local dist = #(coords - vector3(215.0, -810.0, 30.7))
if dist < 3.0 then
-- draw a marker, show text...
end
end
end)Um loop assim custa pouco. Dez deles, em trinta recursos, somam alguns ms de tempo do cliente, que é exatamente o que resmon mostra como ms alto. O custo não é o Wait(0) em si, é tudo que você repete a cada quadro.
Wait apenas tão curto quanto você precisa
A maioria das verificações não precisa ser executada a cada quadro. Pergunte qual é o propósito do loop:
| O que o loop faz | Espera razoável |
|---|---|
| Desenhar um marcador ou texto, lidar com um pressionamento de tecla | 0 (apenas enquanto o jogador está próximo) |
| Verificar uma distância para decidir se algo está próximo | 250 a 1000 |
| Sondagem de um estado que muda raramente (trabalho, veículo) | 1000 ou mais, ou use um evento |
O truque usual é duas velocidades: um loop lento que verifica a distância, e um rápido apenas enquanto o jogador está dentro da área.
local zone = vector3(215.0, -810.0, 30.7)
CreateThread(function()
while true do
local sleep = 1000
local dist = #(GetEntityCoords(PlayerPedId()) - zone)
if dist < 10.0 then
sleep = 0
DrawMarker(1, zone.x, zone.y, zone.z - 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1.5, 1.5, 0.5, 255, 255, 255, 120, false, false, 2, false, nil, nil, false)
end
Wait(sleep)
end
end)Longe da zona, a thread acorda uma vez por segundo. Perto dela, ela desenha a cada quadro. Resmon cai para quase nada para a maioria dos jogadores.
Dica: antes de chegar a um loop mais rápido, pergunte se um evento ou uma sacola de estado pode informar quando o valor muda. Nenhum loop é mais barato que qualquer Wait.
SetTimeout: executar algo uma vez, depois
Se você apenas precisa fazer algo uma vez após um atraso, você não precisa de uma thread com um Wait:
SetTimeout(5000, function()
print('five seconds later')
end)SetTimeout(ms, callback) executa o callback uma vez após o atraso e não bloqueia nada. Também existe no servidor. Para uma tarefa repetida, uma thread com Wait é a ferramenta certa, pois SetTimeout não se repete por conta própria.
Aguardando dentro de event handlers
Um event handler também pode chamar Wait, porque FiveM o executa em sua própria coroutine. Tenha cuidado no servidor: após um Wait, o source global não aponta mais para o jogador que disparou o evento. Salve-o primeiro (veja eventos de cliente e servidor):
RegisterNetEvent('my_script:server:buy', function()
local src = source
Wait(500)
print(('player %s bought something'):format(src))
end)Interrompendo uma thread
Uma thread termina quando sua função retorna. Para interromper um loop de fora, use um sinalizador que o loop verifica:
local running = true
CreateThread(function()
while running do
Wait(500)
-- work
end
end)
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then
running = false
end
end)Todas as threads de um recurso param automaticamente quando o recurso para, portanto você apenas precisa disso para parar um loop enquanto o recurso continua em execução.
Checklist
| Sintoma | Correção |
|---|---|
| Jogo congela quando um script inicia | Um loop while true do não tem Wait: adicione um |
| Recurso mostra ms constantes no resmon enquanto ocioso | Substitua Wait(0) por uma verificação de distância e um wait mais longo |
| Precisa executar algo uma vez após um atraso | SetTimeout(ms, fn) em vez de uma thread |
source está errado após um Wait no servidor |
Salve local src = source antes do Wait |
| Sondagem de um valor que raramente muda | Use um evento ou uma sacola de estado em vez de um loop |
Citizen.Wait ou Wait? |
Mesma função, escolha um estilo |
Respostas rápidas
O que Wait(0) faz no FiveM?
Ele pausa a thread atual até o próximo quadro no cliente, ou o próximo tick do servidor. O loop é executado novamente a cada quadro, portanto tudo dentro dele é pago 60 ou mais vezes por segundo.
Citizen.Wait é diferente de Wait?
Não. Wait e Citizen.Wait são a mesma função, e assim são CreateThread e Citizen.CreateThread. Os nomes curtos são apenas aliases.
Wait paralisa todo o jogo?
Não. Ele apenas pausa a thread que a chama, e as outras threads e o jogo continuam em execução. Um loop que nunca chama Wait é o que bloqueia tudo.
Scripts sem esse problema
Advanced BoostingBoosting de veículos pelo tablet: contratos da classe D à S+, crews e fila ao vivo.Ver script →
CCTV Security CamerasCâmeras posicionáveis, um tablet com várias telas ao vivo e fotos impressas como prova.Ver script →
Mic PhoneUm celular dobrável que abre em tablet e chega ao celular de verdade do jogador.Ver script →