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.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.block.IDroppableContainer import ru.dbotthepony.mc.otm.block.IDroppableContainer
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.capability.* 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.Decimal
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
import ru.dbotthepony.mc.otm.core.math.defineDecimal 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.map
import ru.dbotthepony.mc.otm.core.nbt.set 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 { class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBlockEntity(MBlockEntities.CHEMICAL_GENERATOR, pos, state), IDroppableContainer {
override val defaultDisplayName: Component override val defaultDisplayName: Component
@ -53,11 +50,13 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl
get() = container get() = container
val energy = GeneratorEnergyStorage(this::setChangedLight, CAPACITY, THROUGHPUT) 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() { override fun setChangedLight() {
super.setChangedLight() super.setChangedLight()
check = true checkFuelSlot = true
} }
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> { 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) { override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_) super.setLevel(p_155231_)
tickOnceServer(this::checkSurroundings) tickOnceServer(consumers::update)
} }
override fun saveAdditional(nbt: CompoundTag) { override fun saveAdditional(nbt: CompoundTag) {
@ -107,36 +106,6 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl
workTicksTotal = nbt.getInt(WORK_TICKS_TOTAL_KEY) 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 { val itemHandler = container.handler(object : MatteryContainerHooks {
override fun canInsert(slot: Int, stack: ItemStack): Boolean { override fun canInsert(slot: Int, stack: ItemStack): Boolean {
if (slot == SLOT_INPUT) if (slot == SLOT_INPUT)
@ -160,7 +129,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl
super.setBlockState(p_155251_) super.setBlockState(p_155251_)
if (valid) if (valid)
tickOnceServer(this::checkSurroundings) tickOnceServer(consumers::update)
} }
var workTicks = 0 var workTicks = 0
@ -169,7 +138,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl
var workTicksTotal = 0 var workTicksTotal = 0
private set private set
private var check = true private var checkFuelSlot = true
private fun workWithPower(it: IEnergyStorage) { private fun workWithPower(it: IEnergyStorage) {
val extracted = energy.extractEnergy(THROUGHPUT, true) val extracted = energy.extractEnergy(THROUGHPUT, true)
@ -187,7 +156,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl
if (workTicks == 0) { if (workTicks == 0) {
workTicksTotal = 0 workTicksTotal = 0
check = true checkFuelSlot = true
} }
if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) != WorkerState.WORKING) { 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) 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) { if (!container[SLOT_INPUT].isEmpty) {
val ticks = ForgeHooks.getBurnTime(container[SLOT_INPUT], null) val ticks = ForgeHooks.getBurnTime(container[SLOT_INPUT], null)
val residue = container[SLOT_INPUT].item.getCraftingRemainingItem(container[SLOT_INPUT].copy().also { it.count = 1 }) 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 if (energy.batteryLevel.isZero) return
@ -233,7 +202,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBl
} }
for (consumer in consumers) { 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)
}
}
}