Skip to content

2.13.1-26.2

Latest

Choose a tag to compare

@Ayfri Ayfri released this 24 Aug 10:20
· 3 commits to master since this release
Immutable release. Only release title and notes can be modified.
46e8730

Welcome to Kore 26.2!

kore-2 13 1-26 2-banner

Kore 26.2 is the cycle where the library stopped being JVM-only, and where most of the data-driven DSL got a serious retype.

It started on the tail of 26.1 with a Kotlin 2.4 upgrade and a multiplatform port, then turned into a long pass over worldgen and predicates: scoped builders everywhere, vanilla names everywhere, and a lot of fields fixed to actually match the JSON the game reads. On top of that, the new 26.2 content landed as it came: sulfur cubes, speleothems, geysers, and the usual batch of item component changes.

This cycle is also the most breaking one in a while. Nearly every rename here exists because the old API let you write something the game refuses, or because a global function polluted autocomplete in places it had no business being. The migration is mostly mechanical, and the examples below show the before and after.

What's new?

The big stuff this cycle was:

  • Multiplatform Support: kore, oop, helpers and bindings now target JVM and JS (browser + Node.js) at the same coordinates, powered by a pure-Kotlin ZipReader/ZipWriter and a PlatformIO abstraction (OPFS in the browser). org.joml and kotlin-reflect are gone as dependencies.
  • Kotlin 2.4: no more -Xcontext-parameters compiler argument, and java.util.UUID is replaced by the stable multiplatform kotlin.uuid.Uuid.
  • Worldgen: density functions, surface rules, carvers, processor lists, template pools, structures, block predicates, state providers, vertical anchors and height providers all moved to scoped builders, so autocomplete only offers what is valid in each context. New Providers and Carvers pages came with it.
  • Predicates: every sub-predicate now carries its vanilla *Predicate name, serialization is checked against vanilla-mcdoc, item sub-predicates moved to the component matchers, and score holders became a sealed ScoreProvider.
  • 26.2 content: sulfur_cube_archetype with explosion, contact damage, knockback and sounds, the speleothem rename, geyser particles, the sequence, template, weighted_random_selector and random_patch configured features, and the interval_select density function.

Code examples

Here is what the biggest 26.2 changes look like in practice.

Multiplatform datapacks

kotlin {
	jvm()
	js { browser(); nodejs() }
}

// works on every target
val zipBytes: ByteArray = pack.generateZipBytes()

// JVM/Node only, real filesystem
val datapack = exploreDatapackZip(zipBytes, "uploaded_pack.zip")

Scoped worldgen builders

Configured features, carvers and surface rules are built inside their own scope instead of loose global functions, so autocomplete only offers what is valid where you are.

// Before: global functions, the name is a positional argument of an outer call
configuredFeature("ore_iron", ore(size = 9, discardChanceOnAirExposure = 0.0) {
	targets(target(tagMatch(BlockTags.STONE_ORE_REPLACEABLES), blockState(Blocks.IRON_ORE)))
})
configuredFeature("bamboo_feature", bamboo(probability = 0.2))

// After: one scope, one method per feature type, the name is the first parameter
configuredFeatures {
	ore("ore_iron", size = 9, discardChanceOnAirExposure = 0.0) {
		targets { target(blockState(Blocks.IRON_ORE)) { target = tagMatch(BlockTags.STONE_ORE_REPLACEABLES) } }
	}
	bamboo("bamboo_feature", probability = 0.2)
}
// Before: surface rules built as nested sequence(condition(...), ...) calls
noiseSettings("overworld") {
	surfaceRule = sequence(
		condition(biome(Biomes.DESERT), block(Blocks.SAND)),
		condition(stoneDepthFloor(offset = 0, addSurfaceDepth = false, secondaryDepthRange = 0), block(Blocks.GRASS_BLOCK)),
		block(Blocks.STONE),
	)
}

// After: a surfaceRules { } scope, condition() takes a block lambda
noiseSettings("overworld") {
	surfaceRules {
		condition(biomes(Biomes.DESERT)) { block(Blocks.SAND) }
		condition(stoneDepth(Surface.FLOOR, addSurfaceDepth = false, secondaryDepthRange = 0)) { block(Blocks.GRASS_BLOCK) }
		block(Blocks.STONE)
	}
}
// Before: carvers split between air and liquid lists on the biome
biome("my_biome") {
	carvers(air = listOf(ConfiguredCarvers.CAVE), liquid = listOf(ConfiguredCarvers.CANYON))
}

