2.15.0
MC 26.2ReleaseLatestA feature release on top of 26.2, built around three things: in-game UI helpers, a correctness sweep of everything Kore writes, and reproducible packs. Menus built from dialogs, server-style sidebars and runtime score vectors join the helpers, trial spawner configurations and multi-noise parameter lists join the data-driven features, and every file the game rejected in 26.2 now loads.
Two builds of the same code now produce the same pack: generated functions, event handlers, UUIDs and structure set salts are derived from their content instead of identity hash codes or random numbers. Cold starts got a lot cheaper along the way, the first tag of a pack dropping from 101ms to 3ms and the first advancement trigger from 61ms to 4ms.
A few APIs changed behavior or name, mostly because the old one let you write something wrong without noticing. The Migration notes list each one with the code to change. The website also got a new homepage, a Features page and a redesigned Updates timeline.
What's new?
- Menus: in-game GUIs built from dialogs, with buttons running Kotlin-declared commands for every player (operator or not), sub-pages, links, the pause screen and the Smithed data pack menu.
- Sidebars:
SidebarreplacesScoreboardDisplay, with right-aligned values, live scores, conditional lines and arefresh()rewriting only the lines that can change. - Score vectors: runtime 3D vectors on scoreboards, reading positions, motion and look directions, with dot and cross products, exact length, normalize, teleports and motion. The scoreboard math engine behind them is rewritten around shared functions.
- Trial spawners:
trialSpawnerconfigurations with spawn potentials, equipment, custom spawn rules and reward loot tables. - Multi-noise parameter lists: give a vanilla biome layout its own id and point any
multiNoisebiome source at it. - Execute and selectors:
inverted { }flips the checks of a condition block, the selectortypeaccepts entity type tags, and thekickcommand is in. - Reproducible packs: every generated name is a hash of its content,
hashedGeneratedFunctiongives your own builders the same naming, andDataPack.entityUUIDgives build-stable UUIDs. - Gradle plugin AOT cache:
koreBuildruns your entry point on a JDK AOT cache trained by its first run.
Menus
A dialog button only runs commands with the clicking player's permissions, and /function needs operator rights. So each button runs trigger <namespace>.menu.<name> set <n> instead, and a generated tick function runs the matching button function for whoever clicked. A menu is a dialog like any other, so it opens from a chat link too:
smithed = true lists the menu behind the shared "Data Packs..." pause screen button of the Smithed convention, next to every other compatible pack.
Sidebars
Score numbers are always hidden, values are drawn with numberformat fixed. refresh() only rewrites the lines holding score, selector or NBT components, so calling it every tick stays cheap. Lines can also be conditional or swapped at runtime:
Score vectors
Components are fixed-point, 1500 meaning 1.5 blocks at the default scale. length, distanceTo and normalize are exact to the float: the length comes from the game's own decomposition of a text_display transformation, and normalize teleports a marker one block along facing, instead of approximating a square root on scoreboards.
Trial spawners and parameter lists
Conditions, selectors and commands
inverted { } negates a reusable condition block without rewriting it, and Entity.isPlayer now matches EntityTypes.PLAYER, which it never did.
Reproducible packs
Generated functions are named generated_<hash> after their body, and so are load_<hash>, tick_<hash>, schedule_<hash>, events, cooldowns, timers, game states and scheduler wrappers. The same body always gets the same name, on every run and platform, and two builders producing the same body share one function. entityUUID hashes the pack name, the key and how many times the key was asked for, so two markers get two UUIDs and a rebuild gives both the same ones again.
Performance
- First tag of a pack: 101ms → 3ms, through a generated
wheninstead of a map of 148 lambdas. - First advancement trigger: 61ms → 4ms and 186 → 9 loaded classes, sealed families now loading only the serialized subtype (particles 18ms → 9ms, actions 16ms → 9ms).
- First selector: 163ms → 53ms, each render ~1.3µs → ~0.5µs, rendered directly instead of through an escaped JSON tree.
- First text component: 76ms → 24ms.
- Pruning unreferenced generated functions: 1099ms → 4.5ms on a 1,750 function pack, registering them 178ms → 3ms.
- Disabled
Optimization(): 9.3ms → 1.2ms. - File writes: ~7 fewer filesystem calls per file (a real pack's warm generation 100ms → 66ms), no byte-by-byte copy on OPFS and
fetch. - Kotlin/JS lazy serializers: 40ms → 12ms decoding 20,000 values.
- Gradle plugin: a small pack's generation ~1s → ~0.37s with the AOT cache.
- Artifact size:
kore9,706 → 8,387 classes, its JS library 24.4 MB → 23.4 MB.
Migration notes
Every breaking change of this cycle, with the code to change on your side.
ScoreboardDisplay becomes Sidebar
The old helper created and displayed itself where it was declared. A Sidebar is declared once, then created, refreshed or changed from any function.
copyFrom and copyTo copy the right way
Both were swapped: copyFrom wrote into the source and copyTo read from the target. Code written around the swap has to be flipped back.
ChatComponents.plus and the display maths return new values
+ used to append to its left side, so a shared prefix piled up across messages. It now returns new components, like the Quaternion and Matrix operators and conjugate of the display helpers.
bundlerContents becomes bundleContents
The old name wrote a minecraft:bundler_contents key the game doesn't know.
stopSound takes sound events
It took sound file paths, which /stopsound never matches.
test verify takes one test or a selector
Like vanilla, verify checks a single test or everything a test selector matches.
Loot entry functions and conditions need their own import
The loot table, item modifier and predicate builders are DSL-scoped now. An entry functions { } resolved without its import used to land on the enclosing pool silently, it now fails to compile.
Generated function paths and file collisions
Generated function names change once to their <prefix>_<hash> form, then stay stable. Update anything that references a generated path by hand. Two files claiming one path with different contents now throw instead of one being dropped:
Removed, with no replacement
io.github.ayfri.kore.helpers.displays.maths.Vec3f, a duplicate ofio.github.ayfri.kore.arguments.maths.Vec3f: change the import.Tag.tagClass, which was written on every typed tag but never read.String.unescape, which turned an escaped\ninton.
Kore is now built with Kotlin 2.4.20.
Changelog
New Features
feat(advancements): Add thelocationconditions to theanyBlockUseanddefaultBlockUsetriggers.3956b52feat(arguments): Accept entity type tags in the selectortypeoption, and fixEntity.isPlayerwhich never matchedEntityTypes.PLAYER.35280cafeat(arguments): AddhashedUUIDandDataPack.entityUUID, build-stable UUIDs unique per pack, key and occurrence.96cd1a4feat(chat-types): Addclick_event,hover_eventandshadow_colorto chat type styles.b65f397feat(commands): AddExecuteCondition.inverted { }and thekickcommand.eedb9439659fe0feat(gradle-plugin): Run the datapack entry point on a JDK AOT cache trained by the firstkoreBuild, with anaotCachetoggle.9d72f57feat(helpers): Add the dialog-basedMenuhelper with trigger-driven buttons, sub-pages, links, pause screen and Smithed data pack menu support.c06e26cfeat(helpers): AddnotEqualToandrunUnlessto state delegates.7c94ac0feat(helpers): Rewrite the scoreboard math engine around shared functions and add runtimeScoreVectors.c223d6cfeat(helpers)!: ReplaceScoreboardDisplaywith aSidebarhelper supporting right-aligned values, live refreshes and conditional lines.e3dc1d7feat(item-modifiers): Add asetName(ChatComponents)overload sotextComponent()results and joined components work.1f8173dfeat(trial-spawners): Add trial spawner configurations with spawn potentials, equipment, custom spawn rules and reward loot tables.f80c6fbfeat(worldgen): Add multi-noise biome source parameter lists and letmultiNoisereference them.103820c
Bug Fixes
fix(advancements): Type thekillMobNearSculkCatalystentity asEntityOrPredicateslike the other kill triggers.87d9e91fix(arguments, colors): FixVec2/Vec3maths, print numbers without scientific notation, floor negative block positions, render Int positions without decimals, widenFloats through their shortest form (0.2, not0.20000000298023224), fix theWEAPONandPLAYER.CRAFTINGslot ranges, lowercaseDisplaySlots.sidebarTeamcolors, roundARGB.fromRGBAArraychannels and stopColoraliases beingnulldepending on initialization order.ceeeee840e6546c596ee290084e4f6b649c1221b37c508f73fix(bindings): Parse GitHub, Modrinth and CurseForge responses with kotlinx.serialization instead of regexes that never matched real responses, pick the newest datapack file, URL-encode slugs and write cached files atomically.ca1e7f45868a9bfix(build): Expose kotlinx-io and kotlinx-serialization asapidependencies ofkore, sincePathandJsonare part of its public API.d5ac7d7fix(chat-components, components, nbt): Writeloreand book pages as text components and book titles as filterable strings,shadow_coloras an ARGB int, escape backslashes and line breaks in SNBT strings, makeChatComponents.toComponentstop overwriting the first component'sextra, and serialize user subclasses ofCustomComponent.19b0a19b8f0083bdb1da131be25bf86e24bfix(chat-components, helpers)!: MakeChatComponents.plus, theQuaternionandMatrixoperators andconjugatereturn new values instead of mutating their operands, and fixQuaternion.lengthandTransformation.interpolate/invert.4365a369746cf7fix(commands, functions)!: Keep namespaced ids infunction(name), truncate block positions in every/itemform, serialize the inlineLootSource.fishloot table, set the run target of namedexecute { run(name) { } }blocks, fill skippedplaySoundarguments with the game defaults, escape line breaks in commands, maketest verifytake one test or selector,stopSoundtake sound events andstopWatch(id)resolve insidedataPack { function { } }.b915276c06b6560a9dfaa0d34d2cfcba4dc3ed97d90879c909659fe0ce54861fix(components)!: Rename thebundlerContentsmatcher tobundleContents, which writes theminecraft:bundle_contentskey the game knows.5bff149fix(datapack)!: Throw when two files claim one path with different contents, write thestartDebug()markers again, run the enabled optimization passes inexportAsStrings()likegenerate(), and route every log through a newKoreLogger.5c5b99e261b80bfix(files, generation): Fix merging with other packs replacing theticktag and crashing on.zippacks, reject zip entries escaping the extraction folder, close every file stream and create temporary directories with a random suffix.a7b9459aef89b6fix(functions, helpers, oop, worldgen)!: Make packs reproducible, deriving generated function, event, cooldown, timer, game state, scheduler and slot listener names, display and mannequin UUIDs and default structure set salts from content, and fix two multi-linereturnRunsharing a file and thedynamicRunCommandandInventoryManagerdouble generation.c24eceb336ecc4fc6f4091a600e378e6f06fix(gradle-plugin): Reject pack names that would make linking orkoreCleanhit a whole target folder, and refuse to wipe an output directory containing the project.0278c22fix(helpers): Write display entity brightness assky,billboardModeasbillboardand text display text as a component.f332d72fix(helpers, oop): Keep the once-per-pack setup of cooldowns, timers, game states, events, dynamic strings, math and raycasts in aDataPack.statestore instead of global maps, number inventory managers per pack and makeremoveClickDetectorsremove the right objectives.ca4dc9f43da27ffix(loot-tables)!: Add a@LootDslmarker on the loot table, item modifier and predicate builders so an unimported entryfunctionsorconditionsfails to compile instead of landing on the enclosing pool.bc094e9fix(oop)!: Copy into the receiver inScoreboardEntity.copyFromand into the target incopyTo, both were swapped.da681a7fix(recipes): Write the cooking time ascookingtime, and add anamespaceparameter to every recipe builder and acategoryto shaped, shapeless and cooking recipes.05e2ff5fix(sulfur-cube-archetype, trim-materials, trims, variants, worldgen): Write the fields 26.2 requires, the mob variantbaby_*assets and wolfstep_sound, bare trim palette names withoverride_armor_assets, the dimension typehas_ender_dragon_fight, the sulfur cubesound_settingsand trim patternasset_ids as ids.b366a7f66d29036ba599a96d2d8c0d77152fix(tags): Add to the tag already registered under the same name instead of registering a second one the generator dropped, which lost everyload,tick,setTagand event handler entry after the first.e05b105
Performance improvements
perf(bindings, generation, optimization): Create each output directory once per generation, index every namespaced id once and prune unreferenced generated functions through a worklist, hand OPFS andfetchtheInt8Arraya Kotlin/JSByteArrayalready is, and drop the unused serializer annotation of ~1,300 generated enums.04604d9adb96675479c189ec4c892bf85a5perf(chat-components, optimization, selectors, serializers, tags): Cut the cold-start cost of the first command, creating tag arguments through a generatedwheninstead of 148 lambdas, serializing sealed families through a KSP-generatedSealedDispatcherthat loads only the serialized subtype, rendering selectors directly instead of through a JSON tree, building theChatComponentsdecoder lazily, creating the default optimization passes on first access and reading lazy values through.valueon Kotlin/JS.67fcde6c92ceb4bdee8ce2a7336e1891fba96e235787577fd
Refactors
refactor(helpers)!: Replace the duplicate displaysVec3fwith the core one.1005065refactor(zip): Inflate zip entries withjava.util.zipon the JVM, keep a bounds-checked pure-Kotlin inflater for JS, and read every zip inbindingsthrough Kore'sreadZipEntries, dropping the kompress dependency.c89290a
Documentation
docs: Check every example of the commands, concepts, data-driven, guides, helpers, OOP and advanced pages against the real API and output.053200d231ae5849453f26518fe091f7268d562f53483b9827e7856f93c6d7b4457318b3b26c9docs: Require JDK 25 in the getting started page and the GitHub Actions publishing workflows.946a0255e8c698docs(contributing): Point the contributor docs at thecommonMainandcommonTestsource sets, fixing 22 dead GitHub links, and drop the obsolete collision pitfalls section.67a68ccadd79c96a52babdocs(website): Add the Menus, Sidebars and Score Vectors pages and rewrite the Scoreboard Math one.a8569a809c162b
Full changelog: https://github.com/Ayfri/Kore/compare/v2.14.0-26.2..v2.15.0-26.2
