Getting Started¶
Prerequisites¶
| Tool | Version | Why |
|---|---|---|
| JDK | 25 | build.gradle.kts sets jvmToolchain(25), and the Kore plugin's AOT cache needs 25+. |
| Python | 3.10+ | Only for tools/compare_reference.py. Not needed to build. |
Nothing else needs installing. The Gradle wrapper (gradlew, gradle/wrapper/) downloads
Gradle, and Gradle downloads Kotlin, Kore and the Kore Gradle plugin, at the versions pinned in
gradle/libs.versions.toml:
[versions]
kore = "2.28.0-26.3" # Kore 2.28.0, targeting Minecraft 26.3
kotlin = "2.4.20"
Building¶
cd redstone_additions
./gradlew koreBuild
| Output | Where |
|---|---|
| The pack | build/kore/redstone_additions/ (pack.mcmeta, pack.png, data/) |
| AOT cache | build/kore-aot/ (speeds up later generations; safe to delete) |
koreBuild compiles the Kotlin, runs ra.MainKt with the output directory in the kore.output
system property, and wipes the output folder first (cleanBeforeBuild), so a function removed
from the Kotlin also disappears from the pack.
Gradle skips koreBuild when nothing it tracks changed. To force a fresh generation, for
example to see the validator's output again:
./gradlew koreBuild --rerun
Strict mode¶
Every generation ends with PackValidator (ra/core/PackValidator.kt). It reads every line of
every generated function and checks that each function ns:path and function #ns:path into
one of the pack's namespaces points at a function or function tag the pack defines. Calls whose
path contains a macro (give_$(kit)_kit) are skipped, since only the game can resolve them.
| Mode | Command | On an unresolved call |
|---|---|---|
| strict (default) | ./gradlew koreBuild |
generation fails, listing every caller and missing target |
| report | ./gradlew koreBuild -Pra.strict=false |
prints [ra] N unresolved call(s), strict mode off: and the list, then writes the pack |
-Pra.strict reaches the generator as the ra.strict system property (see build.gradle.kts).
Report mode is for work in progress: a half-written part can still be generated and compared.
A release is always built strict.
Putting it in a world¶
./gradlew koreRun -Pworld="Redstone Test" # a world under <minecraft dir>/saves
./gradlew koreRun -Pworld="/path/to/saves/My World"
./gradlew koreRun -Pworld # ra.world from local.properties
./gradlew koreRun -Pworld --continuous # rebuild and reinstall on every save
Then /reload in game. Without -Pworld, koreRun builds but installs nowhere. The full
reference (the untracked local.properties, launcher instances, RCON, removing the pack again)
is Installing into a world.
Never load the old beet release and the Kore build together
Both write the same namespaces. With both enabled every function exists twice, the pack
that loads last wins file by file, and ra:tick is scheduled by both: you get a mix of two
versions running at once. Remove the old pack from the world first (redstone_additions_data_pack, or a release zip named redstone_additions_v5.x…).
Comparing against a reference pack¶
tools/compare_reference.py (repository root) diffs the generated pack against another data
pack folder, given with --reference: typically the last beet release, v5.1.17, unzipped
somewhere outside the repository. The beet source tree is no longer in the repository, so the
reference always comes from a release. The tool reads the reference's data/ with every
overlay_*/data/ it has laid over it in name order, which is what 26.3 loads. The generated
pack is read from redstone_additions/build/kore/redstone_additions/ (--kore to change it).
(cd redstone_additions && ./gradlew koreBuild)
REF=~/packs/redstone_additions_v5.1.17 # an unzipped release
python3 tools/compare_reference.py --reference $REF ra_gates # summary for one namespace
python3 tools/compare_reference.py --reference $REF ra_gates ra_sensors # several
python3 tools/compare_reference.py --reference $REF ra_gates --diff # plus a unified diff per mismatch
python3 tools/compare_reference.py --reference $REF --all
python3 tools/compare_reference.py --reference $REF ra_gates --strict # no normalisation (see below)
Output:
reference files: 56 kore files: 61
identical: 49 different: 7 missing: 0 extra: 5
EXTRA ra_gates/function/blocks/clock/on_break.mcfunction
DIFFERS ra_gates/function/blocks/clock/tick.mcfunction
It exits 0 only when nothing is missing, extra or different.
What it ignores¶
The comparison removes differences that never change what the game does. --strict turns all
of them off except the first two.
| Normalisation | Example |
|---|---|
| Comment and blank lines are dropped | # Tick all clocks |
| Runs of whitespace collapse to one space | |
minecraft: is removed everywhere (commands, JSON values, JSON keys) |
minecraft:iron_block = iron_block |
| Selector arguments are sorted | @e[type=marker,tag=x] = @e[tag=x,type=marker] |
| Quotes around a bare SNBT word are removed | {block_tag:"clock"} = {block_tag:clock} |
The d suffix on a double is removed |
y:0.45d = y:0.45 |
| SNBT booleans become bytes | italic:false = italic:0b |
| A one-element list of ids equals the bare id (JSON) | "items": ["x"] = "items": "x" |
"replace": false on tags and "frame": "task" on advancements are dropped (JSON) |
the game's own defaults |
A call to a generated_scopes/ function is expanded back into its lines, each prefixed with the calling execute … run |
Kore moving a multi-command run { } into its own function |
What is left after that is a real difference. The Porting Guide explains how to read it.
IDE¶
Open redstone_additions/ in IntelliJ IDEA and let it import the Gradle project. Running
ra.MainKt directly works: without the plugin's kore.output property, Main.kt writes to
out/ in the working directory, and RedstoneAdditions.VERSION reads version from
gradle.properties in that same directory (see Release and Versioning).