// After: a configuredCarvers { } scope, and one flat carvers list on the biome
configuredCarvers {
	cave("my_cave") {
		probability = 0.15
		y = uniformHeightProvider(aboveBottom(8), absolute(180))
		replaceable(Blocks.STONE, Blocks.DIRT, Tags.Block.BASE_STONE_OVERWORLD)
	}
}
biome("my_biome") {
	carvers(ConfiguredCarvers.CANYON, ConfiguredCarvers.CAVE)
}
// Before: positional predicates, offset shared by the whole filter
configuredFeaturesBuilder.spike("test_spike", canPlaceOn = Solid, canReplace = Solid, state = blockStateStone())
blockPredicateFilter(allOf { matchingBlockTag(tag = Tags.Block.DIRT) })

// After: scoped blocks, offset moved inside each predicate
configuredFeaturesBuilder.spike("test_spike", state = blockStateStone()) {
	canPlaceOn { solid() }
	canReplace { solid() }
}
blockPredicateFilter { predicate { matchingBlockTag(Tags.Block.DIRT) { offset(0, -1, 0) } } }
// Before: template pool and processor list entries added through entry()/add()
templatePool("every_element") {
	fallback = TemplatePools.Village.Plains.TOWN_CENTERS
	entry(feature(PlacedFeatures.PILE_HAY), weight = 3, projection = Projection.TERRAIN_MATCHING)
}

// After: one flat builder method per element type, weight/projection as named params
templatePool("every_element") {
	fallback = TemplatePools.Village.Plains.TOWN_CENTERS
	feature(PlacedFeatures.PILE_HAY, weight = 3, projection = Projection.TERRAIN_MATCHING)
	single(Structures.EndCity.BRIDGE_GENTLE_STAIRS, ProcessorLists.HOUSING, weight = 2) {
		overrideLiquidSettings = LiquidSettings.IGNORE_WATERLOGGING
	}
}

processorList("every_processor") {
	blockRot(0.75, Blocks.STONE_BRICKS, Tags.Block.STAIRS)
	capped(6, Nop)
	gravity(HeightMap.OCEAN_FLOOR, offset = -1)
}

World presets keyed by dimension id

Dimensions are keyed by their id instead of their type, so two dimensions can share a type, and generator builders set generator themselves.

// Before: keyed by dimension type, generator assigned by hand, one type usable only once
worldPreset("my_preset") {
	dimension(DimensionTypes.OVERWORLD) {
		generator = noiseGenerator(NoiseSettings.OVERWORLD, multiNoise(BiomePresets.OVERWORLD))
	}
}
dp.noise("hills", firstOctave = -5, amplitudes = listOf(1.0, 0.5, 0.25))

// After: keyed by dimension id, the builder sets generator itself, amplitudes is variadic
worldPreset("my_preset") {
	dimension(Dimensions.OVERWORLD, DimensionTypes.OVERWORLD) {
		noiseGenerator(NoiseSettings.OVERWORLD, multiNoise(BiomePresets.OVERWORLD))
	}
	dimension(DimensionArgument("mining", "test"), DimensionTypes.OVERWORLD_CAVES) {
		noiseGenerator(NoiseSettings.CAVES, multiNoise(BiomePresets.OVERWORLD))
	}
}
dp.noise("cave/entrance") { firstOctave = -5; amplitudes(1.0, 0.5, 0.25) }

// Superflat settings get a layers { } builder, and structureOverrides takes structure set tags
flatLevelGeneratorPreset("tunnelers_dream", Items.STONE) {
	settings {
		layers { layer(Blocks.BEDROCK); layer(Blocks.STONE, height = 230); layer(Blocks.GRASS_BLOCK) }
		structureOverrides(StructureSetTagArgument("my_structure_sets", "test"))
	}
}

Sulfur cubes

// Before: a single explosionFuse field
sulfurCubeArchetype("regular", items = Tags.Item.SWORDS) { explosionFuse = 80 }

// After: structured explosion, contact damage and knockback, plus sound settings
sulfurCubeArchetype("regular", Tags.Item.SWORDS, horizontalKnockbackPower = 0.4f, verticalKnockbackPower = 0.2f) {
	contactDamage(amount = 3f, damageType = DamageTypes.GENERIC, attributeToSource = true)
	explosion(fuse = 80, power = 3, causesFire = true)
}

