attempt to index a nil value (local 'xPlayer') : correction ESX
xPlayer est nil dans votre script ESX ? Pourquoi ESX.GetPlayerFromId(source) retourne nil : joueur non chargé, source perdu après Wait, ids en string, playerDropped. Avec des modèles de garde.
La console serveur montre :
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'a trouvé aucun joueur pour cet id et a retourné nil, et la ligne suivante l'a indexé. L'appel est correct. L'id que vous lui avez donné ne correspond pas à un joueur ESX chargé. Voici les raisons habituelles et comment vous protéger contre chacune.
1. Le joueur n'est pas encore chargé
Un joueur est connecté au serveur avant que ESX ait fini de charger son personnage. Pendant ce laps de temps, et pendant qu'un écran multi-caractères est ouvert, ESX.GetPlayerFromId(source) retourne nil.
Cela affecte le code qui s'exécute tôt, comme les événements FiveM génériques playerJoining ou playerConnecting, ou un thread qui démarre au moment où votre ressource démarre.
Utilisez l'événement ESX qui se déclenche quand le joueur est prêt, sur le serveur :
AddEventHandler('esx:playerLoaded', function(playerId, xPlayer, isNew)
print(('%s is loaded'):format(xPlayer.getName()))
end)Sur le client, le même nom d'événement dit à votre script client que le joueur peut être utilisé :
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
PlayerLoaded = true
end)Si votre script peut être redémarré pendant que les joueurs sont en ligne, cet événement a déjà été déclenché pour eux. Au démarrage, bouclez sur les joueurs qu'ESX a déjà :
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 changé après un Wait
source est une globale spéciale que FiveM définit pour l'événement en cours d'exécution. Si vous attendez à l'intérieur du gestionnaire, un autre événement peut s'exécuter entre-temps et source n'appartient plus à votre joueur.
RegisterNetEvent('my_script:buy', function(item)
Wait(500)
local xPlayer = ESX.GetPlayerFromId(source) -- source may be someone else, or invalid
end)Stockez-le dans une locale avant toute autre chose :
RegisterNetEvent('my_script:buy', function(item)
local src = source
Wait(500)
local xPlayer = ESX.GetPlayerFromId(src)
if not xPlayer then return end
end)La même chose s'applique à l'intérieur d'un rappel ou d'une fonction à laquelle vous passez source plus tard : passez la valeur stockée, pas la globale.
3. L'id est mauvais
ESX.GetPlayerFromId a besoin de l'id serveur en tant que nombre d'un joueur qui est en ligne.
- Un id en string. Les arguments de commande sont du texte.
ESX.GetPlayerFromId(args[1])peut retourner nil oùESX.GetPlayerFromId(tonumber(args[1]))fonctionne. - Le mauvais id du client. Sur le client,
PlayerId()est un index local, pas l'id serveur. L'id serveur estGetPlayerServerId(PlayerId()). Envoyer le mauvais donne un joueur qui n'existe pas, ou le mauvais joueur. - La console. Une commande tapée dans la console serveur a
sourceégal à0, ce qui n'est pas un joueur. - Un id d'une recherche d'identificateur.
ESX.GetPlayerFromIdentifier(identifier)ne trouve que les joueurs qui sont en ligne. Pour un joueur hors ligne, lisez la base de données à la place, voir le guide des requêtes 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)Ne faites pas confiance à un id envoyé du client pour quelque chose qui importe. Utilisez source, qui ne peut pas être falsifié, comme expliqué dans Sécuriser les événements serveur.
4. Le joueur est parti
Si le joueur se déconnecte pendant que votre code attend, par exemple dans une longue boucle ou un minuteur, GetPlayerFromId retourne nil quand il s'exécute finalement. C'est un cas normal, pas un bug, et il a besoin de la même garde.
À l'intérieur de playerDropped, la situation est moins claire, car ESX gère aussi cet événement pour enregistrer et supprimer le joueur, et l'ordre des gestionnaires n'est pas quelque chose sur lequel compter. ESX Legacy déclenche aussi son propre événement esx:playerDropped, mais vérifiez que votre version l'a. L'approche la plus robuste est de garder les données vous-même pendant que le joueur est en ligne :
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)Le modèle de garde
Commencez chaque gestionnaire serveur qui utilise un joueur de la même manière :
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)La même vérification va à l'intérieur des rappels serveur enregistrés avec ESX.RegisterServerCallback, où le premier argument est la source du joueur :
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)Vérifiez aussi le côté client : il doit accepter une réponse nil du rappel.
Astuce : si l'erreur est
attempt to index a nil value (global 'ESX')à la place, le problème est l'objet ESX, pas le joueur. Voir ESX is nil: fixing esx:getSharedObject.
Liste de vérification
| Symptôme | Correction |
|---|---|
| Nil juste après que le joueur se joint | Attendez esx:playerLoaded au lieu de playerJoining |
Nil après un Wait |
Stockez local src = source d'abord et utilisez src |
| Nil d'une commande | Convertissez l'argument avec tonumber ; source est 0 dans la console |
| Nil avec un id envoyé par le client | Utilisez source ; sur le client l'id serveur est GetPlayerServerId(PlayerId()) |
Nil dans playerDropped |
Gardez les données dont vous avez besoin dans votre propre table pendant que le joueur est en ligne |
| Nil pour un joueur hors ligne | GetPlayerFromId ne trouve que les joueurs en ligne ; interrogez la base de données |
| N'importe quel nil | Ajoutez if not xPlayer then return end en haut du gestionnaire |
Réponses rapides
Pourquoi xPlayer est-il nil dans ESX ?
ESX.GetPlayerFromId(source) retourne nil quand ESX n'a pas de joueur chargé pour cet id. Le joueur peut ne pas avoir fini de charger, l'id peut être mauvais ou une string, ou le joueur a déjà quitté.
Dois-je vérifier xPlayer dans chaque événement ?
Oui. N'importe quel événement serveur peut être déclenché avec un joueur qui n'est pas chargé, donc commencez le gestionnaire avec if not xPlayer then return end. Cela ne coûte rien et prévient l'erreur.
Puis-je utiliser xPlayer à l'intérieur de playerDropped ?
Ce n'est pas sûr de s'y fier, car ESX peut avoir déjà supprimé le joueur. Gardez les données dont vous avez besoin dans votre propre table pendant que le joueur est en ligne, et lisez-les de là.
Des scripts sans ce problème
Shop CreatorCréez un magasin en moins d’une minute — propriétaires, employés, coffres et braquages inclus.Voir le script →
Pawn Shop AppUn marché de prêt sur gage entre joueurs, directement dans lb-phone.Voir le script →
Drug Dealer AppLa vente de rue en app lb-phone : zones, acheteurs PNJ, niveaux et alertes police.Voir le script →