Skip to content

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 a Ready server 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