Перейти к содержимому
Максим Касакин
Все статьи

Кейс · HotlineTrade · DayZ

Два файла, из которых вырос портал сервера

Магазин на HotlineTrade у всех одинаковый, и менять в нём можно ровно два файла. Этого хватило, чтобы на dayzhhz.ru появились вики, карта и прайс торговцев, которых нет ни у кого из соседей.

2 файла
весь доступ к чужой CMS
12 КБ
глобальный бандл на всех страницах
1261
позиция в прайсе, с именами вместо классов

· dayzhhz.ru

Сайт есть у всех, а пользы от него мало

HOTLINETRADE выдаёт владельцу сервера готовый магазин: товары, оплата, автоматическая выдача. Это работает, и именно поэтому на нём сидят сотни серверов. Но сайт у всех получается один и тот же. Игрок заходит, покупает сет, закрывает вкладку.

А вопросы у него остаются те же, что и были. Где безопасная зона. Сколько стоит патрон у автоторговца. Когда следующий рестарт. Что вообще лежит в сете за 500 рублей. Всё это живёт в Discord, в закреплённых сообщениях, которые никто не читает, и в ответах администрации по кругу.

Сайт для этого подходит лучше Discord. Проблема в том, что сайт вам не принадлежит.

Что вообще можно менять

CMS закрытая, на Inertia.js. Исходников нет и не будет. В панели доступны ровно два файла, которые подключаются на каждой странице:

Всё. Ни шаблонов, ни роутов, ни API, ни сборки. Отсюда обычно делается вывод, что кастомизация на HotlineTrade заканчивается на перекраске кнопок.

Вывод неверный. Два файла на каждой странице - это точка входа. Дальше вопрос только в том, насколько аккуратно вы ей распорядитесь.

Ловушка, в которую я сам влез первой же версией

На сервере HeadhunterZ не было ничего: оба файла пустые, всё писалось с нуля. Первая версия вышла быстро, 602 строки JavaScript и 1690 строк CSS, и держалась она на одной строке:

document.addEventListener("inertia:navigate", boostrap);

Инициализация висела только на событии навигации Inertia, и последствий было два, оба неприятные. На холодной загрузке, когда игрок пришёл по прямой ссылке, скрипт не срабатывал вообще. А на каждом переходе внутри сайта он вешал ещё один комплект обработчиков поверх предыдущего: походил по разделам, получил пять копий одного и того же слушателя и подтормаживающую страницу.

Ловушка неочевидная, потому что Inertia ведёт себя не как обычный сайт: переходы идут без перезагрузки, так что наивный DOMContentLoaded не работает тоже. Правильный ответ здесь - один комплект наблюдателей за DOM на загрузку страницы, которые сами замечают, что контент переехал. Ровно с этого и началась вторая версия, с которой сайт живёт сейчас.

Если вам уже писали кастом под HotlineTrade, откройте js_custom.js и посмотрите на последние строки. Эта штука встречается постоянно.

Что в итоге стоит на dayzhhz.ru

Дальше по нарастающей, от мелочей до того, ради чего всё затевалось.

Мониторинг с временем рестарта. В баннер онлайна дописывается строка с ближайшим рестартом и обратным отсчётом, которая подсвечивается за десять минут до него. Считается в UTC, чтобы игрок из другого пояса видел своё время, а не московское.

Баннер рейд-тайма. Появляется, когда рейд идёт или начнётся в ближайшие сутки, и молчит в остальное время. Показывать его всю неделю бессмысленно: это перестаёт быть новостью и становится шумом.

Состав сета прямо в карточке товара. Магазин отдаёт конфигурацию сета в разметке карточки. Я разбираю её и рисую боковую панель: что именно входит, в какой слот, какого размера рюкзак, что на оружии навешано. Карточка появляется мгновенно, а размеры инвентаря дозагружаются отдельным запросом и дорисовываются, когда придут. Если не придут, карточка просто останется без них: это рабочее состояние, а не ошибка.

Вики сервера. 20 страниц, 14 готовых: правила, рейды, ивенты, крафт, бусты, валюта, кланы. С оглавлением, поиском и нормальной вёрсткой. Тексты лежат в markdown и собираются в один файл данных.

Интерактивная карта. 57 зон и 47 точек: безопасные зоны, торговцы, места падения спецгруза, зоны патрульного вертолёта, полоса запрета строительства вдоль берега. Координаты игровые, в метрах, взяты из конфигов сервера, а не нарисованы на глаз. Одноимённые зоны одного вида сливаются в одну фигуру через SVG-маску, иначе на пересечениях получается грязь из дуг и потемнений.

