Data Storage
Data storage is Minecraft's general-purpose runtime variable container. Where a scoreboard only holds integers, storage holds any NBT value: strings, decimals, lists, and nested compounds. It is the natural place to keep in-game state that is not a plain number.
If you are unsure when to use Kotlin variables versus storage, read Runtime Logic first - storage lives entirely at runtime, in the running world, not at generation time.
This page covers the storage form of the /data command. The same DSL also works on entities (data(self())) and blocks (data(blockPos)); only the target changes.
Creating a storage handle
A storage is identified by a namespaced id. Create a typed handle with storage(...):
The first argument is the path, the second is the namespace (defaults to minecraft). Reuse the same handle everywhere you read or write that storage.
Writing values
Open the /data DSL with data(target) { ... } and write with set:
Each set("path", value) emits a data modify storage my_pack:state <path> set value <value> line.
Writing compounds and lists
For structured data, use merge with an NBT builder, which writes a whole compound at once:
See NBTs for the full NBT builder DSL.
Reading values
Use get to read a value (Minecraft prints it / returns it as a command result):
The scale parameter multiplies a numeric result - handy for moving a value into a scoreboard with a fixed-point factor (see Bridging storage and scoreboards).
Modifying values
modify exposes every /data modify operation - set, merge, append, prepend, insert, and the from/string source forms. The block receiver is DataModifyOperation:
There are convenient shorthands for the common set cases:
Removing values
Bracket shorthand
For one-off reads and writes you can skip the block and use index syntax:
Bridging storage and scoreboards
Storage and scoreboards are the two runtime containers, and you often move values between them. Use execute store to write a command's numeric result into either one (see Execute):
The reverse - score into storage - is the same pattern with storeResult { storage(state, "level", DataType.INT, 1.0) }.
Macros read straight from storage
Storage is the canonical source for macro arguments. You can pass a storage path as the arguments to a function call, and its compound becomes the macro inputs:
When to use storage vs scoreboards
| Use a scoreboard when... | Use storage when... |
|---|---|
| The value is a single integer | The value is text, a decimal, a list, or a compound |
| You need fast arithmetic / comparisons | You need to pass structured data to a macro |
| It is per-entity (uses the score holder) | It is global or grouped state |
You branch on it with execute if score |
You branch on it with execute if data |
In practice most packs use both: scoreboards for counters and math, storage for everything structured.
See also
- Runtime Logic - where storage fits in the compile-time vs runtime model
- NBTs - the NBT builder DSL used by
set/merge - Execute -
storeresults into storage, branch withif data