Migration notes

Every rename in this cycle, with the code to change on your side.

UUID becomes Uuid

The stable kotlin.uuid.Uuid is idiomatic, simpler, and multiplatform. If you only use the uuid() helpers, nothing changes.

// Before
UUID.fromString("6c0b9f1e-...")
UUID.randomUUID()

// After
Uuid.parse("6c0b9f1e-...")
Uuid.random()

item(...) becomes items(...) on item predicates

The builder takes several items and matches any of them, so the plural fits. Loot table entries keep their own item(...), which drops a single item.

// Before
matchTool { item(Items.DIAMOND_PICKAXE, Items.NETHERITE_PICKAXE) }

// After
matchTool { items(Items.DIAMOND_PICKAXE, Items.NETHERITE_PICKAXE) }

Sub-predicates take their vanilla name

Each one is named after the vanilla type it produces, so a name found in the wiki maps to the Kore one, and the global functions stop clashing with other builders of the same name. The loot context enum EntityType became EntityTarget to stop clashing with the EntityTypes registry.

// Before
effects { this[Effects.INVISIBILITY] = effect { amplifier = rangeOrInt(1) } }
equipment { mainHand = itemStack(Items.DIAMOND_SWORD) }
slots { this[WEAPON.MAINHAND] = itemStack(Items.DIAMOND_SWORD) }
location { block { blocks(Blocks.STONE) } }
scoreNumber("kills", EntityType.THIS)

// After
effects { this[Effects.INVISIBILITY] = mobEffectPredicate { amplifier = rangeOrInt(1) } }
equipment { mainHand = itemStackPredicate(Items.DIAMOND_SWORD) }
slots { this[WEAPON.MAINHAND] = itemStackPredicate(Items.DIAMOND_SWORD) }
location { block(Blocks.STONE) }
scoreNumber("kills", EntityTarget.THIS)

timeCheck requires its clock

Vanilla needs a clock to know which time to read, so it moved from a trailing nullable argument to the mandatory first one.

// Before
timeCheck(10f..20f, clock = WorldClocks.OVERWORLD)
timeCheck(0f..6000f, period = 24000, clock = WorldClocks.THE_END)

// After
timeCheck(WorldClocks.OVERWORLD, 10f..20f)
timeCheck(WorldClocks.THE_END, 0f..6000f, period = 24000)

valueCheck requires its range

A value_check without a range never matches anything, so the range is mandatory. An IntRange overload comes with it, since the game clamps both bounds to an integer anyway.

// Before
valueCheck(scoreNumber("kills", EntityType.THIS))

// After
valueCheck(scoreNumber("kills", EntityTarget.THIS), 1..10)
valueCheck(scoreNumber("kills", EntityTarget.THIS), intRange(0f, 10f))

Score holders are now a sealed ScoreProvider

Two nullable parameters let you pass both or neither and get invalid JSON, so the holder is a sealed type now: contextScore for a loot context entity, fixedScore for a name.

// Before
scoreNumber("kills", target = EntityType.THIS)
scoreNumber("kills", name = "Ayfri")

// After
scoreNumber("kills", contextScore(EntityTarget.THIS))
scoreNumber("kills", fixedScore("Ayfri"))

providersRange(...) becomes intRange(...)

Both built the same bound, so they are merged under intRange, which covers floats, providers, ClosedFloatingPointRange and IntRange.

// Before
limitCount(providersRange(min = constant(1f), max = constant(32f)))

// After
limitCount(intRange(min = constant(1f), max = constant(32f)))

Number providers only resolve inside their scopes

constant, uniform and the others now extend IntProviderScope/FloatProviderScope, so they only show up where a provider is accepted, and the int and float versions stop being ambiguous. weightedList gets a shorter syntax at the same time.

// Before
count(weightedList {
	add(weightedEntry(7, constant(1)))
	add(weightedEntry(3, constant(3)))
})

// After
count(weightedList {
	entry(7, constant(1))
	entry(3, constant(3))
})
count(weightedList(7 to constant(1), 3 to constant(3)))

HeightConstant becomes VerticalAnchor

Vertical anchors and height providers are scoped too, so they only appear where a height is expected.

// Before
heightRange(uniform(HeightConstant.aboveBottom(8), HeightConstant.belowTop(2)))

// After
heightRange(uniformHeightProvider(aboveBottom(8), belowTop(2)))

