Storage

Usage guide of storage.containers: chests, crates and silos with slots, stacks and accepted items, a world side for scripts and machines, a player side against the bag, the storage screen, settings, events and hooks.

Source: module-kits:kits/storage_containers/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs storage-containersMCP tool: gripforge_docs { "topic": "storage-containers" }

Storage containers (storage.containers) #

What it is for #

Somewhere to put things. A container is any entity that holds items: a chest, a crate, a silo, a fridge, a warehouse shelf. It has a number of slots, a stack size and, when wanted, a list of the only items it takes. The player opens one and moves items between the bag and the container; scripts and other kits (carriers, machines, quests) put and take without the player. The contents live in the entity's component, so the save keeps them. The bag itself is Inventory.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "storage.containers" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "storage.containers",
                              config: { "defaultSlots": 12, "defaultStack": 50, "reach": 4 } }

It requires only game.engine. The player side works with inventory.items (the bag), and a container shows its prompt through interaction.use; with automation.logistics installed the screen also offers the open container's logistics (offer its contents to drones, keep a number in stock).

There are three ways to make a container: an archetype with the component storage_container, a structure of build.placement whose components include "storage.container": { … }, or place(at, opts?) from a script (opts: label, slots, stack, accepts, items, variant, yaw).

{ "label": "Crate", "slots": 8, "stack": 50, "accepts": ["wood", "stone"], "items": { "wood": 20 } }

First example #

<!-- test: kits/game_engine/host/tests/parity.rs::rust_host_matches_the_typescript_traces (fixture storage.containers.boxes) --> The parity scenario creates a chest with 2 slots, a stack of 8 and a list of accepted items, then calls the world side: containers() and contents(entity) describe it, room(entity, item) tells how many units still fit (0 for an item that is not accepted), put(entity, item, n) returns the units really stored and take(entity, item, n) the units really removed. Anything that does not fit stays with the caller:

room ["e2", "ore"]      -> units that still fit
put  ["e2", "ore", 9]   -> units stored (what the stack of 8 still leaves)
put  ["e2", "gold", 1]  -> 0   (not in accepts)
take ["e2", "half", 5]  -> units removed (never more than it holds)

Settings and defaults #

SettingDefaultMeaning
openLabelOpen {name}prompt of a container; {name} is its label
defaultNameContainername of a container without a label
reach4the screen closes when the player walks further than this (m)
defaultSlots12distinct items a container holds when it does not say
defaultStack50units per item of a container that does not say

A container's own fields are label, slots (one item takes one slot), stack, accepts (the only items that go in; absent: anything) and items (what it holds, { "<item>": count }).

Services, events, variables #

  • storage.containers (service), world side, never involving the player: contents(entity), count(entity, item), room(entity, item), put(entity, item, n), take(entity, item, n), total(item), containers(), find(item), place(at, opts?).
  • Player side: deposit(entity, item, n?) (all of it by default), withdraw(entity, item, n?) (as much as fits), depositAll(entity), withdrawAll(entity), each returning the units moved; open(entity), close(), current().
  • Events: storage.changed (entity, item, count, delta), storage.full (a put or deposit did not fully fit), storage.opened, storage.closed, storage.refused (entity, action, item, reason).
  • Variables: storage.open, storage.containers.
  • Condition stored_at_least (item, count): every container together holds at least that many.

Hooks and modding #

The player side is asked; the world side (put, take) never is.

HookPayloadEffect
storage.openentity, labelcancelled: the screen stays shut (a locked chest)
storage.depositentity, label, item, countmutable count (never more than the bag holds); cancelled: nothing moves
storage.withdrawentity, label, item, countmutable count (never more than the container holds); cancelled: nothing moves

A cancelled hook emits storage.refused with the reason of the tap.

{ "id": "locked_vault", "hook": "storage.open", "match": { "label": "Vault" }, "cancel": true, "reason": "locked" }

A mod may override openLabel, defaultName, reach, defaultSlots and defaultStack. The kit has no collection of its own.

Engines #

The web player and the native kit host run the same rules. The storage screen shows the bag and the open container side by side (move one, move all, deposit all, take all), drawn as a ui.webui fragment.

When it does not do what you expect #

  • Nothing goes in: the item is not in accepts, the slots are used, or the stack is full; room says how many fit.
  • The prompt never appears: the entity has no storage_container component, or interaction.use is not installed.
  • The screen closes by itself: the player walked further than reach.
  • A deposit does nothing: a tap of storage.deposit cancelled it; read the reason in storage.refused.

Limits #

  • Nothing is ever lost: what does not fit stays with the caller.
  • It stores counts of named items.

Versions #

Kit 1.1.1, service contract 1.0.0. 1.1.0: the game.engine hook contract and storage.refused; 1.0.0: first release.