Исправление скриптового вылета

Материал из Моддинг трилогии S.T.A.L.K.E.R. - modFAQ.RU | +TES/Fallout
Перейти к навигации Перейти к поиску

Исправление скриптового вылета

Автор 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.

Источники

  1. "Malandrinus" amk-team.ru 24.07.2009 Архивировано: web.archive.org 21.11.2025