Как мы делали XVM. Часть первая: начало и сбор команды

в 7:24, , рубрики: game development, Gamedev, wargaming.net, XVM, Блог компании Wargaming.net, оленемер, Программирование

Как мы делали XVM. Часть первая: начало и сбор команды
Как мы делали XVM. Часть первая: начало и сбор команды
Приветствуем, уважаемое читатели! По случаю старта конкурса от Wargaming мы решили, во-первых, в нем поучаствовать, а, во-вторых, написать серию статей о том, как мы докатились до создания одной из самых популярных модификаций World of Tanks и как хобби с парой строк говнокода за вечер переросло в то, что мы имеем. В статьях описаны все встреченные (и опробованные на себе) грабли. Еще одним поводом для написания статьи было зарытие топора войны между XVM и Wargaming и выходом наших отношений на новый уровень — мы рады, что эта статья опубликована именно в блоге WG. Надеемся, что само чтиво вышло интересным, а другие конкурсанты смогут почерпнуть для себя что-нибудь полезное.

Когда я только начинал играть в World of Tanks в начале 2011 года, то, естественно, не заморачивался по поводу модов. Я даже не знал о том, что они существуют. Но есть у меня такая привычка: если какая-то игра мне нравится (то есть, не удаляется после первой же игровой сессии), то я стараюсь узнать об этой игре как можно больше, для чего лезу на тематические форумы. На этом самом форуме я узнал о существовании модов и решил попробовать. Неделю перебирал все эти прицелы, шкурки, иконки техники, разные озвучки и все больше и больше мне это не нравилось. Вроде бы все неплохо, но чего-то не хватает, хочется подкрутить. Почти все моды удалялись после первого же тестового боя. Но как-то раз я наткнулся на мод под незатейливым названием OTM.

OTM

Он же — Over Target Markers. Эта штука заменяла стандартные маркеры танков на свои (маркеры — это те самые полосочки HP, класс техники и другие, находящиеся над всеми танками).

Как мы делали XVM. Часть первая: начало и сбор команды

Самой главной фишкой на тот момент было то, что OTM добавлял эти самые полоски ХП, которых в стандартных маркерах не было. При использовании стандартных маркеров единственной возможностью узнать количество ХП вражеской и союзной техники было наведение курсора мыши на нее и чтение хинта. Найти «подранка», по которому следует сосредоточить огонь, было проблематично: приходилось водить мышью по полю боя, вместо того чтобы сосредоточиться на стрельбе и маневрировании. С OTM же ситуация на поле боя была буквально как на ладони. Но и это еще не все: у мода был конфиг в виде файла OTMData.xml, позволявший настраивать внешний вид и поведение маркеров! От версии к версии количество настраиваемых элементов росло. Конфигом можно было поделиться, чем народ активно занимался на тематических ресурсах. Мод стоял у многих танковых ютуберов, что немало способствовало его популярности.
В общем, это был первый мод, который я не удалил после первого боя. Вместо этого я за пару вечеров настроил его именно так, как мне того хотелось, и играл с удовольствием до следующей заинтересовавшей меня штуки.

Оленемер

