Core Utility
Core Utility — библиотека для создания хуков (овверайда, перезаписывания результатов выполнения методов в нативе) и вызова ванильных методов из JavaScript/TypeScript, что значительно упрощает интеграции небольшого количества методов с нативом. Последняя актуальная версия библиотеки доступна к загрузке в браузере модов.
- Декларации
- core-utility.d.ts — для использования в редакторах на процессоре TypeScript
Функции библиотеки
GlobalContext
Глобальный контекст содержит большую часть ванильных экземпляров, прежде всего стоит поискать здесь.
Возвращает ClientInstance:
GlobalContext.getClientInstance();
Возвращает Level для сервера:
GlobalContext.getServerLevel();
Возвращает Level для клиента:
GlobalContext.getLevel();
NativeAPI
Вспомогательный класс для вызова основных методов конвертации идентификаторов между средой выполнения (runtime-значениями) и скриптом.
Идентификаторы могут быть нескольких видов, для предмета и блока они должны указываться соответственно:
ConversionType.ITEM
ConversionType.BLOCK
Преобразовывает динамический идентификатор в статический:
NativeAPI.dynamicToStatic(id, type);
Преобразовывает статический идентификатор в динамический:
NativeAPI.staticToDynamic(id, type);
Возвращает уникальный идентификатор моба по ссылке на класс:
NativeAPI.getActorID(actor);
Возвращает класс моба по его индентификатору:
NativeAPI.getActorById(id);
ToolTip
Под предметом отображаются дополнительные данные, например количество энергии или категория предмета. Все это можно реализовать с помощью подсказок.
Добавление подсказки предмету (сюда же входят и блоки, при регистрации для них также создается предмет):
ToolTip.addToolTip(id, data, name);
Добавление динамической подсказки (перед основным текстом):
ToolTip.addDynamicPre(id, data, function(item) {
return Translation.translate("tool.unique_artifact.formatter");
});
Добавление динамической подсказки (после основного текста):
ToolTip.addDynamicPost(id, data, function(item) {
return Translation.translate("tool.unique_artifact.name");
});
Добавление нескольких подсказок:
ToolTip.addToolTips(id, data, names);
Удаление определенной подсказки:
ToolTip.deletToolTip(id, data, names);
Очищение добавленных ранее подсказок:
ToolTip.clearToolTip(id, data);
Получение массива из всех добавленных подсказок:
ToolTip.getToolTips(id, data);
Очистка всех добавленных подсказок:
ToolTip.clearToolTips();
ActorDamageCause
Создаёт экземпляр класса ActorDamageCause, id это id будущего урона (можно заменить ванильные типы):
new ActorDamageCause(id);
Устанавливает сообщение смерти:
<ActorDamageCause>.setDeadMessage(function(type, name_player, attacker) {
return "Kill "+name_player;
});
TickingAreasManager
Активная область (англ. Ticking area) — это определённая игроком группа чанков, которые продолжают обновляться, даже если рядом нет игрока. Служит для управления заданными областями и создания новых.
Проверяет существуют ли активные области:
TickingAreasManager.hasActiveAreas();
Добавляет сферическую активную область с определенным радиусом вокруг координат:
TickingAreasManager.addArea(dimension, name, x, y, z, range);
Добавляет параллелепипедную активную область между координатами:
TickingAreasManager.addAreaPositions(dimension, name, x1, y1, z1, x2, y2, z2);
Добавляет активную область для моба из измерения, это означает что моб будет сам прогружать область вокруг себя, например, так работает жемчуг края:
TickingAreasManager.addEntityArea(dimension, entity);
Возвращает количество активных областей для всего мира в целом, они добавляются с помощью команд, в ограничении 10:
TickingAreasManager.countStandaloneTickingAreas();
Возвращает количество активных областей в конкретном измерении:
TickingAreasManager.countPendingAreas(dimension);
FileUtils
Нативный модуль для работы с файлами, упрощает работу с большим количеством контента.
// удаляет всю папку целиком со всем содержимым
FileUtils.deleteDirectory(path);
// удаляет только содержимое, оставляя папку
FileUtils.deleteDirectoryContents(path);
// удаляет папку если она пуста
FileUtils.deleteEmptyDirectory(dir);
// удаляет конкретный файл
FileUtils.deleteFile(file);
// переименовывает папку
FileUtils.renameDirectory(oldName, newName);
// переименовывает файл
FileUtils.renameFile(oldName, newName);
// копирует всю папку, включая файлы в ней
FileUtils.copyDirectory(from, to);
// проверяет существует ли файл
FileUtils.fileExists(file);
// проверяет существует ли папка
FileUtils.directoryExists(dir);
// проверяет может ли на пути размещаться файл или папка
FileUtils.isValidPath(path);
// проверяет является ли путь относительным папки игры
FileUtils.isRelativePath(path);
// проверяет существует ли файл или папка
FileUtils.isExists(pathOrFile);
// создает папку на заданном пути
FileUtils.createDirectory(path);
// создает папку, в который помещается файл, который был по этому пути раньше
FileUtils.createDirectoryForFile(path);
World
Используется для управления списком миров, может быть добавлен любой путь, непредусмотренный самой игрой, например, миры для определенной сборки.
Добавляет путь в список миров:
World.addWorldToCache(path);
Обновляет список миров, используйте для визуального изменения если игра уже загружена:
World.updateWorlds();
Возвращает количество миров в списке, учитываются только валидные, т.е. если мир поврежден, в это количество он входить не будет:
World.getWorldsCount();
EntityRegister
Добавляет хандлер тика на моба, по текстовому идентификатору (например, minecraft:zombie<>, где <> - дополнительные данные для моба, возраст и т.п.):
EntityRegister.setHandlerTick(name, function(ent) {
// моб ускорится вверх и/или вся семья мобов
Entity.setVelocity(ent, 0, 0.0005, 0);
});
Gui
Проигрывавает анимацию разрушения блока на заданных координатах:
Gui.animationDestroy(x, y, z);
Нативные классы
Для небольшого упрощения использования ядра, ванильные классы наследуются от PointerClass.
Возвращает ссылку на класс:
<pointerClass>.getPointer();
BlockUtils
Возвращает PointerClass на LegacyBlock:
BlockUtils.getBlockById(id);
Возвращает PointerClass на BlockState:
BlockUtils.getBlockStateForIdData(id, data);
ItemsUtil
Возвращает PointerClass по динамическому идентификатору предмета:
ItemsUtil.getItemById(id);
Заменяет имя предмету:
ItemsUtil.overrideName(id, data, name);
Заменяет защиту брони:
ItemsUtil.overrideArmorValue(id, value);
ClientInstance
- наследуется от PointerClass
Возвращает Options:
<ClientInstance>.getOptions();
Возвращает GuiData:
<ClientInstance>.getGuiData();
Проигрывавает партикли разрушения блока на заданных координатах:
<ClientInstance>.renderDestroyBlock(x, y, z, speed);
Устанавливает камеру локального игрока на другое существо:
<ClientInstance>.setCameraEntity(entity);
Random
- наследуется от PointerClass
Генерирует случайное число до предела max:
<random>.nextInt(max);
Level
- наследуется от PointerClass
Возвращает класс Random:
<level>.getRandom();
Добавляет существо в мир по заданному неймспейсу:
<level>.addEntity(source, x, y, z, namespace);
Возвращает отступ по времени от создания мира (1 тик = 1/20с):
<level>.getCurrentTick();
Возвращает серверный отступ по времени от создания мира (1 тик = 1/20с):
<level>.getCurrentServerTick();
Вовзвращает количество чанков, прогружаемых вокруг игроков:
<level>.getChunkTickRange();
Options
- наследуется от PointerClass
Возвращает тип интерфейса - 0 для карманного интерфейса, 1 для стандартного:
<options>.getUiProfile();
GuiData
- наследуется от PointerClass
Выводит сообщение заголовка (эквивалентная команда /title <actorUid> title):
<guiData>.setTitle(name);
Выводит сообщение подзаголовка (эквивалентная команда /title <actorUid> subtitle):
<guiData>.setSubtitle(name);
Выводит сообщение взаимодействия (эквивалентная команда /title <actorUid> actionbar):
<guiData>.setActionMessage(name);
BlockPos
- наследуется от PointerClass
const blockPos = new BlockPos(pointer);
const blockPos = new BlockPos(x, y, z);
blockPos.getX();
blockPos.getY();
blockPos.getZ();
blockPos.setX(x);
blockPos.setY(y);
blockPos.setZ(z);
blockPos.free();
Vec3
- наследуется от PointerClass
const vec3 = new Vec3(pointer);
const vec3 = new Vec3(x, y, z);
vec3.getX();
vec3.getY();
vec3.getZ();
vec3.setX(x);
vec3.setY(y);
vec3.setZ(z);
vec3.free();
Vec2
- наследуется от PointerClass
const vec2 = new Vec2(pointer);
const vec2 = new Vec2(x, y);
vec2.getX();
vec2.getY();
vec2.setX(x);
vec2.setY(y);
vec2.free();
ChunkPos
- наследуется от PointerClass
const chunkPos = new ChunkPos(pointer);
const chunkPos = new ChunkPos(x, z);
chunkPos.getX();
chunkPos.getZ();
chunkPos.setX(x);
chunkPos.setZ(z);
chunkPos.free();
Продвинутые возможности
Каждый метод в Майнкрафте имеет свой уникальный индентификатор (символ), по нему можно добавлять хуки (прослушиватели) на метод, ну или просто вызвать его.
Где узнать символ метода?
IDA Pro – Hex Rays - Windows, Linux, macOS
Ghidra Software Reverse Engineering Framework - Windows, Linux, macOS
Disassembler - Viewer, Dumper - Android
Injector
Создан для работы с ванильными классами, позволяет вызывать методы и получать значения из них. Рекомендуется очищать, если последующее использование не предусмотрено. Может быть вызван на основе ссылки на экземпляр объекта.
new Injector(pointer);
new Injector(PointerClass);
Оффсет класса в библиотеке, полезно, например, для поиска необходимого диассемблированного кода:
<injector>.getOffset();
<injector>.getOffset(offset);
Вызывает ванильный метод:
<injector>.call(symbol, parameters, table);
Вызывает ванильный метод, возвращает целочисленное значение:
<injector>.getIntResult(symbol, parameters, table);
Вызывает ванильный метод, возвращает число с плавающей точкой:
<injector>.getFloatResult(symbol, parameters, table);
Вызывает ванильный метод, возвращает булевое значение:
<injector>.getBoolResult(symbol, parameters, table);
Вызывает ванильный метод, возвращает строку:
<injector>.getStringResult(symbol, parameters, table);
Вызывает ванильный метод, возвращает ссылку на класс:
<injector>.getPointerResult(symbol, parameters, table);
Заменяет конкретный метод класса на любой другой:
<injector>.replace(table, method, symbol);
Освобождает память, дальнейшее взаимодействие с этим объектом недоступно:
<injector>.free();
Устанавливает название ванильной библиотеки, откуда вызывается метод:
<injector>.setLib(name);
Вызов статичных методов
Если экземпляра нет и быть не может, то метод является статичным.
Вызов таких методов происходит с помощью пустого конструктора:
new Injector();
Offset
Класс позволяет получать поля класса по оффсету.
<offset>.setOffset(offset);
// получение целочисленного значения
<offset>.getInt(offset?);
// получения числа с плавающей точкой
<offset>.getFloat(offset?);
// получение ссылки на значение
<offset>.getPointer(offset?);
// получение строкового значения
<offset>.getString(offset?);
// освобождение памяти, дальнейшее использование невозможно
<offset>.free();
Хуки
- Создайте в папке мода файл
hooks.json. - Запишите туда объект-описание хука.
- Добавьте калбек хука в скрипт (первым параметром всегда будет controller, а вторым указатель на хукнутый класс; только после идут заданные параметры).
{
"symbol": "<symbol>",
"callback": "<callbackName>",
"args": [], // по умолчанию []
"lib": "mcpe", // по умолчанию mcpe
"return": "bool" // по умолчанию void
}
-
Возвращаемые типы данных
- stl::string
- void - принимает ссылку на класс
- int
- float
- bool
-
Аргументы типы данных
- stl::string
- int
- float
- ptr
- BlockPos
- Vec2
- Vec3
- ChunkPos
- BlockSource
- CompoundTag
Для остальных типов данных возможно использование ссылки на объект
ptr. Также, не забывайте заменять<symbol>на конкретный символ, который необходимо овверайднуть, а<callbackName>на калбек, который будет вызван в результате выполнения метода.
Примеры использования
- Модификации
-
Пример Injector, при нажатии на блок вызывается его randomTick:
function getNativeBlock(region, x, y, z) {let injector = new Injector(region.getPointer()).setArgsType(["ptr"]);let pos = new BlockPos(x, y, z);let block = injector.getPointerResult("_ZNK11BlockSource8getBlockERK8BlockPos", [Parameter.getPointer(pos)]);injector.free();pos.free();return block;}function randomTick(block, x, y, z, region) {let injector = new Injector(block).setArgsType(["ptr", "ptr", "ptr"]);let pos = new BlockPos(x, y, z);injector.call("_ZNK5Block10randomTickER11BlockSourceRK8BlockPosR6Random", [Parameter.getPointer(region.getPointer()),Parameter.getPointer(pos),Parameter.getPointer(GlobalContext.getServerLevel().getRandom())]);pos.free();injector.free();}Callback.addCallback("ItemUse", function(pos, item, block, is, player) {let region = BlockSource.getDefaultForActor(player);randomTick(getNativeBlock(region, pos.x, pos.y, pos.z), pos.x, pos.y, pos.z, region);}); -
Пример Offset, при нажатии на блок будет получен экземпляр BlockPos, в котором есть координаты по оффсету:
Callback.addCallback("ItemUse", function(coords, item, block, is, player) {let pos = new BlockPos(coords.x, coords.y, coords.z);let injector = new Injector(pos);let offset = injector.getOffset();alert(offset.getInt(0) + " " + offset.getInt(4) + " " + offset.getInt(8));pos.free();injector.free();}); -
Пример с хуком, изменим логику для определения времени суток, в этом случае, мобы начнут гореть на солнце независимо от времени суток:
[{"symbol": "_ZNK9Dimension5isDayEv","callback": "Dimension.isDay","args": [],"priority": "pre","return": "bool"}]Callback.addCallback("Dimension.isDay", function(controller, self) {controller.replace();controller.setResult(false);}); -
Пример с установкой силы редстоун сигнала, при нажатии палкой по блоку, установит ему силу редстоун сигнала:
function getDimension(region) {let injector = new Injector(region.getPointer());let dimension = injector.getPointerResult("_ZN11BlockSource12getDimensionEv");injector.free();return dimension;}function getCircuitSystem(region) {let injector = new Injector(getDimension(region));let system = injector.getPointerResult("_ZN9Dimension16getCircuitSystemEv");injector.free();return system;}let cache = {};function redstoneUpdate(region, x, y, z, strength) {let system = cache[region.getDimension()];if (!system) {cache[region.getDimension()] = getCircuitSystem(region);system = cache[region.getDimension()];}let injector = new Injector(system);let pos = new BlockPos(x, y, z);injector.setArgsType(["ptr", "int"]).call("_ZN13CircuitSystem11setStrengthERK8BlockPosi", [Parameter.getPointer(pos),Parameter.getInt(strength || 10)]);injector.free();pos.free();}Callback.addCallback("ItemUse", function(coords, item, block, isExternal, player) {if (item.id == 280)Updatable.addUpdatable({tick: 0,update() {if (this.tick >= 20)this.remove = true;redstoneUpdate(BlockSource.getDefaultForActor(player), coords.x, coords.y, coords.z, 5);}})});
Часть методов с документацией доступна в файле деклараций core-utility.d.ts.
История обновлений
-
Alpha 2.0.0
- добавлена возможность вызывать методы с аргументами в Injector
- добавлено возможность получения значений класса по offset
- Добавлен модуль ToolTip
- Добавлена возможность создавать шкалы(на данный момент документация отстусвует)
- добавлен в api переменная vesion, в этой версии имеет значение 2
- добавлен модуль FileAPI
- добавлен метод injector.replace(table, symbol, replaceSymbol)
-
Alpha 2.1.0
- фикс краша от ToolTip
- фикс методов free
- использование методов free не обязательно(но рекомендуется вызывать в ручную)
- добавлен метод injector.setArgsType(string...)
- добавлен метод item.setIsMirroredArt(boolean)
- добавлен тип в параметры инжектора long
- добавлена возможность получать float по offset getFloat()
- добавлены классы Player, Actor, Mob, Block
- добавлена возможность добавлять кастумный урон