Skip to main content
Multiplayer

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.

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

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 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 declare as 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

Last updated on 10 Oct 2026