attempt to index a nil value (local 'Player'): исправление QBCore и QBox

Player равен nil в вашем скрипте QBCore или QBox? Почему QBCore.Functions.GetPlayer(source) возвращает nil, поиски citizenid только для онлайн игроков и безопасные защитные паттерны.

В консоли сервера:

text
SCRIPT ERROR: @my_script/server/main.lua:27: attempt to index a nil value (local 'Player')
lua
local Player = QBCore.Functions.GetPlayer(source)
Player.Functions.AddMoney('cash', 100)   -- Player is nil

GetPlayer не нашел загруженного игрока для этого id и вернул nil, и следующая строка проиндексировала его. Причины отражают случай ESX в ESX xPlayer is nil, с особенностями QBCore и QBox.

1. Игрок еще не загружен

Игрок может быть подключен до того, как QBCore загрузит его персонажа, особенно пока открыт экран multicharacter. До выбора персонажа GetPlayer(source) возвращает nil.

Код, который запускается на playerJoining или из потока, который начинается немедленно, попадает в это. Дождитесь события QBCore, которое срабатывает, когда игрок готов. На сервере:

lua
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
    print(Player.PlayerData.citizenid .. ' is loaded')
end)

На клиенте соответствующее событие — это QBCore:Client:OnPlayerLoaded:

lua
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
    PlayerLoaded = true
end)

Если ваш ресурс перезагружается, пока игроки онлайн, эти события не срабатывают снова для них. При запуске, переберите игроков, которые уже загружены:

lua
CreateThread(function()
    for _, src in pairs(QBCore.Functions.GetPlayers()) do
        local Player = QBCore.Functions.GetPlayer(src)
        if Player then
            -- set up the player
        end
    end
end)

2. source изменился после Wait

source — это глобальная переменная, которую FiveM устанавливает для запущенного события. Если вы вызовете Wait внутри обработчика, другое событие может запуститься и изменить его.

lua
RegisterNetEvent('my_script:sell', function()
    Wait(1000)
    local Player = QBCore.Functions.GetPlayer(source)   -- may be wrong, or nil
end)

Сохраните его первым:

lua
RegisterNetEvent('my_script:sell', function()
    local src = source
    Wait(1000)

    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end
end)

3. ID неправильный

GetPlayer ожидает серверный id онлайн игрока, как число.

  • Строка из команды. Аргументы — это текст, поэтому преобразуйте их: QBCore.Functions.GetPlayer(tonumber(args[1])).
  • Неправильный id от клиента. На клиенте PlayerId() — это локальный индекс. Серверный id — это GetPlayerServerId(PlayerId()). Лучше, игнорируйте ids, отправленные клиентом, и используйте source, смотрите защита событий сервера.
  • Консоль. Команда, введенная в консоль сервера, имеет source равный 0, это не игрок.
  • Id, который уже ушел. Игрок отключился, пока ваш код ждал.
lua
QBCore.Commands.Add('givecash', 'Give cash', { { name = 'id', help = 'Player id' }, { name = 'amount', help = 'Amount' } }, true, function(source, args)
    local target = QBCore.Functions.GetPlayer(tonumber(args[1]))
    local amount = tonumber(args[2])
    if not target or not amount then return end

    target.Functions.AddMoney('cash', amount)
end, 'admin')

4. Поиски citizenid находят только онлайн игроков

Многие скрипты хранят citizenid вместо source, потому что source изменяется при каждой сессии. Чтобы получить игрока из citizenid:

lua
local Player = QBCore.Functions.GetPlayerByCitizenId(citizenid)

Это работает только пока этот игрок онлайн и загружен. Для оффлайн игрока это возвращает nil, что не является ошибкой в ваших данных. Прочитайте базу данных для оффлайн игроков:

lua
local row = MySQL.single.await('SELECT charinfo, job FROM players WHERE citizenid = ?', { citizenid })
if row then
    local charinfo = json.decode(row.charinfo)
end

charinfo хранится как текст JSON, поэтому декодируйте его безопасно, смотрите ошибки FiveM json.decode. Запись оффлайн игроку — это другая задача: изменяйте строку базы данных только если игрок действительно оффлайн, или следующее сохранение с сервера перезапишет ваше изменение.

5. На QBox

Ядро QBox — это qbx_core. Он держит слой совместимости, поэтому QBCore.Functions.GetPlayer все еще работает в скриптах, написанных для QBCore. Для нового кода используйте его экспорты, которые возвращают тот же вид объекта игрока или nil:

lua
local player = exports.qbx_core:GetPlayer(source)
if not player then return end

print(player.PlayerData.citizenid)

Для онлайн игрока по citizenid:

lua
local player = exports.qbx_core:GetPlayerByCitizenId(citizenid)

Правила этой статьи применяются одинаково: без игрока до загрузки персонажа, сохраняйте source перед Wait и ожидайте nil для оффлайн игрока. Если вы перемещаете скрипт между ядрами, смотрите конвертирование скрипта QBCore в QBox.

6. playerDropped

Когда игрок уходит, qb-core сохраняет и удаляет игрока самостоятельно, и порядок обработчиков для playerDropped не то, на что следует полагаться. В вашем собственном обработчике playerDropped, GetPlayer(source) уже может быть nil. Сохраняйте то, что вам нужно, в своей собственной таблице, пока игрок онлайн:

lua
local sessions = {}

AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
    sessions[Player.PlayerData.source] = Player.PlayerData.citizenid
end)

AddEventHandler('playerDropped', function()
    local citizenid = sessions[source]
    sessions[source] = nil
    if citizenid then
        print(citizenid .. ' left')
    end
end)

Защитный паттерн

Начните каждый обработчик сервера, который нуждается в игроке, одинаково:

lua
RegisterNetEvent('my_script:action', function()
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    -- Player is safe to use here
end)

Совет: если ошибка о QBCore самом по себе равна nil, проблема в объекте ядра, а не в игроке. Смотрите attempt to index a nil value (global 'QBCore').

Контрольный список

Симптом Исправление
Nil сразу после присоединения игрока Дождитесь QBCore:Server:PlayerLoaded вместо playerJoining
Nil после Wait Сохраните local src = source и используйте src
Nil из команды Преобразуйте аргумент с tonumber; консоль source равна 0
GetPlayerByCitizenId возвращает nil Игрок оффлайн; запросите таблицу players
QBox сервер Используйте exports.qbx_core:GetPlayer(source) и проверьте nil
Nil в playerDropped Сохраняйте данные, которые вам нужны, в своей таблице, пока игрок онлайн
Любой nil Добавьте if not Player then return end в начало обработчика

Короткие ответы

Почему QBCore.Functions.GetPlayer возвращает nil?

Нет загруженного игрока с этим id. Игрок может быть еще в процессе загрузки, id может быть неправильным или потеряться после Wait, или игрок уже ушел.

Работает ли GetPlayerByCitizenId для оффлайн игроков?

Нет. Он находит только игроков, которые онлайн. Для оффлайн игрока прочитайте таблицу players в базе данных вместо этого.

Что является QBox эквивалентом QBCore.Functions.GetPlayer?

exports.qbx_core:GetPlayer(source). Он возвращает объект игрока или nil, когда нет загруженного игрока для этого id.

Скрипты без этой проблемы

Advanced BoostingУгон машин на заказ через планшет: контракты от класса D до S+, команды и живая очередь.Смотреть скрипт →Drug Dealer AppУличная торговля в виде приложения lb-phone: зоны, NPC-покупатели, уровни и вызовы полиции.Смотреть скрипт →Pawn Shop AppЛомбард между игроками прямо в lb-phone.Смотреть скрипт →

Читайте также