Библиотека library

API library: entity, player, effect, вспомогательные функции и совместимость.

Подключение

library — дополнительная библиотека модулей TERA. Папка должна называться library, а не library-master. Объявите зависимость в module.json; интерфейс доступен через mod.require.library.

module.exports = function LibraryExample(mod) {
    const lib = mod.require.library;
    mod.command.add('lib-position', () => {
        if (!mod.game.isIngame) return;
        mod.command.message(JSON.stringify(lib.player.loc));
    });
};

Публичные компоненты: entity, player, effect, packet, library. Объект mods содержит ссылки на загруженные компоненты. Вспомогательные функции вызываются через lib.library, например lib.library.dist3D(a, b). lib.command и lib.cmd ссылаются на интерфейс команд.

Entity

lib.entity хранит объекты players, mobs, npcs, unknown и entities, индексированные по gameId. Состояние очищается при загрузке зоны; проверяйте существование записи перед обращением.

Записи списков players/mobs/npcs/unknown содержат pos с координатами и направлением x/y/z/w, info.huntingZoneId, info.templateId, name, job, race. В entities хранятся экземпляры нового класса Entity: у них huntingZoneId и templateId лежат непосредственно на объекте, позиция получается через getLocation(). Геттеры info и loc этого класса выбрасывают исключение; не смешивайте два формата записей.

Исторический README описывает алиасы внешности outfit, app, apperance, appearance и поля weapon, body, hand, feet, underwear, head, face, styleHead, styleFace, styleBack, styleWeapon, styleBody, styleFootprint, styleBodyDye, bodyDye. В проверенном коде эти алиасы не заполняются: для новой разработки не считайте их доступным API.

Метод Результат
getLocationForThisEntity(id) Позиция из подходящего списка или undefined
getLocationForPlayer(id) Позиция игрока; запись должна существовать
getLocationForMob(id), getLocationForNpc(id) Позиция моба/NPC; запись должна существовать
getEntityData(id) Данные сущности из списков
getEntitiesData(huntingZoneId, templateId) Массив сущностей с заданным шаблоном
isNearEntity(pos, playerRadius = 50, entityRadius = 50) Проверка игроков и списка mobs
isNearPlayer(...), isNearBoss(...) Та же проверка отдельно по players/mobs
getSettingsForEntity(id, object) object[huntingZoneId][templateId] для существующей сущности

Несмотря на имя, isNearBoss перебирает список mobs. Проверка использует positionsIntersect, чья геометрия описана ниже; это не универсальная проверка попадания точки в радиус.

Player

lib.player.isMe(gameId) проверяет текущего персонажа. Основные свойства:

Свойства Описание
serverId, playerId, templateId, gameId, name, level Идентификаторы и данные персонажа
race, job Числовые значения из templateId; отличаются от строк mod.game.me
onMount, alive, onPegasus, inCombat Состояние персонажа
loc, pos Координаты, направление w, метка обновления updated
moving, zone, channel Движение, зона, канал
inven weapon, effects, crystals, equipment
stamina, health, maxHealth, mana, maxMana Статистика после обновления
attackSpeed, attackSpeedBonus, aspdDivider, aspd Данные и расчетный коэффициент скорости атаки
playersInParty, partyLeader Данные группы

race = Math.floor((templateId - 10101) / 100), job = (templateId - 10101) % 100. Описанные в старом README алиасы внешности Player также отсутствуют в проверенной реализации. Не считайте остальные поля заполненными до соответствующих игровых пакетов.

Effect

lib.effect хранит abnormals, glyphs, permanentBuffs как объекты ID → состояние. hasAbnormality(id), hasGlyph(id), hasBuff(id) возвращают результат проверки === true. hasEffect(id) возвращает первое истинное состояние из трех хранилищ; при отсутствии может быть undefined, поэтому используйте его как условие, а не ожидайте обязательно false.

getAbnormalities(), getGlyphs(), getBuffs() возвращают сами хранилища, не копии. После завершения эффекта ключ может остаться со значением false; проверяйте значение, а не только наличие ключа. Состояние сбрасывается на S_LOGIN.

Координаты и навыки

Методы lib.library:

