Skip to content

Introduction

A KCD:MP server runs one game mode — the rules of the server: where players spawn, what chat does, which commands exist, what timers run, what happens when someone walks into a zone or hits someone else. Everything a server does beyond moving players around is the mode’s.

Three tiers see the same surface:

Tier How Status
Lua 5.4 [gamemode] script = "gamemodes/freeroam.lua" in server.toml, or --gamemode gamemodes/freeroam.lua the flagship freeroam.lua; this documentation
C# plugin a .dll compiled against kcd2mp.Api, --gamemode mode.dll[:TypeName] available; the built-in FreeroamMode is the reference
TypeScript planned, over the same API

Lua runs in-process (NLua) with the standard libraries. The luanet table NLua adds gives a script the CLR too (luanet.import_type("System.IO.File")) — a server owner’s scripts are trusted.

Everything a mode does happens on the server’s simulation thread, one tick at a time (30 Hz). The world calls the mode’s callbacks from inside a tick, and the mode’s calls into the server (send a message, move a player) take effect in that same tick’s output. A script never races with anything — and, the other way round, a slow callback delays the whole tick.

The server log reports the tick budget every 10 s while it is missed:

the slowest tick took 41 ms against a 33 ms budget (12 over budget since the last report)

A mode’s OnTick that shows up there is the first place to look when the players stutter.

These hold for every function and callback in the reference:

  • Player ids (pid) are 0 .. GetMaxPlayers() - 1, assigned at the handshake and reused after a disconnect. Hold on to names, not ids, across sessions.
  • Positions are metres in the level’s world space. z < 0 means “on the terrain” when spawning or teleporting.
  • Yaw is in degrees: 0 faces +Y, counter-clockwise — the game’s convention.
  • Colours are 0xRRGGBBAA. COLOUR_SERVER, COLOUR_WHITE, COLOUR_RED, COLOUR_GREEN and COLOUR_YELLOW are predefined.
  • Strings are UTF-8.
  • A function that looks something up returns nil when there is nothing to find (a player who is not connected, an unknown item).

A script error inside a callback is logged ([lua] error in OnPlayerText: ..., the first 50 in full) and that callback’s effect is skipped; the server keeps running. A syntax error or an error at load time stops the server at startup with the message — better to know before players join. With hot reload a script that fails to load leaves the built-in freeroam in charge instead.