Каталог




Главная » Статьи » Обучающие статьи » Общая » Исправление багов Skyrim. Восстановление связей


Исправление багов Skyrim. Восстановление связей




flexcreator



Статус: Offline
Автор статьи: flexcreator



Добавлено: 11 октября 2014
Просмотров: 20774 | Комментарии: 21

Понравилось: 33 пользователям


Я немного устал чинить чужие сохраненные игры, поэтому в этой статье расскажу, как самостоятельно исправить некоторые скриптовые баги в Skyrim.

Кроме того, статья будет полезна моддерам, чтобы понять, как работает игра, и избежать популярных ошибок.

Не путать с вылетами на рабочий стол!

Сразу заявляю, что я не буду отвечать на наивные комментарии вида "Я просто делаю чистку с помощью SaveTool, и у меня все прекрасно". Искренне надеюсь, что вы не столкнетесь с ситуацией, когда SaveTool окажется бесполезной. Но если столкнетесь, вспомните про эту статью.

Я понимаю, что единицы на сайте знакомы с такими терминами из программирования, как "указатель", "инструкция" и т.д., поэтому начну с самых основ.

Как чтение статьи, так и исправление сохраненок, требует от пользователя умения и желания ДУМАТЬ. Но я считаю, что с этим проблем нет, так как "нужда заставит". Перспектива потерять 600-часовое сохранение и начать все заново кого угодно заставит пошевелить извилинами.

Кроме того, я кажется где-то слышал, что все жалуются, будто паззлы в Skyrim - недостаточно сложные? ) Что ж, вы можете воспринимать свою проблему как достойную головоломку :)

Чтобы упросить вам задачу, я специально пометил места в статье, где пользователю придется ДУМАТЬ самостоятельно.

Что такое скрипт?

Скрипт в Skyrim - это программа, написанная на интерпретируемом языке, которая расширяет функционал объектов в игре.

Тело скрипта, которое как раз вносит функционал, реагирует на игровые события и содержит всю полезную нагрузку, состоит из инструкций (набор инструкций еще называют байткодом). Тело получается из исходного кода путем компиляции (например, из CreationKit). ВСЕ скрипты, которые поставляются вместе с модами (не важно, в архиве они или нет), идут в форме скомпилированного байткода.

Как и любая другая программа, скрипт должен обладать памятью. Переменные (свойства) скрипта составляют его долгосрочную память. Переменные могут хранить числа, а могут хранить идентификаторы других скриптов для связи с ними (переменные, которые хранят ID других скриптов, называются "указателями").

Как скрипты пишутся в сохраненную игру?

Упрощенно, игра записывает в сейв нити (как часть Стекфреймов) и сущности.

Нить - это копия тела скрипта, его байткод на момент исполнения + номер исполняемой инструкции. Игра пишет тело для того, чтобы после загрузки сейва скрипт возобновил исполнение в том виде, в котором он существовал при записи файла сохранения (даже если вы обновили мод до новой версии).

Сущность - это долгосрочная память скрипта. Каждый объект связан со своей скриптовой сущностью.

Например, если у вас два одинаковых манекена в доме, то у вас будет две скриптовых сущности (каждая из которых привязана к своему манекену). Если вы активируете и добавите предмет в инвентарь, скриптовая сущность соответствующего манекена обработает событие и изменит свою память. Она будет помнить, что за объект находится в соответствующем слоте. Каждый раз, когда вы заходите в дом, скрипт надевает данный кусок брони на манекена.

Но класс скрипта - один на все манекены (все манекены обрабатываются одинаково).

Итак, баги скриптов бывают двух типов:

Баг в самой логике скрипта (теле). В этом случае, надо пинать автора мода. Сами вы проблему не решите. После исправления, достаточно обновиться до новой версии мода.

Баг, связанный с неправильной памятью скриптовой сущности (т.е. неправильным значением какой-либо переменной). При этом в оригинальном моде никаких багов нет. В этом случае, пинать автора бесполезно (и даже вредно), т.к. проблема ТОЛЬКО в вашем сохранении.

"Как такое может быть" - спросите вы? Дело в том, что доступ к памяти скриптов ничем не ограничен. Любой скрипт из любого мода может изменить переменные скрипта из другого мода, игра не запрещает этого делать.

Обычно, такое возникает, если разные скрипты из разных модов переписывают память одних и тех же ванильных скриптов. Каждый мод тянет одеяло на свою сторону, в результате чего возникает конфликт, и что-то в игре перестает работать.

Также в некоторых ситуациях, игре не удается правильно заполнить значения указателей. Это может, например, возникнуть из-за хаотичной смены версий мода.

Например, вы обновили мод до версии 2. Вам не понравилось - вы откатились до версии 1. Все хорошо, используются скрипты 1-ой версии, но память-то осталась от второй версии! Автор мода мог что-то поменять в структурах данных, сущности в вашем сохранении обновились, и память скриптов изменилась. Значения существующих скриптовых переменных НЕ меняются со сменой версии мода.

Если что-то работает не так, как ожидается - скорее всего сбросились какие-то числовые параметры.

Но чаще что-то просто не работает. Например, предметы не активируются. В чем может быть причина?

Помните, я выше писал про "указатели" ? Указатель - это переменная, которая связывает одну скриптовую сущность с другой (хранит числовой идентификатор этой другой сущности). Зачем это вообще нужно?

В случае с манекенами, всю работу делает один скрипт. Одного скрипта достаточно для отслеживать броню и одевать ее на манекена.

Но в случае каких-то сложных квестов одного скрипта никогда не бывает достаточно. Работает сеть взаимосвязанных скриптов. Каждый из этих скриптов выполняет свою часть работы. Когда ему что-то нужно от другого скрипта, он посылает ему "сообщения" (вызывает функции этого скрипта). Но чтобы послать скрипту сообщения, он должен знать его идентификатор. Для этого как раз и существуют "указатели". Они связывают скрипты друг с другом, чтобы те могли взаимодействовать.



Что произойдет, если указатель потеряет свое значение по причинам, описанным выше? Связь оборвется, скрипты не смогут взаимодействовать друг с другом. Одни скрипты не будут отправлять сообщения, а другие не будут их получать, в результате - какая-то часть работы не будет выполнена (что вы, собственно, и наблюдаете в игре).

Итак, мы знаем - чтобы восстановить работоспособность скриптов в этом случае, достаточно восстановить связи между скриптовыми сущностями. Как это сделать?

Если память скрипта не содержит ничего важного, можно попробовать просто удалить сущность. Для этого подойдет как SaveTool, так и Papyrus Data Transfer (Script Scalpel). Вы должны определить имя скрипта и вручную указать его.

Когда игра загружает отредактированный сейв, она видит, что что-то не сходится - сущности больше нет, хотя она должна быть привязана к объекту в игре (согласно информации в файле плагина .esp). После чего, игра восстанавливает скриптовую сущность с памятью по умолчанию (все переменные будут содержать те значения, которые изначально указал моддер). Это как раз и решает нашу проблему в большинстве случаев, т.к. изначально скрипты связаны друг с другом.

Но что если память скрипта хранит важные данные, например, о прохождении квеста? При удалении, игра забудет эти данные, и игрок не сможет завершить квест. Или к примеру, моддер не заполняет значения изначально, и пишет скрипт, который выставит их при определенном событии. В этом случае - удаление скрипта не является решением.

Восстановление связей вручную

SaveTool не умеет редактировать переменные, поэтому потребуется скачать Papyrus Data Transfer.

Рассмотрим реальный случай, когда игрок не может снять печать и освободить Серану из гроба (Жаровни не вообще никак не реагируют, когда их активируют).



В первую очередь, нужно понять, что за скрипт привязан к активатору "Жаровня". Если подумать, то можно понять, что в английской версии активаторы носят название Brazier, поэтому попробуем просто загрузить секцию в PDT и поискать скрипты с таким названием на вкладке "Скриптовые данные" (используйте строку поиска внизу). В случаях, когда название активатора не совпадает с названием скрипта, можно открыть CreationKit, найти активатор и посмотреть, какой конкретно скрипт к нему привязан.



Успех! Мы нашли класс DLC1VQ01PuzzleBrazierScript.

Посмотрим на одну из сущностей. Опасения подтвердились, видим два указателя (Pointer), которые содержат значение 0 (то есть не указывают ни на один из скриптов).



Всего в сохранении 5 сущностей DLC1VQ01PuzzleBrazierScript (очевидно, потому что в загадке используется 5 жаровней). Видим, что у каждой из них имеется данная проблема.



Из названий (mainScript - "Главный Скрипт" и puzzleController) можно логически догадаться, что это ненормально, они ДОЛЖНЫ на что-то указывать, чтобы вся схема работала.

Теперь нужно подумать и догадаться, ID каких конкретно скриптов эти переменные должны содержать.

У mainScript стоит тип скрипта DLC1VQ01PuzzleController. Попробуем вбить это название в поиск и посмотреть, что за скрипты есть с таким классом. Видим одну единственную сущность.



Очевидно, у нас просто нет никаких других вариантов. Все переменные mainScript с типом данных DLC1VQ01PuzzleController должны указывать на данную сущность.

Но у нас остается переменная puzzleController, логика подсказывает (да, в этом месте надо подумать), что она также должна указывать на DLC1VQ01PuzzleController (зачем авторы это сделали, непонятно... но скорее всего, переменная с этим именем была добавлена неофициальным патчем). Чтобы окончательно убедиться, давайте попробуем найти такую переменную в другом скрипте.

Из названия класса и переменных DLC1VQ01PuzzleController, следует, что это скрипт, который управляет головоломкой. В нем должны быть ссылки на другие скрипты, которые задействованы в паззле.

Ага, видим скрипт, связанный с кнопкой DLC1VQ01PuzzleButtonScript. Двойной щелчок и мы попадаем на сущность. Граф зависимостей подтверждает нашу гипотезу: puzzleController должен указывать на DLC1VQ01PuzzleController.



Давайте подумаем и посмотрим, какие еще скрипты головоломки лишены связей. Возвращаемся к DLC1VQ01PuzzleController. С кнопкой все нормально. Кроме кнопки и жаровней, у нас есть еще скрипт Гроба. Двойной щелчок на указателе puzzleCoffin и мы попадаем на скрипт DLC1VQ01PuzzleCoffinScript.

Видим, что он страдает той же проблемой, что и Brazier. mainScript ни на что не указывает. Кроме того, видим указатель Serana (очевидно, должен указывать на скрипт Сераны), который тоже пустой!

Итого, у нас есть 6 скриптов с пустыми указателями. 5 жаровней, каждая из которых содержит по два указателя с нулевым значением. И скрипт гроба, который содержит два пустых указателя (один должен быть связан с Сераной, другой - с контроллером головоломки).

Для начала нужно сделать фильтр, который подскажет инструменту, какие именно переменные можно менять.

Идем в DLC1VQ01PuzzleCoffinScript. Правой кнопкой мыши по mainScript - инструмент добавит переменную в фильтр.



Аналогично поступаем DLC1VQ01PuzzleBrazierScript (достаточно сделать для одной жаровни).

Далее, мы должны выбрать скрипт, на которые переменные должны указывать. Идем на DLC1VQ01PuzzleController и встаем на заголовок (который содержит ScriptID).

Далее выбираем опцию "Значение Переменной" (Set Variables).

Видим такое окно:


Инструмент автоматически заполнил ID скрипта и фильтр (оба значения можно теперь поменять вручную). Помните, что мы построили фильтр только для одной жаровни? Если сейчас подумать, то можно понять, что при нажатии на кнопку "Изменить", исправится только 1 жаровня из 5.

Нам нужен фильтр, который охватит ВСЕ 5 сущностей (т.е. весь класс). Для этого достаточно убрать RefID из фильтра. Вот так это будет выглядеть:


Нажимаем "Изменить". Переменные mainScript теперь содержат верное значение. В логе можно увидеть, что операция затронула 6 переменных.


Аналогично действуем с puzzleController


Итак, у нас остается пустой указатель на Серану. Но как узнать ее скрипт? Можно подумать и поискать похожий указатель где-нибудь по соседству. Далеко ходить не пришлось - если выйти обратно в контроллер головоломки, можно увидеть переменную SeranaRef


Двойной щелчок, и инструмент переносит нас на скрипт DLC1SeranaLevelingScript, видим, что скрипты из других модов и ресурсов тоже ссылаются на него как на Серану, а значит - это наш клиент.


Указатель на Серану в скрипте DLC1VQ01PuzzleCoffinScript выставляется аналогично:


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

Загружаем игру - баг исчез, жаровни двигаются!
Серана спасена!



Если загрузить новое сохранение в PDT, можно четко увидеть различия на графе зависимостей

Связи скрипта Жаровни, до и после:


Связи скрипта Гроба, до и после:


Более подробно про редактирование переменных можно посмотреть на видео:


Спасибо за внимание!



Теги: Script, баг, Cleaning, Scripts, Bugs, скрипты, баги
Всего комментариев: 21

Информация
Для того, чтобы оставлять комментарии к данной публикации необходимо зарегистрироваться .
Набор в команду сайта
Наши конкурсы











Ответ на жалобу смотрите в разделе жалоб