В то время я еще был супертестером WoT. Этим ребятам показывают ранние версии клиента с целью получить фидбек. И как-то вечером за дружескими покатушками услышал в Teamspeak обрывки разговора: «ты ЭТО видел? …. Да как его поставить то? … Сыть!… ПАМАГИТЕ!!!!!». Оказалось, что речь идет о каком-то новом моде, который отличался, помимо прочего, несколько, кхм, неординарным процессом установки.
Итак, запоминайте (а лучше — записывайте!), что было нужно для установки (близкая к оригиналу инструкция):

  1. Скачать и распаковать архив.
  2. Содержимое одной из директорий архива скопировать с заменой в папку клиента игры. (Тут надо упомянуть, что на тот момент это было обычным делом для всех модов, так как папки res_mods и, соответственно, поддержки модов со стороны разработчиков игры еще не было.)
  3. Установить какую-нибудь WAMP сборку. (Да-да, это где Apache, MySQL и PHP. Вообще-то MySQL тут был не нужен, но ставить сборку явно проще.)
  4. В корень веб-сервера нужно было положить скриптик из другой директории архива. Скриптик представлял собой WebDAV сервер с небольшими изменениями.
  5. Создать в Windows сетевой диск и подключить его к созданному на предыдущем шаге серверу командой
    > net use t: http://localhost/local_server/server.php
    либо через мастер.
  6. Установить штуку под названием Dokan (аналог FUSE для Windows).
  7. С помощью утилиты mirror.exe из комплекта Dokan выполнить зеркалирование диска t: в r:. Это приводило к появлению в системе еще одного диска — точной копии сетевого из шага 5, но который система считала локальным.
  8. В каталоге resguiflash клиента WoT создать символьную ссылку с именем stat, показывающую на каталог r:user командой
    > mklink /D c:gamesWorld_of_Tanksresguiflashstat r:user
  9. На этом пункте наконец можно было запустить клиент и насладиться работой мода.

Как мы делали XVM. Часть первая: начало и сбор команды

Сам мод этот показывал ни много ни мало процент побед сокомандников и противников. Причем прямо в бою и прямо в ушах («уши» — это панельки по бокам экрана со списком игроков), применяя самую настоящую цветовую дифференциацию. Честно говоря, первой реакцией, когда я воочию увидел работу мода, была отвисшая челюсть: неужели это все вообще работает?! Еще ниже челюсть отвисла, когда я бой за боем наблюдал количество тех самых представителей фауны, давших название моду.
Хотя стоп. Вы себе представляете, чтобы типичная ЦА «танков» сумела выполнить первые восемь пунктов установки мода без ошибок? Ожидаемо, тема с этим модом на официальном форуме была одной из самых быстрорастущих.
Объяснив нескольким товарищам, как же это все настроить, я понял, что это тупик, и надо все как-то упрощать. Первая мысль была избавиться от WAMP. Тут я подумал, что от PHP, по сути, требуется только WebDAV-сервер, который явно можно сделать много чем.
Как назло я тогда (да и сейчас) увлекался NodeJS и всерьез хотел написать WebDAV на нем. Ну типа, то Apache ставить, а то Node. Ведь это ж огромная разница! В итоге засел за гугл и узнал о user mode file systems вообще и Dokan в частности. Запустил Visual Studio и за полночи родил .NET-экзешник, который делал то же, что и конструкция из WAMP + зеркало Dokan — создавал в системе локальный диск.
Пару слов о том, для чего был нужен этот диск. Пользовательский интерфейс World of Tanks сделан на Action Script. Идея мода — взять нужный AS-файл, декомпилировать его, изменить то, что хочется, скомпилировать и залить обратно. Оленемер был сделан так же — раскрашивал строки ушей в цвета, соответствующие статистике игрока. Вопрос только, как эту статистику получить с сервера мода? Самый очевидный ответ: запросить по http! Да, но есть одно «но»: AS работает в песочнице (причем этих песочниц несколько — для разных частей интерфейса), и у этой песочницы заблокирован выход в сеть. Зато есть доступ к файловой системе по относительным путям. И из-за этого пришлось городить огород с дисками, серверами и линками.
Работа выглядела так:

  1. AS скрипт получает список игроков, которых надо нарисовать в ушах.
  2. Для каждого игрока читается файл stat<ник игрока>.
  3. Так как stat — это линк, то реально читается R:user<ник игрока>.
  4. .NET приложение получает запрос на чтение файла, формирует http-запрос на сервер мода (о котором в следующей статье).
  5. Получив ответ, выдает его в виде «контента файла» AS-скрипту.

Это нехитрое усовершенствование увеличило число пользователей нового мода на порядок. В оригинальной теме, если задавался вопрос об установке, чаще всего отправляли в мою тему с «упрощенной установкой».

Сбор команды и XVM

Тем временем количество пользователей мода росло, а на скорую руку написанный сервер, хостящийся на дешевом VPS, стал не справляться с нагрузкой.
Результатом было:
Как мы делали XVM. Часть первая: начало и сбор команды

