CreateThread e Wait(0) in FiveM spiegati: perché il tuo script costa ms
Come CreateThread, Wait e SetTimeout funzionano in FiveM Lua, perché Wait(0) in ogni loop appare in resmon, e perché un while loop senza Wait congela il gioco.
Scrivi un loop per verificare qualcosa, e il gioco smette di rispondere, oppure resmon mostra il tuo resource a pochi ms mentre stai fermo:
while true do
-- no Wait here: the game never gets control back
local ped = PlayerPedId()
endQuesto articolo spiega come funzionano i thread e Wait in FiveM Lua, quale è il costo reale di Wait(0), e come scrivere loop che rimangono economici.
Come funzionano i thread in FiveM
Lua in FiveM è cooperativo. Il tuo script non viene eseguito in parallelo con il gioco: viene eseguito quando il runtime lo chiama, e deve restituire il controllo. Due cose lo rendono possibile:
- I gestori di eventi e i callback vengono eseguiti una volta, dall'inizio alla fine, quindi ritornano.
- I thread creati con
CreateThreadsono piccole coroutine che lo scheduler riprende ancora e ancora. Un thread restituisce il controllo quando chiamaWait.
CreateThread(function()
while true do
Wait(1000)
print('one second later')
end
end)CreateThread e Citizen.CreateThread sono la stessa funzione, così come Wait e Citizen.Wait. Usa quello che preferisci, ma sii coerente in uno script.
Un while loop senza Wait congela il gioco
Se un thread non chiama mai Wait, lo scheduler non recupera mai il controllo. Sul client il gioco smette di disegnare frame fino al termine del loop, e sul server ogni altro script attende il tuo.
CreateThread(function()
while true do
-- missing Wait: this blocks the whole client
end
end)Lo stesso accade con un repeat … until o un lungo for loop che fa un lavoro pesante. Ogni loop che può funzionare per un po' ha bisogno di un Wait al suo interno. Se uno script blocca il gioco all'inizio, cerca prima un loop come questo.
Quanto costa Wait(0)
Wait(0) significa: metti in pausa fino al frame successivo (client) o al tick successivo (server), quindi continua. Il loop viene eseguito di nuovo su ogni frame, quindi a 60 fps il suo corpo viene pagato 60 volte al secondo.
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)Un loop come questo costa poco. Dieci di loro, tra trenta resource, si sommano a pochi ms di tempo del client, che è esattamente quello che resmon mostra come ms alto. Il costo non è il Wait(0) stesso, è tutto quello che ripeti ogni frame.
Wait solo il più breve possibile
La maggior parte dei controlli non ha bisogno di essere eseguiti ogni frame. Chiediti quale sia lo scopo del loop:
| Cosa fa il loop | Attesa ragionevole |
|---|---|
| Disegna un marker o un testo, gestisci una pressione di tasto | 0 (solo mentre il giocatore è vicino) |
| Controlla una distanza per decidere se qualcosa è vicino | 250 a 1000 |
| Sonda uno stato che cambia di rado (lavoro, veicolo) | 1000 o più, o usa un evento |
Il solito trucco è a due velocità: un loop lento che controlla la distanza, e uno veloce solo mentre il giocatore è all'interno dell'area.
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)Lontano dalla zona, il thread si sveglia una volta al secondo. Vicino a essa, disegna ogni frame. Resmon scende a quasi nulla per la maggior parte dei giocatori.
Consiglio: prima di ricorrere a un loop più veloce, chiediti se un evento o un state bag può dirti quando il valore cambia. Nessun loop è più economico di qualsiasi Wait.
SetTimeout: esegui qualcosa una volta, più tardi
Se hai solo bisogno di fare qualcosa una volta dopo un ritardo, non hai bisogno di un thread con un Wait:
SetTimeout(5000, function()
print('five seconds later')
end)SetTimeout(ms, callback) esegue il callback una volta dopo il ritardo e non blocca nulla. Esiste anche sul server. Per un'attività ripetitiva, un thread con Wait è lo strumento giusto, poiché SetTimeout non si ripete da solo.
Aspettare all'interno dei gestori di eventi
Un gestore di eventi può anche chiamare Wait, perché FiveM lo esegue in una propria coroutine. Fai attenzione al server: dopo un Wait, il source globale non punta più al giocatore che ha attivato l'evento. Salvalo prima (vedi client e server eventi):
RegisterNetEvent('my_script:server:buy', function()
local src = source
Wait(500)
print(('player %s bought something'):format(src))
end)Fermare un thread
Un thread termina quando la sua funzione ritorna. Per fermare un loop dall'esterno, usa un flag che il loop controlla:
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)Tutti i thread di una risorsa si fermano da soli quando la risorsa si ferma, quindi hai solo bisogno di questo per fermare un loop mentre la risorsa continua a funzionare.
Elenco di controllo
| Sintomo | Correzione |
|---|---|
| Il gioco si congela quando uno script si avvia | Un loop while true do non ha Wait: aggiungine uno |
| La risorsa mostra ms costante in resmon mentre è inattiva | Sostituisci Wait(0) con un controllo della distanza e un'attesa più lunga |
| Devi eseguire qualcosa una volta dopo un ritardo | SetTimeout(ms, fn) invece di un thread |
source è sbagliato dopo un Wait sul server |
Salva local src = source prima di Wait |
| Sondare un valore che cambia raramente | Usa un evento o un state bag invece di un loop |
Citizen.Wait o Wait? |
Stessa funzione, scegli uno stile |
Risposte rapide
Cosa fa Wait(0) in FiveM?
Mette in pausa il thread corrente fino al frame successivo sul client, o al tick successivo sul server. Il loop viene quindi eseguito di nuovo su ogni frame, quindi tutto ciò che contiene viene pagato 60 o più volte al secondo.
Citizen.Wait è diverso da Wait?
No. Wait e Citizen.Wait sono la stessa funzione, così come CreateThread e Citizen.CreateThread. I nomi brevi sono solo alias.
Wait ferma l'intero gioco?
No. Mette in pausa solo il thread che lo chiama, e gli altri thread e il gioco continuano a funzionare. Un loop che non chiama mai Wait è quello che blocca tutto.
Script senza questo problema
Advanced BoostingBoosting di veicoli dal tablet: contratti dalla classe D alla S+, crew e coda in tempo reale.Vedi script →
CCTV Security CamerasTelecamere posizionabili, un tablet multi-vista in diretta e foto stampate come prove.Vedi script →
Mic PhoneUn telefono pieghevole che si apre in un tablet e arriva sul telefono vero del giocatore.Vedi script →