Frostdev

KilnWikiGameplay

Inventory transfers

Move gear with its state, read container names, and keep items out of quick stack or move all.

Game test pending 3 min read Updated

New in 0.2.0-dev.20. These APIs require the dev.20 runtime and MDK or newer. Server evidence and pending client checks are tracked on What works today.

Kiln.Items adds moves that preserve the item’s entity: level, upgrades, gem slot and durability follow it. A take followed by a give creates new gear. Everything here runs on the tick thread; moves finish in a later simulation step.

Move a stack

var sword = Items.Read(player)!.Slots.First(s => s.IsGear && !s.IsEquipped);
MoveResult result = await Items.Move(player, sword.Slot, chest);
if (result.Succeeded) Log.Info($"Moved {result.Done} item(s)");

await Items.Move(chest, slot, player, count: 3);
await Items.QuickStack(player, chest);       // items the chest already holds
await Items.MoveAll(chest, otherChest);      // every stack that fits

Check Items.MovesAvailable first. count: 0 means the whole stack. MoveResult reports Done, Partial, NoRoom, NotEnough, NoInventory, SlotChanged, Refused, Unavailable or Dropped. Its Changes ledger lists the first 16 changes, including source, destination, item, amount and gear entity; totals cover all changes.

MoveOptions.DryRun runs and rolls back, without a transfer event. Equipped/held gear and the action bar stay out by default; Equipped and ActionBar opt in. Silent skips transfer rules and events.

Since dev.21 a dry-run move also runs native transfer handlers (kiln.items transfer_listen), unless it is silent too. They see KILN_TRANSFER_DRY_RUN in ev->flags. Keep and cancel as usual, so the dry run predicts the real move, but don’t count, log or change anything outside the transfer: it is all rolled back.

Names, rules and reports

if (Items.ContainerNamesAvailable && Items.ContainerName(chest) == "Ores")
    await Items.QuickStack(player, chest);

using var keep = Items.Transfers.Keep(slot);
using var keepTorches = Items.Transfers.KeepItem(torchItemId, player);
using var deny = Items.Transfers.Deny(vault, TransferKinds.QuickStack);

ContainerName reads the name a player entered; it does not rename a chest. An unnamed chest returns null.

Rules keep a source slot, keep an item for an owner, or deny quick stack/move all into or out of a container. They end when disposed or the mod unloads. They cover the game’s quick stack and move all, including a Quick Stack Station. InventoryTransferEvent reports the result on the tick thread afterwards; it is not cancelable. To cancel a player’s incoming request, handle PlayerClientActionEvent only when e.Name == "inventoryTransferAction" and e.CanCancel. Then parse e.Payload with Items.Transfers.ParseAction and cancel the held action if your rule requires it. The parser does not identify an action from its bytes. Joined-client events cannot cancel the server’s action; a station has no such client request.

Item data and saves

Gear entity data is session-only. EntityData.IsPersistent(gearEntity) is false. An attachment follows Items.Move within the session, but a sword has no stable world-position identity for a restart. Kiln omits those attachments from new saves and refuses old positional gear records, so two identical swords cannot inherit each other’s data. Saved world entities such as chests retain their normal persistence.

This does not add persistent per-item payloads or affixes. Moving the game’s own gear state and saving a mod-defined tag on that gear are separate capabilities.

The transfer implementation is ported from shroudlink’s MIT API and calls the game’s own transactions. Kiln’s hooks can chain with shroudlink; rule disposal ends the rule, not the process-lifetime hook.

A joined player must still confirm the visible slot updates, equipped-item behaviour, R-key transfers, station reports, renamed chests and the optional shroudlink client addon’s slot locks. A server test cannot show those interactions.

Enshrouded is a trademark of Keen Games GmbH. Kiln is unofficial.