Compare commits

...

33 Commits

Author SHA1 Message Date
159125fb4b
Per-block variable storage in enhanced placement 2025-03-25 18:59:53 +07:00
727111cf4a
Fix blockposset subset forgetting blocks at negative y values 2025-03-25 14:13:19 +07:00
d08a928e04
a 2025-03-25 13:22:29 +07:00
398bd532f4
Remove "inefficinet" comment since placements are now efficiently filtered 2025-03-25 12:26:43 +07:00
7e570747c2
Bring back bigger caches since memory usage now is normal again 2025-03-25 12:25:53 +07:00
d683ea1e38
More efficient ellipsoid placement 2025-03-25 12:16:25 +07:00
f74dbbd84a
Fix level being wrong when placing features in-world 2025-03-25 12:10:28 +07:00
2993ae61ca
man. 2025-03-25 11:52:59 +07:00
9577e205e7
Set last returned index to -1 when removing 2025-03-25 11:52:01 +07:00
ba83b89476
Ooprs!!1 2025-03-25 11:11:23 +07:00
bf6fec7753
Create shared state in EnhancedPlacementContext for memory efficiency 2025-03-25 11:09:47 +07:00
0af5fb7301
Use LinkedList in generated chunk for memory efficiency 2025-03-25 11:09:22 +07:00
a4e40bd464
Provide BlockPosSet for vastly improved memory efficiency 2025-03-25 11:03:35 +07:00
07b295ce45
Add enhanced variants for placement modifiers 2025-03-25 00:07:55 +07:00
ba492e0cee
Merge branch '1.21' into worldgen-placement-providers 2025-03-24 23:34:18 +07:00
ddcfe11780
Caused by: java.lang.NoClassDefFoundError: ru/dbotthepony/mc/otm/android/feature/LimbOverclockingFeature 2025-03-24 23:33:52 +07:00
dca02893a4
Make generation cache expire much quicker 2025-03-24 21:11:59 +07:00
ab6e3ad87f
Use sets only for final placements, use lists for intermediate 2025-03-24 20:16:57 +07:00
78c0a5d717
Fix platform declaration clash 2025-03-24 19:32:18 +07:00
e150b57b65
Add passthrough placement modifier, and add argument-less placed feature builder 2025-03-24 19:30:17 +07:00
1e6e38ea7d
Remove enormous placement 2025-03-24 19:27:36 +07:00
1acd105925
Move tritanium worms to enhanced placed feature 2025-03-24 19:27:03 +07:00
5d05fe3bb4
Use tree set 2025-03-24 19:22:35 +07:00
bc81103e38
Use sets for block positions instead of lists
it makes no sense to give same position twice
2025-03-24 19:07:25 +07:00
18f9bc2654
Enhanced placements prototyping 2025-03-24 18:42:52 +07:00
03a1ab9197
Merge branch '1.21' into worldgen-placement-providers 2025-03-24 18:15:59 +07:00
6226621b95
god damn it 2025-03-24 18:15:53 +07:00
055ca7ec43
Merge branch '1.21' into worldgen-placement-providers 2025-03-24 18:15:05 +07:00
1bb612b07c
Fixes for last commit 2025-03-24 18:14:47 +07:00
ca67e796da
Update boolean provider to use MRegistries 2025-03-24 14:01:32 +07:00
9ee561d0fd
Merge branch '1.21' into worldgen-placement-providers 2025-03-24 13:58:58 +07:00
588b9c1b7c
Put all otm registries in one place 2025-03-24 13:55:36 +07:00
e399fa8b68
Mark Boolean Provider register as internal 2025-03-23 12:34:05 +07:00
41 changed files with 1464 additions and 449 deletions

View File

@ -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)

View File

@ -28,26 +28,51 @@ 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
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedChainPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedCountPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedSplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.SplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
import ru.dbotthepony.mc.otm.worldgen.wrap
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 +90,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 +109,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)
@ -115,90 +132,88 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
)
))
context.register(PlacedFeatures.WORM_TRITANIUM, PlacedFeature(
configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE_SMALL),
listOf(
EnormousPlacement(
chunkScanRange = 24,
seedMix = 9284343575495L,
children = listOf(
RarityFilter.onAverageOnceEvery(140),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(-40), 15.0)),
SplitPlacement(
// "heart"
EllipsoidPlacement(
count = UniformInt.of(600, 900),
xLength = UniformFloat.of(9f, 12f),
yLength = UniformFloat.of(9f, 12f),
zLength = UniformFloat.of(9f, 12f),
x = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
y = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
z = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
context.register(
PlacedFeatures.WORM_TRITANIUM,
EnhancedPlacedFeature.Builder(RarityFilter.onAverageOnceEvery(140))
.then(InSquarePlacement.spread())
.then(HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(-40), 15.0)))
.then(
EnhancedSplitPlacement(
// "heart"
EllipsoidPlacement(
count = UniformInt.of(600, 900),
xLength = UniformFloat.of(9f, 12f),
yLength = UniformFloat.of(9f, 12f),
zLength = UniformFloat.of(9f, 12f),
x = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
y = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
z = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
),
// "branches"
EnhancedChainPlacement(
EnhancedCountPlacement(UniformInt.of(2, 7)),
WormPlacement(
length = UniformInt.of(60, 120),
turnRateXY = WormPlacement.normalDistributedTurnRate(2f, 3f),
initialAngleXY = WormPlacement.normalDistributedTurnRate(3f, 4f),
turnRateXZ = WormPlacement.normalDistributedTurnRate(30f),
turnSpeedXZ = WormPlacement.constantTurnRate(10f),
turnSpeedXY = WormPlacement.constantTurnRate(4f),
turnChanceXY = BooleanProvider.Unbiased(6),
turnChanceXZ = BooleanProvider.BiasedLinear(0.6f, 5),
maxTravelUp = 90,
maxTravelDown = 10,
),
// "branches"
ChainPlacement(
CountPlacement.of(UniformInt.of(2, 7)),
WormPlacement(
length = UniformInt.of(60, 120),
turnRateXY = WormPlacement.normalDistributedTurnRate(2f, 3f),
initialAngleXY = WormPlacement.normalDistributedTurnRate(3f, 4f),
turnRateXZ = WormPlacement.normalDistributedTurnRate(30f),
turnSpeedXZ = WormPlacement.constantTurnRate(10f),
turnSpeedXY = WormPlacement.constantTurnRate(4f),
turnChanceXY = BooleanProvider.Unbiased(6),
turnChanceXZ = BooleanProvider.BiasedLinear(0.6f, 5),
maxTravelUp = 90,
maxTravelDown = 10,
),
EllipsoidPlacement(
count = UniformInt.of(3, 6),
xLength = ConstantFloat.of(4f),
yLength = ConstantFloat.of(4f),
zLength = ConstantFloat.of(4f),
x = ClampedNormalFloat.of(0f, 0.2f, -1f, 1f),
y = ClampedNormalFloat.of(0f, 0.2f, -1f, 1f),
z = ClampedNormalFloat.of(0f, 0.2f, -1f, 1f),
)
EllipsoidPlacement(
count = UniformInt.of(3, 6),
xLength = ConstantFloat.of(4f),
yLength = ConstantFloat.of(4f),
zLength = ConstantFloat.of(4f),
x = ClampedNormalFloat.of(0f, 0.2f, -1f, 1f),
y = ClampedNormalFloat.of(0f, 0.2f, -1f, 1f),
z = ClampedNormalFloat.of(0f, 0.2f, -1f, 1f),
)
)
)
)
)
))
.then(configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE_SMALL))
.build(
chunkScanRange = 24,
seedMix = 9284343575495L,
)
)
}
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(
EnormousPlacement(
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),
) as EnhancedPlacementModifier
)
.then(ore)
.build(
chunkScanRange = 6,
seedMix = 237483209523709234L,
children = listOf(
RarityFilter.onAverageOnceEvery(120),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)),
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),
)
),
)
)
))
)
}
val blackHole = configured.getOrThrow(ConfiguredFeatures.BLACK_HOLE)

View File

