FiveM playerConnecting und deferrals: defer, update, done erklärt

Wie AddEventHandler('playerConnecting') mit deferrals funktioniert: defer, Wait(0), update, done(reason), adaptive Karten, und warum Spieler hängen bleiben, wenn du vergisst, done zu rufen.

Du möchtest etwas ausführen, bevor ein Spieler hereingelassen wird: eine Whitelist-Überprüfung, eine Bannliste, eine Datenbankabfrage, eine Willkommenskarte. Der richtige Ort dafür ist das playerConnecting Event, und das Tool ist deferrals. Falsch verwendet, sitzen Spieler auf dem Verbindungsbildschirm ewig fest, also sind die Details wichtig.

lua
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
    -- runs when a player starts connecting
end)

Was die Argumente sind

  • name: der Name des Spielers.
  • setKickReason(reason): eine Funktion, die die Verbindung mit einer Nachricht ablehnt. Sie existiert für einfache, sofortige Ablehnungen.
  • deferrals: ein Objekt mit Funktionen, die dir ermöglichen, die Verbindung zu unterbrechen und mit dem Spieler zu kommunizieren, während du arbeitest.

Innerhalb des Handlers ist source die Verbindungs-ID des Spielers.

Die vier Deferral-Aufrufe

lua
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
    local src = source          -- save it now

    deferrals.defer()           -- 1. pause the connection
    Wait(0)                     -- 2. let the engine register it

    deferrals.update('Checking your account...')   -- 3. show a message

    -- do your checks here

    deferrals.done()            -- 4. let the player in
end)
  1. deferrals.defer() sagt FiveM, den Spieler zu halten, bis du dich entscheidest. Ohne ihn wird der Spieler hereingelassen, sobald dein Handler zurückgegeben wird, selbst wenn du eine asynchrone Aufgabe gestartet hast.
  2. Wait(0) ist direkt nach defer() notwendig. Aufrufe von update, done oder presentCard im gleichen Tick wie defer funktionieren möglicherweise nicht.
  3. deferrals.update(message) ersetzt den Text, den der Spieler auf dem Verbindungsbildschirm sieht. Verwende es, um den Fortschritt anzuzeigen.
  4. deferrals.done(reason) beendet das Warten. Kein Argument bedeutet, der Spieler wird akzeptiert. Eine Zeichenkette bedeutet, dass er abgelehnt wird und diesen Text sieht.

Tipp: Kopiere source gleich am Anfang in eine lokale Variable. Nach Wait oder einer HTTP-Anfrage kann das globale source auf einen anderen Spieler zeigen.

Einen Spieler ablehnen

lua
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
    local src = source
    deferrals.defer()
    Wait(0)

    local license
    for _, id in ipairs(GetPlayerIdentifiers(src)) do
        if id:sub(1, 8) == 'license:' then license = id break end
    end

    if not license then
        return deferrals.done('No Rockstar license found. Restart the game and try again.')
    end

    deferrals.done()
end)

return deferrals.done(...) beendet den Handler zur gleichen Zeit, was dich davor bewahrt, done zweimal aufzurufen.

Asynchrone Überprüfungen

Deferrals sind für Arbeit gemacht, die Zeit braucht: eine Datenbankabfrage oder ein HTTP-Aufruf. Halte die Verbindung aufgeschoben und rufe done im Callback auf.

lua
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
    local src = source
    deferrals.defer()
    Wait(0)
    deferrals.update('Looking you up...')

    local license = GetPlayerIdentifierByType(src, 'license')

    MySQL.scalar('SELECT 1 FROM whitelist WHERE license = ?', { license }, function(found)
        if found then
            deferrals.done()
        else
            deferrals.done('You are not whitelisted.')
        end
    end)
end)

GetPlayerIdentifierByType existiert in aktuellen Server-Builds. Wenn dein Artifact alt ist, schleife über GetPlayerIdentifiers statt dessen, wie oben. Siehe das oxmysql-Handbuch für die Abfragesyntax. Ein vollständiges Beispiel mit Discord-Rollen findest du in Discord-Whitelist für FiveM.

Der häufige Fehler: done vergessen

Spieler, die auf dem Verbindungsbildschirm stecken, bedeuten fast immer, dass ein Code-Pfad niemals deferrals.done erreicht.