equippable takes a generated EquipmentAssets entry

The asset id was typed as ModelArgument, so any model name compiled. EquipmentAssets is generated from the vanilla list instead.

// Before
equippable(EquipmentSlot.HEAD, ModelArgument("iron"))

// After
equippable(EquipmentSlot.HEAD, EquipmentAssets.IRON)

Dripstone becomes speleothem

Vanilla renamed the features in 26.2, and they gained base_block, pointed_block and replaceable_blocks.

// Before
configuredFeature("cluster", dripstoneCluster(...))
configuredFeature("pointed", pointedDripstone(...))

// After
configuredFeatures {
	speleothemCluster("cluster") { /* baseBlock, pointedBlock, replaceableBlocks */ }
	speleothem("pointed") { /* ... */ }
}

Removed, with no replacement

weird_scaled_sampler and the noise_gradient surface rule are gone from vanilla, so they are gone from Kore. noise_threshold gained an optional 3D evaluation and covers most of what noise_gradient was used for.

New builders worth knowing

// team modify <team> color reset
modify("my_team") { colorReset() }

// scatter a placed feature around its origin
configuredFeaturesBuilder.randomPatch("flowers", PlacedFeatures.FLOWER_DEFAULT) {
	tries = 64
	xzSpread = 5
	ySpread = 2
}

Changelog

Documentation

  • docs(commands): Clarify and expand the command docs. 1d1e597
  • docs(components): Revise the components guide to update examples, clarify predicates, and link the component matchers table. 9d3cf51
  • docs(contributing): Update the architecture guide with GeneratedSealedSerializer usage and KSP-based serializer factory details. b5cf883
  • docs(generation): Add short KDoc to the generation module's key entry points. f8b527a
  • docs(home): Add community project links and the jumpr entry to the README list. e6b41aa 5aaf9c4
  • docs(oop): Improve the item, scoreboard, and team documentation. 708af24
  • docs(vfx-engine): Enhance the VfxEngine documentation with detailed explanations on shapes, coordinate spaces, and offsets. 035c1b9
  • docs(website): Update the guide content, clarify Kore usage and migration paths, add a version matrix to the Home page. 0af799b
  • docs(worldgen): Add a dedicated Carvers page and move the carver sections out of the Biomes page. d2f3cf1
  • docs(worldgen): Document the IntProviderScope/FloatProviderScope builder receivers on the Providers page, fix the stale providersRange/binomial snippets in Item Modifiers and Loot Tables. 6aec34b
  • docs(worldgen): Restructure the worldgen pages around a single end-to-end example, extract the shared providers to their own page, and condense the environment attribute reference into tables. 6aa1827
  • docs(worldgen): Rewrite the Noise & Terrain page as a full density function, noise router, and surface rule reference. 38558ae
  • docs(worldgen): Rewrite the Structures processor list and template pool sections with the scoped element builders. c403250 ba6c643

