Multiblock formed/invalid/unloaded states; greatly improve multiblock scanning performance
This commit is contained in:
parent
6ce84804d7
commit
304b6a65c8
@ -6,6 +6,7 @@ import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.entity.FurnaceBlockEntity
|
||||
import net.minecraft.world.level.block.entity.HopperBlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.MultiblockStatus
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.multiblockEntity
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.shapedMultiblock
|
||||
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
|
||||
@ -17,7 +18,7 @@ class MultiblockTestBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (config.update(level!!)) {
|
||||
if (config.update(level!!) == MultiblockStatus.VALID) {
|
||||
println("It matches!")
|
||||
println("hopper block entities: ${config.blockEntities(HOPPERS)}")
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.plus
|
||||
import ru.dbotthepony.mc.otm.core.math.times
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.MultiblockStatus
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.shapedMultiblock
|
||||
import ru.dbotthepony.mc.otm.core.util.InvalidableLazy
|
||||
import ru.dbotthepony.mc.otm.menu.tech.BlackHoleGeneratorMenu
|
||||
@ -169,9 +170,11 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
val multiblock = multiblock
|
||||
|
||||
if (multiblock != null) {
|
||||
if (!multiblock.update(level, blockRotation.back)) {
|
||||
val status = multiblock.update(level, blockRotation.back)
|
||||
|
||||
if (status == MultiblockStatus.INVALID) {
|
||||
this.lastRange = findBlackHoleRange()
|
||||
} else {
|
||||
} else if (status == MultiblockStatus.VALID) {
|
||||
val blackHole = multiblock.blockEntities(BLACK_HOLE).firstOrNull() ?: return
|
||||
|
||||
val matterExtracted = matter.extractMatter(if (mode == Mode.TARGET_MASS && blackHole.mass != targetMass) minOf(injectionRate, (blackHole.mass - targetMass).absoluteValue) else injectionRate, true)
|
||||
|
@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.SectionPos
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
@ -18,9 +19,11 @@ import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||
import ru.dbotthepony.mc.otm.core.getBlockStateNow
|
||||
import ru.dbotthepony.mc.otm.core.getChunkNow
|
||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.plus
|
||||
import ru.dbotthepony.mc.otm.core.math.times
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.MultiblockStatus
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.ShapedMultiblock
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.ShapedMultiblockFactory
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.Strategy
|
||||
@ -37,7 +40,7 @@ class FlywheelBatteryBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
|
||||
private var cachedChargeEfficiency = Decimal.ONE
|
||||
|
||||
val formed: Boolean
|
||||
get() = multiblock?.isValid == true
|
||||
get() = multiblock?.isValid == MultiblockStatus.VALID
|
||||
|
||||
// dangerous as Reference2IntMap.Entry reference live data, and its behavior is undefined once flywheel updates again
|
||||
val currentlyUsedCore: Reference2IntMap.Entry<Block>? get() {
|
||||
@ -109,36 +112,41 @@ class FlywheelBatteryBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
|
||||
super.tick()
|
||||
|
||||
val level = level!!
|
||||
var isUnloaded = false
|
||||
|
||||
if (multiblock == null || !multiblock!!.update(level, blockRotation.front)) {
|
||||
var height = 0
|
||||
val base = blockPos + blockRotation.back.normal * 2
|
||||
if (multiblock == null || multiblock!!.update(level, blockRotation.front) != MultiblockStatus.VALID) {
|
||||
val chunk = level.chunkSource.getChunkNow(blockPos)
|
||||
|
||||
while (level.getBlockStateNow(base.atY(blockPos.y + height)).block == MBlocks.FLYWHEEL_SHAFT) {
|
||||
height++
|
||||
}
|
||||
if (chunk == null) {
|
||||
isUnloaded = true
|
||||
} else {
|
||||
var height = 0
|
||||
val base = blockPos + blockRotation.back.normal * 2
|
||||
|
||||
if (height <= 1) {
|
||||
lastHeight = 0
|
||||
multiblock?.close()
|
||||
multiblock = null
|
||||
} else if (multiblock == null || lastHeight != height) {
|
||||
lastHeight = height
|
||||
multiblock?.close()
|
||||
multiblock = getConfiguration(height).create(blockPos)
|
||||
while (chunk.getBlockState(base.atY(blockPos.y + height)).block == MBlocks.FLYWHEEL_SHAFT) {
|
||||
height++
|
||||
}
|
||||
|
||||
if (height <= 1) {
|
||||
lastHeight = 0
|
||||
multiblock?.close()
|
||||
multiblock = null
|
||||
} else if (multiblock == null || lastHeight != height) {
|
||||
lastHeight = height
|
||||
multiblock?.close()
|
||||
multiblock = getConfiguration(height).create(blockPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (multiblock?.isValid == true) {
|
||||
// energy.parent.batteryLevel *= Decimal("0.99994")
|
||||
// this way energy loss is recorded in graph
|
||||
if (multiblock?.isValid == MultiblockStatus.VALID) {
|
||||
val entry = multiblock!!.blocks(FLYWHEEL_MATERIAL).reference2IntEntrySet().first()
|
||||
val material = FlywheelMaterials[entry.key]!!
|
||||
energy.parent.maxBatteryLevel = material.storage * entry.intValue
|
||||
cachedChargeEfficiency = material.receiveEfficiency
|
||||
currentLossPerTick = energy.parent.batteryLevel * MachinesConfig.Flywheel.PASSIVE_LOSS * material.momentumLossSpeed
|
||||
energy.extractEnergy(currentLossPerTick, false)
|
||||
} else {
|
||||
} else if (!isUnloaded && multiblock?.isValid != MultiblockStatus.NOT_LOADED) {
|
||||
energy.parent.maxBatteryLevel = Decimal.ZERO
|
||||
currentLossPerTick = energy.parent.batteryLevel * MachinesConfig.Flywheel.ACTIVE_LOSS
|
||||
energy.extractEnergy(currentLossPerTick, false)
|
||||
@ -241,10 +249,13 @@ class FlywheelBatteryBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
|
||||
block(MBlocks.FLYWHEEL_HOUSING)
|
||||
|
||||
if (y == height - 1) {
|
||||
block(MBlocks.ENERGY_INPUT_INTERFACE)
|
||||
block(MBlocks.ENERGY_OUTPUT_INTERFACE)
|
||||
tag(EnergyInterfaceBlockEntity.INPUT_TAG)
|
||||
tag(EnergyInterfaceBlockEntity.OUTPUT_TAG)
|
||||
// wrap in subnode to narrow blockentity lookups
|
||||
or {
|
||||
block(MBlocks.ENERGY_INPUT_INTERFACE)
|
||||
block(MBlocks.ENERGY_OUTPUT_INTERFACE)
|
||||
tag(EnergyInterfaceBlockEntity.INPUT_TAG)
|
||||
tag(EnergyInterfaceBlockEntity.OUTPUT_TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import net.minecraft.world.item.component.ItemAttributeModifiers
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import net.minecraft.world.item.crafting.RecipeInput
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.Block
|
||||
@ -44,6 +45,8 @@ import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateHolder
|
||||
import net.minecraft.world.level.block.state.properties.Property
|
||||
import net.minecraft.world.level.chunk.ChunkSource
|
||||
import net.minecraft.world.level.chunk.LevelChunk
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.neoforged.neoforge.fluids.FluidStack
|
||||
import net.neoforged.neoforge.items.IItemHandler
|
||||
@ -127,13 +130,25 @@ operator fun JsonObject.set(s: String, value: Number) = add(s, JsonPrimitive(val
|
||||
operator fun JsonObject.set(s: String, value: Boolean) = add(s, JsonPrimitive(value))
|
||||
|
||||
fun LevelAccessor.getBlockStateNow(pos: BlockPos): BlockState {
|
||||
return chunkSource.getChunkNow(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z))?.getBlockState(pos) ?: Blocks.AIR.defaultBlockState()
|
||||
return getBlockStateNowOrNull(pos) ?: Blocks.AIR.defaultBlockState()
|
||||
}
|
||||
|
||||
fun LevelAccessor.getBlockStateNowOrNull(pos: BlockPos): BlockState? {
|
||||
return chunkSource.getChunkNow(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z))?.getBlockState(pos)
|
||||
}
|
||||
|
||||
fun LevelAccessor.getBlockEntityNow(pos: BlockPos): BlockEntity? {
|
||||
return chunkSource.getChunkNow(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z))?.getBlockEntity(pos)
|
||||
}
|
||||
|
||||
fun ChunkSource.getChunkNow(pos: BlockPos): LevelChunk? {
|
||||
return getChunkNow(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z))
|
||||
}
|
||||
|
||||
fun ChunkSource.getChunkNow(pos: ChunkPos): LevelChunk? {
|
||||
return getChunkNow(pos.x, pos.z)
|
||||
}
|
||||
|
||||
inline val FluidStack.isNotEmpty get() = !isEmpty
|
||||
inline val ItemStack.isNotEmpty get() = !isEmpty
|
||||
|
||||
|
@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.AxisAngle4f
|
||||
import org.joml.Matrix3f
|
||||
@ -420,6 +421,12 @@ operator fun BlockPos.plus(direction: Direction): BlockPos = this.offset(directi
|
||||
operator fun BlockPos.minus(direction: Vec3i): BlockPos = this.subtract(direction)
|
||||
operator fun BlockPos.minus(direction: Direction): BlockPos = this.subtract(direction.normal)
|
||||
|
||||
operator fun ChunkPos.compareTo(other: ChunkPos): Int {
|
||||
val cmp = x.compareTo(other.x)
|
||||
if (cmp != 0) return cmp
|
||||
return z.compareTo(other.z)
|
||||
}
|
||||
|
||||
operator fun Vec3i.plus(direction: Vec3i): Vec3i = this.offset(direction)
|
||||
operator fun Vec3i.plus(direction: Direction): Vec3i = this.offset(direction.normal)
|
||||
operator fun Vec3i.minus(direction: Vec3i): Vec3i = this.subtract(direction)
|
||||
|
@ -12,7 +12,7 @@ interface IMultiblockAccess {
|
||||
/**
|
||||
* Whenever this multiblock is valid (all checks passed)
|
||||
*/
|
||||
val isValid: Boolean
|
||||
val isValid: MultiblockStatus
|
||||
val currentDirection: Direction?
|
||||
val currentNodes: Map<BlockPos, IMultiblockNode>
|
||||
|
||||
|
@ -0,0 +1,32 @@
|
||||
package ru.dbotthepony.mc.otm.core.multiblock
|
||||
|
||||
enum class MultiblockStatus {
|
||||
/**
|
||||
* Multiblock is functional, configuration is valid
|
||||
*/
|
||||
VALID,
|
||||
|
||||
/**
|
||||
* Multiblock status is unknown because it require to check
|
||||
*/
|
||||
NOT_LOADED,
|
||||
|
||||
/**
|
||||
* Multiblock is invalid
|
||||
*/
|
||||
INVALID;
|
||||
|
||||
fun or(other: MultiblockStatus): MultiblockStatus {
|
||||
if (ordinal <= other.ordinal) {
|
||||
return this
|
||||
} else {
|
||||
return other
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun valid(status: Boolean): MultiblockStatus {
|
||||
return if (status) VALID else INVALID
|
||||
}
|
||||
}
|
||||
}
|
@ -7,19 +7,21 @@ import it.unimi.dsi.fastutil.objects.Reference2IntMaps
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.SectionPos
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.chunk.LevelChunk
|
||||
import net.minecraft.world.phys.AABB
|
||||
import ru.dbotthepony.kommons.util.Listenable
|
||||
import ru.dbotthepony.mc.otm.core.collect.collect
|
||||
import ru.dbotthepony.mc.otm.core.collect.map
|
||||
import ru.dbotthepony.mc.otm.core.getBlockEntityNow
|
||||
import ru.dbotthepony.mc.otm.core.getBlockStateNow
|
||||
import ru.dbotthepony.mc.otm.core.getBlockStateNowOrNull
|
||||
import ru.dbotthepony.mc.otm.core.math.Vector
|
||||
import ru.dbotthepony.mc.otm.core.math.compareTo
|
||||
import ru.dbotthepony.mc.otm.core.math.plus
|
||||
import ru.dbotthepony.mc.otm.core.registryName
|
||||
import ru.dbotthepony.mc.otm.network.syncher.IRemoteState
|
||||
@ -35,7 +37,7 @@ import kotlin.collections.HashMap
|
||||
* [close] MUST be called when multiblock goes out of scope
|
||||
*/
|
||||
class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMultiblockAccess, ISynchable, Closeable, GlobalBlockEntityRemovalListener {
|
||||
override var isValid: Boolean = false
|
||||
override var isValid: MultiblockStatus = MultiblockStatus.INVALID
|
||||
private set
|
||||
|
||||
private val customChecks = factory.customChecks
|
||||
@ -68,7 +70,9 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
get() = remotes.isNotEmpty()
|
||||
|
||||
private inner class Config(override val currentDirection: Direction, val pos: BlockPos, parts: Collection<ShapedMultiblockFactory.Part>) : IMultiblockAccess, ISynchable, GlobalBlockEntityRemovalListener, Comparable<Config> {
|
||||
private inner class Part(override val pos: BlockPos, val prototype: ShapedMultiblockFactory.Part) : Comparable<Part>, IMultiblockNode {
|
||||
private inner class Part private constructor(override val pos: BlockPos, val prototype: ShapedMultiblockFactory.Part, val chunkPos: ChunkPos) : Comparable<Part>, IMultiblockNode {
|
||||
constructor(pos: BlockPos, prototype: ShapedMultiblockFactory.Part) : this(pos, prototype, ChunkPos(pos))
|
||||
|
||||
var index = -1
|
||||
|
||||
private var blockEntity: BlockEntity? = null
|
||||
@ -76,12 +80,10 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
private val assignedBlockEntityLists = ArrayList<BlockEntitySet<*>>(prototype.blockEntityTags.size)
|
||||
private val assignedBlockStateLists = ArrayList<Reference2IntMap<BlockState>>()
|
||||
private val assignedBlockLists = ArrayList<Reference2IntMap<Block>>()
|
||||
private val children: ImmutableList<Part> by lazy(LazyThreadSafetyMode.NONE) { prototype.children.iterator().map { Part(pos, it) }.collect(ImmutableList.toImmutableList()) }
|
||||
private val children: ImmutableList<Part> by lazy(LazyThreadSafetyMode.NONE) { prototype.children.iterator().map { Part(pos, it, chunkPos) }.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))
|
||||
return chunkPos.compareTo(other.chunkPos)
|
||||
}
|
||||
|
||||
init {
|
||||
@ -219,9 +221,19 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
return test
|
||||
}
|
||||
|
||||
fun test(levelAccessor: LevelAccessor): Boolean {
|
||||
fun test(levelAccessor: LevelAccessor): MultiblockStatus {
|
||||
val blockEntity = levelAccessor.getBlockEntityNow(pos)
|
||||
val blockState = levelAccessor.getBlockStateNow(pos)
|
||||
val blockState = levelAccessor.getBlockStateNowOrNull(pos) ?: return MultiblockStatus.NOT_LOADED
|
||||
return test(levelAccessor, blockState, blockEntity)
|
||||
}
|
||||
|
||||
fun test(levelAccessor: LevelAccessor, chunk: LevelChunk): MultiblockStatus {
|
||||
val blockEntity = chunk.getBlockEntity(pos)
|
||||
val blockState = chunk.getBlockState(pos)
|
||||
return test(levelAccessor, blockState, blockEntity)
|
||||
}
|
||||
|
||||
fun test(levelAccessor: LevelAccessor, blockState: BlockState, blockEntity: BlockEntity?): MultiblockStatus {
|
||||
val status = test0(levelAccessor, blockState, blockEntity)
|
||||
|
||||
val previous = this.status
|
||||
@ -231,7 +243,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
pushNetworkPartUpdate(this)
|
||||
}
|
||||
|
||||
return status
|
||||
return if (status) MultiblockStatus.VALID else MultiblockStatus.INVALID
|
||||
}
|
||||
|
||||
private fun clearFull() {
|
||||
@ -371,6 +383,20 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
.sorted() // group/localize parts by in-world chunks to maximize getChunk() cache hit rate
|
||||
.collect(ImmutableMap.toImmutableMap({ it.pos }, { it }))
|
||||
|
||||
private val partsByChunk: ImmutableMap<ChunkPos, ImmutableList<Part>>
|
||||
|
||||
init {
|
||||
val builder = HashMap<ChunkPos, ArrayList<Part>>()
|
||||
|
||||
for (part in this.parts.values) {
|
||||
builder.computeIfAbsent(part.chunkPos) { ArrayList() }.add(part)
|
||||
}
|
||||
|
||||
partsByChunk = builder.entries.stream()
|
||||
.map { it.key to ImmutableList.copyOf(it.value) }
|
||||
.collect(ImmutableMap.toImmutableMap({ it.first }, { it.second }))
|
||||
}
|
||||
|
||||
init {
|
||||
for ((i, part) in this.parts.values.withIndex()) {
|
||||
part.index = i
|
||||
@ -395,7 +421,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
while (networkChangelog.size > parts.size) networkChangelog.removeLast()
|
||||
}
|
||||
|
||||
override var isValid: Boolean = false
|
||||
override var isValid: MultiblockStatus = MultiblockStatus.INVALID
|
||||
private set(value) {
|
||||
if (value != field) {
|
||||
field = value
|
||||
@ -403,7 +429,6 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
}
|
||||
}
|
||||
|
||||
private var iterator = this.parts.values.iterator()
|
||||
private var validParts = 0
|
||||
|
||||
override fun compareTo(other: Config): Int {
|
||||
@ -412,42 +437,46 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
return cmp
|
||||
}
|
||||
|
||||
fun updateRemaining(levelAccessor: LevelAccessor) {
|
||||
fun update(levelAccessor: LevelAccessor, updateEverything: Boolean = remotes.isNotEmpty()): MultiblockStatus {
|
||||
val networkVersion = networkVersion
|
||||
|
||||
if (!isValid) { // update rest
|
||||
for (part in iterator) {
|
||||
if (part.test(levelAccessor)) {
|
||||
validParts++
|
||||
}
|
||||
isValid = MultiblockStatus.VALID
|
||||
validParts = 0
|
||||
|
||||
for ((chunkPos, parts) in partsByChunk) {
|
||||
val chunk = levelAccessor.chunkSource.getChunkNow(chunkPos.x, chunkPos.z)
|
||||
|
||||
if (chunk == null) {
|
||||
isValid = MultiblockStatus.NOT_LOADED
|
||||
break
|
||||
}
|
||||
} else {
|
||||
for (part in iterator) {
|
||||
if (part.test(levelAccessor))
|
||||
validParts++
|
||||
else {
|
||||
isValid = false
|
||||
break
|
||||
|
||||
if (updateEverything) {
|
||||
for (part in parts) {
|
||||
val status = part.test(levelAccessor, chunk)
|
||||
if (status === MultiblockStatus.VALID) validParts++
|
||||
isValid = isValid.or(status)
|
||||
}
|
||||
} else {
|
||||
for (part in parts) {
|
||||
val status = part.test(levelAccessor, chunk)
|
||||
|
||||
if (status === MultiblockStatus.VALID)
|
||||
validParts++
|
||||
else {
|
||||
isValid = status
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid != MultiblockStatus.VALID) clear()
|
||||
|
||||
if (this.networkVersion != networkVersion) {
|
||||
remotes.forEach { it.listener.run() }
|
||||
}
|
||||
}
|
||||
|
||||
fun update(levelAccessor: LevelAccessor, updateEverything: Boolean = remotes.isNotEmpty()): Boolean {
|
||||
isValid = true
|
||||
iterator = this.parts.values.iterator()
|
||||
validParts = 0
|
||||
updateRemaining(levelAccessor)
|
||||
|
||||
if (updateEverything && iterator.hasNext()) {
|
||||
updateRemaining(levelAccessor)
|
||||
}
|
||||
|
||||
if (!isValid) clear()
|
||||
return isValid
|
||||
}
|
||||
|
||||
@ -565,7 +594,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
|
||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||
isRemote = true
|
||||
isValid = stream.readBoolean()
|
||||
isValid = MultiblockStatus.entries[stream.readByte().toInt()]
|
||||
activeConfig = configurations[stream.readUnsignedByte().toInt()]
|
||||
activeConfig.read(stream)
|
||||
}
|
||||
@ -580,7 +609,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
}
|
||||
|
||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||
stream.writeBoolean(isValid)
|
||||
stream.writeByte(isValid.ordinal)
|
||||
stream.writeByte(activeConfig.index)
|
||||
remotes[activeConfig.index].write(stream)
|
||||
}
|
||||
@ -608,36 +637,36 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
}
|
||||
|
||||
override val currentDirection: Direction?
|
||||
get() = if (isValid) activeConfig.currentDirection else null
|
||||
get() = if (isValid == MultiblockStatus.VALID) activeConfig.currentDirection else null
|
||||
|
||||
override val boundingBox: AABB?
|
||||
get() = if (isValid) activeConfig.boundingBox else null
|
||||
get() = if (isValid == MultiblockStatus.VALID) activeConfig.boundingBox else null
|
||||
|
||||
override val currentNodes: Map<BlockPos, IMultiblockNode>
|
||||
get() = activeConfig.parts
|
||||
|
||||
override fun <T : Any> blockEntities(tag: BlockEntityTag<T>): Set<T> {
|
||||
if (!isValid) return setOf()
|
||||
if (isValid != MultiblockStatus.VALID) return setOf()
|
||||
return activeConfig.blockEntities(tag)
|
||||
}
|
||||
|
||||
override fun blocks(tag: Any): Reference2IntMap<Block> {
|
||||
if (!isValid) return Reference2IntMaps.emptyMap()
|
||||
if (isValid != MultiblockStatus.VALID) return Reference2IntMaps.emptyMap()
|
||||
return activeConfig.blocks(tag)
|
||||
}
|
||||
|
||||
override fun blockStates(tag: Any): Reference2IntMap<BlockState> {
|
||||
if (!isValid) return Reference2IntMaps.emptyMap()
|
||||
if (isValid != MultiblockStatus.VALID) return Reference2IntMaps.emptyMap()
|
||||
return activeConfig.blockStates(tag)
|
||||
}
|
||||
|
||||
override fun blocks(): Reference2IntMap<Block> {
|
||||
if (!isValid) return Reference2IntMaps.emptyMap()
|
||||
if (isValid != MultiblockStatus.VALID) return Reference2IntMaps.emptyMap()
|
||||
return activeConfig.blocks()
|
||||
}
|
||||
|
||||
override fun blockStates(): Reference2IntMap<BlockState> {
|
||||
if (!isValid) return Reference2IntMaps.emptyMap()
|
||||
if (isValid != MultiblockStatus.VALID) return Reference2IntMaps.emptyMap()
|
||||
return activeConfig.blockStates()
|
||||
}
|
||||
|
||||
@ -645,7 +674,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
return activeConfig.blockEntityRemoved(blockEntity)
|
||||
}
|
||||
|
||||
fun update(levelAccessor: LevelAccessor): Boolean {
|
||||
fun update(levelAccessor: LevelAccessor): MultiblockStatus {
|
||||
isUpdating = true
|
||||
val thisGeneration = generation
|
||||
|
||||
@ -659,25 +688,29 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
configurations.add(max)
|
||||
}
|
||||
|
||||
isValid = false
|
||||
isValid = MultiblockStatus.INVALID
|
||||
var hadUnloadedChunks = false
|
||||
|
||||
while (configurations.isNotEmpty()) {
|
||||
val config = configurations.removeFirst()
|
||||
val status = config.update(levelAccessor)
|
||||
|
||||
if (config.update(levelAccessor)) {
|
||||
if (status == MultiblockStatus.VALID) {
|
||||
if (customChecks.all { it.test(config) }) {
|
||||
activeConfig = config
|
||||
isValid = true
|
||||
isValid = MultiblockStatus.VALID
|
||||
remotes.forEach { it.listener.run() }
|
||||
|
||||
if (thisGeneration != generation) {
|
||||
changeListeners.forEach { it.runnable.run() }
|
||||
}
|
||||
|
||||
return true
|
||||
return status
|
||||
} else {
|
||||
config.clear()
|
||||
}
|
||||
} else if (status == MultiblockStatus.NOT_LOADED) {
|
||||
hadUnloadedChunks = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -685,22 +718,23 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
changeListeners.forEach { it.runnable.run() }
|
||||
}
|
||||
|
||||
return false
|
||||
isValid = if (hadUnloadedChunks) MultiblockStatus.NOT_LOADED else MultiblockStatus.INVALID
|
||||
return isValid
|
||||
} finally {
|
||||
isUpdating = false
|
||||
}
|
||||
}
|
||||
|
||||
fun update(levelAccessor: LevelAccessor, direction: Direction): Boolean {
|
||||
fun update(levelAccessor: LevelAccessor, direction: Direction): MultiblockStatus {
|
||||
isUpdating = true
|
||||
val thisGeneration = generation
|
||||
|
||||
try {
|
||||
var changes = false
|
||||
|
||||
if (activeConfig.currentDirection != direction && isValid) {
|
||||
if (activeConfig.currentDirection != direction && isValid === MultiblockStatus.VALID) {
|
||||
activeConfig.clear()
|
||||
isValid = false
|
||||
isValid = MultiblockStatus.INVALID
|
||||
changes = true
|
||||
}
|
||||
|
||||
@ -716,9 +750,9 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
activeConfig = config
|
||||
isValid = config.update(levelAccessor)
|
||||
|
||||
if (isValid) {
|
||||
isValid = customChecks.all { it.test(config) }
|
||||
if (!isValid) config.clear()
|
||||
if (isValid === MultiblockStatus.VALID) {
|
||||
isValid = MultiblockStatus.valid(customChecks.all { it.test(config) })
|
||||
if (isValid !== MultiblockStatus.VALID) config.clear()
|
||||
}
|
||||
|
||||
if (changes) {
|
||||
@ -739,6 +773,6 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
|
||||
changeListeners.clear()
|
||||
remotes.forEach { it.close() }
|
||||
configurations.forEach { it.clear() }
|
||||
isValid = false
|
||||
isValid = MultiblockStatus.INVALID
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.menu.tech
|
||||
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleGeneratorBlockEntity
|
||||
import ru.dbotthepony.mc.otm.core.multiblock.MultiblockStatus
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
||||
import ru.dbotthepony.mc.otm.menu.input.DecimalInputWithFeedback
|
||||
@ -15,7 +16,7 @@ class BlackHoleGeneratorMenu(
|
||||
inventory: Inventory,
|
||||
tile: BlackHoleGeneratorBlockEntity? = null,
|
||||
) : MatteryMenu(MMenus.BLACK_HOLE_GENERATOR, p_38852_, inventory, tile) {
|
||||
val formed = mSynchronizer.computedBoolean(BooleanSupplier { tile?.multiblock?.isValid ?: false })
|
||||
val formed = mSynchronizer.computedBoolean(BooleanSupplier { tile?.multiblock?.isValid == MultiblockStatus.VALID })
|
||||
val drawBuildingGuide = BooleanInputWithFeedback(this, tile?.let { it::drawBuildingGuide })
|
||||
val energy = CombinedProfiledLevelGaugeWidget(this, tile?.energy)
|
||||
val matter = CombinedProfiledLevelGaugeWidget(this, tile?.matter)
|
||||
|
Loading…
Reference in New Issue
Block a user