BESubscribeList

Fxies #227
This commit is contained in:
DBotThePony 2023-01-14 19:18:16 +07:00
parent ea6a237de8
commit bd2132c06a
Signed by: DBot
GPG Key ID: DCC23B5715498507
3 changed files with 195 additions and 43 deletions

View File

@ -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<LazyOptional<out IEnergyStorage>>()
private val consumers = BESubscribeList(this, ForgeCapabilities.ENERGY)
fun checkSurroundings() = consumers.update()
override fun setChangedLight() {
super.setChangedLight()
check = true
checkFuelSlot = true
}
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
@ -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)
}
}

View File

@ -0,0 +1,53 @@
package ru.dbotthepony.mc.otm.core.collect
import java.util.WeakHashMap
/**
* Wrapper around [WeakHashMap] to behave like set
*/
class WeakHashSet<E : Any> : MutableSet<E> {
private val backing = WeakHashMap<E, Unit>()
override fun add(element: E): Boolean {
return backing.put(element, Unit) == null
}
override fun addAll(elements: Collection<E>): Boolean {
return elements.count(::add) > 0
}
override fun clear() {
backing.clear()
}
override fun iterator(): MutableIterator<E> {
return backing.keys.iterator()
}
override fun remove(element: E): Boolean {
return backing.remove(element) != null
}
override fun removeAll(elements: Collection<E>): Boolean {
return backing.keys.removeAll(elements)
}
override fun retainAll(elements: Collection<E>): 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<E>): Boolean {
return elements.all(::contains)
}
override fun isEmpty(): Boolean {
return backing.isEmpty()
}
}

View File

@ -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<T>(
val block: BlockEntity,
val capability: Capability<T>,
) : Predicate<Direction>, Iterable<T> {
private val knownCaps = WeakHashSet<LazyOptional<T>>()
private val trackedCaps = EnumMap<Direction, WeakReference<LazyOptional<T>>>(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<T>) {}
// override
protected open fun unsubscribe(direction: Direction, capability: LazyOptional<T>?) {}
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<T> {
return object : Iterator<T> {
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)
}
}
}