nil değerine erişme denemesi (yerel 'xPlayer'): ESX düzeltmesi

xPlayer ESX betiğinizde nil mi? ESX.GetPlayerFromId(source) neden nil döndürüyor: oyuncu yüklenmedi, source Wait'ten sonra kayboldu, string kimlikleri, playerDropped. Koruma desenleriyle.

Sunucu konsolunda gösterilir:

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 o kimlik için hiçbir oyuncu bulamadı ve nil döndürdü, ve sonraki satır buna erişmeye çalıştı. Çağrı sorun değildir. Verdiğiniz kimlik yüklü bir ESX oyuncusu ile eşleşmiyor. İşte yaygın nedenler ve her birine karşı nasıl korunacağı.

1. Oyuncu henüz yüklenmedi

Oyuncu ESX karakterini yüklemeyi bitirmeden önce sunucuya bağlanır. O ara sırada ve çok karakterli ekran açık iken, ESX.GetPlayerFromId(source) nil döndürür.

Bu, playerJoining veya playerConnecting gibi genel FiveM olayları veya kaynağınız başladığı andan itibaren başlayan bir iş parçacığı gibi erken çalışan kodu etkiler.

Oyuncu hazır olduğunda sunucuda tetiklenen ESX olayını kullanın:

lua
AddEventHandler('esx:playerLoaded', function(playerId, xPlayer, isNew)
    print(('%s is loaded'):format(xPlayer.getName()))
end)

İstemci tarafında, aynı olay adı istemci betiğinize oyuncunun kullanılabileceğini söyler:

lua
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
    PlayerLoaded = true
end)

Betiğiniz oyuncular çevrimiçi iken yeniden başlatılabilirse, o olay onlar için zaten tetiklenmişdir. Başlangıçta, ESX'in zaten sahip olduğu oyuncular üzerinde döngü kurun:

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 Wait'ten sonra değişti

source FiveM'in çalışan olay için ayarladığı özel bir globaldir. İşleyici içinde beklemeye koyarsanız, bu arada başka bir olay çalışabilir ve source artık oyuncunuza ait olmaz.

lua
RegisterNetEvent('my_script:buy', function(item)
    Wait(500)
    local xPlayer = ESX.GetPlayerFromId(source)   -- source may be someone else, or invalid
end)

Başka bir şey olmadan önce bunu yerel olarak depolayın:

lua
RegisterNetEvent('my_script:buy', function(item)
    local src = source
    Wait(500)

    local xPlayer = ESX.GetPlayerFromId(src)
    if not xPlayer then return end
end)

Aynı şey bir geri çağırı içinde veya source ilettiğiniz bir işlev içinde geçerlidir: global değil depolanan değeri iletin.

3. Kimlik yanlış

ESX.GetPlayerFromId çevrimiçi olan bir oyuncunun sunucu kimliğine numara olarak ihtiyaç duyar.

  • Bir string kimlik. Komut argümanları metindir. ESX.GetPlayerFromId(args[1]) nil döndürebilir, ESX.GetPlayerFromId(tonumber(args[1])) işe yarar.
  • İstemciden yanlış kimlik. İstemci tarafında, PlayerId() yerel bir indeks, sunucu kimliği değil. Sunucu kimliği GetPlayerServerId(PlayerId()) dir. Yanlışını göndermek var olmayan bir oyuncu veya yanlış oyuncu verir.
  • Konsol. Sunucu konsolunda yazılan bir komutun source değeri 0 dir, bu bir oyuncu değildir.
  • Kimlik araması aracılığıyla bir kimlik. ESX.GetPlayerFromIdentifier(identifier) sadece çevrimiçi oyuncuları bulur. Çevrimdışı bir oyuncu için, bunun yerine veritabanını okuyun, bkz. oxmysql sorguları rehberi.
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)

Önemli bir şey için istemciden gönderilen bir kimliğe güvenmeyin. source kullanın, bu sunucu olaylarını güvenli hale getirme kısmında açıklandığı üzere sahte yapılamaz.

4. Oyuncu ayrıldı

Oyuncu kodunuz beklerken ayrılırsa, örneğin uzun bir döngü veya zamanlayıcı içinde, GetPlayerFromId sonunda çalıştığında nil döndürür. Bu normal bir durumdur, bir hata değildir ve aynı korumaya ihtiyaç duyar.

playerDropped içinde durum daha az açıktır, çünkü ESX da bu olayı işleyip oyuncuyu kaydetmek ve kaldırmak için işler ve işleyicilerin sırası güvenilecek bir şey değildir. ESX Legacy da kendi esx:playerDropped olayını tetikler, ancak sürümünüzün bunu içerip içermediğini kontrol edin. En sağlam yaklaşım, oyuncu çevrimiçi iken verileri kendiniz tutmaktır:

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)

Koruma deseni

Bir oyuncu kullanan her sunucu işleyiciyi aynı şekilde başlatın:

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)

Aynı kontrol ESX.RegisterServerCallback ile kaydedilen sunucu geri çağırıları içine gider, ilk argüman oyuncunun kaynağıdır:

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)

İstemci tarafını da kontrol edin: geri çağırıdan nil cevabını kabul etmelidir.

İpucu: hata bunun yerine attempt to index a nil value (global 'ESX') ise, sorun ESX nesnesidir, oyuncu değil. Bkz. ESX nil: esx:getSharedObject düzeltme.

Kontrol Listesi

Belirtisi Düzeltme
Oyuncu katılır katılmaz nil playerJoining yerine esx:playerLoaded beklemeyi yapın
Wait den sonra nil Önce local src = source depolayın ve src kullanın
Komuttan nil Argümanı tonumber ile dönüştürün; source konsolda 0 dir
İstemci tarafından gönderilen kimlikle nil source kullanın; istemci tarafında sunucu kimliği GetPlayerServerId(PlayerId())
playerDropped içinde nil Oyuncu çevrimiçi iken ihtiyaç duyduğunuz verileri kendi tablonuzda tutun
Çevrimdışı oyuncu için nil GetPlayerFromId sadece çevrimiçi oyuncuları bulur; veritabanını sorgulayın
Herhangi bir nil İşleyicinin üstüne if not xPlayer then return end ekleyin

Kısa yanıtlar

xPlayer neden ESX'de nil?

ESX.GetPlayerFromId(source) ESX'in o kimlik için yüklü oyuncu olmadığında nil döndürür. Oyuncu henüz yüklenmemiş olabilir, kimlik yanlış veya string olabilir veya oyuncu zaten ayrılmış olabilir.

Her olayda xPlayer kontrol etmeliyim?

Evet. Herhangi bir sunucu olayı yüklenmemiş bir oyuncu tarafından tetiklenebilir, bu nedenle işleyiciyi if not xPlayer then return end ile başlatın. Hiçbir şeye mal olmaz ve hatayı engeller.

playerDropped içinde xPlayer kullanabilir miyim?

Buna güvenmek güvenli değildir, çünkü ESX oyuncuyu zaten kaldırmış olabilir. Oyuncu çevrimiçi iken ihtiyaç duyduğunuz verileri kendi tablonuzda tutun ve oradan okuyun.

Bu sorunu yaşatmayan scriptler

Shop CreatorBir dakikadan kısa sürede dükkân kurun — sahipler, çalışanlar, kasalar ve soygunlar dahil.Scripti gör →Pawn Shop Applb-phone içinde oyuncudan oyuncuya rehin pazarı.Scripti gör →Drug Dealer Applb-phone uygulaması olarak sokak satışı: bölgeler, NPC alıcılar, seviyeler ve polis ihbarları.Scripti gör →

Okumaya devam edin