Skip to content
KCKingdoms Connected Scripting APIScripting guides and API
GuideMaintainer authored

Server vs client authority

Which machine owns each piece of game state, why a verb on a player is a request, and how to confirm that it worked.

Each player’s own client owns their body: health, stamina, clothes, buffs and where they stand. The server owns everything shared between players, and each player’s inventory. Reading a player’s body gives you its latest snapshot, and most body-changing verbs are requests to its client. The server also rules on player combat and NPC melee against players before health is taken; playerHit lets the game mode refuse or change those hits.

// server
Events.on("playerCommand", (player, command) => {
if (command !== "lift") return;
const above = player.position.clone();
above.z += 20;
const sent = player.teleport(above);
// `sent` means the request went out. player.position still reads the old
// spot here; the new one arrives with the player's next update.
Chat.sendToPlayer(player, sent ? "Up you go." : "Your client could not be reached.");
});
Thing Owner What you can do
A player’s body: pose, health, stamina, stats, appearance, buffs That player’s client Read its last report; send it requests.
A player’s inventory The server Read and change it directly; their game only shows it. See Inventories.
A player’s skills, XP and perks The server rules, their client applies Every gain is asked for and granted as an order. See Progression.
A horse standing idle The server Spawn, move and change it directly.
A horse with a rider The rider’s client Handed over on horseMount, back on horseDismount.
An NPC The server; a nearby client runs the body Identity, orders and health are the server’s. Writes apply at once.
NPC inventory and harvest state The server Set stock with Inventory.set, or wait for the client to report the starting inventory. See Corpse loot.
Food and potion use Server decision, native client execution Approve the serving and effects; react to confirmed use. See Food and potions.
Props, markers, particle effects, ground items, stashes, quests The server Spawn, change and destroy directly.
World clock and weather The server Every client adopts the server’s values.
Entity state bags The server Only the server writes; clients read. See State bags.

Every player property is the value their client last published, not one the server keeps. Until the first report arrives, fields hold placeholders: health reads 0 and position is the world origin.

player.ready turns true once the first pose and character state have arrived. Check it before a position matters:

// server
function nearEnough(a: Player, b: Player, metres: number): boolean {
if (!a.ready || !b.ready) return false;
return a.position.distance(b.position) <= metres;
}

Act on a player: the return value means “sent”

Section titled “Act on a player: the return value means “sent””

A verb on a player returns whether the request went out, not whether it worked. The effect appears a moment later, once their client has applied it and reported back. Their game can still refuse: a buff that conflicts with one already there, or a beard the face was never modelled with.

Verb Returns How you learn it happened
teleport, spawn true when sent player.position changes on a later update.
setAppearance true when sent player.appearance reads back the new look.
addBuff, removeBuff true when sent playerBuffAdded or playerBuffRemoved fires. hasBuff straight after addBuff is still false.
heal true when sent player.health rises; playerInjuryHealed fires per limb.
setStat, setHealth, setHunger true when sent to a ready, living body A later snapshot reports the applied value.
mount true when requested horseMounting can refuse; horseMount confirms the seat.
revive true when sent The player stands up and player.alive turns true.
addXp, setLevel, addPerk true when sent playerXpGained, playerLevelUp or playerPerkAdded fires.

Item verbs are the exception. giveItem, takeItem and the Inventory calls change the inventory the server holds, so their result is final: the player’s game shows it on the next tick.

Why use a method instead of assigning a player property?

A local property assignment does not instruct the player’s game to change. Use setHealth or setStat for writable stats. player.position is inherited from Entity and can be assigned, but the owner’s next pose replaces it. Use teleport or spawn to move them.

When you need to know, listen for the event, or read the value back later.

// server
Events.on("playerCommand", (player, command, args) => {
if (command !== "buff" || !args[0]) return;
const info = Buffs.find(args[0]);
if (!info) return;
player.addBuff(info.name); // hasBuff is still false here
});
Events.on("playerBuffAdded", (player, buff, source) => {
if (source === "server") Chat.sendToPlayer(player, `${buff} is on.`);
});

Writes on server-owned things apply at once. npc.teleport moves the body whoever simulates it; a pose the simulating client already sent cannot put it back. npc.setAppearance and assigning horse.name are writes too.

The client running an NPC’s body changes as players move (npcSimulatorChange), and the NPC does not: the new simulator picks up from the server’s copy. See Spawn NPCs.

Event Checked how
markerEnter Client detects it; server confirms against the position it replicates.
npcInteract Server confirms the distance.
npcDamage Attacker’s client resolves the hit; server agrees, so amount is what was taken.
groundItemPickup Reports a pickup the server already granted.
doorInteract Server has already refused what the game’s rules forbid (out of reach, locked by the server).

Some events are just reported, because the client decides: questTrackingChanged, playerDamage, and buffs the game applies itself (playerBuffAdded with source "native"). To overrule native buffs, Buffs.claim turns them into playerBuffBlocked events for you to decide on.

Decide on the server and let the client do what it is told. A client resource is for input, UI and effects on one machine; never enforce a rule there, because the player controls it.