Введение

CLEO Redux — это среда выполнения сценариев для игр эпохи GTA 3D. Это полноценный член семейства CLEO, который предоставляет знакомый опыт всем, кто использовал библиотеку CLEO для классической GTA San Andreas или ее повторные реализации для других игр. Основная цель CLEO — предоставить возможность легко настраивать игру с помощью многочисленных пользовательских скриптов.

Если вы новичок в CLEO, посетите официальный сайт, чтобы найти больше информации об этом.

Поддерживаемые релизы

Классические:

  • GTA III 1.0
  • GTA Vice City 1.0
  • GTA San Andreas 1.0 (только с CLEO 4.4)

Ремастеры (The Trilogy):

  • Title Update 1.03 и Title Update 1.04 (см. детали)

Остальные:

CLEO Redux поддерживает только ПК-версию каждой игры.

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

Лицензия

CLEO Redux доступен по лицензионному соглашению с конечным пользователем.

Связь с CLEO Library

CLEO — это общее название пользовательских библиотек, разработанных и созданных для GTA III, Vice City или San Andreas. Каждую версию можно найти и скачать здесь. CLEO Redux — это другая реализация CLEO, созданная с нуля, с несколькими отличительными особенностями, такими как единая кодовая база для всех игр или поддержка кода JavaScript.

На данный момент CLEO Redux не может рассматриваться как полноценная замена CLEO Library из-за отсутствия поддержки многих широко используемых CLEO-команд. Чтобы решить эту проблему и получить максимальную отдачу от двух библиотек, CLEO Redux поддерживает две разные стратегии использования.

CLEO Redux может работать как отдельное ПО или как дополнение к CLEO Library. В первом случае ваша директория с игрой будет содержать только файл cleo_redux.asi (или cleo_redux64.asi). Во втором случае в вашем каталоге с игрой будут и cleo.asi (или III.CLEO.asi, и VC.CLEO.asi), и cleo_redux.asi (или cleo_redux64.asi).

Запуск CLEO Redux как отдельного ПО

Как отдельное ПО CLEO Redux запускает скомпилированные скрипты и JavaScript и предоставляет доступ ко всем командам собственного скрипта. Он также предоставляет ограниченный набор команд, обратно совместимых с библиотекой CLEO. Существующие CLEO-скрипты могут не работать, если они используют пользовательские команды (например, из стороннего плагина), не поддерживаемые CLEO Redux.

Этот режим не работает в классической GTA San Andreas.

Запуск CLEO Redux в качестве дополнения к библиотеке CLEO

В качестве дополнения CLEO Redux работает вместе с CLEO Library, делегируя ему всю заботу о пользовательских скриптах. Это означает, что все пользовательские скрипты и плагины, созданные для библиотеки CLEO, будут продолжать работать точно так же. CLEO Redux управляет только JS-скриптами. Все пользовательские команды становятся доступными для среды выполнения JavaScript, что означает, что вы можете использовать команды, которые в настоящее время не реализованы нативно в CLEO Redux, например, для файлов.

Этот режим работает в классической GTA III, GTA Vice City и GTA San Andreas, где существует библиотека CLEO.

Установка

Порядок установки CLEO Redux может отличаться в зависимости от игры. Обратитесь к одному из нижеперечисленных гайдов:

Обратите внимание, что CLEO Redux распознает целевую игру исключительно по имени исполняемого файла, из которого она запускается.

  • GTA III - gta3.exe
  • GTA VC - gta-vc.exe
  • GTA SA - gta_sa.exe, gta-sa.exe, or gta_sa_compact.exe
  • re3 - re3.exe
  • reVC - reVC.exe
  • GTA 3: DE - libertycity.exe
  • GTA VC: DE - vicecity.exe
  • GTA SA: DE - sanandreas.exe

Сопоставление имен не зависит от регистра. Для классических игр CLEO Redux всегда предполагает версию 1.0.

Удаление

  • Удалите cleo_redux.asi (или cleo_redux64.asi).
  • Удалите папку CLEO (необязательно).
  • Удалите cleo_redux.log (необязательно).

Классика GTA III, GTA VC, GTA SA

  • Скопируйте cleo_redux.asi в папку с игрой.

  • Запустите игру, чтобы завершить установку

Во время первого запуска игры может быть заметное отставание, так как CLEO Redux загружает файлы, необходимые для поддержки JavaScript. При последующих запусках этого не произойдет.

Также в папке с игрой* должна появиться новая папка с именем CLEO. Это основное место для всех CLEO-скриптов, плагинов и конфигов.

Если CLEO не обладает правами на запись в директории GTA San Andreas - Definitive Edition\Gameface\Binaries\Win64, он будет использовать другой каталог в C:\Users\<ваше_имя>\AppData\Roaming\CLEO Redux. Там должен быть cleo_redux.log и папка CLEO, куда попадают все ваши скрипты.

Примечание: CLEO Redux не изменяет игровые файлы. Он использует тот факт, что игра изначально загружает файлы .asi в качестве дополнений к библиотеке Miles Sound System. Никакого дополнительного ПО не требуется.

re3 и reVC

  • Скопируйте cleo_redux.asi в папку с игрой.

  • Запустите игру, чтобы завершить установку

Во время первого запуска игры может быть заметное отставание, так как CLEO Redux загружает файлы, необходимые для поддержки JavaScript. При последующих запусках этого не произойдет.

Также в папке с игрой* должна появиться новая папка с именем CLEO. Это основное место для всех CLEO-скриптов, плагинов и конфигов.

Если CLEO не обладает правами на запись в директории GTA San Andreas - Definitive Edition\Gameface\Binaries\Win64, он будет использовать другой каталог в C:\Users\<ваше_имя>\AppData\Roaming\CLEO Redux. Там должен быть cleo_redux.log и папка CLEO, куда попадают все ваши скрипты.

CLEO Redux поддерживает только «Windows D3D9 MSS 32bit» версию re3 или reVC.

При запуске на re3 и reVC убедитесь, что в каталоге игры есть файл re3.pdb (для re3) или reVC.pdb (для reVC). Из-за динамического характера адресов памяти в этих реализациях CLEO Redux использует отладочную информацию, хранящуюся в файле PDB, для правильного определения своего местоположения.

The Definitive Edition

  • Загрузите и установите Ultimate ASI Loader x64 от ThirteenAG (поместите version.dll в каталог Gameface\Binaries\Win64).

  • Скопируйте cleo_redux64.asi в тот же каталог.

  • Запустите игру, чтобы завершить установку

Во время первого запуска игры может быть заметное отставание, так как CLEO Redux загружает файлы, необходимые для поддержки JavaScript. При последующих запусках этого не произойдет.

Также в папке с игрой* должна появиться новая папка с именем CLEO. Это основное место для всех CLEO-скриптов, плагинов и конфигов.

Если CLEO не обладает правами на запись в директории GTA San Andreas - Definitive Edition\Gameface\Binaries\Win64, он будет использовать другой каталог в C:\Users\<ваше_имя>\AppData\Roaming\CLEO Redux. Там должен быть cleo_redux.log и папка CLEO, куда попадают все ваши скрипты.

Папка CLEO

Папка CLEO — это основное место, куда вы устанавливаете CLEO-скрипты, CLEO-плагины и пользовательские тексты. CLEO Redux автоматически создает эту папку при запуске игры.

В большинстве случаев этот каталог можно найти в той же папке, куда вы поместили cleo_redux.asi. Однако, если CLEO не имеет прав на запись и не может создавать новые файлы, он использует альтернативный путь C:\Users\<ваше_имя_пользователя>\AppData\Roaming\CLEO Redux. Там можно найти cleo_redux.log и каталог CLEO.

Плагины

Плагины — это необязательные программы, добавляющие дополнительные команды сценариев с помощью CLEO Redux SDK. Их расширение - .cleo.

Плагины следует копировать в директорию CLEO\CLEO_PLUGINS.

Скрипты

Добавление нового скрипта

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

Удаление скрипта

Удалите файл скрипта из каталога CLEO. Некоторые сценарии могут потребовать дополнительных действий для отмены установки. В случае каких-либо проблем проверьте документацию скрипта или обратитесь к его автору.

Конфигурация

CLEO Redux предоставляет некоторые настраиваемые параметры в файле CLEO\.config\cleo.ini.

  • AllowCs - при значении 1 CLEO загружает и выполняет файлы *.cs, расположенные в каталоге CLEO. Включено по умолчанию.
  • AllowJs - при значении 1 CLEO загружает и выполняет файлы *.js, расположенные в каталоге CLEO. Включено по умолчанию.
  • AllowFxt - при значении 1 CLEO загружает и использует файлы *.fxt, расположенные в каталоге CLEO\CLEO_TEXT. Включено по умолчанию.
  • CheckUpdates - при значении 1 CLEO проверяет наличие нового обновления, доступного для скачивания, при запуске игры. Включено по умолчанию.
  • LogOpcodes - при значении 1 CLEO регистрирует все выполненные опкоды в пользовательских скриптах. Этот журнал является частью файла cleo_redux.log (см. Лог)
  • PermissionLevel - устанавливает уровень разрешений для небезопасных операций (см. ниже). По умолчанию используется «слабый».

