Skip to content

Tutorial Run a Server

johnjohto edited this page Jul 27, 2026 · 5 revisions

Tutorial: Run a Server

Monworks games can meet online. A server is a thin lobby: it checks who may enter, authenticates handles, shows who's here, introduces matched players — and nothing more. Battles and trades always run between the two players' own copies of the game, exactly like a direct cable link; when two machines can't reach each other directly, the server relays their traffic without ever reading it. There is no account system, no central service, no telemetry — a server is something you run, for your friends or your community.

Start one

From the toolkit checkout:

pwsh tools/server.ps1                                    # port 17226, data in user://
pwsh tools/server.ps1 -Port 17300 -ServerData C:/srv/mygame -Project build/mygame

That's the same engine binary you play with, in a headless server mode. It binds a project — by default the one in game/, or whatever -Project points at — and prints who registers, enters, and leaves as it runs.

The door: your project IS the ticket

A connecting player is admitted only if their copy of the game is byte-identical to the server's — same game version, same engine build, same content hashes. Anyone whose data differs is refused with a message naming exactly what differed, before they can enter any session. You never configure this; it is the same handshake two directly-linked games already use.

Two consequences worth knowing:

  • A public server must bind your own original (or CC0) content. Extracted Kanto content is personal-use only — a Kanto-bound server structurally admits only people who built the same extraction themselves, so it cannot serve a public audience, and must not.
  • Cheated content can't get in the door. (Play-level fairness comes with replay verification on the ladder, a later milestone.)

Operator config — <serverdata>/server.json

{
 "invites": ["cinnabar", "fuchsia"],
 "motd": "welcome to the meadow",
 "name": "meadowbrook league",
 "registration": "invite",
 "strike_limit": 3,
 "verify": true
}
  • registrationopen (anyone may enroll a handle), invite (enrolling needs one of the codes in invites), or closed (no new handles). Anything else refuses to boot, loudly — a typo must never silently open your door.
  • motd — shown to players on entry.
  • verify — turn on replay verification (below). Off by default: a casual server records nothing at all.
  • strike_limit — suspend a handle once it accumulates this many strikes from failed verifications (0, the default, records strikes without enforcing).
  • Everything is optional; no file at all means an open server named "monworks server".

Replay verification — cheating can't win

With "verify": true, every match your server introduces is verified: both games independently submit a compact replay at battle end (the seed, both teams as exchanged, every action in order, and a fingerprint of the entire battle). When the two submissions agree — every honest match — the result is recorded instantly, no simulation, no cost. When they disagree, your server replays each side's story on the very engine it runs, because the server IS the game: the version whose replay matches its own claims stands, and the other is voided and struck. A hacked client that sends an illegal move mid-battle fares no better: the honest player's game refuses it on the spot ("ILLEGAL action! Battle voided.") and the cheater's own log convicts them in the replay. Strikes live in players.json; set strike_limit to suspend repeat offenders at the door. Replays and verdicts are plain JSON under <serverdata>/replays/ — keep or delete them as you please.

What this buys, plainly: a cheat cannot beat an honest player on a verified server. The worst any tampering achieves is a voided match — the same as never having played — and a strike on the way out.

Registered players live in <serverdata>/players.json — a plain canonical-JSON table of handle → token hash. There is no database and no personal data: a player is a handle they chose plus a secret token their game stores. Deleting the file resets enrollment (players' games re-enroll automatically on their next visit).

Who is who

  • A handle (2–16 letters/digits/underscore) is registered once per server and authenticated by a server-minted token the player's game keeps. It is not the in-game character name — that stays cosmetic.
  • One live connection per handle. A second connection on the same handle is refused, so a leaked token can't silently displace its owner's session.
  • Each save-file character on a player's machine enrolls as its own handle — a character is a person as far as the server cares.

What players see

Once their game points at your server (see Join a Server), the Cable Club attendant grows an ONLINE option: a lobby of who's open, challenges either way, and then the normal link flow — Trade Center or Colosseum — between the two players directly. If a direct connection can't form (strict NATs), the session runs through your server's content-blind relay instead; players notice nothing, and a mid-battle connection blip reconnects and resumes on its own.

Practicalities

  • Default port: 17226 (UDP). Friends outside your network need it forwarded or the machine otherwise reachable; matched sessions also use the game's own link ports (17225 and a small window above it) when connecting directly.
  • The server is quiet and tiny — it forwards small JSON frames and holds a lobby list. Any machine that runs the game can run it.
  • Stop it with Ctrl+C. State is just the two JSON files in <serverdata>.

The full protocol and design live in docs/engine/server.md.

Clone this wiki locally