@ -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
@ -105,6 +107,8 @@ object OverdriveThatMatters {
fun loc(path: String): ResourceLocation = ResourceLocation.fromNamespaceAndPath(MOD_ID, path)
init {
MBuiltInRegistries.register(MOD_BUS)
MBlocks.register(MOD_BUS)
MFluids.register(MOD_BUS)
MBlockEntities.register(MOD_BUS)
@ -137,9 +141,9 @@ object OverdriveThatMatters {
DecimalProvider.register(MOD_BUS)
BooleanProvider.register(MOD_BUS)
AndroidResearchDescription.register(MOD_BUS)
EnhancedFeature.register(MOD_BUS)
EnhancedPlacementModifier.register(MOD_BUS)
AndroidResearchDescriptions.register(MOD_BUS)
AndroidResearchResult.register(MOD_BUS)
AndroidResearchResults.register(MOD_BUS)
AbstractRegistryAction.register(MOD_BUS)

View File

@ -0,0 +1,207 @@
package ru.dbotthepony.mc.otm.core.collect
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap
import it.unimi.dsi.fastutil.objects.ObjectIterators
import net.minecraft.core.BlockPos
import net.minecraft.core.SectionPos
import net.minecraft.world.level.ChunkPos
import ru.dbotthepony.kommons.collect.iterateSetBits
import java.util.BitSet
import java.util.Collections
class BlockPosSet : MutableSet<BlockPos> {
private class Segment {
val bits = BitSet(16 * 16 * 16)
private fun pos2index(pos: BlockPos): Int {
val x = pos.x.and(15)
val z = pos.z.and(15) shl 4
val y = pos.y.and(15) shl 8
return x or y or z
}
private fun index2pos(index: Int, gX: Int, gY: Int, gZ: Int): BlockPos {
val x = index.and(15) + gX
val z = index.ushr(4).and(15) + gZ
val y = index.ushr(8).and(15) + gY
return BlockPos(x, y, z)
}
operator fun contains(pos: BlockPos): Boolean {
return bits[pos2index(pos)]
}
fun add(pos: BlockPos): Boolean {
val index = pos2index(pos)
if (bits[index])
return false
bits[index] = true
return true
}
fun remove(pos: BlockPos): Boolean {
val index = pos2index(pos)
if (!bits[index])
return false
bits[index] = false
return true
}
fun iterator(gX: Int, gY: Int, gZ: Int): MutableIterator<BlockPos> {
return object : MutableIterator<BlockPos> {
private val iterator = bits.iterateSetBits(4096)
private var last = -1
override fun hasNext(): Boolean {
return iterator.hasNext()
}
override fun next(): BlockPos {
last = iterator.nextInt()
return index2pos(last, gX, gY, gZ)
}
override fun remove() {
if (last == -1)
throw NoSuchElementException()
bits[last] = false
last = -1
}
}
}
val isEmpty: Boolean
get() = bits.isEmpty
val size: Int
get() = bits.cardinality()
}
private val segments = Object2ObjectRBTreeMap<SectionPos, Segment>(Vec3iHashStrategy)
fun subset(pos: ChunkPos): Set<BlockPos> {
val result = BlockPosSet()
for (key in segments.keys.iterator(SectionPos.of(pos.x, Int.MIN_VALUE, pos.z))) {
if (key.x != pos.x || key.z != pos.z) break
result.segments[key] = segments[key]
}
return Collections.unmodifiableSet(result)
}
override fun add(element: BlockPos): Boolean {
return segments.computeIfAbsent(SectionPos.of(element), Object2ObjectFunction { Segment() }).add(element)
}
override val size: Int
get() = segments.values.sumOf { it.size }
override fun addAll(elements: Collection<BlockPos>): Boolean {
var any = false
elements.forEach { any = add(it) || any }
return any
}
override fun clear() {
segments.clear()
}
override fun contains(element: BlockPos): Boolean {
return segments[SectionPos.of(element)]?.contains(element) == true
}
override fun containsAll(elements: Collection<BlockPos>): Boolean {
return elements.all { contains(it) }
}
override fun isEmpty(): Boolean {
var any = true
segments.entries.removeIf { (_, s) ->
any = any && s.isEmpty
s.isEmpty
}
return any
}
override fun iterator(): MutableIterator<BlockPos> {
return object : MutableIterator<BlockPos> {
private val iterator = segments.entries.iterator()
private var current: MutableIterator<BlockPos> = ObjectIterators.emptyIterator()
private var last = current
private var foundNext = false
private fun findNext() {
if (!foundNext) {
foundNext = true
while (!current.hasNext() && iterator.hasNext()) {
val (pos, itr) = iterator.next()
current = itr.iterator(pos.x shl 4, pos.y shl 4, pos.z shl 4)
}
}
}
override fun hasNext(): Boolean {
findNext()
return current.hasNext()
}
override fun next(): BlockPos {
findNext()
last = current
foundNext = false
val blockPos = current.next()
return blockPos
}
override fun remove() {
last.remove()
}
}
}
override fun remove(element: BlockPos): Boolean {
val index = SectionPos.of(element)
val segment = segments[index] ?: return false
if (segment.remove(element)) {
if (segment.isEmpty)
segments.remove(index)
return true
}
return false
}
override fun removeAll(elements: Collection<BlockPos>): Boolean {
var any = false
elements.forEach { any = remove(it) || any }
return any
}
override fun retainAll(elements: Collection<BlockPos>): Boolean {
return removeIf { it !in elements }
}
override fun equals(other: Any?): Boolean {
return this === other || other is Set<*> && other.size == size && other.containsAll(this)
}
override fun hashCode(): Int {
return segments.hashCode()
}
override fun toString(): String {
return "BlockPosSet[${joinToString(", ")}]"
}
}

View File

@ -0,0 +1,32 @@
package ru.dbotthepony.mc.otm.core.collect
import it.unimi.dsi.fastutil.Hash
import it.unimi.dsi.fastutil.HashCommon
import net.minecraft.core.BlockPos
import net.minecraft.core.Vec3i
object Vec3iHashStrategy : Hash.Strategy<Vec3i>, Comparator<Vec3i> {
override fun equals(a: Vec3i?, b: Vec3i?): Boolean {
return a == b
}
// while this avoids collisions, it is rather slow when compared to just using a tree set here
override fun hashCode(o: Vec3i?): Int {
o ?: return 0
return HashCommon.murmurHash3(o.x.toLong().and(1 shl 26 - 1) or o.z.toLong().and(1 shl 26 - 1).shl(26) or o.y.toLong().and(1 shl 12 - 1).shl(52)).toInt()
}
override fun compare(o1: Vec3i?, o2: Vec3i?): Int {
if (o1 == null && o2 == null)
return 0
else if (o1 == null)
return -1 // nulls come first
else if (o2 == null)
return 1
var cmp = o1.x.compareTo(o2.x)
if (cmp == 0) cmp = o1.z.compareTo(o2.z)
if (cmp == 0) cmp = o1.y.compareTo(o2.y)
return cmp
}
}

View File

@ -5,11 +5,10 @@ import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.RandomSource
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.data.codec.inRange
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import ru.dbotthepony.mc.otm.registry.MRegistries
interface BooleanProvider {
interface Type<T : BooleanProvider> {
@ -123,14 +122,7 @@ interface BooleanProvider {
}
companion object {
private val registryHolder = RegistryDelegate<Type<*>>("boolean_provider") {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "zero"))
}
val registry by registryHolder
val registryKey get() = registryHolder.key
private val registrar = MDeferredRegister(registryKey)
private val registrar = MDeferredRegister(MRegistries.BOOLEAN_PROVIDER)
init {
registrar.register("unbiased") { Unbiased.Companion }
@ -140,11 +132,10 @@ interface BooleanProvider {
}
val CODEC: Codec<BooleanProvider> by lazy {
registry.byNameCodec().dispatch({ it.type }, { it.codec })
MBuiltInRegistries.BOOLEAN_PROVIDER.byNameCodec().dispatch({ it.type }, { it.codec })
}
fun register(bus: IEventBus) {
bus.addListener(registryHolder::build)
internal fun register(bus: IEventBus) {
registrar.register(bus)
}
}

View File

@ -7,12 +7,12 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.RandomSource
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.nextDecimal
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import ru.dbotthepony.mc.otm.registry.MRegistries
fun interface SampledDecimal {
fun sample(source: RandomSource): Decimal
@ -28,33 +28,25 @@ abstract class DecimalProvider : SampledDecimal {
abstract val type: Type<*>
companion object {
private val registryHolder = RegistryDelegate<Type<*>>("decimal_provider_type") {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "zero"))
}
val CODEC: Codec<DecimalProvider> by lazy {
Codec
.either(DecimalCodec, registry.byNameCodec().dispatch({ it.type }, { it.codec }))
.either(DecimalCodec, MBuiltInRegistries.DECIMAL_PROVIDER_TYPE.byNameCodec().dispatch({ it.type }, { it.codec }))
.xmap(
{ c -> c.map(::ConstantDecimal, { it }) },
{ if (it.type === ConstantDecimal) Either.left(it.minValue) else Either.right(it) }
)
}
val registry by registryHolder
val registryKey get() = registryHolder.key
private val registror = MDeferredRegister(registryKey, OverdriveThatMatters.MOD_ID)
private val registrar = MDeferredRegister(MRegistries.DECIMAL_PROVIDER_TYPE, OverdriveThatMatters.MOD_ID)
init {
registror.register("zero") { ConstantDecimal.Zero }
registror.register("constant") { ConstantDecimal }
registror.register("uniform") { UniformDecimal }
registrar.register("zero") { ConstantDecimal.Zero }
registrar.register("constant") { ConstantDecimal }
registrar.register("uniform") { UniformDecimal }
}
internal fun register(bus: IEventBus) {
bus.addListener(registryHolder::build)
registror.register(bus)
registrar.register(bus)
}
}
}

View File

@ -13,8 +13,9 @@ import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import ru.dbotthepony.mc.otm.registry.MRegistries
import java.util.*
abstract class AbstractRegistryAction(
@ -99,12 +100,7 @@ abstract class AbstractRegistryAction(
}
companion object {
private val registryDelegate = RegistryDelegate<Type<*>>("matter_registry_action") { sync(false) }
val registryKey get() = registryDelegate.key
val registry by registryDelegate
private val registrar = MDeferredRegister(registryKey, OverdriveThatMatters.MOD_ID)
private val registrar = MDeferredRegister(MRegistries.MATTER_REGISTRY_ACTION, OverdriveThatMatters.MOD_ID)
init {
registrar.register("insert") { InsertAction.Companion }
@ -116,11 +112,10 @@ abstract class AbstractRegistryAction(
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(registryDelegate::build)
}
val CODEC: Codec<AbstractRegistryAction> by lazy {
registry.byNameCodec().dispatch({ it.type }, { it.codec })
MBuiltInRegistries.MATTER_REGISTRY_ACTION.byNameCodec().dispatch({ it.type }, { it.codec })
}
}
}

View File

@ -15,6 +15,7 @@ import net.minecraft.world.item.Item
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.PredicatedCodecList
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import java.util.Optional
import java.util.function.Predicate
@ -29,8 +30,8 @@ class ComputeAction(
it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.byNameCodec().fieldOf("matterFunction").forGetter(Constant::matterFunction),
IMatterFunction.registry.byNameCodec().fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("matterFunction").forGetter(Constant::matterFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
).apply(it, ::Constant)
} to Predicate { true }
}
@ -40,7 +41,7 @@ class ComputeAction(
it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.byNameCodec().fieldOf("function").forGetter(Constant::matterFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("function").forGetter(Constant::matterFunction),
).apply(it) { a, b, c -> Constant(a, b, c, c) }
} to Predicate { it.matterFunction == it.complexityFunction }
}
@ -49,8 +50,8 @@ class ComputeAction(
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("value").forGetter(Constant::matter),
IMatterFunction.registry.byNameCodec().fieldOf("matterFunction").forGetter(Constant::matterFunction),
IMatterFunction.registry.byNameCodec().fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("matterFunction").forGetter(Constant::matterFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
).apply(it) { a, b, c -> Constant(a, a.toDouble(), b, c) }
} to Predicate { it.matter == Decimal(it.complexity.toString()) }
}
@ -59,7 +60,7 @@ class ComputeAction(
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("value").forGetter(Constant::matter),
IMatterFunction.registry.byNameCodec().fieldOf("function").forGetter(Constant::matterFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("function").forGetter(Constant::matterFunction),
).apply(it) { a, b -> Constant(a, a.toDouble(), b, b) }
} to Predicate { it.matter == Decimal(it.complexity.toString()) && it.matterFunction == it.complexityFunction }
}
@ -68,7 +69,7 @@ class ComputeAction(
RecordCodecBuilder.create<Constant> {
it.group(
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.byNameCodec().fieldOf("function").forGetter(Constant::complexityFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("function").forGetter(Constant::complexityFunction),
).apply(it) { a, b -> Constant(Decimal.ZERO, a.toDouble(), IMatterFunction.NOOP, b) }
} to Predicate { it.matterFunction == IMatterFunction.NOOP }
}
@ -77,7 +78,7 @@ class ComputeAction(
RecordCodecBuilder.create<Constant> {
it.group(
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.byNameCodec().fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
).apply(it) { a, b -> Constant(Decimal.ZERO, a.toDouble(), IMatterFunction.NOOP, b) }
} to Predicate { it.matterFunction == IMatterFunction.NOOP }
}
@ -86,7 +87,7 @@ class ComputeAction(
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
IMatterFunction.registry.byNameCodec().fieldOf("function").forGetter(Constant::matterFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("function").forGetter(Constant::matterFunction),
).apply(it) { a, b -> Constant(a, 0.0, b, IMatterFunction.NOOP) }
} to Predicate { it.complexityFunction == IMatterFunction.NOOP }
}
@ -95,7 +96,7 @@ class ComputeAction(
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
IMatterFunction.registry.byNameCodec().fieldOf("matterFunction").forGetter(Constant::matterFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("matterFunction").forGetter(Constant::matterFunction),
).apply(it) { a, b -> Constant(a, 0.0, b, IMatterFunction.NOOP) }
} to Predicate { it.complexityFunction == IMatterFunction.NOOP }
}
@ -182,7 +183,7 @@ class ComputeAction(
RecordCodecBuilder.create {
it.group(
TargetCodec.fieldOf("id").forGetter(Value::id),
IMatterFunction.registry.byNameCodec().fieldOf("function").forGetter(Value::matterFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("function").forGetter(Value::matterFunction),
).apply(it, ::Value)
}
}
@ -191,8 +192,8 @@ class ComputeAction(
RecordCodecBuilder.create {
it.group(
TargetCodec.fieldOf("id").forGetter(Value::id),
IMatterFunction.registry.byNameCodec().fieldOf("matterFunction").forGetter(Value::matterFunction),
IMatterFunction.registry.byNameCodec().fieldOf("complexityFunction").forGetter(Value::complexityFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("matterFunction").forGetter(Value::matterFunction),
MBuiltInRegistries.MATTER_FUNCTION.byNameCodec().fieldOf("complexityFunction").forGetter(Value::complexityFunction),
).apply(it, ::Value)
}
}

View File

@ -7,7 +7,7 @@ import ru.dbotthepony.mc.otm.matter.SimpleMatterFunction.DecimalFunction
import ru.dbotthepony.mc.otm.matter.SimpleMatterFunction.DoubleFunction
import ru.dbotthepony.mc.otm.matter.SimpleMatterFunction.IntFunction
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import ru.dbotthepony.mc.otm.registry.MRegistries
interface IMatterFunction {
fun updateValue(self: Int, other: Int): Int
@ -15,12 +15,7 @@ interface IMatterFunction {
fun updateValue(self: Double, other: Double): Double
companion object : IMatterFunction {
private val registryDelegate = RegistryDelegate<IMatterFunction>("matter_function") { sync(false) }
val registryKey get() = registryDelegate.key
val registry by registryDelegate
private val registrar = MDeferredRegister(registryKey, OverdriveThatMatters.MOD_ID)
private val registrar = MDeferredRegister(MRegistries.MATTER_FUNCTION, OverdriveThatMatters.MOD_ID)
init {
registrar.register("noop") { this }
@ -38,7 +33,6 @@ interface IMatterFunction {
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(registryDelegate::build)
}
override fun updateValue(self: Int, other: Int): Int {

View File

@ -102,8 +102,9 @@ import ru.dbotthepony.mc.otm.core.writeItemType
import ru.dbotthepony.mc.otm.matter.MatterManager.Finder
import ru.dbotthepony.mc.otm.milliTime
import ru.dbotthepony.mc.otm.onceServer
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.secondTime
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
import ru.dbotthepony.mc.otm.storage.StorageStack
@ -397,12 +398,10 @@ object MatterManager {
}
private object Resolver : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), FINDER_DIRECTORY) {
val delegate = RegistryDelegate<Finder>("recipe_finder") { sync(false) }
var ready = false
private set
val registrar = MDeferredRegister(delegate.key, OverdriveThatMatters.MOD_ID)
val registrar = MDeferredRegister(MRegistries.RECIPE_FINDER, OverdriveThatMatters.MOD_ID)
init {
registrar.register("simple") {
@ -639,11 +638,11 @@ object MatterManager {
val location = (json["type"] ?: throw JsonSyntaxException("Missing resolver type")).let { ResourceLocation.tryParse(it.asString) } ?: throw JsonSyntaxException("Invalid resolver type: ${json["type"]}")
if (!recipeFinders.containsKey(location)) {
if (!MBuiltInRegistries.RECIPE_FINDER.containsKey(location)) {
throw JsonParseException("Resolver type $location does not exist (in $key)")
}
val resolver = recipeFinders.get(location) ?: throw ConcurrentModificationException()
val resolver = MBuiltInRegistries.RECIPE_FINDER.get(location) ?: throw ConcurrentModificationException()
builder.put(key, resolver to json)
}
@ -1495,20 +1494,6 @@ object MatterManager {
return Registry.direct(value)
}
/**
* Access recipe finders registry
*
* @throws IllegalStateException if calling too early
*/
@JvmStatic val recipeFinders get() = Resolver.delegate.get()
/**
* Access recipe finders registry key
*
* Use this with your [DeferredRegister]
*/
@JvmStatic val recipeFindersKey get() = Resolver.delegate.key
private val commentary = Reference2ObjectOpenHashMap<Item, ArrayList<Component>>()
@JvmStatic
@ -1533,7 +1518,6 @@ object MatterManager {
}
internal fun initialize(bus: IEventBus) {
bus.addListener(Resolver.delegate::build)
Resolver.registrar.register(bus)
}

View File

@ -11,6 +11,7 @@ import net.minecraft.world.item.Item
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.simpleCodec
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
class UpdateAction(
id: Either<ResourceLocation, TagKey<Item>>,
@ -22,7 +23,7 @@ class UpdateAction(
data class MatterFunction(val function: IMatterFunction, val value: Decimal) {
companion object {
val CODEC: Codec<MatterFunction> by lazy {
simpleCodec(::MatterFunction, MatterFunction::function, IMatterFunction.registry.byNameCodec(), MatterFunction::value, DecimalCodec)
simpleCodec(::MatterFunction, MatterFunction::function, MBuiltInRegistries.MATTER_FUNCTION.byNameCodec(), MatterFunction::value, DecimalCodec)
}
}
}
@ -30,7 +31,7 @@ class UpdateAction(
data class ComplexityFunction(val function: IMatterFunction, val value: Double) {
companion object {
val CODEC: Codec<ComplexityFunction> by lazy {
simpleCodec(::ComplexityFunction, ComplexityFunction::function, IMatterFunction.registry.byNameCodec(), ComplexityFunction::value, Codec.DOUBLE)
simpleCodec(::ComplexityFunction, ComplexityFunction::function, MBuiltInRegistries.MATTER_FUNCTION.byNameCodec(), ComplexityFunction::value, Codec.DOUBLE)
}
}
}
@ -38,7 +39,7 @@ class UpdateAction(
data class PriorityFunction(val function: IMatterFunction, val value: Int) {
companion object {
val CODEC: Codec<PriorityFunction> by lazy {
simpleCodec(::PriorityFunction, PriorityFunction::function, IMatterFunction.registry.byNameCodec(), PriorityFunction::value, Codec.INT)
simpleCodec(::PriorityFunction, PriorityFunction::function, MBuiltInRegistries.MATTER_FUNCTION.byNameCodec(), PriorityFunction::value, Codec.INT)
}
}
}

View File

@ -26,13 +26,14 @@ import ru.dbotthepony.mc.otm.core.readComponent
import ru.dbotthepony.mc.otm.core.writeComponent
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
import ru.dbotthepony.mc.otm.onceServer
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.game.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val data: ByteArrayList) : CustomPacketPayload {
fun write(buff: RegistryFriendlyByteBuf) {
buff.writeInt(MRegistry.ANDROID_FEATURES.getId(type))
buff.writeInt(MBuiltInRegistries.ANDROID_FEATURE.getId(type))
buff.writeBytes(data.elements(), 0, data.size)
}
@ -58,7 +59,7 @@ class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val data: ByteAr
fun read(buff: RegistryFriendlyByteBuf): AndroidFeatureSyncPacket {
return AndroidFeatureSyncPacket(
MRegistry.ANDROID_FEATURES.byIdOrThrow(buff.readInt()),
MBuiltInRegistries.ANDROID_FEATURE.byIdOrThrow(buff.readInt()),
ByteArrayList.wrap(ByteArray(buff.readableBytes()).also { buff.readBytes(it) })
)
}
@ -140,7 +141,7 @@ class AndroidResearchRequestPacket(val type: AndroidResearchType) : CustomPacket
class AndroidFeatureRemovePacket(val type: AndroidFeatureType<*>) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
buff.writeInt(MRegistry.ANDROID_FEATURES.getId(type))
buff.writeInt(MBuiltInRegistries.ANDROID_FEATURE.getId(type))
}
fun play(context: IPayloadContext) {
@ -164,7 +165,7 @@ class AndroidFeatureRemovePacket(val type: AndroidFeatureType<*>) : CustomPacket
StreamCodec.ofMember(AndroidFeatureRemovePacket::write, ::read)
fun read(buff: FriendlyByteBuf): AndroidFeatureRemovePacket {
return AndroidFeatureRemovePacket(MRegistry.ANDROID_FEATURES.byIdOrThrow(buff.readInt()))
return AndroidFeatureRemovePacket(MBuiltInRegistries.ANDROID_FEATURE.byIdOrThrow(buff.readInt()))
}
}
}
@ -219,7 +220,7 @@ class PlayerIterationPacket(val iteration: Int, val deathLog: List<Pair<Int, Com
class SwitchAndroidFeaturePacket(val type: AndroidFeatureType<*>, val newState: Boolean) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
buff.writeInt(MRegistry.ANDROID_FEATURES.getId(type))
buff.writeInt(MBuiltInRegistries.ANDROID_FEATURE.getId(type))
buff.writeBoolean(newState)
}
@ -259,14 +260,14 @@ class SwitchAndroidFeaturePacket(val type: AndroidFeatureType<*>, val newState:
StreamCodec.ofMember(SwitchAndroidFeaturePacket::write, ::read)
fun read(buff: FriendlyByteBuf): SwitchAndroidFeaturePacket {
return SwitchAndroidFeaturePacket(MRegistry.ANDROID_FEATURES.byIdOrThrow(buff.readInt()), buff.readBoolean())
return SwitchAndroidFeaturePacket(MBuiltInRegistries.ANDROID_FEATURE.byIdOrThrow(buff.readInt()), buff.readBoolean())
}
}
}
class ActivateAndroidFeaturePacket(val type: AndroidFeatureType<*>) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
buff.writeInt(MRegistry.ANDROID_FEATURES.getId(type))
buff.writeInt(MBuiltInRegistries.ANDROID_FEATURE.getId(type))
}
fun play(context: IPayloadContext) {
@ -298,7 +299,7 @@ class ActivateAndroidFeaturePacket(val type: AndroidFeatureType<*>) : CustomPack
StreamCodec.ofMember(ActivateAndroidFeaturePacket::write, ::read)
fun read(buff: FriendlyByteBuf): ActivateAndroidFeaturePacket {
return ActivateAndroidFeaturePacket(MRegistry.ANDROID_FEATURES.byIdOrThrow(buff.readInt()))
return ActivateAndroidFeaturePacket(MBuiltInRegistries.ANDROID_FEATURE.byIdOrThrow(buff.readInt()))
}
}
}

View File

@ -97,6 +97,7 @@ import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import ru.dbotthepony.mc.otm.menu.IItemStackSortingSettings
import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.network.SmokeParticlesPacket.Companion.makeSmoke
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.game.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MDamageTypes
import ru.dbotthepony.mc.otm.registry.game.MItems
@ -1004,7 +1005,7 @@ class MatteryPlayer(val ply: Player) {
research.clear()
for (featureTag in tag.getCompoundList("features")) {
val feature = MRegistry.ANDROID_FEATURES.get(ResourceLocation.parse(featureTag.getString("id")))
val feature = MBuiltInRegistries.ANDROID_FEATURE.get(ResourceLocation.parse(featureTag.getString("id")))
if (feature?.isApplicable(this) == true) {
val instance = feature.create(this)

View File

@ -6,6 +6,7 @@ import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.contents.TranslatableContents
import ru.dbotthepony.mc.otm.player.MatteryPlayer
import ru.dbotthepony.mc.otm.core.getKeyNullable
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MRegistry
open class AndroidFeatureType<T : AndroidFeature> {
@ -26,7 +27,7 @@ open class AndroidFeatureType<T : AndroidFeature> {
open fun isApplicable(android: MatteryPlayer) = true
val registryName by lazy {
MRegistry.ANDROID_FEATURES.getKeyNullable(this)
MBuiltInRegistries.ANDROID_FEATURE.getKeyNullable(this)
}
open val displayContents: ComponentContents by lazy {

View File

@ -13,11 +13,12 @@ import ru.dbotthepony.mc.otm.config.PlayerConfig
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import ru.dbotthepony.mc.otm.registry.MRegistries
object AndroidResearchDescriptions {
private val registrar = MDeferredRegister(AndroidResearchDescription.registryKey, OverdriveThatMatters.MOD_ID)
private val registrar = MDeferredRegister(MRegistries.ANDROID_RESEARCH_DESCRIPTION, OverdriveThatMatters.MOD_ID)
init {
registrar.register("plain") { PlainAndroidResearchDescription }
@ -140,17 +141,8 @@ interface AndroidResearchDescription {
val type: Type<*>
companion object {
private val delegate = RegistryDelegate<Type<*>>("android_research_description") {}
val registry by delegate
val registryKey get() = delegate.key
val CODEC: Codec<AndroidResearchDescription> by lazy {
registry.byNameCodec().dispatch({ it.type }, { it.codec })
}
internal fun register(bus: IEventBus) {
bus.addListener(delegate::build)
MBuiltInRegistries.ANDROID_RESEARCH_DESCRIPTION.byNameCodec().dispatch({ it.type }, { it.codec })
}
fun singleton(callback: (research: AndroidResearch) -> Component): Singleton {

View File

@ -7,13 +7,14 @@ import net.minecraft.resources.ResourceLocation
import net.neoforged.bus.api.IEventBus
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.game.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
object AndroidResearchResults {
private val registrar = MDeferredRegister(AndroidResearchResult.registryKey, OverdriveThatMatters.MOD_ID)
private val registrar = MDeferredRegister(MRegistries.ANDROID_RESEARCH_RESULT, OverdriveThatMatters.MOD_ID)
init {
registrar.register("feature") { AndroidResearchResult.Feature }
@ -71,7 +72,7 @@ interface AndroidResearchResult {
* Adds specific android feature [id] to target, does nothing if target already has specified feature
*/
class Feature(val id: ResourceLocation, val optional: Boolean = false) : AndroidResearchResult {
val feature = MRegistry.ANDROID_FEATURES.get(id) ?: if (optional) null else throw NoSuchElementException("Unknown android feature $id")
val feature = MBuiltInRegistries.ANDROID_FEATURE.get(id) ?: if (optional) null else throw NoSuchElementException("Unknown android feature $id")
override val type: Type<*>
get() = Companion
@ -101,7 +102,7 @@ interface AndroidResearchResult {
*/
class FeatureLevel(val id: ResourceLocation, val optional: Boolean = false, val levels: Int = 1) :
AndroidResearchResult {
val feature = MRegistry.ANDROID_FEATURES.get(id) ?: if (optional) null else throw NoSuchElementException("Unknown android feature $id")
val feature = MBuiltInRegistries.ANDROID_FEATURE.get(id) ?: if (optional) null else throw NoSuchElementException("Unknown android feature $id")
override val type: Type<*>
get() = Companion
@ -153,17 +154,9 @@ interface AndroidResearchResult {
companion object {
private val LOGGER = LogManager.getLogger()
private val delegate = RegistryDelegate<Type<*>>("android_research_result") {}
val registry by delegate
val registryKey get() = delegate.key
val CODEC: Codec<AndroidResearchResult> by lazy {
registry.byNameCodec().dispatch({ it.type }, { it.codec })
}
internal fun register(bus: IEventBus) {
bus.addListener(delegate::build)
MBuiltInRegistries.ANDROID_RESEARCH_RESULT.byNameCodec().dispatch({ it.type }, { it.codec })
}
}
}

View File

@ -0,0 +1,66 @@
package ru.dbotthepony.mc.otm.registry
import net.minecraft.core.Registry
import net.minecraft.resources.ResourceKey
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.registries.NewRegistryEvent
import net.neoforged.neoforge.registries.RegistryBuilder
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.ResourceLocation
import kotlin.reflect.KProperty
object MBuiltInRegistries {
private val delegates = ArrayList<Delegate<*>>()
private class Delegate<T : Any>(private val key: ResourceKey<Registry<T>>, private val configurator: RegistryBuilder<T>.() -> Unit = {}) {
init {
delegates.add(this)
}
private var _value: Registry<T>? = null
operator fun getValue(thisRef: Any, property: KProperty<*>): Registry<T> {
return _value ?: throw IllegalStateException("Tried to access uninitialized registry ${key.location()}")
}
fun build(event: NewRegistryEvent) {
if (_value != null) {
throw IllegalStateException("Already created registry ${key.location()}!")
}
_value = RegistryBuilder(key).let {
configurator.invoke(it)
event.create(it)
}
}
}
val DECIMAL_PROVIDER_TYPE by Delegate(MRegistries.DECIMAL_PROVIDER_TYPE) {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "zero"))
}
val MATTER_REGISTRY_ACTION by Delegate(MRegistries.MATTER_REGISTRY_ACTION)
val MATTER_FUNCTION by Delegate(MRegistries.MATTER_FUNCTION) {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "noop"))
}
val RECIPE_FINDER by Delegate(MRegistries.RECIPE_FINDER)
val ANDROID_RESEARCH_DESCRIPTION by Delegate(MRegistries.ANDROID_RESEARCH_DESCRIPTION)
val ANDROID_RESEARCH_RESULT by Delegate(MRegistries.ANDROID_RESEARCH_RESULT)
val ANDROID_FEATURE by Delegate(MRegistries.ANDROID_FEATURE) { sync(true) }
val STACK_TYPE by Delegate(MRegistries.STACK_TYPE)
val BOOLEAN_PROVIDER by Delegate(MRegistries.BOOLEAN_PROVIDER) {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "false"))
}
val FEATURE by Delegate(MRegistries.FEATURE)
val PLACEMENT_MODIFIER by Delegate(MRegistries.PLACEMENT_MODIFIER) {
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "passthrough"))
}
internal fun register(bus: IEventBus) {
delegates.forEach { bus.addListener(it::build) }
}
}

View File

@ -0,0 +1,37 @@
package ru.dbotthepony.mc.otm.registry
import net.minecraft.core.Registry
import net.minecraft.resources.ResourceKey
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.data.world.DecimalProvider
import ru.dbotthepony.mc.otm.matter.AbstractRegistryAction
import ru.dbotthepony.mc.otm.matter.IMatterFunction
import ru.dbotthepony.mc.otm.matter.MatterManager
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>> {
return ResourceKey.createRegistryKey(ResourceLocation(OverdriveThatMatters.MOD_ID, name))
}
val DECIMAL_PROVIDER_TYPE = k<DecimalProvider.Type<*>>("decimal_provider_type")
val MATTER_REGISTRY_ACTION = k<AbstractRegistryAction.Type<*>>("matter_registry_action")
val MATTER_FUNCTION = k<IMatterFunction>("matter_function")
val RECIPE_FINDER = k<MatterManager.Finder>("recipe_finder")
val ANDROID_RESEARCH_DESCRIPTION = k<AndroidResearchDescription.Type<*>>("android_research_description")
val ANDROID_RESEARCH_RESULT = k<AndroidResearchResult.Type<*>>("android_research_result")
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")
}

View File

@ -53,11 +53,6 @@ import ru.dbotthepony.mc.otm.registry.objects.IBlockItemRegistryAcceptor
import ru.dbotthepony.mc.otm.registry.objects.StripedColoredDecorativeBlock
object MRegistry : IBlockItemRegistryAcceptor {
private val features = RegistryDelegate<AndroidFeatureType<*>>("android_features") { sync(true) }
val ANDROID_FEATURES by features
val ANDROID_FEATURES_LOCATION get() = features.location
val ANDROID_FEATURES_KEY get() = features.key
val DYE_ORDER: ImmutableList<DyeColor> = ImmutableList.of(
DyeColor.BLACK,
DyeColor.BLUE,
@ -251,7 +246,6 @@ object MRegistry : IBlockItemRegistryAcceptor {
}
internal fun initialize(bus: IEventBus) {
bus.addListener(features::build)
bus.addListener(this::initializeClient)
bus.addListener(this::initializeCommon)
bus.addListener(MStats::registerVanilla)

View File

@ -1,45 +0,0 @@
package ru.dbotthepony.mc.otm.registry
import net.minecraft.core.Registry
import net.minecraft.resources.ResourceKey
import net.neoforged.neoforge.registries.NewRegistryEvent
import net.neoforged.neoforge.registries.RegistryBuilder
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.ResourceLocation
import java.util.function.Supplier
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class RegistryDelegate<T>(key: String, private val configurator: RegistryBuilder<T>.() -> Unit = {}) : ReadOnlyProperty<Any, Registry<T>>, Supplier<Registry<T>>, Lazy<Registry<T>> {
private var _value: Registry<T>? = null
override val value: Registry<T>
get() = get()
override fun isInitialized(): Boolean {
return _value != null
}
val location = ResourceLocation(OverdriveThatMatters.MOD_ID, key)
val key: ResourceKey<Registry<T>> = ResourceKey.createRegistryKey(location)
override fun get(): Registry<T> {
val value = _value ?: throw IllegalStateException("Tried to access uninitialized registry $location")
return value
}
override fun getValue(thisRef: Any, property: KProperty<*>): Registry<T> {
return get()
}
fun build(event: NewRegistryEvent) {
if (_value != null) {
throw IllegalStateException("Already built registry $location!")
}
_value = RegistryBuilder(key).let {
configurator.invoke(it)
event.create(it)
}
}
}

View File

@ -4,22 +4,34 @@ import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.worldgen.placement.ChainPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedChainPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedCountPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedSplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.SplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
object MPlacementModifiers {
private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE)
private val enhancedRegistry = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
internal fun register(bus: IEventBus) {
registry.register(bus)
enhancedRegistry.register(bus)
}
val ENORMOUS by registry.register("enormous") { PlacementModifierType { EnormousPlacement.CODEC } }
val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } }
val WORM_PLACEMENT by registry.register("worm") { PlacementModifierType { WormPlacement.CODEC } }
val SPLIT by registry.register("split") { PlacementModifierType { SplitPlacement.CODEC} }
val CHAIN by registry.register("chain") { PlacementModifierType { ChainPlacement.CODEC} }
val CHAIN by registry.register("chain") { PlacementModifierType { ChainPlacement.CODEC } }
init {
enhancedRegistry.register("ellipsoid") { EllipsoidPlacement.Companion }
enhancedRegistry.register("worm") { WormPlacement.Companion }
enhancedRegistry.register("split") { EnhancedSplitPlacement.Companion }
enhancedRegistry.register("chain") { EnhancedChainPlacement.Companion }
enhancedRegistry.register("count") { EnhancedCountPlacement.Companion }
}
}

View File

@ -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 }
}

View File

@ -18,10 +18,11 @@ import ru.dbotthepony.mc.otm.player.android.feature.StepAssistFeature
import ru.dbotthepony.mc.otm.player.android.feature.SwimBoostersFeature
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.registry.MRegistry
object AndroidFeatures {
private val registry = MDeferredRegister(MRegistry.ANDROID_FEATURES_KEY)
private val registry = MDeferredRegister(MRegistries.ANDROID_FEATURE)
val AIR_BAGS by registry.register(MNames.AIR_BAGS) { AndroidFeatureType(::DummyAndroidFeature) }
val STEP_ASSIST by registry.register(MNames.STEP_ASSIST) { AndroidFeatureType(::StepAssistFeature) }

View File

@ -5,14 +5,13 @@ import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.world.item.ItemStack
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.readBigInteger
import ru.dbotthepony.mc.otm.core.readItem
import ru.dbotthepony.mc.otm.core.writeBigInteger
import ru.dbotthepony.mc.otm.core.writeItem
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import ru.dbotthepony.mc.otm.registry.MRegistries
import java.math.BigInteger
abstract class StorageStack<S : StorageStack<S>>(val count: BigInteger) {
@ -110,11 +109,7 @@ abstract class StorageStack<S : StorageStack<S>>(val count: BigInteger) {
return o?.hashCodeWithoutCount() ?: 0
}
private val delegate = RegistryDelegate<Type<*>>("stack_type") {}
val REGISTRY by delegate
val REGISTRY_KEY by delegate::key
private val registrar = MDeferredRegister(REGISTRY_KEY, OverdriveThatMatters.MOD_ID)
private val registrar = MDeferredRegister(MRegistries.STACK_TYPE, OverdriveThatMatters.MOD_ID)
val ITEMS: Type<ItemStorageStack> by registrar.register("items") {
SimpleType(
@ -131,7 +126,6 @@ abstract class StorageStack<S : StorageStack<S>>(val count: BigInteger) {
}
internal fun register(bus: IEventBus) {
bus.addListener(delegate::build)
registrar.register(bus)
}
}

View File

@ -15,6 +15,7 @@ import ru.dbotthepony.mc.otm.player.MatteryPlayer
import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.data.codec.SingletonCodec
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
import ru.dbotthepony.mc.otm.registry.MRegistry
import java.util.Optional
import java.util.function.Predicate
@ -76,7 +77,7 @@ object KillAsAndroidTrigger : MCriterionTrigger<KillAsAndroidTrigger.Instance>(R
}
class Has(val name: ResourceLocation) : FeaturePredicate() {
private val resolved by lazy { MRegistry.ANDROID_FEATURES.get(name) }
private val resolved by lazy { MBuiltInRegistries.ANDROID_FEATURE.get(name) }
override val type: PredicateType
get() = PredicateType.HAS

View File

@ -0,0 +1,335 @@
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 it.unimi.dsi.fastutil.objects.ObjectArraySet
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet
import net.minecraft.Util
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.core.SectionPos
import net.minecraft.server.level.ServerLevel
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.collect.BlockPosSet
import ru.dbotthepony.mc.otm.core.collect.Vec3iHashStrategy
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.LinkedList
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(PlacementPos(BlockPos(context.origin.minBlockX, 0, context.origin.minBlockZ), emptyVariableMap)))
}
private fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>) {
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)
}
}
}
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: ObjectRBTreeSet<PlacementPos>, val feature: EnhancedFeature.Configured<*, *>)
private val placed = LinkedList<Placement>()
override fun place(context: EnhancedPlacementContext, positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>) {
if (positions.isNotEmpty()) {
placed.add(Placement(context, ObjectRBTreeSet<PlacementPos>().also { it.addAll(positions) }, feature))
}
}
fun place(context: FeaturePlaceContext<*>): Boolean {
var any = false
val pos = ChunkPos(context.origin())
for ((eContext, positions, feature) in placed) {
val itr = positions.iterator(PlacementPos(BlockPos(pos.minBlockX, Int.MIN_VALUE, pos.minBlockZ), emptyVariableMap))
if (!itr.hasNext())
continue
val result = ObjectRBTreeSet<PlacementPos>()
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
}
}
return any
}
}
private val level2cache = Caffeine.newBuilder()
.scheduler(Scheduler.systemScheduler())
.executor(Util.backgroundExecutor())
.expireAfterAccess(Duration.ofMinutes(10))
.weakKeys()
.build<ServerLevel, Cache<ChunkPos, GeneratedChunk>>()
private fun getCache(level: WorldGenLevel): Cache<ChunkPos, GeneratedChunk> {
return level2cache.get(level.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() : this(EnhancedPlacementModifier.Passthrough)
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)
}
@JvmName("thenVanilla")
fun then(action: ConfiguredFeature<*, *>): Builder {
return then(EnhancedFeature.Wrapper.configure(action))
}
@JvmName("thenVanilla")
fun then(action: Holder<ConfiguredFeature<*, *>>): Builder {
return then(EnhancedFeature.Wrapper.configure(action))
}
inline fun fork(configurator: Builder.() -> Unit): Builder {
configurator(this)
return this
}
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())
}
}
}

View File

@ -0,0 +1,92 @@
package ru.dbotthepony.mc.otm.worldgen
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
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<PlacementPos>, feature: EnhancedFeature.Configured<*, *>)
}
private data class SharedState(
val level: WorldGenLevel,
val generator: ChunkGenerator,
val random: RandomSource,
val origin: ChunkPos,
val placer: Placer,
val vanillaContext: PlacementContext,
)
private val state: SharedState
val level: WorldGenLevel
get() = state.level
val generator: ChunkGenerator
get() = state.generator
val random: RandomSource
get() = state.random
val origin: ChunkPos
get() = state.origin
private val placer: Placer
get() = state.placer
val vanillaContext: PlacementContext
get() = state.vanillaContext
val variables: PlacementVariableMap
constructor(
level: WorldGenLevel,
generator: ChunkGenerator,
random: RandomSource,
origin: ChunkPos,
placer: Placer,
) {
state = SharedState(
level = level,
generator = generator,
random = random,
origin = origin,
placer = placer,
vanillaContext = PlacementContext(level, generator, Optional.empty())
)
variables = PlacementVariableMap()
}
private constructor(parent: EnhancedPlacementContext) {
this.state = parent.state
this.variables = parent.variables.push()
}
private constructor(parent: EnhancedPlacementContext, context: WorldGenLevel) {
this.state = parent.state.copy(level = context)
this.variables = parent.variables.push()
}
fun push(): EnhancedPlacementContext {
return EnhancedPlacementContext(this)
}
fun place(positions: List<PlacementPos>, 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)
}
fun push(context: WorldGenLevel): EnhancedPlacementContext {
return EnhancedPlacementContext(this, context)
}
}

View File

@ -0,0 +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<PlacementPos> {
override fun compareTo(other: PlacementPos): Int {
return Vec3iHashStrategy.compare(pos, other.pos)
}
}

View File

@ -0,0 +1,9 @@
package ru.dbotthepony.mc.otm.worldgen
import net.minecraft.resources.ResourceLocation
class PlacementVariable<T>(val name: ResourceLocation) : Comparable<PlacementVariable<T>> {
override fun compareTo(other: PlacementVariable<T>): Int {
return name.compareTo(other.name)
}
}

View File

@ -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<PlacementVariable<*>, KOptional<*>>()
constructor() {
parent = null
}
private constructor(parent: PlacementVariableMap) {
this.parent = parent
}
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): PlacementVariableMap {
variables[index] = KOptional(value)
return this
}
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) }
}
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
}
}