Разрешения

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

Доступны четыре уровня:

All

Допускаются любые небезопасные операции. Используйте это только в том случае, если вы доверяете сценариям, которые запускаете.

Lax

Это уровень разрешений по умолчанию.

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

Например, если скрипт хочет получить доступ к памяти через 0A8D READ_MEMORY, имя файла должно содержать [mem], т.е. крутой спаунер[mem].cs. Если файл называется по-другому, CLEO отвергает 0A8D, и скрипт вылетает.

Strict

Никакая небезопасная операция не разрешена, если сценарий явно не запрашивает ее (см. "Слабые"), а файл конфигурации CLEO разрешает этот тип операции в разделе Разрешения.

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

mem=0

отключает все коды операций, связанные с памятью, даже если скрипт имеет токен [mem] в имени файла.

Примечание. Раздел Permissions в cleo.ini вступает в силу только в том случае, если PermissionLevel имеет значение Strict.

None

Небезопасная работа не допускается.

Скрипты

CLEO Redux предоставляет среду выполнения, способную выполнять скомпилированные бинарные скрипты (*.cs) в родном формате SCM и простые текстовые скрипты (*.js), написанные на JavaScript.

Чтобы найти информацию о том, как установить скрипты см. эту страницу.

Скомпилированные скрипты

CLEO Redux может выполнять скрипты, скомпилированные в родном для игры бинарном формате SCM. Такие скрипты имеют расширение .cs. На них распространяются ограничения кода SCM.

Проверьте FAQ для получения информации о поддержке CS в обновленных играх.

Написание скомпилированного скрипта

Подробное пошаговое руководство посетите эту страницу. Это применимо к CLEO Redux.

Для создания нового скрипта используйте Sanny Builder 3 в режимах редактирования GTA III, GTA VC или GTA SA соответственно. Добавьте директиву {$CLEO .cs} вверху вашего скрипта, напишите код и запустите «Скомпилировать и скопировать». Sanny Builder создаст новый файл CS в каталоге CLEO.

На данный момент CLEO Redux предоставляет только несколько пользовательских команд. Большинство команд, реализованных в CLEO Library или ее плагинах, пока недоступны.

Есть несколько основных правил, которым нужно следовать при написании скомпилированных скриптов:

  1. Один файл - один скрипт. CLEO поддерживает только один скрипт на файл.

  2. Никогда не используйте код операции 004E для завершения сценария. Этот код операции поддерживается только в main.scm. Вместо этого используйте 0A93.

  3. Минимизируйте использование глобальных переменных, поскольку они могут конфликтовать с другими скриптами. Можно использовать некоторые известные переменные, такие как $PLAYER_CHAR, $PLAYER_ACTOR и $ONMISSION.

CLEO Redux не поддерживает сохранение статуса скрипта (функция CLEO Library) и добавлять эту функцию не планируется.

Пользовательские команды

Примечание: Следующие команды предназначены только для классических игр. Для The Definitive Edition проверьте эту информацию.

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

Этот список может быть неполным, поскольку существуют специальные плагины с дополнительными командами (см. Использование SDK). Обратитесь к библиотеке Sanny Builder для получения полного списка доступных команд для каждой игры.

JavaScript

CLEO Redux ориентируется на JavaScript как на основной язык для пользовательских скриптов. JavaScript — популярный язык программирования с богатой экосистемой и большим количеством доступной информации. Он свободен от ограничений и ловушек языка SCM, таких как отсутствие поддержки функций, массивов или малое количество переменных.

Используйте VS Code (рекомендуется) или любой редактор по вашему выбору. Создайте новый файл с расширением .js и поместите его в папку CLEO. Дополнительную информацию см. в разделе Поддержка JavaScript.

Среда выполнения поддерживает сценарии в стандарте ECMAScript 2020. Это означает, что вы можете использовать самые последние функции JavaScript из коробки, такие как импорт, классы, стрелочные функции и т.д.

CLEO Redux — это не Node.js. Не ожидайте, что здесь будут доступны сокеты, операции с файловой системой или другие функции Node.js.

Предварительные условия

Когда JavaScript включен, CLEO Redux требует определения команд с https://library.sannybuilder.com/. При первом запуске CLEO пытается загрузить их и поместить в вашу локальную директорию CLEO/.config. Если этого не произошло или вы не хотите, чтобы CLEO совершал сетевые вызовы, вручную скачайте необходимый файл (см. таблицу ниже) и поместите его в каталог CLEO/.config.

ИграФайлМинимальная требуемая версия
GTA III, re3gta3.json0.208
GTA VC, reVCvc.json0.210
GTA San Andreas (Классика) 1.0sa.json0.210
GTA III: The Definitive Editiongta3_unreal.json0.210
Vice City: The Definitive Editionvc_unreal.json0.212
San Andreas: The Definitive Editionsa_unreal.json0.216

Жизненный цикл скрипта

Файл с кодом JavaScript должен иметь расширение *.js и содержать известные инструкции, как описано ниже. Скрипт может не иметь инструкций (пустой скрипт). Он запускается, как только начинается новая игра или загружается файл сохранения.

Скрипт завершается автоматически после выполнения последней инструкции. Среда выполнения также завершает зависшие сценарии, чтобы игра не зависала. Застрявший скрипт — это тот, которому потребовалось более 2 секунд для запуска с момента последней команды ожидания. Если это произошло, проверьте свои циклы, в некоторых из них отсутствует команда ожидания.

while (true) {
  // бессмысленный бесконечный цикл, обычно зависающий в игре
  // будет завершено через две секунды
}

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

while (true) {
  wait(250);
  // все равно бессмысленно, но не зависает в игре
}

Организация сценариев

Один самодостаточный файл скрипта можно поместить прямо в директорию CLEO.

Начиная с версии 0.9.3 сложные скрипты, имеющие множество зависимостей (импортированные файлы, словари FXT), могут быть организованы в папки. CLEO Redux сканирует подкаталоги каталога CLEO и проверяет, есть ли файл с именем index.js. Если index.js найден, CLEO Redux запускает его. Он также загружает все файлы FXT из того же каталога.

Давайте посмотрим на структуру примера:

CLEO/
├─ CLEO_TEXT/
│  ├─ main.fxt
├─ папка A/
│  ├─ text.fxt
│  ├─ index.js
├─ папка B/
│  ├─ test.fxt
│  ├─ test.js
├─ script1.js
├─ script2.cs

По умолчанию CLEO Redux загружает все файлы CS и JS из корня каталога CLEO и файлы FXT из папки CLEO_TEX. Таким образом, он загружает script1.js, script2.cs и main.fxt.

После сканирования подкаталогов CLEO загружает index.js и text.fxt, находящиеся в папке A, и пропускает папку B, так как файла index.js нет.

CLEO_TEXT, CLEO_PLUGINS и .config не считаются каталогами скриптов.

Цепочки методов

Методы конструируемых сущностей (таких как Player, Car, Char – любые сущности, созданные с помощью метода конструктора) поддерживают цепочку (также известную как Fluent Interface). Это позволяет писать такой код:

var p = new Player(0);

p.giveWeapon(2, 100)
  .setHealth(5)
  .setCurrentWeapon(2)
  .getChar()
  .setCoordinates(1144, -600, 14)
  .setBleeding(true);

Посмотреть демонстрацию: https://www.youtube.com/watch?v=LLgJ0fWbklg.

Методы деструктора прерывают цепочку. Например. учитывая код:

Char.Create(0, 0, 0, 0, 0).setCoordinates(0, 0, 0).delete()

Цепочка не может продолжаться после метода удаления, так как символ удаляется, а его дескриптор больше не действителен.

Импорт

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

Чтобы избежать запуска включенных файлов .js как отдельных скриптов, либо поместите их в отдельную папку (например, CLEO/includes/), либо используйте расширение .mjs.

// импортирует экспорт по умолчанию из other.js или other.mjs, расположенных в том же каталоге
import func from "./other"; 

// импортирует именованный экспорт PedType из types.js или types.mjs, расположенных в каталоге CLEO/includes
import { PedType } from "./includes/types"; 

// импортирует cars.json как значение JavaScript (массив, объект).
import data from "./vehicles.json";

В настоящее время поддерживается только импорт файлов .js (.mjs) и .json.

Привязки

Следующие переменные и функции доступны только в коде JavaScript.

Переменные

HOST

имя хоста (ранее доступное как переменная GAME). Возможные значения включают gta3, vc, re3, reVC, sa, gta3_unreal, vc_unreal, sa_unreal, unknown.

Плагины CLEO могут использовать SDK для настройки имени под свои нужды.

if (HOST === "gta3") {
  showTextBox("This is GTA III");
}
if (HOST === "sa") {
  showTextBox("This is San Andreas");
}
if (HOST === "unknown") {
  showTextBox("This host is not natively supported");
}

ONMISSION

глобальный флаг, определяющий, находится ли игрок в данный момент на задании. Недоступно на «неизвестном» хосте.

if (!ONMISSION) {
  showTextBox("Not on a mission. Setting ONMISSION to true");
  ONMISSION = true;
}

TIMERA, TIMERB

два автоматически увеличивающихся таймера, полезных для измерения временных интервалов.

