Пакеты и хуки

Перехват, изменение и отправка пакетов, фильтры, raw-хуки и версии протокола.

Чтение пакетов

mod.hook(name, version, [options], callback) регистрирует обработчик и возвращает ссылку на хук. name — точное имя пакета, например S_LOAD_TOPO; version — версия определения .def, а не версия патча игры. Префикс C_ означает направление от клиента, S_ — от сервера.

mod.hook('S_LOAD_TOPO', 3, (event) => {
    mod.log(`Zone: ${event.zone}`);
});

Поля event определяются файлом .def. Например, int32 zone, vec3 loc и bool quick дают соответствующие свойства объекта. ID вроде gameId представлены BigInt; не преобразуйте их в Number, чтобы не терять точность.

Изменение и блокировка

Результат callback Поведение
undefined Пакет проходит без сохранения изменений объекта
true Измененный объект сериализуется обратно; снимается предыдущая блокировка
false Пакет блокируется
mod.hook('S_CHAT', 3, (event) => {
    if (event.message === 'blocked example') return false;
    if (event.message === 'replace example') {
        event.message = 'Replacement text';
        return true;
    }
});

Обработчики выполняются синхронно. async-функция возвращает Promise вместо true/false, поэтому не подходит для принятия решения об изменении или блокировке текущего пакета.

Отправка

mod.send(name, version, data) выбирает направление по имени: C — серверу, S/I — клиенту; учитывается префикс TTB_. mod.toClient и mod.toServer задают направление явно и принимают либо (name, version, data), либо готовый Buffer с заголовком. mod.send(buffer) не поддерживается.

mod.send('S_CHAT', 3, {
    channel: 21,
    name: 'Example',
    message: 'Hello!'
});

Парсер заполняет отсутствующие поля значениями по умолчанию, но это не гарантирует корректную игровую семантику. Указывайте значимые поля по определению пакета. Отправленный модулем пакет проходит через хуки с признаком fake: true. Результат отправки — true при передаче в соединение, false, если отправка не выполнена или пакет заблокирован; это не подтверждение обработки сервером.

Порядок и фильтры

mod.hook('S_LOAD_TOPO', 3, {
    order: 10,
    filter: { fake: false, incoming: true, modified: null, silenced: false }
}, (event) => {
    mod.log(event.zone);
});

Меньший order выполняется раньше; по умолчанию 0. Фильтр true требует признак, false исключает его, null отключает проверку.

Фильтр По умолчанию Значение true
fake false Пакет создан прокси
incoming null Направлен клиенту
modified null Изменен предыдущим хуком
silenced false Заблокирован предыдущим хуком

Признаки доступны в event.$fake, $incoming, $modified, $silenced. Если включаете обработку fake, исключите повторную генерацию того же пакета, иначе возможен бесконечный цикл. Блокировка не прекращает весь обход: более поздние хуки с подходящим фильтром могут увидеть пакет и восстановить его.

Raw и event

'raw' передает (code, data, incoming, fake), где data — расшифрованный Buffer, включая четырехбайтовый заголовок. В текущей сборке это копия: чтобы сохранить изменения, верните измененный Buffer. Возврат false блокирует пакет; true снимает блокировку, но не сохраняет изменения копии. Это отличается от старого README tera-network-proxy.

const observer = mod.hook('*', 'raw', (code, data, incoming) => {
    mod.log(`${incoming ? 'S' : 'C'} opcode=${code}, bytes=${data.length}`);
});
// Stop observing when no longer needed:
mod.unhook(observer);

'event' только уведомляет о пакете без разбора и без аргументов callback. false блокирует его. Например: mod.hook('S_LOGIN', 'event', () => mod.log('Login packet')). Хук '*' обычно используется с 'raw' или 'event'.

Одноразовые хуки и ошибки

mod.hookOnce(...) удаляется перед первым вызовом callback. mod.unhook(handle) снимает конкретный хук; сохраненный handle не нужно изменять. Хуки модуля удаляются при выгрузке.

mod.tryHook(...) возвращает null при ошибке регистрации. mod.tryHookOnce(...) делает то же, но некорректный последний аргумент все равно вызывает исключение. Эти методы не ловят ошибки, возникшие позже внутри callback. mod.trySend(...) возвращает false при синхронной ошибке отправки; для диагностики используйте обычный send с try/catch и mod.error.

Версии

'*' выбирает последнее доступное определение, но не делает несовместимые структуры одинаковыми. API этой сборки принимает одну версию в вызове hook, не массив версий. Для разных патчей выбирайте проверенную версию через mod.majorPatchVersion или регистрируйте подходящее определение по результату проверки. Не маскируйте обязательный неработающий хук с помощью tryHook.

Источники: doc/mod/hooks.md, bin/mod.js, node_modules/tera-network-proxy/lib/connection/dispatch.js. Подробнее о прохождении трафика — сетевая часть.