View File

@ -0,0 +1,68 @@
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
import ru.dbotthepony.mc.otm.worldgen.PlacementPos
abstract class EnhancedFeature<FC>(codec: Codec<FC>) {
abstract fun place(context: EnhancedPlacementContext, config: FC, positions: Set<PlacementPos>, allPositions: Set<PlacementPos>): Boolean
data class Configured<F : EnhancedFeature<FC>, FC>(val feature: F, val config: FC) {
fun place(context: EnhancedPlacementContext, positions: Set<PlacementPos>, allPositions: Set<PlacementPos>): 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: Set<PlacementPos>,
allPositions: Set<PlacementPos>
): Boolean {
var any = false
positions.forEach { any = config.value().place(context.level, context.generator, context.random, it.pos) || 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)
}
}
}

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.FloatProvider
@ -9,15 +10,12 @@ import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.math.minus
import ru.dbotthepony.mc.otm.core.math.plus
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
import kotlin.math.PI
import kotlin.math.roundToInt
/**
* Ellipsoid ("cloud") placement
@ -57,22 +55,19 @@ data class EllipsoidPlacement(
* Ellipsoid size sampler on Y axis
*/
val yLength: FloatProvider,
) : PlacementModifier() {
) : PlacementModifier(), EnhancedPlacementModifier {
init {
require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" }
require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" }
require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" }
}
override fun getPositions(
context: PlacementContext,
random: RandomSource,
position: BlockPos
): Stream<BlockPos> {
private fun evaluate(random: RandomSource, position: BlockPos): ObjectRBTreeSet<BlockPos> {
val count = count.sample(random)
val results = ObjectRBTreeSet<BlockPos>(Vec3iHashStrategy)
if (count <= 0)
return Stream.empty()
return results
val xLength = xLength.sample(random)
val zLength = zLength.sample(random)
@ -82,30 +77,50 @@ data class EllipsoidPlacement(
val zPow = zLength * zLength
val yPow = yLength * yLength
return Stream.generate {
var iterations = 0
val maxIterations = count * 10
while (results.size < count && ++iterations < maxIterations) {
val x = this.x.sample(random) * xLength
val y = this.y.sample(random) * yLength
val z = this.z.sample(random) * zLength
BlockPos(x.toInt(), y.toInt(), z.toInt())
}
.limit(count.toLong() * 10)
.filter {
val (ellipsoidX, ellipsoidY, ellipsoidZ) = it
(ellipsoidX * ellipsoidX) / xPow +
(ellipsoidY * ellipsoidY) / yPow +
(ellipsoidZ * ellipsoidZ) / zPow <= 1.0f
val isValidPoint = (x * x) / xPow +
(y * y) / yPow +
(z * z) / zPow <= 1.0f
if (isValidPoint) {
results.add(BlockPos(x.toInt() + position.x, y.toInt() + position.y, z.toInt() + position.z))
}
.distinct()
.limit(count.toLong())
.map { it + position }
}
return results
}
override fun getPositions(
context: PlacementContext,
random: RandomSource,
position: BlockPos
): Stream<BlockPos> {
return evaluate(random, position).stream()
}
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
val result = ArrayList<PlacementPos>()
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
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.ELLIPSOID_PLACEMENT
}
companion object {
companion object : EnhancedPlacementModifier.Type<EllipsoidPlacement> {
val CODEC: MapCodec<EllipsoidPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
@ -119,5 +134,8 @@ data class EllipsoidPlacement(
).apply(it, ::EllipsoidPlacement)
}
}
override val codec: MapCodec<EllipsoidPlacement>
get() = CODEC
}
}

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
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
*/
class EnhancedChainPlacement(
val children: List<EnhancedPlacementModifier>
) : EnhancedPlacementModifier {
constructor(vararg children: EnhancedPlacementModifier) : this(ImmutableList.copyOf(children))
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
var current = positions
children.forEach { current = it.evaluate(context, current) }
return current
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedChainPlacement> {
override val codec: MapCodec<EnhancedChainPlacement> by lazy {
Codec.list(EnhancedPlacementModifier.CODEC).xmap(::EnhancedChainPlacement, EnhancedChainPlacement::children).fieldOf("children")
}
}
}

View File

@ -0,0 +1,30 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.MapCodec
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<PlacementPos>): List<PlacementPos> {
val count = provider.sample(context.random)
if (count <= 0) {
return listOf()
} else if (count == 1) {
return positions
} else {
val result = ArrayList<PlacementPos>()
result.ensureCapacity(positions.size * count)
for (i in 0 until count) result.addAll(positions)
return result
}
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedCountPlacement> {
override val codec: MapCodec<EnhancedCountPlacement> = IntProvider.CODEC.xmap(::EnhancedCountPlacement, EnhancedCountPlacement::provider).fieldOf("count")
}
}

