FiveM playerConnecting et deferrals : defer, update, done expliqués
Comment AddEventHandler('playerConnecting') fonctionne avec deferrals : defer, Wait(0), update, done(reason), cartes adaptatives, et pourquoi les joueurs attendent quand vous oubliez d'appeler done.
Vous voulez exécuter quelque chose avant qu'un joueur soit autorisé à entrer : une vérification de liste blanche, une liste d'interdiction, une recherche en base de données, une carte de bienvenue. L'endroit pour cela est l'événement playerConnecting, et l'outil est deferrals. Mal utilisées, les joueurs restent sur l'écran de connexion à jamais, les détails comptent donc.
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
-- runs when a player starts connecting
end)Quels sont les arguments
name: le nom du joueur.setKickReason(reason): une fonction qui rejette la connexion avec un message. Elle existe pour les rejets simples et immédiats.deferrals: un objet avec des fonctions qui vous permettent de mettre en pause la connexion et de parler au joueur pendant que vous travaillez.
À l'intérieur du gestionnaire, source est l'ID de connexion du joueur.
Les quatre appels de report
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()dit à FiveM de garder le joueur jusqu'à ce que vous décidiez. Sans lui, le joueur est laissé entrer dès que votre gestionnaire revient, même si vous avez commencé une tâche asynchrone.Wait(0)est nécessaire juste aprèsdefer(). Les appels àupdate,doneoupresentCarddans la même tick quedeferpeuvent ne pas fonctionner.deferrals.update(message)remplace le texte que le joueur voit sur l'écran de connexion. Utilisez-le pour afficher la progression.deferrals.done(reason)termine l'attente. Aucun argument signifie que le joueur est accepté. Une chaîne signifie qu'il est rejeté et voit cette chaîne.
Conseil : copiez
sourcedans une variable locale au tout début. AprèsWaitou une requête HTTP, le globalsourcepeut pointer vers un autre joueur.
Rejeter un joueur
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(...) termine également le gestionnaire en même temps, ce qui vous empêche d'appeler done deux fois.
Vérifications asynchrones
Les reports sont conçus pour les travaux qui prennent du temps : une requête de base de données ou un appel HTTP. Gardez la connexion reportée et appelez done dans le 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 existe dans les versions actuelles du serveur. Si votre artefact est ancien, bouclez plutôt sur GetPlayerIdentifiers, comme ci-dessus. Consultez le guide oxmysql pour la syntaxe des requêtes. Un exemple complet avec les rôles Discord se trouve dans liste blanche Discord pour FiveM.
L'erreur courante : oublier done
Les joueurs bloqués sur l'écran de connexion signifient presque toujours qu'un chemin de code n'atteint jamais 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)Si row est nil, le joueur est retenu à jamais. La correction est une else :
MySQL.single('SELECT * FROM bans WHERE license = ?', { license }, function(row)
if row then
deferrals.done('You are banned.')
else
deferrals.done()
end
end)Trois autres façons de rester bloqué :
- Une erreur de script à l'intérieur du gestionnaire avant
done. Le gestionnaire s'arrête et le joueur attend. Lisez la console. Consultez lire une erreur de script. - Un appel HTTP qui ne répond jamais. Ajoutez un délai d'expiration, par exemple en suivant l'heure de début et en terminant avec un message si rien n'est revenu.
- Appeler
donedeux fois, ou appelerupdateaprèsdone. Utilisezreturnpour quitter le gestionnaire après chaque fin.
Plusieurs gestionnaires à la fois
De nombreuses ressources écoutent playerConnecting, et chacun qui appelle defer s'ajoute à l'attente. Le joueur n'est laissé entrer que quand tous les deux finissent. Un gestionnaire lent ou cassé dans un script retient tout le monde, alors testez d'abord avec seulement cette ressource quand les connexions s'accrochent.
Cartes adaptatives
deferrals.presentCard affiche un formulaire ou un écran de règles à partir d'une Adaptive Card, qui est une mise en page JSON. Le joueur peut appuyer sur un bouton, et votre callback reçoit le résultat.
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)Gardez les cartes simples : texte, une image et un ou deux boutons. Le callback doit toujours se terminer par done.
Liste de contrôle
| Symptôme | Correction |
|---|---|
| Joueur bloqué sur la connexion | Un chemin n'appelle jamais deferrals.done ; ajoutez le else manquant |
update ou done n'a aucun effet |
Appelez Wait(0) juste après deferrals.defer() |
| Mauvais joueur affecté | Enregistrez d'abord local src = source |
| Joueur laissé entrer avant la fin de la vérification | Vous avez oublié deferrals.defer() |
| Rejeter avec un message | deferrals.done('reason') |
| Accepter le joueur | deferrals.done() sans argument |
| L'attente concerne seulement plusieurs scripts | Le gestionnaire playerConnecting d'une autre ressource est lent ou cassé |
Réponses rapides
Pourquoi ai-je besoin de Wait(0) après deferrals.defer() ?
Le report n'est actif qu'une fois que le moteur a avancé d'une tick. Appeler deferrals.update ou deferrals.done dans la même tick peut être ignoré, donc cédez d'abord une fois avec Wait(0).
Comment rejeter un joueur ?
Appelez deferrals.done('votre raison') avec une chaîne. Le joueur voit le texte et ne peut pas entrer. Appeler deferrals.done() sans argument lui permet de se connecter.
Que se passe-t-il si je n'appelle jamais deferrals.done ?
Le joueur reste sur l'écran de connexion jusqu'à ce que la connexion expire. Chaque chemin de code dans votre gestionnaire doit se terminer par deferrals.done.

