- PVSM.RU - https://www.pvsm.ru -
Продолжаем рассказывать, как был устроен наш лазерный квест с уничтожением сервера. Начало в предыдущей статье про разгадку квеста [1].
Всего у бекенда игры было 6 архитектурных единиц, которые мы и разберём в этой статье:
Давайте пройдёмся по каждой из них.
Бекенд был реализован, как spring boot-приложение: оно имело несколько rest-контроллеров, websocket endpoint и сервисы с игровой логикой.
Контроллеров было всего три:
Websocket endpoint использовался для управления гаджетами: лампами, гирляндой и буквами. Его выбрали, чтобы синхронно отображать всем игрокам текущий статус устройства: включено оно или выключено, активно или нет, какой цвет буквы сейчас горит на стене. Для того чтобы чуть-чуть усложнить задачу включения лазера, мы накинули авторизацию на гирлянду и лазер с одинаковым логином и пароль admin/admin.
Игроки могли протестировать его на включении гирлянды и повторить то же самое на лазере.
Нами была выбрана настолько тривиальная пара логин-пароль для того, чтобы не мучить игроков лишним подбором.
Чтобы сделать задачу чуть поинтересней, в качестве идентификаторов устройств в комнате использовались object ID из mongodb.
ObjectId содержит в себе timestamp: два случайных значения, одно из которых берётся на основании идентификатора устройства, а второе на основании pid-процесса, который генерирует его и значение счётчика. Хотелось сделать идентификаторы сгенерированными через равные промежутки времени и с разными pid-процессов, но общим счётчиком, чтобы подбор идентификатора устройства лазера был интереснее. Однако в итоге все запустились с идентификаторами, различающимися только в значении счётчика. Возможно, это сделало этап слишком простым и не требующим анализа структуры идентификаторов objectId.
Питоновский скрипт [3], который занимался таймерами и переводил из абстракций игровых в физическую модель. Например «включить торшер» → «включить реле N2».
Скрипт подключался к очереди RabbitMQ и передавал запросы из очереди на Ардуино. Также на нём была реализована логика параллельного включения света: вместе с некоторыми устройствами на них включался свет, например, при первичной подаче питания на Мегатрон, он подсвечивался сценическим светом. Дизайн света для кинематографичности всей сцены — отдельная история про большую работу нашего сопродюсера проекта и художника-постановщика Ильи Серова, и про неё мы расскажем в отдельном посте.
Транслятор также отвечал за логику запуска шредера по таймеру и передачу изображения на телевизор: таймер запуска шредера, кричащая капибара, рекламный ролик в конце игры.
Каждые 25 секунд генерировался новый токен, его можно было использовать, чтобы включить лазер на 10 секунд на мощности 10/255. Ссылка на гитхаб с кодом Мегатрона [4].
Затем лазер охлаждался 1 минуту — в это время он был недоступен и не принимал новые запросы на выстрел.
Этой мощности не хватало для того, чтобы пережечь верёвку, но любой игрок мог пульнуть из Мегатрона и увидеть лазерный луч в действии.
Для генерации токена использовался алгоритм хеширования MD5. И схема получалась MD5 от MD5 + счётчик + секрет для боевого токена и без секрета для тестового.
MD5 это отсылка к одному коммерческому проекту, который делал Павел, наш бекендер. Всего пару лет назад в этом проекте использовался MD5, и когда он сказал архитектору проекта, что это устаревший алгоритм шифрования, они начали использовать MD5 от MD5. Раз уж мы решили делать максимально нубский проект, то он вспомнил всё и решил сделать маленькую отсылку.
Боевой режим Мегатрона — это 100% мощность лазера в 3 Вт. Этого вполне достаточно на 2 минуты, чтобы пережечь верёвку, которая держала гирю, чтобы разбить аквариум и залить сервер водой.
Мы оставили несколько подсказок на гитхабе проекта: а именно код генерации токена, по которому можно было понять, что тестовый и боевой токены генерируются на основе одного показателя счётчика. В случае боевого токена помимо значения счётчика ещё используется соль, которую почти полностью оставили в истории изменении этого gist, за исключением двух последних символов.
Зная эти данные, можно было перебрать 2 последних символа соли и фактически выяснить, что для неё использовались числа из Lost, переведённые в 16-ичную систему.
Дальше игрокам оставалось поймать значение счётчика (проанализировав тестовый токен) и сгенерировать боевой токен, используя следующее значение счётчика и подобранную на прошлом шаге соль.
Счётчик просто инкрементировался при каждом тестовом выстреле и каждые 25 секунд. Об этом мы нигде не писали, это должно было быть небольшой игровой неожиданностью.
В игровом мире это была та самая капча, которую надо было нагрузить, чтобы включить вентилятор и открыть флипчарт с подсказкой. Рядом с камерой стоял ноутбук с мониторингом нагрузки.
Сервис [5] подсчитывал, что отображать в мониторинге как текущую нагрузку: температуру и CPU Fan. Метрики передавались в timebase database и отрисовывались графаной.
Если за последнее 5 секунд поступало более 50 запросов на отображение капчи, то нагрузка росла на фикс + рандомное количество шагов. Расчёт был на то, чтобы 100% нагрузку можно было получить за две минуты.
В самом деле логики в сервисе было больше, чем отобразилось в конечной игре: мы поставили монитор таким образом, что было видно только вращение CPU Fan.
Графану в начале квеста хотели оставить доступной с сайта Сокола. Но в ней были также springboot-метрики по отчёту бекенд-приложения, которые мы не успевали вычистить, поэтому решили закрыть доступ к ней. И правильно — ещё в начале квеста некоторые игроки догадались, что приложение написано на фреймворке springboot и даже откопали название некоторых сервисов.
Инструмент передачи информации с бекенда на площадку, VPS-сервер на котором было запущено RabbitMQ.
Бекенд и шина данных держались на наших VPS [6]. Его мощность была сопоставима с компьютером, который вы видели на экране: 2-х ядерная VPS-ка с двумя гигабайтами оперативной памяти. Тариф брали за ресурсы, поскольку пиковая нагрузка планировалась всего несколько дней — так и поступают наши клиенты, которые планируют нагружать
Чтобы защитить сервер от DDoSa, мы использовали Cloudflare.
Стоит сказать, что
Это больше тема следующей статью про хардварную часть проекта: бекенд просто присылал запросы включить конкретное реле. Так вышло, что бекенд знал почти все сущности и запросы с него, выглядели как «включи эту сущность». Мы делали это для раннего тестирования площадки (пока ещё не собрали все Ардуино и реле), в итоге так всё и оставили.
Сайт мы быстро создали на тильде, это заняло один рабочий день и сэкономило нам тысяч 30 бюджета.
Изначально мы думали просто экспортнуть сайт и накинуть нехватающую нам логику, но нарвались на terms of use, которые нам это запрещали.
Мы не были готовы нарушать лицензию, поэтому было два варианта: сверстать всё самим или напрямую связаться с Тильдой, рассказать о проекте и попросить разрешения менять код.
Мы выбрали второй вариант и они не просто пошли нам навстречу, а даже подарили нам год бесплатного бизнес-аккаунта, за что мы им очень благодарны. Было очень неловко показывать им дизайн сайта Сокола.
В итоге мы прикрутили к фронтенду js-логику на отправку запросов на элементарные устройства, немного поменяли стили кнопок включения и выключения игровых элементов.
История поисков, которая стоит отдельной главы.
Мы хотели создать не просто старомодный сайт, а абсолютно тошнотворный, нарушающий все базовые правила дизайна. При этом было важно сохранить правдоподобность: он должен был не ломать ЛОР истории, демонстрировать претенциозность автора и игроки должны были бы поверить в то, что такой сайт может существовать и даже приводить клиентов. И привёл! Пока шла игра, нам дважды обращались за созданием сайтов.
Сначала дизайн делала я сама, стараясь воткнуть побольше гифок и блестящих элементов. Но мой муж-дизайнер с 10-летним стажем, заглянув через плечо, забраковал его как «слишком хороший». Чтобы нарушать правила дизайна, надо их знать.
Существует несколько комбинаций цветов, которые вызывают стойкое чувство омерзения: зелёный и красный одинаковой сочности, серый и розовый, синий плюс коричневый. В итоге мы остановились на сочетании красного и зелёного, как базовых цветов, добавили гифок с котиком и выбрали на фотостоке 3-4 фотографии самого Соколова. У меня было только несколько требований: мужчина средних лет, в плохо сидящем костюме на пару размеров больше и в позе «профессиональная фотосессия в студии». Для теста показывали её друзьям и спрашивали «ну как тебе?».
В процессе разработки дизайна мужу приходилось каждые полчаса отходить полежать, начинало вертолётить. Паша старался открывать консоль разработчика на большую часть экрана, пока допиливал фронтенд — берёг глаза.
Вентиляторы и свет монтировали через твердотельные реле, чтобы они включались не сразу на полную мощность — чтобы нарастание мощности происходило параллельно с мониторингом.
Но про это расскажем в следующем посте, про хардварную часть игры и собственно застройку площадки.
Stay tuned!
Остальные статьи про квест с уничтожением сервера
Автор: galimova_ruvds
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/igra/356175
Ссылки в тексте:
[1] статье про разгадку квеста: https://habr.com/ru/company/ruvds/blog/515522/
[2] VPS: https://www.reg.ru/?rlink=reflink-717
[3] Питоновский скрипт: https://github.com/Korshikov/translatorRabbitMQ2Serial/blob/master/main.py
[4] гитхаб с кодом Мегатрона: https://github.com/ooosokol/smartoffice/blob/master/src/main/java/ru/sokol/smartoffice/service/MegatronSokolServiceImpl.java
[5] Сервис: https://github.com/ooosokol/smartoffice/blob/master/src/main/java/ru/sokol/smartoffice/service/FanServiceImpl.java
[6] наших VPS: https://ruvds.com/ru-rub/pricing?utm_source=habr&utm_medium=quest&utm_campaign=backend-story
[7] турбо: https://ruvds.com/ru-rub/pricing?utm_source=habr&utm_medium=quest&utm_campaign=backend-story#order
[8] Начало игры: https://habr.com/ru/company/ruvds/blog/514776/
[9] Первая подсказка: https://habr.com/ru/company/ruvds/blog/515048/
[10] Источник: https://habr.com/ru/post/515830/?utm_source=habrahabr&utm_medium=rss&utm_campaign=515830
Нажмите здесь для печати.