From 7d4d718b1e2f082e94ee09d004835d1307fb7737 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Tue, 30 Jan 2024 17:45:17 +0700 Subject: [PATCH] Global block/state tag, additional predicates for multiblock tests, more multiblock performance improvements --- .../ru/dbotthepony/mc/otm/core/Multiblock.kt | 200 ++++++++++++++---- 1 file changed, 157 insertions(+), 43 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt index f908f54f5..783722ae3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt @@ -2,10 +2,16 @@ package ru.dbotthepony.mc.otm.core import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableSet +import it.unimi.dsi.fastutil.objects.Object2IntMap +import it.unimi.dsi.fastutil.objects.Object2IntMaps import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectArraySet +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet +import it.unimi.dsi.fastutil.objects.ObjectSets +import it.unimi.dsi.fastutil.objects.Reference2IntMap +import it.unimi.dsi.fastutil.objects.Reference2IntMaps import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap import net.minecraft.core.BlockPos import net.minecraft.core.Direction @@ -20,6 +26,7 @@ import net.minecraft.world.level.block.Rotation import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.chunk.ChunkStatus +import ru.dbotthepony.mc.otm.core.collect.SupplierList import ru.dbotthepony.mc.otm.core.collect.WeakHashSet import ru.dbotthepony.mc.otm.core.collect.collect import ru.dbotthepony.mc.otm.core.collect.map @@ -40,6 +47,10 @@ fun onBlockEntityInvalidated(blockEntity: BlockEntity) { fun interface BlockPredicate { fun test(pos: BlockPos, access: LevelAccessor): Boolean + fun rotate(rotation: Rotation): BlockPredicate { + return this + } + fun and(other: BlockPredicate): BlockPredicate { return BlockPredicate { pos, access -> test(pos, access) && other.test(pos, access) } } @@ -107,13 +118,15 @@ inline fun multiblockEntity(): MultiblockBuilder.Entit return MultiblockBuilder.EntityTag(T::class) } +private val GLOBAL_BLOCK_TAG = Any() + class MultiblockBuilder { private val nodes = Object2ObjectOpenHashMap() + private val customChecks = ArrayList>() - // not data classes to allow having multiple tags with same target - class EntityTag(val clazz: KClass) : Predicate { + class EntityTag(val clazz: KClass, val predicate: Predicate = Predicate { true }) : Predicate { override fun test(t: BlockEntity): Boolean { - return clazz.isInstance(t) + return clazz.isInstance(t) && predicate.test(t as T) } } @@ -121,11 +134,19 @@ class MultiblockBuilder { OR, AND } + fun customCheck(predicate: Predicate): MultiblockBuilder { + customChecks.add(predicate) + return this + } + @Suppress("unchecked_cast") - abstract class Part> { + abstract inner class Part> { val predicates = ObjectArraySet() val children = ObjectArraySet>() + val builder: MultiblockBuilder + get() = this@MultiblockBuilder + val blockStateTags = ObjectArraySet() val blockTags = ObjectArraySet() val blockEntityTags = ObjectArraySet>() @@ -135,11 +156,21 @@ class MultiblockBuilder { return this as T } + fun tagBlockState(): T { + blockStateTags.add(GLOBAL_BLOCK_TAG) + return this as T + } + fun tagBlock(value: Any): T { blockTags.add(value) return this as T } + fun tagBlock(): T { + blockTags.add(GLOBAL_BLOCK_TAG) + return this as T + } + fun tag(value: EntityTag<*>): T { blockEntityTags.add(value) return this as T @@ -216,7 +247,7 @@ class MultiblockBuilder { abstract val strategy: Strategy } - class And

>(val parent: P) : Part>() { + inner class And

>(val parent: P) : Part>() { init { parent.children.add(this) } @@ -227,7 +258,7 @@ class MultiblockBuilder { get() = Strategy.AND } - class Or

>(val parent: P) : Part>() { + inner class Or

>(val parent: P) : Part>() { init { parent.children.add(this) } @@ -258,6 +289,18 @@ class MultiblockBuilder { return relative(dir.default) } + fun relative(diff: Vec3i, configurator: Node.() -> Unit): Node { + return node(pos + diff).also(configurator) + } + + fun relative(dir: Direction, configurator: Node.() -> Unit): Node { + return relative(dir.normal).also(configurator) + } + + fun relative(dir: RelativeSide, configurator: Node.() -> Unit): Node { + return relative(dir.default).also(configurator) + } + fun front() = relative(RelativeSide.FRONT) fun back() = relative(RelativeSide.BACK) fun left() = relative(RelativeSide.LEFT) @@ -341,11 +384,11 @@ class MultiblockBuilder { } fun build(): MultiblockFactory { - return MultiblockFactory(nodes.values.iterator().map { it.build() }.collect(ImmutableSet.toImmutableSet())) + return MultiblockFactory(nodes.values.iterator().map { it.build() }.collect(ImmutableSet.toImmutableSet()), ImmutableList.copyOf(customChecks)) } } -class MultiblockFactory(val north: ImmutableSet) { +class MultiblockFactory(val north: ImmutableSet, val customChecks: ImmutableList>) { data class Part( val pos: BlockPos, val strategy: MultiblockBuilder.Strategy, @@ -357,13 +400,7 @@ class MultiblockFactory(val north: ImmutableSet) { ) fun create(pos: BlockPos): Multiblock { - return Multiblock( - pos, - north, - south, - west, - east, - ) + return Multiblock(pos, this) } val south: ImmutableSet = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_180)) }.collect(ImmutableSet.toImmutableSet()) @@ -371,22 +408,22 @@ class MultiblockFactory(val north: ImmutableSet) { val east: ImmutableSet = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet()) } -class Multiblock( - pos: BlockPos, - north: Collection, - south: Collection, - west: Collection, - east: Collection, -) { +class Multiblock(pos: BlockPos, factory: MultiblockFactory) { var isValid = false private set - private val configurations = ArrayList() + private val customChecks = factory.customChecks + private var collectFailedParts = false + private val north by lazy(LazyThreadSafetyMode.NONE) { Config(Direction.NORTH, pos, factory.north) } + private val south by lazy(LazyThreadSafetyMode.NONE) { Config(Direction.SOUTH, pos, factory.south) } + private val west by lazy(LazyThreadSafetyMode.NONE) { Config(Direction.WEST, pos, factory.west) } + private val east by lazy(LazyThreadSafetyMode.NONE) { Config(Direction.EAST, pos, factory.east) } + private val configurations = SupplierList(::north, ::south, ::west, ::east) private inner class BEList(val tag: MultiblockBuilder.EntityTag) { val list = ArrayList() val set = ObjectArraySet() - val setView: Set = Collections.unmodifiableSet(set) + val setView: Set = ObjectSets.unmodifiable(set) fun add(blockEntity: BlockEntity) { if (tag.test(blockEntity)) { @@ -453,9 +490,9 @@ class Multiblock( private var blockEntity: BlockEntity? = null private var blockState: BlockState? = null private val assignedBlockEntityLists = ArrayList>(prototype.blockEntityTags.size) - private val assignedBlockStateLists = ArrayList>() - private val assignedBlockLists = ArrayList>() - private val children: ImmutableList = prototype.children.stream().map { Part(pos, it) }.collect(ImmutableList.toImmutableList()) + private val assignedBlockStateLists = ArrayList>() + private val assignedBlockLists = ArrayList>() + private val children: ImmutableList by lazy(LazyThreadSafetyMode.NONE) { prototype.children.stream().map { Part(pos, it) }.collect(ImmutableList.toImmutableList()) } override fun compareTo(other: Part): Int { val cmp = SectionPos.blockToSectionCoord(pos.x).compareTo(SectionPos.blockToSectionCoord(other.pos.x)) @@ -465,15 +502,15 @@ class Multiblock( init { prototype.blockEntityTags.forEach { - assignedBlockEntityLists.add(tag2BlockEntity.computeIfAbsent(it, Object2ObjectFunction { _ -> BEList(it as MultiblockBuilder.EntityTag).also { blockEntityLists.add(it) } })) + assignedBlockEntityLists.add(getBlockEntityList(it)) } prototype.blockStateTags.forEach { - assignedBlockStateLists.add(tag2BlockState.computeIfAbsent(it, Object2ObjectFunction { Reference2IntOpenHashMap().also { blockStateLists.add(it) } })) + assignedBlockStateLists.add(getBlockStateList(it)) } prototype.blockTags.forEach { - assignedBlockLists.add(tag2Block.computeIfAbsent(it, Object2ObjectFunction { Object2IntOpenHashMap().also { blockLists.add(it) } })) + assignedBlockLists.add(getBlockList(it)) } } @@ -609,13 +646,59 @@ class Multiblock( } } + private fun getBlockEntityList(tag: MultiblockBuilder.EntityTag): BEList { + val existing = tag2BlockEntity[tag] + + if (existing != null) + return existing as BEList + + val new = BEList(tag) + blockEntityLists.add(new) + tag2BlockEntity[tag] = new + return new + } + + private fun getBlockList(tag: Any): Reference2IntMap { + val existing = tag2Block[tag] + + if (existing != null) + return existing + + val new = Reference2IntOpenHashMap() + tag2Block[tag] = new + tag2BlockViews[tag] = Reference2IntMaps.unmodifiable(new) + blockLists.add(new) + return new + } + + private fun getBlockStateList(tag: Any): Reference2IntMap { + val existing = tag2BlockState[tag] + + if (existing != null) + return existing + + val new = Reference2IntOpenHashMap() + tag2BlockState[tag] = new + tag2BlockStateViews[tag] = Reference2IntMaps.unmodifiable(new) + blockStateLists.add(new) + return new + } + private val tag2BlockEntity = Object2ObjectOpenHashMap, BEList<*>>() - private val tag2BlockState = Object2ObjectOpenHashMap>() - private val tag2Block = Object2ObjectOpenHashMap>() + private val tag2BlockState = Object2ObjectOpenHashMap>() + private val tag2Block = Object2ObjectOpenHashMap>() + + private val tag2BlockStateViews = Object2ObjectOpenHashMap>() + private val tag2BlockViews = Object2ObjectOpenHashMap>() private val blockEntityLists = ArrayList>() - private val blockStateLists = ArrayList>() - private val blockLists = ArrayList>() + private val blockStateLists = ArrayList>() + private val blockLists = ArrayList>() + + private val globalBlocks = getBlockList(GLOBAL_BLOCK_TAG) + private val globalBlockStates = getBlockStateList(GLOBAL_BLOCK_TAG) + private val globalBlocksView = tag2BlockViews[GLOBAL_BLOCK_TAG]!! + private val globalBlockStatesView = tag2BlockStateViews[GLOBAL_BLOCK_TAG]!! private val parts: ImmutableList = parts.stream() .map { Part(it.pos + pos, it) } @@ -647,6 +730,22 @@ class Multiblock( return (tag2BlockEntity[tag]?.setView ?: setOf()) as Set } + fun blocks(tag: Any): Reference2IntMap { + return tag2BlockViews[tag] ?: Reference2IntMaps.emptyMap() + } + + fun blockStates(tag: Any): Reference2IntMap { + return tag2BlockStateViews[tag] ?: Reference2IntMaps.emptyMap() + } + + fun blocks(): Reference2IntMap { + return globalBlocksView + } + + fun blockStates(): Reference2IntMap { + return globalBlockStatesView + } + fun blockEntityRemoved(blockEntity: BlockEntity): Boolean { var any = false @@ -658,11 +757,6 @@ class Multiblock( } } - private val north = Config(Direction.NORTH, pos, north).also { configurations.add(it) } - private val south = Config(Direction.SOUTH, pos, south).also { configurations.add(it) } - private val west = Config(Direction.WEST, pos, west).also { configurations.add(it) } - private val east = Config(Direction.EAST, pos, east).also { configurations.add(it) } - private var activeConfig: Config? = null val currentDirection: Direction? @@ -673,6 +767,26 @@ class Multiblock( return activeConfig?.blockEntities(tag) ?: setOf() } + fun blocks(tag: Any): Reference2IntMap { + if (!isValid) return Reference2IntMaps.emptyMap() + return activeConfig?.blocks(tag) ?: Reference2IntMaps.emptyMap() + } + + fun blockStates(tag: Any): Reference2IntMap { + if (!isValid) return Reference2IntMaps.emptyMap() + return activeConfig?.blockStates(tag) ?: Reference2IntMaps.emptyMap() + } + + fun blocks(): Reference2IntMap { + if (!isValid) return Reference2IntMaps.emptyMap() + return activeConfig?.blocks() ?: Reference2IntMaps.emptyMap() + } + + fun blockStates(): Reference2IntMap { + if (!isValid) return Reference2IntMaps.emptyMap() + return activeConfig?.blockStates() ?: Reference2IntMaps.emptyMap() + } + fun blockEntityRemoved(blockEntity: BlockEntity) { activeConfig?.blockEntityRemoved(blockEntity) } @@ -680,11 +794,11 @@ class Multiblock( fun update(levelAccessor: LevelAccessor): Boolean { val activeConfig = activeConfig - if (activeConfig != null && activeConfig.update(levelAccessor)) { + if (activeConfig != null && activeConfig.update(levelAccessor) && customChecks.all { it.test(this) }) { return true } else if (activeConfig != null) { for (config in configurations) { - if (config !== activeConfig && config.update(levelAccessor)) { + if (config !== activeConfig && config.update(levelAccessor) && customChecks.all { it.test(this) }) { this.activeConfig = config return true } @@ -695,7 +809,7 @@ class Multiblock( return false } else { for (config in configurations) { - if (config.update(levelAccessor)) { + if (config.update(levelAccessor) && customChecks.all { it.test(this) }) { this.activeConfig = config this.isValid = true return true @@ -720,7 +834,7 @@ class Multiblock( else -> throw IllegalArgumentException(direction.name) } - isValid = config.update(levelAccessor) + isValid = config.update(levelAccessor) && customChecks.all { it.test(this) } if (isValid) activeConfig = config