intento de indexar un valor nil (local 'Player'): solución de QBCore y QBox

¿Player es nil en tu script de QBCore o QBox? Por qué QBCore.Functions.GetPlayer(source) devuelve nil, las búsquedas de ciudadanid solo para jugadores en línea, y los patrones de protección seguros.

En la consola del servidor:

text
SCRIPT ERROR: @my_script/server/main.lua:27: attempt to index a nil value (local 'Player')
lua
local Player = QBCore.Functions.GetPlayer(source)
Player.Functions.AddMoney('cash', 100)   -- Player is nil

GetPlayer no encontró ningún jugador cargado para ese id y devolvió nil, y la siguiente línea lo indexó. Las causas reflejan el caso de ESX en ESX xPlayer es nil, con especificidades de QBCore y QBox.

1. El jugador aún no está cargado

Un jugador puede estar conectado antes de que QBCore haya cargado su personaje, especialmente mientras está abierta una pantalla multicharacter. Hasta que se elige el personaje, GetPlayer(source) devuelve nil.

El código que se ejecuta en playerJoining, o desde un thread que comienza inmediatamente, lo golpea. Espera el evento de QBCore que se dispara cuando el jugador está listo. En el servidor:

lua
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
    print(Player.PlayerData.citizenid .. ' is loaded')
end)

En el cliente, el evento correspondiente es QBCore:Client:OnPlayerLoaded:

lua
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
    PlayerLoaded = true
end)

Si tu recurso se reinicia mientras los jugadores están en línea, esos eventos no se disparan nuevamente para ellos. Al inicio, pasa por los jugadores que ya están cargados:

lua
CreateThread(function()
    for _, src in pairs(QBCore.Functions.GetPlayers()) do
        local Player = QBCore.Functions.GetPlayer(src)
        if Player then
            -- set up the player
        end
    end
end)

2. source cambió después de un Wait

source es una variable global que FiveM establece para el evento en ejecución. Si llamas a Wait dentro del manejador, otro evento puede ejecutarse y cambiarlo.

lua
RegisterNetEvent('my_script:sell', function()
    Wait(1000)
    local Player = QBCore.Functions.GetPlayer(source)   -- may be wrong, or nil
end)

Guárdalo primero:

lua
RegisterNetEvent('my_script:sell', function()
    local src = source
    Wait(1000)

    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end
end)

3. El id es incorrecto

GetPlayer espera el id de servidor de un jugador en línea, como un número.

  • Una cadena de un comando. Los argumentos son texto, así que conviértelos: QBCore.Functions.GetPlayer(tonumber(args[1])).
  • El id incorrecto del cliente. En el cliente, PlayerId() es un índice local. El id del servidor es GetPlayerServerId(PlayerId()). Mejor, ignora los ids enviados por el cliente y usa source, consulta asegurar eventos del servidor.
  • La consola. Un comando escrito en la consola del servidor tiene source igual a 0, que no es un jugador.
  • Un id que se ha ido. El jugador se desconectó mientras tu código esperaba.
lua
QBCore.Commands.Add('givecash', 'Give cash', { { name = 'id', help = 'Player id' }, { name = 'amount', help = 'Amount' } }, true, function(source, args)
    local target = QBCore.Functions.GetPlayer(tonumber(args[1]))
    local amount = tonumber(args[2])
    if not target or not amount then return end

    target.Functions.AddMoney('cash', amount)
end, 'admin')

4. Las búsquedas de ciudadanid solo encuentran jugadores en línea

Muchos scripts almacenan el ciudadanid en lugar del source, porque el source cambia en cada sesión. Para obtener el jugador de un ciudadanid:

lua
local Player = QBCore.Functions.GetPlayerByCitizenId(citizenid)

Esto solo funciona mientras ese jugador esté en línea y cargado. Para un jugador que está sin conexión devuelve nil, que no es un error en tus datos. Lee la base de datos para jugadores sin conexión:

