Dlaczego FastAPI do gry?

Oczywistymi wyborami dla backendu przeglądarkowej gry są Node.js lub PHP. My wybraliśmy Pythona z FastAPI i okazało się to właściwą decyzją dla gry takiej jak VvW.

Turowe przeglądarkowe RPG to nie gry czasu rzeczywistego. Nie ma wymagań dotyczących opóźnień WebSocket ani pętli gry krytycznych dla liczby klatek. Każda akcja to żądanie HTTP. Serwer rozstrzyga akcję, aktualizuje bazę danych i zwraca wynik. Dokładnie do tego służą REST API.

FastAPI zapewnił nam:

Przegląd architektury

Aplikacja to pojedyncza aplikacja FastAPI z 27 routerami, z których każdy obsługuje domenę funkcji. Wszystkie routery są rejestrowane w main.py pod prefiksem /api/.

vampires-vs-werewolves/
├── backend/
│   ├── main.py              # Aplikacja FastAPI, middleware, routery
│   ├── config.py            # Ustawienia przez pydantic-settings
│   ├── database.py          # AsyncEngine, AsyncSession, get_db
│   ├── auth.py              # JWT, bcrypt, get_current_user
│   ├── models.py            # Wszystkie modele ORM SQLAlchemy
│   ├── limiter.py           # Ogranicznik szybkości SlowAPI
│   ├── scheduler.py         # APScheduler (dzienny reset, wojny klanów)
│   ├── game_logic/
│   │   ├── battle_engine.py # Formuły walki, krytyki, umiejętności
│   │   ├── xp_engine.py     # Krzywe XP, logika awansowania
│   │   └── achievements.py  # Seedowanie i sprawdzanie osiągnięć
│   ├── routers/             # 27 routerów
│   │   ├── auth.py          # rejestracja, logowanie, odświeżanie, eksport danych
│   │   ├── character.py     # statystyki, praca, awansowanie
│   │   ├── battle.py        # polowanie, pvp, historia bitew
│   │   ├── inventory.py     # wyposażanie, sprzedaż, użycie
│   │   ├── shop.py          # sklep NPC
│   │   ├── crafting.py      # receptury, kucie, alchemia
│   │   ├── dungeons.py      # 10 lochów, spotkania z bossami
│   │   ├── boss.py          # Bossowie świata
│   │   ├── clans.py         # tworzenie, dołączanie, wypowiedzenie wojny
│   │   ├── events.py        # Wojna Zaćmienia, punktacja Krwawego Księżyca
│   │   ├── quests.py        # 100 zadań
│   │   ├── skills.py        # 100 umiejętności
│   │   ├── achievements.py  # 80 osiągnięć
│   │   ├── ranking.py       # Globalne/frakcyjne rankingi
│   │   ├── auction.py       # Rynek gracz-gracz
│   │   ├── chat.py          # Czat strefowy/globalny/DM
│   │   ├── social.py        # Znajomi, feed aktywności
│   │   ├── mail.py          # Poczta w grze
│   │   ├── notifications.py # Powiadomienia push
│   │   ├── daily.py         # Dzienne nagrody za logowanie, seria
│   │   ├── prestige.py      # System prestiżu endgame
│   │   ├── missions.py      # Misje czasowe
│   │   ├── map.py           # 30 lokacji
│   │   ├── admin.py         # Endpointy administracyjne
│   │   ├── support.py       # Zgłoszenia supportu
│   │   ├── waitlist.py      # Lista oczekujących przed premierą
│   │   └── tutorial.py      # Samouczek dla nowych graczy
│   ├── alembic/             # 13 migracji bazy danych
│   └── tests/               # 33 pliki testów, ponad 300 testów
├── static/
│   ├── css/                 # 12 plików CSS (zmienne, komponenty)
│   ├── js/                  # cookie-consent.js itp.
│   └── data/                # monsters.json, items.json itp.
├── game/                    # Wszystkie strony HTML gry
├── blog/                    # Posty bloga SEO
└── legal/                   # Regulamin, Polityka prywatności

Projekt bazy danych

Baza danych ma ponad 35 tabel. Podstawowy graf modeli wygląda następująco:

UserCharacterEquipment (jeden do jednego)
CharacterInventoryItem[]
CharacterCharacterSkill[]
CharacterActiveQuest[]
CharacterClan (przez ClanMember)

Najtrudniejszą tabelą jest BattleLog. Rejestruje każdą walkę PvE i PvP z pełnym rozpisaniem runda po rundzie (przechowywane jako JSON). Napędza to:

Używaliśmy Alembic do migracji od samego początku. Wniosek: zawsze konfiguruj Alembic zanim napiszesz swój pierwszy model. Dodawanie go później do istniejącego schematu jest bolesne.

System uwierzytelniania

Auth jest oparty na JWT z rotacją tokenów dostępu + odświeżania. Token dostępu wygasa po 15 minutach; tokeny odświeżania ważne przez 7 dni. Oba są przechowywane po stronie klienta w localStorage (nie w ciasteczkach httpOnly, ponieważ gra działa jako statyczny SPA bez renderowania po stronie serwera).