while (true) {
  TIMERA = 0;
  // wait 1000 ms
  while (TIMERA < 1000) {
    wait(0);
  }
  showTextBox("1 second passed");
}

__dirname

абсолютный путь к каталогу с текущим файлом

__filename

(начиная с 0.9.4) абсолютный путь к текущему файлу

Функции

log

log(...значения) печатает разделенные запятыми {значения} в cleo_redux.log

var x = 1;
log("value of x is ", x);

wait

wait(время в миллисекундах) приостанавливает выполнение скрипта как минимум на {время в миллисекундах} миллисекунд

while (true) {
  wait(1000);
  log("1 second passed");
}

showTextBox

showTextBox(текст) отображает {текст} в черном прямоугольном поле. Недоступно на неизвестном хосте.

showTextBox("Hello, world!");

exit

exit(причина?) немедленно завершает скрипт. Функция exit принимает необязательный строковый аргумент, который будет добавлен в cleo_redux.log.

exit("Script ended");

native

native(имя команды, ...входные аргументы) это низкоуровневая функция для выполнения команды с использованием ее имени {имя команды}. Имя команды соответствует свойству name в файле JSON, предоставленном библиотекой Sanny Builder.

native("SET_TIME_OF_DAY", 12, 30); // устанавливает время суток на 12:30

Для команд, возвращающих одно значение, результатом является это значение.

const progress = native("GET_PROGRESS_PERCENTAGE");
showTextBox(`Progress is ${progress}`);

Для команд, возвращающих несколько значений, результатом является объект, в котором каждый ключ соответствует возвращаемому значению. Имена ключей соответствуют именам выходов, указанным в определении команды.

var pos = native("GET_CHAR_COORDINATES", char); // возвращает вектор координат символа {x, y, z}
showTextBox(`Character pos: x ${pos.x} y ${pos.y} z ${pos.z}`);

Для условных команд результатом является логическое значение true или false.

if (native("HAS_MODEL_LOADED", 101)) {
  // проверяет условие
  showTextBox("Model with id 101 has been loaded");
}

Статические объекты

Memory

  • Объект Memory позволяет манипулировать памятью процесса. См. Руководство по памяти для получения дополнительной информации.

Math

  • Объект Math — это стандартный объект, доступный в среде выполнения JS, который обеспечивает общие математические операции. CLEO Redux расширяет его некоторыми дополнительными командами. См. Объект Math для получения дополнительной информации.

FxtStore

CLEO

  • (начиная с 0.9.4) Объект CLEO предоставляет доступ к информации и утилитам во время выполнения:

    • CLEO.debug.trace(флаг) включает и выключает трассировку команд в текущем скрипте. Когда {флаг} имеет значение true, все выполненные команды добавляются в cleo_redux.log:
      CLEO.debug.trace(true)
      wait(50);
      const p = new Player(0);
      CLEO.debug.trace(false)
    
    • CLEO.version - сложное свойство, предоставляющее информацию о текущей версии библиотеки
      log(CLEO.version)       // "0.9.4-dev.20220427"
      log(CLEO.version.major) // "0"
      log(CLEO.version.minor) // "9"
      log(CLEO.version.patch) // "4"
      log(CLEO.version.pre)   // "dev"
      log(CLEO.version.build) // "20220427"
    

Сравнение ScriptObject и Object

Библиотека Sanny Builder определяет статический класс Object для группировки команд, позволяющих создавать и управлять трехмерными объектами в игре. В то же время в JavaScript есть собственный класс Object со своими методами.

Чтобы не смешивать их, CLEO Redux использует класс ScriptObject вместо класса Object из библиотеки с тем же интерфейсом.

// код операции 0107, создает новый объект в игре
var x = ScriptObject.Create(modelId, x, y, z); 

// собственный код JavaScript, создает новый объект в памяти JS
var x = Object.create(null); 

Использование математических объектов

В JavaScript есть встроенный объект Math, который обеспечивает общие математические операции, такие как abs, sin, cos, random, pow, sqr и т. д. CLEO Redux расширяет этот объект до включать дополнительные операции, поддерживаемые игрой. Интерфейс Math выглядит следующим образом:

interface Math {
    // native code
    readonly E: number;
    readonly LN10: number;
    readonly LN2: number;
    readonly LOG2E: number;
    readonly LOG10E: number;
    readonly PI: number;
    readonly SQRT1_2: number;
    readonly SQRT2: number;
    abs(x: number): number;
    acos(x: number): number;
    asin(x: number): number;
    atan(x: number): number;
    atan2(y: number, x: number): number;
    ceil(x: number): number;
    cos(x: number): number;
    exp(x: number): number;
    floor(x: number): number;
    log(x: number): number;
    max(...values: number[]): number;
    min(...values: number[]): number;
    pow(x: number, y: number): number;
    random(): number;
    round(x: number): number;
    sin(x: number): number;
    sqrt(x: number): number;
    tan(x: number): number;

    // GTA III, GTA Vice City, GTA SA команды
    ConvertMetersToFeet(meters: int): int;
    RandomFloatInRange(min: float, max: float): float;
    RandomIntInRange(min: int, max: int): int;

    // GTA Vice City, GTA SA команды
    GetDistanceBetweenCoords2D(fromX: float, fromY: float, toX: float, toZ: float): float;
    GetDistanceBetweenCoords3D(fromX: float, fromY: float, fromZ: float, toX: float, toY: float, toZ: float): float;

    // GTA SA команды
    GetAngleBetween2DVectors(xVector1: float, yVector1: float, xVector2: float, yVector2: float): float;
    GetHeadingFromVector2D(_p1: float, _p2: float): float;
    LimitAngle(value: float): float;
}

Первая группа включает собственные константы и методы, предоставляемые средой выполнения JavaScript. Они начинаются со строчной буквы, например. Math.abs. Вы можете найти подробную документацию по этим методам здесь.

Затем идут специфичные для игры команды. Согласно соглашению об именах, каждый метод, привязанный к коду операции скрипта, начинается с заглавной буквы, например Math.RandomIntInRange (код операции 0209). Вы можете найти документацию в Sanny Builder Library.

    var x = Math.abs(-1); // x = 1
    var f = Math.ConvertMetersToFeet(10) // f = 32
    var pi = Math.floor(Math.PI) // pi = 3

Примечание. Это руководство предназначено для игр классической эпохи. Для получения информации об использовании класса Memory в Definitive Edition нажмите здесь.

Использование объекта памяти

Внутренний объект Memory предоставляет методы для доступа и управления данными или кодом в текущем процессе. Он имеет следующий интерфейс:

interface Memory {
    ReadFloat(address: int, vp: boolean): float;
    WriteFloat(address: int, value: float, vp: boolean): void;
    ReadI8(address: int, vp: boolean): int;
    ReadI16(address: int, vp: boolean): int;
    ReadI32(address: int, vp: boolean): int;
    ReadU8(address: int, vp: boolean): int;
    ReadU16(address: int, vp: boolean): int;
    ReadU32(address: int, vp: boolean): int;
    WriteI8(address: int, value: int, vp: boolean): void;
    WriteI16(address: int, value: int, vp: boolean): void;
    WriteI32(address: int, value: int, vp: boolean): void;
    WriteU8(address: int, value: int, vp: boolean): void;
    WriteU16(address: int, value: int, vp: boolean): void;
    WriteU32(address: int, value: int, vp: boolean): void;
    Read(address: int, size: int, vp: boolean): int;
    Write(address: int, size: int, value: int, vp: boolean): void;

    ToFloat(value: int): float;
    FromFloat(value: float): int;
    ToU8(value: int): int;
    ToU16(value: int): int;
    ToU32(value: int): int;
    ToI8(value: int): int;
    ToI16(value: int): int;
    ToI32(value: int): int;

    Translate(symbol: string): int;

