FiveM playerConnecting y deferrals: defer, update, done explicado
Cómo funciona AddEventHandler('playerConnecting') con deferrals: defer, Wait(0), update, done(reason), tarjetas adaptables, y por qué los jugadores se quedan colgados cuando olvidas llamar a done.
Quieres ejecutar algo antes de que se permita que un jugador entre: una comprobación de lista blanca, una lista de prohibiciones, una búsqueda en la base de datos, una tarjeta de bienvenida. El lugar para eso es el evento playerConnecting, y la herramienta es deferrals. Si se usa incorrectamente, los jugadores permanecen en la pantalla de conexión para siempre, así que los detalles importan.
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
-- runs when a player starts connecting
end)Cuáles son los argumentos
name: el nombre del jugador.setKickReason(reason): una función que rechaza la conexión con un mensaje. Existe para rechazos simples e inmediatos.deferrals: un objeto con funciones que te permiten pausar la conexión y hablar con el jugador mientras trabajas.
Dentro del manejador, source es el id de conexión del jugador.
Las cuatro llamadas de aplazamiento
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()le dice a FiveM que retenga al jugador hasta que decidas. Sin él, el jugador es dejado entrar tan pronto como tu manejador se devuelve, incluso si comenzaste una tarea asincrónica.Wait(0)es necesario justo después dedefer(). Las llamadas aupdate,doneopresentCarden el mismo tick quedeferpueden no funcionar.deferrals.update(message)reemplaza el texto que el jugador ve en la pantalla de conexión. Úsalo para mostrar progreso.deferrals.done(reason)termina la espera. Sin argumento significa que el jugador es aceptado. Una cadena significa que son rechazados y ven esa cadena.
Consejo: copia
sourceen una variable local al inicio. Después deWaito una solicitud HTTP, elsourceglobal puede apuntar a otro jugador.
Rechazar a un jugador
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 el manejador al mismo tiempo, lo que te impide llamar a done dos veces.
Comprobaciones asincrónicas
Los aplazamientos están hechos para trabajo que toma tiempo: una consulta de base de datos o una llamada HTTP. Mantén la conexión aplazada y llama a done en la devolución de llamada.
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 en compilaciones de servidor actuales. Si tu artefacto es antiguo, itera sobre GetPlayerIdentifiers en su lugar, como arriba. Consulta la guía de oxmysql para la sintaxis de consulta. Un ejemplo completo con roles de Discord está en Lista blanca de Discord para FiveM.
El error común: olvidar done
Los jugadores atrapados en la pantalla de conexión casi siempre significan que una ruta de código nunca alcanza 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 es nil, el jugador es retenido para siempre. La solución es 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)Tres otras formas de quedar atrapado:
- Un error de script dentro del manejador antes de
done. El manejador se detiene y el jugador espera. Lee la consola. Consulta leer un error de script. - Una llamada HTTP que nunca responde. Añade un tiempo de espera, por ejemplo, rastreando la hora de inicio y terminando con un mensaje si nada regresó.
- Llamar a
donedos veces, o llamar aupdatedespués dedone. Usareturnpara salir del manejador después de cada final.
Varios manejadores a la vez
Muchos recursos escuchan playerConnecting, y cada uno que llama a defer se suma a la espera. El jugador es dejado entrar solo cuando todos ellos terminan. Un manejador lento o roto en un script detiene a todos, así que prueba con solo ese recurso primero cuando las conexiones se cuelguen.
Tarjetas adaptables
deferrals.presentCard muestra un formulario o una pantalla de reglas desde una Tarjeta Adaptable, que es un diseño JSON. El jugador puede presionar un botón, y tu devolución de llamada recibe el resultado.
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)Mantén las tarjetas simples: texto, una imagen y uno o dos botones. La devolución de llamada aún necesita terminar en done.
Lista de verificación
| Síntoma | Solución |
|---|---|
| Jugador atrapado en la conexión | Una ruta nunca llama a deferrals.done; añade el else faltante |
update o done no tiene efecto |
Llama a Wait(0) justo después de deferrals.defer() |
| Jugador incorrecto afectado | Guarda local src = source primero |
| Jugador dejado entrar antes de que termine la comprobación | Olvidaste deferrals.defer() |
| Rechazar con un mensaje | deferrals.done('reason') |
| Aceptar al jugador | deferrals.done() sin argumento |
| Cuelgue solo con muchos scripts | El manejador playerConnecting de otro recurso es lento o está roto |
Respuestas rápidas
¿Por qué necesito Wait(0) después de deferrals.defer()?
El aplazamiento solo está activo una vez que el motor ha avanzado un tick. Llamar a deferrals.update o deferrals.done en el mismo tick puede ignorarse, así que antes cede una vez con Wait(0).
¿Cómo rechazo a un jugador?
Llama a deferrals.done('tu razón') con una cadena. El jugador ve el texto y no se le permite entrar. Llamar a deferrals.done() sin argumento los deja conectar.
¿Qué sucede si nunca llamo a deferrals.done?
El jugador permanece en la pantalla de conexión hasta que la conexión agota el tiempo. Cada ruta de código en tu manejador debe terminar en deferrals.done.

