attempt to index a nil value (local 'xPlayer'): naprawa ESX
xPlayer to nil w twoim skrypcie ESX? Dlaczego ESX.GetPlayerFromId(source) zwraca nil: gracz się nie załadował, source zgubiony po Wait, string ids, playerDropped. Z wzorami ochronnymi.
Konsola serwera pokazuje:
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 nie znalazł gracza dla tego id i zwrócił nil, a następna linia go zindeksowała. Wezwanie jest w porządku. Id, które mu dałeś, nie pasuje do załadowanego gracza ESX. Oto zwykłe powody i jak się przed każdym chronić.
1. Gracz nie jest jeszcze załadowany
Gracz jest połączony z serwerem, zanim ESX zakończy ładowanie jego postaci. Podczas tej luki i gdy ekran multicharacter jest otwarty, ESX.GetPlayerFromId(source) zwraca nil.
To trafia kod, który uruchamia się wcześnie, takie jak generyczne zdarzenia FiveM playerJoining lub playerConnecting, lub thread, który uruchamia się w momencie startu zasobu.
Użyj zdarzenia ESX, które uruchamia się, gdy gracz jest gotowy, na serwerze:
AddEventHandler('esx:playerLoaded', function(playerId, xPlayer, isNew)
print(('%s is loaded'):format(xPlayer.getName()))
end)Na kliencie, ta sama nazwa zdarzenia mówi twojemu skryptowi klienta, że gracz może być użyty:
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
PlayerLoaded = true
end)Jeśli twój skrypt można uruchomić ponownie, gdy gracze są online, to zdarzenie już dla nich się uruchomiło. Na start, pętluj po graczach, których ESX już ma:
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 zmienił się po Wait
source to specjalna globala, którą FiveM ustawia dla zdarzenia, które działa. Jeśli czekasz wewnątrz handlera, inne zdarzenie może uruchomić się tymczasem i source nie należy już do twojego gracza.
RegisterNetEvent('my_script:buy', function(item)
Wait(500)
local xPlayer = ESX.GetPlayerFromId(source) -- source may be someone else, or invalid
end)Przechowaj go w lokalnym przed wszystkim innym:
RegisterNetEvent('my_script:buy', function(item)
local src = source
Wait(500)
local xPlayer = ESX.GetPlayerFromId(src)
if not xPlayer then return end
end)To samo dotyczy wewnątrz callback lub funkcji, do której transmittujesz source później: transmituj przechowaną wartość, nie globalę.
3. Id jest zły
ESX.GetPlayerFromId potrzebuje id serwera jako liczby gracza, który jest online.
- Id string. Argumenty poleceń to tekst.
ESX.GetPlayerFromId(args[1])może zwrócić nil, gdzieESX.GetPlayerFromId(tonumber(args[1]))działa. - Zły id z klienta. Na kliencie,
PlayerId()to lokalny indeks, a nie id serwera. Id serwera toGetPlayerServerId(PlayerId()). Wysyłanie złego daje gracza, który nie istnieje, lub złego gracza. - Konsola. Polecenie wpisane w konsoli serwera ma
sourcerówny0, co nie jest graczem. - Id z wyszukiwania identyfikatora.
ESX.GetPlayerFromIdentifier(identifier)znajduje tylko graczy, którzy są online. Dla gracza offline przeczytaj bazę danych zamiast tego, patrz oxmysql queries guide.
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)Nie ufaj id wysłanym z klienta dla czegoś, co ma znaczenie. Używaj source, którego nie można sfałszować, jak wyjaśniono w securing server events.
4. Gracz wyjechał
Jeśli gracz rozłączy się, gdy twój kod czeka, na przykład w długiej pętli lub timerze, GetPlayerFromId zwraca nil, gdy w końcu się uruchomi. To normalny przypadek, nie bug, i potrzebuje tej samej ochrony.
Wewnątrz playerDropped sytuacja jest mniej jasna, ponieważ ESX również obsługuje to zdarzenie, aby zapisać i usunąć gracza, a kolejność handlerów to nie coś, na czym można polegać. ESX Legacy również wyzwala swoje własne zdarzenie esx:playerDropped, ale sprawdź, czy twoja wersja go ma. Najbardziej niezawodne podejście to przechowywanie danych samemu podczas gdy gracz jest 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)Wzór ochronny
Rozpocznij każdy handler serwera, który używa gracza, w ten sam sposób:
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)Ta sama kontrola wchodzi w server callbacki zarejestrowane z ESX.RegisterServerCallback, gdzie pierwszy argument to źródło gracza:
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)Sprawdzaj również po stronie klienta: musi zaakceptować odpowiedź nil z callback.
Porada: jeśli błąd to
attempt to index a nil value (global 'ESX')zamiast, problem to obiekt ESX, a nie gracz. Patrz ESX is nil: fixing esx:getSharedObject.
Lista kontrolna
| Objaw | Naprawa |
|---|---|
| Nil zaraz po dołączeniu gracza | Czekaj zamiast tego esx:playerLoaded playerJoining |
Nil po Wait |
Przechowaj local src = source najpierw i używaj src |
| Nil z polecenia | Konwertuj argument z tonumber; source to 0 w konsoli |
| Nil z id wysłanym przez klienta | Używaj source; na kliencie id serwera to GetPlayerServerId(PlayerId()) |
Nil w playerDropped |
Trzymaj dane, których potrzebujesz w swojej własnej tabeli podczas gdy gracz jest online |
| Nil dla gracza offline | GetPlayerFromId znajduje tylko online graczy; zapytaj bazę danych |
| Każde nil | Dodaj if not xPlayer then return end na górze handlera |
Szybkie odpowiedzi
Dlaczego xPlayer to nil w ESX?
ESX.GetPlayerFromId(source) zwraca nil, gdy ESX nie ma załadowanego gracza dla tego id. Gracz może się nie skończyć ładować, id może być zły lub string, lub gracz już wyjechał.
Czy powinienem sprawdzać xPlayer w każdym zdarzeniu?
Tak. Każde zdarzenie serwera może być wyzwolone z graczem, który nie jest załadowany, więc rozpocznij handler z if not xPlayer then return end. To nic nie kosztuje i zapobiega błędowi.
Czy mogę użyć xPlayer wewnątrz playerDropped?
Nie jest bezpiecznie polegać na tym, ponieważ ESX mógł już usunąć gracza. Trzymaj dane, których potrzebujesz w swojej własnej tabeli, gdy gracz jest online, i przeczytaj je stamtąd.
Skrypty bez tego problemu
Shop CreatorZbuduj sklep w niecałą minutę — właściciele, pracownicy, sejfy i napady w zestawie.Zobacz skrypt →
Pawn Shop AppLombard między graczami wewnątrz lb-phone.Zobacz skrypt →
Drug Dealer AppUliczna sprzedaż jako aplikacja lb-phone: strefy, kupcy NPC, poziomy i alerty dla policji.Zobacz skrypt →