    CallFunction(address: int, numParams: int, pop: int, ...funcParams: int[]): void;
    CallFunctionReturn(address: int, numParams: int, pop: int, ...funcParams: int[]): int;
    CallMethod(address: int, struct: int, numParams: int, pop: int, ...funcParams: int[]): void;
    CallMethodReturn(address: int, struct: int, numParams: int, pop: int, ...funcParams: int[]): int;
    Fn: {
        Cdecl(address: int): (...funcParams: int[]) => int;
        CdeclFloat(address: int): (...funcParams: int[]) => float;
        CdeclI8(address: int): (...funcParams: int[]) => int;
        CdeclI16(address: int): (...funcParams: int[]) => int;
        CdeclI32(address: int): (...funcParams: int[]) => int;
        CdeclU8(address: int): (...funcParams: int[]) => int;
        CdeclU16(address: int): (...funcParams: int[]) => int;
        CdeclU32(address: int): (...funcParams: int[]) => int;

        Stdcall(address: int): (...funcParams: int[]) => int;
        StdcallFloat(address: int): (...funcParams: int[]) => float;
        StdcallI8(address: int): (...funcParams: int[]) => int;
        StdcallI16(address: int): (...funcParams: int[]) => int;
        StdcallI32(address: int): (...funcParams: int[]) => int;
        StdcallU8(address: int): (...funcParams: int[]) => int;
        StdcallU16(address: int): (...funcParams: int[]) => int;
        StdcallU32(address: int): (...funcParams: int[]) => int;

        Thiscall(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallFloat(address: int, struct: int): (...funcParams: int[]) => float;
        ThiscallI8(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallI16(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallI32(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallU8(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallU16(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallU32(address: int, struct: int): (...funcParams: int[]) => int;
    }
}

Чтение и запись значений

Группа методов доступа к памяти (ReadXXX/WriteXXX) может использоваться для чтения или изменения значений, хранящихся в памяти. Каждый метод предназначен для определенного типа данных. Чтобы изменить значение с плавающей запятой (которое в исходной игре занимает 4 байта), используйте Memory.WriteFloat, например:

    Memory.WriteFloat(address, 1.0, false)

Где address — это переменная, хранящая адрес памяти, 1.0 — это значение для записи, а false означает, что нет необходимости изменять защиту памяти с помощью VirtualProtect (адрес уже доступен для записи).

Точно так же, чтобы прочитать значение из памяти, используйте один из методов ReadXXX, в зависимости от того, какой тип данных содержит адрес памяти. Например, чтобы прочитать 8-битное целое число со знаком (также известное как char или uint8), используйте Memory.ReadI8, например:

    var x = Memory.ReadI8(address, true)

переменная x теперь содержит 8-битное целое значение в диапазоне (0..255). Чтобы показать возможные варианты, в этом примере в качестве последнего аргумента используется true, что означает, что атрибут защиты по умолчанию для этого адреса будет изменен на PAGE_EXECUTE_READWRITE перед чтением.

    var gravity = Memory.ReadFloat(gravityAddress, false);
    gravity += 0.05;
    Memory.WriteFloat(gravityAddress, gravity, false);

Наконец, последние два метода Read и Write — это то, что другие методы используют под капотом. Они имеют прямую привязку к опкодам 0A8D READ_MEMORY и 0A8C WRITE_MEMORY и дают тот же результат.

Параметр size в методе Read может быть только 1, 2 или 4. CLEO обрабатывает value как целое число со знаком, хранящееся в формате с прямым порядком байтов.

В методе Write допускается любой size больше 0. Размеры 3 и 5 и далее могут использоваться только вместе с одним байтом value. CLEO использует их для заполнения непрерывного блока памяти, начиная с address, с заданным value (подумайте об этом как о memset в C++).

    Memory.Write(addr, 0x90, 10, true) // "noping" 10 байт кода, начиная с адреса

Обратите внимание, что для использования любого из методов чтения/записи требуется mem разрешение.

Метод приведения типов

По умолчанию методы Read и Write обрабатывают данные как целочисленные значения со знаком. Это может быть неудобно, если память содержит значение с плавающей запятой в формате IEEE 754 или большое 32-битное целое число со знаком (например, указатель). В этом случае используйте методы приведения ToXXX/FromXXX. Они действуют аналогично оператору reinterpret_cast в C++.

Чтобы получить представление о том, чего ожидать от этих методов, см. следующие примеры:

    Memory.FromFloat(1.0) => 1065353216
    Memory.ToFloat(1065353216) => 1.0
    Memory.ToU8(-1) => 255
    Memory.ToU16(-1) => 65535
    Memory.ToU32(-1) => 4294967295
    Memory.ToI8(255) => -1
    Memory.ToI16(65535) => -1
    Memory.ToI32(4294967295) => -1

В качестве альтернативы используйте соответствующие методы для чтения/записи значения в виде числа с плавающей запятой (ReadFloat/WriteFloat) или целого числа без знака (ReadUXXX/WriteUXXX).

Вызов внешних функций

Объект Memory позволяет вызвать чужую (собственную) функцию по ее адресу одним из следующих способов:

    Memory.CallFunction(0x1234567, 2, 0, 1000, 2000)

Где 0x1234567 — адрес функции, 2 — количество аргументов, 0 — параметр pop (см. ниже), 1000 и 2000 — два аргумента, переданных в функцию.

Обратите внимание, что устаревшая реализация команд вызова SCM требует, чтобы аргументы вызываемой функции были перечислены в обратном порядке. Вот и все, вы увидите тот же вызов в SCM, что и:

0AA5: call 0x1234567 num_params 2 pop 0 2000 1000

Где 2000 — второй аргумент, передаваемый функции, расположенной по адресу 0x1234567, а 1000 — первый.

Третий параметр (pop) в Memory.CallFunction определяет соглашение о вызовах. Если установлено значение 0, функция вызывается с использованием соглашения stdcall. Когда для него установлено то же значение, что и для numParam, функция вызывается с использованием соглашения cdecl. Любое другое значение нарушает код.

Memory.CallFunctionReturn имеет тот же интерфейс, но дополнительно записывает результат функции в переменную.

Memory.CallMethod вызывает метод объекта:

    Memory.CallMethod(0x2345678, 0x7001234, 2, 0, 1000, 2000)

Второй параметр (0x7001234) — это адрес объекта. Параметр pop всегда равен 0 (метод использует соглашение thiscall).

Чтобы вызвать метод и получить от него результат, используйте Memory.CallMethodReturn.

Обратите внимание, что все аргументы читаются как 32-битные целые числа со знаком. Если вам нужно предоставить аргумент типа float, используйте Memory.FromFloat, например:

    Memory.CallFunction(0x1234567, 1, 1, Memory.FromFloat(123.456))

CLEO Redux поддерживает вызов сторонних функций с параметрами до 16.

Обратите внимание, что для использования любого из методов вызова требуется mem разрешение.

Удобные методы с объектом Fn

Memory.Fn предоставляет множество удобных методов для вызова различных типов внешних функций.

Fn: {
        Cdecl(address: int): (...funcParams: int[]) => int;
        CdeclFloat(address: int): (...funcParams: int[]) => float;
        CdeclI8(address: int): (...funcParams: int[]) => int;
        CdeclI16(address: int): (...funcParams: int[]) => int;
        CdeclI32(address: int): (...funcParams: int[]) => int;
        CdeclU8(address: int): (...funcParams: int[]) => int;
        CdeclU16(address: int): (...funcParams: int[]) => int;
        CdeclU32(address: int): (...funcParams: int[]) => int;

        Stdcall(address: int): (...funcParams: int[]) => int;
        StdcallFloat(address: int): (...funcParams: int[]) => float;
        StdcallI8(address: int): (...funcParams: int[]) => int;
        StdcallI16(address: int): (...funcParams: int[]) => int;
        StdcallI32(address: int): (...funcParams: int[]) => int;
        StdcallU8(address: int): (...funcParams: int[]) => int;
        StdcallU16(address: int): (...funcParams: int[]) => int;
        StdcallU32(address: int): (...funcParams: int[]) => int;

        Thiscall(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallFloat(address: int, struct: int): (...funcParams: int[]) => float;
        ThiscallI8(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallI16(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallI32(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallU8(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallU16(address: int, struct: int): (...funcParams: int[]) => int;
        ThiscallU32(address: int, struct: int): (...funcParams: int[]) => int;
    }

Эти методы предназначены для охвата всех возможных сигнатур функций. Например, этот код:

    Memory.CallMethod(0x2345678, 0x7001234, 2, 0, 1000, 2000)

Также можно записать как:

    Memory.Fn.Thiscall(0x2345678, 0x7001234)(1000, 2000)

Обратите внимание на несколько ключевых отличий. Прежде всего, методы Memory.Fn не вызывают внешнюю функцию напрямую. Вместо этого они возвращают новую функцию JavaScript, которую можно сохранить в переменной и повторно использовать для многократного вызова связанной внешней функции с разными аргументами:

    var myMethod = Memory.Fn.Thiscall(0x2345678, 0x7001234);
    myMethod(1000, 2000); // вызывает метод 0x2345678 с аргументами 1000 и 2000
    myMethod(3000, 5000); // вызывает метод 0x2345678 с аргументами 3000 и 5000

Второе отличие состоит в том, что отсутствуют параметры numParams и pop. Каждый метод Fn вычисляет их автоматически.

По умолчанию возвращаемый результат считается 32-битным целым числом со знаком. Если функция возвращает другой тип (значение с плавающей запятой или целое число со знаком), используйте один из методов, соответствующих сигнатуре функции, например:

    var flag = Memory.Fn.CdeclU8(0x1234567)()

Этот код вызывает функцию cdecl по адресу 0x1234567 без аргументов и сохраняет результат в виде 8-битного целого числа без знака.

Поиск адресов памяти в re3 и reVC

Поскольку re3 и reVC используют функцию рандомизации адресного пространства (ASLR), может быть трудно найти нужные адреса. CLEO Redux предоставляет вспомогательную функцию Memory.Translate которая принимает имя функции или переменной и возвращает ее текущий адрес. Если запрошенный символ не найден, результат равен 0.

    var addr = Memory.Translate("CTheScripts::MainScriptSize");

    // проверить, не равен ли адрес нулю
    if (addr) {
        showTextBox("MainScriptSize = " + Memory.ReadI32(addr, 0))
    }

На данный момент Memory.Translate следует использовать только в re3 и reVC. В других играх в большинстве случаев вы будете получать 0.

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

Использование объекта памяти

Внутренний объект Memory предоставляет методы для доступа и управления данными или кодом в текущем процессе. Он имеет следующий интерфейс:

interface Memory {
    ReadFloat(address: int, vp: boolean, ib: boolean): float;
    WriteFloat(address: int, value: float, vp: boolean, ib: boolean): void;
    ReadI8(address: int, vp: boolean, ib: boolean): int;
    ReadI16(address: int, vp: boolean, ib: boolean): int;
    ReadI32(address: int, vp: boolean, ib: boolean): int;
    ReadU8(address: int, vp: boolean, ib: boolean): int;
    ReadU16(address: int, vp: boolean, ib: boolean): int;
    ReadU32(address: int, vp: boolean, ib: boolean): int;
    WriteI8(address: int, value: int, vp: boolean, ib: boolean): void;
    WriteI16(address: int, value: int, vp: boolean, ib: boolean): void;
    WriteI32(address: int, value: int, vp: boolean, ib: boolean): void;
    WriteU8(address: int, value: int, vp: boolean, ib: boolean): void;
    WriteU16(address: int, value: int, vp: boolean, ib: boolean): void;
    WriteU32(address: int, value: int, vp: boolean, ib: boolean): void;
    Read(address: int, size: int, vp: boolean, ib: boolean): int;
    Write(address: int, size: int, value: int, vp: boolean, ib: boolean): void;

    ToFloat(value: int): float;
    FromFloat(value: float): int;
    ToU8(value: int): int;
    ToU16(value: int): int;
    ToU32(value: int): int;
    ToI8(value: int): int;
    ToI16(value: int): int;
    ToI32(value: int): int;

    CallFunction(address: int, ib: boolean, numParams: int, ...funcParams: int[]): void;
    CallFunctionReturn(address: int, ib: boolean, numParams: int, ...funcParams: int[]): int;
    CallFunctionReturnFloat(address: int, ib: boolean, numParams: int, ...funcParams: int[]): float;

    Fn: {
        X64(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64Float(address: int, ib: boolean): (...funcParams: int[]) => float;
        X64I8(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64I16(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64I32(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64U8(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64U16(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64U32(address: int, ib: boolean): (...funcParams: int[]) => int;
    }
}

Чтение и запись значений

Группа методов доступа к памяти (ReadXXX/WriteXXX) может использоваться для чтения или изменения значений, хранящихся в памяти. Каждый метод предназначен для определенного типа данных. Чтобы изменить значение с плавающей запятой (которое в исходной игре занимает 4 байта), используйте Memory.WriteFloat, например:

    Memory.WriteFloat(address, 1.0, false, false)

Где address — это переменная, хранящая адрес памяти, 1.0 — это значение для записи, первое «false» означает, что нет необходимости изменять защиту памяти с помощью VirtualProtect (адрес уже доступен для записи). Второй false — это значение флага ib, который предписывает CLEO рассматривать address либо как абсолютный адрес (ib = false), либо как относительное смещение к текущему базовому адресу образа (ib = true). Поскольку в окончательных версиях используется функция ASLR, их абсолютные адреса памяти меняются при запуске игры из-за изменения начального адреса. Рассмотрим следующий пример:

0x1400000000 ImageBase
...
...
0x1400000020 SomeValue

Вы хотите изменить SomeValue, которое в настоящее время находится по адресу 0x1400000020. Вы можете сделать это с помощью Memory.Write(0x1400000020, 1, 1, false, false). Однако при следующем запуске игры расположение памяти может выглядеть так:

0x1500000000 ImageBase
...
...
0x1500000020 SomeValue

Эффективно ломая сценарий. В этом случае рассчитайте относительное смещение от базы изображения (0x1500000020 - 0x1500000000 = 0x20), которое будет постоянным для конкретной версии игры. Используйте Memory.Write следующим образом: Memory.Write(0x20, 1, 1, false, true). CLEO суммирует смещение (0x20) с текущим значением базы изображения (0x1400000000, 0x1500000000 и т. д.) и записывает по правильному абсолютному адресу.

Для вашего удобства вы можете узнать текущее значение базы образа в cleo_redux.log, например:

09:27:35 [INFO] Image base address 0x7ff7d1f50000

Точно так же, чтобы прочитать значение из памяти, используйте один из методов ReadXXX, в зависимости от того, какой тип данных содержит адрес памяти. Например, чтобы прочитать 8-битное целое число со знаком (также известное как char или uint8), используйте Memory.ReadI8, например:

    var x = Memory.ReadI8(offset, true, true)

Переменная x теперь содержит 8-битное целое число в диапазоне (0..255). Чтобы показать возможные варианты, в этом примере в качестве последнего аргумента используется true, что означает, что атрибут защиты по умолчанию для этого адреса будет изменен на PAGE_EXECUTE_READWRITE перед чтением.

    var gravity = Memory.ReadFloat(gravityOffset, false, true);
    gravity += 0.05;
    Memory.WriteFloat(gravityOffset, gravity, false, true);

Наконец, последние два метода Read и Write — это то, что другие методы используют под капотом. Они имеют прямую привязку к коду Rust, который читает и записывает память. В коде JavaScript вы можете использовать входные аргументы размером до 53-битных чисел.

Параметр size в методе Read может быть только 1, 2, 4 или 8. CLEO обрабатывает value` как целое число со знаком, хранящееся в формате с прямым порядком байтов.

В методе Write допускается любой size больше 0. Размеры «3, 5, 6, 7 и 9 и далее могут использоваться только вместе с одним байтом value. CLEO использует их для заполнения непрерывного блока памяти, начиная с address, заданным value (подумайте об этом как о memset в C++).

    Memory.Write(offset, 0x90, 10, true, true) // "noping" 10 байт кода, начиная с базы смещения+изображения

Обратите внимание, что для использования любого из методов чтения/записи требуется mem разрешение.

Метод приведения типов

По умолчанию методы Read и Write обрабатывают данные как целочисленные значения со знаком. Это может быть неудобно, если память содержит значение с плавающей запятой в формате IEEE 754 или большое 32-битное целое число со знаком (например, указатель). В этом случае используйте методы приведения ToXXX/FromXXX. Они действуют аналогично оператору reinterpret_cast в C++.

Чтобы получить представление о том, чего ожидать от этих методов, см. следующие примеры:

    Memory.FromFloat(1.0) => 1065353216
    Memory.ToFloat(1065353216) => 1.0
    Memory.ToU8(-1) => 255
    Memory.ToU16(-1) => 65535
    Memory.ToU32(-1) => 4294967295
    Memory.ToI8(255) => -1
    Memory.ToI16(65535) => -1
    Memory.ToI32(4294967295) => -1

В качестве альтернативы используйте соответствующие методы для чтения/записи значения в виде числа с плавающей запятой (ReadFloat/WriteFloat) или целого числа без знака (ReadUCX/WriteUXXX).

Вызов внешних функций

Объект Memory позволяет вызвать чужую (собственную) функцию по ее адресу одним из следующих способов:

  • Memory.CallFunction
  • Memory.CallFunctionReturn
  • Memory.CallFunctionReturnFloat
    Memory.CallFunction(0xEFFB30, true, 1, 13)

Где 0xEFFB30 — это смещение функции относительно IMAGE BASE (представьте себе, что это случайный начальный адрес игровой памяти), true — это флаг ib (см. ниже), 1 – количество входных аргументов, и 13 — единственный аргумент, передаваемый в функцию.

Параметр ib в Memory.CallFunction имеет то же значение, что и в командах чтения/записи памяти. При значении true CLEO добавляет текущий известный адрес базы образа к значению, указанному в качестве первого аргумента, для вычисления абсолютного адреса памяти функции. Если установлено значение false, первый аргумент не изменяется.

Чтобы передать функции значения с плавающей запятой, преобразуйте значение в целое число, используя Memory.FromFloat:

    Memory.CallFunction(0x1234567, true, 1, Memory.FromFloat(123.456));

Возвращаемое значение функции, вызванной с помощью Memory.CallFunction, игнорируется. Чтобы прочитать результат, используйте Memory.CallFunctionReturn с теми же параметрами. Используйте Memory.CallFunctionReturnFloat для вызова функции, которая возвращает значение с плавающей запятой.

CLEO Redux поддерживает вызов сторонних функций с параметрами до 16.

Обратите внимание, что для использования любого из методов вызова требуется mem разрешение.

Удобные методы с объектом Fn

Memory.Fn предоставляет удобные методы для вызова различных типов внешних функций.

    Fn: {
        X64(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64Float(address: int, ib: boolean): (...funcParams: int[]) => float;
        X64I8(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64I16(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64I32(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64U8(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64U16(address: int, ib: boolean): (...funcParams: int[]) => int;
        X64U32(address: int, ib: boolean): (...funcParams: int[]) => int;
    }

Эти методы предназначены для охвата всех поддерживаемых типов возврата. Например, этот код:

    Memory.CallFunction(0xEFFB30, true, 1, 13)

Также можно записать как:

    Memory.Fn.X64(0xEFFB30, true)(13)

Обратите внимание на несколько ключевых отличий. Прежде всего, методы Memory.Fn не вызывают внешнюю функцию напрямую. Вместо этого они возвращают новую функцию JavaScript, которую можно сохранить в переменной и повторно использовать для многократного вызова связанной внешней функции с разными аргументами:

    var f = Memory.Fn.X64(0xEFFB30, true);
    f(13) // вызывает функцию 0xEFFB30 с аргументом 13
    f(11) // вызывает метод 0xEFFB30 с аргументом 11

Второе отличие состоит в том, что здесь нет параметра numParams. Каждый метод Fn вычисляет это автоматически.

По умолчанию возвращаемый результат считается 64-битным целым числом со знаком. Если функция возвращает другой тип (например, логическое значение), используйте один из методов, соответствующих сигнатуре функции:

    var flag = Memory.Fn.X64U8(0x1234567, true)()

Этот код вызывает функцию по адресу 0x1234567 + IMAGE_BASE без аргументов и сохраняет результат как 8-битное целое число без знака.

    var float = Memory.Fn.X64Float(0x456789, true)()

Этот код вызывает функцию по адресу 0x456789 + IMAGE_BASE без аргументов и сохраняет результат как значение с плавающей запятой.

Устаревшие

Использование следующих команд не рекомендуется.

op

op(opcode_id, ...input_args) - низкоуровневая функция для выполнения любой внутриигровой команды с опкодом {opcode_id}.

Для команд, возвращающих одно значение, результатом является это значение.

Для команд, возвращающих несколько значений, результатом является объект, в котором каждый ключ соответствует возвращаемому значению. Имена ключей соответствуют именам выходов, указанным в определении команды.

Для условных команд результатом является логическое значение `true` или `false`.
op(0x00c0, 12, 30); // устанавливаем время суток на 12:30
var pos = op(0x0054, 0); // возвращает вектор координат игрока 0 {x, y, z}
showTextBox("Player pos:", " x = ", pos.x, " y = ", pos.y, " z = ", pos.z);
if (op(0x0248, 101)) {
  // Проверяем условие.
  showTextBox("Model with id 101 has been loaded");
}

Решение проблем

Если CLEO не работает с re3 или reVC

CLEO Redux поддерживает только «Windows D3D9 MSS 32bit» версию re3 или reVC.

При запуске на re3 и reVC убедитесь, что в каталоге игры есть файл re3.pdb (для re3) или reVC.pdb (для reVC). Из-за динамического характера адресов памяти в этих реализациях CLEO Redux использует отладочную информацию, хранящуюся в файле PDB, для правильного определения своего местоположения.

Если CLEO крашит San Andreas: The Definitive Edition

  • Убедитесь, что вы установили 64-битную версию Ultimate ASI Loader (прямая ссылка на последний релиз).
    • Поместите version.dll в GTA San Andreas - Definitive Edition\Gameface\Binaries\Win64
  • убедитесь, что у вас установлена последняя версия CLEO Redux (0.8.2 и выше)
  • удалить файлы конфигурации из Documents\Rockstar Games\GTA San Andreas Definitive Edition\Config\WindowsNoEditor
  • запустить игру (или Rockstar Games Launcher) от имени администратора

Если CLEO не может создавать файлы в GTA San Andreas - Definitive Edition\Gameface\Binaries\Win64, он будет использовать другой каталог в C:\Users\<ваше_имя>\AppData\Roaming\CLEO Redux. Там должен быть cleo_redux.log и папка CLEO, куда попадают все ваши скрипты.

Лог

CLEO регистрирует важные события и ошибки в файле cleo_redux.log расположенном в папке с игрой (или C:\Users\<ваше_имя_пользователя>\AppData\Roaming\CLEO Redux, см. Первичная настройка примечание). Этот файл перезаписывается при каждом запуске игры. Если у вас возникнут какие-либо проблемы при использовании CLEO Redux, начните исследовать основную причину с этого файла.

Чтобы транслировать события в вашем терминале во время тестирования скрипта, запустите:

tail -f cleo_redux.log

tail - это команда unix, поэтому необходима совместимая среда (например, Git Bash).

Здесь вы можете найти ответы на часто задаваемые вопросы о поддержке ремастера The Trilogy.

Какие версии поддерживаются?

  • GTA III: The Definitive Edition 1.0.0.14718 (Title Update 1.03), 1.0.0.15284 (Title Update 1.04)
  • GTA Vice City: The Definitive Edition 1.0.0.14718 (Title Update 1.03), 1.0.0.15399 (Title Update 1.04)
  • San Andreas: The Definitive Edition 1.0.0.14296, 1.0.0.14388, 1.0.0.14718 (Title Update 1.03), 1.0.0.15483 (Title Update 1.04)

Как установить CLEO Redux в The Definitive Edition?

  • Загрузите и установите Ultimate ASI Loader x64 от ThirteenAG (поместите version.dll в каталог Gameface\Binaries\Win64).

  • Скопируйте cleo_redux64.asi в тот же каталог.

  • Запустите игру один раз, и вы должны создать новый каталог CLEO в том же каталоге. Если этого не произошло, проверьте ниже.

Что делать, если я не могу найти каталог CLEO?

У многих людей запуск игры с установленным CLEO Redux приводит к немедленному вылету. Это происходит, если в текущем каталоге (Win64) нет прав на запись. Чтобы исправить эту проблему, CLEO использует альтернативный путь в C:\Users\<Ваше_имя_пользователя>\AppData\Roaming\CLEO Redux. Там можно найти cleo_redux.log и каталог CLEO. См. также руководство по устранению неполадок.

Как удалить CLEO Redux?

  • Удалите cleo_redux64.asi.
  • Удалите папку CLEO (необязательно).
  • Удалите cleo_redux.log (необязательно)

Есть ли отличия от поддержки классических игр?

Есть. CLEO не отображает версию в меню игры. Также CLEO может запускать только JS-скрипты в GTA III и GTA VC. В San Andreas поддерживаются как скрипты CS, так и JS.

Могу ли я использовать оригинальные опкоды?

Да, ты можешь. Обратитесь к Sanny Builder Library: https://library.sannybuilder.com/#/sa_unreal. Обратите внимание, что некоторые коды операций были изменены по сравнению с классическими играми, поэтому не ожидайте, что все будет работать так же, как в классических играх. Если вы столкнулись с проблемой, найдите помощь в нашем Discord.

Как узнать, какие команды можно использовать в JavaScript?

После каждого запуска игры CLEO создает файл d.ts в каталоге CLEO.config. Он называется gta3.d.ts, vc.d.ts или sa.d.ts в зависимости от игры. В этом файле перечислены все поддерживаемые функции и методы, которые вы можете использовать в коде JavaScript.

Чтобы включить автозаполнение в VS Code, включите в свой JS-скрипт следующую строку:

/// <reference path=".config/sa.d.ts" />

Обновите имя файла соответственно в зависимости от того, для какой игры предназначен ваш скрипт.

Могу ли я использовать опкоды CLEO?

Опкоды из библиотеки CLEO (CLEO 4 или CLEO для GTA III и Vice City) не поддерживаются. Но CLEO Redux добавляет свои новые опкоды для некоторых операций.

Обратите внимание, что Sanny Builder еще не поддерживает эти новые коды операций «из коробки». Чтобы включить новые коды операций в ваших сценариях CS, добавьте следующие строки поверх вашего сценария:

{$O 0C00=1,  is_key_pressed %1d% }
{$O 0C01=3,%3d% = %1d% + %2d% }
{$O 0C02=3,%3d% = %1d% - %2d% }
{$O 0C03=3,%3d% = %1d% * %2d% }
{$O 0C04=3,%3d% = %1d% / %2d% }
{$O 0C05=0,terminate_this_custom_script }
{$O 0C06=5,write_memory %1d% size %2d% value %3d% virtual_protect %4d% ib %5d% }
{$O 0C07=5,%5d% = read_memory %1d% size %2d% virtual_protect %3d% ib %4d% }
{$O 0C08=-1,call_function %1d% ib %2d% num_params %3d%}
{$O 0C09=-1,call_function_return %1d% ib %2d% num_params %3d%}

Можно ли работать с памятью игры или вызывать функции игры?

Да, проверьте Руководство по использованию памяти.

Как компилировать CLEO-скрипты с помощью Sanny Builder?

Используйте режим SA Mobile для компиляции CLEO-скриптов для San Andreas: The Definitive Edition. Обратите внимание, что CLEO Redux не поддерживает CS-скрипты в GTA III: DE и VC: DE. JS-скрипты поддерживаются во всех играх.

Я не могу найти здесь ответ на свой вопрос, куда мне обратиться?

Другие особенности

CLEO Redux фокусируется на улучшении опыта разработки и упрощении процесса написания сценариев.

Интеграция с Visual Studio Code

VS Code имеет широкие возможности настройки. CLEO Redux генерирует типизации для всех поддерживаемых команд, которые вы можете использовать при написании JavaScript в VS Code. Добавьте следующую строку в свой скрипт *.js, чтобы получить полную поддержку автозаполнения:

Для GTA III или re3:

/// <reference path=".config/gta3.d.ts" />

Для Vice City или reVC

/// <reference path=".config/vc.d.ts" />

Для San Andreas

/// <reference path=".config/sa.d.ts" />

Эта строка указывает VS Code, где искать определения команд для функции автозаполнения. Путь может быть относительным относительно файла сценария или быть абсолютным. Дополнительную информацию на официальном портале TypeScript.

SCM Лог

CLEO Redux имеет встроенную поддержку отслеживания инструкций SCM. Чтобы включить трассировку для выполняемых команд, откройте cleo.ini и измените LogOpcodes на 1. Обратите внимание, что это может сильно повлиять на производительность игры из-за частых микрозадержек во время записи в файл журнала. Используйте этот параметр только в целях отладки.

Горячая перезагрузка

CLEO отслеживает активные скрипты и перезагружает их в игре по мере их изменения

Добавление нового файла скрипта в директорию CLEO или удаление во время игры запускает или останавливает скрипт автоматически

Горячая перезагрузка для CS-скриптов не работает, когда CLEO Redux работает вместе с CLEO Library (например, в классическом San Andreas).

Использование FXT

Статические файлы FXT

CLEO Redux может загружать и обслуживать статический текстовый контент. Создайте новый файл с расширением .fxt и поместите его в папку CLEO\CLEO_TEXT. Имя файла может быть любым допустимым именем.

Каждый файл FXT содержит список записей ключ-значение в следующем формате:

<KEY1> <TEXT1>
<KEY2> <TEXT2>
...
<KEYN> <TEXTN>

Между ключом и значением должен быть один пробел. Максимальная длина ключа составляет 7 символов. Попробуйте использовать уникальные ключи, которые вряд ли будут конфликтовать с другими записями. Длина текста не ограничена, однако каждая игра может накладывать свои ограничения.

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

Вы также можете найти редактор файлов FXT на сайте cleo.li: https://cleo.li/download.html

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

Text.PrintHelp('KEY1') // будет отображаться <TEXT1>

Вы можете найти команды, доступные в каждой игре, в библиотеке Sanny Builder, например. для Сан-Андреас: DE https://library.sannybuilder.com/#/sa_unreal/classes/Text

FxtStore

CLEO Redux предоставляет интерфейс для управления произвольным текстом непосредственно в коде JavaScript. Существует статическая переменная с именем FxtStore со следующим интерфейсом:

declare interface FxtStore {
  /**
   * Вставляет новое текстовое содержимое в хранилище fxt сценария, перезаписывая предыдущее содержимое и затеняя статические fxt с тем же ключом.
   * Ключ @param Ключ GXT, который можно использовать в текстовых командах (максимум 7 символов).
   * Текстовое содержимое значения @param.
   */
  insert(key: string, value: string): void;
  /**
   * Удаляет текстовое содержимое, связанное с ключом, в локальном хранилище fxt.
   * Ключ @param Ключ GXT.
   */
  delete(key: string): void;
}

Используя FxtStore, вы можете создавать уникальные ключи и значения в сценарии и помещать их в локальное хранилище FXT. Каждому скрипту принадлежит личное хранилище, и ключи одного скрипта не будут конфликтовать с другими скриптами. Также ключи, определенные в FxtStore, будут дублировать те же ключи, что и в статических файлах FXT. Рассмотрим пример:

custom.fxt:

MY_KEY Text from FXT file

custom.js:

Text.PrintHelp('MY_KEY') // Это отображает "Текст из файла FXT".
FxtStore.insert('MY_KEY', 'Text from script');
Text.PrintHelp('MY_KEY') // Это отображает "Текст из сценария".
FxtStore.delete('MY_KEY')
Text.PrintHelp('MY_KEY') // Это отображает "Текст из файла FXT" снова.

Частное хранилище FXT не поддерживается в San Andreas: The Definitive Edition. Каждый скрипт изменяет глобальное хранилище FXT. Это поведение может измениться в будущем.

Пользовательский текст может создаваться динамически, например:

while(true) {
    wait(0);
    FxtStore.insert('TIME', 'Timestamp: ' + Date.now());
    Text.PrintHelp('TIME') // Отобразится "Timestamp: " и обновленное значение timestamp.
}

Неподдерживаемые или ограниченные сценарии поддержки

Несмотря на все наши усилия, некоторые сценарии, доступные в игре, не поддерживаются или поддерживаются с ограничениями CLEO Redux. Некоторые из них обусловлены природой формата SCM или языка JavaScript или трудностями соединения JavaScript и нативного кода.

Посетите страницу поддержки функций чтобы узнать высокоуровневые фичи и статус их поддержки в разных играх.

Известно, что следующие элементы не работают, и нет конкретных сроков их исправления.

Неподдерживаемые функции в CS

  • В играх x64 (SA:DE) вы не можете читать и записывать 64-битные значения, так как скриптовый движок поддерживает только 32-битные значения. Возможно, вам придется использовать другие средства для доступа к памяти игры (например, из JavaScript).

Неподдерживаемые функции в JS

  • Команды, требующие переменной scm (например, таймеры обратного отсчета). Проблема с отслеживанием.

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

  • нельзя вызывать игровые функции, которым нужны ссылки на переменные для сохранения результата. Нет синтаксиса "take an address of the variable".

Встраивание на пользовательских хостах

CLEO Redux может встраивать и запускать JS-скрипты на неизвестном (т.е. не поддерживаемом официально) хосте. Хост — это приложение, в котором загружается или внедряется процесс cleo_redux.asi или cleo_redux64.asi и в котором работает среда выполнения CLEO. Эта функция является экспериментальной и может быть изменена в любой момент.

Загрузка в пользовательский процесс

Существует несколько способов загрузки файла ASI в целевой процесс. Ultimate ASI Loader — один из них. Или используйте любой DLL-инжектор, доступный на GitHub. При необходимости хост может самостоятельно загрузить файл CLEO ASI как динамическую библиотеку.

Запуск среды выполнения CLEO

Чтобы запустить CLEO на неизвестном хосте сразу после загрузки (в автономном режиме), откройте файл конфигурации и установите EnableSelfHost на 1. CLEO Redux автоматически сканирует директорию CLEO на наличие плагинов и скриптов и запускает их.

Ручное управление средой выполнения

Хост может запускать среду выполнения и продвигать ее основной цикл с помощью методов SDK RuntimeInit и RuntimeNextTick.

Вот как это можно реализовать на Rust:

[dependencies]
ctor = "0.1.21"
cleo_redux_sdk = "0.0.6"

#![allow(unused)]
fn main() {
use std::time;
use ctor::*;
use cleo_redux_sdk;

#[cfg_attr(target_arch = "x86", link(name = "cleo_redux"))]
#[cfg_attr(target_arch = "x86_64", link(name = "cleo_redux64"))]

#[ctor]
fn init() {
    use std::thread;

    // загружаем CLEO-скрипты, FXT, включаем файловый наблюдатель
    cleo_redux_sdk::runtime_init();

    // переменные времени инициализации
    const FPS: i32 = 30;
    let time_step = 1000 / FPS;
    let started = time::Instant::now();

    thread::spawn(move || loop {
        let current_time = started.elapsed().as_millis() as u32;

        // продвигаем основной цикл, предоставляя значения current_time и time_step
        // current_time используется для определения того, должен ли скрипт "просыпаться" после команды ожидания.
        // time_step используется для увеличения переменных TIMERA и TIMERB
        cleo_redux_sdk::runtime_next_tick(current_time, time_step);


        // пауза по крайней мере на time_step миллисекунд
        thread::sleep(time::Duration::from_millis(time_step as u64));
    });
}
}

Доступные команды

В автономном режиме CLEO Redux поддерживает собственные переменные и функции и команды, созданные с помощью SDK. Он использует определения команд для неизвестного хоста из библиотеки Sanny Builder (доступно для 32-разрядных и 64-разрядных). CLEO Redux автоматически загружает необходимые файлы при первом запуске.

Вы можете использовать все стандартные функции JavaScript. Список доступных команд можно увидеть в автоматически сгенерированном файле .config/unknown.d.ts.

CLEO Redux SDK

SDK позволяет создавать новые скриптовые команды для любой игры, которую поддерживает CLEO Redux. Он не зависит от названия игры и базовой среды выполнения (CS или JS). На данный момент CLEO предоставляет SDK для языков C++ и Rust.

Поддержка платформ

CLEO Redux предоставляет SDK как для 32-битных, так и для 64-битных игр. Между ними есть одно заметное отличие: на 32-битной платформе функции SDK GetIntParam и SetIntParam работают с 32-битными числами со знаком, тогда как на 64-битной платформе они работают с 64-битными числами со знаком (объявлено как тип isize).

Структура плагина

Каждый плагин представляет собой динамическую библиотеку с расширением .cleo, которую необходимо поместить в CLEO\CLEO_PLUGINS. CLEO Redux сканирует этот каталог при запуске и загружает все файлы .cleo, используя функцию WinAPI LoadLibrary. Чтобы зарегистрировать обработчик для новой команды, плагин должен вызвать RegisterCommand в функции DllMain. Как только пользовательский скрипт встречает эту команду, CLEO Redux вызывает обработчик с одним аргументом, который является указателем на текущий контекст. Этот указатель необходимо использовать для вызова других методов SDK.

Соглашение об именовании

Для 64-битных плагинов рекомендуется использовать 64 в именах (например, myplugin64.cleo).

Небезопасные команды

Команды, использующие низкоуровневый WinAPI и потенциально способные нанести вред среде пользователя, должны быть явно зарегистрированы с помощью токена разрешения (третий аргумент RegisterCommand). Пользователь может запретить использование небезопасных команд в скриптах с помощью настройки разрешений. На данный момент используются три токена разрешений: mem, fs и dll. Ими отмечаются команды, работающие с хост-процессом, пользовательскими файлами и внешними библиотеками.

Командный интерфейс

CLEO Redux использует Sanny Builder Library, чтобы узнать интерфейс любой команды. Чтобы новая команда стала доступной в сценариях, файл JSON (gta3.json, vc.json, sa.json) должен иметь определение команды, включая имя, совпадающее со значением, которое плагин использует RegisterCommand с. Например. если подключаемый модуль регистрирует команду SHOW_MESSAGE, в файле JSON должна быть команда со свойством имени, установленным на SHOW_MESSAGE. Количество и порядок входных и выходных параметров в определении должны соответствовать порядку методов, используемых подключаемым модулем (т. е. GetXXXParam для каждого входного аргумента и SetXXXParam для каждого выходного аргумента).

Запрос кодов операций

Коды операций назначаются новым командам в Sanny Builder Library в зависимости от их доступности, сходства с существующими командами в других играх и других факторов. Чтобы запросить код операции, обратитесь к сопровождающим Sanny Builder Library на GitHub https://github.com/sannybuilder/library/issues.

Зачем использовать имена команд, а не идентификатор для поиска команды?

Одной из распространенных проблем с плагинами CLEO Library было то, что команды, созданные разными людьми, часто имели конфликты идентификаторов. Если два плагина добавляют команды с одинаковым идентификатором, использовать их оба невозможно. Использование строковых имен сводит к минимуму коллизии с пользовательскими плагинами, а также с собственными кодами операций. Определения библиотеки гарантируют, что каждая команда требует только доступный идентификатор. Также это помогает отслеживать и документировать плагины в одном месте.

Версия SDK

Текущая версия 1. Изменения в SDK увеличат это число на единицу.

Соглашение о разрешении пути

Строковые аргументы, представляющие путь к каталогу или файлу, должны быть нормализованы с помощью функции SDK ResolvePath. Эта функция принимает путь и возвращает абсолютный путь, разрешенный по следующим правилам:

  • абсолютный путь разрешается как есть
  • путь, начинающийся с "CLEO/" или "CLEO\", разрешается относительно каталога CLEO, который либо
    • {игра}\CLEO или
    • {пользователь}\AppData\Roaming\CLEO Redux\CLEO
  • все остальные пути разрешаются относительно текущего рабочего каталога (каталог игры)

Строковые аргументы

Строки, передаваемые в методы SDK имеют кодировку UTF-8.

Если сценарий использует целочисленное значение вместо ожидаемой строки, SDK обрабатывает это число как указатель на последовательность символов UTF-8, заканчивающуюся нулем, для чтения или на достаточно большой буфер для сохранения результата:

IniFile.WriteString(0xDEADBEEF, "my.ini", "section", "key")

SDK прочитает строку с адреса 0xDEADBEEF и запишет ее в ini-файл.

0AF4: read_string_from_ini_file 'my.ini' section 'section' key 'key' store_to 0xDEADBEEF

SDK прочитает строку из ini-файла и запишет ее по адресу 0xDEADBEEF.

C++ SDK

Пользовательские плагины могут вызывать методы, предоставляемые CLEO Redux, используя предоставленный файл .lib. Включите cleo_redux_sdk.h в свой проект DLL и свяжите двоичный файл с cleo_redux.lib (или cleo_redux64.lib, если целевая платформа x86_64), и вы сможете начать писать новые команды.

Пример

См. подключаемый модуль IniFiles, который включает проект для Visual Studio 2019. Он добавляет статический класс IniFile со следующими методами:

interface IniFile {
    ReadFloat(path: string, section: string, key: string): float | undefined;
    ReadInt(path: string, section: string, key: string): int | undefined;
    ReadString(path: string, section: string, key: string): string | undefined;
    WriteFloat(value: float, path: string, section: string, key: string): boolean;
    WriteInt(value: int, path: string, section: string, key: string): boolean;
    WriteString(value: string, path: string, section: string, key: string): boolean;
}

Дополнительную информацию см. в библиотеке Sanny Builder: https://library.sannybuilder.com/#/sa_unreal/classes/IniFile. Для использования класса IniFile требуется fs разрешение.

Rust SDK

Rust SDK использует интерфейс, аналогичный интерфейсу C++, с некоторыми дополнительными методами переноса, позволяющими легко конвертировать типы C и Rust. Заголовочный файл доступен в виде crate на crates.io. См. документацию здесь.

Пример

См. плагин Dylib. Он добавляет класс DynamicLibrary со следующими методами:

declare class DynamicLibrary {
    constructor(handle: number);
    static Load(libraryFileName: string): DynamicLibrary | undefined;
    free(): void;
    getProcedure(procName: string): int | undefined;
}

Дополнительную информацию см. в Sanny Builder Library: https://library.sannybuilder.com/#/sa_unreal/classes/DynamicLibrary. Для использования класса DynamicLibrary требуется dll разрешение.

C++ SDK

На этой странице описаны основные шаги, необходимые для создания новой пользовательской команды с помощью C++ SDK, которая считывает два целочисленных аргумента и возвращает их сумму.

Это руководство было проверено для Visual Studio 2019 (Community Edition) и может отличаться для других IDE и компиляторов.

Разработка плагина

  • Создайте новый проект библиотеки динамической компоновки (DLL). В Настройки проекта->Дополнительно установите для Расширение целевого файла значение .cleo.

  • Загрузите cleo_redux_sdk.h и добавьте его в свой проект.

#include "cleo_redux_sdk.h"

Если этот заголовочный файл находится за пределами каталога вашего проекта, вам необходимо добавить папку с этим файлом в «Настройки проекта->Каталоги VC++->Включить каталоги», чтобы позволить Visual Studio обнаружить этот файл.

  • В Настройки проекта->Линкер->Вход добавьте полный путь к cleo_redux.lib (если вы разрабатываете 32-битный плагин) или в cleo_redux64.lib для 64-битного плагина.

  • в dllmain.cpp создайте новый статический класс с функцией-конструктором. Этот конструктор будет вызван, как только CLEO загрузит ваш плагин

class TestPlugin {
public:
	TestPlugin() {
		Log("My Test Plugin");
	}
} TestPlugin;
  • в конструкторе класса плагина вызовите функцию RegisterCommand для каждой новой команды
class TestPlugin {
public:
	TestPlugin() {
		Log("My Test Plugin");
        RegisterCommand("INT_ADD", IntAdd);
	}

    static HandlerResult IntAdd(Context ctx) {
        return HandlerResult::CONTINUE;
    }
} TestPlugin;
  • реализуйте обработчики для новых команд. Каждый обработчик команд получает один входной аргумент — Context ctx. Этот аргумент используется для вызова других методов SDK.
class TestPlugin {
public:
	TestPlugin() {
		Log("My Test Plugin");
        RegisterCommand("INT_ADD", IntAdd);
	}

    static HandlerResult IntAdd(Context ctx) {
        auto arg1 = GetIntParam(ctx);
        auto arg2 = GetIntParam(ctx);
        SetIntParam(ctx, arg1 + arg2);

        return HandlerResult::CONTINUE;
    }
} TestPlugin;
  • скомпилируйте проект и поместите файл .cleo в папку CLEO_PLUGINS.

  • добавить определение команды в файл JSON для целевой игры (например, gta3.json для GTA III или unknown_x86.json для хоста Unknown (x86)). Каждый GetXXXParam/SetXXXParam должен быть связан с вводом или выводом в определении команды.

{
    "input": [
        { "name": "arg1", "type": "int" },
        { "name": "arg2", "type": "int" }
    ],
    "output": [
        { "name": "result", "type": "int", "source": "var_any" }
    ],
    "id": "0DDD",
    "name": "INT_ADD",
    "num_params": 3,
    "short_desc": "Adds together two integer values and writes the result into the variable",
},

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

Лучший способ создать правильное определение команды — использовать Sanny Builder Library. Если вы планируете поделиться подключаемым модулем и сделать его доступным через Интернет, подумайте о том, чтобы связаться с сопровождающими библиотеки, чтобы ваша команда была опубликована там.

  • теперь вы можете использовать новую команду в коде с помощью команды native
var result = native("INT_ADD", 10, 20); // 30

Пример

См. подключаемый модуль IniFiles, который включает полный проект для Visual Studio 2019.

Rust SDK

Rust SDK использует интерфейс, аналогичный интерфейсу C++, с некоторыми дополнительными методами переноса, позволяющими легко конвертировать типы C и Rust. Заголовочный файл доступен в виде crate на crates.io. См. документацию здесь.

Пример

Рассмотрим плагин Dylib. Он добавляет класс DynamicLibrary со следующими методами:

declare class DynamicLibrary {
    constructor(handle: number);
    static Load(libraryFileName: string): DynamicLibrary | undefined;
    free(): void;
    getProcedure(procName: string): int | undefined;
}

Дополнительная информация в библиотеке Sanny Builder. Для использования класса DynamicLibrary требуется dll разрешение.