View File

@ -0,0 +1,67 @@
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 {
interface Type<T : EnhancedPlacementModifier> {
val codec: MapCodec<T>
}
fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos>
val type: Type<*>
class Wrapper(val parent: PlacementModifier) : EnhancedPlacementModifier {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
return positions.stream()
.flatMap { (pos, vars) -> parent.getPositions(context.vanillaContext, context.random, pos).map { PlacementPos(it, vars) } }
.collect(Collectors.toCollection(::ArrayList))
}
override val type: Type<*>
get() = Companion
companion object : Type<Wrapper> {
override val codec: MapCodec<Wrapper> = PlacementModifier.CODEC.xmap(::Wrapper, Wrapper::parent).fieldOf("parent")
}
}
object Passthrough : EnhancedPlacementModifier, Type<Passthrough> {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
return positions
}
override val type: Type<*>
get() = this
override val codec: MapCodec<Passthrough> = MapCodec.unit(this)
}
companion object {
private val registrar = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
init {
registrar.register("wrapper") { Wrapper.Companion }
registrar.register("passthrough") { Passthrough }
}
val CODEC: Codec<EnhancedPlacementModifier> by lazy {
MBuiltInRegistries.PLACEMENT_MODIFIER.byNameCodec().dispatch({ it.type }, { it.codec })
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
}
}
}