lua
-- Bad: nothing happens when the query returns no row
MySQL.single('SELECT * FROM bans WHERE license = ?', { license }, function(row)
    if row then
        deferrals.done('You are banned.')
    end
end)

Wenn row nil ist, wird der Spieler auf unbestimmte Zeit halten. Die Behebung ist ein else:

lua
MySQL.single('SELECT * FROM bans WHERE license = ?', { license }, function(row)
    if row then
        deferrals.done('You are banned.')
    else
        deferrals.done()
    end
end)

Drei andere Wege, um stecken zu bleiben:

  • Ein Script-Fehler in dem Handler vor done. Der Handler stoppt und der Spieler wartet. Lese die Konsole. Siehe Fehler in einem FiveM-Script lesen.
  • Ein HTTP-Aufruf, der nie antwortet. Füge einen Timeout hinzu, zum Beispiel, indem du die Startzeit verfolgst und mit einer Nachricht endet, wenn nichts zurückgekommen ist.
  • done zweimal aufrufen, oder update nach done aufrufen. Verwende return, um den Handler nach jedem Ende zu verlassen.

Mehrere Handler gleichzeitig

Viele Ressourcen hören auf playerConnecting, und jeder, der defer aufruft, trägt zum Warten bei. Der Spieler wird nur hereingelassen, wenn alle fertig sind. Ein langsamer oder kaputten Handler in einem Script hält alle auf, also teste nur mit dieser Ressource zuerst, wenn Verbindungen hängen bleiben.

Adaptive Karten

deferrals.presentCard zeigt ein Formular oder einen Regelsbildschirm aus einer Adaptive Card, die ein JSON-Layout ist. Der Spieler kann auf einen Button drücken, und dein Callback erhält das Ergebnis.

lua
local card = {
    type = 'AdaptiveCard',
    version = '1.3',
    body = {
        { type = 'TextBlock', text = 'Server rules', weight = 'Bolder', size = 'Large' },
        { type = 'TextBlock', text = 'Be respectful and no cheating.', wrap = true },
    },
    actions = {
        { type = 'Action.Submit', title = 'I agree', data = { accepted = true } },
    },
}

AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
    deferrals.defer()
    Wait(0)

    deferrals.presentCard(json.encode(card), function(data)
        if data and data.accepted then
            deferrals.done()
        else
            deferrals.done('You must accept the rules.')
        end
    end)
end)

Halte Karten einfach: Text, ein Bild und ein oder zwei Buttons. Der Callback muss immer noch mit done enden.

Checkliste

Symptom Behebung
Spieler sitzt auf Verbindung fest Ein Pfad ruft deferrals.done nie auf; füge das fehlende else hinzu
update oder done hat keine Auswirkung Rufe Wait(0) direkt nach deferrals.defer() auf
Falscher Spieler betroffen Speichere local src = source zuerst
Spieler wird rein gelassen, bevor die Überprüfung endet Du hast deferrals.defer() vergessen
Ablehnen mit einer Nachricht deferrals.done('reason')
Akzeptiere den Spieler deferrals.done() ohne Argument
Verzögerung nur bei vielen Scripts Der playerConnecting Handler einer anderen Ressource ist langsam oder kaputt

Kurze Antworten

Warum brauche ich Wait(0) nach deferrals.defer()?

Der Deferral ist nur aktiv, sobald die Engine auf einen Tick fortgeschritten ist. Aufrufe von deferrals.update oder deferrals.done im gleichen Tick können ignoriert werden, also gib zuerst mit Wait(0) einmal nach.

Wie lehne ich einen Spieler ab?

Rufe deferrals.done('dein Grund') mit einer Zeichenkette auf. Der Spieler sieht den Text und darf nicht rein. deferrals.done() ohne Argument erlauben lässt ihn verbinden.

Was passiert, wenn ich deferrals.done nie aufrufe?

Der Spieler bleibt auf dem Verbindungsbildschirm, bis die Verbindung zeitlich abläuft. Jeder Code-Pfad in deinem Handler muss mit deferrals.done enden.

Scripts ohne dieses Problem

Tebex TemplateEin Premium-Theme ohne Code für deinen Tebex-Shop, komplett im Tebex-Panel anpassbar.Script ansehen →Mic PhoneEin faltbares Handy, das sich zum Tablet aufklappt und bis aufs echte Handy des Spielers reicht.Script ansehen →

Weiterlesen