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.addTags
|
||||
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.objects.ColoredDecorativeBlock
|
||||
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
|
||||
@ -560,6 +561,7 @@ object DataGen {
|
||||
val registrySetBuilder = RegistrySetBuilder()
|
||||
.add(Registries.DAMAGE_TYPE, ::registerDamageTypes)
|
||||
.add(Registries.CONFIGURED_FEATURE, ::registerConfiguredFeatures)
|
||||
.add(MRegistries.CONFIGURED_FEATURE, ::registerEnhancedConfiguredFeatures)
|
||||
.add(Registries.PLACED_FEATURE, ::registerPlacedFeatures)
|
||||
.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.data.world.BooleanProvider
|
||||
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.registry.game.MBlocks
|
||||
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.DebugPlacerFeature
|
||||
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
|
||||
import ru.dbotthepony.mc.otm.worldgen.placement.ChainPlacement
|
||||
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousPlacement
|
||||
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement
|
||||
@ -42,12 +45,30 @@ import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
|
||||
private object ConfiguredFeatures {
|
||||
val TRITANIUM_ORE = key("tritanium_ore")
|
||||
val TRITANIUM_ORE_SMALL = key("tritanium_ore_small")
|
||||
val DILITHIUM = key("dilithium")
|
||||
val DILITHIUM = ekey("dilithium")
|
||||
val BLACK_HOLE = key("black_hole")
|
||||
|
||||
private fun key(name: String): ResourceKey<ConfiguredFeature<*, *>> {
|
||||
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<*, *>>) {
|
||||
@ -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())))
|
||||
}
|
||||
|
||||
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(
|
||||
MWorldGenFeatures.BLACK_HOLE_PLACER,
|
||||
BlackHolePlacerFeature.Config(Decimal("0.25"), Decimal(1))))
|
||||
@ -93,6 +105,7 @@ private object PlacedFeatures {
|
||||
|
||||
fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
||||
val configured = context.lookup(Registries.CONFIGURED_FEATURE)
|
||||
val econfigured = context.lookup(MRegistries.CONFIGURED_FEATURE)
|
||||
|
||||
run {
|
||||
val ore = configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE)
|
||||
@ -169,13 +182,14 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
||||
}
|
||||
|
||||
run {
|
||||
val ore = configured.getOrThrow(ConfiguredFeatures.DILITHIUM)
|
||||
val ore = econfigured.getOrThrow(ConfiguredFeatures.DILITHIUM)
|
||||
|
||||
val ringularity = OneOfFloatProvider.of(
|
||||
ClampedNormalFloat.of(0.4f, 0.2f, -2f, 2f),
|
||||
ClampedNormalFloat.of(-0.4f, 0.2f, -2f, 2f),
|
||||
)
|
||||
|
||||
/*
|
||||
context.register(PlacedFeatures.DILITHIUM, PlacedFeature(
|
||||
ore,
|
||||
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)
|
||||
|
@ -92,6 +92,8 @@ import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
|
||||
import ru.dbotthepony.mc.otm.server.MCommands
|
||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
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.FORGE_BUS
|
||||
import thedarkcolour.kotlinforforge.neoforge.forge.LOADING_CONTEXT
|
||||
@ -139,6 +141,8 @@ object OverdriveThatMatters {
|
||||
|
||||
DecimalProvider.register(MOD_BUS)
|
||||
BooleanProvider.register(MOD_BUS)
|
||||
EnhancedFeature.register(MOD_BUS)
|
||||
EnhancedPlacementModifier.register(MOD_BUS)
|
||||
AndroidResearchDescriptions.register(MOD_BUS)
|
||||
AndroidResearchResults.register(MOD_BUS)
|
||||
|
||||
|
@ -55,6 +55,9 @@ object MBuiltInRegistries {
|
||||
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) {
|
||||
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.AndroidResearchResult
|
||||
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 {
|
||||
private fun <T> k(name: String): ResourceKey<Registry<T>> {
|
||||
@ -28,4 +31,7 @@ object MRegistries {
|
||||
val ANDROID_FEATURE = k<AndroidFeatureType<*>>("android_feature")
|
||||
val STACK_TYPE = k<StorageStack.Type<*>>("stack_type")
|
||||
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 ru.dbotthepony.kommons.util.getValue
|
||||
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.DebugPlacerFeature
|
||||
|
||||
@ -16,4 +17,5 @@ object MWorldGenFeatures {
|
||||
|
||||
val BLACK_HOLE_PLACER by registry.register("black_hole_placer") { BlackHolePlacerFeature }
|
||||
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