New Features

  • feat(arguments): Add toStringWithDecimal for consistent decimal point formatting, update the positional, rotational, and vector conversions. 03158e9
  • feat(bindings): Add the DatapackUpload API for exploring and importing datapack ZIPs in-memory, with cross-platform tests. 7dc0800
  • feat(bindings): Enhance parseReference to support dotted GitHub repo names. a3f00b4
  • feat(commands): Add team modify <team> color reset through colorReset. f6c6bc5
  • feat(datapack): Introduce folderName to decouple output folder names from namespaces, add tests for the folder, jar, and zip modes. 8497d8b
  • feat(enchantments): Add the geyser, geyser_base, geyser_plume, and geyser_poof particle options with unit tests. ccea6c1
  • feat(entity-predicates): Introduce type-specific EntitySubPredicate subclasses (CubeMob, FishingHook, Lightning, Player, Raider, Sheep) and an EntityTypeSpecificScope to group them. 987b370
  • feat(execute): Change run's block to Function.() -> Unit, allowing if/for control flow inside it. abc4a5c
  • feat(fabric): Add ResourceCondition with fabricLoadConditions, implement condition-based JSON generation. 10543f8
  • feat(generation): Add a DatapackFolderRegistry generator producing a datapack-folder-to-Argument-type map from the existing generator definitions. 75bf9a9
  • feat(generation): Add a pure-Kotlin ZipReader and Inflate for cross-platform ZIP handling, unify the unzip logic with commonUnzipToTempDir. 2cad91a
  • feat(generation): Generate the EnchantmentProviders and EquipmentAssets enums, retype equippable around EquipmentAssetArgument. c2c2bc5
  • feat(generation): Introduce platform-agnostic I/O with PlatformIO, OPFS browser support, and a pure-Kotlin ZipWriter, unifying file operations across JVM, JS, and browser. 05e7b6a
  • feat(helpers): Add a minimal in-house port of org.joml (Matrix4f, Matrix3f, AxisAngle4f, Quaternionf, Vector3f, JomlMath) for display transformation math. 0265cd6
  • feat(item-components): Add the SulfurCubeContentComponent, with tests and documentation. 8afcac3
  • feat(selector-arguments): Add fromString to parse selectors back from text, with deserialization logic and advanced selector support. 6feb45f
  • feat(sulfur-cube-archetype): Add the sulfur_cube_archetype data-driven registry with attribute modifiers and buoyancy. b2e9a3d
  • feat(sulfur-cube-archetype): Replace explosionFuse with structured explosion, contactDamage, and knockback powers, then add hit and push sound settings with pushSoundCooldown and pushSoundImpulseThreshold. 03233a1 42a9fbb df98bdf
  • feat(test-environments): Add the difficulty test environment with unit tests. 961bbe7
  • feat(vfx-engine): Add coordinate space support to drawShape, enhance VfxShape with positionType and origin. 2a5a916
  • feat(worldgen): Accept a block ID or list of IDs, in addition to a tag, for the geode, root system, vegetation patch, dimension type infiniburn, and protected blocks replaceable fields. 7349b97
  • feat(worldgen): Add the sequence and template configured feature types. 53eb47e
  • feat(worldgen): Add the weighted_random_selector configured feature, plus levelTestDistance and maxLevelDeviation on root_system. bf4163c f9fdad8
  • feat(worldgen): Add the interval_select density function and the matching_biomes block predicate, remove weird_scaled_sampler, make the multiface_growth block and the tree below_trunk_provider mandatory. af33acf
  • feat(worldgen): Add optional 3D evaluation to noise_threshold and remove the obsolete noise_gradient surface rule. d25fd01
  • feat(worldgen): Introduce IntProviderScope/FloatProviderScope as builder receivers across placed features, configured features, dimension types, enchantment providers, carvers, and processors, and add the random_patch configured feature. 779217c
  • feat(worldgen): Enhance the Lake configuration with the canPlaceFeature, canReplaceWithAirOrFluid, and canReplaceWithBarrier block predicates. dccef30
  • feat(worldgen): Rename the dripstone_cluster/pointed_dripstone configured features to speleothem_cluster/speleothem, add the base_block/pointed_block/replaceable_blocks fields, and add replaceable_blocks to large_dripstone. 9a56efe

Bug Fixes

  • fix(bindings): Fix the illegal interface property initializers and unsafe identifiers left in the Resources and Tags generators. 53d8836
  • fix(chat-components): Simplify the serialization logic for extra fields and enhance the JSON/NBT handling in chat components. 0aee14b 34d72bb 400f99f
  • fix(commands-execute): Correct the targetArg logic to handle UUIDArgument equality with a self-check, and optimize the selector argument handling. 59c6eca
  • fix(components): Add the missing saddle equipment slot. b5df734
  • fix(datapack): Set pack_format to an integer only and fix its default value. b613e04
  • fix(enchantments): Write the providers to enchantment_provider instead of trades, and allow any file name and sub-folder. 7d649e9
  • fix(helpers): Rewrite fromAxisAngle to use sin/cos trigonometric calculations instead of instantiating a Quaternionf. c25bcdf
  • fix(item-components): Change food's nutrition field from Float to Int, make use_cooldown's cooldown_group optional, and remove the invalid contact_cooldown_ticks field from kinetic_weapon. d47a492 2175192 1de41ea
  • fix(item-components): Rename attack_range's min_range/max_range to min_reach/max_reach, blocks_attacks's disable_sound to disabled_sound, and firework_explosion's has_flicker to has_twinkle. f6ef4da 26bf1b2 415971b
  • fix(item-components): Serialize the SulfurCubeContentComponent as an inlined item id instead of a nested object. 65db924
  • fix(item-predicates): Add support for partial and negated components, enhance clearPredicate, and simplify the setPartial and negate logic. 3e7804d
  • fix(pack): Fix the packFormat warning wrongly reported as outside the min_format/max_format range when it shares their major version. b458689
  • fix(predicates): Rename the mob effect ambiant field to ambient, and write the periodic_tick and type_specific/cube_mob sub-predicate keys vanilla expects. 9a20f9b f3b3204
  • fix(serializers): Serialize and deserialize half-open bounds in FloatRangeOrFloatJsonSerializer instead of throwing. 2e273bf
  • fix(worldgen): Fix the noise and spline density functions and the stone_depth surface type to match the vanilla JSON, make the noise settings tests exhaustive. d09d92f
  • fix(worldgen): Rename the misspelled biome spawner categories to ambient, axolotls, and waterAmbient, and add the missing misc one. 626e421 c4946a4
  • fix(worldgen): Replace the ocean ruin type field by biomeTemp, and default the jigsaw size to 1 instead of the out-of-range 0. 635cc66 0e9bd53
  • fix(worldgen): Return a ConfiguredStructureArgument from every configured structure builder so structure sets accept them. fab9d5c

