Чому FastAPI для гри?

Очевидний вибір для бекенду браузерної гри — Node.js або PHP. Ми обрали Python із FastAPI, і це виявилося правильним рішенням для такої гри, як VvW.

Покрокові браузерні RPG — це не ігри реального часу. Тут немає вимог до затримки WebSocket, немає ігрових циклів, критичних до частоти кадрів. Кожна дія — це HTTP-запит. Сервер обробляє дію, оновлює базу даних і повертає результат. Саме для цього й призначені REST API.

FastAPI дав нам:

Огляд архітектури

Застосунок — це єдиний додаток FastAPI із 27 роутерами, кожен з яких відповідає за певний домен функцій. Усі роутери зареєстровані в main.py з префіксом /api/.

vampires-vs-werewolves/
├── backend/
│   ├── main.py              # FastAPI app, middleware, routers
│   ├── config.py            # Налаштування через pydantic-settings
│   ├── database.py          # AsyncEngine, AsyncSession, get_db
│   ├── auth.py              # JWT, bcrypt, get_current_user
│   ├── models.py            # Усі моделі SQLAlchemy ORM
│   ├── limiter.py           # Rate limiter SlowAPI
│   ├── scheduler.py         # APScheduler (щоденне скидання, кланські війни)
│   ├── game_logic/
│   │   ├── battle_engine.py # Формули бою, критичні удари, навички
│   │   ├── xp_engine.py     # Криві XP, логіка підвищення рівня
│   │   └── achievements.py  # Засів і перевірка досягнень
│   ├── routers/             # 27 роутерів
│   │   ├── auth.py          # реєстрація, вхід, оновлення, експорт даних
│   │   ├── character.py     # статистика, робота, підвищення рівня
│   │   ├── battle.py        # полювання, pvp, історія битв
│   │   ├── inventory.py     # екіпірування, продаж, використання
│   │   ├── shop.py          # магазин NPC
│   │   ├── crafting.py      # рецепти, кузня, алхімія
│   │   ├── dungeons.py      # 10 підземель, зустрічі з босами
│   │   ├── boss.py          # Світові боси
│   │   ├── clans.py         # створення, вступ, оголошення війни
│   │   ├── events.py        # Затемнення, підрахунок очок Кривавого місяця
│   │   ├── quests.py        # 100 квестів
│   │   ├── skills.py        # 100 навичок
│   │   ├── achievements.py  # 80 досягнень
│   │   ├── ranking.py       # Глобальні/фракційні таблиці лідерів
│   │   ├── auction.py       # Торговий майданчик гравець-гравець
│   │   ├── chat.py          # Чат зони/глобальний/приватні повідомлення
│   │   ├── social.py        # Друзі, стрічка активності
│   │   ├── mail.py          # Внутрішньоігрова пошта
│   │   ├── notifications.py # Push-сповіщення
│   │   ├── daily.py         # Щоденні нагороди входу, серія
│   │   ├── prestige.py      # Система престижу ендгейму
│   │   ├── missions.py      # Місії на основі часу
│   │   ├── map.py           # 30 локацій
│   │   ├── admin.py         # Адміністративні ендпоінти
│   │   ├── support.py       # Тікети підтримки
│   │   ├── waitlist.py      # Список очікування email до запуску
│   │   └── tutorial.py      # Навчання для нових гравців
│   ├── alembic/             # 13 міграцій бази даних
│   └── tests/               # 33 тестові файли, 300+ тестів
├── static/
│   ├── css/                 # 12 CSS файлів (змінні, компоненти)
│   ├── js/                  # cookie-consent.js тощо
│   └── data/                # monsters.json, items.json тощо
├── game/                    # Усі HTML-сторінки гри
├── blog/                    # SEO блог-пости
└── legal/                   # Умови, Політика конфіденційності

Дизайн бази даних

База даних має 35+ таблиць. Основний граф моделей виглядає так:

UserCharacterEquipment (один до одного)
CharacterInventoryItem[]
CharacterCharacterSkill[]
CharacterActiveQuest[]
CharacterClan (через ClanMember)

Найскладніша таблиця — BattleLog. Вона фіксує кожен PvE і PvP бій із повним покрокровим журналом (зберігається як JSON). Це забезпечує:

Ми використовували Alembic для міграцій з самого початку. Урок: завжди налаштовуйте Alembic до написання першої моделі. Додавати його пізніше до існуючої схеми — болісно.

Система автентифікації

Аутентифікація базується на JWT з обертанням токенів доступу + оновлення. Токен доступу спливає за 15 хвилин; токени оновлення діють 7 днів. Обидва зберігаються на стороні клієнта в localStorage (не httpOnly cookies, оскільки гра працює як статичний SPA без серверного рендерингу).

