попытка сравнить число с nil / арифметика на nil значение: исправление

Исправить 'attempt to compare number with nil' и 'arithmetic on a nil value' в FiveM: невагруженные данные игрока, отсутствующие значения конфигурации, tonumber и безопасные значения по умолчанию с `or`.

Вывод консоли:

text
SCRIPT ERROR: @my_script/server/main.lua:58: attempt to compare number with nil
SCRIPT ERROR: @my_script/client/main.lua:91: attempt to perform arithmetic on a nil value (field 'money')

Строка скрипта использует значение, которое ещё не существует в сравнении или расчёте. Вот что обычно оставляет значение пустым и как сделать скрипт безопасным.

Что означает ошибка

Lua даёт вам nil для любой переменной или записи таблицы, которая не имеет значения. Сравнение (<, >, <=) или расчёт (+, -, *, /) с nil останавливает скрипт с одним из этих сообщений:

  • attempt to compare number with nil (или attempt to compare nil with number): одна сторона сравнения это nil.
  • attempt to perform arithmetic on a nil value: расчёт использует переменную nil. Когда Lua знает имя, он добавляет его, как (field 'money') или (local 'price').

Номер строки в сообщении — это строка со сравнением или суммой. nil — это один из значений на этой строке. Выведите их, чтобы увидеть, какой:

lua
print('price', price, type(price))
print('count', count, type(count))
if price > count then
    -- ...
end

1. Данные игрока ещё не загружены

Самая частая причина: скрипт читает игрока до того, как фреймворк закончил их загружать, например в первых строках файла или в потоке, который запускается сразу.

lua
-- runs too early on the client: money is nil
local money = ESX.GetPlayerData().money
if money > 100 then
    -- ...
end

Дождитесь события загрузки фреймворка и прочитайте данные оттуда:

lua
-- ESX client
local PlayerData = {}

RegisterNetEvent('esx:playerLoaded', function(xPlayer)
    PlayerData = xPlayer
end)
lua
-- QBCore client
local QBCore = exports['qb-core']:GetCoreObject()
local PlayerData = {}

RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
    PlayerData = QBCore.Functions.GetPlayerData()
end)

На сервере, объект игрока также может быть nil, когда идентификатор неправильный или игрок уехал. Проверьте это перед его использованием:

lua
local xPlayer = ESX.GetPlayerFromId(source)
if not xPlayer then return end
lua
local Player = QBCore.Functions.GetPlayer(source)
if not Player then return end

На QBox, эквивалент — это exports.qbx_core:GetPlayer(source), с той же проверкой nil.

Совет: если скрипт перезагружается, пока вы находитесь на сервере, playerLoaded уже сработало и не сработает снова. Прочитайте данные один раз, когда ресурс запускается.

2. Отсутствует значение конфигурации

Вы добавили строку в config.lua, которую не имеет скрипт, или скрипт читает ключ, который ваша более старая конфигурация не содержит. После обновления, старый config.lua — это очень частая причина.

lua
-- config.lua of the old version has no Config.Price
if playerMoney >= Config.Price then
    -- ...
end

Сравните вашу конфиг с той, которая поставляется с новой версией, и добавьте отсутствующие ключи. В вашем собственном коде дайте каждой настройке значение по умолчанию:

lua
local price = Config.Price or 100

3. Значение — это строка, а не число

Значения из базы данных, входного поля в меню или сообщения JSON часто приходят как текст. Сравнение строки с числом даёт другую ошибку (attempt to compare number with string), но преобразование, которое не удаётся, даёт вам nil, что приносит эту ошибку обратно. tonumber преобразует и возвращает nil когда не может:

lua
local amount = tonumber(data.amount)
if not amount then
    return -- not a number: stop here
end

4. Используйте or для значения по умолчанию

or возвращает правую сторону, когда левая сторона nil (или false). Это самый короткий способ сделать расчёт безопасным:

lua
local count = tonumber(data.count) or 0
local total = (item.price or 0) * count

Используйте его, где отсутствующее значение действительно может означать ноль, например, необязательный бонус. Где отсутствующее значение — это ошибка, например, цена, которая должна существовать, остановитесь с ясным сообщением вместо его скрытия:

lua
local price = Config.Items[name] and Config.Items[name].price
if not price then
    print(('no price set for %s'):format(name))
    return
end

5. Результат запроса к базе данных или обратного вызова пуст

Запрос, который не находит строку, даёт nil или пустую таблицу. Чтение столбца из неё не удаётся:

lua
local row = MySQL.single.await('SELECT balance FROM bank WHERE owner = ?', { identifier })
if row.balance > 0 then   -- row is nil when the player has no row
    -- ...
end

Проверьте строку в первую очередь:

lua
local row = MySQL.single.await('SELECT balance FROM bank WHERE owner = ?', { identifier })
local balance = row and row.balance or 0

То же самое относится к серверным обратным вызовам: ответ может быть nil, если обратный вызов не был найден или ничего не вернул. Если ваш скрипт ждёт обратного вызова, обработайте этот случай. Связанное: attempt to call a nil value — это сестра ошибка, когда функция, а не значение, отсутствует.

6. Опечатка в имени поля

xPlayer.money и xPlayer.Money — разные. Как и price и Price. Сообщение об ошибке часто называет поле: проверьте его орфографию против документации фреймворка или против таблицы, которую вы выводите с помощью print(json.encode(data)).

Чеклист

Симптом Исправление
Ошибка сразу после присоединения Дождитесь события загрузки игрока перед чтением данных игрока
xPlayer или Player это nil на сервере Проверьте if not xPlayer then return end и источник id
Ошибка после обновления скрипта Добавьте новые ключи поставляемого config.lua в ваши
Значение поступает из меню или JSON Преобразуйте с помощью tonumber(value) и проверьте, что это не nil
Значение может отсутствовать специально Используйте value or 0 как значение по умолчанию
Запрос базы данных не возвращает ничего Проверьте, что строка не nil перед чтением её столбцов
Поле это nil, даже если оно должно существовать Проверьте орфографию и заглавные буквы

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

Что означает 'attempt to compare number with nil'?

Строка сравнивает число с чем-то, что не имеет значения, типа price > 100, когда price это nil. Найдите, какая переменная это nil и почему её никогда не устанавливали.

Как дать переменной значение по умолчанию в Lua?

Используйте or: local count = tonumber(value) or 0. Если значение это nil или не число, count становится 0.

Почему это происходит только сразу после присоединения к серверу?

Скрипт читает данные игрока до того, как фреймворк их загрузил. Дождитесь события загрузки игрока или проверьте, что объект игрока не nil перед чтением из него.

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

Shop CreatorМагазин меньше чем за минуту — владельцы, сотрудники, сейфы и ограбления в комплекте.Смотреть скрипт →Pawn Shop AppЛомбард между игроками прямо в lb-phone.Смотреть скрипт →Quest CreatorВизуальный редактор квестов и диалогов с NPC — узел за узлом прямо в игре.Смотреть скрипт →

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