В прошлых статьях мы рассказывали про архитектуру ГИГАХРУЩА — браузерного survival horror с процедурной генерацией, WebGL-рейкастером и полноценным симулируемым миром (A-Life, Самосбор, физика) без использования готовых движков вроде Unity или Godot. Проект работает полностью локально, загружаясь в браузер за секунды.
Но что, если мы хотим добавить мультиплеер, где игроки смогут вместе ходить по одним и тем же бесконечным бетонным коридорам, встречать друг друга, отстреливаться от монстров и прятаться от гермодверей во время Самосбора?
Обычно для таких задач пишут выделенный headless-сервер на Node.js или C++, который крутится 24/7. Но у нас жесткое правило: мы делаем независимый проект с инфраструктурным бюджетом в $10/month. Мы не можем держать десяток мощных серверов, постоянно просчитывающих сотни процедурных этажей для пустого мира.
Вот как мы решили эту архитектурную задачу. Спойлер: браузер одного из игроков работает как полноценный сервер симуляции, а Cloudflare Durable Objects используются только как легковесный релей.
Отказ от античита (Relaxed Trust)
Главное продуктовое решение, которое развязало нам руки: мы официально отказываемся от античита.
ГИГАХРУЩ — это не соревновательная киберспортивная игра. У нас нет доната, выкачивания прибыли из игроков, реальных ставок или рейтингового ладдера. Это PvE-ориентированный кооп-хоррор. Если кто-то хочет читерить или использовать моды в совместной игре — это социальный риск, а не технический блокер. Наша задача сделать так, чтобы игра работала, была атмосферной и дешевой в поддержке, а не строить неприступную крепость.
Сместив фокус с authority is a fairness requirement на authority is a consistency/cost choice, мы смогли полностью пересмотреть архитектуру мультиплеера.
Архитектура Host-Browser Relay
Вместо того чтобы портировать наш тяжелый монолит симуляции (сотни NPC, поиск путей, перестройка геометрии) в облако, мы используем тот факт, что локальная копия игры уже отлично со всем этим справляется.
Как это работает:
-
Первый зашедший игрок на конкретный этаж (route identity:
{runSeed, floorKey, z, rulesetVersion}) становится хостом. -
Браузер хоста запускает полную честную симуляцию активного этажа размером 1024×1024. Он просчитывает AI всех монстров, последствия Самосбора, баллистику пуль и мутацию мира.
-
В роли бэкенда выступает Cloudflare Worker + Durable Objects. Но они не считают игровую логику. Durable Object — это просто легковесная “комната” (room relay), которая хранит список участников, держит WebSocket соединения, организует порядок событий и служит буфером переподключения.
-
Остальные игроки (peers) отправляют свои действия (input) через Durable Object к хосту. Браузер хоста применяет их действия к “удаленным акторам” в своей симуляции.
-
Хост регулярно отправляет через DO снапшоты состояния (AOI — Area of Interest) обратно клиентам.
Таким образом, для игроков это выглядит как полноценный онлайн, но для нас стоимость сервера сводится к простой ретрансляции JSON-сообщений через Cloudflare.
AOI: Как не сжечь канал связи
Даже если сервер не считает физику, пересылать состояние всего этажа 1024х1024 каждый кадр — смерть для любого браузера.
Мы реализовали динамическую фильтрацию по Area of Interest (AOI). Каждый peer получает не весь мир, а только то, что происходит в его радиусе видимости (например, 48 клеток) плюс связанные события:
-
Состояние дверей, которые он открывает.
-
Монстров, которые вступили с ним в бой (даже если они за пределами жесткого радиуса).
-
Близлежащий лут.
-
Положение других игроков.
Хост собирает эти “обрезанные” куски мира и отправляет как lossy-снапшоты (которые можно терять) вместе с reliable-событиями (выстрел, поднятие предмета, урон).
При этом для клиента всё выглядит прозрачно. Его локальный браузер всё так же генерирует базовую геометрию уровня (ведь сид генерации один на всех), отрисовывает WebGL-сцену и предсказывает базовые движения локального персонажа, чтобы не было инпут-лага, а затем мягко синхронизируется с сервером (хостом).
Что если хост отключится?
Главная слабость архитектуры player-host — хост может закрыть вкладку, у него может отвалиться интернет или браузер просто уснет.
В нашем первом Proof of Concept (до 8 игроков) это обрабатывается жестко: если хост пропадает больше чем на 15 секунд, сессия замораживается и отваливается. Мы жертвуем стабильностью ради обкатки геймплея. Но в будущем запланирован механизм Host Handoff:
-
Если хост решает выйти легально (или мы перехватываем beforeunload), он быстро формирует компактный полный чекпоинт этажа (сжатый массив комнат, NPC, лута и состояния Самосбора).
-
Этот чекпоинт улетает в Durable Object.
-
DO назначает новым хостом другого игрока, передает ему чекпоинт.
-
Новый хост “распаковывает” мир, перестраивает графы поиска путей и забирает власть над симуляцией на себя.
-
Все остальные клиенты запрашивают у нового хоста свежие фреймы и игра продолжается. На экранах это выглядит как секундная загрузка.
Следующие шаги: от 2 к 100+ игрокам
Наша текущая цель — отладить эту схему на 2, 4 и 8 игроках в кооперативном режиме. Мы уже настроили Cloudflare D1 для сохранения постоянных профилей, логов инвентаря и статистики (НЕТ-СФЕРА).
Если же проект вырастет до необходимости собирать сотни игроков на одном этаже, Cloudflare Relay перестанет справляться (браузер хоста просто сгорит от количества расчетов и исходящего трафика). Для этого у нас спроектирован отдельный, строгий серверный трек (Server Scale Addendum).
В нем та же самая логика симуляции из main.ts отрывается от WebGL, Canvas и DOM, упаковывается в Cloudflare Containers или отдельный VPS и работает как Headless Room Server. Но к этому мы перейдем только тогда, когда упремся в лимиты бесплатного хост-роутинга и поймем, что аудитории это действительно нужно.
А пока мы продолжаем строить бесконечную хрущевку, где теперь хотя бы можно будет крикнуть соседу, чтобы он не открывал гермодверь.
Следить за проектом и поиграть в локальную версию прямо в браузере (без регистрации и СМС) можно тут:
-
Игра на MyIndie: myindie.ru/games/game/gigahrush
-
Direct Build: gigahrush.bileter.workers.dev
-
Наш Telegram для обновлений: @gigah_rush
ссылка на оригинал статьи https://habr.com/ru/articles/1054626/