View File

@ -0,0 +1,41 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.world.level.levelgen.placement.PlacementContext
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
/**
* Or "shard" placement, if you will.
*
* Basically, allows multiple [PlacementModifier]s to split/branch off from provided point.
*/
class EnhancedSplitPlacement(
val children: List<EnhancedPlacementModifier>
) : EnhancedPlacementModifier {
constructor(vararg children: EnhancedPlacementModifier) : this(ImmutableList.copyOf(children))
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
val result = ArrayList<PlacementPos>()
children.forEach { result.addAll(it.evaluate(context, positions)) }
return result
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedSplitPlacement> {
override val codec: MapCodec<EnhancedSplitPlacement> by lazy {
Codec.list(EnhancedPlacementModifier.CODEC).xmap(::EnhancedSplitPlacement, EnhancedSplitPlacement::children).fieldOf("children")
}
}
}

View File

@ -1,136 +0,0 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Scheduler
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import net.minecraft.Util
import net.minecraft.core.BlockPos
import net.minecraft.core.SectionPos
import net.minecraft.util.RandomSource
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.WorldGenLevel
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
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.registry.data.MPlacementModifiers
import java.io.DataOutputStream
import java.time.Duration
import java.util.stream.Stream
import kotlin.math.sqrt
/**
* Enormous placement base, which allows it to span over several chunks without issues.
*
* This modifier is designed to be the only "top-level" placement modifier,
* and all logic regarding actual placements embedded in [children]
*/
class EnormousPlacement(
/**
* How many chunks away to look for actual placements
*
* Too small value will cause placement cutoffs
*/
val chunkScanRange: Int,
val seedMix: Long,
/**
* Baseline placement modifiers, dictating how to appear in chunk
*/
val children: List<PlacementModifier>,
) : PlacementModifier() {
private class GeneratedChunk(positions: Stream<BlockPos>) {
// TODO: memory inefficient
private val positions = ObjectOpenHashSet<BlockPos>()
init {
positions.forEach { this.positions.add(it) }
}
// TODO: this is primitive implementation, need to implement better one
fun getPositions(pos: ChunkPos): Stream<BlockPos> {
return positions.stream().filter {
SectionPos.blockToSectionCoord(it.x) == pos.x &&
SectionPos.blockToSectionCoord(it.z) == pos.z
}
}
}
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 computeChunk(context: PlacementContext, pos: ChunkPos): GeneratedChunk {
val bytes = FastByteArrayOutputStream()
val dataStream = DataOutputStream(bytes)
dataStream.writeLong(seedMix)
dataStream.writeInt(pos.x)
dataStream.writeInt(pos.z)
val hash = XXHash64()
hash.update(bytes.array, 0, bytes.length)
val random = GJRAND64RandomSource(context.level.seed, hash.digestAsLong())
var stream = Stream.of(BlockPos(pos.minBlockX, 0, pos.minBlockZ))
children.forEach { modifier -> stream = stream.flatMap { modifier.getPositions(context, random, it).sequential() } }
return GeneratedChunk(stream)
}
override fun getPositions(context: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> {
val cache = getCache(context.level)
val cPos = ChunkPos(pos)
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(cPos.x + x, cPos.z + z)
instances.add(cache.get(thisPos) { computeChunk(context, thisPos) })
}
}
}
return instances.stream().flatMap { it.getPositions(cPos) }
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.ENORMOUS
}
companion object {
val CODEC: MapCodec<EnormousPlacement> = RecordCodecBuilder.mapCodec {
it.group(
Codec.INT.minRange(0).fieldOf("chunk_scan_range").forGetter(EnormousPlacement::chunkScanRange),
Codec.LONG.fieldOf("seed_mix").forGetter(EnormousPlacement::seedMix),
PlacementModifier.CODEC.listOf().optionalFieldOf("children", listOf()).forGetter(EnormousPlacement::children),
).apply(it, ::EnormousPlacement)
}
}
}

