From 159125fb4b03ae8fcfadcc349a48c6f4c1cf4198 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Tue, 25 Mar 2025 18:59:53 +0700 Subject: [PATCH] Per-block variable storage in enhanced placement --- .../mc/otm/worldgen/EnhancedPlacedFeature.kt | 30 +++++++--- .../otm/worldgen/EnhancedPlacementContext.kt | 34 ++--------- .../ru/dbotthepony/mc/otm/worldgen/Ext.kt | 8 +++ .../mc/otm/worldgen/PlacementVariable.kt | 6 +- .../mc/otm/worldgen/PlacementVariableMap.kt | 56 +++++++++++++++++++ .../otm/worldgen/feature/EnhancedFeature.kt | 11 ++-- .../worldgen/placement/EllipsoidPlacement.kt | 8 ++- .../placement/EnhancedChainPlacement.kt | 3 +- .../placement/EnhancedCountPlacement.kt | 6 +- .../placement/EnhancedPlacementModifier.kt | 12 ++-- .../placement/EnhancedSplitPlacement.kt | 5 +- .../otm/worldgen/placement/WormPlacement.kt | 20 ++++--- 12 files changed, 133 insertions(+), 66 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/PlacementVariableMap.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/EnhancedPlacedFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/EnhancedPlacedFeature.kt index c58b33001..56e8ce310 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/EnhancedPlacedFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/EnhancedPlacedFeature.kt @@ -116,10 +116,10 @@ object EnhancedPlacedFeature : Feature( ) { class Node(val children: List, val contents: Either>>){ fun evaluate(context: EnhancedPlacementContext) { - evaluate(context, listOf(BlockPos(context.origin.minBlockX, 0, context.origin.minBlockZ))) + evaluate(context, listOf(PlacementPos(BlockPos(context.origin.minBlockX, 0, context.origin.minBlockZ), emptyVariableMap))) } - private fun evaluate(context: EnhancedPlacementContext, positions: List) { + private fun evaluate(context: EnhancedPlacementContext, positions: List) { val actualPositions = if (contents.left().isPresent) { contents.left().get().evaluate(context, positions) } else { @@ -136,18 +136,20 @@ object EnhancedPlacedFeature : Feature( } } + private val emptyVariableMap = PlacementVariableMap() + class Config( val chunkScanRange: Int, val seedMix: Long, val root: Node, ) : FeatureConfiguration { private class GeneratedChunk : EnhancedPlacementContext.Placer { - private data class Placement(val context: EnhancedPlacementContext, val positions: BlockPosSet, val feature: EnhancedFeature.Configured<*, *>) + private data class Placement(val context: EnhancedPlacementContext, val positions: ObjectRBTreeSet, val feature: EnhancedFeature.Configured<*, *>) private val placed = LinkedList() - override fun place(context: EnhancedPlacementContext, positions: List, feature: EnhancedFeature.Configured<*, *>) { + override fun place(context: EnhancedPlacementContext, positions: List, feature: EnhancedFeature.Configured<*, *>) { if (positions.isNotEmpty()) { - placed.add(Placement(context, BlockPosSet().also { it.addAll(positions) }, feature)) + placed.add(Placement(context, ObjectRBTreeSet().also { it.addAll(positions) }, feature)) } } @@ -156,10 +158,22 @@ object EnhancedPlacedFeature : Feature( val pos = ChunkPos(context.origin()) for ((eContext, positions, feature) in placed) { - val filtered = positions.subset(pos) + val itr = positions.iterator(PlacementPos(BlockPos(pos.minBlockX, Int.MIN_VALUE, pos.minBlockZ), emptyVariableMap)) - if (filtered.isNotEmpty()) { - any = feature.place(eContext.push(context.level()), filtered, positions) || any + if (!itr.hasNext()) + continue + + val result = ObjectRBTreeSet() + + for (placementPos in itr) { + if (SectionPos.blockToSectionCoord(placementPos.pos.x) != pos.x || SectionPos.blockToSectionCoord(placementPos.pos.z) != pos.z) + break + + result.add(placementPos) + } + + if (result.isNotEmpty()) { + any = feature.place(eContext.push(context.level()), result, positions) || any } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/EnhancedPlacementContext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/EnhancedPlacementContext.kt index eb024d5f9..68beaeff6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/EnhancedPlacementContext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/EnhancedPlacementContext.kt @@ -16,7 +16,7 @@ import kotlin.collections.HashMap class EnhancedPlacementContext { fun interface Placer { - fun place(context: EnhancedPlacementContext, positions: List, feature: EnhancedFeature.Configured<*, *>) + fun place(context: EnhancedPlacementContext, positions: List, feature: EnhancedFeature.Configured<*, *>) } private data class SharedState( @@ -43,29 +43,7 @@ class EnhancedPlacementContext { val vanillaContext: PlacementContext get() = state.vanillaContext - private val parent: EnhancedPlacementContext? - private val variables = Reference2ObjectOpenHashMap, KOptional<*>>(0) - - private fun recursiveGet(index: PlacementVariable): KOptional? { - return variables[index] as KOptional? ?: parent?.recursiveGet(index) - } - - operator fun get(index: PlacementVariable): KOptional { - return recursiveGet(index) ?: KOptional() - } - - operator fun set(index: PlacementVariable, value: T) { - variables[index] = KOptional(value) - } - - fun remove(index: PlacementVariable): KOptional { - val old = variables.put(index, KOptional()) - return old as KOptional? ?: KOptional() - } - - fun remove(index: Collection>) { - index.forEach { remove(it) } - } + val variables: PlacementVariableMap constructor( level: WorldGenLevel, @@ -83,24 +61,24 @@ class EnhancedPlacementContext { vanillaContext = PlacementContext(level, generator, Optional.empty()) ) - parent = null + variables = PlacementVariableMap() } private constructor(parent: EnhancedPlacementContext) { this.state = parent.state - this.parent = parent + this.variables = parent.variables.push() } private constructor(parent: EnhancedPlacementContext, context: WorldGenLevel) { this.state = parent.state.copy(level = context) - this.parent = parent + this.variables = parent.variables.push() } fun push(): EnhancedPlacementContext { return EnhancedPlacementContext(this) } - fun place(positions: List, feature: EnhancedFeature.Configured<*, *>) { + fun place(positions: List, feature: EnhancedFeature.Configured<*, *>) { placer.place(this, positions, feature) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/Ext.kt index 207faf3f2..f44556fef 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/Ext.kt @@ -1,8 +1,16 @@ package ru.dbotthepony.mc.otm.worldgen +import net.minecraft.core.BlockPos import net.minecraft.world.level.levelgen.placement.PlacementModifier +import ru.dbotthepony.mc.otm.core.collect.Vec3iHashStrategy import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier fun PlacementModifier.wrap(): EnhancedPlacementModifier { return EnhancedPlacementModifier.Wrapper(this) } + +data class PlacementPos(val pos: BlockPos, val variables: PlacementVariableMap) : Comparable { + override fun compareTo(other: PlacementPos): Int { + return Vec3iHashStrategy.compare(pos, other.pos) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/PlacementVariable.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/PlacementVariable.kt index d0d325f2f..b52deeee9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/PlacementVariable.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/PlacementVariable.kt @@ -2,8 +2,8 @@ package ru.dbotthepony.mc.otm.worldgen import net.minecraft.resources.ResourceLocation -class PlacementVariable(val name: ResourceLocation) { - companion object { - +class PlacementVariable(val name: ResourceLocation) : Comparable> { + override fun compareTo(other: PlacementVariable): Int { + return name.compareTo(other.name) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/PlacementVariableMap.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/PlacementVariableMap.kt new file mode 100644 index 000000000..1cf849322 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/PlacementVariableMap.kt @@ -0,0 +1,56 @@ +package ru.dbotthepony.mc.otm.worldgen + +import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap +import ru.dbotthepony.kommons.util.KOptional + +/** + * This is a "map stack" of sorts, and when you want to push some data to it + * you need to push new map to stack (by calling [push]) and then write new data to + * the map returned by latter. + */ +class PlacementVariableMap { + private val parent: PlacementVariableMap? + private val variables = Object2ObjectAVLTreeMap, KOptional<*>>() + + constructor() { + parent = null + } + + private constructor(parent: PlacementVariableMap) { + this.parent = parent + } + + private fun recursiveGet(index: PlacementVariable): KOptional? { + return variables[index] as KOptional? ?: parent?.recursiveGet(index) + } + + operator fun get(index: PlacementVariable): KOptional { + return recursiveGet(index) ?: KOptional() + } + + operator fun set(index: PlacementVariable, value: T): PlacementVariableMap { + variables[index] = KOptional(value) + return this + } + + fun remove(index: PlacementVariable): KOptional { + val old = variables.put(index, KOptional()) + return old as KOptional? ?: KOptional() + } + + fun remove(index: Collection>) { + index.forEach { remove(it) } + } + + fun push(): PlacementVariableMap { + return PlacementVariableMap(this) + } + + override fun equals(other: Any?): Boolean { + return other === this || other is PlacementVariableMap && variables == other.variables && parent == other.parent + } + + override fun hashCode(): Int { + return parent.hashCode() * 31 + variables.hashCode() + 5 + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/feature/EnhancedFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/feature/EnhancedFeature.kt index 89b4fd343..b731a29ae 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/feature/EnhancedFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/feature/EnhancedFeature.kt @@ -12,12 +12,13 @@ import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries import ru.dbotthepony.mc.otm.registry.MDeferredRegister import ru.dbotthepony.mc.otm.registry.MRegistries import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext +import ru.dbotthepony.mc.otm.worldgen.PlacementPos abstract class EnhancedFeature(codec: Codec) { - abstract fun place(context: EnhancedPlacementContext, config: FC, positions: Set, allPositions: Set): Boolean + abstract fun place(context: EnhancedPlacementContext, config: FC, positions: Set, allPositions: Set): Boolean data class Configured, FC>(val feature: F, val config: FC) { - fun place(context: EnhancedPlacementContext, positions: Set, allPositions: Set): Boolean { + fun place(context: EnhancedPlacementContext, positions: Set, allPositions: Set): Boolean { return feature.place(context, config, positions, allPositions) } } @@ -29,11 +30,11 @@ abstract class EnhancedFeature(codec: Codec) { override fun place( context: EnhancedPlacementContext, config: Holder>, - positions: Set, - allPositions: Set + positions: Set, + allPositions: Set ): Boolean { var any = false - positions.forEach { any = config.value().place(context.level, context.generator, context.random, it) || any } + positions.forEach { any = config.value().place(context.level, context.generator, context.random, it.pos) || any } return any } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EllipsoidPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EllipsoidPlacement.kt index de56dfb3c..686193663 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EllipsoidPlacement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EllipsoidPlacement.kt @@ -13,6 +13,8 @@ import net.minecraft.world.level.levelgen.placement.PlacementModifierType import ru.dbotthepony.mc.otm.core.collect.Vec3iHashStrategy import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext +import ru.dbotthepony.mc.otm.worldgen.PlacementPos +import ru.dbotthepony.mc.otm.worldgen.PlacementVariableMap import java.util.stream.Stream /** @@ -103,9 +105,9 @@ data class EllipsoidPlacement( return evaluate(random, position).stream() } - override fun evaluate(context: EnhancedPlacementContext, positions: List): List { - val result = ArrayList() - val results = positions.map { evaluate(context.random, it) } + override fun evaluate(context: EnhancedPlacementContext, positions: List): List { + val result = ArrayList() + val results = positions.map { (it, c) -> evaluate(context.random, it).map { PlacementPos(it, c) } } result.ensureCapacity(result.size + results.sumOf { it.size } + 1) results.forEach { result.addAll(it) } return result diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedChainPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedChainPlacement.kt index 2dbd19dad..0c79d21c8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedChainPlacement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedChainPlacement.kt @@ -6,6 +6,7 @@ import com.mojang.serialization.MapCodec import net.minecraft.core.BlockPos import net.minecraft.world.level.levelgen.placement.PlacementModifier import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext +import ru.dbotthepony.mc.otm.worldgen.PlacementPos /** * Daisy-chaining placements. Required only when using placement modifiers which operate on children list @@ -15,7 +16,7 @@ class EnhancedChainPlacement( ) : EnhancedPlacementModifier { constructor(vararg children: EnhancedPlacementModifier) : this(ImmutableList.copyOf(children)) - override fun evaluate(context: EnhancedPlacementContext, positions: List): List { + override fun evaluate(context: EnhancedPlacementContext, positions: List): List { var current = positions children.forEach { current = it.evaluate(context, current) } return current diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedCountPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedCountPlacement.kt index 5e665a09c..01033c1a3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedCountPlacement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedCountPlacement.kt @@ -1,12 +1,12 @@ package ru.dbotthepony.mc.otm.worldgen.placement import com.mojang.serialization.MapCodec -import net.minecraft.core.BlockPos import net.minecraft.util.valueproviders.IntProvider import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext +import ru.dbotthepony.mc.otm.worldgen.PlacementPos class EnhancedCountPlacement(val provider: IntProvider) : EnhancedPlacementModifier { - override fun evaluate(context: EnhancedPlacementContext, positions: List): List { + override fun evaluate(context: EnhancedPlacementContext, positions: List): List { val count = provider.sample(context.random) if (count <= 0) { @@ -14,7 +14,7 @@ class EnhancedCountPlacement(val provider: IntProvider) : EnhancedPlacementModif } else if (count == 1) { return positions } else { - val result = ArrayList() + val result = ArrayList() result.ensureCapacity(positions.size * count) for (i in 0 until count) result.addAll(positions) return result diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedPlacementModifier.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedPlacementModifier.kt index 7403b441f..a71f03db2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedPlacementModifier.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedPlacementModifier.kt @@ -3,12 +3,15 @@ package ru.dbotthepony.mc.otm.worldgen.placement import com.mojang.serialization.Codec import com.mojang.serialization.MapCodec import net.minecraft.core.BlockPos +import net.minecraft.world.level.levelgen.placement.PlacementContext import net.minecraft.world.level.levelgen.placement.PlacementModifier import net.neoforged.bus.api.IEventBus import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries import ru.dbotthepony.mc.otm.registry.MDeferredRegister import ru.dbotthepony.mc.otm.registry.MRegistries import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext +import ru.dbotthepony.mc.otm.worldgen.PlacementPos +import ru.dbotthepony.mc.otm.worldgen.PlacementVariableMap import java.util.stream.Collectors interface EnhancedPlacementModifier { @@ -16,14 +19,13 @@ interface EnhancedPlacementModifier { val codec: MapCodec } - fun evaluate(context: EnhancedPlacementContext, positions: List): List + fun evaluate(context: EnhancedPlacementContext, positions: List): List val type: Type<*> class Wrapper(val parent: PlacementModifier) : EnhancedPlacementModifier { - override fun evaluate(context: EnhancedPlacementContext, positions: List): List { + override fun evaluate(context: EnhancedPlacementContext, positions: List): List { return positions.stream() - .flatMap { parent.getPositions(context.vanillaContext, context.random, it) } - // use Red-Black tree set instead of AVL tree set because we are write-intense + .flatMap { (pos, vars) -> parent.getPositions(context.vanillaContext, context.random, pos).map { PlacementPos(it, vars) } } .collect(Collectors.toCollection(::ArrayList)) } @@ -36,7 +38,7 @@ interface EnhancedPlacementModifier { } object Passthrough : EnhancedPlacementModifier, Type { - override fun evaluate(context: EnhancedPlacementContext, positions: List): List { + override fun evaluate(context: EnhancedPlacementContext, positions: List): List { return positions } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedSplitPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedSplitPlacement.kt index 6e3d97eb3..2ead61418 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedSplitPlacement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnhancedSplitPlacement.kt @@ -10,6 +10,7 @@ import net.minecraft.world.level.levelgen.placement.PlacementModifier import net.minecraft.world.level.levelgen.placement.PlacementModifierType import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext +import ru.dbotthepony.mc.otm.worldgen.PlacementPos import java.util.stream.Collectors import java.util.stream.Stream @@ -23,8 +24,8 @@ class EnhancedSplitPlacement( ) : EnhancedPlacementModifier { constructor(vararg children: EnhancedPlacementModifier) : this(ImmutableList.copyOf(children)) - override fun evaluate(context: EnhancedPlacementContext, positions: List): List { - val result = ArrayList() + override fun evaluate(context: EnhancedPlacementContext, positions: List): List { + val result = ArrayList() children.forEach { result.addAll(it.evaluate(context, positions)) } return result } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt index c369998e3..64bd6291b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt @@ -24,6 +24,7 @@ import ru.dbotthepony.mc.otm.data.codec.minRange import ru.dbotthepony.mc.otm.data.world.BooleanProvider import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext +import ru.dbotthepony.mc.otm.worldgen.PlacementPos import java.util.stream.Stream import kotlin.math.PI import kotlin.math.absoluteValue @@ -135,9 +136,10 @@ class WormPlacement( } } - private fun evaluate(random: RandomSource, position: BlockPos, results: ArrayList) { + private fun evaluate(random: RandomSource, position: BlockPos): List { val worms = ArrayList() worms.add(Worm(random, Vector.ZERO)) + val results = ArrayList() results.add(position) while (worms.isNotEmpty()) { @@ -147,15 +149,19 @@ class WormPlacement( it.hasFinished } } + + return results } - override fun evaluate(context: EnhancedPlacementContext, positions: List): List { + override fun evaluate(context: EnhancedPlacementContext, positions: List): List { if (positions.isEmpty()) return positions else { - val results = ArrayList() - positions.forEach { evaluate(context.random, it, results) } - return results + val result = ArrayList() + val results = positions.map { (it, c) -> evaluate(context.random, it).map { PlacementPos(it, c) } } + result.ensureCapacity(result.size + results.sumOf { it.size } + 1) + results.forEach { result.addAll(it) } + return result } } @@ -163,9 +169,7 @@ class WormPlacement( get() = Companion override fun getPositions(context: PlacementContext, random: RandomSource, center: BlockPos): Stream { - val results = ArrayList() - evaluate(random, center, results) - return results.stream() + return evaluate(random, center).stream() } override fun type(): PlacementModifierType<*> {