tentativa de indexar um valor nil (local 'xPlayer'): correção ESX
xPlayer é nil em seu script ESX? Por que ESX.GetPlayerFromId(source) retorna nil: jogador não carregado, source perdido após Wait, ids de string, playerDropped. Com padrões de proteção.
O console do servidor mostra:
SCRIPT ERROR: @my_script/server/main.lua:23: attempt to index a nil value (local 'xPlayer')local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.addMoney(100) -- xPlayer is nilESX.GetPlayerFromId não encontrou jogador para aquele id e retornou nil, e a próxima linha o indexou. A chamada está boa. O id que você deu não corresponde a um jogador ESX carregado. Aqui estão as razões usuais e como se proteger contra cada uma.
1. O jogador ainda não está carregado
Um jogador está conectado ao servidor antes do ESX ter terminado de carregar seu personagem. Durante essa lacuna, e enquanto uma tela multicaráter está aberta, ESX.GetPlayerFromId(source) retorna nil.
Isso atinge código que é executado cedo, como os eventos genéricos do FiveM playerJoining ou playerConnecting, ou uma thread que inicia no momento em que seu recurso inicia.
Use o evento ESX que dispara quando o jogador está pronto, no servidor:
AddEventHandler('esx:playerLoaded', function(playerId, xPlayer, isNew)
print(('%s is loaded'):format(xPlayer.getName()))
end)No cliente, o mesmo nome de evento diz ao seu script cliente que o jogador pode ser usado:
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
PlayerLoaded = true
end)Se seu script pode ser reiniciado enquanto os jogadores estão online, esse evento já disparou para eles. Na inicialização, faça um loop sobre os jogadores que ESX já tem:
CreateThread(function()
for _, playerId in ipairs(GetPlayers()) do
local xPlayer = ESX.GetPlayerFromId(tonumber(playerId))
if xPlayer then
-- set up the player
end
end
end)2. source mudou após um Wait
source é um global especial que FiveM define para o evento que está em execução. Se você esperar dentro do manipulador, outro evento pode rodar enquanto isso e source não pertence mais ao seu jogador.
RegisterNetEvent('my_script:buy', function(item)
Wait(500)
local xPlayer = ESX.GetPlayerFromId(source) -- source may be someone else, or invalid
end)Armazene-o em um local antes de qualquer outra coisa:
RegisterNetEvent('my_script:buy', function(item)
local src = source
Wait(500)
local xPlayer = ESX.GetPlayerFromId(src)
if not xPlayer then return end
end)O mesmo se aplica dentro de um callback ou de uma função que você passa source para depois: passe o valor armazenado, não o global.
3. O id está errado
ESX.GetPlayerFromId precisa do server id como um número de um jogador que está online.
- Um id de string. Argumentos de comando são texto.
ESX.GetPlayerFromId(args[1])pode retornar nil ondeESX.GetPlayerFromId(tonumber(args[1]))funciona. - O id errado do cliente. No cliente,
PlayerId()é um índice local, não o server id. O server id éGetPlayerServerId(PlayerId()). Enviar o errado dá um jogador que não existe ou o jogador errado. - O console. Um comando digitado no console do servidor tem
sourceigual a0, que não é um jogador. - Um id de uma busca de identificador.
ESX.GetPlayerFromIdentifier(identifier)apenas encontra jogadores que estão online. Para um jogador offline, leia o banco de dados em vez disso, veja o guia de consultas oxmysql.
RegisterCommand('givecash', function(source, args)
local target = tonumber(args[1])
local amount = tonumber(args[2])
if not target or not amount then
return print('Usage: /givecash [id] [amount]')
end
local xTarget = ESX.GetPlayerFromId(target)
if not xTarget then
return print('Player not found or not loaded')
end
xTarget.addMoney(amount)
end, true)Não confie em um id enviado do cliente para algo que importa. Use source, que não pode ser falsificado, conforme explicado em eventos de servidor seguro.
4. O jogador saiu
Se o jogador se desconectar enquanto seu código espera, por exemplo em um loop longo ou um timer, GetPlayerFromId retorna nil quando finalmente é executado. Este é um caso normal, não um bug, e precisa da mesma proteção.
Dentro de playerDropped a situação é menos clara, porque ESX também trata esse evento para salvar e remover o jogador, e a ordem dos manipuladores não é algo em que se possa contar. ESX Legacy também dispara seu próprio evento esx:playerDropped, mas verifique se sua versão o tem. A abordagem mais robusta é manter os dados você mesmo enquanto o jogador está online:
local sessions = {}
AddEventHandler('esx:playerLoaded', function(playerId, xPlayer)
sessions[playerId] = {
identifier = xPlayer.identifier,
job = xPlayer.job.name,
}
end)
AddEventHandler('playerDropped', function()
local data = sessions[source]
if data then
print(('%s left, job %s'):format(data.identifier, data.job))
sessions[source] = nil
end
end)O padrão de proteção
Inicie cada manipulador de servidor que usa um jogador da mesma forma:
RegisterNetEvent('my_script:sell', function(item, count)
local src = source
local xPlayer = ESX.GetPlayerFromId(src)
if not xPlayer then return end
-- from here xPlayer is safe to use
xPlayer.addMoney(100)
end)A mesma verificação entra dentro de callbacks de servidor registrados com ESX.RegisterServerCallback, onde o primeiro argumento é a source do jogador:
ESX.RegisterServerCallback('my_script:getJob', function(source, cb)
local xPlayer = ESX.GetPlayerFromId(source)
if not xPlayer then return cb(nil) end
cb(xPlayer.job.name)
end)Verifique também o lado do cliente: ele deve aceitar uma resposta nil do callback.
Dica: se o erro for
attempt to index a nil value (global 'ESX')em vez disso, o problema é o objeto ESX, não o jogador. Veja ESX é nil: corrigindo esx:getSharedObject.
Lista de verificação
| Sintoma | Correção |
|---|---|
| Nil logo após o jogador ingressar | Aguarde esx:playerLoaded em vez de playerJoining |
Nil após um Wait |
Armazene local src = source primeiro e use src |
| Nil de um comando | Converta o argumento com tonumber; source é 0 no console |
| Nil com um id enviado pelo cliente | Use source; no cliente o server id é GetPlayerServerId(PlayerId()) |
Nil em playerDropped |
Mantenha os dados que você precisa em sua própria tabela enquanto o jogador está online |
| Nil para um jogador offline | GetPlayerFromId apenas encontra jogadores online; consulte o banco de dados |
| Qualquer nil | Adicione if not xPlayer then return end no topo do manipulador |
Respostas rápidas
Por que xPlayer é nil no ESX?
ESX.GetPlayerFromId(source) retorna nil quando ESX não tem um jogador carregado para aquele id. O jogador pode não ter terminado de carregar, o id pode estar errado ou ser uma string, ou o jogador já saiu.
Devo verificar xPlayer em cada evento?
Sim. Qualquer evento de servidor pode ser disparado com um jogador que não está carregado, então inicie o manipulador com if not xPlayer then return end. Custa nada e previne o erro.
Posso usar xPlayer dentro de playerDropped?
Não é seguro contar com ele, porque ESX pode já ter removido o jogador. Mantenha os dados que você precisa em sua própria tabela enquanto o jogador está online e leia-os de lá.
Scripts sem esse problema
Shop CreatorMonte uma loja em menos de um minuto — donos, funcionários, cofres e assaltos inclusos.Ver script →
Pawn Shop AppUma casa de penhores entre jogadores dentro do lb-phone.Ver script →
Drug Dealer AppVendas na rua como app do lb-phone: zonas, compradores NPC, níveis e alertas para a polícia.Ver script →