From bd2132c06ab809d3d2494530c9841b8b97d955b2 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 14 Jan 2023 19:18:16 +0700 Subject: [PATCH] BESubscribeList Fxies #227 --- .../entity/ChemicalGeneratorBlockEntity.kt | 55 ++------ .../mc/otm/core/collect/WeakHashSet.kt | 53 +++++++ .../mc/otm/core/util/BESubscribeList.kt | 130 ++++++++++++++++++ 3 files changed, 195 insertions(+), 43 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/WeakHashSet.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BESubscribeList.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/ChemicalGeneratorBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/ChemicalGeneratorBlockEntity.kt index 65397f358..d802eb763 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/ChemicalGeneratorBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/ChemicalGeneratorBlockEntity.kt @@ -18,7 +18,6 @@ import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.energy.IEnergyStorage -import ru.dbotthepony.mc.otm.* import ru.dbotthepony.mc.otm.block.IDroppableContainer import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock import ru.dbotthepony.mc.otm.capability.* @@ -33,11 +32,9 @@ import ru.dbotthepony.mc.otm.core.util.WriteOnce import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue import ru.dbotthepony.mc.otm.core.math.defineDecimal -import ru.dbotthepony.mc.otm.core.math.plus -import ru.dbotthepony.mc.otm.core.math.unaryMinus import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.set -import java.lang.ref.WeakReference +import ru.dbotthepony.mc.otm.core.util.BESubscribeList class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBlockEntity(MBlockEntities.CHEMICAL_GENERATOR, pos, state), IDroppableContainer { override val defaultDisplayName: Component @@ -53,11 +50,13 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl get() = container val energy = GeneratorEnergyStorage(this::setChangedLight, CAPACITY, THROUGHPUT) - private val consumers = ArrayList>() + private val consumers = BESubscribeList(this, ForgeCapabilities.ENERGY) + + fun checkSurroundings() = consumers.update() override fun setChangedLight() { super.setChangedLight() - check = true + checkFuelSlot = true } override fun getCapability(cap: Capability, side: Direction?): LazyOptional { @@ -86,7 +85,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl override fun setLevel(p_155231_: Level) { super.setLevel(p_155231_) - tickOnceServer(this::checkSurroundings) + tickOnceServer(consumers::update) } override fun saveAdditional(nbt: CompoundTag) { @@ -107,36 +106,6 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl workTicksTotal = nbt.getInt(WORK_TICKS_TOTAL_KEY) } - fun checkSurroundings() { - if (!valid) - return - - val known = consumers.clone() as ArrayList<*> - consumers.clear() - val level = level ?: return - - for (direction in Direction.values()) { - // нельзя выталкивать энергию через перед - if (direction == blockState.getValue(RotatableMatteryBlock.FACING)) - continue - - val resolver = level.getBlockEntity(blockPos + direction)?.getEnergySided(-direction) - - resolver?.ifPresentK { - if (!known.contains(resolver)) { - val ref = WeakReference(this) - - resolver.addListener { - if (SERVER_IS_LIVE) - ref.get()?.checkSurroundings() - } - } - - consumers.add(resolver) - } - } - } - val itemHandler = container.handler(object : MatteryContainerHooks { override fun canInsert(slot: Int, stack: ItemStack): Boolean { if (slot == SLOT_INPUT) @@ -160,7 +129,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl super.setBlockState(p_155251_) if (valid) - tickOnceServer(this::checkSurroundings) + tickOnceServer(consumers::update) } var workTicks = 0 @@ -169,7 +138,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl var workTicksTotal = 0 private set - private var check = true + private var checkFuelSlot = true private fun workWithPower(it: IEnergyStorage) { val extracted = energy.extractEnergy(THROUGHPUT, true) @@ -187,7 +156,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl if (workTicks == 0) { workTicksTotal = 0 - check = true + checkFuelSlot = true } if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) != WorkerState.WORKING) { @@ -197,7 +166,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS) } - if (workTicks == 0 && !isBlockedByRedstone && check) { + if (workTicks == 0 && !isBlockedByRedstone && checkFuelSlot) { if (!container[SLOT_INPUT].isEmpty) { val ticks = ForgeHooks.getBurnTime(container[SLOT_INPUT], null) val residue = container[SLOT_INPUT].item.getCraftingRemainingItem(container[SLOT_INPUT].copy().also { it.count = 1 }) @@ -220,7 +189,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl } } - check = false + checkFuelSlot = false } if (energy.batteryLevel.isZero) return @@ -233,7 +202,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl } for (consumer in consumers) { - consumer.ifPresent(this::workWithPower) + workWithPower(consumer) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/WeakHashSet.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/WeakHashSet.kt new file mode 100644 index 000000000..b00538f52 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/WeakHashSet.kt @@ -0,0 +1,53 @@ +package ru.dbotthepony.mc.otm.core.collect + +import java.util.WeakHashMap + +/** + * Wrapper around [WeakHashMap] to behave like set + */ +class WeakHashSet : MutableSet { + private val backing = WeakHashMap() + + override fun add(element: E): Boolean { + return backing.put(element, Unit) == null + } + + override fun addAll(elements: Collection): Boolean { + return elements.count(::add) > 0 + } + + override fun clear() { + backing.clear() + } + + override fun iterator(): MutableIterator { + return backing.keys.iterator() + } + + override fun remove(element: E): Boolean { + return backing.remove(element) != null + } + + override fun removeAll(elements: Collection): Boolean { + return backing.keys.removeAll(elements) + } + + override fun retainAll(elements: Collection): Boolean { + return backing.keys.retainAll(elements) + } + + override val size: Int + get() = backing.size + + override fun contains(element: E): Boolean { + return backing.get(element) === Unit + } + + override fun containsAll(elements: Collection): Boolean { + return elements.all(::contains) + } + + override fun isEmpty(): Boolean { + return backing.isEmpty() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BESubscribeList.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BESubscribeList.kt new file mode 100644 index 000000000..6fa95f0e1 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/BESubscribeList.kt @@ -0,0 +1,130 @@ +package ru.dbotthepony.mc.otm.core.util + +import net.minecraft.core.Direction +import net.minecraft.core.SectionPos +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.LazyOptional +import ru.dbotthepony.mc.otm.core.collect.WeakHashSet +import ru.dbotthepony.mc.otm.core.math.plus +import ru.dbotthepony.mc.otm.core.orNull +import ru.dbotthepony.mc.otm.onceServer +import java.lang.ref.WeakReference +import java.util.EnumMap +import java.util.function.Predicate + +open class BESubscribeList( + val block: BlockEntity, + val capability: Capability, +) : Predicate, Iterable { + private val knownCaps = WeakHashSet>() + private val trackedCaps = EnumMap>>(Direction::class.java) + + var valid = true + set(value) { + if (value != field) { + field = value + + if (value) { + update() + } + } + } + + fun invalidate() { + valid = false + } + + fun revive() { + valid = true + } + + // override + protected open fun subscribe(direction: Direction, capability: LazyOptional) {} + // override + protected open fun unsubscribe(direction: Direction, capability: LazyOptional?) {} + + override fun test(t: Direction): Boolean { + return true + } + + fun validate() { + val iterator = trackedCaps.iterator() + + for ((k, v) in iterator) { + if (v.get() == null) { + unsubscribe(k, null) + } + } + } + + override fun iterator(): Iterator { + return object : Iterator { + val iterator = trackedCaps.iterator() + var value: T? = null + + private fun find() { + while (iterator.hasNext() && value == null) { + val (k, v) = iterator.next() + value = v.get()?.orNull() + + if (value == null) { + iterator.remove() + unsubscribe(k, null) + } + } + } + + override fun hasNext(): Boolean { + find() + return value != null + } + + override fun next(): T { + find() + val value = value + this.value = null + return value ?: throw NoSuchElementException() + } + } + } + + fun update() { + if (!valid) + return + + val sorse = block.level?.chunkSource ?: return + var waitChunkLoad = false + + for (it in Direction.stream().filter(this)) { + val pos = block.blockPos + it + val getChunk = sorse.getChunkNow(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z)) + + if (getChunk == null) { + waitChunkLoad = true + } else { + val cap = getChunk.getBlockEntity(pos)?.getCapability(capability) + val knownCap = trackedCaps[it]?.get() + + if (cap != null && cap.isPresent) { + if (knownCap !== cap) { + if (knownCaps.add(cap)) { + val ref = WeakReference(this) + cap.addListener { ref.get()?.update() } + } + + trackedCaps[it] = WeakReference(cap) + subscribe(it, cap) + } + } else { + trackedCaps.remove(it) + unsubscribe(it, knownCap) + } + } + } + + if (waitChunkLoad) { + onceServer(::update) + } + } +}