javascript

Как мы делаем онлайн-режим для браузерной инди-игры, когда бюджет на сервера — $10 в месяц

  • пятница, 3 июля 2026 г. в 00:00:07
https://habr.com/ru/articles/1054626/

В прошлых статьях мы рассказывали про архитектуру ГИГАХРУЩА — браузерного 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, поиск путей, перестройка геометрии) в облако, мы используем тот факт, что локальная копия игры уже отлично со всем этим справляется.

Как это работает:

  1. Первый зашедший игрок на конкретный этаж (route identity: {runSeed, floorKey, z, rulesetVersion}) становится хостом.

  2. Браузер хоста запускает полную честную симуляцию активного этажа размером 1024x1024. Он просчитывает AI всех монстров, последствия Самосбора, баллистику пуль и мутацию мира.

  3. В роли бэкенда выступает Cloudflare Worker + Durable Objects. Но они не считают игровую логику. Durable Object — это просто легковесная “комната” (room relay), которая хранит список участников, держит WebSocket соединения, организует порядок событий и служит буфером переподключения.

  4. Остальные игроки (peers) отправляют свои действия (input) через Durable Object к хосту. Браузер хоста применяет их действия к “удаленным акторам” в своей симуляции.

  5. Хост регулярно отправляет через 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:

  1. Если хост решает выйти легально (или мы перехватываем beforeunload), он быстро формирует компактный полный чекпоинт этажа (сжатый массив комнат, NPC, лута и состояния Самосбора).

  2. Этот чекпоинт улетает в Durable Object.

  3. DO назначает новым хостом другого игрока, передает ему чекпоинт.

  4. Новый хост “распаковывает” мир, перестраивает графы поиска путей и забирает власть над симуляцией на себя.

  5. Все остальные клиенты запрашивают у нового хоста свежие фреймы и игра продолжается. На экранах это выглядит как секундная загрузка.

Следующие шаги: от 2 к 100+ игрокам

Наша текущая цель — отладить эту схему на 2, 4 и 8 игроках в кооперативном режиме. Мы уже настроили Cloudflare D1 для сохранения постоянных профилей, логов инвентаря и статистики (НЕТ-СФЕРА).

Если же проект вырастет до необходимости собирать сотни игроков на одном этаже, Cloudflare Relay перестанет справляться (браузер хоста просто сгорит от количества расчетов и исходящего трафика). Для этого у нас спроектирован отдельный, строгий серверный трек (Server Scale Addendum).

В нем та же самая логика симуляции из main.ts отрывается от WebGL, Canvas и DOM, упаковывается в Cloudflare Containers или отдельный VPS и работает как Headless Room Server. Но к этому мы перейдем только тогда, когда упремся в лимиты бесплатного хост-роутинга и поймем, что аудитории это действительно нужно.

А пока мы продолжаем строить бесконечную хрущевку, где теперь хотя бы можно будет крикнуть соседу, чтобы он не открывал гермодверь.


Следить за проектом и поиграть в локальную версию прямо в браузере (без регистрации и СМС) можно тут: