The UE5 IOCP MMO Server follows a three-tier architecture that cleanly separates authentication concerns from real-time gameplay concerns. An ASP.NET Core 8 AuthServer owns all pre-game flows — registration, login, server listing, and server selection — and issues cryptographically random tokens that are stored in Redis. The C++ IOCP GameServer owns session state and all real-time gameplay, and only processes a session after it has presented a valid enter token. The UE5 client keeps these two flows in separate subsystems (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/kenzz55/ue5-iocp-mmo-server/llms.txt
Use this file to discover all available pages before exploring further.
AuthClientSubsystem for HTTP and GameClientSubsystem for TCP/UDP), ensuring the UI layer never mixes lobby and gameplay logic.
System Diagram
AuthServer (ASP.NET Core 8)
The AuthServer is a minimal-API service that runs independently of the game server. It is the only component that stores and verifies passwords. Passwords are hashed with PBKDF2 viaPbkdf2PasswordHasher before being written to MariaDB.
REST Endpoints
| Method | Path | Description |
|---|---|---|
GET | / | Health probe — returns {"service":"AuthServer","status":"ok"} |
POST | /auth/register | Creates a new account. Account: 4–64 chars; Password: 8–128 chars |
POST | /auth/login | Validates credentials and issues a short-lived access token (default TTL: 60 minutes) |
GET | /servers | Returns the list of available game servers from the servers table |
POST | /auth/select-server | Validates the access token, resolves or creates the player’s default character, then issues a one-time enter token (default TTL: 60 seconds) |
Token Flow
SelectServerResponse delivers the game server IP, port, enter token, and character ID to the client in a single response. The client never dials the game server until this step completes successfully.
Data Stores
- MariaDB 11 —
accounts,characters, andserverstables. Schema is initialised automatically fromServerSolution/Infra/mariadb/init/on first container start. - Redis 7 — Stores issued access tokens and enter tokens as key-value entries with TTL. The
DuplicateLoginPolicy(default:InvalidatePrevious) ensures old tokens are revoked when a player logs in again.
IOCP GameServer (C++)
The GameServer is a single Windows executable built fromServerSolution/ServerSolution/. It creates an IOCP handle, binds a TCP listen socket and a UDP socket, and drives a pool of worker threads that dequeue completion events.
Network Layers
| Transport | Port | Purpose |
|---|---|---|
| TCP | 7777 | Reliable delivery of all gameplay packets (enter-game, chat, attack, inventory) |
| UDP | 7777 | Low-latency movement input and batched position replication (MoveBatchNotify) |
| HTTP | 9108 | Prometheus metrics scrape endpoint (/metrics and /health) |
MovementNotifyDatagramBytes = 1200 bytes to stay well under typical MTU limits and are dispatched every 100 ms.
Packet Protocol
All TCP payloads are framed with a 4-bytePacketHeader (2-byte size + 2-byte packet ID) and serialised with Protocol Buffers:
PacketId enum ranges cover system packets (0x0001–0x00FF), game packets (0x0200–0x02FF). Auth-range IDs (0x0100–0x01FF) are marked deprecated because those flows have moved to the AuthServer.
Game Simulation
The server ticks aGameSimulation at GameSimulationTickIntervalMs = 33 ms (~30 Hz). Each tick:
- Drains the movement command queue (per-session input sequences).
- Runs monster AI state machines (patrol, chase, attack, death).
- Resolves pending melee and ranged attack hits.
- Broadcasts
MoveBatchNotifyUDP datagrams to AoI peers. - Queues dirty
CharacterStatesnapshots for batched MariaDB writes.
Data Stores
- MariaDB 11 — Character position, HP, and inventory item instances. Character state is saved in staggered bucket batches (
CharacterStateDbBatchSize = 100) to flatten write spikes. - Redis 7 — Enter-token validation. The
RedisTokenValidatorlooks up the token presented inC_EnterGameReqbefore admitting the session to gameplay.
UE5 Client (Unreal Engine 5.4)
The client is split into two Subsystems following the rule stated inAGENTS.md:
MMOClient1 module depends on Engine and UMG, and enables animation plugins (AnimationWarping, PoseSearch, MotionWarping, AnimationLocomotionLibrary) for character locomotion. Protocol Buffers messages are compiled from ServerSolution/ServerSolution/Proto/Packet.proto and shared between client and server — both must be updated together when the protocol changes.
Full Connection Flow
Register
Client sends
POST /auth/register with account, password, and optional nickname. AuthServer hashes the password with PBKDF2 and writes the row to MariaDB accounts.Login
Client sends
POST /auth/login. AuthServer verifies the PBKDF2 hash and, on success, issues an access token stored in Redis with a 60-minute TTL. The token and accountId are returned to the client.Browse & Select Server
Client calls
GET /servers to display the server list, then POST /auth/select-server with the chosen serverId and the access token. AuthServer validates the token, ensures the player has a default character on that server, and returns {ip, port, enterToken, characterId}.TCP Connect & Enter Game
GameClientSubsystem opens a TCP connection to ip:port. It sends C_EnterGameReq containing enter_token and character_id. The GameServer calls RedisTokenValidator, loads the character state from MariaDB, registers the player entity in the AoI grid, and responds with S_EnterGameRes (which includes a udp_token and udp_port).UDP Handshake
The client sends
C_UdpHelloReq over UDP with udp_token, account_id, and character_id. The GameServer locates the session by token and associates the UDP endpoint. From this point movement inputs travel over UDP.Gameplay Loop
- Player input →
C_MoveReq(UDP) → server applies movement, enqueues snapshot. - Every 100 ms, server sends
MoveBatchNotify(UDP) to all AoI peers. - Attack input →
C_AttackReq(TCP) → server validates timing, resolves hit, broadcastsS_AttackNotify. - Inventory actions →
C_InventoryMoveReq/C_InventoryUseReq(TCP) → MariaDB write →S_InventoryOperationResult. - Chat →
C_ChatReq(TCP) → broadcastS_ChatNtfto all sessions.
Responsibility separation is a core rule of this project. The AuthServer must never process gameplay packets, and the GameServer must never handle registration, login, server listing, or server selection. Keeping these boundaries intact makes each component independently testable and deployable.