Подключение
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.