Прайс всех торговцев. 8 торговцев, 1261 позиция, с поиском и фильтром по категориям. Цены берутся из настоящего TraderConfig.txt сервера, то есть разойтись с игрой они не могут в принципе.

Прайс, из-за которого всё усложнилось

Прайс выглядит как самая простая часть работы. Взять конфиг торговцев, разложить в таблицу. Час работы.

Пока не посмотришь, что в конфиге лежит. А лежит там вот это:

HH_PKM, izh43shotgun, HH_Mag_VR80_30Rnd

Игроку нужно «ПКМ», «ИЖ-43» и «Магазин VR80 на 30 патронов». Прайс из классов это не прайс, это дамп базы, который читать невозможно.

Названия предметов лежат в самой игре и в модах, внутри PBO-архивов. Путь получился такой: распаковать PBO, вытащить из них stringtable.csv и config.bin, прогнать конфиги через CfgConvert из DayZ Tools, собрать словарь классов. Работает.

Дальше выяснилось, что половина модпака обфусцирована. ClothingHH2, VestHH, Head_gearHH, медицина, майнинг, машины: эти PBO не открывает даже официальный инструмент, он честно отвечает «Obfuscated PBO». Имён оттуда не достать ничем.

Часть вытаскивается обходным путём. Сервер переносит чужой предмет к себе под своим классом, но локаль исходного мода никуда не девается, и по якорному слову класса имя находится. Правило пришлось сузить, чтобы HH_Cassette_Recorder не получил имя кассеты, а магазин имя оружия. Всё, что найдено так, помечено как догадка и проверено глазами.

Остаток добивается с другой стороны: я написал собственный мод под DayZ, который при запуске игры обходит конфиг предметов изнутри и выгружает его в JSON. 11625 классов с размерами, весом, слотами и навеской. Заодно отсюда же берутся размеры инвентаря для карточек магазина.

Сверху лежит ручной словарь: туда идут серверные предметы, которых нет нигде, и исправления там, где автоматика ошиблась. Прогон инструментов эти правки не затирает.

Итог: в прайсе все 1261 позиция с человеческими названиями. В полном списке предметов 217 классов из 985 так и остались классами, у них имён нет нигде, кроме как у администрации сервера.

Это тот случай, когда задача на час превращается в три дня. И ровно это отличает рабочий прайс от таблицы, которую игроки не открывают.

Как это устроено, чтобы не сломать магазин

Соблазн очевидный: свалить всё в js_custom.js и забыть. Так делать нельзя, этот файл грузится на каждой странице магазина, включая оплату.

Поэтому в глобальном бандле лежит только то, что нужно везде: мониторинг, рейд-тайм, сайдбар, мелочи оформления. 12 килобайт. Плюс два загрузчика: увидев на странице пустой <div id="hhz-wiki"> или <div id="hhz-map">, бандл подтягивает тяжёлую часть с отдельного сервера.

В самой CMS при этом лежит буквально один пустой div на страницу. Всё остальное, код, тексты, карта и цены, живёт в одном репозитории и деплоится оттуда. Добавить статью в вики означает добавить markdown-файл и запушить, а не лезть в админку CMS.

Руками в панель заливаются только те самые два файла, и только когда менялась общая часть.

Что это даёт владельцу сервера

Честно, без выдуманных процентов: сквозной аналитики до и после я не ставил, и обещать вам рост доната на цифрах не буду.

Что происходит по механике:

Сколько это заняло

Первая рабочая версия поднялась за четыре дня: 83 коммита, около 2700 строк TypeScript, плюс мод-выгрузчик для игры. Быстро получилось не потому, что задача простая, а потому что во второй версии архитектура встала правильно: один репозиторий, три цели сборки, контент отдельно от кода.

Для следующего сервера повторять эту дорогу целиком не нужно. Движок вики, карта, прайс и разбор конфигов торговцев уже написаны. Меняются данные, стиль под ваш сайт и то, что специфично именно для вашего мод-пака.

Сделаю то же самое вашему серверу

Беру сайт на HotlineTrade и довожу его до портала: вики, карта, прайс, ТТХ в карточках магазина. Под ключ, от первого созвона до залитых в CMS файлов. Напишите, какой у вас сервер и что болит.

Давайте поговорим

Пишите в любой из каналов - обычно отвечаю в течение дня.