Перейти к основному содержанию

Контекст исполнения

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

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

API — глобальный контекст

Если в браузерах, и других подобных браузеру движках, основным объектом является страница, контент которой мы изменяем, то в случае Inner Core, мы взаимодействуем непосредственно с игрой. А для этого нам нужны пространства, методы и классы, которые и находятся в глобальном контексте.

Inner Core определяет несколько глобальных контекстов, каждый из которых имеет свои определенные цели. На протяжении многих лет основным из них остается Core Engine, так как именно вокруг него и расположен весь функционал лаунчера. Этот контекст расширяет возможности другого, Adapted Script. Он предоставляет сугубо нативные методы, предоставляемые самой игрой, либо вспомогательные для работы самого Core Engine. Существуют еще Preloader, используемый исключительно презагрузчиком, и Preferences Window API, который был задействован для функционирования верстака, а когда-то и внутреигровых настроек модов. Их использование весьма специфично, так что мы не будем рассматривать их подробно.

- AdaptedScript
- CoreEngine
- PrefsWinAPI
- Preloader

Эти контексты используются в каждом скрипте и описываются в свойствах api вашего build.config.

В случае Core Engine возможно изменение глобального контекста, как это сделано, например, в Ender IO:

dev/Base/Items/powder.js
Item.createDyeItem = function(id, name, type) {
IDRegistry.genItemID(id);
Item.createItem(id, name, {
name: "item_material_organic_" + type + "_dye"
}, { stack: 64 });
};

Item.createDyeItem("greenDye", "Organic Green Dye", "green");
Item.createDyeItem("blackDye", "Organic Black Dye", "black");
Item.createDyeItem("brownDye", "Organic Brown Dye", "brown");

Однако думайте о последствиях, это изменит контекст во всех модах. То есть, метод Item.createDyeItem может быть вызван из любого мода, если установлен Ender IO. Но в таком случае существует и опасность заменить существующие методы или добавленные с обновлениями. Если вам необходимо что-то эскпортировать, рассмотрите ModAPI.registerAPI, он создан именно для этих целей.

Глобальные значения

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

const MINECRAFT_VERSION = getMCPEVersion();
if (MINECRAFT_VERSION.main == 16) {
alert("Майнкрафт " + MINECRAFT_VERSION.str + " сейчас актуален!");
// выведет: Майнкрафт 1.16.201 сейчас актуален!
} else if (MINECRAFT_VERSION.main == 11) {
alert("Легаси версия " + MINECRAFT_VERSION.array.join("-") + " неактуальна для " + __name__);
// выведет: Легаси версия 1-11-4 неактуальна для <имя мода>
}

Методы getMCPEVersion и alert здесь являются частью глобального контекста API, в то же время, __name__ — глобальное значение, доступное из любой части мода. Вам не нужно задумываться о том какие свойства доступны здесь, они приведены в сводке API и декларациях тулчейна.

Тело скрипта

Последним и важнейшим шагом в образовании вашего пространства служит именно код. JavaScript использует Rhino 1.7.7 (частично поддерживающим ES6, однако большая часть функционала осталась в реализации ES5) как основной движок, который и выполняет все скрипты. Язык интерпретируемый, а это означает, что он обрабатывается в реальном времени, либо с минимальной обработкой перед запуском. Весь код выполняется последовательно, а телом скрипта мы называем весь тот код, что находится во включенных в сборку файлах.

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

Резапускаемые пространства

Рассматривая жизненный цикл ранее, мы поверхностно представили скрипты custom. В отличии от остальных скриптов, это единственный тип, который может быть запущен сколько угодно раз, а помимо этого, принимает и возвращает значения. Вероятно, у вас могли возникнуть ассоциации с функциями если знакомство с языком однажды прошло успешно. Но в отличии от функций, это все же пространства; они работают в новом контексте и хранят значения.

Рассмотрим это на примере из Solar Flux Reborn:

dev/tests.js
alert(
runCustomSource("custom.js", {
THUNDER_MULTIPLIER: 0.4,
RAIN_MULTIPLIER: 0.6,
WEATHER: World.getWeather()
})
);

Значения, передаваемые в пространство, будут переопределены либо созданы если их еще нет. Любые константы, создаваемые в теле резапускаемых скриптов должны быть заданы лишь один раз. Их повторное изменение вызовет ошибку, в таком случае простым вариантом будет разместить их между try-catch.

custom.js
let raining = WEATHER.rain / 10;
raining = raining > 0.2 ? (raining - 0.2) / 0.8 : 0;
raining = Math.sin(raining * Math.PI / 2);
raining = 1 - raining * (1 - RAIN_MULTIPLIER);

let thundering = WEATHER.thunder / 10;
thundering = thundering > 0.75 ? (thundering - 0.75) / 0.25 : 0;
thundering = Math.sin(thundering * Math.PI / 2);
thundering = 1 - thundering * (1 - THUNDER_MULTIPLIER);

// Значения все равно должны быть возвращены функцией, так
// как тело скрипта не способно возвращать что-либо
(function() {
return raining * thundering;
})();

В зависимости от погодных условий эффективность работы солнечных панелей меняется, задействуются значения из текущего пространства. Так, runCustomSource("custom.js") не изменит значения и вернет предыдущий результат. Контекст не изменяется, так что и значения остаются прежними.

Средства для моддинга используют этот тип для выполнения кода во время игры. Так как их пространство легко настраивается и сохраняется между различными выполнениями независимо от остального контекста мода, подразделить основной код здесь было хорошей идеей. Более конкретного способа применения для этих скриптов попросту не существует. Предлагайте свои идеи, где подобный функционал мог бы быть полезен.