A lightweight, clean-room Minecraft Java datapack sandbox focused on local CLI
debugging, manifest checks, and JVM-level quick tests. Built-in version profiles
cover every Minecraft Java release from 1.20.4 through 26.2, with 26.2 as
the default latest profile.
The runtime does not embed or distribute a vanilla server. Build tooling uses
public SpyglassMC/vanilla-mcdoc data and the official @spyglassmc/mcdoc
parser to generate NBT schemas for validation.
./gradlew :cli:fatJarOn Windows:
.\gradlew.bat :cli:fatJarRun release smoke checks for the standalone jar:
.\gradlew.bat :cli:smokeCliJarRun the full 1.0 release gate, including unit checks, standalone jar smoke, and Maven publication artifact verification:
.\gradlew.bat releaseCheckThe smoke checks build the jar, export the bundled manifest schema, run all
example manifests, exercise the built-in benchmark and diff commands, and
execute the concrete README loot, player event, and stdin run examples.
The standard check lifecycle also runs unit tests, manifest examples, and the
standalone jar smoke checks. CI runs releaseCheck on Linux, Windows, and
macOS, so release jars, sources jars, javadocs jars, and generated Maven POMs
are checked on every platform.
The standalone jar is written to:
cli/build/libs/datapack-sandbox-cli.jar
Use the core module as the embeddable API library. The cli module is the
standalone application and should not be used as an application dependency.
Released artifacts target Java 25. Configure the project Maven repository, Maven
Central, and Mojang's library repository, then depend on core:
repositories {
maven("https://nexus.mcfpp.top/repository/maven-releases/")
mavenCentral()
maven("https://libraries.minecraft.net")
}
dependencies {
testImplementation("moe.afox.dpsandbox:core:1.0.0")
}Use implementation(...) instead of testImplementation(...) if you embed the
sandbox in your own tool or plugin. Inside this repository or another included
multi-project build, depend on the module directly:
dependencies {
testImplementation(project(":core"))
}The main Kotlin entry point is SandboxQuickTest:
SandboxQuickTest.singleFunctionText(
"scoreboard objectives add runs dummy\nscoreboard players set #unit runs 1",
version = "26.2",
)
.function()
.assertScore("#unit", "runs", 1)
.requirePassed()Java callers can use the DatapackSandboxTestApi static facade. See
docs/code-test-api.md for dependency variants, Java examples, lower-level
runtime factories, reports, traces, fixtures, and the full quick-test method
catalog.
Start a REPL:
java -jar cli/build/libs/datapack-sandbox-cli.jar repl --version 26.2 --pack ./my_packList supported version profiles and their data pack formats:
java -jar cli/build/libs/datapack-sandbox-cli.jar version
java -jar cli/build/libs/datapack-sandbox-cli.jar version --json
java -jar cli/build/libs/datapack-sandbox-cli.jar version --docs
java -jar cli/build/libs/datapack-sandbox-cli.jar version --docs --output docs/version-profile-table.md
java -jar cli/build/libs/datapack-sandbox-cli.jar version --docs --check docs/version-profile.md
java -jar cli/build/libs/datapack-sandbox-cli.jar version --docs --locale zh-CN --check docs/version-profile.zh-CN.md
java -jar cli/build/libs/datapack-sandbox-cli.jar version 1.20.4 26.2
java -jar cli/build/libs/datapack-sandbox-cli.jar version --json 1.20.4 26.2
java -jar cli/build/libs/datapack-sandbox-cli.jar version --json --output build/profile-diff.json 1.20.4 26.2List command support and behavior levels:
java -jar cli/build/libs/datapack-sandbox-cli.jar commands
java -jar cli/build/libs/datapack-sandbox-cli.jar commands --docs
java -jar cli/build/libs/datapack-sandbox-cli.jar commands --docs --output docs/command-catalog.md
java -jar cli/build/libs/datapack-sandbox-cli.jar commands --check docs/command-support.md
java -jar cli/build/libs/datapack-sandbox-cli.jar commands --check docs/command-support.zh-CN.md
java -jar cli/build/libs/datapack-sandbox-cli.jar commands --json --version 26.2
java -jar cli/build/libs/datapack-sandbox-cli.jar commands --json --output build/command-catalog.json --version 26.2List resource support and behavior levels:
java -jar cli/build/libs/datapack-sandbox-cli.jar resources
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --docs
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --docs --output docs/resource-catalog.md
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --check docs/resource-formats.md
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --docs --locale zh-CN --check docs/resource-formats.zh-CN.md
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --json --output build/resource-catalog.json
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --registry --registry-group damage_types --json --output build/profile-registry.json
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --pack ./my_pack --type function --namespace demo
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --pack ./my_pack --id demo:main --source-pack ./my_pack --active-only
java -jar cli/build/libs/datapack-sandbox-cli.jar resources --pack ./my_pack --json --output build/loaded-resources.jsonThe REPL supports TAB completion, live multi-line suggestions while typing, history suggestions, colored output, Ctrl+C exit, runtime datapack reloads, trace/diff helpers, rerun-last, reset-world, and fixture loading:
java -jar cli/build/libs/datapack-sandbox-cli.jar repl --pack ./my_pack --watch
dps> reload
dps> function demo:main
dps> trace on
dps> diff last
dps> inspect raw damage_type
dps> inspect resources damage_type
dps> inspect outputsRun a quick smoke test:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --pack ./my_pack --load --ticks 20 --function demo:main --snapshotFor debugging function call chains or command-generator output, enable structured trace output:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --pack ./my_pack --function demo:main --trace --trace-file trace.jsonlWrite observable command outputs as JSONL for CI artifacts:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --pack ./my_pack --function demo:main --outputs-file outputs.jsonlWrite a combined JSON report with assertion failures, outputs, traces, event traces, the final snapshot, snapshot diffs, and resource summary details:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --pack ./my_pack --function demo:main --report-file run-report.jsonUse --trace-filter root=scoreboard, --trace-filter score=#clock,
--trace-filter output=generated ok, --trace-filter output-payload=id=demo:ruin,
or --trace-filter selector=Steve to keep only the relevant trace events in both
console output and JSONL files. Trace entries include per-command outputEvents
and snapshotDiffs, so filters can target output text, structured output
payloads, output targets, state paths, and command text.
To inspect state changes before and after a run, print a snapshot diff:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --pack ./my_pack --function demo:main --snapshot-diffCompare two raw JSON snapshots, or extract snapshot from run/check reports,
when building an external differential workflow:
java -jar cli/build/libs/datapack-sandbox-cli.jar diff --script --output external-replay.mcfunction cases/demo.dps.json
java -jar cli/build/libs/datapack-sandbox-cli.jar diff expected-snapshot.json actual-snapshot.json
java -jar cli/build/libs/datapack-sandbox-cli.jar diff --snapshot --check vanilla-report.json sandbox-report.json
java -jar cli/build/libs/datapack-sandbox-cli.jar diff --json --output build/snapshot-diff.json expected-snapshot.json actual-snapshot.jsondiff --script exports manifest command, commands, functionText,
mcfunction, load, and ticks steps into a replayable command script, while
sandbox-only event, fixture, trace, snapshot, and reset steps are preserved as
comments for external harnesses.
Run the built-in benchmark smoke profile, or scale it up and write JSON for CI artifacts:
java -jar cli/build/libs/datapack-sandbox-cli.jar benchmark --version 26.2 --scale 100
java -jar cli/build/libs/datapack-sandbox-cli.jar benchmark --pack ./my_pack --loot-table demo:gift --scale 1000 --json --output build/benchmark.jsonRun a single .mcfunction file without creating a full datapack:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 --mcfunction ./scratch/test.mcfunction --snapshotRun inline .mcfunction text directly:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 --mcfunction-text "say hello from inline"Run one or more raw command files from a generator:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 \
--command-file ./generated/setup.commands \
--command-file ./generated/body.commands \
--assert "output:generated ok"Read .mcfunction text from standard input:
printf 'scoreboard objectives add runs dummy\nscoreboard players set #stdin runs 1\n' \
| java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 --stdin --snapshotLoad multiple lightweight functions together. Use id=path or id=text when a
function must be callable by another function:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 \
--mcfunction-id demo:main \
--mcfunction demo:main=./scratch/main.mcfunction \
--mcfunction demo:helper=./scratch/helper.mcfunction \
--mcfunction-text "demo:inline=scoreboard players add #clock ticks 1"Add one or more folder/zip datapacks as dependencies for lightweight functions:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 \
--pack ./deps/library_pack \
--pack ./deps/items.zip \
--mcfunction-id demo:main \
--mcfunction-text "demo:main=function library:setup"For quick pack-level sanity checks, run can use the same direct missing
resource validation as manifest checks, and --resources prints the resource
summary without requiring a full manifest:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --pack ./my_pack --resources --fail-on-missing-resourcesFor CI safety, both run and check can cap execution with
--max-commands, --max-function-depth, --max-ticks-per-run,
--max-output-events, and --max-snapshot-bytes.
Run JSON check manifests:
java -jar cli/build/libs/datapack-sandbox-cli.jar check ./sandbox-cases
java -jar cli/build/libs/datapack-sandbox-cli.jar check ./sandbox-cases --validate-schema
java -jar cli/build/libs/datapack-sandbox-cli.jar check ./sandbox-cases --fail-on-missing-resourcesManifests can compare the final snapshot, or a selected snapshot path, with a
committed golden JSON file using {"snapshot":{"equalsFile":"expected.json"}}.
Export the bundled manifest JSON Schema for editors or CI tooling:
java -jar cli/build/libs/datapack-sandbox-cli.jar schema --output dps-manifest.schema.json
java -jar cli/build/libs/datapack-sandbox-cli.jar schema --check docs/dps-manifest.schema.jsonUse --verbose to include deterministic resource summaries, pack overlay
entries, missing load/tick function references, and manifest output events:
java -jar cli/build/libs/datapack-sandbox-cli.jar check ./sandbox-cases --verboseWhen assertions fail, print the final snapshot or a minimal snapshot diff:
java -jar cli/build/libs/datapack-sandbox-cli.jar check ./sandbox-cases --snapshot-on-fail --snapshot-diff-on-failFor CI artifacts, check can also write command traces:
java -jar cli/build/libs/datapack-sandbox-cli.jar check ./sandbox-cases --trace-filter root=scoreboard --trace-file check-trace.jsonl --outputs-file check-outputs.jsonl --report-file check-report.jsonFor ad hoc checks without a full manifest, run can apply a manifest-style
world fixture, override its seed, and evaluate one or more inline JSON
assertions. The seed is visible to the seed command and default random
sequences:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 \
--world ./fixture-world.json \
--seed 42 \
--assert '{"world":{"seed":42}}' \
--assert '{"score":{"target":"#fixture","objective":"ready","equals":1}}' \
--assert-file ./assertions.jsonFor quick score, storage, advancement, player, item, entity-count, diff, trace, trace-output, warning, unsupported warning, output, and normalized output checks,
--assert also accepts compact shorthands. --assert-file can contain JSON
assertions or one shorthand per non-empty, non-comment line:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 \
--command "scoreboard objectives add runs dummy" \
--command "scoreboard players set #fixture runs 1" \
--command "data merge storage demo:env {ready:true}" \
--command "give Steve minecraft:stick 3" \
--command "summon minecraft:pig 0 0 0 {Tags:[\"fixture\"]}" \
--command "place structure demo:ruin 1 64 2" \
--command "tellraw Steve {\"text\":\"styled output\",\"color\":\"yellow\",\"bold\":true}" \
--command "say generated ok" \
--assert "score:#fixture:runs=1" \
--assert "storage:demo:env:ready=true" \
--assert "player:Steve?" \
--assert "advancement:Steve:demo:use_carrot:criterion=use_carrot" \
--assert "predicate:demo:has_carrot:player=Steve" \
--assert "loot:demo:gift:context=minecraft:advancement_reward:player=Steve:seed=42:count=1:item=minecraft:diamond" \
--assert "random-sequence:demo:seq=42" \
--assert "item:Steve:minecraft:stick=3" \
--assert "entity:minecraft:pig@fixture=1" \
--assert "diff:/scores/runs=added" \
--assert "trace:scoreboard=2" \
--assert "trace-output:generated ok@Steve" \
--assert "output:generated ok" \
--assert "output-normalized:generated ok" \
--assert "output-segment:styled output|color=yellow|bold=true@Steve" \
--assert "output-payload:place structure:id=demo:ruin"Player events can be injected in the same quick run flow and checked with
regular assertions; use --event-file for one event per line from generator
output:
java -jar cli/build/libs/datapack-sandbox-cli.jar run --version 26.2 \
--event "player Steve key_input key.jump release" \
--event "player Steve block_placed minecraft:stone 1 64 2" \
--event-trace-file ./event-trace.jsonl \
--assert '{"player":{"name":"Steve","lastInput":{"device":"keyboard","code":"key.jump","action":"release"}}}' \
--assert '{"block":{"pos":[1,64,2],"id":"minecraft:stone"}}' \
--assert "event-trace:Steve:key_input=1" \
--assert '{"eventTrace":{"player":"Steve","type":"block_placed","block":"minecraft:stone","blockX":1,"blockY":64,"blockZ":2,"count":1}}'World fixtures can reference reusable fixture files with fixture, fixtures,
or extends; referenced files are applied first, then local fields override the
shared setup.
Manifest steps can also contain commands, functionText, or a relative
mcfunction path for command-generator output tests; assertions can check
world, team, bossbar, trace, whitespace-normalized output, and player
inventory item results.
For a reusable generator-output harness, start from
examples/generator-template/generator-template.dps.json. It runs strict
validation, loads a dependency pack and a reusable world fixture, then checks
raw command arrays, inline function text, a generated .mcfunction, structured
outputs, trace counts, storage, items, blocks, entities, and world state.
Manifest files use the .dps.json suffix:
{
"version": "26.2",
"unsupported": "warn",
"packs": ["./packs/counter"],
"world": {
"difficulty": "normal",
"blocks": [
{ "pos": [0, 64, 0], "id": "minecraft:chest", "nbt": { "Items": [] } }
],
"entities": [
{ "type": "minecraft:pig", "pos": [1, 64, 0], "tags": ["fixture"] }
],
"players": [
{ "name": "Alex", "position": [2, 65, 3], "xp": 5 }
],
"teams": [
{ "name": "red", "members": ["Alex"], "options": { "color": "red" } }
],
"bossbars": [
{ "id": "demo:bar", "name": "Demo", "value": 3, "max": 10 }
],
"scores": [
{ "target": "#fixture", "objective": "ready", "value": 1 }
],
"storage": {
"demo:env": { "ready": true }
}
},
"steps": [
{ "load": true },
{ "ticks": 20 },
{ "function": "demo:main" }
],
"assertions": [
{
"score": {
"target": "#clock",
"objective": "ticks",
"equals": 20
}
},
{
"world": {
"difficulty": "normal"
}
},
{
"output": {
"command": "tellraw",
"channel": "chat",
"target": "Steve",
"contains": "reward",
"segment": {
"color": "yellow"
},
"count": 1
}
}
]
}The same manifest can also run across multiple version profiles:
{
"versions": ["1.20.4", "26.1.2", "26.2"],
"packs": {
"1.20.4": ["./packs/demo-1_20_4"],
"26.1.2": ["./packs/demo-26_1_2"],
"26.2": ["./packs/demo-26_2"]
},
"steps": [
{ "load": true }
],
"assertions": [
{
"score": {
"target": "#clock",
"objective": "ticks",
"equals": 0
}
}
]
}This is not an embedded vanilla server. It implements a deterministic runtime for datapack-visible logic:
- functions, load/tick tags, and scheduled functions
- scoreboard, storage, gamerules, time, weather, bossbars, world border, forced chunks, biome overrides, and world/player spawn state
- sparse void worlds, explicit block/entity/player fixtures, and selected Java Anvil save imports
- readable player NBT, writable non-player entity and block entity NBT validated against generated vanilla mcdoc schemas
- selectors including
@s,@a,@p,@e,@n - predicates, loot tables, advancements, player events, and keyboard/mouse input events
- raw JSON registry resources such as recipes, item modifiers, damage types, dimensions, worldgen JSON, enchantments, trim resources, and variants
- observable output commands such as
tellraw,title,say,msg,playsound,stopsound, andparticle - configurable unsupported-command policy:
warnby default,errorfor strict validation,ignorefor silent skipping;run --strictandcheck --strictalso fail missing resource references during generator-output checks
The sandbox does not simulate networking, client UI, permissions, chunk
generation, redstone, entity AI, full combat, physics, or the vanilla server
threading model. Entities do not tick AI by default, but the runtime does not
write NoAI:1b unless test data does so explicitly.
Run all example manifests:
java -jar cli/build/libs/datapack-sandbox-cli.jar check examplesThe examples/ directory covers full-stack datapack events, a player-event
matrix, single-function scratch tests, command-generator output, a reusable
generator-output template, golden snapshot assertions, and multi-version
manifests.
Run the shortest example for each common workflow:
java -jar cli/build/libs/datapack-sandbox-cli.jar check examples/full-stack/full-stack.dps.json
java -jar cli/build/libs/datapack-sandbox-cli.jar check examples/player-events/player-events.dps.json
java -jar cli/build/libs/datapack-sandbox-cli.jar check examples/single-function/single-function.dps.json
java -jar cli/build/libs/datapack-sandbox-cli.jar check examples/generator-output/generator-output.dps.json
java -jar cli/build/libs/datapack-sandbox-cli.jar check examples/generator-template/generator-template.dps.json
java -jar cli/build/libs/datapack-sandbox-cli.jar check examples/multi-version/multi-version.dps.jsonGenerate a loot table:
java -jar cli/build/libs/datapack-sandbox-cli.jar loot --pack examples/full-stack/pack --table demo:gift --context minecraft:advancement_reward --seed 42Trigger a player event:
java -jar cli/build/libs/datapack-sandbox-cli.jar event --pack examples/full-stack/pack player Steve item-used minecraft:carrot_on_a_stick
java -jar cli/build/libs/datapack-sandbox-cli.jar event --pack examples/full-stack/pack player Steve block-placed minecraft:stone 1 64 2Inspect support boundaries in:
docs/command-support.mddocs/code-test-api.mddocs/resource-formats.mddocs/player-events.mddocs/version-profile.mddocs/runtime-world.md