Performance improvements

  • perf(arguments): Scope the Argument interface classpath scan to the arguments packages and parallelize the Class.forName lookups. 00bfcde
  • perf(datapack): Cache the jsonEncoder instead of rebuilding a Json config on every access. 7441a97
  • perf(item-components): Encode component values directly instead of round-tripping through a JsonElement/NbtTag tree, and skip the redundant encode-to-element pass in ComponentsScope.asJson/asNbt. de3a95e f69791c

Refactors

  • refactor(uuid)!: Replace java.util.UUID with kotlin.uuid.Uuid across modules, updating the related methods and constructors.
  • refactor(components): Move the item sub-predicates to components/matchers as DataComponentPredicate and EnchantmentPredicate, rename item(...) to items(...), and add the missing villager/variant matcher. 72b3a6b
  • refactor(density-functions): Add the densityFunctions builder with per-type block syntax, fix the snakeCase acronym and digit boundaries. dfe5c62
  • refactor(enchantments): Share the effect builders through scopes, fix the explode, play_sound, and crossbow_charging_sounds codecs, and complete the effect components. 9a2be54
  • refactor(predicates): Rename the sub-predicates to their vanilla *Predicate names, fix their serialization against vanilla-mcdoc, and require clock on timeCheck. 111e1a1
  • refactor(predicates): Split the score targets into a ScoreProvider sealed type, require ranges on valueCheck, and add the IntRange overload. 1674e14
  • refactor(trade-sets): Enhance TradeSet with new builder methods for trade amounts and sequences, with tests for empty and single trades. 892f75e
  • refactor(worldgen): Scope the configured carver builders to ConfiguredCarversScope, add the missing nether_cave type, fix the vanilla defaults and the flat biome carvers list. f68c443
  • refactor(worldgen): Scope the surface rule builders to SurfaceRulesScope, replace entry() with state/empty, and collapse the single-rule conditions. 1068a96
  • refactor(worldgen): Scope the processor list builders to ProcessorsScope, fix the blackstone_replace name and the vanilla defaults. 0ddc09c
  • refactor(worldgen): Scope the template pool builders to PoolEntriesScope and PoolElementsScope, fix the vanilla defaults, and add the legacySingle liquid settings. 87c932c
  • refactor(worldgen): Scope the block predicate, block state provider, and rule test builders, replace offset with a per-predicate offset { }, add unobstructed and targets { } on the ore-like features, and fix the noise_threshold_provider, randomized_int_state_provider, and tag_match naming. 43d05a9 cf5c3c8 872ab1c
  • refactor(worldgen): Rename HeightConstant to VerticalAnchor, scope the anchor and height provider builders, and document them with tests. e5ebca4
  • refactor(worldgen): Key the world preset dimensions by their id instead of their type, make every dimension generator builder set generator, remove the obsolete natural dimension type field, and add a layers { } builder for the superflat settings. e8ca92c 41c83f7 99f997e bed0900
  • refactor(worldgen): Replace the list-based amplitude and octave methods with a unified amplitudes(...). 2de880c
  • refactor(worldgen): Retype the structures builder around a StructuresScope, fix the mineshaft, shipwreck, spawn override, and generation step keys, and document every type with tests. 6a0795f

Full changelog: https://github.com/Ayfri/Kore/compare/v2.6.1-26.1..v2.13.1-26.2-rc-2