Skip to content

Repository files navigation

Длина волны

Онлайн-версия настольной игры Wavelength: открываешь ссылку и играешь компанией, у каждого своё устройство.

Прод: https://wavesite-rho.vercel.app

Как играть

Создай комнату, отправь ссылку друзьям (или покажи QR-код с экрана). Телепат тайно видит цветную зону на шкале и даёт словесную подсказку между двумя противоположностями (без чисел и однокоренных слов). Его команда двигает стрелку к центру зоны; вторая команда угадывает, слева или справа от стрелки центр (+1 очко). Телепат открывает экран: чем ближе к центру, тем больше очков (2-4). Побеждает команда, набравшая 10+ и обошедшая соперника (при равенстве на вершине партия продолжается). В кооперативе все играют вместе против колоды из 7 карт.

Возможности

  • Комнаты по ссылке, синхронизация в реальном времени, у каждого своё устройство. QR-код комнаты для входа с телефона.
  • Авторитет состояния (host) выбирается по presence и мигрирует при отключении; вышедшие игроки удаляются автоматически (с отсрочкой: перезагрузка вкладки не выбивает из партии), зависший раунд можно пропустить.
  • Секрет мишени живёт только на устройстве телепата до раскрытия, а её хеш публикуется заранее (commit-reveal) - подменить мишень задним числом нельзя; ходовые действия проверяются по роли актёра.
  • Присоединение по ссылке-приглашению; в ней секрет комнаты (в hash, не уходит на сервер), из которого выводится ключ шифрования всех realtime-сообщений - чужой без ссылки не прочитает трафик и не влезет в игру (см. «Модель безопасности»).
  • Лобби: автобаланс команд при входе, смена команды, выход из комнаты и кик (опасные действия просят подтверждение); ссылка в мессенджерах разворачивается в превью (OG-теги).
  • Соревновательный и кооперативный режимы, правило догоняющего, таблица итогов сыгранных раундов.
  • Свои карточки-пары: набор хранится в браузере и подмешивается к встроенному.
  • Редакторский («эдиториал») вид, светлая и тёмная темы (по умолчанию как в системе). На фоне - переключаемая 3D-сцена (5 вариантов на тему волны; можно выключить иконкой в углу). Анимации, звук, празднование победы; учтён prefers-reduced-motion.
  • Шкала управляется и мышью/тачем, и клавиатурой (стрелки).

Модель безопасности

Своего сервера с авторизацией нет - комнату защищает секрет из ссылки-приглашения:

  • Секрет комнаты живёт в hash ссылки (#k=) и на сервер не уходит. Из него (HKDF) выводится ключ AES-GCM, которым шифруются все realtime-сообщения и presence: знающий только код комнаты не прочитает трафик и не подделает состояние, действия или выбор хоста.
  • Против replay и реордеринга: окно свежести, отсев повторов, монотонный seq состояния; presence-рекорды имеют TTL и периодически перевыпускаются. В конверте есть версия протокола - устаревшему клиенту предлагается обновить страницу.
  • Мишень по схеме commit-reveal: телепат публикует её хеш до подсказки, хост сверяет при раскрытии - и на своих действиях тоже, так что телепат-хост не может раскрыть чужую мишень.
  • Ролевые действия принимаются, только если presence отправителя объявляет его именно этим игроком; без presence действие отклоняется. Состояние принимается только от клиента, признанного хостом, и проверяется по форме.
  • Входящие ограничены по частоте (общий бюджет и отдельный на каждого отправителя), размер комнаты ограничен, строки и числа клампятся на хосте.

Принятые trade-off'ы (by design): все, у кого есть ссылка-приглашение, доверены - per-device ключей нет, и griefing внутри комнаты полностью не исключён; модель host-authoritative - клиент-хост технически может исказить состояние (как и в настольной игре, жульничать бессмысленно).

Стек

React + Vite + TypeScript (strict). Realtime: Supabase Realtime через @supabase/realtime-js (broadcast + presence). Анимации: Motion. 3D-фон: Three.js (свои сцены в src/ui/Scene3D.tsx, анимация в вершинных шейдерах, грузится лениво отдельным чанком после первого кадра). Шрифты self-host через @fontsource (Inter, Unbounded, JetBrains Mono; сериф - системная Georgia). Логика игры - чистые функции (src/game/), транспорт абстрагирован (src/net/): Supabase для сети, BroadcastChannel для нескольких вкладок одного устройства, локальный для разработки.

Стартовая загрузка около 140 кБ gzip (react, motion, realtime, код). 3D-сцена - отдельный ленивый чанк около 134 кБ gzip, он подгружается после отрисовки игры и только если фон не выключен.

Разработка

npm install
npm run dev

Без .env приложение работает без сети: по ссылке с ?room= - через общий канал браузера (несколько вкладок одного устройства), без ссылки - в однвкладочном режиме разработки. Для настоящего онлайна скопируй .env.example в .env и заполни:

  • VITE_SUPABASE_URL - URL проекта Supabase
  • VITE_SUPABASE_KEY - публичный (publishable) ключ
  • VITE_SITE_URL - адрес сайта для OG-тегов (необязательно, по умолчанию прод)

CSP в vercel.json запинен на поддомен конкретного проекта Supabase. Vercel читает этот файл до сборки, поэтому при смене проекта домен синхронизируется отдельной командой:

npm run sync:csp

Проверки

npm run check # tsc + self-check'и правил и движка + линт
npm run build # прод-сборка
npm run test:e2e # Playwright: смоук раунда и многовкладочные сценарии# (один раз: npx playwright install chromium)

Те же шаги гоняются в CI (GitHub Actions) на каждый push и PR.

Деплой

Авто-деплой при push в main (Vercel). Вручную: npx vercel --prod.

Участие

PR и issue приветствуются. Для локального запуска Supabase не обязателен (см. «Разработка»); для настоящего онлайна нужен свой проект Supabase. Боевые ключи в репозитории не хранятся. Перед PR прогони npm run check && npm run build && npm run test:e2e.

Лицензия и правовая заметка

BSD 3-Clause, см. LICENSE.

Это независимая любительская реализация правил игры на русском языке. Проект не связан с CMYK и правообладателями настольной Wavelength, не является официальным и не использует их оформление и материалы.

About

Online version of the Wavelength board game: one room per link, realtime play from each player's own device, messages encrypted with a key derived from the invite URL secret

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages