MTA:SA: скриптовые ошибки при запуске ресурса
Запускаешь сервер, консоль пробегает списком ресурсов — и тут вместо тишины сыпется красный текст с именем файла и номером строки. Ресурс либо не стартует вообще, либо поднимается наполовину рабочим: часть команд работает, часть молча ничего не делает. Разберём, как читать этот вывод по-человечески, как найти виновный скрипт среди десятка загруженных ресурсов, и почему иногда дело не в твоём коде, а в порядке загрузки или версии сервера.
Содержание
Формат ошибки в консоли: что MTA вообще пишет
MTA:SA не прячет ошибки Lua — она выводит их прямо в консоль сервера (и туда же, откуда её читать удобнее всего, если сервер поднят через systemd — journalctl -u mta-server -f). Стандартная строка выглядит примерно так:
WARNING: имя_ресурса/server.lua:42: bad argument #1 to 'setElementData' (element expected, got nil)
ERROR: имя_ресурса/client.lua:15: attempt to call a nil value (global 'startHud')
Разбор по частям: WARNING или ERROR — уровень серьёзности (warning ресурс обычно переживёт, error чаще всего означает, что дальнейший код в этом файле не выполнится); дальше — имя ресурса и путь к файлу внутри него; после двоеточия — номер строки; в конце — само сообщение об ошибке на английском, которое Lua генерирует при попытке выполнить некорректную операцию.
Отдельно стоят ошибки парсинга meta.xml — они не про Lua вообще, а про XML-синтаксис:
ERROR: Loading meta.xml failed: имя_ресурса/meta.xml:8: mismatched tag
Тут сразу ясно, где искать — открывай указанный файл на указанной строке. С Lua-ошибками чуть сложнее: номер строки честный, но причина не всегда очевидна с первого взгляда, особенно если ошибка — следствие того, что случилось несколькими строками раньше.
Ищем виновника: как читать стек и находить строку
Если при запуске сервера консоль проматывается быстро и ошибка мелькает среди десятков строк загрузки других ресурсов — не гадай, а изолируй проблему. Останови автозапуск подозрительного ресурса в mtaserver.conf (или просто останови его командой stop имя_ресурса в консоли, если сервер уже поднят), затем запусти вручную:
refresh
start имя_ресурса
refresh пересканирует список ресурсов на диске (полезно, если только что скачал или отредактировал файлы), а start выведет в консоль именно ошибки этого ресурса, без шума от остальных. Так гораздо проще сопоставить строку ошибки с реальным содержимым файла.
Если ошибка вылетает не сразу при старте, а в процессе — например, при выполнении команды игроком или срабатывании события, — открой сам файл на указанной строке и посмотри на две-три строки выше. Частая причина: переменная, которую ты ожидаешь непустой, на деле оказалась nil, потому что элемент (игрок, машина, объект) успел исчезнуть между проверкой и использованием — вышел с сервера, машина была уничтожена скриптом-соседом и так далее.
Полезная привычка при отладке своего кода — расставлять outputDebugString в подозрительных местах:
outputDebugString("Дошли до проверки прав: " .. tostring(player))
Это не заменяет чтение ошибки, но быстро показывает, докуда код вообще успевает дойти до падения — особенно если ошибка «attempt to index a nil value» указывает на строку с длинной цепочкой обращений вроде getElementData(getPlayerFromName(name), "money"), где непонятно, какое из двух звеньев вернуло nil.
Поднять сервер MTA:SA за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверПорядок ресурсов в mtaserver.conf: когда он реально важен
Секция <resources> в mtaserver.conf перечисляет ресурсы построчно, и по умолчанию MTA стартует их в этом же порядке при запуске сервера. Пока ресурсы независимы друг от друга, порядок роли не играет. Проблема начинается, когда один ресурс на верхнем уровне скрипта (то есть сразу при загрузке, а не внутри обработчика события) вызывает функцию из другого ресурса — библиотеки интерфейсов, общей утилиты, модуля работы с базой:
-- server.lua гейммода, который ещё не дождался загрузки библиотеки
exports.mylib:doSomething() -- attempt to call a nil value
Если mylib в <resources> стоит ниже гейммода, на момент выполнения этой строки она ещё не стартовала — и вызов падает с той самой ошибкой attempt to call a nil value, хотя сам код абсолютно правильный. Переставлять строки в <resources> вручную — рабочий, но хрупкий способ: при следующей правке конфига порядок легко случайно сломать снова.
Надёжнее — явно объявить зависимость в meta.xml самого ресурса через <include>:
<include resource="mylib" />
MTA учитывает <include> при построении очереди запуска и гарантированно стартует зависимость раньше зависимого ресурса, независимо от того, в каком порядке они перечислены в mtaserver.conf. Это тот случай, когда правильное решение — не в конфиге сервера, а в метаданных самого ресурса. Если зависимость всё равно не гарантирована (например, обращение идёт не при загрузке файла, а очень рано в onResourceStart), оборачивай вызов в проверку:
addEventHandler("onResourceStart", resourceRoot, function()
if exports.mylib and exports.mylib.doSomething then
exports.mylib:doSomething()
else
outputDebugString("mylib ещё не готова", 2)
end
end)
Конфликт версий API: когда ресурс не виноват в своей ошибке
Отдельная категория ошибок — когда скрипт скачан с community.multitheftauto.com или GitHub, выглядит рабочим, но на актуальной версии сервера падает с attempt to call a nil value (global 'ИмяФункции'), хотя имя написано без единой опечатки. Причина — функция была переименована, объявлена устаревшей (deprecated) или вовсе удалена из Lua API в более поздних версиях сервера, а ресурс писался под старую сборку MTA:SA, когда старая функция ещё существовала.
Признаки именно этой проблемы, а не бага в коде:
- ошибка указывает на встроенную функцию MTA (
setElementData,createVehicleи подобные), а не на что-то из логики самого ресурса; - в
meta.xmlресурса указана старая версия или дата коммита в репозитории на GitHub датирована несколькими годами назад; - та же ошибка повторяется у других пользователей ресурса в комментариях на Community или в issues репозитория — значит, дело не в конкретно твоей установке.
Что делать: проверить вики MTA:SA по конкретной функции — там честно помечают устаревшие вызовы и их замену; поискать форк ресурса на GitHub с более свежими коммитами (у популярных RP-гейммодов почти всегда есть поддерживаемые форки); либо, если ресурс небольшой, просто заменить устаревший вызов на актуальный аналог самому — это обычно один-два вызова функции, а не переписывание логики. Точную версию сервера, под которую писался конкретный старый ресурс, лучше не пытаться угадывать — надёжнее свериться с датой в его репозитории и с текущим changelog на официальном сайте.
Частые сообщения об ошибках и что они означают на практике
Разбор самых ходовых сообщений, которые встречаются при отладке ресурсов:
attempt to call a nil value (global 'X')— функцииXне существует в текущей области видимости. Либо опечатка в имени, либо это экспортируемая функция из ресурса, который ещё не стартовал (см. раздел про порядок загрузки), либо устаревший вызов из старой версии API.attempt to index a nil value (field 'X')/(local 'X')— код пытается обратиться к полю или методу переменной, которая на делеnil. Чаще всего — элемент (игрок, машина, объект) уже не существует к моменту обращения к нему.bad argument #N to 'Функция' (тип expected, got nil)— в функцию передан аргумент неправильного типа илиnilтам, где ожидается конкретный тип (element,number,string). Смотри, что реально передаётся в вызове на указанной строке.stack overflow— рекурсия, у которой нет условия выхода, либо взаимные вызовы двух функций друг друга без остановки.Loading meta.xml failed— синтаксическая ошибка в XML: незакрытый тег, лишняя кавычка, неверная вложенность. Номер строки в сообщении точный, ищи прямо там.Warning: dependent resource X missing— ресурс объявил зависимость через<include>, но зависимого ресурса нет вresources/вообще. Нужно скачать и положить рядом, а не просто прописать в<resources>.
Отладка на клиенте: debugscript
Всё, что описано выше, касается в основном серверной консоли — но у client-side скриптов (type="client" в meta.xml) есть собственный вывод ошибок, который в серверной консоли не виден вообще: он идёт в игровую консоль конкретного игрока. Чтобы его увидеть, находясь в игре на своём сервере, открой игровую консоль (по умолчанию ~ или F8) и введи:
debugscript 3
Уровень 3 — самый подробный, показывает не только ошибки, но и предупреждения (warning), которые на уровне по умолчанию можно не заметить. Формат сообщений тот же, что и на сервере: имя ресурса, файл, строка. Это особенно важно при разработке HUD-интерфейсов и обработки ввода — такой код целиком клиентский, и без debugscript ты просто не увидишь, что он вообще падает, пока не проверишь лично зайдя на сервер игроком.
Для быстрой проверки конкретного участка кода на обеих сторонах полезно тот же приём с outputDebugString, что упоминался выше — он выводится и в серверную консоль (для server.lua), и в клиентский debugscript-вывод (для client.lua), так что можно расставлять контрольные точки по обе стороны и сопоставлять, где именно рвётся цепочка событий между клиентом и сервером.
Похожий подход к чтению логов и поиску первопричины падения актуален не только для MTA:SA — общие принципы разбираются в гайде как читать логи и краш-репорты, а если параллельно администрируешь ещё и FiveM-сервер, там похожая логика чтения ошибок в консоли разобрана в статье про скриптовые ошибки FiveM.
Поднять сервер MTA:SA за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверЧастые вопросы
Ресурс запускается без ошибок, но ничего не делает — почему?
Часто это не ошибка Lua, а недостающие права ACL: ресурс тихо не может выполнить действие, на которое у него нет доступа. Проверь aclrequest list имя_ресурса в консоли — если есть невыданные запросы прав, часть функциональности будет молча не работать. Подробнее про структуру прав — в гайде по установке сервера MTA:SA и настройке Lua-скриптов.
Как понять, ошибка на сервере или на клиенте?
По расширению и месту вывода: ошибки server.lua (или файлов с type="server" в meta.xml) видны в серверной консоли, ошибки client.lua (type="client") — только в игровой консоли конкретного игрока через debugscript 3. Если непонятно, где какой файл — смотри атрибут type в <script> внутри meta.xml ресурса.
Ошибка пропадает после restart имя_ресурса, но появляется снова через время — это баг?
Скорее всего да, просто он воспроизводится не сразу, а при определённом условии — например, когда элемент, с которым работает скрипт, успевает исчезнуть до срабатывания обработчика. Такие ошибки обычно связаны с гонкой между событиями, а не с постоянной поломкой кода.
Можно ли увидеть ошибки прошлых запусков, если консоль уже прокрутилась?
Да, если сервер поднят через systemd — смотри journalctl -u mta-server --since "1 hour ago" или без фильтра по времени для полной истории. Если сервер запущен вручную в терминале без логирования в файл, старый вывод консоли восстановить нельзя — стоит сразу настраивать systemd-юнит, чтобы такого не случалось.
Ошибка указывает на строку в чужом ресурсе — можно её просто закомментировать?
Технически да, но обычно это лечит симптом, а не причину: если строка отвечает за какую-то функциональность, ты просто теряешь её вместо того, чтобы понять, почему она падает. Разумнее — понять первопричину (устаревший API, отсутствующая зависимость, неверный порядок загрузки) и исправить именно её.