attempt to index a nil value (local 'Player'): исправление QBCore и QBox
Player равен nil в вашем скрипте QBCore или QBox? Почему QBCore.Functions.GetPlayer(source) возвращает nil, поиски citizenid только для онлайн игроков и безопасные защитные паттерны.
В консоли сервера:
SCRIPT ERROR: @my_script/server/main.lua:27: attempt to index a nil value (local 'Player')local Player = QBCore.Functions.GetPlayer(source)
Player.Functions.AddMoney('cash', 100) -- Player is nilGetPlayer не нашел загруженного игрока для этого id и вернул nil, и следующая строка проиндексировала его. Причины отражают случай ESX в ESX xPlayer is nil, с особенностями QBCore и QBox.
1. Игрок еще не загружен
Игрок может быть подключен до того, как QBCore загрузит его персонажа, особенно пока открыт экран multicharacter. До выбора персонажа GetPlayer(source) возвращает nil.
Код, который запускается на playerJoining или из потока, который начинается немедленно, попадает в это. Дождитесь события QBCore, которое срабатывает, когда игрок готов. На сервере:
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
print(Player.PlayerData.citizenid .. ' is loaded')
end)На клиенте соответствующее событие — это QBCore:Client:OnPlayerLoaded:
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
PlayerLoaded = true
end)Если ваш ресурс перезагружается, пока игроки онлайн, эти события не срабатывают снова для них. При запуске, переберите игроков, которые уже загружены:
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 внутри обработчика, другое событие может запуститься и изменить его.
RegisterNetEvent('my_script:sell', function()
Wait(1000)
local Player = QBCore.Functions.GetPlayer(source) -- may be wrong, or nil
end)Сохраните его первым:
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, который уже ушел. Игрок отключился, пока ваш код ждал.
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:
local Player = QBCore.Functions.GetPlayerByCitizenId(citizenid)Это работает только пока этот игрок онлайн и загружен. Для оффлайн игрока это возвращает nil, что не является ошибкой в ваших данных. Прочитайте базу данных для оффлайн игроков:
local row = MySQL.single.await('SELECT charinfo, job FROM players WHERE citizenid = ?', { citizenid })
if row then
local charinfo = json.decode(row.charinfo)
endcharinfo хранится как текст JSON, поэтому декодируйте его безопасно, смотрите ошибки FiveM json.decode. Запись оффлайн игроку — это другая задача: изменяйте строку базы данных только если игрок действительно оффлайн, или следующее сохранение с сервера перезапишет ваше изменение.
5. На QBox
Ядро QBox — это qbx_core. Он держит слой совместимости, поэтому QBCore.Functions.GetPlayer все еще работает в скриптах, написанных для QBCore. Для нового кода используйте его экспорты, которые возвращают тот же вид объекта игрока или nil:
local player = exports.qbx_core:GetPlayer(source)
if not player then return end
print(player.PlayerData.citizenid)Для онлайн игрока по citizenid:
local player = exports.qbx_core:GetPlayerByCitizenId(citizenid)Правила этой статьи применяются одинаково: без игрока до загрузки персонажа, сохраняйте source перед Wait и ожидайте nil для оффлайн игрока. Если вы перемещаете скрипт между ядрами, смотрите конвертирование скрипта QBCore в QBox.
6. playerDropped
Когда игрок уходит, qb-core сохраняет и удаляет игрока самостоятельно, и порядок обработчиков для playerDropped не то, на что следует полагаться. В вашем собственном обработчике playerDropped, GetPlayer(source) уже может быть nil. Сохраняйте то, что вам нужно, в своей собственной таблице, пока игрок онлайн:
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)Защитный паттерн
Начните каждый обработчик сервера, который нуждается в игроке, одинаково:
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.Смотреть скрипт →