Not translated yet — showing English.
Game Server Hosting
Last updated: 10 October 2026
A game server is a program you write that holds one or more live rooms — an authoritative instance of your game where players actually play. The server SDK gives you a Room base class for your game logic and a RoomHost that runs your rooms, registers them with PlayServ, keeps them alive with a heartbeat, and retires them when they end. Getting a player to a room is the client's job (Matchmaking & Rooms, Connecting to a Game); this page is about the server that owns the room.
Server SDK, not the game client
This is the server-side SDK (PlayServ.Sdk.Rooms), used by a program you deploy as a game server — not the game client. It authenticates as a server, so it isn't limited by client-facing table permissions. A game server can be a managed instance the platform launches for you, or an external process you run yourself; both use the same SDK.
The shape of a game server
Two types do the work: a Room subclass holds your game, and a RoomHost runs rooms of that type.
A minimal room
You subclass Room<TPlayer, TConfig> with your own player and config types. The base class manages the roster (AddPlayer, RemovePlayer, Players) and exposes lifecycle hooks you override.
using PlayServ.Sdk.Rooms;
public sealed class MyPlayer : RoomPlayer { }
public sealed record MyConfig;
public sealed class MyRoom : Room<MyPlayer, MyConfig>
{
protected override void OnPlayerJoined(MyPlayer p) { /* seat them */ }
protected override void OnPlayerLeft(MyPlayer p) { /* free the seat */ }
protected override void OnTick(float dt) { /* advance the room */ }
}
A RoomHost runs rooms of that type from a factory:
var host = new RoomHost<MyRoom, MyPlayer, MyConfig>(name => new MyRoom());
var room = host.GetOrCreate("lobby-1");
That's a working authoritative server: the host wires the heartbeat, registers the room, applies the room configuration it pulls from the platform, and enforces the room's lifetime for you.
Self-registration and heartbeat
You don't tell the platform about your rooms with a separate call — the host registers each room automatically and keeps it current with a periodic heartbeat that reports the room's occupancy and capacity. This is the exact signal the client room browser reads, so a room is discoverable the moment it's running and disappears from browsing shortly after the server stops beating for it.
Room configuration is platform-owned
Room settings — capacity, reservation lifetime, room lifetime, idle timeout, and the per-server max_rooms quota — are owned by the platform and edited in the Backoffice, not shipped in your code or a manifest. The host pulls the live configuration down and applies it as it changes, so an operator can retune a room type without a redeploy.
// capacity and limits come from the platform, not the constructor —
// read them off the room rather than assuming a value you set in code
Note
Because config is pulled, changing capacity or a timeout in the Backoffice (Servers → Room settings) takes effect on the running server within a heartbeat. Don't hard-code these values; treat what the platform hands down as the source of truth.
Reporting where the room is
A client needs to know how to reach the room. Your server declares its connect details — host, port, transport, and optionally a connect string and region — with DeclareConnect, and the platform returns them to the client on its reservation.
DeclareConnect(new RoomConnect { Host = publicHost, Port = port, Transport = "udp" }, region: "eu");
When the platform launched the server for you (a managed instance), the connect details can be filled in from the orchestrator's environment automatically; when you run your own process, you declare them.
The room lifecycle
A room moves through a small, well-defined lifecycle. The host drives most of it; you influence it through the hooks and a few declarations.
| Stage | What happens |
|---|---|
| Open | The room accepts new players; the browser can show it. |
| Occupied | Players join and leave; OnPlayerJoined / OnPlayerLeft fire; the heartbeat reports the live count. |
| Idle | Everyone has left. OnIdle fires; you can pause simulation or let idle cleanup retire the room. |
| Draining | Near end-of-life the room stops taking new players while current ones finish. |
| Closed | The room is retired. Closing marks the room closed rather than deleting it, and voids reservation tickets pointing at it. |
Two cleanups run without any work from you: idle cleanup retires rooms nobody is in, and lifetime cleanup enforces the room's maximum age. Both honour the platform-owned config above.
Reconnect grace
A player who drops keeps their seat for a short reconnect grace before the room reports them gone, so a brief network blip doesn't cost the player their place. Declare it with DeclareReconnect(...).
Accepting players
A player reaches your server carrying a reservation they got from the client SDK. The host validates that reservation before the player counts as present: it must be valid, unspent, and issued for this room. Only then does OnPlayerJoined fire.
- A valid reservation → the player is seated;
OnPlayerJoined(player)runs. - A missing, expired, wrong-room, or already-spent reservation → entry is refused.
This is why the client and server are two halves of one system: the client reserves and connects, and the host you write redeems the reservation. You don't validate tickets by hand — the host does — but you decide what happens on OnPlayerJoined / OnPlayerLeft: assign a spawn, add them to the roster you broadcast, start their timers.
Where the server runs
A game server runs in one of three ways. The Backoffice shows which one on every room and fleet:
- Managed: PlayServ builds and runs the server for you on Cloud Run. Deploys, secrets and scaling work as they do for any function.
- Pooled: PlayServ keeps a machine pool at each deployment point you choose, running your container image (
linux/amd64). Rooms are placed on the pool's machines. - External: you run the server yourself, on your own machines or through a managed orchestrator such as Edgegap. Declare it to PlayServ with
playserv functions declareas a game server. It never needs to be deployed as a cloud function. When Edgegap is used, the orchestrator token is your studio's own, held per project and environment.
Whichever way you choose, the room model is identical: register, heartbeat, accept reservations, report presence. To watch and operate the result, see Servers and Game Rooms.
Capacity and the launch quota
Two different limits apply. A room has a capacity — the seats inside it, from the platform config. A game server has a room quota (max_rooms) — how many rooms may run at once. When the quota is reached, the platform refuses to launch another room before making any paid orchestrator call, so a runaway never turns into a runaway bill.
Environment
A managed server receives what it needs through PLAYSERV_* environment variables — most importantly PLAYSERV_DEPLOYMENT_TOKEN, which the SDK exchanges for a session it uses to talk to the platform. You don't set these by hand for a managed launch; for an external server you provide the equivalents so the SDK can authenticate and register.
Next steps
- Authoritative Simulation — the tick loop, sim systems, and streaming state to clients
- Matchmaking & Rooms — the client that browses and reserves the rooms this server hosts
- Connecting to a Game — how a client reaches and enters a hosted room
- Game Rooms — the live room registry in the Backoffice
Last updated on 10 Oct 2026