Skip to main content

Game Server Hosting

Last updated: 15 September 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.

RoomHostruns your roomsRoomyour game logicRegistryseats · heartbeatClientbrowses · joins

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 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.

StageWhat happens
OpenThe room accepts new players; the browser can show it.
OccupiedPlayers join and leave; OnPlayerJoined / OnPlayerLeft fire; the heartbeat reports the live count.
IdleEveryone has left. OnIdle fires; you can pause simulation or let idle cleanup retire the room.
DrainingNear end-of-life the room stops taking new players while current ones finish.
ClosedThe 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 can run in a few ways, and the platform shows which for each room:

  • Managed — the platform launches the server instance for you through a managed orchestrator (PlayServ integrates with Edgegap). The orchestrator token is your studio's own, held per project and environment.
  • External — you run the server yourself (your own fleet or machine). You declare it to PlayServ with playserv functions declare as a game server, and it registers its rooms like any other. It never needs to be deployed as a cloud function.
  • Pooled — a room served from a pre-provisioned pool.

Either way the room model is identical: register, heartbeat, accept reservations, report presence.


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​