Kore Gotchas¶
Things that cost time during the rewrite, with the symptom first so you can find them.
Item components in JSON¶
Symptom: generation throws while writing a recipe:
SerializationException: No tag in stack for requested element, or
NbtEncodingException: … Expected Encoder to be NbtEncoder, got … JsonTreeEncoder.
Cause: in Kore 2.28, the typed itemName(...), customData { } and entityData { }
components serialize only to SNBT. They work in give and summon, and throw inside a recipe
result, which is JSON.
Fix: set them as Kore's CustomComponent, which holds raw NBT and writes both formats:
this["item_name"] = CustomComponent(NbtString(name))
this["custom_data"] = CustomComponent(nbt { this["ra"] = nbt { this[id] = true } })
this["entity_data"] = CustomComponent(entityDataNbt())
CustomBlock.blockItemComponents() and ItemSpec.applyTo() already do. Keys go
without the minecraft: prefix, or the same scope writes some components prefixed and some
not.
Generated types live only in the jar¶
Symptom: Unresolved reference 'RecipeArgument' (or AdvancementArgument,
PredicateArgument, Items, Blocks, EntityTypes, Tags…) and grepping the Kore repository
finds no declaration.
Cause: these are generated from the game data at Kore's build time and only exist in the published jar.
Fix: import them from io.github.ayfri.kore.generated.*
(io.github.ayfri.kore.generated.arguments.types.RecipeArgument). To find a package:
unzip -l ~/.gradle/caches/modules-2/files-2.1/io.github.ayfri.kore/kore-jvm/2.28.0-26.3/*/kore-jvm-2.28.0-26.3.jar | grep RecipeArgument
lib/ and build in .gitignore¶
Symptom: a fresh clone does not compile: Unresolved reference 'RaLibPart', or a
multiblocklib/build/ class is missing. Locally everything works.
Cause: the repository .gitignore ignores lib/ (Python virtualenvs), which also matches the
Kotlin package ra/lib/. The Kore project's own .gitignore used to ignore a bare build, which
also matched ra/multiblocklib/build/. The files existed on disk and were never committed.
Fix (in place): the root .gitignore un-ignores
!redstone_additions/src/main/kotlin/**/lib/, and redstone_additions/.gitignore
anchors its entries (/build, /out, /.gradle). If you add a package named after a common
build or cache folder, run git status --ignored before you commit.
Selector arguments¶
- Kore writes selector arguments sorted (
@e[distance=..0.5,limit=1,sort=nearest,tag=…,type=…]), whatever order you set them in. Equivalent in game;compare_reference.pysorts both sides. - Setting a filter twice appends:
tag = A; tag = Bwritestag=A,tag=B. That is how to require two tags. It is also how to add a filter by accident. - Kore prefixes ids (
type=minecraft:marker), also equivalent.
execute { run { … } } with several commands¶
Kore moves a multi-command run { } into its own function under generated_scopes/, named
after a hash of its body, in the pack's default namespace (redstone_additions), which is
not one of the pack's namespaces. Nothing in the pack does this today, and it should stay that
way: those functions are invisible to PackValidator and impossible to find in game. When a
run needs several commands, define a real function with fn(...) and call it.
Commands Kore spells differently¶
| You write | Kore writes | Same in game? |
|---|---|---|
schedule(tick).replace(1.ticks) |
schedule function ra:tick 1 replace |
yes: a unit-less time is ticks, replace is the default |
tp(self(), vec3()) |
teleport @s ~ ~ ~ |
yes |
fill(from, to, block, filter) |
fill … <block> replace <filter> |
— this is the replace form; there is no FillOption.REPLACE |
vec3(-1, -1, -1).relative |
~-1 ~-1 ~-1 |
— |
a Double in nbt { } |
0.45d |
yes |
a Boolean in nbt { } |
1b / 0b |
yes; SNBT true / false are bytes |
SNBT booleans when parsing¶
knbt's SNBT parser does not accept true / false, which the game reads as 1b / 0b.
ra.core.item.parseSnbt rewrites them before parsing; do the same if you parse game-style SNBT.
Gradle skips generation¶
Symptom: you expect the validator's warnings and see nothing.
Cause: koreBuild is up to date, so Gradle did not run it.
Fix: ./gradlew koreBuild --rerun. Also note that generation reads only the runtime classpath;
a file the generator reads from disk must be declared in kore { additionalInputs.from(...) }
to trigger a rebuild.
Macro lines need a variable¶
A line starting with $ must contain at least one $(name). One that does not raises
"No variables in macro" at load and Minecraft rejects the whole function, not just the
line — how the Big Torch once shipped without its torch (the macro's only variable was replaced
by a constant and the leftover $ took the function down). PackValidator fails generation on
such a line in strict and report mode alike, naming the function and line. Fix it by dropping
the $ (the line no longer needs to be a macro) or by putting the variable back.
Every macro variable must be in the compound¶
function f with storage s path fails as a whole when f names any $(name) that the compound
at path lacks — even on a line an execute if would have skipped. Nothing is logged and no
command in f runs. When one function serves compounds of different shapes (settings rows of
several types, say), dispatch to one macro per shape and let each name only fields its shape
always has. v6.0.0's ra_settings:act/user read $(min)/$(max)/$(step) for its int
branch, so every bool row's click failed and personal toggles never flipped.
Calls through macros are unchecked¶
PackValidator checks every function ns:path and function #ns:tag in the generated
functions, every entry of the pack's function tags (unless marked required: false) and every
advancement reward function. It skips any path containing a macro
(function ra_jetpacks:items/give_$(kit)_kit), since only the game can resolve those. A typo
there is caught in game only. Keep macro-built paths to the cases that need them, and list their
possible values in a comment.
Don't load two builds at once¶
An old beet release (v5.1.17 and earlier) and the Kore build write the same namespaces. With
both in a world, every function exists twice, the later pack wins file by file, and ra:tick is
scheduled by both. Remove the old pack (redstone_additions_data_pack, or a release zip named redstone_additions_v5.x…) before testing the Kore build.
A module property named like a registry list resolves to the registry¶
Inside fun Ra.register() declared in a module object, an unqualified name is looked up on the
extension receiver (Ra) before the enclosing object. A module's own
private val blocks = listOf(...) was therefore silently replaced by the registry's list of
every block registered so far, and ra_gates:tick / load / give_all ran every earlier
module's blocks a second time. It compiled because both are lists of CustomBlock.
The registry list is now Ra.allBlocks. Still: give module-level collections specific names
(gateBlocks), or declare them as locals inside register(), where they shadow correctly.
set string accepts a number¶
data modify storage s p set string storage s v succeeds when v is a number too: it writes the
number's digits as a string. It fails only for a list or a compound. Used as "is v a string?",
it called every number a string — which is how the Data Handler gave numeric properties a text
editor until 6.0.1. To tell types apart, use a test only that type passes:
- number: two
/computes of a storage provider with different fallbacks,compute default integer {type:"minecraft:storage",storage:"s",path:"v",fallback:0}and the same withfallback:1. The provider returns its fallback when the value is absent or not a number (a string, even"5"), so the results agree exactly whenvis a number. Two fallbacks, not one "impossible" sentinel: every int is a possible value. - list:
data modify storage s v insert 0 value 0bsucceeds only on a list (copy it first).if data storage s v[]is no test: an empty list has no element to match. - compound:
execute if data storage s v{}. - byte flag:
execute if data storage s {v:1b}— an NBT match is typed, so the int1fails it.
If you need "is it a string?" without those, set string and then set from over the copy: the
second changes nothing only when the value was a string already (DataHandlerList.isText).
Kore's compute { } can hoist a provider out of a macro line¶
Kore's own compute { } command may move an inline context int provider into a registry file
and reference it by id. A provider whose storage path holds $(id) then loses its macro
substitution. Build /compute lines with ra/core/Compute.kt (computeScore, computeInto,
computeStorage), which render the provider as SNBT in the command itself and add the $
prefix when the line contains $(. See
Arithmetic with /compute.
/compute cancels where a scoreboard operation wraps¶
A scoreboard *= that leaves 32 bits wraps round; the same product in /compute cancels the
whole evaluation, and so does a division by zero. Under execute store result the target gets
0, and data modify … set compute writes nothing — no error in the log either way. The
bridge fill ratio (amount * 10000 / capacity) read 0 on any network above ~214,748 mL for
this reason until it moved to a float provider (computeScoreScaled). Guard divisors, and
divide in floats when a scaled product can grow large.
Also: scoreboard /= and %= round towards negative infinity. Their /compute equivalents are
floor_div and floor_mod (Kore floorDiv, floorMod), not div and mod.
A property pasted into a macro is part of the command¶
$execute as $(entity_selector) run tag @s add $(tag) does not receive a selector: it receives
text, and the game parses the whole line after substituting it. A player who can set
entity_selector to @a[limit=1] if function <any> run say makes the block run any function,
with the server's permission; "} in a value pasted inside quotes ends the string. Until 6.0.1
the sensors, the Message Block, the Boxer/Unboxer, the vaults and the Teleport Anchor all did
this with values any Data Handler could write.
Declare such a property with its text format
(text("entity_selector", TextFormat.SELECTOR)) and call guardProperty(name, format)
(ra/lib/safe/SafeText.kt) on the marker before the macro call. Better still, do not paste it
at all: compare two strings with data modify storage s a set from storage s b (success 0 means
equal), as the Teleport Anchor and the Wireless Receiver do.