Compare commits
33 Commits
d55fbbe62f
...
159125fb4b
Author | SHA1 | Date | |
---|---|---|---|
159125fb4b | |||
727111cf4a | |||
d08a928e04 | |||
398bd532f4 | |||
7e570747c2 | |||
d683ea1e38 | |||
f74dbbd84a | |||
2993ae61ca | |||
9577e205e7 | |||
ba83b89476 | |||
bf6fec7753 | |||
0af5fb7301 | |||
a4e40bd464 | |||
07b295ce45 | |||
ba492e0cee | |||
ddcfe11780 | |||
dca02893a4 | |||
ab6e3ad87f | |||
78c0a5d717 | |||
e150b57b65 | |||
1e6e38ea7d | |||
1acd105925 | |||
5d05fe3bb4 | |||
bc81103e38 | |||
18f9bc2654 | |||
03a1ab9197 | |||
6226621b95 | |||
055ca7ec43 | |||
1bb612b07c | |||
ca67e796da | |||
9ee561d0fd | |||
588b9c1b7c | |||
e399fa8b68 |
@ -57,6 +57,7 @@ import ru.dbotthepony.mc.otm.datagen.tags.addStructureTags
|
||||
import ru.dbotthepony.mc.otm.datagen.tags.addSuspiciousTags
|
||||
import ru.dbotthepony.mc.otm.datagen.tags.addTags
|
||||
import ru.dbotthepony.mc.otm.matter.MatterDataProvider
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistries
|
||||
import ru.dbotthepony.mc.otm.registry.game.MBlocks
|
||||
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
|
||||
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
|
||||
@ -560,6 +561,7 @@ object DataGen {
|
||||
val registrySetBuilder = RegistrySetBuilder()
|
||||
.add(Registries.DAMAGE_TYPE, ::registerDamageTypes)
|
||||
.add(Registries.CONFIGURED_FEATURE, ::registerConfiguredFeatures)
|
||||
.add(MRegistries.CONFIGURED_FEATURE, ::registerEnhancedConfiguredFeatures)
|
||||
.add(Registries.PLACED_FEATURE, ::registerPlacedFeatures)
|
||||
.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, ::registerBiomeModifiers)
|
||||
|
||||
|
@ -28,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)
|
||||
|
@ -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)
|
||||
|
@ -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(", ")}]"
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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) }
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
16
src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/Ext.kt
Normal file
16
src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/Ext.kt
Normal 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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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'
|
||||
))
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user