Метод Поведение
dist2D(a, b), dist3D(a, b) Расстояния
applyDistance(loc, distance) Меняет x/y по направлению loc.w в радианах; возвращает тот же объект
positionsIntersect(a, b, aRadius, bRadius) Проверяет (aRadius-bRadius)² <= distanceXY² <= (aRadius+bRadius)²
fromAngle(w), toAngle(w) Радианы → 16-битная шкала угла и обратно
getSkillInfo(id, usingMask = true, bossSkill = false) Разбор старого числового ID навыка

positionsIntersect игнорирует Z и при разных радиусах исключает полное вложение одной окружности в другую. Для обычного «не дальше R» используйте dist2D <= R или Vec3.sqrDist2D.

Результат getSkillInfo содержит raw, id, skill, sub, level; методы setValues, getBaseId(skill = 1, level = 1, sub = 0), setValuesTo(skill, level, sub) меняют/собирают числовую форму. Этот старый формат с маской 0x4000000 не заменяет объект skillid современного парсера.

Пакеты и DataCenter

lib.packet подбирает определения по внутренней таблице патчей. Используемый библиотекой вызов lib.packet.get_all(name) возвращает аргументы для mod.hook(...result, callback). Это таблица совместимости данной версии library, а не автоматическая гарантия поддержки любого сервера.

Метод lib.library Назначение
getEvent(opcode, version, payload) Разбор Buffer через Dispatch.fromRaw
getPayload(opcode, version, data) Сериализация через Dispatch.toRaw
getPacketInformation(identifier) Разрешение определения
parseSystemMessage(message), buildSystemMessage(message) Обертки API системных сообщений
query(query, ...args) Обертка queryData; findAll зависит от наличия аргументов
queryM(queries) Последовательное выполнение массивов [query, ...args] с объединением результатов
queryF(query, concat = true, findAll = true, children = true, attributeFilter = null) Запрос без параметров с необязательным объединением узлов
getQueryEntry(queryData, path, ...argsData) Поиск внутри уже полученного дерева

Для предсказуемого управления результатом нового кода удобнее прямой mod.queryData. version/protocolVersion в lib.library относятся к сетевому протоколу, а не версии npm-пакета; command — интерфейс команд, sp — результат попытки обнаружить Skill Prediction.

Вспомогательные функции

Метод lib.library Поведение
arraysItemInArray(a, b) Есть ли хотя бы один элемент a в b; в README имя было написано иначе
jsonEqual(a, b) Сравнение JSON.stringify; порядок ключей влияет на результат
objectLength(obj) Количество собственных перечисляемых ключей
getRandomInt(min, max) Целое от min включительно до max исключительно
jsonStringify(data, spaces), parseJson(data) JSON с собственным строковым кодированием BigInt BI/-
saveFile(filePath, data, dirname) Синхронная запись; объекты сериализуются обычным JSON.stringify
readFile(dirname, filePath) Синхронное чтение, возвращает Buffer
print(...args) Диагностический вывод через util.inspect

Явно задавайте каталог своего модуля при работе с файлами: каталог по умолчанию у saveFile относится к самой библиотеке. Обычный JSON.stringify не поддерживает BigInt; формат library отличается от формата встроенных настроек Toolbox.

Наследование и устаревший API

Для специализированного наследования можно импортировать класс напрямую, но это связывает модуль с внутренней структурой library. В текущей версии Player, как и Entity, использует второй аргумент mods; старый пример super(mod) недостаточен.

const Player = require('../library/class/player');
class CustomPlayer extends Player {
    constructor(mod, mods) {
        super(mod, mods);
    }
}
module.exports = CustomPlayer;

Дополнительный экземпляр регистрирует собственные хуки. Обычно достаточно готового lib.player. emptyLong() и long() в текущем коде выбрасывают исключение: используйте BigInt. opositeDirection (имя с опечаткой) и getDirectionTo помечены устаревшими и выводят предупреждение. startSkillsPackets из README отсутствует в текущем class/library.js — не полагайтесь на него.

Источники: mods/library/README.md, mods/library/index.js, mods/library/class/{library,entity,player,effect,packet}.js.