View File

@ -13,6 +13,7 @@ import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.math.angleDifference
import ru.dbotthepony.mc.otm.core.math.normalizeAngle
@ -22,6 +23,8 @@ import ru.dbotthepony.mc.otm.core.nextDouble
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
@ -43,7 +46,7 @@ class WormPlacement(
val initialAngleXY: FloatProvider = DEFAULT_INITIAL_ANGLE_XY,
val maxTravelDown: Int = Int.MAX_VALUE,
val maxTravelUp: Int = Int.MAX_VALUE,
) : PlacementModifier() {
) : PlacementModifier(), EnhancedPlacementModifier {
private inner class Worm(private val random: RandomSource, private var position: Vector) {
private var remainingDistance = length.sample(random)
private var xzRotation = random.nextDouble(-PI, PI)
@ -133,28 +136,47 @@ class WormPlacement(
}
}
override fun getPositions(context: PlacementContext, random: RandomSource, center: BlockPos): Stream<BlockPos> {
private fun evaluate(random: RandomSource, position: BlockPos): List<BlockPos> {
val worms = ArrayList<Worm>()
worms.add(Worm(random, Vector.ZERO))
val positions = ArrayList<BlockPos>()
positions.add(BlockPos.ZERO)
val results = ArrayList<BlockPos>()
results.add(position)
while (worms.isNotEmpty()) {
worms.removeIf {
val pos = it.follow()
if (pos != null) positions.add(pos)
if (pos != null) results.add(pos + position)
it.hasFinished
}
}
return positions.stream().map { it + center }
return results
}
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
if (positions.isEmpty())
return positions
else {
val result = ArrayList<PlacementPos>()
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
}
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
override fun getPositions(context: PlacementContext, random: RandomSource, center: BlockPos): Stream<BlockPos> {
return evaluate(random, center).stream()
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.WORM_PLACEMENT
}
companion object {
companion object : EnhancedPlacementModifier.Type<WormPlacement> {
private fun increment(value: Float): Float {
var i = 1f
@ -202,19 +224,24 @@ class WormPlacement(
private const val DEGREES_TO_RADIANS = PI.toFloat() / 180f
val CODEC: MapCodec<WormPlacement> = RecordCodecBuilder.mapCodec {
it.group(
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length),
BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ),
BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY),
FloatProvider.CODEC.fieldOf("turn_speed_xz").forGetter(WormPlacement::turnSpeedXZ),
FloatProvider.CODEC.fieldOf("turn_speed_xy").forGetter(WormPlacement::turnSpeedXY),
FloatProvider.CODEC.fieldOf("turn_rate_xz").forGetter(WormPlacement::turnRateXZ),
FloatProvider.CODEC.fieldOf("turn_rate_xy").forGetter(WormPlacement::turnRateXY),
FloatProvider.CODEC.optionalFieldOf("initial_angle_xy", DEFAULT_INITIAL_ANGLE_XY).forGetter(WormPlacement::initialAngleXY),
Codec.INT.minRange(1).optionalFieldOf("max_travel_down", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelDown),
Codec.INT.minRange(1).optionalFieldOf("max_travel_up", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelUp),
).apply(it, ::WormPlacement)
val CODEC: MapCodec<WormPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length),
BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ),
BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY),
FloatProvider.CODEC.fieldOf("turn_speed_xz").forGetter(WormPlacement::turnSpeedXZ),
FloatProvider.CODEC.fieldOf("turn_speed_xy").forGetter(WormPlacement::turnSpeedXY),
FloatProvider.CODEC.fieldOf("turn_rate_xz").forGetter(WormPlacement::turnRateXZ),
FloatProvider.CODEC.fieldOf("turn_rate_xy").forGetter(WormPlacement::turnRateXY),
FloatProvider.CODEC.optionalFieldOf("initial_angle_xy", DEFAULT_INITIAL_ANGLE_XY).forGetter(WormPlacement::initialAngleXY),
Codec.INT.minRange(1).optionalFieldOf("max_travel_down", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelDown),
Codec.INT.minRange(1).optionalFieldOf("max_travel_up", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelUp),
).apply(it, ::WormPlacement)
}
}
override val codec: MapCodec<WormPlacement>
get() = CODEC
}
}

