Architecture Introduction¶
Before diving into any specific system, it helps to have a shared vocabulary for the infrastructure that runs MIP. This page defines the core concepts — servers, containers, sessions, and orchestration primitives — that appear throughout the rest of the architecture documentation.
Game Server Binaries¶
A Game Server Binary is the compiled Unreal Engine executable produced by a dedicated server build. It contains no editor tooling — only the runtime code needed to run the game world, process player connections, and communicate with the backend.
MIP ships Linux dedicated server builds. The binary is packaged inside a Docker container so it can run consistently across any environment without manual dependency management.
Game Server Containers¶
A Game Server Container is a running Docker image that wraps a game server binary along with everything it needs: OS libraries, config files, environment variables, and log mounts.
Each container represents exactly one game world instance. When a new area or dungeon needs to spin up, the orchestration layer starts a new container from the same image — identical binaries, different runtime state.
Containers expose two UDP ports that Agones assigns dynamically at runtime:
| Port name | Container port | Protocol | Purpose |
|---|---|---|---|
game |
7777 |
UDP | Unreal Engine net traffic |
beacon |
12345 |
UDP | UE beacon (session discovery) |
Session Types¶
MIP distinguishes between two kinds of game sessions, which have different lifecycle models.
Persistent Area Sessions¶
Persistent area sessions are always-on game worlds that players drop in and out of freely. They function as shared spaces — towns, leveling zones, gathering areas. The backend boots a fixed set of these on startup:
| Map | Default instances |
|---|---|
| Cloudspire | 1 |
| AuroraPlateau | 1 |
| SoaringSummitVillage | 1 |
| SylphideValley | 1 |
| WindcallerHighlands | 1 |
| ZephyrBluffs | 1 |
When a player logs in for the first time they land in STARTING_MAP_NAME (configurable via .env). Returning players resume at their last saved lastAreaMap and lastTransform.
Dungeon / Instance Sessions¶
Dungeon sessions are ephemeral — created on demand for a specific player or party, then torn down when the session ends. They are allocated dynamically by the backend via the Agones GameServerAllocation API when a player emits allocate_dungeon over Socket.IO. Each dungeon runs on its own isolated container instance.
Kubernetes¶
Kubernetes (K8s) is the container orchestration platform that MIP builds on. It is responsible for:
- Scheduling containers onto available machines (nodes)
- Health monitoring and automatic restarts
- Resource allocation (CPU, memory, network)
- Exposing containers to the network
Kubernetes does not understand game concepts — it only sees generic containers. The game-specific layer is provided by Agones (below).
Minikube (Local Development)¶
Minikube runs a single-node Kubernetes cluster on your local machine. It is the standard local development environment for MIP. The deploy scripts (scripts/deploy-docker-local.bat) build the backend image, export a kubeconfig with the Minikube IP embedded, and use docker-compose to bring up Redis, MongoDB, and the backend — all pointed at the local cluster.
K3S (Production / Staging)¶
K3S is a lightweight Kubernetes distribution used in production and staging deployments. It has a smaller footprint than full K8s and is well-suited to single-host or small-cluster game server deployments.
Agones¶
Agones is a Kubernetes extension (custom resource definitions + controller) that adds game-server-specific lifecycle management on top of standard K8s.
Agones handles things Kubernetes cannot do on its own:
- Dynamic UDP port assignment (no fixed port per server)
- Game server health state tracking (
Starting → Ready → Allocated → Shutdown) - Fleet management with automatic buffer pools
GameServerAllocation— atomically claim aReadyserver and hand it to a player
Core Agones Resources¶
GameServer
The atomic unit in Agones. A GameServer resource wraps exactly one Pod (one container) and tracks its lifecycle state. The backend watches GameServer events over the Kubernetes API; when a server transitions to Ready, the backend publishes a gameservers_ready event via Redis pub/sub so waiting players can be dispatched.
# Simplified create-server-local.yaml
apiVersion: "agones.dev/v1"
kind: GameServer
metadata:
generateName: "mip-server-"
spec:
health:
initialDelaySeconds: 120
periodSeconds: 10
failureThreshold: 10
ports:
- name: game
portPolicy: Dynamic
containerPort: 7777
- name: beacon
portPolicy: Dynamic
containerPort: 12345
Fleet
A Fleet is a named group of identical GameServer resources managed as a pool. MIP uses a single fleet (mip-server-fleet) to maintain a warm buffer of Ready servers that can be allocated instantly without a cold-start delay. The KubernetesService creates the fleet (and its FleetAutoscaler) on backend startup, skipping creation if the fleet already exists.
GameServerAllocation
An Allocation is an API call that atomically claims one Ready GameServer from a fleet. The backend issues an allocation when a dungeon is requested, injecting the session ID, map name, and a signed JWT as container annotations so the game server knows its own identity the moment it starts.
Client emits allocate_dungeon
│
▼
Backend calls GameServerAllocation API
│ Annotations injected: MAP, SESSION_ID, JWT_TOKEN
▼
Agones selects a Ready GameServer → state → Allocated
│
▼
Game server reads annotations on startup
│ Connects to backend Socket.IO as role=server
▼
Backend routes gate_travel to awaiting players
Nodes and Pods¶
A Node is a physical or virtual machine that Kubernetes schedules work onto. In a local Minikube setup there is one node (your machine). In production there may be several.
A Pod is the smallest deployable unit in Kubernetes — a wrapper around one or more containers sharing a network namespace and storage. Each GameServer occupies exactly one Pod. The game port and beacon port assigned by Agones are exposed on the node's IP address, which is what clients connect to.
Redis Key Namespace¶
Redis is used for more than pub/sub — it is the live session store. Keys follow a structured naming convention so sessions, users, and servers can be looked up by multiple identifiers:
| Key pattern | Purpose |
|---|---|
placement:<sessionId> |
Player IDs waiting in a session |
server:scid:<clientId> |
Server socket client ID → session ID |
server:si:<sessionId> |
Session ID → server socket client ID |
server:smd:<sessionId> |
Session ID → server map metadata |
user:<clientId> |
Client ID → user document |
user:client:<userId> |
User ID → client ID |
user:sessionInfo:pinfo:<clientId> |
Client ID → player session info |
user:sessionInfo:cid:<charName> |
Character name → client ID |
user:sessionInfo:connected-game-session-id:<clientId> |
Client's active game session |
user:sessionInfo:preferred-game-session-id:<charName>:<mapName> |
Preferred session for map routing |
jwt:<jwtid> |
Single-use join token whitelist (TTL 120 s) |
server:r-items:<name> |
Server-side item array cache |
count:<mapName> |
Player count per map |
Summary¶
| Concept | Role |
|---|---|
| Game Server Binary | Compiled UE5 dedicated server executable |
| Game Server Container | Docker image wrapping the binary per-instance |
| Persistent Area Session | Always-on shared world (town, grind zone) |
| Dungeon Session | Ephemeral per-party instance, allocated on demand |
| Kubernetes | Container scheduling, health, and networking |
| Minikube | Local single-node K8s for development |
| K3S | Lightweight K8s for production/staging |
| Agones | Game-server lifecycle, port allocation, fleet pools |
| GameServer | Single Agones-managed container instance |
| Fleet | Pool of identical warm GameServers |
| Allocation | Atomic claim of a Ready GameServer |
| Node | Machine hosting one or more Pods |
| Pod | K8s execution unit; one per GameServer |
| Redis | Session store, pub/sub bus, distributed lock provider |