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:

lua
while true do
    -- no Wait here: the game never gets control back
    local ped = PlayerPedId()
end

Questo 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 CreateThread sono piccole coroutine che lo scheduler riprende ancora e ancora. Un thread restituisce il controllo quando chiama Wait.
lua
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.

lua
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.

lua
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.

lua
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:

lua
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):

lua
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:

lua
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 →

Continua a leggere