Исправление скриптового вылета
|
Исправление скриптового вылета | |
| Автор | Malandrinus |
| Тип статьи | руководство |
| Актуальность | ТЧ |
| Необходимый софт | Текстовый редактор |
Все мы знаем, что движок X-Ray ведёт себя, как капризная невеста, и вылетает от малейшего чиха[1].
Причины вылетов
Синтаксические ошибки в коде
Как правило, лог при этом содержит что-то вроде:
attempt to index global '<имя файла>' (a nil value) attempt to index global '<имя файла>' (a nil value)
Т.е. движку не удалось скомпилить модуль и его значение (модуля) равно нулю.
Ошибки времени выполнения Lua
Лог обычно содержит указание ошибки, модуль и строку, в которой ошибка произошла. Типа такого:
LUA error: ....k.e.r. 1.0004\gamedata\scripts\<имя файла>.script:<номер строки>: attempt to index global 'npc' (a nil value) LUA error: ....k.e.r. 1.0004\gamedata\scripts\<имя файла>.script:<номер строки>: attempt to index global 'npc' (a nil value)
К типичным ошибка относятся попытки вызвать несуществующий метод класса (и вообще индексировать что бы то ни было несуществующим ключом) или к примеру, конкатенация строки с nil.
Ошибки времени выполнения самого игрового движка
Лога обычно нет, т.е. движок тихо слетает, сам в лог ничего не пишет, а забуферизированный до этого момента лог — теряется. Иногда правда — что-то в логе есть, но бывает это весьма редко. Ошибки движка вызваны неправильным использованием функций самого движка. Например, попытка заспавнить объект с несуществующей секцией.
Кроме того, скрипты выполняются с прекомпиляцией, поэтому для запуска изменённого скрипта надо, как минимум перегрузить сохранёнку.
Кстати, для справки: в отличие от скриптов, файлы конфигураций подгружаются уже при старте программы ещё до загрузки сохранения. Посему, для тестирования изменения в файлах конфигураций — надо перезапускать программу.
Поиск ошибки
Довольно часто надо проверить некий фрагмент кода, заспавнить объект, проверить некую синтаксическую конструкцию и т.п.
Как мы обычно поступаем в этом случае:
Этап 1
Пишу скрипт и получаю вылет игры без лога, если влез в один из заранее прекомпилируемых файлов и сделал там тупую синтаксическую ошибку.
Этап 2
Выношу скрипт в отдельный файл и получаю вылет игры с до ужаса информативным сообщением в лог-файле "attempt to index global 'my_cool_script' (a nil value)".
Это значит, что тупую синтаксическую ошибку я сделал в файле my_cool_script.script
Можно до хрипоты говорить о пользе предварительной синтаксической проверки, но как часто мы её не делаем, не так ли?
Этап 3
Наконец-то умнею и проверяю скрипт на вшивость заранее. Запускаю игру, загружаю уровень.
загружаю уровень...
загружаю уровень......
Получаю вылет игры, лезу в лог и ищу в конце коротенькое сообщение, из которого следует, что надо поменять пару символов или добавить/стереть одну строчку.
Этап 4
Меняю скрипт, повторяю всё заново, скрипт отрабатывает, но делает не то, что мне надо. Чтобы изменения в скрипте вступили в силу, надо перегрузить уровень.
Так что повторяю этапы 3 и 4, пока не получу то, что мне было нужно.
Вот для частичного решения этой проблемы, в своё время был сделан специальный тестовый полигон, который позволяет менять и вызывать определённый скрипт, не выходя из игры. Кроме того, часть ошибок (а именно ошибки Lua) блокируются и не приводят к вылетам. Полигон этот сэкономил мне кучу времени.
Тестирование скриптов без обрушения и перезапуска игры
Ключевыми элементами являются две функции из стандартной библиотеки Lua:
dofile - позволяет загрузить и выполнить как chunk внешний файл.
pcall - позволяет выполнить произвольную функцию в т.н. защищённом режиме. Это значит, что любая ошибка интерпретатора Lua не выйдет за пределы функции, вызванной таким образом. Напомню, что обычно любая ошибка времени выполнения Lua передаётся хост-программе, т.е. игре, и вызывает её вылет.
Ну и собственно рецепт пилюли.
Cледующий фрагмент может, скажем, выполняться по горячей клавише из главного меню:
local res, err = pcall(my_proxy.start_chunk_from_file)
get_console():execute(res and "Succesfull!" or string.gsub(err, " ", "_")) --имитация тернарного оператора
local res, err = pcall(my_proxy.start_chunk_from_file)
get_console():execute(res and "Succesfull!" or string.gsub(err, " ", "_")) --имитация тернарного оператора
pcall возвращает два значения. Первое - результат запуска. Если все прошло пучком, то true. Второе - добавочное значение с сообщением об ошибке, если ошибка была. Иначе nil.
Файл my_proxy.script содержит всего одну функцию
function start_chunk_from_file()
local chunk, err = dofile("..\\gamedata\\scripts\\test.lua")
if err then
get_console():execute("err="..tostring(string.gsub(err, " ", "_")))
end
end
dofile принимает имя файла со скриптом, загружает его и выполняет как функцию. Путём экспериментов я выяснил, что текущим каталогом Lua считает папку bin. Так что если хотим держать скрипт там, где и все остальные, то пишем путь относительно этой папки "..\\gamedata\\scripts\\test.lua".
test.lua - это отлаживаемый скрипт.
Обработка ошибок осуществляется аналогично pcall.
Так можно не выходя из игры, и даже не перегружая уровень, копаться в скрипте test.lua и запускать его по много раз, существенно менее рискуя обрушить игру. Особенно удобно, если есть два компа и сетка. В этом случае даже Alt-Tab не нужен. Редактируешь файл по сети на одном компе, а запускаешь в загруженной игре на другом.
Само-собой, если сделаем что-то, что фатально затронет игровую логику или что-то из ошибок категории 3, то обрушение может всё равно случиться. Сразу или в другом месте. В основном, в таких случаях уже ничего не поделаешь. Однако, довольно часто можно свести потенциальную ошибку времени выполнения движка, к ошибке Lua. Например, если вызвать функцию get_console():execute(<аргумент>) с нулевым аргументом, то получим вылет без лога. Однако ничто не мешает сделать так:
get_console():execute("value="..obj)
В этом случае, сначала будет выполняться конкатенация двух строк. Если obj будет равно nil, то получим ошибку Lua, а её тестовый полигон не пропустит, и обрушения игры не будет.
И ещё. Это всё работает для Тени Чернобыля. Для Чистого Неба мне наладить такой же полигон не удалось. Что-то там изменено в движке, что вызывает у него аллергию на функцию pcall.
Источники
- ↑ "Malandrinus" amk-team.ru 24.07.2009 Архивировано: web.archive.org 21.11.2025