lua
local row = MySQL.single.await('SELECT charinfo, job FROM players WHERE citizenid = ?', { citizenid })
if row then
    local charinfo = json.decode(row.charinfo)
end

charinfo se almacena como texto JSON, así que decodifícalo de forma segura, consulta errores de FiveM json.decode. Escribir en un jugador sin conexión es una tarea diferente: cambia la fila de la base de datos solo si el jugador realmente está sin conexión, o el siguiente guardado del servidor sobrescribirá tu cambio.

5. En QBox

El núcleo de QBox es qbx_core. Mantiene una capa de compatibilidad, así que QBCore.Functions.GetPlayer aún funciona en scripts escritos para QBCore. Para código nuevo, usa sus exportaciones, que devuelven el mismo tipo de objeto de jugador o nil:

lua
local player = exports.qbx_core:GetPlayer(source)
if not player then return end

print(player.PlayerData.citizenid)

Para un jugador en línea por ciudadanid:

lua
local player = exports.qbx_core:GetPlayerByCitizenId(citizenid)

Las reglas de este artículo se aplican de la misma manera: ningún jugador hasta que se cargue el personaje, guarda source antes de un Wait, y espera nil para un jugador sin conexión. Si estás moviendo un script entre los núcleos, consulta convertir un script de QBCore a QBox.

6. playerDropped

Cuando un jugador se va, qb-core guarda y elimina al jugador por su cuenta, y el orden de los manejadores de playerDropped no es algo en lo que confiar. En tu propio manejador playerDropped, GetPlayer(source) ya puede ser nil. Mantén lo que necesites en tu propia tabla mientras el jugador está en línea:

lua
local sessions = {}

AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
    sessions[Player.PlayerData.source] = Player.PlayerData.citizenid
end)

AddEventHandler('playerDropped', function()
    local citizenid = sessions[source]
    sessions[source] = nil
    if citizenid then
        print(citizenid .. ' left')
    end
end)

El patrón de protección

Comienza cada manejador del servidor que necesita un jugador de la misma manera:

lua
RegisterNetEvent('my_script:action', function()
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    -- Player is safe to use here
end)

Consejo: si el error es sobre QBCore siendo nil, el problema es el objeto del núcleo, no el jugador. Consulta intento de indexar un valor nil (global 'QBCore').

Lista de verificación

Síntoma Solución
Nil justo después de que el jugador se une Espera QBCore:Server:PlayerLoaded en lugar de playerJoining
Nil después de un Wait Guarda local src = source y usa src
Nil de un comando Convierte el argumento con tonumber; el source de consola es 0
GetPlayerByCitizenId devuelve nil El jugador está sin conexión; consulta la tabla players
Servidor QBox Usa exports.qbx_core:GetPlayer(source) y comprueba nil
Nil en playerDropped Mantén los datos que necesitas en tu propia tabla mientras el jugador está en línea
Cualquier nil Añade if not Player then return end en la parte superior del manejador

Respuestas rápidas

¿Por qué QBCore.Functions.GetPlayer devuelve nil?

No hay ningún jugador cargado con ese id. El jugador aún puede estar cargando, el id puede ser incorrecto o perderse después de un Wait, o el jugador ya se ha ido.

¿GetPlayerByCitizenId funciona para jugadores sin conexión?

No. Solo encuentra jugadores que están en línea. Para un jugador sin conexión, lee la tabla players en la base de datos en su lugar.

¿Cuál es el equivalente de QBox de QBCore.Functions.GetPlayer?

exports.qbx_core:GetPlayer(source). Devuelve el objeto del jugador, o nil cuando no hay ningún jugador cargado para ese id.

Scripts que evitan este problema

Advanced BoostingBoosting de vehículos desde una tablet: contratos de clase D a S+, crews y cola en vivo.Ver script →Drug Dealer AppVenta callejera como app de lb-phone: zonas, compradores NPC, niveles y avisos a la policía.Ver script →Pawn Shop AppUn mercado de empeños entre jugadores dentro de lb-phone.Ver script →

Sigue leyendo