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:

text
SCRIPT ERROR: @my_script/server/main.lua:23: attempt to index a nil value (local 'xPlayer')
lua
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.addMoney(100)   -- xPlayer is nil

ESX.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:

lua
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:

lua
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:

lua
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.

lua
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:

lua
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, gdzie ESX.GetPlayerFromId(tonumber(args[1])) działa.
  • Zły id z klienta. Na kliencie, PlayerId() to lokalny indeks, a nie id serwera. Id serwera to GetPlayerServerId(PlayerId()). Wysyłanie złego daje gracza, który nie istnieje, lub złego gracza.
  • Konsola. Polecenie wpisane w konsoli serwera ma source równy 0, 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.
lua
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:

lua
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:

lua
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:

lua
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 →

Czytaj dalej