Что описывает .def
Файл определения связывает последовательность байтов пакета с именованными полями объекта JavaScript. Карта протокола связывает имя пакета с числовым opcode; .def описывает его структуру. Это разные данные: правильный opcode не гарантирует правильную версию структуры.
Имя имеет вид S_EXAMPLE.1.def: имя сообщения, номер определения, расширение. При изменении структуры выпускают новую версию, сохраняя необходимые старые. Версия определения не равна версии патча игры. Исходная спецификация публикуется в репозитории tera-data.
Синтаксис
Строка состоит из типа и имени поля. Комментарий начинается с #. Каждый дефис увеличивает глубину вложения на один уровень. Пробелы служат для выравнивания, но сами по себе не задают вложенность.
uint32 sequence
object position
- vec3 loc
- angle w
array entries
- uint32 id
- string label
Этот учебный пример дает event.sequence, event.position.loc, event.position.w и массив event.entries с объектами { id, label }. object группирует поля в JavaScript и не добавляет отдельного бинарного заголовка. Не создавайте определение реального пакета по этому примеру без проверки его байтов.
Типы полей
| Тип | Представление |
|---|---|
bool |
Логическое значение, один байт |
byte |
Беззнаковое 8-битное число |
int16, int32 |
Знаковые целые Number |
uint16, uint32 |
Беззнаковые целые Number |
int64, uint64 |
64-битные целые BigInt |
float, double |
Числа с плавающей точкой 32/64 бит |
angle |
Угол: 16-битное представление в пакете, радианы в JavaScript |
vec3 |
Три float-координаты; объект Vec3 |
vec3fa |
Три компоненты углового представления, преобразуемые в радианы |
skillid, skillid32 |
Упакованный идентификатор навыка с разобранными свойствами |
customize |
Упакованные параметры внешности |
string |
Строка UTF-16LE с нулевым терминатором |
bytes |
Буфер байтов переменной длины |
array |
Массив объектов с вложенными полями |
array<type> |
Массив значений указанного простого типа |
object |
Логическая группировка вложенных полей |
skillid занимает 64 бита в новых структурах, skillid32 — 32 бита в старых. Выбор зависит от структуры пакета. Объект навыка содержит id, type, npc, huntingZoneId, reserved, а также equals, clone, toString. Не заменяйте его полным числом без учета упаковки. Для 64-битных ID используйте литералы 0n, а не 0, когда операция требует BigInt.
Заголовок и порядок байтов
Для обычного пакета первые четыре байта — uint16 общей длины (включая заголовок) и uint16 opcode. Определение не перечисляет их. Числовые поля кодируются little-endian. Специальные особенности транспорта и проверки целостности обрабатывает сетевая часть; .def описывает структуру, с которой работает парсер.
Ссылки на переменные данные
Строки, массивы и байтовые блоки требуют метаданных. По умолчанию парсер автоматически размещает ссылки перед обычными полями соответствующего уровня:
| Поле | Метаданные |
|---|---|
array |
uint16 количества элементов, затем uint16 смещения |
bytes |
uint16 смещения, затем uint16 длины |
string |
uint16 смещения |
Смещения считаются от начала пакета, включая заголовок. Значения смещений и количества вычисляет сериализатор; их не нужно вручную добавлять в объект event.
Явный ref
Если ссылки в реальном пакете находятся в другом месте, укажите ref fieldName в нужной позиции. Наличие явных ссылок отключает автоматический режим для определения: укажите их для всех строк, массивов и bytes. Для вложенного object путь ссылки может быть составным, например position.name.
uint32 sequence
ref entries
array entries
- uint32 id
Здесь счетчик и смещение массива идут после sequence. Без ref они были бы размещены перед ним. Не смешивайте случайным образом явные и неявные ссылки. Старые count/offset распознаются как устаревший формат совместимости; для новых определений используйте ref.
Устройство массива
Каждый элемент обычного массива начинается с двух скрытых uint16: собственное смещение (here) и смещение следующего элемента (next). У последнего next равен нулю. После них идут поля элемента. Вложенные массивы и строки имеют свои ссылки. Таким образом, массив в протоколе — цепочка элементов, а не обязательно сплошной блок одинаковых записей.
# Logical definition
uint32 sequence
array entries
- uint16 value
# Layout: header, entries count/offset, sequence,
# then array nodes: here, next, value
Проверка определения
Проверьте длину пакета, порядок полей, глубину дефисов и расположение всех ссылок. Сравните разбор и повторную сериализацию на известных пакетах нужного сервера. Ошибка типа или версии сдвигает последующие поля и приводит к некорректным значениям. Учтите, что парсер может лишь записать предупреждение для некоторых неверных строк: отсутствие исключения еще не доказывает правильность структуры.
Проверено по node_modules/tera-data-parser/lib/parsers/def.js, lib/protocol/compiler.js и типам парсера; описание формата сверено с README tera-data. Эта статья написана для текущего парсера Toolbox.