Заходи безпеки для зберігання токенів у localStorage:

Паролі хешуються за допомогою bcrypt, rounds=12. Ендпоінт експорту даних GDPR повертає всі дані користувача, але явно виключає hashed_password.

Рушій ігрової логіки

Бойовий рушій — це серце гри. Він знаходиться в backend/game_logic/battle_engine.py і обробляє:

Формула PvP використовує рейтинг ELO для матчмейкінгу та рейтингу, з обмеженням ±400 очок для атак (ви не можете атакувати когось на 400 очок ELO вище за себе). Це запобігає фармінгу новачків гравцями високого рівня.

Фронтенд: без фреймворку

Ми свідомо уникали React, Vue та Angular. Причини:

  1. Посторінкова модель RPG ідеально підходить для багатосторінкових застосунків. Кожна ігрова дія переходить на нову сторінку (результати полювання, оформлення в магазині, вхід до підземелля). Саме так працював BiteFight у 2006 році. Це працює й досі.
  2. Нуль кроків збірки. Фронтенд — це звичайний HTML + CSS + vanilla JS. Без webpack, без Babel, без node_modules. Весь фронтенд розгортається як статичні файли.
  3. SEO — просто. Кожна сторінка — це реальний HTML-файл, який сканери можуть індексувати безпосередньо. Без SSR, без складнощів гідратації.

Об'єкт-помічник api обертає всі виклики fetch() з автоматичним впровадженням заголовка JWT та логікою оновлення токена. Кожна сторінка включає цей помічник і викликає відповідні ендпоінти API при завантаженні.

Рівні безпеки

Ми цілилися на відповідність OWASP Top 10 із першого дня:

Категорія OWASPЗахист
A01 — Зламаний контроль доступуJWT-аутентифікація на кожному захищеному ендпоінті, залежність require_admin для адмін-маршрутів, журнал аудиту для 401/403/429
A02 — Криптографічні збоїbcrypt 12 раундів, JWT HS256, HTTPS через Nginx HSTS
A03 — Ін'єкціяSQLAlchemy ORM (лише параметризовані запити), валідація вхідних даних Pydantic
A05 — Неправильна конфігурація безпекиCSP, X-Frame-Options, заголовки X-XSS-Protection через SecurityHeadersMiddleware
A07 — Збої автентифікаціїRate limiting на автентифікації (3 спроби/годину), обертання токена оновлення, скасування токена при виході
A09 — Збої логуванняМодель AuditLog — усі події 401/403/429 записуються до БД + ендпоінт honeypot

Засвоєні уроки

  1. Міграції Alembic із першого дня. Ми починали з create_all() у розробці для швидкості, але налаштування правильних міграцій Alembic заощаджує величезний біль під час синхронізації staging/production.
  2. Seed-дані — це функція, а не запізніла думка. Сівач досягнень запускається при старті й є ідемпотентним. Такий же підхід для даних монстрів, каталогів предметів і визначень квестів. Завантаження статичних ігрових даних із JSON-файлів і посів у БД при старті набагато чистіше, ніж запити до JSON-файлів під час виконання.
  3. Rate limiting для всього із першого дня. SlowAPI тривіально інтегрувати, і перший крок хакера — завжди атакувати ендпоінти автентифікації. Ми додали rate limits до написання першого роутера.
  4. Планувальник є необхідністю. APScheduler, що виконує щоденні скидання, очищення токенів та вирішення кланських воєн у фоновому режимі, робить гру живою. Не запускайтеся без запланованих завдань — ігри живуть і вмирають завдяки своєму ритму.
  5. Тестуйте на SQLite, розгортайте на PostgreSQL. Ми використовуємо SQLite+aiosqlite для тестів CI (швидко, без docker-compose). Безперебійно працює з абстракцією діалектів SQLAlchemy. Єдина проблема: SQLite за замовчуванням не застосовує зовнішні ключі — запускайте PRAGMA foreign_keys = ON у налаштуванні тестів.

Що далі

DevLog #2 охопить систему PvP — зокрема, як ми розробили рейтинг на основі ELO, механіку вікна імунітету, що захищає нових гравців, та планувальник автоматичного розв'язання кланських воєн.

DevLog #3 буде присвячений балансу — математиці за кривою XP, золотій економіці та тому, як ми налаштовуємо складність зустрічей, щоб кожен діапазон рівнів був відповідно складним без відчуття стіни прокачки.

Повний вихідний код буде доступний після запуску публічної бети. Приєднуйтеся до списку очікування, щоб отримати сповіщення.