Возможности интеграции Counter-Strike: Global Offensive
Привет! Я Вова, разработчик в Selectel. Люблю игры, но еще больше люблю влезать в их «внутренности». Мне стало интересно, как организован экспорт игрового состояния Counter-Strike в сторонние системы, например, для управления сценическим освещением.
За 20 лет развития Counter-Strike технологии сильно изменились. Крупные соревнования по CS проводятся на огромных стадионах, а количество выводимой на экраны информации зашкаливает. В этой статье я расскажу про то, как это работает, и покажу, как можно превратить телефон на Android в устройство вывода игрового состояния.
Предыстория
Иногда я смотрю матчи с крупных соревнований по CS:GO. В трансляции за спинами команд видны больше экраны, отображающие вид с камеры игрока, а также состояние персонажа и его вооружение в реальном времени. Кроме этого, все сценическое освещение и огневые эффекты реагируют на текущее состояние раунда: мерцания прекращаются на время активной фазы.
Разработчик игры часто заинтересован в проведении соревнований такого масштаба, а это значит, что существуют непубличные инструменты, которые извлекают из игры необходимые данные и не привлекают внимание античит-системы.
Разработчики часто готовы оказывать помощь, но не всегда хватает времени сделать это «по уму». Так, например, в League of Legends не было отдельной роли наблюдателя, от которой ведется трансляция соревнования. На первое время ввели дополнительного невидимого игрока с открытой картой у одной из команд. Изящное решение, но, как только противник применял умение, наносящее урон всем игрокам команды, наблюдатель умирал.
Я долго откладывал погружение в тему, так как полагал, что у разработчиков нет интереса выкладывать инструменты для экспорта состояния, а писать собственное решение для онлайн-игр с античитом — прямая дорога в бан. Началось все с того, что мне подарили клавиатуру, совместимую с технологией RGB-подсветки Razer Chroma.
Изучая возможности клавиатуры, я обнаружил поддержку плагинов, среди которых был плагин для CS:GO. Это расширение подсвечивало клавиши 1-5 в зависимости от наличия соответствующих видов оружия, а блок клавиш F9-F12 превращался в своеобразную шкалу здоровья и брони.Это не давало какого-то преимущества перед другими игроками, так как это всего лишь другое отображение представленной на экране информации, но античиту такие тонкости не объяснишь, пришлось разбираться, как работает плагин.
По запросу в Google был найден официальный ответ Valve: Counter-Strike: Global Offensive Game State Integration. Информация там не полная, но пользователь Reddit под ником Bkid провел собственное исследование API и написал подробный пост с объяснением многих полей, передаваемых игрой.
Как это работает
Экспорт состояния игры возможен ее собственными средствами. Разработчики выбрали красивое решение: указать в файлах конфигурации все заинтересованные сервисы, а игра будет самостоятельно рассылать игровое состояние.
Данный способ использует официальные средства и не требует вмешательства в память процесса игры. Это значит, что VAC-бан получить невозможно.
По умолчанию игровые интеграции отсутствуют, но добавить собственный сервис очень просто. Находим каталог Steam, далее находим каталог с конфигурационными файлами CS:GO. В моем случае путь выглядит так:
В указанном каталоге создаем текстовый файл с именем gamestate_integration_%SERVICENAME%.cfg. Обратите внимание на следующие ограничения:
- имя файла должно начинаться на gamestate_integration_;
- имя файла должно заканчиваться на .cfg.
Несоблюдение этих правил приведет к игнорированию игрой файла конфигурации. Рассмотрим файл конфигурации:
Первая строка задает имя сервиса, это больше похоже на комментарий, чем на действительно важную информацию. Рассмотрим поля, необходимые для подписки на рассылки игры:
- uri — адрес для рассылки. Поддерживаются схемы HTTP и HTTPS;
- timeout — в течение этого времени игра ожидает подтверждение получения рассылки;
- buffer — время, в течение которого игра буферизирует игровые события для отправки одним сообщением. Если значение равно 0, то буферизация будет отключена и игра будет отправлять информацию о каждом событии в отдельном сообщении;
- throttle — время, которое должно пройти между последним получением HTTP OK и отправкой следующего пакета;
- heartbeat — если в игре не произойдет ни одного события за это время, она вышлет полное состояние в качестве keep-alive пакета.
Далее немного безопасности: секция auth позволяет задать поле token. Заданная строка будет передаваться в каждом сообщении от игры. Это позволяет защититься от нежелательных пакетов. Лучше всего использовать это вместе с HTTPS.
В секции data указывается, какая информация интересует сервис. Доступные параметры могут меняться с развитием игры. Обратите внимание, что некоторые параметры доступны только зрителям и в режиме игры будут игнорироваться.
В сообщениях могут передаваться большие наборы данных, а недоступные поля и поля без значений в сообщениях опускаются. Для удобства обработки информации в сообщениях есть поля updated и added. Первое поле содержит старую версию значений, а второе — отмечает, какие поля появились или исчезли по сравнению с предыдущим сообщением.
Эти два поля условно можно называть «дельтой». Игра отправляет дельты только тем, кто дает корректные ответы на POST-запросы. Согласно документации, достаточно отправить ответ с кодом HTTP 2XX.Теперь, когда мы знаем теорию, перейдем к практике и создадим собственный сервер.
Сервер сервиса
В качестве наиболее простого варианта запустим HTTP-сервер с помощью Python 3.7.4 и доступных библиотек. Документация и пост на Reddit достаточно старые, поэтому для начала просто выведем на экран информацию, которую предоставляет нам игра.
Напишем простой обработчик, унаследованный от класса BaseHTTPRequestHandler из пакета http.server:
Мы не передаем никаких данных игре в ответе, поэтому разумно выбрать код 204 No Content. Несмотря на отсутствие данных, необходимо выставить заголовок Content-Type, иначе игра посчитает ответ некорректным и будет раз за разом повторять отправку данных. При малом значении buffer это может вызывать отказ в обслуживании, особенно на слабых устройствах.
Согласно параграфу 7.2.1 в RFC 2616 заголовок Content-Type должен отправляться, если в сообщении есть тело ответа. У нас его нет, но спорить с игрой нецелесообразно.
Запускаем HTTP-сервер:
Запускаем игру, включаем любую трансляцию GO TV и, в зависимости от указанных настроек в конфигурационном файле, получаем состояние игры. На момент написания статьи информация из поста на Reddit актуальна, за исключением некоторых мелочей, которые я упомяну в конце.
Теперь у нас есть минимальный прототип, который можно расширить.
Smart Link
Игра всегда отдает информацию о текущем игроке, и некоторые разделы игрового состояния содержат больше информации, чем отображено на экране. Так, в нижнем правом углу отображается боезапас текущего оружия, а неэкипированное снаряжение скрыто.
В пылу сражения патроны быстро заканчиваются, а выпавшее из противника оружие может быть шансом застать врага врасплох. Тем не менее, в игре необходимо поднять оружие и переключиться на него, чтобы понять доступный боезапас. Если количество патронов близко к нулю, то переключение обратно, на дополнительное оружие, может дать противнику тактическое преимущество.
Обратите внимание, что описанное далее может считаться техническим допингом и наказуемо на турнирах. Будьте честны!
Вот и простая, но практически применимая идея: отображать на экране телефона полное состояние вооружения игрока. Хотя я никогда не занимался разработкой под Android, такая задача выглядит как отличная разминка для мозга, а в случае недостатка оперативной памяти или места на накопителе — еще и для нервной системы.
Я пытался начать с MIT App Inventor — решения для визуального программирования. К счастью или сожалению, HTTP-сервер на Android — это достаточно редкий случай, поэтому пришлось вернуться к традиционному программированию: Android Studio и эмулятор.
С существующими Web-серверами не заладилось, но эту проблему я воспринял как повод вспомнить про формат HTTP и написать собственный парсер, тем более, игра не предъявляет строгих требований к HTTP-серверу.
Для прототипа я ограничился отображением трех видов вооружения:
- тактического ножа;
- дополнительного оружия (пистолет);
- основного оружия (штурмовая или снайперская винтовка).
Самое верхнее текстовое поле отображает тег клана и имя игрока, текстовые поля около картинок — боезапас.
Для упрощения разработки иконки вооружения можно извлечь из файла iconlib.swf, который находится в ресурсах игры. Обратите внимание, что, согласно лицензионному соглашению, использовать эти файлы можно только в личных некоммерческих целях.
При стрельбе из пулемета приложение немного отстает и пропускает патроны, тем не менее, оно отображает больше информации, чем есть на экране игры.
Прототип хороший, API игры интересное, но есть ложка дегтя в бочке меда.
Подводные камни
Несмотря на достаточно большой объем информации, передаваемой игрой, некоторые моменты остаются неясными.
Так, с недавних пор Valve ввела короткий соревновательный режим, который длится до 9 побед в 16 раундах. На текущий момент через игровые интеграции нет возможности узнать, сколько нужно выиграть команде для победы в матче.
Можно утверждать, что матчи на соревнованиях всегда длинные, то есть до 16 побед в 30 раундах. Однако на соревнованиях обычно нет понятия «ничья», и матч продлевается на дополнительное время: еще 6 раундов, в которых нужно одержать 4 победы, что также не учитывается в выдаваемой информации.
Тем не менее, игра отдает поля, более подходящие для больших соревнований: например, количество побед в серии и необходимое количество выигранных матчей для победы. На соревнованиях победителя обычно выявляют по количеству побед в трех или пяти матчах (Bo3, Best of 3, или Bo5, Best of 5).
Также в игре есть подробная статистика матча по раундам для каждого игрока, но в сообщениях — только суммарная статистика за матч.
Заключение
Игровые интеграции можно использовать по-разному. Кто-то в крупной компании может вдохновиться идеей и сделать маленький турнир среди коллег с настоящим комментатором и красивой трансляцией в конференц-зале. Кто-то может развлечься и подсвечивать всю комнату белым светом умных ламп, когда игрока ослепляет световая граната. А может, что-то еще.
Исходный код приложения на Android я не выложу. Даже этот прототип — технический допинг, который дает мизерное, но преимущество. Такое должно быть получено собственным трудом, а не лежать в открытом доступе.
В конце ноября мы в Selectel проводим собственный игровой хакатон с призовым фондом 300 000 рублей. Предлагаем вам повеселиться при создании ретроигрушек и побороться за главный приз. Если технический допинг, то только такой.