View File

@ -18,7 +18,7 @@ function initializeCoreMod() {
if (insn.getOpcode() == Opcodes.BIPUSH) {
node.instructions.insert(insn, new MethodInsnNode(
Opcodes.INVOKESTATIC,
'ru/dbotthepony/mc/otm/android/feature/LimbOverclockingFeature',
'ru/dbotthepony/mc/otm/player/android/feature/LimbOverclockingFeature',
'getBrushCooldown',
'(Lnet/minecraft/world/entity/LivingEntity;)I'
))
@ -31,7 +31,7 @@ function initializeCoreMod() {
if (insn.getOpcode() == Opcodes.ICONST_5) {
node.instructions.insert(insn, new MethodInsnNode(
Opcodes.INVOKESTATIC,
'ru/dbotthepony/mc/otm/android/feature/LimbOverclockingFeature',
'ru/dbotthepony/mc/otm/player/android/feature/LimbOverclockingFeature',
'getBrushTick',
'(Lnet/minecraft/world/entity/LivingEntity;)I'
))
@ -59,7 +59,7 @@ function initializeCoreMod() {
if (insn.getOpcode() == Opcodes.LDC && insn.cst == 10) {
node.instructions.insert(insn, new MethodInsnNode(
Opcodes.INVOKESTATIC,
'ru/dbotthepony/mc/otm/android/feature/LimbOverclockingFeature',
'ru/dbotthepony/mc/otm/player/android/feature/LimbOverclockingFeature',
'getBrushableBlockCooldown',
'(Lnet/minecraft/world/entity/player/Player;)J'
))

View File

@ -0,0 +1,91 @@
package ru.dbotthepony.mc.otm.tests
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import net.minecraft.core.BlockPos
import net.minecraft.core.SectionPos
import net.minecraft.world.level.ChunkPos
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import ru.dbotthepony.mc.otm.core.collect.BlockPosSet
import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource
import java.util.stream.Collectors
object BlockPosSetTests {
@Test
@DisplayName("BlockPosSet additions")
fun add() {
val rand = GJRAND64RandomSource(43752304058048234L, -29394036784594328L)
val positions = ObjectArraySet<BlockPos>()
for (i in 0 until 1000) {
val x = rand.nextInt(-200, 200)
val y = rand.nextInt(-200, 200)
val z = rand.nextInt(-200, 200)
positions.add(BlockPos(x, y, z))
}
val result = BlockPosSet()
result.addAll(positions)
Assertions.assertEquals(positions, result)
Assertions.assertEquals(positions, ObjectArraySet(result))
run {
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 0 && SectionPos.blockToSectionCoord(it.z) == 0 }.collect(Collectors.toCollection(::ObjectArraySet))
val actual = result.subset(ChunkPos.ZERO)
Assertions.assertEquals(expected, actual)
Assertions.assertEquals(expected, ObjectArraySet(actual))
}
run {
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 0 && SectionPos.blockToSectionCoord(it.z) == 1 }.collect(Collectors.toCollection(::ObjectArraySet))
val actual = result.subset(ChunkPos(0, 1))
Assertions.assertEquals(expected, actual)
Assertions.assertEquals(expected, ObjectArraySet(actual))
}
run {
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 0 && SectionPos.blockToSectionCoord(it.z) == -1 }.collect(Collectors.toCollection(::ObjectArraySet))
val actual = result.subset(ChunkPos(0, -1))
Assertions.assertEquals(expected, actual)
Assertions.assertEquals(expected, ObjectArraySet(actual))
}
run {
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 1 && SectionPos.blockToSectionCoord(it.z) == 0 }.collect(Collectors.toCollection(::ObjectArraySet))
val actual = result.subset(ChunkPos(1, 0))
Assertions.assertEquals(expected, actual)
Assertions.assertEquals(expected, ObjectArraySet(actual))
}
run {
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == -1 && SectionPos.blockToSectionCoord(it.z) == 0 }.collect(Collectors.toCollection(::ObjectArraySet))
val actual = result.subset(ChunkPos(-1, 0))
Assertions.assertEquals(expected, actual)
Assertions.assertEquals(expected, ObjectArraySet(actual))
}
run {
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 1 && SectionPos.blockToSectionCoord(it.z) == 1 }.collect(Collectors.toCollection(::ObjectArraySet))
val actual = result.subset(ChunkPos(1, 1))
Assertions.assertEquals(expected, actual)
Assertions.assertEquals(expected, ObjectArraySet(actual))
}
run {
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == -1 && SectionPos.blockToSectionCoord(it.z) == 1 }.collect(Collectors.toCollection(::ObjectArraySet))
val actual = result.subset(ChunkPos(-1, 1))
Assertions.assertEquals(expected, actual)
Assertions.assertEquals(expected, ObjectArraySet(actual))
}
}
}