Как раз в то самое время я для собственных нужд арендовал в Hetzner самый дешевый «выделенный» сервер (EQ4: Intel Core i7-920, 8 GB DDR3, 2x 750 GB SATA II HDD). Видя такое безобразие с понравившимся мне модом, связался с камрадом bkon и предложил посильную помощь с хостингом.
Через несколько дней сервер был благополучно установлен, и на некоторое время пользователи получили относительно безглючную работу мода.
В феврале-маре 2012, одновременно с этим был сделан форум на том же самом сервере для технической поддержки и обмена конфигами.
Небольшой исторический экскурс. Изначально идея и первые реализации OTM принадлежат Nicolas Siver. В ноябре 2011 года ему это дело, похоже, поднадоело, и эстафету подхватили камрады sirmax и bkon, выпустив OTM для клиента танков версии 0.6.7. У них же возникла идея оленемера. Sirmax (sirmax2) по сей день является основным разработчиком клиентской части XVM.
В январе 2012 к команде присоединился и я: сначала в качестве хостера, чуть позже — в качестве разработчика серверной части, а затем — и вспомогательных систем (к примеру, виджет активации статистики).
В середине 2012 из команды как-то незаметно ушел bkon, зато появились: Mr 13 (Wayfarer) — CEO и PR, XlebniDizele4ku (ilit) — разработчик клиентской части, Mr A — помощь в разработке клиентской части и сборка релизов, Mixaill (Mixaill) — организация непрерывной интеграции (ночные сборки), взаимодействие с переводчиками клиентской части, q4x2 — *nix специалист и разработчик серверной части.
И пару слов о том, почему, собственно, были удалены темы с описанием модификаций на официальном форуме. С ростом популярности мода росло и число прецедентов, так сказать, неспортивного использования: оскорбления игроков с низкой статистикой, либо, наоборот, со слишком высокой. Оскорбленные игроки шли на тот же официальный форум и плакались/требовали/угрожали и всяческими способами добивались запрета этого «обидевшего» их мода. Спустя некоторое время «без объявления войны» (то есть, без какого-либо предварительного контакта с разработчиками мода) все темы, относящиеся к оленемеру и XVM были удалены, а в правила форума был внесет запрет на публикацию ссылок на любые моды, отображающие статистику. Официальная причина таких запретов: моды создают дополнительную нагрузку на серверы WG. Любая попытка как-то урегулировать конфликт и снять запрет наталкивалась на синдром вахтера у модераторов (сказано удалять — мы удаляем), или отписки менеджеров.
Мы предлагали различные компромиссные варианты: ограничить частоту запросов до заданной величины. Ввести расписание, когда мы будем слать запросы — все упиралось в глухую стену. Подолбившись в нее пару месяцев мы оставили эти попытки, и просто разделили XVM на XVM-full и XVM-light. В light-версии было вырезано все, что относится к отображению статистики. Этим самым, мы формально не попадали под запрет (с лайт версией, разумеется): вахтеры нас особо не трогали, и канал привлечения новых пользователей через официальный форум снова заработал.

Конфиг и редактор

Как уже было сказано выше, у OTM и, соответственно, XVM был конфиг-файл. Сперва он был в формате XML: OTMData.xml. Настроек было немного, и хватало ручных правок в любом редакторе. Со временем количество настроек росло, как и популярность, и стали очевидны две вещи:

  1. XML не слишком удобен для ручных правок.
  2. Какой бы формат мы ни выбрали, нужен WYSIWYG-редактор.

С редакторами история получилась такая: времени на них вечно не хватало (да и сейчас не хватает), потому они часто не успевают за последними фичами в самом моде. Самый первый редактор был сделан на Adobe Air все тем же Nikolas Siver. Когда эстафета перешла к нашей команде, редактор мы достаточно долго не трогали, и он, по сути, перестал представлять собой серьезную ценность, так как не содержал в себе большого количества актуальных настроек. В какой-то момент я даже попытался сделать редактор на HTML/JS, но ничего интересного из этого не получилось.
Помимо некоторой тормознутости, был у него один фатальный недостаток: чтобы сделать его WYSIWYG, необходимо было проделать приличный объем работ по повторению фукционала AS + поддерживать его в актуальном состоянии. Вариант апплета же позволял взять часть кода прямо из основной ветки и получить визуализацию подкручиваемых параметров «на халяву». Актуальный редактор расположен тут.
Далее у нас была миграция с XML-конфига на JSON. Новый конфиг был назван xvm.xvmconf. По правде сказать, это не совсем строгий JSON: в нем допускаются комментарии. Благодаря этому более продвинутые пользователи могут править конфиг без онлайн редактора — просто читая комментарии.
Не забыли мы и про обратную совместимость. Во-первых, клиент при отсутствии конфига нового формата искал и загружал старый OTMData.xml (причем эту фичу выпилили совсем недавно, так что времени на миграцию было более чем достаточно). Во-вторых, в онлайн-редакторе до недавнего времени можно было открыть конфиг старого формата, а сохранить уже в новом.
Весной 2013 года назрела еще одна необходимость: из-за увеличения количества настраиваемых фич конфиг с каждой версией разрастался и разрастался. В результате ориентироваться в нем стало проблематично, плюс стало проблематично находить ошибки типа пропущенных запятых или скобок. Напрашивалось разделение файла с конфигом на несколько отдельных файлов, но формат JSON, в отличие от XML, не позволяет делать ссылки между файлами. Решением стало расширение формата JSON, которое мы назвали JSONx.
Собственно, изменение было только одно — в качестве значения можно указать ссылку на произвольный объект в этом или в другом файле. Есть два варианта записи: короткий и расширенный:
короткий —

“name”: ${“file”:”path”}

расширенный —

“name”: { “$ref”: { “file”: “<filename>”, “path”: “<path-to-value>” }, [overrides] }

Короткий формат является частным случаем расширенного. Расширенный формат добавляет возможность переопределения значений, унаследованных из подстановки. Параметр “file” не обязательный: если он не указан, значение будет искаться в текущем файле. Параметр “path” указывает путь к значению по иерархии документа, разделенный точками.
Можно рассмотреть это на примере:
root.xc:

{
  "configVersion": "5.1.0",
  "colors": ${"colors.xc":"colors"},
  "colors2": {
    "$ref": { "path": "colors" },
    "ally_dead": "0x009900",
    "enemy_dead": "0x840500"
  }
}

colors.xc:

{
  "def": {
    "al": "0x96FF00", // союзник
    "en": "0xF50800" // противник
  },
  "colors": {
    "ally_alive": ${"def.al"},
    "ally_dead": ${"def.al"},
    "enemy_alive": ${"def.en"},
    "enemy_dead": ${"def.en"}
  }
}

Здесь получается следующее:

  1. Начинает грузиться root.xc.
  2. Параметр «colors» файла root.xc ссылается на файл colors.xc, и подставляет из него объект “colors”.
  3. Параметры в секции «colors» файла colors.xc ссылаются на значения “def.al” и “def.en” того же файла.
  4. Параметр «colors2» файла root.xc ссылается на объект “colors”, копирует его значение и переопределяет значения «ally_dead» и “enemy_dead" другими.

В результате получается валидный JSON, который парсится по стандартным правилам:

{
  "configVersion": "5.1.0",
  "colors": {
    "ally_alive": "0x96FF00",
    "ally_dead": "0x96FF00",
    "enemy_alive": "0xF50800",
    "enemy_dead": "0xF50800"
  },
  "colors2": {
    "ally_alive": "0x96FF00",
    "ally_dead": "0x009900",
    "enemy_alive": "0xF50800",
    "enemy_dead": "0x840500"
  }
}

Расширение файлов конфига было изменено на .xc (от «xvm config»), и загрузочный файл конфига стал называться xvm.xc. Отдельные файлы и папки с говорящими названиями отвечают за отдельные элементы интерфейса. Это так же позволяет довольно легко собрать свой собственный конфиг, используя отдельные понравившиеся элементы.

Поддержка пользователей

Мы изначально понимали, что XVM несколько сложнее в установке, чем основная масса модов (это даже породило мем «Оленемер начинает работу уже в процессе установки»), и что без поддержки много аудитории мы не соберем.
Для уменьшения нагрузки на поддержку мы даже разделили XVM на XVM-full и XVM-light. В light-версии было вырезано все, что относится к отображению статистики. Соответственно, этим игрокам не нужно было ставить ни Dokan, ни .NET-приложение, что серьезно упрощало установку.
Самая первая поддержка осуществлялась на официальном форуме World of Tanks. Это было не очень удобно — как минимум, у нас не было возможности модерации и закрепления тем, темы перемешивались с другими, не имеющими к нам никакого отношения. Поэтому почти одновременно с переездом на первый выделенный сервер (январь 2012) на нем же был установлен первый форум техподдержки. Самый обычный phpBB «из коробки». Тем не менее, он выполнял свою функцию до одного ЧП.
ЧП заключалось в том, что нам понадобилось отресайзить разделы в файловой системе. Зачем — никто уже и не помнит, но было нужно. Спецов у нас тогда не было, решили обойтись своими силами. Результатом сего действа был разваленный рейд при (сюрприз!) отсутствии актуального бекапа.
Как мы делали XVM. Часть первая: начало и сбор команды

Положительным моментом этого инцидента стало появление в нашей команде Mr 13 (он делает бекапы!), который по совместительству является владельцем форума Korean Random, на базе которого сейчас и находится главный ресурс поддержки XVM.
Осенью 2012 у мода появилось лицо в виде собственного сайта http://www.modxvm.com/. На сайте размещен джентельменский набор любого ресурса: описание, ссылки, новости, FAQ. Туда же переехал онлайн-редактор конфигураций. Со временем сайт был переведен на английский, немецкий, французский и украинский языки. Также сайт помог снизить затраты личных средств на развитие XVM, так как пожертвования добровольцев не могли окупить и малой части затрат на поддержание и развитие проекта. Но была тут некоторая нестыковка. Мы по своим метрикам видели, что число уникальных пользователей XVM каждый месяц увеличивается, в какой-то момент оно перевалило за миллион, а число уников в месяц на сайте было меньше 100К.
Причину мы и так знали — модпаки. Модпаки — это продвигаемые разными личностями сборки модов имени себя. Сам XVM в эти сборки включали охотно, но только не ссылки на него. Доходило даже до того, что надпись на экране загрузки боя со ссылкой на наш сайт заменяли на свою. Нас это паразитирование не устаивало, и мы сделали добровольно-принудительную активацию модуля статистики на нашем сайте.
Происходит это так:

  1. Пользователь заходит на сайт XVM.
  2. Логинится, используя Wargaming.net ID (OpenID).
  3. Благодаря этому мы узнаем игровой ник и ID пользователя и проставляем для него признак «запрошен токен».
  4. После этого пользователь запускает игровой клиент с установленным XVM. Мод при старте отправляет запрос в метод /checkToken. В ответ пользователю со статусом «запрошен токен» отправляется собственно токен (GUID), иначе только статус «активен» либо «не активен» — в зависимости от того, есть ли у этого клиента активные токены или нет. Полученные токены сохраняются клиентом в res_modsxvmdbtokens.xdb.
  5. Далее при каждом запросе к нашему API клиент также отправляет этот токен.
  6. Если токен отсутствует или просрочен (а срок его действия составляет две недели) — данные не отдаются, а клиент показывает сообщение об ошибке с предложением зайти на сайт мода и активировать статистику.
  7. Клиент может получить до пяти токенов, если играет своим аккаунтом на нескольких ПК.

Активация дала нам как минимум два эффекта:

  1. Посещаемость сайта сравнялась с количеством пользователей мода.
  2. Примерно на 30% упала нагрузка на сервер статистики.

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

Автор: iBat

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js