Skip to main content

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.

Authentication is split into two distinct phases. First, the client exchanges credentials with the HTTP AuthServer (ASP.NET Minimal API) to receive an AccessToken and, after selecting a game server, a short-lived EnterToken. Second, the client opens a TCP connection to the chosen game server and presents that EnterToken in a C_EnterGameReq packet. Only after the game server validates the token against Redis does it admit the session and assign a UDP port for positional updates.

Phase 1: HTTP AuthServer

The AuthServer exposes four HTTP endpoints backed by MariaDB (account/character data) and Redis (token storage). All request and response bodies are JSON.

Endpoints

1

POST /auth/register

Create a new account. The server hashes the password with Pbkdf2PasswordHasher (PBKDF2-SHA256, 210 000 iterations, 16-byte salt, 32-byte key) before storing it.Request body
Account
string
required
4–64 characters.
Password
string
required
8–128 characters.
Nickname
string
2–32 characters. Defaults to the account name when omitted.
Response — BasicResponse
Success
boolean
Whether the account was created.
Message
string
Human-readable status string.
Returns 400 Bad Request for invalid field lengths and 409 Conflict when the account name is already taken.
curl -X POST http://localhost:5000/auth/register \
  -H "Content-Type: application/json" \
  -d '{"Account":"alice","Password":"hunter2!","Nickname":"Alice"}'
2

POST /auth/login

Validate credentials and receive an AccessToken stored in Redis with a TTL of AccessTokenMinutes (default 60 minutes).Request body
Account
string
required
Registered account name.
Password
string
required
Plain-text password; verified with constant-time PBKDF2 comparison.
Response — AuthResponse
Success
boolean
Login outcome.
AccountId
uint64
Numeric account identifier.
AccessToken
string
64-character hex token; null on failure.
Message
string
Status string.
Returns 401 Unauthorized for wrong credentials. When DuplicateLoginPolicy is InvalidatePrevious (default), any existing session token is revoked before issuing a new one; when RejectNew, the login is rejected with 409 Conflict if an active session already exists.
curl -X POST http://localhost:5000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"Account":"alice","Password":"hunter2!"}'
3

GET /servers

List all registered game servers. No authentication required.Response — array of ServerDto
ServerId
uint32
Unique server identifier.
Name
string
Display name.
Ip
string
Public IP address.
Port
uint32
TCP port.
CurrentUsers
uint32
Current connected player count.
MaxUsers
uint32
Capacity limit.
Maintenance
boolean
When true, the server rejects new connections.
curl http://localhost:5000/servers
4

POST /auth/select-server

Exchange an AccessToken for a short-lived EnterToken (TTL = EnterTokenSeconds, default 60 seconds) bound to a specific server and character. The server creates a default character automatically if none exists for this account + server pair.Request body
AccessToken
string
required
Token received from POST /auth/login.
ServerId
uint32
required
Target server identifier from the server list.
Response — SelectServerResponse
Success
boolean
Operation outcome.
GameServerIp
string
IP address for the TCP game connection.
GameServerPort
uint32
TCP port for the game connection.
EnterToken
string
64-character hex token; valid for 60 seconds.
CharacterId
uint64
Character to enter the world with.
Message
string
Status string.
Returns 401 for an invalid access token, 404 if the server ID does not exist, and 409 if the server is under maintenance.
curl -X POST http://localhost:5000/auth/select-server \
  -H "Content-Type: application/json" \
  -d '{"AccessToken":"a3f9...c1b2","ServerId":1}'

Phase 2: TCP EnterGame

Once the client has a TCP connection to the game server, it sends C_EnterGameReq (packet ID 0x0201). The server calls RedisTokenValidator::ValidateAndConsumeEnterToken(), which atomically reads and deletes the game:enter:<token> key from Redis. Consuming the key on first use prevents replay attacks. C_EnterGameReq — protobuf
message C_EnterGameReq {
  string enter_token = 1;
  uint64 character_id = 2;
}
S_EnterGameRes — protobuf (packet ID 0x0202)
message S_EnterGameRes {
  bool   success       = 1;
  string message       = 2;
  uint64 account_id    = 3;
  uint64 character_id  = 4;
  string udp_token     = 5;
  uint32 udp_port      = 6;
}
On success the server adds the player to GameWorld, loads inventory from MariaDB asynchronously, and pushes S_InventorySnapshot (0x0251) as soon as the load completes.

Phase 3: UDP Bind

After receiving S_EnterGameRes, the client binds a UDP socket and sends C_UdpHelloReq (packet ID 0x0204) to register its UDP endpoint with the server. C_UdpHelloReq — protobuf
message C_UdpHelloReq {
  string udp_token    = 1;
  uint64 account_id   = 2;
  uint64 character_id = 3;
}
S_UdpHelloRes — protobuf (packet ID 0x0205)
message S_UdpHelloRes {
  bool   success        = 1;
  string message        = 2;
  uint64 server_time_ms = 3;
}
server_time_ms is used by the client to seed its clock-synchronisation baseline for movement prediction.

Token Storage

TokenRedis key patternTTL sourceDefault
AccessTokenauth:access:<token>TokenOptions.AccessTokenMinutes60 min
Session markeraccount:session:<accountId>same as AccessToken60 min
EnterTokengame:enter:<token>TokenOptions.EnterTokenSeconds60 s
The DuplicateLoginPolicy setting in appsettings.json controls what happens when a second login arrives for an already-authenticated account:
appsettings.json (Tokens section)
"Tokens": {
  "AccessTokenMinutes": 60,
  "EnterTokenSeconds": 60,
  "DuplicateLoginPolicy": "InvalidatePrevious"
}
  • InvalidatePrevious (default) — the old auth:access:<token> key is deleted before issuing a new token.
  • RejectNew — if account:session:<accountId> exists the login returns 409 Conflict.

Password Hashing

Passwords are hashed by Pbkdf2PasswordHasher using RFC 2898 / PBKDF2 with SHA-256. The stored hash string has the format:
pbkdf2_sha256$<iterations>$<hex-salt>$<hex-key>
// Pbkdf2PasswordHasher constants
private const string Prefix     = "pbkdf2_sha256";
private const int    Iterations = 210_000;
private const int    SaltSize   = 16;   // bytes
private const int    KeySize    = 32;   // bytes
Verification uses CryptographicOperations.FixedTimeEquals to prevent timing-based enumeration of valid accounts.
The legacy TCP authentication packets — C_LoginReq (0x0101), S_LoginRes (0x0102), C_RegisterReq (0x0103), S_RegisterRes (0x0104), C_ServerListReq (0x0111), S_ServerListRes (0x0112), C_SelectServerReq (0x0121), S_SelectServerRes (0x0122) — are still declared in Protocol.h and Packet.proto for backward compatibility but are not processed by the game server. All authentication must go through the HTTP AuthServer.

Build docs developers (and LLMs) love