Skip to content

Server configuration

The server reads server.toml from its working directory (or --config <path>). Every key is optional and the values below are the defaults; a command-line flag overrides the file (--max-players 64 beats [server] max_players). The file the server ships, server.toml.example, carries the same keys with a line per group and a link here; the first start copies it to server.toml, the file you edit, and an update replaces only the example. Without either file the server runs on the defaults alone, which for two keys - the game mode’s script and the game’s tables - means less than the shipped file gives (noted below). Where a key shapes what a game mode sees, its API page is linked.

Key Default
name "KCD:MP dev server" the server’s name in the browser and the welcome
motd "" a message shown to every joining player
description "" a few lines about the server, shown when a player picks it in the launcher and on the public list. Plain text, \n starts a new line, 1000 characters at most
website "" the server’s website, "https://..." - a Website button beside the description. An address without https:// gets it; anything that is no web address is left out (the console says so)
discord "" a Discord invite - "https://discord.gg/abc123", "discord.com/invite/abc123" or the code alone - a Discord button beside the description
tags [] up to 8 short words about the server, 24 characters each - ["roleplay", "pvp", "whitelist"]; shown beside its name in the launcher’s list and details, and matched by the list’s filter - the only tags a server shows
language "" the language its players speak, as a code: "en", "de", "ru", "pt-br" …
port 7777 UDP
max_players 32 the slot count; player ids run to max_players - 1 (GetMaxPlayers). Fifty players walking, riding and fighting at once cost the server about a tenth of one core and 1.5 MB/s outbound; the count is yours to choose
password "" players must send it to join; "" = open
level "klaster" the level every client boots - klaster, trosecko or kutnohorsko (levels, GetLevel)
build_hash "" require this game build; "" = any
stale_session_seconds 5.0 names are not unique - two players may join under one name. A player who comes back under their name (from the same PC) replaces their own old connection once it has been silent this long, so a crashed game can rejoin at once; 0 = never
log_file "server.log" every line the console shows is written to this file too, next to the server; the previous run’s file is kept as server.log.1. "" = the console only
http_status true answers http://<host>:<port>/ with the server’s status as JSON - name, level, game mode, version, players and their names, slots, password, uptime, the world’s time, the description, website, Discord, tags and language - for hosting panels, uptime monitors and server-list sites. It listens on the port number over TCP (the game uses UDP), so open or forward TCP too if you want it reachable from outside; a server nobody can join right now answers 503. false = off
join_rate 20 players let in a second at most. When more come at once - a restart, a connection problem that made everyone’s game rejoin - the rest wait their turn in the order they came, at the game’s “handshaking” step, instead of all loading the server together; a quarter of the rate while the server’s connection is very busy. See below. 0 = no limit
stall_exit 30 seconds: a server that has not been able to send or receive for this long ends itself with exit code 3, so a host that starts a stopped server again (a game panel, a service manager) brings it back within seconds - its players have lost the connection by then anyway. The game mode’s shutdown runs first, so a mode that saves at shutdown still saves. 0 = never; otherwise at least 10
network_threads 1 how many network threads the server runs, each on its own UDP port - for servers of a thousand players and more. 1 = one, on port alone. 2 to 16 also listen on the ports after port (or on network_ports), and every one of them must be open to the players. See below
network_ports [] the other network threads’ UDP ports, when they are not the ones after port - for a game panel that hands out extra ports of its own: [7790, 7791] (that many threads plus the main one; network_threads may be left out)
Key Default
script "gamemodes/freeroam.lua" a .lua script or a .dll compiled against KcdMp.Api (path.dll:TypeName picks a type); "" (the value without a server.toml) = the built-in freeroam
watch false true re-reads the script (or a C# plugin’s DLL) when its file changes on disk - a development server (ReloadGameMode); the client scripts’ folder is watched too. The web interface’s folder is watched as well, apart from the script: a change there reloads the interface alone - the script keeps running and its state stays. A change is read once the folder has been still for a second and a half (a build tool’s writes are over); a folder with an error is read again, less and less often, until it loads

The game mode’s own database, for whatever it wants to keep - accounts, factions, scores, the rules on doors and chests (GetDatabase; the databases guide sets one up). Optional: without a driver the mode’s GetDatabase() answers nil and the mode runs without one. The server’s own state - doors, chests, taken items, the bans - is kept apart in data/server.db, made on the first start, and needs nothing here. The credentials live in this file and nowhere else: never in a game mode script. The environment variables KCDMP_DB_DRIVER and KCDMP_DB_URL, when set, replace driver and url (a server started by a script, a container), the way KCDMP_MASTER_URL does for the master list.

Key Default
driver "" "mysql" (MariaDB too) or "postgres"; "" = no database
host "127.0.0.1"
port 0 0 = the driver’s default (3306 / 5432)
user "" a user with rights on this one database and nothing else
password ""
database "" the database’s name
url "" one line instead of the parts above: "mysql://user:pass@host:3306/kcdmp"
ssl "preferred" "required" for a database on another machine, "preferred", "none"
pool 8 connections at most
timeout_seconds 10 a query running longer is cut off and fails
max_rows 10000 a query returning more is refused with an error (page it)
slow_query_ms 500 a query taking longer gets a line in the server log

The game mode’s client half: Lua files the server sends to every joining player and runs in their game, in a sandbox that reaches nothing outside the game (the client guide), and its web interface: a folder of HTML, CSS and JavaScript files with a ui.toml, which the players’ games show on top of the game. A mode without either needs nothing here.

Key Default
scripts "" a folder of .lua files, sent in name order; "" = the client/ folder next to the game mode’s script or plugin (gamemodes/arena/arena.lua + gamemodes/arena/client/*.lua), or for a single-file mode the folder named after it (gamemodes/freeroam.lua + gamemodes/freeroam/client/*.lua, the way the shipped freeroam draws its clock) - the built-in mode has none unless a folder is named. At most 64 files, 256 KB each, 2 MB together. /reload sends the folder again
console false true lets the players open the F9 window of the KCD:MP overlay on this server - the console line and the debug view. A convenience for a development server, not a security boundary: the server never trusts what a client says either way. The game’s own console (the ~ key) stays closed on every server
ui "" the web interface: a folder (or a .zip of the folder’s contents) of HTML, CSS, JavaScript, image, font and sound files with a ui.toml at the top. "" = the ui/ folder (or ui.zip) next to the game mode (gamemodes/arena/arena.lua + gamemodes/arena/ui/), or for a single-file mode the folder named after it (gamemodes/freeroam.lua + gamemodes/freeroam/ui/); a folder that several modes would share is never taken on its own - the built-in mode has none unless a path is named here. At most 512 files and 64 MB in all. Players download only the files their game does not have yet. The server checks the whole folder before it sends a byte: a folder with an error is not sent at all, the players keep the standard interface and the server log says what is wrong. /reload reads the folder again, and so does [gamemode] watch
ui_cdn "" https://host/folder/ - where the big files come from: a file that is not text (a picture, a font, a sound, a video) and is over 1 MB is not sent by the server; the players’ games fetch it from here, under the hash of its content. The server’s --pack-ui <folder> switch writes the files to upload there and stops; list the host in the connect permissions of ui.toml. Without this key a file that big is an error. "" = none
ui_max_mb 8 the most the web interface may send over the game connection, in MB (1 to 64), counted before compression and without the files that come from the CDN. From 4 MB up the log warns you: every player downloads that much the first time they join
sounds "" the game mode’s own sound files (.ogg, .opus, .wav, .mp3, .flac), played in the world through the game’s sound: a folder. "" = the sounds/ folder next to the game mode (gamemodes/arena/arena.lua + gamemodes/arena/sounds/), or for a single-file mode the folder named after it (gamemodes/freeroam.lua + gamemodes/freeroam/sounds/). Nothing of it goes to the players at the join: a file is sent to a player only when a sound of it plays near them, the first time, and their game keeps it for the next time. The server log names the folder and its size at the start
sounds_cdn "" https://host/folder/ - a copy of your sounds folder on your own web host (Cloudflare R2, Bunny, Amazon S3 or any other that serves files over https): upload the folder as it is, and the players’ games fetch each sound from there (https://host/folder/voices/elder.ogg for voices/elder.ogg) instead of from the server, which keeps its upload for the game. Every copy is checked against the server’s own file: one the host lacks, or an old one it still holds after you changed a sound, comes from the server instead, so upload a changed sound again when you can. "" = the server sends them
sound_max_mb 8 the biggest one sound file may be, in MB (1 to 16). A bigger file is not played, and the log names it at the start
ui_report_errors false true makes the players’ games report script errors in the web interface’s pages: the game mode is told about each one (at most 5 a minute for each player) and the server log gets a line. Off by default, when an error stays in the player’s own game log. What arrives is the player’s game’s word, cut to a short length and cleaned of control characters, and never a reason to trust the player
Key Default
tick_hz 30 the simulation rate - how often OnTick runs
snapshot_hz 30 snapshot rounds per second, at most tick_hz
workers 0 threads for the per-client phase; 0 = the cores minus two
adapt true a player whose connection falls behind gets every second or third snapshot round until it catches up, and a player who has confirmed none gets one full snapshot at a time - what keeps a crowd on slow connections from swamping the server. false = every player gets every round
nearest_full_rate 32 in a crowd, the nearest this many moving players, horses, characters, dogs and carts around a player go in every snapshot, whatever snapshot_budget says; 0 = none is sure of every snapshot
snapshot_budget 1600 the bytes of movement one snapshot may carry to a player. In a crowd that does not fit, the nearer, faster and fighting ones (a party member, an opponent) go first and the others wait their turn, never longer than about 133 ms (some 7 updates a second at the least). A stop, a crouch, a drawn weapon or a seat always goes at once, and nothing waits on a server whose snapshots come less often than every 50 ms. It keeps a player’s download steady in a packed town square instead of growing with every newcomer. 0 = no limit (everyone in every snapshot)
reliable_rate 24 megabytes a second at most of the messages that must arrive - everything but movement, voice and hit effects. A flood beyond it (hundreds of loose items restocked at once, a wave of players joining) waits its turn, each player’s messages in their order, instead of being pushed out at once and choking the server’s connection. On a big server the busiest moments may hold a message back a fraction of a second; set it for your server’s upload speed (below). Left out of the file, the server uses 24; at least 1
grime_rate 10 the other players’ dirt, blood and armour wear on each player’s screen: at most this many updates a second to each player, the nearest bodies first. A body that changes again before its turn sends only its newest state. In a big battle every blow changes them, and without a limit they went to everyone after every blow - see below. 0 = no limit
grime_range 50.0 metres: those updates only for bodies this near; a body farther away is brought up to date when it comes this near (a body coming into view always arrives with its dirt, blood and armour as they are). 0 = everyone in view
crowd_report_hz 15 when the server is too busy to send every player every movement update, or its simulation falls behind (a crowd of a thousand or more), the players who are not fighting send their own movement this many times a second instead of 30 until the crowd thins - at such times the others see them less often than that anyway. A player in a fight, or with a weapon out, keeps 30. 0 = always 30; otherwise 10 to 30
socket_buffer 16 megabytes the operating system may hold for the server’s connection each way, incoming and outgoing. When the server is busy for a moment, the players’ movements and actions wait there; when it is full, the system throws the newest away and players feel lag, rubber-banding and blows that don’t land. On Linux the system caps it - see below. 0 = the network library’s own 1 MB; at most 256

Pick the row of your server’s upload speed. Hosts sell speeds in megabits a second (Mbit/s); reliable_rate is in megabytes a second (MB/s) - divide by 8. The movement updates take their share of the line first (up to about 50 KB a second for each player with the default snapshot_budget: about 5 MB/s at 100 players, 19 MB/s at 385), and reliable_rate is about half of what is left. One server sends about 40 MB/s at most in all, so a value above 48 gains little.

Server’s upload In MB/s Typical crowd reliable_rate
100 Mbit (a small VPS, some game panels) ~12 up to ~100 players 5 - 6
500 Mbit ~60 300 and more 20 - 24
1 Gbit (a dedicated server) ~120 400 and more 24 (the default) - 48
2 Gbit ~240 any 48
10 Gbit ~1 200 any 48

Too low, and a flood takes longer to go out: a hundred players rejoining at once is roughly 30 MB - about a second at 24, half a minute at 1. Too high for the line, and a flood can overflow it again, which is what the setting is there to stop. The default of 24 is more than a 100 Mbit line carries - on such a host, set it from the table.

A big server’s costliest moment is a crowd joining together - after a restart, or when everyone’s game rejoins by itself after a connection problem. Each newcomer is introduced to everyone and everyone to them, so a thousand players joining in the same few seconds asks the server for millions of messages at once. join_rate (in [server]) lets them in a few at a time - 20 a second by default, a quarter of that while the server’s connection is very busy - in the order they came; the others wait at the game’s “handshaking” step without losing their place. A thousand players take under a minute. A server that never sees more than a few dozen players at once never notices it.

When hundreds of players fight in one place, every blow is news for everyone who sees it - and only for them: players elsewhere on the map are not told about a battle they cannot see. The server keeps that news in step with what each player’s connection can take: the others’ dirt, blood and armour wear wait their turn per player (grime_rate, grime_range), and each player’s messages go out only as fast as their own connection passes them on, so a slow connection never holds up the server’s reading of everyone else’s movements and blows. If a battle still sends more than the server can deliver, the players whose messages have fallen furthest behind (about 15 seconds) are let go one at a time - their games rejoin by themselves - instead of everyone timing out at once. The 10-second report in the log shows how close a server is: reliable out: names what was sent, and the network thread: line the players’ round trips and any player let go.

On a server that sees battles of several hundred players, lower grime_rate (5) and grime_range (30) first; then snapshot_budget and nearest_full_rate, and snapshot_hz last (it makes everyone’s movement less smooth).

Linux holds every program’s connection buffer to its own limit, net.core.rmem_max (incoming) and net.core.wmem_max (outgoing) - about 0.2 MB on most systems - whatever socket_buffer asks for. The server’s log says what it got at the start:

udp socket buffers: 0.2 MB receive, 0.2 MB send - [rates] socket_buffer asks for 16 MB; ...

A few dozen players never fill it. With hundreds, a busy moment can, and the server then logs a warning every ten seconds: the system dropped N incoming datagrams .... Raise the limits to at least socket_buffer (as root on the machine itself - a container or a game panel cannot change them; ask your host):

sysctl -w net.core.rmem_max=16777216 net.core.wmem_max=16777216

and keep them across restarts with the same two lines (net.core.rmem_max = 16777216, net.core.wmem_max = 16777216) in a file under /etc/sysctl.d/. Restart the server afterwards. Windows has no such limit.

One network thread sends and receives every player’s messages, and around two thousand players it is what fills up first - the players’ round trips grow, the most in a big battle. network_threads (in [server]) splits the players over several, each with its own UDP port:

[server]
port = 7777
network_threads = 3 # also listens on 7778 and 7779

Every player still joins on port - the server list, the launcher and join links name that port alone - and the server sends each joining game on to the thread with the fewest players; the game follows by itself (a join takes a fraction of a second longer). So open every port in the firewall or the game panel, UDP: a game that cannot reach the port it was sent to comes back to port after a few seconds and joins there, and the log warns that the port could not be reached (three such games in a row and that thread takes no joins for five minutes). On a panel that gives out extra ports that are not next to port, list them: network_ports = [27016, 27020].

The 10-second report then has a network thread N line for each, with its players, and the server’s line counts the joins sent on. If a thread other than the one on port stops working, its players lose their connection, their games rejoin by themselves and are placed on the others - the server goes on. Two to four threads suit a thousand to a few thousand players; a server that never sees more than a few hundred has nothing to gain.

With the connection no longer full, the server’s own simulation is the next limit: when it falls behind with a big crowd, the server sends everyone’s movement a little less often and asks the players who are not fighting for fewer updates (crowd_report_hz), the same as when the connection is full, until it catches up. The log says which of the two is the reason.

Key Default
radius 200.0 metres a client sees around itself; 0 = everyone sees everyone. It also decides who hears of the level’s loose items, doors and locked chests: a change goes to the players within about this distance of it, and a player is told what changed in a part of the level as they come near it - instead of every change going to everyone and every joining player getting the whole level’s
cell 64.0 the grid cell size, metres
Key Default
time "07:30" the world clock at start (SetWorldTime)
time_ratio 15.0 game seconds per real second; 0 freezes the clock (SetTimeRatio)
rain -1.0 rain forced on every client, 0 .. 1; below 0 = the weather’s own (SetRain)
weather "" the sky every client follows - a preset or a profile; "" = the server rotates the presets every few game hours (SetWeather; the game mode sets how often each comes up - SetWeatherChances)
weather_blend 10.0 seconds a weather change blends in over
max_entities 256 horses, pickups, props, actors and dogs the world holds at once
map "data/maps/{level}" the server’s map of the level: the objects admins and the game mode placed on it and the parts of the level they took out (Building on the map, the map API); {level} is the level’s name. Every player’s game downloads it at the join, keeps it for the next one, and gets every change at once. Nothing is written until the first edit; a map this server cannot read (a newer format, a broken file, another level’s) loads read-only and is never written over. "" = no map

The level’s chests and the loose items on its tables: what they hold, when they refill, whether that survives a restart (the doors and containers API). The loot tables come with the tables export (Exporting the game’s tables); without it every chest opens empty. The server keeps the doors, gates, chests, taken items and the bans in data/server.db, made on the first start - there is nothing to configure, and nothing else is kept: the server remembers no player (accounts are a game mode’s, on its [gamemode.database]).

Key Default
loot "game" what a chest holds the first time anyone opens it: "game" = rolled from the game’s own loot tables, the preset the level placed that chest with - the way the single-player game fills it; "empty" = nothing, for a server whose game mode fills chests itself (SetContainerItems, SetContainerPreset)
restock "game" when a chest somebody touched is rolled again, and when a loose item somebody took comes back: "game" = each object’s own period from the level (seven world days unless the level says otherwise), "never", or a number of world days for every one ("2"). World days run on the server’s clock ([world] time_ratio), so a frozen clock restocks nothing; /restock fills a chest at once
world_items true the items lying on tables and shelves are one per world: taken by one player, gone for everyone, back after the period as a pickup; false = every player’s game keeps its own copies, as before
persist true doors, gates, chests and taken items are as they were after a restart (the server’s own database); false = a restart is the level’s defaults again and nothing is written

The speed check measures each player over the last two seconds against the cap of the way they move - on horseback, on foot, crouched. A report that would take them faster is refused and the client pulled back to the last accepted position, so a speed hack gets its player nowhere faster than the cap; a lag spike of up to two seconds is no violation. A single step of more than 2 m faster than the cap (a teleport) is refused at once. Getting on or off a horse may move a player up to 8 m at once - from their feet into the saddle or back - and no further.

Beside it run the finer checks, which strict and speed_scale below govern: a cap for each way of moving (on foot, crouched, in combat mode, with an injured leg, with no stamina left, in the saddle), taken from the game’s own movement figures with about a tenth of room and an allowance of a second or two so a lag spike is no violation; jumps that rise too far or hang too long; a horse galloping on stamina it has not got, and a loose horse moving faster than a rider could make it; and a block, a dodge or a landed blow against a server actor that the server’s own record of the fight does not back. They always count and log; only with strict = true do they pull a player back (and read a sprint’s stamina cost from the pace the server measured). Under strict a server actor’s swing that the player’s game never answers is landed by the server itself, unless the server’s own record shows the player perfectly blocked it, blocked it, stepped away or was out of the actor’s reach (an unarmed actor reaches 0.7 of what an armed one does, [actors] reach). The ones that measure how fast a player or a horse moves run while max_speed is above 0 (the jump check and the checks on a server actor’s blows do not need it), and a game mode hears each as a hint through OnPlayerCheatDetected - speed, blink, noclip, jump, fall, horse, never_hit, void_ratio, contact_ratio. The server also watches falls: a landing from four metres or more that the game reported no damage for is logged as fall, but this version applies no fall damage of its own, with strict or without.

Key Default
max_speed 18.0 metres per second on horseback (a gallop is about 11, a dash with horseshoes and a horse perk about 16), and the most anyone may move; 0 = no speed check at all. Under 16.2 riders at a full dash are pulled back, and the server warns at start
max_speed_on_foot 8.0 walking, running, sprinting (a sprint is about 5); 0 = max_speed
max_speed_crouched 5.0 0 = max_speed_on_foot
heightmap "data/heightmaps/{level}.hmap" the level’s terrain - the files tools/KcdMp.ExportHeightmap.exe writes from your own game (Exporting the terrain). The terrain check is on while the level’s file is there and off while it is missing, so exporting the terrain is all it takes; "" = never (GetTerrainHeight)
navmesh "data/navmesh/{level}.knav" the level’s navigation mesh - the files tools/KcdMp.ExportNavmesh.exe writes from your own game (Exporting the navigation mesh). While the level’s file is there the NPC actors walk around walls and FindPath answers; while it is missing they walk straight lines; "" = never
navmesh_search_nodes 16384 the most nodes one path search may visit - a game mode’s FindPath or IsReachable, an actor’s walk, a chase. Past it the search answers “no way” and the log names it with its two points, so one search can never hold up the server. A walk of several hundred metres through a town usually needs a few thousand; 256 to 10000000
navmesh_search_ms 100.0 the same in milliseconds: whichever of the two comes first ends the search; 1 to 10000. A place no way leads to at all (a room the mesh has closed in, a ledge) answers at once without a search
collision "data/collision/{level}.kcol" the level’s collision geometry - the files tools/KcdMp.ExportCollision.exe writes from your own game (Exporting the collision geometry). While the level’s file is there RayCast answers and the floor under a player counts as the ground; "" = never
line_of_sight false with the geometry: a hit or shot claimed, a pull of a rider off their horse, a grab of a body or a pickpocket’s reach through a wall (or a closed door) is refused; off, the server only logs what it would have refused
no_clip false with the geometry: a player whose reported step goes through a wall (or a closed door) is pulled back to the last accepted position; off, the server only logs what it would have refused. A wall is what a walker bumps into (small things the game lets a player pass through are not), and a player who walks on past one at a believable pace for a second and a half is followed, with a noclip hint to the game mode
max_below_terrain 6.0 metres a player may be under the terrain (cellars, mines)
max_above_terrain 60.0 metres over it (towers, cliffs); with the collision geometry the floor under the player - a bridge, an upper storey - is the ground, so a storey’s worth (10) does
strict false the finer checks above act: true pulls the player (or the horse) back and prices a blow the server’s record does not back; false only counts and logs what it would have done - read the log for a while before switching it on. It also decides the one thing of the progression check that moves a player - sprinting or mounting a horse while carrying more than the game allows (an overloaded player’s jump is never held against them: the game allows it) - which needs [audit] mode = "enforce" as well, and which this version does not do yet (the weight rules are not measured: they only count and log). The XP, level, perk, nourishment and energy checks do not use strict
speed_scale 1.0 the limits of the finer checks above times this (0.5 to 4), for a server whose mods make players or horses faster: the caps of each way of moving, the pace of a player the game slows down, and a horse’s sprint speeds. It does not move max_speed, max_speed_on_foot and max_speed_crouched, which refuse and pull back whatever strict says - a server whose mods double everyone’s speed raises those too. An out-of-range value stops the server at start

The server list: the server announces itself there every interval_seconds and shows up in every launcher’s Servers tab. The environment variables KCDMP_MASTER_URL and KCDMP_MASTER_ADDRESS replace the two addresses when set, and the command line’s --master / --master-address beat both.

Key Default
url "https://kcd-mp.com/server-list" the public list; another list’s address, or "" = announce nowhere (a private server)
interval_seconds 60 how often
public_address "" the address the list should show; "" = the one the master sees the heartbeat come from; "host:port" when players reach the server on another port than [server] port (a tunnel’s)

Admins are the game mode’s: it promotes them with SetPlayerAdmin from its own list or its own accounts (the shipped freeroam takes the names from the KCDMP_ADMINS environment variable, "Henry,Hans"); the server keeps no accounts and no admin list.

Key Default
builtin true false = no built-in chat commands at all; the mode answers everything
public ["help", "pos", "players", "items", "uptime"] the built-ins anyone may use; every other one answers admins only
disabled [] built-ins switched off by name, e.g. ["horse", "rain"]; the mode may still answer them

The audit compares what a player’s game says with what the server knows happened. It has two halves under one switch - the item check and the progression check, on the character - and mode decides what either does about a difference.

The item check. Every item that reaches a player’s bag or their horse’s saddlebags must be explained by something the server saw happen: its own gift (GivePlayerItem, SetPlayerInventory), the spawn’s outfit, a pickup of an item it placed, one of the level’s own items, a chest’s contents, a shop, a body that was searched, the player’s own drop picked up again, an arrow they shot, a move between their bag and their horse’s saddlebags, or the game’s own making (crafting, alchemy, cooking, butchering), which the game mode may judge with OnPlayerNativeItem. The player’s game names where each item came from the moment it arrives, and the server checks that against its own records. An item nothing explains - one a cheat tool conjured or multiplied - is logged and, with mode = "enforce", taken away after a second or two. That alone is never a violation: an honest player whose game and the server disagree for a moment loses the unexplained piece and nothing else. Every kind of item is judged the same way, a sword or a loaf. What leaves a player is judged too: a drop, a use, a shot, a put into a chest or a sale of more than the records explain never becomes somebody’s pickup, a chest’s contents or money.

A violation is a cheat proven beyond doubt: a game that keeps what the server ordered out of it, one that forges where an item came from or the order of its own reports, one that edits the same item again and again after it was taken away, or one that stops reporting its items (nothing at all for two minutes, or no report of its bag within twenty seconds of the spawn’s strip). OnPlayerCheatDetected hears the detections, the proven ones and the hints alike, with how many of each kind the player raised (the movement hints come five times a visit, then once every five seconds per kind), and OnPlayerAuditViolation every item about to be taken away.

Key Default
enabled true false = the audit is off: every item a player’s game reports is taken as the server’s, and the progression check takes levels, XP, perk points, perks, nourishment and energy as the game reports them and judges no carry weight; the server’s own gifts, takes and sales still work
mode "observe" observe judges and logs everything and takes nothing away - items, and the progression check below; enforce takes away what nothing explains and puts back what the server can disprove - switch to it once the log shows that your game mode hands out nothing the server does not see (what each mode does). Any other word stops the server at start. The environment variable KCDMP_AUDIT_MODE replaces the file’s value and the command line’s --audit-mode enforce|observe beats both - the way to fall back to observe without editing the file
action "kick" kick, ban or log for a violation (a cheat proven beyond doubt: the ones above, and a game that writes the same forged XP, level or perk points back again and again); an unexplained item alone is taken away, never punished. Any other word stops the server at start
ban_seconds 0 for action = "ban"; 0 = for good
guarded_categories "Armor,MeleeWeapon,MissileWeapon,Ammo,Helmet,Hood,Money" the item categories (items) the game’s own making never gives a player: a crafted or found item of one of them counts only when the game mode takes it (OnPlayerNativeItem), and a loose item of one that the level’s tables do not list is never taken on the player’s word. It does not choose what is judged - every category is. Money is the purse, and guarding it makes it the server’s: a player starts each visit with the money the game mode gives (GivePlayerMoney) and keeps it through a death, and a shop sells against the money the records explain (GetPlayerMoney). HorseGear (a horse’s saddles, bridles, saddlebags and caparisons) is left out; add it to guard it like armour. An item the game’s tables do not know - a game mod’s - is never guarded

An older file’s strikes line is read and ignored: the item check judges every change at once and no longer counts strikes.

The tables export. The check needs the game’s tables export (Exporting the game’s tables): without it the server cannot tell the items apart, so the item check is passive - it follows what the players’ games report and takes nothing away, until the export is there - and the carry-weight check has no weights to add up. The perks and consumables lists in the same file are what the progression check understands perks, potions and drinks by. Export again after an update of the server: the export carries the game’s clothing sets and weapon sets (with how many of each item), which the check needs to recognise a player’s starting outfit and weapons. With an older file a spawn’s clothing is credited by item category and the arrows of a weapon set as one stack, up to a hundred, and the log says so (the clothing preset ... is not in the tables export: its outfit is credited by item category (re-export the tables)).

observe enforce
An item nothing explains stays with the player; the log says would remove taken out of the player’s game after a second or two; the log says what and why
A drop, a use, a shot, a put or a sale of more than the records explain goes through; the log says what it would have refused refused
The first seconds of a visit (below): a drop, the use of a bag item, a shot, a shop deal, a dice stake goes through refused until the player’s items are known
What a game mode reads of the bag and the purse (GetPlayerInventory, GetPlayerMoney) what the player’s game holds what the server’s records explain - gifts on their way counted, takes on theirs taken off
Levels, XP, perk points, perks, nourishment, energy nothing explains the log says would set back; the record a mode reads follows the game; nothing is sent to the player’s game the rules that act (below) put it back, and the record holds the figure the server explains

Whatever the mode, four things happen: a cheat proven beyond doubt gets the action; an item whose pickup or take the server itself refused (it was out of reach, somebody took it first, OnPlayerPickup or OnPlayerTakeWorldItem said no) is taken out of the bag at once, because that is a decision about a claim and not a judgement; what the game mode orders is carried out - its gifts and takes, a restore, a sale, a body’s search, a dice stake - and a progression order of the mode’s own that the player’s game did not confirm within about 45 seconds is sent again; and the spawn’s strip (below) happens.

The first seconds of a visit. At a player’s first spawn of a visit - and at a later spawn whose clothing or weapon preset differs from the one before - the server has their game empty the bag and put the outfit on, then starts to follow the bag: the player’s game sends the whole of it once and every change after. A respawn with the same presets keeps the bag and the purse. Until the whole bag has arrived - the first moment for an honest game - enforce refuses a drop, the use of a bag item, a shot, a shop deal (Not now: your items are still being checked.) and a dice stake, so nothing is spent from records the server has not got. A game that never sends it is a proven ledger_silent: twenty seconds after the strip, sixty from the spawn when the strip itself never comes.

What explains an item, for the owner.

  • The spawn’s outfit - the pieces of [spawn] clothing_preset and weapon_preset (and a mode’s SetSpawnInfo presets), as the tables export lists them, and [spawn] items. A mode that dresses players with SetPlayerInventory instead gives them.
  • The game’s own making (crafting, alchemy, cooking, butchering, a drink’s empty vial …) - taken for ordinary items within a limit of value a minute, so a normal craft passes and a flood of them does not; a guarded category is refused, smithing a sword included, until the mode adopts it in OnPlayerNativeItem; anything the game’s own scripts make is refused while story quests are off (SetStoryQuests); an item made by code outside the game is never credited - it is a foreign_call hint, and under enforce the item is removed.
  • The level’s items and chests - a pickup of an item the server placed counts when the player was within about eight metres and could see it, the first claim wins and a late one loses the item. One of the level’s own loose items counts when the level’s tables list its place and the player stood within about eight metres of it; a loose item with no place in the export is accepted only for ordinary items and within a limit a minute, and ammunition only up to what the player shot themselves. A chest holds what the server’s record of it holds: a player may open at most four a second, hold sixteen open at once, and a game that names hundreds of chests the level does not have is refused new ones. An honest hand never notices.
  • A horse’s saddlebags - the server keeps what each horse carries, and the player whose game holds the bags (the owner, else the rider, else whoever controls it) sees it; what moves between that player’s bag and the saddlebags is credited only for what the records let go of. The saddle, bridle and horseshoes the game puts on a horse the server brings (/horse, a mode’s CreateHorse) are the horse’s own gear: the check expects them and neither counts them as saddlebag items nor logs them as unexplained.

What an honest player can lose under enforce, for the items: an item a game mod puts in the bag, which the server cannot explain ([mods] policy is the owner’s tool, and OnPlayerAuditViolation a mode’s); a crafted, butchered or found item over the limit above or of a guarded category; and an item a game mode’s client script or an event hands out without the server’s calls. A game that says it cannot report its items is followed and has nothing taken by the check. The log names every removal and where the item seemed to come from.

The line It means
item ledger: observe - ..., item ledger: enforce - ... at the start the item check’s mode; PASSIVE = no tables export, nothing is removed; OFF = enabled = false
progression audit: observe - ..., progression audit: enforce - ... the progression check’s mode; under enforce the next line names the rules that do not act yet; further lines name a missing perk or consumables list and carry-weight audit: INERT (no item list); the nourishment and energy check follows with its own nourishment and energy audit: ... lines, saying what acts under the mode and what is only counted
Henry#0 (...): item ledger open (23 item(s) reported) the server follows Henry’s bag from here on
... shortswordBroad x1 (native script) not explained by the records: observe - left alone a gain nothing explains and where it seemed to come from (below); under enforce removed unless explained within a moment
... observe: would remove shortswordBroad x1 - holds 1, the records explain 0 (native script) what enforce would take, at most one line a class every half minute
... shortswordBroad x1 removed from their bag - holds 1, the records explain 0 (native script) (removal 1 this session, no violation) the same, done: a removal, never a kick
... drop of '<class>' refused, more than the records explain, ... observe: would refuse the use of ... what leaves a player, judged
... anti-cheat: removal_ignored (proven): ..., kick a proven cheat and what the action did; advisory ones are hints (, tolerated by the game mode when a mode said so)
... progression: weapon_sword: would set back 458.1 XP (...) the progression check, per player: the first thirty lines of a visit, then every fiftieth
... weight trace: carried 12.5, capacity judged 140.0 (bound 140.0, strength 5, reported 140.0), not overloaded the carry-weight check’s figures, when they move (strength 5 + 2.5 from a drink while a drink lifts the capacity)
item ledger (observe), since the start: grants 12, native.craft 40, removed 3, would_remove 12, ... every ten minutes: the counters since the start, named for what they count (grants, removed, would_remove, refused.<where>, native.<kind>, progress.*, vitals.*, weight.* …)
Henry#0 (...): measures: 0 steps refused, ...; reported: progress x2 when a player leaves: what the checks counted for them

Per player the item check writes its first forty lines of a visit and every fiftieth after that ((120 ledger lines this session) at the end of such a line), so a flood from a hostile game cannot fill the log; the counters carry the rest.

The words in brackets after an item say where the server thinks it came from: unknown (no part of the game that moves items saw it arrive - a value edited in memory looks like this), native <kind> (the game made it: craft, butcher, loot, tool, eat, script - the game’s own Lua, which a cheat tool uses too - …), pickup, level, container, grant (the server gave it), outfit, ammo, from a horse, own drop. A game that says its item reporting cannot run is followed, not judged, under observe; under enforce it is credited for what the server’s own records explain and nothing is taken from it (the log line reads their hooks do not run: would remove ..., and the hint is ledger_unavailable).

The same switch covers a second check, on the character. A trainer writes a level, XP, perk points, a perk, the nourishment or the energy into a player’s game as easily as a player earns them, so the server keeps its own account of what explains each rise and puts it beside what the player’s game reports:

  • XP, levels, perk points and perks. What explains a rise is the XP the game awarded (the player’s game counts each award it makes and tells the server) at the server’s XP rate - 0 until a mode turns XP on with SetXPRate, so on a server that never does, any rise is unexplained - what the game mode gave (AddPlayerSkillXP, SetPlayerStat, SetPlayerProgress … - every order of the mode’s counts as the character’s from the moment it is sent), the potions and perks that multiply XP, the perk points the levels pay (one for every even level a stat, a skill or Warfare reaches) and the perks the player bought with them. The XP each level takes is the game’s own table, which the server holds. A rise nothing explains is an excess.
  • Nourishment and energy. Both fall steadily with the game’s clock - about two points for every game hour a character stands still, each - so the server judges them on its own game clock ([world] time_ratio), never the player’s calendar. A value above the game’s maximum (150 and 100), one that stands exactly still for an hour and a half of game time or more (six minutes at the default ratio), one that falls far slower than the clock says, or one that rises with no food, drink or bed the server saw looks like a trainer’s. A meal or a potion, the game’s own digestion buffs, a change of the server’s clock and a mode’s own set void the stretch they touch, so an honest player is never judged across them; an open game menu (the inventory, the perks screen) all but stops the game’s digestion while its clock runs on, and the server cannot see menus yet, so a long flat stretch can be an honest player’s and is only logged; a night in a bed gives rest the check cannot tell from a rise yet, so it is only counted. A server whose clock is frozen or runs faster than sixty game seconds a second is not judged for these two at all. With enforce, once the nudge is measured (below), the server may change a value that stands perfectly still by one point to see whether it is written back: at most six times for each of the two, kept through a reconnect for six hours, never below six points and never while a meal holds the value at its maximum.
  • Carry weight. The server adds up the weight of what its item records say a player carries (what they wear counts, as it does for the game; the money is left out, so it is meant to be at or under the game’s own figure) and bounds what they may carry from their strength, perks, potions and drinks - a drink lifts a player’s strength for a while, and the server allows for that for at least ten minutes after it (GetPlayerWeight). The game never stops a player from picking things up; being over the limit only holds them back - no sprinting and no mounting a horse, while a jump is still allowed and so never judged - so the check watches whether an overloaded player does what the game forbids, and whether a player’s game reports a capacity the strength cannot give. Nothing is ever taken from a heavy player. A horse’s saddlebags are compared with the game’s own limit too.

With mode = "observe" (the default) the server judges everything and logs what it would do - would set back 340 XP - and asks the game mode (OnPlayerProgressViolation) for the XP and level it would put back, but sends nothing to a player’s game for the audit: no put-back, no probe, no nudge. The record a game mode reads and saves still follows what the game reports, so a forged value would be the one saved: a mode that keeps characters should not rely on the record until the server runs enforce. With mode = "enforce" an excess is put back with an exact set after a second look at fresh evidence: the player’s game holds what the server can explain, and so does the record a game mode reads and saves (GetPlayerProgress) - a forged value is never the one that is saved, and a level that was put back is never announced (OnPlayerLevelUp). The mode is asked first and may vouch (below). A game that writes the same forged value back again and again, three times within ten minutes, is cheating beyond doubt: a proven progress_write_repeat (never on a server with story quests on), and action follows it as for the item check; it needs enforce, since only a put-back can be written back. Only a rise that matters counts toward the three: an excess of at least 25 XP, or of 2 or more perk points or a perk - a smaller one is never proof. A value that keeps coming back is put back four times in the first half minute and then once a minute for as long as the player’s game holds it, so with action = "log" it is gone within a minute of the trainer letting go.

Which rules act. A rule is measured against real servers before it acts, and this version ships with only the measured ones. Under enforce it puts back:

  • an XP that is over what its level takes, and a level over 30 - a state no character is ever in;
  • a clear XP or level rise: 300 XP or more that nothing explains, and over three times what the server can explain of the rise or three levels or more above it - a trainer’s level 30, a skill written up by a dozen levels - and a game that joins with that much more than a fresh character holds. Not on a server with story quests on, where the story’s lessons and scripts hand out levels the server never sees (vouch for them there, or run observe). A smaller rise is only logged, as below;
  • perk points of a stat, a skill or Warfare that its levels (and what the mode gave) do not pay for, with the perks bought from them counted as points - the newest perks bought with points nothing paid for go with them. The perks list of the tables export is how the check tells a bought perk or a respec’s refund from a gift, and without it a respec that returns points reads as points nothing explains;
  • a perk bought below the level it asks - a perk of a level the server put back is one. A level that reaches the server a moment after the perk keeps it;
  • a nourishment above 150 or an energy above 100, and a mode’s own SetPlayerNourishment / SetPlayerEnergy above them is trimmed to them.

The rest is judged, counted and logged as “would …” and not acted on, under enforce as well: a smaller XP rise a stat or skill gained against what the game awarded, the pool of perk points of the main level, a game that joins with a little more than a fresh character holds, the nudge, a drain that is too slow, a rise with no meal or bed, and the weight. The server’s start-up lines under enforce name every rule that does not act yet. The proven repeat of a frozen nourishment or energy (vitals_write_repeat) needs those rules, so it does not occur in this version. Read the log for a while, as you would for the items, before you count on a rule to do more; a later version switches each one on when it has been measured.

What a game mode hears. OnPlayerCheatDetected gets the proven progress_write_repeat and, as hints that are evidence for a rule of the mode’s own and never a reason to kick by themselves, progress (a rise nothing explains), progress_ack (the player’s game does not confirm the server’s orders), progress_gate (the player’s game says its own XP counting is not running), vitals (a value over the game’s maximum), vitals_rewrite (a value put back to its maximum and written up again) and code_guarded (an edit of the game’s code inside a function these checks rely on). Of the weight hints the mode hears a player’s game reporting a carry capacity above what their strength, perks, potions and drinks allow - a trainer’s capacity; the others (sprinting or mounting while overloaded, a hidden carried weight, a horse’s saddlebags) and the hints about a drain that is too slow wait for their measurements: they are counted and logged and not told to the mode yet. Each hint reaches the mode and the log at most once every ten seconds and thirty times an hour per player and kind, and the check itself lets through one for each record every ten minutes; the rest are only counted. GetPlayerProgressAudit has the counts for a mode that wants to decide for itself - its gate_down, read in OnPlayerProgressViolation, is how a mode treats a large excess from a game whose XP counting is not running as the trainer it most likely is.

What the check compares against.

  • A player who comes back within six hours (by name and address) is compared with what the server counted before, so XP the game awarded in between - offline, or on another server - is explained and a value edited in between is not. A player the server has no history of - after a server restart, or a game that came from another server - starts from what their game reports, with every XP potion that applies assumed to be running for as long as it lasts.
  • A game that has just started is compared with what a fresh character holds - in this version for the log only.
  • A game update changes the game’s tables: the server’s XP table was compared with the game of its own release, and on a newer game build the crossing of a level is judged as observe would until a server update knows the new build. Export the game’s tables again after an update.
  • An order the player’s game did not confirm is sent again after about 45 seconds, in observe as well; progress_ack says so. The game confirms an order by its number in its next report.

What an honest player can lose under enforce - the whole list. The rules that act in this version put back nothing an honest character reaches except as noted; the first three cases are what the XP rule will bring when it acts:

  1. A game whose XP counting could not start (the log says progress_gate): the XP it earns is not seen being earned, so it is put back for the visit. The cause is on the player’s side, and the fix too.
  2. A server with story quests on (SetStoryQuests): a quest, a lesson or a script gives XP and levels the server never sees awarded, so they are put back too - unless the game mode vouches for them in OnPlayerProgressViolation (return false). A story server that switches to enforce must do that; nothing is ever proven against a player on a story server.
  3. Progress a game mode gives in a way the server cannot see - a teacher of its own, an event’s reward - for the same reason and with the same remedy.
  4. The moment between the server noticing an excess and putting it back: XP earned in that second is overwritten - and a game mode’s own exact set that arrives while a player is earning XP overwrites that award as well.
  5. Perk points of a respec on a server without the perks list in its tables export, as above.

A network blip, a server restart or an order the player’s game dropped loses nothing: the server remembers what it counted, or takes the game’s report as the starting point, and sends again an order that was not confirmed.

The game’s own mods a player’s game may run: the Steam Workshop items and the folders of the game’s Mods folder it loaded. The player’s game reports each one with its id, name and version (from its mod.manifest) when it joins; a refused one turns the player away with the mod’s name, and the game mode reads the list with GetPlayerMods. The list is the player’s own game’s word - a modified game can leave a mod out - so the rules that matter (speed, damage, the inventory) stay the server’s checks.

Key Default
policy "any" any: every mod; allow_list: only the ones in allowed; none: no mods at all
allowed [] with allow_list: the mod ids a player may run, e.g. ["smithsreach"]
refused [] mod ids never let in, whatever the policy

The model is the Combat guide.

Key Default
pvp true false: players cannot lock on to or hurt each other
respawn_seconds 10 a dead player stands up again at their spawn after this; 0 = the mode calls SpawnPlayer. The respawn waits (30 s at most) while someone is searching the body
loot_bodies "all" what a dead player’s body gives up when someone searches it: "all" = everything they carry and wear, and their purse; "carried" = the same without what they wear; "none" = bodies cannot be searched. The list is what the server’s records say the dead player carried; whatever the search takes is taken out of the dead player’s game item by item, and the searcher is credited only with what that game really gave up (the few items the game refuses to delete, quest items, are left alone). OnPlayerLootBody can refuse a search
corpse_seconds 0 how long the body stays where it fell after the respawn; 0 = it goes with the respawn. At most 3600. A body can be carried (see the carrying guide); at most two of one player’s, and a quarter of max_entities in all, stay at once - a new one past a limit takes the oldest one’s place. The body holds nothing and the player comes back with everything nobody took - a game mode that wants it otherwise fills it in OnPlayerCorpse
max_reach 4.0 metres the attacker and the target may be apart for a claim to count
fight_timeout 30.0 seconds without a blow that end a fight; 0 = never by time
claim_window_ms 1500 a hit counts only this long after the attacker’s swing; 0 = always
damage_scale 1.0 a multiplier on every hit
max_claim_damage 80.0 without the tables, a claim above this is clamped
tables "data/combat/items.json" the game’s tables: weapons, armour, the item, soul, buff and mesh catalogues, and the loot tables the chests are filled from ([containers]) - the file tools/KcdMp.ExportTables.exe writes from your own copy of the game (Exporting the game’s tables); "" (the value without a server.toml) = no tables: the damage the clients report, clamped by max_claim_damage, the catalogues by GUID only, every chest empty
claimed_buffs [] the buff classes the server takes over (buffs, ClaimBuffClasses)
potion_speed 2.0 a potion’s regeneration runs this many times faster than the game’s own
fall_damage 1.0 the game’s own damage for a fall times this; a deadly fall is a death like any other (the notice, the respawn); 0 = falls hurt nobody
alcohol_per_content 0.01 a drink’s alcohol content × this on the blood-alcohol level
alcohol_decay 0.00125 taken off the level per second
drunk_threshold 0.32 drunk from here up; sober again from half of it down
hangover_seconds 120.0 the hangover after sobering
attack_scale 0.32 a weapon’s table attack × this = its damage before armour
ranged_scale 0.0077 an ammunition’s attack × the launch speed × this = a projectile’s damage before armour
armour_pivot 50.0 the armour on the struck part that halves a hit (d / (d + pivot), 85 % at most)
fist_damage 6.0 bare-handed, before armour
body_parts "" rewrites of the claim’s body part, "claim=part,..."; normally empty
max_stamina 100.0
stamina_regen 15.0 per second standing, three quarters of it walking or running (as in the game), after 1.5 s without a cost; halved with an injured head or torso
attack_stamina_cost 10.0 a swing (× 1.2 slash or smash, × 0.5 bare-handed, × 1.25 with an injured arm)
hit_stamina_damage 12.0 a hit taken
shot_stamina 15.0 a shot
shot_interval_ms 400 shots closer than this are refused
shot_window_seconds 6.0 a ranged hit counts within the flight time plus this
block_stamina_cost 10.0 a swing blocked
perfect_block true perfect blocks and master strikes count: a perfect-blocked swing does no harm, a master strike cuts back; false ignores both
parry_window_ms 500 at most this long a landed blow on a player whose game judges no blows (a bot) waits for their perfect block or block (their round trip decides, a little longer while their block is raised; 0 = no wait, at most 2000)
counter_parry_window_ms 700 the same for a counter after a perfect block, which its victim sees and parries a moment later than a plain swing (0 = as parry_window_ms; at most 2000)
perfect_block_stamina 15.0 taken from the attacker whose swing was perfect-blocked (the defender pays nothing)
victim_blocks true the victim’s own game judges every melee blow between two players: a blow their game blocked or parried does no harm, whatever the attacker’s screen showed a moment earlier (hit_rule says what counts as a touch); false takes the attacker’s word
verdict_window_ms 1500 at most this long a blow the attacker saw land waits for the victim’s game to judge it (their copy of the swing may start a little late on their screen; a blow that is judged lands at once); 0 = no wait, at most 3000
hit_rule "contact" which screen’s touch makes a melee blow between two players count. "contact": a blow counts when it touched on either player’s screen - the attacker’s or the defender’s - unless the defender’s game blocked or parried it in time (a blow the defender’s game never answered counts too, unless the server saw their block up as it arrived); the two screens never agree exactly where everyone is, and this way a blow that clearly connected for the attacker is not lost. "victim": a blow counts only when it touched on the defender’s screen (unanswered_blow and unshown_blow decide the rest)
defense_grace_ms 200 with "contact": the most latency a block or parry is credited for. The defender sees each swing a little late - about the two players’ average ping; past this, a block counts only if it was raised that much earlier, and a perfect block or master strike counts as a plain block. Players under about 150 ms ping (or one high-ping player against a low-ping one) never feel it; 0 = no limit, at most 1000
trades "first" two players swinging at each other at the same time. "first": as in single player, the first blow to land stops the other player’s swing if it had not landed yet - that blow does nothing, whatever its own screen showed. "both": every blow that touched counts, so both players take the hit
trade_tolerance_ms 60 with trades = "first": two blows this close together both land - single player’s “same moment”, widened a little for the players’ clocks; at most 500
dodge_check true a blow that touched only on the attacker’s screen (the defender’s game showed no touch) misses when the defender had already stepped clear of the blade at that moment - the attacker’s screen shows the other player a moment in the past, so a step or a dodge away may not have reached it yet
dodge_margin_m 1.5 how far from the blade the defender must have been, in metres along the ground (0.2 to 5)
dodge_allowance_ms 150 the most a defender is credited for their reaction when both players’ pings are high (they see each swing later); 0 at ordinary pings, at most 500
fair_ping_ms 200 the most ping the fights make up for. Up to it, every blow, block and dodge counts at the moment the player really made it. Past it, the extra lag counts against that player: half of what their ping is over the limit is added to each blow, swing and dodge of theirs, and their blocks are excused that much less. A player with a bad connection is never at an advantage, at worst at a small disadvantage. 0 = no limit, at most 2000
unanswered_blow "miss" with hit_rule = "victim": a blow the victim’s game never judged in that time: "miss" (their game never showed it reach them) or "land" (the attacker’s word)
unshown_blow "miss" with hit_rule = "victim": a blow whose swing the victim’s game says it never showed at all (the other player’s figure was busy or not there on their screen): "miss" or "land"
attacker_preview false true: the attacker’s screen shows a hit the moment their blade touches the other player (a guess - some will do no damage); false: it shows the defender’s verdict - the hit, the clash or nothing - a moment later
master_strike_scale 1.5 a master strike’s damage against a plain swing of the same weapon
master_strike true players may master-strike (and disarm each other); false takes the move away - their games offer only the plain perfect block
clinch true two players may clinch: their blades lock, the one who leads strikes inside the clinch and the other may counter, on every screen at once; false never pairs them (they fight on)
clinch_scale 0.6 a clinch blow’s damage against a plain swing of the same weapon
clinch_parry_window_ms 1300 at most this long a clinch blow waits for its victim’s counter inside the clinch (their round trip decides; 0 = no wait, at most 3000)
sprint_stamina_cost 5.0 per second at a sprint - the game’s own
jump_stamina_cost 9.0 per jump - the game’s own
injury_threshold 15.0 a hit of at least this injures the part struck
bleed_per_damage 0.015 a stab or a slash that gets through the armour opens a wound bleeding this × the damage in health per second (0 = no bleeding)
max_bleed 1.0 health per second at most, all open wounds together
bleed_seconds 40.0 how long each wound bleeds (a new wound does not restart the others)

Heavy armour holds cuts back: a cut bleeds only when the armour on the struck part let at least half of it through, else every third cut it held opens a wound anyway. A game mode can change all of this, and decide any single blow, with SetBleedRules.

What the players’ screens show of each other, drawn in the game’s own UI. A player can hide either on their own client (KcdMp_nameplates 0, KcdMp_health_bars 0 in the console); the server’s false hides it for everyone.

Key Default
nameplates true a name over every player’s head, in the game’s typeface, smaller and fainter with the distance; "party" shows the party members’ alone, false nobody’s; a game mode’s SetPlayerNameplate / SetPlayerColor still change the text and the color
health_bars true a health bar under the name, following the server’s word on that player’s health; "party" or false like the plates
through_walls false true shows the plates through buildings and doors; the default hides one whose player or NPC actor is out of sight - the NPC actors’ names follow the same rule
range 50 metres a plate shows up to; it fades out over the last third. A game mode can give the NPC actors’ names a range of their own (the nameplates option npc_range, Change the interface)
injuries false true adds six marks under the bar, lit red for the body parts the server knows as injured (head, torso, arms, legs)
party_color "" a color like "80E080" for a party member’s name; "" keeps the game’s own gold
team_color "" the same for a teammate’s name (SetPlayerTeam); a game mode’s SetPlayerColor beats both
party_compass true the party members as blips on the game’s compass, at their bearing, at any distance
chat_bubbles true a player’s chat line over their head for a few seconds, in the game’s speech bubble; a player can turn it off on their own client (KcdMp_chat_bubbles 0)
map_fog false true keeps the game’s fog of war over the parts of the world map a player has not walked; false shows every player the whole map from the start. A game mode lifts the fog for one player with RevealPlayerMap

The proximity voice chat: a player’s voice reaches whoever stands near them and plays from where they stand, through the game’s own 3D sound, fading with the distance. The server relays each voice frame the moment it arrives, only to the players within range; a game mode changes the range at run time (SetVoiceRange), gives one player their own (SetPlayerVoiceRange), mutes a player for everyone (SetPlayerVoiceMuted) or for one listener (SetPlayerVoiceMutedFor, what a player’s own /mute <name|id> does) or deafens one (SetPlayerVoiceDeaf), and hears OnPlayerVoiceStart / OnPlayerVoiceStop. Players choose their own microphone, push-to-talk key and mode in the launcher’s Options.

Key Default
enabled true false = no voice chat on this server (the clients are told at the handshake)
range 25 metres a voice carries; a voice is at full level within the first metre and fades to silence at the range
max_bytes 400 the largest voice frame relayed; 400 covers the highest quality a client sends

The groups the server keeps for the game mode’s own commands (the parties guide): the invitation, the frames on the members’ screens, the no-friendly-fire rule. The commands themselves - /invite, /accept, /p - are the mode’s; the shipped freeroam mode has them.

Key Default
max_size 5 members per party (at least 2)
invite_timeout 30 seconds until an unanswered invitation runs out
frames true the party frames on every player’s screen; a mode can switch them per player (ShowPartyFrames, or the party component of Change the interface)
leader_leaves "next" what the leader’s leaving does: "next" hands the lead to the next member in join order, "disband" ends the party
friendly_fire false true lets members hurt each other; off, a hit between members is refused before the mode hears of it, like one between teammates

The gestures players make for everyone around: the wheel on G (hold it, point, let go; a tap leaves it open for a click; the number keys pick directly) and the point held on T (the arm stays out while T is down, walking allowed). The server checks every pick - the player alive, standing still on foot (the point goes with a walk), no weapon out, not fighting - and ends a looping one when the player moves. A chat command for them is the game mode’s (the shipped modes have /anim; the Lua group shows how).

Key Default
enabled true false = no wheel and no point on T (G and T stay the game’s own); a game mode’s PlayPlayerEmote - its /anim - still plays one
wheel ["wave", "bow", "cheer", "clap", "dance", "come"] the wheel’s emotes, clockwise from the top, at most eight; [] = no wheel, and G and T stay the game’s own. The other built-ins are salute, nod, sit, armscrossed, drink and point (T’s)

[emotes.custom] adds your own, one key per emote: the name (1-24 of a-z, 0-9, _, -) = "Label | clip | fragment | tags | loop". The label is what the wheel shows; the clip is what the others see the body play (the animation lists); the fragment and its tags are what the player’s own game plays (the fragment families of the same lists; the tags may be empty); the fifth part is loop (until the player moves) or once, the default, and may add upper (the upper body alone: it plays while the player walks - looped with loop, the hands tied behind the back; no fragment needed) and locked (only a game mode ends it). A sixth and a seventh part are the in and the out, each clip fragment tags: played once before the loop and once when it ends - sitting down and standing up. A custom emote with a built-in’s name replaces it.

[emotes]
wheel = ["wave", "bow", "cheer", "clap", "dance", "come", "kneel", "sit"]
[emotes.custom]
kneel = "Kneel | pray_kneel_loop | PrayKneeling | | loop"
rest = "Rest | resting_ground_idle_01 | Lying | lyingGround+var1 | loop | resting_ground_in_01 LieDown liePlaceGround+lieVar01 | resting_ground_out_01 GetUp lyingGround+var1"
cuffed = "Cuffed | hands_tied_back_idle | | | loop upper locked"

Horse-drawn wagons and two-wheeled carts. A route is a road the server drives its carts along at a steady pace - a loop, or there and back with a turn at each end - with stops where they wait; a wagoner sits on the bench and players ride along in the back. A free cart stands where it was put until a player takes the reins at its bench and drives it themselves (W / S the pace, A / D to steer, Shift held for the canter). Players get in and out with the game’s own prompt and dismount key. Routes are recorded in the game by walking or riding the road (the routes guide); a game mode can make its own with AddCartRoute, put free carts anywhere with CreateCart and decide what each is made of - the body, the wheels, the load, the horses (personal carts).

Key Default
enabled true false = no carts on this server (no routes run, CreateCart makes none)
routes "data/carts/{level}.toml" the routes file, read at the start - {level} is the level’s name; /cartroute save writes it; "" = no file
speed 7.0 metres a second a route’s carts travel at when the route names no speed of its own (7 is a horse’s canter; at most 15)
drive_speed 7.0 metres a second at most for a cart a player drives (SetCartSpeed sets one cart’s own)
driver_clothing "24e4aa5b-cd2c-4dba-9426-b63e674b7037" the clothing preset of the wagoners on the routes (the clothing presets)
Key Default
range 150.0 metres from the point within which players get a mode’s effect, decal or sound (SpawnEffect, SpawnDecal, PlaySound); 0 = everyone in that virtual world

How an NPC actor fights back, blocks and gets through doors; its wounds follow the [combat] injury and bleeding keys like a player’s.

Key Default
fight_back true a blow on an actor makes the attacker its opponent
swing_interval 1.8 seconds between its swings
max_attackers 0 0 = every actor in reach of a player swings at them; a number makes the actors on one player take turns - at most that many swing at the player within a swing_interval, the others stand at their reach and wait their turn, the longest waiting first (3 is close to how the game’s own crowds fight). The waiting actors stay in the fight
reach 2.2 metres it swings from; an unarmed actor closes to 0.7 of it
chase_speed 2.0 metres per second after its opponent
chase_range 30.0 an opponent farther than this is given up
attack_scale 1.0 a multiplier on the blow the victim’s game computed
max_damage 60.0 a blow does at most this
block_chance 0.5 how often an armed actor raises its guard against a swing; 0 never, 1 every time
block_hold 0.7 seconds the guard stays up
drop_weapons true a dying actor’s held weapon and shield fall as pickups everyone can take (its preset’s first melee weapon and first shield); false = it drops nothing
open_doors true a walking or chasing actor opens the closed doors in its way, for everyone; a locked door (SetDoorState) stops it there - a walk ends at the door (OnActorArrive), a chase waits at it. Needs the tables file ([combat] tables), which lists the level’s doors; a file exported by an older KcdMp.ExportTables does not know which way the doors face, so an actor also opens a door it only walks past - export it again. false = actors walk through closed doors
Key Default
x, y, z, yaw 1280.0, 1088.0, -1.0, 0.0 the default spawn (GetDefaultSpawn) of a level without its own row; z below 0 = on the terrain
klaster, trosecko, kutnohorsko kutnohorsko = [752.0, 3346.0, 142.3, 0.0] a level’s own spawn as [x, y, z, yaw], used over the four keys above when the server runs that level (--spawn on the command line beats both); Kuttenberg’s default is the inner yard of the Suchdol fortress, facing its gate
horse_soul Horse2’s GUID the breed of horses made without one (horses; a name or an id works too)
clothing_preset "" the outfit players spawn in (clothing presets); "" = they start naked - a game mode dresses them with SetPlayerInventory or GivePlayerItem. The bag is emptied (the purse the server holds for them stays) and the outfit put on at a player’s first spawn of a visit and whenever a spawn’s two presets differ from the spawn before (SetSpawnInfo); a respawn with the same presets keeps what they carry
weapon_preset "" their weapons (weapon presets), with the number of each item the preset gives; "" = none
items ["torch_weapon"] given at a player’s first spawn of a session, on top of the presets (items)
stat_level 0 strength, agility, vitality and speech raised to at least this at every spawn - the game announces every rise, and a level is never lowered; 0 = the level’s own 5, nothing announced. A game mode that keeps its players’ levels sets them exactly, lower too, with SetPlayerStat and leaves this at 0
appearances thirteen generic men the souls whose faces players get, one each by a hash of the name, unless the mode sets one (souls, SetSpawnInfo)