Force directional multiblock and multiblock performance improvements

This commit is contained in:
DBotThePony 2024-01-28 23:24:24 +07:00
parent dafa54112e
commit 2a8cabe577
Signed by: DBot
GPG Key ID: DCC23B5715498507

View File

@ -9,14 +9,17 @@ import it.unimi.dsi.fastutil.objects.ObjectArraySet
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.core.SectionPos
import net.minecraft.core.Vec3i import net.minecraft.core.Vec3i
import net.minecraft.tags.TagKey import net.minecraft.tags.TagKey
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.Rotation import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.chunk.ChunkStatus
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
import ru.dbotthepony.mc.otm.core.collect.collect import ru.dbotthepony.mc.otm.core.collect.collect
import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.map
@ -119,9 +122,9 @@ class MultiblockBuilder {
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
abstract class BlockPredicates<T : BlockPredicates<T>> { abstract class Part<T : Part<T>> {
val predicates = ObjectArraySet<BlockPredicate>() val predicates = ObjectArraySet<BlockPredicate>()
val children = ObjectArraySet<BlockPredicates<*>>() val children = ObjectArraySet<Part<*>>()
val blockStateTags = ObjectArraySet<Any>() val blockStateTags = ObjectArraySet<Any>()
val blockTags = ObjectArraySet<Any>() val blockTags = ObjectArraySet<Any>()
@ -153,12 +156,20 @@ class MultiblockBuilder {
} }
fun block(block: Block): T { fun block(block: Block): T {
predicates.add { pos, access -> access.getBlockState(pos).`is`(block) } predicates.add { pos, access ->
// use getChunk directly because we don't want to chunkload
(access.getChunk(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z), ChunkStatus.FULL, false)?.getBlockState(pos) ?: Blocks.AIR.defaultBlockState()).`is`(block)
}
return this as T return this as T
} }
fun block(block: TagKey<Block>): T { fun block(block: TagKey<Block>): T {
predicates.add { pos, access -> access.getBlockState(pos).`is`(block) } predicates.add { pos, access ->
// use getChunk directly because we don't want to chunkload
(access.getChunk(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z), ChunkStatus.FULL, false)?.getBlockState(pos) ?: Blocks.AIR.defaultBlockState()).`is`(block)
}
return this as T return this as T
} }
@ -168,7 +179,11 @@ class MultiblockBuilder {
} }
fun block(state: BlockState): T { fun block(state: BlockState): T {
predicates.add { pos, access -> access.getBlockState(pos) == state } predicates.add { pos, access ->
// use getChunk directly because we don't want to chunkload
access.getChunk(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z), ChunkStatus.FULL, false)?.getBlockState(pos) == state
}
return this as T return this as T
} }
@ -201,7 +216,7 @@ class MultiblockBuilder {
abstract val strategy: Strategy abstract val strategy: Strategy
} }
class And<P : BlockPredicates<P>>(val parent: P) : BlockPredicates<And<P>>() { class And<P : Part<P>>(val parent: P) : Part<And<P>>() {
init { init {
parent.children.add(this) parent.children.add(this)
} }
@ -212,7 +227,7 @@ class MultiblockBuilder {
get() = Strategy.AND get() = Strategy.AND
} }
class Or<P : BlockPredicates<P>>(val parent: P) : BlockPredicates<Or<P>>() { class Or<P : Part<P>>(val parent: P) : Part<Or<P>>() {
init { init {
parent.children.add(this) parent.children.add(this)
} }
@ -226,7 +241,7 @@ class MultiblockBuilder {
/** /**
* Behaves as if being [Or] node * Behaves as if being [Or] node
*/ */
inner class Node(val pos: BlockPos) : BlockPredicates<Node>() { inner class Node(val pos: BlockPos) : Part<Node>() {
init { init {
check(nodes.put(pos, this) == null) { "Trying to create new node at $pos while already having one" } check(nodes.put(pos, this) == null) { "Trying to create new node at $pos while already having one" }
} }
@ -433,8 +448,8 @@ class Multiblock(
} }
} }
private inner class Config(val pos: BlockPos, parts: Collection<MultiblockFactory.Part>) { private inner class Config(val direction: Direction, val pos: BlockPos, parts: Collection<MultiblockFactory.Part>) {
private inner class Part(val pos: BlockPos, val prototype: MultiblockFactory.Part) { private inner class Part(val pos: BlockPos, val prototype: MultiblockFactory.Part) : Comparable<Part> {
private var blockEntity: BlockEntity? = null private var blockEntity: BlockEntity? = null
private var blockState: BlockState? = null private var blockState: BlockState? = null
private val assignedBlockEntityLists = ArrayList<BEList<*>>(prototype.blockEntityTags.size) private val assignedBlockEntityLists = ArrayList<BEList<*>>(prototype.blockEntityTags.size)
@ -442,6 +457,12 @@ class Multiblock(
private val assignedBlockLists = ArrayList<Object2IntOpenHashMap<Block>>() private val assignedBlockLists = ArrayList<Object2IntOpenHashMap<Block>>()
private val children: ImmutableList<Part> = prototype.children.stream().map { Part(pos, it) }.collect(ImmutableList.toImmutableList()) private val children: ImmutableList<Part> = 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))
if (cmp != 0) return cmp
return SectionPos.blockToSectionCoord(pos.z).compareTo(SectionPos.blockToSectionCoord(other.pos.z))
}
init { init {
prototype.blockEntityTags.forEach { prototype.blockEntityTags.forEach {
assignedBlockEntityLists.add(tag2BlockEntity.computeIfAbsent(it, Object2ObjectFunction { _ -> BEList(it as MultiblockBuilder.EntityTag<BlockEntity>).also { blockEntityLists.add(it) } })) assignedBlockEntityLists.add(tag2BlockEntity.computeIfAbsent(it, Object2ObjectFunction { _ -> BEList(it as MultiblockBuilder.EntityTag<BlockEntity>).also { blockEntityLists.add(it) } }))
@ -596,7 +617,10 @@ class Multiblock(
private val blockStateLists = ArrayList<Reference2IntOpenHashMap<BlockState>>() private val blockStateLists = ArrayList<Reference2IntOpenHashMap<BlockState>>()
private val blockLists = ArrayList<Object2IntOpenHashMap<Block>>() private val blockLists = ArrayList<Object2IntOpenHashMap<Block>>()
private val parts: ImmutableSet<Part> = parts.stream().map { Part(it.pos + pos, it) }.collect(ImmutableSet.toImmutableSet()) private val parts: ImmutableList<Part> = parts.stream()
.map { Part(it.pos + pos, it) }
.sorted() // group/localize parts by in-world chunks to maximize getChunk() cache hit rate
.collect(ImmutableList.toImmutableList())
fun clear() { fun clear() {
blockEntityLists.forEach { it.clear() } blockEntityLists.forEach { it.clear() }
@ -634,13 +658,16 @@ class Multiblock(
} }
} }
private val north = Config(pos, north).also { configurations.add(it) } private val north = Config(Direction.NORTH, pos, north).also { configurations.add(it) }
private val south = Config(pos, south).also { configurations.add(it) } private val south = Config(Direction.SOUTH, pos, south).also { configurations.add(it) }
private val west = Config(pos, west).also { configurations.add(it) } private val west = Config(Direction.WEST, pos, west).also { configurations.add(it) }
private val east = Config(pos, east).also { configurations.add(it) } private val east = Config(Direction.EAST, pos, east).also { configurations.add(it) }
private var activeConfig: Config? = null private var activeConfig: Config? = null
val currentDirection: Direction?
get() = activeConfig?.direction
fun <T : BlockEntity> blockEntities(tag: MultiblockBuilder.EntityTag<T>): Set<T> { fun <T : BlockEntity> blockEntities(tag: MultiblockBuilder.EntityTag<T>): Set<T> {
if (!isValid) return emptySet() if (!isValid) return emptySet()
return activeConfig?.blockEntities(tag) ?: setOf() return activeConfig?.blockEntities(tag) ?: setOf()
@ -678,4 +705,26 @@ class Multiblock(
return false return false
} }
} }
fun update(levelAccessor: LevelAccessor, direction: Direction): Boolean {
if (activeConfig?.direction != direction) {
activeConfig?.clear()
activeConfig = null
}
val config = when (direction) {
Direction.NORTH -> north
Direction.SOUTH -> south
Direction.WEST -> west
Direction.EAST -> east
else -> throw IllegalArgumentException(direction.name)
}
isValid = config.update(levelAccessor)
if (isValid)
activeConfig = config
return isValid
}
} }