Enhanced placements prototyping
This commit is contained in:
parent
03a1ab9197
commit
18f9bc2654
@ -57,6 +57,7 @@ import ru.dbotthepony.mc.otm.datagen.tags.addStructureTags
|
|||||||
import ru.dbotthepony.mc.otm.datagen.tags.addSuspiciousTags
|
import ru.dbotthepony.mc.otm.datagen.tags.addSuspiciousTags
|
||||||
import ru.dbotthepony.mc.otm.datagen.tags.addTags
|
import ru.dbotthepony.mc.otm.datagen.tags.addTags
|
||||||
import ru.dbotthepony.mc.otm.matter.MatterDataProvider
|
import ru.dbotthepony.mc.otm.matter.MatterDataProvider
|
||||||
|
import ru.dbotthepony.mc.otm.registry.MRegistries
|
||||||
import ru.dbotthepony.mc.otm.registry.game.MBlocks
|
import ru.dbotthepony.mc.otm.registry.game.MBlocks
|
||||||
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
|
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
|
||||||
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
|
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
|
||||||
@ -560,6 +561,7 @@ object DataGen {
|
|||||||
val registrySetBuilder = RegistrySetBuilder()
|
val registrySetBuilder = RegistrySetBuilder()
|
||||||
.add(Registries.DAMAGE_TYPE, ::registerDamageTypes)
|
.add(Registries.DAMAGE_TYPE, ::registerDamageTypes)
|
||||||
.add(Registries.CONFIGURED_FEATURE, ::registerConfiguredFeatures)
|
.add(Registries.CONFIGURED_FEATURE, ::registerConfiguredFeatures)
|
||||||
|
.add(MRegistries.CONFIGURED_FEATURE, ::registerEnhancedConfiguredFeatures)
|
||||||
.add(Registries.PLACED_FEATURE, ::registerPlacedFeatures)
|
.add(Registries.PLACED_FEATURE, ::registerPlacedFeatures)
|
||||||
.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, ::registerBiomeModifiers)
|
.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, ::registerBiomeModifiers)
|
||||||
|
|
||||||
|
@ -28,11 +28,14 @@ import net.neoforged.neoforge.registries.NeoForgeRegistries
|
|||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
|
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
|
||||||
import ru.dbotthepony.mc.otm.data.world.OneOfFloatProvider
|
import ru.dbotthepony.mc.otm.data.world.OneOfFloatProvider
|
||||||
|
import ru.dbotthepony.mc.otm.registry.MRegistries
|
||||||
import ru.dbotthepony.mc.otm.worldgen.placement.StandardDeviationHeightProvider
|
import ru.dbotthepony.mc.otm.worldgen.placement.StandardDeviationHeightProvider
|
||||||
import ru.dbotthepony.mc.otm.registry.game.MBlocks
|
import ru.dbotthepony.mc.otm.registry.game.MBlocks
|
||||||
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
|
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacedFeature
|
||||||
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
|
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
|
||||||
import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
|
import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
|
||||||
import ru.dbotthepony.mc.otm.worldgen.placement.ChainPlacement
|
import ru.dbotthepony.mc.otm.worldgen.placement.ChainPlacement
|
||||||
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousPlacement
|
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousPlacement
|
||||||
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement
|
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement
|
||||||
@ -42,12 +45,30 @@ import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
|
|||||||
private object ConfiguredFeatures {
|
private object ConfiguredFeatures {
|
||||||
val TRITANIUM_ORE = key("tritanium_ore")
|
val TRITANIUM_ORE = key("tritanium_ore")
|
||||||
val TRITANIUM_ORE_SMALL = key("tritanium_ore_small")
|
val TRITANIUM_ORE_SMALL = key("tritanium_ore_small")
|
||||||
val DILITHIUM = key("dilithium")
|
val DILITHIUM = ekey("dilithium")
|
||||||
val BLACK_HOLE = key("black_hole")
|
val BLACK_HOLE = key("black_hole")
|
||||||
|
|
||||||
private fun key(name: String): ResourceKey<ConfiguredFeature<*, *>> {
|
private fun key(name: String): ResourceKey<ConfiguredFeature<*, *>> {
|
||||||
return ResourceKey.create(Registries.CONFIGURED_FEATURE, modLocation(name))
|
return ResourceKey.create(Registries.CONFIGURED_FEATURE, modLocation(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ekey(name: String): ResourceKey<EnhancedFeature.Configured<*, *>> {
|
||||||
|
return ResourceKey.create(MRegistries.CONFIGURED_FEATURE, modLocation(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerEnhancedConfiguredFeatures(context: BootstrapContext<EnhancedFeature.Configured<*, *>>) {
|
||||||
|
val stone = TagMatchTest(BlockTags.STONE_ORE_REPLACEABLES)
|
||||||
|
val deepslate = TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES)
|
||||||
|
|
||||||
|
run {
|
||||||
|
val target = listOf(
|
||||||
|
OreConfiguration.target(stone, MBlocks.DILITHIUM_ORE.defaultBlockState()),
|
||||||
|
OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_DILITHIUM_ORE.defaultBlockState()),
|
||||||
|
)
|
||||||
|
|
||||||
|
context.register(ConfiguredFeatures.DILITHIUM, EnhancedFeature.Wrapper.configure(ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>>) {
|
fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>>) {
|
||||||
@ -65,15 +86,6 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
|
|||||||
//context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(MWorldGenFeatures.DEBUG_PLACEMENT, DebugPlacerFeature.Config(MBlocks.TRITANIUM_ORE.defaultBlockState())))
|
//context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(MWorldGenFeatures.DEBUG_PLACEMENT, DebugPlacerFeature.Config(MBlocks.TRITANIUM_ORE.defaultBlockState())))
|
||||||
}
|
}
|
||||||
|
|
||||||
run {
|
|
||||||
val target = listOf(
|
|
||||||
OreConfiguration.target(stone, MBlocks.DILITHIUM_ORE.defaultBlockState()),
|
|
||||||
OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_DILITHIUM_ORE.defaultBlockState()),
|
|
||||||
)
|
|
||||||
|
|
||||||
context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target)))
|
|
||||||
}
|
|
||||||
|
|
||||||
context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(
|
context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(
|
||||||
MWorldGenFeatures.BLACK_HOLE_PLACER,
|
MWorldGenFeatures.BLACK_HOLE_PLACER,
|
||||||
BlackHolePlacerFeature.Config(Decimal("0.25"), Decimal(1))))
|
BlackHolePlacerFeature.Config(Decimal("0.25"), Decimal(1))))
|
||||||
@ -93,6 +105,7 @@ private object PlacedFeatures {
|
|||||||
|
|
||||||
fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
||||||
val configured = context.lookup(Registries.CONFIGURED_FEATURE)
|
val configured = context.lookup(Registries.CONFIGURED_FEATURE)
|
||||||
|
val econfigured = context.lookup(MRegistries.CONFIGURED_FEATURE)
|
||||||
|
|
||||||
run {
|
run {
|
||||||
val ore = configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE)
|
val ore = configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE)
|
||||||
@ -169,13 +182,14 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run {
|
run {
|
||||||
val ore = configured.getOrThrow(ConfiguredFeatures.DILITHIUM)
|
val ore = econfigured.getOrThrow(ConfiguredFeatures.DILITHIUM)
|
||||||
|
|
||||||
val ringularity = OneOfFloatProvider.of(
|
val ringularity = OneOfFloatProvider.of(
|
||||||
ClampedNormalFloat.of(0.4f, 0.2f, -2f, 2f),
|
ClampedNormalFloat.of(0.4f, 0.2f, -2f, 2f),
|
||||||
ClampedNormalFloat.of(-0.4f, 0.2f, -2f, 2f),
|
ClampedNormalFloat.of(-0.4f, 0.2f, -2f, 2f),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
context.register(PlacedFeatures.DILITHIUM, PlacedFeature(
|
context.register(PlacedFeatures.DILITHIUM, PlacedFeature(
|
||||||
ore,
|
ore,
|
||||||
listOf(
|
listOf(
|
||||||
@ -199,6 +213,30 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
*/
|
||||||
|
|
||||||
|
context.register(
|
||||||
|
PlacedFeatures.DILITHIUM,
|
||||||
|
EnhancedPlacedFeature.Builder(RarityFilter.onAverageOnceEvery(120))
|
||||||
|
.then(InSquarePlacement.spread())
|
||||||
|
.then(HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)))
|
||||||
|
.then(
|
||||||
|
EllipsoidPlacement(
|
||||||
|
x = ringularity,
|
||||||
|
y = ringularity,
|
||||||
|
z = ringularity,
|
||||||
|
count = UniformInt.of(8000, 16000),
|
||||||
|
xLength = UniformFloat.of(30f, 70f),
|
||||||
|
yLength = UniformFloat.of(40f, 90f),
|
||||||
|
zLength = UniformFloat.of(30f, 70f),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(ore)
|
||||||
|
.build(
|
||||||
|
chunkScanRange = 6,
|
||||||
|
seedMix = 237483209523709234L,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val blackHole = configured.getOrThrow(ConfiguredFeatures.BLACK_HOLE)
|
val blackHole = configured.getOrThrow(ConfiguredFeatures.BLACK_HOLE)
|
||||||
|
@ -92,6 +92,8 @@ import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
|
|||||||
import ru.dbotthepony.mc.otm.server.MCommands
|
import ru.dbotthepony.mc.otm.server.MCommands
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger
|
import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
|
||||||
import thedarkcolour.kotlinforforge.neoforge.forge.DIST
|
import thedarkcolour.kotlinforforge.neoforge.forge.DIST
|
||||||
import thedarkcolour.kotlinforforge.neoforge.forge.FORGE_BUS
|
import thedarkcolour.kotlinforforge.neoforge.forge.FORGE_BUS
|
||||||
import thedarkcolour.kotlinforforge.neoforge.forge.LOADING_CONTEXT
|
import thedarkcolour.kotlinforforge.neoforge.forge.LOADING_CONTEXT
|
||||||
@ -139,6 +141,8 @@ object OverdriveThatMatters {
|
|||||||
|
|
||||||
DecimalProvider.register(MOD_BUS)
|
DecimalProvider.register(MOD_BUS)
|
||||||
BooleanProvider.register(MOD_BUS)
|
BooleanProvider.register(MOD_BUS)
|
||||||
|
EnhancedFeature.register(MOD_BUS)
|
||||||
|
EnhancedPlacementModifier.register(MOD_BUS)
|
||||||
AndroidResearchDescriptions.register(MOD_BUS)
|
AndroidResearchDescriptions.register(MOD_BUS)
|
||||||
AndroidResearchResults.register(MOD_BUS)
|
AndroidResearchResults.register(MOD_BUS)
|
||||||
|
|
||||||
|
@ -55,6 +55,9 @@ object MBuiltInRegistries {
|
|||||||
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "false"))
|
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "false"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val FEATURE by Delegate(MRegistries.FEATURE)
|
||||||
|
val PLACEMENT_MODIFIER by Delegate(MRegistries.PLACEMENT_MODIFIER)
|
||||||
|
|
||||||
internal fun register(bus: IEventBus) {
|
internal fun register(bus: IEventBus) {
|
||||||
delegates.forEach { bus.addListener(it::build) }
|
delegates.forEach { bus.addListener(it::build) }
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,9 @@ import ru.dbotthepony.mc.otm.player.android.AndroidFeatureType
|
|||||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescription
|
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescription
|
||||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResult
|
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResult
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacedFeature
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
|
||||||
|
|
||||||
object MRegistries {
|
object MRegistries {
|
||||||
private fun <T> k(name: String): ResourceKey<Registry<T>> {
|
private fun <T> k(name: String): ResourceKey<Registry<T>> {
|
||||||
@ -28,4 +31,7 @@ object MRegistries {
|
|||||||
val ANDROID_FEATURE = k<AndroidFeatureType<*>>("android_feature")
|
val ANDROID_FEATURE = k<AndroidFeatureType<*>>("android_feature")
|
||||||
val STACK_TYPE = k<StorageStack.Type<*>>("stack_type")
|
val STACK_TYPE = k<StorageStack.Type<*>>("stack_type")
|
||||||
val BOOLEAN_PROVIDER = k<BooleanProvider.Type<*>>("boolean_provider")
|
val BOOLEAN_PROVIDER = k<BooleanProvider.Type<*>>("boolean_provider")
|
||||||
|
val FEATURE = k<EnhancedFeature<*>>("feature")
|
||||||
|
val CONFIGURED_FEATURE = k<EnhancedFeature.Configured<*, *>>("configured_feature")
|
||||||
|
val PLACEMENT_MODIFIER = k<EnhancedPlacementModifier.Type<*>>("placement_modifier")
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import net.minecraft.core.registries.BuiltInRegistries
|
|||||||
import net.neoforged.bus.api.IEventBus
|
import net.neoforged.bus.api.IEventBus
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacedFeature
|
||||||
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
|
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
|
||||||
import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
|
import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
|
||||||
|
|
||||||
@ -16,4 +17,5 @@ object MWorldGenFeatures {
|
|||||||
|
|
||||||
val BLACK_HOLE_PLACER by registry.register("black_hole_placer") { BlackHolePlacerFeature }
|
val BLACK_HOLE_PLACER by registry.register("black_hole_placer") { BlackHolePlacerFeature }
|
||||||
val DEBUG_PLACEMENT by registry.register("debug") { DebugPlacerFeature }
|
val DEBUG_PLACEMENT by registry.register("debug") { DebugPlacerFeature }
|
||||||
|
val ENHANCED_FEATURE by registry.register("enhanced_feature") { EnhancedPlacedFeature }
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,299 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.worldgen
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine
|
||||||
|
import com.github.benmanes.caffeine.cache.Scheduler
|
||||||
|
import com.mojang.datafixers.util.Either
|
||||||
|
import com.mojang.datafixers.util.Pair
|
||||||
|
import com.mojang.serialization.Codec
|
||||||
|
import com.mojang.serialization.DataResult
|
||||||
|
import com.mojang.serialization.DynamicOps
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||||
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||||
|
import net.minecraft.Util
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Holder
|
||||||
|
import net.minecraft.core.SectionPos
|
||||||
|
import net.minecraft.world.level.ChunkPos
|
||||||
|
import net.minecraft.world.level.WorldGenLevel
|
||||||
|
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
|
||||||
|
import net.minecraft.world.level.levelgen.feature.Feature
|
||||||
|
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
|
||||||
|
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
|
||||||
|
import net.minecraft.world.level.levelgen.placement.PlacedFeature
|
||||||
|
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||||
|
import ru.dbotthepony.kommons.util.XXHash64
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource
|
||||||
|
import ru.dbotthepony.mc.otm.data.codec.minRange
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
|
||||||
|
import java.io.DataOutputStream
|
||||||
|
import java.time.Duration
|
||||||
|
import java.util.IdentityHashMap
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
private object NodeCodec : Codec<EnhancedPlacedFeature.Node> {
|
||||||
|
private val list = Codec.list(this)
|
||||||
|
|
||||||
|
override fun <T : Any?> encode(input: EnhancedPlacedFeature.Node, ops: DynamicOps<T>, prefix: T): DataResult<T> {
|
||||||
|
val result = HashMap<T, T>()
|
||||||
|
|
||||||
|
if (input.children.isNotEmpty()) {
|
||||||
|
val re = list.encodeStart(ops, input.children)
|
||||||
|
|
||||||
|
if (re.isError)
|
||||||
|
return re
|
||||||
|
|
||||||
|
result[ops.createString("children")] = re.resultOrPartial().get()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.contents.left().isPresent) {
|
||||||
|
val re = EnhancedPlacementModifier.CODEC.encodeStart(ops, input.contents.left().get())
|
||||||
|
|
||||||
|
if (re.isError)
|
||||||
|
return DataResult.error { "Failed to serialize placement modifier: ${re.error().get().message()}" }
|
||||||
|
|
||||||
|
result[ops.createString("modifier")] = re.resultOrPartial().get()
|
||||||
|
} else {
|
||||||
|
val re = EnhancedFeature.CODEC.encodeStart(ops, input.contents.right().get())
|
||||||
|
|
||||||
|
if (re.isError)
|
||||||
|
return DataResult.error { "Failed to serialize feature: ${re.error().get().message()}" }
|
||||||
|
|
||||||
|
result[ops.createString("feature")] = re.resultOrPartial().get()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ops.mergeToMap(prefix, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<EnhancedPlacedFeature.Node, T>> {
|
||||||
|
return ops.getMap(input).flatMap {
|
||||||
|
val children = it.get("children")
|
||||||
|
val modifier = it.get("modifier")
|
||||||
|
val feature = it.get("feature")
|
||||||
|
|
||||||
|
if (modifier != null && feature != null) {
|
||||||
|
return@flatMap DataResult.error { "Enhanced feature placement node should have either modifier or feature, but both are present" }
|
||||||
|
} else if (modifier == null && feature == null) {
|
||||||
|
return@flatMap DataResult.error { "Enhanced feature placement node contains no modifier and no feature" }
|
||||||
|
}
|
||||||
|
|
||||||
|
val deserializeChildren: List<EnhancedPlacedFeature.Node>
|
||||||
|
|
||||||
|
if (children != null) {
|
||||||
|
val result = list.decode(ops, children)
|
||||||
|
|
||||||
|
if (result.isError)
|
||||||
|
return@flatMap DataResult.error { result.error().get().message() }
|
||||||
|
|
||||||
|
deserializeChildren = result.map { it.first }.getOrThrow()
|
||||||
|
} else {
|
||||||
|
deserializeChildren = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modifier != null) {
|
||||||
|
return@flatMap EnhancedPlacementModifier.CODEC.decode(ops, modifier).map { Pair(EnhancedPlacedFeature.Node(deserializeChildren, Either.left(it.first)), ops.empty()) }
|
||||||
|
} else {
|
||||||
|
return@flatMap EnhancedFeature.CODEC.decode(ops, feature).map { Pair(EnhancedPlacedFeature.Node(deserializeChildren, Either.right(it.first)), ops.empty()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
|
||||||
|
RecordCodecBuilder.create {
|
||||||
|
it.group(
|
||||||
|
Codec.INT.minRange(0).fieldOf("chunk_scan_range").forGetter(Config::chunkScanRange),
|
||||||
|
Codec.LONG.fieldOf("seed_mix").forGetter(Config::seedMix),
|
||||||
|
NodeCodec.fieldOf("root").forGetter(Config::root),
|
||||||
|
).apply(it, ::Config)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
class Node(val children: List<Node>, val contents: Either<EnhancedPlacementModifier, Holder<EnhancedFeature.Configured<*, *>>>){
|
||||||
|
fun evaluate(context: EnhancedPlacementContext) {
|
||||||
|
evaluate(context, listOf(BlockPos(context.origin.minBlockX, 0, context.origin.minBlockZ)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluate(context: EnhancedPlacementContext, positions: List<BlockPos>) {
|
||||||
|
val actualPositions = if (contents.left().isPresent) {
|
||||||
|
contents.left().get().evaluate(context, positions)
|
||||||
|
} else {
|
||||||
|
positions
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents.right().isPresent) {
|
||||||
|
context.place(actualPositions, contents.right().get().value())
|
||||||
|
}
|
||||||
|
|
||||||
|
children.forEach {
|
||||||
|
it.evaluate(context.push(), actualPositions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: List<BlockPos>, val feature: EnhancedFeature.Configured<*, *>)
|
||||||
|
|
||||||
|
// TODO: extremely inefficient
|
||||||
|
private val placed = ArrayList<Placement>()
|
||||||
|
|
||||||
|
override fun place(context: EnhancedPlacementContext, positions: List<BlockPos>, feature: EnhancedFeature.Configured<*, *>) {
|
||||||
|
placed.add(Placement(context, positions, feature))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun place(context: FeaturePlaceContext<*>): Boolean {
|
||||||
|
var any = false
|
||||||
|
val pos = ChunkPos(context.origin())
|
||||||
|
|
||||||
|
for ((eContext, positions, feature) in placed) {
|
||||||
|
val filtered = positions.filter { SectionPos.blockToSectionCoord(it.x) == pos.x && SectionPos.blockToSectionCoord(it.z) == pos.z }
|
||||||
|
|
||||||
|
if (filtered.isNotEmpty()) {
|
||||||
|
any = feature.place(eContext, filtered, positions) || any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val level2cache = Caffeine.newBuilder()
|
||||||
|
.scheduler(Scheduler.systemScheduler())
|
||||||
|
.executor(Util.backgroundExecutor())
|
||||||
|
.expireAfterAccess(Duration.ofMinutes(10))
|
||||||
|
.weakKeys()
|
||||||
|
.build<WorldGenLevel, Cache<ChunkPos, GeneratedChunk>>()
|
||||||
|
|
||||||
|
private fun getCache(level: WorldGenLevel): Cache<ChunkPos, GeneratedChunk> {
|
||||||
|
return level2cache.get(level) {
|
||||||
|
Caffeine.newBuilder()
|
||||||
|
.scheduler(Scheduler.systemScheduler())
|
||||||
|
.executor(Util.backgroundExecutor())
|
||||||
|
.maximumSize(16384L)
|
||||||
|
.expireAfterWrite(Duration.ofMinutes(5))
|
||||||
|
.softValues()
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluate(context: FeaturePlaceContext<*>, chunkPos: ChunkPos): GeneratedChunk {
|
||||||
|
val bytes = FastByteArrayOutputStream()
|
||||||
|
val dataStream = DataOutputStream(bytes)
|
||||||
|
|
||||||
|
dataStream.writeLong(seedMix)
|
||||||
|
dataStream.writeInt(chunkPos.x)
|
||||||
|
dataStream.writeInt(chunkPos.z)
|
||||||
|
|
||||||
|
val hash = XXHash64()
|
||||||
|
hash.update(bytes.array, 0, bytes.length)
|
||||||
|
|
||||||
|
val random = GJRAND64RandomSource(context.level().seed, hash.digestAsLong())
|
||||||
|
|
||||||
|
val chunk = GeneratedChunk()
|
||||||
|
val enhancedContext = EnhancedPlacementContext(context.level(), context.chunkGenerator(), random, chunkPos, chunk)
|
||||||
|
root.evaluate(enhancedContext)
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
fun place(context: FeaturePlaceContext<*>): Boolean {
|
||||||
|
val cache = getCache(context.level())
|
||||||
|
val chunkPos = ChunkPos(context.origin())
|
||||||
|
val instances = ArrayList<GeneratedChunk>()
|
||||||
|
|
||||||
|
for (x in -chunkScanRange .. chunkScanRange) {
|
||||||
|
for (z in -chunkScanRange .. chunkScanRange) {
|
||||||
|
// floor, so chunk scan range of 1 will give square instead of diamond
|
||||||
|
val radius = sqrt(x.toDouble() * x + z.toDouble() * z).toInt()
|
||||||
|
|
||||||
|
if (radius <= chunkScanRange) {
|
||||||
|
val thisPos = ChunkPos(chunkPos.x + x, chunkPos.z + z)
|
||||||
|
instances.add(cache.get(thisPos) { evaluate(context, thisPos) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var any = false
|
||||||
|
instances.forEach { any = it.place(context) }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun place(context: FeaturePlaceContext<Config>): Boolean {
|
||||||
|
return context.config().place(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Builder {
|
||||||
|
private val root: Builder?
|
||||||
|
private val children = ArrayList<Builder>()
|
||||||
|
private val contents: Either<EnhancedPlacementModifier, Holder<EnhancedFeature.Configured<*, *>>>
|
||||||
|
|
||||||
|
constructor(root: EnhancedPlacementModifier) {
|
||||||
|
this.root = null
|
||||||
|
this.contents = Either.left(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(root: PlacementModifier) : this(EnhancedPlacementModifier.Wrapper(root))
|
||||||
|
|
||||||
|
constructor(root: Holder<EnhancedFeature.Configured<*, *>>) {
|
||||||
|
this.root = null
|
||||||
|
this.contents = Either.right(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(root: EnhancedFeature.Configured<*, *>) {
|
||||||
|
this.root = null
|
||||||
|
this.contents = Either.right(Holder.direct(root))
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(parent: Builder, root: PlacementModifier) : this(parent, EnhancedPlacementModifier.Wrapper(root))
|
||||||
|
|
||||||
|
private constructor(parent: Builder, root: EnhancedPlacementModifier) {
|
||||||
|
this.root = parent
|
||||||
|
this.contents = Either.left(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(parent: Builder, root: Holder<EnhancedFeature.Configured<*, *>>) {
|
||||||
|
this.root = parent
|
||||||
|
this.contents = Either.right(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(parent: Builder, root: EnhancedFeature.Configured<*, *>) {
|
||||||
|
this.root = parent
|
||||||
|
this.contents = Either.right(Holder.direct(root))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun then(action: PlacementModifier): Builder {
|
||||||
|
return Builder(root ?: this, action).also(children::add)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun then(action: EnhancedPlacementModifier): Builder {
|
||||||
|
return Builder(root ?: this, action).also(children::add)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun then(action: EnhancedFeature.Configured<*, *>): Builder {
|
||||||
|
return Builder(root ?: this, action).also(children::add)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun then(action: Holder<EnhancedFeature.Configured<*, *>>): Builder {
|
||||||
|
return Builder(root ?: this, action).also(children::add)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildNode(): Node {
|
||||||
|
return Node(children.map { it.buildNode() }, contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(
|
||||||
|
chunkScanRange: Int,
|
||||||
|
seedMix: Long,
|
||||||
|
): PlacedFeature {
|
||||||
|
if (root != null)
|
||||||
|
return root.build(chunkScanRange, seedMix)
|
||||||
|
|
||||||
|
return PlacedFeature(Holder.direct(ConfiguredFeature(EnhancedPlacedFeature, Config(chunkScanRange, seedMix, buildNode()))), listOf())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.worldgen
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.util.RandomSource
|
||||||
|
import net.minecraft.world.level.ChunkPos
|
||||||
|
import net.minecraft.world.level.WorldGenLevel
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGenerator
|
||||||
|
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
|
||||||
|
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
|
||||||
|
import net.minecraft.world.level.levelgen.placement.PlacementContext
|
||||||
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
|
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
|
class EnhancedPlacementContext {
|
||||||
|
fun interface Placer {
|
||||||
|
fun place(context: EnhancedPlacementContext, positions: List<BlockPos>, feature: EnhancedFeature.Configured<*, *>)
|
||||||
|
}
|
||||||
|
|
||||||
|
val level: WorldGenLevel
|
||||||
|
val generator: ChunkGenerator
|
||||||
|
val random: RandomSource
|
||||||
|
val origin: ChunkPos
|
||||||
|
private val placer: Placer
|
||||||
|
val vanillaContext: PlacementContext
|
||||||
|
|
||||||
|
private var parent: EnhancedPlacementContext? = null
|
||||||
|
private val variables = HashMap<PlacementVariable<*>, KOptional<*>>()
|
||||||
|
|
||||||
|
private fun <T> recursiveGet(index: PlacementVariable<T>): KOptional<T>? {
|
||||||
|
return variables[index] as KOptional<T>? ?: parent?.recursiveGet(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun <T> get(index: PlacementVariable<T>): KOptional<T> {
|
||||||
|
return recursiveGet(index) ?: KOptional()
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun <T> set(index: PlacementVariable<T>, value: T) {
|
||||||
|
variables[index] = KOptional(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> remove(index: PlacementVariable<T>): KOptional<T> {
|
||||||
|
val old = variables.put(index, KOptional<T>())
|
||||||
|
return old as KOptional<T>? ?: KOptional()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(index: Collection<PlacementVariable<*>>) {
|
||||||
|
index.forEach { remove(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
level: WorldGenLevel,
|
||||||
|
generator: ChunkGenerator,
|
||||||
|
random: RandomSource,
|
||||||
|
origin: ChunkPos,
|
||||||
|
placer: Placer,
|
||||||
|
) {
|
||||||
|
this.level = level
|
||||||
|
this.generator = generator
|
||||||
|
this.random = random
|
||||||
|
this.origin = origin
|
||||||
|
this.placer = placer
|
||||||
|
|
||||||
|
vanillaContext = PlacementContext(level, generator, Optional.empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(parent: EnhancedPlacementContext) {
|
||||||
|
this.level = parent.level
|
||||||
|
this.generator = parent.generator
|
||||||
|
this.random = parent.random
|
||||||
|
this.origin = parent.origin
|
||||||
|
this.placer = parent.placer
|
||||||
|
this.parent = parent
|
||||||
|
this.vanillaContext = parent.vanillaContext
|
||||||
|
}
|
||||||
|
|
||||||
|
fun push(): EnhancedPlacementContext {
|
||||||
|
return EnhancedPlacementContext(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun place(positions: List<BlockPos>, feature: EnhancedFeature.Configured<*, *>) {
|
||||||
|
placer.place(this, positions, feature)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <FC : FeatureConfiguration> vanillaFeatureContext(config: FC, position: BlockPos): FeaturePlaceContext<FC> {
|
||||||
|
return FeaturePlaceContext(Optional.empty(), level, generator, random, position, config)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.worldgen
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
|
||||||
|
class PlacementVariable<T>(val name: ResourceLocation) {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.worldgen.feature
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec
|
||||||
|
import com.mojang.serialization.MapCodec
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Holder
|
||||||
|
import net.minecraft.resources.RegistryFileCodec
|
||||||
|
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
|
||||||
|
import net.neoforged.bus.api.IEventBus
|
||||||
|
import net.neoforged.neoforge.registries.DataPackRegistryEvent
|
||||||
|
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
|
||||||
|
|
||||||
|
abstract class EnhancedFeature<FC>(codec: Codec<FC>) {
|
||||||
|
abstract fun place(context: EnhancedPlacementContext, config: FC, positions: List<BlockPos>, allPositions: List<BlockPos>): Boolean
|
||||||
|
|
||||||
|
data class Configured<F : EnhancedFeature<FC>, FC>(val feature: F, val config: FC) {
|
||||||
|
fun place(context: EnhancedPlacementContext, positions: List<BlockPos>, allPositions: List<BlockPos>): Boolean {
|
||||||
|
return feature.place(context, config, positions, allPositions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val codec: MapCodec<Configured<*, FC>> = codec.fieldOf("config").xmap({ Configured(this, it) }, { it.config })
|
||||||
|
fun configure(config: FC) = Configured(this, config)
|
||||||
|
|
||||||
|
object Wrapper : EnhancedFeature<Holder<ConfiguredFeature<*, *>>>(ConfiguredFeature.CODEC) {
|
||||||
|
override fun place(
|
||||||
|
context: EnhancedPlacementContext,
|
||||||
|
config: Holder<ConfiguredFeature<*, *>>,
|
||||||
|
positions: List<BlockPos>,
|
||||||
|
allPositions: List<BlockPos>
|
||||||
|
): Boolean {
|
||||||
|
var any = false
|
||||||
|
positions.forEach { any = config.value().place(context.level, context.generator, context.random, it) || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
fun configure(config: ConfiguredFeature<*, *>) = configure(Holder.direct(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val registrar = MDeferredRegister(MRegistries.FEATURE)
|
||||||
|
|
||||||
|
val DIRECT_CODEC: Codec<Configured<*, *>> by lazy {
|
||||||
|
MBuiltInRegistries.FEATURE.byNameCodec().dispatch({ it.feature }, { it.codec })
|
||||||
|
}
|
||||||
|
|
||||||
|
val CODEC: Codec<Holder<Configured<*, *>>> by lazy {
|
||||||
|
RegistryFileCodec.create(MRegistries.CONFIGURED_FEATURE, DIRECT_CODEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
registrar.register("wrapper") { Wrapper }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerRegistry(event: DataPackRegistryEvent.NewRegistry) {
|
||||||
|
event.dataPackRegistry(MRegistries.CONFIGURED_FEATURE, DIRECT_CODEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun register(bus: IEventBus) {
|
||||||
|
registrar.register(bus)
|
||||||
|
bus.addListener(::registerRegistry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
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.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
|
||||||
|
|
||||||
|
interface EnhancedPlacementModifier {
|
||||||
|
interface Type<T : EnhancedPlacementModifier> {
|
||||||
|
val codec: MapCodec<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun evaluate(context: EnhancedPlacementContext, positions: List<BlockPos>): List<BlockPos>
|
||||||
|
val type: Type<*>
|
||||||
|
|
||||||
|
class Wrapper(val parent: PlacementModifier) : EnhancedPlacementModifier {
|
||||||
|
override fun evaluate(context: EnhancedPlacementContext, positions: List<BlockPos>): List<BlockPos> {
|
||||||
|
return positions.stream().flatMap { parent.getPositions(context.vanillaContext, context.random, it) }.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val type: Type<*>
|
||||||
|
get() = Companion
|
||||||
|
|
||||||
|
companion object : Type<Wrapper> {
|
||||||
|
override val codec: MapCodec<Wrapper> = PlacementModifier.CODEC.xmap(::Wrapper, Wrapper::parent).fieldOf("parent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val registrar = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
|
||||||
|
|
||||||
|
init {
|
||||||
|
registrar.register("wrapper") { Wrapper.Companion }
|
||||||
|
}
|
||||||
|
|
||||||
|
val CODEC: Codec<EnhancedPlacementModifier> by lazy {
|
||||||
|
MBuiltInRegistries.PLACEMENT_MODIFIER.byNameCodec().dispatch({ it.type }, { it.codec })
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun register(bus: IEventBus) {
|
||||||
|
registrar.register(bus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user