FiveM CreateThread und Wait(0) erklärt: Warum dein Skript ms kostet

Wie CreateThread, Wait und SetTimeout in FiveM Lua funktionieren, warum Wait(0) in jeder Schleife in resmon angezeigt wird und warum eine While-Schleife ohne Wait das Spiel einfriert.

Du schreibst eine Schleife, um etwas zu überprüfen, und das Spiel hört auf zu antworten, oder resmon zeigt deine Ressource bei ein paar ms, während du still stehst:

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

Dieser Artikel erklärt, wie Threads und Wait in FiveM Lua funktionieren, was Wait(0) wirklich kostet und wie man Schleifen schreibt, die billig bleiben.

Wie Threads in FiveM funktionieren

Lua in FiveM ist kooperativ. Dein Skript läuft nicht parallel mit dem Spiel: Es läuft, wenn die Runtime es aufruft, und es muss die Kontrolle zurückgeben. Zwei Dinge machen das möglich:

  • Event-Handler und Callbacks laufen einmal, von Anfang bis Ende, dann kehren zurück.
  • Threads, die mit CreateThread erstellt werden, sind kleine Coroutines, die der Scheduler immer wieder aufnimmt. Ein Thread gibt die Kontrolle zurück, wenn er Wait aufruft.
lua
CreateThread(function()
    while true do
        Wait(1000)
        print('one second later')
    end
end)

CreateThread und Citizen.CreateThread sind die gleiche Funktion, ebenso wie Wait und Citizen.Wait. Verwende, welche du magst, aber sei in einem Skript konsistent.

Eine While-Schleife ohne Wait friert das Spiel ein

Wenn ein Thread niemals Wait aufruft, gibt der Scheduler nie die Kontrolle zurück. Auf dem Client stoppt das Spiel das Zeichnen von Frames bis die Schleife endet, und auf dem Server warten alle anderen Skripte auf deines.

lua
CreateThread(function()
    while true do
        -- missing Wait: this blocks the whole client
    end
end)

Das gleiche passiert mit einer repeat … until oder einer langen for-Schleife, die schwere Arbeit tut. Jede Schleife, die eine Weile laufen kann, braucht ein Wait darin. Wenn ein Skript das Spiel beim Start aufhängt, schau zuerst nach einer Schleife wie dieser.

Was Wait(0) kostet

Wait(0) bedeutet: Pause bis zum nächsten Frame (Client) oder zum nächsten Tick (Server), dann weiter. Die Schleife läuft wieder auf jedem Frame, sodass bei 60 fps sein Körper 60 Mal pro Sekunde bezahlt wird.

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)

Eine Schleife wie diese kostet wenig. Zehn von ihnen, über dreißig Ressourcen verteilt, summieren sich auf mehrere ms an Client-Zeit, was genau das ist, das resmon als hohe ms zeigt. Die Kosten sind nicht Wait(0) selbst, es ist alles, das du jeden Frame wiederholst.

Warte nur so kurz, wie du brauchst

Die meisten Überprüfungen brauchen nicht auf jedem Frame zu laufen. Frag dich, wofür die Schleife ist:

Was die Schleife macht Angemessenes Warten
Zeichne einen Marker oder Text, verwalte einen Tastendruck 0 (nur wenn der Spieler nah ist)
Überprüfe eine Entfernung, um zu entscheiden, ob etwas nah ist 250 bis 1000
Frage einen Zustand ab, der sich selten ändert (Job, Fahrzeug) 1000 oder mehr, oder verwende ein Event

Der übliche Trick ist zwei Geschwindigkeiten: eine langsame Schleife, die die Entfernung überprüft, und eine schnelle nur, während der Spieler im Gebiet ist.

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)

Weit weg von der Zone wacht der Thread einmal pro Sekunde auf. In der Nähe zeichnet er auf jedem Frame. Resmon fällt für die meisten Spieler auf fast nichts.

Tipp: Bevor du nach einer schnelleren Schleife greifst, frag dich, ob ein Event oder ein State Bag dir mitteilen kann, wenn sich der Wert ändert. Gar keine Schleife ist billiger als jedes Wait.

SetTimeout: Etwas einmal, später ausführen

Wenn du nur etwas einmal nach einer Verzögerung tun musst, brauchst du keinen Thread mit einem Wait:

lua
SetTimeout(5000, function()
    print('five seconds later')
end)

SetTimeout(ms, callback) führt den Callback einmal nach der Verzögerung aus und blockiert nichts. Es existiert auch auf dem Server. Für eine wiederholte Aufgabe ist ein Thread mit Wait das richtige Werkzeug, da SetTimeout nicht von allein wiederholt.

Warten innerhalb von Event-Handlern

Ein Event-Handler kann auch Wait aufrufen, weil FiveM ihn in seiner eigenen Coroutine ausführt. Seien Sie vorsichtig auf dem Server: Nach einem Wait zeigt die globale source nicht mehr auf den Spieler, der das Event ausgelöst hat. Speichern Sie es zuerst (siehe Client und Server Events):

lua
RegisterNetEvent('my_script:server:buy', function()
    local src = source
    Wait(500)
    print(('player %s bought something'):format(src))
end)

Einen Thread stoppen

Ein Thread endet, wenn seine Funktion zurückkehrt. Um eine Schleife von außen zu stoppen, verwende eine Flagge, die die Schleife überprüft:

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)

Alle Threads einer Ressource stoppen von allein, wenn die Ressource stoppt, daher brauchst du das nur, um eine Schleife zu stoppen, während die Ressource weiterlauft.

Checkliste

Symptom Behebung
Spiel friert ein, wenn ein Skript startet Eine while true do-Schleife hat kein Wait: Füge einen hinzu
Ressource zeigt konstante ms in resmon im Leerlauf Ersetze Wait(0) durch eine Entfernungsprüfung und ein längeres Warten
Brauche, etwas einmal nach einer Verzögerung auszuführen SetTimeout(ms, fn) anstelle eines Threads
source ist nach einem Wait auf dem Server falsch Speichere local src = source vor dem Wait
Polling eines Wertes, der sich selten ändert Verwende ein Event oder einen State Bag anstelle einer Schleife
Citizen.Wait oder Wait? Die gleiche Funktion, wähle einen Stil

Kurze Antworten

Was macht Wait(0) in FiveM?

Es pausiert den aktuellen Thread bis zum nächsten Frame auf dem Client oder zum nächsten Server-Tick. Die Schleife läuft dann auf jedem Frame erneut, sodass alles darin 60 oder mehr Mal pro Sekunde bezahlt wird.

Ist Citizen.Wait anders als Wait?

Nein. Wait und Citizen.Wait sind die gleiche Funktion, ebenso wie CreateThread und Citizen.CreateThread. Die Kurznamen sind nur Aliase.

Friert Wait das ganze Spiel ein?

Nein. Es pausiert nur den Thread, der es aufruft, und die anderen Threads und das Spiel laufen weiter. Eine Schleife, die niemals Wait aufruft, ist das, was alles blockiert.

Scripts ohne dieses Problem

Advanced BoostingFahrzeug-Boosting per Tablet: Aufträge von Klasse D bis S+, Crews und eine Live-Warteschlange.Script ansehen →CCTV Security CamerasPlatzierbare Kameras, ein Live-Tablet mit Multi-View und ausgedruckte Beweisfotos.Script ansehen →Mic PhoneEin faltbares Handy, das sich zum Tablet aufklappt und bis aufs echte Handy des Spielers reicht.Script ansehen →

Weiterlesen