Dedicated server
Matchdock starts one copy of your server for every match and stops it when the match is over. This page is the contract between the two.
How your server is started
Inside a container, with the command line from your build's argument template and the session in the environment:
| Variable | Meaning |
|---|---|
MATCHDOCK_API_URL |
Base URL for the calls below. |
MATCHDOCK_SERVER_SESSION_ID |
The session this process serves. |
MATCHDOCK_SERVER_SECRET |
Authenticates this process. Never log it. |
MATCHDOCK_MATCH_ID |
The match. |
MATCHDOCK_SERVER_PORT |
The UDP port to listen on (also available as {port} in arguments). |
Argument templates may use {port}, {serverSessionId}, {serverSecret}, {apiUrl} and {matchId}. Prefer the environment for the secret: command lines are visible in process lists.
The sandbox
- Read-only root filesystem; your build is mounted read-only at
/game. Only/tmpis writable, andHOMEpoints there. - Unprivileged user, no Linux capabilities, no privilege escalation.
- CPU and memory limits from the build (defaults: 1 core, 1024 MB, no swap).
- Only the session's port is published. Outbound HTTPS works; CA certificates are present in the runtime image.
- stdout and stderr are captured per session.
Do not write next to your executable and do not depend on the working directory.
1. Admit players
When a client connects and presents its join token, ask Matchdock:
POST /internal/sessions/{serverSessionId}/players/{userId}/connected
{ "serverSecret": "…", "joinToken": "…", "slot": 1 }2xx - the player belongs to this match in that slot: let them in. 4xx - drop the connection. 5xx or no answer means Matchdock is unreachable, not that the token is bad; refuse the player rather than admit someone unverified. When every expected player is admitted the session becomes ACTIVE by itself.
Report what happens afterwards:
POST /internal/sessions/{serverSessionId}/players/{userId}/heartbeat { "serverSecret": "…" }
POST /internal/sessions/{serverSessionId}/players/{userId}/disconnected { "serverSecret": "…", "reason": "CLIENT_DISCONNECTED" }A heartbeat every 10 seconds per connected player is plenty; a player silent for 30 seconds is considered disconnected.
2. Report the result
POST /internal/sessions/result
{
"serverSessionId": "…", "serverSecret": "…",
"winnerUserId": "…", "loserUserId": "…",
"winnerScore": 3, "loserScore": 1,
"durationSeconds": 184, "serverBuildVersion": "1.4.0"
}A draw uses "resultType": "DRAW" with player1UserId, player2UserId and equal player1Score / player2Score.
The call is idempotent: sending it again returns the stored result and never counts a match twice. So retry it - on network errors, 429 and 5xx, with backoff (1, 2, 4, 8, 16 seconds) - and do not retry a 4xx. A player who quits is a normal win for the opponent; report it like any other result.
3. Exit
| Exit code | When |
|---|---|
0 |
Only after the result was accepted. |
| non-zero | Anything else: nobody showed up, a fatal error, a rejected result. |
Exit on your own; do not wait to be killed. Handle SIGTERM (sent when a session is cancelled) by shutting down within a few seconds. Give up and exit non-zero if players do not arrive - for example no player after 90 seconds.
What Matchdock does when a server misbehaves
These exist so a bad build cannot corrupt ratings - not so a server can skip reporting.
- Exit
0without a result → the match isABORTED(SERVER_EXITED_WITHOUT_RESULT), no rating change. - The server exits without a result and one player had clearly left before the other → the player who stayed wins by forfeit.
- Both players vanish together (crash, network) → aborted, nobody loses rating.
- A player stays disconnected past the 30-second reconnect window and no result arrives → forfeit awarded by the platform.
- A session that makes no progress is stopped: about 3 minutes waiting for players, 15 minutes of play.