Files
cacto a9d2a6b15c Каркас проекта: ТЗ, nginx+gameserver(uWebSockets) в Docker, /ws echo
Frontend — чистый HTML/CSS/JS без сборки (резюме, проекты, игра-заглушки).
Gameserver — C++ на uWebSockets (CMake FetchContent), протокол hello/lobby.list_rooms/error проверен end-to-end.
2026-07-26 19:31:44 +05:00

141 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ТЗ: cactoz.su
## 1. Общее
Личный сайт-визитка на домене `cactoz.su`. Три страницы, единая Matrix-стилистика.
**Тема:** чёрный фон, зелёный и серебро, моноширинный шрифт (JetBrains Mono / Share Tech Mono), строго и технично — без лишних декоративных элементов.
**Ник `cacto` (кактус) — сквозной мотив:**
- Пиксельный кактус (16×16 или 32×32 спрайт, зелёный/серебро на чёрном) как лого в шапке и favicon.
- ASCII-арт кактуса в hero-блоке главной страницы.
- Терминальные префиксы вместо обычных заголовков/навигации: `cacto@matrix:~$ whoami`, `cacto@matrix:~$ ls projects/`, `cacto@matrix:~$ ./battlecity`.
- Названия комнат в лобби генерируются в формате `cactus-room-01`, `cactus-room-02`, ...
- Характер, а не просто ник: живучесть, минимум ресурсов, "колючий" стиль текста в отдельных местах (например, error-сообщения).
## 2. Страницы
| # | Файл | Назначение |
|---|------|------------|
| 1 | `index.html` | Резюме — PHP/Go backend, опыт КИПиА/АСУТП |
| 2 | `projects.html` | Карточки проектов со ссылками на репозитории |
| 3 | `game.html` | BattleCity: лобби + игра |
## 3. Структура проекта
```
cactoz.su/
├── docker-compose.yml
├── nginx/
│ ├── Dockerfile
│ └── nginx.conf
├── frontend/ # чистый HTML/CSS/JS, без сборки
│ ├── index.html
│ ├── projects.html
│ ├── game.html
│ ├── favicon.ico
│ ├── css/
│ │ ├── theme.css # переменные темы, общий сброс
│ │ ├── resume.css
│ │ ├── projects.css
│ │ └── game.css
│ ├── js/
│ │ ├── matrix-rain.js # canvas-эффект цифрового дождя
│ │ ├── projects.js # рендер карточек проектов из data
│ │ └── game/
│ │ ├── network.js # обёртка над WebSocket, очередь сообщений
│ │ ├── lobby.js # UI лобби: список комнат, создание/вход
│ │ ├── protocol.js # константы типов сообщений, (де)сериализация
│ │ ├── entities.js # клиентские представления Tank/Bullet/Wall для Quintus
│ │ └── main.js # инициализация Quintus, игровой цикл рендера
│ └── vendor/
│ └── quintus/ # Quintus.js, подключается напрямую <script>
├── gameserver/ # C++ / uWebSockets
│ ├── CMakeLists.txt
│ ├── Dockerfile
│ ├── third_party/ # uWebSockets, uSockets, nlohmann/json (submodules)
│ └── src/
│ ├── main.cpp # запуск uWS::App, роутинг /ws
│ ├── Protocol.hpp # структуры сообщений + (де)сериализация JSON
│ ├── Player.hpp # id, nickname, ws-handle, room_id
│ ├── Lobby.hpp / .cpp # реестр комнат, create/join/leave/list
│ ├── Room.hpp / .cpp # жизненный цикл комнаты (waiting/playing/finished)
│ ├── Game.hpp / .cpp # игровой цикл тика, применение input, коллизии
│ ├── Map.hpp # тайловые карты (кирпич/сталь/вода/куст), парсинг
│ ├── Tank.hpp
│ ├── Bullet.hpp
│ └── Ai.hpp / .cpp # AI противника для coop-режима
└── TZ.md
```
## 4. Docker
Два контейнера, как договорились:
- **nginx** — раздаёт статику из `frontend/`, проксирует `/ws` на `gameserver:9001` (с `Upgrade`/`Connection` заголовками для WebSocket). При необходимости терминирует TLS (certbot/сертификаты монтируются volume'ом).
- **gameserver** — multi-stage `Dockerfile`: build-стадия (g++/cmake, сборка uWebSockets из submodule), финальный минимальный runtime-образ с одним бинарником.
`docker-compose.yml` связывает оба сервиса общей сетью; наружу открыт только nginx (80/443), gameserver доступен только изнутри docker-сети.
## 5. WebSocket-протокол
Один WS-канал на клиента на всё: hello → лобби → комната → игра. Формат сообщений — JSON-конверт:
```json
{ "type": "namespace.action", "payload": { ... } }
```
### Client → Server
| type | payload | Когда |
|---|---|---|
| `hello` | `{ nickname: string }` | Сразу после открытия соединения |
| `lobby.list_rooms` | `{}` | При входе в лобби / по запросу обновления |
| `lobby.create_room` | `{ mode: "coop" \| "pvp" }` | Создание комнаты (имя генерируется сервером: `cactus-room-NN`) |
| `lobby.join_room` | `{ room_id: string }` | Присоединение к существующей комнате |
| `lobby.leave_room` | `{}` | Выход из комнаты обратно в лобби |
| `game.ready` | `{}` | Игрок готов к старту матча |
| `game.input` | `{ seq: number, up: bool, down: bool, left: bool, right: bool, fire: bool }` | При каждом изменении состояния клавиш |
### Server → Client
| type | payload | Когда |
|---|---|---|
| `hello.ack` | `{ player_id: string }` | Ответ на `hello` |
| `error` | `{ code: string, message: string }` | Любая ошибка протокола/логики |
| `lobby.rooms` | `{ rooms: [{ id, name, mode, players: number, max_players: 2, state }] }` | Ответ на `lobby.list_rooms`, и при любом изменении списка (broadcast всем в лобби) |
| `lobby.room_joined` | `{ room_id, name, mode, players: [{ id, nickname, slot }] }` | Игроку, который вошёл в комнату |
| `lobby.room_updated` | `{ players: [...] }` | Broadcast остальным в комнате при join/leave |
| `game.start` | `{ map: Tile[][], mode, players: [...], tick_rate }` | Когда оба игрока готовы (или для coop — один готов + таймаут) |
| `game.state` | `{ tick, tanks: [...], bullets: [...], base?: {...}, enemies?: [...] }` | Каждый серверный тик (полный снапшот, без дельт — упрощение для MVP) |
| `game.over` | `{ result: "win" \| "lose" \| "draw", reason }` | Конец матча |
Снапшоты, а не дельты — сознательное упрощение ради простоты клиента и отладки; при необходимости оптимизации это единственное место, которое переделывается (дельты/бинарный формат), протокол на это не завязан жёстко.
## 6. Игровая логика на сервере
- **Тик** 20 Гц (50 мс), authoritative-симуляция: клиент не считает физику, только рендерит `game.state` и шлёт `game.input`.
- **Сущности:** `Tank {id, x, y, direction, lives, fire_cooldown}`, `Bullet {id, owner_id, x, y, direction, speed}`, тайлы карты (кирпич — разрушается, сталь — нет, вода — препятствие для танков/не для пуль, куст — визуально скрывает).
- **Движение** — по классике, сеточное/пошаговое (проще для коллизий и портфолио, чем free-form пиксельная физика).
- **Коллизии:** пуля-стена (разрушает кирпич), пуля-танк (урон/уничтожение), танк-стена/танк-танк (блокировка).
- **Режим coop:** сервер спавнит AI-противников волнами (спавн-точки сверху карты, простая логика: движение к базе + случайный огонь), оба игрока защищают базу (орла). Поражение — база или оба игрока уничтожены; победа — все враги волны уничтожены.
- **Режим pvp:** без AI, симметричная карта, у каждого игрока пул жизней (например, 3), поражение — жизни закончились.
- **Жизненный цикл комнаты:** `waiting` (сбор игроков/ready) → `playing` (активен игровой цикл) → `finished` (отправлен `game.over`, комната закрывается через короткий таймаут).
- **Дисконнект:** при обрыве соединения во время матча — пауза с таймаутом (например, 30 сек), затем техническое поражение отключившегося.
## 7. Лобби
Отдельного REST нет — всё через тот же WS-канал (см. таблицы сообщений в разделе 5). Список комнат обновляется broadcast'ом при любом изменении (создание/удаление/смена состава), не только по запросу клиента.
## 8. MVP-этапы (предложение)
1. Каркас: nginx + gameserver в Docker, статика отдаётся, `/ws` echo-проверка.
2. Вёрстка страниц 1–2 (резюме, проекты) в теме Matrix/cacto.
3. Лобби: список/создание/присоединение комнат (без игровой логики).
4. Игровая логика PvP (проще двух режимов) → затем coop + AI.
5. Полировка: спрайты танков, звук (опционально), доп. карты.
---
Открытые вопросы, требующие решения до реализации: количество и вид карт для MVP (сколько уровней/волн для coop), спрайты (рисовать самим/найти свободные ассеты в стиле Matrix), таймаут ready-фазы в лобби.