ДокументированиеЕсть у оригинального UniMod такая важная проблема, что документации почти нет. Много неописанных возможностей и так далее. Это касается и пользовательского интерфейса, так и исходников.
Четыре уровня выполнения кода:Иерархия уровней выполнения кода позволит обезопасить код, сделать отладку более дружелюбной, а выполнение скриптов более надёжным.
Каждый из уровней имеет свою точку (момент времени) загрузки и выгрузки из игры.
Предлагаю следующие уровни:
- Уровень C++
- Основные скрипты - core-script действующие для всей игры в целом
- Mod-script, скрипты которые пользователь может добавлять по желанию. Они так же действуют для всей игры в целом
- Map-scirpt, скрипты, действующие на конкретной карте.
Теперь чуть подробнее о каждом из уровней.
Уровень C++UniMod3 будет использовать тот же способ загрузки, что и UniMod2 (что отличается от первой версии): загрузка ложной dll.
Хак следующий: выбирается одна из dll, загружаемых Nox: "a.dll", вместо неё кладётся другая с таким же названием. Она загружает UniMod3.dll и оригинальную dll.
Таким образом оригинальный файл выполнения Nox.exe остаётся нетронутым.
На этом уровне выполнения инициализируются все необходимые процессы. Предполагаемый порядок примерно следующий:
- Инициализируется Lua
- Загружается файл конфигураций UniModConfig.lua. В нём устанавливаются важные флаги.
- В зависимости от установленных флагов выполняются или не выполняются дальнейшие действия
- Загружаются скрипты уровня core-scirpt
Примеры флагов:
- DebugMode - активировать возможность исполнять небезопасные функции (например из lua.debug). Необходим для упрощения отладки при написании скриптов
- AllowStartMultipleNoxs - позволяет запустить несколько экземпляров Nox
- EnhanceConsole - активировать улучшенную консоль (в первом UniMod мы немного изменили консоль: добавили возможность редактирования введённого текста, историю)
- CoreLevelFiles - lua-файлы, которые будут загружены на уровне core-script
Core-scriptПосле инициализации первого уровня загружаются эти скрипты.
Я пока слабо представляю основные их функции.
Если будет такая возможность, я бы хотел возложить на них инициализацию части lua-среды. То есть создание каких-то глобальные переменных и так далее.
Возможно именно тут будет реализована возможность загрузки модов.
Так же есть идея разделять эти скрипты на модули, чтобы была возможность поставлять их отдельно, обновлять и так далее.
Mod-scriptПользовательские глобальные скрипты. Действуют после core-script. Пока не знаю можно ли будет их включать и отключать во время игры и что им можно будет, а что нельзя.
Их можно использовать как некие библиотеки функций, которыми пользователи могут делиться друг с другом и использовать в скриптах карт.
Тема для активного обсуждения.
Для упрощения разработки, пока предлагаю обойтись без поддержки модов. Держать в голове мысль, что они могут быть добавлены позже.Map-scirptЗагружаются вместе с картой.
Предлагаю как в первом UniMod сделать .tar архив с возможностью сжатия для хранения всех связанных файлов.
В самом скрипте необходимо так же несколько функций инициализации. Вроде предварительной загрузки (чтобы как-то настроить загрузку карты - отключить стандартные скрипты). Постинициализации - после того, как все объекты были загружены на карту.
Выгружаются при переходе на следующую карту или выходе из режима игры.
Full userdata вместо light userdata.В первой версии UniMod взаимодействие с объектами происходило с помощью light userdata (фактически - указатель). Теперь же каждому полученному с помощью lua объекту будет соответствовать userdata.
Это даст следующие преимущества:
- Методы связанные с объектом будут реализованы как методы объекта (доступ через двоеточие ":" в Lua)
- Исчезнет проблема с "битыми" объектами. Например, есть удалить монстра из игры, его userdata узнает об этом и не даст выполнить код, вызывающий падение игры (например перемещение удалённого объекта)
- Есть возможность связать lua объекты с объектами из игры в направлении lua -> Nox. Чуть подробнее в Event System
- Для разных типов объектов можно добавлять специфичные для них функции. Например тот же playerLook только для игроков
Event system. Внутриигровые событияВ оригинальном UniMod была возможность выполнять скрипт после какого-то времени, или выполнить скрипт при смерти монстра. Однако управлять этими событиями было совсем непросто.
Предлагаю каждому событию поставить в соответствие lua-объект: при создании таймера, создаётся объект taimer, с помощью него таймер можно остановить, перезапустить или удалить (и это удалит сам таймер).
Lua-scope как основной инструмент безопасности кодаЕсли отключить функции lua.debug, то по умолчанию нельзя выйти за пределы своей основной таблицы. То есть уже существует некий sandbox механизм.
Покажу как можно развить эту идею на примере map-script:
Для каждой карты создаётся отдельная lua таблица, куда загружаются все функции, что может использовать игрок через консоль (или они доступны через метаметод _index).
Игрок не может сохранить ссылку на объект за пределами этой таблицы (это очень тонкий момент, его необходимо гарантировать).
Таким образом, если игрок создал timer, то ссылку на него вынужден хранить он сам. При загрузке другой карты, lua таблица соответствующая карте удаляется, затем необходимо вызывать дважды garbage collector. Это и удалит все созданные таймеры и прочее. Поэтому скрипты уровня карты не смогут больше никак влиять на дальнейшую игру. По этой же причине игрок не может влиять на скрипты более высокого уровня.
Получается что механизмы sandboxing-а можно реализовать используя Lua и ничего более.