FiveM playerConnecting e deferrals: defer, update, done spiegati
Come funziona AddEventHandler('playerConnecting') con deferrals: defer, Wait(0), update, done(reason), adaptive cards, e perché i giocatori rimangono bloccati quando dimentichi di chiamare done.
Vuoi eseguire qualcosa prima che un giocatore sia autorizzato a entrare: un controllo whitelist, una lista dei ban, una ricerca nel database, una scheda di benvenuto. Il posto per quello è l'evento playerConnecting, e lo strumento è deferrals. Usato male, i giocatori rimangono sulla schermata di connessione per sempre, quindi i dettagli contano.
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
-- runs when a player starts connecting
end)Quali sono gli argomenti
name: il nome del giocatore.setKickReason(reason): una funzione che rifiuta la connessione con un messaggio. Esiste per rifiuti semplici e immediati.deferrals: un oggetto con funzioni che ti permettono di mettere in pausa la connessione e parlare con il giocatore mentre lavori.
Dentro l'handler, source è l'id di connessione del giocatore.
Le quattro chiamate deferrals
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)deferrals.defer()dice a FiveM di trattenere il giocatore fino a quando decidi. Senza di esso, il giocatore è lasciato entrare non appena il tuo handler ritorna, anche se hai avviato un compito asincrono.Wait(0)è necessario subito dopodefer(). Le chiamate aupdate,doneopresentCardnello stesso tick dideferpotrebbero non funzionare.deferrals.update(message)sostituisce il testo che il giocatore vede sulla schermata di connessione. Usalo per mostrare i progressi.deferrals.done(reason)termina l'attesa. Nessun argomento significa che il giocatore è accettato. Una stringa significa che è rifiutato e vede quella stringa.
Suggerimento: copia
sourcein una variabile locale all'inizio. DopoWaito una richiesta HTTP, ilsourceglobale può puntare a un altro giocatore.
Rifiutare un giocatore
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(...) termina l'handler allo stesso tempo, il che ti impedisce di chiamare done due volte.
Controlli asincroni
I deferrals sono fatti per il lavoro che richiede tempo: una query al database o una chiamata HTTP. Mantieni la connessione differita e chiama done nel callback.
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 esiste nelle build server attuali. Se il tuo artefatto è vecchio, fai un loop su GetPlayerIdentifiers invece, come sopra. Vedi la guida oxmysql per la sintassi della query. Un esempio completo con ruoli Discord è in Discord whitelist per FiveM.
L'errore comune: dimenticare done
I giocatori bloccati sulla schermata di connessione quasi sempre significano che un percorso del codice non raggiunge mai deferrals.done.
-- 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)Se row è nil, il giocatore è tenuto per sempre. La soluzione è un else:
MySQL.single('SELECT * FROM bans WHERE license = ?', { license }, function(row)
if row then
deferrals.done('You are banned.')
else
deferrals.done()
end
end)Tre altri modi per rimanere bloccati:
- Un errore di script dentro l'handler prima di
done. L'handler si interrompe e il giocatore aspetta. Leggi la console. Vedi lettura di un errore di script. - Una chiamata HTTP che non risponde mai. Aggiungi un timeout, per esempio tracciando il tempo di inizio e terminando con un messaggio se non è tornato nulla.
- Chiamare
donedue volte, o chiamareupdatedopodone. Usareturnper lasciare l'handler dopo ogni conclusione.
Più handler contemporaneamente
Molte risorse ascoltano playerConnecting, e ognuna che chiama defer aggiunge all'attesa. Il giocatore è lasciato entrare solo quando tutti loro finiscono. Un handler lento o rotto in uno script blocca tutti, quindi testa con solo quella risorsa per prima quando le connessioni rimangono bloccate.
Adaptive cards
deferrals.presentCard mostra un modulo o una schermata di regole da un Adaptive Card, che è un layout JSON. Il giocatore può premere un pulsante, e il tuo callback riceve il risultato.
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)Mantieni le schede semplici: testo, un'immagine e uno o due pulsanti. Il callback deve ancora terminare in done.
Checklist
| Sintomo | Soluzione |
|---|---|
| Giocatore bloccato sulla connessione | Un percorso non chiama mai deferrals.done; aggiungi l'else mancante |
update o done non ha effetto |
Chiama Wait(0) subito dopo deferrals.defer() |
| Giocatore sbagliato interessato | Salva local src = source per primo |
| Giocatore lasciato entrare prima che il controllo finisca | Hai dimenticato deferrals.defer() |
| Rifiuta con un messaggio | deferrals.done('reason') |
| Accetta il giocatore | deferrals.done() senza argomento |
| Blocco solo con molti script | L'handler playerConnecting di un'altra risorsa è lento o rotto |
Risposte rapide
Perché ho bisogno di Wait(0) dopo deferrals.defer()?
Il deferral è attivo solo una volta che il motore ha fatto un tick. Chiamare deferrals.update o deferrals.done nello stesso tick può essere ignorato, quindi fai un yield prima con Wait(0).
Come posso rifiutare un giocatore?
Chiama deferrals.done('your reason') con una stringa. Il giocatore vede il testo e non è autorizzato a entrare. Chiamare deferrals.done() senza argomento lo lascia connettere.
Cosa succede se non chiamo mai deferrals.done?
Il giocatore rimane sulla schermata di connessione fino al timeout della connessione. Ogni percorso del codice nel tuo handler deve terminare in deferrals.done.

