# website Full Documentation # Guides ## Why Kore - Kotlin DSL vs Raw Datapacks & Other Generators --- root: .components.layouts.MarkdownLayout title: Why Kore - Kotlin DSL vs Raw Datapacks & Other Generators nav-title: Why Kore description: Kore compared with raw datapacks, Sandstone and Beet. Type safety, autocomplete and one Kotlin language for JSON and MCFunction, plus when to skip Kore. keywords: kore vs sandstone, kore vs beet, datapack generator comparison, kotlin datapack dsl, minecraft datapack alternative, type-safe datapack, why kore, datapack generator benefits, mcfunction generator, minecraft json generator date-created: 2026-06-24 date-modified: 2026-08-05 routeOverride: /docs/guides/why-kore position: 0 --- # Why Kore Datapacks are powerful but painful to write by hand: stringly-typed commands, sprawling JSON, no autocomplete, and silent failures you only discover after `/reload`. Kore replaces all of that with a single, strongly-typed Kotlin DSL that generates the exact same vanilla output - just faster, safer, and without the boilerplate. This page is for **deciding**: what Kore gives you over raw datapacks, how it compares to other generators, and when it is the wrong tool. If you are already convinced, go to [Getting Started](/docs/getting-started) instead. ## The problem with hand-written datapacks A real datapack is a mix of two error-prone formats: - **`.mcfunction`** - plain text commands with no validation. A typo in a selector, a wrong block id, or a misremembered argument order fails silently or breaks at runtime. - **JSON** - loot tables, predicates, advancements, recipes, worldgen, dialogs. Deeply nested, verbose, and easy to get subtly wrong. No editor tells you a field is misspelled until the game refuses to load it. There is no autocomplete, no type checking, no refactoring, and no way to share logic except copy-paste. As a pack grows, this gets worse fast. ## What Kore changes ### One language, no JSON You write Kotlin. Kore generates the `.mcfunction` and JSON for you. You never hand-write a brace. ```kotlin dataPack("example") { function("hello") { tellraw(allPlayers(), textComponent("Hello World!")) } }.generateZip() ``` ### Everything is typed Every vanilla list - blocks, items, entities, effects, enchantments, biomes, sounds - is a generated enum. You cannot misspell `minecraft:diamond_sword`, because you write `Items.DIAMOND_SWORD` and the compiler checks it. The same applies to command arguments, NBT paths, and data-driven schemas. See [Arguments](/docs/contributing/arguments). ### Real code structure Because it is Kotlin, you get everything the language offers: extension functions to split a pack across files, loops and conditionals to generate repetitive content at build time (see [Runtime Logic](/docs/concepts/runtime-logic)), and refactoring/autocomplete from your IDE. Big projects stay maintainable. ### Tested, predictable output Kore is a **build-time generator**. There is no runtime, no plugin, no server mod. The output is exactly the vanilla datapack you would have written by hand - every public feature ships with tests asserting the exact generated JSON and command strings. What you generate is what loads. ### Up to date Kore tracks recent Minecraft versions closely, including newer systems like command macros, dialogs, timelines, and the current pack format. You get modern features with type safety on day one. ## How Kore compares to other generators Kore is not the only datapack generator. Here is an honest, broad-strokes comparison. (Verify specifics against each project before quoting - tooling moves fast.) | Tool | Language | Style | Type safety | Vanilla lists as symbols | |------------------------|-------------------|-------------------------------|------------------------------------|---------------------------------------------| | **Kore** | Kotlin | Type-safe builder DSL | Strong (no `any`, generated enums) | Yes (`Items.DIAMOND_SWORD`, `Blocks.STONE`) | | **Sandstone** | TypeScript | Functional TS API | Good (TS strict) | Partial | | **Beet** (+ **mecha**) | Python | Plugin / pipeline, decorators | Medium (type hints) | Via libraries | | **Raw datapack** | mcfunction + JSON | Hand-written | None | No (plain strings) | **Where Kore wins:** a single strongly-typed Kotlin DSL with generated registries, so vanilla ids are real symbols the compiler checks, plus exact-output tests shipped with every feature. The DSL mirrors Minecraft's own structure, so it reads naturally to anyone who knows datapacks. **Where others may fit better:** Beet has a very mature plugin ecosystem and `mecha`'s command compiler, and is a good fit if your team lives in Python. Sandstone is a strong choice for TypeScript-first teams. Kore's bet is that for anything beyond trivial, a typed compiler-checked DSL saves more time than it costs to learn. ## Choose Kore when - Your pack is more than a few functions, or you expect it to grow. - You want autocomplete, refactoring, and the compiler catching mistakes before `/reload`. - You already know Kotlin (or are happy to learn a small subset - see [Getting Started](/docs/getting-started)). - You want data-driven content (loot, recipes, advancements, worldgen, dialogs) without writing JSON. ## Skip Kore when Being honest about the cost matters more than winning the comparison: - **The pack is tiny or one-off.** Three functions and a loot table are not worth a Gradle project. - **Your collaborators will not touch a JVM toolchain.** Kore needs a JDK, Gradle, and a compile step between editing and `/reload` - slower than saving an `.mcfunction` and reloading. - **You need resource packs too.** Kore targets datapacks only, see [Known Issues](/docs/advanced/known-issues). - **You expect large runtime gains.** Kore improves authoring first: the generated commands run as fast as the ones you would have written. The opt-in [optimization passes](/docs/guides/optimization) shave some cost (shorter `execute` chains, conditions hoisted into selectors), but a slow algorithm stays slow. ## Next steps - [Getting Started](/docs/getting-started) - build your first datapack - [Runtime Logic](/docs/concepts/runtime-logic) - the one concept that makes everything click - [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore) - port a pack you already ship --- ## Creating a Minecraft Datapack with Kore - Metadata, Output & Packaging --- root: .components.layouts.MarkdownLayout title: Creating a Minecraft Datapack with Kore - Metadata, Output & Packaging nav-title: Creating a Datapack description: Shape a Kore datapack end to end - namespace, output folder, pack.mcmeta, overlays and filters, then generate a folder, ZIP or a mod JAR for Fabric or NeoForge. keywords: minecraft datapack, kore datapack, pack.mcmeta, pack format, datapack zip, datapack jar, fabric datapack mod, datapack overlays, datapack namespace, generate datapack date-created: 2024-02-26 date-modified: 2026-08-05 routeOverride: /docs/guides/creating-a-datapack position: 1 --- # Creating a Datapack Everything in Kore hangs off one object: the `DataPack`. It holds your namespace, your metadata, every function and data-driven resource you declare, and it decides what lands on disk. This page is about the **shell** around your gameplay code - naming, metadata, packaging - in the order you actually need it: 1. [Declaring the pack](#declaring-the-pack) and choosing where it is written. 2. [Pack metadata](#pack-metadata) - `pack.mcmeta`, pack formats, overlays, filters. 3. [Adding content](#adding-content) - the one-line version, with links to the real reference pages. 4. [Generating output](#generating-output) - folder, ZIP, mod JAR, and merging with other packs. For the gameplay code itself, see [Functions](/docs/commands/functions) and [Commands](/docs/commands/commands). If you already maintain large hand-written datapacks and want migration and architecture patterns instead of basics, read [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore). ## Declaring the pack `dataPack` builds the pack; `generate()` writes it. Nothing is written to disk until you call one of the generation functions. ```kotlin dataPack("my_datapack") { // datapack code here }.generate() ``` The name you pass does two things: it becomes the **output folder name** and the **default namespace** for every function and resource that does not set its own. ### Output folder `path` sets the base directory that generation writes into. It defaults to `out`. ```kotlin dataPack("my_datapack") { path("%appdata%/.minecraft/saves/my_world/datapacks") }.generate() ``` Pointing `path` straight at a world's `datapacks` folder is the fastest development loop: regenerate, then `/reload` in-game. ### Separating the folder name from the namespace Sometimes the folder and the namespace must differ. The usual case is plugging Kore into a modded Fabric project's datagen step: Fabric Loom expects generated resources under a fixed `generated` directory, but the resource namespace inside should be your mod ID, not the literal string `generated`. `folderName` decouples the two: ```kotlin dataPack("mymod") { folderName("generated") path("src/main") predicate("always_true") }.generate() // -> src/main/generated/pack.mcmeta // -> src/main/generated/data/mymod/predicate/always_true.json ``` The output folder (and `.zip`/`.jar` file name) becomes `generated`, while every resource still defaults to the `mymod` namespace. Individual functions and generators can still override their own `namespace` as before. ### Icon `iconPath` points at the `pack.png` shown in the datapack list: ```kotlin dataPack("my_datapack") { iconPath("icon.png") } ``` ### Serialization settings How the JSON is formatted, and where Kore puts the functions it generates for you, is controlled by a separate `configuration { }` block - see [Configuration](/docs/guides/configuration). ## Pack metadata Every datapack needs a `pack.mcmeta`. Kore always generates one; the `pack` block configures it. ```kotlin dataPack("mydatapack") { pack { minFormat(107) maxFormat(107) description = textComponent("My Datapack") } } ``` - `minFormat` - the minimum supported pack format version. - `maxFormat` - the maximum supported pack format version. - `description` - a text component for the datapack description. `minFormat` and `maxFormat` are shortcut functions accepting the same arguments as `packFormat()`: ```kotlin pack { minFormat(107) // plain integer minFormat(107, 0) // [major, minor] pair maxFormat(107) } ``` You can also assign a `PackFormat` value directly: ```kotlin pack { minFormat = packFormat(107) maxFormat = packFormat(107) } ``` ### Targeting Minecraft 1.21.9+ Since Minecraft 1.21.9 (25w31a), `min_format` and `max_format` are the primary fields in `pack.mcmeta`, and accept a `[major, minor]` pair to target snapshots or minor versions. Minecraft 26.2 uses data pack format `[107, 1]`: ```kotlin dataPack("my_datapack") { pack { minFormat(107, 1) maxFormat(107, 1) description = textComponent("Targeting 26.2") } } ``` `minFormat`, `maxFormat` and `packFormat` do not accept decimal values - use a plain integer or a `[major, minor]` pair for `minFormat`/`maxFormat`, and a plain integer for `packFormat`. `PackFormatDecimal` exists only for parsing legacy third-party `pack.mcmeta` files that used a decimal `pack_format`; do not set it yourself. ### Legacy `pack_format` and `supportedFormats` `pack.mcmeta`'s `pack_format` field is always a plain integer, even on modern Minecraft versions - some third-party tools (Modrinth, for example) still read it for compatibility. Kore includes it by default, using the current Minecraft version's pack format, unless you set `packFormat = null` yourself. If your `minFormat` is below the threshold (82 for datapacks, 65 for resource packs), Kore additionally writes the legacy `supported_formats` field so older Minecraft versions still accept the pack. Overriding `packFormat` explicitly only accepts a plain integer (`PackFormatMajor`); a `[major, minor]` pair or a decimal value triggers a warning: ```kotlin dataPack("mydatapack") { pack { minFormat(107) maxFormat(107) packFormat = packFormat(107) // must stay a plain integer } } ``` `supportedFormats` can also be set explicitly: ```kotlin dataPack("mydatapack") { pack { minFormat(48) maxFormat(60) description = textComponent("My Datapack") supportedFormats(48..60) supportedFormats(min = 48) // max is optional } } ``` ### Overlays Overlays let one pack ship different resources per client pack format - the standard way to support several Minecraft versions from a single download. Declare each overlay directory with `entry`: ```kotlin dataPack("my_datapack") { overlays { entry("my_overlay") { minFormat(82) maxFormat(93) } } } ``` Each `entry` takes a directory name and a block configuring `minFormat`/`maxFormat` with the same shortcut functions as the `pack` block. Clients in that format range load the overlay directory on top of the base pack. ### Filters A filter tells Minecraft to **ignore** resources coming from packs applied *below* yours - the vanilla way to disable data from another datapack (or from vanilla itself) rather than trying to override it. Any file matching a blocked pattern is treated as if it were not present at all. Each `block` entry takes a `namespace` and a `path`, both optional and both interpreted as regexes. Omitting one means "match anything": ```kotlin dataPack("my_datapack") { filter { block(namespace = "minecraft", path = "recipe/.*_boat.json") block(path = "loot_table/blocks/stone.*") } } ``` Here every vanilla boat recipe, and every stone-related block loot table from lower packs, stop existing for the game. `block` also accepts a `FilteredBlock` or a builder block, and `blocks(...)` adds several at once. Careful: this is not scoped to your namespace. `block(path = ".*")` with no namespace disables the entire data of every pack below yours. ## Adding content Everything else in the `dataPack { }` block is content, declared through the builder matching the resource you want: `function`, `advancement`, `lootTable`, `recipes`, `predicate`, `enchantment`, `biome`, and so on. ```kotlin dataPack("my_datapack") { recipes { craftingShaped("enchanted_golden_apple") { pattern( "GGG", "GAG", "GGG" ) key("G", Items.GOLD_BLOCK) key("A", Items.APPLE) result(Items.ENCHANTED_GOLDEN_APPLE) } } } ``` Each builder has its own reference page - start from [Recipes](/docs/data-driven/recipes), [Loot Tables](/docs/data-driven/loot-tables), [Predicates](/docs/data-driven/predicates), or [Functions](/docs/commands/functions). ## Generating output Three generation functions, three shapes. All of them write into `path` (default `out`). | Call | Produces | Use it for | |------|----------|------------| | `generate()` | `//` folder | development, reviewable diffs, CI checks | | `generateZip()` | `/.zip` | distributing a normal datapack | | `generateJar()` | `/.jar` | shipping the pack as a mod | ### Folder ```kotlin dataPack("my_datapack") { // datapack code here }.generate() ``` Best default while developing: you can open the generated `.mcfunction` and JSON files and check exactly what Kore emitted. ### ZIP ```kotlin dataPack("my_datapack") { // datapack code here }.generateZip() ``` Same contents as the folder, in one archive - what you upload for players to install. Generated ZIP entries follow the ZIP specification and always use forward slashes (`/`) internally, keeping the archive readable by strict tools such as Windows Explorer as well as WinRAR and other archive managers. ### Mod JAR `generateJar()` packages the datapack as a **mod**, so players install it in `mods/` instead of per-world `datapacks/`: ```kotlin dataPack("my_datapack") { // datapack code here }.generateJar() ``` The JAR lands in the same output folder; `path` moves it like for the other modes. On its own, `generateJar()` produces a bare archive - you also declare a loader block to get the metadata that loader needs. #### Fabric ```kotlin dataPack("my_datapack") { // datapack code here }.generateJar { fabric { version = "1.2.5" contact { email = "kore@kore.kore" homepage = "https://kore.ayfri.com" } author("Ayfri") } } ``` This writes the Fabric mod metadata with the version, contact information, and author. To make individual resources load conditionally inside a Fabric mod (gated on other mods, tags, registries, or feature flags), see [Fabric Resource Conditions](/docs/guides/fabric-resource-conditions). #### Forge ```kotlin dataPack("my_datapack") { // datapack code here }.generateJar { forge { mod { authors = "Ayfri" credits = "Generated by Kore" dependency("my_dependency") { mandatory = true version = "1.2.5" } } } } ``` #### Quilt ```kotlin dataPack("my_datapack") { // datapack code here }.generateJar { quilt("kore") { metadata { contact { email = "kore@kore.kore" homepage = "https://kore.ayfri.com" } contributor("Ayfri", "Author") } version = "1.2.5" } } ``` #### NeoForge ```kotlin dataPack("my_datapack") { // datapack code here }.generateJar { neoForge { mod { authors = "Ayfri" credits = "Generated by Kore" dependency("my_dependency") { type = NeoForgeDependencyType.REQUIRED version = "1.2.5" } } } } ``` ### Merging with existing datapacks `mergeWithPacks` folds other packs into the generated output, so you can ship one download that also contains a dependency or a pack you do not own the source of: ```kotlin import kotlinx.io.files.Path dataPack("my_datapack") { // datapack code here }.generate { mergeWithPacks(Path("existing_datapack_1"), Path("existing_datapack_2.zip")) } ``` A path to a zip is treated as a datapack and merged too: Kore unzips it into a temporary directory, merges the extracted files with the generated datapack, then deletes that directory once merging is done. #### Pack format compatibility When merging, Kore checks whether the pack format ranges overlap and warns when they do not: ```kotlin val myDatapack1 = dataPack("my_datapack_1") { // datapack code here pack { minFormat(40) maxFormat(40) } } val myDatapack2 = dataPack("my_datapack_2") { // datapack code here pack { minFormat(50) maxFormat(50) } } myDatapack1.generate { mergeWithPacks(myDatapack2) } ``` Prints: ``` The pack format range of the other pack is different from the current one. This may cause issues. Format range: current: 40..40 other: 50..50. ``` `supportedFormats` is checked the same way, with a warning when the other pack is not supported. #### Lifecycle tags are merged, not overwritten Both packs' `minecraft:load` and `minecraft:tick` function tags are combined, so nothing silently loses its entry point: ```kotlin val myDatapack1 = dataPack("my_datapack_1") { // datapack code here load("my_main_function") { say("Hello World!") } } val myDatapack2 = dataPack("my_datapack_2") { // datapack code here load("load") { say("Hello Everyone!") } } myDatapack1.generate { mergeWithPacks(myDatapack2) } ``` The resulting `load.json` contains both: ```json { "replace": false, "values": [ "my_datapack_1:generated_scopes/my_main_function", "my_datapack_2:generated_scopes/load" ] } ``` ## What to read next - [Configuration](/docs/guides/configuration) - JSON formatting and generated function naming - [Functions](/docs/commands/functions) - reusable logic, lifecycle tags, and generated functions - [Cookbook](/docs/guides/cookbook) - practical patterns once your pack structure is in place - [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore) - migration strategy and production workflow - [Fabric Resource Conditions](/docs/guides/fabric-resource-conditions) - load resources conditionally in a Fabric mod - [GitHub Actions Publishing](/docs/advanced/github-actions-publishing) - automate releases to Modrinth, CurseForge and GitHub --- ## Kore Configuration - JSON Formatting & Generated Function Options --- root: .components.layouts.MarkdownLayout title: Kore Configuration - JSON Formatting & Generated Function Options nav-title: Configuration description: Tune how Kore serializes your datapack - pretty-printed JSON, indentation, the generated functions folder, and debug comments on generated function calls. keywords: kore configuration, datapack pretty print, datapack json formatting, generated functions folder, kore settings, minecraft datapack debug, generated_scopes, mcfunction output date-created: 2024-04-06 date-modified: 2026-10-07 routeOverride: /docs/guides/configuration position: 2 --- # DataPack configuration The `configuration { }` block controls **how Kore writes** your pack: JSON formatting, the naming of the functions Kore generates on your behalf, and the [optimization passes](/docs/guides/optimization) run before writing. All options are listed at the bottom of this page. It does **not** control *where* output goes or *what shape* it takes - that is `path` plus your choice of `generate()`, `generateZip()` or `generateJar()`, covered in [Creating a Datapack](/docs/guides/creating-a-datapack). None of these settings change in-game behavior. They exist to make development and debugging easier, and releases leaner. ## Example ```kotlin dataPack("mypack") { configuration { prettyPrint = true prettyPrintIndent = " " } // ... rest of datapack code } ``` ## `prettyPrint` When `prettyPrint` is `true`, Kore’s shared `Json` encoder formats JSON resources (advancements, tags, recipes, worldgen JSON, `pack.mcmeta`, etc.) with line breaks and indentation. When `false`, output is compact (smaller files, slightly faster I/O). - Default: `false` (release-friendly; smaller packs). - Set `true` when you want readable diffs in version control or easier manual inspection while developing. `prettyPrint` does not change game behavior; it only affects on-disk JSON layout. ## Indentation (`prettyPrintIndent`) `prettyPrintIndent` is the indent string passed to Kotlin serialization when `prettyPrint` is enabled. The default is a single tab (`"\t"`). Common choices are `"\t"`, `" "`, or `" "`. For **TOML** serialization (used for some generated files), Kore maps the indent string to [ktoml](https://github.com/akuleshov7/ktoml) styles: tab, two spaces, and four spaces get proper TOML indentation; other values fall back to no extra indentation for TOML output. Matching your JSON indent to one of those three keeps JSON and TOML visually consistent. ## Generated function folder (`generatedFunctionsFolder`) Kore may emit **generated** `.mcfunction` files (for example when `execute` chains are lowered into separate functions). Those files are placed under: `data//function//...` The default folder name is `generated_scopes` (see `DataPack.DEFAULT_GENERATED_FUNCTIONS_FOLDER` in the Kore source). Change `generatedFunctionsFolder` if you want a different directory name (shorter paths, naming that matches your project, or avoiding clashes with hand-written `function/` trees). ## Callers of generated functions (`generateCommentOfGeneratedFunctionCall`) When `generateCommentOfGeneratedFunctionCall` is `true`, every generated function starts with one comment per function calling it, so you can trace a `generated_scopes/` file back to the code that produced it: ```mcfunction # Called by my_pack:shop/buy # Called by my_pack:shop/sell give @s minecraft:diamond tellraw @s "Done!" ``` A function calling it through a function tag (`#minecraft:tick`) isn't listed. The comments are added when the pack is written, so they never change the hash in a generated function's name. Default: `false`. Turn it on while debugging or learning generated control flow; turn it off for minimal `.mcfunction` output in releases. ## Logging Kore and its modules print their progress and warnings (pack format mismatches, merged packs, optimization summaries, bindings downloads) through `KoreLogger`, a global object shared by every pack in the process. Raise its `level` to hide them, or replace its `handler` to send them to your own logger: ```kotlin KoreLogger.level = KoreLogger.Level.WARN // keeps warnings, hides progress lines KoreLogger.level = KoreLogger.Level.OFF // silent KoreLogger.handler = { level, message -> if (level == KoreLogger.Level.WARN) logger.warn(message) else logger.info(message) } ``` The default handler prints to the standard output, warnings in yellow. ## Development vs release setups The defaults are release-oriented (compact JSON, no comments). While developing, flipping both booleans makes the output far easier to read: | Concern | Development | Release | |---------|-------------|---------| | `prettyPrint` | `true` for readable JSON and clean git diffs | `false` for smaller files | | `generateCommentOfGeneratedFunctionCall` | `true` to trace where a generated function came from | `false` for lean `.mcfunction` output | | Output mode | `generate()` into a world's `datapacks` folder | `generateZip()`, or `generateJar()` when shipping as a mod | Keep one `dataPack { }` definition and branch on a build constant, or use separate `main`/`debug` entry points, depending on your Gradle setup: ```kotlin val debug = System.getenv("KORE_DEBUG") != null dataPack("mypack") { configuration { prettyPrint = debug generateCommentOfGeneratedFunctionCall = debug } } ``` ## Reference | Option | Description | Default | |------------------------------------------|--------------------------------------------------------------------------------------|----------------------| | `generateCommentOfGeneratedFunctionCall` | List the callers of each generated function in comments atop it. | `false` | | `generatedFunctionsFolder` | Subfolder under `function/` for generated `.mcfunction` files. | `"generated_scopes"` | | `optimization` | Whole-pack passes run before writing, see [Optimization](/docs/guides/optimization). | disabled | | `prettyPrint` | Pretty-print JSON resources. | `false` | | `prettyPrintIndent` | Indent string when pretty-printing JSON. | `"\t"` | ## What to read next - [Creating a Datapack](/docs/guides/creating-a-datapack) - metadata, output location, and packaging - [Functions](/docs/commands/functions) - what produces the generated functions this page names - [Execute](/docs/commands/execute) - the main source of generated scope functions - [Optimization](/docs/guides/optimization) - the passes cleaning up the finished pack --- ## Kore Cookbook - Practical Datapack Patterns in Kotlin --- root: .components.layouts.MarkdownLayout title: Kore Cookbook - Practical Datapack Patterns in Kotlin nav-title: Cookbook description: "Working Kore patterns to copy: bootstrap and tick structure, reusable functions, selector and predicate helpers, scheduled actions, and module choice." keywords: minecraft datapack patterns, kore examples, kotlin datapack code, datapack bootstrap, reusable mcfunction, datapack selectors, scheduled function, kore cookbook date-created: 2026-04-21 date-modified: 2026-08-05 routeOverride: /docs/guides/cookbook position: 3 --- # Cookbook Short, self-contained answers to questions that come up in every real pack. Each entry states the problem, shows the Kore version, and says when to prefer it over the alternative. These are patterns, not a reference: each one links to the page that documents the API in full. If you are porting an existing hand-written pack, read [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore) first for the migration strategy, then use this page for the individual pieces. ## Structure a pack: bootstrap vs tick **Problem:** everything ends up in one function, and one-time setup gets re-run every tick. Split the two lifecycles explicitly. `load` runs once after `/reload`, `tick` runs every game tick: ```kotlin fun DataPack.registerCoreSystems() { load("setup") { scoreboard.objectives.add("round", "dummy") scoreboard.objectives.add("lives", "dummy") } tick("game_loop") { execute { asTarget(allPlayers()) run { say("tick") } } } } fun main() = dataPack("arena") { registerCoreSystems() }.generateZip() ``` Registering objectives inside `tick` would work but wastes a command every tick; anything idempotent and one-time belongs in `load`. See [Functions](/docs/commands/functions) and [Creating a Datapack](/docs/guides/creating-a-datapack). ## Reuse logic as a real function or inlined **Problem:** the same commands appear in several places, and it is not obvious whether they deserve their own `.mcfunction`. Both options are `Function` extensions - the difference is whether you call `function(...)` inside. **Generate a real function** when the logic must be callable in its own right (scheduling, function tags, `/function` from chat, debug visibility): ```kotlin fun Function.myFunction() = function("my_function") { say("yay") say("also, yay") } load { function(myFunction()) } ``` Several call sites re-declaring the same named function is fine. Kore is optimized for this, so regenerating the same function is effectively instant and stays cleaner than caching every `FunctionArgument` by hand. **Inline the commands** when the snippet is small and only exists to avoid copy-paste - no extra `/function` call at runtime: ```kotlin fun Function.saySomething() { say("yay") say("also, yay") } load { saySomething() } ``` Rule of thumb: inline by default, extract into a real function the moment something needs to *reference* it. See [Functions](/docs/commands/functions) and [Commands](/docs/commands/commands). ## Stop repeating long selectors **Problem:** the same `@a[...]` constraints are rewritten in ten places, and updating the rule means finding all ten. A selector is a value. Name it once and reuse it: ```kotlin val activePlayers = allPlayers { scores { "round" greaterThanOrEqualTo 1 "lives" greaterThan 0 } gamemode = !Gamemode.SPECTATOR } function("start_wave") { effect(activePlayers) { give(Effects.RESISTANCE, duration = 5, amplifier = 0) } tellraw(activePlayers, textComponent("Wave started")) } ``` The name documents the intent ("who counts as playing right now") and one edit updates every use. See [Selectors](/docs/concepts/selectors) and [Scoreboards](/docs/concepts/scoreboards). ## Make a custom item recognizable later **Problem:** you give a player a custom item, then cannot reliably identify it once it has moved between inventories. Define the item once with its components, and derive a predicate from that same value: ```kotlin val arenaBlade = Items.DIAMOND_SWORD { customName(textComponent("Arena Blade", Color.AQUA)) } val arenaBladePredicate = predicate("arena_blade") { matchTool(arenaBlade) } function("check_weapon") { execute { ifCondition(arenaBladePredicate) run { say("Correct weapon equipped") } } } ``` Because the predicate is built from `arenaBlade`, changing the item cannot desynchronize the check. See [Components](/docs/concepts/components), [Predicates](/docs/data-driven/predicates) and [Item Modifiers](/docs/data-driven/item-modifiers). ## Delay an action without duplicating it **Problem:** a telegraph, cooldown, or cutscene beat needs the same commands now and again later. Name the delayed step as a function and schedule that: ```kotlin val explosionWarning = function("explosion_warning") { tellraw(allPlayers(), textComponent("Boom in 5 seconds!", Color.RED)) } val explodeNow = function("explode_now") { summon(EntityTypes.TNT, vec3()) } function("trigger_explosion") { function(explosionWarning) schedule(5.seconds, explodeNow) } ``` `schedule` needs a real function reference, which is exactly why this case justifies generating one rather than inlining. See [Commands](/docs/commands/commands), [Helpers Utilities](/docs/helpers/utilities) and [Scheduler](/docs/helpers/scheduler). ## Pick the right module **Problem:** unclear whether a system should use plain `kore`, or pull in `helpers`/`oop`. Escalate only when the simpler layer starts hurting: - **`kore`** - raw commands, data-driven JSON, tags, functions, selectors. Start here, always. - **`helpers`** - add it when you are rewriting infrastructure glue: renderers, geometry, raycasts, scheduler patterns, scoreboard math, state delegates. - **`oop`** - add it when gameplay needs long-lived identities that several systems share: players, teams, boss bars, timers, spawners, state machines. The failure mode is reaching for `oop` on day one and wrapping everything in abstractions the pack never needed. Write it with `kore`, then move up when the duplication is real. See [Home](/docs/home), [Helpers Utilities](/docs/helpers/utilities) and [OOP Utilities](/docs/oop/oop-utilities). ## Depend on an existing datapack **Problem:** your pack calls into another pack's functions using hand-typed ID strings that break silently when that pack changes. Import it with `bindings` and get typed Kotlin constants instead: 1. Configure a binding source. 2. Generate the Kotlin wrappers. 3. Call the imported functions and resources from your own pack. Worth it for large internal libraries or third-party packs you track across versions - a renamed function then becomes a compile error rather than a silent no-op in-game. See [Bindings](/docs/advanced/bindings), which is **experimental**, and [Functions](/docs/commands/functions). ## Principles behind these - Extract repeated code into small helpers before it spreads. - Move cross-cutting conditions into named selectors or predicates. - Prefer typed arguments over hand-written command strings. - Let Kotlin values (items, selectors, function references) be the single source of truth, and derive everything else from them. --- ## Migrating a Minecraft Datapack to Kore - Guide for Datapack Veterans --- root: .components.layouts.MarkdownLayout title: Migrating a Minecraft Datapack to Kore - Guide for Datapack Veterans nav-title: Datapack Veterans description: Port an existing hand-written datapack to Kore without a rewrite - vanilla-to-Kotlin mapping, slice-by-slice migration, project structure and common mistakes. keywords: datapack migration, migrate datapack to kotlin, mcfunction to kotlin, kore migration guide, datapack architecture, datapack refactor, advanced datapack development date-created: 2026-04-22 date-modified: 2026-08-05 routeOverride: /docs/guides/from-datapacks-to-kore position: 4 --- # From Datapacks to Kore This page is for authors who **already ship a working datapack** and want to move it to Kore without losing low-level control or freezing development for a rewrite. You are the target audience if you already structure packs with folders, tags and shared utility functions, work comfortably with scoreboards, predicates, loot tables and worldgen, and care about maintainability and deterministic output. New to datapacks? Start with [Getting Started](/docs/getting-started) instead. Still deciding whether to migrate at all? [Why Kore](/docs/guides/why-kore) covers the trade-off, including when to stay hand-written. ## The one thing to internalize first Kore is a **compile-time authoring DSL and generator**, not an in-game runtime framework. - Your Kotlin runs on your machine or in CI, during generation. - The generated `.mcfunction` and JSON files run in Minecraft. - Kotlin objects and functions do **not** exist in-game afterwards. Almost every early misunderstanding traces back to this line. A Kotlin `val` is not a scoreboard value; a Kotlin `if` picks what gets *written into* the pack, not what the game evaluates at runtime. When you do want in-game conditionals and variables, that is a separate mechanism - see [Runtime Logic](/docs/concepts/runtime-logic). Think of Kore as a programmable build system for datapack content. ## Vanilla-to-Kore mapping Direct translation of what you already have: | Hand-written | Kore | |-------------------------------------------|-----------------------------------------------------------------------------------------------------------------| | `data//function/feature/x.mcfunction` | `fun Function.x() = function("feature/x") { ... }` | | editing the `minecraft:load` tag | `load("...") { function(someFeature()) }` | | editing the `minecraft:tick` tag | `tick("...") { function(runtimeStep()) }` | | `@a[tag=fighter,gamemode=!spectator]` | typed selector builders, or `selector("@a[tag=fighter,gamemode=!spectator]")` to parse the vanilla string as-is | | JSON resource files | typed builders: `advancement`, `lootTable`, `recipes`, `predicate`, `enchantment`, `biome`, ... | | folder nesting | the path string in the builder (`"feature/combat/init"`) | Kore does not hide vanilla concepts. It formalizes them - the generated output is the same pack you would have written by hand, so anything you know about load order, tag dispatch, or `execute` semantics still applies. ### What Kore will not do for you Set expectations before you start: - **Not a resource pack tool.** Datapacks only. - **Some SNBT gaps.** Heterogeneous SNBT lists and operations like `bool(arg)`/`uuid(arg)` are not fully supported. - **No algorithmic optimization.** Execution cost is still defined by the commands you generate; the opt-in [optimization passes](/docs/guides/optimization) only clean up `execute` chains, selectors and dead functions. - **`bindings` is experimental** and may change between versions. See [Known Issues](/docs/advanced/known-issues) for the current list. ## Migrate incrementally, never big-bang The mistake that kills migrations is porting the whole pack before testing any of it. Go slice by slice: 1. **Freeze behavior.** Write a smoke checklist first: `/reload`, expected bootstrap output, one command per feature. This is your regression baseline, and you cannot recover it later. 2. **Port one vertical slice** - a full feature end to end, not a folder. Onboarding flow, or one combat mechanic. 3. **Keep hostile details verbatim.** Paste complex selectors into `selector("@a[...]")` during this pass rather than retyping them into the builder; convert them once the slice is validated. 4. **Generate to a folder** with `.generate()` and read the output. Diff it against the original files. 5. **Compare in-game** against your baseline before moving on. 6. **Repeat by subsystem**, then extract the shared Kotlin helpers the repetition has revealed. Steps 4 and 5 are the point. A slice that generates plausible-looking files but was never loaded in-game is not migrated. Porting selectors deserves a note, because it is where most of the friction is. Both forms below produce the same selector model, so a parsed string can be migrated property-by-property with no behavior change: ```kotlin val activePlayersPorted = selector("@a[gamemode=!spectator,scores={lives=1..,round=1..}]") val nearbyActivePlayers = selector("@a[gamemode=!spectator]") { distance = rangeOrIntEnd(16) } ``` If your pack depends on **another** datapack, import it with [Bindings](/docs/advanced/bindings) rather than re-declaring its IDs as string literals. ## Project structure that scales Once more than one slice has landed, the source layout starts to matter. A shape that works for large packs: - `pack/` - pack bootstrap and configuration. - `feature/` - domain modules (combat, quests, economy, UI, progression). - `runtime/` - lifecycle and tick routing. - `resources/` - data-driven definitions. - `interop/` - imported or bound external packs. Mirror those prefixes in the **generated** paths too (`feature/`, `runtime/`, `system/`), so a function you find in-game maps back to a Kotlin file without guessing. ```kotlin data object Objectives { const val LIVES = "lives" const val ROUND = "round" } fun Function.combatInit() = function("feature/combat/init") { tellraw(allPlayers(), textComponent("[combat] initialized")) } fun Function.combatTick() = function("feature/combat/tick") { // Keep tick work small and dispatch if it grows. } fun Function.progressionTick() = function("feature/progression/tick") { // Keep progression routing isolated from combat. } fun DataPack.registerLifecycle() { load("system/bootstrap") { scoreboard.objectives.add(Objectives.LIVES, "dummy") scoreboard.objectives.add(Objectives.ROUND, "dummy") function(combatInit()) } tick("runtime/main") { function(combatTick()) function(progressionTick()) } } fun main() { dataPack("arena_core") { registerLifecycle() }.generate() } ``` Two conventions carry most of the weight here: `DataPack` extensions **register** things, `Function` extensions **produce or emit** commands. ## Kotlin patterns worth adopting ### Reference functions by value, not by path string Function factories give you rename, find-usages, and a compile error on every caller when something moves. String paths give you none of that: ```kotlin fun Function.welcomeAnnounce() = function("feature/welcome/announce") { tellraw(allPlayers(), textComponent("Welcome to the server")) } fun Function.joinEffects() = function("feature/welcome/join_effects") { effect(allPlayers()) { give(Effects.RESISTANCE, duration = 3, amplifier = 0) } say("Join effects applied") } fun DataPack.registerJoinFlow() { load("system/join_bootstrap") { function(welcomeAnnounce()) function(joinEffects()) } } ``` This is the single highest-value habit to adopt early - retrofitting it later means touching every call site. ### Split registration from reusable snippets ```kotlin fun Function.combatPipeline() = function("feature/combat/pipeline") { applyJoinEffects() runRoundRules() } fun Function.applyJoinEffects() { effect(allPlayers()) { give(Effects.RESISTANCE, duration = 3, amplifier = 0) } } fun Function.runRoundRules() { say("Round rules applied") } fun DataPack.registerCombatPipeline() { tick("runtime/combat_router") { function(combatPipeline()) } } ``` `combatPipeline` becomes a real `.mcfunction`; `applyJoinEffects` and `runRoundRules` inline into it. See [the Cookbook](/docs/guides/cookbook#reuse-logic-as-a-real-function-or-inlined) for when to pick which. ### Turn tuning values into typed configs Balancing numbers scattered across functions are the hardest thing to review in a hand-written pack. Make them data: ```kotlin data class WaveConfig( val id: String, val title: String, val warningSeconds: Int, ) fun DataPack.registerWave(config: WaveConfig) { val warning = function("feature/waves/${config.id}_warning") { tellraw(allPlayers(), textComponent(config.title)) } function("feature/waves/${config.id}_start") { function(warning) schedule(config.warningSeconds.seconds, warning) } } ``` One `WaveConfig` list now defines every wave, and a balance pass is a reviewable diff instead of an archaeology session. More patterns - selectors as domain rules, custom items validated by predicates, module choice - are in the [Cookbook](/docs/guides/cookbook). ## Data-driven resources are values, not files This is where the migration pays off most, and it is the part veterans usually underestimate. In a hand-written pack, a loot table or a predicate is a **file**, and everything that points at it is a **string** you retyped: `"mypack:predicates/is_active"`, spelled slightly differently in four places, verified only by `/reload`. In Kore, every data-driven builder **returns a typed argument** identifying the resource it just declared: | Builder | Returns | |---------|---------| | `predicate("...") { }` | `PredicateArgument` | | `lootTable("...") { }` | `LootTableArgument` | | `itemModifier("...") { }` | `ItemModifierArgument` | | `advancement("...") { }` | `AdvancementArgument` | | `function("...") { }` | `FunctionArgument` | Assign that to a `val` and the resource becomes a first-class value. Rename it, find its usages, and get a compile error the moment something points at a resource that no longer exists. ### Declare once, reference everywhere The same `PredicateArgument` works as an `execute` condition, as a nested condition inside another predicate via `reference`, and as an enchantment effect requirement - all from one declaration: ```kotlin fun DataPack.registerCombatRules() { val isActiveFighter = predicate("rules/is_active_fighter") { entityScores(EntityTarget.THIS) { this["lives"] = intRange(1f, 99f) } } // 1. As a command condition. function("feature/combat/tick") { execute { ifCondition(isActiveFighter) run { say("fighting") } } } // 2. Composed into another predicate. predicate("rules/is_wounded_fighter") { reference(isActiveFighter) entityScores(EntityTarget.THIS) { this["health"] = intRange(1f, 5f) } } } ``` Compare that with the vanilla version, where the second predicate embeds `{"condition": "minecraft:reference", "name": "mypack:rules/is_active_fighter"}` - a string nothing checks. The same holds across resource kinds. A `LootTableArgument` feeds the `loot` command, the `containerLoot` item component, and advancement rewards: ```kotlin fun DataPack.registerRewards() { val bossDrop = lootTable("rewards/boss_drop") { pool { entries { item(Items.DIAMOND) } } } function("feature/boss/on_death") { loot(allPlayers(), bossDrop) } advancement("progression/boss_slain") { rewards { loots(bossDrop) } } } ``` Move or rename `bossDrop` and both call sites follow. In a hand-written pack this is a grep-and-pray refactor. ### Generate families of resources instead of copy-pasting JSON Hand-written packs accumulate near-identical JSON files: nine tier variants of a loot table, one advancement per collectible, a recipe per material. Kore turns that into a loop over data, which is the single biggest file-count reduction in most migrations: ```kotlin data class Tier(val id: String, val item: ItemArgument, val weight: Int) val tiers = listOf( Tier("common", Items.IRON_INGOT, 10), Tier("rare", Items.GOLD_INGOT, 4), Tier("epic", Items.DIAMOND, 1), ) fun DataPack.registerTierDrops(): Map = tiers.associate { tier -> tier.id to lootTable("rewards/${tier.id}_drop") { pool { entries { item(tier.item) { weight = tier.weight } } } } } ``` One list is now the source of truth for every tier. Adding a tier is one line, not a new file plus three call sites you have to remember to update - and the returned map keeps every table addressable by id, still typed. ### Factor out shared fragments, not just whole resources Reuse is not limited to complete resources. Any nested builder chunk can become a `fun`, so a condition or a pool that appears in twelve loot tables is written once: ```kotlin fun LootTable.commonJunkPool() = pool { rolls = constant(1f) entries { item(Items.STRING) { weight = 5 } item(Items.BONE) { weight = 3 } } } fun DataPack.registerChests() { lootTable("chests/hallway") { commonJunkPool() pool { entries { item(Items.IRON_INGOT) } } } lootTable("chests/vault") { commonJunkPool() pool { entries { item(Items.DIAMOND) } } } } ``` The rule mirrors the one for functions: extension functions on the **builder type** (`LootTable`, `Predicate`, `Advancement`) emit into whatever resource is being built, while extensions on `DataPack` register new resources. ### Migration order for data-driven content Data-driven resources are usually the **easiest slices to port first**, before touching any command logic: 1. Pick one resource family (all your loot tables, or all your predicates). 2. Port them as-is, one `val` per file, without trying to deduplicate yet. 3. Generate with `.generate()` and **diff the JSON against the original files**. This is the strongest verification available anywhere in the migration - byte-level parity, no game needed. 4. Only once the diff is clean, collapse the near-duplicates into loops and shared fragments, and re-diff. Step 3 is why data-driven content is a good starting point: unlike command logic, correctness is mechanically checkable before you ever load the world. Per-resource references: [Loot Tables](/docs/data-driven/loot-tables), [Predicates](/docs/data-driven/predicates), [Recipes](/docs/data-driven/recipes), [Advancements](/docs/data-driven/advancements), [Item Modifiers](/docs/data-driven/item-modifiers), [Tags](/docs/data-driven/tags). ## Common migration mistakes - **Porting file-by-file instead of behavior-by-behavior.** Migrate vertical slices and validate each runtime path before moving on. - **Building giant wrappers on day one.** Start with direct DSL usage; extract only patterns that have actually repeated. - **Letting generated paths drift from your Kotlin structure.** Adopt stable prefixes (`feature/`, `runtime/`, `system/`) from the first slice. - **Treating Kotlin as runtime state.** Kotlin runs at generation time only. See [Runtime Logic](/docs/concepts/runtime-logic) for real in-game state. - **Keeping critical IDs as ad-hoc strings.** Centralize objectives and resource IDs in constants, as in `Objectives` above. - **Skipping the in-game check because the JSON looks right.** Valid JSON that loads is not the same as behavior that matches your baseline. ## Verification loop Once migrated, a release loop that catches regressions: 1. Generate unpacked output with `.generate()` so it is reviewable. 2. Run the smoke checks from step 1 of the migration in-game. 3. Check generated tags and resources for namespace and path correctness. 4. Package with `.generateZip()` or `.generateJar()` only after behavior checks pass. 5. Keep output deterministic so CI can diff generated files and code review catches surprises. Generation is deterministic from your Kotlin source and config, which is what makes step 5 work: committing generated output (or diffing it in CI) turns "did that refactor change anything?" into a mechanical question. ### Parity checkpoints When something looks wrong in-game, inspect the generated files and compare against the vanilla references before suspecting Kore: - **`pack.mcmeta` format**: modern packs use `min_format`/`max_format`. See [Minecraft Wiki - pack.mcmeta](https://minecraft.wiki/w/Pack.mcmeta) and [pack format](https://minecraft.wiki/w/Pack_format). - **Root and namespace rules**: see [Minecraft Wiki - data pack](https://minecraft.wiki/w/Data_pack). - **Lifecycle tags**: `load` and `tick` are still plain function-tag wiring; runtime behavior is vanilla tag dispatch. - **Scheduling**: `schedule function ...` semantics are Minecraft-native. Kore only improves the authoring ergonomics. ## Migration checklist **Before you start** - Freeze current behavior with a manual test matrix. - Inventory shared naming conventions and objective IDs. - Decide your module set (`kore` alone, or plus `helpers`/`oop` - see the [Cookbook](/docs/guides/cookbook#pick-the-right-module)). **First slice** - Port lifecycle (`load`, `tick`) plus one gameplay feature. - Introduce extension-based registration (`DataPack.registerX()`). - Port one data-driven resource to a typed builder and diff the JSON against the original. **Stabilization** - Extract reusable selector and predicate helpers. - Introduce typed config objects for balancing-heavy systems. - Optionally bring in external resources through `bindings`. ## Where to go next - [Creating a Datapack](/docs/guides/creating-a-datapack) - metadata, output modes, packaging - [Runtime Logic](/docs/concepts/runtime-logic) - in-game conditionals and variables - [Functions](/docs/commands/functions) - composition and lifecycle hooks - [Cookbook](/docs/guides/cookbook) - the individual patterns referenced above - [Bindings](/docs/advanced/bindings) - importing external datapacks - [Known Issues](/docs/advanced/known-issues) - current limitations --- ## Fabric Resource Conditions --- root: .components.layouts.MarkdownLayout title: Fabric Resource Conditions nav-title: Fabric Resource Conditions description: "Load Kore-generated recipes, advancements and loot tables conditionally in Fabric mods, gated on mods, tags, registries or feature flags." keywords: minecraft, datapack, kore, fabric, resource conditions, datagen, load conditions, mod date-created: 2026-07-21 date-modified: 2026-08-05 routeOverride: /docs/guides/fabric-resource-conditions position: 5 --- # Fabric Resource Conditions [Fabric API resource conditions](https://docs.fabricmc.net/develop/resource-conditions) let the game load a data file only when some runtime condition holds: another mod is present, a tag is populated, a registry contains an entry, or a feature flag is enabled. Fabric reads them from a `fabric:load_conditions` array at the root of the JSON. Kore can attach these conditions to **any generator** (recipes, advancements, loot tables, predicates, item modifiers, worldgen, ...). The array is injected at the JSON root during generation, so the same Kore code produces a plain datapack for vanilla and a conditionally-loaded one when shipped inside a Fabric mod. This pairs well with generating straight into Fabric Loom's datagen folder, see [Creating a DataPack](/docs/guides/creating-a-datapack#separating-the-folder-name-from-the-namespace). ## Attaching conditions Call `fabricLoadConditions { }` inside any generator builder. Each factory both creates a condition and adds it: ```kotlin dataPack("mymod") { advancement("secret") { fabricLoadConditions { allModsLoaded("create") not(anyModsLoaded("badmod")) } // ... advancement content } } ``` Produces: ```json { "fabric:load_conditions": [ { "condition": "fabric:all_mods_loaded", "values": ["create"] }, { "condition": "fabric:not", "value": { "condition": "fabric:any_mods_loaded", "values": ["badmod"] } } ], "criteria": {} } ``` ## Available conditions | DSL call | Condition | Succeeds when | |-----------------------------------------------------------------|----------------------------|--------------------------------------------------------------| | `allModsLoaded("a", "b")` | `fabric:all_mods_loaded` | every listed mod is loaded | | `anyModsLoaded("a", "b")` | `fabric:any_mods_loaded` | at least one listed mod is loaded | | `tagsPopulated("minecraft:logs")` | `fabric:tags_populated` | every tag is populated (in `minecraft:item` by default) | | `tagsPopulated("stone", registry = "minecraft:block")` | `fabric:tags_populated` | every tag is populated in the given registry | | `registryContains("mymod:custom")` | `fabric:registry_contains` | the registry (`minecraft:item` by default) contains every id | | `registryContains("mymod:tree", registry = "worldgen/feature")` | `fabric:registry_contains` | the given registry contains every id | | `featuresEnabled("minecraft:vanilla")` | `fabric:features_enabled` | every feature flag is enabled | | `conditionTrue()` | `fabric:true` | always | ### Combinators `not(...)` inverts a single condition. `and { }` and `or { }` open nested scopes that combine the conditions declared inside them: ```kotlin lootTable("rare_drop") { fabricLoadConditions { and { tagsPopulated("c:ingots/copper") featuresEnabled("minecraft:vanilla") } or { registryContains("minecraft:diamond") registryContains("mymod:custom_block", registry = "minecraft:block") } } // ... loot table content } ``` The `registry` argument is optional and defaults to `minecraft:item` on the Fabric side, so it is omitted from the JSON when you leave it out. ## Limitation: object-form resources only Conditions are injected at the JSON **root**, which must be an object. That covers recipes, advancements, loot tables, and single-entry predicates or item modifiers. Predicates and item modifiers with **multiple** entries serialize as a top-level JSON array, which has no root to attach `fabric:load_conditions` to. Kore emits a warning and generates the file without conditions in that case. Wrap the logic differently (for example a single `allOf`/`anyOf` predicate) if you need conditions on it. --- ## Kore Gradle Plugin - Build, Link and Live Reload Your Datapack --- root: .components.layouts.MarkdownLayout title: Kore Gradle Plugin - Build, Link and Live Reload Your Datapack nav-title: Gradle Plugin description: Generate your Kore datapack from Gradle, copy it straight into your Minecraft worlds, and reload a running server automatically on every source change. keywords: kore gradle plugin, minecraft datapack watch mode, datapack live reload, datapack hot reload, gradle continuous build, minecraft rcon reload, datapack development loop date-created: 2026-09-04 date-modified: 2026-09-29 routeOverride: /docs/guides/gradle-plugin position: 6 --- # Gradle plugin The Kore Gradle plugin turns the edit-generate-copy-reload cycle into one command. It runs your entry point, copies the generated pack into every world you name, and sends `/reload` to a running server over RCON. Combined with Gradle's continuous build, `gradlew koreRun --continuous` rebuilds and relinks on every source change. ## Setup The plugin is on the Gradle Plugin Portal, so applying it needs no repository configuration. It shares its version with the library, so the same string goes in both blocks: ```kotlin plugins { kotlin("jvm") id("io.github.ayfri.kore") version "2.16.0-26.2" } dependencies { implementation("io.github.ayfri.kore:kore:2.16.0-26.2") } kore { mainClass = "com.example.MainKt" packName = "my_pack" worlds = listOf("My World") } ``` Applying the plugin does not bring the Kore dependency in, so declare it yourself. The Minecraft suffix in the version is what the *library* targets; the plugin is version-agnostic and only carries the suffix so both coordinates stay in step. It is published to Maven Central too, as `io.github.ayfri.kore:kore-gradle-plugin`, for builds that resolve plugins from there instead. The entry point receives the output directory twice, as its first program argument and as the `kore.output` system property. Use whichever fits your `main`: ```kotlin fun main() { dataPack("my_pack") { path(System.getProperty("kore.output") ?: "out") function("load") { say("Hello from Kore") } }.generate() } ``` `packName` must match the folder Kore writes, which is the datapack `name` unless you override it with `folderName`. The plugin also passes that value as the `kore.packName` system property, so `folderName(System.getProperty("kore.packName"))` keeps the two in sync automatically. It must be a plain folder name: an empty name, `.`, `..` or one containing a slash fails the build, since linking and `koreClean` would otherwise hit the whole `datapacks` folder. ## Tasks Every task sits in the `kore` group, so `gradlew tasks --group kore` lists them. | Task | What it does | |------------------------|------------------------------------------------------------------------------------------------------| | `koreBuild` | Runs the entry point and generates the packs into `outputDirectory`. | | `koreLinkDataPack` | Copies the datapack into every world and extra target. | | `koreLinkResourcePack` | Copies the resource pack into every `resourcepacks` folder. Skipped until `resourcePackName` is set. | | `koreLink` | Runs both link tasks. | | `koreReload` | Sends a command to a running server over RCON, `reload` by default. | | `koreRun` | `koreLink` then `koreReload`. The task to use with `--continuous`. | | `koreClean` | Deletes the generated output and unlinks every pack from its targets. | | `koreWorlds` | Lists the worlds found in the Minecraft directory, with the `datapacks` folder of each. | ## The development loop ```bash gradlew koreRun --continuous ``` Gradle watches your sources, so every save regenerates the pack and copies it into the worlds. Add an RCON password and the running server reloads too, with no alt-tab at all: ```properties # server.properties enable-rcon=true rcon.port=25575 rcon.password=changeit ``` ```kotlin kore { rcon { password = providers.environmentVariable("RCON_PASSWORD") } } ``` `RCON_PASSWORD` is read by default, so the block above is only needed to point somewhere else. Without a password the reload is reported and skipped rather than failing, which keeps the loop running while the game is closed. A server that is down behaves the same way, since `failOnError` is `false`: a refused connection is a warning, not a broken build. RCON is a dedicated server feature. A singleplayer world reaches the plugin through `koreLink` only, and picks the new pack up on the next `/reload` you type in chat. ## Choosing where the pack lands The Minecraft directory defaults to the standard location of your OS, `%APPDATA%\.minecraft` on Windows, `~/Library/Application Support/minecraft` on macOS and `~/.minecraft` elsewhere. The `MINECRAFT_DIR` environment variable overrides it, and so does `minecraftDirectory`. Run `gradlew koreWorlds` to see what the plugin finds. A world counts as a world when it has a `level.dat`. ```kotlin kore { // Named worlds, discovered worlds, or arbitrary folders. They add up. worlds = listOf("My World", "Test World") linkToAllWorlds = false dataPackTargets = listOf("D:/servers/survival/world/datapacks") } ``` `linkMode = LinkMode.SYMLINK` links instead of copying, so the game reads the generated folder directly. Windows refuses symlinks without Developer Mode or administrator rights, in which case the plugin falls back to a copy and says so. ## Keeping rebuilds correct `cleanBeforeBuild` is on by default: the output directory is wiped before each generation, so a resource you deleted from your Kotlin sources also disappears from the pack. Point `outputDirectory` at a dedicated folder: the build refuses to wipe one that contains the project. Up-to-date checks follow the runtime classpath. If your entry point reads files that are not on it, textures or an external JSON for instance, declare them so a change to one triggers a rebuild: ```kotlin kore { additionalInputs.from(file("assets"), file("data/balance.json")) } ``` ## Faster generation with the AOT cache With a JDK 25+ toolchain, `koreBuild` runs your entry point on a [JDK AOT cache](https://openjdk.org/jeps/514) stored in `build/kore-aot`. The first generation records which classes Kore loads, the next ones start with them already parsed and linked: on a small pack the generation drops from about 1s to under 0.4s. Your own classes load normally, so editing them keeps the cache valid, and a dependency or JDK update trains a new one on the next run. The generated pack is identical with or without it. Set `aotCache = false` to turn it off. ## Full configuration reference ```kotlin kore { mainClass = "com.example.MainKt" // required packName = "my_pack" // defaults to the project name outputDirectory = layout.buildDirectory.dir("kore") cleanBeforeBuild = true aotCache = true // JDK 25+ only, ignored on older JDKs runtimeClasspath.setFrom(/* ... */) // defaults to the `main` source set, set it for Kotlin Multiplatform arguments = listOf("--verbose") // appended after the output directory jvmArguments = listOf("-Xmx2g") systemProperties = mapOf("debug" to "true") additionalInputs.from(file("assets")) minecraftDirectory = file("D:/games/minecraft") worlds = listOf("My World") linkToAllWorlds = false dataPackTargets = listOf("D:/servers/survival/world/datapacks") resourcePackName = "my_assets" // unset means no resource pack linking resourcePackTargets = listOf("D:/games/minecraft/resourcepacks") linkMode = LinkMode.COPY // or LinkMode.SYMLINK rcon { enabled = true host = "localhost" port = 25575 password = providers.environmentVariable("RCON_PASSWORD") command = "reload" timeoutMillis = 5000 failOnError = false } } ``` ## Kotlin Multiplatform projects A multiplatform project has no `main` source set, so the classpath has to be named explicitly: ```kotlin kore { mainClass = "com.example.MainKt" runtimeClasspath.setFrom(kotlin.targets.getByName("jvm").compilations.getByName("main").runtimeDependencyFiles) } ``` ## What to read next - [Creating a Datapack](/docs/guides/creating-a-datapack) - what `path`, `folderName` and `generate()` do to the output this plugin moves around - [Configuration](/docs/guides/configuration) - JSON formatting and generated function naming - [GitHub Actions Publishing](/docs/advanced/github-actions-publishing) - automate releases to Modrinth, CurseForge and GitHub --- ## Lantern Load - Datapack Load Order, Versioning and Dependencies --- root: .components.layouts.MarkdownLayout title: Lantern Load - Datapack Load Order, Versioning and Dependencies nav-title: Lantern Load description: Generate the full Lantern Load boilerplate from Kotlin, publish your pack version on the load.status scoreboard, and guard against missing or outdated dependencies. keywords: lantern load kore, minecraft datapack load order, datapack dependencies, load.status scoreboard, datapack versioning, smithed interop, bookshelf dependency date-created: 2026-09-05 date-modified: 2026-09-05 routeOverride: /docs/guides/lantern-load position: 7 --- # Lantern Load [Lantern Load](https://github.com/LanternMC/load) is the convention nearly every serious datapack library follows to control **load order** and to **advertise its version**. Bookshelf, Smithed libraries, GM4 and PlayerDB all rely on it, so a pack that speaks it can depend on them and be depended on in return. `lanternLoad { }` generates the whole thing, boilerplate included: ```kotlin dataPack("my_pack") { lanternLoad { version(1, 2, 3) dependency("bs.math", 3, 1) load { say("my_pack loaded") } } } ``` ## What it generates | File | Role | |-----------------------------------------------------|---------------------------------------------------------------------| | `data/minecraft/tags/function/load.json` | Hands control to `#load:_private/load`. | | `data/load/tags/function/_private/load.json` | The Lantern Load chain: init, then `pre_load`, `load`, `post_load`. | | `data/load/tags/function/_private/init.json` | Points at the objective setup function. | | `data/load/function/_private/init.mcfunction` | Creates `load.status` and resets it on every load. | | `data/load/tags/function/load.json` | Lists every pack in the world, yours included as `#my_pack:load`. | | `data/my_pack/tags/function/load.json` | Your entry point: dependencies, then `enumerate`, then `resolve`. | | `data/my_pack/tags/function/load/dependencies.json` | Pulls each dependency's own load tag in first. | | `data/my_pack/function/load/enumerate.mcfunction` | Publishes your version on `load.status`. | | `data/my_pack/function/load/resolve.mcfunction` | Checks every dependency version, then calls `init`. | | `data/my_pack/function/load/init.mcfunction` | The body of your `load { }` block. | The boilerplate is written once per pack and merges cleanly with other packs shipping the same files, so leaving `generateBoilerplate` on is the normal choice. Turn it off when another artifact in the same build already emits it: ```kotlin lanternLoad { generateBoilerplate = false } ``` ## Versioning Your version is published as three fake players on the `load.status` objective, which is how other packs detect you: ```mcfunction # my_pack:load/enumerate scoreboard players set my_pack.major load.status 1 scoreboard players set my_pack.minor load.status 2 scoreboard players set my_pack.patch load.status 3 ``` `version` accepts either components or a string, so it can be wired straight to your build: ```kotlin lanternLoad { version("2.0.1") scoreHolder = "#my_pack" // fake player prefix, defaults to the namespace } ``` ## Dependencies A dependency does two things: it pulls the dependency's load tag in before yours, and it generates a runtime guard. ```kotlin lanternLoad { dependency("bs.math", 3, 1) // major 3, minor 1 or higher dependency("smithed.actionbar") // load order only, no version check dependency("bs.block", "2.4.0", required = true) } ``` The guard follows the ecosystem compatibility rule: the major version must match exactly, the minor version must be at least the requested one, and the patch version is ignored. ```mcfunction # my_pack:load/resolve execute if score bs.math.major load.status matches 3 if score bs.math.minor load.status matches 1.. if score bs.block.major load.status matches 2 if score bs.block.minor load.status matches 4.. run function my_pack:load/init execute unless score bs.math.major load.status matches 3 run tellraw @a {type:"text",color:"red",text:"[my_pack] Missing dependency bs.math 3.1."} execute if score bs.math.major load.status matches 3 unless score bs.math.minor load.status matches 1.. run tellraw @a {type:"text",color:"red",text:"[my_pack] Missing dependency bs.math 3.1."} execute unless score bs.block.major load.status matches 2 run tellraw @a {type:"text",color:"red",text:"[my_pack] Missing dependency bs.block 2.4."} execute if score bs.block.major load.status matches 2 unless score bs.block.minor load.status matches 4.. run tellraw @a {type:"text",color:"red",text:"[my_pack] Missing dependency bs.block 2.4."} ``` `init` runs only when every guard passes, so your pack never half-initializes against a missing library. A dependency without a version affects load order only and generates no guard. `required = true` makes Minecraft error out when the dependency is absent instead of letting the guard report it in chat. Change the wording with `missingDependencyMessage`: ```kotlin lanternLoad { dependency("bs.math", 3, 1) missingDependencyMessage = { dependency -> textComponent("My Pack needs ${dependency.namespace} ${dependency.version}", Color.GOLD) } } ``` ## Pre-load and post-load `#load:pre_load` runs before every pack's `load`, `#load:post_load` after. Use them for work that has to bracket the whole world's initialization, such as clearing shared state or printing a summary: ```kotlin lanternLoad { preLoad { scoreboard.players.reset(literal("*"), "my_pack.state") } load { function("setup") } postLoad { say("Every pack is ready") } } ``` ## Ticking Lantern Load recommends against `#minecraft:tick`, because it runs *before* `#minecraft:load` and can tick your pack before it is initialized. Schedule your tick function from `load` instead: ```kotlin dataPack("my_pack") { val tick = function("tick") { say("tick") schedule(1.ticks, "my_pack:tick") } lanternLoad { version(1) load { schedule(1.ticks, tick) } } } ``` ## Reference `lanternLoad { }` returns the generated `init` function, so it can be called from elsewhere in the pack. | Property | Default | Role | |----------------------------|---------------|----------------------------------------------------------------| | `namespace` | the pack name | Namespace owning the generated functions and tags. | | `version` | `1.0.0` | Version published on the status objective. | | `scoreHolder` | `namespace` | Fake player prefix carrying the version. | | `objective` | `load.status` | Objective holding the load status of every pack. | | `directory` | `load` | Directory of the generated functions, also the entry tag name. | | `generateBoilerplate` | `true` | Emits the Lantern Load pack files. | | `missingDependencyMessage` | red chat line | Message broadcast when a guard fails. | ## What to read next - [Creating a Datapack](/docs/guides/creating-a-datapack) - `path`, `folderName` and `generate()`, the basics this guide builds on - [Gradle Plugin](/docs/guides/gradle-plugin) - build, link and reload your pack automatically on every change - [Fabric Resource Conditions](/docs/guides/fabric-resource-conditions) - gate generated resources on mods, tags or feature flags --- ## Kore Optimization Passes - Prune Dead Functions From Your Datapack --- root: .components.layouts.MarkdownLayout title: Kore Optimization Passes - Prune Dead Functions From Your Datapack nav-title: Optimization description: Run whole-pack optimization passes before Kore writes your datapack, pruning dead functions, shortening execute chains and sorting selectors, or plug in your own. keywords: kore optimization, datapack dead code, prune empty mcfunction, datapack size, minecraft datapack optimization, kore configuration passes date-created: 2026-09-05 date-modified: 2026-10-07 routeOverride: /docs/guides/optimization position: 8 --- # Optimization passes Kore already optimizes while you build: a single-command `run { }` block is inlined straight into its `execute` chain, an empty block generates no function at all, and two generated functions with the same body are merged. Those are *local* decisions, taken with only the surrounding statement in view. Optimization passes are the opposite: they run once, on the finished pack, right before it is written, when every function, tag and resource is known. Every output path runs them, `generate()`, `generateZip()`, `generateJar()`, `generateZipBytes()` and `exportAsStrings()` alike. They are disabled by default, since a pass deletes content. ## Enabling them ```kotlin dataPack("mypack") { configuration { optimization() } // ... rest of datapack code } ``` Writing the `optimization { }` block enables the passes, and every built-in pass runs in order. Each pass that changed something prints one summary line, `[kore:prune-empty-functions] pruned 12 empty functions and 4 calls to them`. Set `verbose = false` to silence them. The lines go through `KoreLogger`, see [Configuration](/docs/guides/configuration#logging) to route them to your own logger. ```kotlin configuration { optimization { verbose = false } } ``` The built-in passes run in this order, each one feeding the next: `prune-empty-functions`, `simplify-execute-chains`, `hoist-conditions-into-selectors`, `reorder-selector-arguments`, `dedupe-functions`, `prune-unreferenced-generated-functions`, `warn-unreachable-code`. ## `prune-empty-functions` Removes user functions that contain no command, then removes the calls made to them. Comment-only functions count as empty, since they do nothing in game. A function is kept when its id still appears in any resource, so a function listed in a function tag, used as an advancement reward or targeted by an item modifier survives even while empty. A call is only removed when removing it changes nothing. Lines carrying `store`, `summon`, `on`, `return` or an `if function` condition keep their call, because the `execute` chain itself has an effect there, and macro lines (starting with `$`) are left alone. A function mentioned by any line that isn't a removable call (`schedule function`, `return run function`, a kept call) is kept too, so no line ever points at a missing function. Pruning repeats until nothing changes, so a function left empty by the removal of its own calls is pruned in turn: ```kotlin val empty = function("empty") {} val relay = function("relay") { function(empty) } function("caller") { function(relay) } // all three functions are gone ``` ## `simplify-execute-chains` Rewrites every `execute` chain into the shortest form running the exact same command. - `execute run ` loses its chain entirely, and `run execute` merges into one chain: `execute as @a run execute at @s run say hi` becomes `execute as @a at @s run say hi`. - An `as @s` clause preceded by another `as` is dropped, since the executor it re-selects is already the current one. - A clause repeated right after itself is dropped when running it twice changes nothing, so `at @s at @s` collapses while `at @e[type=pig] at @e[type=pig]` stays (the second one forks over every pig again), and so does `positioned ~ ~1 ~ positioned ~ ~1 ~` (the second one moves one block further). - `if entity` and `unless entity` on `@e` or `@a` only test whether something matches, so they lose their `sort` and get `limit=1`, which lets the game stop scanning at the first match instead of collecting and sorting every entity. - `if data ` guarding a `data remove` of the same path is dropped, since the removal fails on its own when the path is missing. A chain carrying `store` keeps it, the stored result being different. ``` execute if entity @e[type=zombie,sort=nearest] run say near execute if entity @e[type=zombie,limit=1] run say near execute if data storage ns:s queue[0] run data remove storage ns:s queue[0] data remove storage ns:s queue[0] ``` A leading `as @s` is kept: a function called from a function tag has no executor, so the chain must still fail there. Macro lines, and chains whose clauses contain a quoted string, are left alone. ## `hoist-conditions-into-selectors` Moves the score conditions testing the executor into the selector that picked it: ``` execute as @e[type=marker] if score @s timer matches 1 run say hi execute as @e[type=marker,scores={timer=1}] run say hi ``` The second form builds one execution context per matching entity instead of one per marker, and runs one command node less. The range syntax is identical on both sides, so the rewrite is a move, not a translation. A condition is only hoisted while nothing between the `as` and the condition can change the executor (`as`, `on`, `summon`, `store`), and only into an `@a`, `@e` or `@s` selector carrying neither `scores` nor `limit`: a limit picks its entities before the hoisted condition would filter them, and `@p`, `@r` and `@n` carry an implicit `limit=1`. A second condition on an objective already hoisted stays a condition, since `scores` keeps only one range per objective. ## `reorder-selector-arguments` The game tests selector arguments in the order they are written, so `@e[nbt={...},type=marker]` deserializes the NBT of every entity in range before checking the type, while `@e[type=marker,nbt={...}]` reads it for markers only. The result set is the same either way, which makes the reordering free. The pass sorts them cheapest first, roughly `type`, `tag`, `team`, `scores`, `level`, `gamemode`, `name`, position and distance, then `advancements`, `predicate` and `nbt`. ## `dedupe-functions` Merges functions sharing the exact same body and redirects the calls to the survivor. `addGeneratedFunction` already merges two generated functions built with identical lines, but only at the moment they are created, and two of them become identical again once `simplify-execute-chains` rewrites their lines. Hand-written functions stay untouched by default, since their names are part of what the pack exposes and something outside the pack may call them. Opt in with `this += DedupeFunctionsPass(includeUserFunctions = true)` after removing the default instance. A duplicate is also kept when a resource mentions its id, since a function tag or an advancement reward holds it in a typed field the pass cannot rewrite. ## `prune-unreferenced-generated-functions` Generated functions only exist because a DSL construct created one, so they are unreachable as soon as the line calling them is gone, which happens when a later edit of the same `DataPack` drops the callsite or when an earlier pass removes it. Pruning a function re-checks the generated functions it called, so a chain of generated functions calling each other collapses in one run. Two of them calling each other are kept, since each still references the other. Every pass reading references (`prune-empty-functions`, `dedupe-functions` and this one) indexes the whole pack once, matching whole `namespace:path` tokens: `ns:foo` never counts as a mention of it inside `ns:foo/bar` or `xns:foo`. ## `warn-unreachable-code` Reports the commands sitting after an unconditional `return` in the same function, which the game never runs. Only a top-level `return` ends the function for sure, so a `return` behind an `execute` chain or on a macro line is ignored. Nothing is removed: dead code after a `return` is almost always a mistake in the surrounding logic rather than something to silently drop. ## Writing your own pass A pass is a `DataPackPass`, mutating the pack in place and reporting what it did: ```kotlin data object PrefixCommentPass : DataPackPass { override val name = "prefix-comment" override fun run(dataPack: DataPack): PassResult { dataPack.functions.forEach { it.lines.add(0, "# built with Kore") } return PassResult(dataPack.functions.size, "commented ${dataPack.functions.size} functions") } } configuration { optimization { this += PrefixCommentPass } } ``` `this -= PruneEmptyFunctionsPass` drops a built-in pass, and `passes` can be replaced outright to control the exact list and its order. ## What to read next - [Configuration](/docs/guides/configuration) - the rest of the `configuration { }` block - [Functions](/docs/commands/functions) - what the passes operate on --- ## Kore Version Support - Minecraft Versions, Versioning and Deprecations --- root: .components.layouts.MarkdownLayout title: Kore Version Support - Minecraft Versions, Versioning and Deprecations nav-title: Version Support description: Which Minecraft version each Kore release targets, how to read versions like 2.15.0-26.2, what counts as a breaking change and how deprecations migrate. keywords: kore version, kore minecraft version, datapack library versioning, kore breaking changes, kore deprecation, kore snapshot builds, pack format date-created: 2026-10-07 date-modified: 2026-10-07 routeOverride: /docs/guides/version-support position: 9 --- # Version support and stability Every Kore artifact carries two versions in one string: the Kore version, then the Minecraft version it targets. ```kotlin dependencies { implementation("io.github.ayfri.kore:kore:2.16.0-26.2") // Kore 2.16.0, for Minecraft 26.2 } ``` The [Version Matrix](/docs/home#version-matrix) lists the latest Kore release for each Minecraft version, built from the GitHub releases on every website build. ## Reading a version Kore versions never contain a dash, so everything before the first `-` is the Kore version and everything after it is the Minecraft version: | Version | Kore | Minecraft | |--------------------------|----------|------------------------------| | `2.15.0-26.2` | `2.15.0` | 26.2 | | `2.13.1-26.2-pre-3` | `2.13.1` | 26.2 Pre-Release 3 | | `2.12.0-26.2-snapshot-8` | `2.12.0` | 26.2 Snapshot 8 | | `2.15.0-SNAPSHOT` | `2.15.0` | continuous build of `master` | That is why the string isn't plain semver: tools comparing versions have to split it first. The Kore part follows its own numbering: | Change | Example | Meaning | |----------------|-------------------|---------------------------------------------------------------------| | Second number | `2.14.0 → 2.15.0` | Breaking changes possible, read the release notes | | Third number | `2.13.0 → 2.13.1` | Fixes and additions, existing code keeps compiling | | Minecraft part | `-26.1.2 → -26.2` | New target version, see [below](#one-minecraft-version-per-release) | All modules (`kore`, `oop`, `helpers`, `bindings`) and the [Gradle plugin](/docs/guides/gradle-plugin) share the same version, so one string goes in both the `plugins { }` and the `dependencies { }` block. ## One Minecraft version per release A Kore release targets exactly one Minecraft version. That version decides: - the generated registries (`Items`, `Blocks`, `Biomes`...), downloaded from the game data of that version, so an item removed from the game disappears from `Items` too; - the shape of every command and JSON resource Kore writes; - the default `min_format`/`max_format` of `pack.mcmeta`, both set to that version's pack format. `MINECRAFT_VERSION` (`io.github.ayfri.kore.generated`) holds the targeted version at runtime. Loading the pack in another Minecraft version only works when nothing it uses changed between the two versions. You can widen the declared range with `minFormat`/`maxFormat` and split version-specific files with overlays, both shown in [Creating a Datapack](/docs/guides/creating-a-datapack#pack-metadata), but Kore only checks its output against the targeted version. Fixes land on the latest release only, they aren't backported to older Minecraft targets. To stay on an older game version, pin the last Kore release made for it in the Version Matrix. ## Snapshot and pre-release builds Kore publishes builds for Minecraft snapshots, pre-releases and release candidates, tagged with the game version (`2.12.0-26.2-snapshot-8`). They let you start on the next version early, with two caveats: - Mojang changes snapshot features between snapshots, and Kore follows: an API added for one snapshot can change in the next one. - They are GitHub pre-releases, so the stable rows of the Version Matrix skip them; the latest one shows up as the `Snapshot` row. The `-SNAPSHOT` build is something else: a Maven snapshot published from every commit on `master`, see [Getting Started](/docs/getting-started). Use it to try a fix before its release, never in a pack you ship. ## What counts as a breaking change These bump the second number: - removing or renaming a public declaration; - changing a signature so existing calls stop compiling; - changing what an existing call writes to the pack, when the game didn't ask for it. A Minecraft update is the exception. When Mojang renames a command argument, removes a field or changes a JSON shape, Kore writes what the new version reads, even if that breaks a Kore API: a pack that compiles but fails to load is worse than a compile error. Those changes come with the Minecraft part of the version and are listed in the release notes. ## Deprecations A replaced API stays available under `@Deprecated`, with a message naming its replacement and, when the new call maps one to one, a `ReplaceWith` that the IDE applies through its *Replace with* quick fix: ```kotlin advancement("get_diamond") { // Deprecated, the IDE offers the replacement criteria("get_diamond", InventoryChanged()) // Current form criteria { inventoryChanged("get_diamond") } } ``` Fields the game ignores are deprecated with the reason and the field it reads instead. ## Release notes Every release lists its additions, fixes and breaking changes on the [Updates](/updates) page and on [GitHub releases](https://github.com/Ayfri/Kore/releases). Read the notes before bumping the second number or the Minecraft part. ## What to read next - [Getting Started](/docs/getting-started) - adding Kore to a project, stable or snapshot - [Creating a Datapack](/docs/guides/creating-a-datapack) - pack format ranges and overlays - [Debugging](/docs/guides/debugging) - finding what broke after an update --- ## Debugging Kore Datapacks - From In-Game Errors Back to Kotlin --- root: .components.layouts.MarkdownLayout title: Debugging Kore Datapacks - From In-Game Errors Back to Kotlin nav-title: Debugging description: Trace an in-game datapack error back to the Kotlin that wrote it, read generated function names, validate the output with Spyglass and catch load errors in CI. keywords: kore debugging, datapack debugging, minecraft function error, failed to load function, spyglass datapack, packtest, debug function, generated_scopes date-created: 2026-10-07 date-modified: 2026-10-07 routeOverride: /docs/guides/debugging position: 10 --- # Debugging a Kore datapack Kore writes plain `.mcfunction` and JSON files, so everything that works on a hand-written pack works on a Kore pack: the game log, `/debug function`, Spyglass, PackTest. This page shows how to use them, and how to get from a file of the generated pack back to the Kotlin that wrote it. ## Make the output readable first The defaults aim at releases. While debugging, turn on pretty-printed JSON and caller comments, and write the pack to a folder instead of a zip, so you can open and search it: ```kotlin dataPack("my_pack") { configuration { prettyPrint = true generateCommentOfGeneratedFunctionCall = true } // ... }.generate() ``` [Configuration](/docs/guides/configuration#development-vs-release-setups) shows how to switch both flags from one build constant. ## Reading generated function names The functions you name with `function("give_reward")` keep their name. The ones Kore creates for you land in `data//function/generated_scopes/`, named after a hash of their body: | Created by | File name | |----------------------------------------------|-------------------------------| | `execute { run { ... } }` with several lines | `generated_` | | `load { }` / `tick { }` without a name | `load_` / `tick_` | | `schedule` with a block | `schedule_` | | `hashedGeneratedFunction("on_click") { }` | `on_click_` | The hash only depends on the commands inside, so the same body gets the same name on every build, and two places producing the same body share one file. Changing one command changes the name. With `generateCommentOfGeneratedFunctionCall` on, each generated function starts with the functions calling it: ```mcfunction # Called by my_pack:shop/buy give @s minecraft:diamond tellraw @s "Reward!" ``` Without it, search the pack for the generated function's name, the caller holds the `function my_pack:generated_scopes/generated_` line. When a generated function keeps coming up, give it a real name instead: ```kotlin execute { asTarget(allPlayers()) run("give_reward") { give(self(), Items.DIAMOND) tellraw(self(), textComponent("Reward!")) } } // data/my_pack/function/generated_scopes/give_reward.mcfunction ``` ## From an in-game error back to Kotlin When a function contains a command the game can't parse, `/reload` skips that whole function and writes the reason to the game log (`logs/latest.log`, or the launcher's log window): ``` Failed to load function my_pack:shop/buy ... Whilst parsing command on line 4: Unknown or incomplete command ... ``` 1. Open `data/my_pack/function/shop/buy.mcfunction` in the generated pack and go to line 4. 2. `shop/buy` is the name you gave `function("buy", directory = "shop")`, so that is the Kotlin block to look at. For a `generated_scopes/` file, find its caller first, as shown above. 3. Search your Kotlin sources for a literal of that line (a score name, a tag, a text) to land on the call. JSON resources fail the same way, with the resource path (`my_pack:loot_table/chest_reward`) and the field the game refused. Most errors never get that far: Kore's builders are typed against the targeted Minecraft version, and generation throws when two resources write one path with different contents: ``` Two resources of datapack 'my_pack' write 'data/my_pack/function/init.mcfunction' with different contents, rename one of them. ``` A pack that loads in one game version and fails in another usually targets a different version than the game, see [Version Support](/docs/guides/version-support#one-minecraft-version-per-release). ## Validating the output with Spyglass [Spyglass](https://spyglassmc.com/) checks every command and JSON file against the game's data, the same checks the game runs on `/reload`, but inside your editor. Open the generated pack folder (the one holding `pack.mcmeta`) in VS Code with the [Spyglass extension](https://marketplace.visualstudio.com/items?itemName=SPGoding.datapack-language-server), and pin the game version in a `spyglass.json` at its root: ```json { "env": { "gameVersion": "26.2" } } ``` Use the version in your Kore artifact (`MINECRAFT_VERSION` at runtime). With `"Auto"`, Spyglass reads it from `pack.mcmeta`. ## Tracing a function at runtime Kore's [`debug` helpers](/docs/commands/functions#debugging) print each command to the chat as it runs: ```kotlin function("buy") { debug { scoreboard.players.remove(self(), "coins", 10) give(self(), Items.DIAMOND) } } ``` The vanilla `/debug function my_pack:shop/buy` goes further: it runs the function once and writes a trace of every command it ran, nested calls included, to a file in the `debug` folder of the game directory (the server folder on a dedicated server). Generated functions show up in it under their `generated_scopes/` name. ## Catching errors in CI [PackTest](https://github.com/misode/packtest) is a Fabric mod that runs datapack tests on a headless server. Started with `-Dpacktest.auto -Dpacktest.auto.annotations`, it exits with the number of failed tests and reports every resource load error as a GitHub annotation, so a pack that no longer loads fails the build. Its README holds a ready-to-copy workflow: point its `cp -r datapack ...` step at your Kore output folder. For gameplay tests, Kore generates the vanilla GameTest registries, see [Test Features](/docs/advanced/test-features). ## Common errors | Symptom | Cause | Fix | |---------------------------------------------------------------------|-----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------| | The pack shows as incompatible in `/datapack list` | The game version differs from the one your Kore release targets | Use the Kore release for that game version, see [Version Support](/docs/guides/version-support) | | `Two resources of datapack ... write ... with different contents` | Two `function("init")`, or two resources with one file name | Rename one of them | | `The pack format range of the other pack is different` when merging | The merged pack targets another game version | See [Pack format compatibility](/docs/guides/creating-a-datapack#pack-format-compatibility) | | A change doesn't show up in game | The game still runs the previous files | Run `/reload`, or use [`koreRun --continuous`](/docs/guides/gradle-plugin#the-development-loop) with RCON | | `Unknown function` when calling a function | The function failed to load | Check the game log for `Failed to load function` | ## What to read next - [Functions](/docs/commands/functions) - generated functions and the `debug` helpers - [Configuration](/docs/guides/configuration) - development and release settings - [Version Support](/docs/guides/version-support) - which game version a Kore release targets - [Known Issues](/docs/advanced/known-issues) - limits Kore documents --- # Commands ## Minecraft Commands in Kore - Type-Safe Command DSL --- root: .components.layouts.MarkdownLayout title: Minecraft Commands in Kore - Type-Safe Command DSL nav-title: Commands description: Every Minecraft command in a type-safe Kotlin DSL. From /say, /teleport, and /give to /execute, /data, /scoreboard, and /summon -- all with code examples and generated mcfunction output. keywords: minecraft commands, kore commands, kotlin commands dsl, mcfunction generator, execute command, data command, teleport command, summon command, minecraft command builder, type-safe commands date-created: 2026-02-03 date-modified: 2026-09-27 routeOverride: /docs/commands/commands --- # Commands Kore provides type-safe builders for all Minecraft commands. This page covers both simple and complex command usage with examples. Commands are used inside [Functions](/docs/commands/functions) to perform actions in the game. For dynamic command arguments, see [Macros](/docs/commands/macros). ## Simple Commands Simple commands are straightforward and take basic arguments like strings, numbers, or [selectors](/docs/concepts/selectors). ### Say Command The `say` command broadcasts a message to all players in the chat. The message appears with the sender's name (the entity executing the command). For more advanced chat formatting, see [Chat Components](/docs/concepts/chat-components). ```kotlin function("greetings") { say("Hello, world!") say("Welcome to the server!") } ``` Generated output: ```mcfunction say Hello, world! say Welcome to the server! ``` ### Teleport Command The `teleport` command instantly moves entities to a new location. You can teleport to absolute coordinates, relative positions, or another entity's location. Optionally specify rotation (yaw/pitch) for the entity to face after teleporting. ```kotlin function("teleport_examples") { teleport(allPlayers(), vec3(100, 64, 100)) // second argument is the destination, so this sends everyone to the command's executor teleport(allPlayers(), self()) // rotation is yaw then pitch: yaw turns left/right, pitch looks up/down teleport(self(), vec3(0, 100, 0), rotation(0.rot, 90.rot)) } ``` Generated output: ```mcfunction teleport @a 100 64 100 teleport @a @s teleport @s 0 100 0 0 90 ``` ### Give Command The `give` command adds items directly to a player's inventory. If the inventory is full, items drop on the ground. You can specify item count and use [Components](/docs/concepts/components) for custom item data. ```kotlin function("give_items") { give(allPlayers(), Items.DIAMOND_SWORD) give(allPlayers(), Items.GOLDEN_APPLE, 64) } ``` Generated output: ```mcfunction give @a minecraft:diamond_sword give @a minecraft:golden_apple 64 ``` ### Kill Command The `kill` command instantly removes entities from the world. Killed entities trigger death events (drops, death messages for players). Use selectors to target specific entity types. ```kotlin function("cleanup") { kill(allEntities { type = EntityTypes.ZOMBIE }) kill(self()) } ``` Generated output: ```mcfunction kill @e[type=minecraft:zombie] kill @s ``` ### Kick Command The `kick` command disconnects players from the server, with an optional reason that can contain target selectors. It needs permission level 3, one above the default `function-permission-level` of 2, so a function only runs it on a server that raises this setting in `server.properties`. ```kotlin function("kick_examples") { kick(allPlayers()) kick(allPlayers(), "Server restarting") } ``` Generated output: ```mcfunction kick @a kick @a Server restarting ``` ### Effect Command The `effect` command applies or removes status effects (like Speed, Regeneration, Poison) from entities. Effects have duration (in seconds or infinite) and amplifier levels (0 = level I, 1 = level II, etc.). ```kotlin function("effects") { effect(allPlayers()) { give(Effects.SPEED, duration = 60, amplifier = 1) } effect(self()) { giveInfinite(Effects.REGENERATION) } effect(allPlayers()) { clear() } effect(self()) { clear(Effects.POISON) } } ``` Generated output: ```mcfunction effect give @a minecraft:speed 60 1 effect give @s minecraft:regeneration infinite effect clear @a effect clear @s minecraft:poison ``` ### Gamemode Command The `gamemode` command changes a player's game mode (Survival, Creative, Adventure, Spectator). Each mode has different abilities and restrictions. ```kotlin function("modes") { gamemode(Gamemode.CREATIVE, allPlayers()) gamemode(Gamemode.SURVIVAL, player("Steve")) } ``` Generated output: ```mcfunction gamemode creative @a gamemode survival @a[limit=1,name=Steve] ``` ### Time Command The `time` command controls world clocks. Time is measured in [ticks](/docs/concepts/time) (20 ticks = 1 second, 24 000 ticks = 1 Minecraft day). The `time` property on a `Function` returns a `Time` DSL scope. For a full reference covering world clocks, timelines, time markers, and the `timeCheck` predicate, see [World Clocks](/docs/data-driven/world-clocks). #### Basic Time Operations ```kotlin function("time_control") { time.add(6000) // advance the clock, 6000 ticks = 5 minutes time.add(1.days) // units spare you the maths, 1 day = 24000 ticks time.pause() // freeze the clock, the world keeps running time.resume() time.set(TimePeriod.DAY) // jump to a named moment instead of a raw tick time.set(6000) time.query(TimeType.DAYS) time.queryTime() // total ticks since the world was created } ``` Generated output: ```mcfunction time add 6000 time add 1d time pause time resume time set day time set 6000 time query day time query time ``` #### Querying Timelines Use `query(timeline)` to read a timeline's progress, and `queryRepetitions(timeline)` to read how many times it has looped: ```kotlin function("time_query_timeline") { time.query(Timelines.DAY) time.queryRepetitions(Timelines.DAY) } ``` Generated output: ```mcfunction time query minecraft:day time query minecraft:day repetitions ``` #### Setting the Day-Night Cycle Rate Use `rate(rate)` to control how fast the day-night cycle progresses. `1` is the default speed, `0` freezes the cycle, and the maximum is `1000`. This is independent of the server tick rate: ```kotlin function("time_rate") { time.rate(1.0f) // default speed time.rate(0.0f) // sun and moon stop moving, but the world still ticks time.rate(2.0f) // days pass twice as fast time.rate(0.5f) // days pass half as fast } ``` Generated output: ```mcfunction time rate 1 time rate 0 time rate 2 time rate 0.5 ``` #### Setting to a Time Marker `TimeMarkerArgument` (created with the `timeMarker()` factory) references a named tick position defined inside a timeline. Pass it to `time.set()` to jump the clock to that position: ```kotlin function("skip_to_noon") { time.set(timeMarker("noon", "mymod")) } ``` Generated output: ```mcfunction time set mymod:noon ``` #### Targeting a Specific Clock with `time.of(clock)` When your datapack defines multiple [world clocks](/docs/data-driven/world-clocks), use `time.of(clock)` to scope every subcommand to that clock. It returns a `TimeWithClock` instance that mirrors the full `Time` API: ```kotlin val seasonClock = worldClock("season") function("season_control") { time.of(seasonClock).add(6000) time.of(seasonClock).pause() time.of(seasonClock).resume() time.of(seasonClock).set(TimePeriod.DAY) time.of(seasonClock).set(timeMarker("summer", "mymod")) time.of(seasonClock).query(TimeType.DAYS) time.of(seasonClock).query(Timelines.DAY) time.of(seasonClock).queryRepetitions(Timelines.DAY) time.of(seasonClock).queryTime() time.of(seasonClock).rate(2.0f) } ``` Generated output: ```mcfunction time of my_datapack:season add 6000 time of my_datapack:season pause time of my_datapack:season resume time of my_datapack:season set day time of my_datapack:season set mymod:summer time of my_datapack:season query day time of my_datapack:season query minecraft:day time of my_datapack:season query minecraft:day repetitions time of my_datapack:season query time time of my_datapack:season rate 2 ``` ### Weather Command The `weather` command changes the world's weather state. Clear weather has full sunlight, rain reduces light and affects mob spawning, thunder enables lightning strikes and charged creeper creation. ```kotlin function("weather_control") { weatherClear() weatherRain(6000) weatherThunder() } ``` Generated output: ```mcfunction weather clear weather rain 6000 weather thunder ``` ### Summon Command The `summon` command spawns a new entity at the specified location. You can provide NBT data to customize the entity's properties (name, AI, equipment, etc.). ```kotlin function("spawn_mobs") { summon(EntityTypes.ZOMBIE, vec3(0, 64, 0)) summon(EntityTypes.CREEPER, vec3()) { this["CustomName"] = "Boom" this["NoAI"] = true } } ``` Generated output: ```mcfunction summon minecraft:zombie 0 64 0 summon minecraft:creeper ~ ~ ~ {CustomName:"Boom",NoAI:1b} ``` The trailing block is the raw [NBT builder](/docs/concepts/nbts), so entity tags are written by name and are not type-checked against the entity type. For item entities, the [`oop` module](/docs/oop/items) provides a typed alternative: `itemStack(...).summon { }` builds the `Item` compound for you and exposes the common entity tags (`Motion`, `Rotation`, `Tags`, `Glowing`, `NoGravity`, `PickupDelay`, `Age`, …) as named properties on `ItemEntitySummonData` instead of string keys. See [Items → Configuring the spawned entity](/docs/oop/items#configuring-the-spawned-entity). Higher-level spawning options: - [Spawners](/docs/oop/spawners) - reusable named spawner handles with a fixed type and position. - [Display Entities](/docs/helpers/display-entities) - block, item, and text displays with transformations and interpolation. - [Mannequins](/docs/helpers/mannequins) - posable player-model entities. ### SetBlock Command The `setblock` command places a single block at the specified coordinates. Use modes to control behavior: `destroy` (drops items), `keep` (only if air), or `replace` (default). ```kotlin function("build") { setBlock(vec3(0, 64, 0), Blocks.DIAMOND_BLOCK) setBlock(vec3(0, 65, 0), Blocks.STONE, SetBlockMode.REPLACE) } ``` Generated output: ```mcfunction setblock 0 64 0 minecraft:diamond_block setblock 0 65 0 minecraft:stone replace ``` ### Fill Command The `fill` command fills a rectangular region with blocks. `FillOption` picks the mode: `HOLLOW` (outer shell, air inside), `OUTLINE` (shell without clearing inside), `KEEP` (only air blocks) and `DESTROY` (drops items). Passing a block filter instead emits `replace `, changing only the blocks that match it. ```kotlin function("fill_area") { fill(vec3(0, 64, 0), vec3(10, 70, 10), Blocks.STONE) fill(vec3(0, 64, 0), vec3(10, 70, 10), Blocks.AIR, Blocks.STONE) fill(vec3(0, 64, 0), vec3(10, 70, 10), Blocks.GLASS, FillOption.HOLLOW) } ``` Generated output: ```mcfunction fill 0 64 0 10 70 10 minecraft:stone fill 0 64 0 10 70 10 minecraft:air replace minecraft:stone fill 0 64 0 10 70 10 minecraft:glass hollow ``` ### Enchant Command The `enchant` command adds an enchantment to the item held by the target entity. The enchantment must be compatible with the item type. For more control over enchantments, see [Enchantments](/docs/data-driven/enchantments). ```kotlin function("enchant_examples") { enchant(self(), Enchantments.MENDING) enchant(self(), Enchantments.SHARPNESS, 5) } ``` Generated output: ```mcfunction enchant @s minecraft:mending enchant @s minecraft:sharpness 5 ``` ### Difficulty Command The `difficulty` command gets or sets the world's difficulty level (Peaceful, Easy, Normal, Hard). Difficulty affects mob damage, hunger depletion, and whether hostile mobs spawn. Called without arguments it queries the current value. ```kotlin function("difficulty_examples") { difficulty() // no argument reads the current difficulty instead of setting it difficulty(Difficulty.HARD) } ``` Generated output: ```mcfunction difficulty difficulty hard ``` ### SpawnPoint Command The `spawnpoint` command sets where a player respawns after death. Each player can have their own spawn point. Called without a position it uses the executor's current one, and an optional rotation sets the facing direction on respawn. ```kotlin function("spawnpoint_examples") { spawnPoint() // sets the executor's spawn to wherever the command runs from spawnPoint(self()) spawnPoint(self(), vec3(100, 64, 100)) spawnPoint(self(), vec3(100, 64, 100), rotation(90, 0)) } ``` Generated output: ```mcfunction spawnpoint spawnpoint @s spawnpoint @s 100 64 100 spawnpoint @s 100 64 100 90 0 ``` ### SetWorldSpawn Command The `setworldspawn` command sets the default spawn point for all new players and players without a personal spawn point. This is where the world compass points to. ```kotlin function("worldspawn_examples") { setWorldSpawn() setWorldSpawn(vec3(0, 64, 0)) setWorldSpawn(vec3(0, 64, 0), rotation(0, 0)) } ``` Generated output: ```mcfunction setworldspawn setworldspawn 0 64 0 setworldspawn 0 64 0 0 0 ``` ### PlaySound Command The `playsound` command plays a sound event (from `SoundEvents`) to players. Its arguments are positional, so when you skip one but give a later one, Kore fills the skipped ones with the game defaults: `master` for the source, `@s` for the target, `~ ~ ~` for the position and `1` for the volume and pitch. ```kotlin function("playsound_examples") { playSound(SoundEvents.Entity.Bat.TAKEOFF) playSound(SoundEvents.Entity.Bat.TAKEOFF, target = allPlayers()) playSound(SoundEvents.Entity.Bat.TAKEOFF, volume = 2.0) playSound(SoundEvents.Entity.Bat.TAKEOFF, PlaySoundMixer.PLAYER, allPlayers(), vec3(), 1.0, 2.0, 0.5) } ``` Generated output: ```mcfunction playsound minecraft:entity.bat.takeoff playsound minecraft:entity.bat.takeoff master @a playsound minecraft:entity.bat.takeoff master @s ~ ~ ~ 2 playsound minecraft:entity.bat.takeoff player @a ~ ~ ~ 1 2 0.5 ``` ### StopSound Command The `stopsound` command stops currently playing sounds for players. You can filter by sound source (master, music, weather, etc.) and specific sound event (from `SoundEvents`, not the sound files in `Sounds`). Useful for stopping looping sounds or music. ```kotlin function("stopsound_examples") { stopSound(self()) stopSound(self(), PlaySoundMixer.MASTER) stopSound(self(), PlaySoundMixer.MASTER, SoundEvents.Entity.Bat.TAKEOFF) stopSound(self(), sound = SoundEvents.Entity.Bat.TAKEOFF) stopSoundAllSources(self()) stopSoundAllSources(self(), SoundEvents.Entity.Bat.TAKEOFF) } ``` Generated output: ```mcfunction stopsound @s stopsound @s master stopsound @s master minecraft:entity.bat.takeoff stopsound @s * minecraft:entity.bat.takeoff stopsound @s * stopsound @s * minecraft:entity.bat.takeoff ``` ### Stopwatch Command The `stopwatch` command manages server-side timers that count game ticks. Stopwatches persist across sessions and can be queried in execute conditions. Useful for cooldowns, timed events, and measuring durations. ```kotlin function("stopwatch_examples") { val myStopwatch = stopWatch("my_timer") stopwatchCreate(myStopwatch) stopwatchQuery(myStopwatch) stopwatchRestart(myStopwatch) stopwatchRemove(myStopwatch) } ``` Generated output: ```mcfunction stopwatch my_datapack:my_timer create stopwatch my_datapack:my_timer query stopwatch my_datapack:my_timer restart stopwatch my_datapack:my_timer remove ``` You can also use stopwatches in execute conditions: ```kotlin function("stopwatch_condition") { execute { ifCondition { stopwatch(stopWatch("my_timer"), rangeOrIntStart(100)) } run { say("Timer reached 100 seconds!") } } } ``` Generated output: ```mcfunction execute if stopwatch my_datapack:my_timer 100.. run say Timer reached 100 seconds! ``` ### Message Commands The `msg` command sends a private message to a specific player, and `teammsg` sends a message to all members of the sender's team. `tell` and `w` are aliases of `msg`, `tm` is an alias of `teamMsg` - each pair emits the same command. See [Scoreboards](/docs/concepts/scoreboards) for team management. ```kotlin function("message_examples") { msg(self(), "Hello!") tell(self(), "Hello!") // alias for msg w(self(), "Hello!") // alias for msg teamMsg("Hello team!") tm("Hello team!") // alias for teamMsg } ``` Generated output: ```mcfunction msg @s Hello! msg @s Hello! msg @s Hello! teammsg Hello team! teammsg Hello team! ``` ### Spectate Command The `spectate` command makes a player in Spectator mode view the game from another entity's perspective. Call it without arguments to stop spectating, with one argument for the target to spectate, and with two to also name the spectator. ```kotlin function("spectate_examples") { spectate() // stop spectating and return to your own body spectate(self()) // the entity to look through spectate(self(), self()) // target first, then which player does the spectating } ``` Generated output: ```mcfunction spectate spectate @s spectate @s @s ``` ### Debug Commands These commands are server debugging utilities. `debug` starts/stops profiling and creates a report. `perf` captures performance metrics for 10 seconds. `jfr` starts/stops Java Flight Recorder profiling. ```kotlin function("debug_examples") { debugStart() debugStop() perfStart() perfStop() jfrStart() jfrStop() } ``` Generated output: ```mcfunction debug start debug stop perf start perf stop jfr start jfr stop ``` ## Complex Commands Complex commands have nested structures and multiple sub-commands. Kore provides specialized builders for these. ### Execute Command The `execute` command is one of the most powerful commands in Minecraft. It allows you to: - Change the execution context (who/where the command runs) - Add conditions (only run if criteria are met) - Store command results in scores or NBT - Chain multiple modifiers together The examples below cover the basics. For the full subcommand, condition, store, and `run` reference, see the dedicated [Execute](/docs/commands/execute) page. Use `execute` with [Predicates](/docs/data-driven/predicates) for complex conditions. #### Basic Execute ```kotlin function("execute_basic") { execute { asTarget(allPlayers()) run { say("Hello from execute!") } } } ``` Generated output: ```mcfunction execute as @a run say Hello from execute! ``` Conditions (`if`/`unless`), score comparisons, position/dimension/anchoring context, entity relations, and the full subcommand list are documented on the dedicated [Execute](/docs/commands/execute) page. A quick conditional example: ```kotlin function("execute_conditions") { execute { asTarget(allEntities { limit = 3 sort = Sort.RANDOM }) ifCondition { score(self(), "points") greaterThanOrEqualTo 10 } run { say("You have enough points!") } } } ``` Generated output: ```mcfunction execute as @e[limit=3,sort=random] if score @s points matches 10.. run say You have enough points! ``` #### Execute Store Store command results in scores or NBT: ```kotlin function("execute_store") { execute { storeResult { score(self(), "my_score") } run { time.query(TimeType.DAYS) } } } ``` Generated output: ```mcfunction execute store result score @s my_score run time query day ``` ### Data Command The `data` command reads and writes NBT (Named Binary Tag) data on entities, block entities (chests, signs, etc.), and command storage. NBT stores complex data like inventory contents, entity attributes, and custom tags. Operations include `get` (read), `merge` (combine), `modify` (change specific paths), and `remove` (delete). #### Basic Data Operations ```kotlin function("data_basic") { data(self()) { get("Health") get("Inventory", 1.0) } } ``` Generated output: ```mcfunction data get entity @s Health data get entity @s Inventory 1 ``` #### Data Merge ```kotlin function("data_merge") { data(self()) { merge { this["CustomName"] = "Hero" this["Invulnerable"] = true } } } ``` This `merge { ... }` block uses the same NBT builder described in [NBTs](/docs/concepts/nbts), so you can reuse the same assignment patterns in commands, predicates, and chat-related APIs. Generated output: ```mcfunction data merge entity @s {CustomName:"Hero",Invulnerable:1b} ``` #### Data Modify ```kotlin function("data_modify") { data(self()) { modify("Inventory") { append(Items.DIAMOND) } modify("Tags") { prepend("new_tag") } modify("Health") { set(20) } modify("Pos[0]") { set(self(), "Pos[0]") } } } ``` Generated output: ```mcfunction data modify entity @s Inventory append value "minecraft:diamond" data modify entity @s Tags prepend value new_tag data modify entity @s Health set value 20 data modify entity @s Pos[0] set from entity @s Pos[0] ``` `data modify ... string ... [start] [end]` is also supported for every string-capable operation (`set`, `append`, `insert`, `merge`, `prepend`): ```kotlin function("data_modify_string_ranges") { data(self()) { modify("foo") { append(self(), "name", 1) } modify("foo") { insert(0, self(), "name", 0, 4) } modify("foo") { merge(self(), "name", -5) } modify("foo") { prepend(self(), "name", 0, 2) } modify("foo") { set(self(), "name", 0, 3) } } } ``` Generated output: ```mcfunction data modify entity @s foo append string entity @s name 1 data modify entity @s foo insert 0 string entity @s name 0 4 data modify entity @s foo merge string entity @s name -5 data modify entity @s foo prepend string entity @s name 0 2 data modify entity @s foo set string entity @s name 0 3 ``` #### Data Remove ```kotlin function("data_remove") { data(self()) { remove("CustomName") remove("Tags[0]") } } ``` Generated output: ```mcfunction data remove entity @s CustomName data remove entity @s Tags[0] ``` ### Scoreboard Command The `scoreboard` command manages objectives (score types) and player/entity scores. Scoreboards are essential for tracking game state, creating timers, and building game mechanics. See [Scoreboards](/docs/concepts/scoreboards) for detailed usage. ```kotlin function("scoreboard_examples") { scoreboard.objectives.add("kills", ScoreboardCriteria.PLAYER_KILL_COUNT, textComponent("Player Kills")) scoreboard.objectives.remove("old_objective") scoreboard.objectives.setDisplay(DisplaySlots.sidebar, "kills") scoreboard.players.set(allPlayers(), "kills", 0) scoreboard.players.add(self(), "kills", 1) scoreboard.players.remove(self(), "kills", 5) scoreboard.players.reset(self(), "kills") scoreboard.players.operation(self(), "total", Operation.ADD, self(), "kills") } ``` Criteria come from the `ScoreboardCriteria` enum, or from the builders for compound criteria: `criteriaKilled(EntityTypes.ZOMBIE)`, `criteriaMined(Blocks.STONE)`, `criteriaTeamKill(FormattingColor.RED)`, `criteriaCustom(...)`, and friends. Passing a raw string does not compile - `ScoreboardCriterion` is an interface. `scoreboard.objective(target, "objective")` returns a `PlayerObjective` handle when you want to run several operations on the same score, including the `+=`, `-=`, `++`, `--` operators and the `min` / `max` infix operations: ```kotlin function("score_handle") { val kills = scoreboard.objective(self(), "kills") kills += 1 kills.reset() } ``` Generated output: ```mcfunction scoreboard players add @s kills 1 scoreboard players reset @s kills ``` ### Bossbar Command The `bossbar` command creates and controls boss bars - the progress bars normally shown during boss fights. Boss bars can display custom text, colors, and progress values. They're useful for timers, progress indicators, and UI elements. ```kotlin function("bossbar_examples") { bossBars.add("my_bar", displayName = textComponent("My Boss Bar")) val bar = bossBars.get("my_bar") bar.setColor(BossBarColor.RED) bar.setMax(100) bar.setValue(50) bar.setVisible(true) bar.setPlayers(allPlayers()) bar.setStyle(BossBarStyle.NOTCHED_10) bar.remove() } ``` ### Team Command The `team` command creates and manages teams for players and entities. Teams control PvP (friendly fire), name tag visibility, collision, and chat colors. See [Scoreboards](/docs/concepts/scoreboards) for more on teams. ```kotlin function("team_examples") { teams.add("red_team", textComponent("Red Team")) teams.modify("red_team") { color(Color.RED) friendlyFire(false) seeFriendlyInvisibles(true) } teams.join("red_team", allPlayers()) teams.leave(self()) } ``` ### Attribute Command The `attribute` command reads and modifies entity attributes like max health, movement speed, attack damage, and armor. You can get/set base values or add temporary modifiers that stack. ```kotlin function("attribute_examples") { val maxHealth = attributes(self(), Attributes.MAX_HEALTH) maxHealth.get() maxHealth.base.get() maxHealth.base.set(40.0) val speed = attributes(self(), Attributes.MOVEMENT_SPEED) speed.modifiers.add("speed_boost", value = 0.1, operation = AttributeModifierOperation.ADD_VALUE) speed.modifiers.remove("speed_boost") } ``` ### Schedule Command The `schedule` command delays function execution by a specified time. Useful for timers, cooldowns, and delayed effects. Time can be specified in [ticks, seconds, or days](/docs/concepts/time). See [Scheduler Helper](/docs/helpers/scheduler) for advanced scheduling patterns. ```kotlin function("schedule_examples") { val myFunction = function("delayed_action") { say("This runs later!") } schedule(100.ticks, myFunction) // appends a new schedule schedule(myFunction).replace(5.seconds) // replaces any pending schedule of the function schedule(myFunction).clear() } ``` Generated output: ```mcfunction schedule function my_datapack:delayed_action 100 append schedule function my_datapack:delayed_action 5s replace schedule clear my_datapack:delayed_action ``` ### Loot Command The `loot` command generates items from [Loot Tables](/docs/data-driven/loot-tables) and distributes them to players, containers, or the world. Sources include fishing, killing entities, mining blocks, or direct loot table references. ```kotlin function("loot_examples") { // roll a loot table and put the result straight into the player's inventory loot(self()) { loot(LootTables.Gameplay.CAT_MORNING_GIFT) } // roll it as if fished at a position with that rod, so rod enchantments apply loot(self()) { fish(LootTables.Gameplay.CAT_MORNING_GIFT, vec3(), Items.FISHING_ROD) } // roll the drops the entity would give if killed, without killing it loot(self()) { kill(self()) } // roll the drops the block would give if mined with that tool loot(self()) { mine(vec3(), Items.DIAMOND_PICKAXE) } // drop the items into the container at that position instead of an inventory loot { target { insert(vec3()) } source { kill(self()) } } // overwrite one slot of a container block, CONTAINER[0] being its first slot loot { target { replaceBlock(vec3(), CONTAINER[0]) } source { loot(LootTables.Gameplay.CAT_MORNING_GIFT) } } // overwrite an equipment slot, here the helmet loot { target { replaceEntity(self(), ARMOR.HEAD) } source { loot(LootTables.Gameplay.CAT_MORNING_GIFT) } } // mobs that carry their own inventory (villagers, piglins) use mob.inventory.* loot { target { replaceEntity(self(), MOB.INVENTORY[0]) } source { loot(LootTables.Gameplay.CAT_MORNING_GIFT) } } // the source can also be a loot table written inline, with no separate file loot { target { give(self()) } source { loot { pool { rolls(1f) entries { item(Items.ANVIL) } } } } } } ``` Generated output: ```mcfunction loot give @s loot minecraft:gameplay/cat_morning_gift loot give @s fish minecraft:gameplay/cat_morning_gift ~ ~ ~ minecraft:fishing_rod loot give @s kill @s loot give @s mine ~ ~ ~ minecraft:diamond_pickaxe loot insert ~ ~ ~ kill @s loot replace block ~ ~ ~ container.0 loot minecraft:gameplay/cat_morning_gift loot replace entity @s armor.head loot minecraft:gameplay/cat_morning_gift loot replace entity @s mob.inventory.0 loot minecraft:gameplay/cat_morning_gift loot give @s loot {pools:[{rolls:1.0f,entries:[{type:"minecraft:item",name:"minecraft:anvil"}]}]} ``` The `target` block chooses where items go (`give`, `insert` into a container, `replaceBlock`, `replaceEntity`) and the `source` block chooses where they come from (`loot`, `fish`, `kill`, `mine`). Slot constants come from the item slot types: `CONTAINER[n]` for block inventories, `ARMOR.HEAD` and friends for equipment, and `MOB.INVENTORY[n]` for mobs that carry their own inventory such as villagers and piglins. The last example shows that `source { loot { ... } }` also accepts an inline [loot table](/docs/data-driven/loot-tables) instead of a reference. ### Particle Command The `particle` command spawns visual particle effects in the world. Particles have position, spread (delta), speed, and count. Use `force` mode to make particles visible from far away or through blocks. ```kotlin function("particle_examples") { particle(Particles.ASH) // arguments are position, delta (how far particles scatter), speed, then count particle(Particles.ASH, vec3(), vec3(), 1.0, 2) // force shows the particle even far away or through blocks particle(Particles.ASH, vec3(), vec3(), 1.0, 2, ParticleMode.FORCE) // a last selector limits who sees the particle particle(Particles.ASH, vec3(), vec3(), 1.0, 2, ParticleMode.NORMAL, allEntities()) } ``` Generated output: ```mcfunction particle minecraft:ash particle minecraft:ash ~ ~ ~ ~ ~ ~ 1 2 particle minecraft:ash ~ ~ ~ ~ ~ ~ 1 2 force particle minecraft:ash ~ ~ ~ ~ ~ ~ 1 2 normal @e ``` #### Special Particle Types Some particles carry extra data beyond a position. The `particles { }` block exposes one builder per such type: | Builder | Extra data | |------------------------|-----------------------------------------------------| | `block` | A block state, so the particle matches that block | | `blockCrumble` | A block state, for the crumbling effect | | `blockMarker` | A block state, used to visualise invisible blocks | | `dragonBreath` | A power value | | `dust` | A color and a scale | | `dustColorTransition` | A start color, a scale, and an end color | | `entityEffect` | A color tint | | `fallingDust` | A block state | | `flash` | An ARGB color | | `instantEffect` | A color tint | | `item` | An item stack, [components](/docs/concepts/components) included | | `sculkCharge` | A roll angle in radians | | `shriek` | A delay in ticks | | `tintedLeaves` | A color tint | | `trail` | A color, a target position, and a duration | | `vibration` | A destination position and a travel duration | Every builder also takes an optional trailing position, and `particle(Particles.X)` inside the block emits a plain particle with no extra data. ```kotlin import kotlin.math.PI function("special_particles") { particles { block(Blocks.STONE_SLAB(states = mapOf("half" to "top"))) blockCrumble(Blocks.STONE) blockMarker(Blocks.STONE) fallingDust(Blocks.STONE) dust(Color.PURPLE, 2.0) dust(rgb(0xabcdef), 2.0) dustColorTransition(Color.BLUE, 2.0, Color.RED) dragonBreath(0.5f) entityEffect(color = Color.GREEN) flash(Color.WHITE.toARGB()) instantEffect(color = Color.GREEN) tintedLeaves(Color.RED) item(Items.DIAMOND_SWORD { enchantments { enchantment(Enchantments.SHARPNESS, 5) } }) sculkCharge(PI / 2) shriek(100) trail(Color.RED, Triple(1, 2, 3), 10) vibration(vec3(1, 2, 3), 10) } } ``` Generated output: ```mcfunction particle block{block_state:{Name:"minecraft:stone_slab",Properties:{half:"top"}}} particle block_crumble{block_state:{Name:"minecraft:stone"}} particle block_marker{block_state:{Name:"minecraft:stone"}} particle falling_dust{block_state:{Name:"minecraft:stone"}} particle dust{color:11141375,scale:2.0d} particle dust{color:11259375,scale:2.0d} particle dust_color_transition{from_color:5592575,to_color:16733525,scale:2.0d} particle dragon_breath{power:0.5f} particle entity_effect{color:[0.3333333333333333d,1.0d,0.3333333333333333d]} particle flash{color:"#ffffffff"} particle instant_effect{color:[0.3333333333333333d,1.0d,0.3333333333333333d]} particle tinted_leaves{color:[1.0d,0.3333333333333333d,0.3333333333333333d,1.0d]} particle item{item:{id:"minecraft:diamond_sword",components:{enchantments:{"minecraft:sharpness":5}}}} particle sculk_charge 1.5707963267948966 particle shriek 100 particle trail{color:16733525,duration:10,target:[1,2,3]} particle vibration 1 2 3 10 ``` ### Clone Command The `clone` command copies blocks from one region to another. Supports cross-dimension cloning, filtering by block type, and different modes: `replace` (all blocks), `masked` (non-air only), `move` (removes source). Use `strict` to fail if regions overlap incorrectly. ```kotlin function("clone_examples") { // begin and end are the two opposite corners of the region to copy, // destination is where its lowest corner lands clone { begin = vec3(0, 64, 0) end = vec3(10, 74, 10) destination = vec3(100, 64, 100) } clone { begin = vec3(0, 64, 0) end = vec3(10, 74, 10) destination = vec3(0, 64, 0) from = Dimensions.THE_NETHER to = Dimensions.OVERWORLD } // masked skips air blocks, so the copy does not erase what is already there clone { begin = vec3(0, 64, 0) end = vec3(10, 74, 10) destination = vec3(100, 64, 100) masked(CloneMode.MOVE) } // only copies blocks matching the tag, everything else is left untouched clone { begin = vec3(0, 64, 0) end = vec3(10, 74, 10) destination = vec3(100, 64, 100) filter(Tags.Block.BASE_STONE_OVERWORLD, CloneMode.FORCE) } // source and destination overlap here, strict makes that fail instead of corrupting the copy clone { begin = vec3(0, 64, 0) end = vec3(10, 74, 10) destination = vec3(5, 64, 5) strict = true } } ``` Generated output: ```mcfunction clone 0 64 0 10 74 10 100 64 100 clone from minecraft:the_nether 0 64 0 10 74 10 to minecraft:overworld 0 64 0 clone 0 64 0 10 74 10 100 64 100 masked move clone 0 64 0 10 74 10 100 64 100 filtered #minecraft:base_stone_overworld force clone 0 64 0 10 74 10 5 64 5 strict ``` ### WorldBorder Command The `worldborder` command controls the world border size, position, damage, and warning settings. The time parameter for `add` and `set` is specified in ticks. ```kotlin function("worldborder_examples") { worldBorder { // sizes are the border's full diameter in blocks, times are in ticks add(10.0, time = 200) // grow by 10 blocks over 10 seconds set(1000.0) // instant set(500.0, time = 6000) // shrink over 5 minutes center(0.0, 0.0) damageAmount(0.5f) // damage per second per block past the buffer damageBuffer(5.0) // blocks of grace beyond the border before damage starts setWarningDistance(10) setWarningTime(15) get() } } ``` Generated output: ```mcfunction worldborder add 10 200 worldborder set 1000 worldborder set 500 6000 worldborder center 0 0 worldborder damage amount 0.5 worldborder damage buffer 5 worldborder warning distance 10 worldborder warning time 15 worldborder get ``` ## Selectors Selectors target entities in the world. Kore provides type-safe selector builders with filters for entity type, distance, scores, NBT, and more. If you want a selector-focused walkthrough beyond the command examples below, read the [Selectors](/docs/concepts/selectors) page alongside this reference: ```kotlin function("selector_examples") { tellraw(allPlayers(), textComponent("Hi")) // @a teleport(nearestPlayer(), vec3(0, 64, 0)) // @p give(randomPlayer(), Items.DIAMOND) // @r // sort decides who the limit keeps, so this is the 10 closest zombies within 10 blocks kill(allEntities { type = EntityTypes.ZOMBIE limit = 10 sort = Sort.NEAREST distance = rangeOrIntEnd(10) }) effect(allEntities { scores { score("kills") greaterThanOrEqualTo 5 } }) { give(Effects.STRENGTH, duration = 60) } kill(allEntities { nbt = nbt { this["CustomName"] = "Target" } }) } ``` ## Macros Macros allow dynamic command arguments that are substituted at runtime. They're useful for creating reusable functions with parameters. ```kotlin function("greet_player") { say("Hello, ${macro("player_name")}!") } load { function("greet_player", arguments = nbt { this["player_name"] = "Steve" }) } ``` Generated output: ```mcfunction $say Hello, $(player_name)! ``` For detailed macro usage including macro classes and validation, see [Macros](/docs/commands/macros). ## Raw Commands For commands not yet supported by Kore or for special cases, use `addLine`. This is also useful when working with [Macros](/docs/commands/macros) for fully dynamic commands: ```kotlin function("raw_commands") { addLine("say This is a raw command") addLine("execute as @a run say Hello") } ``` > Note: Using raw commands bypasses type safety. Prefer the DSL builders when available. ## Custom Commands Create your own command builders for mods or custom functionality. See [Functions](/docs/commands/functions) for more details on the Function context: ```kotlin import io.github.ayfri.kore.functions.Function fun Function.myModCommand(target: EntityArgument, value: Int) = addLine(command("mymod", literal(target.asString()), int(value))) function("custom") { myModCommand(self(), 42) } ``` Generated output: ```mcfunction mymod @s 42 ``` For broader composition patterns such as extracting reusable wrappers around commands, the [Cookbook](/docs/guides/cookbook) gives more realistic project-scale examples. ## See Also - [Functions](/docs/commands/functions) - Create and organize command functions - [Macros](/docs/commands/macros) - Dynamic command arguments - [Chat Components](/docs/concepts/chat-components) - Formatted text in commands - [Cookbook](/docs/guides/cookbook) - Practical command composition patterns in real datapacks - [World Clocks](/docs/data-driven/world-clocks) - World clocks, timelines, time markers, and `timeCheck` ### External Resources - [Minecraft Wiki: Commands](https://minecraft.wiki/w/Commands) - Complete command reference - [Minecraft Wiki: Target selectors](https://minecraft.wiki/w/Target_selectors) - Selector syntax --- ## Execute Command - Type-Safe Context, Conditions & Stores in Kore --- root: .components.layouts.MarkdownLayout title: Execute Command - Type-Safe Context, Conditions & Stores in Kore nav-title: Execute description: Master the /execute command with Kore's type-safe DSL. Context subcommands (as, at, positioned), conditions (if/unless), stores (score, bossbar, storage), and the run clause. Full guide with mcfunction examples. keywords: minecraft execute command, execute as at positioned, execute if unless, execute store, datapack execute, kore execute, minecraft command conditions, execute run, context subcommands date-created: 2026-06-24 date-modified: 2026-10-08 routeOverride: /docs/commands/execute --- # Execute `execute` is the most important command in any datapack. It modifies the **context** a command runs in (who runs it, where, facing which way), **branches** on world state, and **stores** a command's result. Almost all runtime logic flows through it - see [Runtime Logic](/docs/concepts/runtime-logic) for the bigger picture, and the [Minecraft Wiki](https://minecraft.wiki/w/Commands/execute) for the vanilla command semantics. In Kore you build an execute chain with the `execute { }` builder. Each call inside the block appends one subcommand, in order, and the final `run` clause is the command that actually runs: ```kotlin function("greet_nearby") { execute { asTarget(allPlayers()) at(self()) run { say("Hello!") } } } ``` This generates `execute as @a at @s run say Hello!`. ## Context subcommands These change who, where, and how the chained command runs. They can be combined freely and apply left to right. ```kotlin execute { align(Axes.XYZ) // floor the position to block grid anchored(Anchor.EYES) // set the local-coordinate anchor asTarget(allPlayers()) // run as each player (changes @s) at(self()) // run at the executor's position/rotation/dimension facing(vec3(10, 64, 10)) // face an absolute position facingEntity(self(), Anchor.EYES) // face an entity's eyes/feet inDimension(Dimensions.THE_END) // switch dimension on(Relation.OWNER) // hop to a related entity (owner, vehicle, ...) positioned(vec3(0, 64, 0)) // override the position positionedAs(self()) // take the position from a target positionedOver(HeightMap.WORLD_SURFACE) // snap Y to a heightmap rotated(rotation(0.rot, 0.rot)) // override the rotation rotatedAs(self()) // take rotation from a target summon(EntityTypes.MARKER) // summon and run as the new entity run { say("context set") } } ``` | Subcommand | Effect | |-------------------------------------------------|-----------------------------------------------------------------------------| | `align(axes, offset?)` | Floors the position onto the block grid on the given axes | | `anchored(anchor)` | Sets `EYES`/`FEET` anchor for `^ ^ ^` and `facing` | | `asTarget(target)` | Runs as each matched entity, changing `@s` and the executor | | `at(target)` | Sets position, rotation, and dimension to the target's | | `facing(vec3)` / `facingEntity(target, anchor)` | Rotates to face a point or an entity | | `inDimension(dimension)` | Switches the execution dimension | | `on(relation)` | Moves to a related entity (`OWNER`, `VEHICLE`, `TARGET`, `PASSENGERS`, ...) | | `positioned(vec3)` / `positionedAs(target)` | Sets the position absolutely or from a target | | `positionedOver(heightMap)` | Sets Y to the top block of a heightmap at the current X/Z | | `rotated(rotation)` / `rotatedAs(target)` | Sets the rotation absolutely or from a target | | `summon(entityType)` | Summons a new entity and runs as/at it | ## Conditions: if / unless `ifCondition` and `unlessCondition` gate the rest of the chain. A block can hold several checks - they are ANDed together. ```kotlin function("conditional") { execute { ifCondition { entity(allPlayers()) // at least one player exists block(vec3(0, 63, 0), Blocks.STONE) // block at pos is stone score(self(), "coins", rangeOrIntStart(10)) // score matches 10.. } unlessCondition { score(self(), "frozen", rangeOrIntStart(1)) // and NOT frozen >= 1 } run { say("All conditions matched") } } } ``` Available checks inside the condition block: | Check | Meaning | |---------------------------------------------|----------------------------------------------------| | `biome(pos, biome)` | The biome at `pos` matches a biome or biome tag | | `block(pos, block)` | The block at `pos` matches a block or block tag | | `blocks(start, end, dest, mode)` | A region matches another region (`ALL` / `MASKED`) | | `data(target, path)` | The data target has something at the NBT path | | `dimension(dimension)` | The execution is in the given dimension | | `entity(target)` | The selector matches at least one entity | | `function(function)` | A function returns a non-zero value | | `items(source, slots, predicate)` | Items in a container's slots match a predicate | | `loaded(pos)` | The chunk at `pos` is fully loaded | | `predicate(...)` | A predicate passes (by id, name, or inline block) | | `score(target, obj, range)` | A score matches an int range | | `score(target, obj, src, srcObj, relation)` | Two scores compare with a relation | | `slots(source, slots)` | A container has any of the selected slots | | `stopwatch(id, range)` | A stopwatch's elapsed seconds are within a range | `inverted { }` flips the checks inside it, `if` becoming `unless` and the reverse. It lets a reusable condition block be negated, `ifCondition { inverted { score(self(), "frozen", rangeOrInt(1)) } }` emitting `execute unless score @s frozen matches 1`. ### Comparing scores fluently Inside a condition block, `score(target, objective)` returns a handle with the full set of infix comparison operators: | Kotlin DSL | Generated syntax | |---------------------------------------------------|-----------------------------------| | `score(self(), "points") equalTo 10` | `if score @s points matches 10` | | `score(self(), "points") greaterThan 10` | `if score @s points matches 11..` | | `score(self(), "points") greaterThanOrEqualTo 10` | `if score @s points matches 10..` | | `score(self(), "points") lessThan 10` | `if score @s points matches ..9` | | `score(self(), "points") lessThanOrEqualTo 10` | `if score @s points matches ..10` | | `score(self(), "points") matches 1..5` | `if score @s points matches 1..5` | | `score(self(), "a") equalTo score(self(), "b")` | `if score @s a = @s b` | Comparing with a literal always emits a `matches` range, since vanilla's `<`, `>` and `=` operators only compare two scores. The relation operators also accept another score handle to compare two scores directly: ```kotlin execute { ifCondition { val coins = score(self(), "coins") coins greaterThanOrEqualTo 10 // if score @s coins matches 10.. coins matches 1..5 // if score @s coins matches 1..5 val lives = score(self(), "lives") coins greaterThan lives // if score @s coins > @s lives } run { say("rich and winning") } } ``` ### Referencing predicates directly If you already have a [Predicate](/docs/data-driven/predicates), pass it straight to `ifCondition`: ```kotlin val isRaining = predicate("is_raining") { weatherCheck(raining = true, thundering = false) } execute { ifCondition(isRaining) run { say("It is raining") } } ``` You can also write a one-off inline predicate without registering a file: ```kotlin execute { ifCondition { predicate { randomChance(0.5f) } } run { say("Heads") } } ``` ## Stores: capturing the result `storeResult` writes the chained command's numeric **return value**; `storeSuccess` writes `1`/`0` for whether it succeeded. Both can target a score, storage, entity NBT, block NBT, or a boss bar. ```kotlin function("count_players") { execute { storeResult { score(literal("#total"), "players") // store the count into a fake player's score } run { // `data get` returns the numeric value stored at the path data(storage("state", "my_pack")) { get("player_count") } } } } ``` Store destinations: | Destination | Builder call | |-------------|---------------------------------------| | Score | `score(target, objective)` | | Storage | `storage(target, path, type, scale)` | | Entity NBT | `entity(target, path, type, scale)` | | Block NBT | `block(pos, path, type, scale)` | | Boss bar | `bossBarValue(id)` / `bossBarMax(id)` | The `type` is a [`DataType`](/docs/concepts/data-storage) (`BYTE`, `SHORT`, `INT`, `LONG`, `FLOAT`, `DOUBLE`) and `scale` is a multiplier applied before writing. ## The run clause `run` is always last. It accepts three forms: ```kotlin // 1. Inline block - a single command is inlined, multiple commands become a generated function execute { at(self()) run { say("inline") } } // 2. An existing function val reward = function("give_reward") { give(self(), Items.DIAMOND) } execute { ifCondition { score(self(), "coins", rangeOrIntStart(100)) } run(reward) } // 3. A newly named generated function execute { asTarget(allPlayers()) run("welcome") { say("Welcome!") } } ``` If you omit `run` entirely, Kore emits the bare `execute ...` chain (useful when the last subcommand, like `summon`, already has an effect). ## Ordering matters Subcommands apply in the order you call them, exactly like vanilla. `asTarget` then `at` is different from `at` then `asTarget`. Keep `run` last - anything after a `run` in the same block replaces the previous run. ## See also - [Runtime Logic](/docs/concepts/runtime-logic) - why execute is the backbone of in-game logic - [Predicates](/docs/data-driven/predicates) - reusable conditions for `ifCondition` - [Commands](/docs/commands/commands) - the full command reference --- ## Minecraft Datapack Functions - Create MCFunctions with Kore DSL --- root: .components.layouts.MarkdownLayout title: Minecraft Datapack Functions - Create MCFunctions with Kore DSL nav-title: Functions description: Create Minecraft datapack functions with Kore's Kotlin DSL. Build tick.json and load.json tags, organize commands into reusable functions, and generate clean MCFunction output. keywords: datapack functions, mcfunction, tick.json datapack, load.json datapack, minecraft function tags, tags/function datapack, kore functions, datapack mcfunction generator, function scheduling, minecraft function creator date-created: 2024-04-06 date-modified: 2026-09-29 routeOverride: /docs/commands/functions --- # Functions Functions represent reusable pieces of logic callable in a datapack. Create a function with the `function` builder: ```kotlin function("my_function") { say("Hello world!") } ``` Then in game, call the function with `/function my_datapack:my_function`. To call functions from other datapacks, see [Bindings](/docs/advanced/bindings). The `function` builder returns a `FunctionArgument` object that you can reuse to call the function from other functions: ```kotlin val myFunction = function("my_function") { say("Hello world!") } function("my_second_function") { function(myFunction) } ``` You can also package this pattern into reusable `Function` extensions: ```kotlin fun Function.myFunction() = function("my_function") { say("Hello world!") } load { function(myFunction()) } ``` That helper may be called from several places without worry. Kore is optimized for recreating the same named function, so using this pattern stays effectively instant while keeping your code easy to organize. If you only want to reuse a small block of commands without generating a separate `/function`, prefer a regular extension instead: ```kotlin fun Function.saySomething() { say("yay") say("also, yay") } load { saySomething() } ``` ## Tags You can set the tag of the current function you're working in with the `setTag` function: ```kotlin function("my_function") { setTag(tagFile = "load", tagNamespace = "minecraft") } ``` This will add the function to the `minecraft:load` tag. But you have simpler builders for the most common tags: ```kotlin load { say("Hello world!") } tick { execute { ifCondition(myPredicate) run { say("Hello world!") } } } ``` - `load` tag: `minecraft:load` - `tick` tag: `minecraft:tick` This will create functions with randomly generated names, but you can also specify the name of the function: ```kotlin load("my_load_function") { say("Hello world!") } ``` # Commands Many common commands have convenience builders like `say`, `teleport`, etc. See the [Commands](/docs/commands/commands) page for a comprehensive guide with examples. For example: ```kotlin function("commands") { say("Hello!") // say command teleport(player("Steve"), vec3(100.0, 64.0, 100.0)) // teleport command } ``` You can also build raw command strings and execute them: ```kotlin addLine("say Hello from raw command!") ``` > Note: This is not recommended, but can be useful for commands not yet supported by the DSL, or if you > use [Macros](/docs/commands/macros). ## Available Commands All commands from the version cited in the [README](https://github.com/Ayfri/Kore/blob/master/README.md) are available. For detailed documentation on each command, see [Commands](/docs/commands/commands). ## Custom Commands You can pretty easily add new commands by creating your own builders. For example, imagine you created a mod that adds a new command `/my_command` that takes a player name and a message as arguments. You can create a builder for this command like this: ```kotlin import io.github.ayfri.kore.functions.Function fun Function.myCommand(player: String, message: String) = addLine(command("my_command", literal(player), literal(message))) ``` Then you can use it like any other command: ```kotlin function("my_function") { myCommand("Steve", "Hello!") } ``` `Argument` types (selectors, positions, resource locations, ranges...) go into `command()` as they are, and so do the enums of Kore's commands like `Gamemode`, which are `EnumArgument`s written as their lowercase name: ```kotlin fun Function.myMode(target: EntityArgument, mode: Gamemode) = addLine(command("my_mode", target, mode)) ``` Your own enum becomes an argument the same way, overriding `asString()` when the game names an entry differently: ```kotlin enum class Speed : EnumArgument { SLOW, FAST, VERY_FAST; override fun asString() = name.lowercase().replace("_", "") } ``` See the code of the repository for more examples.
[Link to `time` command.](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/commands/Time.kt)
[Link to `weather` command.](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/commands/Weather.kt) ## Complex Commands Some commands are more complex and require more than just a few arguments. For example, the [`execute`](/docs/commands/execute) or [`data`](/docs/concepts/data-storage) commands. In that case, you can use complex builders that includes all the arguments of the command. But the syntax may vary depending on the command, so pairing this page with the broader [Commands](/docs/commands/commands) reference and selector-heavy examples from [Selectors](/docs/concepts/selectors) is often helpful. An example of the `execute` command: ```kotlin execute { asTarget(allEntities { limit = 3 sort = Sort.RANDOM }) ifCondition { score(self(), "test") lessThan 10 predicate(myPredicate) } run { // be sure to import the run function, do not use the one from kotlin. teleport(entity) } } ``` You can use predicates in the `ifCondition` block to check complex conditions. See the [Predicates](/docs/data-driven/predicates) documentation for more details. You may also have commands where you can create "contexts". An example of the `data` command: ```kotlin data(self()) { modify("Health", 20) modify("Inventory[0]", Items.DIAMOND_SWORD) } ``` # Macros See [Macros](/docs/commands/macros). # Generated Functions The same way the unnamed `load` and `tick` builders generate their own functions, the `execute` builder also generates a function if you call multiple commands inside the `run` block. ```kotlin execute { run { say("Hello world!") say("Hello world2!") } } ``` This will generate a function that will be called by the `execute` command. > Note: The generated functions will be generated inside a folder named `generated_scopes` in the `functions` folder. > You can change the folder to whatever you want in [Configuration](/docs/guides/configuration). > Note: The generated name follows the pattern `generated_`, where `` is a hash of the function body > (`load_`, `tick_` and `schedule_` for the other builders). The same body always gets the same name, > on every run and platform, and two builders producing the same body reuse a single function. Your own builders get the same naming with `hashedGeneratedFunction(prefix)`, which names the function `_`. `generatedFunctionName(prefix, lines)` returns that name alone, for a function you register yourself: ```kotlin val onClick = hashedGeneratedFunction("on_click") { say("clicked") } // data//function/generated_scopes/on_click_.mcfunction ``` Two generated functions can't share a path with different bodies: `generatedFunction("init")` called twice with two different blocks throws an `IllegalStateException` instead of silently keeping only the first one. The same goes for any two files of the pack at generation time, two `function("init")` or two resources with one file name writing different contents throw instead of dropping one of them. If you want to turn that into an explicit project pattern, the [Cookbook](/docs/guides/cookbook) shows how to wrap reusable logic in `Function` extensions with or without dedicated generated functions. # Debugging You have multiple ways to debug your functions. First, a `debug` function is available, it is pretty much the same as `tellraw` but always displaying the message to everyone. ```kotlin function("my_function") { debug("Hello world!", Color.RED) } ``` You also have a `debug` block for printing a log message to the console for each command you call inside the block. ```kotlin function("my_function") { debug { say("hello !") } } ``` This will add a command call to `tellraw` command, writing the exact command generated, clicking on the text will also call the command. Example of what is generated: ```mcfunction say hello ! tellraw @a {"type":"text","click_event":{"action":"suggest_command","command":"/say hello !"},"hover_event":{"action":"show_text","value":{"type":"text","color":"gray","italic":true,"text":"Click to copy command"}},"text":"/say hello !"} ``` Calling `startDebug()` yourself, without the matching `endDebug()` the `debug` block adds, keeps debug mode on until the pack is written: every command gets its log message, and the written file also starts and ends with a message naming the function. `toString()` and `lines` never contain those two markers, only the written file does. ```kotlin function("my_function") { startDebug() say("hello !") } ``` ```mcfunction tellraw @a [{"type":"text","color":"gray","italic":true,"text":"Running function "},{"type":"text","bold":true,"click_event":{"action":"run_command","command":"function my_datapack:my_function"},"color":"white","hover_event":{"action":"show_text","value":{"type":"text","color":"gray","italic":true,"text":"Click to execute function"}},"italic":true,"text":"my_datapack:my_function"}] say hello ! tellraw @a {"type":"text","click_event":{"action":"suggest_command","command":"/say hello !"},"hover_event":{"action":"show_text","value":{"type":"text","color":"gray","italic":true,"text":"Click to copy command"}},"text":"/say hello !"} tellraw @a [{"type":"text","color":"gray","italic":true,"text":"Finished running function "},{"type":"text","bold":true,"click_event":{"action":"run_command","command":"function my_datapack:my_function"},"color":"white","hover_event":{"action":"show_text","value":{"type":"text","color":"gray","italic":true,"text":"Click to execute function"}},"italic":true,"text":"my_datapack:my_function"}] ``` You can call the command by clicking on the debug texts added. Also running `toString()` in a function will return the generated function as a string, so you can manipulate it as you want. ## See Also - [Commands](/docs/commands/commands) - Complete command reference - [Macros](/docs/commands/macros) - Dynamic command arguments - [Tags](/docs/data-driven/tags) - Function tags for load and tick events - [Cookbook](/docs/guides/cookbook) - Practical patterns for reusable function helpers ### External Resources - [Minecraft Wiki: Function](https://minecraft.wiki/w/Function_(Java_Edition)) - Official function format --- ## Macros --- root: .components.layouts.MarkdownLayout title: Macros nav-title: Macros description: A guide for using macros in Minecraft functions. keywords: minecraft, datapack, kore, guide, macros, functions date-created: 2024-04-06 date-modified: 2026-10-02 routeOverride: /docs/commands/macros --- # Macros Macros allow dynamic command arguments that are substituted at runtime. Added in Minecraft 1.20.2, they enable creating reusable functions with parameters. They are one of the runtime tools covered in [Runtime Logic](/docs/concepts/runtime-logic), and their values typically come from [data storage](/docs/concepts/data-storage). For basic command usage, see [Commands](/docs/commands/commands). The [macros example](/playground#example=macros) of the playground runs in your browser and shows the functions it generates. ## Using Macros To define a macro, use the `macro()` function: ```kotlin say("I'm gonna use the macro ${macro("foo")}") ``` Inside a Minecraft function: ```kotlin function("my_function") { say("This is my macro: ${macro("bar")}") } ``` When called, this will substitute the actual text of the macro. You can also evaluate a list of macros and have fully dynamic commands: ```kotlin eval("command", "arg1", "arg2") // equals to minecraft code: // $$(command) $(arg1) $(arg2) ``` ## Calling functions with macros You can call a function with macros by using the new `arguments` argument. ```kotlin function("my_function", arguments = nbt { this["bar"] = "baz" }) ``` That can also be a DataArgument (block position/entity selector/storage). ```kotlin function( "my_function", arguments = allEntities { type = EntityTypes.MARKER name = "test" }, path = "data.test" // optional path is available ) ``` ## Defining Macro Classes For more complex macro usage, you can create a `Macros` subclass to define your macros: ```kotlin class MyMacros : Macros() { val myMacro by "my_macro" } ``` Then pass an instance to your function: ```kotlin function("my_function", ::MyMacros) { say(macros.myMacro) } ``` Now you can access the macros on the `macros` property. This also allows validating macros that are required when calling the function with an NBT Compound. Example: ```kotlin class TeleportMacros : Macros() { val player by "player" } dataPack("my_datapack") { val teleportToSpawn = function("teleport_to_spawn", ::TeleportMacros) { teleport(player(macros.player), vec3()) } load { function(teleportToSpawn, arguments = nbt { this["name"] = "jeb_" }) // Will throw an error because function expects "player" macro function(teleportToSpawn, arguments = nbt { this["player"] = "jeb_" }) // Works fine } } ``` ## Best Practice When using macros, you can create a function with arguments that calls the function with the macros: ```kotlin fun main() { dataPack("my_datapack") { function("teleport_to_spawn") { teleport(player(macro("player")), vec3()) } } } fun Function.teleportToSpawn(player: String) { function("teleport_to_spawn", arguments = nbt { this["player"] = player }) } ``` Then you can call this function with your argument as a macro: ```kotlin teleportToSpawn("jeb_") ``` ## Limitations - Macros can only be used in functions. - Macros aren't variables, they are just text substitutions, you can't do operations on them. - Macros are not type-checked. - It would be very difficult and long for Kore to allow macros as any argument of commands because of the wide variety of argument types and contexts in Minecraft commands. --- # Data driven ## Minecraft Advancements with Kore - Type-Safe DSL Guide --- root: .components.layouts.MarkdownLayout title: Minecraft Advancements with Kore - Type-Safe DSL Guide nav-title: Advancements description: Create custom Minecraft advancements with Kore's Kotlin DSL. Covers all triggers, criteria, rewards (functions, loot, recipes), display settings, and frames. Replace hand-written JSON with type-safe Kotlin. keywords: minecraft advancements, datapack advancements, advancement triggers, using_item trigger, inventory_changed, minecraft achievement, kore advancements, custom advancements, advancement criteria, advancement rewards date-created: 2024-01-08 date-modified: 2026-10-02 routeOverride: /docs/data-driven/advancements --- # Advancements Advancements are a system in Minecraft Java Edition that guides players through the game by setting goals and challenges to complete. They serve as in-game achievements that track player progress across various activities. The [advancement example](/playground#example=advancement) of the playground runs in your browser and shows the JSON it generates. ## Overview Advancements have several key characteristics: - **World-specific**: Progress is saved per world, not globally - **Game mode independent**: Can be completed in any game mode (Survival, Creative, Adventure, Spectator) - **Non-linear**: Can be completed in any order, regardless of parent-child relationships - **Notification system**: Completed advancements trigger toast notifications and chat messages - **Customizable**: Data packs can add custom advancements with unique criteria and rewards ## File Structure Advancements are stored as JSON files in data packs at: ``` data//advancement/.json ``` For complete JSON specification, see the [Minecraft Wiki - Advancement Definition](https://minecraft.wiki/w/Advancement_definition). ## Creating Advancements Use the `advancement` builder function to create advancements in Kore: ```kotlin dataPack("my_datapack") { advancement("my_first_advancement") { display(Items.DIAMOND, "My First Advancement", "Complete this challenge!") criteria { inventoryChanged("get_diamond") { item { items(Items.DIAMOND) } } } } } ``` This generates `data/my_datapack/advancement/my_first_advancement.json`. ## Display Properties The display configuration controls how the advancement appears in the advancement screen and notifications. ### Basic Display ```kotlin advancement("example") { display(Items.DIAMOND_SWORD, "Title", "Description") { frame = AdvancementFrameType.TASK } } ``` ### Display with Chat Components For styled text with colors and formatting: ```kotlin advancement("styled_advancement") { display( icon = Items.GOLDEN_APPLE, title = textComponent("Golden Achievement") { color = Color.GOLD }, description = textComponent("Eat a golden apple") { color = Color.GRAY } ) { frame = AdvancementFrameType.GOAL } } ``` ### Display Properties Reference | Property | Type | Default | Description | |------------------|------------------------|----------|---------------------------------------------| | `icon` | `AdvancementIcon` | Required | Item displayed as the advancement icon | | `title` | `ChatComponents` | Required | Title shown in the advancement screen | | `description` | `ChatComponents` | Required | Description text below the title | | `frame` | `AdvancementFrameType` | `TASK` | Frame style: `TASK`, `GOAL`, or `CHALLENGE` | | `background` | `ModelArgument?` | `null` | Background texture (root advancements only) | | `showToast` | `Boolean?` | `true` | Show toast notification on completion | | `announceToChat` | `Boolean?` | `true` | Announce completion in chat | | `hidden` | `Boolean?` | `false` | Hide until completed (and hide children) | ### Frame Types Each frame type produces different visual feedback: | Frame | Notification Header | Header Color | Sound | |-----------------------------------------------------------------------------------------|-----------------------|--------------|---------------| | ![Task frame](mc:gui/sprites/advancements/task_frame_obtained) `TASK` | "Advancement Made!" | Yellow | Standard | | ![Goal frame](mc:gui/sprites/advancements/goal_frame_obtained) `GOAL` | "Goal Reached!" | Yellow | Standard | | ![Challenge frame](mc:gui/sprites/advancements/challenge_frame_obtained) `CHALLENGE` | "Challenge Complete!" | Pink | Special music | {{{ .components.mc.AdvancementToastsMockup }}} > **Note:** Root advancements (without a parent) don't trigger notifications or chat messages. ### Icon with Components Customize the icon with item components like enchantments: ```kotlin advancement("enchanted_icon") { display(Items.DIAMOND_SWORD, "Master Swordsman", "Wield a legendary blade") { icon(Items.DIAMOND_SWORD, count = 1) { enchantments { enchantment(Enchantments.SHARPNESS, 5) enchantment(Enchantments.UNBREAKING, 3) } customName(textComponent("Legendary Sword", Color.GOLD)) } frame = AdvancementFrameType.CHALLENGE } } ``` ### Hidden Advancements Hidden advancements remain invisible (along with their children) until completed: ```kotlin advancement("secret_discovery") { display(Items.ENDER_EYE, "???", "A mysterious discovery") { hidden = true frame = AdvancementFrameType.CHALLENGE } // ...criteria } ``` ## Parent Advancements Set a parent to position the advancement in an existing tree: ```kotlin advancement("child_advancement") { // Reference vanilla advancement parent = Advancements.Story.ROOT display(Items.IRON_PICKAXE, "Mining Progress", "Continue your journey") // ... } ``` Or reference a custom advancement: ```kotlin advancement("child_advancement") { parent = AdvancementArgument("my_root", "my_namespace") // ... } ``` ### Creating a New Tab To create a new advancement tab, create a root advancement (no parent) with display and a background: ```kotlin advancement("my_custom_tab") { display(Items.COMPASS, "Custom Adventures", "Begin your journey") { frame = AdvancementFrameType.TASK background = Textures.Gui.Advancements.Backgrounds.STONE } criteria { tick("auto_grant") // Grants immediately } } ``` ## Criteria Criteria define the conditions that must be met to complete the advancement. Each criterion has a **trigger** that activates when specific game events occur. ### Basic Criteria ```kotlin advancement("eat_apple") { criteria { consumeItem("eat_golden_apple") { item { items = listOf(Items.GOLDEN_APPLE) } } } } ``` ### Multiple Criteria Add multiple criteria to an advancement: ```kotlin advancement("multi_criteria") { criteria { inventoryChanged("get_diamond") { item { items(Items.DIAMOND) } } inventoryChanged("get_emerald") { item { items(Items.EMERALD) } } enterBlock("enter_water") { block = Blocks.WATER } } } ``` ### Criteria with Predicate Conditions Add predicate conditions to criteria for additional checks: ```kotlin advancement("conditional_criteria") { criteria { consumeItem("eat_apple_lucky") { item { items = listOf(Items.GOLDEN_APPLE) } conditions { randomChance(0.5f) // 50% chance timeCheck(WorldClocks.OVERWORLD, 6000f..18000f) // Daytime only } } } } ``` For a complete guide on predicates, see the [Predicates](/docs/data-driven/predicates) documentation. ## Triggers Triggers are the events that activate criteria. Kore supports all vanilla triggers: | Trigger | Description | |--------------------------------|-------------------------------------------| | `allayDropItemOnBlock` | Allay drops an item on a block | | `anyBlockUse` | Player uses any block | | `avoidVibration` | Player avoids a vibration while sneaking | | `beeNestDestroyed` | Player breaks a bee nest/beehive | | `bredAnimals` | Player breeds two animals | | `brewedPotion` | Player takes item from brewing stand | | `changedDimension` | Player travels between dimensions | | `channeledLightning` | Player uses Channeling enchantment | | `constructBeacon` | Beacon structure is updated | | `consumeItem` | Player consumes an item | | `crafterRecipeCrafted` | Crafter crafts a recipe | | `curedZombieVillager` | Player cures a zombie villager | | `defaultBlockUse` | Player uses a block (default interaction) | | `effectsChanged` | Player's effects change | | `enchantedItem` | Player enchants an item | | `enterBlock` | Player enters a block | | `entityHurtPlayer` | Entity hurts the player | | `entityKilledPlayer` | Entity kills the player | | `fallAfterExplosion` | Player falls after an explosion | | `fallFromHeight` | Player falls from a height | | `filledBucket` | Player fills a bucket | | `fishingRodHooked` | Player hooks something with fishing rod | | `heroOfTheVillage` | Player becomes Hero of the Village | | `impossible` | Never triggers (manual grant only) | | `inventoryChanged` | Player's inventory changes | | `itemDurabilityChanged` | Item durability changes | | `itemUsedOnBlock` | Player uses item on a block | | `killedByArrow` | Player kills entities with arrows | | `killMobNearSculkCatalyst` | Kill a mob near sculk catalyst | | `levitation` | Player has Levitation effect | | `lightningStrike` | Lightning strikes near player | | `location` | Player is at a specific location | | `netherTravel` | Player travels via Nether | | `placedBlock` | Player places a block | | `playerGeneratesContainerLoot` | Player generates container loot | | `playerHurtEntity` | Player hurts an entity | | `playerInteractedWithEntity` | Player interacts with entity | | `playerKilledEntity` | Player kills an entity | | `playerShearedEquipment` | Player shears equipment from entity | | `recipeCrafted` | Player crafts a recipe | | `recipeUnlocked` | Player unlocks a recipe | | `rideEntityInLava` | Player rides entity in lava | | `shotCrossbow` | Player shoots a crossbow | | `sleptInBed` | Player sleeps in bed | | `slideDownBlock` | Player slides down a block | | `spearMobs` | Player spears mobs | | `startedRiding` | Player starts riding | | `summonedEntity` | Player summons an entity | | `tameAnimal` | Player tames an animal | | `targetHit` | Player hits a target block | | `thrownItemPickedUpByEntity` | Entity picks up thrown item | | `thrownItemPickedUpByPlayer` | Player picks up thrown item | | `tick` | Every game tick (use for auto-grant) | | `usedEnderEye` | Player uses Eye of Ender | | `usedTotem` | Player uses Totem of Undying | | `usingItem` | Player is using an item | | `villagerTrade` | Player trades with villager | | `voluntaryExile` | Player gets Bad Omen | For detailed trigger documentation, see the [Triggers](/docs/data-driven/advancements/triggers) page. ### Trigger Examples ```kotlin advancement("trigger_examples") { criteria { // Dimension travel changedDimension("enter_nether") { from = Dimensions.OVERWORLD to = Dimensions.THE_NETHER } // Block interaction enterBlock("step_on_pressure_plate") { block = Blocks.STONE_PRESSURE_PLATE } // Entity interaction playerKilledEntity("kill_zombie") { entity { conditionEntity { entityType(EntityTypes.ZOMBIE) } } } // Effect-based effectsChanged("get_speed") { effect(Effects.SPEED) { amplifier = rangeOrInt(1..3) duration = rangeOrInt(100..200) } } // Never triggers - for manual grant via commands impossible("manual_only") } } ``` ## Requirements Requirements define how criteria combine to complete the advancement. By default, **all criteria must be completed** (AND logic). ### Simple Requirements Require specific criteria by name: ```kotlin advancement("single_requirement") { criteria { inventoryChanged("get_diamond") { item { items(Items.DIAMOND) } } inventoryChanged("get_emerald") { item { items(Items.EMERALD) } } } // Only diamond is required (emerald is optional) requirements("get_diamond") } ``` ### AND Logic (All Required) Require multiple criteria (all must be met): ```kotlin advancement("and_requirements") { criteria { inventoryChanged("get_diamond") { item { items(Items.DIAMOND) } } inventoryChanged("get_emerald") { item { items(Items.EMERALD) } } } // Both required requirements("get_diamond", "get_emerald") } ``` ### OR Logic (Any Required) Use nested lists for OR groups: ```kotlin advancement("or_requirements") { criteria { inventoryChanged("get_diamond") { item { items(Items.DIAMOND) } } inventoryChanged("get_emerald") { item { items(Items.EMERALD) } } inventoryChanged("get_gold") { item { items(Items.GOLD_INGOT) } } } // Need diamond OR emerald, AND gold requirements( listOf("get_diamond", "get_emerald"), // Either diamond or emerald listOf("get_gold") // AND gold ) } ``` ## Rewards Define rewards granted when the advancement is completed: ```kotlin advancement("rewarding_advancement") { // ...display and criteria rewards { experience = 100 loots(LootTables.Chests.IGLOO_CHEST) recipes(Recipes.DIAMOND_SWORD) } } ``` ### Reward Properties | Property | Type | Description | |--------------|----------------------------|-----------------------------------| | `experience` | `Int?` | Experience points awarded | | `function` | `FunctionArgument?` | Function to execute on completion | | `loot` | `List?` | Loot tables to roll | | `recipes` | `List?` | Recipes to unlock | ### Function Rewards Execute commands when the advancement is completed: ```kotlin // Anonymous generated function rewards { function { say("Congratulations!") } } // Named function rewards { function("celebration") { say("You did it!") playSound(SoundEvents.Ui.Toast.CHALLENGE_COMPLETE, PlaySoundMixer.MASTER, self()) } } // Reference existing function rewards { function = myExistingFunction } ``` ### Multiple Rewards ```kotlin advancement("full_rewards") { rewards { experience = 500 function("reward_function") { give(self(), Items.DIAMOND, 10) effect(self()) { give(Effects.REGENERATION, duration = 200, amplifier = 2) } } loots( LootTables.Chests.END_CITY_TREASURE, LootTables.Chests.STRONGHOLD_CORRIDOR ) recipes( Recipes.DIAMOND_PICKAXE, Recipes.DIAMOND_SWORD ) } } ``` ## Telemetry Control whether completing this advancement sends telemetry data: ```kotlin advancement("tracked_advancement") { sendsTelemetryEvent = true // Default is false } ``` ## Managing Advancements with Commands Use the `/advancement` command to grant, revoke, or test advancements. ### Using the Advancement Command Block ```kotlin function("manage_advancements") { advancement { // Grant/revoke everything grantEverything(self()) revokeEverything(self()) // Specific advancement grant(self(), Advancements.Adventure.KILL_A_MOB) revoke(self(), Advancements.Adventure.KILL_A_MOB) // With route and criterion grant(self(), AdvancementRoute.ONLY, Advancements.Story.ROOT, "criterion_name") } } ``` ### Target-Specific Block ```kotlin function("player_advancements") { advancement(self()) { grantEverything() grant(Advancements.Story.IRON_TOOLS) revoke(AdvancementRoute.FROM, Advancements.Nether.ROOT) } } ``` ### Advancement Routes | Route | Description | |-----------|--------------------------------------------| | `ONLY` | Only the specified advancement | | `FROM` | Advancement and all its children | | `THROUGH` | Advancement, all parents, and all children | | `UNTIL` | Advancement and all its parents | ## Complete Example Here's a comprehensive example demonstrating multiple features: ```kotlin dataPack("adventure_pack") { // Create a custom tab val customRoot = advancement("custom/root") { display(Items.COMPASS, "Custom Adventures", "Begin your custom journey") { frame = AdvancementFrameType.TASK background = Textures.Gui.Advancements.Backgrounds.ADVENTURE } criteria { tick("start") } } // Child advancement with multiple criteria advancement("custom/explorer") { parent = customRoot display(Items.MAP, "Explorer", "Visit multiple biomes") { frame = AdvancementFrameType.GOAL showToast = true announceToChat = true } criteria { location("visit_forest") { conditions { locationCheck { biomes(Biomes.FOREST) } } } location("visit_desert") { conditions { locationCheck { biomes(Biomes.DESERT) } } } location("visit_ocean") { conditions { locationCheck { biomes(Biomes.OCEAN) } } } } // Any two biomes complete the advancement requirements( listOf("visit_forest", "visit_desert"), listOf("visit_forest", "visit_ocean"), listOf("visit_desert", "visit_ocean") ) rewards { experience = 50 } } // Challenge advancement advancement("custom/master") { parent = customRoot display(Items.NETHERITE_SWORD, "Master Adventurer", "Complete the ultimate challenge") { icon(Items.NETHERITE_SWORD) { enchantments { enchantment(Enchantments.SHARPNESS, 5) } } frame = AdvancementFrameType.CHALLENGE hidden = true } criteria { playerKilledEntity("kill_dragon") { entity { conditionEntity { entityType(EntityTypes.ENDER_DRAGON) } } } playerKilledEntity("kill_wither") { entity { conditionEntity { entityType(EntityTypes.WITHER) } } } } rewards { experience = 1000 function("master_reward") { title(self(), TitleLocation.TITLE, textComponent("MASTER ADVENTURER", Color.GOLD)) } } } } ``` ## Best Practices ### 1. Logical Progression Structure advancements to guide players naturally, even though completion order is flexible. ### 2. Meaningful Rewards Match reward value to advancement difficulty - challenging advancements should have worthwhile rewards. ### 3. Clear Descriptions Write descriptions that clearly explain what players need to do. ### 4. Use Hidden Sparingly Reserve hidden advancements for genuine surprises or easter eggs. ### 5. Test Criteria Verify criteria trigger correctly in-game before releasing your data pack. ## See Also - [Triggers](/docs/data-driven/advancements/triggers) - Complete trigger reference - [Predicates](/docs/data-driven/predicates) - Conditions for advancement criteria - [Loot Tables](/docs/data-driven/loot-tables) - Loot rewards - [Functions](/docs/commands/functions) - Function rewards - [Tags](/docs/data-driven/tags) - Use tags in conditions ## External Resources - [Minecraft Wiki: Advancement](https://minecraft.wiki/w/Advancement) - Game mechanics overview - [Minecraft Wiki: Advancement Definition](https://minecraft.wiki/w/Advancement_definition) - JSON format specification --- ## Advancements Triggers --- root: .components.layouts.MarkdownLayout title: Advancements Triggers nav-title: Advancements Triggers description: A guide for using advancements triggers in Minecraft with Kore. keywords: minecraft, datapack, kore, guide, advancements, triggers date-created: 2024-08-01 date-modified: 2026-09-23 routeOverride: /docs/data-driven/advancements/triggers --- ## Available Triggers Below is the comprehensive list of available trigger types. Each trigger includes a description, its properties with explanations, and an example usage in Kotlin. --- ### `allayDropItemOnBlock` **Description:** Triggers when an allay drops an item on a block. **Properties:** - `location`: The location where the item is dropped. **Example:** ```kotlin allayDropItemOnBlock("allay_drop") { location { block { blocks(Blocks.GRASS_BLOCK, Blocks.DIRT) } } } ``` --- ### `anyBlockUse` **Description:** Triggers when a player uses any block. **Properties:** - `location`: Conditions checked against the used block. **Example:** ```kotlin anyBlockUse("use_block_while_sneaking") { conditions { entityProperties { flags { isSneaking = true } } } } anyBlockUse("use_crafting_table") { location { predicate { locationCheck { block { blocks(Blocks.CRAFTING_TABLE) } } } } } ``` --- ### `avoidVibration` **Description:** Triggers when a player avoids a vibration by sneaking. **Properties:** _None._ **Example:** ```kotlin avoidVibration("avoid_sculk") { conditions { locationCheck { block(Blocks.SCULK) } } } ``` --- ### `beeNestDestroyed` **Description:** Triggers when a bee nest is destroyed. **Properties:** - `block`: The type of block that was destroyed. - `item`: The item involved in the destruction. - `numBeesInside`: The number of bees that were inside the nest. **Example:** ```kotlin beeNestDestroyed("destroy_nest") { block = Blocks.BEE_NEST numBeesInside = rangeOrInt(1) item { items(Items.HONEYCOMB) } } ``` --- ### `bredAnimals` **Description:** Triggers when animals are bred. **Properties:** - `child`: The child animal resulting from the breeding. - `parent`: One of the parent animals. - `partner`: The other parent animal. **Example:** ```kotlin bredAnimals("breed_animals") { child { conditions { entityProperties { entityType(EntityTypes.COW) } } } parent { conditions { entityProperties { entityType(EntityTypes.COW) } } } partner { conditions { entityProperties { entityType(EntityTypes.COW) } } } } ``` --- ### `brewedPotion` **Description:** Triggers when a potion is brewed (player takes item from brewing stand). **Properties:** - `potion`: The potion that was brewed. **Example:** ```kotlin brewedPotion("brewed_potion") ``` --- ### `changedDimension` **Description:** Triggers when a player changes dimension. **Properties:** - `from`: The original dimension. - `to`: The new dimension. **Example:** ```kotlin changedDimension("enter_nether") { from = Dimensions.OVERWORLD to = Dimensions.THE_NETHER } ``` --- ### `channeledLightning` **Description:** Triggers when lightning is channeled. **Properties:** - `victims`: A list of entities affected by the lightning. **Example:** ```kotlin channeledLightning("lightning_rod") { victim { conditions { entityProperties { entityType(EntityTypes.CREEPER) } } } } ``` --- ### `constructBeacon` **Description:** Triggers when a beacon is constructed. **Properties:** - `level`: The level of the beacon. **Example:** ```kotlin constructBeacon("make_beacon") { level = rangeOrInt(4) } ``` --- ### `consumeItem` **Description:** Triggers when an item is consumed. **Properties:** - `item`: The item that was consumed. **Example:** ```kotlin consumeItem("eat_apple") { item { items = listOf(Items.GOLDEN_APPLE) } } ``` --- ### `crafterRecipeCrafted` **Description:** Triggers when a recipe is crafted. **Properties:** - `recipeId`: The ID of the crafted recipe. - `ingredients`: The ingredients used in the recipe. **Example:** ```kotlin crafterRecipeCrafted("craft_diamond") { recipeId = Recipes.DIAMOND ingredient(Items.DIAMOND) { components { damage(0) } } } ``` --- ### `curedZombieVillager` **Description:** Triggers when a zombie villager is cured. **Properties:** - `villager`: The villager involved in the curing. - `zombie`: The zombie involved in the curing. **Example:** ```kotlin curedZombieVillager("cure_zombie") { villager { conditions { entityProperties { entityType(EntityTypes.VILLAGER) } } } zombie { conditions { entityProperties { entityType(EntityTypes.ZOMBIE) } } } } ``` --- ### `defaultBlockUse` **Description:** Triggers when a block is used with default interaction. **Properties:** - `location`: Conditions checked against the used block, set with `location { predicate { ... } }` like [`anyBlockUse`](#anyblockuse). **Example:** ```kotlin defaultBlockUse("use_default_in_nether") { conditions { entityProperties { location { dimension = Dimensions.THE_NETHER } } } } ``` --- ### `effectsChanged` **Description:** Triggers when a player's effects change. **Properties:** - `effects`: The effects that have changed. - `source`: The source of the effect changes. **Example:** ```kotlin effectsChanged("get_effect") { effect(Effects.SPEED) { amplifier = rangeOrInt(1..3) duration = rangeOrInt(100..200) } source { conditions { entityProperties { entityType(EntityTypes.WITCH) } } } } ``` --- ### `enchantedItem` **Description:** Triggers when an item is enchanted. **Properties:** - `item`: The item that was enchanted. - `levels`: The levels of enchantment applied. **Example:** ```kotlin enchantedItem("enchant_item") { item { items(Items.DIAMOND_SWORD) } levels = rangeOrInt(1..3) } ``` --- ### `enterBlock` **Description:** Triggers when a player enters a specific block. **Properties:** - `block`: The block being entered. - `state`: The state properties of the block, set with `state(key, value)` or `states { }`. **Example:** ```kotlin enterBlock("enter_block") { block = Blocks.REDSTONE_LAMP states { this["lit"] = "true" } } ``` --- ### `entityHurtPlayer` **Description:** Triggers when an entity hurts a player. **Properties:** - `damage`: Details about the damage inflicted. **Example:** ```kotlin entityHurtPlayer("hurt_player") { damage { sourceEntity { entityType(EntityTypes.ZOMBIE) } taken = rangeOrDouble(5.0..10.0) type { tag(Tags.DamageType.IS_FALL) } } } ``` --- ### `entityKilledPlayer` **Description:** Triggers when an entity kills a player. **Properties:** - `entity`: The entity that killed the player. - `killingBlow`: Details about the killing blow. **Example:** ```kotlin entityKilledPlayer("killed_by_creeper") { entity { conditions { entityProperties { entityType(EntityTypes.CREEPER) } } } } ``` --- ### `fallAfterExplosion` **Description:** Triggers after falling from an explosion. **Properties:** - `startPosition`: The starting position of the fall. - `distance`: The distance fallen. - `cause`: The cause of the fall. **Example:** ```kotlin fallAfterExplosion("tnt_launch") { startPosition { position { y = rangeOrInt(100..200) } } distance { horizontal(10f) } } ``` --- ### `fallFromHeight` **Description:** Triggers when falling from a height. **Properties:** - `startPosition`: The starting position of the fall. - `distance`: The distance fallen. **Example:** ```kotlin fallFromHeight("high_fall") { distance { vertical(20f) } } ``` --- ### `filledBucket` **Description:** Triggers when a bucket is filled. **Properties:** - `item`: The bucket item that was filled. **Example:** ```kotlin filledBucket("fill_bucket") { item { items(Items.WATER_BUCKET) } } ``` --- ### `fishingRodHooked` **Description:** Triggers when a fishing rod hooks something. **Properties:** - `entity`: The entity hooked by the fishing rod. - `item`: The item used as the fishing rod. - `rod`: Details about the fishing rod. **Example:** ```kotlin fishingRodHooked("catch_fish") { item { items(Items.FISHING_ROD) } rod { components { enchantments { enchantment(Enchantments.LUCK_OF_THE_SEA, 3) } } } } ``` --- ### `heroOfTheVillage` **Description:** Triggers when becoming a hero of the village. **Properties:** _None._ **Example:** ```kotlin heroOfTheVillage("save_village") { conditions { locationCheck { dimension = Dimensions.OVERWORLD } } } ``` --- ### `impossible` **Description:** Prevents the advancement from being achieved. Useful for creating advancements that should only trigger functions. **Properties:** _None._ **Example:** ```kotlin impossible("impossible") ``` --- ### `inventoryChanged` **Description:** Triggers when inventory contents change. **Properties:** - `items`: The items involved in the inventory change. - `slots`: The inventory slots affected. **Example:** ```kotlin inventoryChanged("get_diamond") { item { items(Items.DIAMOND) } slots { empty = rangeOrInt(1..3) } } ``` --- ### `itemDurabilityChanged` **Description:** Triggers when item durability changes. **Properties:** - `delta`: The change in durability. - `durability`: The current durability of the item. - `item`: The item whose durability changed. **Example:** ```kotlin itemDurabilityChanged("tool_break") { delta = rangeOrInt(-10..-1) item { items(Items.DIAMOND_PICKAXE) } } ``` --- ### `itemUsedOnBlock` **Description:** Triggers when an item is used on a block. **Properties:** - `location`: The location where the item was used. **Example:** ```kotlin itemUsedOnBlock("bone_meal_use") { location { predicate { locationCheck { block { blocks(Blocks.GRASS_BLOCK, Blocks.DIRT) } } } } } ``` --- ### `killedByArrow` **Description:** Triggers when a player kills entities with arrows shot from a bow or crossbow. **Properties:** - `firedFromWeapon`: The weapon used to fire the arrow. - `uniqueEntityTypes`: The number of unique entity types involved. - `victims`: The entities that were killed. **Example:** ```kotlin killedByArrow("killed_by_arrow") { firedFromWeapon { items = listOf(Items.BOW) components { enchantments { enchantment(Enchantments.POWER, 5) } } } uniqueEntityTypes = rangeOrInt(1..5) victim { conditions { entityProperties { entityType(EntityTypes.PLAYER) } } } } ``` --- ### `killMobNearSculkCatalyst` **Description:** Triggers when a mob is killed near a sculk catalyst. **Properties:** - `entity`: The entity that was killed. - `killingBlow`: Details about the killing blow. **Example:** ```kotlin killMobNearSculkCatalyst("kill_mob") { entity { conditions { entityProperties { entityType(EntityTypes.ZOMBIE) } } } killingBlow { sourceEntity { entityType(EntityTypes.PLAYER) } } } ``` --- ### `levitation` **Description:** Triggers during levitation. **Properties:** - `distance`: The distance of levitation. - `duration`: The duration of levitation. **Example:** ```kotlin levitation("float_up") { distance { y(10f) } duration = rangeOrInt(10..20) } ``` --- ### `lightningStrike` **Description:** Triggers on a lightning strike. **Properties:** - `bystander`: The bystanders affected by the lightning. - `lightning`: Details about the lightning strike. **Example:** ```kotlin lightningStrike("struck") { bystander { type { conditions { entityProperties { entityType(EntityTypes.CREEPER) } } } } } ``` --- ### `location` **Description:** Triggers every second based on location conditions. **Properties:** - `location`: The specific location conditions for the trigger. **Example:** ```kotlin location("reach_end") { conditions { locationCheck { dimension = Dimensions.THE_END } } } ``` --- ### `netherTravel` **Description:** Triggers when a player enters or exits the Nether. **Properties:** - `distance`: The distance traveled during the dimension change. - `startPosition`: The starting position before the change. **Example:** ```kotlin netherTravel("enter_nether") { distance { horizontal(100f) } startPosition { position { x = rangeOrInt(0..100) z = rangeOrInt(0..100) } } } ``` --- ### `placedBlock` **Description:** Triggers when a block is placed. **Properties:** - `location`: The location where the block was placed. **Example:** ```kotlin placedBlock("place_block") { location { location { biomes(Biomes.PLAINS) } } } ``` --- ### `playerGeneratesContainerLoot` **Description:** Triggers when container loot is generated. **Properties:** - `lootTable`: The loot table used to generate the container loot. **Example:** ```kotlin playerGeneratesContainerLoot("find_treasure", LootTables.Chests.BURIED_TREASURE) ``` --- ### `playerHurtEntity` **Description:** Triggers when a player hurts an entity. **Properties:** - `damage`: Details about the damage inflicted. - `entity`: The entity that was hurt. **Example:** ```kotlin playerHurtEntity("hurt_mob") { damage { taken = rangeOrDouble(5.0..10.0) } } ``` --- ### `playerInteractedWithEntity` **Description:** Triggers when a player interacts with an entity. **Properties:** - `item`: The item used during the interaction. - `entity`: The entity that was interacted with. **Example:** ```kotlin playerInteractedWithEntity("interact_with_golem") { item { items = listOf(Items.IRON_INGOT) } entity { conditionEntity { entityType(EntityTypes.IRON_GOLEM) } } } ``` --- ### `playerKilledEntity` **Description:** Triggers when a player kills an entity. **Properties:** - `entity`: The entity that was killed. - `killingBlow`: Details about the killing blow. **Example:** ```kotlin playerKilledEntity("kill_mob") { entity { conditions { entityProperties { entityType(EntityTypes.ZOMBIE) } } } } ``` --- ### `playerShearedEquipment` **Description:** Triggers after a player shears equipment off of a mob, such as wolf armor. **Properties:** - `entity`: The entity whose equipment was sheared. - `item`: The item of equipment that was sheared off. **Example:** ```kotlin playerShearedEquipment("shear_wolf_armor") { entity { entityType(EntityTypes.WOLF) } item { items(Items.LEATHER) } } ``` --- ### `recipeCrafted` **Description:** Triggers when a recipe is crafted. **Properties:** - `recipeId`: The ID of the crafted recipe. - `ingredients`: The ingredients used in the recipe. **Example:** ```kotlin recipeCrafted("craft_diamond") { recipeId = Recipes.DIAMOND ingredient(Items.DIAMOND) { components { damage(0) } } } ``` --- ### `recipeUnlocked` **Description:** Triggers when a recipe is unlocked. **Properties:** - `recipe`: The recipe that was unlocked. **Example:** ```kotlin recipeUnlocked("unlock_recipe", Recipes.DIAMOND) ``` --- ### `rideEntityInLava` **Description:** Triggers when riding an entity in lava. **Properties:** - `distance`: The distance traveled while riding in lava. - `startPosition`: The starting position before riding. **Example:** ```kotlin rideEntityInLava("lava_ride") { distance { horizontal(10f) } startPosition { position { y = rangeOrInt(100..200) } } } ``` --- ### `shotCrossbow` **Description:** Triggers when shooting a crossbow. **Properties:** - `item`: The crossbow item that was shot. **Example:** ```kotlin shotCrossbow("shoot_crossbow") { item { items(Items.CROSSBOW) enchantments { enchantment(Enchantments.MULTISHOT, 1) } } } ``` --- ### `sleptInBed` **Description:** Triggers when a player sleeps in a bed. **Properties:** _None._ **Example:** ```kotlin sleptInBed("sleep_in_bed") ``` --- ### `slideDownBlock` **Description:** Triggers when sliding down a block. **Properties:** - `block`: The block being slid down. **Example:** ```kotlin slideDownBlock("slide_down") { block { blocks(Blocks.SNOW_BLOCK) } } ``` --- ### `spearMobs` **Description:** Triggers when a player spears mobs. **Properties:** - `count`: The number of mobs speared. **Example:** ```kotlin spearMobs("spear_mobs") { count = 3 } ``` --- ### `startedRiding` **Description:** Triggers when a player starts riding an entity. **Properties:** _None._ **Example:** ```kotlin startedRiding("ride_horse") { conditions { entityProperties { vehicle { entityType(EntityTypes.HORSE) } } } } ``` --- ### `summonedEntity` **Description:** Triggers when an entity is summoned. **Properties:** - `entity`: The entity that was summoned. **Example:** ```kotlin summonedEntity("summon_iron_golem") { entity { conditionEntity { entityType(EntityTypes.IRON_GOLEM) } } } ``` --- ### `tameAnimal` **Description:** Triggers when an animal is tamed. **Properties:** - `entity`: The animal that was tamed. **Example:** ```kotlin tameAnimal("tame_wolf") { entity { conditionEntity { entityType(EntityTypes.WOLF) } } } ``` --- ### `targetHit` **Description:** Triggers when a target block is hit. **Properties:** - `signalStrength`: The strength of the signal when the target is hit. - `projectile`: The projectile used to hit the target. **Example:** ```kotlin targetHit("hit_target") { signalStrength = rangeOrInt(1..15) projectile { conditions { entityProperties { entityType(EntityTypes.ARROW) } } } } ``` --- ### `thrownItemPickedUpByEntity` **Description:** Triggers when a thrown item is picked up by an entity. **Properties:** - `entity`: The entity that picked up the item. - `item`: The item that was picked up. **Example:** ```kotlin thrownItemPickedUpByEntity("feed_animal") { entity { conditionEntity { entityType(EntityTypes.COW) } } item { items(Items.WHEAT) } } ``` --- ### `thrownItemPickedUpByPlayer` **Description:** Triggers when a thrown item is picked up by a player. **Properties:** - `entity`: The entity that picked up the item. - `item`: The item that was picked up. **Example:** ```kotlin thrownItemPickedUpByPlayer("catch_trident") { item { items(Items.TRIDENT) } } ``` --- ### `tick` **Description:** Triggers every tick (20 times per second). **Properties:** - `conditions`: Conditions that must be met for the trigger to activate. **Example:** ```kotlin tick("game_tick") { conditions { timeCheck(WorldClocks.OVERWORLD, 0f, 12000f, period = 24000) // daytime only } } ``` --- ### `usedEnderEye` **Description:** Triggers when an ender eye is used. **Properties:** - `distance`: The distance traveled using the ender eye. **Example:** ```kotlin usedEnderEye("find_stronghold") { distance { horizontal(100f) } } ``` --- ### `usedTotem` **Description:** Triggers when a totem is used. **Properties:** - `item`: The totem item that was used. **Example:** ```kotlin usedTotem("save_life") { item { items(Items.TOTEM_OF_UNDYING) } } ``` --- ### `usingItem` **Description:** Triggers while using an item. **Properties:** - `item`: The item being used. **Example:** ```kotlin usingItem("shield_block") { item { items = listOf(Items.SHIELD) } } ``` --- ### `villagerTrade` **Description:** Triggers when a villager trades. **Properties:** - `item`: The item involved in the trade. - `villager`: The villager involved in the trade. **Example:** ```kotlin villagerTrade("trade") { item { items(Items.EMERALD) } villager { conditions { entityProperties { team = "villager" } } } } ``` --- ### `voluntaryExile` **Description:** Triggers when a player causes a raid in a village. **Properties:** - `location`: The location where the raid occurred. **Example:** ```kotlin voluntaryExile("raid_village") { conditions { locationCheck { dimension = Dimensions.OVERWORLD } } } ``` --- Each trigger example demonstrates the basic usage with common properties and conditions. You can customize these triggers by adding more conditions and requirements to suit your specific advancement needs. --- ## Dialogs --- root: .components.layouts.MarkdownLayout title: Dialogs nav-title: Dialogs description: Create interactive dialog screens in Minecraft with Kore's comprehensive dialog system. keywords: minecraft, datapack, kore, dialogs, ui, interactive, forms, confirmation, notice date-created: 2025-09-18 date-modified: 2026-09-23 routeOverride: /docs/data-driven/dialogs --- # Dialogs Minecraft includes a powerful dialog system that allows creating interactive modal windows for displaying information and receiving player input. Dialogs are native Minecraft features introduced in Java Edition 1.21.6 that enable sophisticated user interfaces within the game. With **Kore**, you can easily create and manage these dialogs using a comprehensive Kotlin DSL that maps directly to Minecraft's dialog format. Minecraft's dialog system supports various interaction types including: - Displaying rich text with formatting and clickable elements - Receiving player input through text fields, toggles, sliders, and option selections - Executing commands via action buttons (with appropriate permissions) - Navigating between multiple dialogs using nested structures - Integration with the pause menu and quick actions hotkey For the vanilla reference, see the [Minecraft Wiki - Dialog](https://minecraft.wiki/w/Dialog) and [Commands/dialog](https://minecraft.wiki/w/Commands/dialog). ## Minecraft Dialog System Overview Minecraft dialogs consist of three main elements: - **Header**: Contains the title and warning button - **Body elements**: Labels, inputs, buttons, and submit actions (scrollable if needed) - **Optional footer**: Confirmation buttons and submit actions When a dialog opens, player controls are temporarily disabled until the user exits through an action button, the Escape key, or the warning button. In single-player mode, dialogs can be configured to pause the game and trigger an autosave. ## Set Up Your Data Pack Function Begin by creating a function within your `DataPack` where you'll define your dialogs: ```kotlin fun DataPack.createDialogs() { // Your dialog definitions will go here } ``` ## Initialize the Dialogs Block Use the `dialogBuilder` to start defining your dialogs: ```kotlin val myDialog = dialogBuilder.confirmation("welcome", "Welcome!") { // Define dialog properties here } ``` Or use the `dialogs` DSL: ```kotlin dialogs { confirmation("my-dialog", "Title!") { // Define dialog properties here } } ``` ## Dialog Types Minecraft currently supports five different dialog types: ### Confirmation Dialog A dialog with two action buttons (yes/no) for binary choices: ```kotlin val confirmDialog = dialogBuilder.confirmation("delete_world", "Delete World?") { afterAction = AfterAction.WAIT_FOR_RESPONSE externalTitle("Delete", Color.RED) pause = true bodies { plainMessage("Are you sure you want to delete this world? This action cannot be undone.") } yes("Delete") { action { runCommand { say("World deleted!") } } } no("Cancel") { action { suggestChatMessage("Cancelled deletion") } } } ``` {{{ .components.mc.ConfirmationDialogMockup }}} ### Notice Dialog A simple dialog with a single action button for displaying information: ```kotlin val noticeDialog = dialogBuilder.notice("achievement", "Achievement Unlocked!") { bodies { item(Items.DIAMOND_SWORD) { description = ItemDescription(textComponent("Your first diamond tool!")) showTooltip = true } plainMessage("You've crafted your first diamond sword!") } action("Awesome!") { tooltip("Click to continue") action { dynamicCustom("celebrate") { this["achievement"] = "first_diamond_tool" } } } } ``` {{{ .components.mc.NoticeDialogMockup }}} ### Multi Action Dialog A dialog with multiple action buttons arranged in columns, perfect for menus: ```kotlin val menuDialog = dialogBuilder.multiAction("main_menu", "Server Menu") { columns = 3 inputs { text("player_name", "Your Name") { maxLength = 16 initial = "Steve" } numberRange("difficulty", "Difficulty", range = 1..10, initial = 5) { step = 1f } boolean("pvp_enabled", "Enable PVP") { initial = false onTrue = "PVP On" onFalse = "PVP Off" } } actions { action("Start Game") { action { runCommand { say("Game starting with settings!") } } } action("Settings") { action { openUrl("https://example.com/settings") } } action("Quit") { action { dynamicRunCommand { tellraw(allPlayers(), textComponent("Thanks for playing!")) } } } } } ``` {{{ .components.mc.MultiActionDialogMockup }}} ### Dialog List A dialog that displays a scrollable list of other dialogs: ```kotlin val listDialog = dialogBuilder.dialogList("dialog_menu", "Available Dialogs") { columns = 2 buttonWidth = 200 dialogs(confirmDialog, noticeDialog, menuDialog) // or use a tag: // dialogs(Tags.Dialog.PAUSE_SCREEN_ADDITIONS) exitAction("Back to Game") { action { dynamicRunCommand { say("Returning to game...") } } } } ``` ### Server Links Dialog A specialized dialog for displaying server links (configured server-side): ```kotlin val linksDialog = dialogBuilder.serverLinks("server_links", "Server Links") { buttonWidth = 150 columns = 3 exitAction("Close") { action { dynamicRunCommand { say("Links closed") } } } } ``` ## Dialog Properties All dialogs share common properties that can be customized: ### Basic Properties ```kotlin dialogBuilder.confirmation("example", "Title") { // External title shown on buttons leading to this dialog externalTitle("Custom Button Text", Color.AQUA) // Action performed after dialog interactions afterAction = AfterAction.WAIT_FOR_RESPONSE // or CLOSE // Whether dialog can be dismissed with Escape key canCloseWithEscape = true // Whether to pause the game in single-player pause = true } ``` ### After Actions Control what happens after dialog interactions: - `AfterAction.NONE` - Do nothing - `AfterAction.CLOSE` - Close the dialog (default) - `AfterAction.WAIT_FOR_RESPONSE` - Keep dialog open awaiting response ## Body Elements Dialogs can contain rich content between the title and action buttons: ### Plain Messages Display text content: ```kotlin bodies { plainMessage("Welcome to our server!") { width = 300 } plainMessage(textComponent("Colored text", Color.GREEN)) } ``` ### Items Display items with descriptions: ```kotlin bodies { item(Items.ENCHANTED_BOOK) { description = ItemDescription(textComponent("A mysterious tome")) showTooltip = true showDecorations = false height = 64 width = 64 } } ``` ## Input Controls Multi-action dialogs can include various input controls for user interaction: ### Text Input Single-line or multi-line text input: ```kotlin inputs { text("username", "Username") { maxLength = 20 initial = "Player" width = 200 labelVisible = true } text("bio", "Biography") { multiline(maxLines = 5, height = 100) maxLength = 500 } } ``` ### Number Range Slider controls for numeric input: ```kotlin inputs { numberRange("volume", "Volume", range = 0..100, initial = 50) { step = 5f labelFormat = "Volume: %d%%" width = 250 } // Float ranges also supported numberRange("speed", "Speed", range = 0.1f..2.0f, initial = 1.0f) { step = 0.1f } } ``` ### Boolean Toggle Checkbox-style boolean input: ```kotlin inputs { boolean("notifications", "Enable Notifications") { initial = true onTrue = "✓ Enabled" onFalse = "✗ Disabled" } } ``` ### Single Option Dropdown-style selection: ```kotlin inputs { singleOption("gamemode", "Game Mode") { width = 200 labelVisible = true option("survival", "Survival", initial = true) option("creative", "Creative") option("adventure", "Adventure") option("spectator", "Spectator") } } ``` ## Actions Dialog actions define what happens when buttons are clicked: ### Available Action Types ```kotlin action { // Run a command runCommand { say("Hello world!") } // Suggest a chat message suggestChatMessage("/gamemode creative") // Open a URL openUrl("https://minecraft.net") // Copy text to clipboard copyToClipboard("Server IP: mc.example.com") // Change page in a book changePage(5) // Dynamic commands with macros named after the inputs dynamicRunCommand { say("Player name is ${macro("username")}") } // Custom dynamic actions, for sending custom packets for server plugins/mods dynamicCustom("custom_action") { this["data"] = "value" this["count"] = 42 } } ``` ### Action Properties Enhance actions with labels, tooltips, and sizing: ```kotlin action("My Button") { tooltip("Click me for awesome results!") width = 150 action { runCommand { say("Button clicked!") } } } ``` ## Using Dialogs in Commands Commands can show and clear dialogs to players, using a reference or an inline dialog: ```kotlin load { // Show dialog to specific players dialogShow(allPlayers(), myDialog) // Create and show dialog inline dialogShow(allPlayers()) { confirmation("inline_dialog", "Quick Confirmation") { yes("Yes") { action { runCommand { say("Yes selected") } } } no("No") { action { runCommand { say("No selected") } } } } } // Clear dialogs dialogClear(allPlayers()) } ``` ## Advanced Examples ### Complex Form Dialog ```kotlin val registrationForm = dialogBuilder.multiAction("register", "Player Registration") { columns = 1 bodies { plainMessage("Welcome! Please fill out your information:") } inputs { text("display_name", "Display Name") { maxLength = 32 labelVisible = true } text("email", "Email Address") { maxLength = 100 } numberRange("age", "Age", range = 13..99, initial = 18) { step = 1f } singleOption("region", "Region") { option("na", "North America") option("eu", "Europe") option("as", "Asia") option("other", "Other") } boolean("newsletter", "Subscribe to Newsletter") { initial = false } text("comments", "Additional Comments") { multiline(maxLines = 3, height = 80) maxLength = 200 } } actions { action("Register") { action { dynamicRunCommand { say("Registration submitted!") give(allPlayers(), Items.WRITTEN_BOOK) } } } action("Cancel") { action { suggestChatMessage("Registration cancelled") } } } } ``` ### Interactive Tutorial System ```kotlin val tutorialDialog = dialogBuilder.dialogList("tutorials", "Tutorial Menu") { columns = 2 buttonWidth = 180 bodies { plainMessage("Choose a tutorial to begin:") item(Items.BOOK) { description = ItemDescription(textComponent("Learn the basics")) } } // Reference other tutorial dialogs dialogs( basicTutorial, advancedTutorial, pvpTutorial ) exitAction("Skip Tutorials") { action { runCommand { advancement.grant(allPlayers(), AdvancementArgument("tutorial:skipped")) } } } } ``` ## Best Practices 1. **Keep dialogs focused**: Each dialog should serve a single, clear purpose 2. **Use appropriate dialog types**: - Confirmation for yes/no decisions - Notice for information display - Multi-action for complex forms or menus 3. **Provide clear labels**: Make button and input labels descriptive 4. **Include tooltips**: Add helpful tooltips for complex actions 5. **Handle edge cases**: Provide cancel/exit options where appropriate ## Integration with Other Systems For multi-page GUIs whose buttons run functions for players without operator rights, the [Menus helper](/docs/helpers/menus) builds the dialogs and the trigger wiring for you. Dialogs work seamlessly with other Kore features: ```kotlin val conditionalDialog = dialogBuilder.confirmation("weather_change", "Change Weather?") { // Only show if it's currently raining yes("Make Sunny") { action { runCommand { weatherClear() } } } no("Keep Current") { action { suggestChatMessage("Weather unchanged") } } } val firstDeathScoreboard = "first_death" advancement("first_death") { criteria { tick("check_each_ticks") { conditions { entityProperties { nbt { this["Health"] = 0f } } } } } display(Items.AIR) { announceToChat = false hidden = true } rewards { function { scoreboard.objective(self(), firstDeathScoreboard).set(1) execute { ifCondition { score(self(), firstDeathScoreboard) equalTo 1 } run { dialogShow(self(), conditionalDialog) scoreboard.objective(self(), firstDeathScoreboard).set(2) // Prevent re-triggering } } } } } // Integration with advancements load { // Define the objective scoreboard.objective(firstDeathScoreboard).create(ScoreboardCriteria.DUMMY) } ``` ## See also - [Advancements](/docs/data-driven/advancements) - Create custom conditions and rewards for dialogs - [Components](/docs/concepts/components) - Learn about the component system used in Kore - [Predicates](/docs/data-driven/predicates) - Create custom conditions for dialogs --- ## Enchantments --- root: .components.layouts.MarkdownLayout title: Enchantments nav-title: Enchantments description: Create custom Minecraft enchantments using Kore's type-safe Kotlin DSL with support for all vanilla effect components and level-based values. keywords: minecraft, datapack, kore, enchantments, effects, custom enchantments date-created: 2025-03-02 date-modified: 2026-10-07 routeOverride: /docs/data-driven/enchantments --- # Enchantments Enchantments are data-driven definitions that modify item behavior, apply effects, change damage calculations, and alter various game mechanics. In Minecraft Java Edition 1.21+, enchantments are fully customizable through data packs, allowing you to create entirely new enchantments with unique effects. ## Overview Custom enchantments have several key characteristics: - **Data-driven**: Defined as JSON files in data packs, not hardcoded - **Effect components**: Modular system of 30+ effect types - **Level-based scaling**: Values can scale with enchantment level - **Slot-aware**: Effects apply based on equipment slot configuration - **Conditional**: Effects can have predicate requirements ### Enchantment Properties Every enchantment defines these core properties: | Property | Description | |-------------------------|--------------------------------------------------------------| | `description` | Text component displayed on items | | `supported_items` | Items or item tags that can receive the enchantment | | `primary_items` | Items or item tags offered by the enchanting table | | `exclusive_set` | Incompatible enchantments | | `weight` | Probability weight (1-1024) | | `max_level` | Maximum level (1-255) | | `min_cost` / `max_cost` | Enchanting power window, as `base` + `per_level_above_first` | | `anvil_cost` | Base cost for anvil application | | `slots` | Equipment slots where effects apply | | `effects` | Effect components that define behavior | ## File Structure Enchantments are stored as JSON files in data packs at: ``` data//enchantment/.json ``` For complete JSON specification, see the [Minecraft Wiki - Enchantment definition](https://minecraft.wiki/w/Enchantment_definition). ## Creating Enchantments Use the `enchantment` builder function to create enchantments in Kore: ```kotlin dataPack("my_datapack") { enchantment("fire_aspect_plus") { description("Fire Aspect+") supportedItems(Items.DIAMOND_SWORD, Items.NETHERITE_SWORD) primaryItems(Tags.Item.SWORDS) exclusiveSet(Enchantments.FIRE_ASPECT) weight = 2 maxLevel = 3 minCost(15, 10) // base 15, +10 per level maxCost(65, 10) anvilCost = 4 slots(EquipmentSlot.MAINHAND) effects { // Define effects here } } } ``` This generates `data/my_datapack/enchantment/fire_aspect_plus.json`. {{{ .components.mc.EnchantmentTooltipMockup }}} ## Basic Properties ### Description The text shown on enchanted items: ```kotlin enchantment("test") { // Simple string description("Test Enchantment") // Or with a text component for formatting description = textComponent("Test") { color = Color.GOLD } } ``` ### Supported and Primary Items Both accept items and item tags, mixed freely: ```kotlin enchantment("bow_enchant") { // Items the enchantment can be applied to on an anvil or with /enchant supportedItems(Items.BOW, Items.CROSSBOW) // Subset offered by the enchanting table primaryItems(Tags.Item.Enchantable.BOW) } ``` ### Exclusive Set Enchantments that cannot coexist: ```kotlin enchantment("protection_variant") { exclusiveSet(Tags.Enchantment.ExclusiveSet.ARMOR) // Or individual enchantments exclusiveSet(Enchantments.PROTECTION, Enchantments.FIRE_PROTECTION) } ``` ### Cost and Weight ```kotlin enchantment("rare_enchant") { weight = 1 // Very rare (compare to Mending: 2, Unbreaking: 5) maxLevel = 5 // Enchanting power formula: base + (level - 1) * perLevelAboveFirst minCost(base = 1, perLevelAboveFirst = 11) // 1, 12, 23, 34, 45 maxCost(base = 21, perLevelAboveFirst = 11) // 21, 32, 43, 54, 65 anvilCost = 8 // Expensive to combine } ``` ### Equipment Slots Where the enchantment's effects apply: ```kotlin enchantment("armor_enchant") { slots(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET) } enchantment("weapon_enchant") { slots(EquipmentSlot.MAINHAND, EquipmentSlot.OFFHAND) } ``` Available slots: `ANY`, `HAND`, `MAINHAND`, `OFFHAND`, `ARMOR`, `FEET`, `LEGS`, `CHEST`, `HEAD`, `BODY`, `SADDLE`. ## Effect Components Effects define what the enchantment actually does. Kore supports all vanilla effect components. ### Value Effect Components These components change a number the game computes, and share the `add`, `multiply`, `set`, `removeBinomial` and `allOf` builders: | Component | Description | |-----------------------------|------------------------------------| | `ammoUse` | Ammunition consumption | | `armorEffectiveness` | Armor effectiveness multiplier | | `blockExperience` | XP from breaking blocks | | `crossbowChargeTime` | Crossbow charge time | | `damage` | Bonus attack damage | | `damageProtection` | Damage reduction (max 80% total) | | `equipmentDrops` | Equipment drop chance | | `fishingLuckBonus` | Fishing luck bonus | | `fishingTimeReduction` | Fishing speed bonus | | `itemDamage` | Durability loss multiplier | | `knockback` | Knockback strength | | `mobExperience` | XP from killing mobs | | `projectileCount` | Projectiles fired | | `projectilePiercing` | Targets pierced | | `projectileSpread` | Accuracy spread in degrees | | `repairWithXp` | Durability repaired per XP | | `smashDamagePerFallenBlock` | Mace bonus damage per block fallen | | `tridentReturnAcceleration` | Trident return speed | | `tridentSpinAttackStrength` | Riptide attack strength | ```kotlin effects { // Simple damage bonus damage { add(linearLevelBased(2f, 0.5f)) // +2 base, +0.5 per level } // Protection with conditions damageProtection { add(4) { requirements { weatherCheck(raining = true) } } } // Several operations, applied in order armorEffectiveness { allOf { add(2) multiply(1.2f) removeBinomial(0.5f) // 50% chance to remove one unit } } } ``` `equipmentDrops` names which side of the fight has to carry the enchantment, so its effects go inside an `on` block: ```kotlin effects { equipmentDrops { on(EquipmentDropsSpecifier.ATTACKER) { add(0.05f) } } } ``` `crossbowChargeTime` and `tridentSpinAttackStrength` hold a single effect instead of a list, so they take no `requirements`. Use an `allOf` block to apply several operations: ```kotlin effects { crossbowChargeTime { add(linearLevelBased(-0.25f, -0.25f)) } } ``` ### Entity Effect Components These components run actions on an entity, and share the entity effect builders listed below: | Component | Description | |----------------------|-------------------------------------------------| | `hitBlock` | After hitting a block with the enchanted item | | `locationChanged` | When the holder moves, equips the item or loads | | `postAttack` | After damaging an entity | | `postPiercingAttack` | After a piercing projectile goes through it | | `projectileSpawned` | When a projectile is created | | `tick` | Every game tick while equipped | ```kotlin effects { // Apply effects when hitting blocks hitBlock { applyMobEffect(Effects.SPEED) { minDuration(5) maxDuration(10) maxAmplifier(2) } } // Periodic effects tick { damageEntity(DamageTypes.MAGIC, 1, 2) } } ``` `postAttack` names both the side carrying the enchantment and the side taking the effect, so its effects go inside an `on` block: ```kotlin effects { // Thorns-like retaliation postAttack { on(PostAttackSpecifier.VICTIM, PostAttackSpecifier.ATTACKER) { damageEntity(DamageTypes.THORNS, 1, 3) } } } ``` ### Special Effect Components | Component | Description | |--------------------------|--------------------------------------| | `attributes` | Applies attribute modifiers | | `crossbowChargingSounds` | Custom crossbow sounds | | `damageImmunity` | Grants immunity to matching hits | | `preventArmorChange` | Prevents removing from armor slot | | `preventEquipmentDrop` | Prevents item from dropping on death | | `tridentSound` | Custom trident sounds | ```kotlin effects { // Immunity to matching hits, one entry per condition set damageImmunity { requirements { weatherCheck(raining = true) } } // Curse-like effects preventEquipmentDrop() preventArmorChange() // Attribute modifiers attributes { attribute( "bonus_speed", name, Attributes.MOVEMENT_SPEED, AttributeModifierOperation.ADD_MULTIPLIED_BASE, constantLevelBased(0.1f) // +10% speed ) } // One sound entry per enchantment level crossbowChargingSounds { level { start(SoundEvents.Item.Crossbow.QUICK_CHARGE_1) mid(SoundEvents.Item.Crossbow.QUICK_CHARGE_2) end(SoundEvents.Item.Crossbow.QUICK_CHARGE_3) } } } ``` ## Entity Effects The builders below are available in every entity effect component, in `allOf` blocks and inside a `postAttack` `on` block. ### All Of ```kotlin allOf { requirements { weatherCheck(raining = true) } // only on the outermost allOf ignite(5) damageItem(1) } ``` ### Apply Exhaustion ```kotlin applyExhaustion(5) ``` ### Apply Impulse ```kotlin applyImpulse( coordinateScale = Vec3f(1f, 1f, 1f), // factor applied to the current motion, per axis direction = Vec3f(y = 1f), // direction the entity is pushed towards magnitude = constantLevelBased(2) // strength of the push, in blocks per tick ) ``` ### Apply Mob Effect ```kotlin applyMobEffect(Effects.SLOWNESS) { toApply(Effects.SLOWNESS, Effects.WEAKNESS) // one is picked at random minDuration(5) maxDuration = linearLevelBased(5, 5) amplifier(1) // sets both bounds at once } ``` ### Damage Entity ```kotlin damageEntity(DamageTypes.MAGIC, minDamage = 1, maxDamage = 5) ``` ### Damage Item ```kotlin damageItem(1) // durability removed from the enchanted item ``` ### Explode `smallParticle`, `largeParticle` and `sound` are required; everything else has a vanilla default: ```kotlin explode( smallParticle = Particles.GUST_EMITTER_SMALL, largeParticle = Particles.GUST_EMITTER_LARGE, sound = SoundEvents.Entity.WindCharge.WIND_BURST, ) { blockInteraction = BlockInteraction.TRIGGER radius(3.5f) knockbackMultiplier(linearLevelBased(1.5f, 0.35f)) immuneBlocks(Tags.Block.BLOCKS_WIND_CHARGE_EXPLOSIONS) blockParticles { particle(2, Particles.ASH, scaling = 0.5f) } } ``` ### Ignite ```kotlin ignite(linearLevelBased(4, 4)) // seconds ``` ### Play Sound ```kotlin playSound(SoundEvents.Entity.FireworkRocket.LAUNCH, range = 16f) { volume(1f) pitch(1.2f) } ``` ### Replace Block / Disk ```kotlin replaceBlock { blockState = simpleStateProvider(Blocks.FIRE) offset(0, 1, 0) triggerGameEvent = GameEvents.BLOCK_PLACE } replaceDisk { blockState = simpleStateProvider(Blocks.FROSTED_ICE) radius = clampedLevelBased(linearLevelBased(3, 1), 0f, 16f) height(1) offset(0, -1, 0) predicate { matchingBlocks(Blocks.WATER) } } ``` ### Set Block Properties ```kotlin setBlockProperties { properties("lit" to "true") offset(0, -1, 0) } ``` ### Spawn Particles ```kotlin spawnParticles( Particles.FLAME, horizontalPositionType = ParticlePositionType.IN_BOUNDING_BOX, verticalPositionType = ParticlePositionType.IN_BOUNDING_BOX ) { horizontalVelocity(base = 0.1f, movementScale = 0f) verticalVelocity(base = 0.5f, movementScale = 0f) } ``` Particles carrying options take a builder instead of a plain `ParticleTypeArgument`: `blockParticleType`, `dustParticleType`, `dustColorTransitionParticleType`, `itemParticleType`, `geyserParticleType`, `geyserBaseParticleType`, `geyserPlumeParticleType` and `geyserPoofParticleType`. ```kotlin spawnParticles( geyserBaseParticleType(Particles.GEYSER_BASE, burstImpulseBase = 0.5f, waterBlocks = 3), horizontalPositionType = ParticlePositionType.ENTITY_POSITION, verticalPositionType = ParticlePositionType.ENTITY_POSITION ) ``` ### Run Function ```kotlin runFunction(FunctionArgument("on_hit", "my_datapack")) ``` ### Summon Entity ```kotlin summonEntity(EntityTypes.LIGHTNING_BOLT, joinTeam = true) ``` ## Level-Based Values Level-based values allow effects to scale with enchantment level. Every one of them is a float, so fractional results are expressible, and every builder is scoped to the block that accepts a value. | Type | Description | Example | |--------------------------------------|-------------------|-----------------------------------------------| | `clampedLevelBased(value, min, max)` | Clamped range | `clampedLevelBased(linear, 1f, 10f)` | | `constantLevelBased(value)` | Fixed value | `constantLevelBased(5)` | | `exponentLevelBased(base, power)` | Exponential | `exponentLevelBased(1, 5)` | | `fractionLevelBased(num, denom)` | Fractional | `fractionLevelBased(1, 2)` | | `levelsSquaredLevelBased(added)` | Quadratic scaling | `levelsSquaredLevelBased(1)` → 1, 4, 9... | | `linearLevelBased(base, perLevel)` | Linear scaling | `linearLevelBased(2f, 0.5f)` → 2, 2.5, 3... | | `lookupLevelBased(values, fallback)` | Lookup table | `lookupLevelBased(1, 3, 7, fallback = 10)` | Outside a DSL block, `LevelBased` is a scope of its own, so `LevelBased.linearLevelBased(1, 1)` builds a value anywhere. ```kotlin effects { damage { // Linear: 2 + 0.5 per level → 2, 2.5, 3, 3.5, 4 for levels 1-5 add(linearLevelBased(2f, 0.5f)) } blockExperience { allOf { add(clampedLevelBased(linearLevelBased(1, 2), 0f, 10f)) multiply(levelsSquaredLevelBased(0.1f)) } } } ``` ## Requirements (Conditions) Every effect accepts a `requirements` block holding [predicate](/docs/data-driven/predicates) conditions, lifted next to the effect in the generated JSON. The game validates enchantments without access to other files and refuses to load one holding a `reference` condition, so Kore copies the conditions of each referenced predicate of the pack in its place (an `all_of` when it holds several), and throws for a predicate defined outside the pack: ```kotlin effects { damage { add(5) { requirements { weatherCheck(raining = true) } } } postAttack { on(PostAttackSpecifier.ATTACKER, PostAttackSpecifier.VICTIM) { applyMobEffect(Effects.POISON) { requirements { entityProperties { type(Tags.EntityType.UNDEAD) } } } } } } ``` ## Full Example ```kotlin dataPack("custom_enchants") { enchantment("vampiric") { description = textComponent("Vampiric") { color = Color.DARK_RED } supportedItems(Tags.Item.SWORDS) primaryItems(Tags.Item.Enchantable.SHARP_WEAPON) exclusiveSet(Enchantments.MENDING) weight = 2 maxLevel = 3 minCost(20, 15) maxCost(50, 15) anvilCost = 8 slots(EquipmentSlot.MAINHAND) effects { // Lifesteal on hit postAttack { on(PostAttackSpecifier.ATTACKER, PostAttackSpecifier.ATTACKER) { applyMobEffect(Effects.INSTANT_HEALTH) { duration(1) requirements { randomChance(0.3f) } } } } // Bonus damage to undead damage { add(linearLevelBased(2, 1)) { requirements { entityProperties { type(Tags.EntityType.UNDEAD) } } } } } } } ``` ## Enchantment Providers Enchantment providers pick the enchantments an item receives outside the enchanting table, such as the gear mobs spawn with or the crossbow of a raid pillager. They generate `data//enchantment_provider/.json`, and a name holding slashes lands in subfolders. Every vanilla provider is listed as an `EnchantmentProviders` constant, such as `EnchantmentProviders.Raid.VINDICATOR`, whose `asId()` is the name to reuse to override it. ```kotlin dataPack("custom_enchants") { enchantmentProviders { // One enchantment, at a level rolled from an int provider single("pillager_spawn_crossbow", Enchantments.PIERCING, uniform(1, 3)) // An enchanting power budget spent on a set of enchantments byCost("mob_spawn_equipment", Tags.Enchantment.ON_MOB_SPAWN_EQUIPMENT, cost = uniform(5, 25)) // The same, scaled with the regional difficulty byCostWithDifficulty("raid/pillager_post_wave_3", Enchantments.PIERCING, minCost = 10, maxCostSpan = 15) // Villager trade providers follow the __ naming single(villagerTradeName(Biomes.PLAINS, VillagerProfessions.ARMORER, 2), Enchantments.PROTECTION) } } ``` ## Best Practices 1. **Balance carefully** - Test enchantment power at all levels; use appropriate weights 2. **Use exclusive sets** - Prevent overpowered combinations with incompatible enchantments 3. **Scale appropriately** - Use level-based values that provide meaningful progression 4. **Add requirements** - Use conditions to create situational bonuses 5. **Consider slots** - Ensure effects only apply in appropriate equipment slots 6. **Test thoroughly** - Verify effects work correctly in all contexts (PvP, PvE, etc.) ## See Also - [Predicates](/docs/data-driven/predicates) - Conditions for enchantment effect requirements - [Components](/docs/concepts/components) - Item components and matchers - [Tags](/docs/data-driven/tags) - Use enchantment and item tags - [Villager Trades](/docs/data-driven/villager-trades) - `doubleTradePriceEnchantments` and enchanting villager trade outputs ### External Resources - [Minecraft Wiki: Enchantment definition](https://minecraft.wiki/w/Enchantment_definition) - Official JSON format reference - [Minecraft Wiki: Enchantment provider](https://minecraft.wiki/w/Enchantment_provider) - Provider JSON format reference - [Minecraft Wiki: Enchanting](https://minecraft.wiki/w/Enchanting) - Enchanting mechanics overview --- ## Item Modifiers --- root: .components.layouts.MarkdownLayout title: Item Modifiers nav-title: Item Modifiers description: Transform item stacks using Kore's type-safe DSL for loot functions - set counts, add enchantments, copy data, and more. keywords: minecraft, datapack, kore, item modifiers, loot functions, /item modify, components date-created: 2025-08-11 date-modified: 2026-10-07 routeOverride: /docs/data-driven/item-modifiers --- # Item Modifiers Item modifiers (also called loot functions) transform item stacks by adjusting counts, adding enchantments, copying data, setting components, and more. They can be defined as standalone JSON files referenced by commands, or used inline within loot tables. ## Overview Item modifiers have several key characteristics: - **Composable**: Chain multiple functions together for complex transformations - **Conditional**: Each function can have predicate conditions - **Context-aware**: Access loot context for killer, tool, block entity, etc. - **Reusable**: Define once as a file, reference anywhere ### Common Use Cases | Use Case | Functions | |----------------------|-----------------------------------------------------------| | Set stack count | `setCount` | | Add enchantments | `enchantRandomly`, `enchantWithLevels`, `setEnchantments` | | Modify durability | `setDamage` | | Copy NBT/components | `copyComponents`, `copyCustomData`, `copyName` | | Set name/lore | `setName`, `setLore` | | Create explorer maps | `explorationMap` | | Fill containers | `setContents`, `setLootTable` | | Apply formulas | `applyBonus`, `enchantedCountIncrease` | ## File Structure Item modifiers are stored as JSON files in data packs at: ``` data//item_modifier/.json ``` For complete JSON specification, see the [Minecraft Wiki - Item modifier](https://minecraft.wiki/w/Item_modifier). ## Creating Item Modifiers Use the `itemModifier` builder function to create item modifiers in Kore: ```kotlin dataPack("my_datapack") { val modifier = itemModifier("fortune_bonus") { enchantRandomly(Enchantments.FORTUNE) setCount(uniform(1f, 5f)) } } ``` This generates `data/my_datapack/item_modifier/fortune_bonus.json`. ## Using Item Modifiers ### With Commands Apply modifiers to items using the `/item modify` command: ```kotlin load { // Modify item in player's mainhand items.modify(self(), WEAPON.MAINHAND, modifier) // Modify item in the first slot of the container at 0 64 0 items.modify(vec3(0, 64, 0), CONTAINER[0], modifier) } ``` ### In Loot Tables Use functions directly in loot tables at table, pool, or entry level: ```kotlin lootTable("treasure") { // Table-level functions (applied to all drops) functions { enchantRandomly() } pool { // Pool-level functions functions { setCount(uniform(1f, 3f)) } entries { item(Items.DIAMOND) { // Entry-level functions functions { setName("Lucky Diamond") } } } } } ``` ## Function Reference ### Count and Damage #### setCount Sets or modifies the stack count: ```kotlin itemModifier("set_count") { // Exact count setCount(5f) // Random range setCount(uniform(1f, 10f)) // Add to current count setCount(5f, add = true) // With condition setCount(10f) { conditions { killedByPlayer() } } } ``` #### setDamage Sets item durability (1.0 = full, 0.0 = broken): ```kotlin itemModifier("damage") { // Set to 80% durability setDamage(0.8f) // Random damage setDamage(uniform(0.5f, 1.0f)) // Add to current damage setDamage(-0.1f, add = true) // Repair 10% } ``` #### limitCount Clamps stack count to a range: ```kotlin itemModifier("limit") { // Exact limit limitCount(64) // Range limitCount(intRange(min = constant(1f), max = constant(32f))) } ``` ### Enchantments #### enchantRandomly Adds a random enchantment: ```kotlin itemModifier("random_enchant") { // Any enchantment enchantRandomly() // From specific list enchantRandomly { enchantments { add(Enchantments.SHARPNESS) add(Enchantments.SMITE) add(Enchantments.BANE_OF_ARTHROPODS) } } // Only compatible enchantments enchantRandomly(onlyCompatible = true) // Include the additional cost component from trade costs enchantRandomly(Enchantments.SHARPNESS) { includeAdditionalCostComponent = true } } ``` An empty enchantment list leaves `options` out of the JSON: the game reads `"options": []` as an empty candidate set and leaves the item unenchanted. #### enchantWithLevels Enchants as if using an enchanting table: ```kotlin itemModifier("table_enchant") { // Fixed level enchantWithLevels(levels = constant(30f)) // Random level range enchantWithLevels(levels = uniform(20f, 39f)) // Limit to specific enchantments enchantWithLevels(Enchantments.PROTECTION, levels = constant(30f)) // Include the additional cost component from trade costs enchantWithLevels(levels = constant(30f)) { includeAdditionalCostComponent = true } } ``` #### setEnchantments Sets specific enchantments and levels: ```kotlin itemModifier("specific_enchants") { setEnchantments { this[Enchantments.SHARPNESS] = constant(5f) this[Enchantments.UNBREAKING] = constant(3f) this[Enchantments.MENDING] = constant(1f) } } ``` #### enchantedCountIncrease Increases count based on enchantment level (like Looting): ```kotlin itemModifier("looting_bonus") { enchantedCountIncrease(Enchantments.LOOTING, count = 1f, limit = 5) } ``` ### Names and Lore #### setName Sets the item's display name: ```kotlin itemModifier("named") { // Simple string setName("Legendary Sword") // Text component with formatting setName(text("Legendary Sword", Color.GOLD) { bold = true }) // Several components joined with + setName(textComponent("Legendary ", Color.GOLD) + textComponent("Sword")) // Set item name vs custom name setName("Base Name") { target = SetNameTarget.ITEM_NAME // or CUSTOM_NAME } } ``` #### setLore Sets or modifies item lore: ```kotlin itemModifier("lore") { setLore { lore("First line", Color.GRAY) lore("Second line", Color.DARK_GRAY) // Insert at specific position mode(Mode.INSERT, offset = 0) } } ``` ### Components #### setComponents Directly set item components: ```kotlin itemModifier("components") { setComponents { customName(textComponent("Custom Item", Color.GOLD)) damage(10) unbreakable() // Remove component with ! !ItemComponentTypes.FOOD } } ``` #### copyComponents Copy components from a source: ```kotlin itemModifier("copy_from_block") { copyComponents { source = CopyComponentsSource.BLOCK_ENTITY // Include specific components include(ItemComponentTypes.CUSTOM_NAME, ItemComponentTypes.LORE) // Or exclude specific components exclude(ItemComponentTypes.DAMAGE) } } ``` #### copyName Copy entity/block name to item: ```kotlin itemModifier("named_drop") { copyName(Source.BLOCK_ENTITY) // Or from entity copyName(Source.THIS) copyName(Source.ATTACKER) } ``` #### setCustomData Set custom NBT data on an item: ```kotlin itemModifier("custom_data") { setCustomData { this["test"] = 1 } } ``` #### setCustomModelData Set custom model data on an item: ```kotlin itemModifier("custom_model") { setCustomModelData( colors = listOf(Color.RED, Color.BLUE), flags = listOf(true, false), floats = listOf(1.0f, 2.0f), strings = listOf("test1", "test2") ) } ``` Each float is a [number provider](/docs/data-driven/loot-tables#number-providers), so `floats(...)` can read it from a score or add several sources with `sum`, which also works as float math on scores: ```kotlin itemModifier("model_from_score") { setCustomModelData { floats(sum(scoreNumber("charge", "#gun"), constant(0.5f))) } } ``` #### copyCustomData Copy NBT data to custom_data component: ```kotlin itemModifier("copy_nbt") { copyCustomData(Source.BLOCK_ENTITY) { replace("Items", "BlockItems") merge("Lock", "OriginalLock") } } ``` ### Container Contents #### setContents Fill container items (bundles, shulker boxes): ```kotlin itemModifier("filled_bundle") { setContents(ContentComponentTypes.BUNDLE_CONTENTS) { entries { item(Items.DIAMOND) { functions { setCount(16f) } } item(Items.EMERALD) { functions { setCount(32f) } } } } } ``` #### setLootTable Set a container's loot table: ```kotlin itemModifier("chest_loot") { setLootTable(BlockEntityTypes.CHEST, LootTables.Chests.SIMPLE_DUNGEON, seed = 42) } ``` #### modifyContents Apply modifiers to items inside a container: ```kotlin itemModifier("enchant_bundle_contents") { modifyContents(ContentComponentTypes.BUNDLE_CONTENTS) { modifiers { enchantRandomly() } } } ``` ### Maps and Exploration #### explorationMap Convert empty map to explorer map: ```kotlin itemModifier("treasure_map") { explorationMap { destination = Tags.Worldgen.Structure.ON_TREASURE_MAPS decoration = MapDecorationTypes.RED_X zoom = 2 searchRadius = 50 skipExistingChunks = true } } ``` ### Special Items #### setInstrument Set a goat horn instrument using a tag, a single ID, or multiple IDs/tags: ```kotlin // tag itemModifier("horn_tag") { setInstrument(Tags.Instrument.GOAT_HORNS) } // single ID itemModifier("horn_id") { setInstrument(Instruments.ADMIRE_GOAT_HORN) } // list of IDs/tags itemModifier("horn_list") { setInstrument(Instruments.ADMIRE_GOAT_HORN, Instruments.SING_GOAT_HORN) } ``` #### setItem Change the item type: ```kotlin itemModifier("change_item") { setItem(Items.APPLE) } ``` #### setOminousBottleAmplifier Set the amplifier of an ominous bottle: ```kotlin itemModifier("ominous_bottle") { setOminousBottleAmplifier(5) } ``` #### setPotion Set potion type: ```kotlin itemModifier("potion") { setPotion(Potions.HEALING) } ``` #### setRandomPotion Set a random potion from a list of options (or tags): ```kotlin itemModifier("random_potion") { // Random from specific list setRandomPotion(Potions.HEALING, Potions.SWIFTNESS) } ``` #### setRandomDyes Set a random number of dyes on the item using a number provider: ```kotlin itemModifier("random_dyes") { // Fixed count setRandomDyes(3f) // Dynamic count setRandomDyes(uniform(1f, 5f)) } ``` #### setStewEffect Set suspicious stew effects: ```kotlin itemModifier("stew") { setStewEffect { potionEffect(Potions.NIGHT_VISION, constant(200f)) } } ``` #### setFireworkExplosion Set a single firework explosion: ```kotlin itemModifier("firework_explosion") { setFireworkExplosion(FireworkExplosionShape.STAR) { colors = listOf(Color.RED.toRGB()) fadeColors = listOf(Color.BLUE.toRGB()) hasTwinkle = true hasTrail = true } } ``` #### setFireworks Configure firework rocket: ```kotlin itemModifier("firework") { setFireworks { flightDuration = 5 explosions { explosion(FireworkExplosionShape.BURST) { colors(Color.RED) fadeColors(Color.BLUE) hasTrail = true hasTwinkle = true } mode(Mode.REPLACE_ALL) } } } ``` #### setBookCover Set written book cover. The title is plain text up to 32 characters, not a text component, pass `WritablePage("Adventure Log", filtered = "...")` to also give the version shown with chat filtering: ```kotlin itemModifier("book") { setBookCover( title = "Adventure Log", author = "Player", generation = 0 ) } ``` #### setWritableBookPages Set pages of a writable book: ```kotlin itemModifier("writable_book") { setWritableBookPages { page("test", filtered = "filtered") mode(Mode.INSERT, 1) } } ``` #### setWrittenBookPages Set pages of a written book: ```kotlin itemModifier("written_book") { setWrittenBookPages { page("test", filtered = "test2") } } ``` ### Attributes #### setAttributes Add attribute modifiers: ```kotlin itemModifier("buffed") { setAttributes { attribute( attribute = Attributes.ATTACK_DAMAGE, operation = AttributeModifierOperation.ADD_VALUE, amount = constant(5f), id = "bonus_damage", slot = listOf(EquipmentSlot.MAINHAND) ) attribute( attribute = Attributes.MOVEMENT_SPEED, operation = AttributeModifierOperation.ADD_MULTIPLIED_BASE, amount = constant(0.1f), id = "speed_boost", slot = listOf(EquipmentSlot.FEET) ) } } ``` ### Banners and Patterns #### setBannerPattern Add banner patterns: ```kotlin itemModifier("banner") { setBannerPattern(append = true) { bannerPattern(BannerPatterns.CREEPER, FormattingColor.BLACK) } } ``` ### Block State #### copyState Copy block state to item: ```kotlin itemModifier("block_state") { copyState(Blocks.FURNACE, "facing", "lit") } ``` ### Bonus Formulas #### applyBonus Apply enchantment-based bonus formulas: ```kotlin itemModifier("fortune") { // Ore drops formula applyBonus(Enchantments.FORTUNE, OreDrops) // Uniform bonus applyBonus(Enchantments.FORTUNE, UniformBonusCount(bonusMultiplier = 1f)) // Binomial distribution applyBonus(Enchantments.FORTUNE, BinomialWithBonusCount(extra = 3, probability = 0.5f)) } ``` ### Smelting and Decay #### furnaceSmelt Smelt the item as if in a furnace: ```kotlin itemModifier("auto_smelt") { furnaceSmelt() } ``` #### explosionDecay Random chance to destroy items based on explosion: ```kotlin itemModifier("explosion") { explosionDecay() } ``` ### Player Heads #### fillPlayerHead Set player head skin: ```kotlin itemModifier("head") { fillPlayerHead(Source.ATTACKER) } ``` ### Tooltips #### toggleTooltips Show/hide tooltip sections: ```kotlin itemModifier("clean_tooltip") { toggleTooltips { toggle(true, ItemComponentTypes.TRIM, ItemComponentTypes.CAN_PLACE_ON) toggles(ItemComponentTypes.DYED_COLOR to false) } } ``` ### Composition #### sequence Run multiple functions in sequence: ```kotlin itemModifier("complex") { sequence { setCount(1f) enchantRandomly() setName("Mystery Item") } } ``` #### discard Replaces the produced item stack with an empty one: ```kotlin itemModifier("discard_example") { discard() } ``` #### filtered Apply functions depending on whether the item matches a filter: ```kotlin itemModifier("filter") { filtered { itemFilter(Items.DIAMOND, Items.EMERALD) onFail { discard() } onPass { setCount(uniform(1f, 5f)) } } } ``` #### reference Reference another item modifier: ```kotlin val baseModifier = itemModifier("base") { setCount(1f) } itemModifier("extended") { reference(baseModifier) enchantRandomly() } ``` ## Conditions Every function can have conditions that must pass: ```kotlin itemModifier("conditional") { setCount(10f) { conditions { // Multiple conditions are AND-ed killedByPlayer() randomChance(0.5f) } } enchantRandomly { conditions { weatherCheck(raining = true) } } } ``` See [Predicates](/docs/data-driven/predicates) for all available conditions. ## Full Example ```kotlin dataPack("legendary_items") { val legendaryModifier = itemModifier("legendary_weapon") { // High-level enchantments enchantWithLevels(levels = constant(30f)) { conditions { randomChance(0.3f) } } // Guaranteed enchantments setEnchantments { this[Enchantments.UNBREAKING] = constant(3f) } // Custom name with formatting setName(text("Legendary Weapon", Color.GOLD) { bold = true }) // Lore setLore { lore("Forged in ancient flames", Color.GRAY) lore("", Color.WHITE) lore("▸ +5 Attack Damage", Color.GREEN) mode(Mode.REPLACE_ALL) } // Attributes setAttributes(replace = false) { attribute( attribute = Attributes.ATTACK_DAMAGE, operation = AttributeModifierOperation.ADD_VALUE, amount = constant(5f), id = "legendary_damage", slot = listOf(EquipmentSlot.MAINHAND) ) } // Full durability setDamage(1.0f) } // Use in loot table lootTable("boss_weapon") { pool { rolls = constant(1f) entries { item(Items.DIAMOND_SWORD) { functions { reference(legendaryModifier) } } } } } // Use with command load { items.modify(self(), WEAPON.MAINHAND, legendaryModifier) } } ``` ### Generated JSON ```json [ { "function": "minecraft:enchant_with_levels", "levels": 30.0, "conditions": [ { "condition": "minecraft:random_chance", "chance": 0.3 } ] }, { "function": "minecraft:set_enchantments", "enchantments": { "minecraft:unbreaking": 3 } }, { "function": "minecraft:set_name", "name": { "text": "Legendary Weapon", "color": "gold", "bold": true } }, { "function": "minecraft:set_lore", "lore": [ { "text": "Forged in ancient flames", "color": "gray" }, { "text": "" }, { "text": "▸ +5 Attack Damage", "color": "green" } ], "mode": "replace_all" }, { "function": "minecraft:set_attributes", "modifiers": [ { "attribute": "minecraft:attack_damage", "id": "minecraft:legendary_damage", "amount": 5.0, "operation": "add_value", "slot": "mainhand" } ], "replace": false }, { "function": "minecraft:set_damage", "damage": 1.0 } ] ``` ## Best Practices 1. **Keep modifiers focused** - Create small, reusable modifiers and compose with `reference()` 2. **Use conditions wisely** - Guard expensive operations with appropriate conditions 3. **Prefer components** - Use `setComponents` for direct component manipulation when possible 4. **Consider context** - Some functions require specific loot contexts (killer, tool, etc.) 5. **Test thoroughly** - Verify modifiers work in all intended contexts (loot, commands, etc.) ## See Also - [Predicates](/docs/data-driven/predicates) - Conditions for item functions - [Components](/docs/concepts/components) - Understanding item components - [Loot Tables](/docs/data-driven/loot-tables) - Use item modifiers in loot tables - [Commands](/docs/commands/commands) - Using the `/item` command - [Villager Trades](/docs/data-driven/villager-trades) - Apply item modifiers to villager trade outputs via `givenItemModifiers` ### External Resources - [Minecraft Wiki: Item modifier](https://minecraft.wiki/w/Item_modifier) - Official JSON format reference --- ## Loot Tables --- root: .components.layouts.MarkdownLayout title: Loot Tables nav-title: Loot Tables description: Create and customize Minecraft loot tables using Kore's type-safe Kotlin DSL for drops, container contents, fishing, and more. keywords: minecraft, datapack, kore, loot tables, pools, entries, item modifiers, drops date-created: 2025-08-11 date-modified: 2026-10-08 routeOverride: /docs/data-driven/loot-tables --- # Loot Tables Loot tables are JSON files that dictate what items should generate in various game situations. They control drops from mobs and blocks, contents of naturally generated containers (chests, barrels, dispensers), fishing rewards, archaeology brushing results, bartering exchanges, and more. The [loot table example](/playground#example=loot-table) of the playground runs in your browser and shows the JSON it generates. ## Overview Loot tables have several key characteristics: - **Context-dependent**: Different loot contexts provide different parameters (killer entity, tool, luck level, etc.) - **Randomized**: Use number providers for rolls and weighted entries for varied results - **Conditional**: Apply predicates to pools, entries, and functions - **Composable**: Reference other loot tables as entries for modularity - **Transformable**: Apply item modifier functions to modify generated items ## File Structure Loot tables are stored as JSON files in data packs at: ``` data//loot_table/.json ``` For complete JSON specification, see the [Minecraft Wiki - Loot table](https://minecraft.wiki/w/Loot_table). ## Creating Loot Tables Use the `lootTable` builder function to create loot tables in Kore: ```kotlin dataPack("my_datapack") { lootTable("custom_chest") { pool { rolls = constant(3f) entries { item(Items.DIAMOND) { weight = 1 } item(Items.GOLD_INGOT) { weight = 5 } item(Items.IRON_INGOT) { weight = 10 } } } } } ``` This generates `data/my_datapack/loot_table/custom_chest.json`. {{{ .components.mc.LootChestMockup }}} ## Table Structure A loot table consists of: | Property | Type | Description | |------------------|--------------------------|-----------------------------------------------------| | `type` | `LootTableType` | Optional context type for validation | | `pools` | `List` | One or more pools that generate items | | `functions` | `ItemModifier` | Optional global item functions applied to all drops | | `randomSequence` | `RandomSequenceArgument` | Optional deterministic random sequence | ### Setting the Type The type validates that the loot table uses appropriate context parameters: ```kotlin lootTable("entity_drops") { type = LootTableType.ENTITY pool { // Entity context allows accessing killer, damage source, etc. } } ``` Available types: `ADVANCEMENT_ENTITY`, `ADVANCEMENT_LOCATION`, `ADVANCEMENT_REWARD`, `ARCHEOLOGY`, `BARTER`, `BLOCK`, `BLOCK_USE`, `CHEST`, `COMMAND`, `EMPTY`, `ENTITY`, `EQUIPMENT`, `FISHING`, `GIFT`, `GENERIC`, `SELECTOR`, `SHEARING`, `VAULT`, `VILLAGER_TRADE`. ### Global Functions Apply item modifier functions to all items dropped by the table: ```kotlin lootTable("enchanted_loot") { functions { enchantRandomly(Enchantments.LOOTING) } pool { entries { item(Items.DIAMOND_SWORD) } } } ``` ## Pools Each pool represents an independent set of rolls. A table can have multiple pools that all contribute to the final loot. ### Pool Properties | Property | Type | Description | |--------------|-------------------|------------------------------------------------| | `rolls` | `NumberProvider` | How many times to select from entries | | `bonusRolls` | `NumberProvider` | Additional rolls per luck level | | `conditions` | `Predicate` | Conditions that must pass for pool to activate | | `entries` | `List` | Possible entries to select from | | `functions` | `ItemModifier` | Functions applied to items from this pool | ### Basic Pool ```kotlin lootTable("simple_pool") { pool { rolls = constant(2f) bonusRolls = constant(1f) entries { item(Items.EMERALD) } } } ``` ### Conditional Pool ```kotlin lootTable("weather_dependent") { pool { rolls = constant(1f) conditions { weatherCheck(raining = true) } entries { item(Items.WATER_BUCKET) } } } ``` ### Pool with Functions ```kotlin lootTable("modified_drops") { pool { rolls = constant(1f) entries { item(Items.DIAMOND_PICKAXE) } functions { setDamage(0.5f) enchantWithLevels(levels = constant(30f)) } } } ``` ## Number Providers Number providers determine dynamic numeric values for rolls, counts, and other quantities. | Provider | Description | Example | |-----------------------------------|-----------------------------------------------------------|-------------------------------------------------------------------------| | `binomial(n, p)` | Binomial distribution | `binomial(5f, 0.5f)` | | `constant(value)` | Fixed value | `constant(5f)` | | `enchantmentLevel(...)` | Based on enchantment level (requires enchantment context) | `enchantmentLevel(5)` | | `environmentAttribute(attribute)` | Current value of a numeric env attribute | `environmentAttribute(EnvironmentAttributes.Visual.FOG_START_DISTANCE)` | | `scoreNumber(...)` | Value from a scoreboard objective | `scoreNumber("kills", EntityTarget.THIS)` | | `storageNumber(storage, path)` | Value from command storage | `storageNumber("my_pack:data", "player.health")` | | `sum(...)` | Sum of multiple providers | `sum(constant(1f), uniform(1f, 3f))` | | `uniform(min, max)` | Random value between min and max | `uniform(1f, 5f)` | ```kotlin pool { rolls = uniform(2f, 5f) // 2-5 rolls randomly bonusRolls = constant(1f) // +1 roll per luck level entries { item(Items.GOLD_INGOT) } } ``` ## Entries Entries define what can be selected during a pool roll. There are singleton entries (yield items) and composite entries (combine other entries). ### Singleton Entries #### Item Entry Drops a specific item: ```kotlin entries { item(Items.DIAMOND) { weight = 1 quality = 2 conditions { randomChance(0.5f) } functions { setCount(uniform(1f, 3f)) } } } ``` The entry's `conditions` and `functions` live in the `features.loottables.entries` package, the pool's and table's in `features.loottables`. The loot table, item modifier and predicate builders are DSL-scoped, so calling a builder that only an enclosing pool or table has fails to compile rather than silently adding to that pool: import the entry's version when the compiler reports it. #### Loot Table Entry References another loot table: ```kotlin entries { lootTable(LootTables.Gameplay.PIGLIN_BARTERING) { weight = 1 functions { setCount(2f) } } } ``` #### Tag Entry Drops items from an item tag: ```kotlin entries { tag(Tags.Item.ARROWS) { expand = true // Each item becomes a separate entry weight = 1 } } ``` #### Dynamic Entry For block-specific drops (decorated pot sherds or shulkers contents): ```kotlin entries { dynamic(LootEntryDynamicName.SHERDS) { // Drops contents of decorated pot sherds } } ``` #### Empty Entry A weighted entry that drops nothing (useful for rarity): ```kotlin entries { empty(weight = 10) // 10x more likely than weight=1 entries } ``` ### Composite Entries #### Alternatives Selects the first entry whose conditions pass: ```kotlin entries { alternatives { children { item(Items.DIAMOND) { conditions { randomChance(0.1f) } } item(Items.GOLD_INGOT) { conditions { randomChance(0.3f) } } item(Items.IRON_INGOT) // Fallback } } } ``` #### Group All children are added to the pool if conditions pass: ```kotlin entries { group { conditions { weatherCheck(raining = true) } children { item(Items.WATER_BUCKET) item(Items.COD) } } } ``` #### Sequence Children are added until one fails its conditions: ```kotlin entries { sequence { children { item(Items.DIAMOND) { conditions { randomChance(0.5f) } } item(Items.EMERALD) { conditions { randomChance(0.5f) } } item(Items.GOLD_INGOT) } } } ``` #### Slots Entry Selects items from inventory slots specified by a [slot source](/docs/data-driven/slot-sources): ```kotlin entries { slots { slotSources { slotRange(SlotSourceOrigin.THIS, HOTBAR) } } } ``` ### Entry Properties Singleton entries share these properties: | Property | Type | Description | |--------------|----------------|----------------------------------------------------------| | `weight` | `Int` | Selection weight (higher = more likely) | | `quality` | `Int` | Modifies weight based on luck: `weight + quality × luck` | | `conditions` | `Predicate` | Entry only available if conditions pass | | `functions` | `ItemModifier` | Functions applied to this entry's items | ## Conditions (Predicates) Conditions control when pools, entries, or functions apply. They use the same Predicate system as advancements. ```kotlin pool { conditions { // Multiple conditions are AND-ed weatherCheck(raining = true) randomChance(0.5f) // Check entity properties entityProperties { entityType(EntityTypes.PLAYER) } // Check killer killedByPlayer() // Check tool matchTool { items = listOf(Items.DIAMOND_PICKAXE) } } } ``` See [Predicates](/docs/data-driven/predicates) for the complete list of available conditions. ## Functions (Item Modifiers) Functions transform the generated items. They can be applied at table, pool, or entry level. ### Common Functions ```kotlin functions { // Set stack count setCount(uniform(1f, 5f)) // Apply enchantments enchantRandomly(Enchantments.FORTUNE) // Set damage (durability) setDamage(0.8f) // Add custom name setName("Legendary Sword") // Conditional function setCount(10f) { conditions { killedByPlayer() } } } ``` See [Item Modifiers](/docs/data-driven/item-modifiers) for the complete list of available functions. ## Full Example ```kotlin dataPack("treasure_hunt") { // Custom boss drop table lootTable("boss_drops") { type = LootTableType.ENTITY // Global enchantment on all drops functions { enchantRandomly() } // Guaranteed drops pool { rolls = constant(1f) entries { item(Items.NETHER_STAR) } } // Rare equipment drops pool { rolls = constant(1f) bonusRolls = constant(0.5f) conditions { killedByPlayer() } entries { item(Items.NETHERITE_SWORD) { weight = 1 functions { enchantWithLevels(levels = constant(30f)) setName("Boss Slayer") } } item(Items.DIAMOND_SWORD) { weight = 5 functions { enchantWithLevels(levels = uniform(15f, 25f)) } } empty(weight = 10) } } // Bonus loot from existing table pool { rolls = uniform(1f, 3f) entries { lootTable(LootTables.Chests.END_CITY_TREASURE) } } } } ``` ### Generated JSON ```json { "type": "minecraft:entity", "functions": [ { "function": "minecraft:enchant_randomly" } ], "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "minecraft:nether_star" } ] }, { "rolls": 1.0, "bonus_rolls": 0.5, "conditions": [ { "condition": "minecraft:killed_by_player" } ], "entries": [ { "type": "minecraft:item", "name": "minecraft:netherite_sword", "weight": 1, "functions": [ { "function": "minecraft:enchant_with_levels", "levels": 30.0 }, { "function": "minecraft:set_name", "name": "Boss Slayer" } ] }, { "type": "minecraft:item", "name": "minecraft:diamond_sword", "weight": 5, "functions": [ { "function": "minecraft:enchant_with_levels", "levels": { "type": "minecraft:uniform", "min": 15.0, "max": 25.0 } } ] }, { "type": "minecraft:empty", "weight": 10 } ] }, { "rolls": { "type": "minecraft:uniform", "min": 1.0, "max": 3.0 }, "entries": [ { "type": "minecraft:loot_table", "name": "minecraft:chests/end_city_treasure" } ] } ] } ``` ## Using with Commands Spawn loot from a table using the `/loot` command. The surrounding command builders are documented in [Commands](/docs/commands/commands), while reusable conditions usually come from [Predicates](/docs/data-driven/predicates): ```kotlin load { // Give loot directly to player loot(self(), myLootTable) // Spawn loot at position loot { target { spawn(vec3(0, 64, 0)) } source { loot(myLootTable) } } // Insert loot into the container at 0 64 0 loot { target { insert(vec3(0, 64, 0)) } source { loot(myLootTable) } } } ``` ## Overriding Vanilla Tables To modify vanilla loot tables, create a file with the same path in your datapack. You can combine that with [Item Modifiers](/docs/data-driven/item-modifiers) when you want reusable post-processing logic instead of duplicating functions inside every pool: ```kotlin dataPack("better_zombies") { // This overrides minecraft:entities/zombie lootTable("entities/zombie") { namespace = "minecraft" pool { rolls = constant(1f) entries { item(Items.ROTTEN_FLESH) { functions { setCount(uniform(0f, 2f)) enchantedCountIncrease(Enchantments.LOOTING, uniform(0f, 1f)) } } } } // Add custom drops pool { rolls = constant(1f) conditions { randomChance(0.1f) } entries { item(Items.DIAMOND) } } } } ``` ## Best Practices 1. **Use appropriate types** - Set the `type` field to catch invalid context parameter usage early 2. **Organize with multiple pools** - Use separate pools for different drop categories (guaranteed, rare, conditional) 3. **Leverage composition** - Reference other loot tables instead of duplicating entries 4. **Weight appropriately** - Use meaningful weights (e.g., 1/5/10/50) for clear rarity tiers 5. **Apply conditions at the right level** - Pool conditions for entire categories, entry conditions for specific items ## See Also - [Item Modifiers](/docs/data-driven/item-modifiers) - Functions applied to loot table items - [Advancements](/docs/data-driven/advancements) - Rewards can reference loot tables - [Tags](/docs/data-driven/tags) - Use item tags for tag entries ### External Resources - [Minecraft Wiki: Loot table](https://minecraft.wiki/w/Loot_table) - Official JSON format reference - [Minecraft Wiki: Loot context](https://minecraft.wiki/w/Loot_context) - Understanding loot contexts --- ## Minecraft Predicates - Type-Safe Condition DSL in Kore --- root: .components.layouts.MarkdownLayout title: Minecraft Predicates - Type-Safe Condition DSL in Kore nav-title: Predicates description: Create Minecraft predicates with Kore's type-safe Kotlin DSL. Covers entity properties, location, weather, time, enchantments, damage, and NBT checks. Use in execute if/unless, loot tables, and advancements. keywords: minecraft predicates, datapack conditions, execute if predicate, entity properties check, location check, weather check, time check, damage predicate, kore predicates, minecraft condition dsl date-created: 2024-01-08 date-modified: 2026-10-02 routeOverride: /docs/data-driven/predicates --- # Predicates Predicates are JSON structures used in data packs to check conditions within the world. They return a pass or fail result to the invoker, which acts differently based on the result. In practical terms, predicates are a flexible way for data packs to encode "if this, then that" logic without needing custom code. Predicates can be used in: - **Commands**: Via [`execute if predicate`](/docs/commands/execute) or target selector argument `predicate=` - **Loot tables**: As conditions for loot entries - **Advancements**: As trigger conditions - **Other predicates**: Via the `reference` condition Kore provides a type-safe DSL to create predicates, eliminating the need to write raw JSON. ## Basic Usage Here's a simple example of creating a predicate that checks if a player is holding a diamond pickaxe: ```kotlin val myPredicate = predicate("test") { matchTool { items(Items.DIAMOND_PICKAXE) } } ``` The `predicate` function creates and registers a predicate in your DataPack. It produces a file at `data//predicate/.json` and returns a `PredicateArgument` that can be used in commands. ## Conditions Predicates can have multiple conditions that must be met. You can combine them using `allOf` or `anyOf`: ```kotlin predicate("complex_test") { allOf { enchantmentActiveCheck(true) randomChance(0.5f) randomChanceWithEnchantedBonus( unenchantedChance = 0.3f, enchantedChance = 2, Enchantments.EFFICIENCY ) weatherCheck(raining = true, thundering = false) } } ``` You can also use the `inverted` condition to invert the result of a predicate: ```kotlin predicate("inverted_test") { inverted { randomChance(0.5f) } } ``` ### Available Conditions Conditions are categorized by their **loot context requirements**. Some conditions can be invoked from any context, while others require specific data to be available. #### Universal Conditions (invokable from any context) | Condition | Description | |-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `allOf` | Evaluates a list of predicates and passes if **all** of them pass | | `anyOf` | Evaluates a list of predicates and passes if **any one** of them passes | | `entityProperties` | Checks properties of an entity | | `environmentAttributeCheck` | Passes if the specified environment attribute currently matches the given value | | `inverted` | Inverts another predicate condition | | `randomChance` | Passes if a random float between 0.0 and 1.0 is below the given `NumberProvider` value | | `reference` | Invokes another predicate file and returns its result (cannot be cyclic) | | `timeCheck` | Compares a world clock's time against a `NumberProvider` range (mandatory `clock` to select the clock, optional `period` for modulo) - see [World Clocks](/docs/data-driven/world-clocks#timecheck-predicate-condition) | | `valueCheck` | Compares a `NumberProvider` value against another `NumberProvider` or range | | `weatherCheck` | Checks the current game weather (raining, thundering) | > `randomChance`, `timeCheck`, and `valueCheck` accept a [ `NumberProvider`](/docs/data-driven/loot-tables#number-providers) for their numeric arguments, so you can use dynamic > values like scoreboard scores, enchantment levels, or environment attributes instead of plain floats. ### Environment Attribute Check `environmentAttributeCheck` passes when the specified environment attribute equals the given value. The value type is inferred from the attribute - booleans for toggle attributes, floats for numeric ones, strings for enum-like ones (moon phase, villager activity), and objects for compound ones (ambient sounds, background music). ```kotlin predicate("is_daytime") { // Boolean attribute - convenience overload, no wrapping needed environmentAttributeCheck(EnvironmentAttributes.Gameplay.MONSTERS_BURN, true) } predicate("dim_sky") { // Float attribute - convenience overload, no wrapping needed environmentAttributeCheck(EnvironmentAttributes.Visual.SKY_LIGHT_FACTOR, 0.5f) } predicate("full_moon") { // Builder block: call exactly one typed helper from EnvironmentAttributesScope. // It sets both the attribute ID and the expected value automatically. environmentAttributeCheck { moonPhase(Textures.Environment.Celestial.Moon.FULL_MOON) } } ``` The builder block accepts any number of calls from the same scope helpers used in dimension types and biomes ( `moonPhase`, `beesStayInHive`, `fogColor`, `ambientSounds`, etc.). Each attribute set in the block produces one `environment_attribute_check` condition, so multiple attributes are an implicit AND - all must match for the predicate to pass. #### Context-Dependent Conditions Most of these conditions require specific loot context data and will **always fail** if not provided. Three exceptions have optional context with graceful fallback behavior (noted in the table): | Condition | Required Context | Description | |----------------------------------|-----------------------------|---------------------------------------------------------------------------------------| | `blockStateProperty` | Block state | Checks the mined block and its block states | | `damageSourceProperties` | Origin + damage source | Checks properties of the damage source | | `enchantmentActiveCheck` | Enchantment active status | Checks if an enchantment is active (only usable from `enchanted_location` context) | | `entityScores` | Specified entity | Checks scoreboard scores of an entity against `NumberProvider` ranges | | `killedByPlayer` | `attacking_player` entity | Checks if there is an attacking player entity | | `locationCheck` | Origin | Checks the current location against location criteria (supports offsets) | | `matchTool` | Tool | Checks tool used to mine the block | | `randomChanceWithEnchantedBonus` | Attacker entity (optional) | Random chance modified by enchantment level (level 0 if no attacker) | | `survivesExplosion` | Explosion radius (optional) | Returns success with 1 ÷ explosion radius probability (always passes if no explosion) | | `tableBonus` | Tool (optional) | Passes with probability from a list indexed by enchantment power (level 0 if no tool) | ## Entity Properties The `entityProperties` condition allows you to check various properties of an entity. You must specify which entity to check using the `entity` parameter: ### Entity Context Options The `entity` parameter takes an `EntityTarget`: | Value | Description | |----------------------|-----------------------------------------------------------| | `this` | The entity that invoked the predicate (default) | | `attacker` | The entity that attacked | | `direct_attacker` | The direct cause of damage (e.g., arrow, not the shooter) | | `attacking_player` | The attacking player specifically | | `target_entity` | The targeted entity | | `interacting_entity` | The entity interacting with something | ### Entity Predicate Example ```kotlin predicate("entity_check") { entityProperties { // Check entity components (e.g., axolotl variant) components { axolotlVariant(AxolotlVariants.CYAN) damage(12) !unbreakable() // Negated component check } // Check effects effects { this[Effects.INVISIBILITY] = mobEffectPredicate { amplifier = rangeOrInt(1) } } // Check equipment, one entry per equipment slot equipment { mainHand = itemStackPredicate(Items.DIAMOND_SWORD) body = itemStackPredicate(Items.SADDLE) } // Check entity flags flags { isBaby = true } // Check location location { block(Blocks.STONE) } // Check movement movement { x(1.0, 4.0) horizontalSpeed(1.0) } // Check what affects entity movement movementAffectedBy { canSeeSky = true } // Check NBT data nbt { this["foo"] = "bar" } // Check entity passenger passenger { team("foo") } // Check scoreboard tags set through /tag entityTags { allOf("boss") noneOf("tamed") } // Check the entity this one's AI is targeting targetedEntity { entityType(EntityTypes.VILLAGER) } // Check custom data predicates predicates { customData { this["foo"] = "bar" } } // Check specific inventory slots slots { this[WEAPON.MAINHAND] = itemStackPredicate(Items.DIAMOND_SWORD) } // Check block the entity is standing on steppingOn { block(Blocks.STONE) { components { damage(5) } predicates { customData { this["foo"] = "bar" } } state("up", "bottom") } } // Check entity type entityType(EntityTypes.MARKER) // Check player-specific properties typeSpecific { player { gamemodes(Gamemode.SURVIVAL) } } // Check entity vehicle with distance vehicle { distance { x(1f..4f) z(1f) } } } } ``` ## Sub-Predicates Sub-predicates are nested data structures that allow you to define specific properties to check within a predicate condition. Each condition type can have its own set of sub-predicates. ### Entity Sub-Predicates The `entityProperties` condition supports various sub-predicates to check different aspects of an entity. Each one is serialized as its own identifier-keyed entry (`minecraft:`) rather than a flat object: | Sub-Predicate | JSON key | Description | Example | |------------------------|----------------------------------|------------------------------------------|--------------------------------------------------------------------------------------| | `components` | `minecraft:components` | Check entity data components | `components { axolotlVariant(AxolotlVariants.CYAN) }` | | `distance` | `minecraft:distance` | Check distance between entities | `distance { x(1f..4f) }` | | `effects` | `minecraft:effects` | Check potion effects | `effects { this[Effects.SPEED] = mobEffectPredicate { amplifier = rangeOrInt(1) } }` | | `entityTags` | `minecraft:entity_tags` | Check scoreboard tags set through `/tag` | `entityTags { allOf("boss") }` | | `entityType` | `minecraft:entity_type` | Check entity type | `entityType(EntityTypes.MARKER)` | | `equipment` | `minecraft:equipment` | Check equipped items | `equipment { mainHand = itemStackPredicate(Items.DIAMOND_SWORD) }` | | `flags` | `minecraft:flags` | Check entity flags (baby, on fire, etc.) | `flags { isBaby = true }` | | `location` | `minecraft:location` | Check entity location | `location { block(Blocks.STONE) }` | | `movement` | `minecraft:movement` | Check entity movement | `movement { x(1.0, 4.0); horizontalSpeed(1.0) }` | | `movementAffectedBy` | `minecraft:movement_affected_by` | Check what affects entity movement | `movementAffectedBy { canSeeSky = true }` | | `nbt` | `minecraft:nbt` | Check entity NBT data | `nbt { this["foo"] = "bar" }` | | `passenger` | `minecraft:passenger` | Check entity passenger | `passenger { team("foo") }` | | `periodicTick` | `minecraft:periodic_tick` | Check entity periodic ticks | `periodicTick(20)` | | `predicates` | `minecraft:predicates` | Check custom data predicates | `predicates { customData { this["key"] = "value" } }` | | `slots` | `minecraft:slots` | Check specific inventory slots | `slots { this[WEAPON.MAINHAND] = itemStackPredicate(Items.DIAMOND_SWORD) }` | | `steppingOn` | `minecraft:stepping_on` | Check block the entity is standing on | `steppingOn { block(Blocks.STONE) }` | | `targetedEntity` | `minecraft:targeted_entity` | Check entity being targeted | `targetedEntity { entityType(EntityTypes.ZOMBIE) }` | | `team` | `minecraft:team` | Check entity team | `team("my_team")` | | `typeSpecific { ... }` | `minecraft:type_specific/` | Check type-specific properties | See [Type-Specific Properties](#entity-type-specific-properties) | | `vehicle` | `minecraft:vehicle` | Check entity vehicle | `vehicle { distance { x(1f..4f) } }` | The `EntityPredicate` class provides all the functions for these sub-predicates, backed by a single `EntitySubPredicate` sealed family - each call appends one entry to `EntityPredicate.subPredicates`. ### Entity Type-Specific Properties Entities expose a handful of hard-coded type-specific predicates, covering the state that data components do not: fishing hooks, lightning bolts, players, raiders, sheep and cube mobs. Every visual *variant* check lives in the components system instead, so it is matched through the `components` block. #### Component-based variant checks Query an entity's data components directly from `entityProperties` with the `components` block: ```kotlin // Check axolotl variant via its component predicate("axolotl_component_check") { entityProperties { components { axolotlVariant(AxolotlVariants.LUCY) } } } ``` Any component you can put on an **item** can be matched on an **entity** in exactly the same way - just call the corresponding extension inside the `components {}` scope. #### Built-in `typeSpecific` helpers These helpers cover information that is **not** represented by components. They're grouped under a `typeSpecific { }` scope, and each keys under `minecraft:type_specific/`: ##### Fishing Hook Check if a fishing hook is in open water: ```kotlin predicate("fishing_hook_check") { entityProperties { typeSpecific { fishingHook(inOpenWater = true) } } } ``` ##### Lightning Check lightning bolt properties like blocks set on fire: ```kotlin predicate("lightning_check") { entityProperties { typeSpecific { lightning { blocksSetOnFire = rangeOrInt(1..5) } } } } ``` ##### Player Check player-specific properties including gamemode, experience level, food stats, unlocked recipes, statistics, what the player is looking at, and input state: ```kotlin predicate("player_check") { entityProperties { typeSpecific { player { gamemodes(Gamemode.CREATIVE) level = rangeOrInt(1..5) food { level = rangeOrInt(5..15) saturation = rangeOrDouble(1.0, 10.0) } recipes { this[Recipes.BOW] = true } lookingAt { entityType(EntityTypes.CREEPER) } stats { statistic(StatisticTypes.CUSTOM, CustomStats.JUMP, 10) statistic(StatisticTypes.MINED, Blocks.STONE, 1..5) } input { forward = true backward = false left = true right = false jump = true sneak = false sprint = true } } } } } ``` ##### Raider Check raider properties like raid participation and captain status: ```kotlin predicate("raider_check") { entityProperties { typeSpecific { raider(hasRaid = true, isCaptain = false) } } } ``` ##### Sheep Check if a sheep has been sheared: ```kotlin predicate("sheep_check") { entityProperties { typeSpecific { sheep(sheared = true) } } } ``` ##### Cube Mob (slimes and magma cubes) Check cube mob size, keyed under `minecraft:type_specific/cube_mob`: ```kotlin predicate("cube_mob_check") { entityProperties { typeSpecific { cubeMob(rangeOrInt(2)) } } } ``` > **Note** Variant checks (axolotl, cat, fox, frog, horse, llama, mooshroom, painting, parrot, pig, rabbit, salmon, > tropical fish, villager, wolf) are component matches, not `typeSpecific` helpers. ### Item Sub-Predicates When using `matchTool` or checking equipment, you can use item sub-predicates. There are two main ways to check item properties: 1. Basic item properties: ```kotlin predicate("basic_item_check") { matchTool { items(Items.DIAMOND_SWORD) count(1..64) } } ``` 2. Component Matchers - A powerful system to check component properties: ```kotlin predicate("component_check") { matchTool { items(Items.DIAMOND_SWORD) predicates { // Check damage and durability damage { durability(1) damage = rangeOrInt(4..5) } // Check enchantments enchantments { enchantment(Enchantments.SHARPNESS, level = 3) } } } } ``` Component Matchers allow you to check various item components like: - Attribute modifiers - Container contents (bundles, shulker boxes) - Damage and durability - Enchantments - Firework properties - Book contents - And many more Each matcher corresponds to a component type in Minecraft and provides type-safe ways to check their properties. See the [Available Component Matchers](/docs/concepts/components#available-component-matchers) table in the Components guide for the full list. ## Using Predicates in Commands Predicates can be invoked in commands in two ways: ### Execute If Predicate Use `/execute if predicate` to conditionally run commands. If you need a refresher on the surrounding execution DSL, the [Commands](/docs/commands/commands) page covers the broader command surface: ```kotlin function("test") { execute { ifCondition { predicate(myPredicate) } run { debug("predicate validated!") } } } ``` ### Target Selector Argument Use the `predicate=` selector argument to filter entities. This pairs naturally with Kore's typed [Selectors](/docs/concepts/selectors): ```kotlin function("filter_entities") { // Kill all entities matching the predicate kill(allEntities { predicate = myPredicate }) } ``` ### Pairing with Inventory Manager Predicates excel at validating complex item properties. When you need to both validate and actively manage inventories ( e.g., keep a GUI slot populated with an item matching specific [Components](/docs/concepts/components)), use them alongside the [Inventory Manager](/docs/helpers/inventory-manager). ## Item Predicates Item predicates check the item involved in a predicate context - most commonly the tool used to mine a block via `matchTool`, but the same shape is used for `equipment` slots and `slots` checks inside `entityProperties` (see [Entity Predicate Example](#entity-predicate-example) above). A basic item predicate matches on the item type plus optional `count`/`durability` ranges: ```kotlin predicate("enchanted_tool") { matchTool { items(Items.DIAMOND_PICKAXE) predicates { enchantments(Enchantments.EFFICIENCY) } } } ``` The `predicates { }` block accepts any [component matcher](/docs/concepts/components#component-matchers-item-predicates) - `damage`, `enchantments`, `storedEnchantments`, `customData`, `container`, and more - so you can gate a predicate on arbitrary component state, not just enchantments. If you instead need the inline command-syntax form (`minecraft:diamond_sword[damage=10]`) for use outside a predicate file - e.g. in `/give`, `/clear`, or the `items` selector - see [Item Predicates](/docs/concepts/components#item-predicates) and [Component Matchers (Sub-Predicates)](/docs/concepts/components#component-matchers-sub-predicates) in the Components guide, which cover both forms side by side with more examples (existence checks, partial matching, negation, OR). ## Referencing Other Predicates Use the `reference` condition to invoke another predicate file, which keeps larger predicate sets composable in the same spirit as helper extraction in the [Cookbook](/docs/guides/cookbook): ```kotlin val basePredicate = predicate("base_check") { weatherCheck(raining = true) } predicate("combined_check") { allOf { reference(basePredicate) randomChance(0.5f) } } ``` > **Warning**: Cyclic references (predicate A references B, which references A) will cause a parsing failure. ## Best Practices 1. **Descriptive names**: Give your predicates names that reflect their purpose (e.g., `is_holding_sword`, `in_rain_at_night`) 2. **Logical composition**: Use `allOf` and `anyOf` to combine multiple conditions clearly 3. **Reusability**: Keep predicates focused on a single concern and use `reference` to compose them 4. **Context awareness **: Be mindful of which loot context your predicate will be invoked from. Context-dependent conditions will silently fail if required data is missing 5. **Testing**: Test your predicates in-game using `/execute if predicate ` to verify they work as expected Predicates are powerful tools for creating complex conditions in your datapack. They enable sophisticated game mechanics and enhance player experience without requiring custom code. ## See Also - [Loot Tables](/docs/data-driven/loot-tables) - Use predicates as conditions for loot entries - [Advancements](/docs/data-driven/advancements) - Use predicates as trigger conditions - [Item Modifiers](/docs/data-driven/item-modifiers) - Modify items conditionally with predicates - [Components](/docs/concepts/components#component-matchers-item-predicates) - Item predicates and component matchers in depth: command-syntax predicates, sub-predicate matchers, existence checks, and a complete tool-upgrade example - [Inventory Manager](/docs/helpers/inventory-manager) - Pair predicates with inventory management - [Villager Trades](/docs/data-driven/villager-trades) - Gate trade availability via `merchantPredicate` ### External Resources - [Minecraft Wiki: Predicate](https://minecraft.wiki/w/Predicate) - Official JSON format reference - [Minecraft Wiki: Loot context](https://minecraft.wiki/w/Loot_context) - Understanding loot contexts for conditions --- ## Recipes --- root: .components.layouts.MarkdownLayout title: Recipes nav-title: Recipes description: Create custom Minecraft recipes using Kore's type-safe Kotlin DSL for crafting, smelting, smithing, and more. keywords: minecraft, datapack, kore, recipes, crafting, smelting, smithing, stonecutting date-created: 2024-01-08 date-modified: 2026-10-02 routeOverride: /docs/data-driven/recipes --- # Recipes Recipes define how items are transformed through crafting tables, furnaces, smithing tables, stonecutters, and other workstations. Kore provides a type-safe DSL to create all vanilla recipe types programmatically. The [recipes example](/playground#example=recipes) of the playground runs in your browser and shows the JSON it generates. ## Overview Recipes have several key characteristics: - **Type-specific**: Each workstation has its own recipe format - **Discoverable**: Recipes can be unlocked via advancements - **Customizable results**: Output items can have custom components - **Tag-based ingredients**: Use item tags for flexible ingredient matching ### Recipe Types | Type | Workstation | Description | |---------------------------------------|----------------------------------------------------------------|-------------------------------------| | `blasting` | ![Blast Furnace](mc:block/blast_furnace_front) Blast Furnace | Faster ore smelting | | `campfire_cooking` | ![Campfire](mc:item/campfire) Campfire | Slow food cooking | | `crafting_decorated_pot` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Craft a decorated pot from sherds | | `crafting_dye` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Dye an item with a dye | | `crafting_imbue` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Imbue items (e.g. tip arrows) | | `crafting_shaped` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Pattern-based crafting | | `crafting_shapeless` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Order-independent crafting | | `crafting_special_bannerduplicate` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Copy a banner pattern | | `crafting_special_bookcloning` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Copy a written book | | `crafting_special_firework_rocket` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Craft a firework rocket | | `crafting_special_firework_star` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Craft a firework star | | `crafting_special_firework_star_fade` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Add a fade colour to a star | | `crafting_special_mapextending` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Extend a map with paper | | `crafting_special_shielddecoration` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Apply a banner to a shield | | `crafting_special_*` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Remaining hardcoded special recipes | | `crafting_transmute` | ![Crafting Table](mc:block/crafting_table_front) Crafting Table | Transform item with material | | `smelting` | ![Furnace](mc:block/furnace_front) Furnace | Standard smelting | | `smithing_transform` | ![Smithing Table](mc:block/smithing_table_front) Smithing Table | Upgrade items | | `smithing_trim` | ![Smithing Table](mc:block/smithing_table_front) Smithing Table | Apply armor trims | | `smoking` | ![Smoker](mc:block/smoker_front) Smoker | Faster food cooking | | `stonecutting` | ![Stonecutter](mc:block/stonecutter_saw) Stonecutter | Cut blocks | ## File Structure Recipes are stored as JSON files in data packs at: ``` data//recipe/.json ``` For complete JSON specification, see the [Minecraft Wiki - Recipe](https://minecraft.wiki/w/Recipe). ## Creating Recipes Use the `recipes` block inside a data pack to define recipes: ```kotlin dataPack("my_datapack") { recipes { craftingShaped("diamond_sword_upgrade") { pattern( " E ", " D ", " S " ) keys { "E" to Items.EMERALD "D" to Items.DIAMOND_SWORD "S" to Items.STICK } result(Items.DIAMOND_SWORD) { enchantments { enchantment(Enchantments.SHARPNESS, 5) } } } } } ``` This generates `data/my_datapack/recipe/diamond_sword_upgrade.json`. ## Crafting Recipes ### Banner Duplicate Copy a banner's pattern onto a blank banner: ```kotlin recipes { craftingSpecialBannerDuplicate("banner_copy") { banner(Tags.Item.BANNERS) result(Items.WHITE_BANNER) } } ``` ### Book Cloning Copy a written book, with an optional generation limit: ```kotlin recipes { craftingSpecialBookCloning("book_clone") { source(Items.WRITTEN_BOOK) material(Items.WRITABLE_BOOK) result(Items.WRITTEN_BOOK) allowedGenerations = rangeOrInt(0..2) } } ``` ### Decorated Pot Craft a decorated pot from sherds: ```kotlin recipes { craftingDecoratedPot("decorated_pot") { back(Tags.Item.DECORATED_POT_INGREDIENTS) front(Tags.Item.DECORATED_POT_INGREDIENTS) left(Tags.Item.DECORATED_POT_INGREDIENTS) right(Tags.Item.DECORATED_POT_INGREDIENTS) result(Items.DECORATED_POT) } } ``` ### Dye Crafting Dye an item using a dye ingredient. _Replaces the old `crafting_special_armordye` recipe_: ```kotlin recipes { craftingDye("red_wool_dye") { dye(Items.RED_DYE) target(Tags.Item.WOOL) result(Items.RED_WOOL) } } ``` ### Firework Rocket Craft a firework rocket from its component ingredients: ```kotlin recipes { craftingSpecialFireworkRocket("firework_rocket") { fuel(Items.GUNPOWDER) shell(Items.PAPER) star(Items.FIREWORK_STAR) result(Items.FIREWORK_ROCKET) } } ``` ### Firework Star Craft a firework star with dye, fuel, optional shape modifiers, trail, and twinkle effects: ```kotlin recipes { craftingSpecialFireworkStar("red_burst_star") { dye(Items.RED_DYE) fuel(Items.GUNPOWDER) shape("burst", Items.FIRE_CHARGE) trail(Items.DIAMOND) twinkle(Items.GLOWSTONE_DUST) result(Items.FIREWORK_STAR) } } ``` ### Firework Star Fade Add a fade colour to an existing firework star: ```kotlin recipes { craftingSpecialFireworkStarFade("blue_fade_star") { dye(Items.BLUE_DYE) target(Items.FIREWORK_STAR) result(Items.FIREWORK_STAR) } } ``` ### Imbue Crafting Imbue items with properties from a source (e.g. tip arrows with lingering potions). _Replaces the old `crafting_special_tippedarrow` recipe_: ```kotlin recipes { craftingImbue("tipped_arrow") { material(Tags.Item.ARROWS) source(Items.LINGERING_POTION) result(Items.TIPPED_ARROW) } } ``` ### Map Extending Extend a map by combining it with paper: ```kotlin recipes { craftingSpecialMapExtending("extended_map") { map(Items.FILLED_MAP) material(Items.PAPER) result(Items.FILLED_MAP) } } ``` ### Shaped Crafting Pattern-based recipes where ingredient positions matter: ```kotlin recipes { craftingShaped("my_pickaxe") { // Define the pattern (up to 3x3) pattern( "DDD", " S ", " S " ) // Map characters to items key("D", Items.DIAMOND) key("S", Items.STICK) // Or use keys block keys { "D" to Items.DIAMOND "S" to Items.STICK } // Set the result result(Items.DIAMOND_PICKAXE) } } ``` {{{ .components.mc.ShapedRecipeMockup }}} #### Pattern Rules - Patterns can be 1x1 to 3x3 - Use space ` ` for empty slots - Each character must be mapped in `key()` or `keys {}` - Patterns are automatically trimmed (no need for padding) ```kotlin // 2x2 recipe craftingShaped("torch") { pattern( "C", "S" ) keys { "C" to Items.COAL "S" to Items.STICK } result(Items.TORCH, count = 4) } // Using tags as ingredients craftingShaped("planks") { pattern("L") key("L", Tags.Item.LOGS) result(Items.OAK_PLANKS, count = 4) } ``` ### Shapeless Crafting Order-independent recipes: ```kotlin recipes { craftingShapeless("mushroom_stew") { ingredient(Items.BOWL) ingredient(Items.BROWN_MUSHROOM) ingredient(Items.RED_MUSHROOM) result(Items.MUSHROOM_STEW) } } ``` #### Multiple of Same Ingredient ```kotlin craftingShapeless("book") { ingredient(Items.PAPER) ingredient(Items.PAPER) ingredient(Items.PAPER) ingredient(Items.LEATHER) result(Items.BOOK) } ``` #### Using Tags ```kotlin craftingShapeless("dye_mix") { ingredient(Tags.Item.DYES) ingredient(Tags.Item.DYES) result(Items.MAGENTA_DYE, count = 2) } ``` ### Shield Decoration Apply a banner pattern to a shield: ```kotlin recipes { craftingSpecialShieldDecoration("shield_decor") { banner(Tags.Item.BANNERS) target(Items.SHIELD) result(Items.SHIELD) } } ``` ### Special Crafting Remaining hardcoded special recipes with no configurable ingredients: ```kotlin recipes { craftingSpecial("repair", CraftingSpecialRepairItem) craftingSpecial("shulker_color", CraftingSpecialShulkerBoxColoring) } ``` These are useful when the vanilla datapack is disabled and you need to re-enable specific special recipes. ### Transmute Crafting Transform an item while preserving its components: ```kotlin recipes { craftingTransmute("dye_shulker") { input(Tags.Item.SHULKER_BOXES) material(Items.BLUE_DYE) result(Items.BLUE_SHULKER_BOX) } } ``` The result item copies all components from the input item. ### Recipe Book Category Shaped, shapeless, dye, imbue and transmute recipes take an optional `category` (`BUILDING`, `EQUIPMENT`, `MISC` or `REDSTONE`) choosing the recipe book tab they show up in: ```kotlin recipes { craftingShapeless("quick_torch") { category = RecipeCategory.EQUIPMENT ingredient(Items.COAL) ingredient(Items.STICK) result(Items.TORCH, count = 4) } } ``` ## Cooking Recipes All cooking recipes share a similar structure: | Property | Description | |---------------|-----------------------------------------------------------------------| | `ingredient` | Input item or tag | | `result` | Output item | | `category` | Recipe book tab, a `CookingRecipeCategory` (`BLOCKS`, `FOOD`, `MISC`) | | `experience` | XP awarded when collecting output | | `cookingTime` | Time in ticks | ### Blasting (Blast Furnace) Twice as fast as smelting (100 ticks default): ```kotlin recipes { blasting("iron_ingot_fast") { ingredient(Items.RAW_IRON) result(Items.IRON_INGOT) experience = 0.7 cookingTime = 100 // 5 seconds (default) } } ``` ### Campfire Cooking Slow cooking without fuel: ```kotlin recipes { campfireCooking("baked_potato") { ingredient(Items.POTATO) result(Items.BAKED_POTATO) experience = 0.35 cookingTime = 600 // 30 seconds (default) } } ``` ### Smelting (Furnace) ```kotlin recipes { smelting("iron_ingot") { ingredient(Items.RAW_IRON) result(Items.IRON_INGOT) experience = 0.7 cookingTime = 200 // 10 seconds (default) } // Using tags smelting("glass") { ingredient(Tags.Item.SMELTS_TO_GLASS) result(Items.GLASS) experience = 0.1 } } ``` {{{ .components.mc.SmeltingRecipeMockup }}} ### Smoking (Smoker) For food items, twice as fast as furnace: ```kotlin recipes { smoking("cooked_beef") { ingredient(Items.BEEF) result(Items.COOKED_BEEF) experience = 0.35 cookingTime = 100 } } ``` ## Smithing Recipes ### Smithing Transform Upgrade items at the smithing table: ```kotlin recipes { smithingTransform("netherite_sword") { template(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE) base(Items.DIAMOND_SWORD) addition(Items.NETHERITE_INGOT) result(Items.NETHERITE_SWORD) } } ``` {{{ .components.mc.SmithingRecipeMockup }}} The result item copies components from the base item. #### Multiple Addition Options ```kotlin smithingTransform("custom_upgrade") { template(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE) base(Items.DIAMOND_SWORD) addition(Items.NETHERITE_INGOT, Items.NETHERITE_SCRAP) // Either works result(Items.NETHERITE_SWORD) } ``` ### Smithing Trim Apply armor trims: ```kotlin recipes { smithingTrim("sentry_trim") { template(Items.SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE) base(Tags.Item.TRIMMABLE_ARMOR) addition(Tags.Item.TRIM_MATERIALS) pattern = TrimPatterns.SENTRY } } ``` ## Stonecutting Single-item recipes for the stonecutter: ```kotlin recipes { stoneCutting("stone_slab") { ingredient(Items.STONE) result(Items.STONE_SLAB, count = 2) } stoneCutting("stone_stairs") { ingredient(Items.STONE) result(Items.STONE_STAIRS) } // Multiple outputs from same ingredient (separate recipes) stoneCutting("stone_bricks") { ingredient(Items.STONE) result(Items.STONE_BRICKS) } } ``` ## Result Items with Components Add custom components to recipe results: ```kotlin recipes { craftingShaped("enchanted_book") { pattern( "E E", " B ", "E E" ) keys { "E" to Items.EMERALD "B" to Items.BOOK } result(Items.ENCHANTED_BOOK) { enchantments { enchantment(Enchantments.SHARPNESS, 5) } } } craftingShaped("damaged_sword") { pattern( " D", " D", " S" ) keys { "D" to Items.DIAMOND "S" to Items.STICK } result(Items.DIAMOND_SWORD) { damage(100) // Partially damaged } } craftingShapeless("named_diamond") { ingredient(Items.DIAMOND) ingredient(Items.PAPER) result(Items.DIAMOND) { customName(textComponent("Certified Diamond", Color.AQUA)) lore(textComponent("100% Genuine", Color.GRAY)) } } } ``` ## Recipe Categories The recipe book tab of `craftingDye`, `craftingImbue` and `craftingTransmute` recipes is set with `category`, a `RecipeCategory`: ```kotlin craftingTransmute("dyed_bundle") { // ... input, material and result category = RecipeCategory.EQUIPMENT } ``` - `BUILDING` - Building blocks - `REDSTONE` - Redstone components - `EQUIPMENT` - Tools, weapons, armor - `MISC` - Everything else Setting a category on shaped, shapeless or cooking recipes isn't possible yet because those Kore recipe types don't expose the `category` field; the game then files them under its default tab. ## Recipe Groups Group similar recipes in the recipe book: ```kotlin recipes { craftingShaped("oak_planks") { pattern("L") key("L", Items.OAK_LOG) result(Items.OAK_PLANKS, count = 4) group = "planks" } craftingShaped("birch_planks") { pattern("L") key("L", Items.BIRCH_LOG) result(Items.BIRCH_PLANKS, count = 4) group = "planks" } } ``` Recipes with the same group appear together in the recipe book. ## Using Recipes in Commands ### Reference Recipes Store a recipe reference for use in commands: ```kotlin val myRecipe = recipesBuilder.craftingShaped("special_item") { pattern( " G ", "GBG", " G " ) keys { "G" to Items.GOLD_BLOCK "B" to Items.DIAMOND_BLOCK } result(Items.BEACON) } load { // Give recipe to all players recipeGive(allPlayers(), myRecipe) } ``` ### Give/Take Recipes ```kotlin load { // Give specific recipe recipeGive(self(), myRecipe) // Take recipe recipeTake(self(), myRecipe) // Give all recipes recipeGiveAll(allPlayers()) } ``` ## Overriding Vanilla Recipes A recipe overrides the vanilla one with the same id, so it has to live in the `minecraft` namespace. Every recipe builder takes an optional `namespace` after the name, defaulting to the pack's: ```kotlin dataPack("better_recipes") { recipes { // Override vanilla diamond sword recipe, returns RecipeArgument minecraft:diamond_sword craftingShaped("diamond_sword", namespace = "minecraft") { pattern( " D ", " D ", " S " ) keys { "D" to Items.DIAMOND "S" to Items.STICK } result(Items.DIAMOND_SWORD) { enchantments { enchantment(Enchantments.UNBREAKING, 1) } } } } } ``` ## Full Example ```kotlin dataPack("custom_recipes") { recipes { // Shaped crafting with components craftingShaped("legendary_sword") { pattern( " N ", " N ", " B " ) keys { "N" to Items.NETHERITE_INGOT "B" to Items.BLAZE_ROD } result(Items.NETHERITE_SWORD) { customName(textComponent("Blade of Flames", Color.GOLD)) enchantments { enchantment(Enchantments.FIRE_ASPECT, 2) enchantment(Enchantments.SHARPNESS, 5) } unbreakable() } } // Shapeless recipe craftingShapeless("quick_tnt") { ingredient(Items.GUNPOWDER) ingredient(Items.GUNPOWDER) ingredient(Items.GUNPOWDER) ingredient(Items.GUNPOWDER) ingredient(Tags.Item.SAND) result(Items.TNT) } // Transmute recipe craftingTransmute("repaint_bed") { input(Tags.Item.BEDS) material(Items.WHITE_DYE) result(Items.WHITE_BED) } // Smelting with experience smelting("ancient_debris") { ingredient(Items.ANCIENT_DEBRIS) result(Items.NETHERITE_SCRAP) experience = 2.0 cookingTime = 200 } // Smithing upgrade smithingTransform("netherite_boots") { template(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE) base(Items.DIAMOND_BOOTS) addition(Items.NETHERITE_INGOT) result(Items.NETHERITE_BOOTS) } // Smithing trim smithingTrim("ward_trim") { template(Items.WARD_ARMOR_TRIM_SMITHING_TEMPLATE) base(Tags.Item.TRIMMABLE_ARMOR) addition(Tags.Item.TRIM_MATERIALS) pattern = TrimPatterns.WARD } // Stonecutting variants stoneCutting("cut_copper_slab") { ingredient(Items.COPPER_BLOCK) result(Items.CUT_COPPER_SLAB, count = 8) } } // Reference recipe in function val beaconRecipe = recipesBuilder.craftingShaped("easy_beacon") { pattern( "GGG", "GSG", "OOO" ) keys { "G" to Items.GLASS "S" to Items.NETHER_STAR "O" to Items.OBSIDIAN } result(Items.BEACON) } load { recipeGive(allPlayers(), beaconRecipe) } } ``` ### Generated JSON (Shaped Recipe) ```json { "type": "minecraft:crafting_shaped", "pattern": [ " N ", " N ", " B " ], "key": { "N": "minecraft:netherite_ingot", "B": "minecraft:blaze_rod" }, "result": { "id": "minecraft:netherite_sword", "components": { "custom_name": { "text": "Blade of Flames", "color": "gold" }, "enchantments": { "minecraft:fire_aspect": 2, "minecraft:sharpness": 5 }, "unbreakable": {} } } } ``` ## Best Practices 1. **Use meaningful names** - Recipe file names should describe the output 2. **Group related recipes** - Use the `group` field for recipe book organization 3. **Prefer tags** - Use item tags for flexible ingredient matching 4. **Test in-game** - Verify recipes work as expected in all crafting interfaces 5. **Consider balance** - Ensure custom recipes maintain game balance ## See Also - [Advancements](/docs/data-driven/advancements) - Unlock recipes as advancement rewards - [Components](/docs/concepts/components) - Customize recipe result items - [Commands](/docs/commands/commands) - Give or take recipes with commands - [Tags](/docs/data-driven/tags) - Use item tags for flexible ingredient matching ### External Resources - [Minecraft Wiki: Recipe](https://minecraft.wiki/w/Recipe) - Official JSON format reference - [Minecraft Wiki: Crafting](https://minecraft.wiki/w/Crafting) - Crafting mechanics overview --- ## Slot Sources --- root: .components.layouts.MarkdownLayout title: Slot Sources nav-title: Slot Sources description: Select inventory slots in Minecraft loot tables, /item and /execute with Kore's type-safe slot source DSL and slot source files. keywords: minecraft, datapack, kore, slot sources, slot_source, item command, execute if items, execute if slots, loot tables date-created: 2026-10-08 date-modified: 2026-10-08 routeOverride: /docs/data-driven/slot-sources --- # Slot Sources A slot source selects inventory slots: the slots of an entity or a block entity, the items stored inside an item, or a filtered, limited or merged mix of them. The same builders are used in four places: | Where | Kore | |--------------------------------------|--------------------------------------------| | Slot source file | `slotSource("name") { ... }` | | `slots` loot table entry | `slots { slotSources { ... } }` | | `/item` source and destination slots | `items.replace(self(), slots, ...)` | | `/execute if items` and `if slots` | `ifCondition { slots(self(), slots) }` | Several sources in one block are concatenated, a single one is written as an object and several as an array. Reference: [Slot source](https://minecraft.wiki/w/Slot_source) ## Slot Source Files `slotSource` writes `data//slot_source/.json` and returns its `SlotSourceArgument`: ```kotlin val hands = slotSource("hands") { slotRange(WEAPON.all()) } ``` ```json { "type": "minecraft:slot_range", "slots": "weapon.*" } ``` Another slot source points to a file with `reference(hands)`, which writes `{"type": "minecraft:reference", "name": ":hands"}`. ## In Commands `/item` and `/execute if items|slots` take a `SlotsArgument`, which is one of: - a slot or a range, written as a slot string: `ARMOR.CHEST` is `armor.chest`, `HOTBAR` and `HOTBAR.all()` are `hotbar.*`; - a slot source file, like `hands` above; - an inline slot source, `inlineSlotSource { ... }`, written as SNBT. ```kotlin function("give_kit") { items.fill(self(), HOTBAR, Items.BREAD) // item fill entity @s hotbar.* with minecraft:bread items.override(self(), hands, Items.STONE_SWORD) // item override entity @s my_pack:hands with minecraft:stone_sword execute { ifCondition { slots(self(), inlineSlotSource { slotRange(ARMOR) }) } run { say("Wearing armor") } } } ``` `items.replace`, `items.fill` and `items.override` spread the source items over the destination slots differently, here with destination slots `[12345]` and source items `(ABC)`: | Function | Result | Destination slots left over | |------------|-----------|-----------------------------| | `replace` | `[ABC45]` | untouched | | `fill` | `[ABCAB]` | filled by repeating sources | | `override` | `[ABC__]` | emptied | `/execute if slots` counts the selected slots that exist on the target, `/execute if items` the selected slots whose item matches the predicate. Slot sources used by commands are evaluated with the `command_slot_source` loot context. ## Slot Source Types ### Slot Range Selects the slots of a slot string from an inventory. Without an origin, it reads the loot context's container: the target of the command, or the inventory a loot table runs on. ```kotlin slotSources { // Every hotbar slot of the context's container slotRange(HOTBAR) // One slot of the block entity slotRange(SlotSourceOrigin.BLOCK_ENTITY, ARMOR.HEAD) // A raw slot string from the looting entity slotRange(SlotSourceOrigin.THIS, "container.*") } ``` Available origins: `ATTACKING_ENTITY`, `BLOCK_ENTITY`, `CONTAINER`, `DIRECT_ATTACKER`, `INTERACTING_ENTITY`, `LAST_DAMAGE_PLAYER`, `TARGET_ENTITY`, `THIS`. ### Contents Selects the slots stored in the inventory component of the items selected by `slotSource`: ```kotlin slotSources { contents(InventoryComponentType.CONTAINER) { slotSource { slotRange(SlotSourceOrigin.BLOCK_ENTITY, "container.*") } } } ``` Available component types: `BUNDLE_CONTENTS`, `CHARGED_PROJECTILES`, `CONTAINER`. ### Empty An empty selection containing no slots: ```kotlin slotSources { empty() } ``` ### Filtered Keeps only the selected slots whose item matches `itemFilter`: ```kotlin slotSources { filtered { slotSource { slotRange(SlotSourceOrigin.THIS, HOTBAR) } itemFilter { count = rangeOrInt(16..64) } } } ``` ### Group Merges several slot sources into one: ```kotlin slotSources { group { slotRange(SlotSourceOrigin.THIS, HOTBAR) empty() } } ``` ### Limit Slots Keeps the first `limit` selected slots: ```kotlin slotSources { limitSlots(5) { slotSource { slotRange(SlotSourceOrigin.THIS, HOTBAR) } } } ``` ### Reference Selects the slots of a slot source file: ```kotlin slotSources { reference(hands) } ``` ## See Also - [Loot Tables](/docs/data-driven/loot-tables) - The `slots` entry - [Execute](/docs/commands/execute) - The `items` and `slots` conditions --- ## Sulfur Cube Archetypes - Custom Sulfur Cube Behavior in Kore --- root: .components.layouts.MarkdownLayout title: Sulfur Cube Archetypes - Custom Sulfur Cube Behavior in Kore nav-title: Sulfur Cube Archetypes description: Define custom sulfur cube archetypes for Minecraft 26.2+ with Kore's Kotlin DSL. Configure attribute modifiers, buoyancy, contact damage, explosion, knockback and item contents. keywords: minecraft sulfur cube, datapack sulfur_cube_archetype, kore sulfur cube archetype, custom mob archetype datapack, sulfur cube attribute modifiers date-created: 2026-07-29 date-modified: 2026-09-23 routeOverride: /docs/data-driven/sulfur-cube-archetypes --- # Sulfur Cube Archetypes Sulfur cube archetypes are data-driven JSON files that control the attribute modifiers, buoyancy, contact damage, explosion, knockback, and valid item contents of a sulfur cube. Each archetype is referenced by its file name and can be assigned to sulfur cube entities to change their behavior. ## File Structure ``` data//sulfur_cube_archetype/.json ``` ## Creating an Archetype Use `sulfurCubeArchetype` on your `DataPack`. Pass an `ItemTagArgument` describing which items the sulfur cube can hold and the knockback it deals via `horizontalKnockbackPower`/`verticalKnockbackPower`, then add attribute modifiers with `modifier`, contact damage with `contactDamage`, and an explosion with `explosion`: ```kotlin import io.github.ayfri.kore.commands.AttributeModifierOperation import io.github.ayfri.kore.features.sulfurcubearchetype.contactDamage import io.github.ayfri.kore.features.sulfurcubearchetype.explosion import io.github.ayfri.kore.features.sulfurcubearchetype.modifier import io.github.ayfri.kore.features.sulfurcubearchetype.soundSettings import io.github.ayfri.kore.features.sulfurcubearchetype.sulfurCubeArchetype import io.github.ayfri.kore.generated.Attributes import io.github.ayfri.kore.generated.DamageTypes import io.github.ayfri.kore.generated.SoundEvents import io.github.ayfri.kore.generated.Tags datapack.sulfurCubeArchetype( "regular", items = Tags.Item.SWORDS, horizontalKnockbackPower = 0.4f, verticalKnockbackPower = 0.2f, ) { buoyant = true contactDamage(amount = 3f, damageType = DamageTypes.GENERIC, attributeToSource = true) explosion(fuse = 80, power = 3, causesFire = true) modifier( amount = 4.0, attribute = Attributes.MAX_HEALTH, id = "sulfur_cube_archetype:regular", operation = AttributeModifierOperation.ADD_VALUE, ) soundSettings( hitSound = SoundEvents.Entity.SulfurCube.Hot.HIT, pushSound = SoundEvents.Entity.SulfurCube.Sticky.PUSH, pushSoundCooldown = 5f, pushSoundImpulseThreshold = 0.6f, ) } ``` ## SulfurCubeArchetype Fields | Field | Type | Description | |----------------------|-----------------------------------------------------|---------------------------------------------------------------------------| | `attributeModifiers` | `MutableList` | Attribute modifiers applied while the archetype is active. | | `buoyant` | `Boolean` | Whether the sulfur cube floats on liquids. | | `contactDamage` | `SulfurCubeArchetypeContactDamage?` | Damage dealt to entities that touch the sulfur cube, omitted when `null`. | | `explosion` | `SulfurCubeArchetypeExplosion?` | The explosion triggered by the sulfur cube, omitted when `null`. | | `items` | `ItemTagArgument` | Item tag defining which items the sulfur cube can hold. **Required.** | | `knockbackModifiers` | `SulfurCubeArchetypeKnockbackModifiers` | Knockback dealt to entities that touch the sulfur cube. **Required.** | | `soundSettings` | `SulfurCubeArchetypeSoundSettings` | Hit and push sounds, required by the game, vanilla `regular` by default. | ### SulfurCubeArchetypeAttributeModifier Fields | Field | Type | Description | |-------------|------------------------------|-----------------------------------------------------------| | `amount` | `Double` | The amount applied by the modifier. | | `attribute` | `AttributeArgument` | The attribute being modified. | | `id` | `String` | The identifier of the modifier. | | `operation` | `AttributeModifierOperation` | How `amount` is combined with the attribute's base value. | ### SulfurCubeArchetypeContactDamage Fields | Field | Type | Description | |---------------------|----------------------|--------------------------------------------------------------------| | `amount` | `Float` | The amount of damage dealt. | | `attributeToSource` | `Boolean` | Whether the damage is attributed to the sulfur cube as its source. | | `damageType` | `DamageTypeArgument` | The type of damage dealt. | ### SulfurCubeArchetypeExplosion Fields | Field | Type | Description | |--------------|-----------|-----------------------------------------------------| | `causesFire` | `Boolean` | Whether the explosion sets blocks on fire. | | `fuse` | `Int` | The delay, in ticks, before the explosion goes off. | | `power` | `Int` | The explosion's power. | ### SulfurCubeArchetypeKnockbackModifiers Fields | Field | Type | Description | |-------------------|---------|---------------------------------| | `horizontalPower` | `Float` | The horizontal knockback power. | | `verticalPower` | `Float` | The vertical knockback power. | ### SulfurCubeArchetypeSoundSettings Fields | Field | Type | Description | |-----------------------------|----------------------|------------------------------------------------------------| | `hitSound` | `SoundEventArgument` | The sound played when the sulfur cube is hit. | | `pushSound` | `SoundEventArgument` | The sound played when the sulfur cube pushes an entity. | | `pushSoundCooldown` | `Float` | The delay, in ticks, before `pushSound` can play again. | | `pushSoundImpulseThreshold` | `Float` | The minimum push impulse required for `pushSound` to play. | ## See Also - [Item Predicates](/docs/concepts/components#item-predicates) - Match items against tags - [Tags](/docs/data-driven/tags) - Group items into reusable tags for `items` ### External Resources - [Minecraft Wiki: Sulfur cube archetype definition](https://minecraft.wiki/w/Sulfur_cube_archetype_definition) --- ## Tags --- root: .components.layouts.MarkdownLayout title: Tags nav-title: Tags description: Create and manage tags for grouping game elements with Kore's type-safe DSL keywords: minecraft, datapack, kore, tags, grouping, blocks, items, entities, functions date-created: 2026-02-03 date-modified: 2026-10-02 routeOverride: /docs/data-driven/tags --- # Tags Tags are JSON structures used in data packs to group related game elements together. They allow you to reference multiple items, blocks, entities, or other resources as a single unit. Tags are extensively used in commands, loot tables, advancements, recipes, and other data-driven features. The [tags example](/playground#example=tags) of the playground runs in your browser and shows the JSON it generates. ## Basic Usage Kore provides a generic `tag` function as well as specialized helper functions for each tag type: ```kotlin // Generic tag with explicit type tag("my_blocks", "block") { add(Blocks.STONE) add(Blocks.GRANITE) add(Blocks.DIORITE) } // Specialized helper function blockTag("my_blocks") { add(Blocks.STONE) add(Blocks.GRANITE) add(Blocks.DIORITE) } ``` This creates a tag file at `data//tags/block/my_blocks.json`. ## Adding Values There are several ways to add values to a tag: ### Simple Addition ```kotlin blockTag("building_blocks") { // Add a single block add(Blocks.STONE) // Using operators this += Blocks.COBBLESTONE this += "minecraft:granite" // Add with namespace add("custom_stone", namespace = "mymod") } ``` ### Adding Other Tags You can include other tags within a tag by prefixing with `#`: ```kotlin blockTag("all_stones") { // Reference another tag add("#minecraft:stone_bricks", tag = true) // Or using the name/namespace form add("base_stone_overworld", namespace = "minecraft", tag = true) } ``` ### Optional Entries Mark entries as optional with the `required` parameter. Optional entries won't cause errors if they don't exist: ```kotlin itemTag("optional_items") { add(Items.DIAMOND, required = true) // Must exist add("mymod:custom_gem", required = false) // Optional // Using TagEntry directly this += TagEntry("minecraft:emerald", required = false) } ``` ### Replace Mode By default, tags merge with existing tags of the same name. Set `replace = true` to completely override: ```kotlin // This will replace the vanilla tag entirely blockTag("logs", namespace = "minecraft", replace = true) { add(Blocks.OAK_LOG) add(Blocks.BIRCH_LOG) // Other logs won't be included } ``` ## Tag Types Kore provides specialized helper functions for all tag types. Here are the most commonly used ones: ### Block Tags ```kotlin blockTag("fragile_blocks") { add(Blocks.GLASS) add(Blocks.ICE) add(Blocks.GLOWSTONE) } ``` ### Item Tags ```kotlin itemTag("valuable_gems") { add(Items.DIAMOND) add(Items.EMERALD) add(Items.AMETHYST_SHARD) } ``` ### Entity Type Tags ```kotlin entityTypeTag("friendly_mobs") { add(EntityTypes.VILLAGER) add(EntityTypes.IRON_GOLEM) add(EntityTypes.CAT) } ``` ### Function Tags Function tags are special - they define groups of functions that can be called together or triggered by game events: ```kotlin // Create functions to be tagged function("on_load") { say("Datapack loaded!") } function("setup_scores") { scoreboard.objectives.add("kills", ScoreboardCriteria.PLAYER_KILL_COUNT) } // Tag them to run on load functionTag("load", namespace = "minecraft") { add("on_load", namespace = name) add("setup_scores", namespace = name) } ``` The `minecraft:load` and `minecraft:tick` function tags are special: - `minecraft:load` - Functions run once when the datapack loads - `minecraft:tick` - Functions run every game tick ### Biome Tags ```kotlin biomeTag("hot_biomes") { add(Biomes.DESERT) add(Biomes.BADLANDS) add(Biomes.SAVANNA) } ``` ### Damage Type Tags ```kotlin damageTypeTag("magic_damage") { add(DamageTypes.MAGIC) add(DamageTypes.INDIRECT_MAGIC) add(DamageTypes.DRAGON_BREATH) } ``` ### Enchantment Tags ```kotlin enchantmentTag("combat_enchants") { add(Enchantments.SHARPNESS) add(Enchantments.SMITE) add(Enchantments.BANE_OF_ARTHROPODS) } ``` ### Fluid Tags ```kotlin fluidTag("dangerous_fluids") { add(Fluids.LAVA) add(Fluids.FLOWING_LAVA) } ``` ## Complete List of Tag Helpers Kore provides helpers for all vanilla tag types: | Function | Tag Type | Path | |-------------------------------|----------------------|---------------------------------------------| | `bannerPatternTag` | Banner patterns | `tags/banner_pattern` | | `biomeTag` | Biomes | `tags/worldgen/biome` | | `blockTag` | Blocks | `tags/block` | | `catVariantTag` | Cat variants | `tags/cat_variant` | | `configuredCarverTag` | World carvers | `tags/worldgen/configured_carver` | | `configuredFeatureTag` | World features | `tags/worldgen/feature` | | `configuredStructureTag` | Structures | `tags/worldgen/structure` | | `damageTypeTag` | Damage types | `tags/damage_type` | | `enchantmentTag` | Enchantments | `tags/enchantment` | | `entityTypeTag` | Entity types | `tags/entity_type` | | `flatLevelGeneratorPresetTag` | Flat world presets | `tags/worldgen/flat_level_generator_preset` | | `fluidTag` | Fluids | `tags/fluid` | | `frogVariantTag` | Frog variants | `tags/frog_variant` | | `functionTag` | Functions | `tags/function` | | `gameEventTag` | Game events | `tags/game_event` | | `instrumentTag` | Goat horns | `tags/instrument` | | `itemTag` | Items | `tags/item` | | `noiseSettingsTag` | Noise settings | `tags/worldgen/noise_settings` | | `noiseTag` | Noise | `tags/worldgen/noise` | | `paintingVariantTag` | Paintings | `tags/painting_variant` | | `pigVariantTag` | Pig variants | `tags/pig_variant` | | `placedFeatureTag` | Placed features | `tags/worldgen/placed_feature` | | `pointOfInterestTypeTag` | POI types | `tags/point_of_interest_type` | | `processorListTag` | Structure processors | `tags/worldgen/processor_list` | | `structureTag` | Structures | `tags/worldgen/structure` | | `templatePoolTag` | Jigsaw pools | `tags/worldgen/template_pool` | | `trimMaterialTag` | Trim materials | `tags/trim_material` | | `trimPatternTag` | Trim patterns | `tags/trim_pattern` | | `wolfVariantTag` | Wolf variants | `tags/wolf_variant` | | `worldPresetTag` | World presets | `tags/worldgen/world_preset` | ## Modifying Existing Tags Calling a tag builder again with the same name, type and namespace adds to the tag already registered, so the pack writes a single file listing every entry. `addToTag` does the same when you only know the registry type: ```kotlin // First creation blockTag("my_ores") { add(Blocks.IRON_ORE) add(Blocks.GOLD_ORE) } // Later in code, add more entries to the same data//tags/block/my_ores.json blockTag("my_ores") { add(Blocks.DIAMOND_ORE) } addToTag("my_ores", "block") { add(Blocks.EMERALD_ORE) } ``` ## Using Tags in Commands Tags can be referenced in commands with the `#` prefix, so they appear naturally across the [Commands](/docs/commands/commands) API surface: ```kotlin function("clear_valuables") { // Clear all items matching a tag clear(allPlayers(), ItemPredicate(ItemTagArgument("valuable_gems", datapack.name))) // Target every raider, the selector `type` accepts entity types and entity type tags kill(allEntities { type = Tags.EntityType.RAIDERS }) // kill @e[type=#minecraft:raiders] } ``` ## Using Tags in Predicates Tags work seamlessly with [Predicates](/docs/data-driven/predicates): ```kotlin predicate("holding_tool") { matchTool { items(Tags.Item.PICKAXES) } } predicate("in_hot_biome") { locationCheck { biomes(BiomeTagArgument("hot_biomes", name)) } } ``` ## Using Tags in Recipes Tags are commonly used in [Recipes](/docs/data-driven/recipes) for flexible ingredient matching: ```kotlin craftingShapeless("any_planks_to_sticks") { ingredient(Tags.Item.PLANKS) ingredient(Tags.Item.PLANKS) result(Items.STICK, 4) } ``` ## Generated JSON A basic tag generates JSON like this: ```json { "replace": false, "values": [ "minecraft:stone", "minecraft:granite", "minecraft:diorite" ] } ``` With optional entries: ```json { "replace": false, "values": [ "minecraft:diamond", { "id": "mymod:custom_gem", "required": false } ] } ``` With tag references: ```json { "replace": false, "values": [ "#minecraft:stone_bricks", "minecraft:cobblestone" ] } ``` ## Best Practices 1. **Use meaningful names**: Name tags based on their purpose (e.g., `mineable/pickaxe` not `pickaxe_blocks`) 2. **Leverage existing tags**: Reference vanilla tags when appropriate instead of duplicating entries 3. **Keep tags focused**: Each tag should serve a single, clear purpose 4. **Use optional entries**: Mark modded content as `required = false` for cross-mod compatibility 5. **Avoid replace when possible**: Merging with existing tags maintains compatibility with other datapacks ## See Also - [Predicates](/docs/data-driven/predicates) - Use tags in predicate conditions - [Recipes](/docs/data-driven/recipes) - Use tags for flexible recipe ingredients - [Loot Tables](/docs/data-driven/loot-tables) - Use tags in loot conditions - [Trims](/docs/data-driven/trims) - Trim material and pattern tags ### External Resources - [Minecraft Wiki: Tag (Java Edition)](https://minecraft.wiki/w/Tag_(Java_Edition)) - Complete reference for all tag types and vanilla tags --- ## Timelines --- root: .components.layouts.MarkdownLayout title: Timelines nav-title: Timelines description: Learn how to use timelines in your Kore datapacks keywords: minecraft, datapack, kore, timelines, environment attributes, easing, keyframes, time markers, world clock date-created: 2026-02-10 date-modified: 2026-06-16 routeOverride: /docs/data-driven/timelines --- # Timelines Timelines control game behaviour and visuals based on a world clock through environment attributes. They define tracks that animate environment attributes over time using keyframes and easing functions. Optional named **time markers** act as labelled tick positions inside a timeline. Timelines were added in snapshot [**25w45a**](https://minecraft.wiki/w/Java_Edition_25w45a) (Minecraft 1.21.11). For a broader overview that covers world clocks, the `/time` command, and the `timeCheck` predicate condition together, see [World Clocks](/docs/data-driven/world-clocks). ## Basic Usage Every timeline must be bound to a `WorldClockArgument`. Register a clock first, then pass it when creating the timeline: ```kotlin val dayClock = dataPack.worldClock("day") val myTimeline = dataPack.timeline("day_fog", clock = dayClock) { periodTicks = 24000 track(EnvironmentAttributes.Visual.FOG_START_DISTANCE) { ease = Linear keyframe(0) { value(0.0f) } keyframe(12000) { value(100.0f) } } } ``` The `timeline` function creates and registers a timeline in your DataPack. It produces a file at `data//timeline/.json` and returns a `TimelineArgument`. ## Timeline Properties | Property | Type | Description | |---------------|--------------------------------|-------------------------------------------------------------------------------| | `clock` | `WorldClockArgument` | **Required.** The world clock this timeline reads from. | | `periodTicks` | `Int?` | Duration in ticks before the timeline loops. Omit for a one-shot timeline. | | `timeMarkers` | `Map?` | Named tick positions inside this timeline. See [Time Markers](#time-markers). | ## Tracks Tracks map environment attributes to keyframe-based animations. Each track specifies an easing type, an optional modifier, and a list of keyframes. ```kotlin dataPack.timeline("multi_track", clock = dayClock) { periodTicks = 24000 track(EnvironmentAttributes.Visual.FOG_START_DISTANCE) { ease = Linear keyframe(0) { value(0.0f) } keyframe(12000) { value(100.0f) } } track(EnvironmentAttributes.Visual.FOG_END_DISTANCE) { ease = InOutCubic keyframe(0) { value(50.0f) } keyframe(6000) { value(200.0f) } } } ``` ### Track Properties | Property | Type | Description | |-------------|--------------------------------|----------------------------------------------------------------------| | `ease` | `EasingType` | The easing type for interpolation between keyframes. Default: linear | | `modifier` | `EnvironmentAttributeModifier` | The environment attribute modifier ID. Default: `OVERRIDE` | | `keyframes` | `List` | A list of keyframes defining values at specific ticks | ## Keyframes Each keyframe defines a value at a specific tick within the timeline period: ```kotlin keyframe(0) { value(0.0f) } keyframe(12000) { value(100.0f) } ``` The `value` function accepts `Float`, `Int`, `Boolean`, `String`, `Color`, or any `EnvironmentAttributesType` (e.g., `FloatValue`, `BooleanValue`, `ColorValue`). Using typed values with `EnvironmentAttributesType`: ```kotlin keyframe(0) { value(RGB(255, 128, 0)) // Color value } keyframe(12000) { value(FloatValue(1.0f)) // Explicit EnvironmentAttributesType } ``` ## Time Markers Time markers are named tick positions inside a timeline. Commands can jump a clock to a marker position via `/time set `, and they appear in command auto-complete when `showInCommands` is `true`. ```kotlin dataPack.timeline("seasons", clock = seasonClock) { periodTicks = 96000 timeMarker("spring", ticks = 0, showInCommands = true) timeMarker("summer", ticks = 24000, showInCommands = true) timeMarker("autumn", ticks = 48000, showInCommands = true) timeMarker("winter", ticks = 72000, showInCommands = true) } ``` Reference a marker in a function using `TimeMarkerArgument`: ```kotlin function("skip_to_summer") { time.set(timeMarker("summer", "mymod")) } ``` See [World Clocks - Time Markers](/docs/data-driven/world-clocks#time-markers) for the full command usage. ## Easing Types Easing types control how values are interpolated between keyframes. ### Non-interpolating Types | Easing Type | Description | |-------------|-----------------------------------------------------| | `Constant` | Always selects the value from the previous keyframe | | `Linear` | Linearly interpolates between keyframes (lerp) | ### Interpolating Types Each interpolation kind is available in three forms: `In*`, `Out*`, and `InOut*`. | Kind | Ease In | Ease Out | Ease In-Out | |---------|-------------|--------------|----------------| | Back | `InBack` | `OutBack` | `InOutBack` | | Bounce | `InBounce` | `OutBounce` | `InOutBounce` | | Circ | `InCirc` | `OutCirc` | `InOutCirc` | | Cubic | `InCubic` | `OutCubic` | `InOutCubic` | | Elastic | `InElastic` | `OutElastic` | `InOutElastic` | | Expo | `InExpo` | `OutExpo` | `InOutExpo` | | Quad | `InQuad` | `OutQuad` | `InOutQuad` | | Quart | `InQuart` | `OutQuart` | `InOutQuart` | | Quint | `InQuint` | `OutQuint` | `InOutQuint` | | Sine | `InSine` | `OutSine` | `InOutSine` | ### Cubic Bezier For custom easing curves, use `CubicBezier` with two control points: ```kotlin dataPack.timeline("bezier_example", clock = dayClock) { periodTicks = 12000 track(EnvironmentAttributes.Gameplay.SKY_LIGHT_LEVEL) { ease = CubicBezier(0.25f, 0.1f, 0.25f, 1.0f) keyframe(0) { value(0) } keyframe(6000) { value(15) } } } ``` This serializes as: ```json { "ease": { "cubic_bezier": [ 0.25, 0.1, 0.25, 1.0 ] } } ``` ## Environment Attributes Tracks reference environment attributes from the `EnvironmentAttributes` sealed interface, organized into categories: - **`EnvironmentAttributes.Audio`** - Sound-related attributes (ambient sounds, music volume, etc.) - **`EnvironmentAttributes.Gameplay`** - Gameplay mechanics (monster burning, bed rules, sky light level, etc.) - **`EnvironmentAttributes.Visual`** - Visual effects (fog, clouds, sky color, particles, etc.) ## Complete Example Here's a complete example of a day/night cycle timeline: ```kotlin val dayClock = dataPack.worldClock("day") dataPack.timeline("day_night_cycle", clock = dayClock) { periodTicks = 24000 timeMarker("noon", ticks = 6000, showInCommands = true) timeMarker("midnight", ticks = 18000, showInCommands = true) track(EnvironmentAttributes.Visual.FOG_START_DISTANCE) { ease = InOutSine modifier = EnvironmentAttributeModifier.ADD keyframe(0) { value(10.0f) } keyframe(6000) { value(100.0f) } keyframe(12000) { value(100.0f) } keyframe(18000) { value(10.0f) } } track(EnvironmentAttributes.Gameplay.MONSTERS_BURN) { ease = Constant keyframe(0) { value(true) } keyframe(12000) { value(false) } } track(EnvironmentAttributes.Visual.SKY_LIGHT_FACTOR) { ease = CubicBezier(0.42f, 0.0f, 0.58f, 1.0f) keyframe(0) { value(1.0f) } keyframe(12000) { value(0.0f) } } } ``` ## See Also - [World Clocks](/docs/data-driven/world-clocks) - Clocks, time markers, the `/time` command, and `timeCheck` - [Environment Attributes](/docs/data-driven/worldgen/environment-attributes) - Environment attributes used in timeline tracks - [Tags](/docs/data-driven/tags) - Use tags to group timelines ### External Resources - [Minecraft Wiki: Timeline](https://minecraft.wiki/w/Timeline) - Official JSON format reference --- ## Trial Spawners - Custom Trial Spawner Configurations in Kore --- root: .components.layouts.MarkdownLayout title: Trial Spawners - Custom Trial Spawner Configurations in Kore nav-title: Trial Spawners description: Configure custom Minecraft trial spawners with Kore's Kotlin DSL. Set spawned mobs, their NBT and equipment, mob counts, spawn delay and reward loot tables. keywords: minecraft trial spawner, datapack trial_spawner, kore trial spawner, trial spawner configuration, custom trial chamber, ominous trial spawner, spawn_potentials date-created: 2026-09-23 date-modified: 2026-09-23 routeOverride: /docs/data-driven/trial-spawners --- # Trial Spawners A trial spawner configuration decides what a trial spawner block spawns, how many mobs it spawns per player, how fast, and which loot tables it ejects once the players beat it. A trial spawner block points to one configuration for its normal state and one for its ominous state, so a custom trial chamber can mix vanilla and custom configurations freely. ## File Structure ``` data//trial_spawner/.json ``` ## Creating a Configuration Use `trialSpawner` on your `DataPack`. Every field is optional, the game falling back to its defaults: a 4 block spawn range, 6 total mobs, 2 simultaneous mobs, 40 ticks between spawns and the vanilla reward loot tables. ```kotlin val skeletonArchers = dp.trialSpawner("skeleton_archers") { simultaneousMobs = 3f simultaneousMobsAddedPerPlayer = 0.5f ticksBetweenSpawn = 20 totalMobs = 8f spawnPotential(EntityTypes.SKELETON, weight = 3) { equipment(LootTables.Equipment.TRIAL_CHAMBER_RANGED, 0f) } spawnPotential(EntityTypes.STRAY) { equipment(LootTables.Equipment.TRIAL_CHAMBER_RANGED, mapOf(EquipmentSlot.HEAD to 0.5f)) } } ``` | Property | Default | Description | |----------------------------------|-------------------------|--------------------------------------------------------------| | `itemsToDropWhenOminous` | vanilla loot table | Loot table of the items dropped around players when ominous. | | `lootTablesToEject` | vanilla key/consumables | Weighted reward loot tables, see `lootTableToEject`. | | `simultaneousMobs` | `2` | Mobs alive at the same time for a single player. | | `simultaneousMobsAddedPerPlayer` | `1` | Extra simultaneous mobs for each additional player. | | `spawnPotentials` | none | Weighted mobs to spawn, see `spawnPotential`. | | `spawnRange` | `4` | Spawn distance from the spawner, from `1` to `128`. | | `ticksBetweenSpawn` | `40` | Delay between two spawns. | | `totalMobs` | `6` | Mobs to defeat for a single player. | | `totalMobsAddedPerPlayer` | `2` | Extra mobs to defeat for each additional player. | ## Spawned Mobs `spawnPotential` adds a weighted mob, its block customizing what gets spawned: - `entity { }` adds NBT to the spawned entity, its `id` being always kept. - `equipment(lootTable, slotDropChance)` rolls the mob's equipment from a loot table, each slot dropping with the same chance. Pass a `Map` instead to set a chance per slot. - `customSpawnRules(blockLightLimit, skyLightLimit)` replaces the mob's own spawn rules with light ranges, like `0..7`. ```kotlin dp.trialSpawner("slimes") { spawnPotential(EntityTypes.SLIME, weight = 3) { entity { this["Size"] = 1 } } spawnPotential(EntityTypes.SLIME) { entity { this["Size"] = 2 } customSpawnRules(blockLightLimit = 0..7) } } ``` ## Rewards `lootTableToEject` adds a weighted loot table ejected for each player once the trial is beaten, replacing the vanilla ones. ```kotlin dp.trialSpawner("ominous_breezes") { itemsToDropWhenOminous = LootTables.Spawners.TrialChamber.ITEMS_TO_DROP_WHEN_OMINOUS lootTableToEject(LootTables.Spawners.Ominous.TrialChamber.KEY, weight = 3) lootTableToEject(LootTables.Spawners.Ominous.TrialChamber.CONSUMABLES, weight = 7) spawnPotential(EntityTypes.BREEZE) } ``` Produces JSON: ```json { "items_to_drop_when_ominous": "minecraft:spawners/trial_chamber/items_to_drop_when_ominous", "loot_tables_to_eject": [ { "data": "minecraft:spawners/ominous/trial_chamber/key", "weight": 3 }, { "data": "minecraft:spawners/ominous/trial_chamber/consumables", "weight": 7 } ], "spawn_potentials": [ { "data": { "entity": { "id": "minecraft:breeze" } }, "weight": 1 } ] } ``` Reference: [Trial spawner configuration](https://minecraft.wiki/w/Trial_spawner_configuration) --- ## Trims --- root: .components.layouts.MarkdownLayout title: Trims nav-title: Trims description: Create custom armor trim materials and patterns with Kore's type-safe DSL keywords: minecraft, datapack, kore, trims, armor, trim material, trim pattern, customization date-created: 2026-02-03 date-modified: 2026-10-08 routeOverride: /docs/data-driven/trims --- # Trims Armor trims are a customization system that allows players to add decorative patterns to their armor pieces. Trims consist of two components: **materials** (which determine the color/texture) and **patterns** (which determine the shape/design). Kore provides type-safe DSL builders for creating custom trim materials and patterns. ## Trim Materials Trim materials define the color palette and appearance when a trim is applied to armor. Each material specifies: - **Palette ID**: The palette texture coloring the trim, `minecraft:trim/amethyst` reads `textures/palettes/trim/amethyst.png` from the resource pack - **Description**: The text shown in-game when hovering over trimmed armor ### Basic Usage ```kotlin trimMaterial("ruby", Textures.Palettes.Trim.AMETHYST) { description("Ruby", Color.RED) } ``` This creates a trim material file at `data//trim_material/ruby.json`: ```json { "description": { "text": "Ruby", "color": "red" }, "palette_id": "minecraft:trim/amethyst" } ``` A custom palette takes its path relative to `textures/palettes/`: ```kotlin // Reads assets/my_pack/textures/palettes/trim/ruby.png trimMaterial("ruby", TrimColorPaletteArgument("trim/ruby", "my_pack")) { description("Ruby", Color.RED) } ``` The darker palette vanilla uses on armor of the same color as the trim, like `gold_darker` on gold armor, isn't set by the trim material: the resource pack's equipment asset swaps it through `trim_palette_replacements`. ## Trim Patterns Trim patterns define the visual design applied to armor. Each pattern specifies: - **Asset ID**: The id the game builds the texture paths from, `minecraft:coast` reads `textures/trims/entity/humanoid/coast.png` and `textures/trims/entity/humanoid_leggings/coast.png` from the resource pack. It defaults to the pattern's own id - **Description**: The text shown in-game when hovering over trimmed armor - **Decal**: Whether the pattern should render as a decal overlay (like netherite patterns) ### Basic Usage ```kotlin // Reuses the vanilla coast textures trimPattern("stripes", TrimPatterns.COAST, textComponent("Stripes")) { description("Striped Pattern", Color.GRAY) } // Asset id :custom, textures in assets//textures/trims/entity/humanoid/custom.png trimPattern("custom") { description("Custom Pattern") } ``` This creates trim pattern files at `data//trim_pattern/stripes.json` and `custom.json`. ### Pattern as Decal Setting `decal = true` makes the pattern render as an overlay, which is useful for patterns that should appear on top of the base armor texture without replacing it (similar to how netherite trim patterns work): ```kotlin trimPattern("overlay", TrimPatterns.SENTRY, textComponent("Overlay"), decal = true) { description("Overlay Pattern") } ``` ### Generated JSON A trim pattern generates JSON like this: ```json { "asset_id": "minecraft:coast", "description": { "text": "Striped Pattern", "color": "gray" }, "decal": false } ``` With decal enabled: ```json { "asset_id": "minecraft:sentry", "description": { "text": "Overlay Pattern" }, "decal": true } ``` ## See Also - [Chat Components](/docs/concepts/chat-components) - For trim description text formatting - [Tags](/docs/data-driven/tags) - Organize trim materials and patterns into groups ### External Resources - [Minecraft Wiki: Tutorial - Adding custom trims](https://minecraft.wiki/w/Tutorial:Adding_custom_trims) - Official guide for creating custom trims - [Minecraft Wiki: Armor - Trimming](https://minecraft.wiki/w/Armor#Trimming) - Information about armor trimming mechanics --- ## Variants --- root: .components.layouts.MarkdownLayout title: Variants nav-title: Variants description: Define entity, painting and decorated pot pattern variants with Kore's type-safe DSL keywords: minecraft, datapack, kore, variants, cat, cow, chicken, frog, pig, wolf, zombie nautilus, painting, decorated pot pattern, sound variants date-created: 2026-02-03 date-modified: 2026-10-08 routeOverride: /docs/data-driven/variants --- # Variants Variants allow you to customize the appearance and spawn conditions of various entities and paintings in Minecraft. Kore provides type-safe DSL builders for creating custom variants for cats, cows, chickens, frogs, pigs, wolves, zombie nautiluses, paintings and decorated pot patterns. ## Entity Variants ### Spawn Conditions Most entity variants support spawn conditions that control when and where the variant appears. Available condition types: - **add(priority)**: Base condition with just a priority - **biome(priority, biomes...)**: Spawn in specific biomes or biome tags - **moonBrightness(priority, value)**: Spawn based on moon brightness (single value) - **moonBrightness(priority, min, max)**: Spawn based on moon brightness (range) - **structures(priority, structures...)**: Spawn near specific structures or structure tags Higher priority values take precedence when multiple conditions match. ### Cat Variants Cat variants define the texture and spawn conditions for cats. You can specify biomes, moon brightness, and structures as spawn conditions. The game requires a kitten texture too. `babyAssetId` defaults to the adult texture suffixed with `_baby`, the vanilla naming, so only pass it when your kitten texture is named differently. Cow, chicken and pig variants work the same way. ```kotlin catVariant("test_cat_variant", Textures.Entity.Cat.CAT_TABBY) { spawnConditions { add(10) biome(5, Biomes.PLAINS) biome(2, Tags.Worldgen.Biome.IS_OVERWORLD) moonBrightness(1, 1.0) moonBrightness(1, 0.5, 0.75) structures(0, ConfiguredStructures.VILLAGE_PLAINS) } } ``` Produces JSON: ```json { "asset_id": "minecraft:entity/cat/cat_tabby", "baby_asset_id": "minecraft:entity/cat/cat_tabby_baby", "spawn_conditions": [ { "priority": 10 }, { "priority": 5, "condition": { "type": "minecraft:biome", "biomes": "minecraft:plains" } }, { "priority": 2, "condition": { "type": "minecraft:biome", "biomes": "#minecraft:is_overworld" } }, { "priority": 1, "condition": { "type": "minecraft:moon_brightness", "range": 1.0 } }, { "priority": 1, "condition": { "type": "minecraft:moon_brightness", "range": { "min": 0.5, "max": 0.75 } } }, { "priority": 0, "condition": { "type": "minecraft:structure", "structures": "minecraft:village_plains" } } ] } ``` ### Cow Variants Cow variants define the texture, model, and spawn conditions for cows. ```kotlin cowVariant("test_cow_variant", Textures.Entity.Cow.COW_COLD, CowModel.COLD) { spawnConditions { structures(0, Tags.Worldgen.Structure.MINESHAFT) } } ``` Produces JSON: ```json { "asset_id": "minecraft:entity/cow/cow_cold", "baby_asset_id": "minecraft:entity/cow/cow_cold_baby", "model": "cold", "spawn_conditions": [ { "priority": 0, "condition": { "type": "minecraft:structure", "structures": "#minecraft:mineshaft" } } ] } ``` ### Chicken Variants Chicken variants define the texture, model, and spawn conditions for chickens. ```kotlin chickenVariant("test_chicken_variant", Textures.Entity.Chicken.CHICKEN_TEMPERATE, ChickenModel.NORMAL) { spawnConditions { structures(0, Tags.Worldgen.Structure.VILLAGE) } } ``` Produces JSON: ```json { "asset_id": "minecraft:entity/chicken/chicken_temperate", "baby_asset_id": "minecraft:entity/chicken/chicken_temperate_baby", "model": "normal", "spawn_conditions": [ { "priority": 0, "condition": { "type": "minecraft:structure", "structures": "#minecraft:village" } } ] } ``` ### Frog Variants Frog variants define the texture and spawn conditions for frogs. ```kotlin frogVariant("test_frog_variant", Textures.Entity.Frog.FROG_TEMPERATE) { spawnConditions { add(10) biome(5, Biomes.SNOWY_PLAINS) biome(2, Tags.Worldgen.Biome.SPAWNS_COLD_VARIANT_FROGS) moonBrightness(1, 1.0) moonBrightness(1, 0.5, 0.75) structures(0, ConfiguredStructures.END_CITY) } } ``` Produces JSON: ```json { "asset_id": "minecraft:entity/frog/frog_temperate", "spawn_conditions": [ { "priority": 10 }, { "priority": 5, "condition": { "type": "minecraft:biome", "biomes": "minecraft:snowy_plains" } }, { "priority": 2, "condition": { "type": "minecraft:biome", "biomes": "#minecraft:spawns_cold_variant_frogs" } }, { "priority": 1, "condition": { "type": "minecraft:moon_brightness", "range": 1.0 } }, { "priority": 1, "condition": { "type": "minecraft:moon_brightness", "range": { "min": 0.5, "max": 0.75 } } }, { "priority": 0, "condition": { "type": "minecraft:structure", "structures": "minecraft:end_city" } } ] } ``` ### Pig Variants Pig variants define the texture, model, and spawn conditions for pigs. ```kotlin pigVariant("test_pig_variant", Textures.Entity.Pig.PIG_COLD, PigModel.COLD) { spawnConditions { structures(0, Tags.Worldgen.Structure.ON_TREASURE_MAPS) } } ``` Produces JSON: ```json { "asset_id": "minecraft:entity/pig/pig_cold", "baby_asset_id": "minecraft:entity/pig/pig_cold_baby", "model": "cold", "spawn_conditions": [ { "priority": 0, "condition": { "type": "minecraft:structure", "structures": "#minecraft:on_treasure_maps" } } ] } ``` ### Wolf Variants Wolf variants define separate textures for angry, tame, and wild states, for adults and puppies, along with spawn conditions. The puppy textures are required by the game and default to the vanilla ones. ```kotlin wolfVariant("test_wolf_variant") { assets( angry = Textures.Entity.Wolf.WOLF_STRIPED, tame = Textures.Entity.Wolf.WOLF_RUSTY_ANGRY, wild = Textures.Entity.Wolf.WOLF_BLACK, ) babyAssets( angry = Textures.Entity.Wolf.WOLF_BLACK_ANGRY_BABY, tame = Textures.Entity.Wolf.WOLF_BLACK_TAME_BABY, wild = Textures.Entity.Wolf.WOLF_BLACK_BABY, ) spawnConditions { biome(5, Biomes.OCEAN, Biomes.SNOWY_SLOPES) } } ``` Produces JSON: ```json { "assets": { "angry": "minecraft:entity/wolf/wolf_striped", "tame": "minecraft:entity/wolf/wolf_rusty_angry", "wild": "minecraft:entity/wolf/wolf_black" }, "baby_assets": { "angry": "minecraft:entity/wolf/wolf_black_angry_baby", "tame": "minecraft:entity/wolf/wolf_black_tame_baby", "wild": "minecraft:entity/wolf/wolf_black_baby" }, "spawn_conditions": [ { "priority": 5, "condition": { "type": "minecraft:biome", "biomes": [ "minecraft:ocean", "minecraft:snowy_slopes" ] } } ] } ``` ### Zombie Nautilus Variants Zombie nautilus variants define the texture, model, and spawn conditions for zombie nautiluses. ```kotlin zombieNautilusVariant( "test_zombie_nautilus_variant", Textures.Entity.Nautilus.ZOMBIE_NAUTILUS_CORAL, ZombieNautilusModel.WARM, ) { spawnConditions { structures(0, Tags.Worldgen.Structure.ON_TREASURE_MAPS) } } ``` Produces JSON: ```json { "asset_id": "minecraft:entity/nautilus/zombie_nautilus_coral", "model": "warm", "spawn_conditions": [ { "priority": 0, "condition": { "type": "minecraft:structure", "structures": "#minecraft:on_treasure_maps" } } ] } ``` ## Sound Variants ### Cat Sound Variants Cat sound variants define custom sounds for cats. Sounds are split into `adultSounds` and `babySounds`, both required by the game. When `babySounds` is left unset, Kore writes `adultSounds` in its place. ```kotlin catSoundVariant("funny") { adultSounds { ambientSound = SoundEvents.Entity.Cat.AMBIENT begForFoodSound = SoundEvents.Entity.Cat.BEG_FOR_FOOD deathSound = SoundEvents.Entity.Cat.DEATH eatSound = SoundEvents.Entity.Cat.EAT hissSound = SoundEvents.Entity.Cat.HISS hurtSound = SoundEvents.Entity.Cat.HURT purreowSound = SoundEvents.Entity.Cat.PURREOW purrSound = SoundEvents.Entity.Cat.PURR strayAmbientSound = SoundEvents.Entity.Cat.STRAY_AMBIENT } } ``` Produces JSON: ```json { "adult_sounds": { "ambient_sound": "minecraft:entity.cat.ambient", "beg_for_food_sound": "minecraft:entity.cat.beg_for_food", "death_sound": "minecraft:entity.cat.death", "eat_sound": "minecraft:entity.cat.eat", "hiss_sound": "minecraft:entity.cat.hiss", "hurt_sound": "minecraft:entity.cat.hurt", "purreow_sound": "minecraft:entity.cat.purreow", "purr_sound": "minecraft:entity.cat.purr", "stray_ambient_sound": "minecraft:entity.cat.stray_ambient" }, "baby_sounds": { "ambient_sound": "minecraft:entity.cat.ambient", "beg_for_food_sound": "minecraft:entity.cat.beg_for_food", "death_sound": "minecraft:entity.cat.death", "eat_sound": "minecraft:entity.cat.eat", "hiss_sound": "minecraft:entity.cat.hiss", "hurt_sound": "minecraft:entity.cat.hurt", "purreow_sound": "minecraft:entity.cat.purreow", "purr_sound": "minecraft:entity.cat.purr", "stray_ambient_sound": "minecraft:entity.cat.stray_ambient" } } ``` ### Chicken Sound Variants Chicken sound variants define custom sounds for chickens. Sounds are split into `adultSounds` and `babySounds`, both required by the game. When `babySounds` is left unset, Kore writes `adultSounds` in its place. ```kotlin chickenSoundVariant("clucky") { adultSounds { ambientSound = SoundEvents.Entity.Chicken.AMBIENT deathSound = SoundEvents.Entity.Chicken.DEATH hurtSound = SoundEvents.Entity.Chicken.HURT stepSound = SoundEvents.Entity.Chicken.STEP } } ``` Produces JSON: ```json { "adult_sounds": { "ambient_sound": "minecraft:entity.chicken.ambient", "death_sound": "minecraft:entity.chicken.death", "hurt_sound": "minecraft:entity.chicken.hurt", "step_sound": "minecraft:entity.chicken.step" }, "baby_sounds": { "ambient_sound": "minecraft:entity.chicken.ambient", "death_sound": "minecraft:entity.chicken.death", "hurt_sound": "minecraft:entity.chicken.hurt", "step_sound": "minecraft:entity.chicken.step" } } ``` ### Cow Sound Variants Cow sound variants define custom sounds for cows. Unlike other sound variants, cows use a flat structure without age-group nesting. ```kotlin cowSoundVariant("moody") { ambientSound = SoundEvents.Entity.Cow.AMBIENT deathSound = SoundEvents.Entity.Cow.DEATH hurtSound = SoundEvents.Entity.Cow.HURT stepSound = SoundEvents.Entity.Cow.STEP } ``` Produces JSON: ```json { "ambient_sound": "minecraft:entity.cow.ambient", "death_sound": "minecraft:entity.cow.death", "hurt_sound": "minecraft:entity.cow.hurt", "step_sound": "minecraft:entity.cow.step" } ``` ### Pig Sound Variants Pig sound variants define custom sounds for pigs. Sounds are split into `adultSounds` and `babySounds`, both required by the game. When `babySounds` is left unset, Kore writes `adultSounds` in its place. ```kotlin pigSoundVariant("oinking") { adultSounds { ambientSound = SoundEvents.Entity.Pig.AMBIENT deathSound = SoundEvents.Entity.Pig.DEATH eatSound = SoundEvents.Entity.Pig.AMBIENT hurtSound = SoundEvents.Entity.Pig.HURT stepSound = SoundEvents.Entity.Pig.STEP } } ``` Produces JSON: ```json { "adult_sounds": { "ambient_sound": "minecraft:entity.pig.ambient", "death_sound": "minecraft:entity.pig.death", "eat_sound": "minecraft:entity.pig.ambient", "hurt_sound": "minecraft:entity.pig.hurt", "step_sound": "minecraft:entity.pig.step" }, "baby_sounds": { "ambient_sound": "minecraft:entity.pig.ambient", "death_sound": "minecraft:entity.pig.death", "eat_sound": "minecraft:entity.pig.ambient", "hurt_sound": "minecraft:entity.pig.hurt", "step_sound": "minecraft:entity.pig.step" } } ``` ### Wolf Sound Variants Wolf sound variants define custom sounds for wolves. Sound variants are independent of color variants and spawning biome. Wolves make the sounds associated with their variant when they bark, pant, whine, growl, step, die, or get hurt. Sounds are split into `adultSounds` and `babySounds`, both required by the game. When `babySounds` is left unset, Kore writes `adultSounds` in its place. ```kotlin wolfSoundVariant("funny") { adultSounds { ambientSound = SoundEvents.Entity.Pig.AMBIENT deathSound = SoundEvents.Entity.Creeper.DEATH growlSound = SoundEvents.Entity.Player.LEVELUP hurtSound = SoundEvents.Entity.Zombie.HURT pantSound = SoundEvents.Entity.EnderDragon.FLAP whineSound = SoundEvents.Entity.Cat.PURR } } ``` Produces JSON: ```json { "adult_sounds": { "ambient_sound": "minecraft:entity.pig.ambient", "death_sound": "minecraft:entity.creeper.death", "growl_sound": "minecraft:entity.player.levelup", "hurt_sound": "minecraft:entity.zombie.hurt", "pant_sound": "minecraft:entity.ender_dragon.flap", "step_sound": "minecraft:entity.wolf.step", "whine_sound": "minecraft:entity.cat.purr" }, "baby_sounds": { "ambient_sound": "minecraft:entity.pig.ambient", "death_sound": "minecraft:entity.creeper.death", "growl_sound": "minecraft:entity.player.levelup", "hurt_sound": "minecraft:entity.zombie.hurt", "pant_sound": "minecraft:entity.ender_dragon.flap", "step_sound": "minecraft:entity.wolf.step", "whine_sound": "minecraft:entity.cat.purr" } } ``` With separate baby sounds: ```kotlin wolfSoundVariant("funny") { adultSounds { ambientSound = SoundEvents.Entity.Wolf.AMBIENT deathSound = SoundEvents.Entity.Wolf.DEATH growlSound = SoundEvents.Entity.Wolf.GROWL hurtSound = SoundEvents.Entity.Wolf.HURT pantSound = SoundEvents.Entity.Wolf.PANT whineSound = SoundEvents.Entity.Wolf.WHINE } babySounds { ambientSound = SoundEvents.Entity.Pig.AMBIENT deathSound = SoundEvents.Entity.Pig.DEATH hurtSound = SoundEvents.Entity.Pig.HURT } } ``` Produces JSON: ```json { "adult_sounds": { "ambient_sound": "minecraft:entity.wolf.ambient", "death_sound": "minecraft:entity.wolf.death", "growl_sound": "minecraft:entity.wolf.growl", "hurt_sound": "minecraft:entity.wolf.hurt", "pant_sound": "minecraft:entity.wolf.pant", "step_sound": "minecraft:entity.wolf.step", "whine_sound": "minecraft:entity.wolf.whine" }, "baby_sounds": { "ambient_sound": "minecraft:entity.pig.ambient", "death_sound": "minecraft:entity.pig.death", "growl_sound": "minecraft:entity.wolf.growl", "hurt_sound": "minecraft:entity.pig.hurt", "pant_sound": "minecraft:entity.wolf.pant", "step_sound": "minecraft:entity.wolf.step", "whine_sound": "minecraft:entity.wolf.whine" } } ``` ## Other Variants ### Painting Variants Painting variants define custom paintings with dimensions and optional metadata like author and title. ```kotlin // Basic painting variant paintingVariant( assetId = Textures.Painting.KEBAB, height = 16, width = 16 ) ``` Produces JSON: ```json { "asset_id": "minecraft:kebab", "height": 16, "width": 16 } ``` With default dimensions (1x1): ```kotlin paintingVariant( assetId = Textures.Painting.AZTEC ) ``` Produces JSON: ```json { "asset_id": "minecraft:aztec", "height": 1, "width": 1 } ``` With author and title: ```kotlin paintingVariant( assetId = Textures.Painting.AZTEC, ) { author = textComponent("Ayfri") title = textComponent("Aztec") } ``` Produces JSON: ```json { "asset_id": "minecraft:aztec", "height": 1, "width": 1, "author": "Ayfri", "title": "Aztec" } ``` ### Decorated Pot Patterns A decorated pot pattern is the texture a pottery sherd shows on a face of a decorated pot, read from `textures/entity/decorated_pot/.png`. An item shows it through its `providesPotteryPattern` component: ```kotlin val star = decoratedPotPattern("star", DecoratedPotPatternAssetArgument("star_pottery_pattern", "my_pack")) val sherd = Items.BRICK { providesPotteryPattern(star) } ``` Produces JSON: ```json { "asset_id": "my_pack:star_pottery_pattern" } ``` The vanilla textures are typed, like `Textures.Entity.DecoratedPot.ANGLER_POTTERY_PATTERN`. ### See Also - [Chat Components](/docs/concepts/chat-components) - For painting title and author text formatting - [Tags](/docs/data-driven/tags) - Use variant tags for spawn conditions --- ## Villager Trades - Custom Trade Sets & Merchant Tables in Kore --- root: .components.layouts.MarkdownLayout title: Villager Trades - Custom Trade Sets & Merchant Tables in Kore nav-title: Villager Trades description: Define custom villager trades and trade sets for Minecraft 26.1+ with Kore's type-safe Kotlin DSL. Configure items, prices, demand, and profession-level trade tables without hand-writing JSON. keywords: minecraft villager trade, custom villager trades, datapack trade_set, datapack villager_trade, kore villager trades, minecraft merchant trades, custom trades datapack, profession trades, villager trade generator date-created: 2026-06-15 date-modified: 2026-08-20 routeOverride: /docs/data-driven/villager-trades --- # Villager Trades Villager trades are data-driven JSON files introduced in Minecraft Java Edition 26.1 that define what individual trades a villager can offer and how they are grouped into trade sets per profession level. This system fully replaces the old hardcoded trade lists and lets data packs control everything about villager economies - what items are bought and sold, at what quantities, for how many uses, with what item modifier functions applied to the output, and under what conditions. ## Overview The trade system uses two file types that work together: - **`villager_trade`** - a single trade offer: inputs the villager wants, the output it gives, limits, and rewards. - **`trade_set`** - a pool of `villager_trade` references that Minecraft samples from when a villager gains a level. A villager profession's trade list is built from a stack of trade sets, one per level. Each time a villager levels up, Minecraft draws `amount` trades at random from the appropriate trade set. ## File Structure ``` data//villager_trade/.json data//trade_set/.json ``` For the full JSON specification see: - [Minecraft Wiki: Villager trade definition](https://minecraft.wiki/w/Villager_trade_definition) - [Minecraft Wiki: Trade set](https://minecraft.wiki/w/Trade_set) --- ## Villager Trade ### Creating a Trade Use `villagerTrade` on your `DataPack`. Call `wants` and `gives` inside the block to set the required items - both accept an `ItemArgument`, an optional fixed `count`, and an optional component builder. ```kotlin datapack.villagerTrade("wheat_for_emerald") { wants(Items.WHEAT, count = 20) gives(Items.EMERALD) maxUses = constant(12f) xp = constant(1f) } ``` ### Item Slots `wants`, `gives`, and `additionalWants` all follow the same shape: an item id, an optional stack size, and an optional block to set data components on the item. ```kotlin datapack.villagerTrade("enchanted_sword") { wants(Items.EMERALD, count = 30) gives(Items.DIAMOND_SWORD) { enchantments { add(Enchantments.SHARPNESS, 5) } } maxUses = constant(3f) xp = constant(30f) } ``` Two-input trades use `additionalWants` for the second slot: ```kotlin datapack.villagerTrade("book_trade") { wants(Items.EMERALD, count = 5) additionalWants(Items.BOOK) gives(Items.ENCHANTED_BOOK) maxUses = constant(1f) xp = constant(15f) } ``` {{{ .components.mc.VillagerTradesMockup }}} ### VillagerTrade Fields All fields are optional except `wants` and `gives`, which must be set before the data pack is generated. | Field | Type | Description | |--------------------------------|--------------------------------------------|-----------------------------------------------------------------------------------| | `additionalWants` | `ItemStack?` | Second item slot the villager requests alongside `wants`. | | `doubleTradePriceEnchantments` | `InlinableList?` | Enchantments on the player's item that double the trade cost. | | `givenItemModifiers` | `ItemModifierAsList?` | Item modifier functions applied to the output item before delivery. | | `gives` | `ItemStack?` | Item the villager offers in return. **Required.** | | `maxUses` | `NumberProvider?` | Maximum uses before the trade locks. Unlocks when the villager restocks. | | `merchantPredicate` | `PredicateCondition?` | Condition checked against the merchant entity; trade only appears when it passes. | | `reputationDiscount` | `NumberProvider?` | Price multiplier applied based on the player's village reputation. | | `wants` | `ItemStack?` | Primary item the villager requests. **Required.** | | `xp` | `NumberProvider?` | XP points awarded to the villager when the trade completes. | ### Applying Item Modifiers to the Output Use `givenItemModifiers` to run item modifier functions on the item the villager gives. This is how you add random enchantments, custom lore, or any other post-processing: ```kotlin datapack.villagerTrade("random_enchanted_book") { wants(Items.EMERALD, count = 10) additionalWants(Items.BOOK) gives(Items.ENCHANTED_BOOK) givenItemModifiers { enchantRandomly() } maxUses = constant(1f) xp = constant(15f) } ``` See [Item Modifiers](/docs/data-driven/item-modifiers) for all available functions. ### Gating a Trade with a Predicate `merchantPredicate` is a condition evaluated against the villager entity. The trade only appears in the merchant's offer list when the condition passes - useful for biome-locked or NBT-gated trades. ```kotlin datapack.villagerTrade("desert_trade") { wants(Items.SAND, count = 8) gives(Items.GLASS) merchantPredicate = LocationCheck(predicate = LocationPredicate(biomes = listOf(Biomes.DESERT))) maxUses = constant(16f) xp = constant(2f) } ``` See [Predicates](/docs/data-driven/predicates) for all available condition types. ### Double-Price Enchantments `doubleTradePriceEnchantments` lists enchantments that, when present on the player's traded-in item, cause the villager to charge twice as much. Vanilla uses this for the `Curse of Binding` and similar effects. ```kotlin doubleTradePriceEnchantments = listOf(Enchantments.BINDING_CURSE) ``` --- ## Trade Set ### Creating a Trade Set A `TradeSet` groups references to `villager_trade` files and controls how many are rolled per level. Everything is set inside the block: `trades` takes the references returned by `villagerTrade` (or trade tags), and `amount` takes the number of trades rolled out of the pool. ```kotlin datapack { val wheatTrade = villagerTrade("wheat_for_emerald") { wants(Items.WHEAT, count = 20) gives(Items.EMERALD) xp = constant(1f) } val potatoTrade = villagerTrade("potato_for_emerald") { wants(Items.POTATO, count = 26) gives(Items.EMERALD) xp = constant(1f) } tradeSet("novice_farmer") { trades(wheatTrade, potatoTrade) amount(2f) } } ``` A file name holding slashes lands in subfolders, the way vanilla groups its sets per profession and level, so `tradeSet("farmer/level_1")` writes `data//trade_set/farmer/level_1.json`. ### Sampling with Tags Individual trade references and tag references mix in the same pool, and `trade` appends a single one to what is already there: ```kotlin tradeSet("apprentice_farmer") { trades(Tags.VillagerTrade.Farmer.LEVEL_2, myCustomTrade) trade(VillagerTradeTagArgument("farmer_apprentice", "mymod")) amount(1f, 3f) allowDuplicates = false } ``` A pool holding a single entry serializes as a plain string rather than an array, matching vanilla. ### Reproducible Rolls `randomSequence` seeds the rolls from a named random sequence, so the same set always produces the same trades for a given world seed: ```kotlin tradeSet("farmer/level_1") { trades(Tags.VillagerTrade.Farmer.LEVEL_1) amount(2f) randomSequence("trade_set/farmer/level_1", "mypack") } ``` ### TradeSet Fields | Field | Type | Description | |-------------------|---------------------------------------------|--------------------------------------------------------------------------------| | `allowDuplicates` | `Boolean?` | Whether the same trade can be rolled more than once. Defaults to `false`. | | `amount` | `NumberProvider` | How many trades are rolled out of the set. Defaults to `constant(1f)`. | | `randomSequence` | `RandomSequenceArgument?` | Named random sequence the rolls are seeded from, non-deterministic when unset. | | `trades` | `InlinableList` | Trade references or tags rolled from. | ### TradeSet Builders | Builder | Description | |-------------------------------|---------------------------------------------------------------| | `amount(value)` | Rolls exactly `value` trades. | | `amount(min, max)` | Rolls between `min` and `max` trades, both included. | | `randomSequence(name, ns)` | Seeds the rolls from a named random sequence. | | `trade(trade)` | Appends one trade or tag to the pool. | | `trades(vararg / list)` | Sets the pool, replacing the entries already there. | --- ## Full Example: Custom Farmer Profession Level 1 ```kotlin datapack { val hayTrade = villagerTrade("hay_for_emerald") { wants(Items.HAY_BLOCK, count = 1) gives(Items.EMERALD) maxUses = constant(16f) xp = constant(2f) } val wheatTrade = villagerTrade("wheat_for_emerald") { wants(Items.WHEAT, count = 20) gives(Items.EMERALD) maxUses = constant(16f) xp = constant(1f) } val breadTrade = villagerTrade("emerald_for_bread") { wants(Items.EMERALD) gives(Items.BREAD, count = 6) maxUses = constant(16f) xp = constant(1f) } tradeSet("custom_farmer_novice") { trades(hayTrade, wheatTrade, breadTrade) amount(2f) allowDuplicates = false } } ``` --- ## See Also - [Item Modifiers](/docs/data-driven/item-modifiers) - Apply functions to the item a villager gives - [Predicates](/docs/data-driven/predicates) - Gate trade availability via `merchantPredicate` - [Enchantments](/docs/data-driven/enchantments) - `doubleTradePriceEnchantments` and enchanting trade outputs - [Tags](/docs/data-driven/tags) - Group trades into reusable `villager_trade` tags for trade sets - [Loot Tables](/docs/data-driven/loot-tables) - Related data-driven item generation system ### External Resources - [Minecraft Wiki: Villager trade definition](https://minecraft.wiki/w/Villager_trade_definition) - [Minecraft Wiki: Trade set](https://minecraft.wiki/w/Trade_set) - [Minecraft Wiki: Trading](https://minecraft.wiki/w/Trading) - How the vanilla trading system works - [Minecraft Wiki: Villager](https://minecraft.wiki/w/Villager) - Villager behavior, professions, and leveling --- ## World Clocks - Custom Time Systems & TimeCheck Predicates in Kore --- root: .components.layouts.MarkdownLayout title: World Clocks - Custom Time Systems & TimeCheck Predicates in Kore nav-title: World Clocks description: Create custom world clocks and timelines in Kore datapacks. Define named time counters, time markers, timeCheck predicates, and control the /time command. Integrates with dimension types and environment timelines. keywords: minecraft world_clock, timeCheck predicate, custom time system datapack, datapack timeline, time marker minecraft, kore world clock, dimension_type clock, datapack time command, minecraft time counter date-created: 2026-06-16 date-modified: 2026-06-16 routeOverride: /docs/data-driven/world-clocks --- # World Clocks Minecraft's time system is built around **world clocks** - named counters that advance every tick. Timelines animate environment attributes by reading from a clock. The `/time` command lets you read and manipulate clocks at runtime. Time markers are named tick positions inside a timeline that commands and predicates can reference. All of these features were introduced alongside the environment-attributes overhaul in snapshot **26.1** and are fully supported in Kore's type-safe Kotlin DSL. --- ## World Clock A world clock is a data-driven resource with no properties. Its mere existence registers a named ticker under `data//world_clock/.json`. ```kotlin val dayClock = dataPack.worldClock("day") val seasonClock = dataPack.worldClock("season") ``` Each call registers the clock and returns a `WorldClockArgument` that you can pass to timelines, dimension types, and `/time of` commands. ### File Structure ``` data//world_clock/.json ``` The generated JSON is an empty object: ```json {} ``` --- ## Timelines Timelines animate environment attributes by reading from a `WorldClockArgument`. Every track is a keyframe curve tied to one attribute; the timeline drives it forward as the clock ticks. ### Creating a Timeline ```kotlin val dayClock = dataPack.worldClock("day") val dayNight = dataPack.timeline("day_night", clock = dayClock) { periodTicks = 24000 track(EnvironmentAttributes.Visual.FOG_START_DISTANCE) { ease = Linear keyframe(0) { value(10.0f) } keyframe(6000) { value(100.0f) } keyframe(18000) { value(10.0f) } keyframe(24000) { value(10.0f) } } track(EnvironmentAttributes.Gameplay.MONSTERS_BURN) { ease = Constant keyframe(0) { value(true) } keyframe(12000) { value(false) } } } ``` `timeline()` produces `data//timeline/.json` and returns a `TimelineArgument`. ### Timeline Properties | Property | Type | Description | |---------------|-----------------------------------------------------------------|-------------------------------------------------------------------------------| | `clock` | `WorldClockArgument` | **Required.** Clock this timeline reads from. | | `periodTicks` | `Int?` | Loop period in ticks. Omit for a one-shot timeline that runs to the end. | | `timeMarkers` | `Map?` | Named tick positions inside this timeline. See [Time Markers](#time-markers). | | `tracks` | `Map?` | Attribute animations. | ### Tracks Each `track()` call maps an `EnvironmentAttributeArgument` to an animation curve: ```kotlin track(EnvironmentAttributes.Visual.SKY_LIGHT_FACTOR) { ease = CubicBezier(0.42f, 0.0f, 0.58f, 1.0f) modifier = EnvironmentAttributeModifier.OVERRIDE keyframe(0) { value(1.0f) } keyframe(12000) { value(0.0f) } } ``` The `value()` function inside a keyframe block accepts `Float`, `Int`, `Boolean`, `String`, `Color`, or any `EnvironmentAttributesType`. ### Easing Types | Type | Description | |---------------|------------------------------------------------------| | `Constant` | Holds the previous keyframe value until the next one | | `Linear` | Straight lerp between keyframes | | `CubicBezier` | Custom four-point Bézier curve | Interpolating variants are available as `In*`, `Out*`, and `InOut*` for: `Back`, `Bounce`, `Circ`, `Cubic`, `Elastic`, `Expo`, `Quad`, `Quart`, `Quint`, `Sine`. --- ## Time Markers Time markers are named tick positions inside a timeline. Commands can target them with `/time set ` to jump the clock to that exact position. They also serve as human-readable labels for `/time query`. ### Defining Time Markers ```kotlin dataPack.timeline("seasons", clock = seasonClock) { periodTicks = 96000 // 4 × 24000 - one in-game "year" timeMarker("spring", ticks = 0) timeMarker("summer", ticks = 24000) timeMarker("autumn", ticks = 48000) timeMarker("winter", ticks = 72000, showInCommands = true) } ``` `showInCommands` controls whether the marker appears in command auto-complete suggestions. When omitted (or `null`), the default game behaviour applies. ### Referencing a Time Marker in Commands Use `TimeMarkerArgument` (or the `timeMarker()` factory) to pass a marker to `/time set`: ```kotlin function("skip_to_summer") { time.set(timeMarker("summer", "mymod")) } ``` ```kotlin function("skip_to_summer_on_clock") { time.of(seasonClock).set(timeMarker("summer", "mymod")) } ``` --- ## The `/time` Command Kore's `time` DSL mirrors the full Minecraft `/time` command tree. Access it via the `time` property on any `Function`: ```kotlin function("my_func") { time.add(1000) time.set(TimePeriod.NOON) time.query(TimeType.DAYS) } ``` ### Subcommands | DSL call | Emitted command | Effect | |--------------------------------|------------------------------------|-----------------------------------------------| | `time.add(1000)` | `time add 1000` | Advance the default clock by 1000 ticks | | `time.add(1.days)` | `time add 1d` | Advance by one full day | | `time.pause()` | `time pause` | Freeze the default clock | | `time.resume()` | `time resume` | Unfreeze the default clock | | `time.set(6000)` | `time set 6000` | Jump to tick 6000 | | `time.set(TimePeriod.NOON)` | `time set noon` | Jump to noon | | `time.set(marker)` | `time set :` | Jump to a named time marker | | `time.query(TimeType.DAYS)` | `time query day` | Output the clock time in days | | `time.query(timeline)` | `time query :` | Output progress through a timeline | | `time.queryRepetitions(tl)` | `time query : repetitions` | Output how many times the timeline has looped | | `time.queryTime()` | `time query time` | Output absolute game time | ### Targeting a Specific Clock with `time.of(clock)` When your datapack defines multiple world clocks, use `time.of(clock)` to scope every subcommand to that specific clock: ```kotlin function("advance_season") { time.of(seasonClock).add(6000) time.of(seasonClock).set(timeMarker("summer", "mymod")) time.of(seasonClock).query(TimeType.DAYS) } ``` Or use the builder form to avoid repeating `of(seasonClock)`: ```kotlin function("advance_season") { val seasonTime = time.of(seasonClock) seasonTime.add(6000) seasonTime.query(TimeType.DAYS) } ``` `time.of(clock)` returns a `TimeWithClock` instance. It supports the same `add`, `pause`, `resume`, `set`, `query`, `queryRepetitions`, and `queryTime` methods as the default `Time` DSL. --- ## `timeCheck` Predicate Condition The `timeCheck` condition passes when the time of a world clock falls within a specified range. The `clock` parameter is mandatory and selects which clock is queried. ```kotlin predicate("is_daytime") { timeCheck(WorldClocks.OVERWORLD, 0f..12000f) } ``` Pass a `period` to apply a modulo first - useful for checking time within a repeating sub-cycle: ```kotlin predicate("is_second_quarter") { timeCheck(WorldClocks.OVERWORLD, min = 6000f, max = 12000f, period = 24000) } ``` Custom clocks work the same way: ```kotlin predicate("is_summer") { timeCheck(seasonClock, min = 24000f, max = 48000f) } ``` See [Predicates](/docs/data-driven/predicates) for the full condition reference. --- ## `defaultClock` in Dimension Type `DimensionType` has an optional `defaultClock` field that selects which world clock drives the dimension's default time-dependent behaviour (sunrise/sunset, mob spawning light threshold, etc.). When omitted, the standard overworld day clock is used. ```kotlin dataPack.dimensionType("twilight_dimension") { defaultClock = dayClock hasSkylight = true logicalHeight = 256 infiniburn = listOf(Tags.Block.INFINIBURN_OVERWORLD) minY = -64 height = 384 monsterSpawnBlockLightLimit = 0 monsterSpawnLightLevel = constant(0) } ``` See [Dimensions](/docs/data-driven/worldgen/dimensions) for the full `DimensionType` reference. --- ## Complete Example The following snippet wires together all the concepts in this guide: ```kotlin dataPack("my_mod") { // 1. Register a custom clock val season = worldClock("season") // 2. Build a timeline driven by that clock timeline("seasons", clock = season) { periodTicks = 96000 timeMarker("spring", ticks = 0, showInCommands = true) timeMarker("summer", ticks = 24000, showInCommands = true) timeMarker("autumn", ticks = 48000, showInCommands = true) timeMarker("winter", ticks = 72000, showInCommands = true) track(EnvironmentAttributes.Visual.FOG_END_DISTANCE) { ease = InOutSine keyframe(0) { value(200.0f) } keyframe(24000) { value(300.0f) } keyframe(48000) { value(200.0f) } keyframe(72000) { value(80.0f) } } } // 3. Wire the clock to a custom dimension val dimType = dimensionType("my_dimension_type") { defaultClock = season hasSkylight = true logicalHeight = 256 infiniburn = listOf(Tags.Block.INFINIBURN_OVERWORLD) minY = -64 height = 384 monsterSpawnBlockLightLimit = 0 monsterSpawnLightLevel = constant(0) } // 4. Predicate: is it currently summer? predicate("is_summer") { timeCheck(season, min = 24000f, max = 48000f) } // 5. Command: skip to winter function("skip_to_winter") { time.of(season).set(timeMarker("winter", "my_mod")) } // 6. Command: advance the season clock function("tick_season") { time.of(season).add(1) time.of(season).query(TimeType.DAYS) } } ``` --- ## See Also - [Timelines](/docs/data-driven/timelines) - detailed track, keyframe, and easing reference - [Predicates](/docs/data-driven/predicates) - all available predicate conditions including `timeCheck` - [Dimensions](/docs/data-driven/worldgen/dimensions) - `DimensionType` and the dimensions using a clock - [Environment Attributes](/docs/data-driven/worldgen/environment-attributes) - attributes available in timeline tracks ### External Resources - [Minecraft Wiki: World clock](https://minecraft.wiki/w/World_clock) - [Minecraft Wiki: Timeline](https://minecraft.wiki/w/Timeline) - [Minecraft Wiki: Commands/time](https://minecraft.wiki/w/Commands/time) - [Minecraft Wiki: Predicate - time_check](https://minecraft.wiki/w/Predicate#time_check) --- ## Minecraft World Generation - Custom Dimensions, Biomes & Noise with Kore --- root: .components.layouts.MarkdownLayout title: Minecraft World Generation - Custom Dimensions, Biomes & Noise with Kore nav-title: Worldgen description: Build custom Minecraft world generation in Kotlin. Dimensions, biomes, noise, density functions, structures and features, type-safe, no hand-written JSON. keywords: minecraft worldgen, datapack worldgen, custom dimension minecraft, custom biome, noise settings minecraft, minecraft noise router, density function, datapack terrain generation, minecraft world preset, custom world generation date-created: 2025-08-11 date-modified: 2026-10-08 routeOverride: /docs/data-driven/worldgen --- # World Generation World generation is how Minecraft turns a seed into terrain: noise decides where blocks are solid, biomes decide what they look like and what spawns on them, features and structures decorate the result. All of it is data-driven, so a datapack can replace any part of it. Kore maps that data onto Kotlin builders. Everything on these pages produces JSON files under `data//`, and the whole surface is typed, so `Blocks.STONE` or `Biomes.DESERT` are checked at compile time instead of at world load. Reference: [World generation](https://minecraft.wiki/w/World_generation) ## Pages | Page | Covers | |-----------------------------------------------------------------------------|--------------------------------------------------------------------------| | [Biomes](/docs/data-driven/worldgen/biomes) | Climate, colors, mob spawns, carver and feature lists | | [Block Predicates](/docs/data-driven/worldgen/block-predicates) | Tests on the block at a position, used by features and enchantments | | [Carvers](/docs/data-driven/worldgen/carvers) | Caves, nether caves and canyons | | [Dimensions](/docs/data-driven/worldgen/dimensions) | Dimension types, dimensions, generators and biome sources | | [Environment Attributes](/docs/data-driven/worldgen/environment-attributes) | Visual, audio and gameplay rules shared by biomes and dimension types | | [Features](/docs/data-driven/worldgen/features) | Configured and placed features: trees, ores, vegetation | | [Noise & Terrain](/docs/data-driven/worldgen/noise) | Noises, density functions, noise settings, noise routers, material rules | | [Providers](/docs/data-driven/worldgen/providers) | Vertical anchors and the height, int and float providers used everywhere | | [Structures](/docs/data-driven/worldgen/structures) | Configured structures, template pools, processors and structure sets | | [World Presets](/docs/data-driven/worldgen/world-presets) | World types in the world creation screen, and superflat presets | ## How A Chunk Is Built Every chunk goes through the same ordered steps. A chunk that has not reached the last one is a **proto-chunk**, invisible to players; once it does, it becomes a **level chunk**. | # | Step | What happens | |---|-------------------------|---------------------------------------------------------| | 1 | `structures_starts` | Picks the origin of every structure piece | | 2 | `structures_references` | Records the nearby structure starts | | 3 | `biomes` | Assigns biomes, still without terrain | | 4 | `noise` | Builds the terrain shape and the liquid bodies | | 5 | `surface` | Repaints the top layers with biome-dependent blocks | | 6 | `carvers` | Cuts caves and canyons out of the terrain | | 7 | `features` | Places features and structures, computes the heightmaps | | 8 | `light` | Computes light levels | | 9 | `spawn` | Spawns the initial mobs | This ordering explains most of the behavior you will run into: carvers run before features, which is why a cave never destroys a tree, and structures start before terrain, which is why terrain can adapt around them. Reference: [Generation steps](https://minecraft.wiki/w/World_generation#Steps) ### Decoration Steps The `features` step is itself split into 11 decoration steps, run in order. A biome lists its placed features per step, and the structures of a step are placed before the features of that same step. | # | Step | Examples | |----|--------------------------|----------------------------------------------| | 1 | `raw_generation` | Small end islands | | 2 | `lakes` | Lava lakes | | 3 | `local_modifications` | Geodes, icebergs | | 4 | `underground_structures` | Trial chambers, mineshafts | | 5 | `surface_structures` | Desert wells, blue ice patches | | 6 | `strongholds` | Unused, strongholds use `surface_structures` | | 7 | `underground_ores` | Ore blobs, sand/gravel/clay disks | | 8 | `underground_decoration` | Infested blobs, nether gravel/blackstone | | 9 | `fluid_springs` | Water and lava springs | | 10 | `vegetal_decoration` | Trees, cacti, kelp, vegetation | | 11 | `top_layer_modification` | Freeze top layer | Reference: [Decoration steps](https://minecraft.wiki/w/World_Generation#Decoration_steps) ## Files Kore Writes Replace `` with your namespace. | Kore API | Output path | |---------------------------------|--------------------------------------------------------------| | `biome(...)` | `data//worldgen/biome/.json` | | `configuredCarvers { ... }` | `data//worldgen/configured_carver/.json` | | `configuredFeatures { ... }` | `data//worldgen/feature/.json` | | `densityFunctions { ... }` | `data//worldgen/density_function/.json` | | `dimension(...)` | `data//dimension/.json` | | `dimensionType(...)` | `data//dimension_type/.json` | | `flatLevelGeneratorPreset(...)` | `data//worldgen/flat_level_generator_preset/.json` | | `materialCondition(...)` | `data//worldgen/material_condition/.json` | | `materialRule(...)` | `data//worldgen/material_rule/.json` | | `noise(...)` | `data//worldgen/noise/.json` | | `noiseSettings(...)` | `data//worldgen/noise_settings/.json` | | `placedFeature(...)` | `data//worldgen/placed_feature/.json` | | `processorList(...)` | `data//worldgen/processor_list/.json` | | `structures { ... }` | `data//worldgen/structure/.json` | | `structureSet(...)` | `data//worldgen/structure_set/.json` | | `templatePool(...)` | `data//worldgen/template_pool/.json` | | `worldPreset(...)` | `data//worldgen/world_preset/.json` | A file name containing slashes lands in subfolders, so `noise("cave/entrance")` writes `worldgen/noise/cave/entrance.json`. ## A Complete Custom Dimension The smallest set of files producing a playable world of your own: a noise, the density functions shaping it, the noise settings wiring them together, a biome, a dimension type and the dimension itself. ```kotlin fun DataPack.createCustomWorld() { // 1) A Perlin noise, sampled by the density function below. val hills = noise("hills") { firstOctave = -5 amplitudes(1.0, 0.5, 0.25) } // 2) Density functions: hills on top of a gradient turning solid into air with height. val terrainDensity = with(densityFunctionsBuilder) { val hillsNoise = noise("hills_noise", hills, xzScale = 0.35, yScale = 0.0) val gradient = yClampedGradient("height_gradient") { fromY = 0 toY = 128 fromValue = 1.0 toValue = -1.0 } add("final_density", hillsNoise, gradient) } // 3) Noise settings: world bounds, blocks, and the router feeding the density to the game. val terrain = noiseSettings("custom_terrain") { seaLevel = 63 noiseOptions(minY = -64, height = 384, sizeHorizontal = 1, sizeVertical = 2) defaultBlock(Blocks.STONE) defaultFluid(Blocks.WATER) { this["level"] = "0" } noiseRouter { finalDensity(terrainDensity) } materialRules { condition(stoneDepth(Surface.FLOOR)) { block(Blocks.GRASS_BLOCK) } block(Blocks.STONE) } } // 4) A biome, used everywhere in this world. val plains = biome("custom_plains") { temperature = 0.8f downfall = 0.4f hasPrecipitation = true attributes { skyColor(0x78A7FF) fogColor(0xC0D8FF) } } // 5) The rules of the world. val dimType = dimensionType("custom_type") { minY = -64 height = 384 hasSkylight = true } // 6) The dimension itself. dimension("custom_world", type = dimType) { noiseGenerator(settings = terrain, biomeSource = fixed(plains)) } } ``` Go there in game with `/execute in :custom_world run tp @s 0 200 0`. To offer the world as a world type in the creation screen instead, wrap the same generator in a [world preset](/docs/data-driven/worldgen/world-presets). ## Testing - `/execute in : run tp @s 0 200 0` teleports into a dimension. - `/locate structure :` finds the nearest instance of a structure. - Worldgen files are read when the world is created, so `/reload` does not reshape the chunks already generated. Test terrain changes on a fresh world. - [Test Features](/docs/advanced/test-features) runs GameTest assertions for deterministic validation. ## See Also - [Colors](/docs/concepts/colors) - the RGB and ARGB formats used by biome effects and color attributes - [Predicates](/docs/data-driven/predicates) - condition logic reused across the datapack - [Tags](/docs/data-driven/tags) - grouping biomes, structures and presets so vanilla picks them up - [Timelines](/docs/data-driven/timelines) - animating environment attributes over time - [World Clocks](/docs/data-driven/world-clocks) - the clocks driving `DimensionType.defaultClock` --- ## Biomes --- root: .components.layouts.MarkdownLayout title: Biomes nav-title: Biomes description: Define Minecraft biomes with climate, colors, mob spawns, carvers and features using Kore's type-safe Kotlin DSL. keywords: minecraft, datapack, kore, worldgen, biome, carver, spawner, biome effects, climate parameters date-created: 2026-02-03 date-modified: 2026-08-21 routeOverride: /docs/data-driven/worldgen/biomes --- # Biomes A biome describes a region of the world: its climate, its colors and sounds, which mobs spawn there, which carvers dig through it and which features decorate it. It does not decide where it appears - that is the job of the [biome source](/docs/data-driven/worldgen/dimensions) of a dimension. ```kotlin val myBiome = dp.biome("my_biome") { temperature = 0.8f downfall = 0.4f hasPrecipitation = true attributes { skyColor(0x78A7FF) fogColor(0xC0D8FF) } effects { waterColor = color(0x3F76E4) } } ``` References: [Biome](https://minecraft.wiki/w/Biome), [Biome definition](https://minecraft.wiki/w/Biome_definition) ## Where Biomes Land In The World The vanilla Overworld places biomes in a 6-dimensional climate space. Each biome claims an interval on every axis, and the game picks the closest match for a position. These are the same six parameters a `multiNoise` biome source lists per entry: | Parameter | Controls | |-------------------|-------------------------------------------------------------| | `temperature` | Snow and ice coverage, vegetation types | | `humidity` | Vegetation density, arid to humid | | `continentalness` | Ocean, beach or inland | | `erosion` | Flat versus mountainous terrain | | `weirdness` | Biome variants, for example Jungle to Bamboo Jungle | | `depth` | Surface versus cave biome | The `temperature` and `downfall` fields on the biome itself are a different thing: they drive in-world behavior such as snow instead of rain, water freezing, and cauldrons filling. ## Climate Fields | Field | Type | Default | Effect | |----------------------------|-----------|---------|------------------------------------------------------------------------------| | `temperature` | `Float` | `0.8` | Below `0.15`, precipitation falls as snow and water freezes. | | `downfall` | `Float` | `0.4` | Wetness, driving foliage tinting and how fast fire spreads. | | `hasPrecipitation` | `Boolean` | `true` | Whether rain or snow falls at all. | | `temperatureModifier` | enum | none | `FROZEN` applies the noise-based warm patches of the frozen ocean. | | `creatureSpawnProbability` | `Float?` | none | Chance per chunk for the initial creature spawn during world generation. | ## Colors And Atmosphere Two blocks share this job, and the split matters: - `attributes { }` covers everything the camera sees or hears - sky, fog, particles, music, ambient sounds - plus gameplay rules. It is the same system dimension types use, so a biome value simply overrides the dimension value. See [Environment Attributes](/docs/data-driven/worldgen/environment-attributes). - `effects { }` covers the biome-local block and liquid tints, which are not attributes. ```kotlin attributes { skyColor(0x78A7FF) fogColor(0xC0D8FF, EnvironmentAttributeModifier.ADD) } effects { waterColor = color(0x3F76E4) // Defaults to the vanilla overworld blue. foliageColor = color(0x59AE30) dryFoliageColor = color(0xA0A04C) grassColor = color(0x79C05A) grassColorModifier = GrassColorModifier.SWAMP } ``` Colors serialize as decimal integers. `grassColorModifier` applies the vanilla `SWAMP` or `DARK_FOREST` post-processing on top of the computed grass color. Reference: [Biome definition - Effects](https://minecraft.wiki/w/Biome_definition#Effects) ## Mob Spawns `spawners` declares what may spawn per mob category. Within a category, `weight` is a relative probability against the other entries, and `minCount`/`maxCount` bound the size of a spawned group. ```kotlin spawners { creature { spawner(EntityTypes.COW, weight = 6, minCount = 2, maxCount = 4) spawner(EntityTypes.SHEEP, weight = 8, minCount = 2, maxCount = 4) } monster { spawner(EntityTypes.SKELETON, weight = 80, minCount = 1, maxCount = 2) spawner(EntityTypes.ZOMBIE, weight = 80, minCount = 1, maxCount = 2) } } ``` Categories: `ambient`, `axolotls`, `creature`, `misc`, `monster`, `undergroundWaterCreature`, `waterAmbient`, `waterCreature`. `spawnCosts` caps density with an energy budget instead of a raw count. Each mob spends `charge` from a shared `energyBudget`, so a crowded area stops spawning before it becomes a mob farm. ```kotlin spawnCosts { spawnCost(EntityTypes.COW, energyBudget = 1.2f, charge = 0.1f) } ``` References: [Mob spawning](https://minecraft.wiki/w/Biome_definition#Mob_spawning), [Spawn costs](https://minecraft.wiki/w/Biome_definition#Spawn_costs) ## Carvers `carvers` is a flat list of configured carver IDs, or a single carver tag. They run during the `carvers` step, after the terrain noise and before any feature. ```kotlin carvers(myCaveCarver, ConfiguredCarvers.CAVE) ``` See [Carvers](/docs/data-driven/worldgen/carvers). ## Features `features` lists placed features per [decoration step](/docs/data-driven/worldgen#decoration-steps). Every property is a plain list, and leaving one empty simply means nothing of yours generates at that step. ```kotlin features { fluidSprings = listOf(PlacedFeatures.SPRING_WATER) lakes = listOf(PlacedFeatures.LAKE_LAVA_UNDERGROUND) localModifications = listOf(PlacedFeatures.AMETHYST_GEODE) surfaceStructures = listOf(PlacedFeatures.DESERT_WELL) topLayerModification = listOf(PlacedFeatures.FREEZE_TOP_LAYER) undergroundDecoration = listOf(PlacedFeatures.GLOW_LICHEN) undergroundOres = listOf(orePlaced) undergroundStructures = listOf(PlacedFeatures.MONSTER_ROOM) vegetalDecoration = listOf(treePlaced, flowerPlaced) } ``` See [Features](/docs/data-driven/worldgen/features). ## Complete Example ```kotlin fun DataPack.highlandsBiome(treePlaced: PlacedFeatureArgument, orePlaced: PlacedFeatureArgument) { val cave = configuredCarversBuilder.cave("highlands_cave") { probability = 0.08 y = uniformHeightProvider(absolute(32), absolute(128)) floorLevel = constant(-0.2f) } biome("highlands") { temperature = 0.8f downfall = 0.3f hasPrecipitation = true attributes { fogColor(0xBFEFFF) skyColor(0x99D9FF) waterFogColor(0x0A2C4F) } effects { waterColor = color(0x34A7F0) } spawners { creature { spawner(EntityTypes.COW, 6, 2, 4) spawner(EntityTypes.SHEEP, 8, 2, 4) } monster { spawner(EntityTypes.SKELETON, 80, 1, 2) } } spawnCosts { spawnCost(EntityTypes.COW, 1.2f, 0.1f) } carvers(cave) features { undergroundOres = listOf(orePlaced) vegetalDecoration = listOf(treePlaced) } } } ``` ## See Also - [Carvers](/docs/data-driven/worldgen/carvers) - the caves and canyons a biome runs - [Dimensions](/docs/data-driven/worldgen/dimensions) - biome sources placing biomes in a world - [Environment Attributes](/docs/data-driven/worldgen/environment-attributes) - the full attribute reference - [Features](/docs/data-driven/worldgen/features) - the placed features a biome lists - [World Generation](/docs/data-driven/worldgen) - overview of the worldgen system --- ## Block Predicates --- root: .components.layouts.MarkdownLayout title: Block Predicates nav-title: Block Predicates description: Test blocks during world generation with Kore's block predicate DSL - solid, matching blocks, fluids, biomes, tags and offsets. keywords: minecraft, datapack, kore, worldgen, block predicate, placement condition, matching blocks, would survive date-created: 2026-08-19 date-modified: 2026-08-21 routeOverride: /docs/data-driven/worldgen/block-predicates --- # Block Predicates A block predicate is a test on the state of the block at a given position. Placed feature placement modifiers, many configured features and the enchantment effects all use them to decide whether something may generate at a position. Reference: [Block predicate](https://minecraft.wiki/w/Block_predicate) ## Writing a Predicate Every field taking a block predicate exposes a builder block named after it. Inside that block a single predicate is used as-is, and several predicates are combined into an `all_of`: ```kotlin placedFeature("my_flower", flowerFeature) { blockPredicateFilter { // One predicate: used directly. predicate { solid { offset(0, -1, 0) } } } blockPredicateFilter { // Several predicates: wrapped in an `all_of`. predicate { solid { offset(0, -1, 0) } not { matchingFluids(Fluids.WATER) } } } } ``` Wrap the body in `anyOf { }` to combine the predicates with an *or* instead: ```kotlin blockPredicateFilter { predicate { anyOf { matchingBlocks(Blocks.STONE) matchingBlocks(Blocks.DEEPSLATE) } } } ``` The builders are scoped: they only resolve inside a block that accepts a block predicate, so they never show up in the global completion list. The same field also accepts a value directly, which is handy to reuse a predicate across several features: ```kotlin blockPredicateFilter { predicate = allOf { solid() insideWorldBounds() } } ``` ## Offsets Every predicate testing a block accepts an offset, applied to the position being placed. Each component has to be between `-16` and `16`: ```kotlin predicate { // Tests the block one below the position. solid { offset(0, -1, 0) } } ``` `all_of`, `any_of`, `not`, `true` and `matching_biomes` have no offset - they either combine other predicates or test the position itself. ## Predicate Types | Builder | Passes when | |----------------------------------|-------------------------------------------------------------------| | `allOf { }` | Every child predicate passes | | `alwaysTrue()` | Always | | `anyOf { }` | At least one child predicate passes | | `hasSturdyFace(direction)` | The block has a full supporting surface on that face | | `insideWorldBounds()` | The position is inside the height limits of the world | | `matchingBiomes(biomes)` | The biome is one of the given biomes or in the given biome tag | | `matchingBlockTag(tag)` | The block is in the given block tag | | `matchingBlocks(blocks)` | The block is one of the given blocks or in the given block tag | | `matchingFluids(fluids)` | The fluid is one of the given fluids or in the given fluid tag | | `not { }` | The child predicate does not pass | | `replaceable()` | The block can be replaced by a placed block (air, water, grass) | | `solid()` | The block is solid | | `unobstructed()` | No entity occupies the space of the block | | `wouldSurvive(state)` | The given block state is a valid placement there | ### Blocks, Fluids and Biomes `matchingBlocks`, `matchingFluids` and `matchingBiomes` accept several entries or a tag, and serialize a lone entry as a bare string: ```kotlin predicate { matchingBlocks(Blocks.STONE, Blocks.DEEPSLATE) // "blocks": ["minecraft:stone", "minecraft:deepslate"] } predicate { matchingBlocks(Tags.Block.LOGS) // "blocks": "#minecraft:logs" } predicate { matchingBiomes(Tags.Worldgen.Biome.IS_SAVANNA) // "biomes": "#minecraft:is_savanna" } ``` `matchingBlockTag` is the separate `matching_block_tag` type, which writes the tag without its `#` prefix. ### Would Survive `wouldSurvive` tests a block state as if a player had placed it, which is the reliable way to check that a plant has valid ground: ```kotlin blockPredicateFilter { predicate { wouldSurvive(blockState(Blocks.OAK_SAPLING)) } } ``` ## Where Predicates Are Used | Field | Purpose | |-----------------------------------------------------------------------------------|------------------------------------------------| | `blockPredicateFilter { predicate { } }` | Discards the placement positions failing it | | `environmentScan { targetCondition { } }` | Stops the scan on the first block passing it | | `environmentScan { allowedSearchCondition { } }` | Restricts the blocks the scan may walk through | | `blockBlob / hugeBrownMushroom / hugeRedMushroom / spike { canPlaceOn { } }` | Ground the feature may grow on | | `blockColumn { allowedPlacement { } }` | Blocks the column may be placed in | | `disk { target { } }` | Blocks the disk replaces | | `hugeFungus { replaceableBlocks { } }` | Blocks the fungus may grow through | | `lake { canPlaceFeature / canReplaceWithAirOrFluid / canReplaceWithBarrier { } }` | Lake carving rules | | `rootSystem { allowedTreePosition { } }` | Positions the tree may be placed at | | `spike { canReplace { } }` | Blocks the spike may grow through | | `ruleBasedStateProvider { rule(provider) { } }` | Condition of a block state provider rule | ## See Also - [Features](/docs/data-driven/worldgen/features) - the placement modifiers and providers taking predicates - [Structures](/docs/data-driven/worldgen/structures) - the separate rule tests used by processors - [World Generation](/docs/data-driven/worldgen) - overview of the worldgen system --- ## Carvers --- root: .components.layouts.MarkdownLayout title: Carvers nav-title: Carvers description: Carve caves, nether caves and canyons into Minecraft terrain with Kore - probability, height, radius and debug settings. keywords: minecraft, datapack, kore, worldgen, carver, cave, canyon, ravine date-created: 2026-08-17 date-modified: 2026-08-21 routeOverride: /docs/data-driven/worldgen/carvers --- # Carvers Carvers hollow out terrain after the noise and surface steps but before features, which is why caves never destroy trees or ores placed later. A configured carver pairs a carver type with its configuration and lives in `data//worldgen/configured_carver/.json`. Biomes reference configured carvers by ID. Three carver types exist: - `cave` - Winding tunnel systems, sometimes branching out of a circular room. - `nether_cave` - Same algorithm, wider tunnels, and aquifers do not apply: everything carved below `bottom_y + 32` fills with lava. - `canyon` - Deep ravines with steep walls. References: [Carver definition](https://minecraft.wiki/w/Carver_definition), [Carver](https://minecraft.wiki/w/Carver) ## Declaring Carvers `configuredCarvers` opens a scope where `cave`, `netherCave`, and `canyon` each create exactly one file, so a configured carver can only ever hold the config it was declared with. Every field has a neutral default, so a carver only needs the values that differ from it. ```kotlin fun DataPack.myCarvers() { configuredCarvers { cave("my_cave") { probability = 0.15 y = uniformHeightProvider(aboveBottom(8), absolute(180)) yScale = uniform(0.1f, 0.9f) replaceable(Blocks.STONE, Blocks.DIRT, Tags.Block.BASE_STONE_OVERWORLD) horizontalRadiusMultiplier = uniform(0.7f, 1.4f) verticalRadiusMultiplier = uniform(0.8f, 1.3f) floorLevel = uniform(-1.0f, -0.4f) } } } ``` ```json { "type": "minecraft:cave", "config": { "probability": 0.15, "y": { "type": "minecraft:uniform", "min_inclusive": { "above_bottom": 8 }, "max_inclusive": { "absolute": 180 } }, "yScale": { "type": "minecraft:uniform", "min_inclusive": 0.1, "max_exclusive": 0.9 }, "lava_level": { "absolute": -54 }, "replaceable": ["minecraft:stone", "minecraft:dirt", "#minecraft:base_stone_overworld"], "horizontal_radius_multiplier": { "type": "minecraft:uniform", "min_inclusive": 0.7, "max_exclusive": 1.4 }, "vertical_radius_multiplier": { "type": "minecraft:uniform", "min_inclusive": 0.8, "max_exclusive": 1.3 }, "floor_level": { "type": "minecraft:uniform", "min_inclusive": -1.0, "max_exclusive": -0.4 } } } ``` Each function returns a `ConfiguredCarverArgument`, which is what a biome's `carvers` list takes. To capture that argument outside a `configuredCarvers` block, call the same function on `configuredCarversBuilder`: ```kotlin val cave = configuredCarversBuilder.cave("my_cave") { probability = 0.15 } ``` ## Shared Configuration Every carver type shares these fields: | Field | Type | Default | Meaning | |---------------|--------------------------------------|---------------------------------------|----------------------------------------------------------------------------------| | `probability` | `Double` in `[0, 1]` | `0.1` | Chance for each chunk to attempt a carve. | | `y` | [Height provider](#providers) | `constantAbsolute(0)` | Height at which the carve starts. | | `yScale` | [Float provider](#providers) | `constant(1f)` | Vertical scaling of the carved shape. | | `lavaLevel` | Vertical anchor | `absolute(-54)` | Y level at or below which carved areas fill with lava. Ignored by `nether_cave`. | | `replaceable` | Block IDs and block tags | the carver's vanilla replaceables tag | Blocks the carver is allowed to remove. | `replaceable` defaults to `Tags.Block.OVERWORLD_CARVER_REPLACEABLES` for `cave` and `canyon`, and to `Tags.Block.NETHER_CARVER_REPLACEABLES` for `netherCave`. It serializes as a single string when it holds one entry and as an array otherwise, and the `replaceable` helper replaces the whole list: ```kotlin cave("my_cave") { replaceable(Blocks.STONE, Blocks.DIRT, Tags.Block.BASE_STONE_OVERWORLD) } ``` Aquifers also fill carved areas, and always place lava below Y -54 regardless of `lavaLevel`. ## Cave And Nether Cave `cave` and `netherCave` add three fields on top of the shared ones: | Field | Type | Default | Meaning | |------------------------------|---------------------------------|----------------|-------------------------------------------------------------------------------| | `horizontalRadiusMultiplier` | Float provider | `constant(1f)` | Horizontal scaling of tunnels. Does not change their length. | | `verticalRadiusMultiplier` | Float provider | `constant(1f)` | Vertical scaling of tunnels. Does not change their length. | | `floorLevel` | Float provider in `[-1.0, 1.0]` | `constant(0f)` | `0.0` carves ellipsoids, `1.0` carves upper half-ellipsoids for a flat floor. | ```kotlin netherCave("my_nether_cave") { probability = 0.2 y = uniformHeightProvider(aboveBottom(1), belowTop(1)) lavaLevel = aboveBottom(31) floorLevel = constant(-0.7f) } ``` `netherCave` produces `"type": "minecraft:nether_cave"`. ## Canyon `canyon` adds a vertical rotation and a `shape` object, configured with the `shape` block: | Field | Type | Default | Meaning | |-------------------------------|-------------------------|----------------|------------------------------------------------------------------| | `verticalRotation` | Float provider | `constant(0f)` | Vertical rotation applied as the canyon extends. | | `distanceFactor` | Float provider | `constant(1f)` | Length of the canyon. Higher is longer. | | `thickness` | Float provider | `constant(1f)` | Breadth and height of the canyon. | | `widthSmoothness` | `Int`, greater than `0` | `1` | Smoothing of the walls along the vertical axis. | | `horizontalRadiusFactor` | Float provider | `constant(1f)` | Breadth of the canyon. Higher is wider. | | `verticalRadiusDefaultFactor` | `Float` | `1f` | Depth of the canyon. Higher is deeper. | | `verticalRadiusCenterFactor` | `Float` | `0f` | Extra depth based on horizontal distance from the canyon center. | ```kotlin canyon("my_canyon") { probability = 0.02 y = constantAboveBottom(65) yScale = constant(3.0f) lavaLevel = belowTop(10) verticalRotation = clampedNormal(0.0f, 1.0f, -1.0f, 1.0f) shape { distanceFactor = constant(0.5f) thickness = trapezoid(0.0f, 6.0f, 2.0f) widthSmoothness = 3 horizontalRadiusFactor = uniform(0.75f, 1.0f) verticalRadiusDefaultFactor = 1.0f verticalRadiusCenterFactor = 0.0f } } ``` ## Debug Settings `debugSettings` replaces the blocks a carver would place, which makes the carved volume visible without exploring it. It is the only part of a carver config Minecraft treats as optional, and every state inside it defaults to acacia button. ```kotlin canyon("my_canyon") { debugSettings { debugMode = true airState = blockState(Blocks.ACACIA_BUTTON, "face" to "floor") barrierState = blockState(Blocks.BARRIER) lavaState = blockState(Blocks.ORANGE_STAINED_GLASS) waterState = blockState(Blocks.BLUE_STAINED_GLASS) } } ``` `airState` replaces carved air, `waterState` replaces water and waterlogs the block, `lavaState` replaces lava, and `barrierState` replaces the barrier blocks aquifers generate. ## Providers `lavaLevel` takes a vertical anchor (`absolute(y)`, `aboveBottom(n)`, `belowTop(n)`), `y` takes a height provider wrapping anchors into a distribution, and every float field takes a float provider. All three families are scoped to the carver block, so they resolve inside `cave { }`, `netherCave { }` and `canyon { }`: ```kotlin cave("my_cave") { y = uniformHeightProvider(aboveBottom(8), absolute(180)) lavaLevel = belowTop(10) yScale = uniform(0.1f, 0.9f) floorLevel = constant(-0.2f) } ``` See [Providers](/docs/data-driven/worldgen/providers) for the full list of distributions. ## Using Carvers In A Biome A biome's `carvers` field is a flat list of configured carver IDs, or a single carver tag: ```kotlin val cave = configuredCarversBuilder.cave("highlands_cave") { probability = 0.08 } biome("highlands") { carvers(cave, ConfiguredCarvers.CANYON) } ``` ```json { "carvers": ["mypack:highlands_cave", "minecraft:canyon"] } ``` `ConfiguredCarvers` lists the vanilla configured carvers: `CANYON`, `CAVE`, `CAVE_EXTRA_UNDERGROUND` and `NETHER_CAVE`. The field also accepts a `ConfiguredCarverTagArgument`, though vanilla ships no carver tag. ## See Also - [Biomes](/docs/data-driven/worldgen/biomes) - listing the carvers a biome runs - [Noise & Terrain](/docs/data-driven/worldgen/noise) - the terrain shape the carvers cut through - [Providers](/docs/data-driven/worldgen/providers) - height and float providers - [World Generation](/docs/data-driven/worldgen) - overview of the worldgen system --- ## Dimensions --- root: .components.layouts.MarkdownLayout title: Dimensions nav-title: Dimensions description: Create custom Minecraft dimensions and dimension types with Kore - world bounds, lighting rules, terrain generators and biome sources. keywords: minecraft, datapack, kore, worldgen, dimension, dimension type, generator, biome source, multi noise, superflat date-created: 2026-02-03 date-modified: 2026-09-23 routeOverride: /docs/data-driven/worldgen/dimensions --- # Dimensions A dimension is a separate world, reachable with `/execute in :` or through a portal. It is always two things put together: - A **dimension type** - the rules of the world: vertical bounds, lighting, time flow, and the environment attributes applying everywhere in it. Written to `data//dimension_type/.json`. - A **generator** - how the terrain is built, and which biomes go where. Written inline in the dimension file itself. ```kotlin val myDimType = dp.dimensionType("my_dim_type") { minY = -64 height = 384 hasSkylight = true } val myDim = dp.dimension("my_dimension", type = myDimType) { noiseGenerator(settings = myNoiseSettings, biomeSource = fixed(myBiome)) } ``` A datapack loaded into an existing world adds its dimensions on the next load; to replace the Overworld, the Nether or the End instead, use a [world preset](/docs/data-driven/worldgen/world-presets). References: [Dimension](https://minecraft.wiki/w/Dimension), [Custom dimension](https://minecraft.wiki/w/Custom_dimension) --- ## Dimension Type Every property defaults to its vanilla Overworld value, so a dimension type only declares what it changes. | Property | Default | Description | |-------------------------------|-------------------------|-----------------------------------------------------------------------| | `ambientLight` | `0f` | Minimum light level everywhere, from `0` to `1`. | | `attributes` | none | Environment attributes: visual, audio and gameplay rules. | | `cardinalLight` | `DEFAULT` | `CardinalLight.DEFAULT` or `NETHER`, which flattens the shading. | | `coordinateScale` | `1.0` | Coordinate multiplier when travelling to the dimension. | | `defaultClock` | none | World clock driving the day cycle, none freezing the time. | | `hasCeiling` | `false` | Whether the world has a bedrock ceiling. | | `hasEnderDragonFight` | `false` | Whether the Ender Dragon fight can happen in the world. | | `hasFixedTime` | `false` | Whether the time of day is frozen. | | `hasSkylight` | `true` | Whether the sky lights the world. | | `height` | `384` | Total height, a multiple of `16`, from `16` to `4064`. | | `infiniburn` | `#infiniburn_overworld` | Block tag listing the blocks that burn forever. | | `logicalHeight` | `384` | How high chorus fruits and portals bring a player, at most `height`. | | `minY` | `-64` | Lowest buildable Y, a multiple of `16`, from `-2032` to `2031`. | | `monsterSpawnBlockLightLimit` | `0` | Block light level at or below which monsters spawn, from `0` to `15`. | | `monsterSpawnLightLevel` | `constant(0)` | Sky light levels at which monsters spawn, from `0` to `15`. | | `skybox` | `OVERWORLD` | `SkyboxType.NONE`, `OVERWORLD` or `END`. | | `timelines` | none | List of [timelines](/docs/data-driven/timelines) or timeline tags. | Everything else about how a world feels is an [environment attribute](/docs/data-driven/worldgen/environment-attributes): lava spreading fast, water evaporating, piglins zombifying, beds exploding, raids starting. ```kotlin val netherLike = dp.dimensionType("nether_like") { minY = 0 height = 128 logicalHeight = 128 hasCeiling = true hasSkylight = false hasFixedTime = true ambientLight = 0.1f cardinalLight = CardinalLight.NETHER infiniburn = listOf(Tags.Block.INFINIBURN_NETHER) attributes { fastLava(true) waterEvaporates(true) piglinsZombify(false) respawnAnchorWorks(true) bedRule(canSleep = BedSleepRule.NEVER, canSetSpawn = BedSleepRule.NEVER, explodes = true) } } ``` Reference: [Dimension type](https://minecraft.wiki/w/Dimension_type) --- ## Generators A dimension declares exactly one generator, picked with one of the three builders below. Without a call to any of them, the dimension generates the debug world. ### Noise Generator The standard terrain generator, used by all three vanilla dimensions. It pairs **noise settings** (the terrain algorithm, see [Noise & Terrain](/docs/data-driven/worldgen/noise)) with a **biome source** (which biome is where). ```kotlin dimension("my_dimension", type = myDimType) { noiseGenerator(settings = NoiseSettings.OVERWORLD, biomeSource = multiNoise(BiomePresets.OVERWORLD)) } ``` #### Biome Sources | Builder | Places biomes by | |--------------------------------------|--------------------------------------------------------------------------| | `fixed(biome)` | One biome everywhere, the simplest option. | | `checkerboard(scale, vararg biomes)` | A square grid, each square `2^scale` chunks wide (`scale` defaults `2`). | | `multiNoise(preset)` | A parameter list: `BiomePresets.OVERWORLD`, `NETHER`, or your own one. | | `multiNoise { }` | Your own biomes, each claiming a range in the 6D climate space. | | `theEnd()` | The hardcoded End layout: central island, void, outer islands. | `multiNoise` entries carry the six climate parameters (plus an `offset` acting as a distance penalty). Each takes either a single value or a `min to max` pair; unset parameters default to `0.0`, so an entry only declares the axes it actually cares about. ```kotlin noiseGenerator( settings = myNoiseSettings, biomeSource = multiNoise { add(multiNoiseEntry(coldPeaks) { temperature = doubleOrPair(-1.0, -0.4) continentalness = doubleOrPair(0.3, 1.0) erosion = doubleOrPair(-1.0, -0.4) }) add(multiNoiseEntry(warmPlains) { temperature = doubleOrPair(0.2, 1.0) humidity = doubleOrPair(-0.3, 0.3) }) }, ) ``` #### Multi-Noise Parameter Lists A parameter list gives a vanilla biome layout its own id, written to `data//worldgen/multi_noise_biome_source_parameter_list/.json`. Its `preset` is either `BiomePresets.OVERWORLD` or `NETHER`, and any `multiNoise(preset)` biome source can point to it. ```kotlin val skylandsBiomes = dp.multiNoiseBiomeSourceParameterList("skylands", BiomePresets.OVERWORLD) dp.dimension("skylands", type = myDimType) { noiseGenerator(settings = NoiseSettings.FLOATING_ISLANDS, biomeSource = multiNoise(skylandsBiomes)) } ``` References: [Biome source](https://minecraft.wiki/w/Biome_source), [Multi-noise biome source parameter list](https://minecraft.wiki/w/Multi-noise_biome_source_parameter_list) ### Flat Generator Builds a superflat world out of block layers. Useful for test worlds, creative builds and minigame dimensions. ```kotlin dimension("flat_world", type = myDimType) { flatGenerator(biome = Biomes.PLAINS) { layers { layer(Blocks.BEDROCK) layer(Blocks.DIRT, height = 2) layer(Blocks.GRASS_BLOCK) } structureOverrides(StructureSets.VILLAGES) features = true lakes = false } } ``` | Field | Type | Default | Description | |----------------------|-------------------------|---------------------|-----------------------------------------------------| | `biome` | Biome | mandatory | The biome every chunk of the world uses. | | `features` | `Boolean?` | `false` | Whether the placed features of the biome generate. | | `lakes` | `Boolean?` | `false` | Whether lava lakes generate. | | `layers` | `List` | empty | Block layers, read from the bottom of the world up. | | `structureOverrides` | Structure sets or a tag | every structure set | The only structure sets allowed to generate. | Layers can be declared three interchangeable ways: `layer(block, height)` appends one to whatever is already there, `layers(...)` takes them as varargs, and `layers { }` builds the whole stack at once. `height` defaults to `1` and goes up to `4064`. `structureOverrides` takes structure sets as varargs, a builder block, or a single structure set tag. The same settings drive the [flat level generator presets](/docs/data-driven/worldgen/world-presets#flat-level-generator-preset) offered in the superflat customization screen. Reference: [Superflat](https://minecraft.wiki/w/Superflat) ### Debug Generator A flat grid of every block state in the game, at Y 70, on a barrier plane. A development tool, not a playable world. ```kotlin dimension("debug_world", type = myDimType) { debugGenerator() } ``` Reference: [Debug mode](https://minecraft.wiki/w/Debug_mode) ## See Also - [Biomes](/docs/data-driven/worldgen/biomes) - what a biome source distributes - [Environment Attributes](/docs/data-driven/worldgen/environment-attributes) - the rules a dimension type sets for the whole world - [Noise & Terrain](/docs/data-driven/worldgen/noise) - the noise settings a noise generator points at - [World Generation](/docs/data-driven/worldgen) - a complete custom dimension, end to end - [World Presets](/docs/data-driven/worldgen/world-presets) - offering a set of dimensions as a world type --- ## Environment Attributes --- root: .components.layouts.MarkdownLayout title: Environment Attributes nav-title: Environment Attributes description: Data-driven visual, audio and gameplay rules for Minecraft biomes and dimensions - fog, sky, music, mob behavior - with Kore's DSL. keywords: minecraft, datapack, kore, worldgen, environment attributes, biome, dimension type, fog, sky color, music, modifiers date-created: 2026-02-09 date-modified: 2026-08-21 routeOverride: /docs/data-driven/worldgen/environment-attributes --- # Environment Attributes Environment attributes are the data-driven knobs controlling how a place in the world looks, sounds and behaves. Each one covers a single effect: `visual/sky_color` is the color of the sky, `gameplay/water_evaporates` decides whether a bucket can place water. Both [biomes](/docs/data-driven/worldgen/biomes) and [dimension types](/docs/data-driven/worldgen/dimensions) declare them in an `attributes { }` block, and both use the exact same builders. Everything the removed `effects`, `natural`, `ultrawarm`, `piglin_safe`, `bed_works` and `has_raids` fields used to control lives here. ```kotlin attributes { skyColor(0x78A7FF) fogEndDistance(192.0f) waterEvaporates(true) } ``` References: [Biome definition](https://minecraft.wiki/w/Biome_definition), [Dimension type](https://minecraft.wiki/w/Dimension_type) --- ## Priority, Modifiers, Interpolation A dimension type provides the base value for the whole world, a biome overrides it locally. Setting `sky_color` to green on the dimension and to red on the plains biome gives a red sky in the plains, green everywhere else. By default a value **overrides** whatever the lower-priority source provided. Pass a modifier as the second argument to combine with it instead, and the JSON expands from a bare value to `{ "argument": ..., "modifier": ... }`: ```kotlin attributes { skyColor(0x78A7FF) // Override, the default. fogEndDistance(0.85f, EnvironmentAttributeModifier.MULTIPLY) // 85% of the dimension value. } ``` Which modifiers apply depends on the value type of the attribute: | Value type | Available modifiers | |-------------|-------------------------------------------------------------------------| | **Boolean** | `OVERRIDE`, `AND`, `NAND`, `OR`, `NOR`, `XOR`, `XNOR` | | **Float** | `OVERRIDE`, `ADD`, `SUBTRACT`, `MULTIPLY`, `MINIMUM`, `MAXIMUM` | | **Color** | `OVERRIDE`, `ADD`, `SUBTRACT`, `MULTIPLY`, `ALPHA_BLEND`, `BlendToGray` | | **Object** | `OVERRIDE` only | `ALPHA_BLEND` takes an ARGB argument and does traditional alpha blending; an alpha of `1` behaves as an override. `BlendToGray` is the one modifier carrying data of its own: ```kotlin fogColor(rgb(255, 170, 0), EnvironmentAttributeModifier.BlendToGray(brightness = 0.5f, factor = 0.8f)) ``` ```json { "argument": 16755200, "modifier": { "type": "blend_to_gray", "brightness": 0.5, "factor": 0.8 } } ``` Attributes marked **interpolated** in the tables below fade smoothly as the player crosses a biome border, sampling the biomes within 8 blocks of the camera. The others snap to the biome at the exact position. --- ## Visual | Builder | Value | Default | Interp. | Resolved at | Controls | |----------------------------------|----------------|-----------------|---------|------------------|------------------------------------------------------------------| | `ambientLightColor(color)` | RGB color | `#000000` | Yes | Camera | Tint of the scene's ambient illumination. | | `ambientParticles(...)` | Particle list | `[]` | No | Camera | Particles randomly spawning around the camera. | | `blockLightTint(color)` | RGB color | `#000000` | Yes | Camera | Tint applied to block-emitted light. | | `cloudColor(argb)` | ARGB color | `#00000000` | Yes | Camera | Color of the clouds. | | `cloudFogEndDistance(float)` | Float, >= 0 | `1024.0` | Yes | Camera | Distance in blocks at which cloud fog ends. | | `cloudHeight(float)` | Float | `192.33` | Yes | Camera | Height all clouds are drawn at. | | `defaultDripstoneParticle(type)` | Particle | dripstone water | No | Dripstone block | Particle dripped when no fluid sits above the dripstone. | | `fogColor(color)` | RGB color | `#000000` | Yes | Camera | Fog color out of water. Time, weather and potions affect it too. | | `fogEndDistance(float)` | Float, >= 0 | `1024.0` | Yes | Camera | Distance in blocks at which fog ends. | | `fogStartDistance(float)` | Float, >= 0 | `0.0` | Yes | Camera | Distance in blocks at which fog starts. | | `moonAngle(float)` | Float, degrees | `0.0` | Yes | Overworld camera | Angle of the moon. | | `moonPhase(texture)` | Moon phase | `full_moon` | No | Overworld camera | Which moon phase texture is drawn. | | `nightVisionColor(color)` | RGB color | `#000000` | Yes | Camera | Tint applied under the Night Vision effect. | | `skyColor(color)` | RGB color | `#000000` | Yes | Camera | Sky color, Overworld sky only. Time and weather affect it too. | | `skyFogEndDistance(float)` | Float, >= 0 | `512.0` | Yes | Camera | Distance in blocks at which sky fog ends. | | `skyLightColor(color)` | RGB color | - | Yes | Camera | Tint applied to sky light. | | `skyLightFactor(float)` | Float | - | Yes | Camera | How strongly sky light contributes to the scene. | | `starAngle(float)` | Float, degrees | - | Yes | Overworld camera | Rotation of the star field. | | `starBrightness(float)` | Float | - | Yes | Overworld camera | Brightness of the stars. | | `sunAngle(float)` | Float, degrees | - | Yes | Overworld camera | Angle of the sun. | | `sunriseSunsetColor(argb)` | ARGB color | - | Yes | Overworld camera | Color of the sunrise and sunset glow. | | `waterFogColor(color)` | RGB color | `#050533` | Yes | Camera | Fog color while submerged in water. | | `waterFogEndDistance(float)` | Float | `-8.0` | Yes | Camera | Distance in blocks at which underwater fog ends. | | `waterFogStartDistance(float)` | Float | `0.0` | Yes | Camera | Distance in blocks at which underwater fog starts. | ```kotlin attributes { skyColor(Color.RED) fogColor(rgb(255, 170, 0), EnvironmentAttributeModifier.ADD) cloudColor(ARGB(255, 128, 64, 32)) blockLightTint(rgb(255, 180, 80), EnvironmentAttributeModifier.MULTIPLY) moonPhase(Textures.Environment.Celestial.Moon.FULL_MOON) defaultDripstoneParticle(Particles.DRIPPING_LAVA) } ``` Color builders take an `Int` hex literal, a `Color`, or an `rgb(r, g, b)` / `ARGB(a, r, g, b)` call. See [Colors](/docs/concepts/colors). ### Ambient Particles `ambientParticles` takes `Particle` entries as varargs or a list, each pairing a particle type with the chance to spawn it in an empty space. The `entry(...)` helper inside the block is the shortest form. ```kotlin attributes { ambientParticles { entry(Particles.ASH, probability = 0.01f) entry(Particles.WHITE_ASH, probability = 0.005f) } } ``` --- ## Audio | Builder | Value | Default | Interp. | Resolved at | Controls | |----------------------------|-----------------|---------|---------|--------------|------------------------------------------------------| | `ambientSounds(...)` | Object | `{}` | No | Camera | Looping, mood and random ambient sounds. | | `backgroundMusic(...)` | Object | `{}` | No | Camera | Which background music plays, and how often. | | `fireflyBushSounds(bool)` | Boolean | `false` | No | Firefly bush | Whether firefly bushes make noise. | | `musicVolume(float)` | Float, 0 to 1 | `1.0` | No | Camera | Target volume music fades to. | ### Ambient Sounds - `loop` - a sound continually looped. - `mood { }` - the cave-ambience style sound: `sound`, `tickDelay` (default `6000`), `blockSearchExtent` (default `8`), `offset` (default `2.0`). - `addition(sound, tickChance)` - a sound with a per-tick chance to play, repeatable. ```kotlin attributes { ambientSounds(loop = SoundEvents.Ambient.CAVE) { mood(sound = SoundEvents.Ambient.CAVE, tickDelay = 6000) addition(SoundEvents.Ambient.CAVE, 0.01f) } } ``` ### Background Music Three tracks, each taking a `sound`, a `minDelay`/`maxDelay` range in ticks, and an optional `replaceCurrentMusic`. `creative` and `underwater` override `default` in their respective situations. ```kotlin attributes { backgroundMusic { default(sound = SoundEvents.Music.GAME, minDelay = 12000, maxDelay = 24000) creative(sound = SoundEvents.Music.CREATIVE, minDelay = 100, maxDelay = 200) underwater(sound = SoundEvents.Music.UNDER_WATER, minDelay = 100, maxDelay = 200, replaceCurrentMusic = true) } } ``` --- ## Gameplay | Builder | Value | Default | Interp. | Resolved at | Controls | |----------------------------------|--------------------------|-------------|---------|--------------------|------------------------------------------------------------------| | `babyVillagerActivity(activity)` | Activity | `idle` | No | Villager | What baby villagers do. | | `bedRule(...)` | Object | `when_dark` | No | Bed head | Whether a bed lets you sleep, set spawn, or explodes. | | `beesStayInHive(bool)` | Boolean | `false` | No | Hive | Whether bees stay inside their hive. | | `canPillagerPatrolSpawn(bool)` | Boolean | `true` | No | Patrol spawn | Whether pillager patrols spawn. | | `canStartRaid(bool)` | Boolean | `true` | No | Raid start | Whether Raid Omen can start a raid. | | `catWakingUpGiftChance(float)` | Float | `0.0` | Yes | Cat | Chance for a cat to leave a waking-up gift. | | `creakingActive(bool)` | Boolean | `false` | No | Creaking | Whether the Creaking is active. | | `eyeblossomOpen(state)` | `TRUE`/`FALSE`/`DEFAULT` | `DEFAULT` | No | Eyeblossom | Whether eyeblossoms are open. | | `fastLava(bool)` | Boolean | `false` | No | Whole dimension | Lava spreading faster, further, and pushing harder. | | `increasedFireBurnout(bool)` | Boolean | `false` | No | Burning fire block | Whether fire blocks burn out faster. | | `monstersBurn(bool)` | Boolean | `false` | No | Mob | Whether monsters burn in sunlight. | | `netherPortalSpawnsPiglin(bool)` | Boolean | `false` | No | Nether portal | Whether nether portal blocks spawn piglins. | | `piglinsZombify(bool)` | Boolean | `true` | No | Zombifying entity | Whether piglins and hoglins zombify. | | `respawnAnchorWorks(bool)` | Boolean | `false` | No | Respawn anchor | Whether a respawn anchor sets spawn instead of exploding. | | `skyLightLevel(float)` | Float, 0 to 15 | `15.0` | No | Whole dimension | Sky light level of the dimension. | | `snowGolemMelts(bool)` | Boolean | `false` | No | Snow golem | Whether snow golems take damage. | | `surfaceSlimeSpawnChance(float)` | Float | `0.0` | Yes | Spawn position | Chance for surface slimes to spawn. | | `turtleEggHatchChance(float)` | Float | `0.0` | Yes | Turtle egg | Chance for turtle eggs to hatch. | | `villagerActivity(activity)` | Activity | `idle` | No | Villager | What villagers do. | | `waterEvaporates(bool)` | Boolean | `false` | No | Interaction | Buckets failing, ice melting dry, sponges drying, dripstone dry. | `fastLava` and `skyLightLevel` resolve for the whole dimension and cannot be set on a biome. ```kotlin attributes { fastLava(true) waterEvaporates(true, EnvironmentAttributeModifier.OR) piglinsZombify(false) villagerActivity(Activities.WORK) eyeblossomOpen(EyeblossomOpenState.TRUE) } ``` ### Bed Rule `canSleep` and `canSetSpawn` each take `BedSleepRule.ALWAYS`, `WHEN_DARK` or `NEVER`. `explodes` makes the bed detonate on interaction, and `errorMessage` replaces the message shown when sleeping is refused. ```kotlin attributes { bedRule( canSleep = BedSleepRule.NEVER, canSetSpawn = BedSleepRule.NEVER, explodes = true, errorMessage = textComponent("The beds here do not like you."), ) } ``` ## See Also - [Biomes](/docs/data-driven/worldgen/biomes) - per-biome attribute overrides - [Colors](/docs/concepts/colors) - the RGB and ARGB formats used by color attributes - [Dimensions](/docs/data-driven/worldgen/dimensions) - dimension-wide base attributes - [Timelines](/docs/data-driven/timelines) - animating attributes over time with keyframes and easing - [World Generation](/docs/data-driven/worldgen) - overview of the worldgen system --- ## Features --- root: .components.layouts.MarkdownLayout title: Features nav-title: Features description: Generate trees, ores and vegetation in Minecraft with Kore - configured features, placement modifiers and block state providers. keywords: minecraft, datapack, kore, worldgen, configured feature, placed feature, tree, ore, block state provider date-created: 2026-02-03 date-modified: 2026-10-08 routeOverride: /docs/data-driven/worldgen/features --- # Features Features are everything the game scatters over the terrain during the `features` step: trees, ores, flowers, geodes, springs. They are declared in two files: - A **configured feature** says *what* to place - the tree species, the ore type, the block - with all its parameters. - A **placed feature** wraps a configured feature and says *where* and *how often* - count per chunk, height range, rarity, filters. The split is what lets one tree configuration be placed densely in a forest and sparsely in plains. A biome only ever references placed features, listed per [decoration step](/docs/data-driven/worldgen#decoration-steps). References: [Configured feature](https://minecraft.wiki/w/Configured_feature), [Placed feature](https://minecraft.wiki/w/Placed_feature) --- ## Configured Features Each feature type is a function on `configuredFeaturesBuilder`, taking the file name first. One call produces one file and returns its `FeatureArgument`, so a configured feature always holds exactly one feature type. Declare several at once inside a `configuredFeatures { }` block, or call `configuredFeaturesBuilder` directly to capture the argument: ```kotlin val flower = dp.configuredFeaturesBuilder.simpleBlock("my_flower") { toPlace = simpleStateProvider(Blocks.DANDELION) } dp.configuredFeatures { seagrass("my_seagrass", probability = 0.3) desertWell("my_well") } ``` Each file lands in `data//worldgen/feature/.json`, its fields next to `type`: ```json { "type": "minecraft:seagrass", "probability": 0.3 } ``` ### Tree Trees combine a trunk placer, a foliage placer and block state providers for the blocks themselves. `belowTrunkProvider` takes a `ruleBasedStateProvider` and defaults to an empty one, which places nothing under the trunk. ```kotlin tree("my_tree") { blobFoliagePlacer(radius = constant(2), offset = constant(0), height = 3) foliageProvider = simpleStateProvider(Blocks.OAK_LEAVES) straightTrunkPlacer(baseHeight = 6, heightRandA = 3, heightRandB = 1) trunkProvider = simpleStateProvider(Blocks.OAK_LOG) belowTrunkProvider = ruleBasedStateProvider { fallback = simpleStateProvider(Blocks.DIRT) rule(simpleStateProvider(Blocks.GRASS_BLOCK)) { hasSturdyFace(Direction.DOWN) } } } ``` Every vanilla trunk placer, foliage placer and decorator has its builder, like the poplar ones: a straight trunk branching where the rhombus-shaped foliage starts, with shelf mushrooms on the logs. ```kotlin tree("my_poplar") { poplarTrunkPlacer { baseHeight = 7 branchAmount = uniform(1, 4) trunkHeightAboveBranches = constant(4) } poplarFoliagePlacer { height = uniform(5, 6) sideHoleChance = 0.15 } decorators { shelfMushroom(0.4) } } ``` ### Ore Ore features replace existing terrain blocks. `size` is the maximum vein size, and `discardChanceOnAirExposure` skips blocks exposed to air (`0.0` for full veins, `1.0` to never show an ore in a cave wall). ```kotlin ore("my_ore", size = 10, discardChanceOnAirExposure = 0.1) { targets { target(blockState(Blocks.IRON_ORE)) { target = tagMatch(Tags.Block.STONE_ORE_REPLACEABLES) } target(blockState(Blocks.DEEPSLATE_IRON_ORE)) { target = tagMatch(Tags.Block.DEEPSLATE_ORE_REPLACEABLES) } } } ``` `targets { }` is shared by `ore`, `scatteredOre` and `replaceSingleBlock`, the three features replacing terrain blocks; the first target matching a block wins. The rule test builders (`blockMatch`, `tagMatch`, `randomBlockMatch`, ...) are scoped to `target { }` and are the same ones the [structure processors](/docs/data-driven/worldgen/structures#rules) use. Vanilla bounds each ore target in height by combining them: ```kotlin target(blockState(Blocks.IRON_ORE)) { target = allOf(tagMatch(Tags.Block.STONE_ORE_REPLACEABLES), heightMatch(minInclusive = 0, maxInclusive = 2031)) } ``` Reference: [Ore feature](https://minecraft.wiki/w/Ore_(feature)) ### Block Column `blockColumn` stacks layers along a direction, each declared inside `layers { }` with its height and its provider. ```kotlin blockColumn("cave_vines", direction = Direction.DOWN) { allowedPlacement { replaceable() } layers { layer(constant(3)) { provider = simpleStateProvider(Blocks.CAVE_VINES_PLANT) } layer(constant(1)) { provider = simpleStateProvider(Blocks.CAVE_VINES) } } } ``` ### All Feature Types Kore covers every vanilla configured feature type. | Builder | Places | Vanilla use | |-----------------------------------|--------------------------------------|--------------------------------| | `bamboo(probability)` | Bamboo stalks | Jungle bamboo | | `basaltColumns(reach, height)` | Paired basalt pillar columns | Nether basalt deltas | | `blockBlob(...)` | Small block blob on a surface | Mossy cobblestone in forests | | `blockColumn(...)` | Vertical stack of blocks with layers | Custom pillars | | `blockPile(...)` | Piles of blocks | Pumpkin/melon patches | | `deltaFeature(...)` | Basalt delta with contents and rim | Nether basalt deltas | | `disk(...)` | Circular disk of blocks | Clay, sand, gravel patches | | `endGateway(...)` | End gateway portal | End outer islands | | `endPodium(active)` | End exit portal podium | Dragon fight portal | | `endSpike(...)` | End obsidian pillar with crystal | The End respawn pillars | | `fillLayer(...)` | A full layer of blocks | Custom dimension layers | | `fossil(...)` | Structure-based fossil | Underground fossils | | `geode(...)` | Hollow structure with layered shells | Amethyst geodes | | `hugeBrownMushroom(...)` | Large brown mushroom | Swamp/mushroom island fungi | | `hugeFungus(...)` | Huge nether fungus | Crimson/warped forests | | `hugeRedMushroom(...)` | Large red mushroom | Swamp/mushroom island fungi | | `iceberg(...)` | Iceberg | Frozen ocean icebergs | | `lake(...)` | Liquid pool | Underground lava lakes | | `largeDripstone(...)` | Tall stalactite or stalagmite | Cave ceilings/floors | | `multifaceGrowth(block, ...)` | Multi-face block spread | Glow lichen, sculk vein | | `netherForestVegetation(...)` | Nether plant scatter | Warped/crimson forest floors | | `netherrackReplaceBlobs(...)` | Blobs replacing netherrack | Nether gravel/blackstone blobs | | `ore(...)` | Ore vein replacing terrain | Every overworld ore | | `randomBooleanSelector(...)` | One of two features, at random | Symmetric ore variants | | `randomSelector(...)` | Weighted random feature picker | Mixed ore deposits | | `replaceSingleBlock(...)` | Blocks replaced by rule targets | Custom block swaps | | `rootSystem(...)` | Roots under a tree | Mangrove roots | | `scatteredOre(...)` | Scattered ore deposits | Nether gold ore blobs | | `sculkPatch(...)` | Sculk spread with catalyst | Ancient city surroundings | | `seaPickle(count)` | Sea pickle colonies | Warm ocean floors | | `seagrass(probability)` | Seagrass | Ocean floors | | `sequence(...)` | Several features, in order | Multi-step feature chains | | `simpleBlock(...)` | A single block state | Flowers, mushrooms | | `simpleRandomSelector(...)` | Uniform random feature picker | Coral type variety | | `speleothem(...)` | Single pointed speleothem | Cave stalactites/stalagmites | | `speleothemCluster(...)` | Dense speleothem growth | Cave speleothem rooms | | `spike(...)` | Tall spiky columns | Ice spikes in frozen biomes | | `springFeature(...)` | Fluid source block | Water/lava springs | | `template(...)` | A weighted structure template | Random structure variants | | `tree(...)` | A tree, trunk and foliage | Every vanilla tree | | `twistingVines(...)` | Twisting vine growth | Warped forest floors | | `underwaterMagma(...)` | Underwater magma blocks | Ocean floors | | `vegetationPatch(...)` | Vegetation on surfaces | Cave moss patches | | `waterloggedVegetationPatch(...)` | Waterlogged vegetation patches | Underwater cave plants | | `weightedRandomSelector(...)` | Weighted random feature picker | Mixed ore/vegetation deposits | ### Features Without Configuration Eighteen feature types have no fields at all. Their builders take only a file name and nothing else: ```kotlin configuredFeatures { desertWell("my_well") monsterRoom("my_dungeon") } ``` `basaltPillar`, `blueIce`, `bonusChest`, `chorusPlant`, `coralClaw`, `coralMushroom`, `coralTree`, `desertWell`, `endIsland`, `endPlatform`, `freezeTopLayer`, `glowstoneBlob`, `kelp`, `monsterRoom`, `noOp`, `vines`, `voidStartPlatform`, `weepingVines`. Reference: [Feature types](https://minecraft.wiki/w/Configured_feature#Types) --- ## Block State Providers A block state provider decides which block state is placed at a position. It fills every field typed `BlockStateProvider`: `toPlace` on `simpleBlock`, `stateProvider` on `disk`, `trunkProvider` and `foliageProvider` on `tree`, and so on. Every builder extends `BlockStateProviderScope`, so they resolve inside the configuration block of a feature actually taking a provider, and nowhere else. Providers nest freely: any of them can be the `source` of a `randomizedIntStateProvider`, the `data` of a weighted entry or the `then` of a rule. | Builder | Picks the state from | |--------------------------------------------|-------------------------------------------------------------------| | `dualNoiseProvider { }` | Two Perlin noises, one selecting the variety, one picking a state | | `noiseProvider { }` | A single Perlin noise mapped onto a list of states | | `noiseThresholdProvider { }` | A Perlin noise compared against a threshold | | `randomizedIntStateProvider(property) { }` | Another provider, with one integer property randomized | | `rotatedBlockProvider(block)` | A fixed block with a random `axis` | | `ruleBasedStateProvider { }` | Ordered block predicate rules, with an optional fallback | | `simpleStateProvider(block)` | A fixed block | | `weightedStateProvider { }` | A weighted random pick across several providers | Reference: [Block state provider](https://minecraft.wiki/w/Block_state_provider) ### Simple And Rotated Both take a block, a block plus a property map, or a block plus a block state builder. `rotatedBlockProvider` ignores the `axis` of the state it is given and randomizes it at every position. ```kotlin simpleStateProvider(Blocks.DANDELION) simpleStateProvider(Blocks.OAK_LOG, mapOf("axis" to "y")) rotatedBlockProvider(Blocks.OAK_LOG) ``` ### Weighted Entries are declared inside the provider block, each drawn proportionally to its weight, which defaults to `1`. An entry takes a block, a block state, another provider, or a receiver block setting `data` itself. ```kotlin weightedStateProvider { entry(Blocks.DANDELION, weight = 3) entry(Blocks.POPPY) entry(weight = 2) { data = rotatedBlockProvider(Blocks.OAK_LOG) } } ``` ### Noise-Based The three noise-based providers share `seed`, `scale` and a `noise { }` block. `noise(firstOctave, vararg amplitudes)` is the short form and `noise(firstOctave) { }` the receiver form; `dualNoiseProvider` gets the same pair as `slowNoise`. `states(...)` takes either blocks or block states. ```kotlin noiseProvider { seed = 2345 scale = 0.05 noise(-3, 1.0, 1.0) states(Blocks.MOSS_BLOCK, Blocks.STONE) } dualNoiseProvider { seed = 2345 scale = 0.05 slowScale = 0.005 noise(-3, 1.0) slowNoise(-10, 1.0, 1.0) variety(1, 3) // How many states are in play at a position. states(Blocks.CRIMSON_ROOTS, Blocks.WARPED_ROOTS) } noiseThresholdProvider { seed = 2345 scale = 0.05 threshold = -0.8 highChance = 0.31 noise(-3, 1.0) defaultState = blockState(Blocks.GRASS_BLOCK) lowStates(Blocks.PODZOL) highStates(Blocks.COARSE_DIRT) } ``` ### Randomized Int State Takes the state given by `source` and overrides one integer block state property with a sampled value. The block placed by `source` has to declare that property. ```kotlin randomizedIntStateProvider("age") { values(0, 7) // Shorthand for a uniform int provider. source = simpleStateProvider(Blocks.WHEAT) } ``` ### Rule Based Rules are evaluated top-to-bottom and the first matching one wins, falling back to `fallback` when nothing matches. With no `fallback` and no matching rule, the feature places nothing. ```kotlin ruleBasedStateProvider { fallback = simpleStateProvider(Blocks.STONE) // The provider first, the predicate in the trailing lambda. rule(simpleStateProvider(Blocks.DIRT)) { solid() } // Or a receiver block, with ifTrue { } and then. rule { ifTrue { not { matchingBlockTag(Tags.Block.CANNOT_REPLACE_BELOW_TREE_TRUNK) } solid() } then = simpleStateProvider(Blocks.SAND) } } ``` Both predicate blocks run on a block predicate scope: a single predicate is used as-is, several are wrapped in an `all_of`. See [Block Predicates](/docs/data-driven/worldgen/block-predicates) for the full list. --- ## Placed Features A placed feature wraps a configured feature with **placement modifiers**, applied in order. Each modifier filters or transforms the stream of candidate positions, so the order matters: `count()` multiplies positions, `heightRange()` moves them vertically, `biome()` drops the ones that ended up outside the biome. ```kotlin val treePlaced = dp.placedFeature("my_tree_placed", treeConfigured) { inSquare() count(constant(10)) heightRange(uniformHeightProvider(64, 128)) biome() } ``` The usual shape is: pick a frequency, spread horizontally, position vertically, then filter. `biome()` belongs last, since it has to see the final position. | Modifier | Effect | |---------------------------------------|-----------------------------------------------------| | `biome()` | Drops positions outside a biome listing the feature | | `blockPredicateFilter { }` | Drops positions failing a block predicate | | `carvingMask(step)` | Keeps only blocks carved by `air` or `liquid` | | `count(n)` | Repeats the position `n` times per chunk | | `countOnEveryLayer(n)` | Repeats `n` times on every solid layer | | `environmentScan(...)` | Scans up or down for a valid position | | `fixedPlacement(...)` | Replaces positions with absolute coordinates | | `heightMap(type)` | Moves the position onto a heightmap | | `heightRange(provider)` | Moves the position to a sampled Y level | | `inSquare()` | Randomizes X and Z inside the chunk | | `noiseBasedCount(...)` | Count driven by the noise value at the position | | `noiseThresholdCount(...)` | One of two counts, chosen by a noise threshold | | `randomOffset(xzSpread, ySpread)` | Scatters the position within an XZ/Y radius | | `rarityFilter(chance)` | Keeps the position with a `1/chance` probability | | `surfaceRelativeThresholdFilter(...)` | Filters on the distance to the surface | | `surfaceWaterDepthFilter(maxDepth)` | Drops positions under deeper water than `maxDepth` | `heightRange` and `count` take a height provider and an int provider; see [Providers](/docs/data-driven/worldgen/providers) for the distributions available. Reference: [Placement modifier](https://minecraft.wiki/w/Placed_feature#Placement_modifiers) --- ## Complete Example An ore and a tree, from configured feature to biome. ```kotlin fun DataPack.createForestFeatures() { val oakTree = configuredFeaturesBuilder.tree("oak_tree") { blobFoliagePlacer(radius = constant(2), offset = constant(0), height = 3) foliageProvider = simpleStateProvider(Blocks.OAK_LEAVES) straightTrunkPlacer(baseHeight = 5, heightRandA = 2, heightRandB = 0) trunkProvider = simpleStateProvider(Blocks.OAK_LOG) } val oakTreePlaced = placedFeature("oak_tree_placed", oakTree) { inSquare() count(constant(8)) heightRange(uniformHeightProvider(64, 100)) biome() } val ironOre = configuredFeaturesBuilder.ore("iron_ore", size = 9, discardChanceOnAirExposure = 0.0) { targets { target(blockState(Blocks.IRON_ORE)) { target = tagMatch(Tags.Block.STONE_ORE_REPLACEABLES) } } } val ironOrePlaced = placedFeature("iron_ore_placed", ironOre) { inSquare() count(constant(20)) heightRange(uniformHeightProvider(-64, 72)) biome() } biome("custom_forest") { temperature = 0.7f downfall = 0.8f hasPrecipitation = true features { undergroundOres = listOf(ironOrePlaced) vegetalDecoration = listOf(oakTreePlaced) } } } ``` ## See Also - [Biomes](/docs/data-driven/worldgen/biomes) - listing placed features per decoration step - [Block Predicates](/docs/data-driven/worldgen/block-predicates) - the tests used by placement filters and providers - [Providers](/docs/data-driven/worldgen/providers) - height, int and float providers - [World Generation](/docs/data-driven/worldgen) - overview of the worldgen system --- ## Minecraft Noise Settings, Density Functions & Material Rules with Kore --- root: .components.layouts.MarkdownLayout title: Minecraft Noise Settings, Density Functions & Material Rules with Kore nav-title: Noise description: Shape Minecraft terrain from Kotlin - noise definitions, density functions, noise routers and material rules, fully type-safe with Kore. keywords: minecraft datapack, kore, worldgen, noise settings, density function, noise router, material rule, terrain generation, perlin noise, octaves date-created: 2026-02-03 date-modified: 2026-10-08 routeOverride: /docs/data-driven/worldgen/noise --- # Noise & Terrain Noise, density functions and noise settings are the three layers that shape Minecraft terrain. They build on each other in that order: 1. **Noise definitions** (`worldgen/noise`) - raw Perlin noise, described by octaves and amplitudes. 2. **Density functions** (`worldgen/density_function`) - composable math nodes that sample noises and combine them into a density value for any 3D position. 3. **Noise settings** (`worldgen/noise_settings`) - the terrain configuration a dimension points at: world bounds, default blocks, the noise router wiring density functions to generation roles, and the material rules painting the top layers. Minecraft decides whether a position is solid from the density value the router's `finalDensity` produces: positive density is a block, negative density is air (or fluid, below sea level). References: [Noise](https://minecraft.wiki/w/Noise), [Density function](https://minecraft.wiki/w/Density_function), [Noise settings](https://minecraft.wiki/w/Noise_settings) --- ## Noise Definitions A noise definition configures one Perlin noise instance. **Octaves** layer noise samples at different scales - low octaves make large features (continents), high octaves add fine detail (small bumps). `firstOctave = -7` starts at the 2⁷ = 128 block scale. Each following octave doubles in frequency, and `amplitudes` weights each one, usually decreasing so detail never overwhelms the base shape. ```kotlin val hills = dp.noise("hills") { firstOctave = -5 amplitudes = listOf(1.0, 0.5, 0.25) } ``` `amplitudes(...)` sets the weights inline instead of building the list: ```kotlin val hills = dp.noise("hills") { firstOctave = -5 amplitudes(1.0, 0.5, 0.25) } ``` A file name holding slashes lands in subfolders, so `noise("cave/entrance")` writes `data//worldgen/noise/cave/entrance.json`. | Field | Type | Description | |---------------|----------------|----------------------------------------------------| | `firstOctave` | `Int` | Starting octave, negative values are larger scale. | | `amplitudes` | `List` | Amplitude weight per octave, `0.0` skipping one. | Every call returns a `NoiseArgument`. Vanilla noises are available as `Noises.AquiferBarrier`, `Noises.Continentalness` and so on, and can be passed anywhere a `NoiseArgument` is expected. --- ## Density Functions Density functions are math nodes evaluated per position. Each one lives in its own file and holds exactly one node type, so composing them means referencing other density function files. Declare them inside a `densityFunctions { ... }` block, one builder call per file: ```kotlin dp.densityFunctions { val base = constant("base", 1.0) val hills = noise("hills", Noises.Continentalness, xzScale = 0.25, yScale = 0.0) abs("abs_hills", hills) add("terrain", base, hills) } ``` Each call returns a `DensityFunctionArgument` usable as an input to another node, as a [noise router](#noise-router) field, or anywhere else a density function is expected. Vanilla nodes come from the generated `DensityFunctions` object, e.g. `DensityFunctions.Overworld.BASE_3D_NOISE`. The block itself returns the builder scope, not the last node, so arguments you need later are captured in `val`s inside it. For a single node, or to return one node out of a group, `dp.densityFunctionsBuilder` exposes the same builders: ```kotlin dp.densityFunctionsBuilder.abs("abs_base_3d_noise", DensityFunctions.Overworld.BASE_3D_NOISE) val terrain = with(dp.densityFunctionsBuilder) { val gradient = yClampedGradient("gradient") { fromY = 0; toY = 128; fromValue = 1.0; toValue = -1.0 } add("terrain", DensityFunctions.Overworld.BASE_3D_NOISE, gradient) } ``` Most builders accept either a `Double` or a `DensityFunctionArgument` for their inputs, in every combination. `noise`, `shift`, `shiftA`, `shiftB` and `shiftedNoise` take a `NoiseArgument` instead, since they sample a noise definition directly. ### Density Function Types | Type | Builder | Description | |------------------------|---------------------------|-------------------------------------------------------------------------------| | `abs` | `abs(...)` | Absolute value of the input. | | `add` | `add(...)` | Sums two inputs. | | `beardifier` | `beardifier(...)` | Blends nearby terrain into structures. No parameters. | | `blend_alpha` | `blendAlpha(...)` | Smooths transitions between chunk generation versions. No parameters. | | `blend_density` | `blendDensity(...)` | Blends the input across chunk generation version transitions. | | `blend_offset` | `blendOffset(...)` | Supports legacy chunk compatibility blending. No parameters. | | `cache_2d` | `cache2D(...)` | Caches the input once per horizontal (X/Z) position. | | `cache_all_in_cell` | `cacheAllInCell(...)` | Caches the input for the duration of its interpolation cell. | | `cache_once` | `cacheOnce(...)` | Caches the input once per block position, even if referenced multiple times. | | `clamp` | `clamp(...)` | Restricts the input between `min` and `max`. | | `constant` | `constant(...)` | A fixed value, ignoring the input position. | | `cube` | `cube(...)` | Raises the input to the power of 3 (x³). | | `end_islands` | `endIslands(...)` | Samples the End's island noise. No parameters. | | `find_top_surface` | `findTopSurface(...)` | Scans a column for the topmost position where a density is above zero. | | `flat_cache` | `flatCache(...)` | Caches the input per 4x4 column, computed once at Y=0. | | `half_negative` | `halfNegative(...)` | Halves the input when negative, otherwise leaves it unchanged. | | `interpolated` | `interpolated(...)` | Interpolates the input across the surrounding grid cells. | | `interval_select` | `intervalSelect(...)` | Picks one of several functions from where the input falls between thresholds. | | `invert` | `invert(...)` | Reciprocal (1 / x) of the input. | | `max` | `max(...)` | Larger of two inputs. | | `min` | `min(...)` | Smaller of two inputs. | | `mul` | `mul(...)` | Multiplies two inputs. | | `noise` | `noise(...)` | Samples a noise definition, scaled horizontally and vertically. | | `old_blended_noise` | `oldBlendedNoise(...)` | Legacy blended noise used before the 1.18 terrain rewrite. | | `quarter_negative` | `quarterNegative(...)` | Quarters the input when negative, otherwise leaves it unchanged. | | `range_choice` | `rangeChoice(...)` | Picks between two inputs based on whether a value falls within a range. | | `shift` | `shift(...)` | Samples a noise at `(x/4, y/4, z/4)`, scaled back up by 4. | | `shift_a` | `shiftA(...)` | Samples a noise at `(x/4, 0, z/4)`, scaled back up by 4. | | `shift_b` | `shiftB(...)` | Samples a noise at `(z/4, x/4, 0)`, scaled back up by 4. | | `shifted_noise` | `shiftedNoise(...)` | Like `noise`, but with the sampled coordinates shifted. | | `spline` | `spline(...)` | Cubic spline interpolating control points over a coordinate. | | `square` | `square(...)` | Raises the input to the power of 2 (x²). | | `squeeze` | `squeeze(...)` | Clamps the input to [-1, 1], then applies `x/2 - x³/24`. | | `y_clamped_gradient` | `yClampedGradient(...)` | Linear gradient between two values as Y goes from one bound to another. | All builders live in `io.github.ayfri.kore.features.worldgen.densityfunction.types` and take the file's name as their first argument. `beardifier`, `blend_alpha`, `blend_offset`, `blend_density` and `cache_all_in_cell` are internal to vanilla generation and are not meant to be referenced from a datapack, even though the builders exist. ### Multi-Parameter Nodes Nodes with more than a couple of inputs take a builder block instead of a long positional list: ```kotlin dp.densityFunctions { rangeChoice("caves") { input(DensityFunctions.Overworld.BASE_3D_NOISE) minInclusive = -0.3 maxExclusive = 0.3 whenInRange(-1.0) whenOutOfRange(1.0) } yClampedGradient("depth_bias") { fromY = -64 toY = 320 fromValue = 1.0 toValue = -1.0 } shiftedNoise("shifted_hills", Noises.Continentalness) { xzScale = 0.25 yScale = 0.0 shiftX(DensityFunctions.ShiftX) shiftZ(DensityFunctions.ShiftZ) } intervalSelect("cave_shape") { input(DensityFunctions.Overworld.BASE_3D_NOISE) thresholds(-0.3f, 0.3f) function(DensityFunctions.Nether.BASE_3D_NOISE) function(0.0) function(DensityFunctions.End.BASE_3D_NOISE) } findTopSurface("top_surface", DensityFunctions.Overworld.BASE_3D_NOISE, DensityFunctions.Y, lowerBound = -64, cellHeight = 8) } ``` `intervalSelect` holds one more function than it has thresholds: the first function applies below the first threshold, each following one applies between two consecutive thresholds, and the last one applies at or above the last threshold. Thresholds must be sorted ascending. ### Splines A `spline` interpolates control points over a coordinate density function. Points hold either a constant value or a nested spline, which is how vanilla layers continentalness, erosion and ridges into a single terrain offset: ```kotlin dp.densityFunctions { spline("offset", DensityFunctions.Overworld.CONTINENTS) { point(-1.1f, 0.044f) point(-0.51f, DensityFunctions.Overworld.EROSION, derivative = 0.5f) { point(-0.6f, 1.0f) point(0.5f, -1.0f) } } spline("flat", 0.5f) } ``` Points are ordered by increasing `location`, and `derivative` sets the slope of the curve at that point. Fields typed `DensityFunctionOrDouble` are set through their matching setter function (`input(...)`, `whenInRange(...)`, `shiftX(...)`), overloaded for both a `Double` and a `DensityFunctionArgument`. Assigning the field directly needs an explicit `densityFunctionOrDouble(...)` wrapper. --- ## Noise Settings Noise settings are the complete terrain configuration for a dimension. A dimension references one through its noise generator. ```kotlin val terrain = dp.noiseSettings("custom_terrain") { seaLevel = 63 aquifersEnabled = true oreVeinsEnabled = true noiseOptions(minY = -64, height = 384, sizeHorizontal = 1, sizeVertical = 2) defaultBlock(Blocks.STONE) defaultFluid(Blocks.WATER) { this["level"] = "0" } noiseRouter { finalDensity(DensityFunctions.Overworld.SLOPED_CHEESE) } materialRules { block(Blocks.STONE) } } ``` ### Properties Defaults below are Kore's, which are not always vanilla's - the Overworld for instance ships with `aquifersEnabled` and `oreVeinsEnabled` set to `true`. | Property | Type | Kore default | Description | |------------------------|-----------------------------------------|--------------------|---------------------------------------------------------| | `aquifersEnabled` | `Boolean` | `false` | Generates local water/lava tables instead of a flat sea | | `defaultBlock` | `BlockState` | `stone` | Block placed where density is positive | | `defaultFluid` | `BlockState` | `water[level=0]` | Fluid placed below `seaLevel` where density is negative | | `disableMobGeneration` | `Boolean` | `false` | Skips mob spawning during chunk generation | | `legacyRandomSource` | `Boolean` | `false` | Uses the pre-1.18 random source | | `materialRule` | `MaterialRule` | `bandlands` | Rule picking the block of each position, inline or ID | | `noise` | `NoiseOptions` | `(-64, 384, 1, 2)` | Vertical range and sampling resolution | | `noiseRouter` | `NoiseRouter` | all zeroes | Density functions wired to generation roles | | `oreVeinsEnabled` | `Boolean` | `false` | Enables copper and iron ore veins | | `seaLevel` | `Int` | `63` | Y level the default fluid fills up to | | `spawnTarget` | `List` | empty | Climate parameters the world spawn point searches for | ### Noise Options ```kotlin noiseOptions( minY = -64, // Lowest generated Y level, -2032 to 2031, multiple of 16 height = 384, // Total height, 0 to 4064, multiple of 16 sizeHorizontal = 1, // Horizontal cell size, 1 to 4 sizeVertical = 2 // Vertical cell size, 1 to 4 ) ``` `minY + height` must stay within the dimension type's own vertical range, and larger `size` values mean coarser, cheaper terrain. ### Default Blocks ```kotlin defaultBlock(Blocks.DEEPSLATE) defaultFluid(Blocks.LAVA) { this["level"] = "0" } ``` Both take an optional block-state property block, and both accept a plain `Map` instead. ### Noise Router The noise router maps density functions to generation roles. Every field is a `DensityFunctionOrDouble`, so set it with the matching function, which is overloaded for both a `Double` and a `DensityFunctionArgument`: ```kotlin noiseRouter { barrier(0.5) lava(DensityFunctions.End.BASE_3D_NOISE) finalDensity(myTerrainDensity) } ``` | Field | Role | |---------------------------|-----------------------------------------------------------------| | `barrier` | Aquifer barrier noise, separating fluid pockets from stone | | `continents` | Continentalness climate parameter, ocean vs inland | | `depth` | Depth climate parameter, distance below the surface | | `erosion` | Erosion climate parameter, flat vs mountainous | | `finalDensity` | Final solid/air decision for every position | | `fluidLevelFloodedness` | How often aquifers are filled | | `fluidLevelSpread` | How much aquifer fluid levels vary | | `lava` | Whether an aquifer holds lava instead of water | | `preliminarySurfaceLevel` | Estimated surface height, used by material rules and structures | | `ridges` | Weirdness climate parameter, driving ridged terrain | | `temperature` | Temperature climate parameter for biome placement | | `vegetation` | Humidity climate parameter for biome placement | | `veinGap` | Gaps punched through ore veins | | `veinRidged` | Ore vein shape | | `veinToggle` | Whether ore veins generate at a position | Unset fields serialize as `0.0`, which means flat, featureless terrain - so a hand-written router usually starts from the vanilla density functions in the generated `DensityFunctions` object. Reference: [Noise router](https://minecraft.wiki/w/Noise_settings#Noise_router) ### Material Rules Material rules decide which blocks replace the top layers of terrain. They are evaluated in order, and the first rule producing a block wins. ```kotlin materialRules { condition(biomes(Biomes.DESERT)) { block(Blocks.SAND) } condition(stoneDepth(Surface.FLOOR, addSurfaceDepth = true)) { condition(water(offset = -1, surfaceDepthMultiplier = 0)) { block(Blocks.GRASS_BLOCK) } block(Blocks.DIRT) } block(Blocks.STONE) } ``` Every builder below lives on the `materialRules` scope, so nothing leaks into the global namespace and the IDE completes the whole rule set from inside the block. | Rule | Builder | Description | |-------------|----------------------------|----------------------------------------------| | `bandlands` | `bandlands()` | Vanilla badlands terracotta banding. | | `block` | `block(block) { }` | Places a block state. | | `condition` | `condition(condition) { }` | Runs nested rules when the condition passes. | | `sequence` | `sequence { }` | Groups rules, first match wins. | A `condition` block holding a single rule serializes as that rule, several rules are wrapped in a `sequence`. #### Conditions Condition builders live on the `materialRules` scope too, so they resolve without extra imports inside the block. | Condition | Builder | Description | |-----------------------------|--------------------------------------------------------------|-----------------------------------------------------| | `above_preliminary_surface` | `AbovePreliminarySurface` | Position is above the router's preliminary surface. | | `biome` | `biomes(...)` | Position is in one of the listed biomes. | | `hole` | `Hole` | Column has a surface depth of 0. | | `noise_threshold` | `noiseThreshold(noise, minThreshold, maxThreshold) { is3d }` | Noise value falls within a range, in 2D or 3D. | | `not` | `not(condition)` | Inverts another condition. | | `steep` | `Steep` | Position is on a steep north or east facing slope. | | `stone_depth` | `stoneDepth(surfaceType, offset, ...)` | Depth below the floor or ceiling of the terrain. | | `temperature` | `Temperature` | Biome is cold enough for snowfall. | | `vertical_gradient` | `verticalGradient(name, trueAtAndBelow, falseAtAndAbove)` | Random blend between two Y anchors. | | `water` | `water(offset, surfaceDepthMultiplier, addStoneDepth)` | Position is above the local water level. | | `y_above` | `yAbove(anchor, surfaceDepthMultiplier, addStoneDepth)` | Position is above a Y anchor, exclusive. | `noiseThreshold` evaluates its noise in 2D (X/Z) by default. Set `is3d = true` to evaluate it in 3D instead: ```kotlin condition(noiseThreshold(hills) { minThreshold = -0.5 maxThreshold = 0.5 is3d = true }) { block(Blocks.GRANITE) } ``` `AbovePreliminarySurface`, `Hole`, `Steep` and `Temperature` take no arguments, so they are passed directly as objects: ```kotlin condition(AbovePreliminarySurface) { block(Blocks.GRASS_BLOCK) } condition(not(Steep)) { block(Blocks.GRAVEL) } ``` The vertical anchors used by `yAbove` and `verticalGradient` are built with `absolute(y)`, `aboveBottom(offset)` or `belowTop(offset)`, all scoped to the `materialRules { }` block: ```kotlin condition(verticalGradient("bedrock_floor", aboveBottom(0), aboveBottom(5))) { block(Blocks.BEDROCK) } ``` #### Material Rule and Condition Files Rules and conditions can live in their own files, `worldgen/material_rule/.json` and `worldgen/material_condition/.json`, and be used by ID wherever an inline one fits. `materialRule` holds the rules of its block, a single one as-is and several in a `sequence`, `materialCondition` the condition its block returns: ```kotlin val highlands = materialCondition("highlands") { yAbove(absolute(120)) } val peaks = materialRule("peaks") { condition(reference(highlands)) { block(Blocks.SNOW_BLOCK) } } noiseSettings("my_settings") { materialRules { rule(peaks) rule(MaterialRules.Overworld.SURFACE) } } ``` `rule(id)` appends a rule file, `reference(id)` turns a condition file into a condition, and `materialRule(id)` sets the noise settings' rule to a file, like vanilla's `materialRule(MaterialRules.Overworld)`. The vanilla files are typed in the generated `MaterialRules` and `MaterialConditions`. Reference: [Material rule](https://minecraft.wiki/w/Surface_rule) --- ## Putting It Together The [worldgen overview](/docs/data-driven/worldgen#a-complete-custom-dimension) walks through a noise, its density functions, the noise settings wiring them together, and the dimension pointing at the result. ## See Also - [Biomes](/docs/data-driven/worldgen/biomes) - the biomes a noise generator distributes over the terrain - [Carvers](/docs/data-driven/worldgen/carvers) - the caves cut out of the terrain afterwards - [Dimensions](/docs/data-driven/worldgen/dimensions) - the dimension pointing at noise settings - [Providers](/docs/data-driven/worldgen/providers) - the vertical anchors used by material rule conditions - [World Generation](/docs/data-driven/worldgen) - overview of the worldgen system --- ## Providers --- root: .components.layouts.MarkdownLayout title: Providers nav-title: Providers description: Vertical anchors, height providers, int providers and float providers - the randomized values shared by every Kore worldgen builder. keywords: minecraft, datapack, kore, worldgen, vertical anchor, height provider, int provider, float provider, uniform, trapezoid date-created: 2026-08-21 date-modified: 2026-08-21 routeOverride: /docs/data-driven/worldgen/providers --- # Providers Most worldgen fields do not take a plain number: they take a **provider**, a small object the game samples at every position. That is how one ore configuration can produce veins of varying size, or one carver can dig tunnels at varying heights. Four families exist, and they show up across carvers, features, structures and material rules: - **Vertical anchors** - a single Y level, expressed absolutely or relative to the world bounds. - **Height providers** - a distribution of vertical anchors. - **Int providers** - a distribution of integers, for counts and sizes. - **Float providers** - a distribution of floats, for radii, chances and scales. Every builder is scoped: it only resolves inside a block actually accepting that kind of value, so nothing pollutes the global completion list. ## Vertical Anchors A vertical anchor is one Y level. It serializes as a one-key object, and the resolved Y is always clamped to the build height of the dimension. | Builder | JSON | Meaning | |------------------|-----------------------|-------------------------------------------------------------------| | `absolute(y)` | `{"absolute": y}` | Absolute Y coordinate, the one shown on the F3 screen. | | `aboveBottom(n)` | `{"above_bottom": n}` | `n` blocks above the bottom of the dimension, `0` being `min_y`. | | `belowTop(n)` | `{"below_top": n}` | `n` blocks below the top of the dimension, larger values go down. | The builders extend `VerticalAnchorScope`, so they resolve inside a placed feature, a carver configuration, a `materialRules { }` block, and anywhere a height provider is being built. Reference: [Vertical anchor](https://minecraft.wiki/w/Custom_world_generation/vertical_anchor) ## Height Providers A height provider picks a Y level between two anchors, following a distribution. It is what `heightRange()` on a placed feature, `y` on a carver and `startHeight` on a jigsaw structure take. | Builder | Behavior | |--------------------------------------------------------------------------|---------------------------------------------------------------------------| | `constantHeightProvider(anchor)` | Always the given anchor. Serializes as the bare anchor, with no wrapper. | | `constantAbsolute(y)` / `constantAboveBottom(n)` / `constantBelowTop(n)` | Shorthands for `constantHeightProvider` on each anchor form. | | `uniformHeightProvider(min, max)` | Equal chance at every level between both bounds, included. | | `trapezoidHeightProvider(min, max, plateau)` | Flat top of `plateau` blocks in the middle, linear falloff on both sides. | | `biasedToBottomHeightProvider(min, max, inner)` | Uniform over the `inner` bottom blocks, exponential falloff above them. | | `veryBiasedToBottomHeightProvider(min, max, inner)` | Same shape with a sharper falloff, the vanilla diamond pattern. | | `weightedListHeightProvider { }` | Picks one of the nested providers, by weight. | Every builder except `weightedListHeightProvider` also takes plain `Int` bounds, read as absolute Y coordinates. They extend `HeightProviderScope`, which itself extends `VerticalAnchorScope`, so anchors are available in the same block. ```kotlin heightRange(uniformHeightProvider(minInclusive = 0, maxInclusive = 64)) heightRange(trapezoidHeightProvider(minInclusive = 0, maxInclusive = 64, plateau = 20)) heightRange(constantAbsolute(32)) heightRange(constantHeightProvider(belowTop(16))) heightRange(veryBiasedToBottomHeightProvider(aboveBottom(8), absolute(16))) heightRange(weightedListHeightProvider { entry(3, constantAbsolute(32)) entry(1, uniformHeightProvider(aboveBottom(0), absolute(16))) }) ``` Reference: [Height provider](https://minecraft.wiki/w/Custom_world_generation/height_provider) ## Int Providers Int providers fill every field expecting a variable count or size: `count()` and `countOnEveryLayer()` on placed features, the `limit` of a `capped` processor, the layer heights of a `blockColumn`, and so on. | Builder | Behavior | |--------------------------------------------------------|-------------------------------------------------------------------------------------| | `constant(value)` | Always returns `value`. Serializes as a plain integer, no wrapper object. | | `uniform(minInclusive, maxInclusive)` | Uniform random integer in `[min, max]`. | | `biasedToBottom(minInclusive, maxInclusive)` | Random integer in `[min, max]`, weighted towards the minimum. | | `trapezoid(min, max, plateau)` | Trapezoid distribution over `[min, max]` with a flat top of width `plateau`. | | `clampedNormal(minInclusive, maxInclusive, mean, dev)` | Samples a normal distribution (`mean`/`dev`) and clamps the result to `[min, max]`. | | `clamped(minInclusive, maxInclusive, source)` | Evaluates `source` and clamps its result to `[min, max]`. | | `weightedList { }` | Randomly selects one entry from a weighted pool. | The builders extend `IntProviderScope`, so they resolve inside a placed feature, a configured feature configuration, a `processorList { }` block, a dimension type and an enchantment provider. ```kotlin count(constant(10)) count(biasedToBottom(3, 8)) count(clampedNormal(1, 10, mean = 5.0f, deviation = 2.0f)) // Reroll uniform(0, 20), but never below 4 or above 12. count(clamped(4, 12, uniform(0, 20))) // 70% chance of 1, 30% chance of 3. count(weightedList { entry(7, constant(1)) entry(3, constant(3)) }) // Same pool, written as weight-to-provider pairs. count(weightedList(7 to constant(1), 3 to constant(3))) ``` Reference: [Int provider](https://minecraft.wiki/w/Custom_world_generation/int_provider) ## Float Providers Float providers fill float fields such as `yScale` and `floorLevel` on carvers, `radius` on foliage placers, `heightScale` and `stalactiteBluntness` on features, and `volume` or `pitch` on enchantment effects. | Builder | Behavior | |--------------------------------------------|-------------------------------------------------------------------------------------------| | `constant(value)` | Always returns `value`. Serializes as a plain float, no wrapper object. | | `uniform(minInclusive, maxExclusive)` | Uniform random float in `[min, max)`. `maxExclusive` cannot be less than `minInclusive`. | | `trapezoid(min, max, plateau)` | Trapezoid distribution over `[min, max]` with a flat top of width `plateau`. | | `clampedNormal(mean, deviation, min, max)` | Samples a normal distribution (`mean`/`deviation`) and clamps the result to `[min, max]`. | The builders extend `FloatProviderScope`, so they resolve inside a carver configuration, a configured feature configuration and the `play_sound` / `spawn_particles` enchantment effects. ```kotlin yScale = constant(1.5f) pitch = uniform(0.8f, 1.2f) floorLevel = clampedNormal(mean = 0.5f, deviation = 0.2f, min = 0.0f, max = 1.0f) thickness = trapezoid(min = 0.0f, max = 2.0f, plateau = 0.5f) ``` `constant`, `uniform`, `trapezoid` and `clampedNormal` are named the same for ints and floats. A block accepting both, such as `largeDripstone { }`, tells them apart by argument type: `constant(3)` builds an int provider and `constant(3f)` a float one. Reference: [Float provider](https://minecraft.wiki/w/Custom_world_generation/float_provider) ## See Also - [Carvers](/docs/data-driven/worldgen/carvers) - `y`, `yScale`, radius multipliers and canyon shape fields - [Features](/docs/data-driven/worldgen/features) - `heightRange`, `count` and the configured feature parameters - [Noise & Terrain](/docs/data-driven/worldgen/noise) - `yAbove` and `verticalGradient` material rule conditions - [Structures](/docs/data-driven/worldgen/structures) - `startHeight` on jigsaw structures, `height` on nether fossils --- ## Structures --- root: .components.layouts.MarkdownLayout title: Structures nav-title: Structures description: Build Minecraft structures with Kore - configured structures, jigsaw template pools, block processors and structure set placement. keywords: minecraft, datapack, kore, worldgen, structure, template pool, processor list, jigsaw, structure set date-created: 2026-02-03 date-modified: 2026-10-08 routeOverride: /docs/data-driven/worldgen/structures --- # Structures Structures are the large, hand-authored pieces of the world: villages, temples, strongholds, trial chambers. Unlike a feature, a structure can span many chunks and be assembled from separate building blocks, and it starts generating during `structures_starts`, before the terrain itself, which is why terrain can adapt around it instead of cutting through it. Four file types work together, from the smallest to the largest scale: | File | Answers | Kore API | |--------------------------|---------------------------------------------------------------------|-----------------------| | **Processor list** | How are the blocks of a piece changed as it is placed? | `processorList(...)` | | **Template pool** | Which pieces may connect here, and with what weights? | `templatePool(...)` | | **Configured structure** | What kind of structure is this, in which biomes, at which step? | `structures { }` | | **Structure set** | How far apart do instances of it stand across the world? | `structureSet(...)` | A structure made of a single fixed shape only needs the last two; a jigsaw structure like a village uses all four. References: [Structure](https://minecraft.wiki/w/Structure), [Structure definition](https://minecraft.wiki/w/Structure_definition) --- ## Processor List Processors transform the blocks of a structure template as it is stamped into the world: aging bricks, randomizing block types, dropping the structure onto the terrain. They run in declaration order, each one taking the output of the previous. ```kotlin val processors = dp.processorList("my_processors") { blockIgnore(Blocks.STRUCTURE_BLOCK, Blocks.JIGSAW) blockAge(0.5) gravity(HeightMap.WORLD_SURFACE_WG) } ``` ```json { "processors": [ { "processor_type": "minecraft:block_ignore", "blocks": [ { "Name": "minecraft:structure_block" }, { "Name": "minecraft:jigsaw" } ] }, { "processor_type": "minecraft:block_age", "mossiness": 0.5 }, { "processor_type": "minecraft:gravity", "heightmap": "WORLD_SURFACE_WG", "offset": 0 } ] } ``` Every processor is a function scoped to the `processorList { }` block. | Processor | Effect | |---------------------------------------|------------------------------------------------------------------------| | `blackstoneReplace()` | Replaces blocks with their blackstone counterparts, like bastions | | `blockAge(mossiness)` | Ages blocks: moss, cracks, and randomly missing blocks | | `blockIgnore(vararg blocks)` | Skips those block states, keeping whatever the world already has | | `blockRot(integrity, rottableBlocks)` | Randomly removes blocks, `integrity` being the chance to keep one | | `capped(limit) { }` | Runs a single delegate processor on at most `limit` blocks | | `gravity(heightmap, offset)` | Drops the blocks onto a heightmap so the structure follows the terrain | | `jigsawReplacement()` | Turns leftover jigsaw blocks into their final state block | | `lavaSubmergedBlock()` | Fills the positions around blocks placed inside a lava lake with lava | | `nop()` | Does nothing, useful as a placeholder delegate | | `protectedBlocks(vararg blocks)` | Keeps those world blocks untouched, the template never replaces them | | `rules { }` | Replaces blocks using a list of rules, the first matching rule winning | `limit` accepts a plain `Int` or any [int provider](/docs/data-driven/worldgen/providers#int-providers), and the delegate cannot be another `capped` processor: ```kotlin processorList("capped_aging") { capped(uniform(1, 4)) { blockAge(0.8) } } ``` Reference: [Processor list](https://minecraft.wiki/w/Processor_list) ### Rules A rule replaces a template block by `outputState` when its three predicates pass, and the first matching rule wins. | Property | Tests or sets | Default | |-----------------------|-----------------------------------------------------------|--------------| | `positionPredicate` | The position inside the structure piece | `null` | | `inputPredicate` | The block of the structure template | `AlwaysTrue` | | `locationPredicate` | The block already in the world at that position | `AlwaysTrue` | | `outputState` | The block state placed when every predicate passes | stone | | `blockEntityModifier` | What happens to the block entity data of the placed block | `null` | ```kotlin processorList("mossify") { rules { rule { inputPredicate = blockMatch(Blocks.STONE_BRICKS) locationPredicate = tagMatch(Tags.Block.DIRT) outputState = blockState(Blocks.MOSSY_STONE_BRICKS) } rule { positionPredicate = axisAlignedLinearPos(Axis.Y) { minDist = 0 maxDist = 4 minChance = 1.0 maxChance = 0.0 } inputPredicate = randomBlockStateMatch(blockState(Blocks.STONE), 0.5) outputState = blockState(Blocks.CRACKED_STONE_BRICKS) blockEntityModifier = appendLoot(LootTables.Chests.SIMPLE_DUNGEON) } } } ``` **Position predicates**, scoped to `rule { }`: | Builder | Passes | |----------------------------------|------------------------------------------------------------------| | `alwaysTruePos()` | Everywhere, same as leaving `positionPredicate` to `null` | | `linearPos(...)` | With a chance interpolated over the distance from the origin | | `axisAlignedLinearPos(axis) { }` | Same, with the distance measured along one axis only | **Block entity modifiers**, also scoped to `rule { }`: | Builder | Effect | |-------------------------|---------------------------------------------------------------------| | `appendLoot(lootTable)` | Fills the placed container from a loot table | | `appendStatic { }` | Merges NBT data into the block entity | | `clear()` | Drops the block entity data of the template | | `passthrough()` | Keeps it untouched, same as leaving `blockEntityModifier` to `null` | **Rule tests**, used by `inputPredicate` and `locationPredicate`. These are the shared worldgen ones from `io.github.ayfri.kore.features.worldgen.ruletest`, also available in the `target { }` block of the [ore-like features](/docs/data-driven/worldgen/features#ore): | Builder | Matches | |---------------------------------------------|-------------------------------------------------------------| | `allOf(rules...)` | A block matching every rule test of `rules` | | `alwaysTrue()` | Any block, same as leaving the predicate to its default | | `blockMatch(block)` | One block, whatever its block state properties are | | `blockStateMatch(blockState)` | One exact block state, every property having to match | | `heightMatch(minInclusive, maxInclusive)` | Any block whose Y level is between the two bounds | | `randomBlockMatch(block, probability)` | A block with a probability, clamped between `0.0` and `1.0` | | `randomBlockStateMatch(state, probability)` | A block state with a probability | | `tagMatch(tag)` | Any block of a block tag | --- ## Template Pool A template pool is a weighted bag of pieces the jigsaw system may pick from. Pieces connect through jigsaw blocks: each one names the pool it wants next, so a village street pool can point at a house pool that points back at the street pool, and the structure grows until it hits its `size` budget. ```kotlin val pool = dp.templatePool("my_pool") { fallback = TemplatePools.Empty single(Structures.Village.Plains.TownCenters.PLAINS_FOUNTAIN_01, ProcessorLists.EMPTY, weight = 3) feature(PlacedFeatures.PILE_HAY, weight = 1) empty(weight = 2) } ``` `fallback` is the pool used when a piece cannot connect any further - usually `TemplatePools.Empty`, which is also the default. | Builder | `element_type` | Places | |--------------------------------------|----------------------------------------|--------------------------------------------------------------| | `single(location, processors)` | `minecraft:single_pool_element` | A structure template | | `legacySingle(location, processors)` | `minecraft:legacy_single_pool_element` | Same, but keeps existing world blocks instead of placing air | | `feature(feature)` | `minecraft:feature_pool_element` | A placed feature, in a 1x1x1 piece | | `list { }` | `minecraft:list_pool_element` | Several elements at the same position, in order | | `empty()` | `minecraft:empty_pool_element` | Nothing, useful as filler weight | Every builder takes `weight` (1 to 150, default `1`) and `projection` (`Projection.RIGID` keeps the original shape, `TERRAIN_MATCHING` adapts it to the terrain height; default `RIGID`). `processors` defaults to `ProcessorLists.EMPTY`. A trailing block reaches the remaining fields, such as `overrideLiquidSettings` on `single` and `legacySingle`. ```kotlin dp.templatePool("houses") { single(Structures.Village.Plains.Houses.PLAINS_SMALL_HOUSE_1, weight = 4) { projection = Projection.TERRAIN_MATCHING overrideLiquidSettings = LiquidSettings.IGNORE_WATERLOGGING } list(weight = 1) { single(Structures.Village.Plains.Houses.PLAINS_SMALL_HOUSE_2, ProcessorLists.MOSSIFY_10_PERCENT) feature(PlacedFeatures.PILE_HAY) } } ``` Elements inside a `list` are unweighted: the weight belongs to the list entry itself. Reference: [Template pool](https://minecraft.wiki/w/Template_pool) --- ## Configured Structure A configured structure picks the generation algorithm and says where it is allowed to appear. Each type is a function on the `structures { }` scope, taking the file name first and everything else in a trailing block, so one call writes one file: ```kotlin dp.structures { desertPyramid("my_pyramid") { biomes(Biomes.DESERT, Biomes.BADLANDS) terrainAdaptation = TerrainAdaptation.BEARD_BOX spawnOverrides { monster(BoundingBox.FULL) { spawner(EntityTypes.HUSK, weight = 1, minCount = 4, maxCount = 4) } } } } ``` `dp.structuresBuilder.desertPyramid(...)` writes the same file outside a `structures { }` block, and `dp.structure(fileName, type)` takes an already built type, for the rare case where the generator itself needs tweaking, such as overriding its namespace. ### Shared Fields | Field | Type | Default | Meaning | |---------------------|-----------------------|-----------------------------------------|---------------------------------------------------------------------------------| | `biomes` | Biomes or a biome tag | empty, meaning nowhere | Biomes the structure start is allowed to land in. | | `step` | `GenerationStep` | the vanilla step of that structure type | Which [decoration step](/docs/data-driven/worldgen#decoration-steps) places it. | | `spawnOverrides` | `SpawnOverrides` | empty, keeping the biome spawns | Mob spawn lists replacing the biome ones inside the structure. | | `terrainAdaptation` | `TerrainAdaptation` | none | How the terrain reacts around the structure. | Every type defaults `step` to the step its vanilla counterpart uses, so it only needs setting when a structure should generate at another point of the world generation. `spawnOverrides` takes one block per mob category (`ambient`, `axolotls`, `creature`, `misc`, `monster`, `undergroundWaterCreature`, `waterAmbient`, `waterCreature`), each with a `BoundingBox`: `FULL` applies to the whole structure, `PIECE` to the individual piece and is the default. A category left out keeps the biome spawns, while a category declared with an empty block stops it from spawning inside the structure entirely: ```kotlin spawnOverrides { // No monster ever spawns inside the mansion, whatever the biome says. monster(BoundingBox.PIECE) // Guardians spawn anywhere inside the monument, on top of the biome spawns being dropped. waterCreature(BoundingBox.FULL) { spawner(EntityTypes.GUARDIAN, weight = 20, minCount = 2, maxCount = 4) } } ``` | `TerrainAdaptation` | Effect | |---------------------|-------------------------------------------------------------------| | `NONE` | No adaptation, the structure is stamped as-is. | | `BEARD_THIN` | Grows terrain under the structure and clears the inside. | | `BEARD_BOX` | Same, over the full bounding box. | | `BURY` | Grows terrain around the structure so it ends up buried. | | `ENCAPSULATE` | Same over the full bounding box, used by the trial chambers. | ### Structure Types | Builder | Default `step` | Extra fields | |--------------------------|--------------------------|------------------------------------------------------------------------------------------| | `buriedTreasure(...)` | `underground_structures` | none | | `desertPyramid(...)` | `surface_structures` | none | | `endCity(...)` | `surface_structures` | none | | `fortress(...)` | `underground_decoration` | none | | `igloo(...)` | `surface_structures` | none | | `jigsaw(..., startPool)` | `surface_structures` | see below | | `jungleTemple(...)` | `surface_structures` | none | | `mineshaft(...)` | `underground_structures` | `mineshaftType`: `MineshaftType.NORMAL` or `MESA` | | `netherFossil(...)` | `underground_decoration` | `height`: a height provider | | `oceanMonument(...)` | `surface_structures` | none | | `oceanRuin(...)` | `surface_structures` | `biomeTemp`: `BiomeTemperature.COLD` or `WARM`, `largeProbability`, `clusterProbability` | | `ruinedPortal(...)` | `surface_structures` | `setup(...)` entries, see below | | `shipwreck(...)` | `surface_structures` | `isBeached` | | `stronghold(...)` | `surface_structures` | none | | `swampHut(...)` | `surface_structures` | none | | `woodlandMansion(...)` | `surface_structures` | none | Only `jigsaw` builds its shape from data; every other type runs a hardcoded algorithm and only exposes the knobs listed above. ```kotlin structures { mineshaft("badlands_mineshaft") { biomes(Tags.Worldgen.Biome.IS_BADLANDS) mineshaftType = MineshaftType.MESA } netherFossil("deep_fossil") { biomes(Biomes.SOUL_SAND_VALLEY) height = uniformHeightProvider(aboveBottom(32), belowTop(2)) } ruinedPortal("my_ruined_portal") { biomes(Biomes.DESERT) setup(RuinedPortalPlacement.PARTLY_BURIED, mossiness = 0.0f, weight = 1f) setup(RuinedPortalPlacement.ON_LAND_SURFACE, overgrown = true, vines = true, weight = 0.5f) } oceanRuin("my_ocean_ruin") { biomes(Biomes.WARM_OCEAN) biomeTemp = BiomeTemperature.WARM largeProbability = 0.4f clusterProbability = 0.8f } } ``` A `ruinedPortal` needs at least one `setup`: it is the weighted list one variant is drawn from per instance, and vanilla rejects the structure when it is empty. ### Jigsaw ```kotlin structures { jigsaw("my_village", startPool = villageStart) { biomes(Biomes.PLAINS) terrainAdaptation = TerrainAdaptation.BEARD_THIN size = 6 startHeight = constantAbsolute(0) projectStartToHeightmap = HeightMap.WORLD_SURFACE_WG maxDistanceFromCenter(80) } } ``` | Field | Default | Meaning | |---------------------------|-----------------------|-----------------------------------------------------------------------------------------| | `dimensionPadding` | none | Blocks kept free above and below the structure, `dimensionPadding(top, bottom)`. | | `liquidSettings` | none | Whether pieces waterlog, `LiquidSettings.APPLY_WATERLOGGING` or `IGNORE_WATERLOGGING`. | | `maxDistanceFromCenter` | `80` | Radius the structure may not grow past, up to `128`, or `116` when it beards or buries. | | `poolAliases` | none | Per-instance pool rewiring, see below. | | `projectStartToHeightmap` | none | Snaps the start piece onto a heightmap instead of using `startHeight`. | | `size` | `1` | How many times pieces may branch out from the start piece, from `1` to `20`. | | `startHeight` | `constantAbsolute(0)` | Height provider for the start piece. | | `startJigsawName` | none | Only connect through jigsaw blocks carrying this name. | | `startPool` | mandatory | The template pool the first piece is drawn from. | | `useExpansionHack` | `false` | The legacy village terrain hack, raising pieces above the ground. | `dimensionPadding(value)` and `maxDistanceFromCenter(value)` are functions, taking one value for both directions or two for each. Given one value, they write a bare number instead of an object. #### Pool Aliases Pool aliases rewire which pool an alias resolves to, per structure instance. That is how a single jigsaw structure can produce differently themed variants without duplicating every pool. ```kotlin poolAliases { // Always resolve this alias to that pool. directPoolAlias(TemplatePools.Empty, housesPool) // Draw the target from a weighted list, per instance. randomPoolAlias(TemplatePools.Empty) { weightedPoolEntry(1, desertHouses) weightedPoolEntry(2, plainsHouses) } // Draw a whole group of aliases at once, keeping a variant coherent. randomGroupPoolAlias { weightedGroupEntry(1) { directPoolAlias(TemplatePools.Empty, desertHouses) directPoolAlias(TemplatePools.Empty, desertStreets) } } } ``` --- ## Structure Set A structure set decides how instances spread over the world. Several structures can share a set, in which case the weights make them mutually exclusive: only one generates per grid cell. ```kotlin val villages = dp.structureSet("custom_villages") { structure(ConfiguredStructures.VILLAGE_PLAINS, weight = 3) structure(ConfiguredStructures.VILLAGE_DESERT, weight = 1) randomSpreadPlacement(spacing = 34, separation = 8) { salt = 10387312 spreadType = SpreadType.LINEAR } } ``` Every configured structure builder returns a `ConfiguredStructureArgument`, which is exactly what `structure()` takes, so a set mixes your own structures and the vanilla ones from the generated `ConfiguredStructures` object: ```kotlin val myPyramid = dp.structuresBuilder.desertPyramid("my_pyramid") { biomes(Biomes.DESERT) } dp.structureSet("my_pyramids") { structure(myPyramid, weight = 2) randomSpreadPlacement(spacing = 32, separation = 8) } ``` ### Placement Three placement types exist, and a set has exactly one. Without a call to any, it uses an empty random spread. **`randomSpreadPlacement(spreadType, spacing, separation)`** is the common one. The world is cut into a grid of `spacing` chunks; each cell gets at most one structure, placed at random inside it, never closer than `separation` chunks to the cell edge. `separation` must stay below `spacing`, and the closer the two, the more regular the layout. `SpreadType.LINEAR` picks the position uniformly, `TRIANGULAR` biases it towards the cell center. **`concentricRingsPlacement(distance, spread, count)`** places structures in rings around the world origin, the way strongholds do: `distance` chunks to the first ring, `spread` chunks of jitter, `count` structures in total. `preferredBiomes(...)` restricts where within a ring a structure may land. **`dimensionOriginPlacement()`** places a single structure at the dimension origin: the `spawn_target` of the noise settings for a noise dimension, chunk `0 0` otherwise. All three share these fields, set inside the trailing block: | Field | Meaning | |----------------------------|----------------------------------------------------------------------------------------------| | `salt` | Seed modifier keeping different sets from aligning their grids. Derived from the file name. | | `frequency` | Fraction of eligible cells actually generating a structure, from `0.0` to `1.0`. | | `frequencyReductionMethod` | How `frequency` is applied: `DEFAULT`, or one of the three `LEGACY_TYPE_*` vanilla variants. | | `exclusionZone(set, n)` | Skips a placement within `n` chunks of a structure of another set. | | `locateOffset` | Offset applied to what `/locate` reports, as `listOf(x, y, z)`. | ```kotlin dp.structureSet("my_strongholds") { structure(ConfiguredStructures.STRONGHOLD) concentricRingsPlacement(distance = 32, spread = 3, count = 128) { preferredBiomes(Biomes.PLAINS, Biomes.FOREST) exclusionZone(villages, chunkCount = 5) } } ``` Reference: [Structure set](https://minecraft.wiki/w/Structure_set) ## See Also - [Biomes](/docs/data-driven/worldgen/biomes) - the biomes a structure restricts itself to - [Features](/docs/data-driven/worldgen/features) - the smaller decorations placed alongside structures - [Providers](/docs/data-driven/worldgen/providers) - height and int providers used by jigsaw and processors - [Tags](/docs/data-driven/tags) - grouping processor lists, template pools and structures - [World Generation](/docs/data-driven/worldgen) - overview of the worldgen system --- ## World Presets --- root: .components.layouts.MarkdownLayout title: World Presets nav-title: World Presets description: Add your own world types to the Minecraft world creation screen, and superflat presets to its customization screen, with Kore. keywords: minecraft, datapack, kore, worldgen, world preset, world type, flat level generator preset, superflat date-created: 2026-02-03 date-modified: 2026-08-21 routeOverride: /docs/data-driven/worldgen/world-presets --- # World Presets A world preset is an entry in the **World Type** dropdown of the world creation screen. It describes the full set of dimensions a new world starts with, so it is how you replace the Overworld rather than merely add a dimension next to it. Vanilla ships Default, Superflat, Large Biomes, Amplified and Single Biome. A flat level generator preset is the smaller sibling: an entry in the Superflat customization screen, holding one layer stack. References: [World preset](https://minecraft.wiki/w/World_preset), [Superflat presets](https://minecraft.wiki/w/Superflat#Presets) --- ## World Preset A preset needs at least a `minecraft:overworld` dimension; the Nether, the End and any dimension of your own are optional. `dimension(id, type)` takes two distinct things. `id` is the id the world knows the dimension by - what `/execute in` and portals resolve. `type` is the [dimension type](/docs/data-driven/worldgen/dimensions) file holding the height bounds, lighting and environment attributes. They happen to share a name for the vanilla dimensions, which is exactly why a preset can put a custom type behind the vanilla `minecraft:overworld` id. ```kotlin val customWorld = dp.worldPreset("custom_world") { // Vanilla id, custom type and terrain. dimension(Dimensions.OVERWORLD, myOverworldType) { noiseGenerator(settings = myNoiseSettings, biomeSource = multiNoise(BiomePresets.OVERWORLD)) } // Untouched vanilla Nether. dimension(Dimensions.THE_NETHER, DimensionTypes.THE_NETHER) { noiseGenerator(settings = NoiseSettings.NETHER, biomeSource = multiNoise(BiomePresets.NETHER)) } // Untouched vanilla End. dimension(Dimensions.THE_END, DimensionTypes.THE_END) { noiseGenerator(settings = NoiseSettings.END, biomeSource = theEnd()) } // A dimension of your own, reachable with /execute in my_pack:mining. dimension(DimensionArgument("mining", "my_pack"), miningDimType) { noiseGenerator(settings = NoiseSettings.CAVES, biomeSource = fixed(Biomes.DRIPSTONE_CAVES)) } } ``` The generator block is the same one a standalone dimension uses, `noiseGenerator`, `flatGenerator` or `debugGenerator` included. See [Dimensions](/docs/data-driven/worldgen/dimensions#generators). A preset only shows up in the dropdown once it is listed in the `minecraft:normal` world preset tag: ```kotlin dp.worldPresetTag("normal", namespace = "minecraft") { add(customWorld) } ``` --- ## Flat Level Generator Preset A preset is an icon plus the superflat settings it applies. Everything left untouched keeps the vanilla `classic_flat` values, so a preset only declares what it changes; called with no arguments at all, it writes `classic_flat` itself - a grass block icon, the plains biome, a bedrock/dirt/grass stack, and villages as the only structure set. ```kotlin val tunnelersDream = dp.flatLevelGeneratorPreset("tunnelers_dream", Items.STONE) { settings { biome = Biomes.WINDSWEPT_HILLS layers { layer(Blocks.BEDROCK) layer(Blocks.STONE, height = 230) layer(Blocks.DIRT, height = 5) layer(Blocks.GRASS_BLOCK) } structureOverrides(StructureSets.MINESHAFTS, StructureSets.STRONGHOLDS) } } ``` `settings { }` is the same `FlatGeneratorSettings` block the [flat generator](/docs/data-driven/worldgen/dimensions#flat-generator) of a dimension takes, with the same fields and the same three ways of declaring layers. A preset only shows up in the superflat customization screen once it is listed in the `minecraft:visible` flat level generator preset tag: ```kotlin dp.flatLevelGeneratorPresetTag("visible", namespace = "minecraft") { add(tunnelersDream) } ``` ## See Also - [Dimensions](/docs/data-driven/worldgen/dimensions) - dimension types and the generator builders reused here - [Noise & Terrain](/docs/data-driven/worldgen/noise) - the noise settings a preset points at - [Tags](/docs/data-driven/tags) - the `normal` and `visible` tags making a preset selectable - [World Generation](/docs/data-driven/worldgen) - overview of the worldgen system --- # Concepts ## Runtime Logic - Kotlin vs Minecraft: Variables, Conditions & Loops --- root: .components.layouts.MarkdownLayout title: "Runtime Logic - Kotlin vs Minecraft: Variables, Conditions & Loops" nav-title: Runtime Logic description: "The key concept for Kore datapacks: how compile-time Kotlin maps to runtime Minecraft logic. Variables via scoreboards and storage, conditions via execute if/unless, and loops via tick functions and schedulers." keywords: minecraft runtime logic, datapack variables, datapack conditions, datapack loops, kore runtime, datapack tick function, execute if unless, minecraft scoreboard variable, datapack programming, kore compile time date-created: 2026-06-24 date-modified: 2026-06-24 routeOverride: /docs/concepts/runtime-logic position: 2 --- # Runtime Logic This is the single most important concept to understand when moving from raw datapacks (or any imperative language) to Kore. Get this right and everything else clicks into place. ## The two worlds When you write Kore, your code runs in **two completely separate worlds** at two different times: | | Compile-time (build) | Runtime (in-game) | |---------------------|--------------------------------------------------------|----------------------------------------------------| | **When** | When you run your `main` function to generate the pack | When Minecraft executes the generated functions | | **Language** | Kotlin | Minecraft commands (`.mcfunction`) | | **`val` / `var`** | Real Kotlin variables | Do not exist - use scoreboards / storage | | **`if` / `else`** | Real Kotlin branches | Do not exist - use `execute if` / predicates | | **`for` / `while`** | Real Kotlin loops | Do not exist - use recursion / `schedule` / macros | | **Knows** | Everything in your Kotlin code | Only the live world state | Kotlin `val`, `var`, `if`, `for`, `while` are **generation-time** tools. They decide *what commands get written into your datapack*. They are gone by the time the pack runs. They never look at the player, the world, or a score. To react to the live game (a player's score, a block in the world, an entity that exists right now), you need **runtime tools**: scoreboards, data storage, the `execute` command, predicates, and macros. ## Compile-time: Kotlin builds your commands Standard Kotlin runs once, when you generate the pack. Use it to remove repetition and assemble functions. ```kotlin function("setup_teams") { // This Kotlin `for` runs at generation time. It does NOT loop in-game. // It writes 3 separate command lines into the generated function. for (color in listOf("red", "blue", "green")) { scoreboard.objectives.add("kills_$color") } } ``` The generated `.mcfunction` is just three flat lines: ```mcfunction scoreboard objectives add kills_red dummy scoreboard objectives add kills_blue dummy scoreboard objectives add kills_green dummy ``` There is no loop left in the output. The `for` was a **code generator**, not in-game behavior. The same goes for `if`: ```kotlin val debugMode = true function("tick") { say("tick") if (debugMode) { // Included only because `debugMode` was true AT BUILD TIME. // Flip it to false and this command simply isn't generated. say("debug: tick ran") } } ``` ## Runtime: variables In-game you cannot store a value in a Kotlin `var`. The two runtime containers are **scoreboards** (for integers) and **data storage** (for any NBT: strings, lists, compounds, decimals). ### Scoreboards - integer variables See [Scoreboards](/docs/concepts/scoreboards) for the full command reference. ```kotlin load { scoreboard.objectives.add("coins") } function("give_coin") { // runtime: read-modify-write a per-player integer scoreboard.players.add(self(), "coins", 1) } ``` For arithmetic between scores, use `operation`, or reach for [Scoreboard Math](/docs/helpers/scoreboard-math) when you need trigonometry/algebra. ### Data storage - everything else See [Data Storage](/docs/concepts/data-storage) for the full guide. Storage holds strings, lists and compounds that scores cannot: ```kotlin val state = storage("state", "my_pack") function("set_name") { data(state) { set("player_name", "Steve") } } ``` ## Runtime: conditions (if / else) There is no in-game `if`. You branch with the `execute if` / `execute unless` chain ( see [Execute](/docs/commands/execute)) or by referencing a [Predicate](/docs/data-driven/predicates). ```kotlin function("reward_rich_players") { // runtime: "if the player's coins >= 100, run the reward" execute { ifCondition { score(self(), "coins", rangeOrIntStart(100)) // matches 100.. } run { say("You are rich!") } } } ``` ### Emulating if / else Minecraft has no `else`. The cleanest pattern is two mirrored checks (`if` then `unless`), or an early `return` so the second branch only runs when the first did not. ```kotlin function("check_score") { execute { ifCondition { score(self(), "coins", rangeOrIntStart(100)) } run { say("rich") } } execute { unlessCondition { score(self(), "coins", rangeOrIntStart(100)) } run { say("poor") } } } ``` For reusable, complex conditions, build a [Predicate](/docs/data-driven/predicates) once and reference it: ```kotlin val isRaining = predicate("is_raining") { weatherCheck(raining = true, thundering = false) } function("rain_warning") { execute { ifCondition(isRaining) run { say("It is raining") } } } ``` ## Runtime: loops There is no in-game `for` or `while`. The three runtime looping patterns are **function recursion**, **`schedule`**, and **iterating an entity selector**. ### Recursion (loop a fixed/conditional number of times) A function that calls itself runs again immediately, in the same tick, so the whole loop completes before the tick ends (the game caps it with the `max_command_sequence_length` gamerule). Use a score as the counter and `execute if`/`unless` as the guard so it stops; to spread iterations over several ticks, use `schedule` instead. ```kotlin load { scoreboard.objectives.add("countdown") scoreboard.players.set(self(), "countdown", 5) } val tickFunction = function("countdown_tick") { say("tick") scoreboard.players.remove(self(), "countdown", 1) // keep going only while countdown > 0 execute { ifCondition { score(self(), "countdown", rangeOrIntStart(1)) } // 1.. run(this@function) // self-recursion: call this same function again } } ``` ### `schedule` (loop over time) `schedule` re-runs a function after a delay - ideal for spacing work across ticks instead of hammering every tick. See [Time](/docs/concepts/time) for the `.ticks` / `.seconds` helpers. ```kotlin val loop = function("spawn_wave") { summon(EntityTypes.ZOMBIE, vec3()) schedule(2.seconds, this@function) // re-run myself in 2 seconds } ``` ### Iterating entities To "loop over all players", run the body **as** each matched entity - the selector does the iteration: ```kotlin function("heal_everyone") { execute { asTarget(allPlayers()) run { effect(self()) { give(Effects.REGENERATION, duration = 5) } } } } ``` ## Macros: when text must be dynamic A score holds a number, storage holds NBT, but sometimes you need to inject a runtime value into a part of a command that is normally fixed text (a name, a coordinate). That is what [Macros](/docs/commands/macros) are for - runtime string substitution. They are not variables: you cannot do math on them, only paste them into the command text. ```kotlin function("greet") { // `$(name)` is filled in from the NBT passed when the function is called say("Hello ${macro("name")}!") } ``` ## Mental model cheat sheet - Need to **remove repetition while writing** the pack? Use Kotlin `for` / `if` / functions. (compile-time) - Need to **remember a number** in-game? Scoreboard. (runtime) - Need to **remember text / a list / decimals** in-game? Data storage. (runtime) - Need to **branch** on live world state? `execute if` / `unless` or a predicate. (runtime) - Need to **repeat** in-game? Recursion, `schedule`, or an entity selector. (runtime) - Need to **paste a runtime value into command text**? Macro. (runtime) ## See also - [Execute](/docs/commands/execute) - the runtime control-flow command - [Data Storage](/docs/concepts/data-storage) - the runtime NBT variable container - [Scoreboards](/docs/concepts/scoreboards) - runtime integer variables --- ## Data Storage - Runtime NBT Variables with /data Command in Kore --- root: .components.layouts.MarkdownLayout title: Data Storage - Runtime NBT Variables with /data Command in Kore nav-title: Data Storage description: Use Minecraft data storage as runtime NBT variables in Kore. Set, modify, copy, and read values with the /data command DSL. Store strings, lists, and compounds beyond integer-only scoreboards. keywords: minecraft data storage, /data command, datapack nbt storage, data modify, data get, data merge, runtime variables minecraft, kore data, storage macros, datapack variable container date-created: 2026-06-24 date-modified: 2026-06-24 routeOverride: /docs/concepts/data-storage position: 3 --- # Data Storage Data storage is Minecraft's general-purpose **runtime variable container**. Where a [scoreboard](/docs/concepts/scoreboards) 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](/docs/concepts/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](https://minecraft.wiki/w/Commands/data). 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(...)`: ```kotlin val state = storage("state", "my_pack") // -> my_pack:state ``` 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`: ```kotlin function("init_state") { data(state) { set("player_name", "Steve") // string set("level", 1) // int set("ratio", 0.5f) // float set("active", true) // boolean } } ``` Each `set("path", value)` emits a `data modify storage my_pack:state set value ` line. ### Writing compounds and lists For structured data, use `merge` with an NBT builder, which writes a whole compound at once: ```kotlin function("init_player") { data(state) { merge { this["name"] = "Steve" this["level"] = 1 this["inventory"] = nbtListOf("sword", "shield") } } } ``` See [NBTs](/docs/concepts/nbts) for the full NBT builder DSL. ## Reading values Use `get` to read a value (Minecraft prints it / returns it as a command result): ```kotlin function("show_level") { data(state) { get("level") // data get storage my_pack:state level get("ratio", scale = 100.0) // read and scale the numeric 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](#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`: ```kotlin function("update_state") { data(state) { // overwrite a path modify("level") { set(2) } // copy a value from another data source (entity, block, or storage) modify("position") { set(self(), "Pos") } // list operations modify("inventory") { append("bow") } modify("inventory") { prepend("helmet") } modify("inventory") { insert(1, "potion") } // substring of a string source modify("initial") { set(self(), "SelectedItem.id", 0, 1) } } } ``` There are convenient shorthands for the common `set` cases: ```kotlin data(state) { modify("level", 5) // set to a literal modify("owner", self(), "UUID") // copy from another target's path } ``` ## Removing values ```kotlin function("reset_state") { data(state) { remove("inventory") remove("level") } } ``` ## Bracket shorthand For one-off reads and writes you can skip the block and use index syntax: ```kotlin function("quick") { data(state)["level"] = 5 // write data(state)["level"] // read (data get) } ``` ## 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](/docs/commands/execute)): ```kotlin function("level_to_score") { // read storage `level` and store it into the `levels` scoreboard objective execute { storeResult { score(self(), "levels") } run { data(state) { get("level") } } } } ``` 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](/docs/commands/macros) arguments. You can pass a storage path as the arguments to a function call, and its compound becomes the macro inputs: ```kotlin val teleport = function("teleport") { teleport(player(macro("name")), vec3()) } function("run_teleport") { // fills $(name) from the `target` compound in storage function(teleport, arguments = state, path = "target") } ``` ## 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](/docs/concepts/runtime-logic) - where storage fits in the compile-time vs runtime model - [NBTs](/docs/concepts/nbts) - the NBT builder DSL used by `set` / `merge` - [Execute](/docs/commands/execute) - `store` results into storage, branch with `if data` --- ## Chat Components --- root: .components.layouts.MarkdownLayout title: Chat Components nav-title: Chat Components description: A guide for creating Chat Components in a Minecraft datapack using Kore. keywords: minecraft, datapack, kore, guide, chat-components date-created: 2024-09-05 date-modified: 2026-10-07 routeOverride: /docs/concepts/chat-components --- # Chat Components Chat Components are used to create rich text messages in Minecraft. They can include formatting, interactivity, and nested components. Kore has functions to create and manipulate Chat Components in a datapack.
Note that they always work by groups named `ChatComponents`, whenever you create a chat component, you actually create a `ChatComponents`, and you can chain multiple components together using the `+` operator. Minecraft sometimes does not allow "complex" chat components with data resolving (`score`, `nbt` and `entity` chat components), if you use them, you'll get an empty text component. Simple chat components are inheriting the `SimpleComponent` interface, and you have a `containsOnlySimpleComponents` property to check if a `ChatComponents` only contains simple components. You also have a `containsOnlyText()` function to check if a `ChatComponents` only contains plain text components with no formatting. ### Common Properties - `bold` - Whether the text is bold. - `clickEvent` - The action to perform when the text is clicked. - `color` - The color of the text. - `extra` - Additional components to display after this one (prefer using the `+` operator). - `font` - The font to use. - `hoverEvent` - The action to perform when the text is hovered over. - `insertion` - The text to insert into the chat when the text is shift-clicked. - `italic` - Whether the text is italic. - `obfuscated` - Whether the text is obfuscated. - `shadowColor` - The color of the shadow behind the text, written as an ARGB int (use `argb(...)` to control transparency). - `strikethrough` - Whether the text is strikethrough. - `text` - The text to display. - `underlined` - Whether the text is underlined. ## PlainTextComponent The `PlainTextComponent` displays simple text with optional formatting such as color and bold.
To create a `PlainTextComponent`, use the `textComponent` function. ### Example ```kotlin val plainText = textComponent("Hello, world!") { color = Color.RED bold = true } ``` In-game output:
![Simple Hello World in bold red](/doc/chat-components/hello-world.png) See how to set custom colors in the [Colors](/docs/concepts/colors) article. A line break in the text (`"line 1\nline 2"`) is written as the `\n` escape in commands, since a raw one would split the function line in two, and a backslash is written as `\\` so the game doesn't read it as an escape. ### Combined Components Components can be combined using the `+` operator, use the `text` function to create a simple text component and not a `ChatComponents`. `+` returns new `ChatComponents` and leaves both sides untouched, so a shared prefix can be reused across messages. ```kotlin val combinedComponents = textComponent("Hello, ") + text("world!") { color = Color.RED bold = true } ``` In-game output:
![Combined Hello World](/doc/chat-components/combined-hello-world.png) > (only the "world!" part is bold and red) The game reads a list `[a, b]` as `a` with `b` among its children, so `b` inherits the style of `a`. When the first component carries a style, Kore writes an empty root first, so each component keeps its own style: ```kotlin textComponent("Gold ", Color.GOLD) + text("plain") // [{type:"text",text:""},{type:"text",color:"gold",text:"Gold "},{type:"text",text:"plain"}] ``` Item lore is the exception: its list holds one line per element, so it's written as is. ## EntityComponent The `EntityComponent` displays the name of an entity selected by a selector. If multiple entities are found, their names are displayed in the form `Name1, Name2` etc.
The `separator` property can be used to change the separator between the names of the entities.
If no entities are found, the component displays nothing. ### Example ```kotlin val entityComponent = entityComponent(self()) ``` In-game example:
![Hello World of the player](/doc/chat-components/entity.png) ## KeybindComponent The `KeybindComponent` displays a keybind. The keybind is displayed in the player's keybind settings. ### Example ```kotlin val keybindComponent = keybindComponent("key.sprint") ``` In-game example:
![Keybind Component Example](/doc/chat-components/keybind.png) ## NbtComponent The `NbtComponent` displays NBT data from a block, an entity, or a storage. The `interpret` property can be used to interpret the NBT data as a text component, if the parsing fails, nothing is displayed.
The `plain` property suppresses SNBT key/value coloring when set to `true`; it cannot be combined with `interpret`.
The `nbt` property can be used to specify the path to the NBT data.
If `nbt` points to an array, then it will display all the elements joined in the form `Element1, Element2` etc.
The `separator` property can be used to change the separator between the elements of the array. If you need a refresher on how Kore builds NBT in general, see [NBTs](/docs/concepts/nbts) for the shared builder DSL, common helpers, and other contexts that use the same patterns. ### Example ```kotlin val nbtComponent = nbtComponent("Health", entity = nearestEntity { type = EntityTypes.CREEPER }) ``` You can also point it at block positions or storages depending on the source you want to read from. In-game output:
![NBT Component Example](/doc/chat-components/nbt-health.png) ## ScoreComponent The `ScoreComponent` displays the score of an entity for a specific objective. The `name` property can be used to specify the name of the entity whose score to display, it can be a selector or a literal name (will use the player with that name). It can also be `*` to select the entity seeing the text component.
The `objective` property can be used to specify the name of the objective to display the score of.
A `value` property can be used to specify a fixed value to display regardless of the score. ### Example ```kotlin val scoreComponent = scoreComponent("test") ``` In-game output:
![Score Component Example](/doc/chat-components/score.png) ## TranslatedTextComponent The `TranslatedTextComponent` displays translated text using translation keys. You can also pass arguments to the translation key with the `with` argument, which is a list of strings or a list of components - so the result of any component factory ( `scoreComponent`, `entityComponent`, `textComponent`, etc) can be passed directly.
A `fallback` property can be used to specify a fallback text if the translation key is not found. ### Example ```kotlin // Strings are wrapped as text components automatically. val translatedFromStrings = translatedTextComponent("chat.type.text", listOf("Ayfri", "Hello !")) // Or pass components directly, no need to unwrap them. val translatedFromComponents = translatedTextComponent( "chat.type.text", listOf( textComponent("Ayfri", color = Color.AQUA), scoreComponent("kills", self()), ) ) ``` In-game output:
![Translated Text Component Example](/doc/chat-components/translation.png) ## Hover Event Hover events display extra information when the text is hovered over, it can be either text, an item, or an entity. Use `showText` to display text, `showItem` to display an item, and `showEntity` to display an entity.
Note that to show an entity, you have to have its UUID as a string. ### Hover Event Example ```kotlin val hoverEventComponent = textComponent("Hover over me!") { hoverEvent { showText("Hello, world!") } } ``` In-game output:
![Hover Event Example](/doc/chat-components/hover.png) ### Hover Item Example ```kotlin val hoverItemComponent = textComponent("Hover over me!") { hoverEvent { showItem(Items.DIAMOND_SWORD { damage(5) }) } } ``` In-game output:
![Hover Event with an Item Example](/doc/chat-components/hover-item.png) ## Click Event Click events perform an action when the text is clicked. The action can be to: - Change the page of the book if reading a book - Copy some text to the clipboard - Open a file - Open a URL - Run a command - Suggest a command (insert the command in the chat but don't run it) ### Click Event Example ```kotlin val clickEventComponent = textComponent("Click me!") { clickEvent { runCommand { say("Hello, world!") } } } ``` The `runCommand { }` and `suggestCommand { }` builders have no datapack in scope, so a function call inside them needs the function itself or a namespaced id. A bare name like `function("join")` throws because its namespace can't be resolved: ```kotlin val join = function("join") { say("Joined!") } val joinButton = textComponent("[Join]") { clickEvent { runCommand { function(join) } // or function("my_pack:join") } } ``` ## Object Components Object components render atlas sprites or player skins inside chat. They require the `ObjectTextComponent` family and can be built via `objectComponent` or `playerObjectComponent` depending on the source. All object components share a `fallback` property: a `ChatComponents` value used when the object cannot be displayed (for example, when printing messages in server logs or during narration). Pass it directly to the factory or set it in the builder block. ### AtlasObjectTextComponent - `atlas` - The atlas that contains the sprite. Optional when the sprite already resolves to an atlas entry, otherwise provide an explicit `AtlasArgument`. - `fallback` - Text component used when the sprite cannot be displayed (e.g. in server logs or during narration). - `sprite` - The `ModelArgument` that identifies the sprite to render and is required. Use `objectComponent` to construct atlas objects, optionally passing an atlas override and a fallback. ```kotlin val atlasObject = objectComponent( sprite = Textures.Block.COMMAND_BLOCK_BACK, atlas = Atlases.BLOCKS ) val atlasObjectWithFallback = objectComponent( sprite = Textures.Block.COMMAND_BLOCK_BACK, fallback = textComponent("command block") ) ``` In-game output:
![object-command-block.png](/doc/chat-components/object-command-block.png) ### PlayerObjectTextComponent - `fallback` - Text component used when the player model cannot be displayed (e.g. in server logs or during narration). - `hat` - Whether to display the player's hat layer (true/false) or leave it untouched when null. - `player` - A `PlayerProfile` describing the skin whose head should render; provide either a name, UUID, or both plus properties. `playerObjectComponent` accepts a `PlayerProfile`, name, or `UUIDArgument`, and the nested `player` block lets you add `PlayerProperty` estimations. ```kotlin val playerObject = playerObjectComponent("ayfri") { player { property("textures", "base64_encoded_texture_data") } } val playerObjectWithFallback = playerObjectComponent( playerName = "ayfri", fallback = textComponent("ayfri's head") ) ``` In-game output:
![player.png](/doc/chat-components/player.png) Notice that there is a shadow on the player's head, you can disable it by setting `shadowColor` to 0. ```kotlin val playerObject = playerObjectComponent("ayfri") { shadowColor = argb(0, 0, 0, 0) } ``` In-game output:
![player-no-shadow.png](/doc/chat-components/player-no-shadow.png) These components respect the same formatting as any other chat component, so you can still chain them, color them, or attach hover and click behaviors. ## Parsing existing components Chat components round-trip: besides building them in Kotlin, you can decode vanilla JSON (or SNBT) back into `ChatComponents` using `ChatComponents.serializer()`. Every component type is supported (text, translatable, score, selector, keybind, nbt, object), along with nested `extra`, styling, and hover/click events. A bare string, a single object, and an array of components are all accepted. ```kotlin val component = Json.decodeFromString( ChatComponents.serializer(), """{"type": "text", "text": "Hello", "color": "red"}""", ) ``` This is what the [datapack importer](/docs/advanced/bindings) uses to read components (such as a `pack.mcmeta` description) from existing packs. --- ## Colors --- root: .components.layouts.MarkdownLayout title: Colors nav-title: Colors description: Guide to using colors in Kore, including named colors, RGB/ARGB, dye colors, and how different contexts serialize them. keywords: minecraft, kore, colors, rgb, argb, dyes, components, particles date-created: 2025-08-11 date-modified: 2026-09-29 routeOverride: /docs/concepts/colors --- # Overview Kore provides a unified `Color` API that covers three families of colors used by Minecraft: - `FormattingColor` and `BossBarColor` (named colors for chat, UI, teams, etc.) - `RGB` and `ARGB` (numeric colors) - `DyeColors` (the 16 dye colors used by entities and items) For the vanilla reference on color behavior, see the [Minecraft Wiki - Color](https://minecraft.wiki/w/Color). ## Color types in Kore - `Color` (sealed interface): umbrella type accepted by most helpers. - `FormattingColor`: named chat/formatting colors (e.g. `Color.RED`, `Color.AQUA`). - `BossBarColor`: bossbar’s named colors. - `RGB` / `ARGB`: numeric colors. `RGB` is `#rrggbb`, `ARGB` is `#aarrggbb`. - `DyeColors`: 16 dye colors (white, orange, magenta, …, black) for collars, shulkers, sheep, tropical fish, etc. Helpers to create numeric colors: ```kotlin import io.github.ayfri.kore.arguments.colors.* val c1 = color(85, 255, 255) // RGB val c2 = color("#55ffff") // RGB from hex string val c3 = color(0x55ffff) // RGB from decimal val c4 = argb(255, 85, 255, 255) // ARGB val c5 = argb("#ff55ffff") // ARGB from hex string ``` Conversions and utils: ```kotlin val rgb = Color.AQUA.toRGB() // Named/Bossbar/ARGB → RGB val argb = rgb.toARGB(alpha = 200) val mixed = mix(rgb(255, 0, 0), 0.25, rgb(0, 0, 255), 0.75) ``` ## Random colors Kore provides static `random()` helpers on the color classes to simplify testing and procedural generation. You can also use a `Random` instance to generate random colors with a specific seed. These helpers are available on: - **`RGB`**: `RGB.random(random: Random = Random)`: returns a random RGB - **`ARGB`**: `ARGB.random(random: Random = Random, alpha: Boolean = false)`: returns a random ARGB; set `alpha = true` to randomize the alpha channel - **`FormattingColor`**: `FormattingColor.random(random: Random = Random)`: returns a random named formatting color - **`BossBarColor`**: `BossBarColor.random(random: Random = Random)`: returns a random named bossbar color ### Example usage: ```kotlin import kotlin.random.Random import io.github.ayfri.kore.arguments.colors.* import io.github.ayfri.kore.arguments.enums.DyeColors val randomRgb = RGB.random() val randomArgb = ARGB.random(random = Random(12345)) val randomArgbWithAlpha = ARGB.random(alpha = true) val randomFormatting = FormattingColor.random() ``` They make it easy to generate example content, tests, or procedurally-generated visuals. ## Serialization formats by context Different Minecraft systems expect colors in different formats. Kore picks the right format automatically via serializers. - Chat components `color`: string - Named colors emit lowercase names (e.g. `"red"`). - `RGB` emits `"#rrggbb"`; `ARGB` emits `"#aarrggbb"`. - Chat components `shadow_color`: signed ARGB int (`ColorAsARGBDecimalSerializer`), colors without alpha being fully opaque - `Color.BLUE` emits `-11184641` (`0xFF5555FF`), `argb(0, 0, 0, 0)` emits `0`. - Item components (decimal ints): - `dyedColor(..)`: decimal (or object with `rgb` decimal when tooltip flag is present) [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/arguments/components/item/DyedColorComponent.kt) ```kotlin @Serializable(RGB.Companion.ColorAsDecimalSerializer::class) var rgb: RGB, ``` - `mapColor(..)`: decimal [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/arguments/components/item/MapColorComponent.kt) ```kotlin InlineAutoSerializer(RGB.Companion.ColorAsDecimalSerializer, MapColorComponent::color, ::MapColorComponent) ``` - `potionContents(customColor=..)`: decimal [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/arguments/components/item/PotionContentsComponent.kt) ```kotlin @Serializable(RGB.Companion.ColorAsDecimalSerializer::class) var customColor: RGB? = null, ``` - Firework explosion `colors` / `fade_colors`: decimal list [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/arguments/components/item/FireworkExplosionComponent.kt) ```kotlin var colors: List<@Serializable(RGB.Companion.ColorAsDecimalSerializer::class) RGB>? = null, @SerialName("fade_colors") var fadeColors: List<@Serializable(RGB.Companion.ColorAsDecimalSerializer::class) RGB>? = null, ``` - Worldgen Biomes (decimal ints): - `effects.waterColor`, `effects.grassColor`, `effects.foliageColor`, etc. use decimal ints. - Sky/fog/water fog colors are set via **environment attributes** (`attributes`). [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/features/worldgen/biome/types/BiomeEffects.kt) ```kotlin // BiomeEffects (decimal ints in JSON) @Serializable(ColorAsDecimalSerializer::class) var waterColor: Color = color(4159204) @Serializable(ColorAsDecimalSerializer::class) var grassColor: Color? = null @Serializable(ColorAsDecimalSerializer::class) var foliageColor: Color? = null @Serializable(ColorAsDecimalSerializer::class) var dryFoliageColor: Color? = null // Environment attributes (also typically decimal ints for worldgen colors) attributes { skyColor(0x78A7FF) fogColor(0xC0D8FF) waterFogColor(0x050533) } ``` - Particles: - Command particles (decimal ints): Dust, DustColorTransition, Trail [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/commands/particle/types/DustParticleType.kt) ```kotlin var color: @Serializable(ColorAsDecimalSerializer::class) Color, ``` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/commands/particle/types/DustColorTransitionParticleType.kt) ```kotlin var fromColor: @Serializable(ColorAsDecimalSerializer::class) Color, var toColor: @Serializable(ColorAsDecimalSerializer::class) Color, ``` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/commands/particle/types/TrailParticleType.kt) ```kotlin var color: @Serializable(ColorAsDecimalSerializer::class) Color, ``` - Enchantment effect particles (double arrays `[r, g, b]` in 0..1): [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/features/enchantments/effects/entity/spawnparticles/types/DustParticleType.kt) ```kotlin var color: @Serializable(ColorAsDoubleArraySerializer::class) Color, ``` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/features/enchantments/effects/entity/spawnparticles/types/DustColorTransitionParticleType.kt) ```kotlin var fromColor: @Serializable(ColorAsDoubleArraySerializer::class) Color, var toColor: @Serializable(ColorAsDoubleArraySerializer::class) Color, ``` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/features/enchantments/effects/entity/spawnparticles/types/EffectParticleType.kt) ```kotlin var color: @Serializable(ColorAsDoubleArraySerializer::class) Color, ``` - UI and commands using named colors (strings): - Teams, Scoreboards, Bossbar: `FormattingColor` / `BossBarColor` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/commands/Teams.kt) ```kotlin fun color(color: FormattingColor) = fn.addLine(..., literal("color"), color) ``` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/commands/BossBar.kt) ```kotlin fun setColor(color: BossBarColor) = fn.addLine(..., literal("color"), color) ``` ## Dye colors and where they’re used `DyeColors` are used for entity variants and certain item/entity data components: - `catCollar(..)`, `wolfCollar(..)`, `sheepColor(..)`, `shulkerColor(..)` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/arguments/components/entity/CatCollar.kt) ```kotlin data class CatCollar(var color: DyeColors) fun ComponentsScope.catCollar(color: DyeColors) ``` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/arguments/components/entity/WolfCollar.kt) ```kotlin data class WolfCollar(var color: DyeColors) fun ComponentsScope.wolfCollar(color: DyeColors) ``` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/arguments/components/entity/SheepColor.kt) ```kotlin data class SheepColor(var color: DyeColors) fun ComponentsScope.sheepColor(color: DyeColors) ``` [See on GitHub](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/arguments/components/entity/ShulkerColor.kt) ```kotlin data class ShulkerColor(var color: DyeColors) fun ComponentsScope.shulkerColor(color: DyeColors) ``` ## Practical examples Chat components (string serialization): ```kotlin import io.github.ayfri.kore.arguments.chatcomponents.textComponent import io.github.ayfri.kore.arguments.colors.Color val title = textComponent("Legendary Sword", Color.AQUA) ``` Dyed leather color (decimal serialization): ```kotlin import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.arguments.colors.Color val dyedHelmet = Items.LEATHER_HELMET { dyedColor(Color.AQUA) } ``` Map color (decimal serialization): ```kotlin import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.arguments.colors.rgb val withMapColor = Items.STONE { mapColor(rgb(85, 255, 255)) } ``` Fireworks (decimal lists): ```kotlin import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.arguments.components.item.FireworkExplosionShape import io.github.ayfri.kore.arguments.colors.Color val rocket = Items.FIREWORK_ROCKET { fireworks(flightDuration = 1) { explosion(FireworkExplosionShape.BURST) { colors(Color.AQUA) fadeColors(Color.BLACK, Color.WHITE) hasTrail = true hasTwinkle = true } } } ``` Biome effects (decimal): ```kotlin import io.github.ayfri.kore.features.worldgen.biome.types.BiomeEffects import io.github.ayfri.kore.arguments.colors.color val effects = BiomeEffects( waterColor = color(4159204), grassColor = color(0x79C05A) ) ``` Particles - Command dust (decimal): ```kotlin import io.github.ayfri.kore.commands.particle.types.Dust import io.github.ayfri.kore.arguments.colors.Color val p = Dust(color = Color.RED, scale = 1.0) ``` - Enchantment dust (double array): ```kotlin import io.github.ayfri.kore.features.enchantments.effects.entity.spawnparticles.types.DustParticleType import io.github.ayfri.kore.generated.arguments.types.ParticleTypeArgument import io.github.ayfri.kore.arguments.colors.rgb val enchantDust = DustParticleType( type = ParticleTypeArgument("dust"), color = rgb(255, 0, 0) ) ``` Entity variant dye usage: ```kotlin import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.arguments.enums.DyeColors val cat = Items.CAT_SPAWN_EGG { catCollar(DyeColors.RED) } ``` ## Notes - Passing `Color` to component helpers that use decimal or double-array formats is safe: Kore converts automatically via `toRGB()`. - Use `DyeColors` when the game mechanic expects a dye color (collars, sheep, shulkers, tropical fish), and `FormattingColor`/ `BossBarColor` for chat/UI tints. ## Further reading - [Chat Components](/docs/concepts/chat-components) - Use colors in chat components - [Minecraft Wiki - Color](https://minecraft.wiki/w/Color) --- ## Components --- root: .components.layouts.MarkdownLayout title: Components nav-title: Components description: A guide for using components in Minecraft with Kore. keywords: minecraft, datapack, kore, guide, components date-created: 2024-01-08 date-modified: 2026-10-08 routeOverride: /docs/concepts/components --- In Minecraft, data components are structured key-value properties used to define and store behavior and attributes. They are attached to different things: - Item components: properties that live on item stacks (e.g., `enchantments`, `food`, `attribute_modifiers`). They affect how items behave in inventories, commands, containers, etc. - Entity variant components: properties exposed as components for certain entity variants when represented as items or spawn eggs (e.g., `wolf/variant`, `cat/collar`). These follow the same component mechanics but target entity-specific customization. This page focuses on using item components with Kore. For the vanilla reference and exhaustive definitions, see the [Minecraft Wiki - Data component format](https://minecraft.wiki/w/Data_component_format). The [custom item example](/playground#example=custom-item) of the playground builds an item with components and runs in your browser. The Kore library provides a comprehensive and user-friendly way to work with these components, enabling you to create custom items with ease. This article will guide you through the process of using components with Kore, showcasing examples and best practices. ## Creating Custom Items with Components Let's dive into creating custom items with various components using Kore. Below are examples of how to define and manipulate item properties such as attribute modifiers, enchantments, and more. ### Attribute Modifiers Attribute modifiers allow you to alter the attributes of an item, such as increasing damage or changing the scale. Here's how to define a stone sword with an attribute modifier using Kore: ```kotlin import io.github.ayfri.kore.commands.AttributeModifierOperation import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.generated.Attributes val attributeModifiersTest = Items.STONE_SWORD { attributeModifiers { modifier( type = Attributes.SCALE, amount = 1.0, name = "big", operation = AttributeModifierOperation.ADD_VALUE, ) } } ``` ### Enchantments You can add enchantments to items to give them special abilities. Here’s an example of adding the Sharpness enchantment to a stone sword: ```kotlin import io.github.ayfri.kore.generated.Enchantments val enchantmentsTest = Items.STONE_SWORD { enchantments(mapOf(Enchantments.SHARPNESS to 5)) } ``` Check out the [Enchantments](/docs/data-driven/enchantments) article for more information on how to use enchantments with Kore. ### Custom Names and Lore Custom names and lore can be added to items to give them unique identifiers and background stories: ```kotlin import io.github.ayfri.kore.arguments.chatcomponents.textComponent import io.github.ayfri.kore.arguments.colors.Color val customNameTest = Items.STONE_SWORD { customName(textComponent("Legendary Sword", Color.AQUA)) } ``` ### Fireworks You can define the properties of fireworks, including the shape and colors of the explosions: ```kotlin import io.github.ayfri.kore.arguments.components.item.FireworkExplosionShape import io.github.ayfri.kore.arguments.colors.Color val fireworksTest = Items.FIREWORK_ROCKET { fireworks(flightDuration = 1) { explosion(FireworkExplosionShape.BURST) { colors(Color.AQUA) fadeColors(Color.BLACK, Color.WHITE) hasTrail = true hasTwinkle = true } } } ``` See how to set custom colors in the [Colors](/docs/concepts/colors) article. ### Custom Block Data You can define custom properties for blocks using block entity data. Here's an example of adding custom data to a bee nest block: ```kotlin import io.github.ayfri.kore.generated.Blocks import io.github.ayfri.kore.generated.Items val blockEntityDataTest = Items.BEE_NEST { blockEntityData(Blocks.BEE_NEST) { this["test"] = "test" } } ``` ### Profile The profile component can be either a player profile or a texture-based profile. #### Player Profile A player profile uses a player's name or UUID: ```kotlin Items.PLAYER_HEAD { playerProfile("Notch") } ``` #### Texture Profile A texture profile allows you to specify textures and models: ```kotlin Items.PLAYER_HEAD { textureProfile(texture = "tex", model = MannequinModel.SLIM) } ``` ### Recipes result with components You can define recipes with components as well. Here's an example of crafting a custom enchanted golden apple using a shaped recipe: ```kotlin recipes { craftingShaped("enchanted_golden_apple") { pattern( "GGG", "GAG", "GGG" ) key("G", Items.GOLD_BLOCK) key("A", Items.APPLE) result(Items.ENCHANTED_GOLDEN_APPLE { food(nutrition = 10, saturation = 5.0f) consumable(consumeSeconds = 1.6f, animation = ConsumeAnimation.EAT, sound = SoundEvents.Entity.Generic.EAT) { onConsumeEffects { applyEffects( probability = 1f, Effect( id = Effects.REGENERATION, duration = 40, amplifier = 1, ambient = true, showParticles = true, showIcon = true ) ) } } }) } } ``` ## Example Usage To give yourself an item with custom components using the `/give` command, you can define the item and its components as shown in the following example: ```kotlin import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.utils.set // Define the item with a custom name val customStone = Items.STONE { damageResistant(Tags.DamageType.IS_FIRE) customName(textComponent("Special Stone", Color.AQUA)) rarity(Rarities.EPIC) lore( textComponent("A stone with special properties.", Color.GRAY) + text("Use it wisely!", Color.GRAY) ) } // Use the /give command to give the item to yourself give(self(), customStone) ``` This example creates a custom stone item with a special name "Special Stone" in aqua color and gives it to the player using the `/give` command. ### Full list of Item components Below is an alphabetical list of all item component helpers available in Kore. The names match the DSL functions you call inside an `Items.* { }` builder. | Helper | Description | |--------------------------------------|----------------------------------------------------------------------------------------------------------| | `additionalTradeCost(..)` | Sets an extra emerald cost added on top of the base price for villager trades. | | `attackRange(..)` | Configures the attack range of an item (min/max reach, hitbox margin, mob factor). | | `attributeModifiers(..)` | Modifies entity attributes (e.g., attack damage, speed, armor) when the item is equipped or held. | | `bannerPatterns(..)` | Defines the layered patterns displayed on a banner or shield. | | `baseColor(..)` | Sets the base color of a banner before patterns are applied. | | `bees { .. }` | Stores bee entities inside a beehive or bee nest item. | | `blockEntityData(..)` | Attaches custom NBT data to a block entity when the item is placed. | | `blocksAttacks(..)` | Configures how the item blocks incoming attacks when used (like a shield). | | `blockState(..)` | Sets block state properties (e.g., facing, powered) when the item is placed. | | `breakSound(..)` | Specifies the sound played when the item breaks from durability loss. | | `bucketEntityData(..)` | Stores entity data for mobs captured in buckets (e.g., fish, axolotl). | | `bundleContents(..)` | Defines the items stored inside a bundle. | | `canBreak(..)` | Restricts which blocks this item can break in Adventure mode. | | `canPlaceOn(..)` | Restricts which blocks this item can be placed on in Adventure mode. | | `chargedProjectiles(..)` | Stores projectiles loaded into a crossbow. | | `consumable(..) { .. }` | Makes the item consumable with configurable eating time, animation, sound, and effects. | | `container(..)` | Stores items inside a container item (e.g., shulker box). | | `containerLoot(..)` | References a loot table to generate container contents when opened. | | `customData(..)` | Attaches arbitrary custom NBT data for use by datapacks or mods. | | `customModelData(..)` | Provides numeric values for custom item model selection in resource packs. | | `customName(..)` | Sets a custom display name for the item (supports text components). | | `damage(..)` | Sets the current damage/durability consumed on a damageable item. | | `damageResistant(..)` | Makes the item entity resistant to specific damage types (e.g., fire, explosions). | | `damageType(..)` | Specifies the damage type dealt when attacking with this item. | | `deathProtection(..)` | Prevents death and applies effects when the holder would die (like a totem). | | `debugStickState(..)` | Stores the selected block state property for the debug stick per block type. | | `dye(..)` | Sets the dye color of an item using a `DyeColors` value. | | `dyedColor(..)` | Sets the dye color for leather armor or other dyeable items. | | `enchantable(..)` | Defines the enchantability value affecting enchantment quality at enchanting tables. | | `enchantmentGlintOverride(..)` | Forces the enchantment glint on or off regardless of enchantments. | | `enchantments(..)` | Applies enchantments with their levels to the item. | | `entityData(..)` | Stores entity NBT data for spawn eggs or items that spawn entities. | | `equippable(..)` | Configures equipment slot, sounds, and `EquipmentAssets` model when the item is worn. | | `fireworkExplosion(..)` | Defines a single firework star explosion shape, colors, and effects. | | `fireworks(..)` | Configures firework rocket flight duration and explosion effects. | | `food(..)` | Makes the item edible with nutrition, saturation, and optional effects. | | `glider()` | Enables elytra-like gliding when equipped in the chest slot. | | `instrument(..)` | Specifies the goat horn sound variant when the item is used. | | `intangibleProjectile()` | Makes projectiles from this item pass through entities without collision. | | `itemModel(..)` | Overrides the item's model with a custom model resource location. | | `itemName(..)` | Sets the item's base name (different from custom name; not italicized). | | `jukeboxPlayable(..)` | Allows the item to be played in a jukebox with a specified music disc track. | | `kineticWeapon(..) { .. }` | Configures kinetic weapon properties for mounted combat (damage multiplier, conditions). | | `lock(..)` | Locks a container so only players holding a matching item (item predicate) can open it. | | `lodestoneTarget(..)` | Makes a compass point to specific coordinates in a dimension. | | `lore(..)` | Adds tooltip lines below the item name for descriptions or flavor text. | | `mapColor(..)` | Sets the color tint for filled map item textures. | | `mapDecorations(..)` | Adds custom icons/markers displayed on a filled map. | | `mapId(..)` | Links the item to a specific map data ID for filled maps. | | `maxDamage(..)` | Sets the maximum durability before the item breaks. | | `maxStackSize(..)` | Overrides how many items can stack in a single inventory slot (1-99). | | `minimumAttackCharge(..)` | Sets the minimum attack charge (0.0-1.0) required for full damage. | | `noteBlockSound(..)` | Specifies the sound a note block plays when this player head is above it. | | `ominousBottleAmplifier(..)` | Sets the Bad Omen effect amplifier (0-4) when consuming an ominous bottle. | | `piercingWeapon(..) { .. }` | Configures piercing weapon properties (knockback, dismount behavior). | | `playerProfile(..)` | Sets the player skin displayed on a player head item. | | `potDecorations(..)` | Defines the item stack on each face of a decorated pot (`back`, `left`, `right`, `front`). | | `potionContents(..)` | Configures potion color, effects, and custom potion mixtures. | | `potionDurationScale(..)` | Multiplies the duration of potion effects from this item. | | `providesBannerPatterns(..)` | Registers this item as a banner pattern source for the loom. | | `providesPotteryPattern(..)` | Sets the pattern this item shows on the face of a decorated pot. | | `providesTrimMaterial(..)` | Registers this item as an armor trim material for the smithing table. | | `rarity(..)` | Sets the item name color tier (common, uncommon, rare, epic). | | `recipes(..)` | Unlocks specified recipes when this knowledge book is used. | | `repairable(..)` | Defines which items can repair this item on an anvil. | | `repairCost(..)` | Sets the anvil repair cost penalty for combining or renaming. | | `storedEnchantments(..)` | Stores enchantments in an enchanted book for anvil application. | | `sulfurCubeContent(..)` | Stores the item held inside a sulfur cube. | | `suspiciousStewEffectsComponent(..)` | Defines the status effects applied when consuming suspicious stew. | | `swingAnimation(..)` | Configures the swing animation type (none, stab, whack) and duration. | | `tool { .. }` | Configures mining speeds, suitable blocks, and durability cost for tools. | | `tooltipDisplay(..)` | Controls which tooltip sections are shown or hidden. | | `tooltipStyle(..)` | Applies a custom tooltip background/border style from a resource pack. | | `trim(..)` | Applies an armor trim pattern and material to armor items. | | `unbreakable()` | Prevents the item from taking durability damage. | | `useCooldown(..)` | Applies a cooldown period after using this item. | | `useEffects(..)` | Configures use effects like allowing sprinting, interacting vibrations and speed multiplier while using. | | `useRemainder(..)` | Specifies an item left behind after this item is fully consumed. | | `weapon(..)` | Configures melee weapon properties like damage and attack speed. | | `writableBookContent(..)` | Stores editable pages in a book and quill. | | `writtenBookContent(..)` | Stores signed book content including title, author, and pages. | ## Works great with Inventory Manager If you build rich items with components and want to enforce them in player GUIs or chest slots, pair them with the [Inventory Manager](/docs/helpers/inventory-manager). It lets you keep specific items in slots, react to takes, and clean up other slots while preserving all component data. ## Patch items When you need to patch components on existing stacks (set name/lore, toggle tooltips, edit container contents, etc.) at runtime, use [Item Modifiers](/docs/data-driven/item-modifiers). Kore maps the vanilla functions like `set_components`, `set_contents`, `set_fireworks`, and more. ## Custom Component For a component Kore has no helper for (a modded one, or a brand-new vanilla one), wrap its raw NBT in a `CustomComponent` and store it under the component name. An extension on `ComponentsScope` gives it the same call syntax as the built-in helpers: ```kotlin package your.package import io.github.ayfri.kore.arguments.components.ComponentsScope import io.github.ayfri.kore.arguments.components.item.CustomComponent import io.github.ayfri.kore.arguments.types.resources.FunctionArgument import io.github.ayfri.kore.utils.nbt import io.github.ayfri.kore.utils.set fun ComponentsScope.use(function: FunctionArgument, damage: Int = 0, cooldown: Float = 0f) = apply { this["mymod:use"] = CustomComponent(nbt { this["function"] = function.asId() this["damage"] = damage this["cooldown"] = cooldown }) } ``` And here's how you can use this custom component in an item definition: ```kotlin import io.github.ayfri.kore.generated.Items import your.package.use val myFunction = function("use_weapon") { // Your function code here. } val customItem = Items.DIAMOND_SWORD { use(myFunction, damage = 4, cooldown = 1.5f) } // Result: // minecraft:diamond_sword[mymod:use={function:"datapack:use_weapon",damage:4,cooldown:1.5f}] ``` ## Entity Variant Components (25w04a+) > *Introduced in snapshot [25w04a](https://www.minecraft.net/en-us/article/minecraft-snapshot-25w04a)* > > Entity variants such as axolotl colours, cat collars or tropical-fish patterns are now exposed as **data components** and can be used on entities, spawn-egg items, mob buckets and paintings. This replaces the old `type_specific` NBT fields. Kore ships dedicated helpers for each of these components. You can attach them to an `Items.*` builder the exact same way you attach any other component: ```kotlin import io.github.ayfri.kore.arguments.enums.* import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.generated.PaintingVariants // Axolotl bucket with the blue variant val blueAxolotlBucket = Items.AXOLOTL_BUCKET { axolotlVariant(AxolotlVariants.BLUE) } // Cat spawn-egg with a red collar val redCollarCat = Items.CAT_SPAWN_EGG { catCollar(DyeColors.RED) } // Painting item selecting the "kebab" variant (namespace implied) val kebabPainting = Items.PAINTING { paintingVariant(PaintingVariants.KEBAB) } ``` These components can also be queried inside predicates: ```kotlin predicate("only_blue_axolotls") { entityProperties { components { axolotlVariant(AxolotlVariants.BLUE) } } } ``` The full list of variant helpers currently included in Kore is: - `axolotlVariant(..)` - `catCollar(..)` / `catVariant(..)` - `chickenVariant(..)` - `cowVariant(..)` - `foxVariant(..)` - `frogVariant(..)` - `horseVariant(..)` - `llamaVariant(..)` - `mooshroomVariant(..)` - `paintingVariant(..)` - `parrotVariant(..)` - `pigVariant(..)` - `rabbitVariant(..)` - `salmonSize(..)` - `sheepColor(..)` - `shulkerColor(..)` - `tropicalFishBaseColor(..)` / `tropicalFishPattern(..)` / `tropicalFishPatternColor(..)` - `villagerVariant(..)` - `wolfCollar(..)` / `wolfSoundVariant(..)` / `wolfVariant(..)` - `zombieNautilusVariant(..)` They follow the exact same naming and DSL pattern you already know: ```kotlin fun ComponentsScope.wolfVariant(variant: WolfVariants) { /*…*/ } ``` Because the logic lives in regular data components, you automatically get: - Compatibility with the `itemStack` / `Items.*` DSL - Predicate support through `components {}` - Correct serialization back to the vanilla command and NBT syntax Feel free to mix several variant components on the same `ComponentsScope`: ```kotlin Items.WOLF_SPAWN_EGG { wolfCollar(DyeColors.BLACK) wolfVariant(WolfVariants.SNOWY) } ``` ## Component Matchers & Item Predicates Kore provides powerful tools for matching and filtering items based on their components. This is useful in predicates, execute conditions, and loot tables. There are two distinct forms: - **Item predicates** (`.predicate { }` / `itemPredicate { }`) produce the inline command syntax `[predicate]`, used anywhere an item argument is accepted - `/give`, `/clear`, the `items` selector, `execute if items`. - **Component matchers** (`predicates { }` inside `matchTool { }`, or `subPredicates { }` inside `.predicate { }`) check the same components from within a predicate *file* - see [Item Predicates](/docs/data-driven/predicates#item-predicates) and [Item Sub-Predicates](/docs/data-driven/predicates#item-sub-predicates) in the Predicates guide for how they plug into a full predicate. Both forms share the same set of component checks - see [Available Component Matchers](#available-component-matchers) below for the full list. ### Item Predicates Item predicates let you filter items by their components using the command syntax `[predicate]`: ```kotlin import io.github.ayfri.kore.arguments.components.* import io.github.ayfri.kore.arguments.components.item.* import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.generated.ItemComponentTypes // Match items with specific component values val damagedSword = Items.DIAMOND_SWORD.predicate { damage(10) } // Result: minecraft:diamond_sword[damage=10] // Match any item with a component present (existence check) val hasInstrument = itemPredicate { isPresent(ItemComponentTypes.INSTRUMENT) } // Result: *[instrument] // Partial matching with ~ syntax val customDataMatch = Items.STONE.predicate { customData { this["myKey"] = "myValue" } partial(ItemComponentTypes.CUSTOM_DATA) } // Result: minecraft:stone[custom_data~{myKey:"myValue"}] // Negated predicates (component must NOT have this value) val notDamaged = Items.DIAMOND_SWORD.predicate { !damage(0) } // Result: minecraft:diamond_sword[!damage=0] // Multiple alternatives with OR val multipleValues = Items.STONE.predicate { damage(1) or damage(2) or damage(3) } // Result: minecraft:stone[damage=1|damage=2|damage=3] // Count predicate val stackOf10 = Items.DIAMOND.predicate { count(10) } // Result: minecraft:diamond[count=10] ``` #### Practical Example: Clearing Items by Name A common vanilla pattern is clearing every item with a specific `item_name`, regardless of its item type - the raw command looks like `clear @s *[minecraft:item_name="Blank"] 2`. It breaks down into three pieces: `@s` is the target, `*[minecraft:item_name="Blank"]` is an item predicate matching **any** item (`*`) whose `item_name` component equals `"Blank"`, and `2` is the max count to remove. Build the same thing with Kore's `clear` command and `itemPredicate { }`: ```kotlin import io.github.ayfri.kore.commands.clear import io.github.ayfri.kore.arguments.types.literals.self function("clear_blank_items") { clear(self(), itemPredicate { itemName("Blank") }, 2) } ``` This generates `clear @s *[item_name="Blank"] 2`. Because no `ItemArgument` is passed to `itemPredicate { }`, it defaults to `*` (any item) - pass an item to `.predicate { }` instead (e.g. `Items.PAPER.predicate { itemName("Blank") }`) if you want to restrict the match to a specific item type too. ### Component Matchers (Sub-Predicates) For matching logic beyond a plain item predicate, use the `predicates { }` builder inside `matchTool { }` (or `subPredicates { }` inside `.predicate { }`) with component matchers: ```kotlin import io.github.ayfri.kore.arguments.components.matchers.* import io.github.ayfri.kore.arguments.numbers.ranges.rangeOrInt predicate("upgradeable_pickaxe") { matchTool { items(Items.DIAMOND_PICKAXE) predicates { // Match damage component with range damage { durability = rangeOrInt(1..100) damage = rangeOrInt(0..10) } // Match enchantments enchantments { enchantment(Enchantments.SHARPNESS, level = 3) } // Match potion contents potionContents(Potions.SWIFTNESS, Potions.STRENGTH) } } } ``` ### Existence Checks You can check if a component exists on an item without matching a specific value. In an item predicate (command form), use `isPresent`: ```kotlin val hasInstrument = itemPredicate { isPresent(ItemComponentTypes.INSTRUMENT) } // Result: *[instrument] ``` Inside a `predicates { }` / `subPredicates { }` block, use `exists`: ```kotlin predicate("has_instrument") { matchTool { predicates { exists(ItemComponentTypes.INSTRUMENT) exists(ItemComponentTypes.DAMAGE) } } } ``` ### Available Component Matchers | Matcher | Description | |---------------------------|------------------------------------------------| | `attributeModifiers { }` | Match attribute modifier properties | | `bundleContents { }` | Match bundle contents | | `container { }` | Match container slot contents | | `customData { }` | Match custom NBT data | | `damage { }` | Match damage/durability values | | `enchantments { }` | Match enchantment types and levels | | `exists(component)` | Check if component exists (empty `{}` matcher) | | `fireworkExplosion { }` | Match firework star properties | | `fireworks { }` | Match firework rocket properties | | `jukeboxPlayable { }` | Match jukebox song | | `potionContents(..)` | Match potion types or a potion tag | | `storedEnchantments { }` | Match stored enchantments (enchanted books) | | `trim { }` | Match armor trim pattern/material | | `villagerVariant(..)` | Match the villager type of an entity | | `writableBookContent { }` | Match book pages | | `writtenBookContent { }` | Match signed book content | ### Complete Example: Custom Tool Upgrade System Here's a practical example showing how to use item predicates to create a tool upgrade system that detects enchanted, damaged tools and replaces them with upgraded versions: ```kotlin dataPack("tool_upgrades") { // Predicate to find diamond swords that need upgrading predicate("upgradeable_sword") { matchTool { items(Items.DIAMOND_SWORD) predicates { // Must have Sharpness enchantment enchantments { enchantment(Enchantments.SHARPNESS, level = rangeOrInt(1..4)) } // Must be damaged (durability used) damage { damage = rangeOrInt(1..1000) } } } } // Swaps the item id while keeping every component, enchantments included val upgradeToNetherite = itemModifier("upgrade_to_netherite") { setItem(Items.NETHERITE_SWORD) } // Function to check player's held item and upgrade it function("check_upgrade") { // Check if holding an upgradeable sword execute { ifCondition { items( self(), WEAPON.MAINHAND, Items.DIAMOND_SWORD.predicate { subPredicates { enchantments { enchantment(Enchantments.SHARPNESS, level = rangeOrInt(3..4)) } } } ) } run { // Replace with netherite sword keeping enchantments items.modify(self(), WEAPON.MAINHAND, upgradeToNetherite) tellraw(self(), textComponent("Your sword has been upgraded!", Color.GOLD)) } } } // Clear specific items from inventory using predicates function("clear_broken_tools") { // Clear any tool with 1 durability left clear(allPlayers(), itemPredicate { subPredicates { damage { durability = rangeOrInt(1) } } }) } // Give reward only if player has specific item combination function("check_collection") { execute { // Check for a goat horn (any variant) ifCondition { items( self(), INVENTORY, itemPredicate { isPresent(ItemComponentTypes.INSTRUMENT) } ) } // Check for enchanted book with Mending ifCondition { items( self(), INVENTORY, Items.ENCHANTED_BOOK.predicate { subPredicates { storedEnchantments { enchantment(Enchantments.MENDING) } } } ) } run { give(self(), Items.NETHER_STAR) } } } } ``` ## Conclusion Components are a powerful tool for customizing Minecraft objects, and the Kore library makes it easier than ever to work with these components programmatically. Whether you're adding custom attributes, enchantments, or creating complex items with multiple components, Kore provides a robust and intuitive API for enhancing your Minecraft experience. By following the examples and practices outlined in this article, you can leverage the full potential of components in your Minecraft projects, creating richer and more engaging content for players. Happy crafting! ## See Also - [Predicates](/docs/data-driven/predicates) - Use components in predicate conditions; see [Item Predicates](/docs/data-driven/predicates#item-predicates) and [Item Sub-Predicates](/docs/data-driven/predicates#item-sub-predicates) for how the matchers on this page plug into a full `predicate { }` file - [Item Modifiers](/docs/data-driven/item-modifiers) - Patch components at runtime - [Recipes](/docs/data-driven/recipes) - Use components in recipe results - [Inventory Manager](/docs/helpers/inventory-manager) - Enforce component-rich items in slots ### External Resources - [Minecraft Wiki: Data component format](https://minecraft.wiki/w/Data_component_format) - Official component reference --- ## NBTs --- root: .components.layouts.MarkdownLayout title: NBTs nav-title: NBTs description: Work with Minecraft NBT data in Kore using a shared Kotlin DSL across commands, predicates, and chat components. Includes SNBT helpers, path access, and reuse patterns. keywords: minecraft, datapack, kore, nbt, snbt, knbt, chat components, predicates, data command date-created: 2026-05-29 date-modified: 2026-09-29 routeOverride: /docs/concepts/nbts --- # NBTs NBT (Named Binary Tag) is Minecraft's structured data format. Kore uses it anywhere vanilla expects embedded NBT or SNBT, such as: - command payloads like `summon`, `data merge`, or [storage writes](/docs/concepts/data-storage) - chat components that read values from blocks, entities, or storage - predicate sub-structures that expose an `nbt { ... }` block - helpers and domain objects that serialize themselves with `toNbt()` Kore's NBT support is built on top of [`knbt`](https://github.com/BenWoodworth/knbt), so the same builder style is reused throughout the DSL. An NBT **compound** is the object/map-shaped tag in NBT: a group of named entries such as `CustomName`, `Health`, or `display`. In Kore, `nbt { ... }` builds that compound structure. ## Core builder The main entry point is `nbt { ... }`, which creates an `NbtCompound`. ```kotlin val customData = nbt { this["CustomName"] = "Hero" this["Health"] = 20 this["Invulnerable"] = true this["Tags"] = nbtListOf("kore", "example") this["Item"] = nbt { this["id"] = "minecraft:diamond_sword" this["count"] = 1 } } ``` This pattern is used by Kore in most places that accept raw NBT. When you write compounds manually, it is often worth sorting keys by name to keep large payloads easier to scan and diff. ## Common ways to build values Inside an `nbt { ... }` block, the most common pattern is assigning with `this["key"] = value`. ### Primitive values ```kotlin val payload = nbt { this["cooldown"] = 40.toShort() this["Slot"] = 1.toByte() this["enabled"] = true this["level"] = 3 this["name"] = "Kore" this["seed"] = 1234L this["speed"] = 0.5 } ``` Vanilla key names are not perfectly consistent across Minecraft versions. Mojang used `PascalCase` for most older keys, then introduced some newer keys in `camelCase` for a while, and more recent additions often use `snake_case`. The migration is slow, so real-world NBT frequently mixes conventions in the same compound. Because of that, I advise you to not guess key names. Always verify them against the Minecraft Wiki, or inspect the exact live data with `/data get entity ...`, `/data get block ...`, or `/data get storage ...` in-game. ### Nested compounds Use another `nbt { ... }` block for a nested compound when one key contains another group of named values: ```kotlin val nested = nbt { this["components"] = nbt { this["minecraft:custom_name"] = "Treasure" } } ``` ### Lists Use `nbtListOf(...)` for a homogeneous NBT list when you already know the elements: ```kotlin val listExample = nbt { this["Pos"] = nbtListOf(0.0, 64.0, 0.0) this["Tags"] = nbtListOf("boss", "phase_1") } ``` `knbt` lists are typed, so a single NBT list must contain the same kind of values. If you want to build a list incrementally, use `nbtList { ... }`: ```kotlin val passengers = nbtList { addNbtCompound { this["CustomName"] = "Left Guard" this["id"] = "minecraft:armor_stand" } addNbtCompound { this["CustomName"] = "Right Guard" this["id"] = "minecraft:armor_stand" } } ``` This is especially useful for lists of compounds, where each element is itself a small NBT object. ### Raw SNBT when needed Most of the time you should prefer the typed builders above. When an API takes SNBT text instead of an `NbtTag`, call `toSnbt()` on the tag: `nbt { this["Count"] = 3.toByte() }.toSnbt()` gives `{Count:3b}`, with strings fully escaped. `encodeToSnbt(value)` does the same for any `@Serializable` value. The reverse direction is `String.toNbt()`, which parses an SNBT compound back into an `NbtCompound`. It is the shortest way to reuse a snippet copied from a command or the wiki without transcribing it into a builder: ```kotlin function("raw_snbt") { data(storage("shop", "kore")).modify("offer", """{id:"minecraft:diamond",count:3}""".toNbt()) } ``` `String.toNbtTag()` parses any SNBT value instead, including lists, arrays, and bare primitives such as `[I;1,2]` or `12b`. Both throw on invalid input, so keep them for constants you control rather than for runtime strings. See [Known Issues](/docs/advanced/known-issues#nbt-and-snbt-via-knbt) for the main `knbt` limitations and trade-offs. ## Where you use NBT in Kore Different APIs expose NBT in different ways, but the underlying builder style stays the same. ### Commands Commands usually accept an `NbtCompound` directly. ```kotlin function("summon_example") { summon(EntityTypes.ARMOR_STAND, vec3(0, 64, 0), nbt = nbt { this["CustomName"] = "Guide" this["NoGravity"] = true }) } ``` The `data` command also uses NBT builders naturally: ```kotlin function("storage_seed") { data(self()) { merge { this["CustomName"] = "Hero" this["Invulnerable"] = true } } } ``` If you are learning the command surface itself, see [Commands](/docs/commands/commands#data-command). ### Chat components `NbtComponent` reads a value from block, entity, or storage NBT and displays it in chat. ```kotlin val nameFromStorage = nbtComponent("player.name", storage("ui", "kore")) { interpret = true separator = textComponent(", ") } ``` Common properties in this context: - `nbt`: the NBT path to read - `interpret`: parse the resulting text as a chat component when the stored value contains component JSON - `separator`: custom separator when the path resolves to multiple values - source-specific arguments: block position, entity selector, or storage id See [Chat Components](/docs/concepts/chat-components#nbtcomponent) for the full component guide. ### Predicates and sub-predicates Some predicate builders expose an `nbt { ... }` block directly. For example, block sub-predicates can match block-entity NBT: ```kotlin block(Blocks.CHEST) { nbt { this["CustomName"] = "Loot Chest" } } ``` That mirrors the attached `Block` predicate DSL, where `fun Block.nbt(block: NbtCompoundBuilder.() -> Unit)` stores the resulting compound in the predicate JSON. When a predicate surface exposes `predicates { customData { ... } }` or another nested NBT-aware structure, you still use the same value assignment style. See [Predicates](/docs/data-driven/predicates) for the surrounding condition DSL. ### Objects and helpers with `toNbt()` Several Kore types can serialize themselves to NBT using `toNbt()`. ```kotlin val entityNbt = myDisplayEntity.toNbt() ``` This is convenient when an API already knows its target NBT shape and you only need to pass the generated compound into a command or another builder. Examples appear in helper docs such as [Display Entities](/docs/helpers/display-entities) and [Mannequins](/docs/helpers/mannequins). ## Typical methods by context Here is the short version of what you usually reach for: | Context | Typical methods | |----------------------------|----------------------------------------------------------| | Build a compound | `nbt { ... }` | | Build a list from values | `nbtListOf(...)` | | Build a list with a DSL | `nbtList { ... }`, `add(...)`, `addNbtCompound { ... }` | | Write entries | `this["key"] = value` | | Nest another compound | `this["key"] = nbt { ... }` | | Reuse generated object NBT | `toNbt()` | | Read NBT into chat | `nbtComponent(path, block/entity/storage)` | | Match NBT in a DSL | context-specific `nbt { ... }` methods | | Write SNBT text | `tag.toSnbt()`, `encodeToSnbt(value)` | | Parse an SNBT compound | `"{...}".toNbt()` | | Parse any SNBT value | `"[I;1,2]".toNbtTag()` | ## Practical tips - Prefer the typed builder over raw SNBT strings whenever possible. - Keep reusable compounds in local variables when several commands share the same payload. - Remember that list element types must stay homogeneous. - If you want readable generated output while iterating, enable `prettyPrint` in your configuration. - When an API already exposes higher-level builders or `toNbt()`, prefer those over manually recreating the same shape. ## NBT vs SNBT in short `Nbt` is the structured tag model itself: compounds, lists, strings, numbers, and the other tag types Kore builds in memory with `nbt { ... }`, `nbtListOf(...)`, `nbtList { ... }`, and `toNbt()`. `Snbt` (stringified NBT) is the text form of that same data, written as a string such as `{CustomName:"Hero",Health:20}`. In Kore, you usually work with typed `Nbt` objects first, then let Kore serialize them when needed. Reach for `toSnbt()` when a target API specifically expects NBT as text instead of an `NbtTag` object, and `String.toNbt()` / `String.toNbtTag()` to go the other way. ## Related pages - [Chat Components](/docs/concepts/chat-components) - includes `NbtComponent` - [Commands](/docs/commands/commands) - especially the data command and NBT-carrying commands - [Predicates](/docs/data-driven/predicates) - NBT-aware predicate helpers - [Components](/docs/concepts/components) - item and custom component structures that often embed NBT-backed data - [Known Issues](/docs/advanced/known-issues#nbt-and-snbt-via-knbt) - `knbt`-specific constraints --- ## Scoreboards --- root: .components.layouts.MarkdownLayout title: Scoreboards nav-title: Scoreboards description: A guide for managing scoreboards in a Minecraft datapack using Kore. keywords: minecraft, datapack, kore, guide, scoreboards date-created: 2024-04-06 date-modified: 2026-09-23 routeOverride: /docs/concepts/scoreboards --- # Scoreboards Scoreboards track numeric values for players and entities. They're one of the two runtime variable containers in a datapack (the other being [data storage](/docs/concepts/data-storage)), and are essential for game mechanics, timers, and counters. If you're unsure when to use a scoreboard versus a Kotlin variable, read [Runtime Logic](/docs/concepts/runtime-logic) first. For the full scoreboard command reference, see [Commands](/docs/commands/commands#scoreboard-command). You can manage scoreboards with the `scoreboard` command: ```kotlin scoreboard.objectives.add("my_objective", ScoreboardCriteria.DUMMY) ``` ## Creating objectives You have multiple forms of the `scoreboard` command: ```kotlin scoreboard { objectives { add("my_objective", ScoreboardCriteria.DUMMY) // this form lets you manage multiple objectives at once } } scoreboard { objective("my_objective") { add(ScoreboardCriteria.DUMMY) // this form lets you manage a single objective } } ``` ## Managing objectives You can add, remove, set display name, set display slot, set render type of objectives: ```kotlin scoreboard { objective("my_objective") { add(ScoreboardCriteria.DUMMY, displayName = textComponent("My Objective", Color.GOLD)) setDisplaySlot(DisplaySlots.sidebar) setRenderType(RenderType.INTEGER) } } ``` ## Manage players You can manage players with the `players` block: ```kotlin scoreboard { players { add(allPlayers(), "my_objective", 1) remove(self(), "my_objective", 5) reset(self(), "my_objective") set(self(), "my_objective", 10) operation(self(), "my_objective", Operation.ADD, self(), "my_objective") } player(self()) { add("my_objective", 1) remove("my_objective", 5) reset("my_objective") set("my_objective", 10) operation("my_objective", Operation.ADD, self(), "my_objective") } } ``` You can also manage an objective for multiple selectors at once: ```kotlin scoreboard { players { objective("my_objective") { add(self(), 1) remove(self(), 5) reset(self()) set(self(), 10) operation(self(), Operation.ADD, self(), objective) } } } ``` Or also manage an objective for a single selector: ```kotlin scoreboard { player(self()) { objective("my_objective") { add(1) remove(5) reset() set(10) operation(Operation.ADD, self(), objective) } } } ``` These methods offer a more readable way to manage objectives, and avoid repetition operations invoking multiple times the same selector/objective. ## Sidebars To show custom text lines on the right of the screen, like on servers, use the [Sidebars helper](/docs/helpers/sidebars) from the `helpers` module. --- ## Selectors --- root: .components.layouts.MarkdownLayout title: Selectors nav-title: Selectors description: Build Minecraft target selectors in Kore with typed Kotlin builders. Compose entity filters, sorting, and score-based conditions instead of writing @e[...] strings by hand. keywords: minecraft, datapack, kore, selectors, target selectors, entities, players, commands date-created: 2026-04-21 date-modified: 2026-10-02 routeOverride: /docs/concepts/selectors --- # Selectors Minecraft target selectors choose players or entities without hardcoding a UUID or exact player name. Kore exposes them as typed builders, so you can compose filters in Kotlin instead of manually writing `@e[...]` strings. For the vanilla syntax reference, see the [Minecraft Wiki target selectors page](https://minecraft.wiki/w/Target_selectors). The [selectors example](/playground#example=selectors) of the playground runs in your browser and shows the commands it generates. ## Base selector helpers Kore provides helpers for the common Java Edition selector bases: - `allPlayers()` -> `@a` - `allEntities()` -> `@e` - `nearestPlayer()` -> `@p` - `nearestEntity()` -> `@n` - `randomPlayer()` -> `@r` - `self()` -> `@s` - `player("Name")` -> player-name-filtered `@a[...]` ```kotlin val everyone = allPlayers() val executor = self() val nearest = nearestPlayer() ``` `allPlayers` and `allEntities` take either a `limitToOne: Boolean` or an explicit `limit: Int` as their first argument, so `allEntities(3)` and `allEntities(limitToOne = true)` both avoid writing `limit` inside the builder. `allEntitiesLimitToOne()` is a named shorthand for the latter. `selector(SelectorType.X)` is the generic entry point the others delegate to. All of them return a `SelectorArgument`. That type implements `EntityArgument`, `DataArgument`, `PossessorArgument`, and `ScoreHolderArgument`, which is why the same value can be passed to commands that target entities, read NBT, or hold scores. ## Filtering targets Each selector helper accepts a `SelectorArguments` builder. ```kotlin val nearbyZombies = allEntities { type = EntityTypes.ZOMBIE distance = rangeOrIntEnd(16) // ..16, meaning up to 16 blocks away sort = Sort.NEAREST // sort runs before limit, so this keeps the 5 closest limit = 5 } ``` This generates a selector equivalent to: ```mcfunction @e[distance=..16,limit=5,sort=nearest,type=minecraft:zombie] ``` ## Common selector arguments Kore exposes the main Java Edition selector filters directly as mutable properties. - position: `x`, `y`, `z` - volume: `dx`, `dy`, `dz` - distance: `distance` - scoreboard filters: `scores` - advancement filters: `advancements` - sort and cap: `sort`, `limit` - player/entity metadata: `name`, `team`, `tag`, `gamemode`, `type`, `predicate`, `nbt` - rotations: `xRotation`, `yRotation` Example with position and volume: ```kotlin // x/y/z is one corner of the box, dx/dy/dz its size along each axis val entitiesInRoom = allEntities { x = 10.0 y = 64.0 z = -4.0 dx = 8.0 dy = 4.0 dz = 8.0 } ``` ## Score-based filtering Selectors integrate nicely with [Scoreboards](/docs/concepts/scoreboards) and other scoreboard-driven logic. Open a `scores { }` block inside the selector builder - it assigns the filter for you, so do not write `scores = scores { }`. ```kotlin val activePlayers = allPlayers { scores { "round" greaterThanOrEqualTo 1 "lives" greaterThan 0 } } ``` Three notations are available inside the block, and they can be mixed: ```kotlin val filtered = allEntities { scores { "baz" lessThan 1 score("bar") greaterThanOrEqualTo 1 score("foo", rangeOrInt(1)) } } ``` ```mcfunction @e[scores={baz=..0,bar=1..,foo=1}] ``` The comparison words are `lessThan`, `lessThanOrEqualTo`, `equalTo`, `greaterThanOrEqualTo`, `greaterThan`, and `matches` (which takes a range). Kore converts each into the vanilla inclusive-range syntax, so `greaterThan 0` becomes `1..`. `advancements { }` works the same way for advancement filters. That is especially useful in [`execute`](/docs/commands/commands), timers, game loops, and mini-game state tracking. ## Inverting filters Several selector filters support inversion. ```kotlin // ! negates a filter, becoming gamemode=!spectator in the generated selector val nonSpectators = allPlayers { gamemode = !Gamemode.SPECTATOR team = !"admins" } ``` You can also invert `type`, `predicate`, and `nbt` filters. ## Limit and sorting Kore keeps the vanilla `sort` + `limit` pattern explicit. ```kotlin val oneRandomPlayer = allPlayers { sort = Sort.RANDOM limit = 1 } val nearestMarkedEntity = allEntities(limitToOne = true) { tag = "arena_marker" sort = Sort.NEAREST } ``` Use `limitToOne = true` when you want a concise single-target selector without repeating `limit = 1`. ## Parsing selector strings You can build a selector from its vanilla command representation with the `selector(String)` overload, then optionally refine it with the regular builder. ```kotlin val fighters = selector("@a[tag=fighter,gamemode=!spectator]") val nearbyFighters = selector("@a[tag=fighter]") { distance = rangeOrIntEnd(16) } ``` Lower-level entry points are also available: `Selector.fromString("@e[limit=1]")` returns a `Selector`, and `SelectorArguments.fromString("limit=1,tag=!foo")` parses only the bracket content. All selector arguments are supported, including nested `scores`, `advancements`, and SNBT `nbt` filters. ## Using selectors in commands Selectors can be reused anywhere an `EntityArgument`, `DataArgument`, `PossessorArgument`, or `ScoreHolderArgument` is accepted, so they show up naturally across the [Commands](/docs/commands/commands) and [Functions](/docs/commands/functions) APIs. ```kotlin val fighters = allPlayers { tag = "fighter" } function("round_start") { effect(fighters) { give(Effects.SPEED, duration = 10, amplifier = 1) } tellraw(fighters, textComponent("Fight!")) scoreboard.players.set(fighters, "combo", 0) } ``` ## Selectors and OOP entity handles The [`oop` module](/docs/oop/entities-and-players) wraps selectors in `Entity` handles so gameplay code can call methods instead of rebuilding filters. The two layers connect in one direction only: - `Entity.asSelector()` turns a handle into a `SelectorArgument` you can pass to any command. - There is **no** conversion the other way. `self()`, `allPlayers()`, and friends return `SelectorArgument`, not `Entity`, so they cannot be passed to `Entity`-only helpers such as `getScoreEntity` or `batch`. `Entity` also always renders as `@e[...]`, so no handle can represent `@s`. When the executing entity is already `@s`, stay on the selector layer and use the core command DSL: ```kotlin function("charge_up") { val lastCharge = scoreboard.objective(self(), "last_crystal_charge") lastCharge += 10 } ``` ```mcfunction scoreboard players add @s last_crystal_charge 10 ``` ## Practical tips - Prefer reusable selector values when the same filter appears in several functions. - Use `self()` when logic should apply to the current execution context. - Use generated entity types and predicates instead of raw strings whenever possible. - Keep complex filters readable by assigning them to `val`s before entering large command blocks, and check the [Cookbook](/docs/guides/cookbook) if you want to turn those values into reusable helpers. ## See also - [Entities & Players](/docs/oop/entities-and-players) - OOP handles built on top of these selectors - [Commands](/docs/commands/commands) - where selectors are consumed - [Arguments Internals](/docs/contributing/arguments) - contributor-facing details about Kore's broader argument system - [Minecraft Wiki: Target selectors](https://minecraft.wiki/w/Target_selectors) - vanilla syntax and semantics --- ## TimeNumber - Minecraft Ticks, Seconds & Days in Kore --- root: .components.layouts.MarkdownLayout title: TimeNumber - Minecraft Ticks, Seconds & Days in Kore nav-title: Time description: Work with Minecraft time in Kore using TimeNumber. Type-safe ticks, seconds, and days with arithmetic, conversions, and integration with commands, schedulers, and timers. keywords: minecraft time, ticks to seconds, minecraft tick timer, kore TimeNumber, datapack duration, tick conversion, minecraft time command, schedule delay, minecraft day cycle, time arithmetic date-created: 2026-06-16 date-modified: 2026-06-16 routeOverride: /docs/concepts/time --- # Overview Minecraft time is measured in **[ticks](https://minecraft.wiki/w/Tick)** (20 ticks = 1 second, 24 000 ticks = 1 day). Kore models this with `TimeNumber`, a typed value that carries both the numeric amount and the time unit (`TICKS`, `SECONDS`, `DAYS`). Commands that accept a duration - `schedule`, `title times`, `time add`, `weather`, `worldborder` - take a `TimeNumber` argument, so you never have to remember the raw tick math yourself. ## Creating time values Use the extension properties on any `Number`: ```kotlin import io.github.ayfri.kore.arguments.numbers.ticks import io.github.ayfri.kore.arguments.numbers.seconds import io.github.ayfri.kore.arguments.numbers.days val a = 100.ticks // 100t → 5 seconds val b = 5.seconds // 5s val c = 2.days // 2d → 48 000 ticks val d = 1.5.seconds // 1.5s (fractional values are preserved) ``` Or use the `time()` factory for dynamic construction: ```kotlin import io.github.ayfri.kore.arguments.numbers.time import io.github.ayfri.kore.arguments.numbers.TimeType val e = time(200) // 200t val f = time(10, TimeType.SECONDS) // 10s ``` ## Serialization (command output) `TimeNumber.toString()` produces what Minecraft commands expect: | Expression | Output | |---------------|--------| | `100.ticks` | `100` | | `1.5.ticks` | `1.5` | | `5.seconds` | `5s` | | `1.5.seconds` | `1.5s` | | `2.days` | `2d` | | `0.2.days` | `0.2d` | Integer values drop the `.0` suffix; fractional values are preserved as-is. ## Arithmetic `TimeNumber` supports the standard operators (same unit required for meaningful results): ```kotlin val delay = 3.seconds + 2.seconds // TimeNumber(5.0, SECONDS) val half = 10.ticks / 2.ticks // TimeNumber(5.0, TICKS) val neg = (-1).days // TimeNumber(-1.0, DAYS) ``` ## Unit conversions Convert between units while keeping the logical duration the same: ```kotlin val twentyTicks = 20.ticks twentyTicks.inSeconds() // TimeNumber(1.0, SECONDS) twentyTicks.inDays() // TimeNumber(1/1200 ≈ 0.000833, DAYS) val oneDay = 1.days oneDay.inTicks() // TimeNumber(24000.0, TICKS) oneDay.inSeconds() // TimeNumber(1200.0, SECONDS) ``` `toTicks()` / `toSeconds()` / `toDays()` reinterpret the raw number without converting (rarely needed): ```kotlin 100.ticks.toSeconds() // TimeNumber(100.0, SECONDS) - still 100, just retagged as "s" ``` ## Usage in commands Any command that takes a duration accepts `TimeNumber` directly: ```kotlin import io.github.ayfri.kore.arguments.numbers.seconds import io.github.ayfri.kore.arguments.numbers.ticks import io.github.ayfri.kore.arguments.numbers.days // schedule schedule(5.seconds, myFunction) schedule(myFunction).replace(100.ticks) // title times (fade-in, stay, fade-out) title(self(), 0.5.seconds, 3.seconds, 0.5.seconds) // world time time.add(1.days) // weather weatherClear(30.seconds) // worldborder grow worldBorder.add(10.0, 200.ticks) ``` ## Further reading - [Schedule command](/docs/commands/commands#schedule-command) - scheduling functions with a delay - [Minecraft Wiki - Schedule command](https://minecraft.wiki/w/Commands/schedule) - vanilla `/schedule` syntax and behavior - [Scheduler helper](/docs/helpers/scheduler) - OOP wrapper for recurring schedules - [Timers](/docs/oop/timers) - OOP timer utilities built on ticks - [Minecraft Wiki - Tick](https://minecraft.wiki/w/Tick) --- # Helpers ## Helpers Utilities --- root: .components.layouts.MarkdownLayout title: Helpers Utilities nav-title: Helpers Utilities description: Overview of helper-focused utilities in Kore - renderers, displays, inventories, mannequins, math, NBT paths, raycasts, scheduling, and state delegates. keywords: minecraft, datapack, kore, helpers, ansi, markdown, minimessage, raycast, math, vector, area, state, nbt, vfx, particles, text, display, inventory, mannequin, scheduler, menu, dialog, sidebar date-created: 2026-03-31 date-modified: 2026-09-23 routeOverride: /docs/helpers/utilities position: 0 --- # Helpers Utilities The `helpers` module contains utility-style features that build on top of Kore and, when needed, the OOP abstractions. These helpers are not centered around long-lived object models, so they now live outside the `oop` module. ## Why use `helpers` Use `helpers` when you want higher-level building blocks without introducing gameplay objects such as teams, timers, or entity wrappers. - **Rendering helpers** convert human-friendly text formats into Minecraft text components. - **Display and entity helpers** cover display entities, mannequins, and other world-facing utility objects. - **Math, scheduling, and geometry helpers** pre-generate command logic for raycasts, particle shapes, spatial zones, delayed execution, or fixed-point math. - **State helpers** let you write concise Kotlin that still compiles to vanilla-friendly scoreboard and storage commands. The module is designed to stay composable: you can use it with plain Kore functions or mix it with the [`oop` module](/docs/oop/oop-utilities) when a system grows beyond a few stateless helpers. ## Install this module Add the `helpers` artifact when you want renderer, display, inventory, mannequin, math, raycast, scheduler, delegate, or particle utilities on top of Kore. ```kotlin dependencies { implementation("io.github.ayfri.kore:helpers:VERSION") } ``` ## Typical workflow Most helper APIs follow the same pattern: 1. **Register or build** a helper once (`registerMath()`, `raycast { ... }`, `drawCircle(...)`, etc.). 2. **Call it from functions** wherever you need the generated commands. 3. **Combine it with core Kore or OOP** if you need selectors, scoreboards, teams, timers, or reusable gameplay objects. That makes helpers a good fit for packs that start simple and progressively adopt more structure only where needed. ## All Features - **[ANSI Renderer](/docs/helpers/ansi-renderer)** - Convert ANSI SGR escape sequences into Minecraft text components when your source text already contains terminal-style formatting. - **[Area](/docs/helpers/area)** - Work with axis-aligned 3D regions for containment checks, intersections, unions, and coordinate transforms. - **[Display Entities](/docs/helpers/display-entities)** - Configure block, item, and text display entities with shared transformations, billboards, brightness, and interpolation settings. - **[Inventory Manager](/docs/helpers/inventory-manager)** - Register slot listeners and container policies for player, entity, or block inventories. - **[Mannequins](/docs/helpers/mannequins)** - Build mannequin entities with profiles, hidden layers, poses, and hand selection. - **[Markdown Renderer](/docs/helpers/markdown-renderer)** - Turn Markdown snippets into rich Minecraft text for chat, titles, signs, or boss bars. - **[Menus](/docs/helpers/menus)** - Build dialog-based GUIs whose buttons run functions for any player, with sub-pages, links, and pause screen or Smithed menu entries. - **[MiniMessage Renderer](/docs/helpers/minimessage-renderer)** - Parse Adventure MiniMessage tags into Minecraft text components while keeping authoring ergonomic. - **[NBT Paths](/docs/helpers/nbt-paths)** - Build typed NBT paths and resolve trees that mix literal values with runtime scores. - **[Raycasts](/docs/helpers/raycasts)** - Generate recursive step-based raycasts with callbacks for hits, misses, and per-step side effects. - **[Scheduler](/docs/helpers/scheduler)** - Schedule delayed or repeating functions with load-time registration and cancelation helpers. - **[Score Vectors](/docs/helpers/score-vectors)** - Runtime 3D vectors for look directions, dashes, knockback, exact lengths, teleports, and motion. - **[Scoreboard Math](/docs/helpers/scoreboard-math)** - Shared fixed-point sine, cosine, atan2, and exact integer square root, two commands per call. - **[Sidebars](/docs/helpers/sidebars)** - Show up to 15 custom lines on the sidebar, with right-aligned values, live scores, and conditional lines. - **[State Delegates](/docs/helpers/state-delegates)** - Map scoreboard objectives or NBT storage paths to Kotlin properties for terser command-generation code. - **[VFX Particles](/docs/helpers/vfx-particles)** - Generate circles, spheres, spirals, helixes, and lines as reusable particle functions. ## Helpers vs OOP Choose the `helpers` module when you mainly need: - data conversion and rendering, - world-facing utility objects such as displays, mannequins, and inventory controllers, - geometry or math utilities, - scheduling helpers, - lightweight compile-time sugar over vanilla commands. Choose the [`oop` module](/docs/oop/oop-utilities) when you need named gameplay objects such as players, teams, scoreboards, timers, spawners, or state machines. If you need entities, teams, scoreboards, items, timers, or other object-oriented gameplay abstractions, see the [OOP Utilities](/docs/oop/oop-utilities) page. --- ## ANSI Text Renderer --- root: .components.layouts.MarkdownLayout title: ANSI Text Renderer nav-title: ANSI Renderer description: Convert ANSI SGR escape sequences into Minecraft text components with the Kore helpers module. keywords: minecraft, datapack, kore, helpers, ansi, sgr, escape, text, renderer, color, bold, italic date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/helpers/ansi-renderer --- # ANSI Text Renderer Converts text containing [ANSI SGR escape sequences](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters) into Minecraft `ChatComponents` for use in [tellraw](https://minecraft.wiki/w/Commands/tellraw), title commands, or boss bar names. ANSI codes are stripped from the output and mapped to Minecraft text component styles. > [!FOOTPRINT] > Nothing at runtime: no objective, storage, entity, forceloaded chunk, tick or load function. The renderer runs while Kore builds the pack and returns `ChatComponents`. ## Basic usage ```kotlin val lines = ansiToTextComponents("\u001B[1;31mBold red\u001B[0m normal text") // Produces: [{bold:true, color:"red", text:"Bold red"}, {text:" normal text"}] ``` Each input line becomes a `ChatComponents` instance. Consecutive characters sharing the same style are merged into runs for efficiency. ## With config defaults ```kotlin val lines = ansiToTextComponents("\u001B[32mGreen\u001B[0m rest") { color = Color.WHITE } // "Green" gets green from ANSI, "rest" gets white from config fallback ``` ## Supported ANSI SGR codes | ANSI code | Minecraft property | Description | |-----------------------|------------------------|--------------------------| | `\x1B[1m` | `bold = true` | Bold on | | `\x1B[3m` | `italic = true` | Italic on | | `\x1B[4m` | `underlined = true` | Underline on | | `\x1B[8m` | `obfuscated = true` | Obfuscated on | | `\x1B[9m` | `strikethrough = true` | Strikethrough on | | `\x1B[22m` | `bold = null` | Bold off | | `\x1B[23m` | `italic = null` | Italic off | | `\x1B[24m` | `underlined = null` | Underline off | | `\x1B[28m` | `obfuscated = null` | Obfuscated off | | `\x1B[29m` | `strikethrough = null` | Strikethrough off | | `\x1B[30m`-`\x1B[37m` | `color` | Standard foreground | | `\x1B[90m`-`\x1B[97m` | `color` | Bright foreground | | `\x1B[38;5;Nm` | `color` (RGB) | 256-color palette | | `\x1B[38;2;R;G;Bm` | `color` (RGB) | 24-bit RGB color | | `\x1B[39m` | `color = null` | Default foreground color | | `\x1B[0m` | reset all | Reset all attributes | ANSI styles override the config defaults (`color`, `bold`, `italic`). When no ANSI style is active, the config values are used as fallback. ## Configuration | Property | Default | Description | |----------|---------|----------------------------------| | `color` | `null` | Fallback text color | | `bold` | `null` | Fallback bold styling | | `italic` | `null` | Fallback italic styling | | `font` | `null` | Minecraft font resource location | --- ## Area --- root: .components.layouts.MarkdownLayout title: Area nav-title: Area description: Axis-aligned 3D bounding box with the Kore helpers module - geometric operations, containment checks, and spatial queries. keywords: minecraft, datapack, kore, helpers, area, bounding box, vec3, intersect, union, contains date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/helpers/area --- # Area The `Area` class represents an axis-aligned 3D bounding box defined by two `Vec3` corners. It provides geometric operations useful for zone detection, region math, and spatial queries. Because it is purely geometric, `Area` is especially useful when you need to pre-compute regions in Kotlin and then reuse the resulting coordinates in multiple commands, predicates, or generated functions. > [!FOOTPRINT] > Nothing at runtime: no objective, storage, entity, forceloaded chunk, tick or load function. An `Area` is computed in Kotlin, only its coordinates end up in the commands you write. ## Creating an area ```kotlin val zone = area(vec3(0, 0, 0), vec3(10, 5, 10)) zone.center // vec3(5, 2.5, 5) zone.size // vec3(10, 5, 10) zone.radius // vec3(5, 2.5, 5) val expanded = zone.expand(2) // grow by 2 in all directions val moved = zone.move(vec3(5, 0, 0)) // shift the area val overlap = zone.intersect(otherZone) // intersection of two areas val combined = zone.union(otherZone) // union of two areas // Containment checks val inside = vec3(3, 2, 3) in zone // true val contains = smallerZone in zone // true // Operator shortcuts val shifted = zone + vec3(1, 0, 0) val back = zone - vec3(1, 0, 0) // Range operator val area = vec3(0, 0, 0)..vec3(10, 10, 10) ``` ## Practical example ```kotlin val lobby = area(vec3(-8, 64, -8), vec3(8, 72, 8)) val arena = lobby.expand(16) val bossRoom = arena.move(vec3(32, 0, 0)) val entrance = bossRoom.center val safeZone = bossRoom.contract(2) val overlapsLobby = bossRoom.intersect(lobby) ``` This style works well when you want a single source of truth for multiple related regions: the base area defines the layout and the derived areas stay consistent even if the original dimensions change. ## Common use cases - Define lobby, arena, checkpoint, or boss-room bounds once in Kotlin. - Derive a slightly larger trigger zone with `expand(...)`. - Compute a smaller "safe interior" with `contract(...)`. - Test whether a point or another region belongs inside a larger gameplay area. ## Function reference | Function / Property | Description | |---------------------|------------------------------------| | `center` | Center point of the area | | `contains` (`in`) | Check if a point or area is inside | | `contract` | Shrink the area inward | | `expand` | Grow the area outward | | `intersect` | Intersection with another area | | `move` | Translate the area by a vector | | `radius` | Half-dimensions | | `size` | Dimensions (x, y, z) | | `union` | Union with another area | ## See also - [Raycasts](/docs/helpers/raycasts) - Combine spatial bounds with line-of-sight checks or interaction beams. - [Entities & Players](/docs/oop/entities-and-players) - Reuse computed areas to place, move, or query gameplay entities. - [Predicates](/docs/data-driven/predicates) - Turn region logic into reusable condition checks when needed. --- ## Display Entities --- root: .components.layouts.MarkdownLayout title: Display Entities nav-title: Display Entities description: A guide for creating Display Entities in the world. keywords: minecraft, datapack, kore, guide, display-entities date-created: 2024-04-06 date-modified: 2026-10-07 routeOverride: /docs/helpers/display-entities --- # Display Entities Display entities share a common set of world-rendering options and then add a few type-specific fields for blocks, items, or text. > [!FOOTPRINT] > Only the entities you summon: each `summon` creates one display entity. No objective, storage, forceloaded chunk, tick or load function. ## Shared display settings All display entities inherit these properties from the shared `DisplayEntity` base type: - `billboardMode` - how the display faces the camera (`FIXED`, `VERTICAL`, `HORIZONTAL`, `CENTER`). - `brightness` - overrides the block and sky light levels used for rendering (`Brightness(block = 15, sky = 15)`, 0 to 15). - `glowColorOverride` - replace the outline color with a custom RGB value. - `height` / `width` - resize the display bounds. - `interpolationDuration` / `startInterpolation` - animate transformation changes over time. - `shadowRadius` / `shadowStrength` - control the projected shadow. - `transformation` - combine translation, rotation, scale, or custom matrices. - `viewRange` - control when the entity is rendered from a distance. ## Entity Displays Entity displays are used to display blocks/items/text in the world. You can define multiple properties for the display, such as transformation, billboard mode, shadow etc. ```kotlin val entityDisplay = blockDisplay { blockState(Blocks.GRASS_BLOCK) { properties { this["snowy"] = true } } transformation { leftRotation { quaternionNormalized(0.0, 0.0, 0.0, 1.0) } scale = vec3(2.0) translation { y = 2.0 } } billboardMode = BillboardMode.CENTER shadowRadius = 0.5f } summon(entity = entityDisplay.entityType, pos = vec3(0, 0, 0), nbt = entityDisplay.toNbt()) ``` This summons a snowy grass block at `0 0 0`, scaled by 2 and translated 2 blocks up, facing the camera on both axes. ## Block Displays Block displays are used to display blocks in the world. They are created by calling the `blockDisplay()` DSL. ```kotlin val blockDisplay = blockDisplay { blockState(Blocks.GRASS_BLOCK) { properties { this["snowy"] = true } } } ``` ## Item Displays Item displays are used to display items in the world. They are created by calling `itemDisplay()` DSL. The optional `displayMode` property uses `ItemDisplayModelMode`, which serializes to the lowercase values Minecraft expects: - `FIRSTPERSON_LEFTHAND` - `FIRSTPERSON_RIGHTHAND` - `FIXED` - `GROUND` - `GUI` - `HEAD` - `NONE` - `ON_SHELF` - `THIRDPERSON_LEFTHAND` - `THIRDPERSON_RIGHTHAND` ```kotlin val itemDisplay = itemDisplay { item(Items.DIAMOND_SWORD) { customName(textComponent("test")) enchantments { enchantment(Enchantments.SHARPNESS, 1) enchantment(Enchantments.UNBREAKING, 3) } attributeModifiers { modifier( type = Attributes.ATTACK_DAMAGE, amount = 1.0, name = "bonus_damage", operation = AttributeModifierOperation.ADD_VALUE, ) } } } ``` ## Text Displays Text displays are used to display text in the world. They are created by calling `textDisplay()` DSL. `alignment` uses `TextAlignment`, which currently supports: - `LEFT` - `CENTER` - `RIGHT` ```kotlin val textDisplay = textDisplay { text("test", Color.RED) { bold = true } } ``` ## Transformations Transformations are used to modify the translation, left/right rotations and scale of displays. They are created by calling `transformation` DSL. You can also apply directly matrix transformations and use quaternions, axis angles or use Euler angles for rotations. ```kotlin transformation { leftRotation { quaternionNormalized(0.0, 0.0, 0.0, 1.0) } scale = vec3(2.0) translation { y = 2.0 } } ``` `Quaternion` and `Matrix` operators (`+`, `-`, `*`, `/`) return a new value and leave both operands untouched, while named methods like `normalize()`, `slerp()` or `multiply()` modify the receiver. `Quaternion.IDENTITY` and `Matrix.IDENTITY` give a new instance on each access, so modifying one never affects the others. Transformations can also be combined: - `a.interpolate(b, t)` returns the transformation `t` of the way from `a` (`0`) to `b` (`1`). - `a.invert()` returns the transformation undoing `a`, as a matrix. - `a.decompose()` splits a matrix transformation back into translation, rotations and scale. ## Interpolations You can convert your display entity into an "interpolable" display entity by calling `interpolable()` on it inside a function. This will allow you to interpolate between the current transformation and the target transformation in a given time. The interpolable gets a UUID hashed from the pack name, the display and its position, so it stays the same across builds and two identical displays still get different ones. ```kotlin val interpolableEntityDisplay = blockDisplay { blockState(Blocks.STONE) }.interpolable(position = vec3(0, 0, 0)) interpolableEntityDisplay.summon() interpolableEntityDisplay.interpolateTo(duration = 2.seconds) { translation { y = 2.0 } } ``` Interpolation is especially useful when you want display entities to move or morph smoothly between ticks without rebuilding the entity from scratch. ## OOP Entity Handles After creating an interpolable, call `toEntity()` to get a typed OOP entity handle (`BlockDisplayEntity`, `ItemDisplayEntity`, or `TextDisplayEntity`). This gives access to all `Entity` extension functions such as `kill`, `teleportTo`, `addTag`, and more. ```kotlin val display = blockDisplay { blockState(Blocks.STONE) }.interpolable(vec3(0, 64, 0)) display.summon() // toEntity() is typed as Entity, so cast when you want the specific subclass val entity: BlockDisplayEntity = display.toEntity() as BlockDisplayEntity // every Entity OOP extension works on the handle entity.addTag("my_display") entity.teleportTo(0, 65, 0) entity.kill() ``` You can also construct the typed entity handles directly when you already have a UUID: ```kotlin val uuid = uuid("12345678-1234-1234-1234-123456789012") val block = BlockDisplayEntity(uuid) val item = ItemDisplayEntity(uuid) val text = TextDisplayEntity(uuid) ``` All three target their entity with `@e[type=minecraft:,nbt={UUID:[I;...]}]`. These handle classes live in the `oop` module and extend `Entity`, so every entity-scoped extension applies to them - see [Entities & Players](/docs/oop/entities-and-players) for the full list. --- ## Inventory Manager --- root: .components.layouts.MarkdownLayout title: Inventory Manager nav-title: Inventory Manager description: Listen to slot events and control containers (players, blocks) with Kore's Inventory Manager. keywords: minecraft, datapack, kore, inventory, container, slots, events, gui date-created: 2025-08-11 date-modified: 2026-10-08 routeOverride: /docs/helpers/inventory-manager --- # Inventory Manager Kore’s Inventory Manager lets you declaratively control [inventories](https://minecraft.wiki/w/Inventory) on entities or blocks and react to slot events. - Manage items in any `ContainerArgument` (players, entities, or block containers). - Register slot listeners that fire when an item is taken from a slot. - Keep a slot populated, clear other slots, or run custom logic every tick. - Auto-generate the required load/tick functions and minimal scoreboards to drive the listeners. > [!FOOTPRINT] > - **Objectives:** `_inventory_manager__click_listener_`, one per manager. > - **Storage:** none. > - **Entities:** none on an entity container. A block container gets one `minecraft:marker` tagged `_inventory_manager__click_listener__marker`, killed and summoned again on each load. > - **Forceloaded chunks:** none. > - **Every tick:** `tick_inventory_manager_` checks each listener's slot, plus what `onTick` adds: `clearAllItemsNotInSlot()` tests the 36 inventory slots and the offhand. > - **Load and files:** `load_inventory_manager_` creates the objective, and each handler gets its own `when_taken_event_` / `during_taken_event_` function. ## Quick start ```kotlin import io.github.ayfri.kore.arguments.types.literals.nearestPlayer import io.github.ayfri.kore.arguments.colors.Color import io.github.ayfri.kore.arguments.chatcomponents.textComponent import io.github.ayfri.kore.commands.TitleLocation import io.github.ayfri.kore.generated.Items import io.github.ayfri.kore.helpers.inventorymanager.inventoryManager function("inventory_demo") { val playerInv = inventoryManager(nearestPlayer()) playerInv.slotEvent(HOTBAR[0], Items.NETHER_STAR { this["display"] = nbt { this["Name"] = textComponent("Do not move me", color = Color.RED).toJsonString() } }) { onTake { title(self(), TitleLocation.ACTIONBAR, textComponent("Stop taking me!", color = Color.RED)) } duringTake { setItemInSlot() } onTick { clearAllItemsNotInSlot(); killAllItemsNotInSlot() } setItemInSlot() // seed the slot once } // Start listeners for this manager playerInv.generateSlotsListeners() } ``` {{{ .components.mc.InventoryManagerMockup }}} Tip: Use the builder variant to both declare listeners and auto-generate them in one go: ```kotlin inventoryManager(nearestPlayer()) { slotEvent(HOTBAR[0], Items.DIAMOND) { setItemInSlot() } generateSlotsListeners() } ``` ## Block containers You can target block inventories as well by passing a `Vec3` and (optionally) placing a container block first: ```kotlin val chestPos = vec3(0, -59, 0) inventoryManager(chestPos) { setBlock(Blocks.CHEST) slotEvent(CONTAINER[0], Items.DIAMOND_SWORD) { onTake { tellraw(allPlayers(), text("You took the sword!", Color.RED)) } duringTake { setItemInSlot() } onTick { clearAllItemsNotInSlot(); killAllItemsNotInSlot() } setItemInSlot() } generateSlotsListeners() } ``` ## API overview - `inventoryManager(container)` - Create a manager for any `ContainerArgument` (`EntityArgument` or `Vec3`). Internally uses [`/item`](https://minecraft.wiki/w/Commands/item) to place items in slots. - `slotEvent(slot, expectedItem) { … }` - Register handlers for a single slot. - `onTake { … }` - Fires once when the slot transitions from expected item to something else. - `duringTake { … }` - Fires every tick while the slot is not holding the expected item; useful to enforce state. - `onTick { … }` - Runs every tick regardless of state; convenient for housekeeping. - Helpers inside the scope: - `setItemInSlot()` - Put back the expected item into the slot. - `clearAllItemsNotInSlot([targets])` - Clear all other slots. - `killAllItemsNotInSlot()` - Remove dropped items that don’t belong to this slot. - `generateSlotsListeners()` - Emits the `load`/`tick` functions and scoreboard wiring for all registered listeners. - `setBlock(block)` - When the container is a position, place a block (e.g., a chest) before managing its contents. - `clear(slot)`, `clearAll()`, `clearAll(item)` - Utilities to wipe inventory content. - `fill(slot, item)`, `replace(slot, item)`, `modify(slot, modifier)`, `manager[slot] = item` - `/item` on the managed container. `slot` is any [slot selection](/docs/data-driven/slot-sources#in-commands): one slot, a range like `HOTBAR` or a slot source. `fill` and `clear` write every selected slot, `replace` only the first one. Internally, Inventory Manager relies on a scoreboard objective and a tiny helper marker entity (for non-entity containers) to detect state transitions. Names are auto-namespaced and numbered in the order managers generate their listeners within the datapack, so the same pack always produces the same names. ## Removing detectors To clean up the objectives of every Inventory Manager the pack generated before this call: ```kotlin dataPack("my_dp") { InventoryManager.removeClickDetectors() } ``` ## Full example This combines a player inventory policy with messaging and a chest that constantly re-seeds its first slot. It mirrors the test coverage used in Kore’s own suite. ```kotlin fun Function.inventoryManagerTests() { val counter = "take_counter" val playerInv = inventoryManager(nearestPlayer()) playerInv.slotEvent(HOTBAR[0], Items.NETHER_STAR) { onTake { title(self(), TitleLocation.ACTIONBAR, text("Don’t take me", Color.RED)) scoreboard.players.add(self(), counter, 1) } duringTake { setItemInSlot() } onTick { clearAllItemsNotInSlot(); killAllItemsNotInSlot() } setItemInSlot() } playerInv.generateSlotsListeners() datapack.load { scoreboard.objectives.add(counter) scoreboard.players.set(playerInv.container as ScoreHolderArgument, counter, 0) } inventoryManager(vec3(0, -59, 0)) { setBlock(Blocks.CHEST) slotEvent(CONTAINER[0], Items.DIAMOND_SWORD) { onTake { tellraw(allPlayers(), text("You took the diamond sword from the chest", Color.RED)) } duringTake { setItemInSlot() } onTick { clearAllItemsNotInSlot(); killAllItemsNotInSlot() } setItemInSlot() } generateSlotsListeners() } } ``` ## See also - [Scheduler](/docs/helpers/scheduler) - Run repeated or delayed logic that complements inventory policies. - [Components](/docs/concepts/components) - Define complex items (names, lore, enchantments) you can enforce in slots. - [Predicates](/docs/data-driven/predicates) - Validate component-based item properties in other contexts. - [Scoreboards](/docs/concepts/scoreboards) - Background knowledge on objectives used under the hood. --- ## Mannequins --- root: .components.layouts.MarkdownLayout title: Mannequins nav-title: Mannequins description: A guide for creating Mannequins in the world. keywords: minecraft, datapack, kore, guide, mannequins date-created: 2026-01-25 date-modified: 2026-10-07 routeOverride: /docs/helpers/mannequins --- # Mannequins Mannequins are special entities that can display player skins and textures. They are highly customizable, allowing you to change their profile, hidden layers, and main hand. > [!FOOTPRINT] > Only the entities you summon: each `summon` creates one `minecraft:mannequin`. No objective, storage, forceloaded chunk, tick or load function. ## Creating a Mannequin You can create a mannequin using the `mannequin` DSL. ```kotlin val myMannequin = mannequin { hiddenLayers(MannequinLayer.CAPE, MannequinLayer.HAT) mainHand = MannequinHand.LEFT playerProfile("Ayfri") } summon(myMannequin.entityType, vec3(), myMannequin.toNbt()) ``` ## Profiles Mannequins can have two types of profiles: `PlayerProfile` and `TextureProfile`. ### Player Profile A player profile uses a player's name or UUID to fetch their skin and properties. ```kotlin mannequin { playerProfile(name = "Steve", id = uuid("8667ba71-b85a-4004-af54-457a9734eed7")) } ``` ### Texture Profile A texture profile allows you to specify a direct texture, and optionally a cape, elytra, and model type. ```kotlin mannequin { textureProfile(texture = "tex") { cape = model("cape") model = MannequinModel.SLIM } } ``` ## Customization Mannequins support additional fields for further customization: ```kotlin mannequin { description = textComponent("Test") hideDescription = false immovable = true pose = MannequinPose.CROUCHING } ``` ### Pose The `pose` field allows you to set the mannequin's pose. Available poses are: - `STANDING` - `CROUCHING` - `SWIMMING` - `FALL_FLYING` - `SLEEPING` ## Hidden Layers You can hide specific layers of the mannequin's skin using the `hiddenLayers` function. Available layers: - `CAPE` - `HAT` - `JACKET` - `LEFT_PANTS_LEG` - `LEFT_SLEEVE` - `RIGHT_PANTS_LEG` - `RIGHT_SLEEVE` ```kotlin mannequin { hiddenLayers(MannequinLayer.JACKET, MannequinLayer.HAT) } ``` ## Main Hand You can set which hand is the main hand of the mannequin. ```kotlin mannequin { mainHand = MannequinHand.RIGHT } ``` ## Summoning and OOP Handle Use `summon()` inside a function to spawn the mannequin and receive a `MannequinEntity` handle. The handle uniquely identifies this instance by its UUID, giving access to all OOP entity commands. ```kotlin val myMannequin = mannequin { pose = MannequinPose.STANDING playerProfile("Ayfri") } // summon() spawns the entity and hands back a handle pointing at that exact instance val handle: MannequinEntity = myMannequin.summon(vec3(0, 64, 0)) // every Entity OOP extension works on the handle handle.swing(SwingHand.MAINHAND) handle.kill() handle.teleportTo(0, 64, 5) ``` The generated selector is `@e[type=minecraft:mannequin,nbt={UUID:[I;...]}]`, uniquely targeting the spawned instance. `MannequinEntity` lives in the `oop` module and extends `Entity`, so every entity-scoped extension applies to it - see [Entities & Players](/docs/oop/entities-and-players) for the full list. --- ## Markdown Text Renderer --- root: .components.layouts.MarkdownLayout title: Markdown Text Renderer nav-title: Markdown Renderer description: Convert Markdown-formatted text into Minecraft text components with the Kore helpers module. keywords: minecraft, datapack, kore, helpers, markdown, text, renderer, bold, italic, link, heading, list date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/helpers/markdown-renderer --- # Markdown Text Renderer Converts [Markdown](https://commonmark.org/)-formatted text into Minecraft `ChatComponents` for use in [tellraw](https://minecraft.wiki/w/Commands/tellraw), title commands, or boss bar names. > [!FOOTPRINT] > Nothing at runtime: no objective, storage, entity, forceloaded chunk, tick or load function. The renderer runs while Kore builds the pack and returns `ChatComponents`. ## Basic usage ```kotlin val lines = markdownToTextComponents( """ # Welcome Hello **world**! Click [here](https://example.com). - item one - item two """.trimIndent() ) ``` Each input line becomes a `ChatComponents` instance. Inline styles are parsed and mapped to Minecraft text component properties. ## Supported inline syntax | Markdown syntax | Minecraft property | Description | |---------------------|------------------------|----------------------------------| | `**text**` | `bold = true` | Bold | | `*text*` / `_text_` | `italic = true` | Italic | | `~~text~~` | `strikethrough = true` | Strikethrough | | `__text__` | `underlined = true` | Underline (Minecraft extension) | | `\|\|text\|\|` | `obfuscated = true` | Obfuscated (Minecraft extension) | | `` `code` `` | `color` (codeColor) | Inline code span | | `[label](url)` | `clickEvent = OpenUrl` | Clickable link | | `§(#rrggbb)text§()` | `color` (RGB) | Custom colored span | ## Supported block syntax | Markdown syntax | Rendering | |------------------------|--------------------------------------| | `# Heading` - `######` | Bold + heading color (configurable) | | `- item` / `* item` | Unordered list with bullet prefix | | `1. item` | Ordered list with number prefix | | `> quote` | Blockquote with `│ ` prefix | | `---` / `***` / `___` | Horizontal rule (strikethrough line) | ## Not supported (Minecraft limitations) The following Markdown features are **not supported** because Minecraft text components cannot represent them: - Images (`![alt](url)`) - Tables - Nested blockquotes - Multi-line code blocks (fenced ` ``` `) - HTML tags - Footnotes - Task lists (`- [ ]` / `- [x]`) - Reference-style links (`[text][ref]`) ## Examples ```kotlin // Bold + italic combined val styled = markdownToTextComponents("**bold and *italic* text**") // Link with custom color val link = markdownToTextComponents("Visit [Kore](https://kore.ayfri.com)") { linkColor = Color.GREEN } // Custom color syntax val colored = markdownToTextComponents("§(#ff0000)red text§() normal text") // Heading with inline styles val heading = markdownToTextComponents("# Welcome to **Kore**") ``` {{{ .components.mc.MarkdownRendererMockup }}} ## Configuration | Property | Default | Description | |--------------------|---------------|----------------------------------------------| | `color` | `null` | Default text color | | `bold` | `null` | Default bold state | | `italic` | `null` | Default italic state | | `font` | `null` | Minecraft font resource location | | `headingColors` | level 1-6 map | Color per heading level (gold, yellow, etc.) | | `codeColor` | `Color.GRAY` | Color for inline code spans | | `linkColor` | `Color.AQUA` | Color for link text | | `linkUnderline` | `true` | Whether links are underlined | | `blockquotePrefix` | `"│ "` | String prepended to blockquote lines | | `bulletChar` | `"• "` | Bullet string for unordered list items | | `hrChar` | `"─"` | Character repeated for horizontal rules | | `hrLength` | `20` | Number of repetitions for horizontal rules | --- ## Minecraft Datapack Menus - Interactive Dialog GUIs with Kore --- root: .components.layouts.MarkdownLayout title: Minecraft Datapack Menus - Interactive Dialog GUIs with Kore nav-title: Menus description: "Build interactive Minecraft menus from dialogs with Kore: buttons running functions for any player, sub-pages, links, pause screen and Smithed menu." keywords: minecraft menu, datapack menu, dialog menu, datapack gui, minecraft dialog, trigger command, smithed data pack menu, pause screen, kore menu date-created: 2026-09-23 date-modified: 2026-10-07 routeOverride: /docs/helpers/menus --- # Menus A `Menu` is an in-game GUI built from [dialogs](/docs/data-driven/dialogs): pages of buttons, where each button runs Kotlin-declared commands on the server. Buttons work for every player, operator or not. ```kotlin import io.github.ayfri.kore.helpers.menus.menu val kits = menu("kits", "Choose your kit", Color.GOLD) { text("Your inventory is replaced by the kit.") button("Warrior") { clear(self()) give(self(), Items.IRON_SWORD) give(self(), Items.SHIELD) } button("Archer") { clear(self()) give(self(), Items.BOW) give(self(), Items.ARROW, 32) } page("Help") { text("Pick a new kit after each death.") link("Rules", "https://example.net/rules") } pauseScreen = true } function("open_kits") { kits.open() } ``` {{{ .components.mc.KitsMenuMockup }}} > [!FOOTPRINT] > - **Objectives:** `.menu.`, with the `trigger` criterion. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** `menu__tick` enables the trigger for every player and runs the dispatcher for the players who clicked. > - **Load and files:** `menu__load` creates the objective. Each page is a dialog, each button a function called by `menu__dispatch`. `pauseScreen` adds the menu to `#minecraft:pause_screen_additions`, `smithed` adds the `smithed:data_packs` dialog and the `#smithed:data_packs` tag. ## Buttons `button(label) { ... }` runs its block as the clicking player, at their position. `reopen = true` shows the page again afterwards, handy for counters or toggles. ```kotlin menu("settings", "Settings") { button("Toggle night vision", reopen = true) { function("my_pack:toggle_night_vision") } } ``` `tooltip` sets the text shown on hover, `buttonWidth` and `columns` control the page layout. ## Pages and links `page(title) { ... }` adds a button opening a sub-page. Its exit button goes back to the parent page, the root page exit button closes the menu. `link(label, url)` opens a website after the game's confirmation screen. ```kotlin menu("guide", "Adventure Guide") { page("Bosses") { text("The Warden sleeps in the Deep Dark.") link("Warden on the wiki", "https://minecraft.wiki/w/Warden") } } ``` Pages are generated at `data//dialog/.json` and `data//dialog/_.json`. ## Opening a menu `open(targets)` shows the root page, to `@s` by default. Each page's `dialog` works anywhere a dialog is expected, such as `dialogShow` or a text click event. ```kotlin tellraw(allPlayers(), textComponent("[Choose a kit]", Color.GREEN) { clickEvent { showDialog(kits.dialog) } }) ``` ## Pause screen and Smithed menu `pauseScreen = true` adds the menu to the pause screen through the `#minecraft:pause_screen_additions` dialog tag. `smithed = true` lists the menu in the shared data pack menu of the [Smithed convention](https://docs.smithed.dev/conventions/data-pack-menu/), so every compatible pack sits behind a single "Data Packs..." pause screen button. The root exit button then goes back to that list. ```kotlin menu("about", "Sky Wars") { text("Version 1.2.0, by Ayfri") link("Report a bug", "https://github.com/Ayfri/Kore/issues") smithed = true } ``` ## How it works A dialog button can only run commands with the clicking player's permissions, and `/function` needs operator rights. So every button runs `trigger .menu. set ` instead, which any player can use. The menu generates: - one function per button, - a `load` function creating the trigger objective, - a `tick` function enabling the trigger for all players and running the matching button function for each player who clicked, then resetting their score. A menu without buttons, only pages and links, generates no function at all. --- ## MiniMessage Renderer & Viewer - Parse Adventure Format in Minecraft --- root: .components.layouts.MarkdownLayout title: MiniMessage Renderer & Viewer - Parse Adventure Format in Minecraft nav-title: MiniMessage Renderer description: Parse Adventure MiniMessage format into Minecraft text components with Kore. Supports colors, decorations, click/hover events, gradients, and fonts. Use as a MiniMessage viewer for rich text in datapacks. keywords: minimessage, minimessage viewer, minimessage renderer, minimessage parser, adventure text, minecraft text components, minimessage generator, mini message format, kore minimessage, minimessage to chat component date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/helpers/minimessage-renderer --- # MiniMessage Renderer The `helpers` module includes a MiniMessage renderer that parses [Adventure MiniMessage](https://docs.advntr.dev/minimessage/format.html) format strings into Kore `ChatComponents`. This lets you write human-readable styled text that compiles into Minecraft's JSON text component format. MiniMessage is part of the [Adventure](https://github.com/KyoriPowered/adventure) library, widely used by Paper, Velocity, and other Minecraft server platforms. You can preview MiniMessage text in the [MiniMessage Web Viewer](https://webui.advntr.dev/) without starting a Minecraft instance. > [!FOOTPRINT] > Nothing at runtime: no objective, storage, entity, forceloaded chunk, tick or load function. The renderer runs while Kore builds the pack and returns `ChatComponents`. ## Basic Usage ```kotlin import io.github.ayfri.kore.text.miniMessageToTextComponents val components = miniMessageToTextComponents("Hello world!") ``` The function returns a `ChatComponents` instance that can be used anywhere Kore expects text components. ## Configuration Pass a configuration lambda to customize defaults: ```kotlin val components = miniMessageToTextComponents("Styled text") { color = Color.AQUA // default color for unstyled text bold = true // default bold state italic = false // default italic state font = "minecraft:alt" // default font strict = true // throw on parse errors } ``` ## Colors ### Named Colors All vanilla formatting colors are supported: ``` Red text Blue text Purple text ``` Aliases `` and `` are also supported: ``` Red text Blue text ``` ### Hexadecimal Colors ``` <#ff0000>Red text Green text Blue text ``` ### Gradients Gradient tags are recognized and apply the first color in the gradient: ``` Gradient text ``` ### Rainbow & Transition Rainbow and transition tags are recognized as style tags: ``` Rainbow text Transition text ``` ## Decorations | Tag | Aliases | Effect | |-------------------|---------------|---------------| | `` | `` | **Bold** | | `` | ``, `` | *Italic* | | `` | `` | Underlined | | `` | `` | Strikethrough | | `` | `` | Obfuscated | All decorations support closing tags to limit their scope: ``` Bold and italic just bold ``` ### Reset The `` (or ``) tag clears all active styles and returns to defaults: ``` Styled textNormal text ``` ## Click Events ``` Click to visit Click to spawn Click to message Next page Click to copy ``` ## Hover Events ``` Tooltip text'>Hover me Hover for item Hover for entity ``` The `show_text` value is itself parsed as MiniMessage, so you can use tags inside hover text. ## Fonts ``` Uniform font text Icon font ``` ## Insertion Shift-clicking the rendered component inserts the specified text into the chat prompt: ``` Shift-click me ``` ## Newlines ``` Line oneLine two Line one
Line two ``` ## Advanced Components ### Translatable (I18n) ``` ``` ### Keybinds ``` ``` ### Selectors ``` ``` ### Scoreboards ``` ``` ### NBT Data ``` ``` ## Escaping & Preformatted Text ### Backslash Escaping Prefix a tag with `\` to render it as literal text: ``` \This is not red ``` ### Preformatted Blocks Everything inside `
...
` is treated as raw text: ```
This renders as literal  tags
``` ## Tag Resolvers Custom placeholder tags can be resolved dynamically at render time: ```kotlin val components = miniMessageToTextComponents(" joined the game") { tagResolvers = mapOf( "player_name" to TagResolver { text("Ayfri") { color = Color.GOLD } } ) } ``` The resolved component inherits the current style context (color, bold, italic, font) unless it defines its own. ## Strict Mode Enable strict mode to throw a `MiniMessageParseException` on parse errors: ```kotlin miniMessageToTextComponents("Unclosed tag") { strict = true // throws MiniMessageParseException("Unclosed tags: bold") } ``` Strict mode catches: - Unclosed tags - Unknown/unrecognized tags - Closing tags with no matching opening tag ## References - [MiniMessage Format Specification](https://docs.advntr.dev/minimessage/format.html) - full tag reference from the Adventure docs. - [MiniMessage Web Viewer](https://webui.advntr.dev/) - interactive playground to preview MiniMessage text. - [Adventure GitHub Repository](https://github.com/KyoriPowered/adventure) - the library that defines the MiniMessage format. - [Minecraft Wiki - Raw JSON Text](https://minecraft.wiki/w/Raw_JSON_text_format) - the underlying component format MiniMessage compiles to. --- ## NBT Paths --- root: .components.layouts.MarkdownLayout title: NBT Paths nav-title: NBT Paths description: Build typed Minecraft NBT paths and split one NBT tree into static values plus runtime scores with the Kore helpers module. keywords: minecraft, datapack, kore, helpers, nbt, path, storage, execute store, resolve, score, macro date-created: 2026-09-04 date-modified: 2026-10-07 routeOverride: /docs/helpers/nbt-paths --- # NBT Paths Two related helpers for the NBT half of a datapack: `NbtPath` replaces interpolated path strings with typed segments, and `resolveNbt` writes one tree whose values mix literals and runtime scores. > [!FOOTPRINT] > Nothing at runtime: no objective, storage, entity, forceloaded chunk, tick or load function. `nbtPath` builds path strings in Kotlin, and `resolveNbt` writes into the storage, entity or block you pass, with one `execute store` per score. ## Typed paths Every core `data` and `execute store` API takes an NBT path as a `String`, so a typo compiles. `NbtPath` builds the path from segments instead, quoting each one only when vanilla requires it: ```kotlin val enchantments = nbtPath("equipment") / "mainhand" / "components" / "minecraft:enchantments" // equipment.mainhand.components."minecraft:enchantments" ``` | Operation | Result | |---------------------|------------------------------| | `path / "name"` | `path.name` | | `path / otherPath` | Appends every segment | | `path[0]` | `path[0]` | | `path.all()` | `path[]` | | `path.matching { }` | `path[{Slot:0b}]` | | `path.isRoot` | `true` for the root compound | `String.toNbtPath()` parses a path already written in Minecraft's syntax, so an existing constant can join the typed API. It splits only on the dots that separate segments, leaving quoted keys, list indices, and compound filters intact: ```kotlin val custom = """Items[{Slot:0b}].components."minecraft:custom_data".id""".toNbtPath() custom.segments // [Items[{Slot:0b}], components, "minecraft:custom_data", id] ``` The helpers module adds `NbtPath` overloads to `Data.modify`, `Data.get`, `Data.remove`, `ExecuteStore.storage`, `ExecuteStore.entity`, and `ScoreboardEntity.copyTo`, so both halves of a store-plus-modify pair share one value: ```kotlin function("equip") { val level = nbtPath("equipment") / "mainhand" / "components" / "minecraft:enchantments" / "minecraft:protection" data(player.asSelector()).modify(level, 4) execute { storeResult { entity(player.asSelector(), level, DataType.INT, 1.0) } run { scoreboard.players.get(literal("#armor"), "stats") } } } ``` ## Resolving scores into NBT Minecraft cannot write a score into an NBT tree directly. The vanilla shape is one `data modify ... set value` for the literal parts, then one `execute store result` per runtime value - two halves that drift apart as the tree grows. `resolveNbt` declares the tree once and emits both halves from it: ```kotlin function("build_offer") { val price = fakePlayer("price").getScoreEntity("shop") val stock = fakePlayer("stock").getScoreEntity("shop") resolveNbt(storage("shop", "kore"), nbtPath("offer")) { this["item"] = "minecraft:diamond" this["price"] = price compound("meta") { this["featured"] = true this["stock"] = stock } } } ``` ```mcfunction data modify storage kore:shop offer set value {item:"minecraft:diamond",meta:{featured:1b}} execute store result storage kore:shop offer.price int 1 run scoreboard players get #price shop execute store result storage kore:shop offer.meta.stock int 1 run scoreboard players get #stock shop ``` `execute store result` creates the parent compounds it needs, so a nested compound holding only scores emits no `data modify` line of its own. A tree with no literal at all emits only the store lines. Values accept `NbtTag` and every Kotlin primitive, a `ScoreboardEntity`, or a [`ScoreboardDelegate`](/docs/helpers/state-delegates). Scores default to `int` with no scaling; pass a `DataType` and a `scale` for fixed-point scores: ```kotlin resolveNbt(storage("physics", "kore"), nbtPath("motion")) { // a score counting thousandths of a block becomes a double set("y", velocity, DataType.DOUBLE, 0.001) } ``` Passing the root path (the default) writes the literal half with `data merge` instead of `data modify`. An `Entity` overload targets entity NBT rather than storage. ## Feeding a macro The usual reason to assemble a tree at runtime is calling a macro function with it, which makes `resolveNbt` and `function ... with storage` a natural pair: ```kotlin function("teleport_to_score") { val macro = storage("macro", "kore") resolveNbt(macro) { this["x"] = posX this["y"] = posY this["z"] = posZ } function("do_teleport", arguments = macro) } ``` ## See also - [Data Storage](/docs/concepts/data-storage) - The core `data` command DSL these helpers wrap. - [Scoreboards](/docs/oop/scoreboards) - Fake players and the score handles `resolveNbt` reads from. - [State Delegates](/docs/helpers/state-delegates) - Delegated scores usable as `resolveNbt` values. --- ## Raycasts --- root: .components.layouts.MarkdownLayout title: Raycasts nav-title: Raycasts description: Recursive raycast system with the Kore helpers module - step-based raycasting with block hit, max distance, and per-step callbacks. keywords: minecraft, datapack, kore, helpers, raycast, ray, block, hit, step, recursive date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/helpers/raycasts --- # Raycasts Raycasts generate a set of recursive functions that step forward from the entity's eyes until they hit a block or reach max distance. Each step is a local-coordinate [`execute positioned`](https://minecraft.wiki/w/Commands/execute) call, so precision scales with the step size. They are ideal for "look at" interactions, custom tools, line-of-sight checks, or visual debugging because the helper pre-builds the recursive command chain for you. > [!FOOTPRINT] > - **Objectives:** `kore_raycast`, shared by every ray. > - **Storage:** none. > - **Entities:** none. The caster carries the `kore_raycasting` tag while its ray runs, removed on hit or at max distance. > - **Forceloaded chunks:** none. > - **Every tick:** no, a cast runs within the tick it starts in. > - **Load and files:** `kore_raycast_init` creates the objective, and each ray gets `raycast__start`, `_step`, `_hit` and `_max`. ## API overview The helper is centered around two entry points: - `raycast { ... }` - builds a fresh `RaycastConfig` inline. - `raycast(config)` - reuses an already configured `RaycastConfig` instance. The generated `RaycastHandle` exposes a single `cast()` method that launches the raycast from the current function context. `RaycastConfig` defaults: - `name = "raycast"` - `maxDistance = 100` - `step = 0.5` - `onHitBlock = {}` - `onStep = null` - `onMaxDistance = null` ## DSL builder ```kotlin val ray = raycast { name = "my_ray" maxDistance = 50 step = 0.5 onHitBlock = { say("Hit!") } onMaxDistance = { say("Too far!") } onStep = { particle(Particles.FLAME, vec3()) } } function("use_ray") { with(ray) { cast() } } ``` ## Callback lifecycle - `onStep` runs for every successful step before the ray stops. - `onHitBlock` runs when the next step collides with a block. - `onMaxDistance` runs when the ray reaches the configured limit without hitting anything. If `onMaxDistance` is omitted, the ray quietly removes its internal tag when it reaches the limit, so the generated functions stop recursing without firing a fallback callback. This makes it easy to keep gameplay logic explicit: particles can live in `onStep`, impact logic in `onHitBlock`, and fallback behavior in `onMaxDistance`. ## Practical usage pattern ```kotlin val scanner = raycast { name = "scanner" maxDistance = 24 step = 0.25 onStep = { particle(Particles.END_ROD, vec3()) } onHitBlock = { say("Target acquired") } onMaxDistance = { say("No target found") } } function("scan_once") { with(scanner) { cast() } } ``` For precise interaction beams, prefer a smaller `step`; for cheaper "good enough" scans, use a larger one. The step value is measured in blocks and is applied through a local-position offset, so smaller values increase precision while also generating more recursive calls. ## See also - [Area](/docs/helpers/area) - Define spatial zones that complement what a ray can detect or trigger. - [VFX Particles](/docs/helpers/vfx-particles) - Use particles inside `onStep` for visual debugging or beam effects. - [Entities & Players](/docs/oop/entities-and-players) - Execute raycasts from player or mob contexts with reusable selectors. --- ## Minecraft Datapack Scheduler - Loop, Delay & Schedule Tasks with Kore --- root: .components.layouts.MarkdownLayout title: Minecraft Datapack Scheduler - Loop, Delay & Schedule Tasks with Kore nav-title: Scheduler description: Schedule and loop tasks in Minecraft datapacks with Kore. Tick-based loops, delayed actions, repeating schedules, and timed callbacks without complex scoreboard chains. keywords: datapack scheduler, minecraft schedule, timer datapack, datapack loop, tick loop minecraft, schedule function, repeating task datapack, delay command minecraft, kore scheduler, datapack timer loop date-created: 2025-03-26 date-modified: 2026-10-07 routeOverride: /docs/helpers/scheduler --- # Scheduler in Kore This document explains how to schedule and run tasks at specific times or intervals using Kore's built-in scheduler. Schedulers help automate recurring actions, delayed tasks, and cleanup when tasks are no longer needed. They are the higher-level way to do the time-based loops described in [Runtime Logic](/docs/concepts/runtime-logic). > [!FOOTPRINT] > - **Objectives:** none. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** no, tasks run through `/schedule`. > - **Load and files:** `scheduler_setup` schedules every task on load. A task given as a block becomes `scheduler_task_`, a repeating one gets a `scheduler_` wrapper that schedules itself again. ## Overview A "Scheduler" in Kore lets you: - Schedule a one-time execution of a function or a block of code. - Schedule repeating tasks with a fixed period. - Persist references to scheduled tasks so they can be modified or canceled. All scheduling logic revolves around three core classes: 1. `Scheduler` - Represents a single scheduled task (with optional delay and period). 2. `UnScheduler` - Cancels, or clears, repeating tasks. 3. `SchedulerManager` - Maintains a list of schedulers for a given DataPack and offers convenience methods to add or remove them. All three live in [Scheduler.kt](https://github.com/Ayfri/Kore/blob/master/helpers/src/commonMain/kotlin/io/github/ayfri/kore/helpers/Scheduler.kt) in the `helpers` module. **Note:** **Schedulers are saved and loaded from a `scheduler_setup` function that is added to the `minecraft/load.json` tag.** ## Basic Usage Use the DataPack extension function schedulerManager to get or create a SchedulerManager for your datapack: ```kotlin val datapack = dataPack("my_datapack") { // ... schedulerManager { // ... } // ... } ``` Inside the schedulerManager block, you can add schedulers with different behaviors by calling addScheduler. Common calls are: - `addScheduler(delay)` - Executes once after a given delay ([`TimeNumber`](/docs/concepts/time)). - `addScheduler(delay, period)` - Executes once after delay, then repeats every period. - `addScheduler(block)` - Executes right away if no delay or period is specified. In many cases, you'll pass a function block (Function.() -> Command) so you can include DSL commands: ### One-Time Execution If you only want to run a function once after a delay: ```kotlin schedulerManager { addScheduler(5.seconds) { say("I run after 5 seconds!") } } ``` ### Recurrent Execution For periodic tasks: ```kotlin schedulerManager { addScheduler(3.seconds, 1.seconds) { // This code runs first after 3 seconds, then every 1 second. debug("Repeated task!") } } ``` ### Schedule by reference You can also schedule a task by reference: ```kotlin val myFunction = function("my_function") { debug("Hello, world!") } schedulerManager { addScheduler(myFunction, 10.seconds) } ``` ### Canceling a Repeating Task If you have a repeating scheduler, you can unschedule it when you no longer need it: 1. Pass a named function or store the return value of addScheduler. 2. Call unSchedule or removeScheduler. Example removing by reference: ```kotlin val repeatingScheduler = addScheduler(2.seconds, 2.seconds) { debug("Repeat every 2 seconds!") } // Later in the code, for instance in another function: unSchedule(repeatingScheduler.function) // stops the repeating task ``` Or remove by function name: ```kotlin removeScheduler("my_function_to_remove") ``` ### Canceling all tasks You can cancel every repeating task by calling `unScheduleAll()` from a function: ```kotlin unScheduleAll() ``` ## Complex Example Below is a more advanced scenario showing how to: 1. Run a periodic task (initial delay + repeating period). 2. Store custom data in a named storage using the /data command. 3. Use execute to conditionally run commands based on stored data. In this example, we keep a running "counter" in a storage and increment it every time the repeating task runs. We also demonstrate how to read from that storage in subsequent commands. ### Full Example ```kotlin fun DataPack.complexSchedulerExample() { // Create a storage reference for storing and reading data val myStorage = storage("counter_storage", "kore_example") // A function that resets the counter at any time function("reset_counter") { data(myStorage) { merge { this["counter"] = 0 } } say("Counter has been reset to 0!") } schedulerManager { // Turn on debug logs for demonstration debug = true // Create a repeating scheduler. It starts after 2 seconds, repeats every 4 seconds. addScheduler(2.seconds, 4.seconds) { val tempEntity = literal("#temp_entity") // a fake player holding the score // 1) Store the value in a score execute { storeResult { score(tempEntity, "counter") } run { data(myStorage) { get("counter") } } } // 2) Increment the score scoreboard.objective(tempEntity, "counter").add(1) // 3) Store the score in the storage execute { storeResult { storage(myStorage, "counter", DataType.INT, 1.0) } run { scoreboard.objective(tempEntity, "counter").get() } } // 4) Print out the current counter using execute + data get execute { // We can conditionally run commands if the counter is above a threshold, etc. run { // Show the updated counter in chat every time this repeats data(myStorage) { get("counter") } // This prints the raw integer. Let's also do a friendly message: say("Counter incremented!") } } // 5) Condition example: if the counter >= 5, summon something or do logic // Check if the score is >= 5, as we already stored the value in a score execute { // Compare the counter with a threshold, e.g. 5 ifCondition { score(tempEntity, "counter") greaterThanOrEqualTo 5 } run { summon(EntityTypes.LIGHTNING_BOLT) say("Counter is >= 5. Summoned lightning!") } } } // Another single-run task in 10 seconds to reset everything addScheduler(10.seconds) { function("reset_counter") say("All done, resetting!") } } } ``` Explanation: 1. We create a custom storage named "kore_example:counter_storage" to maintain a key called "counter". 2. We set up a repeating task (delayed by 2 seconds, repeats every 4 seconds). Inside that repeating schedule: - We store the counter in a score - We increment the counter in storage. - We show the updated counter using data get and say commands. - We run a score condition to check if "counter" has reached 5 or more, then summon a lightning bolt. 3. We also add a single-run scheduler at 10 seconds that calls a function to reset the counter and prints a final message. ## Conclusion - Schedulers let you automate tasks in your datapack with fixed delays or repetition. - Use a SchedulerManager on your DataPack via `schedulerManager { … }`. - Add, remove, or clear schedulers by referencing either the assigned function or the function name. - Combine schedulers with any usual commands for fully automated or repeated logic (debugging, storing data, advanced "execute" conditions, etc.). This powerful system helps keep your datapack logic neatly organized and easy to maintain when you need repeated or delayed operations. --- ## Score Vectors --- root: .components.layouts.MarkdownLayout title: Score Vectors nav-title: Score Vectors description: Runtime 3D vectors on Minecraft scoreboards with Kore - look directions, dot and cross products, exact length, normalize, teleports and motion. keywords: minecraft, datapack, kore, helpers, vector, position, motion, velocity, normalize, dash, knockback, teleport, look direction date-created: 2026-09-23 date-modified: 2026-10-07 routeOverride: /docs/helpers/score-vectors --- # Score Vectors A `ScoreVector` is a runtime 3D vector stored in three scores of an entity (`_x`, `_y`, `_z`). It reads positions, motion and look directions, does the vector maths, then writes the result back as a teleport, a motion or a rotation. The same data covers what Bookshelf splits into `bs.vector`, `bs.position`, `bs.move` and `bs.view`. ```kotlin val math = registerMath() function("dash") { val dash = math.vector("dash", player) dash.setToLookDirection(self()) dash *= 3 dash.teleport(self(), TeleportMode.RELATIVE) } ``` Components are fixed-point with a `scale` of `1000` by default, so `1500` means `1.5` blocks. `math.vector` registers the three objectives in the math load function. Operations between two vectors expect the same scale. > [!FOOTPRINT] > - **Objectives:** `_x`, `_y` and `_z` per vector, plus `kore_math`. > - **Storage:** `:kore_math`, holding the rotation matrix. > - **Entities:** one `minecraft:marker` and one `minecraft:text_display`, UUIDs `4b4f5245-0000-0000-0000-000000000001` and `...02`, tagged `kore.math`, `smithed.entity` and `smithed.strict`, summoned once at -30000000 0 1664 by the operations that read positions or directions. > - **Forceloaded chunks:** the one holding those entities, x -30000000 z 1664. > - **Every tick:** no. > - **Load and files:** `kore_math_init` creates the objectives, entities, storage and the `#` constants. ## Reading vectors ```kotlin function("track_arrow") { val velocity = math.vector("velocity", arrow) velocity.setToMotion(self()) // blocks per tick } ``` - `setToPosition(entity)` reads the position off the math marker teleported onto the entity, never touching its NBT, since reading any field of a player's NBT serializes the whole player. `setToMotion(entity)` copies `Motion` to storage once and reads the three components from there. - `setToLookDirection(entity)` gives the unit vector the entity looks along, the direction of `^ ^ ^1`. - `set(x, y, z)` takes plain numbers, `set(0.0, 0.5, 0.0)` stores `0 500 0`. - `setTo(other)` copies another vector. ## Arithmetic Operators come first, they read like plain Kotlin: ```kotlin function("wind_push") { velocity += wind velocity *= 2 velocity /= 4 } ``` - `+=` / `-=` add or subtract another vector. - `*=` / `/=` with an `Int` scale every component, with Minecraft's floored division. - `*=` with a `ScoreboardEntity` multiplies by a fixed-point factor of the same scale, `500` halves the vector. ## Dot and cross products ```kotlin function("backstab_check") { attackerLook.dot(victimLook, alignment) // 1000 = same direction } ``` `dot(other, output)` writes the fixed-point dot product. `cross(other, output)` writes `this × other` into a third vector, which must differ from both operands. ## Length and distance ```kotlin function("leash_range") { playerPos.distanceTo(petPos, distance) } ``` `length(output)` and `distanceTo(other, output)` write the length in the vector's scale, exact to the float and without overflow. The components go into the first column of a `text_display` transformation matrix, and the game's own decomposition returns the length as `scale[0]`. `lengthSquared(output)` stays on scoreboards and is enough for comparisons, but it overflows past about `46` blocks at scale `1000`. ## Normalizing ```kotlin function("homing_missile") { toTarget.setTo(targetPos) toTarget -= missilePos toTarget.normalize() } ``` `normalize()` rescales the vector to a length of `1000`. It moves the math marker to the vector's coordinates and teleports it one block along `facing`, so it is exact to the float. A zero vector has no direction and gives an undefined result. ## Writing vectors back ```kotlin function("launch_pad") { launch.set(0.0, 1.2, 0.0) launch.applyAsMotion(self()) } ``` - `applyAsMotion(entity)` writes `Motion`, in blocks per tick. Players ignore `Motion` changes, teleport them instead. - `teleport(entity, mode)` teleports through a macro `tp`, so it works on players: - `TeleportMode.ABSOLUTE` treats the vector as world coordinates, - `TeleportMode.RELATIVE` offsets from the entity (`~x ~y ~z`), - `TeleportMode.LOCAL` offsets along the entity's view (`^x ^y ^z`, x is left, z is forward). - `lookAlong(entity)` turns the entity to face along the vector, through `tp` so it also works on players. ## Native physics attributes For bounces and sliding, the game's attributes handle collision themselves, with no per-tick resolution in the pack: `bounciness` sets how much velocity survives a collision, `friction_modifier` scales ground friction, and `air_drag_modifier` scales air drag. ```kotlin function("rubber_slime") { attributes(self(), Attributes.BOUNCINESS) { base { set(0.9) } } } ``` ## See also - [Scoreboard Math](/docs/helpers/scoreboard-math) - Sine, cosine, atan2 and square root on the same fixed-point scale. - [Raycasts](/docs/helpers/raycasts) - Step along a look direction until a block is hit. - [Display Entities](/docs/helpers/display-entities) - Compile-time transformations with `Vec3f` and quaternions. --- ## Scoreboard Math Engine --- root: .components.layouts.MarkdownLayout title: Scoreboard Math Engine nav-title: Scoreboard Math description: Fast fixed-point sine, cosine, atan2 and exact integer square root on Minecraft scoreboards with Kore, shared functions and two commands per call. keywords: minecraft, datapack, kore, helpers, math, scoreboard, trigonometry, sine, cosine, atan2, sqrt, fixed point date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/helpers/scoreboard-math --- # Scoreboard Math Engine The math module computes trigonometry and roots at runtime on scoreboards. Every heavy routine lives in **one shared generated function**, so each call site costs two commands: copy the input, then store the function's return value. ```kotlin val math = registerMath() function("orbit") { math.cos(angle, offsetX) math.sin(angle, offsetZ) } ``` ```mcfunction scoreboard players operation #x kore_math = @s angle execute store result score @s offset_x run function my_pack:generated_scopes/kore_math/cos_1 ``` > [!FOOTPRINT] > - **Objectives:** `kore_math`, with its `#` constants. > - **Storage:** none, except for the routines listed in [Helper entities](#helper-entities). > - **Entities:** none, except for the routines listed in [Helper entities](#helper-entities). > - **Forceloaded chunks:** none, except for those same routines. > - **Every tick:** no. > - **Load and files:** `kore_math_init` creates the objective and constants, and each routine is one shared function under `generated_scopes/kore_math/`. ## Registering the module `registerMath()` returns the datapack's `MathHandle` (the same one on every call) and creates the `kore_math_init` load function. Constants, scratch scores and helper entities are added to it only when an operation needs them, so an unused routine adds nothing to the pack. Every example below uses `ScoreboardEntity` scores. Each routine also accepts an `(entity, "input", "output")` form and [scoreboard delegates](/docs/helpers/state-delegates). ## Fixed-point convention Scoreboards only hold integers, so results are scaled by **1000**, `MATH_SCALE`: - `1000` means `1.0` - `-707` means `-0.707` ## Sine and cosine The input is an angle in degrees, any value including negative ones. The result is `sin × 1000`, off by at most one unit, from a degree-5 polynomial evaluated purely on scoreboards: no entity and no lookup table. ```kotlin function("swing_sword") { math.sin(swingAngle, liftY) // 90 -> 1000, -30 -> -500 } ``` Pass `angleScale` when the angle carries decimals, `100` means hundredths of a degree. Kore generates one function per scale with its constants precomputed. Scales above `238` are rejected because the fixed-point product would overflow an `Int`. ```kotlin function("smooth_orbit") { math.cos(preciseAngle, orbitX, angleScale = 100) // 4500 -> 707 } ``` ## atan2 `atan2(y, x, output)` returns the angle of the point `(x, y)` in degrees, in `(-180, 180]`. Both inputs only need to share a scale. It uses the math marker's `facing` rotation, so it is exact to the float. ```kotlin function("turret_aim") { math.atan2(deltaZ, deltaX, yaw) } ``` ## Integer square root `sqrt` returns exactly `floor(sqrt(input))` for every non-negative `Int`, and `0` for negative inputs. It runs a piecewise-linear first guess plus three Newton steps, checked against all 2³¹ inputs. ```kotlin function("shockwave_radius") { math.sqrt(energy, radius) // 1000000 -> 1000 } ``` For the length of a vector, prefer [`ScoreVector.length`](/docs/helpers/score-vectors#length-and-distance): it never overflows and keeps decimals. ## Squared distance and parabola `distanceSquared` computes `(x2 - x1)² + (y2 - y1)² + (z2 - z1)²` from six scores of one entity. It is enough for range checks, but it overflows once an axis difference goes past `46340`. ```kotlin function("in_blast_range") { math.distanceSquared(player, "x1", "y1", "z1", "x2", "y2", "z2", "dist_sq") } ``` `parabola` computes a projectile height `v0 × t - g × t² / 2` from four scores: ```kotlin function("arrow_preview") { math.parabola(time, launchSpeed, gravity, height) } ``` ## Helper entities `atan2` and the vector helpers use two entities that `kore_math_init` summons in a forceloaded chunk at `-30000000 1664`: a `marker` and a `text_display`. Both have fixed UUIDs (`4b4f5245-0-0-0-1` and `-2`) and carry the Smithed `smithed.entity` and `smithed.strict` tags, so well-behaved packs leave them alone. Change `HelpersConstants.mathEntitiesX` / `mathEntitiesZ` before registering to use another chunk. ## Function reference | Function | Output | Cost at the call site | |------------------------------|---------------------------------------------|-----------------------| | `sin` / `cos` | `value × 1000`, error of at most one unit | 2 commands | | `atan2` | Degrees × `angleScale`, in `(-180, 180]` | 3 commands | | `sqrt` | Exact `floor(sqrt(x))` | 2 commands | | `distanceSquared` | Squared 3D distance | 13 commands | | `parabola` | `v0 × t - g × t² / 2` | 7 commands | | `sinTo` / `cosTo` / `sqrtTo` | Infix forms for scoreboard delegates | 2 commands | ## See also - [Score Vectors](/docs/helpers/score-vectors) - Runtime 3D vectors, directions, teleports and motion built on this module. - [State Delegates](/docs/helpers/state-delegates) - Scoreboard-backed values to feed into these helpers. - [Scoreboards](/docs/oop/scoreboards) - Higher-level scoreboard utilities. --- ## Minecraft Datapack Sidebar - Custom Scoreboard Lines with Kore --- root: .components.layouts.MarkdownLayout title: Minecraft Datapack Sidebar - Custom Scoreboard Lines with Kore nav-title: Sidebars description: "Build server-style Minecraft sidebars with Kore: up to 15 text lines, right-aligned values, live scores, conditional lines and cheap refreshes." keywords: minecraft sidebar, datapack sidebar, scoreboard display, custom scoreboard, scoreboard lines, numberformat, kore sidebar, minigame scoreboard date-created: 2026-09-23 date-modified: 2026-10-07 routeOverride: /docs/helpers/sidebars --- # Sidebars A `Sidebar` shows up to 15 lines of text on the right of the screen, like a minigame server scoreboard. Each line has a left text and an optional right-aligned value, and can hold live scores, selectors or NBT values. ```kotlin import io.github.ayfri.kore.helpers.sidebar.sidebar val lobby = sidebar("lobby") { title("Sky Wars", Color.GOLD) line("Map: Floating Isles") emptyLine() line("Players", value = scoreComponent("players_alive", literal("#game"))) line("Kills", Color.RED, scoreComponent("kills", literal("#top"))) emptyLine() line("play.example.net", Color.YELLOW) } load { lobby.create() } tick { lobby.refresh() } ``` {{{ .components.mc.SidebarMockup }}} `sidebar(...)` only declares the sidebar, commands are emitted where you call its functions. > [!FOOTPRINT] > - **Objectives:** one named after the sidebar, removed and created again by `create()`, which also takes the `sidebar` display slot. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** only if you call `refresh()` from a tick function, it rewrites the live lines. > - **Load and files:** none of its own, `create()` writes its commands into the function calling it. ## Lines `line(text, color, value)` adds a line, `emptyLine()` adds a blank one. Lines appear in declaration order, top to bottom. ```kotlin sidebar("stats") { line("Coins") { value("1 250", Color.GOLD) } line(textComponent("Rank: ") + text("VIP", Color.AQUA)) } ``` Without a value, a line shows nothing on its right: the score numbers are always hidden. ## Live values and refresh The game resolves score, selector and NBT components once, when a line is written. `refresh()` rewrites only the lines holding such components, so calling it every tick stays cheap. ```kotlin val arena = sidebar("arena") { line("Time left", value = scoreComponent("timer", literal("#round"))) line("Arena: Desert") } tick { arena.refresh() } ``` Here `refresh()` emits the three commands of the "Time left" line, the static "Arena" line is never rewritten. Score components read a named score holder, such as a fake player like `#round`. `@s` has no meaning in a sidebar refreshed from `tick`. ## Conditional lines `visibleIf` only shows a line while its condition passes. The condition is checked on `create()` and `refresh()`. ```kotlin sidebar("event") { line("Double XP active!", Color.GREEN) { visibleIf { score(literal("#event"), "double_xp", rangeOrInt(1)) } } } ``` ## Runtime changes `setLine` replaces a line from any function, without changing its declaration. ```kotlin function("switch_map") { lobby.setLine(0, textComponent("Map: Nether Fortress")) } ``` `show(slot)`, `hide(slot)` and `remove()` manage the display. `DisplaySlots.sidebarTeam(color)` shows a different sidebar to each team color. ```kotlin function("red_team_sidebar") { redTeamSidebar.show(DisplaySlots.sidebarTeam(FormattingColor.RED)) } function("end_game") { lobby.hide() lobby.remove() } ``` ## How it works Each sidebar owns its objective, and each line is the fake player `$` scored `-index`, so lines sort top to bottom and two sidebars never share a line. `create()` recreates the objective, hides score numbers with `numberformat blank`, writes the text with `scoreboard players display name` and the value with `numberformat fixed`. A sidebar looks the same for every player watching the same slot. Per-player sidebars aren't possible in vanilla because display slots are global, team-colored slots are the closest option. --- ## State Delegates --- root: .components.layouts.MarkdownLayout title: State Delegates nav-title: State Delegates description: Kotlin property delegates that map scoreboard objectives or NBT storage to simple var properties with the Kore helpers module. keywords: minecraft, datapack, kore, helpers, state, delegate, scoreboard, storage, nbt, property, operators, fake player date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/helpers/state-delegates --- # State Delegates Kotlin property delegates that map scoreboard objectives or [data storage](/docs/concepts/data-storage) paths to simple `var` properties. Writing to the property emits the corresponding Minecraft command. This helper reduces repetitive boilerplate in command-generation code. You write Kotlin that looks like state mutation, while Kore still emits explicit vanilla commands underneath. The two main delegate types are: - `ScoreboardDelegate` for integer scoreboard-backed state. - `StorageDelegate` for NBT-backed storage paths. > [!FOOTPRINT] > Only the objective or storage path you name: a scoreboard delegate writes `scoreboard objectives add` once, in the function using it. No entity, forceloaded chunk, tick or load function. ## Scoreboard delegate ```kotlin function("mana_system") { var mana by player.scoreboard("mana_obj", default = 100) mana = 80 // emits: /scoreboard players set mana_obj 80 } ``` When the property is read during code generation, the delegate returns the compile-time `default` value. The real runtime value still lives in the scoreboard, so you should think of the delegate as a concise **command emitter**, not a live synchronized Kotlin variable. The objective is created lazily the first time the property is accessed or assigned, and it uses `dummy` by default. If you need relative changes instead of absolute sets, use the paired scoreboard entity handle: ```kotlin function("mana_delta") { val manaObj = player.scoreboardEntity("mana_obj") manaObj += 10 manaObj -= 20 } ``` `scoreboardEntity(...)` returns a `ScoreboardEntity`, which exposes the same intent in command form: - `set(value)` for absolute updates. - `add(value)` and `remove(value)` for relative updates. - `plusAssign` / `minusAssign` as Kotlin operator sugar for those relative updates. ### Arithmetic between delegates Two delegates combine directly, without unwrapping either of them into a score handle. `+=`, `-=`, `*=`, `/=` and `%=` each compile to one `scoreboard players operation`, and `setTo`, `minWith`, `maxWith` and `swapWith` cover the remaining vanilla operations: ```kotlin function("apply_damage") { val health = player.scoreboard("health") val damage = player.scoreboard("incoming_damage") val floor = player.scoreboard("min_health") health -= damage health maxWith floor damage *= 2 } ``` `*=`, `/=` and `%=` also accept an `Int`, read from a fake-player constant Kore declares once per function. See [Scoreboards](/docs/oop/scoreboards#operators) for the emitted commands. ### Global scores A delegate is not tied to a real entity. Pair it with a [fake player](/docs/oop/scoreboards#fake-players) for values that belong to the pack rather than to a player: ```kotlin function("start_wave") { var wave by fakePlayer("wave").scoreboard("game_state") wave = 1 // emits: /scoreboard players set #wave game_state 1 } ``` ## Storage delegate ```kotlin function("storage_demo") { var customTag by player.storage("my_namespace:data", "customTag", default = "hello") customTag = "world" // emits: /data modify storage my_namespace:data customTag set value "world" } ``` `storage(...)` accepts `Int`, `Float`, `String`, and `Boolean` directly; other values are stringified before being written. ## Mixing delegates with other helpers State delegates become more useful when you mix them with other command helpers in the same function. A common pattern is to keep an integer on a scoreboard for arithmetic, while richer UI or session state stays in storage: ```kotlin function("combat_state") { val player = entity() var combo by player.scoreboard("combo", default = 0) val comboScore = player.scoreboardEntity("combo") var phase by player.storage("my_namespace:state", "combat.phase", default = "idle") var shieldReady by player.storage("my_namespace:state", "combat.shield_ready", default = false) combo = 1 comboScore += 4 phase = "charged" shieldReady = true debug("Combat state updated") } ``` This emits concise vanilla commands while keeping your Kotlin code expressive: - `combo` stays easy to reuse with other scoreboard-based helpers such as cooldowns, timers, selectors, or math. - `phase` and `shieldReady` live in storage, which fits better for descriptive or boolean state. - `scoreboardEntity(...)` keeps relative changes (`+=`, `-=`) readable next to direct assignments. You can also combine scoreboard-backed delegates with selector-heavy flows. For example, use a delegated score for the canonical state, then feed the same objective into `execute if score`, inventory listeners, or scheduler callbacks. If you want a condition like `if score == 45`, you can now keep the delegate itself around and feed it directly to a helper. Internally, these helpers lean on the same `ExecuteCondition.score(...)` and `Relation` primitives as the regular `execute` DSL, so the generated syntax stays aligned with the rest of Kore. ```kotlin function("combo_ready") { val player = entity() val combo = player.scoreboard("combo", default = 0) var comboValue by combo comboValue = 45 runIf(combo equalTo 45) { say("Combo is exactly 45") } } ``` This still generates an `execute if score combo matches 45 run ...` command, but without manually opening an `execute` block every time. In practice, `equalTo(45)` is the most readable way to express an exact score check. If you want a quick reference for every comparison helper available with delegated scores, they map directly to the same scoreboard comparisons as the `execute` DSL: | Kotlin helper | Against a literal | Against another score | |-----------------------------|-------------------|-----------------------| | `equalTo(...)` | `matches 10` | `=` | | `greaterThan(...)` | `matches 11..` | `>` | | `greaterThanOrEqualTo(...)` | `matches 10..` | `>=` | | `lessThan(...)` | `matches ..9` | `<` | | `lessThanOrEqualTo(...)` | `matches ..10` | `<=` | | `matches(...)` | `matches ` | - | | `notEqualTo(...)` | `matches 10` | `=` | Scores have no `!=`, so `notEqualTo` emits `execute unless score ...` with the `equalTo` check. `runUnless` does the same for any condition, running its block when the condition doesn't match: ```kotlin function("combo_negations") { val player = entity() val combo = player.scoreboard("combo", default = 0) runIf(combo notEqualTo 10) { say("combo != 10") } // execute unless score combo matches 10 run ... runUnless(combo greaterThan 5) { say("combo <= 5") } // execute unless score combo matches 6.. run ... } ``` ```kotlin function("combo_comparisons") { val player = entity() val combo = player.scoreboard("combo", default = 0) val threshold = player.scoreboard("threshold", default = 10) runIf(combo equalTo 10) { say("combo == 10") } runIf(combo greaterThan 10) { say("combo > 10") } runIf(combo greaterThanOrEqualTo 10) { say("combo >= 10") } runIf(combo lessThan 10) { say("combo < 10") } runIf(combo lessThanOrEqualTo 10) { say("combo <= 10") } runIf(combo greaterThan threshold) { say("combo > threshold") } } ``` You can also compare two delegated scores directly: ```kotlin function("combo_threshold") { val player = entity() val combo = player.scoreboard("combo", default = 0) val threshold = player.scoreboard("threshold", default = 0) var comboValue by combo var thresholdValue by threshold comboValue = 45 thresholdValue = 45 runIf(combo equalTo threshold) { say("Combo reached the threshold") } } ``` That form emits `execute if score combo = threshold run ...`, which is useful when both the current value and the threshold are computed elsewhere in your datapack. For repeated checks, the same delegate can drive higher-level control flow helpers: ```kotlin function("combo_loops") { val player = entity() val combo = player.scoreboard("combo", default = 0) var comboValue by combo comboValue = 3 runWhile(combo greaterThan 0, name = "combo_while") { say("Combo loop") combo.scoreboardEntity() -= 1 } comboValue = 9 repeat(combo, name = "combo_repeat") { i -> runIf(i equalTo 1) { say("Second iteration") } say("Repeat once per score point") } } ``` - `runIf(...)` wraps a single `execute if score ... run function ...` call. - `runWhile(...)` generates a recursive helper function that reruns itself while the score condition stays true. - `repeat(...)` is a small `for`-style helper built on top of `runWhile(...)`: by default it creates an internal counter score with a generated unique name, copies the input score into it, and decrements that internal counter so the main delegated score stays intact. You can still pass `counter = ...` to reuse an existing delegated score as the loop counter instead. - The lambda parameter (`it`) is itself a `ScoreboardDelegate` for the current iteration index, so you can compare it, pass it to `runIf(...)`, or even delegate it again with `var iteration by it`. ## When to use which delegate - Use **`scoreboard(...)`** for integers that must participate in score comparisons, timers, cooldowns, or arithmetic. - Use **`storage(...)`** for richer state that fits naturally in NBT-backed data trees. - Use **`scoreboardEntity(...)`** when you want concise `+=` / `-=` style operations instead of direct assignment. - Mix **`scoreboard(...)`** and **`storage(...)`** when you need both arithmetic-friendly counters and descriptive session data in the same workflow. ## API summary | Function / type | Purpose | |----------------------------------------------|---------------------------------------------------------------------------------------------------| | `scoreboard(...)` | Create a scoreboard-backed delegate for an entity. | | `scoreboardEntity(...)` | Create a score handle for relative arithmetic. | | `runIf(...)` | Run a block when a delegated score condition matches. | | `runUnless(...)` | Run a block when a delegated score condition doesn't match. | | `runWhile(...)` | Re-run a block while a delegated score condition stays true. | | `repeat(...)` | Run a block once per score point, with an optional separate counter score and iteration delegate. | | `storage(...)` | Create an NBT storage-backed delegate. | | `setTo` / `minWith` / `maxWith` / `swapWith` | Infix score operations between two delegates. | | `ScoreboardDelegate` | Lazy scoreboard delegate implementation plus score conditions. | | `StorageDelegate` | Generic storage delegate implementation. | ## See also - [Scoreboard Math](/docs/helpers/scoreboard-math) - Feed delegated scoreboard values into trigonometric or algebraic helpers. - [Scheduler](/docs/helpers/scheduler) - Pair delegated state with delayed or repeating helper callbacks. - [Cooldowns](/docs/oop/cooldowns) - A concrete scoreboard-based gameplay system where delegated state can stay concise. - [Scoreboards](/docs/oop/scoreboards) - Broader patterns for organizing objectives and player state. --- ## Geometric Particle VFX Engine --- root: .components.layouts.MarkdownLayout title: Geometric Particle VFX Engine nav-title: VFX Particles description: "Generate geometric particle effects with Kore: circles, lines, spheres, spirals and helixes, in world, relative or local coordinates." keywords: minecraft, datapack, kore, helpers, vfx, particles, shape, circle, line, sphere, spiral, helix, geometry, coordinates, relative, local date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/helpers/vfx-particles --- # Geometric Particle VFX Engine The VFX engine generates [particle](https://minecraft.wiki/w/Commands/particle) commands for geometric shapes. Each shape is emitted as a generated function containing pre-computed positions. This is especially useful when you want repeatable visual effects without manually writing dozens of particle commands. You describe the geometry once and then call the generated function wherever you need it. > [!FOOTPRINT] > One function per shape, `vfx_`, holding one `particle` command per point and running only when you call it. No objective, storage, entity, forceloaded chunk, tick or load function. ## Drawing shapes ```kotlin drawCircle("fire_ring", Particles.FLAME, radius = 5.0, points = 16) drawShape("soul_helix") { shape = Shape.HELIX particle = Particles.SOUL_FIRE_FLAME radius = 2.0 points = 40 height = 5.0 turns = 4 } ``` ## Calling the generated function Both helpers return the generated `Function`, which you call with the `function` command. Since positions default to the relative coordinate space, running it under `execute at` centers the shape on the target: ```kotlin val ring = drawCircle("fire_ring", Particles.FLAME, radius = 5.0, points = 16) function("cast_ring") { execute { at(self()) run { function(ring) } } } ``` The geometry is computed at generation time, so the runtime cost is one `function` call plus one `particle` command per point. Keep `points` in check for effects that run every tick. ## Choosing between helpers - Use `drawCircle(...)` when you only need a quick single-purpose helper. - Use `drawShape(...)` when you want one DSL entry point that can switch shape types or expose more parameters. Both approaches generate reusable functions, so you can keep expensive geometry decisions at generation time rather than recomputing them mentally for every particle command. ## Available shapes | Shape | Description | |----------|--------------------------------------------------| | `CIRCLE` | Flat circle on the XZ plane | | `LINE` | Straight line along a direction vector | | `SPHERE` | Fibonacci-distributed points on a sphere surface | | `SPIRAL` | Expanding spiral that rises along Y | | `HELIX` | Fixed-radius helix that rises along Y | ## VfxShape properties | Property | Default | Used by | |----------------|---------------------------|-------------------------------| | `particle` | - | All shapes | | `radius` | `1.0` | CIRCLE, SPHERE, SPIRAL, HELIX | | `points` | `20` | All shapes | | `height` | `3.0` | SPIRAL, HELIX | | `length` | `5.0` | LINE | | `dx/dy/dz` | `1,0,0` | LINE direction | | `turns` | `3` | SPIRAL, HELIX | | `positionType` | `PosNumber.Type.RELATIVE` | All shapes | | `origin` | `vec3(0, 0, 0)` | All shapes | `points` must be strictly positive, otherwise `drawShape` throws. The `dx`/`dy`/`dz` direction is normalized, so only its ratio matters, and a zero direction vector produces no particles. ## Coordinate space Every generated point is written in `positionType`'s coordinate space: - `PosNumber.Type.RELATIVE` (the default) writes `~x ~y ~z`, so the shape is centered on wherever the generated function is executed. - `PosNumber.Type.LOCAL` writes `^x ^y ^z`, so the shape additionally rotates with the executing entity's facing direction. - `PosNumber.Type.WORLD` writes absolute coordinates, so the shape always lands at the same place in the world regardless of where the function is executed. ```kotlin drawCircle("fire_ring", Particles.FLAME, radius = 5.0, points = 4, positionType = PosNumber.Type.LOCAL) ``` ```mcfunction particle minecraft:flame ^5 ^ ^ ... ``` Local coordinates read as `^left ^up ^forward`, so a circle generated on the XZ plane maps to the left/forward plane and tilts with where the entity looks, which suits casting and shield effects that should track the player's aim. ## Offsetting with origin `origin` shifts every point of the shape before the coordinate space is applied. It is the way to lift a shape off the ground or to place a world-space shape somewhere other than `0 0 0`: ```kotlin drawShape("halo") { shape = Shape.CIRCLE particle = Particles.END_ROD radius = 0.5 points = 12 origin = vec3(0, 2.2, 0) } ``` Only the three numeric values of `origin` are used. Any `~` or `^` marker it carries is discarded, since `positionType` alone decides the coordinate space of the output. ## Example: arena intro effect ```kotlin drawShape("arena_intro") { shape = Shape.SPIRAL particle = Particles.HAPPY_VILLAGER radius = 4.0 points = 60 height = 6.0 turns = 5 } ``` This kind of effect works well for spawn platforms, ritual circles, victory moments, or waypoint markers. --- # Oop ## OOP Utilities --- root: .components.layouts.MarkdownLayout title: OOP Utilities nav-title: OOP Utilities description: Overview of object-oriented gameplay utilities in the Kore OOP module - entities, teams, scoreboards, items, events, timers, spawners, and state machines. keywords: minecraft, datapack, kore, oop, entity, player, commands, teams, scoreboard, items, events, cooldown, bossbar, effects, timer, spawner, gamestate date-created: 2026-02-21 date-modified: 2026-08-14 routeOverride: /docs/oop/oop-utilities position: 0 --- # OOP Utilities The OOP module provides high-level, object-oriented wrappers around Minecraft systems such as entities, teams, scoreboards, timers, and gameplay state. Each feature is documented on its own page - see the links below. ## Install this module Add the `oop` artifact when you want object-oriented gameplay abstractions on top of the core Kore DSL. ```kotlin dependencies { implementation("io.github.ayfri.kore:oop:VERSION") } ``` Generated resource names for OOP-specific features are centralized in `OopConstants` so they're easy to find and override. Helper-specific generated names now live in `HelpersConstants` inside the `helpers` module. Utility-style features such as renderers, math helpers, raycasts, areas, state delegates, and VFX now live in the [`helpers` module](/docs/helpers/utilities). ## All Features - **[Boss Bars](/docs/oop/boss-bars)** - Register, configure, and manage boss bars. - **[Cooldowns](/docs/oop/cooldowns)** - Scoreboard-based cooldown system that decrements every [tick](/docs/concepts/time). - **[Dynamic Strings](/docs/oop/dynamic-strings)** - NBT-backed string handles with a Kotlin-like API (substring, split, replace, trim, pad). - **[Entities & Players](/docs/oop/entities-and-players)** - Create entities and players, execute helpers, batch commands, entity commands, and entity effects. - **[Events](/docs/oop/events)** - Advancement-based event system for player and entity actions. - **[Game State Machine](/docs/oop/game-state-machine)** - Scoreboard-based state machine with transition helpers. - **[Items](/docs/oop/items)** - Object-oriented item creation and spawning. - **[Scoreboards](/docs/oop/scoreboards)** - Objective management and per-entity score operations. - **[Spawners](/docs/oop/spawners)** - Reusable entity spawner handles for summoning mobs. - **[Teams](/docs/oop/teams)** - Object-oriented team management with colors, collision rules, and nametag visibility. - **[Timers](/docs/oop/timers)** - Scoreboard-based timers with optional boss bar integration. ## Why use `oop` Choose the `oop` module when your datapack starts to revolve around reusable gameplay concepts rather than isolated commands. - **Entities and players** become named handles instead of repeated selectors. - **Systems such as timers, cooldowns, boss bars, and spawners** generate their own supporting commands and objectives. - **Your Kotlin code reads closer to gameplay intent**, which makes larger datapacks easier to maintain. You do not have to go "all in": the OOP layer is meant to sit on top of Kore, not replace it. ## Typical workflow Most OOP utilities follow a common structure: 1. **Register or declare** the gameplay object once (`team(...)`, `registerCooldown(...)`, `registerSpawner(...)`, etc.). 2. **Let Kore generate** the supporting commands, objectives, or load/tick handlers. 3. **Use the handle in functions** with concise methods such as `player.giveEffect(...)`, `cooldown.start(...)`, or `spawner.spawn()`. That workflow keeps setup centralized while leaving your gameplay functions focused on intent. ## Vanilla Kore vs OOP Kore The OOP module wraps the low-level command DSL into object-oriented abstractions. Here's a complete side-by-side comparison showing how the same mini-game setup looks with each approach. ### Vanilla Kore ```kotlin dataPack("arena") { // --- Scoreboard objectives and teams --- function("setup") { scoreboard.objectives.add("kills", ScoreboardCriteria.DUMMY, textComponent("Kills")) scoreboard.objectives.add("game_state", ScoreboardCriteria.DUMMY) scoreboard.objectives.add("cooldown_dash", ScoreboardCriteria.DUMMY) teams.add("red") teams.modify("red") { color(FormattingColor.RED) } teams.modify("red") { collisionRule(CollisionRule.PUSH_OTHER_TEAMS) } teams.add("blue") teams.modify("blue") { color(FormattingColor.BLUE) } teams.modify("blue") { collisionRule(CollisionRule.PUSH_OTHER_TEAMS) } } // --- Player setup, selector rebuilt in each function --- function("join_red") { val player = allPlayers { limit = 1 sort = Sort.NEAREST } teams.join("red", player) gamemode(Gamemode.SURVIVAL, player) effect(player) { give(Effects.SPEED, duration = 999999, amplifier = 1) } scoreboard.players.set(player, "kills", 0) scoreboard.players.set(player, "cooldown_dash", 0) } function("join_blue") { val player = allPlayers { limit = 1 sort = Sort.NEAREST } teams.join("blue", player) gamemode(Gamemode.SURVIVAL, player) effect(player) { give(Effects.SPEED, duration = 999999, amplifier = 1) } scoreboard.players.set(player, "kills", 0) scoreboard.players.set(player, "cooldown_dash", 0) } // --- State transitions, tracked by hand on a fake player --- function("start_game") { scoreboard.players.set(literal("#game_state"), "game_state", 1) execute { asTarget(allPlayers()) run { title(self(), TitleLocation.TITLE, textComponent("Game Started!", Color.GREEN) { bold = true }) } } } // --- Cooldown, decremented manually every tick --- function("tick_cooldowns") { execute { ifCondition { score(allPlayers(), "cooldown_dash", rangeOrIntStart(1)) } run { scoreboard.players.remove(allPlayers(), "cooldown_dash", 1) } } } // --- Spawning --- function("spawn_guardian") { summon(EntityTypes.IRON_GOLEM, vec3(0, 64, 0)) } } ``` ### OOP Kore ```kotlin dataPack("arena") { // --- Players as reusable objects --- val redPlayer = player("RedPlayer") val bluePlayer = player("BluePlayer") // --- Teams --- val redTeam = team("red") val blueTeam = team("blue") function("setup_teams") { redTeam.ensureExists() redTeam.setColor(FormattingColor.RED) redTeam.setCollisionRule(CollisionRule.PUSH_OTHER_TEAMS) blueTeam.ensureExists() blueTeam.setColor(FormattingColor.BLUE) blueTeam.setCollisionRule(CollisionRule.PUSH_OTHER_TEAMS) } // --- Scoreboard --- val kills = scoreboard("kills") // --- Cooldown, tick function generated for you --- val dashCooldown = registerCooldown("dash", 3.seconds) // --- Game states --- val states = registerGameStates { state("lobby") state("running") state("finished") } // --- Spawner --- val guardian = registerSpawner("guardian", EntityTypes.IRON_GOLEM) { position = vec3(0, 64, 0) } // --- Player setup --- function("join_red") { redPlayer.joinTeam(redTeam) redPlayer.setGamemode(Gamemode.SURVIVAL) redPlayer.giveEffect(Effects.SPEED, duration = 999999, amplifier = 1) kills.getScore(redPlayer).set(0) } function("join_blue") { bluePlayer.joinTeam(blueTeam) bluePlayer.setGamemode(Gamemode.SURVIVAL) bluePlayer.giveEffect(Effects.SPEED, duration = 999999, amplifier = 1) kills.getScore(bluePlayer).set(0) } // --- State transition --- function("start_game") { states.transitionTo("running") redPlayer.showTitle(textComponent("Game Started!", Color.GREEN) { bold = true }) bluePlayer.showTitle(textComponent("Game Started!", Color.GREEN) { bold = true }) } // --- Spawning --- function("spawn_guardian") { guardian.spawn() } } ``` ### Key Differences | Aspect | Vanilla Kore | OOP Kore | |-----------------------|---------------------------------------------------------------------------------|------------------------------------------------------------------------------| | **Entity references** | Manual selectors (`allPlayers { ... }`) repeated everywhere | Named objects (`player("RedPlayer")`) reused across functions | | **Commands** | Low-level calls like `scoreboard.players.set(...)`, `effect(...) { give(...) }` | Method calls on entities: `player.giveEffect(...)`, `player.joinTeam(...)` | | **Game state** | Manual scoreboard objectives and raw `set` calls | `registerGameStates { state("running") }` + `states.transitionTo("running")` | | **Cooldowns** | Manual scoreboard decrement loops | `registerCooldown("dash", 3.seconds)` - tick function auto-generated | | **Spawning** | Raw `summon(EntityTypes.X, pos)` | `registerSpawner(...)` + `spawner.spawn()` | | **Boilerplate** | Selector construction, objective registration, execute blocks | Handled internally by the OOP abstractions | The OOP module doesn't replace vanilla Kore - it builds on top of it. You can freely mix both styles, using OOP utilities where they simplify your code and dropping to vanilla commands when you need fine-grained control. --- ## Boss Bars --- root: .components.layouts.MarkdownLayout title: Boss Bars nav-title: Boss Bars description: Object-oriented boss bar management with the Kore OOP module - register, configure, show, hide, and update boss bars. keywords: minecraft, datapack, kore, oop, bossbar, boss bar, color, style, notched, team, handle, config date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/boss-bars --- # Boss Bars The OOP module wraps [Minecraft boss bars](https://minecraft.wiki/w/Commands/bossbar) into a simple config + handle pattern. This is useful when you want one place to configure a bar and then reuse it across multiple gameplay functions. `BossBarConfig` stores the initial registration settings, while `BossBarHandle` exposes the runtime commands you call from functions. > [!FOOTPRINT] > - **Objectives:** none. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** no. > - **Load and files:** `bossbar__init` creates the boss bar `:` (namespace `minecraft` by default) and applies its settings on every load. ## Registering a boss bar ```kotlin val bar = registerBossBar("my_bar", name) { color = BossBarColor.RED max = 200 style = BossBarStyle.NOTCHED_10 value = 50 } ``` A load function is generated that creates the bar and applies all initial settings. ## Typical workflow ```kotlin function("show_phase_bar") { bar.apply { setPlayers(player) setValue(150) show() } } function("hide_phase_bar") { bar.hide() } ``` {{{ .components.mc.BossBarMockup }}} In practice, boss bars often pair well with timers, boss fights, or round-based activities where the bar is configured once and then updated from several different functions. ## Manipulating a boss bar ```kotlin function("boss_fight") { bar.apply { setValue(100) setColor(BossBarColor.BLUE) setPlayers(player) show() hide() remove() } } ``` You can also target all members of a team: ```kotlin bar.apply { setPlayers(team) setStyle(BossBarStyle.NOTCHED_12) } ``` ## Configuration vs handle methods `BossBarConfig` controls the values that are written when the bar is registered: - `color` - `displayName` - `max` - `style` - `value` - `visible` `BossBarHandle` exposes the runtime methods listed below. ## Function reference | Function | Description | |----------------------|--------------------------------------------------| | `hide` | Hide the bar without deleting its configuration | | `remove` | Remove the boss bar entirely | | `setColor` | Change the boss bar color | | `setMax` | Update the maximum value used to render progress | | `setName` | Change the displayed boss bar name | | `setPlayers(entity)` | Control which players currently see the bar | | `setPlayers(team)` | Show the bar to every member of a team | | `setStyle` | Change the boss bar rendering style | | `setValue` | Update the current boss bar value | | `show` | Make the bar visible to the assigned players | --- ## Cooldowns --- root: .components.layouts.MarkdownLayout title: Cooldowns nav-title: Cooldowns description: Scoreboard-based cooldown system with the Kore OOP module - register, start, check, and reset cooldowns. keywords: minecraft, datapack, kore, oop, cooldown, scoreboard, timer, tick date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/cooldowns --- # Cooldowns Cooldowns use a [scoreboard objective](https://minecraft.wiki/w/Scoreboard) that decrements every [tick](/docs/concepts/time). When the score reaches 0 the cooldown is ready. Durations are expressed as [`TimeNumber`](/docs/concepts/time) values - `2.seconds`, `40.ticks`, etc. They are a good fit for abilities, interactions, item usage limits, or any mechanic that should be reusable for several players without hand-writing the decrement logic every time. > [!FOOTPRINT] > - **Objectives:** one per cooldown, named after it. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** `cooldown__tick` removes 1 from every entity above 0. > - **Load and files:** `cooldown__init` creates the objective. Each `ifReady` block becomes a `cooldown__ready_` function, listed in `#kore:cooldown_ready_handlers`. ## Registering a cooldown ```kotlin val cd = registerCooldown("attack_cd", 2.seconds) ``` This generates: - A **load function** that creates the scoreboard objective. - A **tick function** that decrements the score of every entity with a score ≥ 1, players or not. ## Typical usage pattern ```kotlin function("dash_skill") { with(cd) { ifReady(player) { say("Dash!") } } } function("dash_hit") { cd.start(player) } ``` This split keeps the "can I use it?" check separate from the event or action that actually starts the cooldown. ## Using a cooldown ```kotlin function("combat") { with(cd) { start(player) // sets the score to the duration ifReady(player) { // runs only when score == 0, then restarts say("Attack ready!") } reset(player) // forces score to 0 } } ``` ## Function reference | Function | Description | |-----------|----------------------------------------------------------------| | `start` | Set the entity score to the configured duration | | `ifReady` | Run a block only when the cooldown is ready | | `reset` | Force the cooldown back to `0` | | tick hook | Auto-generated function that decrements active cooldown scores | ## See also - [Timers](/docs/oop/timers) - Use timers when you need scheduled callbacks rather than per-player readiness checks. - [Events](/docs/oop/events) - Start or reset cooldowns from gameplay triggers such as clicks, kills, or item use. - [State Delegates](/docs/helpers/state-delegates) - Reduce scoreboard boilerplate in adjacent stateful systems. --- ## Dynamic Strings --- root: .components.layouts.MarkdownLayout title: Dynamic Strings nav-title: Dynamic Strings description: Manipulate Minecraft datapack strings with a Kotlin-like API - substring, split, join, replace, trim, pad and case conversion backed by NBT storage and macros. keywords: minecraft, datapack, kore, oop, string, nbt, storage, macro, substring, split, join, replace, trim, pad, concat date-created: 2026-04-16 date-modified: 2026-10-07 routeOverride: /docs/oop/dynamic-strings --- # Dynamic Strings `DynamicString` wraps an NBT slot inside a shared storage (`kore_string_lib:memory`, path `heap.`) so datapack strings can be manipulated with an idiomatic Kotlin API: `substring`, `split`, `join`, `replace`, `trim`, `padStart`, `uppercase`, and the rest of the `kotlin.String` vocabulary. Only the helpers you actually call are materialized as `mcfunction` files, so an unused API costs nothing in the generated pack. > [!FOOTPRINT] > - **Objectives:** `kore_string_len`. > - **Storage:** `kore_string_lib:memory`, shared by every pack using Kore strings. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** no. > - **Load and files:** a load function creates the objective, and `lowercase`/`uppercase` add a `kore_string__table_init` load function filling their table. Only the operations you call get their `kore_string_*` functions. ## Quick start ```kotlin fun DataPack.hello() { registerDynamicStrings() val greeting = dynamicString("greeting") val buffer = dynamicString("buffer") function("greet") { greeting.set(" Hello, WORLD ") greeting.trim() greeting.lowercase() greeting += '!' // "hello, world!" buffer.build { // "kore says: hello, world!" +"kore says: " +greeting } tellraw(allPlayers(), buffer.asChatComponents()) } } ``` ## Concepts Three things are worth knowing before reading the rest of this page. **Everything lives in one storage.** Strings sit at `heap.`, lists at `lists.`, macro arguments at `args.`, and scratch values at `tmp.`. Every root is configurable, see [Customising the runtime](#customising-the-runtime). **Results come back as scoreboard scores, not values.** A datapack cannot return a value, so every helper that computes something (`length`, `indexOf`, `contains`, `count`, `equalsTo`, …) writes it into a fake-player score on the `kore_string_len` objective and returns a `DynamicStringResult`. Predicates all use the same convention: `1` means true, `0` means false. `then` and `otherwise` branch on it, `matching` takes any range. ```kotlin (greeting contains "kore").then { tellraw(allPlayers(), textComponent("found")) } greeting.count(",").matching(rangeOrIntStart(3)) { tellraw(allPlayers(), textComponent("too many")) } ``` A `DynamicStringResult` **is** a `ScoreboardEntity`, so it also feeds straight into any helper taking a runtime score, and into the whole scoreboard DSL: ```kotlin bar.repeat(greeting.length()) // as many bars as characters myScore.copyFrom(greeting.indexOf("=")) // persist a result before the next call reuses the holder ``` **Most operations write into a target.** Helpers that produce a new string take an optional `target: DynamicString` parameter defaulting to `this`, so `greeting.uppercase()` mutates in place while `greeting.uppercase(buffer)` leaves the source untouched. Each one also has an expression form (`uppercased()`, `trimmed()`, `repeated(3)`, `paddedStart(4)`, …) writing into a fresh anonymous slot and returning it, so transformations chain without declaring a scratch string. ## Registering the module One call registers the runtime, declares the `kore_string_len` objective and returns the `DynamicStringRuntime` that every helper self-registers against: ```kotlin val stringRuntime = registerDynamicStrings() val greeting = dynamicString("greeting") // on the DataPack val buffer = stringRuntime.dynamicString("buffer") // or on the runtime ``` Call it before any string helper, otherwise `dynamicString` throws. Names are allocated against the runtime, so a duplicate name fails at generation time instead of silently sharing a slot. The `kore_string_` prefix is reserved for the module's own scratch slots and is rejected for user names. ## Creating and mutating a string ```kotlin greeting.set("Hello, world!") // data modify ... set value "Hello, world!" greeting.setFrom(buffer) // alias: greeting.set(buffer) greeting.copyTo(buffer) // buffer := greeting greeting.clear() // data remove ... heap.greeting ``` Display it with `asChatComponents()`, which builds an NBT chat component. `interpret` defaults to `true`, so the content is parsed as a chat component itself; pass `false` to print it as raw text: ```kotlin tellraw(allPlayers(), greeting.asChatComponents(interpret = false)) ``` ## Length `length()` stores the character count into the configured objective and returns it as a `DynamicStringResult`. Pass a custom holder when several lengths must stay alive at once: ```kotlin val len = greeting.length() // DynamicStringResult("#kore_string_len", "kore_string_len") val alive = greeting.length("#my_len") // a second, independent length buffer.padStart(len, '0') // a result is a score, so it drives any runtime-width helper ``` ## Substring, take and drop Indexing slices into a fresh anonymous slot and returns it, with inclusive Kotlin range semantics on the indices: ```kotlin greeting.set("minecraft") val initial = greeting[0] // "m" val word = greeting[0..3] // "mine" val tail = greeting[4..<9] // "craft" ``` The named forms write into an explicit target (defaulting to `this`) instead of allocating one, with `kotlin.String.substring` semantics (`start` inclusive, `end` exclusive, `end = null` meaning "to the end"). All of them compile down to a single `data modify ... set string`: ```kotlin greeting.substring(0, 4) // "mine", in place greeting.substring(0..3) // same slice, range form greeting.substringTo(buffer, 4, 9) // buffer = "craft" greeting.take(4, buffer) // buffer = "mine" greeting.drop(4, buffer) // buffer = "craft" greeting.takeLast(5, buffer) // buffer = "craft" greeting.dropLast(5, buffer) // buffer = "mine" greeting.charAt(1, buffer) // buffer = "i" greeting.setFrom(buffer, start = 2) // greeting = buffer[2..] ``` `takeLast` and `dropLast` measure the length at runtime, so they cost one extra `execute store result` and go through the substring macro. Runtime bounds held in scores use the dynamic variants: ```kotlin // startEntity holds 4 and endEntity holds 9 at runtime greeting.substringDynamic(startEntity, endEntity, target = buffer) // buffer = "craft" greeting.charAt(indexEntity, buffer) // buffer = "c" ``` ## Concatenation ```kotlin greeting.set("Hello") greeting += ", world" // "Hello, world" greeting += '!' // "Hello, world!" greeting += other // append another dynamic string greeting += kills // append a score, rendered as its decimal value val fullName = first + " " + last // expression form, into a fresh anonymous slot ``` `build` rewrites a string from any number of operands: literals, characters, other dynamic strings and scores, each pushed with a `+`. Consecutive literals are folded at generation time into a single `set value`, the first operand seeds the target instead of being appended to it, and writing into one of the operands is safe because it is read before it is overwritten. ```kotlin summary.build { +"Player " +playerName // another DynamicString +": " +kills // a ScoreboardEntity, rendered as its decimal value +'!' } ``` An empty block clears the target, and `plus` leaves both its operands untouched. The named forms cover prepending and partial operands, which have no operator: ```kotlin greeting.append(", world") // same as += greeting.prepend("> ") // "> Hello, world!" greeting.appendFrom(other, start = 1) // append only other[1..] greeting.prependFrom(other, 0, 3) // prepend only other[0..3) ``` ## Comparisons Every comparison writes a `0` / `1` flag into a holder on the length objective and returns a `DynamicStringResult`. `eq`, `startsWith` and `endsWith` are infix, so a comparison reads like a condition: ```kotlin greeting.set("minecraft") greeting eq "minecraft" // 1 -> #kore_string_equals greeting eq buffer // dynamic operand greeting.isEmpty() // 0 -> #kore_string_is_empty greeting startsWith "mine" // 1 -> #kore_string_starts greeting endsWith buffer // -> #kore_string_ends, runtime suffix length (greeting startsWith "mine").then { tellraw(allPlayers(), textComponent("a mine")) } (greeting eq buffer).otherwise { buffer.setFrom(greeting) } ``` `equalsTo` is the non-infix form of `eq`, and takes an optional `resultHolder` when several equality results must stay alive at the same time. `startsWith` and `endsWith` reject an empty literal, which would always match. ## Searching ```kotlin greeting.set("minecraft") greeting.indexOf("craft") // 4 -> #kore_string_find, -1 when absent greeting.indexOf("kore") // -1 greeting.indexOf(buffer) // dynamic needle greeting contains "mine" // 1 -> #kore_string_contains, 0 / 1 greeting.count("a") // 1 -> #kore_string_count ``` All four share one recursive find controller that walks the string one offset at a time, so their cost grows with the length of the haystack. An empty needle is rejected. ## Replacing ```kotlin greeting.set("minecraft") greeting -= "mine" // "craft", strips every occurrence greeting.set("minecraft") greeting.replaceRange(0, 4, "war") // "warcraft", literal or dynamic replacement greeting.replace("a", "4") // "w4rcr4ft", every occurrence greeting.replaceFirst("r", "R") // "w4Rcr4ft", first occurrence only ``` Both the needle and the replacement accept a `DynamicString`, so a find-and-replace can be driven entirely by values computed in game. An empty runtime needle replaces nothing instead of looping forever: ```kotlin greeting.replace(needle, replacement) greeting.replaceFirst("-", replacement) ``` `replace` resumes the search past the text it just inserted, so a growing replacement such as `replace("a", "aa")` terminates instead of matching its own output forever. ## Case conversion (ASCII) ```kotlin greeting.set("KoRe 42") greeting.uppercase() // "KORE 42" greeting.lowercase(target = buffer) // buffer = "kore 42" buffer.capitalize() // "Kore 42", first character only buffer.decapitalize() // "kore 42" ``` A translation table is written to `tables.` on world load, and each character is mapped through a single `set from` on that table instead of a 26-branch `if` chain. `capitalize` and `decapitalize` map their single character directly through the table, skipping the per-character loop entirely. Characters with no table entry, including every non-ASCII one, come out unchanged. Double quotes and backslashes skip the lookup because they cannot be used as an NBT path key; neither has a case, so the result is the same either way. ## Trim, pad and repeat ```kotlin greeting.set(" hi ") greeting.trimStart() // "hi " greeting.trimEnd() // "hi" greeting.trim() // strips both ends at once greeting.set("42") greeting.padStart(5, padChar = '0') // "00042", no-op when already 5 characters or longer greeting.padEnd(7, padChar = '.') // "00042..", pads on the right instead greeting.set("ab") greeting *= 3 // "ababab", 0 clears, negative throws greeting.repeat(3) // named form, takes an explicit target ``` The width and the count also accept a score, so a progress bar or an aligned column can be sized in game: ```kotlin // width holds 5 and count holds 3 at runtime greeting.padStart(width, '0') // "00042" bar.repeat(count) // "|||" when bar holds "|" ``` The whitespace set is `DynamicStringConfig.trimWhitespace`, see below. ## Reverse ```kotlin greeting.reverse(target = buffer) ``` A tail-recursive macro walks the string from its last character to its first, appending each one to an accumulator. The recursion depth equals the string length, so it is bounded by the [`max_command_sequence_length`](https://minecraft.wiki/w/Game_rule#Miscellaneous) game rule (65 536 by default). ## Splitting and lists ```kotlin val parts = koreStringList("parts") greeting.set("a,b,,c") greeting.split(",", parts) // ["a", "b", "", "c"], empty tokens are preserved greeting.split(separator, parts) // the delimiter can itself be a DynamicString greeting.set("kore") greeting.toList(parts) // ["k", "o", "r", "e"], one element per character ``` Both clear the target list first and reuse the find / substring primitives. A runtime delimiter that turns out to be empty leaves the list empty rather than looping forever. `KoreStringList` wraps an NBT list at `lists.` and offers the usual list primitives: ```kotlin val tokens = koreStringList("tokens") val current = dynamicString("current") function("tokenize") { tokens.clear() tokens += "alpha" // append a literal tokens += current // append a dynamic string val second = tokens[1] // read into a fresh anonymous slot tokens.prepend("beta") tokens.insertAt(1, "middle") tokens.removeAt(2) tokens.setAt(0, current) tokens.elementAt(0, current) // read into an explicit target tokens.size() // -> DynamicStringResult on #kore_string_len tokens.forEach(current) { tellraw(allPlayers(), current.asChatComponents()) } } ``` `forEach` generates a dedicated macro loop per call site and binds the current element into the `DynamicString` you pass as the cursor. Inside the block the `Function` receiver is active, so any Kore DSL call is valid. ## Joining a list back into a string `join` is the inverse of `split`: it walks the list once and appends every element into a target string, inserting the separator between them and wrapping the result with an optional `prefix` / `postfix`, exactly like `kotlin.collections.joinToString`. ```kotlin val tokens = koreStringList("tokens") val summary = dynamicString("summary") function("show_tokens") { tokens.clear() tokens.append("alpha") tokens.append("beta") tokens.join(", ", summary, prefix = "Tokens: ") // "Tokens: alpha, beta" tellraw(allPlayers(), summary.asChatComponents(interpret = false)) } ``` The target is overwritten, an empty list leaves it as `prefix + postfix`, and the separator only lands between elements, never at the ends. Paired with `split`, it round-trips a list through a single string, which is how a list fits in an item name, a sign line or one storage field: ```kotlin tokens.join(",", csv) // csv := "alpha,beta" csv.split(",", tokens) // back to a list ``` ## Numbers and scores Scores are the only values a datapack can compute, so `DynamicString` converts in both directions. `setFrom(score)` renders a score as text, `toScore` parses the text back into a score: ```kotlin val kills = ScoreboardEntity("stats", fakePlayer("#kills")) label.setFrom(kills) // "7" label.padStart(3, '0') // "007" label.prepend("Kills: ") // "Kills: 007" label.appendFrom(kills) // append a score to an existing string label.prependFrom(kills) // prepend one input.toScore(kills) // "42" -> the score 42 ``` This is what makes a formatted clock, a leaderboard line or a numeric config value work without a `score` chat component, and the result stays a string that `padStart`, `split` or `join` can keep working on. ## Parsing and serialization `parseTo` evaluates the string content as SNBT and writes the resulting value anywhere, which covers numbers (`42`, `3.14`, `1L`), booleans, quoted strings and full NBT literals such as `{a: 1, b: [1,2,3]}`. `setFromNbt` goes the other way, turning an arbitrary NBT value into its textual form: ```kotlin greeting.parseTo(storage, "some.path") buffer.setFromNbt(storage, "some.path") ``` ## Customising the runtime Every storage slot, scoreboard objective and NBT root is configurable through `DynamicStringConfig`. This matters when several modules share a datapack, or when you want the module to live inside an existing storage convention: ```kotlin val runtime = registerDynamicStrings( DynamicStringConfig( argsRoot = "kore.args", heapRoot = "kore.heap", lengthHolder = "#my_pack.len", lengthObjective = "my_pack_len", listsRoot = "kore.lists", storageName = "state", storageNamespace = "my_pack", tablesRoot = "kore.tables", tmpRoot = "kore.tmp", ), ) ``` `trimWhitespace` belongs to the same config. Its entries land verbatim inside the generated commands, so they use SNBT escapes, not Kotlin ones: ```kotlin registerDynamicStrings( DynamicStringConfig(trimWhitespace = listOf(" ", "\\t", "\\n", "\\r", "\\u00A0")), ) ``` Every field defaults to the matching `OopConstants.string*` value, so changing one of those moves the default for every datapack in the project instead of for a single one. ## End-to-end example: a typewriter dialogue box Revealing a line of dialogue one character at a time is a staple of adventure maps, and it is the exact thing vanilla cannot do: a text component is fixed at write time, so the usual workarounds are one hardcoded `tellraw` per frame (`"T"`, `"Th"`, `"The"`, …) or shipping a third-party string library such as [String-Parser](https://github.com/5uso/String-Parser) and driving it by hand. With a `DynamicString` the effect is a growing substring, and the dialogue text stays a plain Kotlin string: ```kotlin import io.github.ayfri.kore.DataPack import io.github.ayfri.kore.arguments.enums.Relation import io.github.ayfri.kore.arguments.numbers.ticks import io.github.ayfri.kore.arguments.types.literals.allPlayers import io.github.ayfri.kore.arguments.types.literals.literal import io.github.ayfri.kore.commands.TitleLocation import io.github.ayfri.kore.commands.execute.execute import io.github.ayfri.kore.commands.playSound import io.github.ayfri.kore.commands.schedule import io.github.ayfri.kore.commands.title import io.github.ayfri.kore.entities.fakePlayer import io.github.ayfri.kore.functions.function import io.github.ayfri.kore.generated.SoundEvents import io.github.ayfri.kore.scoreboard.ScoreboardEntity import io.github.ayfri.kore.scoreboard.add import io.github.ayfri.kore.scoreboard.set import io.github.ayfri.kore.strings.* import io.github.ayfri.kore.commands.function as callFunction fun DataPack.dialogueTypewriter() { registerDynamicStrings() val line = dynamicString("dialogue_line") val shown = dynamicString("dialogue_shown") val start = ScoreboardEntity("dialogue", fakePlayer("#start")) val cursor = ScoreboardEntity("dialogue", fakePlayer("#cursor")) function("dialogue_tick") { cursor.add(1) line.substringDynamic(start, cursor, target = shown) // shown := line[0..cursor) title(allPlayers(), TitleLocation.ACTIONBAR, shown.asChatComponents(interpret = false)) playSound(SoundEvents.Block.NoteBlock.HAT, target = allPlayers()) val length = line.length() execute { ifCondition { score(cursor.entity.asScoreHolder(), cursor.name, literal(length.holder), length.objective, Relation.LESS_THAN) } run { schedule(1.ticks, "${datapack.name}:dialogue_tick") } } } function("dialogue_start") { line.set("The keeper looks up. You made it.") start.set(0) cursor.set(0) callFunction(datapack.name, "dialogue_tick") } } ``` The whole effect is a handful of commands per tick, one of them the substring macro, and the text is never duplicated: changing the line, translating it, or feeding it from a sign, a book or a `KoreStringList` of lines only touches `line`. `substringDynamic` reads its bounds from scores, so the same function drives a slow reveal, an instant skip (set `cursor` to the length) or a scrolling window (advance `start` too). ## Another example: parsing a config line A pack setting is the other everyday fit: admins edit one string with a single `/data modify`, the pack turns it into typed values. The alternative is one `execute if data` per accepted value, hardcoded, which caps the setting to the values you thought of when writing the pack. ```kotlin import io.github.ayfri.kore.DataPack import io.github.ayfri.kore.arguments.types.literals.allPlayers import io.github.ayfri.kore.arguments.types.resources.storage import io.github.ayfri.kore.commands.data import io.github.ayfri.kore.commands.tellraw import io.github.ayfri.kore.functions.function import io.github.ayfri.kore.strings.* fun DataPack.packConfig() { registerDynamicStrings() val settings = storage("settings", "my_pack") // /data modify storage my_pack:settings config set value "Spawn_Radius = 64 ; hud = on" val raw = dynamicString("config_raw") val entry = dynamicString("config_entry") val key = dynamicString("config_key") val value = dynamicString("config_value") val summary = dynamicString("config_summary") val entries = koreStringList("config_entries") val pair = koreStringList("config_pair") function("load_config") { raw.setFromNbt(settings, "config") raw.trim() raw.lowercase() raw.split(";", entries) entries.forEach(entry) { entry.trim() entry.split("=", pair) pair.elementAt(0, key) pair.elementAt(1, value) value.trim() value.parseTo(settings, "pending") // "64" -> the int 64, usable by execute store / if data (key eq "spawn_radius").then { data(settings) { modify("spawn_radius") { set(settings, "pending") } } } } entries.join(", ", summary, prefix = "Config loaded: ") tellraw(allPlayers(), summary.asChatComponents(interpret = false)) } } ``` The `lowercase` pass makes `Spawn_Radius` and `spawn_radius` the same key, and `trim` absorbs the spaces around each separator. An entry without `=` leaves index `1` of the pair out of range, so check `pair.size()` first when the input is player-written. Every helper is generated once per datapack, so calling the same pipeline from several places adds no extra function. ## Operator reference Every operator is a thin alias over the named helper, so it carries the exact same cost. | Operator | Equivalent | Notes | |-----------------------|------------------------------|--------------------------------------------------------| | `s += "x"` / `+= 'x'` | `s.append("x")` | also accepts a `DynamicString` or a `ScoreboardEntity` | | `s -= "x"` | `s.replace("x", "")` | strips every occurrence, literal or dynamic needle | | `s *= 3` | `s.repeat(3)` | also accepts a runtime count as a `ScoreboardEntity` | | `a + b` | copy of `a` then `append(b)` | expression form, writes into a fresh anonymous slot | | `s[2]` / `s[1..3]` | `charAt` / `substringTo` | inclusive range, writes into a fresh anonymous slot | | `list += "x"` | `list.append("x")` | also accepts a `DynamicString` | | `list[1]` | `list.elementAt(1, target)` | writes into a fresh anonymous slot | | `a eq b` | `a.equalsTo(b)` | infix, like `startsWith`, `endsWith` and `contains` | Anonymous slots come from `tempString()` (or `tempDynamicString()` on a `DataPack`), which is also the helper to call when a pipeline needs a scratch string of its own. They live in the same heap as named strings, under the reserved `kore_string_temp_` prefix, and are never reused across call sites. ## Cost and limits Helpers fall into three tiers, worth keeping in mind when a string is long or a helper runs every tick: | Tier | Helpers | Cost | |----------------|---------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------| | Constant | `set`, `setFrom`, `clear`, `substring`, `substringTo`, `take`, `drop`, `charAt(Int)`, `append`, `prepend`, `build` | 1 to 3 commands, no macro | | One macro call | `substringDynamic`, `takeLast`, `dropLast`, `capitalize`, `decapitalize`, `parseTo`, `setFromNbt`, `setFrom(score)`, `toScore` | a handful of commands plus one function call | | Recursive | `reverse`, `indexOf`, `contains`, `count`, `replace`, `split`, `join`, `toList`, `uppercase`, `lowercase`, `trim`, `repeat`, `padStart`, `padEnd` | one function call per character, offset or element | Recursive helpers are bounded by the `max_command_sequence_length` game rule (65 536 by default), which is far above any realistic string length but is the hard ceiling. The module handles ASCII text. Case conversion only maps `a-z` / `A-Z`, and indices are NBT string indices, so characters outside the Basic Multilingual Plane do not behave like single characters. ## See also - [Scoreboards](/docs/oop/scoreboards) – consume the length / find / predicate score holders returned by these helpers. - [Macros](/docs/commands/macros) – the underlying mechanism used by every dynamic helper. - [Data command](/docs/commands/commands#data-command) – NBT read/write via Kore's `data` command helpers. --- ## Entities & Players --- root: .components.layouts.MarkdownLayout title: Entities & Players nav-title: Entities & Players description: Create and manage entities and players with the Kore OOP module - selectors, execute helpers, batch commands, entity commands, and entity effects. keywords: minecraft, datapack, kore, oop, entity, player, commands, execute, batch, effects, teleport, kill, damage date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/entities-and-players --- # Entities & Players The OOP module models Minecraft entities and players as Kotlin objects with selectors and context-aware extension functions. > [!FOOTPRINT] > Nothing of its own: a handle is a selector, and only the commands you call are written. Score operators against a plain `Int` add the `kore_constants` objective, see [Scoreboards](/docs/oop/scoreboards). ## Creating entities ```kotlin val player = player("Steve") { gamemode = Gamemode.SURVIVAL team = "red" } val arenaMobs = entity("ArenaMob", limitToOne = false) { tag = "arena" } val zombie = entity { type = EntityTypes.ZOMBIE } ``` `player()` creates a `Player` instance (subclass of `Entity`) with `type = minecraft:player`, `limit = 1`, and the given name. `entity()` creates a generic `Entity` with custom selector arguments, and can also start from a named entity selector when you already know the exact entity name to target. Use `player(...)` when you want a selector already scoped to players, and `entity { ... }` when you need a reusable selector for mobs, armor stands, projectiles, or a more generic execute target. Use `entity("Name", ...)` when you want the same convenience as `player("Name")` without forcing the selector to `minecraft:player`. ### Creating entities from an EntityType You can also construct an `Entity` handle directly from an `EntityTypes` value using `toEntity()` or the typed overload of `entity()`: ```kotlin // extension on EntityTypeArgument, the selector type is set for you val creeper = EntityTypes.CREEPER.toEntity() val skeletons = EntityTypes.SKELETON.toEntity(limitToOne = false) { team = "arena" } // same result, written like the other entity(...) overloads val spider = entity(EntityTypes.SPIDER) val endermen = entity(EntityTypes.ENDERMAN, limitToOne = false) { tag = "target" } ``` Both forms set `selector.type` automatically so you never need to write `type = EntityTypes.X` by hand. Use `toEntity()` when calling from an `EntityTypeArgument` receiver; use `entity(type, ...)` when you want a form that reads like the other `entity(...)` overloads. ### Handles for spawned displays and mannequins Four `Entity` subclasses target one specific spawned entity by UUID rather than by selector filters: `BlockDisplayEntity`, `ItemDisplayEntity`, `TextDisplayEntity`, and `MannequinEntity`. Each pins `selector.type` and a `UUID` NBT filter, so it always resolves to exactly that entity. You do not construct them directly in normal use - the [`helpers`](/docs/helpers/utilities) module returns them when it spawns something. `DisplayEntityInterpolable.toEntity()` picks the matching display subclass, and `Mannequin.summon(position)` returns a `MannequinEntity`: ```kotlin function("spawn_display") { val display = blockDisplay { blockState(Blocks.DIAMOND_BLOCK) }.interpolable(vec3(0, 64, 0)) display.summon() display.toEntity().addTag("arena_decor") val statue = mannequin { pose = MannequinPose.STANDING }.summon(vec3(0, 64, 0)) statue.swing(SwingHand.MAINHAND) } ``` Both spawn calls embed a UUID in the summon NBT, which is what the returned handle filters on - so the handle resolves to that one instance even if several are spawned. The UUID comes from `DataPack.entityUUID()`, a hash of the pack name, the entity and its position, so it stays the same across builds and differs between two identical entities. Once you hold one it behaves like any other `Entity`: every extension on this page applies. ```mcfunction kill @e[limit=1,nbt={UUID:[I;...]},type=minecraft:block_display] ``` See [Display Entities](/docs/helpers/display-entities) and [Mannequins](/docs/helpers/mannequins) for building and spawning them. ## Reading and reshaping a handle An `Entity` is a thin wrapper around `SelectorArguments`, exposed through these members: | Member | Description | |---------------------------|--------------------------------------------------------------------| | `selector` | The underlying `SelectorArguments`, mutable | | `type` | The constrained entity type, or `null` | | `isPlayer` | Whether the handle currently resolves to `minecraft:player` | | `team` | Read/write the team filter on the selector | | `limitToOne` | Whether `asSelector()` defaults to a single-entity selector | | `asSelector(limitToOne)` | Build the `@e[...]` selector, optionally overriding the limit and refining the arguments | `asSelector()` is the escape hatch: every OOP command ultimately calls it, and you can call it yourself to drop back into the core command DSL at any point. ```kotlin function("inspect") { val mobs = entity(EntityTypes.ZOMBIE, limitToOne = false) { tag = "arena" } kill(mobs.asSelector(limitToOne = false) { tag = "marked" }) } ``` `toEntity()` and `toEntityOrNull()` narrow a generic `Entity` to a subtype such as `Player` - the first throws when the conversion is impossible, the second returns `null`. ## Targeting `@s` `Entity` always renders as `@e[...]`, because `asSelector()` builds an `allEntities` selector from the stored `SelectorArguments`. There is no `Entity` handle that renders as `@s`, and `self()` returns a `SelectorArgument`, not an `Entity`, so it cannot be passed to `Entity`-only helpers such as `getScoreEntity`, `batch`, or `executeAs`. When the executing entity is already `@s` - inside an `execute as` block, an [event](/docs/oop/events) handler, or a `batch` body - use the core command DSL with `self()` instead of trying to wrap it in an `Entity`: ```kotlin function("charge_up") { val lastCharge = scoreboard.objective(self(), "last_crystal_charge") lastCharge += 10 } ``` `scoreboard.objective(selector, objective)` returns a `PlayerObjective` supporting the same operations as `ScoreboardEntity` (`set`, `add`, `remove`, `reset`, `+=`, `-=`, `min`, `max`, `operation`), so nothing is lost by dropping the OOP wrapper here. See [Scoreboards → Raw selectors and `@s`](/docs/oop/scoreboards#raw-selectors-and-s). The same applies to every other command: pass `self()` directly to `kill(...)`, `tellraw(...)`, `give(...)` and friends rather than looking for an `Entity` equivalent. ## Execute helpers Entity-scoped execute shortcuts emit `/execute as`, `/execute at`, or both: ```kotlin function("teleport_self") { player.executeAs { run { it.teleportTo(it) } } player.executeAt { run { it.giveItem(Items.DIAMOND) } } player.executeAsAt { run { it.sendMessage("Hello from my location!") } } } ``` These helpers are especially valuable when you would otherwise repeat the same `execute as`, `execute at`, or `execute as ... at ...` boilerplate around several commands. ## Batch `batch()` creates a named sub-function that groups multiple commands under a single entity context: ```kotlin function("setup") { player.batch("init_player") { give(self(), Items.DIAMOND) effect(self()) { give(Effects.SPEED, duration = 200) } tellraw(self(), "Welcome!") } } ``` The block receives a plain `Function` run `as` the entity, so it targets that entity with `self()`: the `Entity` extensions like `giveItem` need an entity receiver and aren't in scope there. `batch()` is a good fit for onboarding flows, class kits, respawn setup, or any repeated multi-command routine that should stay grouped under one entity context. ## Entity Commands Extension functions on `Entity` for common [Minecraft commands](https://minecraft.wiki/w/Commands): ```kotlin function("commands_demo") { player.kill() player.damage(5f) player.addTag("vip") player.removeTag("vip") player.giveXp(10.levels) player.setXp(0.points) player.setGamemode(Gamemode.CREATIVE) player.sendMessage("Hello!") player.showTitle(textComponent("Title"), textComponent("Subtitle")) player.showActionBar(textComponent("Action bar text")) player.playSound(SoundEvents.Entity.ExperienceOrb.PICKUP) player.mount(zombie) player.dismount() player.clearItems() player.giveItem(Items.DIAMOND) player.replaceItem(WEAPON.MAINHAND, itemStack(Items.NETHERITE_SWORD)) } ``` {{{ .components.mc.TitleMockup }}} | Function | Description | |-----------------|-------------------------------------------| | `addTag` | Add a scoreboard tag | | `clearItems` | Clear inventory (optionally filtered) | | `damage` | Deal damage with optional damage type | | `dismount` | Dismount from current vehicle | | `giveItem` | Give an item stack | | `giveXp` | Add experience (levels or points) | | `kill` | Kill the entity | | `mount` | Mount another entity | | `playSound` | Play a sound at the entity | | `removeTag` | Remove a scoreboard tag | | `replaceItem` | Replace an item in a specific slot | | `sendMessage` | Send a tellraw message | | `setGamemode` | Change the player's gamemode | | `setXp` | Set experience to an exact value | | `showActionBar` | Display text on the action bar | | `showTitle` | Display a title and optional subtitle | | `swing` | Swing the left or right hand | | `teleportTo` | Teleport to coordinates or another entity | ## Scores, teams, and counts These extensions bridge an `Entity` handle to the other OOP systems: ```kotlin function("bridge_demo") { val kills = player.getScoreEntity("kills") // -> ScoreboardEntity player.setScore("kills", 0) // one-shot write, no handle player.joinTeam("red") // by name, or joinTeam(redTeam) player.leaveAnyTeam() // counts how many entities match right now, then writes that number to a score or NBT path val mobs = entity(EntityTypes.ZOMBIE, limitToOne = false) mobs.storeCountIn(kills) mobs.storeCountIn(player, "kore.mob_count") mobs.storeCountIn(storage("stats", "kore"), "mob_count") } ``` | Function | Description | |------------------|--------------------------------------------------------------------------------------| | `getScoreEntity` | Get a [`ScoreboardEntity`](/docs/oop/scoreboards) for an objective | | `joinTeam` | Join a [team](/docs/oop/teams) by name or `Team` handle | | `leaveAnyTeam` | Leave the current team | | `setScore` | Set a score without building a handle | | `storeCountIn` | Store how many entities match this selector into a score, entity NBT, or storage NBT | ## Entity Effects Extension functions on `Entity` for giving, clearing, and managing [mob effects](https://minecraft.wiki/w/Effect): ```kotlin function("buff_player") { player.giveEffect(Effects.SPEED, duration = 200, amplifier = 1) player.giveInfiniteEffect(Effects.NIGHT_VISION, hideParticles = true) player.clearEffect(Effects.SPEED) player.clearAllEffects() } ``` | Function | Description | |----------------------|---------------------------------------| | `giveEffect` | Give a timed effect with optional amp | | `giveInfiniteEffect` | Give an infinite-duration effect | | `clearEffect` | Remove a specific effect | | `clearAllEffects` | Remove all effects | | `effects { ... }` | Builder block for multiple operations | ## Putting it together ```kotlin dataPack("arena") { val player = player("Steve") val mobs = entity(EntityTypes.ZOMBIE, limitToOne = false) { tag = "arena" } function("round_start") { player.joinTeam("red") player.batch("round_start_player") { give(self(), Items.DIAMOND) effect(self()) { give(Effects.SPEED, duration = 200) } title(self(), TitleLocation.ACTIONBAR, textComponent("Fight!")) } mobs.storeCountIn(player.getScoreEntity("mobs_left")) } } ``` One reusable handle per gameplay concept, declared once at datapack level, then used across as many functions as needed - that is the core value of the OOP entity API. ## See also - [Items](/docs/oop/items) - Reuse item stacks with `giveItem`, `replaceItem`, or summon-based reward flows. - [Events](/docs/oop/events) - Attach gameplay reactions directly to the entity and player handles you define here. - [Spawners](/docs/oop/spawners) - Pair selectors and entity utilities with reusable spawning entry points. --- ## Events --- root: .components.layouts.MarkdownLayout title: Events nav-title: Events description: Advancement-based event system for player and entity actions with the Kore OOP module. keywords: minecraft, datapack, kore, oop, events, advancement, player, entity, death, click, consume, kill, recipe, dimension, riding, tame, bed, target, fishing, crossbow, totem, tick, bucket, potion date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/events --- # Events Events use [advancements](https://minecraft.wiki/w/Advancement/JSON_format) to detect player/entity actions and dispatch them to [function tags](https://minecraft.wiki/w/Tag#Function_tags). Multiple handlers can be registered for the same event - they all fire together. This makes events a convenient bridge between vanilla triggers and OOP-style gameplay code: you register interest once, then let the generated dispatchers call your handlers when Minecraft reports the action. > [!FOOTPRINT] > - **Objectives:** none. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** no for player events, each one is an advancement. `onDeath` adds a `kore_oop_death__tracker` function per selector, which tags the new matching entities, and `kore_oop_death_dispatcher`, which checks every item entity. > - **Load and files:** per event, the advancement `kore_oop/` and a `dispatch_` function that revokes it and calls the `#:` tag, plus one function per handler. `onDeath` adds the entity tag `kore_oop_death_` per selector and the loot table `kore_oop/death_trigger_`. ## Registering events Event helpers only need a `DataPack` in scope, so you can register them straight in the `datapack { }` block, without wrapping them in a `function { }`: ```kotlin datapack("my_pack") { val player = player("Steve") player.onKill { say("Kill!") } } ``` Registering them inside a `function { }` still works (the surrounding datapack is in scope there too), which is handy when you want to mix event registration with other commands. ### Handler receives the entity Each handler is invoked with the entity/player handle it was registered on, so you can keep chaining the OOP API without re-declaring the handle: ```kotlin datapack("my_pack") { val player = player("Steve") player.onKill { self -> self.setScore("kills", 1) say("Kill counted!") } } ``` > Minecraft runs reward functions **as** the player, so the handle you get back is always the player/entity the event > was registered on. The other entity involved (the mob you killed, the attacker, etc.) is not exposed by the game and > cannot be passed in. ## Player events ```kotlin val player = player("Steve") player.onBlockUse { say("Interacted with a block!") } player.onConsumeItem(Items.GOLDEN_APPLE) { say("Golden apple!") } player.onFishingRodHooked { say("Got a bite!") } player.onRecipeCrafted(Recipes.CRAFTING_TABLE) { say("Crafted a recipe!") } player.onRightClick(Items.STICK) { say("Right click with stick!") } player.onUsedTotem { say("Cheated death!") } ``` Available player events (alphabetical): | Function | Trigger | |------------------------|---------------------------------------| | `onBlockUse` | Right-click any block | | `onBredAnimals` | Breed two animals | | `onBrewedPotion` | Take a potion out of a brewing stand | | `onChangeDimension` | Change dimension | | `onConsumeItem` | Consume any item (food, potion, etc.) | | `onConsumeItem(item)` | Consume a specific item | | `onEffectsChanged` | Effects on the player change | | `onEnchantItem` | Enchant an item | | `onEntityHurtPlayer` | A player is hurt by an entity | | `onFallFromHeight` | Fall from a height | | `onFilledBucket` | Fill a bucket | | `onFishingRodHooked` | Hook something with a fishing rod | | `onHurtEntity` | Deal damage to an entity | | `onInteractWithEntity` | Right-click an entity | | `onInventoryChange` | Inventory contents change | | `onItemUsedOnBlock` | Use an item on a block | | `onKill` | Kill an entity | | `onKilledByArrow` | Get killed by an arrow | | `onPlaceBlock` | Place a block | | `onRecipeCrafted` | Craft a recipe | | `onRightClick(item)` | Right-click while holding an item | | `onShotCrossbow` | Shoot a crossbow | | `onSleptInBed` | Sleep in a bed | | `onStartRiding` | Start riding an entity | | `onTameAnimal` | Tame an animal | | `onTargetHit` | Hit a target block | | `onTick` | Every tick (per player) | | `onUsedEnderEye` | Use an eye of ender | | `onUsedTotem` | Trigger a totem of undying | ## Typical workflow 1. Declare the entity or player handle that should receive the event helpers. 2. Register one or more event handlers, either directly in the datapack or inside a function. 3. Keep the handler bodies focused on gameplay reactions such as score updates, messaging, or spawning. This pattern works well for mini-games where multiple systems need to react to the same player action. ## Entity events ```kotlin val zombie = entity(EntityTypes.ZOMBIE) { tag = "my_tag" } zombie.onDeath { self -> say("A ${self.type?.name} died!") } ``` The death event uses a loot-table trigger. Every tick, the entities matching the selector for the first time get a `kore_oop_death_` tag and a `DeathLootTable` that drops their usual loot plus a hidden structure void carrying their tags. A tick dispatcher finds that item, runs the handlers of every selector whose tag it carries, then removes it. Two selectors on the same type stay apart: here a zombie without `my_tag` runs no handler. The trigger relies on the death loot table, which only mobs have, so: - `onDeath` throws on a player or an entity without a type. A player's death is a `deathCount` objective instead. - Nothing fires while the `doMobLoot` game rule is off, and the tracker replaces a `DeathLootTable` set by another pack. - The handlers run as the dropped item, at the death position. ## See also - [World Events](/docs/oop/world-events) - The world-side counterpart: tick, weather, day/night, and interval triggers. - [Cooldowns](/docs/oop/cooldowns) - Gate event-driven abilities or interactions so players cannot spam them. - [Entities & Players](/docs/oop/entities-and-players) - Build the selectors and entity handles that receive these event helpers. - [Items](/docs/oop/items) - React to item usage or rewards with reusable item stacks. --- ## Game State Machine --- root: .components.layouts.MarkdownLayout title: Game State Machine nav-title: Game State Machine description: Scoreboard-based game state machine with the Kore OOP module - register states, transition between them, and react to state changes. keywords: minecraft, datapack, kore, oop, game state, state machine, scoreboard, transition, lobby date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/game-state-machine --- # Game State Machine The game state system uses a [scoreboard](https://minecraft.wiki/w/Scoreboard) to track the current state and dispatches handlers when the state matches. It is a good fit for lobbies, round systems, multi-phase boss fights, tutorials, or any flow that repeatedly switches between a known set of named states. > [!FOOTPRINT] > - **Objectives:** `kore_state`, holding the state on `#game_state`. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** no. > - **Load and files:** `kore_state_init` creates the objective and sets the first state back on every load and `/reload`. `whenState` blocks become functions listed in `#kore:state_when_handlers`. ## Registering states ```kotlin val states = registerGameStates { state("lobby") state("playing") state("ended") } ``` This generates a **load function** that creates the `kore_state` objective and sets the initial state. ## Typical lifecycle ```kotlin function("prepare_game") { states.transitionTo("lobby") } function("start_game") { states.transitionTo("playing") } function("finish_game") { states.transitionTo("ended") } ``` Keeping transitions explicit like this makes it easier to reason about which setup code belongs to each phase. ## Transitioning and reacting ```kotlin function("game_loop") { states.transitionTo("playing") states.whenState("playing") { say("The game is in progress!") } states.whenState("ended") { say("Game over!") } } ``` ## Game state actions Helper functions integrate states with other OOP systems: ```kotlin // only switches state if the cooldown has expired, so the transition cannot be spammed states.transitionWithCooldown("playing", cooldown, player) // runs the spawner every time the game enters this state states.whenStateSpawn("playing", zombieSpawner, count = 3) // starts the timer on entering this state states.whenStateStartTimer("playing", timer, player) ``` ## Best practices - Keep the state list small and meaningful (`lobby`, `playing`, `ended`, etc.). - Reserve `whenState(...)` for behavior that should only run in one phase. - Use the integration helpers when a state change should also start a timer, consume a cooldown, or trigger a spawn. --- ## Items --- root: .components.layouts.MarkdownLayout title: Items nav-title: Items description: Object-oriented item creation, spawning, and NBT serialization with the Kore OOP module. keywords: minecraft, datapack, kore, oop, items, item stack, summon, give, nbt, data modify date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/items --- # Items The OOP module keeps item usage concise by letting you build an [item stack](https://minecraft.wiki/w/Item) once and then reuse it for giving, spawning, or embedding it in wider entity workflows. > [!FOOTPRINT] > Nothing of its own: each `summon()` creates one `minecraft:item` entity. No objective, storage, forceloaded chunk, tick or load function. ## Basic usage ```kotlin function("item_demo") { val sword = itemStack(Items.DIAMOND_SWORD) { enchantments { enchantment(Enchantments.SHARPNESS, 5) enchantment(Enchantments.UNBREAKING, 3) } } player.giveItem(sword) // spawns the stack as a dropped item entity in the world, not into an inventory sword.summon() sword.summon(textComponent("My Sword", Color.GOLD)) } ``` {{{ .components.mc.ItemTooltipMockup }}} `summon()` spawns the stack as a `minecraft:item` entity at the given position (defaulting to `~ ~ ~`). The overload taking a `ChatComponents` or a `String` + `Color` sets the entity's visible custom name. ## Writing a stack into NBT `toNbt()` returns the `{id, count?, components?}` compound Minecraft expects wherever an item stack lives in NBT: the `Item` tag of an item entity, a container's `Items` entries, or a `data modify ... set value` target. Empty components are omitted, matching how vanilla serializes a stack with no component patch. ```kotlin function("stock_shop") { val sword = itemStack(Items.DIAMOND_SWORD, 3) { enchantments { enchantment(Enchantments.SHARPNESS, 5) } } data(storage("shop", "kore")).modify("offer.item", sword.toNbt()) } ``` ```mcfunction data modify storage kore:shop offer.item set value {id:"minecraft:diamond_sword",count:3,components:{enchantments:{"minecraft:sharpness":5}}} ``` Pass explicit components to serialize a variant of the same stack without rebuilding it: `sword.toNbt(otherComponents)`. ## Configuring the spawned entity `summon` takes a trailing `ItemEntitySummonData` block that controls the spawned entity's root NBT: ```kotlin function("shop_display") { val item = itemStack(Items.DIAMOND_SWORD) { customName(textComponent("Shop", Color.GOLD)) } item.summon { showcase = true // never despawns, cannot be picked up tags = listOf("display") // scoreboard tags, handy to select it again later } } ``` Generated output: ```mcfunction summon minecraft:item 0.0 0.0 0.0 {Invulnerable:1b,NoGravity:1b,Tags:["display"],Item:{id:"minecraft:diamond_sword",components:{custom_name:{text:"Shop",color:"gold"}}},PickupDelay:32767s,Age:-32768s,CustomName:{type:"text",color:"gold",text:"Shop"},CustomNameVisible:1b} ``` `showcase = true` is a shortcut for a static display item: it sets `PickupDelay` and `Age` so the entity never despawns and cannot be picked up, plus `Invulnerable` and `NoGravity`. Any of those set explicitly win over the shortcut, and the item's `custom_name` component is mirrored onto the entity so the name renders in the world. ### Shared entity fields These come from `EntitySummonData` and apply to every entity summoned through this API: | Field | NBT tag | |----------------------------------|-------------------------------| | `air` | `Air` | | `customName` | `CustomName` | | `fallDistance` | `FallDistance` | | `fire` | `Fire` | | `glowing` | `Glowing` | | `hasVisualFire` | `HasVisualFire` | | `invulnerable` | `Invulnerable` | | `motion` / `motion(dx, dy, dz)` | `Motion` | | `noGravity` | `NoGravity` | | `onGround` | `OnGround` | | `portalCooldown` | `PortalCooldown` | | `pos` | `Pos` | | `rotation` / `rotation(yaw, pitch)` | `Rotation` | | `silent` | `Silent` | | `tags` | `Tags` | `motion(dx, dy, dz)` and `rotation(yaw, pitch)` are helper functions so you do not have to build the underlying `Triple` or `Vec2` yourself: ```kotlin function("launch_item") { itemStack(Items.TNT).summon { motion(0.0, 0.6, 0.0) // blocks per tick on each axis, so this pops it upwards rotation(90f, 0f) // yaw then pitch, in degrees glowing = true // draws the outline seen through walls } } ``` ### Item-only fields `ItemEntitySummonData` adds the fields that only exist on `minecraft:item`: | Field | NBT tag | Description | |----------------------|----------------------|----------------------------------------------------| | `age` | `Age` | Ticks lived - negative values delay despawning | | `displayName` | `CustomName` | Name as `ChatComponents`, also settable via `displayName(...)` | | `displayNameVisible` | `CustomNameVisible` | Whether the name renders without looking at it | | `health` | `Health` | Item entity health | | `owner` | `Owner` | UUID allowed to pick the item up | | `pickupDelay` | `PickupDelay` | Ticks before pickup is allowed | | `showcase` | - | Shortcut described above | | `thrower` | `Thrower` | UUID that threw the item | For anything not covered, `summon` also accepts an `extraEntityNbt` builder applied last, so you can write arbitrary root tags (a fixed `UUID`, modded data, and so on). ## Practical example ```kotlin function("reward_drop") { val reward = itemStack(Items.NETHERITE_INGOT) { lore(textComponent("A rare reward dropped by the champion", Color.GRAY)) } player.executeAt { run { reward.summon(textComponent("Champion Reward", Color.GOLD)) } } } ``` This pattern is useful when the same item should be given directly in one context and spawned as a visible reward in another. ## See also - [Entities & Players](/docs/oop/entities-and-players) - Give, replace, or spawn item stacks from entity-scoped helpers. - [Events](/docs/oop/events) - React to item use or consumption with event-driven logic. - [Components](/docs/concepts/components) - Define richer custom names, lore, and metadata for the items you build. --- ## Scoreboards --- root: .components.layouts.MarkdownLayout title: Scoreboards nav-title: Scoreboards description: Object-oriented scoreboard management with the Kore OOP module - objectives, display slots, per-entity score operations, and score arithmetic operators. keywords: minecraft, datapack, kore, oop, scoreboard, objective, score, display slot, fake player, operators, constants date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/scoreboards --- # Scoreboards Wraps [Minecraft scoreboards](https://minecraft.wiki/w/Scoreboard) with two distinct handle types: - **`Scoreboard`** - an objective, identified by name. Create it, display it, name it. - **`ScoreboardEntity`** - one entity's score *in* one objective. Read it, write it, copy it. That split mirrors how vanilla splits `scoreboard objectives ...` from `scoreboard players ...`. > [!FOOTPRINT] > Only the objectives you create. `*=`, `/=` and `%=` against a plain `Int` add the `kore_constants` objective and a `#` holder, written inline the first time a function uses that value. No storage, entity, forceloaded chunk, tick or load function. ## Which handle do I need? | You want to... | Use | |---------------------------------------------------|----------------------------------------------------------| | Create/remove an objective, set its display slot | `scoreboard("name")` → `Scoreboard` | | Read or write an OOP `Entity`'s score | `entity.getScoreEntity("obj")` or `scoreboard.getScore(entity)` | | Read or write a whole team's score | `scoreboard.getScore(team)` | | Read or write the score of `@s`, or any raw selector | `scoreboard.objective(selector, "obj")` - see [Raw selectors](#raw-selectors-and-s) | `ScoreboardEntity`, `getScoreEntity`, and `getScore` all require a `Function` context, so call them **inside** a `function { }` block, not at datapack top level. ## Objectives ```kotlin function("scoreboard_setup") { scoreboard("kills") { create() // the objective must exist before any score is written setDisplaySlot(DisplaySlots.sidebar) // shows the scores on the right of the screen setDisplayName("Kill Count") // shown to players, unlike the internal name "kills" } } ``` | Function | Description | |------------------|------------------------------------------| | `create` | Create the objective (default `dummy`) | | `remove` | Remove the objective | | `setDisplaySlot` | Assign a display slot | | `setDisplayName` | Set the display name (string or component) | | `setRenderType` | Set the render type | | `getScore` | Get a `ScoreboardEntity` for an entity or team | `create()` takes an optional criterion, e.g. `create(ScoreboardCriteria.DEATH_COUNT)`. ## Per-entity scores There are two equivalent ways to reach the same `ScoreboardEntity` - pick whichever reads better at the call site: ```kotlin function("score_ops") { val kills = scoreboard("kills") val fromEntity = player.getScoreEntity("kills") // entity-first val fromObjective = kills.getScore(player) // objective-first, same result fromEntity.set(10) fromEntity.add(5) fromEntity.remove(2) fromEntity.reset() } ``` | Function | Description | |-----------------------|-------------------------------------------------------------------------| | `set` | Set score to a value | | `add` | Add to the score | | `remove` | Subtract from the score | | `reset` | Reset the score | | `copyTo` | Copy this score to another holder/objective, or into entity/storage NBT | | `copyFrom` | Copy from another holder/objective | | `copyDataFrom` | Store a numeric NBT value (entity or storage) into this score | | `copyEntityCountFrom` | Store how many entities match a selector into this score | | `copyMemberCountFrom` | Store a team's member count into this score | ### Operators `ScoreboardEntity` supports `+=` and `-=` against a literal: ```kotlin function("on_kill") { val kills = player.getScoreEntity("kills") kills += 1 kills -= 1 } ``` The same operators work between two score handles, and `*=`, `/=`, `%=` are available on both sides. Every one of them compiles to a single `scoreboard players operation`: ```kotlin function("score_math") { val score = player.getScoreEntity("score") val kills = player.getScoreEntity("kills") score += kills // scoreboard players operation ... += ... score *= kills score /= kills // floored division, as vanilla does score %= kills // floored modulo score setTo kills score minWith kills // keeps the smaller of the two score maxWith kills // keeps the larger of the two score swapWith kills } ``` `*=`, `/=` and `%=` also accept an `Int`. Vanilla has no literal right-hand side for `operation`, so Kore reads the value from a fake-player constant, declared once per function in the `kore_constants` objective (rename it through `OopConstants.constantsObjective`): ```kotlin function("halve_score") { val score = player.getScoreEntity("score") score /= 2 } ``` ```mcfunction scoreboard objectives add kore_constants dummy scoreboard players set #2 kore_constants 2 scoreboard players operation @e[...] score /= #2 kore_constants ``` ### Fake players A score holder starting with `#` is a *fake player*: a holder no real entity owns, which is how datapacks keep globals and constants out of the sidebar. `fakePlayer` builds an `Entity` handle for one, so every `ScoreboardEntity` API works against it: ```kotlin function("next_wave") { val wave = fakePlayer("wave").getScoreEntity("game_state") wave += 1 wave.copyTo(storage("game", "kore"), "wave") } ``` ```mcfunction scoreboard players add #wave game_state 1 execute store result storage kore:game wave int 1 run scoreboard players get #wave game_state ``` `fakePlayer("wave")` and `fakePlayer("#wave")` both produce `#wave`. Use the `FakePlayer` constructor directly for holders that must not carry that prefix, such as the `§0`..`§f` holders behind sidebar lines. A fake player has no entity behind it, so `asSelector()` throws instead of emitting `@e[name=#wave]`, which matches an entity custom name and would silently target nothing. Only score APIs, which go through `asScoreHolder()`, accept one. ### Copying between scores, NBT, and counts ```kotlin function("sync") { val stat = player.getScoreEntity("stats.charge") stat.copyFrom(player.asSelector(), "deaths") // from another score // from NBT: scores are integers, so a scale converts fractional values stat.copyDataFrom(player, "Inventory[0].count") stat.copyDataFrom(storage("stats", "kore"), "charge") // from a live count of matching entities stat.copyEntityCountFrom(entity(EntityTypes.ZOMBIE, limitToOne = false)) stat.copyMemberCountFrom(team("red")) // and back out into NBT stat.copyTo(player, "kore.charge", DataType.INT) stat.copyTo(storage("stats", "kore"), "charge", DataType.INT) } ``` A score to score copy is a single `scoreboard players operation = `, the NBT and count copies compile to an `execute store result ... run ...` chain, so you never have to write the store plumbing by hand. ## Raw selectors and `@s` `ScoreboardEntity` is built around an OOP [`Entity`](/docs/oop/entities-and-players) handle, and `Entity` always renders as `@e[...]`. There is no `Entity` that renders as `@s`, and `self()` returns a `SelectorArgument`, not an `Entity` - so it cannot be passed to `getScoreEntity` or `getScore`. For `@s` (or any other raw selector) use the core `scoreboard.objective(...)` DSL instead. It returns a `PlayerObjective`, which supports the same arithmetic including operators: ```kotlin function("charge_up") { val lastCharge = scoreboard.objective(self(), "last_crystal_charge") lastCharge += 10 } ``` This emits `scoreboard players add @s last_crystal_charge 10`. `PlayerObjective` covers `set`, `add`, `remove`, `reset`, `get`, `enable`, `operation`, the `+=` / `-=` / `++` / `--` operators (`++` / `--` need a `var`, Kotlin reassigns the variable), and the `min` / `max` infix operations: ```kotlin function("self_score_math") { var mine = scoreboard.objective(self(), "score") val best = scoreboard.objective(literal("#best"), "score") mine++ best max mine // keeps the larger of the two in best mine.operation(Operation.SET, literal("#best"), "score") } ``` Use `scoreboard.objective(...)` whenever the score holder is `@s` or any selector you already have as an `Argument`. Use `ScoreboardEntity` when you hold an OOP `Entity`, including a [fake player](#fake-players). ## Practical pattern ```kotlin function("match_setup") { val kills = scoreboard("kills") kills.create() kills.setDisplaySlot(DisplaySlots.sidebar) val playerKills = kills.getScore(player) playerKills.set(0) } ``` Configure the objective once, then retrieve score handles wherever gameplay code needs to increment, reset, or copy values. ## See also - [Entities & Players](/docs/oop/entities-and-players) - The `Entity` handles that `getScoreEntity` and `getScore` need. - [Teams](/docs/oop/teams) - `scoreboard.getScore(team)` and team member counters. - [Timers](/docs/oop/timers) - Scoreboard-backed countdowns built on this API. --- ## Spawners --- root: .components.layouts.MarkdownLayout title: Spawners nav-title: Spawners description: Reusable entity spawner handles with the Kore OOP module - register, spawn, and batch-summon entities. keywords: minecraft, datapack, kore, oop, spawner, summon, entity, mob, wave date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/spawners --- # Spawners Spawners wrap [entity summoning](https://minecraft.wiki/w/Commands/summon) into reusable, pre-configured handles. They are useful whenever the same mob or entity should be spawned multiple times with the same base configuration. > [!FOOTPRINT] > Only the entities you spawn, plus one `spawner__spawn` function per spawner for your own commands: Kore never calls it, `spawn()` writes the `summon` inline. No objective, storage, forceloaded chunk, tick or load function. ## Registering a spawner ```kotlin val zombieSpawner = registerSpawner("zombie", EntityTypes.ZOMBIE) { position = vec3(0, 64, 0) } ``` Registering a spawner once helps keep coordinates, entity type, and spawn behavior in one predictable place. ## Using a spawner ```kotlin function("spawn_wave") { with(zombieSpawner) { spawn() // summon at configured position spawnAt(vec3(10, 64, 10)) // summon at a specific position spawnMultiple(5) // summon multiple at configured position } } ``` | Function | Description | |-----------------|-------------------------------------| | `spawn` | Summon at the configured position | | `spawnAt` | Summon at a specific position | | `spawnMultiple` | Summon N entities at configured pos | ## Example: spawning a wave ```kotlin function("spawn_wave") { with(zombieSpawner) { spawnMultiple(5) spawnAt(vec3(12, 64, 12)) } } ``` This makes spawners a natural fit for waves, encounter scripts, arena refills, or scripted boss phases. ## See also - [Entities & Players](/docs/oop/entities-and-players) - Operate on the spawned entities afterwards with higher-level helpers. - [Teams](/docs/oop/teams) - Assign spawned waves to teams when organizing PvE or faction-based encounters. - [Events](/docs/oop/events) - React to deaths or interactions involving spawned entities. --- ## Teams --- root: .components.layouts.MarkdownLayout title: Teams nav-title: Teams description: Object-oriented team management with the Kore OOP module - create, configure, and manage Minecraft teams. keywords: minecraft, datapack, kore, oop, teams, scoreboard, collision, nametag, friendly fire date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/teams --- # Teams Wraps [Minecraft teams](https://minecraft.wiki/w/Scoreboard#Teams) into an object-oriented API: ```kotlin function("team_setup") { val red = team("red") { ensureExists() setColor(Color.DARK_RED) setPrefix(textComponent { text = "RED "; color = Color.DARK_RED; bold = true }) setFriendlyFire(false) setCollisionRule(CollisionRule.NEVER) setNametagVisibility(Visibility.HIDE_FOR_OTHER_TEAMS) addMembers(player) } player.joinTeam(red) player.leaveAnyTeam() } ``` {{{ .components.mc.TeamChatMockup }}} The main benefit is that configuration stays grouped by team, which makes lobby setup, role assignment, and PvP rules much easier to read than a long list of raw `team modify` commands. Teams can also act as a bridge to other OOP features. Once you have a `Team` handle, you can reuse its members as an entity selector for [Scoreboards](/docs/oop/scoreboards), [Boss Bars](/docs/oop/boss-bars), or any API that already works with `Entity`. They also integrate nicely with `execute` conditions and score tracking helpers, which makes it easy to express checks like "does this team still have this player?" or "how many players are still alive in this team?" without rebuilding selectors by hand. > [!FOOTPRINT] > Only the teams you create, `ensureExists()` writes `team add`. No objective, storage, entity, forceloaded chunk, tick or load function. ## Reusing a team in other OOP features ```kotlin val red = team("red") val phaseBar = registerBossBar("phase", name) val points = scoreboard("phase.points") function("sync_red_team") { phaseBar.setPlayers(red) points.getScore(red).set(10) red.clearMembers() } ``` This keeps team-centric logic in one place: the same `Team` wrapper can drive visibility, score updates, and member management without rebuilding selectors manually. ## Conditions and counters ```kotlin val red = team("red") val tracker = player("Tracker") function("check_red_team") { val membersLeft = tracker.getScoreEntity("red.members") execute { ifCondition { hasMembers(red) hasPlayer(red, "Ayfri") hasScore(red, "round.points", rangeOrInt(3)) } run { say("Red team is still in the round") } } membersLeft.copyMemberCountFrom(red) membersLeft.copyTo(storage("match", namespace = name), "state.red_members") } ``` This pattern is useful when several OOP systems need to exchange state. A `Team` can expose conditions for `execute`, while a `ScoreboardEntity` can cache the current member count or mirror that count into entity/storage NBT for later reuse by timers, [Events](/docs/oop/events), or custom game rules. ## Team functions | Function | Description | |-----------------------------|-----------------------------------------------------| | `ensureExists` | Create the team if it doesn't exist | | `delete` | Delete the team | | `setDisplayName` | Set the team display name | | `setPrefix` / `setSuffix` | Set name prefix/suffix | | `setColor` | Set team color | | `setFriendlyFire` | Toggle friendly fire | | `setSeeFriendlyInvisibles` | Toggle seeing invisible teammates | | `setCollisionRule` | Set collision rule | | `setNametagVisibility` | Set nametag visibility | | `setDeathMessageVisibility` | Set death message visibility | | `addMembers` | Add entities to the team | | `hasMembers` | Check if the team still has at least one member | | `hasPlayer` / `hasMember` | Check if a specific player or entity is in the team | | `hasScore` | Check a score range directly on the team selector | | `members` | Reuse the team as an `Entity` selector | | `clearMembers` | Remove every current team member | ## Practical workflow - Define the team once with its visual identity and gameplay rules. - Add members when players join a role or side. - Reuse `members()` or higher-level helpers like `bossBar.setPlayers(team)` and `scoreboard.getScore(team)` when other features need to target that whole team. - Reuse `player.joinTeam(...)` and `player.leaveAnyTeam()` in gameplay functions instead of raw selectors. ## See also - [Entities & Players](/docs/oop/entities-and-players) - Build the player and entity handles that join, leave, or target teams. --- ## Minecraft Timer Datapack - Scoreboard Timers in Kore OOP --- root: .components.layouts.MarkdownLayout title: Minecraft Timer Datapack - Scoreboard Timers in Kore OOP nav-title: Timers description: Scoreboard-based timers with Kore's OOP module. Countdown timers, boss bar timers, tick-based schedules, and reusable handles. Build cooldowns and game timers without raw command blocks. keywords: timer datapack, minecraft timer, datapack timer, scoreboard timer, countdown timer, minecraft cooldown, boss bar timer, tick timer, kore timer, datapack schedule date-created: 2026-03-03 date-modified: 2026-10-07 routeOverride: /docs/oop/timers --- # Timers Timers use a [scoreboard objective](https://minecraft.wiki/w/Scoreboard) that increments every [tick](/docs/concepts/time). When the score reaches the configured duration, completion handlers fire. This is a strong fit for round timers, capture phases, delayed rewards, warmups, or any mechanic that should complete after a predictable amount of [ticks](/docs/concepts/time). Durations are expressed as [ `TimeNumber`](/docs/concepts/time) values - `5.seconds`, `200.ticks`, etc. > [!FOOTPRINT] > - **Objectives:** one per timer, named after it. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** `timer__tick` adds 1 to every entity at 0 or more, and `registerTimerWithBossBar` adds `timer__bar_tick`, which copies the timer into the boss bar. > - **Load and files:** `timer__init` creates the objective, `onComplete` blocks become functions listed in `#kore:timer_complete_handlers`, and `registerTimerWithBossBar` adds the boss bar `:_bar`. ## Registering a timer ```kotlin val timer = registerTimer("round_timer", 5.seconds) ``` This generates: - A **load function** that creates the scoreboard objective. - A **tick function** that increments the score of every entity with a score ≥ 0, players or not. ## Typical lifecycle ```kotlin function("round_start") { timer.start(player) } function("round_cancel") { timer.stop(player) } ``` Using dedicated start and stop points keeps it clear which gameplay events begin or cancel the countdown. ## Using a timer ```kotlin function("round") { with(timer) { start(player) onComplete(player) { say("Time's up!") } stop(player) } } ``` | Function | Description | |--------------|--------------------------------------------------------| | `start` | Set the score to 0, beginning the countdown | | `stop` | Set the score to −1, halting the timer | | `onComplete` | Register a handler that fires when duration is reached | ## Timer with Boss Bar Combines a timer with an auto-managed boss bar: ```kotlin val timedBar = registerTimerWithBossBar("boss_timer", 10.seconds) { color = BossBarColor.GREEN style = BossBarStyle.NOTCHED_20 } function("boss_round") { with(timedBar) { start(player) onComplete(player) { say("Boss round over!") } stop(player) } } ``` The boss-bar variant is useful when you want a countdown that is both mechanical and visible to players without having to manually synchronize a separate UI layer. Its max is the duration in ticks, and every tick copies the timer score into the bar: ```mcfunction execute as @e[limit=1,scores={boss_timer=0..}] store result bossbar my_pack:boss_timer_bar value run scoreboard players get @s boss_timer ``` One bar shows one timer: when several entities run the same timer at once, the bar follows one of them. Register a timer per entity, or per team, to show each its own bar. --- ## World Events - Tick, Weather & Day/Night Event System in Kore --- root: .components.layouts.MarkdownLayout title: World Events - Tick, Weather & Day/Night Event System in Kore nav-title: World Events description: React to world-level events in Minecraft with Kore's OOP module. Tick events, weather changes (rain/thunder), day/night cycle triggers (noon/midnight), and configurable interval timers. keywords: minecraft world events, datapack tick event, minecraft weather event, day night cycle datapack, kore world events, datapack interval timer, rain thunder event minecraft, world load event, datapack time of day date-created: 2026-06-23 date-modified: 2026-10-07 routeOverride: /docs/oop/world-events --- # World Events World events are the world-side counterpart of the [entity/player events](/docs/oop/events): instead of reacting to an entity action, they react to the world itself ticking, the weather changing, the day/night cycle, or a fixed interval elapsing. Everything stays **100% datapack-side** - no in-world command blocks. Dynamic data is read with predicates and scoreboards, and where a value has to be read at runtime you should reach for [macros](/docs/commands/macros) rather than command blocks. Like entity events, multiple handlers can be registered for the same event and they all fire together. Each event is backed by a function registered in the vanilla `tick`/`load` tag that dispatches to a per-event [function tag](https://minecraft.wiki/w/Tag#Function_tags). > [!FOOTPRINT] > - **Objectives:** `kore_world`, with `#.now`, `.prev` and `.counter` holders for edge and interval events. > - **Storage:** none. > - **Entities:** none. > - **Forceloaded chunks:** none. > - **Every tick:** one `dispatch_` function per tick, weather, day/night, time of day or interval event, and per dimension. > - **Load and files:** `kore_world_init` creates the objective, `onLoad` handlers run from `dispatch_on_world_load`. Each event gets a `#:` tag and one function per handler. ## The world handle Register events on a `world()` handle. Pass a dimension to scope a handler so it runs with the execution context set to that dimension (`execute in run ...`): ```kotlin datapack("my_pack") { val world = world() val nether = world(Dimensions.THE_NETHER) world.onTick { say("Runs every tick, globally.") } nether.onTick { say("Runs every tick, executed in the Nether.") } } ``` World event helpers only need a `DataPack` in scope, so you can register them straight in the `datapack { }` block, or inside a `function { }` when you want to mix them with other commands. ## Available events | Function | Trigger | |----------------------|-----------------------------------------------------------------------| | `onDayStart` | The tick `daytime` enters `0..11999` (dawn) | | `onInterval(period)` | Every `period` ticks (scoreboard counter, immune to a frozen daytime) | | `onLoad` | Once on datapack load / `/reload` | | `onMidnight` | The tick `daytime` reaches `18000` | | `onNightStart` | The tick `daytime` enters `13000..23999` | | `onNoon` | The tick `daytime` reaches `6000` | | `onRainStart` | The tick precipitation starts (rain or thunder) | | `onRainStop` | The tick precipitation stops | | `onThunderStart` | The tick a thunderstorm starts | | `onThunderStop` | The tick a thunderstorm stops | | `onTick` | Every tick (20 times per second) | | `onTimeOfDay(time)` | The tick `daytime` reaches `time` (`0..23999`) | ```kotlin datapack("my_pack") { val world = world() world.onLoad { say("Pack loaded!") } world.onInterval(5.seconds) { say("Tick every 5 seconds.") } world.onRainStart { say("It started raining!") } world.onThunderStop { say("The storm passed.") } world.onDayStart { say("Good morning!") } world.onNoon { say("High noon.") } world.onNightStart { say("Watch out for mobs!") } world.onTimeOfDay(23000) { say("Sunrise is near.") } } ``` ## How edge events work `onRainStart`, `onThunderStart`, `onDayStart` and friends are **edge-triggered**: they fire only on the single tick the condition flips, not every tick the condition is true. The dispatcher stores the current state into a `kore_world` scoreboard, compares it to the previous tick, runs the handlers on a transition, then saves the new state: ```mcfunction # dispatch_on_rain_start execute store result score #on_rain_start.now kore_world if predicate {condition:"minecraft:weather_check",raining:1b} execute if score #on_rain_start.now kore_world matches 1 if score #on_rain_start.prev kore_world matches 0 run function #my_pack:on_rain_start scoreboard players operation #on_rain_start.prev kore_world = #on_rain_start.now kore_world ``` Because the previous state is unset on the first tick after a reload, edge events never fire spuriously on load. `onInterval` instead keeps a counter on the same objective, so it stays accurate even when `advance_time` is off: ```mcfunction # dispatch_on_interval scoreboard players add #on_interval.counter kore_world 1 execute if score #on_interval.counter kore_world matches 100.. run function #my_pack:on_interval execute if score #on_interval.counter kore_world matches 100.. run scoreboard players set #on_interval.counter kore_world 0 ``` ## Notes - `onRainStart`/`onRainStop` track precipitation in general (a thunderstorm also counts as raining). Use the thunder events when you specifically care about storms. - Weather and time are global in vanilla; binding an event to a dimension changes **where the handler runs**, not which world's weather is read. - The shared `kore_world` objective is created once per datapack in the generated `kore_world_init` load function. - `onNoon` and `onMidnight` are thin wrappers over `onTimeOfDay`. Use `onTimeOfDay` directly for sunset (`12000`), sunrise (`23000`), or any custom moment. Moon-phase logic can be layered on top: react in `onNightStart`, then read the game time with a macro to compute `(day % 8)`. ## See also - [Events](/docs/oop/events) - the entity/player event system this mirrors. - [Timers](/docs/oop/timers) - count up to a duration per entity, with an optional boss bar. - [Cooldowns](/docs/oop/cooldowns) - gate abilities so they cannot be spammed. --- # Advanced ## Bindings --- root: .components.layouts.MarkdownLayout title: Bindings nav-title: Bindings description: Import existing datapacks and generate Kotlin bindings. keywords: kore, bindings, import, datapack, github, modrinth, curseforge date-created: 2026-01-23 date-modified: 2026-09-26 routeOverride: /docs/advanced/bindings position: 3 --- # Bindings (Import Existing Datapacks) > [!WARNING] > The bindings module is **experimental**. APIs and generated output may change without notice. The bindings module lets you import existing datapacks and generates Kotlin types so you can reference functions, resources, and tags safely in your code. ## Install this module Add the `bindings` artifact when you want to import an existing datapack and generate Kotlin bindings from it. ### Artifact ```kotlin dependencies { implementation("io.github.ayfri.kore:bindings:VERSION") } ``` ### Best fit - Use `kore` for the main DSL. - Add `bindings` when you want type-safe access to resources coming from an external datapack. - Combine it with `oop` or `helpers` only if your project also needs those higher-level utilities. ## Quick start 1. Add the dependency. 2. Import `importDatapacks`. 3. Configure the output directory and package prefix. 4. Declare at least one source pack. ```kotlin import io.github.ayfri.kore.bindings.api.importDatapacks importDatapacks { configuration { outputPath("src/main/kotlin") packagePrefix = "kore.dependencies" } github("pixigeko.minecraft-default-data:1.21.8") { subPath = "data" } } ``` This example generates bindings into `src/main/kotlin` under the `kore.dependencies` package. If you need another source, replace `github(...)` with `modrinth(...)`, `curseforge(...)`, or `url(...)`. Those are explained below in the [Download sources](#download-sources) section. ## Using generated bindings When you import a datapack, Kore generates a `data object` with a name derived from the datapack's name (e.g., `VanillaRefresh`). ### Accessing resources Resources are organized by namespace and type. If a datapack has only one namespace, resources are accessible directly: ```kotlin import kore.dependencies.vanillarefresh.VanillaRefresh function("my_function") { // Call a function from the imported datapack function(VanillaRefresh.Functions.MAIN_TICK) // Reference a loot table (see Commands for loot usage) loot(VanillaRefresh.LootTables.BLOCKS.IRON_ORE) } ``` For more on using commands with imported resources, see [Commands](/docs/commands/commands). If the datapack uses multiple namespaces, they are nested: ```kotlin VanillaRefresh.Minecraft.LootTables.CHESTS.ABANDONED_MINESHAFT ``` ### Tags Tags are also imported and can be used wherever a tag of that type is expected: ```kotlin function("my_function") { // Wrap the imported tag in an item predicate so `execute` can read it. execute { ifCondition { // Check if the nearest player has the tagged item in their cursor. items(nearestPlayer(), PLAYER.CURSOR, itemPredicate { itemArgument = VanillaRefresh.Tags.Items.MY_CUSTOM_TAG }) } } } ``` ### Pack metadata The generated object also contains information about the original datapack: ```kotlin val path = VanillaRefresh.PATH // Path to the source file/folder val packMeta = VanillaRefresh.pack // PackSection object from pack.mcmeta ``` When pack metadata is reconstructed in generated bindings, Kore emits the `PackSection` using the same helpers as handwritten code, including `packFormat(...)` for version formats and `SupportedFormats(...)` when legacy supported formats are present. ## Generated structure For each imported datapack, a Kotlin file is generated containing: - A `data object` named after the datapack. - Nested `data object`s for each namespace (if multiple). - Nested `enum`s or `object`s for each resource type: - `Functions`: All `.mcfunction` files. - `Advancements`: All advancements. - `LootTables`: All loot tables. - `Recipes`: All recipes. - `Tags`: All tags, further nested by type (`Blocks`, `Items`, `Functions`, etc.). - `Worldgen`: All worldgen resources, further nested by type (`Biomes`, `Structures`, etc.). Subfolders in the datapack are preserved as nested objects. ## Explore without generating You can explore the content of a datapack programmatically without generating any code. ```kotlin import io.github.ayfri.kore.bindings.api.exploreDatapacks val datapacks = exploreDatapacks { github("user.repo:main") } val pack = datapacks.first() println("Datapack: ${pack.name}") println("Functions: ${pack.functions.size}") pack.functions.forEach { println(" - ${it.id}") } ``` ## Download sources ### GitHub Downloads a repository or a specific asset from a release. For higher API rate limits (or when your environment requires authenticated GitHub API access), set `GITHUB_API_KEY` as an environment variable or `github.api.key` as a system property. Patterns: - `user.repo`: Latest commit on the default branch. - `user.repo:tag`: Specific tag, branch, or commit. - `user.repo:tag:asset.zip`: Specific asset from a release. ```kotlin github("pixigeko.minecraft-default-data:1.21.8") ``` ### Modrinth Downloads the latest or a specific version of a Modrinth project. Patterns: - `slug`: Newest version published for the `datapack` loader, so a project also shipping mod jars resolves to its datapack. - `slug:version`: Specific version ID or number, whatever its loader. The primary file of the version is downloaded, or its first file when none is marked primary. ```kotlin modrinth("vanilla-refresh") ``` ### CurseForge Downloads from CurseForge. Requires the `CURSEFORGE_API_KEY` environment variable or the `curseforge.api.key` system property Patterns: - `projectId`: Latest file for the project that allows third-party downloads. - `projectId:fileId`: Specific file. - `slug`: Project slug. - `slug:fileId`: Specific file for a project slug. - `URL`: Full CurseForge project URL. ```kotlin curseforge("418120") // Project ID ``` ### URL / local path Patterns: - `https://example.com/pack.zip` - `./path/to/pack` (Local folder) - `./path/to/pack.zip` (Local zip) ```kotlin url("https://example.com/pack.zip") ``` ## Configuration ### Global configuration Defined in the `configuration {}` block: | Property | Default | Description | |----------------------|---------------------------------|--------------------------------------------------| | `outputPath` | `build/generated/kore/imported` | Directory where Kotlin files will be generated. | | `packagePrefix` | `kore.dependencies` | Base package for all generated bindings. | | `generateSingleFile` | `true` | If true, generates one Kotlin file per datapack. | | `skipCache` | `false` | If true, re-downloads even if already in cache. | | `debug` | `false` | Prints extra information during the process. | ### Per-datapack configuration Defined in the block following a source: ```kotlin github("user.repo") { packageName = "custom.pkg" // Change the package for this pack subPath = "datapacks/main" // Only import from this subfolder includes = listOf("data/**") // Only include files matching these patterns excludes = listOf("**/test/**") // Exclude files matching these patterns body("{\"token\":\"abc123\"}") // Optional HTTP request body for url("https://...") sources header("Authorization", "Bearer your-token") // Add one request header for url("https://...") headers(mapOf("Accept" to "application/zip")) // Replace all request headers for url("https://...") remappings { objectName("MyPack") // Change the generated object name namespace("old_namespace", "NewNamespace") // Rename a specific namespace object } } ``` > [!NOTE] > The `remappedName` property is deprecated. Use `remappings { objectName("...") }` instead. For HTTP(S) sources declared with `url("https://...")`, you can customize request payload and headers from the per-datapack block with `body("...")`, `header("key", "value")`, and `headers(mapOf(...))`. ### Namespace normalization Namespace names are automatically normalized when generating Kotlin object names: dots (`.`) and other non-alphanumeric characters are replaced with underscores, then converted to PascalCase. For example, `my.namespace` becomes `MyNamespace`. You can override this behavior for any namespace using the `remappings {}` block described above. ## Cache Downloaded files are cached to speed up subsequent runs in a different directory depending on your OS: - On Windows, `LOCALAPPDATA` or `~/AppData/Local/kore`. - On macOS, `~/Library/Caches`. - On Linux, XDG cache directory or fallback to `~/.cache/kore/datapacks`. This can be overridden by the `KORE_CACHE_HOME` environment variable or the `kore.cache.home` system property. Use `skipCache = true` in the global configuration or delete the cache folder to force a re-download. ## Troubleshooting - **Rate Limits**: GitHub API is limited for unauthenticated calls. - **CurseForge API**: Ensure your API key is valid and has permissions for the project. - **Invalid resources**: If a resource type is unknown to Kore, it will be skipped to avoid generating invalid code. --- ## GitHub Actions Publishing --- root: .components.layouts.MarkdownLayout title: GitHub Actions Publishing nav-title: GitHub Actions Publishing description: Automatically publish your Kore datapacks using GitHub Actions with mc-publish. keywords: minecraft, datapack, kore, github actions, publishing, automation, ci/cd date-created: 2025-09-30 date-modified: 2026-09-26 routeOverride: /docs/advanced/github-actions-publishing --- # GitHub Actions Publishing This guide shows you how to automatically publish your Kore-generated datapacks to various platforms using GitHub Actions and the `mc-publish` action. ## Prerequisites - A GitHub repository containing your Kore project - Accounts on target platforms (Modrinth, CurseForge, etc.) - API tokens for each platform you want to publish to ## What is mc-publish? [mc-publish](https://github.com/Kira-NT/mc-publish) is a GitHub Action that simplifies publishing Minecraft projects across multiple platforms including Modrinth, CurseForge, and GitHub Releases. It automatically detects project metadata and handles the complex publication process with minimal configuration. ## Setting Up GitHub Secrets Before configuring your workflow, you'll need to add your platform tokens as GitHub secrets: 1. Go to your repository's **Settings** → **Secrets and variables** → **Actions** 2. Add the following secrets: - `MODRINTH_TOKEN`: Your Modrinth API token - `CURSEFORGE_TOKEN`: Your CurseForge API token - `GITHUB_TOKEN` is automatically provided by GitHub > **Note:** Only add secrets for platforms you plan to publish to. ## Basic Workflow Setup Create `.github/workflows/publish.yml` in your repository: ```yaml name: Publish Datapack on: release: types: [ published ] workflow_dispatch: # Allow manual triggering jobs: publish: runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v5 - name: Set up JDK 25 uses: actions/setup-java@v5 with: cache: gradle distribution: 'temurin' java-version: 25 - name: Ensure Gradle is executable run: chmod +x gradlew - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - name: Generate datapack run: ./gradlew run - name: Publish to platforms uses: Kir-Antipov/mc-publish@v3.3 with: # Modrinth configuration modrinth-id: YOUR_PROJECT_ID modrinth-token: ${{ secrets.MODRINTH_TOKEN }} # CurseForge configuration curseforge-id: YOUR_PROJECT_ID curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} # GitHub Releases github-token: ${{ secrets.GITHUB_TOKEN }} ``` Ensure to use `datapack.generateZip()` in your build script to generate a zip file containing your datapack. ## Advanced Configuration For more control over the publishing process, you can customize various aspects: ```yaml - name: Publish to platforms uses: Kir-Antipov/mc-publish@v3.3 with: # Project identification modrinth-id: AANobbMI modrinth-token: ${{ secrets.MODRINTH_TOKEN }} curseforge-id: 394468 curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }} # Version configuration name: "My Datapack v${{ github.ref_name }}" version: ${{ github.ref_name }} version-type: release # alpha, beta, or release # File selection files: | out/*.zip !out/*-unfinished.zip # Supported game versions game-versions: | 26.2 # Mod loaders (if exporting to Jars) loaders: | fabric forge # Release configuration changelog-file: CHANGELOG.md dependencies: | fabric-api(required){modrinth:P7dR8mSH}{curseforge:306612} # Platform-specific settings modrinth-featured: true curseforge-java-versions: | Java 25 github-prerelease: ${{ contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }} github-draft: false ``` ## Kore-Specific Configuration Kore generates your datapack from Kotlin code, so the workflow needs a build step that runs your `main()` and produces a zip. Make sure your entry point calls `datapack.generateZip()` (Kore writes the archive to the `out/` directory by default), then point `mc-publish`'s `files` input at it: ```yaml files: out/*.zip ``` Kore projects come in two flavours depending on the build tool you started from. Use the matching build step. ### Gradle (Kore-Template) The default [Kore-Template](https://github.com/Ayfri/Kore-Template) is a Gradle project, so the build step uses the Gradle wrapper: ```yaml - name: Set up JDK 25 uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 25 - name: Ensure Gradle is executable run: chmod +x gradlew - name: Generate datapack run: ./gradlew run ``` ### Kotlin Toolchain (formerly Amper) Projects configured with the [Kotlin Toolchain](https://kotlinlang.org/docs/multiplatform/amper.html) (the `module.yaml` / `project.yaml` setup, previously called Amper) ship a `kotlin` wrapper script instead of `gradlew`. The wrapper downloads the toolchain and a JDK on first run, so you don't need a separate `setup-java` step: ```yaml - name: Make the Kotlin wrapper executable run: chmod +x kotlin - name: Generate datapack env: CI: "true" run: ./kotlin run ``` For a multi-module project (several datapacks in one repository), target a specific module with `-m`: ```yaml run: ./kotlin run -m my-datapack ``` Gate `generateZip()` behind an environment variable so local runs still emit loose files while CI produces the publishable zip: ```kotlin val isCI get() = System.getenv("CI") != null fun main() { dataPack("my_datapack") { // ... your content ... }.run { if (isCI) generateZip() else generate() } } ``` ## Workflow Triggers You can configure when the publishing workflow runs: ```yaml on: # Trigger on new releases release: types: [ published ] # Trigger on version tags push: tags: - 'v*' # Allow manual triggering workflow_dispatch: inputs: version-type: description: 'Release type' required: true default: 'release' type: choice options: - release - beta - alpha ``` ## Platform-Specific Features ### Modrinth - Supports featured projects - Automatic dependency resolution - Rich markdown changelog support ```yaml modrinth-featured: true modrinth-unfeature-mode: subset ``` ### CurseForge - Java version specification - Custom display name support ```yaml curseforge-java-versions: | Java 25 curseforge-display-name: "My Awesome Datapack" ``` ### GitHub Releases - Draft and prerelease support - Asset management ```yaml github-draft: false github-prerelease: ${{ contains(github.ref_name, 'pre') }} github-tag: ${{ github.ref_name }} ``` This helps mc-publish automatically detect project metadata. ## Best Practices ### 1. Version Management Use semantic versioning and Git tags: ```bash git tag v1.0.0 git push origin v1.0.0 ``` ### 2. Changelog Management Maintain a `CHANGELOG.md` file following [Keep a Changelog](https://keepachangelog.com/) format: ```markdown # Changelog ## [1.0.0] - 2025-09-30 ### Added - Initial release with basic functionality - Support for Minecraft 26.2 ### Changed - Improved performance of item generation ### Fixed - Fixed issue with advancement rewards ``` ### 3. Testing Before Publishing Add a test job before publishing: ```yaml jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Set up JDK 25 uses: actions/setup-java@v5 with: java-version: '25' distribution: 'temurin' - name: Run tests run: ./gradlew test publish: needs: test runs-on: ubuntu-latest # ... publishing steps ``` ## Troubleshooting ### Common Issues 1. **Missing files**: Ensure you exported the datapack to the correct directory 2. **Invalid tokens**: Verify your secrets are correctly set in GitHub 3. **Permission errors**: Ensure the workflow has `contents: write` permission ### Debugging Enable debug logging by adding: ```yaml env: ACTIONS_RUNNER_DEBUG: true ACTIONS_STEP_DEBUG: true ``` ## Complete Example Here's a complete workflow for a Kore datapack project: ```yaml name: Publish Datapack on: release: types: [ published ] workflow_dispatch: jobs: publish: runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v5 - name: Set up JDK 25 uses: actions/setup-java@v5 with: cache: gradle distribution: 'temurin' java-version: 25 - name: Ensure Gradle is executable run: chmod +x gradlew - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - name: Generate datapack run: ./gradlew run - name: Publish to platforms uses: Kir-Antipov/mc-publish@v3.3 with: name: "${{ github.event.repository.name }} ${{ github.ref_name }}" version: ${{ github.ref_name }} version-type: release files: out/*.zip game-versions: | 26.2 modrinth-id: YOUR_MODRINTH_ID modrinth-token: ${{ secrets.MODRINTH_TOKEN }} modrinth-featured: true curseforge-id: YOUR_CURSEFORGE_ID curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }} github-tag: ${{ github.ref_name }} changelog-file: CHANGELOG.md ``` ## See Also - [Creating a Datapack](/docs/guides/creating-a-datapack) - [Configuration](/docs/guides/configuration) - [mc-publish Documentation](https://github.com/Kir-Antipov/mc-publish) - [GitHub Actions Documentation](https://docs.github.com/en/actions) --- ## Known Issues --- root: .components.layouts.MarkdownLayout title: Known Issues nav-title: Known Issues description: Kore-focused limitations, documented DSL constraints, and generation rough edges with links to docs and source. keywords: kore, guide, documentation, known issues, compatibility, limitations, workarounds date-created: 2025-08-27 date-modified: 2026-09-29 routeOverride: /docs/advanced/known-issues --- # Known issues and compatibility This page lists **limitations and rough edges that touch Kore directly**: the `kore` module, **`bindings`**, and * *`helpers`** where Kore documents behavior. It is not a general Minecraft gameplay reference. Each item is tagged: | Category | Meaning | |---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------| | **Kore limitation** | Kore or a dependency Kore wires in (for example [knbt](https://github.com/BenWoodworth/knbt)) cannot express or round-trip the case yet. | | **Documented DSL / helper cap** | Behavior is spelled out in Kore’s own command or helper docs (often because the vanilla command format or text API is narrow). | | **Generation rough edge** | `generate()` / `generateZip()` / merge paths behave as implemented; surprising until you read the generation docs. | For the high-level "what Kore does not include," see [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore#what-kore-will-not-do-for-you). --- ## Kore limitations ### NBT and SNBT (via knbt) **Category:** Kore limitation Kore builds NBT and SNBT through **[knbt](https://github.com/BenWoodworth/knbt)** (`StringifiedNbt` and builders). Entry points include [ `NbtTagUtils.kt`](https://github.com/Ayfri/Kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/utils/NbtTagUtils.kt) and the `nbt { }` / `toSnbt()` helpers used across commands and components. For normal day-to-day usage and examples, start with [NBTs](/docs/concepts/nbts); this section focuses on limitations and workarounds. Implications: - **Typed NBT lists:** In knbt, `NbtList` is **homogeneous** (one element type per list). That matches Java edition NBT, but it means you cannot build a single list that mixes unrelated tag kinds through the normal DSL. See the [knbt README](https://github.com/BenWoodworth/knbt/blob/main/README.md) (`NbtList` description). - **Exotic SNBT text:** Some SNBT spellings or parser extensions you might see in external tools are **not** necessarily available through Kore’s knbt-backed path. Prefer compounds, homogeneous lists, and literals Kore’s builders support. **Workaround:** Emit a **`literal("...")`** only when you must hand-craft a fragment, or post-process generated files. Upgrading or patching **[knbt](https://github.com/BenWoodworth/knbt)** (Kore pins a version in [ `gradle/libs.versions.toml`](https://github.com/Ayfri/Kore/blob/master/gradle/libs.versions.toml)) is the right place for format-level fixes. ### Resource packs **Category:** Kore limitation Kore generates **datapacks** only (`data/`, functions, JSON registries, `pack.mcmeta`). It does **not** emit **resource packs** (`assets/`). That scope is stated on the site (for example [Home](/docs/home)) and in [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore#what-kore-will-not-do-for-you). **Workaround:** Maintain a separate resource-pack project or tooling; you can still keep both in one Gradle repo. ### Bindings importer **Category:** Kore limitation The **`bindings`** module is **experimental**. Official docs: [Bindings](/docs/advanced/bindings). Roadmap and design discussion: [GitHub #176](https://github.com/Ayfri/Kore/issues/176). Notable constraints from Kore’s own docs and README: - Generated APIs may **change** between Kore versions; pin versions and review diffs. - **Unknown resource types** are **skipped** so codegen stays valid ([Bindings troubleshooting](/docs/advanced/bindings#troubleshooting)). - **Macros** in imported `.mcfunction` files are detected via **`$()`-style patterns**, not a full Minecraft parser ([Bindings](/docs/advanced/bindings)). - **CurseForge** downloads need a **CurseForge API key**; **GitHub** may need a token for rate limits ([Download sources](/docs/advanced/bindings#download-sources)). **Workaround:** Treat imports as a typed starting point; simplify or remap packs that confuse the importer ( `remappings { }`, `includes` / `excludes` in [Configuration](/docs/advanced/bindings#configuration)). ### JSON / NBT serialization (generate vs decode) **Category:** Kore limitation Decoding support varies by serializer: - **Chat components** decode fully into their typed forms via `ChatComponents.serializer()` (text, translatable, score, selector, keybind, nbt, object), including nested `extra`, styling, and hover/click events. **`NbtAsJsonSerializer`** decodes JSON/NBT into an `NbtTag`. - **Item components** (`ComponentsSerializer`) decode **generically**: each entry becomes a raw `CustomComponent` keyed by its component name. This round-trips, but the values stay opaque instead of becoming their typed counterparts (such as `DamageComponent`), because `Component` is not a sealed hierarchy and has no name to serializer registry to dispatch on. Typed item-component decoding would require that registry (ideally generated). - Book **page** serializers stay **write-only**: they encode what you build in Kotlin but do not decode vanilla JSON/SNBT. **Workaround:** For the write-only and generic cases, treat your Kotlin project as the **source of truth** and inspect **generated files** under `path` / zip. For contributors extending serializers, see [Arguments](/docs/contributing/arguments) and the relevant feature docs. ### Custom components and `@SerialName` **Category:** Kore limitation When you define **[custom components](/docs/concepts/components)** backed by NBT, property names sometimes need an explicit **`@SerialName("vanilla_json_key")`** because the **KNBT** stack does not always follow the same naming path as Kore’s JSON `namingStrategy` for that shape. The guide shows this in context (for example the `UseComponent` example with `durability_damages`). **Workaround:** Copy patterns from [Components](/docs/concepts/components) and match the keys your target **pack format** expects (use Minecraft’s registry JSON or Kore’s generators as reference, not guesswork). --- ## Documented DSL / helper caps These are **not Kore bugs**; Kore documents them so authors know what the DSL will and will not do. ### Command macros **Category:** Documented DSL / helper cap [Macros](/docs/commands/macros) require **Minecraft 1.20.2+** for the feature itself. Kore’s doc lists DSL limits: macros only in **functions**, **no type checking**, and macros are **not** threaded through every possible command argument slot. **Workaround:** Prefer normal Kotlin command builders where types matter; keep macro surfaces small and **test in-game**. ### Markdown renderer (`helpers`) **Category:** Documented DSL / helper cap The [Markdown renderer](/docs/helpers/markdown-renderer) turns Markdown into **text components**. The helper lists features Minecraft **cannot** represent (images, tables, fenced code blocks, etc.) in [Not supported (Minecraft limitations)](/docs/helpers/markdown-renderer#not-supported-minecraft-limitations). **Workaround:** Stay in the **supported** subset documented on that page, or build **`textComponent`** trees by hand. ## Quick reference | Need | Where to look | |-----------------------|------------------------------------------------------------------------------------------| | Tune JSON output | [Configuration](/docs/guides/configuration) (`prettyPrint`, comments, paths) | | Folder vs zip vs jar | [Creating A Datapack - Generation](/docs/guides/creating-a-datapack#generating-output) | | Import external packs | [Bindings](/docs/advanced/bindings) | | Module layout | [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore) | | Report a bug or gap | [Kore on GitHub](https://github.com/Ayfri/Kore/issues) with pack format and Kore version | --- ## Multiplatform Support --- root: .components.layouts.MarkdownLayout title: Multiplatform Support nav-title: Multiplatform description: Kore's kore, oop, helpers, and bindings modules are Kotlin Multiplatform (JVM + JS). Generate real datapacks on Node.js, or export strings/zip bytes anywhere. keywords: kore, multiplatform, kotlin multiplatform, kotlin/js, jvm, kmp, nodejs, exportAsStrings, generateZipBytes, kotest, karma date-created: 2026-07-12 date-modified: 2026-10-02 routeOverride: /docs/advanced/multiplatform --- # Multiplatform Support `kore`, `oop`, `helpers`, and `bindings` are all **Kotlin Multiplatform** libraries: each declares a `jvm()` target and a `js { browser(); nodejs() }` target, built from a shared `commonMain` source set. There is no separate "Kore JS" artifact - the same `io.github.ayfri.kore::VERSION` coordinates publish JVM, JS, and common metadata variants side by side. ## What runs where | Module | JVM | JS (Node.js) | JS (browser) | |------------|----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------| | `kore` | Full DSL + `generate()` / `generateZip()` / `generateJar()` (file, ZIP, JAR I/O) | Full DSL + real `generate()` / `generateZip()` / `generateJar()`, backed by Node's `fs` | Full DSL, JSON/NBT building, `exportAsStrings()`, `generateZipBytes()` | | `oop` | Full gameplay abstractions | Full gameplay abstractions (built on `kore`'s common API) | Full gameplay abstractions | | `helpers` | Full helper utilities | Full helper utilities | Full helper utilities | | `bindings` | Full: import, download, codegen to disk | Exploration and code-generation model compile, but there is no supported JS-side downloader/writer yet | Same as Node.js | The DSL itself - building a `dataPack { }`, commands, data-driven features, JSON/NBT - is written once in `commonMain` and behaves identically on every target. **File system access** for `kore` works on both the JVM and Node.js (real files, ZIPs, and JARs); the browser has no real filesystem, so it only gets the in-memory paths (`exportAsStrings()`, `generateZipBytes()`). `bindings`' network downloaders and disk caching stay JVM-only, since there's no common Kotlin abstraction for those across JVM/browser/Node yet. ## Using `kore` from Kotlin/JS `kore` has no `@JsExport` declarations, so this is a **Kotlin-to-Kotlin** target: it's for a project written in Kotlin that compiles to JS (browser or Node), not for importing from plain JavaScript/TypeScript via npm. If you're writing Kotlin and targeting `js()`, `kore` is just another multiplatform dependency. ### In Node.js (Kotlin/JS with `nodejs()`) Node has a real filesystem, and `kore` uses it directly - `generate()`, `generateZip()`, and `generateJar()` behave exactly like they do on the JVM, no extra interop code needed: ```kotlin val pack = dataPack("my_pack") { function("hello") { say("hi") } } pack.path("out") // kotlinx.io.files.Path or a String, same as the JVM guide pack.generate() // writes out/my_pack/data/... to Node's real fs pack.generateZip() // writes out/my_pack.zip ``` This works because Kore's generator is built on `kotlinx.io.files.SystemFileSystem`, which already targets Node's `fs` module under the hood - there's no `external`/`@JsModule("fs")` declaration to write yourself. ZIP/JAR bytes are produced by Kore's own pure-Kotlin ZIP writer (no compression library dependency), and merging with an existing third-party `.zip` datapack (`mergeWithPacks`) works too, decompressing STORE and DEFLATE entries with a pure-Kotlin inflate implementation. This is useful for a Kore-based build step in a Kotlin/JS or Kotlin Multiplatform project that doesn't run a JVM at all - for example, a Node-based CI step or CLI tool written entirely in Kotlin/JS. ### In a browser (Kotlin/JS with `browser()`) The browser has no real filesystem, so `generate()` / `generateZip()` / `generateJar()` aren't available there - use the in-memory exports instead: ```kotlin val pack = dataPack("my_pack") { function("hello") { say("hi") } } val files: Map = pack.exportAsStrings() // files["data/my_pack/function/hello.mcfunction"] == "say hi" val zipBytes: ByteArray = pack.generateZipBytes() // suspend, works on every target ``` `generateZipBytes()` is the one archive-producing call that works everywhere, including the browser: it's a `suspend` function that stages files through the [Origin Private File System](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system) (OPFS) behind the scenes and returns the finished ZIP as a plain `ByteArray`, ready to hand to a `Blob` + `URL.createObjectURL` download link (both reachable via Kotlin/JS's `kotlinx.browser` / `org.w3c.dom` bindings, no extra dependency). An in-browser tool builds the pack with the same DSL as the JVM guide, calls `generateZipBytes()` (or `exportAsStrings()` to hand off individual files instead of an archive), and wires the result into whichever browser API triggers the download. The site's [playground](/playground) works this way: it compiles your snippet to JavaScript, runs it in a worker and lists the files `exportAsStrings()` returns. ## `bindings` on JS `bindings`' exploration, entity model, and code generation (`KtRenderer`/`KtType`, a hand-rolled common codegen model that replaced KotlinPoet) live in `commonMain` and compile for the JS target. The `importDatapacks { }` DSL entry point, GitHub/CurseForge/Modrinth/local downloaders, and disk caching stay `jvmMain`-only, since they depend on network I/O. In practice, use `bindings` from the JVM as documented in [Bindings](/docs/advanced/bindings). ## Testing across platforms `kore`, `oop`, `helpers`, and `bindings` all run their [Kotest](https://kotest.io) suites on every Kotlin Multiplatform target: the JVM, Node.js (`jsNodeTest`), and a headless-Chrome browser via Karma (`jsBrowserTest`). Most test specs live in `commonTest` and build a `dataPack { }` in memory, so the same `FunSpec` executes identically on all three platforms. A `dataPack` that never calls `generate()` / `generateZip()` / `generateJar()` is browser-safe by construction, since those calls are the only part of the pipeline that touches a real filesystem. Each module's built-in `allTests` task aggregates its JVM/Node/browser test tasks, and the root `testAll` task aggregates `allTests` across all four modules - this is what CI runs. ## Adding the dependency for a JS target Same coordinates as JVM, resolved through Gradle's Kotlin Multiplatform metadata: ```kotlin kotlin { jvm() js { browser(); nodejs() } sourceSets { commonMain.dependencies { implementation("io.github.ayfri.kore:kore:VERSION") } } } ``` ## See also - [Getting Started](/docs/getting-started) - the JVM-first walkthrough (`generate()` / `generateZip()`), works the same way on Node.js. - [Creating A Datapack](/docs/guides/creating-a-datapack) - output paths and generation options. - [Bindings](/docs/advanced/bindings) - the datapack importer, JVM usage. - [Known Issues](/docs/advanced/known-issues) - other documented limitations. --- ## Test Features --- root: .components.layouts.MarkdownLayout title: Test Features nav-title: Test Features description: A comprehensive guide for creating test instances and test environments in Minecraft's GameTest framework with Kore. keywords: minecraft, datapack, kore, guide, test, testing, environment, instance, gametest, automation date-created: 2025-01-08 date-modified: 2026-08-18 routeOverride: /docs/advanced/test-features --- # Test Features Minecraft 1.21.5 (Snapshot 25w03a) introduced a major overhaul to the **GameTest framework** - an automated end-to-end testing system for datapack functionality. Kore provides a type-safe Kotlin DSL that generates JSON files for the `test_instance` and `test_environment` registries. ## Test Environments Test environments define the preconditions under which tests run. There are seven types: ### Weather Sets a fixed weather condition: ```kotlin testEnvironments { weather("clear_weather", Weather.CLEAR) weather("rain_weather", Weather.RAIN) weather("storm_weather", Weather.THUNDER) } ``` ### Clock Time Locks a world clock to a specific tick value: ```kotlin testEnvironments { clockTime("morning", WorldClocks.OVERWORLD, 1000) clockTime("noon", WorldClocks.OVERWORLD, 6000) clockTime("night", WorldClocks.OVERWORLD, 18000) } ``` ### Difficulty Forces the game difficulty, keeping difficulty-dependent mechanics deterministic: ```kotlin testEnvironments { difficulty("peaceful_env", Difficulty.PEACEFUL) difficulty("hard_env", Difficulty.HARD) } ``` ### Game Rules Overrides game rules for controlled conditions: ```kotlin testEnvironments { gameRules("controlled_env") { this[Gamerules.ADVANCE_TIME] = false this[Gamerules.SPAWN_MOBS] = false this[Gamerules.RANDOM_TICK_SPEED] = 0 } } ``` ### Function Runs datapack functions before (`setup`) and/or after (`teardown`) each test: ```kotlin val setupFn = function("test_setup") { say("Setting up") } val teardownFn = function("test_teardown") { say("Tearing down") } testEnvironments { function("my_function_env") { setup(setupFn) teardown(teardownFn) } } ``` ### Timeline Attributes Applies a set of timelines during test execution, useful for testing time-driven mechanics: ```kotlin val dayCycle = timeline("day_cycle", WorldClocks.OVERWORLD) val endCycle = timeline("end_cycle", WorldClocks.THE_END) testEnvironments { timelineAttributes("timeline_env", dayCycle, endCycle) } ``` ### Combined (allOf) Merges multiple environments into one: ```kotlin testEnvironments { val rules = gameRules("no_mobs") { this[Gamerules.SPAWN_MOBS] = false } val time = clockTime("dawn", WorldClocks.OVERWORLD, 1000) allOf("controlled_dawn", rules, time) } ``` > **Tip:** You can also create environments outside a `testEnvironments` block using `testEnvironmentsBuilder`: > ```kotlin > val env = testEnvironmentsBuilder.weather("clear", Weather.CLEAR) > ``` > This is useful when reusing environments across multiple test instances. ## Test Instances Test instances define the actual tests. Each one references a structure, an environment, and execution parameters. ### Test Types - **Block-based** (`blockBased()`): Test logic is driven by redstone inside the structure using special test blocks (Start, Log, Fail, Accept). - **Function-based** (`functionBased()`): Test logic is handled by a Java method reference (used by Mojang internally and mod developers). Requires a `function` field with a fully qualified method reference (e.g., `"com.example.MyMod::myTest"`). ### Creating Test Instances ```kotlin testInstances { // Block-based test testInstance("redstone_test") { blockBased() environment(env) maxTicks = 100 structure(Structures.AncientCity.Structures.BARRACKS) } // Function-based test testInstance("function_test") { functionBased() environment(env) function("com.example.MyMod::myTest") maxTicks = 200 structure(Structures.Igloo.TOP) } } ``` ### Configuration Options All available properties for a test instance: | Property | Type | Description | |------------------------------------|---------------------------|------------------------------------------------------| | `environment(env)` | `TestEnvironmentArgument` | **Required.** The test environment to use. | | `structure(struct)` | `StructureArgument` | **Required.** The structure template for the test. | | `blockBased()` / `functionBased()` | - | Sets the test type (default: block-based). | | `function(fn)` | `String` | Java method reference for function-based tests. | | `maxTicks` | `Int` | Maximum ticks before timeout (default: 100). | | `setupTicks` | `Int` | Ticks to wait before starting the test. | | `maxAttempts` | `Int` | Maximum retry attempts. | | `requiredSuccesses` | `Int` | Successes needed out of `maxAttempts`. | | `required` | `Boolean` | Whether the test must pass for the suite to succeed. | | `manualOnly` | `Boolean` | Whether the test is only run manually. | | `skyAccess` | `Boolean` | Whether the structure needs sky access. | | `rotation(rot)` | `StructureRotation` | Rotation applied to the structure. | Rotation helpers: `noRotation()`, `clockwise90()`, `rotate180()`, `counterclockwise90()`. ## Structures Kore provides constants for all vanilla structures: ```kotlin // Examples structure(Structures.AncientCity.Structures.BARRACKS) structure(Structures.Bastion.Treasure.Bases.LAVA_BASIN) structure(Structures.Igloo.TOP) structure(Structures.TrialChambers.Chamber.ASSEMBLY) structure(Structures.Village.Plains.Houses.PLAINS_SMALL_HOUSE_1) ``` ## Test Commands ### Selectors ```kotlin allTests() // "*:*" - all tests minecraftTests() // "minecraft:*" - minecraft tests testSelector("my_pack:*") // All tests in namespace testSelector("*_combat") // Pattern matching ``` ### Command Usage ```kotlin function("run_tests") { test { val selector = testSelector("my_pack:test_*") run(selector) runClosest() runMultiple(selector, 5) create(TestInstanceArgument("test", "pack")) locate(selector) pos("variable") clearAll() resetClosest() stop() verify(TestInstanceArgument("test1", "pack")) } } ``` This generates: ```mcfunction test run my_pack:test_* test runclosest test runmultiple my_pack:test_* 5 test create pack:test test locate my_pack:test_* test pos variable test clearall test resetclosest test stop test verify pack:test1 ``` ## Complete Example ```kotlin fun DataPack.createTestSuite() { val setupFn = function("test_setup") { say("Setting up test") } val cleanupFn = function("test_cleanup") { say("Cleaning up test") } // Create reusable environments val controlled = testEnvironmentsBuilder.gameRules("controlled") { this[Gamerules.ADVANCE_TIME] = false this[Gamerules.SPAWN_MOBS] = false this[Gamerules.RANDOM_TICK_SPEED] = 0 } val dayTime = testEnvironmentsBuilder.clockTime("day", WorldClocks.OVERWORLD, 6000) val controlledDay = testEnvironmentsBuilder.allOf("controlled_day", controlled, dayTime) // Function environment (setup/teardown) testEnvironments { function("test_functions") { setup(setupFn) teardown(cleanupFn) } } testInstances { testInstance("redstone_basic") { blockBased() environment(controlledDay) maxTicks = 100 required = true structure(Structures.AncientCity.Structures.BARRACKS) } testInstance("complex_logic_test") { functionBased() environment(controlled) function("com.example.MyMod::myTest") maxAttempts = 2 maxTicks = 200 required = true structure(Structures.AncientCity.Structures.BARRACKS) } testInstance("directional_blocks") { blockBased() clockwise90() environment(controlled) maxTicks = 120 required = true structure(Structures.AncientCity.Structures.BARRACKS) } } } ``` ## Best Practices - **Combine environments** with `allOf` for complex scenarios. - **Mark critical tests** as `required = true`. - **Set appropriate timeouts** with `maxTicks`. - **Use game rules** to disable randomness (mob spawning, daylight cycle, tick speed). - **Reuse environments** via `testEnvironmentsBuilder` to avoid duplication. ## See Also - [Functions](/docs/commands/functions) - Kore's DSL for writing datapack functions. - [GameTest Framework](https://minecraft.fandom.com/wiki/GameTest_Framework) - Official Minecraft documentation. --- # Contributing ## Contributing: Architecture and Patterns --- root: .components.layouts.MarkdownLayout title: "Contributing: Architecture and Patterns" nav-title: "Architecture and Patterns" description: Detailed internal architecture, project layout, module responsibilities, and recurring implementation patterns for Kore contributors. keywords: architecture, bindings, commands, generator, kore, patterns, serializers, website date-created: 2026-04-10 date-modified: 2026-10-02 routeOverride: /docs/contributing/architecture-and-patterns --- # Contributing: Architecture and Patterns This page documents the contributor-facing architecture of Kore and the implementation patterns worth reusing. It is intentionally focused on the parts that help you answer two questions quickly: 1. *Which module should I edit?* 2. *Which project pattern should I follow instead of inventing a new one?* ## Project tree at a glance ```text Kore/ ├─ bindings/ ├─ build-logic/ ├─ generation/ ├─ helpers/ ├─ kore/ ├─ oop/ └─ website/ ``` This tree is intentionally small. It reflects the modules contributors should reason about first; local-only or git-excluded sandboxes are omitted on purpose. ## How the main modules relate - `bindings/` imports datapacks and emits Kotlin bindings for resources and registries. - `build-logic/` centralizes shared Gradle conventions and project metadata. - `generation/` transforms upstream Minecraft data into generated Kotlin/resources consumed by `kore/`. - `helpers/` and `oop/` layer higher-level APIs on top of the core DSL. - `kore/` is the main DSL and runtime surface most contributors touch first. - `website/` documents both the public API and contributor workflows. ## Module boundaries and edit surfaces ### [`bindings/`](https://github.com/ayfri/kore/tree/master/bindings) - Purpose: datapack importer and Kotlin binding generator. - Typical flow: explorer -> normalized entities -> writer output. - Common edit surface: [`explorer.kt`](https://github.com/ayfri/kore/blob/master/bindings/src/commonMain/kotlin/io/github/ayfri/kore/bindings/explorer.kt), [`entities.kt`](https://github.com/ayfri/kore/blob/master/bindings/src/commonMain/kotlin/io/github/ayfri/kore/bindings/entities.kt), [ `writer.kt`](https://github.com/ayfri/kore/blob/master/bindings/src/commonMain/kotlin/io/github/ayfri/kore/bindings/writer.kt), then tests under [`bindings/src/commonTest`](https://github.com/ayfri/kore/tree/master/bindings/src/commonTest/kotlin/io/github/ayfri/kore/bindings). - Pattern to preserve: single-namespace packs stay compact, multi-namespace packs become namespace-nested objects, and worldgen content is grouped under `Worldgen`. ### [`build-logic/`](https://github.com/ayfri/kore/tree/master/build-logic) - Purpose: shared Gradle conventions, publishing logic, and project metadata. - Common edit surface: convention plugins and [`Project.kt`](https://github.com/ayfri/kore/blob/master/build-logic/convention/src/main/kotlin/Project.kt). - Edit here when changing build behavior, publication rules, or project versioning. ### [`generation/`](https://github.com/ayfri/kore/tree/master/generation) - Purpose: source-data processing and generated Kotlin/resource output. - Edit here when a generated enum, registry wrapper, or source-derived structure is wrong. - **Never** fix a generation issue by editing `kore/src/commonMain/kotlin/io/github/ayfri/kore/generated` or `build/generated/...` directly. - Full walkthrough: [Contributing: The Generation Pipeline](/docs/contributing/generation-pipeline). ### [`helpers/`](https://github.com/ayfri/kore/tree/master/helpers) - Purpose: optional higher-level helpers built on the core DSL. - Edit here only when the issue explicitly targets helper abstractions or reusable convenience APIs. - Mirror core DSL patterns instead of creating a parallel architecture. ### [`kore/`](https://github.com/ayfri/kore/tree/master/kore) - Purpose: core DSL, typed arguments, command wrappers, serializers, worldgen builders, and data-driven resources. - Common edit surface: feature classes, `DataPack` registration, `Function` extensions, serializers, and tests under [ `kore/src/commonTest`](https://github.com/ayfri/kore/tree/master/kore/src/commonTest/kotlin/io/github/ayfri/kore). - A typical change in this module touches one feature family end to end: model, registration, builder entry point, tests, and docs. ### [`oop/`](https://github.com/ayfri/kore/tree/master/oop) - Purpose: object-oriented abstractions layered on top of core Kore primitives. - Edit here when the issue is specifically about that façade, not when the underlying DSL itself is wrong. - Keep naming and behavior aligned with `kore/` to avoid divergent APIs. ### [`website/`](https://github.com/ayfri/kore/tree/master/website) - Purpose: documentation markdown, docs navigation, and frontend rendering. - Docs live in [`website/src/jsMain/resources/markdown/doc`](https://github.com/ayfri/kore/tree/master/website/src/jsMain/resources/markdown/doc). - Edit this module in the same PR as any user-visible behavior change. ## Core patterns to reuse ### Argument wrappers and literals Kore prefers typed wrappers over raw strings. - `Argument` is the base abstraction for command-safe value types. - Generated argument wrappers are the preferred representation for registry references. - Tag variants encode `#namespace:name` conventions. - Literal helpers such as `literal()`, `int()`, and `float()` keep command assembly explicit and safe. This improves autocomplete quality, reduces malformed command strings, and keeps serialization predictable. ### Command wrappers Command APIs generally live as `Function` extension functions. - Build lines with `addLine(command("name", args...))`. - Accept typed arguments directly when possible. - Keep wrappers thin: syntax composition belongs here, domain state belongs in arguments or data classes. This keeps generated commands deterministic and test-friendly. ### Generator pattern Most data-driven resources in `kore` follow one consistent model: 1. A serializable feature class extends [`Generator`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/Generator.kt). 2. The class defines its `resourceFolder`, transient `fileName`, and `generateJson(dataPack)` implementation. 3. [`DataPack`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/DataPack.kt) registers a typed generator list through `registerGenerator()`. 4. A `DataPack` extension function instantiates and registers the feature. 5. The extension returns a typed `*Argument` for later references. Concrete references: - [`Generator.kt`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/Generator.kt) - [`DataPack.kt`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/DataPack.kt) - [`Instrument.kt`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/features/instruments/Instrument.kt) - [`InstrumentTests.kt`](https://github.com/ayfri/kore/blob/master/kore/src/commonTest/kotlin/io/github/ayfri/kore/features/InstrumentTests.kt) For the procedural version of this pattern, use [Contributing: Creating a New Generator](/docs/contributing/creating-a-new-generator). ### Serializer strategy Prefer an existing serializer before creating a new one. Frequently reused serializers, in alphabetical order: - [`EitherInlineSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/EitherInlineSerializer.kt) - [`InlineAutoSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/InlineAutoSerializer.kt) - [`InlinableListSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/InlinableListSerializer.kt) - [`LowercaseSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/LowercaseSerializer.kt) - [`NamespacedPolymorphicSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/NamespacedPolymorphicSerializer.kt) - [`NbtAsJsonSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/NbtAsJsonSerializer.kt) - [`ProviderSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/ProviderSerializer.kt) - [`SinglePropertySimplifierSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/SinglePropertySimplifierSerializer.kt) - [`ToStringSerializer`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/serializers/ToStringSerializer.kt) Decision rule: 1. Check whether an existing serializer already matches the target JSON shape. 2. If not, see whether the model can be expressed with an existing pattern. 3. Only then add a new serializer, with focused tests. New sealed hierarchies serialized with `NamespacedPolymorphicSerializer` (a `type`-discriminated Minecraft polymorphic shape) must not enumerate subtypes by hand or via reflection. Annotate the sealed base with `@GeneratedSealedSerializer`; the `kore-ksp` module's `SealedSerializerProcessor` generates a reflection-free `fooSealedSerializer()` factory at compile time, which the family's serializer object passes to `NamespacedPolymorphicSerializer`'s constructor. The factory returns a `SealedDispatcher` that switches on the subtype's simple name, so serializing one subtype only loads that subtype, not the whole family. ### Per-pack state Helpers that must set something up once per datapack (a `load` function, a shared objective, a runtime object) keep that bookkeeping on the pack with `DataPack.state`, never in a top-level map or set. The state is released with the pack, and two packs sharing a name never see each other's state. ```kotlin private val initializedTimers = DataPackStateKey>("oop.timers") fun DataPack.registerTimer(timer: Timer) { if (state(initializedTimers) { mutableSetOf() }.add(timer.name)) load("timer_${timer.name}_init") { /* ... */ } } ``` `stateOrNull(key)` reads the state without creating it. ## Fast heuristics when you are unsure where a change belongs - **Build, publishing, or versioning behavior** -> `build-logic/` and root Gradle metadata. - **Contributor or user-facing explanation** -> `website/` in the same PR. - **Datapack import output shape** -> `bindings/` pipeline and tests. - **Generated enum or registry wrapper shape** -> `generation/`, then regenerate and update `kore/` consumers. - **New registry-backed DSL resource** -> usually `kore/`, plus generation if a new argument wrapper is required. ## Testing patterns that speed up contribution - Feature tests usually assert emitted JSON payloads and generated command/resource lines. - Most specs build a `dataPack { }` in `commonTest` and never call `generate()`, so they run on the JVM, Node.js, and a headless browser alike (see [Multiplatform Support](/docs/advanced/multiplatform)). The `testDataPack(...)` helper wraps real file/ZIP/JAR generation and is JVM/Node-only - keep specs that call it in `jvmTest`. - Serializer tests should focus on roundtrip behavior and JSON or SNBT shape checks. When you touch generators, the usual path is: model -> `DataPack` registration -> builder entry point -> tests -> docs. ## Documentation pages Contributor pages are easier to maintain when they link to a single source of truth instead of restating the same rules. Required frontmatter keys, in alphabetical order: - `date-created` - `date-modified` - `description` - `keywords` - `nav-title` - `root` - `routeOverride` - `title` An image whose destination starts with `mc:` renders a vanilla texture inline, the path being relative to `textures/` without extension, e.g. `![Furnace](mc:block/furnace_front)`. Use it only where seeing the in-game block, item or GUI sprite tells the reader something (a workstation column, frame styles), never as decoration, and check the path exists in [misode/mcmeta](https://github.com/misode/mcmeta/tree/assets/assets/minecraft/textures) first. Animated textures show their first frame. Bigger in-game renders (dialogs, containers, HUD elements, toasts, trades) live in `components/mc/DocMockups.kt`, built from the vanilla GUI textures in `components/mc/McUi.kt`. Embed one right after the example it renders with `{{{ .components.mc.KitsMenuMockup }}}`, and update it whenever that example changes. Mockups use vanilla GUI coordinates through `gui(pixels)` (GUI scale 2) and `McAt`, so offsets copied from the game's screen classes land on the same pixels. Text is drawn as SVG pixel squares by `McFont.kt`, from glyph bitmaps of the OFL [Minecraft font](https://github.com/IdreesInc/Minecraft-Font), so it stays sharp at any zoom where browser font rendering blurs pixel fonts. `McText` takes a list of `McSpan` runs (color, bold, italic, underlined, strikethrough) for lines mixing styles, like chat messages built from several text components. Keep routes stable, keep navigation intentional, and update entry pages when a new doc should become discoverable. ### How to write a documentation page Content rules that keep the docs consistent and trustworthy: - **Example-first.** Every public feature needs at least one copy-pastable Kotlin snippet. Verify each snippet against the real API (the source or a test) before committing - never invent method names or argument shapes. Use tabs for indentation, matching the Kotlin code style. - **State capabilities, not changes.** Write what the API *does*, not what changed between versions. Avoid changelog phrasing like "now possible" or "new in this release" in body copy; that information belongs in commit messages and release notes, not in a reference page that must read correctly a year later. - **Cross-link inline, not in a pile.** Link related pages from the sentence that mentions the concept (for example, link [Scoreboards](/docs/concepts/scoreboards) the first time you mention scoreboards). Prefer one good inline link over a long trailing list. Keep any `See also` section short - three or four of the most relevant links, not a dump of everything related. - **Link to the Minecraft Wiki for vanilla concepts.** When a page touches a vanilla system (a command, a registry, an NBT structure, a pack format), link the relevant [Minecraft Wiki](https://minecraft.wiki) page. It is high quality and saves Kore from restating vanilla behavior - Kore docs should explain the *Kore* DSL and defer vanilla semantics to the wiki. - **Do not duplicate.** If a concept already has a home page, link it instead of re-explaining it. One source of truth per topic; everything else points at it. ## Why Kore uses these technical choices - **Centralized serializers** keep JSON and NBT output stable across modules and make diffs easier to review. - **Generator-first data-driven features** keep feature additions mechanical instead of bespoke. - **Tests close to feature families** help catch schema drift when Minecraft updates resource definitions. - **Thin command wrappers** preserve command predictability while still covering vanilla syntax broadly. - **Type-safe arguments over raw strings** reduce command regressions and improve IDE discoverability. ## See also - [Contributing: Contributing](/docs/contributing/contributing) - [Contributing: Creating a New Generator](/docs/contributing/creating-a-new-generator) - [Contributing: The Generation Pipeline](/docs/contributing/generation-pipeline) - [Contributing: Workflow](/docs/contributing/contributing-workflow) - [Multiplatform Support](/docs/advanced/multiplatform) --- ## Contributing: Arguments Internals --- root: .components.layouts.MarkdownLayout title: "Contributing: Arguments Internals" nav-title: Arguments Internals description: Contributor-facing overview of Kore's argument system, including Argument, literals, resource-location wrappers, and tag-aware abstractions. keywords: minecraft, datapack, kore, arguments, internals, contributors, resources, tags, commands date-created: 2026-04-21 date-modified: 2026-04-21 routeOverride: /docs/contributing/arguments --- # Contributing: Arguments Internals This page is mostly for contributors and advanced readers who want to understand how Kore models command inputs internally. If you are just using Kore to build a datapack, you usually do **not** need to learn the full argument architecture. The more practical user-facing pages are [Selectors](/docs/concepts/selectors), [Functions](/docs/commands/functions), and the [Cookbook](/docs/guides/cookbook). ## Why the argument layer exists Kore models most command inputs as typed Kotlin values that implement the `Argument` interface. Instead of assembling raw command strings everywhere, builders pass values that know how to serialize themselves through `asString()`. This is one of the core reasons the DSL stays readable and safe to refactor. Typical examples: - `allPlayers()` serializes to `@a` - `Items.DIAMOND_SWORD` serializes to `minecraft:diamond_sword` - a tag wrapper serializes to `#minecraft:planks` - `literal("replace")` serializes to a raw command token ## The pieces contributors should know ### `Argument` is the common contract At the bottom, Kore uses the `Argument` interface as the shared abstraction for command-safe values. This gives the command layer a uniform way to serialize: - selectors - generated resource enums - tag wrappers - literals - some range-like and time-like values When adding a new command wrapper or argument family, prefer plugging into that existing contract rather than passing plain strings around. ### Literal helpers keep raw tokens explicit Literal helpers are used when a command slot expects a keyword-like token instead of a resource wrapper. Common examples include: - `literal("value")` - `bool(true)` / `bool(false)` - `all()` for wildcard score holders - numeric wrappers such as `int(...)` and `float(...)` They make raw command assembly explicit while still fitting the same typed pipeline. ### Selectors are just another argument family Selectors are user-facing, but internally they matter because they show the expected Kore pattern well: build a typed object first, then serialize late. ```kotlin val nearestZombie = allEntities(limitToOne = true) { type = EntityTypes.ZOMBIE sort = Sort.NEAREST } function("cleanup") { kill(nearestZombie) } ``` For selector usage itself, see [Selectors](/docs/concepts/selectors). ### `ResourceLocationArgument` is the main user-facing thing worth knowing Even though most users do not need the architecture details, one concept *is* worth knowing: a large part of Kore is generated as enums or wrappers that implement resource-location-based argument interfaces. This is why you can write things like: - `Items.DIAMOND_SWORD` - `Blocks.STONE` - `SoundEvents.Entity.Player.LEVELUP` - generated predicates, loot tables, functions, and tags Those values serialize to the namespaced IDs Minecraft expects, which means users usually do not need to handwrite `minecraft:...` strings. ```kotlin function("starter_kit") { give(allPlayers(), Items.DIAMOND_SWORD) playSound(SoundEvents.Entity.Player.LEVELUP, PlaySoundMixer.PLAYER, self()) } ``` When documenting Kore for users, this is usually the part to emphasize: generated enums and wrappers already model many Minecraft registries. ### Tag-aware parent interfaces preserve vanilla flexibility Some command slots accept either a concrete resource or a tag. Kore models this with dedicated parent interfaces such as: - `BlockOrTagArgument` - `ItemOrTagArgument` - `FunctionOrTagArgument` When the tag variant is used, it serializes with the vanilla `#namespace:name` syntax. This is an important contributor pattern: if vanilla accepts both a resource and its tag counterpart, Kore usually wants an umbrella interface instead of two unrelated overloads. ## When raw strings are still appropriate Raw strings still make sense when: - a new Minecraft command has not been wrapped yet - a mod command has no Kore DSL yet - you deliberately use `addLine(...)` or `command(...)` Even then, contributors should prefer composing raw command assembly with existing typed arguments rather than reverting to fully handwritten command strings. ## Practical contributor checklist When you add or modify argument-related APIs, check the following: 1. Can this reuse `Argument` instead of a plain `String`? 2. Should the value be a literal helper, a resource wrapper, or a tag-aware parent type? 3. Does vanilla allow both resource and tag forms? 4. Is there already a generated enum or wrapper that should be reused instead of inventing a new string API? 5. Do docs for regular users really need this detail, or should it stay in contributor-facing documentation? ## See also - [Selectors](/docs/concepts/selectors) - user-facing selector builders built on the same typed model - [Functions](/docs/commands/functions) - function builders that consume many argument types - [Architecture and Patterns](/docs/contributing/architecture-and-patterns) - broader contributor-facing project patterns - [Cookbook](/docs/guides/cookbook) - practical usage patterns rather than internals - [Minecraft Wiki: Argument types](https://minecraft.wiki/w/Argument_types) - vanilla command argument reference --- ## Contributing: CI/CD and Releases --- root: .components.layouts.MarkdownLayout title: "Contributing: CI/CD and Releases" nav-title: "CI/CD and Releases" description: Project and Minecraft versioning, CI automation, CodeQL scanning, release naming, and operational release practices for Kore maintainers. keywords: cd, ci, kore, maintenance, minecraft, releases, versioning date-created: 2026-04-10 date-modified: 2026-10-02 routeOverride: /docs/contributing/ci-cd-and-releases --- # Contributing: CI/CD and Releases This page summarizes the maintenance and release process used for Kore. ## Version ownership Kore tracks two coordinated version streams: - Minecraft target version in [`gradle.properties`](https://github.com/ayfri/kore/blob/master/gradle.properties). - Project version in [`build-logic/convention/src/main/kotlin/Project.kt`](https://github.com/ayfri/kore/blob/master/build-logic/convention/src/main/kotlin/Project.kt). Expected increment conventions: - Breaking/major change: `+0.1` - Minor fix/addition: `+0.0.1` Every published module carries the concatenation of the two, `-`, produced once in the `publish-conventions` convention plugin. `kore-gradle-plugin` is included even though a build plugin targets no game version, so one version string covers both the `plugins {}` and the `dependencies {}` block of a consumer build, and a release that only bumps `minecraft.version` still moves every coordinate forward. ## Release naming pattern Observed release tags combine project and Minecraft targets, for example: - `2.0.0-1.21.11` - `1.42.1-1.21.11` - `1.41.1-1.21.11-rc3` This pattern keeps compatibility intent visible directly in release identifiers. ## Minecraft update flow For update cycles: 1. Add/update tests for new snapshot/release behavior. 2. Update project and Minecraft versions. 3. Run relevant module tests. 4. Update docs for API or behavior changes. ## Website release metadata behavior Website build logic includes GitHub release fetching to generate website-side release metadata. Maintainer notes: - `GITHUB_TOKEN` helps avoid API limits and improves reliability. - Generated outputs are artifacts, not hand-maintained source. ## GitHub Actions automation overview Current repository automation is split across dedicated workflows under `.github/workflows`: - `ci.yml`: runs `./gradlew testAll` on pushes and pull requests targeting `master`. `testAll` is a root task defined in the `kotest-conventions` convention plugin that aggregates every Kotlin Multiplatform module's `allTests` task (JVM, Node.js, and browser via Karma) - see [Multiplatform Support](/docs/advanced/multiplatform) for what runs where. Pull requests run on Ubuntu only; `master` additionally runs the Windows matrix entry. - `codeql.yml`: runs GitHub CodeQL analysis for `actions` and `java-kotlin` on pushes, pull requests, manual dispatch, and a weekly schedule. - `publish.yml`: performs the manual release publication flow, to the Central Portal for every module and to the Gradle Plugin Portal for `kore-gradle-plugin`. - `publish-snapshot.yml`: publishes snapshot artifacts from `master`. ### CI build tuning `jsBrowserTest` tasks are skipped unless `-Pkore.jsBrowserTests=true` is passed. Browser and Node.js execute the same JS IR, so the Karma round-trip costs startup time without adding coverage; CI passes the flag on `master` only. The flag drives `onlyIf` rather than `enabled`, because a disabled task stops contributing its npm dependencies and both modes have to agree on `kotlin-js-store/package-lock.json`. That lock is produced by npm - the Kotlin Gradle plugin uses npm instead of Yarn here, the direction the plugin itself is heading in ([KT-84662](https://youtrack.jetbrains.com/issue/KT-84662)). Its contents depend on which tasks are in the graph, so regenerate it by deleting the file and running `./gradlew testAll`, not with `kotlinUpgradePackageLock` alone. Test reports are uploaded as artifacts on failure only. `gradle/actions/setup-gradle` publishes a Build Scan for every run, which is the fastest way to see where wall-clock time went. CodeQL is intentionally scoped to the meaningful code in this repository: - `actions` covers the GitHub workflow files themselves. - `java-kotlin` covers the Gradle/Kotlin codebase. The scheduled CodeQL run uses GitHub Actions cron syntax in UTC and is set to once per week to keep regular security coverage without adding noise to every day. ## Commit message conventions - `chore(minecraft): Increase Minecraft version to X.Y.Z.` - `chore(project): Increase project version to X.Y.Z.` - `feat(code): Update project to Minecraft version X.Y.Z.` Consistent messages improve changelog scanning and release auditability. ## Where to start for maintainers - For version bumps: start with test updates, then change version files, then docs. - For release issues: check module labels and recent update issues for similar patterns. - For contribution workflow details: use [Contributing: Workflow](/docs/contributing/contributing-workflow). ## See also - [Version Matrix](/docs/home#version-matrix) - Kore-to-Minecraft version table generated from GitHub releases at each website build. - [Contributing](/docs/contributing/contributing) - [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns) - [Contributing: Workflow](/docs/contributing/contributing-workflow) - [Multiplatform Support](/docs/advanced/multiplatform) --- ## Contributing to Kore --- root: .components.layouts.MarkdownLayout title: Contributing to Kore nav-title: Contributing description: Entry point for contributors who want to work on Kore architecture, workflows, and project quality. keywords: architecture, contributing, kore, patterns, quality, workflow date-created: 2026-04-10 date-modified: 2026-10-02 routeOverride: /docs/contributing/contributing --- # Contributing to Kore This page is the *entry point* for contributors who do not know the codebase yet. Use it to identify the right guide before touching code, tests, or documentation. ## Choose the right guide - **Add a new data-driven feature:** read [Contributing: Creating a New Generator](/docs/contributing/creating-a-new-generator) before editing `kore`. - **Add or update DSL features:** start with [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns), then use [Contributing: Workflow](/docs/contributing/contributing-workflow). - **Add or fix a generated enum/registry/argument type:** read [Contributing: The Generation Pipeline](/docs/contributing/generation-pipeline). - **Prepare an issue or a pull request:** go straight to [Contributing: Workflow](/docs/contributing/contributing-workflow). - **Ship a release or update versions:** use [Contributing: CI/CD and Releases](/docs/contributing/ci-cd-and-releases). ## Repository map at a glance - `bindings/`: datapack importer and Kotlin bindings code generation. - `build-logic/`: shared Gradle conventions and project metadata. - `generation/`: Minecraft source-data processing and generated Kotlin/resource pipelines. - `helpers/`: optional higher-level helpers built on top of the core DSL. - `kore/`: core DSL, arguments, commands, serializers, and generators. - `oop/`: object-oriented abstractions layered on top of Kore primitives. - `website/`: documentation content, docs navigation, and generated doc indexes. ## Contribution principles - Keep generated files generated: fix the generator or source pipeline, not `build/generated/...` or `kore/src/commonMain/kotlin/io/github/ayfri/kore/generated`. - Prefer existing serializers, typed arguments, and command helpers before introducing new abstractions. - Ship tests and docs with behavior changes, especially in `bindings/` and `kore/`. - Stay within one clear module boundary when possible; use [Architecture and Patterns](/docs/contributing/architecture-and-patterns) to decide where a change belongs. ## Suggested first pass for a newcomer 1. Read [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns) to understand module boundaries. 2. Read [Contributing: Workflow](/docs/contributing/contributing-workflow) to understand tests, docs, and PR expectations. 3. Open one existing feature in `kore` end to end: feature class, `DataPack` registration, tests, then docs. 4. If the change is data-driven, mirror the closest feature with [Contributing: Creating a New Generator](/docs/contributing/creating-a-new-generator). --- ## Contributing: Workflow --- root: .components.layouts.MarkdownLayout title: "Contributing: Workflow" nav-title: "Contributing Workflow" description: End-to-end workflow for opening issues, implementing changes, validating them, and preparing pull requests in Kore. keywords: contributing, docs, issues, kore, pull-request, quality, tests, workflow date-created: 2026-04-10 date-modified: 2026-10-02 routeOverride: /docs/contributing/contributing-workflow --- # Contributing: Workflow This page is the *process reference* for changes in Kore. Use it when you already know **what** you want to change and need to validate **how** to carry that change from issue to PR. ## 1) Qualify the issue or proposal first Before writing code, make sure the scope is explicit. If you are opening an issue: - Pick the closest template in [`.github/ISSUE_TEMPLATE`](https://github.com/ayfri/kore/tree/master/.github/ISSUE_TEMPLATE). - Search for duplicates first. - Include a minimal reproduction or explicit reproduction steps. - Include expected behavior, actual behavior, and the Kore + Minecraft versions you tested. If you are starting from an existing issue or drafting a PR directly: - Keep the first pass focused on one behavior change. - Pick the narrowest module scope possible. - Read one similar merged change before adding new abstractions. Useful scope labels when triaging work: | Scope | Use it for | |-------------------|------------------------------------------------------| | `bindings` | Datapack importer and generated bindings output | | `dsl` | Command DSL and data-driven `kore` APIs | | `generation` | Source-data processing and code generation pipelines | | `helpers` / `oop` | Higher-level APIs built on top of the core DSL | | `update` | Minecraft version tracking and update work | | `website` | Documentation content and docs site behavior | ## 2) Trace the change before editing Before implementation: - Confirm the target module in [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns). - Identify symbol usages and nearby call paths. - Reuse an existing helper, serializer, or feature pattern when one already exists. This is the fastest way to avoid duplicate APIs and hidden coupling. If the change is a new data-driven feature in `kore`, use [Contributing: Creating a New Generator](/docs/contributing/creating-a-new-generator) instead of inventing a new shape from scratch. ## 3) Implement with tests and docs in the same pass For `bindings` and `kore`, tests are expected alongside code changes. Test locations, per module (all four modules are Kotlin Multiplatform - see [Multiplatform Support](/docs/advanced/multiplatform)): - `commonTest` for specs that build a `dataPack { }` in memory and assert JSON/command output; these run on the JVM, Node.js, and a headless browser. - `jvmTest` for specs that need real file I/O (`generate()`, `generateZip()`) or a JVM-only dependency. For example, [`bindings/src/commonTest/kotlin/io/github/ayfri/kore/bindings`](https://github.com/ayfri/kore/tree/master/bindings/src/commonTest/kotlin/io/github/ayfri/kore/bindings) and [`kore/src/commonTest/kotlin/io/github/ayfri/kore`](https://github.com/ayfri/kore/tree/master/kore/src/commonTest/kotlin/io/github/ayfri/kore). At minimum, validate: - Output behavior for commands or generated resources. - Regression behavior for bugs. - Serialization or deserialization shapes when JSON/NBT is involved. Any user-visible behavior change should update docs in [`website/src/jsMain/resources/markdown/doc`](https://github.com/ayfri/kore/tree/master/website/src/jsMain/resources/markdown/doc). Documentation updates usually include: - A usage-oriented example when the feature is public. - A migration note or caveat when behavior changed. - Updated internal links when contributor navigation should change. ## 4) Validate locally before opening the PR Run the checks that match the modules you changed. Typical commands: - `./gradlew :bindings:jvmTest` for `bindings` changes, fast JVM-only iteration. - `./gradlew :kore:jvmTest` for core DSL changes, fast JVM-only iteration. - `./gradlew :oop:jvmTest` for OOP module changes, fast JVM-only iteration. - `./gradlew :helpers:jvmTest` for helpers module changes, fast JVM-only iteration. - `./gradlew testAll` before opening the PR, to also run every module's Node.js and browser (Karma) test tasks. - Your local website workflow when you touched docs navigation or page structure. Validation checklist: - Docs reflect the final API or DSL shape. - Generated outputs were **not** edited by hand. - Manual lists and registries still follow project ordering conventions. - Tests cover the changed behavior. ## 5) Prepare a reviewable pull request A good Kore PR is easy to review because it stays narrow and links the right context. In the PR body, link: - The docs pages updated in the same PR. - The issue or proposal being solved. - The tests that cover the change. - Any migration impact or behavior caveat for existing users. For the title and commit messages, follow conventional commits. Common examples: - `chore(minecraft): Increase Minecraft version to X.Y.Z.` - `chore(project): Increase project version to X.Y.Z.` - `feat(code): Update project to Minecraft version X.Y.Z.` ## 6) Frequent review blockers - Editing generated output instead of updating the generator or source pipeline. - Mixing refactors and feature behavior changes in one large PR. - Re-implementing a serializer or helper that already exists. - Shipping an API change without matching documentation. ## See also - [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns) - [Contributing: Contributing](/docs/contributing/contributing) - [Contributing: Creating a New Generator](/docs/contributing/creating-a-new-generator) - [Contributing: CI/CD and Releases](/docs/contributing/ci-cd-and-releases) - [Multiplatform Support](/docs/advanced/multiplatform) --- ## Contributing: Creating a New Generator --- root: .components.layouts.MarkdownLayout title: "Contributing: Creating a New Generator" nav-title: "Creating a New Generator" description: Step-by-step contributor guide for adding a new data-driven generator to Kore, from model and registration to tests and documentation. keywords: contributing, datapack, generator, kore, patterns, tests date-created: 2026-04-15 date-modified: 2026-10-02 routeOverride: /docs/contributing/creating-a-new-generator --- # Contributing: Creating a New Generator This page is the end-to-end recipe for adding a new data-driven generator in `kore`. The examples below use a *fictional* `custom_reward` resource so the shape stays easy to transpose to real Minecraft schemas. ## 1) Start from the closest existing feature Before creating files, pick the nearest generator already in Kore and mirror its structure. Good references, in alphabetical order: - [`DamageType`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/features/damagetypes/DamageType.kt) - [`Instrument`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/features/instruments/Instrument.kt) - [`PaintingVariant`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/features/paintingvariant/PaintingVariant.kt) What you want to mirror: - `DataPack` registration. - `DataPack` extension function. - `Generator` inheritance and path behavior. - Package layout. - Test shape. If the feature needs a brand-new registry argument type, add it through the generation pipeline first - see [Contributing: The Generation Pipeline](/docs/contributing/generation-pipeline). Do **not** hand-edit `kore/src/commonMain/kotlin/io/github/ayfri/kore/generated` or `build/generated/...`. ## 2) Create the feature class Place the feature in the matching package under `kore/src/commonMain/kotlin/io/github/ayfri/kore/features/...`. ```kotlin package io.github.ayfri.kore.features.customrewards import io.github.ayfri.kore.DataPack import io.github.ayfri.kore.Generator import io.github.ayfri.kore.arguments.chatcomponents.ChatComponents import io.github.ayfri.kore.arguments.chatcomponents.textComponent import io.github.ayfri.kore.generated.arguments.types.LootTableArgument import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @Serializable data class CustomReward( @Transient override var fileName: String = "starter_kit", var displayName: ChatComponents = textComponent(), var lootTable: LootTableArgument, var cooldown: Int = 0, ) : Generator("custom_reward") { override fun generateJson(dataPack: DataPack) = dataPack.jsonEncoder.encodeToString(this) } ``` Checklist for the class itself: - Extend [`Generator`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/Generator.kt) with the correct `resourceFolder`. - Keep `generateJson(dataPack)` thin and delegate to the shared encoder when the structure is straightforward. - Mark `fileName` as `@Transient`. - Make the feature `@Serializable`. - Only override `getPathFromDataDir(...)` when the resource path is genuinely special. ## 3) Register the generator in `DataPack` Add the new list in [`DataPack.kt`](https://github.com/ayfri/kore/blob/master/kore/src/commonMain/kotlin/io/github/ayfri/kore/DataPack.kt), in alphabetical order with the other registered generators. ```kotlin val customRewards = registerGenerator() ``` This is what makes the feature participate in the normal datapack generation lifecycle. ## 4) Add the `DataPack` extension function The extension function is the public builder entry point. ```kotlin fun DataPack.customReward( fileName: String = "starter_kit", lootTable: LootTableArgument, displayName: ChatComponents = textComponent(), cooldown: Int = 0, init: CustomReward.() -> Unit = {}, ): CustomRewardArgument { val customReward = CustomReward(fileName, displayName, lootTable, cooldown).apply(init) customRewards += customReward return CustomRewardArgument(fileName, customReward.namespace ?: name) } ``` Keep it consistent with the rest of Kore: - Add the instance to the registered list. - Apply `init` last. - Instantiate the feature once. - Return the matching typed argument. If the resource can be referenced by tags, preserve the corresponding `*OrTagArgument` pattern as well. ## 5) Reuse serializers and argument types Before adding custom serialization logic, check [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns). In practice, the decision order is: 1. Reuse an existing serializer. 2. Reshape the model if that makes an existing serializer work. 3. Add a new serializer only when the first two options fail. This keeps JSON output aligned with the rest of Kore instead of creating one-off conventions. ## 6) Add tests immediately Generator changes in `kore` should ship with targeted tests under [ `kore/src/commonTest/kotlin/io/github/ayfri/kore`](https://github.com/ayfri/kore/tree/master/kore/src/commonTest/kotlin/io/github/ayfri/kore). For a simple resource, the test usually follows this shape: ```kotlin package io.github.ayfri.kore.features import io.github.ayfri.kore.DataPack import io.github.ayfri.kore.assertions.assertGeneratorsGenerated import io.github.ayfri.kore.assertions.assertsIs import io.github.ayfri.kore.features.customrewards.customReward import io.github.ayfri.kore.generated.LootTables import io.github.ayfri.kore.utils.pretty import io.github.ayfri.kore.utils.testDataPack import io.kotest.core.spec.style.FunSpec fun DataPack.customRewardTests() { customReward("starter_kit", lootTable = LootTables.Chests.SPAWN_BONUS_CHEST) { cooldown = 200 } customRewards.last() assertsIs """ { "display_name": "", "loot_table": "minecraft:chests/spawn_bonus_chest", "cooldown": 200 } """.trimIndent() } class CustomRewardTests : FunSpec({ test("custom reward") { testDataPack("custom_reward") { pretty() customRewardTests() }.apply { assertGeneratorsGenerated() generate() } } }) ``` At minimum, validate: - Edge cases or regressions specific to the feature. - The emitted JSON shape. - The generated file path. ## 7) Update documentation in the same PR Every user-visible feature needs docs under [`website/src/jsMain/resources/markdown/doc`](https://github.com/ayfri/kore/tree/master/website/src/jsMain/resources/markdown/doc). Typical documentation work includes: - A contributor note when the implementation pattern is non-obvious. - A user-facing usage page when the feature becomes public API. - Updated links from entry pages when navigation changes. For page placement, routing, and required frontmatter keys, reuse the documentation contract described in [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns). ## 8) Validate before opening the PR For `kore` changes, the baseline check is: ```bash ./gradlew :kore:test ``` Before opening the PR, verify that: - `DataPack` registration remains alphabetically ordered. - Docs reflect the final API shape. - No generated file was hand-edited. - Tests cover the feature. ## 9) Done checklist You are usually done when the feature includes all of the following: - A `DataPack` extension function returning the typed argument. - A `DataPack` registration entry. - A `Generator` subclass with the correct path behavior. - Documentation updates in `website/.../markdown/doc`. - Tests in `kore/src/commonTest`. ## See also - [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns) - [Contributing: Contributing](/docs/contributing/contributing) - [Contributing: The Generation Pipeline](/docs/contributing/generation-pipeline) - [Contributing: Workflow](/docs/contributing/contributing-workflow) --- ## Contributing: The Generation Pipeline --- root: .components.layouts.MarkdownLayout title: "Contributing: The Generation Pipeline" nav-title: "Generation Pipeline" description: How Kore's generation module downloads Minecraft source data and codegens enums and argument types. Covers registries, data sources, caching, and the generator extension API for contributors. keywords: arguments, codegen, contributing, generation, kore, registries, minecraft data, enum generation, kotlin codegen, datapack generator internals date-created: 2026-07-01 date-modified: 2026-10-02 routeOverride: /docs/contributing/generation-pipeline --- # Contributing: The Generation Pipeline `generation/` is a standalone Kotlin/JVM app, not a library other modules depend on. Running it writes Kotlin files straight into `kore/src/commonMain/kotlin/io/github/ayfri/kore/generated` - it is the *only* thing allowed to touch that folder. The folder is gitignored: every `kore` compilation regenerates it through the cacheable `:generation:generateSources` task. ## What it does `generation/src/main/kotlin/Main.kt` runs, in order: 1. Clear `kore/src/commonMain/kotlin/io/github/ayfri/kore/generated` and (with `--reload-cache`) the download cache. 2. Download datapacks, the default datapack version, gamerules and item component types. 3. Run every simple generator ([`launchAllSimpleGenerators`](https://github.com/ayfri/kore/blob/master/generation/src/main/kotlin/generators/generators.kt)) - lists and registries that become enums or enum trees. 4. Run the argument type generator ([`launchArgumentTypeGenerators`](https://github.com/ayfri/kore/blob/master/generation/src/main/kotlin/generators/arguments.kt)) - registries that become typed `*Argument` / `*OrTagArgument` / `*TagArgument` interfaces. 5. Write the resolved Minecraft version into the generated package. Source data is fetched from the [`PixiGeko/Minecraft-generated-data`](https://github.com/PixiGeko/Minecraft-generated-data) GitHub repo, pinned to the `minecraft.version` in `gradle.properties`. Downloads are cached under `generation/build/cache`; pass `--reload-cache` to force a re-download after a Minecraft version bump. ## Adding a new generated list or registry Most new registries only need an entry in [`generators/generators.kt`](https://github.com/ayfri/kore/blob/master/generation/src/main/kotlin/generators/generators.kt), inside `lists` (plain resource lists, e.g. `loot_table`) or `registries` (vanilla registries, e.g. `item`): ```kotlin gen("TradeSets", "trade_set") ``` `gen(name, fileName) { ... }` ([`generators/Generator.kt`](https://github.com/ayfri/kore/blob/master/generation/src/main/kotlin/generators/Generator.kt)) takes the enum name and the upstream file name, and exposes a small builder for the common cases: - `argumentClassName` - override the generated `*Argument` type name when it should differ from `name` (for example `"Model M"` routes the argument to `arguments/types/resources` instead of the generated tree - see the `M`-suffix rule in [Contributing: Creating a New Generator](/docs/contributing/creating-a-new-generator)). - `transform { ... }` - strip a suffix/prefix from raw entries (`.json`, `.ogg`, `minecraft:`, ...). - `enumTree` / `separator` - force or configure a path-based enum tree instead of a flat enum, for entries that contain `/`. - `extractEnums(...)` - split out a sub-enum from entries sharing a prefix. - `tagsParents(...)` / `subInterfacesParents(...)` - map resource folders or sub-namespaces to their parent argument type, used by the `Tags` and `Textures` generators. Whether an entry lands in `lists` or `registries` only changes which upstream `.txt` folder it reads from (`custom-generated/lists/...` vs `custom-generated/registries/...`); both feed the same enum/enum-tree codegen. Registries that need a typed argument (referenced from other DSL builders, taggable, etc.) are handled separately by `launchArgumentTypeGenerators()` in [`generators/arguments.kt`](https://github.com/ayfri/kore/blob/master/generation/src/main/kotlin/generators/arguments.kt) - it reads the registry list straight from the datapack report (`minecraft-generated/reports/datapack.json`) rather than from `generators.kt`, so a vanilla registry usually needs no manual entry at all. `additionalTypes`/`ignoreList` in that file are only for registries the report doesn't expose or that already have a hand-written model (`block`, `item`, `tag`). ## Running it ``` ./gradlew :generation:run ``` Add `--args='--reload-cache'` after bumping the Minecraft version to invalidate the download cache. Once it finishes, read the new files in `kore/src/commonMain/kotlin/io/github/ayfri/kore/generated` (enums and `Argument` types alike) to confirm the new registry produced the expected enum/argument shape before writing any DSL code against it. ## See also - [Contributing: Architecture and Patterns](/docs/contributing/architecture-and-patterns) - [Contributing: Creating a New Generator](/docs/contributing/creating-a-new-generator) --- # Home ## Kore - Type-Safe Minecraft Datapack Generator --- root: .components.layouts.MarkdownLayout title: Kore - Type-Safe Minecraft Datapack Generator nav-title: Home description: Kore is a Kotlin DSL datapack generator for Minecraft Java Edition. Create datapacks with type-safe code instead of writing JSON and MCFunction by hand. Open-source and production-ready. keywords: minecraft datapack generator, datapack maker, minecraft data pack creator, kotlin datapack, kore, minecraft datapack dsl, datapack development, minecraft java edition, mcfunction generator, datapack library date-created: 2024-04-06 date-modified: 2026-10-07 routeOverride: /docs/home position: 0 --- # Kore **Welcome to the Kore wiki!** Kore is a Kotlin library for building Minecraft datapacks with a concise, type-safe Kotlin DSL. It focuses on readable builders, stable generation of datapack JSON, and tight integration with vanilla concepts (functions, loot tables, predicates, worldgen, ...). Coming from raw datapacks? Read [Why Kore](/docs/guides/why-kore) or the migration-focused [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore). ## Quick start - **Getting started**: Check out the [Getting Started](/docs/getting-started) guide for a step-by-step introduction to creating your first datapack. - **Prerequisites**: Java 25 (JDK 25) and a Kotlin 2.4+ build environment. - **Starter template**: use the `Kore Template` for a ready-to-run project: [ `Kore Template`](https://github.com/Kore-Minecraft/Kore-Template). - **Build faster**: browse the [Cookbook](/docs/guides/cookbook) for practical patterns you can reuse. - **Try it first**: the [playground](/playground) runs Kore in your browser, with nothing to install. ### Minimal example ```kotlin fun main() { val datapack = dataPack("example") { function("display_text") { tellraw(allPlayers(), textComponent("Hello World!")) } } datapack.generateZip() } ``` ### IDE tooling Kore Assistant brings gutter icons, hovers, and declaration navigation for the Kore DSL straight into your editor: [IntelliJ IDEA](https://plugins.jetbrains.com/plugin/27025-kore-assistant) | [VS Code](https://marketplace.visualstudio.com/items?itemName=ayfri.kore-assistant). ## Jump to a topic {{{ .components.doc.FeatureGrid }}} Looking for something more specific? The full page list, with search, lives in the sidebar on the left, and the [Features](/features) page lists everything Kore covers with in-game previews. ## Installable modules Kore is split into installable modules. Start with `kore`, then add the others depending on the abstractions or tooling you need. All four modules are **Kotlin Multiplatform** (JVM + JS): the same coordinates publish JVM, JS, and common metadata variants. File/ZIP/JAR generation works on the JVM and Node.js; browser JS consumers build the DSL and call `exportAsStrings()` / `generateZipBytes()` instead. See [Multiplatform Support](/docs/advanced/multiplatform) for what runs where. ### `kore` - Core DSL - Build datapacks with the main Kore DSL. - Artifact: `io.github.ayfri.kore:kore:VERSION` - Snapshot builds from each commit on `master`: add `https://central.sonatype.com/repository/maven-snapshots/` and use `VERSION-SNAPSHOT` ### `oop` - Object-oriented gameplay utilities - Add higher-level abstractions for boss bars, cooldowns, entities, game states, scoreboards, spawners, teams, and timers. - Especially useful when several gameplay systems need to exchange data cleanly, such as syncing a `Team` with a boss bar or reusing an `Entity` handle across scoreboards and commands. - Artifact: `io.github.ayfri.kore:oop:VERSION` - Explore: [OOP Utilities](/docs/oop/oop-utilities) ### `helpers` - Utility-focused helpers - Add renderers, raycasts, scheduler utilities, scoreboard math, state delegates, particle helpers, and related utilities. - These helpers complement the core DSL well for advanced text pipelines, reusable state access, geometric particles, or command-heavy math routines. - Artifact: `io.github.ayfri.kore:helpers:VERSION` - Explore: [Helpers Utilities](/docs/helpers/utilities) ### `bindings` - Datapack importer - Import existing datapacks and generate type-safe Kotlin bindings for their functions, resources, and tags. - Artifact: `io.github.ayfri.kore:bindings:VERSION` - Explore: [Bindings](/docs/advanced/bindings) ## Version Matrix One Kore version per stable Minecraft version, the latest tagged pre-release as a separate `Snapshot` row, and the latest continuous `-SNAPSHOT` build from `master` as `Maven Snapshot`. Gradle coordinates link straight to Maven Central. Generated from GitHub releases and the current project version on every website build, so it always reflects the latest published version without manual edits. Each stable Kore version links to its release notes on the [Updates](/updates) page. [Version Support](/docs/guides/version-support) explains how to read these versions and what changes between them. {{{ .components.doc.VersionMatrix }}} ## Contributing to Kore If you want to contribute to Kore itself, start with [Contributing to Kore](/docs/contributing/contributing), the hub for architecture, workflow, issue/PR, and maintainer docs. Useful contributor-facing internals: - [Architecture and Patterns](/docs/contributing/architecture-and-patterns) - module boundaries and recurring implementation patterns. - [Arguments Internals](/docs/contributing/arguments) - how the typed argument layer, resource wrappers, and literals fit together. ## Short tips - Keep builders small and reusable; prefer extracting predicates and modifiers. - Enable `prettyPrint` in [`Configuration`](/docs/guides/configuration) during development for readable JSON. - Reach for [`OOP Utilities`](/docs/oop/oop-utilities) when multiple gameplay features should share the same handles instead of re-building selectors and score names manually. - Use [`Components`](/docs/concepts/components) + [`Predicates`](/docs/data-driven/predicates) together for robust item checks and inventory management. - Reach for [`NBTs`](/docs/concepts/nbts) when you need to build a payload once and reuse it across commands, chat, or predicates. - Use [`Helpers Utilities`](/docs/helpers/utilities) to avoid reimplementing common glue code such as renderers, scheduler patterns, raycasts, or scoreboard-based maths. - Hit a wall? Check [Known Issues](/docs/advanced/known-issues) for known workarounds before filing a bug. ## Need help? - **Discord**: join the [Kore Discord](https://discord.ayfri.com) for questions and to chat with other datapack devs. - **Issues & bugs**: report them on the [GitHub repository](https://github.com/Ayfri/Kore/issues). - **Stuck on setup?**: the [Getting Started](/docs/getting-started) guide has a dedicated Troubleshooting section. - **Read first**: [Kore Hello World](https://ayfri.com/articles/kore-hello-world/), a hands-on intro article. ## Community & source - **Repository**: [Kore](https://github.com/Ayfri/Kore) - **Starter template**: [Kore Template](https://github.com/Kore-Minecraft/Kore-Template) - **LLM-friendly documentation**: [llms.txt](https://kore.ayfri.com/llms.txt) | [llms-full.txt](https://kore.ayfri.com/llms-full.txt) - **AI Agents skills**: [Kore-Skill](https://github.com/Kore-Minecraft/Kore-Skill) (optional skills pack for AI-assisted Kore work) ### Community creations Real projects built with Kore, useful to see the DSL used at full project scale: - [SimplEnergyKore](https://github.com/e-psi-lon/SimplEnergyKore) - [Kore-Bindings](https://github.com/e-psi-lon/Kore-Bindings) - [OreCrops](https://github.com/e-psi-lon/OreCrops) - [realms-map](https://github.com/Aeltumn/realms-maps) - [jumpr](https://github.com/LluisJM/jumpr) To add your project to the list, open an issue on the [repository](https://github.com/Ayfri/Kore/issues) or ping me on [Discord](https://discord.ayfri.com). For hands-on examples, follow the doc pages above - most pages include runnable snippets and links to test cases in the repository. --- # Getting started ## Getting Started with Kore --- root: .components.layouts.MarkdownLayout title: Getting Started with Kore nav-title: Getting Started description: Step-by-step guide to create your first Minecraft datapack with Kore. Set up a Kotlin project, write type-safe commands and functions, generate the datapack, and test in-game. keywords: minecraft datapack tutorial, kore getting started, create datapack with kotlin, minecraft datapack generator tutorial, kore setup guide, kotlin datapack beginner, minecraft function generator, datapack development guide date-created: 2025-08-21 date-modified: 2026-10-02 routeOverride: /docs/getting-started position: 1 --- # Getting Started This guide takes you from zero to a real development workflow with Kore. Instead of stopping at a minimal "hello world," you will build a small but structured datapack, run it in-game, iterate quickly, and learn how to scale your project. This guide targets the JVM, the primary way to build and export a datapack (`generate()` / `generateZip()`). The same calls also work on Node.js; see [Multiplatform Support](/docs/advanced/multiplatform) for that and for the browser-only `exportAsStrings()` / `generateZipBytes()` alternatives. If you already have solid datapack experience and want an architecture-first migration guide, jump to [From Datapacks to Kore](/docs/guides/from-datapacks-to-kore). To try Kore before setting anything up, the [playground](/playground) runs it in your browser and shows the generated pack. ## What you will build By the end of this page, you will have: - A Kotlin project configured for Kore. - A datapack with metadata and multiple functions. - A simple game loop entry point (`load` + user-triggered functions). - A practical local development cycle to edit, regenerate, and test quickly. - A clean starting structure you can keep expanding. ## Prerequisites - [Java 25 (JDK 25)](https://jdk.java.net/25/) or higher, required by both Kore and Minecraft 26.2. - Gradle (wrapper recommended: `./gradlew`). - IntelliJ IDEA (recommended) or another IDE with Kotlin support. - Basic understanding of Minecraft datapacks (helpful but not required). ## Kotlin basics you need before Kore If you are new to Kotlin, do not worry. You only need a small subset to be productive with Kore. ### `val` and `var` - `val` means read-only reference (preferred by default). - `var` means mutable reference (use only when reassignment is needed). ```kotlin val packName = "starter_kore" var buildNumber = 1 buildNumber += 1 ``` ### Functions You declare functions with `fun`. Kore code is mostly function calls inside builders. ```kotlin fun greet(name: String): String { return "Hello, $name" } ``` ### Null safety Kotlin distinguishes nullable (`String?`) and non-null (`String`) types. ```kotlin val maybePlayer: String? = System.getenv("PLAYER_NAME") val displayName = maybePlayer ?: "Player" ``` Use: - `?.` for safe calls, - `?:` for fallback values (Elvis operator). ### Lambdas and builder blocks Kore uses Kotlin lambdas heavily: ```kotlin function("hello") { tellraw(allPlayers(), textComponent("Hello")) } ``` The `{ ... }` block is a lambda, and inside it Kore exposes a DSL context with helper functions. ### Extension functions (very important in Kore projects) Kotlin lets you add functions to existing types without inheritance. This is ideal for structuring large datapacks. ```kotlin fun DataPack.registerWelcome() { function("feature/welcome") { tellraw(allPlayers(), textComponent("Welcome")) } } ``` You can then call `registerWelcome()` inside your `dataPack {}` builder. ## Step 1: Create your project You have two ways to start. ### Option A: Use the Kore Template (recommended) The fastest option is the template repository, which is already configured for Kotlin + Gradle + Kore. 1. Open [Kore Template](https://github.com/Kore-Minecraft/Kore-Template). 2. Click "Use this template" (or clone directly). 3. Open it in IntelliJ IDEA. 4. Let Gradle sync. 5. Run the `main` function in `src/main/kotlin/Main.kt`. ### Option B: Add Kore to an existing Kotlin project If you already have a Kotlin project, add Kore manually. #### Core modules - `kore` - core DSL to generate datapacks. - `oop` - object-oriented gameplay abstractions. - `helpers` - helper utilities built on top of Kore. - `bindings` - experimental importer for existing datapacks. For your first datapack, use only `kore`. #### Gradle Kotlin DSL ```kotlin dependencies { implementation("io.github.ayfri.kore:kore:VERSION") } ``` #### Gradle Groovy DSL ```groovy dependencies { implementation 'io.github.ayfri.kore:kore:VERSION' } ``` #### Snapshot builds (latest unreleased changes) If you want bleeding-edge features: ```kotlin repositories { mavenCentral() maven("https://central.sonatype.com/repository/maven-snapshots/") } dependencies { implementation("io.github.ayfri.kore:kore:VERSION-SNAPSHOT") } ``` #### Kotlin compiler and JVM settings Kore relies on context parameters, which are stable since Kotlin 2.4. Make sure your `build.gradle.kts` contains: ```kotlin kotlin { jvmToolchain(25) } ``` ## Step 2: Write a first useful datapack Instead of a single command, create a tiny but realistic pack with: - metadata (`pack`), - one function for player feedback, - one function for setup behavior. If you are new to Kotlin syntax, read the code like this: - `val datapack = ...` stores the generated datapack object. - `dataPack("starter_kore") { ... }` creates a builder context. - each `function("...") { ... }` block writes one generated `.mcfunction` file. Create `Main.kt`: ```kotlin fun main() { val datapack = dataPack("starter_kore") { pack { description = textComponent("Starter datapack generated with Kore") } function("hello") { tellraw(allPlayers(), textComponent("Hello from Kore")) } function("setup") { tellraw(allPlayers(), textComponent("Setup complete")) } } datapack.generateZip() } ``` Run `main`, then put the generated zip into your world's `datapacks` folder. ## Step 3: Load and test in Minecraft 1. Open your world. 2. Run `/reload`. 3. Trigger the function: ```mcfunction /function starter_kore:hello ``` If everything is correct, chat displays your message. ## Step 4: Use `load {}` as your real entry point In Kore, using `load {}` is usually better than manually naming a startup function and wiring tags yourself. It directly creates and registers a function in `minecraft:load`, which makes your startup flow explicit and less error-prone. Most datapacks become easier to maintain when you separate: - initialization (`load`), - recurring logic (`tick` when needed), - feature functions (your own namespaced functions). Add this structure in Kore by defining dedicated functions and using consistent names: ```kotlin fun main() { val datapack = dataPack("starter_kore") { pack { description = textComponent("Starter datapack generated with Kore") } load("bootstrap") { tellraw(allPlayers(), textComponent("[starter_kore] datapack loaded")) function("feature/give_welcome") } function("feature/give_welcome") { tellraw(allPlayers(), textComponent("Welcome to the world")) } tick("runtime/checks") { // Keep tick lightweight. Route heavy logic to dedicated functions. } } datapack.generate() } ``` With this approach: - `load("bootstrap")` runs once after `/reload`. - `tick("runtime/checks")` runs every game tick. - `function("feature/...")` stays your reusable API surface. - folder-like names keep generated files organized as the project grows. ## Step 5: Organize code before it gets messy A common beginner mistake is placing everything in one `main` function. Prefer small helpers and focused files early. Example using extensions: ```kotlin fun DataPack.registerCoreFunctions() { load("bootstrap") { tellraw(allPlayers(), textComponent("[starter_kore] datapack loaded")) } } fun DataPack.registerFeatureFunctions() { function("feature/give_welcome") { tellraw(allPlayers(), textComponent("Welcome to the world")) } } fun main() { val datapack = dataPack("starter_kore") { pack { description = textComponent("Starter datapack generated with Kore") } registerCoreFunctions() registerFeatureFunctions() } datapack.generateZip() } ``` This pattern scales much better than one giant builder block. ## Step 6: Use a fast development loop During development, your loop should be: 1. Edit Kotlin code. 2. Re-run `main`. 3. Copy or regenerate output into your world datapack folder. 4. In-game: `/reload`. 5. Run the function you are testing. Tips: - Use `.generate()` when you want to inspect generated files locally. - Use `.generateZip()` when you want easy distribution. - Enable pretty JSON in [Configuration](/docs/guides/configuration) when debugging generated resources. ## Step 7: Build an advanced mini-pack with a custom enchantment At this point, create a pack that is closer to production structure: - startup flow with `load`, - runtime flow with regular functions, - one custom data-driven feature (a custom enchantment). Example: ```kotlin fun main() { val datapack = dataPack("starter_kore") { pack { description = textComponent("Starter datapack with a custom enchantment") } // 1) Runtime functions function("feature/welcome") { tellraw(allPlayers(), textComponent("Welcome to starter_kore")) } function("feature/show_vampiric_hint") { tellraw(allPlayers(), textComponent("Try the Vampiric enchantment on a sword")) } // 2) Startup entry point load("bootstrap") { function("feature/welcome") function("feature/show_vampiric_hint") } // 3) Custom enchantment definition (data-driven content) enchantment("vampiric") { description("Vampiric") supportedItems(Tags.Item.SWORDS) primaryItems(Tags.Item.Enchantable.SHARP_WEAPON) weight = 2 maxLevel = 3 minCost(20, 15) maxCost(50, 15) anvilCost = 8 slots(EquipmentSlot.MAINHAND) effects { // Bonus damage that scales by level damage { add(linearLevelBased(1, 1)) } // Chance to heal attacker on hit postAttack { on(PostAttackSpecifier.ATTACKER, PostAttackSpecifier.ATTACKER) { applyMobEffect(Effects.INSTANT_HEALTH) { minAmplifier(0) maxAmplifier(0) minDuration(1) maxDuration(1) requirements { randomChance(enchantmentLevel(linearLevelBased(0.08f, 0.08f))) } } } } } } } datapack.generateZip() } ``` What this gives you: - a generated `data/starter_kore/enchantment/vampiric.json`, - startup player feedback via `minecraft:load`, - a clear split between command functions and data-driven definitions. To validate in-game: 1. Regenerate your datapack. 2. Put it in your world. 3. Run `/reload`. 4. Check logs/chat for load output. 5. Test enchantment behavior with commands or controlled test scenarios. ## Going beyond a minimal datapack Once your first commands work, move to data-driven features: - Add recipes, loot tables, predicates, and tags. - Create multiple function entry points per feature. - Separate "bootstrap" code from "gameplay" code. - Reuse helper functions to avoid duplicated command blocks. Good expansion ideas after the custom enchantment: - A welcome system with per-player conditions. - A starter kit function with basic inventory setup. - A scoreboard-based progression mechanic. - A custom recipe that complements your enchantment. - A balancing pass for enchantment costs, rarity, and max level. ## Kotlin tips that matter specifically in Kore projects - Prefer `val` by default; immutable declarations reduce accidental state issues. - Use extension functions on `DataPack` to keep your DSL composable. - If your IDE imports the wrong DSL symbol, qualify temporarily with `this.` in the builder scope, then fix the import. - Keep function names and file-like paths consistent (`feature/x`, `system/y`) to keep generated output predictable. - Declare each function path once: two functions sharing a path with different bodies make generation throw. - Prefer `load {}` and `tick {}` builders over manual tag wiring for standard lifecycle hooks. ### Quick Kotlin learning resources - [Kotlin documentation](https://kotlinlang.org/docs/home.html) - [Kotlin null safety](https://kotlinlang.org/docs/null-safety.html) - [Kotlin extensions](https://kotlinlang.org/docs/extensions.html) - [Learn X in Y Minutes - Kotlin](https://learnxinyminutes.com/kotlin/) - [Kotlin Playground](https://play.kotlinlang.org/) ## Troubleshooting ### Unresolved Kore DSL symbols - Check that the dependency exists in the correct module. - Confirm you're on Kotlin 2.4 or higher (context parameters are stable there). - Refresh/sync Gradle in your IDE. ### Java/Kotlin toolchain errors - Confirm JDK 25 is installed and selected by Gradle. - Confirm `jvmToolchain(25)` is configured. ### Datapack generates but does not work in-game - Confirm the namespace/function path in `/function`. - Run `/reload` after every regeneration. - Check the world folder path and datapack placement. - Open logs and verify no JSON or command syntax error is reported. ## Recommended learning path after this guide Start here next: 1. [Creating a Datapack](/docs/guides/creating-a-datapack) 2. [Runtime Logic](/docs/concepts/runtime-logic) - how Kotlin `val`/`if`/`for` map to in-game scoreboards, storage, and `execute` 3. [Functions](/docs/commands/functions) 4. [Commands](/docs/commands/commands) 5. [Selectors](/docs/concepts/selectors) 6. [Cookbook](/docs/guides/cookbook) 7. [Recipes](/docs/data-driven/recipes) 8. [Enchantments](/docs/data-driven/enchantments) For the full index, see [Home](/docs/home), and for a tour of everything Kore covers, the [Features](/features) page. ## See also - [Kore Hello World](https://ayfri.com/articles/kore-hello-world/) - [Kore on GitHub](https://github.com/Ayfri/Kore) - [Kore on Maven Central](https://central.sonatype.com/search?q=io.github.ayfri.kore) - [Kore on Discord](https://discord.ayfri.com) - [Minecraft Wiki: Datapack](https://minecraft.wiki/w/Data_pack) ---