FishMMO — Introduction
FishMMO is a multiplayer online game framework built on Unity, FishNet, and a custom WebTransport (QUIC/HTTP3) networking stack. It provides a complete server-authoritative MMO architecture with:
- Client: Unity-based game client with launcher, auto-updater, and WebTransport networking
- Login Server: SRP-6a zero-knowledge password authentication with TOTP two-factor auth
- World Server: Player management, world state, and cross-scene coordination
- Scene Server: Per-zone/scene gameplay logic and entity simulation. Hosts any number of scene instances — multiple instances of one open-world scene are channels, and instanced content (dungeons) is a private instance per character or party. A scene server is not owned by a world server; it pulls from one global pending-scene queue and may host scenes for several at once.
- Web Servers: IPFetch (server discovery), Patcher (auto-update), WebGL (browser client hosting)
- Database: PostgreSQL via Entity Framework Core with Npgsql provider and pgBouncer connection pooling
- Reverse Proxy: NGINX with L4 UDP stream forwarding for game traffic and L7 HTTPS for API/WebGL
Architecture
See the Connection Pipeline for the complete end-to-end connection flow from client launch through authentication to gameplay.
Key Design Decisions
| Decision | Rationale |
|---|---|
| WebTransport (QUIC) for all platforms | Unified transport; no TCP fallback, no WebSocket shim |
| NGINX L4 UDP stream proxy | Zero-copy packet forwarding; no TLS termination at proxy |
| Each game server terminates its own TLS | End-to-end encrypted; NGINX never sees plaintext game data |
| SRP-6a + X25519 ECDH | Zero-knowledge password proof; forward secrecy for session keys |
| HMAC-signed auth tokens | Stateless World/Scene server auth; no LoginServer dependency after login |
| No server-to-server character handover | Every scene transfer releases the character, routes the client back through the World server, and re-claims it at the destination. One code path for teleports, channel switches, dungeon entry and reconnects — and a database-backed session claim, rather than trust between servers, decides who owns a character |
| Scene instances identified by database row, not process handle | A scene-manager handle is only unique inside the process that allocated it, so it cannot name an instance across servers |
| Server lifecycle controlled through the database, not the process | locked and shutdown_at_utc on the world and scene server rows are the authority; each process reads its own row back on every heartbeat and adopts it. One in-game /admin command therefore reaches servers the player is not connected to, and any other tool that can write those rows controls the servers identically |
Repository Structure
FishMMO-Unity/— Unity project (client + server game code)FishMMO-Auth/— Shared authentication library (SRP, token, crypto)FishMMO-Database/— Database layer (EF Core + Npgsql entities and services)FishMMO-WebTransport/— C++ msquic wrapper for QUIC/WebTransportFishMMO-WebServers/— ASP.NET Core web servers (IPFetch, Patcher, WebGL)FishMMO-Setup/— Deployment configuration (nginx, systemd, config files)FishMMO-SharedUtility/— Cross-cutting utility libraryFishMMO-Dependencies/— NuGet package aggregation for Unity compatibility