Środki bezpieczeństwa dla przechowywania tokenów w localStorage:

Hasła są hashowane przez bcrypt, rounds=12. Endpoint eksportu danych GDPR zwraca wszystkie dane użytkownika, ale wyraźnie wyklucza hashed_password.

Silnik logiki gry

Silnik walki to serce gry. Mieszka w backend/game_logic/battle_engine.py i obsługuje:

Formuła PvP używa rankingu ELO do matchmakingu i rankingowania, z ograniczeniem ±400 punktów podczas ataków (nie możesz zaatakować kogoś 400 punktów ELO powyżej ciebie). Zapobiega to farmowaniu początkujących przez graczy wysokopoziomowych.

Frontend: bez frameworka

Celowo unikaliśmy Reacta, Vue i Angulara. Powody:

  1. Model RPG oparty na stronach doskonale pasuje do aplikacji wielostronicowych. Każda akcja gry nawiguje do nowej strony (wyniki polowania, kasa sklepu, wejście do lochu). Tak działał BiteFight w 2006 roku. Działa do dziś.
  2. Zero kroków budowania. Frontend to czyste HTML + CSS + vanilla JS. Brak webpack, brak Babel, brak node_modules. Cały frontend wdraża się jako pliki statyczne.
  3. SEO jest proste. Każda strona to prawdziwy plik HTML, który boty mogą indeksować bezpośrednio. Nie potrzeba SSR, brak złożoności hydracji.

Obiekt pomocniczy api opakowuje wszystkie wywołania fetch() z automatycznym wstrzykiwaniem nagłówka JWT i logiką odświeżania tokenów. Każda strona zawiera ten helper i wywołuje odpowiednie endpointy API przy ładowaniu.

Warstwy bezpieczeństwa

Od pierwszego dnia celowaliśmy w zgodność z OWASP Top 10:

Kategoria OWASPŚrodek zaradczy
A01 — Zepsuty Kontrola DostępuAuth JWT na każdym chronionym endpoincie, zależność require_admin dla tras admina, dziennik audytu dla 401/403/429
A02 — Błędy Kryptograficznebcrypt 12 rund, JWT HS256, HTTPS egzekwowane przez Nginx HSTS
A03 — WstrzykiwanieORM SQLAlchemy (tylko sparametryzowane zapytania), walidacja danych wejściowych Pydantic
A05 — Błędna Konfiguracja BezpieczeństwaNagłówki CSP, X-Frame-Options, X-XSS-Protection przez SecurityHeadersMiddleware
A07 — Błędy UwierzytelnianiaOgraniczanie szybkości auth (3 próby/godzinę), rotacja tokenów odświeżania, unieważnianie tokenów przy wylogowaniu
A09 — Błędy LogowaniaModel AuditLog — wszystkie zdarzenia 401/403/429 zapisywane do DB + endpoint honeypot

Wnioski

  1. Migracje Alembic od pierwszego dnia. Zaczęliśmy od create_all() w fazie development dla szybkości, ale wczesna konfiguracja właściwych migracji Alembic oszczędza ogromnego bólu podczas synchronizacji staging/production.
  2. Dane startowe to funkcja, nie afterthought. Seeder osiągnięć uruchamia się przy starcie i jest idempotentny. To samo podejście dla danych potworów, katalogów przedmiotów i definicji zadań. Ładowanie statycznych danych gry z plików JSON i seedowanie do DB przy starcie jest znacznie czystsze niż odpytywanie plików JSON w czasie rzeczywistym.
  3. Ograniczanie szybkości wszystkiego od pierwszego dnia. SlowAPI jest trywialny do integracji, a pierwszym ruchem hakera jest zawsze bombardowanie endpointów auth. Dodaliśmy limity szybkości przed napisaniem pierwszego routera.
  4. Scheduler jest niezbędny. APScheduler wykonujący dzienne resety, czyszczenie tokenów i rozstrzyganie wojen klanów w tle sprawia, że gra czuje się żywa. Nie uruchamiaj bez zaplanowanych zadań — gry żyją i umierają przez swój rytm.
  5. Testuj w SQLite, wdrażaj na PostgreSQL. Używamy SQLite+aiosqlite do testów CI (szybkie, bez docker-compose). Działa bezproblemowo z abstrakcją dialektów SQLAlchemy. Jedyna pułapka: SQLite domyślnie nie wymusza kluczy obcych — uruchom PRAGMA foreign_keys = ON w konfiguracji testów.

Co dalej?

DevLog #2 obejmie system PvP — konkretnie jak zaprojektowaliśmy ranking oparty na ELO, mechanikę okna odporności chroniącą nowych graczy i scheduler automatycznego rozstrzygania wojen klanów.

DevLog #3 będzie o balansie — matematyce za krzywą XP, gospodarką złota i jak dostrajamy trudność spotkań, aby każdy zakres poziomów był odpowiednio wymagający bez uczucia grindowania.

Pełny kod źródłowy zostanie udostępniony po publicznym uruchomieniu beta. Dołącz do listy oczekujących, aby zostać powiadomionym.