Move everything to BESubscribeList (and eliminate old, bugged code)
This commit is contained in:
parent
3c73f809dc
commit
95a9aa72aa
@ -19,6 +19,7 @@ import net.minecraft.world.level.block.Block
|
|||||||
import net.minecraft.world.phys.shapes.CollisionContext
|
import net.minecraft.world.phys.shapes.CollisionContext
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape
|
import net.minecraft.world.phys.shapes.VoxelShape
|
||||||
import ru.dbotthepony.mc.otm.once
|
import ru.dbotthepony.mc.otm.once
|
||||||
|
import ru.dbotthepony.mc.otm.oncePre
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ class BatteryBankBlock : RotatableMatteryBlock(), EntityBlock {
|
|||||||
) {
|
) {
|
||||||
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
|
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
|
||||||
val blockEntity = level.getBlockEntity(pos) as? BatteryBankBlockEntity ?: return
|
val blockEntity = level.getBlockEntity(pos) as? BatteryBankBlockEntity ?: return
|
||||||
level.once { blockEntity.checkSurroundings(level) }
|
level.oncePre { blockEntity.checkSurroundings() }
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -31,6 +31,7 @@ import ru.dbotthepony.mc.otm.core.*
|
|||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.unaryMinus
|
import ru.dbotthepony.mc.otm.core.math.unaryMinus
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.BESubscribeList
|
||||||
import ru.dbotthepony.mc.otm.menu.BatteryBankMenu
|
import ru.dbotthepony.mc.otm.menu.BatteryBankMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
|
|
||||||
@ -263,7 +264,7 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
|
|||||||
|
|
||||||
override fun setLevel(p_155231_: Level) {
|
override fun setLevel(p_155231_: Level) {
|
||||||
super.setLevel(p_155231_)
|
super.setLevel(p_155231_)
|
||||||
tickOnceServer(this::checkSurroundings)
|
checkSurroundings()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
|
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
|
||||||
@ -294,41 +295,23 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
|
|||||||
return super.getCapability(cap,side)
|
return super.getCapability(cap,side)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var consumingCapability = LazyOptional.empty<IEnergyStorage>()
|
private val consumers = object : BESubscribeList<IEnergyStorage>(this@BatteryBankBlockEntity, ForgeCapabilities.ENERGY) {
|
||||||
|
override fun test(t: Direction): Boolean {
|
||||||
fun checkSurroundings(level: Level) {
|
return -blockState.getValue(RotatableMatteryBlock.FACING) == t
|
||||||
if (isRemoved) return
|
|
||||||
|
|
||||||
val tile = level.getBlockEntity(blockPos.offset(blockState.getValue(RotatableMatteryBlock.FACING).normal))
|
|
||||||
|
|
||||||
if (tile == null) {
|
|
||||||
consumingCapability = LazyOptional.empty()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
consumingCapability = getAndBind(
|
|
||||||
old = consumingCapability,
|
|
||||||
provider = tile,
|
|
||||||
capability = ForgeCapabilities.ENERGY,
|
|
||||||
side = -blockState.getValue(RotatableMatteryBlock.FACING)
|
|
||||||
) {
|
|
||||||
@Suppress("name_shadowing")
|
|
||||||
val level = this.level
|
|
||||||
|
|
||||||
if (level is ServerLevel && !SERVER_IS_LIVE)
|
|
||||||
checkSurroundings(level)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkSurroundings() = consumers.update((-blockState.getValue(RotatableMatteryBlock.FACING))::equals)
|
||||||
|
|
||||||
fun tick() {
|
fun tick() {
|
||||||
if (isBlockedByRedstone)
|
if (isBlockedByRedstone)
|
||||||
return
|
return
|
||||||
|
|
||||||
consumingCapability.ifPresentK {
|
for (it in consumers) {
|
||||||
val (_, maxThroughput) = energy.getDistribution(false)
|
val (_, maxThroughput) = energy.getDistribution(false)
|
||||||
|
|
||||||
if (maxThroughput.isZero)
|
if (maxThroughput.isZero)
|
||||||
return@ifPresentK
|
continue
|
||||||
|
|
||||||
val diff = it.receiveEnergy(maxThroughput, true)
|
val diff = it.receiveEnergy(maxThroughput, true)
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import ru.dbotthepony.mc.otm.core.nbt.getByteArrayList
|
|||||||
import ru.dbotthepony.mc.otm.core.nbt.ifHas
|
import ru.dbotthepony.mc.otm.core.nbt.ifHas
|
||||||
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 ru.dbotthepony.mc.otm.core.util.BESubscribeList
|
||||||
import ru.dbotthepony.mc.otm.menu.EnergyCounterMenu
|
import ru.dbotthepony.mc.otm.menu.EnergyCounterMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
@ -125,8 +126,17 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
private val energyInput = EnergyCounterCap(true)
|
private val energyInput = EnergyCounterCap(true)
|
||||||
private val energyOutput = EnergyCounterCap(false)
|
private val energyOutput = EnergyCounterCap(false)
|
||||||
|
|
||||||
private var inputCapability: LazyOptional<out IEnergyStorage> = LazyOptional.empty()
|
private val inputCapability = object : BESubscribeList<IEnergyStorage>(this@EnergyCounterBlockEntity, ForgeCapabilities.ENERGY) {
|
||||||
private var outputCapability: LazyOptional<out IEnergyStorage> = LazyOptional.empty()
|
override fun test(t: Direction): Boolean {
|
||||||
|
return t == blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val outputCapability = object : BESubscribeList<IEnergyStorage>(this@EnergyCounterBlockEntity, ForgeCapabilities.ENERGY) {
|
||||||
|
override fun test(t: Direction): Boolean {
|
||||||
|
return t == -blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun setLevel(p_155231_: Level) {
|
override fun setLevel(p_155231_: Level) {
|
||||||
super.setLevel(p_155231_)
|
super.setLevel(p_155231_)
|
||||||
@ -137,9 +147,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
override val energyFlow = FlowDirection.input(isInput)
|
override val energyFlow = FlowDirection.input(isInput)
|
||||||
|
|
||||||
override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
|
override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
if (inputCapability.isPresent) {
|
val it = inputCapability.first
|
||||||
val it = inputCapability.resolve().get()
|
|
||||||
|
|
||||||
|
if (it != null) {
|
||||||
val diff: Decimal
|
val diff: Decimal
|
||||||
|
|
||||||
val ioLimit = ioLimit
|
val ioLimit = ioLimit
|
||||||
@ -162,9 +172,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
|
override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
if (outputCapability.isPresent) {
|
val it = outputCapability.first
|
||||||
val it = outputCapability.resolve().get()
|
|
||||||
|
|
||||||
|
if (it != null) {
|
||||||
val diff: Decimal
|
val diff: Decimal
|
||||||
|
|
||||||
val ioLimit = ioLimit
|
val ioLimit = ioLimit
|
||||||
@ -192,9 +202,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
override var batteryLevel: Decimal
|
override var batteryLevel: Decimal
|
||||||
get() {
|
get() {
|
||||||
if (energyFlow.input) {
|
if (energyFlow.input) {
|
||||||
if (outputCapability.isPresent) {
|
val it = outputCapability.first
|
||||||
val it = outputCapability.resolve().get()
|
|
||||||
|
|
||||||
|
if (it != null) {
|
||||||
if (it is IMatteryEnergyStorage) {
|
if (it is IMatteryEnergyStorage) {
|
||||||
return it.batteryLevel
|
return it.batteryLevel
|
||||||
}
|
}
|
||||||
@ -202,9 +212,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
return Decimal(it.energyStored)
|
return Decimal(it.energyStored)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (inputCapability.isPresent) {
|
val it = inputCapability.first
|
||||||
val it = inputCapability.resolve().get()
|
|
||||||
|
|
||||||
|
if (it != null) {
|
||||||
if (it is IMatteryEnergyStorage) {
|
if (it is IMatteryEnergyStorage) {
|
||||||
return it.batteryLevel
|
return it.batteryLevel
|
||||||
}
|
}
|
||||||
@ -222,9 +232,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
override val maxBatteryLevel: Decimal
|
override val maxBatteryLevel: Decimal
|
||||||
get() {
|
get() {
|
||||||
if (energyFlow.input) {
|
if (energyFlow.input) {
|
||||||
if (outputCapability.isPresent) {
|
val it = outputCapability.first
|
||||||
val it = outputCapability.resolve().get()
|
|
||||||
|
|
||||||
|
if (it != null) {
|
||||||
if (it is IMatteryEnergyStorage) {
|
if (it is IMatteryEnergyStorage) {
|
||||||
return it.maxBatteryLevel
|
return it.maxBatteryLevel
|
||||||
}
|
}
|
||||||
@ -232,9 +242,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
return Decimal(it.maxEnergyStored)
|
return Decimal(it.maxEnergyStored)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (inputCapability.isPresent) {
|
val it = inputCapability.first
|
||||||
val it = inputCapability.resolve().get()
|
|
||||||
|
|
||||||
|
if (it != null) {
|
||||||
if (it is IMatteryEnergyStorage) {
|
if (it is IMatteryEnergyStorage) {
|
||||||
return it.maxBatteryLevel
|
return it.maxBatteryLevel
|
||||||
}
|
}
|
||||||
@ -249,9 +259,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
override val missingPower: Decimal
|
override val missingPower: Decimal
|
||||||
get() {
|
get() {
|
||||||
if (energyFlow.input) {
|
if (energyFlow.input) {
|
||||||
if (outputCapability.isPresent) {
|
val it = outputCapability.first
|
||||||
val it = outputCapability.resolve().get()
|
|
||||||
|
|
||||||
|
if (it != null) {
|
||||||
if (it is IMatteryEnergyStorage) {
|
if (it is IMatteryEnergyStorage) {
|
||||||
return it.missingPower
|
return it.missingPower
|
||||||
}
|
}
|
||||||
@ -259,9 +269,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
return Decimal((it.maxEnergyStored - it.energyStored).coerceAtLeast(0))
|
return Decimal((it.maxEnergyStored - it.energyStored).coerceAtLeast(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (inputCapability.isPresent) {
|
val it = inputCapability.first
|
||||||
val it = inputCapability.resolve().get()
|
|
||||||
|
|
||||||
|
if (it != null) {
|
||||||
if (it is IMatteryEnergyStorage) {
|
if (it is IMatteryEnergyStorage) {
|
||||||
return it.missingPower
|
return it.missingPower
|
||||||
}
|
}
|
||||||
@ -326,45 +336,9 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAndBind(
|
|
||||||
level: Level,
|
|
||||||
old: LazyOptional<out IEnergyStorage>,
|
|
||||||
side: Direction
|
|
||||||
): LazyOptional<out IEnergyStorage> {
|
|
||||||
val ent = level.getBlockEntity(blockPos.offset(side.normal)) ?: return LazyOptional.empty()
|
|
||||||
val resolve = ent.getEnergySided(-side)
|
|
||||||
|
|
||||||
if (resolve !== old) {
|
|
||||||
if (resolve.isPresent) {
|
|
||||||
val weak = WeakReference(this)
|
|
||||||
|
|
||||||
resolve.addListener {
|
|
||||||
if (SERVER_IS_LIVE)
|
|
||||||
weak.get()?.checkSurroundings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve
|
|
||||||
}
|
|
||||||
|
|
||||||
return old
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkSurroundings() {
|
fun checkSurroundings() {
|
||||||
val level = level
|
inputCapability.update((blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION))::equals)
|
||||||
if (isRemoved || level !is ServerLevel) return
|
outputCapability.update((-blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION))::equals)
|
||||||
|
|
||||||
inputCapability = getAndBind(
|
|
||||||
level,
|
|
||||||
inputCapability,
|
|
||||||
blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION)
|
|
||||||
)
|
|
||||||
|
|
||||||
outputCapability = getAndBind(
|
|
||||||
level,
|
|
||||||
outputCapability,
|
|
||||||
-blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
|
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
|
||||||
|
@ -98,29 +98,6 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
level.oncePre { if (!isRemoved) func.invoke(level) }
|
level.oncePre { if (!isRemoved) func.invoke(level) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun <T> getAndBind(
|
|
||||||
old: LazyOptional<T>,
|
|
||||||
provider: ICapabilityProvider?,
|
|
||||||
capability: Capability<T>,
|
|
||||||
side: Direction,
|
|
||||||
invalidate: Runnable
|
|
||||||
): LazyOptional<T> {
|
|
||||||
val get = provider?.getCapability(capability, side) ?: LazyOptional.empty()
|
|
||||||
|
|
||||||
if (old !== get) {
|
|
||||||
if (get.isPresent) {
|
|
||||||
val ref = WeakReference(invalidate)
|
|
||||||
get.addListener {
|
|
||||||
ref.get()?.run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return get.cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
return get.cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getDisplayName(): Component {
|
override fun getDisplayName(): Component {
|
||||||
return customDisplayName ?: defaultDisplayName
|
return customDisplayName ?: defaultDisplayName
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,11 @@ import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
|
|||||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||||
import ru.dbotthepony.mc.otm.core.*
|
import ru.dbotthepony.mc.otm.core.*
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.plus
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.toIntSafe
|
import ru.dbotthepony.mc.otm.core.math.toIntSafe
|
||||||
import ru.dbotthepony.mc.otm.core.math.unaryMinus
|
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 ru.dbotthepony.mc.otm.core.util.BESubscribeList
|
||||||
import ru.dbotthepony.mc.otm.graph.Graph6Node
|
import ru.dbotthepony.mc.otm.graph.Graph6Node
|
||||||
import ru.dbotthepony.mc.otm.graph.GraphNodeListener
|
import ru.dbotthepony.mc.otm.graph.GraphNodeListener
|
||||||
import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
|
import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
|
||||||
@ -81,12 +81,14 @@ abstract class AbstractStorageImportExport<T>(
|
|||||||
override fun invalidateCaps() {
|
override fun invalidateCaps() {
|
||||||
super.invalidateCaps()
|
super.invalidateCaps()
|
||||||
cell.invalidate()
|
cell.invalidate()
|
||||||
|
target.invalidate()
|
||||||
valid = false
|
valid = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reviveCaps() {
|
override fun reviveCaps() {
|
||||||
super.reviveCaps()
|
super.reviveCaps()
|
||||||
cell.revive()
|
cell.revive()
|
||||||
|
target.revive()
|
||||||
valid = true
|
valid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,21 +103,22 @@ abstract class AbstractStorageImportExport<T>(
|
|||||||
|
|
||||||
if (p_155231_ is ServerLevel) {
|
if (p_155231_ is ServerLevel) {
|
||||||
StorageNetworkGraph.discoverFull(this, cell.storageNode)
|
StorageNetworkGraph.discoverFull(this, cell.storageNode)
|
||||||
}
|
|
||||||
|
|
||||||
tickOnceServer(this::checkSurroundings)
|
tickOnceServer(this::checkSurroundings)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected var target: LazyOptional<T> = LazyOptional.empty()
|
|
||||||
protected abstract val targetCapability: Capability<T>
|
protected abstract val targetCapability: Capability<T>
|
||||||
|
|
||||||
|
protected val target by lazy {
|
||||||
|
object : BESubscribeList<T>(this@AbstractStorageImportExport, targetCapability) {
|
||||||
|
override fun test(t: Direction): Boolean {
|
||||||
|
return t == -this@AbstractStorageImportExport.blockState.getValue(RotatableMatteryBlock.FACING_FULL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun checkSurroundings() {
|
fun checkSurroundings() {
|
||||||
target = getAndBind(
|
target.update()
|
||||||
target,
|
|
||||||
level?.getBlockEntity(blockPos + blockState.getValue(RotatableMatteryBlock.FACING_FULL).normal),
|
|
||||||
targetCapability,
|
|
||||||
-blockState.getValue(RotatableMatteryBlock.FACING_FULL),
|
|
||||||
) { tickOnceServer(this::checkSurroundings) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract val filter: ItemFilter
|
abstract val filter: ItemFilter
|
||||||
@ -226,18 +229,18 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
|
|
||||||
nextTick--
|
nextTick--
|
||||||
|
|
||||||
if (nextTick <= 0 && target.isPresent && enoughEnergy) {
|
val target = target.firstOrNull()
|
||||||
|
|
||||||
|
if (nextTick <= 0 && target != null && enoughEnergy) {
|
||||||
val graph = cell.storageGraph ?: return
|
val graph = cell.storageGraph ?: return
|
||||||
val items = graph.getVirtualComponent(ITEM_STORAGE)
|
val items = graph.getVirtualComponent(ITEM_STORAGE)
|
||||||
|
|
||||||
val resolved = target.orThrow()
|
if (lastSlot >= target.slots) {
|
||||||
|
|
||||||
if (lastSlot >= resolved.slots) {
|
|
||||||
lastSlot = 0
|
lastSlot = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val maxMove = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, MAX_MOVE_PER_OPERATION, true)
|
val maxMove = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, MAX_MOVE_PER_OPERATION, true)
|
||||||
var extracted = resolved.extractItem(lastSlot, maxMove, true)
|
var extracted = target.extractItem(lastSlot, maxMove, true)
|
||||||
|
|
||||||
if (extracted.isEmpty || !filter.match(extracted)) {
|
if (extracted.isEmpty || !filter.match(extracted)) {
|
||||||
lastSlot++
|
lastSlot++
|
||||||
@ -245,7 +248,7 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
val leftOver = items.insertStack(ItemStackWrapper(extracted), true)
|
val leftOver = items.insertStack(ItemStackWrapper(extracted), true)
|
||||||
|
|
||||||
if (leftOver.count.toInt() != extracted.count) {
|
if (leftOver.count.toInt() != extracted.count) {
|
||||||
extracted = resolved.extractItem(lastSlot, extracted.count - leftOver.count.toInt(), false)
|
extracted = target.extractItem(lastSlot, extracted.count - leftOver.count.toInt(), false)
|
||||||
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, extracted.count, false)
|
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, extracted.count, false)
|
||||||
items.insertStack(ItemStackWrapper(extracted), false)
|
items.insertStack(ItemStackWrapper(extracted), false)
|
||||||
} else {
|
} else {
|
||||||
@ -335,43 +338,50 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
|||||||
|
|
||||||
nextTick--
|
nextTick--
|
||||||
|
|
||||||
if (nextTick <= 0 && target.isPresent && enoughEnergy) {
|
val target = target.firstOrNull()
|
||||||
|
|
||||||
|
if (nextTick <= 0 && target != null && enoughEnergy) {
|
||||||
val graph = cell.storageGraph ?: return
|
val graph = cell.storageGraph ?: return
|
||||||
val items = graph.getVirtualComponent(ITEM_STORAGE)
|
val items = graph.getVirtualComponent(ITEM_STORAGE)
|
||||||
|
|
||||||
val resolved = target.orThrow()
|
if (lastSlot >= target.slots) {
|
||||||
|
|
||||||
if (lastSlot >= resolved.slots) {
|
|
||||||
lastSlot = 0
|
lastSlot = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var hit = false
|
var hit = false
|
||||||
|
|
||||||
for (stack in exportStacks) {
|
for (stack in exportStacks) {
|
||||||
if (!resolved.isItemValid(lastSlot, stack.second.item)) {
|
if (!target.isItemValid(lastSlot, stack.second.item)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val exportAmountA = items.extractStack(stack.first, stack.second.count.coerceAtMost(
|
val exportAmountA = items.extractStack(
|
||||||
|
stack.first, stack.second.count.coerceAtMost(
|
||||||
MAX_MOVE_PER_OPERATION
|
MAX_MOVE_PER_OPERATION
|
||||||
), true).count
|
), true
|
||||||
|
).count
|
||||||
|
|
||||||
if (exportAmountA == BigInteger.ZERO) {
|
if (exportAmountA == BigInteger.ZERO) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var exportAmount = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmountA, true).toIntSafe()
|
var exportAmount =
|
||||||
|
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmountA, true).toIntSafe()
|
||||||
|
|
||||||
if (exportAmount == 0) {
|
if (exportAmount == 0) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
val leftover = resolved.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, true)
|
val leftover = target.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, true)
|
||||||
|
|
||||||
if (leftover.count != exportAmount) {
|
if (leftover.count != exportAmount) {
|
||||||
hit = true
|
hit = true
|
||||||
exportAmount = items.extractStack(stack.first, (exportAmount - leftover.count).toBigInteger(), false).count.toInt()
|
exportAmount = items.extractStack(
|
||||||
resolved.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, false)
|
stack.first,
|
||||||
|
(exportAmount - leftover.count).toBigInteger(),
|
||||||
|
false
|
||||||
|
).count.toInt()
|
||||||
|
target.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, false)
|
||||||
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmount, false)
|
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmount, false)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,84 @@ package ru.dbotthepony.mc.otm.core.util
|
|||||||
|
|
||||||
import net.minecraft.core.Direction
|
import net.minecraft.core.Direction
|
||||||
import net.minecraft.core.SectionPos
|
import net.minecraft.core.SectionPos
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
import net.minecraftforge.common.capabilities.Capability
|
import net.minecraftforge.common.capabilities.Capability
|
||||||
import net.minecraftforge.common.util.LazyOptional
|
import net.minecraftforge.common.util.LazyOptional
|
||||||
|
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
||||||
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
||||||
import ru.dbotthepony.mc.otm.core.math.plus
|
import ru.dbotthepony.mc.otm.core.math.plus
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.unaryMinus
|
||||||
import ru.dbotthepony.mc.otm.core.orNull
|
import ru.dbotthepony.mc.otm.core.orNull
|
||||||
import ru.dbotthepony.mc.otm.onceServer
|
import ru.dbotthepony.mc.otm.onceServer
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.EnumMap
|
import java.util.EnumMap
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
private inline val Direction.flag: Int get() = 1 shl (ordinal + 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class allows block entities to track [capability] per block side.
|
||||||
|
*
|
||||||
|
* Allowed sides are governed by [test] method, which is open for override.
|
||||||
|
*
|
||||||
|
* **IMPORTANT:** Once rules behind [test] are changed, you MUST call [validate] or [update] right away, or expect hard to debug bugs.
|
||||||
|
* If rule changes can't be tracked in practical way, then flip on [alwaysValidate], although it will come with performance
|
||||||
|
* penalty (this will cause [iterator] to invoke [validate] on each its invocation).
|
||||||
|
*
|
||||||
|
* Subclasses can also override [subscribe] and [unsubscribe] to more finely track changes in neighbours.
|
||||||
|
*
|
||||||
|
* **THIS CLASS IS COMPLETELY THREAD UNSAFE**, it is assumed you only ever invoke it on server thread,
|
||||||
|
* or, at very least, thread where [ServerLevel] resides.
|
||||||
|
*/
|
||||||
open class BESubscribeList<T>(
|
open class BESubscribeList<T>(
|
||||||
val block: BlockEntity,
|
val block: BlockEntity,
|
||||||
val capability: Capability<T>,
|
val capability: Capability<T>,
|
||||||
|
val alwaysValidate: Boolean = false
|
||||||
) : Predicate<Direction>, Iterable<T> {
|
) : Predicate<Direction>, Iterable<T> {
|
||||||
private val knownCaps = WeakHashSet<LazyOptional<T>>()
|
private val knownCaps = WeakHashSet<LazyOptional<T>>()
|
||||||
private val trackedCaps = EnumMap<Direction, WeakReference<LazyOptional<T>>>(Direction::class.java)
|
private val trackedCaps = EnumMap<Direction, WeakReference<LazyOptional<T>>>(Direction::class.java)
|
||||||
|
private var updateQueued = false
|
||||||
|
private var firstKey: Direction? = null
|
||||||
|
private var lastDirectionSet = 0
|
||||||
|
private var isWaitingOnChunk = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First valid capability, this is faster than invoking [iterator] and getting first value from it.
|
||||||
|
*/
|
||||||
|
val first: T? get() {
|
||||||
|
return firstLO.orNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First valid capability as [LazyOptional], this is faster than invoking [iterator] and getting first value from it.
|
||||||
|
*
|
||||||
|
* If required, calls [update]
|
||||||
|
*/
|
||||||
|
val firstLO: LazyOptional<T> get() {
|
||||||
|
if (updateQueued) {
|
||||||
|
update()
|
||||||
|
} else if (alwaysValidate) {
|
||||||
|
validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 .. 2) {
|
||||||
|
if (firstKey == null) {
|
||||||
|
return LazyOptional.empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
val get = trackedCaps[firstKey]?.get()
|
||||||
|
|
||||||
|
if (get == null || !get.isPresent) {
|
||||||
|
update()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return get
|
||||||
|
}
|
||||||
|
|
||||||
|
return LazyOptional.empty()
|
||||||
|
}
|
||||||
|
|
||||||
var valid = true
|
var valid = true
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -48,17 +109,13 @@ open class BESubscribeList<T>(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validate() {
|
|
||||||
val iterator = trackedCaps.iterator()
|
|
||||||
|
|
||||||
for ((k, v) in iterator) {
|
|
||||||
if (v.get() == null || !test(k)) {
|
|
||||||
unsubscribe(k, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun iterator(): Iterator<T> {
|
override fun iterator(): Iterator<T> {
|
||||||
|
if (updateQueued) {
|
||||||
|
update()
|
||||||
|
} else if (alwaysValidate) {
|
||||||
|
validate()
|
||||||
|
}
|
||||||
|
|
||||||
return object : Iterator<T> {
|
return object : Iterator<T> {
|
||||||
val iterator = trackedCaps.iterator()
|
val iterator = trackedCaps.iterator()
|
||||||
var value: T? = null
|
var value: T? = null
|
||||||
@ -68,9 +125,13 @@ open class BESubscribeList<T>(
|
|||||||
val (k, v) = iterator.next()
|
val (k, v) = iterator.next()
|
||||||
value = v.get()?.orNull()
|
value = v.get()?.orNull()
|
||||||
|
|
||||||
if (value == null || !test(k)) {
|
if (value == null) {
|
||||||
iterator.remove()
|
iterator.remove()
|
||||||
unsubscribe(k, null)
|
unsubscribe(k, null)
|
||||||
|
} else if (!test(k)) {
|
||||||
|
iterator.remove()
|
||||||
|
unsubscribe(k, null)
|
||||||
|
updateQueued = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,29 +154,99 @@ open class BESubscribeList<T>(
|
|||||||
return update(direction::equals)
|
return update(direction::equals)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var updateTick = 0
|
private fun calculateDirectionSet(): Int {
|
||||||
|
var set = 0
|
||||||
|
|
||||||
|
for (value in VALUES)
|
||||||
|
if (test(value))
|
||||||
|
set = set or value.flag
|
||||||
|
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates subscription list, searching for new capabilities or "removing" outdated ones
|
* Checks for one of these conditions to be true:
|
||||||
|
* * If [test] was determined to be changed during last time [iterator] was invoked AND tracked capability was filtered out due to [test] returning false
|
||||||
|
* * Performs a fast bitflag intersection test, by calling [test] on all directions and seeking difference between built set and previous set checked during last [update]
|
||||||
|
* * Tracked capabilities are checked for validity (if references are still valid)
|
||||||
*
|
*
|
||||||
* [predicate] predicate narrows sides-to-check list, not replaces list generated by this class' [test]
|
* Once *any* of conditions above end up true, [update] is called and nothing else down the list is checked further.
|
||||||
|
*/
|
||||||
|
fun validate() {
|
||||||
|
check(valid) { "Subscription list is marked as invalid" }
|
||||||
|
|
||||||
|
if (updateQueued) {
|
||||||
|
update()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val new = calculateDirectionSet()
|
||||||
|
|
||||||
|
if (new != lastDirectionSet) {
|
||||||
|
val intersect = new and lastDirectionSet
|
||||||
|
val removed = lastDirectionSet and (intersect.inv())
|
||||||
|
val added = new and (intersect.inv())
|
||||||
|
|
||||||
|
for (dir in VALUES) {
|
||||||
|
if (dir.flag and removed != 0) {
|
||||||
|
val value1 = trackedCaps.remove(dir)
|
||||||
|
val value = value1?.get()
|
||||||
|
|
||||||
|
if (value1 != null) {
|
||||||
|
unsubscribe(dir, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (added != 0) {
|
||||||
|
update { it.flag and added != 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
lastDirectionSet = new
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val iterator = trackedCaps.iterator()
|
||||||
|
var any = 0
|
||||||
|
|
||||||
|
for ((k, v) in iterator) {
|
||||||
|
if (v.get() == null) {
|
||||||
|
unsubscribe(k, null)
|
||||||
|
any = any or k.flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any != 0) {
|
||||||
|
update { it.flag and any != 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates subscription list, searching for new capabilities and "removing" outdated ones.
|
||||||
|
*
|
||||||
|
* [predicate] narrows sides-to-check list, not replaces the set generated by this class' [test]
|
||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun update(predicate: Predicate<Direction>? = null) {
|
fun update(predicate: Predicate<Direction>? = null) {
|
||||||
if (!valid)
|
if (!valid || block.isRemoved)
|
||||||
return
|
return
|
||||||
|
|
||||||
val sorse = block.level?.chunkSource ?: return
|
val level = block.level as? ServerLevel ?: return // don't run on client
|
||||||
|
val sorse = level.chunkSource
|
||||||
var waitChunkLoad = false
|
var waitChunkLoad = false
|
||||||
|
|
||||||
updateTick++
|
var stream = Direction.stream()
|
||||||
val updateTick = updateTick
|
|
||||||
var stream = Direction.stream().filter(this)
|
|
||||||
|
|
||||||
if (predicate != null) {
|
if (predicate != null) {
|
||||||
stream = stream.filter(predicate)
|
stream = stream.filter(predicate)
|
||||||
|
} else {
|
||||||
|
updateQueued = false
|
||||||
|
lastDirectionSet = calculateDirectionSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream = stream.filter(this)
|
||||||
|
firstKey = null
|
||||||
|
|
||||||
for (dir in stream) {
|
for (dir in stream) {
|
||||||
val pos = block.blockPos + dir
|
val pos = block.blockPos + dir
|
||||||
val getChunk = sorse.getChunkNow(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z))
|
val getChunk = sorse.getChunkNow(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z))
|
||||||
@ -123,7 +254,7 @@ open class BESubscribeList<T>(
|
|||||||
if (getChunk == null) {
|
if (getChunk == null) {
|
||||||
waitChunkLoad = true
|
waitChunkLoad = true
|
||||||
} else {
|
} else {
|
||||||
val cap = getChunk.getBlockEntity(pos)?.getCapability(capability)
|
val cap = getChunk.getBlockEntity(pos)?.getCapability(capability, -dir)
|
||||||
val knownCap = trackedCaps[dir]?.get()
|
val knownCap = trackedCaps[dir]?.get()
|
||||||
|
|
||||||
if (cap != null && cap.isPresent) {
|
if (cap != null && cap.isPresent) {
|
||||||
@ -147,6 +278,10 @@ open class BESubscribeList<T>(
|
|||||||
trackedCaps[dir] = WeakReference(cap)
|
trackedCaps[dir] = WeakReference(cap)
|
||||||
subscribe(dir, cap)
|
subscribe(dir, cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (firstKey == null) {
|
||||||
|
firstKey = dir
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
trackedCaps.remove(dir)
|
trackedCaps.remove(dir)
|
||||||
unsubscribe(dir, knownCap)
|
unsubscribe(dir, knownCap)
|
||||||
@ -154,8 +289,15 @@ open class BESubscribeList<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitChunkLoad) {
|
if (waitChunkLoad && SERVER_IS_LIVE) {
|
||||||
onceServer { if (updateTick == this.updateTick) update(predicate) }
|
onceServer { if (isWaitingOnChunk) update() }
|
||||||
|
isWaitingOnChunk = true
|
||||||
|
} else if (predicate == null) {
|
||||||
|
isWaitingOnChunk = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val VALUES = Direction.values()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user