Skip to content

Server API (C#)

A plugin is a class library with one public class that implements IGameMode and has a parameterless constructor. The server loads it in its own load context - the plugin’s private dependencies stay private, KcdMp.Api resolves to the server’s copy - and calls it exactly as it calls a Lua mode: every method on the simulation thread, one tick at a time, so a plugin may keep plain fields and touch IServerApi freely from inside its methods, and must not from anywhere else. The one door for other threads is Post (below). The behaviour of each call is documented on the Lua pages; this page maps the C# names and types onto them, and the IDE shows each member’s summary (KcdMp.Api.xml ships next to the DLL).

The server folder (Setting up has it whole, and the first run) has two places for this:

KcdMp-server/
sdk/KcdMp.Api.dll what a plugin builds against (KcdMp.Api.xml next to it: the IDE shows every member's description)
gamemodes/arena/Arena.dll where a plugin lives: its own folder under gamemodes/, next to the Lua modes

The plugin is a .NET class library that references sdk/KcdMp.Api.dll - the server’s own copy is the one that runs, so the reference is not copied along, and the DLL to build against is the one of the server version you run (a member the running server does not have fails at the call). NuGet packages are added as usual; they travel with the plugin.

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Reference Include="KcdMp.Api">
<HintPath>../KcdMp-server/sdk/KcdMp.Api.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
</Project>

dotnet publish puts the plugin, its .deps.json and every package it uses in one folder - publish straight into the mode’s folder and name the DLL in server.toml:

Terminal window
dotnet publish -c Release -o ../KcdMp-server/gamemodes/arena
[gamemode]
script = "gamemodes/arena/Arena.dll" # or "gamemodes/arena/Arena.dll:MyModes.ArenaMode" when the assembly has several IGameMode types
Terminal window
KcdMp.Server --gamemode gamemodes/arena/Arena.dll

The same on Linux and Windows; the server folder is laid out identically on both.

/reload and ReloadGameMode reload a plugin like a script: the old load context is dropped, the file is read again, OnInit runs anew and every player is announced again. The server reads the plugin and its own packages into memory and keeps no file open, so dotnet publish can overwrite them while the server runs - publish, then /reload (or let [gamemode] watch see the new DLL and reload by itself). Because of that, Assembly.Location is empty inside a plugin: find the files you keep beside the DLL through api.GameModeFolder. Whatever the server itself has loaded - KcdMp.Api, the runtime, its own packages - is shared with the plugin; everything else the plugin brings is its own.

using KcdMp.Api;
public sealed class HelloMode : IGameMode
{
private IServerApi _api = null!;
private IHudText? _clock;
public string Name => "hello";
public void OnInit(IServerApi api)
{
_api = api;
_clock = api.CreateHudText(0.99f, 0.02f, "", 0xFFFF40FF, 1f, HudAlign.Right);
}
public void OnShutdown() { }
public void OnPlayerConnect(IPlayer player) => _api.Broadcast(0xFFD070FF, $"{player.Name} joined");
public SpawnPoint? OnPlayerRequestSpawn(IPlayer player)
{
var s = _api.DefaultSpawn;
return s with { X = s.X + 1.5f * (player.Id % 16) }; // null would hold the player until SpawnPlayer
}
public void OnPlayerSpawn(IPlayer player) => _clock?.Show(player);
public bool OnPlayerText(IPlayer player, string text) => true;
public bool OnPlayerCommand(IPlayer player, string command, string args)
{
if (command != "hello") return false;
_api.SendClientMessage(player, 0xFFD070FF, $"Hello, {player.Name}");
return true;
}
public void OnPlayerDisconnect(IPlayer player, string reason) { }
public void OnTick(float dtSeconds)
{
if (_clock is not null && _api.Tick % 60 == 0)
_clock.Text = TimeSpan.FromSeconds(_api.TimeOfDay).ToString(@"hh\:mm");
}
}

The Lua conventions hold: metres, degrees (0 = facing +Y, counter-clockwise), 0xRRGGBBAA colors, milliseconds. Where the Lua API takes a pid or an entity id, C# takes the IPlayer / IWorldEntity object; api.GetPlayer(id) and api.GetEntity(netId) translate, and both answer null once the player or entity is gone - keep the id, not the object, across ticks if in doubt.

The server owner connects a MySQL / MariaDB or PostgreSQL database in server.toml ([gamemode.database], the databases guide) and the plugin gets it as IDatabase? Database on IServerApi - null when the owner configured none, so say so and run without. The server’s own state (doors, chests, bans) is not in it. The handle:

Member What it does
Task<IReadOnlyList<IReadOnlyDictionary<string, object?>>> QueryAsync(sql, parameters) the rows, each a column → value map
Task<DbWriteResult> ExecuteAsync(sql, parameters) a write: Affected and, on MySQL, InsertId
Task<object?> ScalarAsync(sql, parameters) the first column of the first row
Task BatchAsync(IEnumerable<DbStatement>) several writes in one transaction, all or nothing
Query(...), Execute(...) the synchronous pair, inside OnInit only (a table to make, a rule set to load); they throw anywhere else
DbConnection CreateConnection() a fresh connection from the same pool for Dapper or a reader of your own
string Driver "mysql" or "postgres"

Parameters only: @name in the SQL, the values in the dictionary (string, long, double, bool, null, a DateTime as ISO text); never concatenate a value into the SQL. Integers come back as long, floating numbers as double, dates as ISO 8601 text, NULL as null. Every call is bounded by the owner’s timeout_seconds and max_rows; a failing query faults the task and the server goes on. The async members run on the thread pool - come back through Post (below) for anything that touches the API:

public void OnPlayerConnect(IPlayer player)
{
if (_api.Database is not { } db) return;
var id = player.Id;
var name = player.Name;
_ = Task.Run(async () =>
{
object? visits;
try { visits = await db.ScalarAsync("SELECT visits FROM players WHERE name = @name", new Dictionary<string, object?> { ["name"] = name }); }
catch (Exception ex) { _api.Post(() => _api.Log($"players: {ex.Message}")); return; }
_api.Post(() =>
{
if (_api.GetPlayer(id) is { } p && p.Name == name)
_api.SendClientMessage(p, 0xFFD070FF, visits is long v ? $"Welcome back, {name} - visit {v + 1}" : $"Welcome, {name}");
});
});
}

For a mode’s own accounts the API adds SetSecretCommand("login") (the clients mask the password as it is typed), HashPassword and VerifyPassword (PBKDF2, one text to store) - the shipped freeroam.lua shows the flow, the same calls exist here. Admins are the mode’s: SetPlayerAdmin in OnPlayerConnect, from whatever list or table you keep.

Slow work: your own driver and web requests

Section titled “Slow work: your own driver and web requests”

A plugin’s callbacks run inside the tick, so a query that takes 40 ms inside OnPlayerConnect stalls every player for 40 ms - and nothing in IServerApi may be touched from another thread. The one member made for other threads is void Post(Action action): the action runs on the simulation thread at the start of the next tick, before OnTick, in posting order, and inside it the whole API is legal again. So the slow part runs on a task of the plugin’s own and posts its result back. A plugin may also bring any .NET database driver or HTTP client of its own (a database the owner did not configure, a web API); MySQL through the MySqlConnector package, for example:

using KcdMp.Api;
using MySqlConnector; // dotnet add package MySqlConnector
public sealed class VisitsMode : IGameMode
{
private IServerApi _api = null!;
private MySqlDataSource _db = null!;
private readonly CancellationTokenSource _stopping = new();
public string Name => "visits";
public void OnInit(IServerApi api)
{
_api = api;
_db = new MySqlDataSource("Server=127.0.0.1;Database=kcdmp;User=kcdmp;Password=...");
}
public void OnShutdown()
{
_stopping.Cancel(); // a reload replaces the plugin: tasks still running must not post into the new one
_db.Dispose(); // ... and the pool must go, or the old copy lingers
}
public void OnPlayerConnect(IPlayer player)
{
var id = player.Id; // read what you need now; the objects stay on the simulation thread
var name = player.Name;
_ = Task.Run(async () =>
{
int visits;
try
{
await using var cmd = _db.CreateCommand("SELECT visits FROM players WHERE name = @name");
cmd.Parameters.AddWithValue("@name", name);
visits = Convert.ToInt32(await cmd.ExecuteScalarAsync(_stopping.Token) ?? 0);
}
catch (Exception ex) when (ex is MySqlException or OperationCanceledException)
{
if (!_stopping.IsCancellationRequested) _api.Post(() => _api.Log($"players table: {ex.Message}")); // the log too
return;
}
if (_stopping.IsCancellationRequested) return;
_api.Post(() =>
{
if (_api.GetPlayer(id) is { } p && p.Name == name) // ids are reused: the slot may hold someone else by now
_api.SendClientMessage(p, 0xFFD070FF, $"Welcome back, {name} - visit {visits + 1}");
});
});
}
public void OnPlayerDisconnect(IPlayer player, string reason)
{
var name = player.Name;
_ = Task.Run(async () => // a write nobody waits for needs no Post
{
try
{
await using var cmd = _db.CreateCommand(
"INSERT INTO players (name, visits) VALUES (@name, 1) ON DUPLICATE KEY UPDATE visits = visits + 1");
cmd.Parameters.AddWithValue("@name", name);
await cmd.ExecuteNonQueryAsync(_stopping.Token);
}
catch (Exception ex) when (ex is MySqlException or OperationCanceledException)
{
if (!_stopping.IsCancellationRequested) _api.Post(() => _api.Log($"players table: {ex.Message}"));
}
});
}
public SpawnPoint? OnPlayerRequestSpawn(IPlayer player) => _api.DefaultSpawn;
public void OnPlayerSpawn(IPlayer player) { }
public bool OnPlayerText(IPlayer player, string text) => true;
public void OnTick(float dtSeconds) { }
}

The rules in one place:

  • Read the IPlayer / IWorldEntity values you need (Id, Name, Position …) before the task starts; the objects and every IServerApi member other than Post stay on the simulation thread - Log included.
  • Come back through Post with the id, and check the player is still the one you meant: GetPlayer(id) answers null once they left, and a freed slot is given to the next player.
  • Catch inside the task. An exception a task swallows is lost silently; an exception inside a posted action is the mode’s, like one in any callback.
  • Post from the simulation thread itself is allowed too: the action simply runs on the next tick. Actions posted from one thread run in the order they were posted; an action that posts another gets it a tick later.
  • A reload drops what the old plugin still had queued and calls its OnShutdown: cancel your tasks there, as above, so a late answer does not run under the new mode.

The server log tells you when your code is the slow part. Every 10 seconds its tick time: line names the costliest jobs of the tick’s world step, and your OnTick and posted actions appear there as the game mode's OnTick and the game mode's posted work (the world step's top: the game mode's OnTick 6.20 ms a tick (at most 85.0), ...). The warning: the slowest tick took ... line names the same jobs for the slowest tick. A callback that answers a player’s message counts towards that message in the events' top: part.

Every member has a default that does nothing (or lets the action through), so a mode implements the ones it needs. Name, OnInit, OnShutdown, OnPlayerConnect, OnPlayerRequestSpawn, OnPlayerSpawn, OnPlayerText, OnPlayerDisconnect and OnTick are abstract - a mode must have them.

C# Lua page
string Name { get; } SetGameModeText - the mode’s name
void OnInit(IServerApi api) OnGameModeInit - keep api
void OnShutdown() OnGameModeExit
void OnTick(float dtSeconds) OnTick
void OnPlayerConnect(IPlayer) OnPlayerConnect
SpawnPoint? OnPlayerRequestSpawn(IPlayer) OnPlayerRequestSpawn - return the point, or null to hold the player for SpawnPlayer
void OnPlayerSpawn(IPlayer) OnPlayerSpawn
bool OnPlayerText(IPlayer, string text) OnPlayerText - false swallows the line; the text is cleaned first and a flood of lines never arrives
ChatResult OnPlayerChat(IPlayer, string text, string channel) OnPlayerText with the channel the line was typed in ("" the default one): return ChatResult.Allow, ChatResult.Suppress, ChatResult.Rewrite(text) or ChatResult.Rewrite(ChatMessage); its default asks OnPlayerText
void OnPlayerKeyBinding(IPlayer, string name, bool pressed) OnPlayerKeyBinding - a key the mode declared with RegisterKeyBinding went down (pressed) or up
bool OnPlayerCommand(IPlayer, string command, string args) OnPlayerCommandText - true = handled
void OnPlayerDisconnect(IPlayer, string reason) OnPlayerDisconnect
void OnPlayerMount(IPlayer, IWorldEntity horse), OnPlayerDismount OnPlayerMount, OnPlayerDismount
void OnPlayerStanceChange(IPlayer, PlayerStance stance, PlayerStance previous) OnPlayerStanceChange - the player stood up, crouched, mounted or sat in a cart (PlayerStance is Standing, Crouching, Mounted or Seated)
void OnPlayerVisorChange(IPlayer, bool down) OnPlayerVisorChange - the player’s helmet visor went down (down) or up; a visor already down at the spawn calls it once
void OnPlayerSeatChange(IPlayer, PlayerSeat? seat, PlayerSeat? previous) OnPlayerSeatChange - the player sat down on a bench, a chair or a bed of the level, sat, stood up, lay down, lay or got up (PlayerSeat is Kind - SeatKind.Table, Bench, Bed, Ground, OnTable, Shelter, Tent, Bath or Other - and State - SeatState.SittingDown, Sitting, StandingUp, LyingDown, Lying or GettingUp; IsSitting, IsLeaving); null = off the seat. A cart’s seat is OnPlayerStanceChange’s PlayerStance.Seated
void OnPlayerGrimeChange(IPlayer) OnPlayerGrimeChange - the player’s dirt or blood changed (IPlayer.Grime holds it); twice a second at most
void OnPlayerWhistle(IPlayer) OnPlayerWhistle - the player whistled for their horse with the game’s own whistle; bring it with MoveActor on the horse
void OnPlayerScreenChange(IPlayer, GameScreen screen, GameScreen previous) OnPlayerScreenChange - the player opened one of the game’s player screens, switched to another or closed them (GameScreen.None)
void OnPlayerSleep(IPlayer, bool lying) OnPlayerSleep - the player lay down in a bed (lying) or got up again, the game’s wheel or not
void OnPlayerThrown(IPlayer, IWorldEntity horse, int cause, bool flung) OnPlayerThrown - cause 1 a head hit, 2 the horse, 3 other, 4 the mode’s order (THROW_* in Lua); comes before the OnPlayerDismount of the same fall
double OnPlayerPullDown(IPlayer puller, IPlayer victim, IWorldEntity horse) OnPlayerPullDown - the chance the pull works (1.0 by default, 0 plays the failed pair), a negative number refuses it
void OnPlayerPulledDown(IPlayer puller, IPlayer victim, IWorldEntity horse, bool success) OnPlayerPulledDown - the rider was thrown off (success, before the OnPlayerDismount of it) or the pull failed
bool OnPlayerCarry(IPlayer carrier, CarryParty target, IPlayer? victim) OnPlayerCarry - false refuses a player’s grab of a body that lies down (while SetCarrying is on); target.Kind is Player, Corpse or Actor, victim the player a corpse was
void OnPlayerCarryStart(IPlayer carrier, CarryParty target), void OnPlayerCarryEnd(IPlayer carrier, CarryParty target, string reason, Vector3 at) OnPlayerCarryStart, OnPlayerCarryEnd - the grab begins; the carry is over (reason: "put down", "mode", "hit", "failed", "timeout", "carrier died", "target died", "carrier left", "target left", "moved", "respawned", "mounted", "cart", "flying", "destroyed"), the body lies at at
bool OnPlayerPickUpCarryItem(IPlayer, IWorldEntity item), void OnPlayerPutDownCarryItem(IPlayer, IWorldEntity item, string reason, Vector3 at) OnPlayerPickUpCarryItem - false refuses a player’s pickup of a carry item with the prompt (while SetItemCarrying is on); OnPlayerPutDownCarryItem - the item left his hands and lies at at (reason as the Lua page says)
bool OnPlayerUseCarryItem(IPlayer, IWorldEntity item, bool pressed) OnPlayerUseCarryItem - the left mouse button pressed or let go on a carry item SetCarryItemUse made usable; false on a tankard’s press skips its sip
void OnPlayerKnockedOut(IPlayer), void OnPlayerWake(IPlayer, string reason) OnPlayerKnockedOut, OnPlayerWake - reason "woke" (the timer), "mode", "died", "left", "respawned"
bool OnPlayerPickpocketStart(IPlayer thief, IPlayer? victim, IWorldEntity? actor) OnPlayerPickpocketStart - false refuses a player’s reach for another’s pockets (or an NPC actor’s) with the game’s “Rob” (while SetPickpocketing is on); exactly one of victim and actor is not null
bool OnPlayerPickpocket(IPlayer thief, IPlayer? victim, IWorldEntity? actor, string itemClass, int amount) OnPlayerPickpocket - false leaves that stack where it is (the purse is the Groschen’s class, the amount in tenths)
void OnPlayerPickpocketEnd(IPlayer thief, IPlayer? victim, IWorldEntity? actor, string outcome, string reason, int takenCount) OnPlayerPickpocketEnd - outcome "took", "nothing", "caught" (nothing moves; the consequence is the mode’s) or "cancelled"; reason says why
bool OnPlayerStealHorse(IPlayer, IWorldEntity horse) OnPlayerStealHorse - false puts the thief off it
void OnHorseItemsChange(IWorldEntity horse) OnHorseItemsChange - the saddlebags or the gear changed
float OnHorseHit(IWorldEntity horse, IPlayer attacker, in HitInfo hit) OnHorseDamage - return the damage, 0 cancels
void OnHorseDeath(IWorldEntity horse, IPlayer? attacker) OnHorseDeath - attacker null for the mode’s SetHorseHealth(0) or the horse’s own game
int OnPlayerEnterCart(IPlayer, IWorldEntity cart, int seat) OnPlayerEnterCart - the seat they get: seat lets them in, another free one seats them there, -1 keeps them out
void OnPlayerExitCart(IPlayer, IWorldEntity cart, int seat) OnPlayerExitCart
void OnPlayerChangeCartSeat(IPlayer, IWorldEntity cart, int from, int to) OnPlayerChangeCartSeat
void OnCartStop(IWorldEntity cart, string route, string stop) OnCartStop
bool OnSiegeEngineAction(IPlayer, IWorldEntity engine, string action) OnSiegeEngineAction - false refuses "operate", "load" or "fire" (a ladder’s "push" or "raise", a pile’s "lift" or "throw")
void OnSiegeShot(IWorldEntity engine, IPlayer? shooter, int shot) OnSiegeShot - shooter null = the mode fired it
void OnSiegeImpact(IWorldEntity engine, IPlayer? shooter, float x, float y, float z, int shot, string what, IPlayer? player, IWorldEntity? entity, string? gate) OnSiegeImpact - after its damage went out; what = "player" (player), "actor" / "engine" (entity), "gate" (gate = its key), "wall" or "ground"
float OnPlayerSiegeHit(IPlayer victim, IWorldEntity engine, IPlayer? shooter, float damage) OnPlayerSiegeHit - the damage to apply; 0 spares them
float OnSiegeEngineHit(IWorldEntity target, IWorldEntity engine, IPlayer? shooter, float damage) OnSiegeEngineHit - the structure to take off another engine; 0 spares it
void OnSiegeEngineDestroyed(IWorldEntity engine, IPlayer? by) OnSiegeEngineDestroyed - by null = the mode
void OnPlayerInventoryChange(IPlayer) OnPlayerInventoryChange - what the server’s records hold of their bag or purse changed (the mode’s own gives and takes on their way count; at most once a second a player)
bool OnPlayerLootBody(IPlayer, IPlayer? victim, out string? message) OnPlayerLootBody - false refuses the search; victim null = a corpse whose player left; a refusal says nothing unless message holds a text, which the searcher’s chat shows. Its default calls the two-argument OnPlayerLootBody(IPlayer, IPlayer? victim) (a refusal without a word)
bool OnPlayerLootActor(IPlayer, IWorldEntity actor, out string? message) OnPlayerLootActor - false refuses the search of a dead actor’s or animal’s body that holds what the mode put on it (SetActorLoot); a refusal says nothing unless message holds a text. Its default calls the two-argument OnPlayerLootActor(IPlayer, IWorldEntity)
void OnPlayerCorpse(IPlayer, int corpse) OnPlayerCorpse - the body stays behind: fill it here
bool OnPlayerPickup(IPlayer, IWorldEntity item) OnPlayerPickup - false refuses
void OnPlayerDrop(IPlayer, IWorldEntity item) OnPlayerDrop
bool OnPlayerUseItem(IPlayer, string itemClass, float health, string buffGuid) OnPlayerUseItem - false cancels (a checked item’s use itself)
bool OnPlayerUseItem(IPlayer, string itemClass, float health, string buffGuid, string itemId) the same with a tagged item’s id ("" for an untagged one); the server calls this one, which asks the four-argument one by default
bool OnPlayerCraft(IPlayer, string station, string itemClass) OnPlayerCraft - false keeps a checked station from starting
int? OnPlayerItemWear(IPlayer, string itemClass, int before, int after) OnPlayerItemWear - null lets it be, before keeps it, another number sets it
void OnPlayerDrunk(IPlayer, bool drunk) OnPlayerDrunk
float OnPlayerDamage(IPlayer victim, IPlayer? attacker, float damage, int zone, int bodyPart) OnPlayerDamage - return the damage to apply, 0 cancels
float OnPlayerHit(IPlayer victim, IPlayer? attacker, in HitInfo hit) the same hit with everything the model resolved (HitInfo below); the default hands it to OnPlayerDamage
bool OnPlayerBleed(IPlayer victim, IPlayer? attacker, in HitInfo hit) the hit is landing (the hit callback let it through, hit.Damage is what it returned): return whether it opens a bleeding wound; the default is hit.Bleeds, the server’s own answer (SetBleedRules) - Lua’s second return value of OnPlayerDamage
void OnPlayerInjury(IPlayer victim, IPlayer? attacker, BodyPart part) OnPlayerInjury
void OnPlayerDeath(IPlayer victim, IPlayer? attacker) OnPlayerDeath
void OnFightStart(IPlayer a, IPlayer b, string reason), OnFightEnd OnFightStart, OnFightEnd
void OnActorArrive(IWorldEntity actor) OnActorArrive
void OnActorMount(IWorldEntity actor, IWorldEntity horse), OnActorDismount OnActorMount, OnActorDismount
void OnActorVoiceEnd(IWorldEntity actor, string line) OnActorVoiceEnd
void OnActorConversationEnd(IWorldEntity first, IWorldEntity second, string conversation) OnActorConversationEnd
void OnActorActivity(IWorldEntity actor, string activity, string state) OnActorActivity - "start", "end", "cancel"
float OnActorHit(IWorldEntity actor, IPlayer attacker, in HitInfo hit) OnActorDamage - return the damage, 0 cancels
bool OnActorBleed(IWorldEntity actor, IPlayer? attacker, in HitInfo hit) OnPlayerBleed for an actor or an animal (after OnActorHit / OnActorAttackActor); attacker is null for an actor’s blow
void OnActorDeath(IWorldEntity actor, IPlayer? attacker) OnActorDeath
void OnActorInjury(IWorldEntity actor, IPlayer? attacker, BodyPart part) OnActorInjury
float OnActorAttack(IWorldEntity actor, IPlayer victim, in HitInfo hit) OnActorAttack - the default hands it to OnPlayerHit without an attacker
float OnActorAttackActor(IWorldEntity attacker, IWorldEntity victim, in HitInfo hit) OnActorAttackActor - return the damage, 0 cancels
void OnActorBlocked(IWorldEntity actor, IPlayer victim, bool perfect) OnActorBlocked - the actor’s swing met the player’s block (perfect: a perfect block or a master strike); the actor’s stagger follows
void OnPlayerBlockedByActor(IPlayer attacker, IWorldEntity actor) OnPlayerBlockedByActor - the player’s swing met the actor’s raised guard
bool OnActorInteract(IWorldEntity actor, IPlayer player) OnPlayerInteractActor - the use key on an actor that has a prompt (a shop’s “Trade” counts); false stops the actor’s shop from opening, and on an actor with a prompt of the mode’s own this is all that happens
void OnPlayerInteractPlayer(IPlayer player, IPlayer target) OnPlayerInteractPlayer - the use key on another player whose prompt the player sees (SetPlayerPrompt, SetPlayerPromptFor)
bool OnPlayerOpenShop(IPlayer, IShop shop, IWorldEntity? vendor) OnPlayerOpenShop - the shop screen is about to open; false keeps it closed (say why yourself); vendor is null for a shop opened without one
bool OnPlayerBuy(IPlayer, IShop shop, string itemClass, int amount, float price), bool OnPlayerSell(...) OnPlayerBuy, OnPlayerSell - one line of a deal about to go through, price in Groschen a piece; false refuses it
bool OnPlayerShopDeal(IPlayer, IShop shop, ShopDeal deal) OnPlayerShopDeal - the whole deal, asked last, after every line passed; false calls all of it off
void OnPlayerCloseShop(IPlayer, IShop shop, string reason) OnPlayerCloseShop - the screen closed; reason is "" for the player’s own close
void OnDialogueResponse(IPlayer, int dialogId, int option, string reason) OnDialogueResponse - the choice picked (1 = the top one), or 0 when the page closed without a pick (reason says why); a pick closes the page unless this shows the next
void OnPlayerEnterZone(IPlayer, IZone), OnPlayerLeaveZone OnPlayerEnterZone, OnPlayerLeaveZone
void OnProjectileHitZone(IPlayer, IZone, string weapon, string ammo, float x, float y, float z), void OnMeleeHitZone(IPlayer, IZone, string weapon, float x, float y, float z) OnProjectileHitZone, OnMeleeHitZone
void OnPlayerHitScenery(IPlayer, SceneryHit hit) OnPlayerHitScenery - a melee blow struck the level (a tree, a rock, a wall): SceneryHit has the kind, the point, the surface, the weapon and, for scenery, the Mesh and its box
void OnClientEvent(IPlayer, string name, string payload, string source) OnClientEvent - source is "lua" for a client script’s event and the frame’s name for a web page’s; the three-argument OnClientEvent(IPlayer, string name, string payload) is what its default calls
void OnPlayerWebStatus(IPlayer, string status, string reason), void OnPlayerWebReady(IPlayer, string frame), void OnPlayerWebError(IPlayer, string frame, string message, string source, int line) OnPlayerWebStatus, OnPlayerWebReady, OnPlayerWebError - the player’s web layer ("off", "starting", "ready", "failed"), a frame’s page loaded and listening, a script error of a page (with [client] ui_report_errors)
bool OnPlayerUseDoor(IPlayer, string key, bool open, bool locked, out string? message) OnPlayerUseDoor - false refuses; for a door checked first (SetDoorCheck) it is asked before the door moves and message is what the player’s chat says. Its default calls the four-argument OnPlayerUseDoor(IPlayer, string key, bool open, bool locked)
void OnPlayerInteractDoor(IPlayer, string key) OnPlayerInteractDoor - the mode’s own row on a door (SetDoorPrompt) was used; the door did not move
bool OnPlayerUnlockContainer(IPlayer, string key) OnPlayerUnlockContainer - their game unlocked a chest (its key, a pick); false locks it again
void OnPlayerInteract(IPlayer, IWorldEntity entity) OnPlayerInteract - the use key on an interaction point (CreateInteraction) or a prop with a prompt (SetEntityPrompt)
bool OnPlayerOpenContainer(IPlayer, string key, out string? message), void OnPlayerCloseContainer(IPlayer, string key) OnPlayerOpenContainer, OnPlayerCloseContainer - a refusal says nothing unless message holds a text, which the player’s chat shows. Its default calls the two-argument OnPlayerOpenContainer(IPlayer, string key) (a refusal without a word)
bool OnContainerRestock(string key) OnContainerRestock - false holds the chest for another period
bool OnPlayerTakeWorldItem(IPlayer, string key, string itemClass) OnPlayerTakeWorldItem - false takes the item back
void OnGateChange(string key, string cycle, IPlayer? by) OnGateChange - by is the admin whose /gate did it, null otherwise
bool OnPlayerAuditViolation(IPlayer, string itemClass, int amount, int allowed) OnPlayerAuditViolation - the item check found amount of a class where the records credit allowed; false vouches (the records take the excess, in observe too), true (the default) lets it be removed - never a kick. Asked after the 1.5 s settle, at most 8 a tick and 30 a minute per player; not for an item whose pickup or take the server itself refused
bool OnPlayerAuditViolation(IPlayer, string itemClass, int amount, int allowed, string origins) the same with origins, where the unexplained items came from ("native unmapped, unknown"); the server calls this one, and by default it answers what the four-argument one does
bool? OnPlayerNativeItem(IPlayer, string kind, string itemClass, int amount) OnPlayerNativeItem - the game made an item of its own; kind is "craft", "butcher", "herb", "loot", "dice", "tool", "eat", "wear", "smith", "script", "setitems", "unmapped" or "foreign_inv"; true adopts it, false has it removed, null (the default) leaves it to the server’s rule (ordinary items within a budget, guarded categories refused - smithing included)
bool OnPlayerCheatDetected(IPlayer, string kind, bool proven, string detail) OnPlayerCheatDetected - every detection; proven ones (a game that ignores an order, forges a claim, goes silent, writes a forged value back again and again) act by [audit] action - in observe too - unless this returns false; an advisory one is only counted and logged, what it means is the mode’s to decide. The default returns true
bool OnPlayerCheatDetected(IPlayer, string kind, bool proven, string detail, int count) the same with how many of the kind the player raised this visit (this one included); the rapid kinds reach the mode five times a visit, then once every 5 seconds per kind. The server calls this one, which asks the four-argument one by default
bool OnPartyInvite(IPlayer from, IPlayer target) OnPartyInvite - false refuses the invitation
void OnPartyInviteResponse(IPlayer from, IPlayer target, string answer) OnPartyInviteResponse
void OnPartyCreate(IParty, IPlayer leader), void OnPartyDisband(IParty, string reason) OnPartyCreate, OnPartyDisband
void OnPlayerJoinParty(IParty, IPlayer, string reason), OnPlayerLeaveParty OnPlayerJoinParty, OnPlayerLeaveParty
void OnPartyLeaderChange(IParty, IPlayer leader, IPlayer previous) OnPartyLeaderChange
void OnPlayerVoiceStart(IPlayer), void OnPlayerVoiceStop(IPlayer) OnPlayerVoiceStart, OnPlayerVoiceStop
bool OnPlayerEmote(IPlayer, string name) OnPlayerEmote - false refuses the emote
bool OnPlayerLookChange(IPlayer, Look look) OnPlayerLookChange - true takes the look; the default false takes none
void OnDiceMatchStart(IDiceMatch), void OnDiceMatchEnd(IDiceMatch, IPlayer? winner, string reason) OnDiceMatchStart, OnDiceMatchEnd - winner null when it was called off
float? OnPlayerGainXP(IPlayer, string kind, string name, float xp), bool WantsXpGains OnPlayerGainXP - the XP the player’s game awarded, already times the XP rate; return the XP to give (null gives xp, 0 or less refuses). The players’ games hold their awards for this word only while WantsXpGains is true (default false), so a mode that judges awards overrides both
void OnPlayerLevelUp(IPlayer, string kind, string name, int level) OnPlayerLevelUp - the player earned a level; kind is "stat" or "skill", and the main level comes as the stat "mainlevel"; not called for a level the mode set, nor for the main level’s rise that follows such a set
void OnPlayerProgressChange(IPlayer, bool levels) OnPlayerProgressChange - the player’s game reported a change: levels is true for a level, a perk or perk points (save now), false for XP and the two states
void OnPlayerOutfitChange(IPlayer) OnPlayerOutfitChange - the player’s three outfits changed (GetPlayerOutfits): the moment to save them
bool OnPlayerLearnRecipe(IPlayer, string recipe, string kind) OnPlayerLearnRecipe - the player’s game taught them a recipe (a document read): save it, or false takes it away again
bool OnPlayerProgressViolation(IPlayer, string kind, string name, float reported, float allowed) OnPlayerProgressViolation - the progression check found XP, a level, perk points or a perk it cannot explain (kind is "xp", "level", "points", "perk" or "baseline") and is about to put it back ([audit] mode = "enforce"; observe asks all the same for XP and levels and sends nothing); false vouches - the server takes the reported value (a teacher of the mode’s, quest XP on a story server), true (the default) lets it be put back. In this version "xp", "level" and "baseline" reach it in both modes, "points" under enforce, "perk" not yet; at most 8 calls in a burst and 30 a minute per player. For a perk below its level name is the perk’s GUID
void OnPlayerDiscoveryChange(IPlayer, DiscoveryKinds kind, string level) OnPlayerDiscoveryChange - the record of what the player discovered grew (a place found, a book studied further) or their game reported a kind for the first time; at most once a kind every five seconds, and once more just before OnPlayerDisconnect for a change still waiting; level is empty for the reading (it is global) - the moment to save
void OnPlayerDiscoveryRestore(IPlayer, DiscoveryKinds kind, bool ok) OnPlayerDiscoveryRestore - the player’s game answered a SetPlayerDiscovery or ForgetPlayerDiscovery that carried this kind: applied (ok) or given up; the record keeps what the mode set either way
void OnPlayerMapReady(IPlayer, MapReadyState state, string reason) OnPlayerMapReady - the player’s game drew the server’s map (Drawn, before their spawn) or could not (Failed, Unsupported: they play without it - kick them if your world needs the map)
bool OnMapEdit(IPlayer, MapEditInfo edit) OnMapEdit - a player’s edit in the map editor, about to be applied: false refuses it (tell them why); edit holds what it places, changes, deletes, removes and restores, the meshes and where. The mode’s own map functions and the admins’ /map never come here
void OnPlayerEnterArea(IPlayer, string area), void OnPlayerLeaveArea(IPlayer, string area) OnPlayerEnterArea, OnPlayerLeaveArea - one of the map’s areas, by name, checked ten times a second
void OnPlayerTrespassChange(IPlayer, PlayerTrespass now, TrespassLevel was) OnPlayerTrespassChange - the trespass level the player stands in changed (now.Level, now.Source - TrespassSource.Level, Area or Player -, now.Area): the game’s own icon shows it, the consequences are the mode’s
void OnPlayerUseMapObject(IPlayer, uint objectId) OnPlayerUseMapObject - the use key on a map object’s use prompt (MapObjectSpec.Use)

Grouped as the Lua index is; the Lua page has the behaviour.

Server, threads and timers - string Level, string LevelName (the level as a player reads it: Trosky, Kuttenberg, Sedletz Monastery; Levels.DisplayName(id) is the static helper), int MaxPlayers, long Tick, uint TimeMs, SpawnPoint DefaultSpawn, void Log(string), void ReloadGameMode(string reason = "requested"), string GameModeFolder (the full path of the plugin’s folder, for the files it keeps beside itself), void Post(Action) (the one member for other threads: the action runs on the next tick, above). There is no timer API: a plugin counts ticks in OnTick or keeps its own TimeMs deadlines (SetTimer is the Lua tier’s own).

Players - IReadOnlyList<IPlayer> Players, IPlayer? GetPlayer(int id), void SpawnPlayer(IPlayer, SpawnPoint at) (SetSpawnInfo + SpawnPlayer in one: SpawnPoint(X, Y, Z, YawDeg, ClothingPreset = "", WeaponPreset = "", Appearance = "")), void SetPlayerPos(IPlayer, float x, float y, float z, float? yawDeg = null), bool SetPlayerFlying(IPlayer, bool flying, float speed, out string reason) (SetPlayerFlying; speed 0 = 12 m/s), void SetPlayerChatVisible(IPlayer, bool) (SetPlayerChatVisible), void SetPlayerThirdPerson(IPlayer, ThirdPersonMode, bool inFights = true, bool whenAiming = true, bool crosshair = true) (SetPlayerThirdPerson; ThirdPersonMode is Off, Allow or Force; false for inFights / whenAiming puts the player in first person while they fight / while a bow, a crossbow or a gun is drawn; false for crosshair keeps the aiming dot off in third person), void Kick(IPlayer, string reason), void SetPlayerNameplate(IPlayer, string), void SetPlayerNameplateHidden(IPlayer, bool) (SetPlayerNameplateHidden: no label over the player on any other screen), void SetPlayerInvisible(IPlayer, bool), bool SetPlayerInvisibleFor(IPlayer target, IPlayer viewer, bool? invisible), bool IsPlayerVisibleTo(IPlayer target, IPlayer viewer) and IPlayer.IsInvisible (SetPlayerInvisible: nothing of the player on any other screen - body, gear, name plate, horse, voice; SetPlayerInvisibleFor: one viewer’s own rule, null = the player’s again), bool SetPlayerNameplateFor(IPlayer viewer, IPlayer target, string? text) and string? GetPlayerNameplateFor(IPlayer viewer, IPlayer target) (SetPlayerNameplateFor: one viewer’s own label for a player, null = everyone’s again; SetActorNameplateFor / GetActorNameplateFor the same for an actor), bool SetPlayerPrompt(IPlayer, string), string GetPlayerPrompt(IPlayer), bool SetPlayerPromptFor(IPlayer viewer, IPlayer target, string? text), string? GetPlayerPromptFor(IPlayer viewer, IPlayer target) (SetPlayerPrompt: “[E] Talk” over a player, the use key on them reaches OnPlayerInteractPlayer; bool SetPlayerPrompt(IPlayer, string, bool hidden), bool SetPlayerPromptFor(IPlayer, IPlayer, string?, bool hidden) and bool IsPlayerPromptHidden(IPlayer) arm the use key with nothing drawn over the head), void SetPlayerColor(IPlayer, uint), void SetPlayerTeam(IPlayer, int) (IPlayer.NoTeam), void TogglePlayerControllable(IPlayer, bool), bool SetPlayerCamera(IPlayer, float x, float y, float z, float lookX, float lookY, float lookZ, int glideMs = 0, float fov = 0), bool ResetPlayerCamera(IPlayer, int glideMs = 0), bool IsPlayerCameraSet(IPlayer) (SetPlayerCamera: the view at a point looking at another, a glide from where it is, a field of view), IPlayer? GetPlayerByUid(string uid) (GetPlayerUid), IReadOnlyList<IPlayer> GetPlayersByHwid(string hwid, int minMatches = 2) (GetPlayersByHwid: the players online on one PC; HardwareId.Matches(a, b) counts the parts two hwids share, HardwareId.Part(hwid, "disk") reads one - CompareHwid), bool SetTextOverride(string key, string? text, string language = ""), string? GetTextOverride(string key, string language = ""), int ClearTextOverrides(string? language = null), int LoadTextOverrides(string path, string language, out string error), bool SetPlayerTextOverride(IPlayer, string key, string? text), string? GetPlayerTextOverride(IPlayer, string key) and string GetPlayerLanguage(IPlayer) (SetTextOverride: the game’s own texts - the map’s names, the Codex - in the mode’s words), bool SetPlayerCodexEntry(IPlayer, string entry, bool unlocked, bool silent = false), bool HasPlayerCodexEntry(IPlayer, string entry), bool SetCodexEntryHidden(string entry, bool hidden = true), bool IsCodexEntryHidden(string entry), IReadOnlyList<string> GetHiddenCodexEntries() (SetPlayerCodexEntry), bool OpenPlayerScreen(IPlayer, GameScreen), bool ClosePlayerScreens(IPlayer), bool SetPlayerScreenBlocked(IPlayer, GameScreen, bool blocked = true), bool IsPlayerScreenBlocked(IPlayer, GameScreen), GameScreen GetPlayerScreen(IPlayer) (OpenPlayerScreen), bool SetPlayerSleepWheel(IPlayer, bool shown, bool getUp = true), bool EndPlayerSleep(IPlayer) (SetPlayerSleepWheel: a bed without the game’s sleep wheel - the mode’s own sleep), void SetPlayerVirtualWorld(IPlayer, int), void SetPlayerAdmin(IPlayer, bool) (admins are the mode’s: no list on the server), void SetSecretCommand(string name, bool secret = true), IReadOnlyCollection<string> SecretCommands, string HashPassword(string), bool VerifyPassword(string password, string hash), IDatabase? Database (above).

Chat, GameText and HUD - void SendClientMessage(IPlayer, uint color, string), void Broadcast(uint color, string) (SendClientMessageToAll), void GameText(IPlayer, string, int durationMs, GameTextStyle = Centre), void GameTextForAll(...), IHudText? CreateHudText(float x, float y, string text, uint color = white, float scale = 1, HudAlign = Left), void DestroyHudText(IHudText), IReadOnlyList<IHudText> HudTexts, IHudText? GetHudText(int id); a line over a player’s head for everyone near them, in a color of the mode’s own: void SetPlayerChatBubble(IPlayer, string text, uint color, float drawDistance, uint ms = 0) (SetPlayerChatBubble; ms 0 = by the length of the line; the speaker does not see their own bubble). A chat text over 16384 bytes of UTF-8 (SendClientMessage, Broadcast, SendPartyMessage) is cut at that size, never thrown on.

Chat channels, commands and keys (the Lua group, the keys) - styled lines: bool SendChatMessage(IPlayer to, ChatMessage, out string? reason) and bool SendChatMessageToAll(ChatMessage, out string? reason) (a ChatMessage holds Text or Spans, a Channel, a Color and, for the web chat, a Template of the web interface’s with its Params; at most 16 spans and 512 characters - refused, never cut), void ClearPlayerChat(IPlayer); channels: bool RegisterChatChannel(string name, ChatChannelOptions, out string? reason), bool UnregisterChatChannel(string name), void RefreshChatChannels(IPlayer? player = null) (a player types into a channel with the web chat’s tabs; the native chat writes a channel’s label in front of its lines); commands: bool RegisterCommand(string name, Action<IPlayer, string> handler, CommandOptions? options = null) (asked before OnPlayerCommand and the server’s own commands; /help lists it and the web chat suggests it to whoever may use it), bool UnregisterCommand(string name), bool AddChatSuggestion(string name, string help, string parameters, IPlayer? player = null), bool RemoveChatSuggestion(string name, IPlayer? player = null); keys of the mode’s own: bool RegisterKeyBinding(string name, string description, string defaultKey, out string? reason) - each player keeps their own key for it on your server, and a press comes back as OnPlayerKeyBinding; lines of the mode’s own in the player’s key hints, the game’s corner at the bottom right (AddPlayerActionHint): bool AddPlayerActionHint(IPlayer, string id, string key, string text, ActionHintOptions? options, out string? reason) (options.Binding shows the player’s own key for a key binding), bool RemovePlayerActionHint(IPlayer, string id), int ClearPlayerActionHints(IPlayer), IReadOnlyList<ActionHint> GetPlayerActionHints(IPlayer).

The game’s own toasts - void ShowInfoText(IPlayer, string, int durationMs = 3000, int priority = 0, bool background = false) (ShowInfoText), void ShowInfoTextForAll(...), void HideInfoText(IPlayer), void ShowNotification(IPlayer, string) / ShowNotificationForAll(string), void ShowGameLog(IPlayer, string, int type = 0, int level = 0) / ShowGameLogForAll(...), void ShowObjectiveEvent(IPlayer, string title, string text, int type = 0) / ShowObjectiveEventForAll(...) (0 new, 1 done, 2 failed), void ShowTutorial(IPlayer, string id, string text, int durationMs = 8000) / ShowTutorialForAll(...), void HideTutorial(IPlayer, string id).

Compass markers - IMarker? CreateMarker(float x, float y, float z, MarkerKind = Quest, uint color = 0, string text = "") (CreateMarker), void DestroyMarker(IMarker), IReadOnlyList<IMarker> Markers, IMarker? GetMarker(int id); IMarker: int Id, float X / Y / Z and SetPos(x, y, z), MarkerKind Kind, uint Color, string Text (settable - a change is re-sent to every viewer), IReadOnlyList<IPlayer> ShownTo, Show(IPlayer), Hide(IPlayer), ShowForAll(), HideForAll(), bool IsShownTo(IPlayer).

Script events and state bags - void SendClientEvent(IPlayer, string name, string payload), void SendClientEventToAll(...), and bool TrySendClientEvent(IPlayer, string name, string payload) / bool TrySendClientEventToAll(string name, string payload), which say whether the event went. The name is at most 64 bytes and the payload at most 60000 bytes of UTF-8 (the other way, OnClientEvent gets at most 4096 bytes): a bigger event is refused and logged, never cut - the Try methods return false, the void ones do nothing and never throw for it - so split large data over several events (the Lua page); IReadOnlyDictionary<string, string> GlobalState, string? GetGlobalState(string), bool SetGlobalState(string, string?), GetPlayerState / SetPlayerState(IPlayer, ...), GetEntityState / SetEntityState(IWorldEntity, ...); the limits are IServerApi.StateValueMax (1024) and StateKeysMax (64).

Storage - IPlayer.Data and IWorldEntity.Data / IZone.Data (IDictionary<string, object?>, the private bags); Where the level’s objects stand (GetDoorPosition and the rest): Vector3? GetDoorPosition(string key), GetContainerPosition, GetWorldItemPosition; string? NearestDoor(Vector3 point, float radius, out float distance), NearestContainer, NearestWorldItem (radius 0 = no limit; null and -1 when none); the doors include the map editor’s ("KcdMp_map_" + id). The server remembers nothing about a player between visits: what should outlive a session goes into the mode’s database.

Vitals, buffs, drink - void SetPlayerHealth(IPlayer, float), void SetPlayerStamina(IPlayer, float), void HealPlayer(IPlayer), bool SetPlayerBleeding(IPlayer, float ratePerSecond, float seconds = 0) (SetPlayerBleeding), void SetBleedRules(BleedRules?) / BleedRules GetBleedRules() (SetBleedRules: BleedRules has float? PerDamage, MaxRate, Seconds, MinThrough, int? ArmouredCuts and bool? Ranged (false = shots never bleed), null = the server’s own), void SetPerkEffects(PerkEffects?) / PerkEffects GetPerkEffects() (SetPerkEffects: the game’s health perks the server plays - PerkEffects has bool? Revenant, float? RevenantRate, RevenantCap, RevenantIICap, RevenantPause, bool? Diehard, float? DiehardHeal, DiehardSeconds, DiehardCooldown, bool? GuardianAngel (the prayer’s save - off until set true), null = the game’s own numbers); bool GivePlayerBuff(IPlayer, string buff, float seconds = 0), bool RemovePlayerBuff, bool HasPlayerBuff, void ClearPlayerBuffs, string? ResolveBuff(string key), BuffInfo? GetBuffInfo(string key), IReadOnlyList<BuffInfo> FindBuffs(string pattern, int max = 10), IReadOnlyList<string> ClaimedBuffClasses, void ClaimBuffClasses(IEnumerable<string>); float GetPlayerAlcohol(IPlayer), bool IsPlayerDrunk(IPlayer), void SetPlayerAlcohol(IPlayer, float). The readers are properties of IPlayer (below).

Dirt and blood (the Lua group) - bool SetPlayerDirt(IPlayer, float value, string part = "all"), bool SetPlayerBlood(IPlayer, BloodZone? zone, float value, string part = "all") (null = every zone), bool WashPlayer(IPlayer, string part = "all") (the game’s own wash) - a part is "all", "skin", "clothes" or an item’s class GUID or name (SetPlayerDirt, SetPlayerBlood, WashPlayer); the reader is IPlayer.Grime.

Stats, skills, XP and perks (the Lua group) - a stat is strength, agility, vitality, speech, storyprogress (the story’s progress) or prestige (the Blacksmith Prestige), a skill one of the thirty of the list (warfare for fencing and the other common words work too), a level 0 .. 30; names are lower case and a stat call given a skill’s name - or the reverse - is refused. The main level is the stat mainlevel: the game works it out from the other levels, so it is read only - byte GetPlayerLevel(IPlayer) (the same figure as GetPlayerStat(player, "mainlevel"), 0 until the player’s game has reported), while SetPlayerStat and AddPlayerStatXP refuse the name. Levels: bool SetPlayerStat(IPlayer, string stat, int level, float xp = 0) and bool SetPlayerSkill(...) (SetPlayerStat: exact, they lower a level too and show no level-up notice, and the record holds the set at once; xp is the XP gathered toward the next level), byte GetPlayerStat(IPlayer, string) and byte GetPlayerSkill(...) (0 = the server does not know the level yet), (float Xp, float Next) GetPlayerStatXP(IPlayer, string) and GetPlayerSkillXP(...) (Next 0 at the top level), bool AddPlayerStatXP(IPlayer, string, float xp) and bool AddPlayerSkillXP(...) (XP the game’s own way, above 0 and at most 100000; a level-up notice and perk points follow). Perk points, the ones not spent yet: int GetPlayerPerkPoints(IPlayer, string name) (a stat’s or skill’s name, or "mainlevel" for the pool that belongs to no stat or skill; 0 = none or not known yet) and bool SetPlayerPerkPoints(IPlayer, string name, int points) (SetPlayerPerkPoints: exactly, 0 .. 999, the level left as it is). Perks: IReadOnlyList<string> GetPlayerPerks(IPlayer) (GUIDs), bool HasPlayerPerk(IPlayer, string perk), bool AddPlayerPerk(IPlayer, string perk), bool RemovePlayerPerk(IPlayer, string perk) (a GUID, or a name from the perks list when the server has the tables export), string? GetPerkName(string perk). The character’s states: (float Current, float Max)? GetPlayerNourishment(IPlayer), bool SetPlayerNourishment(IPlayer, float), GetPlayerEnergy / SetPlayerEnergy (null until the player’s game has reported). The whole character: ProgressSnapshot GetPlayerProgress(IPlayer) and bool SetPlayerProgress(IPlayer, ProgressSnapshot) - read it in OnPlayerProgressChange and OnPlayerDisconnect to save, set it at the login to restore, and save nothing until the restore has gone out (a player whose game has not reported yet reads as an empty snapshot). What the progression check holds of a player: PlayerWeight GetPlayerWeight(IPlayer) (GetPlayerWeight: the weight the server’s records say they carry, what they may carry and whether that is too much; Known is false while it cannot tell) and ProgressAuditInfo GetPlayerProgressAudit(IPlayer) (GetPlayerProgressAudit: the excesses, set-backs, vouches, rewrites, probes and stale reports of the session, and whether the player’s game says its XP counting is down or that this is its first session). The XP the game awards on its own is off until the mode turns it on: bool SetXPRate(float rate) (0 .. 100, 0 when the server starts, 1 the game’s own), float GetXPRate(), bool SetPlayerXPRate(IPlayer, float? rate) (null = follow the server-wide rate), float GetPlayerXPRate(IPlayer); a mode that judges each award overrides OnPlayerGainXP and WantsXpGains (above).

Recipes - the alchemy table’s and the anvil’s books (the recipes list; a recipe is its key, its name or the class of the document that teaches it): IReadOnlyList<string> GetRecipes(string? kind = null) (every key, "alchemy" or "smithing" for one kind), RecipeInfo? GetRecipeInfo(string recipe) (Key, Kind, Id, Name, Document, Perks, Products), IReadOnlyList<string> GetPlayerRecipes(IPlayer, string? kind = null), bool HasPlayerRecipe(IPlayer, string), bool GivePlayerRecipe(IPlayer, string) and bool RemovePlayerRecipe(IPlayer, string) (GivePlayerRecipe: silently, the book shows it next time), and bool SetPlayerRecipes(IPlayer, IEnumerable<string>) (SetPlayerRecipes: exactly these at a login - every other one taken away, also what the player’s game still holds from an earlier character). A recipe the player’s game teaches them (a recipe document read) comes to bool OnPlayerLearnRecipe(IPlayer, string recipe, string kind) - save it there, or return false to take it away again.

What a player discovered (the guide) - off until the mode turns it on: DiscoveryKinds DiscoveryTracking { get; set; } (SetDiscoveryTracking: the kinds the players’ games report, None when the server starts), bool SetPlayerDiscoveryTracking(IPlayer, DiscoveryKinds? kinds) (null = follow the server-wide set again), DiscoveryKinds GetPlayerDiscoveryTracking(IPlayer). The record: DiscoverySnapshot GetPlayerDiscovery(IPlayer, string? level = null) (GetPlayerDiscovery: a block per kind for the level, the reading block whatever the level, and the kinds the player’s game can do; it only ever grows), bool SetPlayerDiscovery(IPlayer, DiscoverySnapshot, bool replace = false) (SetPlayerDiscovery: the restore, united with what the record and the game hold unless replace; false when a block was skipped), bool ForgetPlayerDiscovery(IPlayer, DiscoveryKinds kinds = DiscoveryKinds.All) (false when a kind cannot be forgotten - the places, today - the others are forgotten all the same) and bool IsPlayerDiscoveryKnown(IPlayer, DiscoveryKinds kinds) (the record is the game’s own: save nothing before it, or before the restore went out). Save each block’s Format and Data as they are (the Lua tier hands the bytes out as base64 text). In this version the players’ games report and put back Places and Reading; Ui is not reported yet.

Combat - bool StartFight(IPlayer a, IPlayer b), void EndFight(IPlayer a, IPlayer b), bool AreFighting(IPlayer a, IPlayer b), IReadOnlyList<IPlayer> GetOpponents(IPlayer), string GetPlayerWeapon(IPlayer).

Items and catalogues - void GivePlayerItem(IPlayer, string itemClass, int amount) (a negative amount is an exact take) and void GivePlayerItem(IPlayer, string itemClass, int amount, int health) (made at that condition, 0..100), IReadOnlyList<ContainerItem> GetPlayerInventory(IPlayer) (what they wear marked Worn, each stack’s Health its condition; the money is not in it), void SetPlayerInventory(IPlayer, IEnumerable<ContainerItem>) (SetPlayerInventory; Worn puts it on, Health makes or sets the condition), void EmptyPlayerInventory(IPlayer, bool keepWorn = false), the game’s three outfits PlayerOutfits? GetPlayerOutfits(IPlayer) and bool SetPlayerOutfits(IPlayer, PlayerOutfits) (SetPlayerOutfits: record PlayerOutfits(int Current, IReadOnlyList<IReadOnlyList<string>> Outfits), Current 0..2 here - the Lua table’s current is 1..3 - set after SetPlayerInventory at a login), the items’ wear int? GetPlayerItemHealth(IPlayer, string itemClass), int SetPlayerItemHealth(IPlayer, string itemClass, int health), bool SetItemWear(int rate, string? kindOrClass = null), int GetItemWear(string? kindOrClass = null) (SetItemWear: a percent of the game’s own wear per class, kind or every item), bool SetPickupHealth(IWorldEntity pickup, int health), int? GetPickupHealth(IWorldEntity pickup) (the condition a pickup’s item is made at on every screen; a drop keeps the dropped item’s), bool SetPickupQuality(IWorldEntity pickup, int quality), int? GetPickupQuality(IWorldEntity pickup) (SetPickupQuality: its quality the same way, 1..4, 0 = the class’s own - a mode that keeps dropped items itself carries both), the uses decided first bool SetItemUseCheck(string classOrCategory, bool on), bool IsItemUseChecked(string itemClass) (SetItemUseCheck: a use of that class or category waits for OnPlayerUseItem, whose false refuses it), the crafting stations decided first bool SetCraftCheck(string station, bool on), bool IsCraftChecked(string station) (SetCraftCheck: “alchemy”, “smithing”, “sharpening” or “*” waits for OnPlayerCraft), the mode’s tagged items bool GivePlayerTaggedItem(IPlayer, string itemClass, ItemTag tag, int health = 100), bool SetPlayerItemTag(IPlayer, string id, string name, string text), ContainerItem? FindPlayerItem(IPlayer, string id) (GivePlayerTaggedItem: record ItemTag(string Id, string Name = "", string Text = ""), a ContainerItem’s Tag - GetPlayerInventory lists each tagged item with it, SetPlayerInventory keeps or makes it by its id), the purse in Groschen float GetPlayerMoney(IPlayer) and void GivePlayerMoney(IPlayer, float groschen) (GetPlayerMoney, GivePlayerMoney; a negative amount takes, never more than the purse holds). All of these work on the server’s item records, not on the game’s own list: what a mode gives or takes counts at once - a GetPlayerInventory right after a GivePlayerItem shows the item, a SetPlayerInventory called twice orders nothing twice, GivePlayerMoney(p, saved - GetPlayerMoney(p)) ends with the saved purse - while the player’s game carries the order out a moment later; under [audit] mode = "observe" the readers show what the player’s game holds (the item check). Corpses ([combat] corpse_seconds): IReadOnlyList<ContainerItem>? GetCorpseItems(int corpse), bool SetCorpseItems(int corpse, IEnumerable<ContainerItem>), bool RemoveCorpse(int corpse); string? ResolveItemClass(string key), ItemInfo? GetItemInfo(string key), IReadOnlyList<ItemInfo> FindItems(string pattern, int max = 10); the mode’s own items: bool DefineItem(string name, ItemDefinition definition, out string reason), bool RemoveItemDefinition(string name), ItemDefinition? GetItemDefinition(string name), IReadOnlyList<string> GetItemDefinitions() (DefineItem: an item of your own on one of the game’s items - its name, text, weight, price, condition, quality, look and whether its pieces stack - given, stored, sold and dropped by its name like any item; GetItemInfo answers it under its own name, weight and price); string? ResolveHorseSoul(string key), SoulInfo? GetSoulInfo(string key), IReadOnlyList<SoulInfo> FindSouls(string pattern, int max = 10); string? ResolveMesh(string key), IReadOnlyList<MeshInfo> FindMeshes(string pattern, int max = 10).

World entities - IReadOnlyList<IWorldEntity> Entities, IWorldEntity? GetEntity(int netId), IWorldEntity? CreateEntity(EntityKind kind, string template, float x, float y, float z, float yawDeg, IPlayer? controller = null, int virtualWorld = -1) (a horse: template = the soul key or ""; a pickup: the item key; EntityKind.Npc makes an NPC actor - template = its soul, the rest as CreateActor with no name and the server’s clothing and weapons; EntityKind.Animal an animal - template as CreateAnimal‘s kind or soul), bool DestroyEntity(IWorldEntity), void SetEntityPos(IWorldEntity, float x, float y, float z, float? yawDeg = null), bool SetEntityController(IWorldEntity, IPlayer?), void SetEntityVirtualWorld(IWorldEntity, int), bool MountPlayer(IPlayer, IWorldEntity horse), IWorldEntity? GetPlayerMount(IPlayer), a rider thrown off (ThrowPlayerFromHorse and the rest): bool ThrowPlayerFromHorse(IPlayer, bool flung, out string reason), void SetPlayerRiderHeadHit(IPlayer, bool), void SetPlayerRiderThrow(IPlayer, bool), pulling a rider off his horse (SetPlayerPullDown and the rest; off for everyone until a mode allows it): void SetPlayerPullDown(IPlayer, bool), bool PullPlayerFromHorse(IPlayer victim, IPlayer puller, bool success, out string reason), carrying bodies and the knock-out (SetCarrying and the rest; the players’ own grab is off until a mode switches it on): void SetCarrying(bool enabled, CarryRules? rules = null), (bool Enabled, CarryRules Rules) GetCarrying(), bool CarryPlayer(IPlayer carrier, IPlayer target, float speed, out string reason), bool CarryEntity(IPlayer carrier, IWorldEntity entity, float speed, out string reason), bool StopCarry(IPlayer who, float x = NaN, float y = NaN, float z = NaN, float yawDeg = NaN, bool immediate = false), CarryParty? GetPlayerCarrying(IPlayer), CarryParty? GetPlayerCarriedBy(IPlayer), CarryParty? GetEntityCarrier(IWorldEntity), void SetEntityCarryable(IWorldEntity, bool), bool ActorCarryEntity(IWorldEntity actor, IWorldEntity entity), bool ActorCarryPlayer(IWorldEntity actor, IPlayer), bool ActorPutDown(IWorldEntity actor, float x = NaN, ...), bool SetPlayerKnockedOut(IPlayer, bool on, float seconds, out string reason), bool SetActorKnockedOut(IWorldEntity, bool on, float seconds) (CarryRules is a record of Range, Speed, Corpses, Players, Actors; CarryParty names a player or an entity, its CarryKind (Player, Corpse, Actor), its Id and the KindName a Lua mode reads), carry items - a sack, a basket, a bucket carried in the hands (SetItemCarrying and the rest; the players’ own pickup is off until a mode switches it on): void SetItemCarrying(bool enabled, ItemCarryRules? rules = null), (bool Enabled, ItemCarryRules Rules) GetItemCarrying(), IWorldEntity? CreateCarryItem(string item, float x, float y, float z, float yawDeg = 0, int virtualWorld = -1), bool PickUpCarryItem(IPlayer, IWorldEntity item, bool atOnce, out string reason), IWorldEntity? GivePlayerCarryItem(IPlayer, string item, bool atOnce, out string reason), bool DropPlayerCarryItem(IPlayer, float x = NaN, float y = NaN, float z = NaN), bool TakePlayerCarryItem(IPlayer), IWorldEntity? GetPlayerCarryItem(IPlayer), IPlayer? GetCarryItemHolder(IWorldEntity), bool SetCarryItemDrink(IWorldEntity item, string itemClass, int sips), CarryItemDrink GetCarryItemDrink(IWorldEntity) (SetCarryItemDrink; CarryItemDrink is a record of ItemClass and Sips) (ItemCarryRules is a record of Range; a carry item is an entity of EntityKind.Carry), the left mouse button on a carried thing (SetCarryItemUse; off by default): bool SetCarryItemUse(IWorldEntity item, bool enabled, string label = ""), bool SetCarryItemUse(string itemClass, bool enabled, string label = ""), CarryItemUse GetCarryItemUse(IWorldEntity), CarryItemUse GetCarryItemUse(string itemClass) (CarryItemUse is a record of Enabled and Label), keeping a player from attacking and what a block costs an actor (SetPlayerCanAttack, SetActorBlockRules): bool SetPlayerCanAttack(IPlayer, bool canAttack), bool CanPlayerAttack(IPlayer), void SetActorBlockRules(ActorBlockRules?), ActorBlockRules GetActorBlockRules(), bool StaggerActor(IWorldEntity actor, float seconds) (ActorBlockRules is a record of PerfectStagger and BlockStagger, seconds; a null field keeps the server’s own), pickpocketing between players (SetPickpocketing and the rest; off until a mode switches it on): void SetPickpocketing(bool enabled, PocketRules? rules = null), (bool Enabled, PocketRules Rules) GetPickpocketing(), void SetPlayerPickpocketable(IPlayer, bool), bool IsPlayerPickpocketable(IPlayer), void SetPlayerCanPickpocket(IPlayer, bool), bool CanPlayerPickpocket(IPlayer), bool SetActorPocket(IWorldEntity actor, IEnumerable<ContainerItem>? items, float money = 0) (null items take the pockets away), (IReadOnlyList<ContainerItem> Items, float Money)? GetActorPocket(IWorldEntity), bool CancelPickpocket(IPlayer, string reason = ""), PocketTarget? GetPickpocketTarget(IPlayer) (PocketRules is a record of Range, Cooldown, MaxItems, MaxStack, BehindOnly, MinSeconds, Money, MoneyShare, MoneyCap, Actors, Deny; PocketTarget names a player (Player) or an entity (Entity), with an Id and a KindName of "player" or "actor"), a horse’s owner and what it carries (SetHorseOwner and the rest): bool SetHorseOwner(IWorldEntity, IPlayer?), IReadOnlyList<IWorldEntity> GetPlayerHorses(IPlayer), IReadOnlyList<ContainerItem>? GetHorseItems(IWorldEntity), bool SetHorseItems(IWorldEntity, IEnumerable<ContainerItem>), IReadOnlyList<string>? GetHorseGear(IWorldEntity), bool SetHorseGear(IWorldEntity, IEnumerable<string>), a horse’s stats (SetHorseStat: strength, agility, vitality or courage, 0 .. 30, exact): bool SetHorseStat(IWorldEntity, string stat, int level), byte GetHorseStat(IWorldEntity, string stat) (0 = never set, the breed’s own), IReadOnlyDictionary<string, byte> GetHorseStats(IWorldEntity), a horse made with its breed and coat on (CreateHorse): IWorldEntity? CreateHorse(float x, float y, float z, float yawDeg, IPlayer? controller = null, int virtualWorld = -1, string soul = "", string coat = "") (the same as CreateEntity(EntityKind.Horse, soul, ...); soul is a horse soul’s name, id or GUID and "" the server’s [spawn] horse_soul, coat one of GetHorseCoats() and "" a pick that differs from one new horse to the next; null for an unknown soul or coat, or a full world), a horse’s coat (SetHorseCoat: colour, mane and tail on every screen, at once, the gear kept): bool SetHorseCoat(IWorldEntity, string? coat) (any case; null or "" goes back to the pick; false for a non-horse or an unknown coat), string? GetHorseCoat(IWorldEntity) (the coat it shows now - the one a mode set, else its own pick; null for a non-horse), IReadOnlyList<string> GetHorseCoats() ("black", "bay_dun", "chestnut", "light_grey", "buckskin", "palomino", "seal_brown", "dark_grey", "red_roan", "brown"; the coats a cart’s horses wear too) - a coat goes with the horse, so a mode that keeps horses saves it and gives it back in CreateHorse, a horse’s own coat, mane and tail from the game’s horses over its coat (SetHorseLook, the names on the horse looks): bool SetHorseLook(IWorldEntity, HorseLook? look, out string reason) (record HorseLook(string Body = "", string Mane = "", string Tail = "") - "" = the coat’s own part, null or HorseLook.None takes the look off; ToString() / HorseLook.Parse are the one-line form a database keeps), HorseLook? GetHorseLook(IWorldEntity, bool shown = false) (the look set; shown: every part it shows), IReadOnlyList<HorseLookPart> GetHorseLookParts(string kind, string pattern = "") ("body", "mane" or "tail"; record HorseLookPart(Kind, Name, Family, DerivesFrom, Build)), and the horse made with it on: CreateHorse(x, y, z, yawDeg, controller, virtualWorld, soul, coat, HorseLook look), a horse’s health (GetHorseHealth: 0 .. 100, 100 when it is made; a player’s blow or arrow takes the model’s damage off it, OnHorseHit may change it, and at 0 it dies on every screen, its rider thrown off): float GetHorseHealth(IWorldEntity) (also .Health; -1 for a non-horse), bool SetHorseHealth(IWorldEntity, float health) (0 kills it; refused on a dead horse), bool SetHorseInvulnerable(IWorldEntity, bool) (.Invulnerable: a hit on it is refused before OnHorseHit), IWorldEntity? CreateProp(string mesh, float x, float y, float z, float yawDeg, float scale = 1, bool rigid = false, int virtualWorld = -1), IWorldEntity? CreateInteraction(float x, float y, float z, string label, float size = 0.6f, int virtualWorld = -1) (CreateInteraction: an invisible spot with a use prompt, EntityKind.Point), bool SetEntityPrompt(IWorldEntity, string? text), string GetEntityPrompt(IWorldEntity) (a prop’s or a point’s prompt; an actor’s is SetActorPrompt), bool SetPropCollision(IWorldEntity prop, bool solid) / bool IsPropSolid(IWorldEntity) (SetPropCollision: a static prop walked into - the default - or through, on every screen at once; a rigid prop is always solid), IWorldEntity? CreateDog(IPlayer who, string soul = "", float x = NaN, float y = NaN, float z = NaN, float yawDeg = NaN), IWorldEntity? GetPlayerDog(IPlayer), a dog’s coat (SetDogCoat: a player’s dog or a CreateAnimal("dog"), on every screen at once): bool SetDogCoat(IWorldEntity, string? coat) (any case; null or "" goes back to the pick that changes with each new dog), string? GetDogCoat(IWorldEntity) (the coat it shows now; null for anything that is no dog), IReadOnlyList<string> GetDogCoats().

Carts (the Lua group, the routes guide, personal carts) - IWorldEntity? CreateCart(string kind, float x, float y, float z, float yawDeg, int virtualWorld = -1, IReadOnlyDictionary<string, string>? parts = null), IReadOnlyList<CartRoute> CartRoutes, bool AddCartRoute(CartRoute) (a record CartRoute(string Name, string Level, IReadOnlyList<Vector3> Points) with Speed, Mode "loop" / "back", Carts, Kind, Driver, DriverSoul, Parts (slot → part) and Stops - record CartStop(int Point, string Name, float Wait), the point counted from 0), bool RemoveCartRoute(string name), bool SaveCartRoutes(), bool SetCartRoute(IWorldEntity cart, string? route, int startPoint = 0), string? GetCartRoute(IWorldEntity), bool SetCartSpeed(IWorldEntity, float metresPerSecond), bool HoldCart(IWorldEntity, float seconds), bool PutPlayerInCart(IPlayer, IWorldEntity cart, int seat = -1), bool RemovePlayerFromCart(IPlayer), IWorldEntity? GetPlayerCart(IPlayer), int GetPlayerCartSeat(IPlayer) (-1 = in none), IReadOnlyList<IPlayer> GetCartPassengers(IWorldEntity); the seats one by one: bool SetPlayerCartSeat(IPlayer, int seat), int GetCartSeatCount(IWorldEntity), IPlayer? GetCartSeatPlayer(IWorldEntity, int seat), bool SetCartSeatLocked(IWorldEntity, int seat, bool locked), bool IsCartSeatLocked(IWorldEntity, int seat); the parts: bool SetCartPart(IWorldEntity, string slot, string? part) (null = the default), string? GetCartPart(IWorldEntity, string slot), IReadOnlyDictionary<string, string> GetCartParts(IWorldEntity), IReadOnlyList<string> GetCartPartSlots(string kind = "wagon"), IReadOnlyList<string> GetCartPartNames(string slot, string kind = "wagon") (a horse slot’s "none" = no horse there); the mode’s own models and materials: int AddCartProp(IWorldEntity cart, string model, float x, float y, float z, float pitchDeg = 0, float rollDeg = 0, float yawDeg = 0, float scale = 1, string? material = null) (the id, 0 = refused), bool SetCartProp(IWorldEntity, int id, ...) (the same numbers), bool RemoveCartProp(IWorldEntity, int id), int ClearCartProps(IWorldEntity), IReadOnlyList<CartProp> GetCartProps(IWorldEntity) (a record CartProp(int Id, string Model, float X, float Y, float Z, float Pitch, float Roll, float Yaw, float Scale, string? Material)), bool SetCartPartMaterial(IWorldEntity, string slot, string? material), string? GetCartPartMaterial(IWorldEntity, string slot); the team: bool HitchHorse(IWorldEntity cart, IWorldEntity horse, int slot = 0), IWorldEntity? UnhitchHorse(IWorldEntity cart, int slot = 0), IWorldEntity? GetCartHorse(IWorldEntity cart, int slot), IWorldEntity? GetHorseCart(IWorldEntity horse), int GetHorseCartSlot(IWorldEntity horse), bool SetCartLoad(IWorldEntity, float kg), float GetCartLoad(IWorldEntity), bool SetCartLoadFactor(IWorldEntity, float), float GetCartLoadFactor(IWorldEntity), bool SetCartStaminaRate(IWorldEntity, float), float GetCartStaminaRate(IWorldEntity), bool SetCartStamina(IWorldEntity, float), float GetCartStamina(IWorldEntity), CartPull? GetCartPull(IWorldEntity) (a record CartPull(int Horses, float PullKg, float WeightKg, float Ratio, float MaxSpeed, float Stamina, bool Tired), Ratio infinite with no horse) and the callback void OnCartHorsesTired(IWorldEntity cart); horse types: bool SetHorseType(IWorldEntity horse, string? type), string? GetHorseType(IWorldEntity horse), bool SetHorseTypeRule(string type, float pullKg, float ride) (ride 0.65-1.15), HorseTypeRule? GetHorseTypeRule(string type) (a record HorseTypeRule(string Name, float PullKg, float Ride)), IReadOnlyList<string> GetHorseTypes(). The entity kind is EntityKind.Cart, its Template the cart’s kind - "wagon" or "cart" (the two-wheeler).

Siege engines (the Lua group) - IWorldEntity? CreateSiegeEngine(string kind, float x, float y, float z, float yawDeg, int virtualWorld = -1) ("trebuchet", "cannon", "ladder" - a siege ladder’s foot, yawDeg the way it leans onto the wall - or "stones" - a pile of stones on a wall walk, at the walk’s height; z below 0 = on the ground, which needs the level’s heightmap or navmesh), SiegeResult PushSiegeLadder(IWorldEntity) and SiegeResult RaiseSiegeLadder(IWorldEntity), IReadOnlyList<IWorldEntity> GetSiegeEngines(), SiegeEngineInfo? GetSiegeEngineInfo(IWorldEntity) (a record struct SiegeEngineInfo(string Kind, string Phase, IPlayer? Operator, float YawDeg, Vector3? Aim, float PitchDeg, float Speed, float Range, bool InReach, float Health, int Shots, int Fired, float? SecondsLeft)), IWorldEntity? GetPlayerSiegeEngine(IPlayer), bool SetSiegeEngineOperator(IWorldEntity, IPlayer?), bool AimSiegeEngine(IWorldEntity, float x, float y, float z), SiegeResult LoadSiegeEngine(IWorldEntity) and SiegeResult FireSiegeEngine(IWorldEntity) (a record struct SiegeResult(bool Accepted, string Reason)), bool SetSiegeEngineShots(IWorldEntity, int shots) (-1 = no limit), bool SetSiegeEngineHealth(IWorldEntity, float percent) (0 wrecks it, more repairs it, below 0 = it cannot be wrecked), bool SetSiegeEngineRules(IWorldEntity, SiegeRules) (a record SiegeRules with WindSeconds, LoadSeconds, FireSeconds, MinRange, MaxRange, TurnRate, Traverse, Damage, Radius, FullRadius, StructureDamage, OperateRange, a ladder’s FallSeconds and RaiseSeconds - null keeps the kind’s own); the entity kind is EntityKind.Siege, its Template the kind.

NPC actors - IWorldEntity? CreateActor(string soul, float x, float y, float z, float yawDeg, string name = "", string clothingPreset = "", string weaponPreset = "", int virtualWorld = 0), bool MoveActor(IWorldEntity, float x, float y, float z, float speed), bool StopActor(IWorldEntity), bool TurnActor(IWorldEntity, float yawDeg), bool SetActorAnim(IWorldEntity, string clip, bool loop = true), bool SetActorHealth(IWorldEntity, float), bool HealActor(IWorldEntity), bool SetActorBleeding(IWorldEntity, float ratePerSecond, float seconds = 0), bool SetActorHostile(IPlayer, IWorldEntity, bool), bool SetActorHostileToActor(IWorldEntity actor, IWorldEntity target, bool) (the one it fights is IWorldEntity.OpponentActor), bool SetActorNameplate(IWorldEntity, string), bool SetActorColor(IWorldEntity, uint) (read back as .Nameplate and .Color), bool SetActorInvulnerable(IWorldEntity, bool) (.Invulnerable: no blow hurts it, and a hit on it is refused before OnActorHit), bool SetActorInvisible(IWorldEntity, bool), bool SetActorInvisibleFor(IWorldEntity actor, IPlayer viewer, bool? invisible), bool IsActorVisibleTo(IWorldEntity, IPlayer viewer) and .IsInvisible (SetActorInvisible: an actor or animal off every player’s screen - its body, the horse it rides, its voice - and no player hits, uses, carries or robs it; SetActorInvisibleFor: one viewer’s own rule, null = the actor’s again), bool SetActorPrompt(IWorldEntity, string prompt) (.Prompt: the “Trade” or “Talk” under its name; the use key on it reaches OnActorInteract, and "" takes it off), bool SetActorTorch(IWorldEntity, bool lit) and bool IsActorTorchLit(IWorldEntity) (SetActorTorch: the game’s own burning torch in the actor’s free hand on every screen, at peace or in a fight, standing or walking; a death puts it out; false for anything but an NPC actor, and for lighting a dead one), bool SetActorTorch(IWorldEntity, bool lit, string light) and string GetActorLight(IWorldEntity) (GetActorLight: "torch", "lantern" or "lantern_fancy" - the lanterns players carry; "" for none); bool SetActorHandItem(IWorldEntity, string? item, bool leftHand, out string reason) and string? GetActorHandItem(IWorldEntity, bool leftHand) (SetActorHandItem: a tool in the actor’s hand on every screen for its animation - the game’s item or your own; null empties the hand; a death drops it); the wounds are IWorldEntity.Injuries and .Bleeding. bool SetActorRanged(IWorldEntity, bool enabled, float range = 30, float accuracy = 0.7f) (SetActorRanged: an actor whose gear holds a bow or a crossbow and its ammo shoots in its fights - on by default; false keeps it to its melee weapon). bool SetActorButcher(IWorldEntity, bool enabled) and bool IsActorButchered(IWorldEntity) (SetActorButcher: a dead animal’s carcass offers the game’s own “Butcher”, the butcher’s game butchers it; bool OnPlayerButcher(IPlayer, IWorldEntity animal, out string? message) may refuse - OnPlayerButcher). MoveActor, StopActor and TurnActor take a horse no player rides too (MoveActor: the server walks it, OnActorArrive gets the horse). Its stats, skills and items: bool SetActorStat(IWorldEntity, string stat, int level), int? GetActorStat(IWorldEntity, string stat), bool SetActorSkill(IWorldEntity, string skill, int level), int? GetActorSkill(IWorldEntity, string skill) (SetActorStat: the levels on its body on every screen, so the game prices its blows by them; null = never set), bool SetActorInventory(IWorldEntity, IEnumerable<ContainerItem> items), bool GiveActorItem(IWorldEntity, string itemClass, int amount = 1, bool wear = true), bool RemoveActorItem(IWorldEntity, string itemClass, int amount = 1), IReadOnlyList<ContainerItem>? GetActorInventory(IWorldEntity) (SetActorInventory: the Worn items dress and arm it in place of its presets, its armour counts against the blows on it, its weapon prices its own; null = never given items), bool SetActorLoot(IWorldEntity, IEnumerable<ContainerItem> items), IReadOnlyList<ContainerItem>? GetActorLoot(IWorldEntity) (SetActorLoot: what a dead actor’s or animal’s body gives the players who search it). How they fight is [actors] in server.toml - with max_attackers set, the actors on one player take turns instead of all swinging at once (server configuration). Animals (CreateAnimal) - IWorldEntity? CreateAnimal(string animal, float x, float y, float z, float yawDeg, string name = "", int virtualWorld = 0): the kind ("wolf", "red deer stag" …) or an animal’s soul; EntityKind.Animal, Template its soul. The actor calls above walk it, hurt it and kill it; it never poses or trades, and the nameplate, torch, knock-out, pocket and carrying calls refuse it. Its own will: bool SetAnimalHostile(IWorldEntity, IPlayer?), bool SetAnimalFlee(IWorldEntity, IPlayer?, float distance = 30), bool SetAnimalBehavior(IWorldEntity, string behavior, float radius = 15), string GetAnimalBehavior(IWorldEntity), bool SetAnimalAttack(IWorldEntity, float damage, float interval = 0), (string State, IPlayer? Player) GetAnimalState(IWorldEntity) (SetAnimalHostile: it runs after the player and bites - the bite through their armour and OnActorAttack; SetAnimalBehavior: "calm", "aggressive", "defensive", "skittish").

Actor life (the Lua pages) - on horseback: bool MountActor(IWorldEntity actor, IWorldEntity horse, bool instant = false), bool DismountActor(IWorldEntity, bool instant = false), IWorldEntity? GetActorHorse(IWorldEntity), bool IsActorMounted(IWorldEntity) (MoveActor then rides the horse); the game’s lines: float PlayActorVoice(IWorldEntity, string line, string voice = "", float range = 0), string PlayActorBark(IWorldEntity, string bark, float range = 0), float PlayActorConversation(IWorldEntity first, IWorldEntity second, string conversation, int sequence = -1, float range = 0), bool StopActorVoice(IWorldEntity), bool IsActorSpeaking(IWorldEntity), string GetActorVoice(IWorldEntity), bool SetActorVoice(IWorldEntity, string voice), string GetVoiceLineText(string line), IReadOnlyList<string> GetBarkLines(string bark, bool female = false); the level’s seats, beds and work spots and the pairs: bool SetActorUse(IWorldEntity, string objectKey), bool SetActorUseAt(IWorldEntity, string activity, float x, float y, float z, float yaw), bool StopActorUse(IWorldEntity, bool immediate = false), string GetActorUse(IWorldEntity), string GetActorUseObject(IWorldEntity), IReadOnlyList<SmartObjectInfo> GetSmartObjects(float x, float y, float z, float radius, string use = "", bool freeOnly = false), bool PlayActorPairAnim(IWorldEntity first, IWorldEntity second, string pair), IReadOnlyList<string> GetPairAnims().

Sound files (the Lua pages) - your own recordings from the mode’s sounds/ folder (or the one [client] sounds names) through the game’s sound, each file sent to a player only when a sound of it reaches them: uint PlayEntitySound(IWorldEntity, string file, float volume, float range, bool loop, bool lipSync, out string reason), uint PlaySoundAt(Vector3 position, int virtualWorld, string file, float volume, float range, bool loop, out string reason) (0 and the reason when it cannot play), bool StopSound(uint sound), int StopEntitySounds(IWorldEntity), bool IsSoundPlaying(uint sound), double GetSoundLength(string file, out string reason) (-1 when it cannot be played), bool PreloadSound(string file, IPlayer? player, out string reason) (the file sent ahead; null = every player in the game), and IGameMode.OnEntitySoundEnd(IWorldEntity? entity, uint sound, string file, string reason) (“finished”, “stopped”, “removed”).

Effects and sounds - int SpawnEffect(string effect, float x, float y, float z, float scale = 1, float dirX = 0, float dirY = 0, float dirZ = 1, int virtualWorld = 0), int SpawnDecal(string material, float x, float y, float z, float size = 1, float seconds = 0, float normalX = 0, float normalY = 0, float normalZ = 1, int virtualWorld = 0), int PlaySound(string trigger, float x, float y, float z, float seconds = 0, int virtualWorld = 0) - each returns how many players got it.

Zones - IZone? CreateZone(float x1, float y1, float z1, float x2, float y2, float z2), IZone? CreateCircleZone(float x, float y, float radius, float zMin = 1, float zMax = 0), IZone? CreateSphereZone(float x, float y, float z, float radius), void DestroyZone(IZone), IReadOnlyList<IZone> Zones, IZone? GetZone(int id); targets: bool SetZoneHittable(IZone, bool) (IZone.Hittable; SetZoneHittable: the shots and swings that reach it come to OnProjectileHitZone / OnMeleeHitZone), bool AttachZoneToEntity(IZone, IWorldEntity? entity, float offsetX = 0, float offsetY = 0, float offsetZ = 0) (IZone.AttachedTo; null detaches).

Parties (the parties guide) - bool InviteToParty(IPlayer from, IPlayer target, out string reason) (InviteToParty: the reason is the Lua tier’s second value), bool AcceptPartyInvite(IPlayer), bool DeclinePartyInvite(IPlayer), IPlayer? GetPartyInviter(IPlayer, out float secondsLeft) (GetPlayerPartyInvite), bool AddPlayerToParty(IPlayer host, IPlayer), bool RemovePlayerFromParty(IPlayer, string reason = "left"), bool SetPartyLeader(IParty, IPlayer), bool DisbandParty(IParty), IParty? GetPlayerParty(IPlayer), IParty? GetParty(int id), IReadOnlyList<IParty> Parties, int PartyMaxSize ([party] max_size; the Lua GetPartySize / IsPartyFull are IParty.Members.Count against it), bool ArePartyMembers(IPlayer a, IPlayer b), void SetPartyName(IParty, string), bool SetPartyMemberLabel(IPlayer, string), bool ShowPartyFrames(IPlayer, bool shown), bool SendPartyMessage(IParty, uint color, string).

Shops and vendors (the Lua group) - a shop is the server’s own object: a title, a stock, a purse, and whether it buys from the players. IShop CreateShop(string title, float sellFactor = 0, float money = -1) (sellFactor is the fraction of the catalogue price it pays for a player’s item, 0 = it buys nothing; money is its purse in Groschen, negative = bottomless), bool DestroyShop(IShop), IShop? GetShop(int id), IReadOnlyList<IShop> Shops; the stock and the shop: bool SetShopItem(IShop, string item, float price, int amount = -1) (the price in Groschen a piece, the stock -1 = unlimited, 0 = the row goes; false for an unknown item), bool RemoveShopItem(IShop, string item), void SetShopMoney(IShop, float), void SetShopTitle(IShop, string); what it buys: void SetShopBuyCategories(IShop, IEnumerable<string>? categories) (the catalogue’s categories, "-Document" = never that one, empty = everything the catalogue prices), bool SetShopBuyPrice(IShop, string item, float price) (0 = never buys that class, negative takes the rule off), float GetShopBuyPrice(IShop, string item); the screen: void SetShopScreen(IShop, ShopScreen) (ShopScreen.Trade, the game’s own trade screen when the shop is opened on a vendor, or Panel, the list panel). bool OpenShop(IPlayer, IShop, IWorldEntity? vendor = null) opens it on a player’s screen (with a vendor it closes when the player walks 4 m away or the vendor dies; without one it stays until CloseShop or the player’s own close), bool CloseShop(IPlayer); IPlayer.Shop is the one a player has open. A vendor is an NPC actor with a shop on it: bool SetActorShop(IWorldEntity actor, IShop? shop) (.Shop; the use key opens it, null takes it away) or, in one call, IWorldEntity? CreateVendor(IShop shop, string soul, float x, float y, float z, float yawDeg, string name = "", string clothingPreset = "", int virtualWorld = 0) (an unarmed actor nobody can hurt, with the shop on it). An IShop has Id, Title, Items (ShopItem(string ItemClass, float Price, int Amount)), SellFactor, Money, Customers, Screen, BuyCategories, BuyPrices and Data. Every purchase and sale passes through OnPlayerBuy / OnPlayerSell and the whole deal through OnPlayerShopDeal (ShopDeal(Lines, Cost, Proceeds), a line is ShopDealLine(ItemClass, Amount, Price, Buying) with ItemId for a tagged item and Health, the pieces’ condition 0-100 - 100 for a purchase, the average of the pieces taken for a sale); the items and the money move through the server’s own item records, so the item check explains them.

Dialogue (the Lua group) - bool ShowPlayerDialogue(IPlayer, int dialogId, string speaker, string text, IReadOnlyList<DialogueOption> options, IWorldEntity? actor, out string reason) shows a page on the player’s screen or replaces the one open: the speaker, their line and 1 to DialogueOption.MaxOptions (8) choices, DialogueOption(string Text, bool Enabled = true) (a disabled one shows greyed out and cannot be picked; the text is cut at DialogueOption.MaxTextLength, 200). The player’s movement is held while it is open; with an actor it closes when the player walks 5 m away or the actor dies or goes, and the speaker defaults to the actor’s name. The answer comes to OnDialogueResponse with the mode’s own dialogId. false with the reason when it cannot (not in the world, dead, no choices, too many …). bool HidePlayerDialogue(IPlayer) closes it without the callback, int? GetPlayerDialogue(IPlayer) is the id of the page open (null = none). The usual start is an actor with a Talk prompt and OnActorInteract.

The world - double TimeOfDay / void SetTimeOfDay(double seconds) (game seconds since midnight - the Lua GetWorldTime divides by 3600), float TimeRatio / SetTimeRatio, float Rain / SetRain, string Weather, bool SetWeather(string presetOrProfile, float blendSeconds = -1), IReadOnlyDictionary<string, string> WeatherPresets, bool SetWeatherChances(IReadOnlyDictionary<string, double>? chances, double minHours, double maxHours, out string reason) (the server rotation’s weight of each kind and how long a sky stays - -1 for the default hold; the Lua SetWeatherChances says the rules), IReadOnlyDictionary<string, double> WeatherChances, (double MinHours, double MaxHours) WeatherHold, float? TerrainHeight(float x, float y), bool StoryQuests (get / set: the game’s own story quests in the players’ games, off by default - the Lua SetStoryQuests says what turning them on means), void RevealPlayerMap(IPlayer) and void RevealPlayerMap(IPlayer, bool includePlaces) (RevealPlayerMap: the whole world map without the fog of war, for a server that keeps the fog ([ui] map_fog) and lifts it for someone; one way - a revealed map is not clouded again, and a player who comes back in a new game has the fog again, so call it again from OnPlayerSpawn. includePlaces also asks the player’s game for the level’s places, so the map shows their names; no player’s game does that part in this version, and the map is revealed without the names).

The weather’s chances in a C# mode: the server rolls one sky for every player from the mode’s weights - the kinds are the Lua ones (clear, fair, cloudy, overcast, showers, drizzle, storm, dry_storm, or a level profile from the Weather reference), the weights are relative, a kind left out never comes up. The hold is in game hours: -1 for minHours keeps the server’s 3 to 5, -1 for maxHours makes it minHours. null chances hand the rotation back to the server’s own mix.

public void OnInit(IServerApi api)
{
_api = api;
// a dry summer: mostly clear, a little rain, no storms; a sky stays 2 to 6 game hours
var chances = new Dictionary<string, double>
{
["clear"] = 40, ["fair"] = 35, ["cloudy"] = 15, ["showers"] = 10,
};
if (!api.SetWeatherChances(chances, 2, 6, out var reason))
api.Log($"weather: {reason}"); // an unknown kind, a weight below 0, every weight 0, a hold outside 0..168 hours
}
// e.g. in an admin command of the mode: the chances in force and the sky up now
private void ShowWeather(IPlayer admin)
{
var (minHours, maxHours) = _api.WeatherHold;
var mix = string.Join(", ", _api.WeatherChances.Select(c => $"{c.Key} {c.Value}"));
_api.SendClientMessage(admin, 0xFFD070FF, $"weather: {mix}; a sky stays {minHours}-{maxHours} game hours; now {_api.Weather}");
}

The voice chat (the Lua group) - float VoiceRange (get / set: how far a voice carries, the Lua SetVoiceRange), void SetPlayerVoiceRange(IPlayer, float metres), void SetPlayerVoiceMuted(IPlayer, bool), void SetPlayerVoiceDeaf(IPlayer, bool), float VoiceOcclusion (get / set: how much walls muffle a voice, 0 = not at all - SetVoiceOcclusion), float VoiceVisorMuffle (get / set: how much a closed helmet visor muffles its wearer’s voice, 0 = never, 1 = the default - SetVoiceVisorMuffle); a player’s own mutes - bool SetPlayerVoiceMutedFor(IPlayer listener, IPlayer talker, bool muted), bool IsPlayerVoiceMutedFor(IPlayer listener, IPlayer talker), IReadOnlyList<IPlayer> GetPlayerVoiceMutes(IPlayer listener) and the mode’s bool OnPlayerVoiceMute(IPlayer player, IPlayer target, bool muted) (false refuses a player’s /mute, /unmute or mute button - SetPlayerVoiceMutedFor); on IPlayer: bool IsTalking, bool IsVoiceMuted, bool IsVoiceDeaf, float VoiceRange, bool VoiceEnabled.

Emotes (the Lua group) - IReadOnlyList<string> Emotes (every name the server knows), IReadOnlyList<string> EmoteWheel (clockwise from the top), bool SetEmoteWheel(IEnumerable<string> names) (at most eight known names; empty takes the wheel away), bool AddEmote(string name, string label, string clip, string fragment, string tags = "", bool loop = false) and bool AddEmote(string name, string label, string clip, string fragment, string tags, bool loop, EmoteOptions options) (EmoteOptions: Upper - the upper body alone, walking along, looped with loop; Locked - only the mode ends it; InClip / InFragment / InTags played once before the loop, OutClip / OutFragment / OutTags once when it ends, Item - an item held while it plays, in the left hand with ItemLeftHand - AddEmote), bool PlayPlayerEmote(IPlayer, string name, out string reason) (the reason as the player reads it - the Lua tier’s second value; the heart of a mode’s own command, the shipped modes’ /anim), bool StopPlayerEmote(IPlayer); the one playing is IPlayer.Emote (null when none).

Dice (the Lua group) - IDiceMatch? StartDiceMatch(IPlayer challenger, IPlayer opponent, float bet, int gameType, out string reason, float turnTimeout = 90f, float range = 4f) (null with the reason as the player reads it; the rules are the mode’s - turnTimeout 5 to 3600 seconds for one move, range 0.5 to 8 metres between the two at the start), bool EndDiceMatch(IDiceMatch, IPlayer? winner) (null calls it off), IDiceMatch? GetPlayerDiceMatch(IPlayer), IDiceMatch? GetDiceMatch(int id). An IDiceMatch has Id, Challenger, Opponent, Bet, GameType, Goal, TurnTimeout, ChallengerScore, OpponentScore, Turn (the player to move) and Running.

Looks (the Lua group) - bool SetPlayerLook(IPlayer, Look, out string reason), bool SetActorLook(IWorldEntity actor, Look, out string reason) (the reason as a player reads it when a part will not do), IReadOnlyList<LookPart> GetLookParts(string kind, string pattern = "") ("head", "hair", "beard", "body"; a man’s parts) and GetLookParts(kind, pattern, "female") (a woman’s), LookPart? GetLookPart(string name); the look worn is IPlayer.Look / IWorldEntity.Look (Look.None = the soul’s own). A Look is a record of four part names and a Gender ("" a man’s body, Look.Female a woman’s: IsFemale); look.ToString() is the one line a database keeps and Look.Parse(text) reads it back - the same line as the Lua tier’s LookToString.

Interface (the Lua group, the guide) - who draws each piece of a player’s screen and how it is laid out: bool SetPlayerInterface(IPlayer, string component, string drawer, out string? reason) ("native" or "hidden", and on a server with a web interface "default" or a frame’s name; the "party" drawer is ShowPartyFrames), (string Requested, string Effective)? GetPlayerInterface(IPlayer, string component) (null for a player who is not connected or a component that does not exist), bool SetInterfaceForAll(string component, string drawer, out string? reason) (for everyone, and for whoever joins later), and the options of a component - "chat", "scoreboard", "party", "nameplates", "compass", "hud" - bool SetPlayerInterfaceOptions(IPlayer, string component, IReadOnlyDictionary<string, string> options, out string? reason) and bool SetInterfaceOptionsForAll(string component, IReadOnlyDictionary<string, string> options, out string? reason). The values are text ("12", "top-right", "true", "F6E890"), "default" takes a key back to its default, and a call with a bad word changes nothing and returns false with the reason as a person reads it ("lines must be a whole number from 4 to 20", "the shop cannot be hidden"). A reload of the mode puts everything back, so set the interface in OnInit (for everyone) and OnPlayerConnect (for one player). With them: bool SetScoreboardColumns(IReadOnlyList<ScoreboardColumnSpec>? columns, IPlayer? player, out string? reason) (columns of the mode’s own on the web scoreboard, read from the players’ state values; null takes them away) and bool SetPlayerHudVisible(IPlayer, string piece, bool visible, out string? reason) (one of the game’s own HUD pieces, named by the HudPieces constants).

Web interface (the Lua group, the guide) - the player’s web layer and frames: bool IsPlayerWebReady(IPlayer), bool IsPlayerWebFrameReady(IPlayer, string frame), bool ShowPlayerWebFrame(IPlayer, string frame, out string? reason), bool HidePlayerWebFrame(IPlayer, string frame, out string? reason), bool DestroyPlayerWebFrame(IPlayer, string frame, out string? reason) (the page closed and its memory freed; a show makes it anew), bool SetPlayerWebFocus(IPlayer, string frame, bool keyboard, bool cursor, bool keepInput, out string? reason), bool SetPlayerCursor(IPlayer, bool shown, out string? reason) (the player’s own cursor, as their cursor key switches it; no web interface needed), bool SendPlayerWebMessage(IPlayer, string frame, object? value, out string? reason) (written as JSON by System.Text.Json: a string as a JSON string, a JsonNode or JsonElement as it is, anything else serialised; at most 60000 bytes), bool RegisterUiFallback(string frame, string luaEventPrefix, out string? reason); the loading screen: bool SetPlayerLoadingHandover(IPlayer, object? value, out string? reason) (the same JSON, at most 16384 bytes) and bool ClosePlayerLoadingScreen(IPlayer, out string? reason). A page’s emitServer arrives in OnClientEvent with the frame’s name as source, and SendClientEvent reaches every page whose events_in takes the name.

Web surfaces (the Lua group) - pages of the web interface drawn on boards in the world (ui.toml [surfaces.<name>]): IWorldEntity? CreateWebSurface(string name, float x, float y, float z, float yawDeg, float width, float height, WebSurfaceOptions? options = null) (WebSurfaceOptions: Pitch, Roll, Glow 0..1, Transparency - WebSurfaceTransparency.None / Blend / Cutout, the world through the page’s see-through parts -, Prompt, VirtualWorld; an entity of EntityKind.Surface, removed with DestroyEntity), bool SetWebSurfacePos(IWorldEntity, float x, float y, float z, float yawDeg, float pitchDeg = 0, float rollDeg = 0), bool SetWebSurfaceSize(IWorldEntity, float width, float height), bool SetWebSurfaceGlow(IWorldEntity, float glow), bool SetWebSurfaceTransparency(IWorldEntity, WebSurfaceTransparency), IReadOnlyList<IWorldEntity> GetWebSurfaces(), string GetWebSurfacePage(IWorldEntity) (“” for no surface), bool SendWebSurfaceMessage(IWorldEntity, object? value, out string? reason) (every copy of its page there is now) and bool SendPlayerWebSurfaceMessage(IPlayer, IWorldEntity, object? value, out string? reason) (one player’s; the JSON of SendPlayerWebMessage). The callbacks: OnPlayerWebSurfaceReady(IPlayer, IWorldEntity) (a player’s copy loaded: send it what it shows), OnPlayerWebSurfaceEvent(IPlayer, IWorldEntity, string name, string payload) (its page’s emitServer) and OnPlayerUseWebSurface(IPlayer, IWorldEntity) (the use key on its prompt).

World data (the guides) - the navigation mesh: bool HasNavmesh, IReadOnlyList<Vector3>? FindPath(Vector3 from, Vector3 to) (the corners of the walk, null off the mesh or unreachable), bool IsReachable(Vector3, Vector3), float? NavmeshHeight(Vector3), Vector3? NearestNavmeshPoint(Vector3); the collision geometry: bool HasCollision, RayHit? RayCast(Vector3 from, Vector3 to) (the nearest surface: Point, Normal, Distance, the owning entity’s EntityClass / EntityName / EntityId, the Material), bool IsLineOfSight(Vector3, Vector3), float? GroundZ(Vector3). Metres, the level’s frame (Z up); the doors the server knows are open let a ray through.

Doors and containers - IReadOnlyList<string> Doors (the touched ones), IReadOnlyList<string> LevelDoors (GetLevelDoors: every door of the export), bool GetDoorState(string key, out bool open, out bool locked), void SetDoorState(string key, bool open, bool locked) and, for a virtual world with doors of its own, bool SetVirtualWorldOwnDoors(int world, bool own), bool HasVirtualWorldOwnDoors(int world), bool GetDoorState(string key, int world, out bool open, out bool locked) / void SetDoorState(string key, bool open, bool locked, int world) (SetVirtualWorldOwnDoors: by default every world shares world 0’s doors), bool SetDoorCheck(string key, bool on = true), bool IsDoorChecked(string key) (SetDoorCheck: the use waits for OnPlayerUseDoor, "*" every door), bool SetDoorPrompt(string key, string? text), string GetDoorPrompt(string key) (SetDoorPrompt), the chests’ locks bool SetContainerLocked(string key, bool? locked), bool? GetContainerLocked(string key), bool SetContainerLockedFor(IPlayer, string key, bool? locked), bool? GetContainerLockedFor(IPlayer, string key) (SetContainerLocked: null = the level’s own lock, "*" every chest), IReadOnlyList<string> Containers, IReadOnlyList<ContainerItem>? GetContainerItems(string key), void SetContainerItems(string key, IEnumerable<ContainerItem>), IPlayer? GetContainerUser(string key); the loot tables (RollLoot and the rest): IReadOnlyList<ContainerItem> RollLoot(string preset), bool HasLootPreset(string), ContainerInfo? GetContainerInfo(string key) (Preset, RestockInSeconds, Touched, RecordKey), string GetContainerPreset(string key), bool SetContainerPreset(string key, string preset), bool RestockContainer(string key), void LinkContainers(IEnumerable<string> keys); the loose items on the tables: IReadOnlyList<string> TakenWorldItems, WorldItemInfo? GetWorldItem(string key) (ItemClass, X, Y, Z, TakenBy, RespawnInSeconds, PickupNetId), bool RespawnWorldItem(string key), bool TakeWorldItem(string key), bool ResetWorldItem(string key) and int ResetWorldItems(Vector3? point = null, float radius = 0) (ResetWorldItem: the level’s own item back in its place on every client; a point needs a radius above 0, and TakenWorldItems is a copy that may be walked while resetting), IReadOnlyList<WorldItemSlot> WorldItemsNear(Vector3 point, float radius, int max = 100) (GetWorldItemsNear: Key, ItemClass, X, Y, Z, Taken, Interactable), bool SetWorldItemInteractable(string key, bool interactable, out string reason) and bool IsWorldItemInteractable(string key) (SetWorldItemInteractable: one slot’s item stays where it lies with no prompt, no pickup and no physics); double WorldSeconds (the clock the restock timers run on).

Gates (the Lua page) - IReadOnlyList<string> Gates, GateInfo? GetGateInfo(string key) (Kind, Cycle, Openness, X, Y, Z, OpenSeconds, CloseSeconds), GateResult SetGateState(string key, bool open), GateResult ToggleGate(string key) (Accepted, Reason, Opening, Seconds), string? NearestGate(float x, float y, float z, float radius). The story’s five gates, malesov_courtyard_gate, raborsch_gate and ruthard_palace_gate (kutnohorsko), trosky_south_gate and nebakov_gate (trosecko), are at the other end at once (Seconds 0), start a server open like the others and keep their end across a restart: a mode that wants Trosky’s shut calls _api.SetGateState("trosky_south_gate", false) in OnInit (refused, "is already closed", once it is).

Level layers (the Lua page) - LayerResult SetLevelLayer(string name, bool on) (Accepted, Reason), bool? GetLevelLayer(string name) (null = never switched), IReadOnlyList<string> LevelLayers, IReadOnlyList<LayerNear> LevelLayersNear(float x, float y, float z, float radius) (the layers with a solid piece near a point, the nearest first: Name, PiecesNear, Pieces, Distance, Switched; needs a current tables export). The level’s invisible barriers: BarrierResult SetLevelBarriers(float x, float y, float radius, bool on) (on = false lifts them in the circle; Accepted, Reason), IReadOnlyList<BarrierArea> LevelBarrierAreas (X, Y, Radius).

The server’s map (the Lua group, Building on the map, Using the map editor) - every change answers a MapEditResult (Accepted, Reason, Id). Objects: CreateMapObject(MapObjectSpec), SetMapObject(uint id, MapObjectSpec) (the spec whole), DeleteMapObject(uint id), MapObjectInfo? GetMapObject(uint id) (Id, Spec, By, Revision, PartOf - the prefab unit an object of a unit belongs to, 0 none), IReadOnlyList<MapObjectInfo> GetMapObjectsNear(float x, float y, float z, float radius) (a unit and each of its objects). MapObjectSpec: Mesh (the game’s mesh path; "entity:light", "entity:effect", "entity:sound", "entity:marker", "entity:door" or "entity:seat" for the working things - their settings in Params, as the Lua pages of CreateMapLight, CreateMapSeat and their kin say; the game’s chairs, benches and beds placed as meshes get their seats by themselves, a chair’s Params = "table" seats the player at a table; the game’s own ladders (the levels’ climbable meshes in objects/manmade/common_fixtures/ladders/) are climbed like the level’s while Solid and not stretched; "picture:<name>" or "surface:<page>" for a board), X, Y, Z (below 0 = on the ground), YawDeg, PitchDeg, RollDeg, Scale with ScaleY / ScaleZ (0 = the scale: uniform; a board’s width and its height’s stretch), Material, Tint (0xRRGGBBAA), Solid, Nav (NavEffect.Auto, Block, Walkable, Ignore), Use (a use prompt), Params, ViewDistance (how far it is drawn, 1-255; 0 = by its size), LodRatio (how soon the game swaps in the mesh’s simpler models: 1-255, 100 = the game’s normal, higher sooner and lighter to draw, lower keeps the detailed model farther; 0 = the game’s own), Layer, Group, Persist (false: this run of the mode only), Attach (the object it sits on - moved, turned, stretched and deleted with it; 0 none) and OccludeRain (true: a roof keeps the rain out under it, as the level’s own roofs do; false lets it fall through). Markers: IReadOnlyList<MapObjectInfo> GetMapMarkers(string tag = ""). Pictures and pages: IReadOnlyList<MapPictureInfo> GetMapPictures() (Name, Width, Height in pixels), MapEditResult ReloadMapPictures(), float GetMapBoardAspect(string asset) (a board’s height over its width; 0 = no such picture or page), IWorldEntity? GetMapObjectSurface(uint objectId) (a "surface:" object’s own web surface - talk to it with the web surface functions; it moves with its object) and uint GetWebSurfaceMapObject(IWorldEntity); a new picture board with an empty Params shows a picture’s see-through pixels soft ("blend"), "solid" keeps it plain. The level’s own things: RemoveLevelObject(LevelRemovalSpec) (Kind - LevelObjectKind.Entity, Brush, Vegetation -, Class, Name, Mesh, X, Y, Z, Layer, Persist), RestoreLevelObject(uint id), IReadOnlyList<LevelRemovalInfo> GetLevelRemovals(). Layers: SetMapLayer(string name, bool on), IReadOnlyDictionary<string, bool> GetMapLayers(), bool SetPlayerMapLayer(IPlayer, string layer, bool? on) (one player’s own view; null = the map’s switch again), GetPlayerMapLayers(IPlayer). bool ResendPlayerMap(IPlayer) sends one player the map again as at their join - their game draws it anew, without a relog (the server does so by itself when a player’s game gives up on the map; once every 10 seconds a player at most). Prefabs: IReadOnlyList<string> GetMapPrefabs(), SaveMapPrefab(string name, IReadOnlyList<uint> objectIds), DeleteMapPrefab(string name) (refused while a unit of it stands), MapPlaceResult PlaceMapPrefab(string name, float x, float y, float z, float yawDeg, string layer = "", bool persist = true) (Accepted, Reason, Ids - copies of its objects). A prefab placed as one unit - one map object drawn as its objects everywhere, following the prefab when it is saved again (the Lua page of CreateMapPrefab says how): MapEditResult CreateMapPrefab(string name, float x, float y, float z, float yawDeg, string layer = "", bool persist = true) (or CreateMapObject / SetMapObject with Mesh = "prefab:<name>": one Scale, Solid, ViewDistance, LodRatio over its objects’), IReadOnlyList<MapObjectInfo> GetMapPrefabParts(uint unitId) (its objects, their ids fixed: a door’s key, a use prompt’s id) and MapPlaceResult UnpackMapPrefab(uint unitId) (its objects as map objects of their own). Sharing and keeping: ExportMap(string name), IReadOnlyList<string> GetMapExports(), ImportMap(string name, bool replace), BackupMap(), ImportSandboxLayer(string name), MapInfo GetMapInfo() (Objects, Removals, Revision, Hash, Folder, ReadOnly, ReadOnlyReason), bool SaveMap(). The editor: bool SetPlayerEditor(IPlayer, EditorScope? scope, out string reason) (null closes it; EditorScope: Place, ChangeOthers, Delete, RemoveLevel, Layers, Areas, MaxObjects, Layer), EditorScope? GetPlayerEditor(IPlayer). Areas: CreateArea(MapAreaSpec) (Name, Shape - AreaShape.Box, Cylinder, Sphere, Polygon -, Min / Max for a box (their Z the height of a cylinder or a polygon too), Center and Radius for a cylinder or a sphere, Points for a polygon, Label, LabelPublic, Color, Nav, Layer, Persist), SetArea(string name, MapAreaSpec), DeleteArea(string name), MapAreaInfo? GetArea(string name), IReadOnlyList<MapAreaInfo> GetAreas(), IReadOnlyList<string> GetAreasAt(float x, float y, float z), bool IsPlayerInArea(IPlayer, string area), IReadOnlyList<string> GetPlayerAreas(IPlayer), SetAreaProperty(string area, string key, string? json) and string? GetAreaProperty(string area, string key) (the mode’s own values, JSON scalars). An area’s Trespass (TrespassLevel.Public .. Prohibited; null = no word) and Rain (0 .. 1, the most rain inside; null = the world’s): SetAreaTrespass(string area, TrespassLevel?), SetAreaRain(string area, float?), bool SetPlayerAreaTrespass(IPlayer, string area, TrespassLevel?) (one player’s rule over the area’s), void SetPlayerTrespass(IPlayer, TrespassLevel?) (over every area), SetLevelTrespass(bool on) / bool IsLevelTrespassOn() (the level’s own private places; on by default), void SetPlayerLevelTrespass(IPlayer, bool?), PlayerTrespass GetPlayerTrespass(IPlayer), void SetPlayerRain(IPlayer, float?), float GetPlayerRain(IPlayer). Map icons (the world map and the compass): uint CreateMapIcon(MapIconSpec, out string reason) (Position, Type - a map icon type’s name or GUID -, Icon (-1: the type’s), Name, Visible; 0 = refused), bool SetMapIcon(uint id, MapIconSpec, out string reason), bool DeleteMapIcon(uint id), MapIconInfo? GetMapIcon(uint id), IReadOnlyList<MapIconInfo> GetMapIcons(), bool SetMapIconVisibleFor(IPlayer, uint id, bool?); the level’s own: uint HideLevelMapIcons(LevelIconRule, IPlayer? player, out string reason) (Mark - one icon’s id -, or Type with Position and Radius (0 = the whole level); null player = everyone), bool ShowLevelMapIcons(uint rule), IReadOnlyList<LevelIconRuleInfo> GetLevelIconRules(), IReadOnlyList<MapIconType> GetMapIconTypes(). Nav links: CreateNavLink(NavLinkSpec) (From, To, Radius, TwoWay, Type, Layer, Persist), DeleteNavLink(uint id), IReadOnlyList<NavLinkInfo> GetNavLinks(float x, float y, float z, float radius).

Bans - void Ban(IPlayer, string reason, int seconds = 0), void BanName(string, string reason, int seconds = 0), void BanAddress(...), bool Unban(string nameOrAddress), string? IsBanned(string nameOrAddress).

The player as the mode sees them; the readers of the Lua tier are properties here.

Property Lua
int Id, string Name, string Address, int Ping, string ClientVersion, string GameBuildHash GetPlayerName, GetPlayerIP, GetPlayerPing
bool IsInWorld, Vector3 Position, float YawDeg, Vector3 Velocity, uint ReportsReceived IsPlayerInWorld, GetPlayerPos …
bool IsAdmin IsPlayerAdmin
IReadOnlyList<PlayerMod> Mods GetPlayerMods - the game’s mods their game loaded
string Uid, ulong SteamId GetPlayerUid - their id on this server (32 hex, theirs alone; "" = none), GetPlayerSteamId - their game’s word (0 = none)
string Hwid GetPlayerHwid - their PC on this server (system-board-disk-machine, four parts of 16 hex; "" = none)
string Language GetPlayerLanguage - their game’s text language ("en", "de" …; "" until their level loaded)
float Health, float MaxHealth, bool IsDead, float Stamina, float MaxStamina, int Injuries (a bit per part: 1 head, 2 torso, 4 left arm, 8 right arm, 16 left leg, 32 right leg), float Bleeding, float Healing GetPlayerHealth …, GetPlayerInjuries, GetPlayerBleeding
float Alcohol, bool IsDrunk GetPlayerAlcohol, IsPlayerDrunk
IReadOnlyList<string> Equipment, IReadOnlyList<ContainerItem> Inventory, float Money, int AuditViolations GetPlayerEquipment (what they wear, from the item records once the server follows their bag), GetPlayerInventory (the server’s records, the mode’s gives and takes on their way counted; Health is always 100), GetPlayerMoney, GetPlayerAuditViolations (how many times the item check took unexplained items out of their game this session - a count of removals, not of violations)
IReadOnlyList<string> Buffs GetPlayerBuffs
IDictionary<string, object?> Data, IReadOnlyDictionary<string, string> State SetPlayerData, GetPlayerStates
int Team, uint Color, string Nameplate GetPlayerTeam, GetPlayerColor, GetPlayerNameplate
IParty? Party, string PartyLabel GetPlayerParty, GetPartyMemberLabel
IReadOnlyList<IZone> Zones GetPlayerZones
int VirtualWorld, bool IsControllable GetPlayerVirtualWorld, IsPlayerControllable
PlayerStance Stance, bool IsCrouching, bool IsNameplateHidden GetPlayerStance, IsPlayerCrouching, IsPlayerNameplateHidden
bool IsVisorDown IsPlayerVisorDown - the helmet’s visor down, as their game last reported it
PlayerSeat? Seat GetPlayerSeat - the bench, chair or bed of the level they sit or lie on, null on none (a cart’s seat is Stance’s Seated)
bool IsSleeping IsPlayerSleeping - lying in a bed, as their game last reported it
PlayerGrime Grime GetPlayerGrime, GetPlayerDirt, GetPlayerBlood - the dirt and blood of the skin (Skin) and each worn piece (Items), as their game last reported it; ClothesDirt, ClothesBlood(zone) the dirtiest / bloodiest piece’s
bool IsWeaponDrawn, string RightHandItem, string LeftHandItem IsPlayerWeaponDrawn, GetPlayerHandItems - what the hands hold now, "" empty
bool IsTalking, bool IsVoiceMuted, bool IsVoiceDeaf, float VoiceRange, bool VoiceEnabled IsPlayerTalking, IsPlayerVoiceMuted, IsPlayerVoiceDeaf, GetPlayerVoiceRange, IsPlayerVoiceEnabled
string? Emote GetPlayerEmote
bool IsFlying IsPlayerFlying
bool ChatVisible GetPlayerChatVisible
ThirdPersonMode ThirdPerson, bool ThirdPersonInFights, bool ThirdPersonWhenAiming, bool ThirdPersonCrosshair GetPlayerThirdPerson - Off until a mode changed it; the three flags are true (third person stays on in a fight and when aiming, the aiming dot shows) unless a mode said otherwise
bool IsThrown, bool RiderHeadHit, bool RiderThrow IsPlayerThrown, SetPlayerRiderHeadHit, SetPlayerRiderThrow - what a mode set (both true by default)
bool IsCarrying, bool IsCarried, bool IsKnockedOut IsPlayerCarrying, IsPlayerCarried, IsPlayerKnockedOut
bool IsPickpocketable, bool CanPickpocket IsPlayerPickpocketable, CanPlayerPickpocket - true unless a mode took the pockets out, or the right to rob away
bool CanPullDown, bool IsInPullDown GetPlayerPullDown (false until a mode called SetPlayerPullDown(true)), IsPlayerInPullDown
Look Look GetPlayerLook
IShop? Shop GetPlayerShop - the shop they have open (OpenShop, the use key on a vendor); null = none

int NetId, EntityKind Kind (Player, Horse, Item, Npc, Prop, Dog, Cart, Animal, Carry, Siege, Point), string Template (the item class, the soul, the mesh path), Vector3 Position, float YawDeg, Vector3 Velocity, IPlayer? Controller, IPlayer? Rider, IPlayer? Owner (a horse’s), int VirtualWorld, uint CreatedAt, uint LastReportAt (GetEntityIdleTime is TimeMs - LastReportAt), IDictionary<string, object?> Data, IReadOnlyDictionary<string, string> State, float Scale, bool Rigid (props), int Amount (a pickup’s stack; 1 for everything else), string Name, bool IsMoving, float Health, bool IsDead, int Injuries (a bit per part, as IPlayer.Injuries), float Bleeding, Look Look (actors), bool IsKnockedOut (IsActorKnockedOut), bool IsCarried (GetEntityCarrier says by whom), bool IsCarrying (an NPC actor with a body or a player on its shoulder - ActorCarryEntity, ActorCarryPlayer); each is false unless it is so now. For an NPC actor also bool Invulnerable (IsActorInvulnerable), string Prompt (GetActorPrompt: “” = none), IShop? Shop (GetActorShop), string Nameplate, uint Color and IWorldEntity? OpponentActor.

IZone: int Id, bool IsCircle, MinX .. MaxZ, CentreX, CentreY, Radius, bool Contains(float x, float y, float z), IReadOnlyList<IPlayer> Players, IDictionary<string, object?> Data - GetZoneInfo, GetZonePlayers, SetZoneData.

IHudText: int Id, float X, float Y, string Text, uint Color, float Scale, HudAlign Align (settable - a change is re-sent), IReadOnlyList<IPlayer> ShownTo, void Show(IPlayer), void Hide(IPlayer), void ShowForAll(), void HideForAll(), bool IsShownTo(IPlayer) - CreateHudText and its family.

IParty: int Id (never reused while the server runs), IPlayer Leader, IReadOnlyList<IPlayer> Members (in join order), string Name, IDictionary<string, object?> Data (the mode’s private bag, gone with the party) - GetPartyLeader, GetPartyMembers, GetPartyName, SetPartyData.

Type
SpawnPoint(float X, float Y, float Z, float YawDeg, string ClothingPreset = "", string WeaponPreset = "", string Appearance = "") where and as what a player spawns
enum GameScreen : byte { None, Inventory, Character, Map, Codex, Journal, Other } the game’s player screens - OpenPlayerScreen; Other is only ever reported
enum BloodZone { Head, Torso, LeftArm, RightArm, LeftLeg, RightLeg }, GrimeLevels(float Dirt, float Head, float Torso, float LeftArm, float RightArm, float LeftLeg, float RightLeg) (Blood(zone), MostBlood, IsClean), ItemGrime(string ItemClass, GrimeLevels Levels), PlayerGrime(GrimeLevels Skin, IReadOnlyList<ItemGrime> Items) a player’s dirt and blood, 0 clean .. 1 - GetPlayerGrime
DbWriteResult(int Affected, long InsertId), DbStatement(string Sql, IReadOnlyDictionary<string, object?>? Parameters = null) what IDatabase.ExecuteAsync answers; one statement of a BatchAsync (above)
ContainerItem(string ItemClass, int Amount, int Health = 100, bool Worn = false) one stack of a container or an inventory (Worn: one item the player wears)
ProgressLevel(byte Level, float Xp = 0, float Next = 0, ushort Points = 0) one stat or skill: the level, the XP gathered toward the next level, the XP the next level takes in all (Next 0 at the top level, or not reported yet) and the perk points not spent yet; a value to set needs the level, the XP and the points - SetPlayerProgress sets Points exactly, 0 too (SetPlayerStat / SetPlayerSkill leave them)
ProgressSnapshot(IReadOnlyDictionary<string, ProgressLevel> Stats, IReadOnlyDictionary<string, ProgressLevel> Skills, IReadOnlyList<string>? Perks = null, float? Nourishment = null, float? Energy = null) with Empty everything a mode saves with a character - GetPlayerProgress, SetPlayerProgress; Stats holds the main level as the entry "mainlevel" (its Level is the game’s own figure and is ignored on a set, its Points the pool of perk points that belong to no stat or skill); on a set, Perks null leaves the perks alone and a list makes them exactly that list, Nourishment / Energy null are left alone
DiscoverySnapshot(string Level, IReadOnlyDictionary<DiscoveryKinds, DiscoveryBlockInfo> Blocks, DiscoveryKinds Capable = DiscoveryKinds.None) with Empty(level) what GetPlayerDiscovery returns and SetPlayerDiscovery takes; Capable the kinds the player’s game can do (ignored on a set)
DiscoveryBlockInfo(byte Format, byte[] Data, DateTimeOffset Updated) one kind’s block: the version of its format (1 today; the formats only grow), the bytes (never changed once handed out), when the record of the kind last changed
[Flags] enum DiscoveryKinds : ushort { None, Places = 2, Reading = 4, Ui = 8, All } with DiscoveryKindNames (Of, Join, TryParse, TryParseList: the names places, reading, ui) discovery kinds
PlayerWeight(float Carried, float Capacity, bool Overloaded, bool Known) GetPlayerWeight: the carry-weight check’s own estimate (what they wear counts in Carried, the money does not; Capacity allows for the strength, perks, potions and drinks); default = not known
ProgressAuditInfo(int Excesses, int SetBacks, int Vouched, int Rewrites, int Probes, int StaleReports, bool GateDown, bool FirstSession) GetPlayerProgressAudit: the session’s counts of the progression check for one player
HitInfo(float Damage, float Raw, int Zone, BodyPart BodyPart, int DamageType, string WeaponClass, string WeaponName, float Defense, bool Exhausted) with DamageTypeName ("stab", "slash", "smash"), float ArmourStopped (the share the armour on that part took, 0 .. 0.85), bool Ranged (an arrow, a bolt or a bullet) and bool Bleeds (the server’s answer: the blow opens a bleeding wound) everything the model resolved for a hit
ItemInfo(int Id, string Name, string ItemClass, string Category, string DisplayName, float Weight, float Price) GetItemInfo
ItemDefinition(string Base) with Name, Text, float? Weight, float? Price (Groschen), Health (100), Quality (0 = the base’s), string? Model, Stacks (true) one of the mode’s own items - DefineItem
SoulInfo(int Id, string Name, string SoulGuid, string Archetype, string Voice = "") GetSoulInfo; Voice the game’s voice its lines are recorded in ("" = none)
SmartObjectInfo(string Key, string Activity, string Kind, string Helper, float X, float Y, float Z, float Yaw, float Distance, IWorldEntity? User) one of the level’s seats, beds or work spots - GetSmartObjects
Look(string Body = "", string Head = "", string Hair = "", string Beard = "", string Gender = "") with None, Female, IsFemale, IsEmpty, Parts, ToString(), Parse(text) a look: the skin, the face, the hair, the beard, a woman’s body or a man’s - SetPlayerLook
LookPart(string Kind, string Name, string Gender, string Style, string Traits, bool Generic) GetLookParts
BuffInfo(int Id, string Name, string BuffGuid, string Class, string DisplayName, float Duration) GetBuffInfo
MeshInfo(int Id, string Path) with Name FindMeshes
RayHit(Vector3 Point, Vector3 Normal, float Distance, string EntityClass, string EntityName, uint EntityId, byte Material) RayCast
ChatMessage { Text, Spans, Template, Params, Channel, Color } (a string converts to one) and ChatMessageSpan(string Text, uint Color = 0, bool Bold = false, bool Italic = false) a chat line of SendChatMessage; colours are 0xRRGGBBAA
ChatResult - Allow, Suppress, Rewrite(string), Rewrite(ChatMessage) what OnPlayerChat answers
ChatChannelOptions { Label, Color, Visible, VisibleTo, CanSend, CanSendTo }, enum ChatChannelVisibility { All, Admin, Party }, enum ChatChannelSending { Everyone, Nobody, Admin } RegisterChatChannel: who sees the channel and who may write in it (VisibleTo / CanSendTo ask a function instead)
ActionHintOptions { Order, Hold, HoldMs, Modifier, Binding, Disabled } and ActionHint(string Id, string Key, string Text, ActionHintOptions Options) a line of the mode’s own in a player’s key hints - AddPlayerActionHint: its place among the game’s lines (a higher order sits lower, nearer the screen’s edge), the hold mark and the time its ring fills, a second key, a key binding whose key is shown, drawn faint
CommandOptions { Help, Params, Admin, Secret } RegisterCommand: the help line, the usage, admins only, a secret argument (a login)
ScoreboardColumnSpec { Key, Label, Width, Align, Format, Color, Visible } SetScoreboardColumns: the player state key the cells show, the heading, the width, "left" / "center" / "right", "" / "number" / "time" / "percent", the cells’ colour, "all" or "admin"
enum BodyPart { Unknown, Head, Torso, ArmLeft, ArmRight, LegLeft, LegRight } the body parts 0 .. 6
enum EntityKind : byte { Player, Horse, Item, Npc, Prop, Dog, Cart, Animal, Carry, Siege, Point } entity kinds
enum HudAlign : byte { Left, Centre, Right }, enum GameTextStyle : byte { Centre, Top, Lower } constants