attempt to index a nil value (local 'xPlayer'): correzione ESX
xPlayer è nil nel tuo script ESX? Perché ESX.GetPlayerFromId(source) restituisce nil: giocatore non caricato, source perso dopo Wait, id string, playerDropped. Con modelli di protezione.
La console del server 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 non ha trovato un giocatore per quell'id e ha restituito nil, e la riga successiva l'ha indicizzato. La chiamata va bene. L'id che gli hai dato non corrisponde a un giocatore ESX caricato. Ecco le solite ragioni e come proteggere se stesso da ognuna.
1. Il giocatore non è ancora caricato
Un giocatore è connesso al server prima che ESX abbia finito di caricare il suo personaggio. Durante quel gap, e mentre uno schermo multicarattere è aperto, ESX.GetPlayerFromId(source) restituisce nil.
Questo colpisce il codice che viene eseguito in anticipo, come gli eventi FiveM generici playerJoining o playerConnecting, o un thread che si avvia nel momento in cui la tua risorsa si avvia.
Usa l'evento ESX che si attiva quando il giocatore è pronto, sul server:
AddEventHandler('esx:playerLoaded', function(playerId, xPlayer, isNew)
print(('%s is loaded'):format(xPlayer.getName()))
end)Sul client, lo stesso nome di evento dice al tuo script client che il giocatore può essere utilizzato:
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
PlayerLoaded = true
end)Se il tuo script può essere riavviato mentre i giocatori sono online, quell'evento ha già sparato per loro. All'avvio, scorre i giocatori che ESX ha già:
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 è cambiato dopo un Wait
source è un globale speciale che FiveM imposta per l'evento che è in esecuzione. Se aspetti dentro il handler, un altro evento può essere eseguito nel frattempo e source non appartiene più al tuo giocatore.
RegisterNetEvent('my_script:buy', function(item)
Wait(500)
local xPlayer = ESX.GetPlayerFromId(source) -- source may be someone else, or invalid
end)Salvalo in una variabile locale prima di tutto il resto:
RegisterNetEvent('my_script:buy', function(item)
local src = source
Wait(500)
local xPlayer = ESX.GetPlayerFromId(src)
if not xPlayer then return end
end)Lo stesso vale dentro un callback o una funzione a cui passi source più tardi: passa il valore memorizzato, non il globale.
3. L'id è sbagliato
ESX.GetPlayerFromId ha bisogno dell'id del server come numero di un giocatore che è online.
- Un id string. Gli argomenti del comando sono testo.
ESX.GetPlayerFromId(args[1])può restituire nil doveESX.GetPlayerFromId(tonumber(args[1]))funziona. - L'id sbagliato dal client. Sul client,
PlayerId()è un indice locale, non l'id del server. L'id del server èGetPlayerServerId(PlayerId()). Inviare quello sbagliato dà un giocatore che non esiste, o il giocatore sbagliato. - La console. Un comando digitato nella console del server ha
sourceuguale a0, che non è un giocatore. - Un id da una ricerca di identificatore.
ESX.GetPlayerFromIdentifier(identifier)trova solo giocatori che sono online. Per un giocatore offline, leggi invece il database, vedi la guida alle query 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)Non fidarti di un id inviato dal client per qualcosa che importa. Usa source, che non può essere falsificato, come spiegato in protezione degli eventi del server.
4. Il giocatore se ne è andato
Se il giocatore si disconnette mentre il tuo codice aspetta, ad esempio in un ciclo lungo o un timer, GetPlayerFromId restituisce nil quando finalmente viene eseguito. È un caso normale, non un bug, e ha bisogno della stessa protezione.
Dentro playerDropped la situazione è meno chiara, perché ESX gestisce anche quell'evento per salvare e rimuovere il giocatore, e l'ordine dei handler non è qualcosa su cui fare affidamento. ESX Legacy attiva anche il suo proprio evento esx:playerDropped, ma controlla che la tua versione lo abbia. L'approccio più robusto è mantenere i dati tu stesso mentre il giocatore è 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)Il modello di protezione
Inizia ogni handler del server che usa un giocatore nello stesso modo:
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)Lo stesso controllo va dentro i callback del server registrati con ESX.RegisterServerCallback, dove il primo argomento è la source del giocatore:
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)Controlla anche il lato client: deve accettare una risposta nil dal callback.
Suggerimento: se l'errore è
attempt to index a nil value (global 'ESX')invece, il problema è l'oggetto ESX, non il giocatore. Vedi ESX è nil: correzione di esx:getSharedObject.
Lista di controllo
| Sintomo | Soluzione |
|---|---|
| Nil subito dopo che il giocatore si unisce | Aspetta invece esx:playerLoaded invece di playerJoining |
Nil dopo un Wait |
Memorizza local src = source prima e usa src |
| Nil da un comando | Converti l'argomento con tonumber; source è 0 nella console |
| Nil con un id inviato dal client | Usa source; sul client l'id del server è GetPlayerServerId(PlayerId()) |
Nil in playerDropped |
Mantieni i dati di cui hai bisogno nella tua tabella mentre il giocatore è online |
| Nil per un giocatore offline | GetPlayerFromId trova solo giocatori online; interroga il database |
| Qualsiasi nil | Aggiungi if not xPlayer then return end in cima al handler |
Risposte rapide
Perché xPlayer è nil in ESX?
ESX.GetPlayerFromId(source) restituisce nil quando ESX non ha un giocatore caricato per quell'id. Il giocatore potrebbe non aver finito di caricarsi, l'id potrebbe essere sbagliato o una stringa, oppure il giocatore se n'è già andato.
Devo controllare xPlayer in ogni evento?
Sì. Qualsiasi evento server può essere attivato con un giocatore che non è caricato, quindi inizia il handler con if not xPlayer then return end. Non costa nulla e previene l'errore.
Posso usare xPlayer dentro playerDropped?
Non è sicuro fare affidamento su di esso, perché ESX potrebbe aver già rimosso il giocatore. Mantieni i dati di cui hai bisogno nella tua tabella mentre il giocatore è online, e leggili da lì.
Script senza questo problema
Shop CreatorCrea un negozio in meno di un minuto — proprietari, dipendenti, casseforti e rapine inclusi.Vedi script →
Pawn Shop AppUn banco dei pegni tra giocatori dentro lb-phone.Vedi script →
Drug Dealer AppSpaccio di strada come app per lb-phone: zone, acquirenti NPC, livelli e allerte alla polizia.Vedi script →