Capability subscriptions embedded into MatteryBlockEntity.Side

This commit is contained in:
DBotThePony 2023-02-18 09:59:09 +07:00
parent aab9e09e30
commit d8648d7088
Signed by: DBot
GPG Key ID: DCC23B5715498507
25 changed files with 262 additions and 694 deletions

View File

@ -23,7 +23,9 @@ import net.minecraft.world.level.material.Material
import net.minecraft.world.level.material.MaterialColor
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.core.get
@ -97,6 +99,7 @@ abstract class MatteryBlock @JvmOverloads constructor(
if (this is EntityBlock && level.isClientSide)
return InteractionResult.SUCCESS
@Suppress("DEPRECATION")
return super.use(blockState, level, blockPos, ply, hand, blockHitResult)
}
@ -167,6 +170,7 @@ abstract class MatteryBlock @JvmOverloads constructor(
neighbourPos: BlockPos,
movedByPiston: Boolean
) {
@Suppress("DEPRECATION")
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
if (this is EntityBlock && !level.isClientSide) {
@ -174,6 +178,9 @@ abstract class MatteryBlock @JvmOverloads constructor(
if (tile is IRedstoneControlled)
tile.redstoneControl.redstoneSignal = level.getBestNeighborSignal(pos)
if (tile is MatteryBlockEntity && SERVER_IS_LIVE)
tile.neighborChanged(neighbour, neighbourPos, movedByPiston)
}
}

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.block.entity
import com.google.common.collect.ImmutableSet
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
@ -9,10 +10,8 @@ import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.SectionPos
import net.minecraft.nbt.ByteTag
import net.minecraft.core.Vec3i
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.DoubleTag
import net.minecraft.nbt.FloatTag
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
@ -20,6 +19,7 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity
@ -41,25 +41,30 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
import ru.dbotthepony.mc.otm.core.collect.SupplierList
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
import ru.dbotthepony.mc.otm.core.forValidRefs
import ru.dbotthepony.mc.otm.core.forValidRefsBreak
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.minus
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
import ru.dbotthepony.mc.otm.core.util.Savetables
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
import ru.dbotthepony.mc.otm.once
import ru.dbotthepony.mc.otm.oncePre
import ru.dbotthepony.mc.otm.onceServer
import java.lang.ref.WeakReference
import java.util.Collections
import java.util.EnumMap
import java.util.WeakHashMap
import java.util.function.Supplier
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KMutableProperty0
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
@ -155,6 +160,11 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
exposeGlobally(ForgeCapabilities.ITEM_HANDLER, value)
}
@Suppress("SuspiciousCallableReferenceInLambda")
protected fun <T> trackGlobally(capability: Capability<T>): List<LazyOptional<T>> {
return SupplierList(_sides.values.map { it.track(capability)::get })
}
enum class SideMode(val filter: FlowDirection, val automation: FlowDirection) {
DISABLED (FlowDirection.NONE, FlowDirection.NONE),
NONE (FlowDirection.BI_DIRECTIONAL, FlowDirection.NONE),
@ -197,6 +207,81 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
private val caps = Reference2ObjectArrayMap<Capability<*>, Cap<*>>()
private val modeStates = Object2ObjectArrayMap<String, ModeState>()
private val data = Object2ObjectArrayMap<ResourceLocation, INBTSerializable<Tag>>()
private val subscriptions = Reference2ObjectArrayMap<Capability<*>, SubRef<*>>()
private val knownLOs = WeakHashSet<LazyOptional<*>>()
private inner class SubRef<T>(var value: LazyOptional<T>) : Supplier<LazyOptional<T>> {
override fun get(): LazyOptional<T> {
return value
}
fun unset() {
value = LazyOptional.empty()
}
}
fun <T> track(capability: Capability<T>): Supplier<LazyOptional<T>> {
var subref = subscriptions[capability] as Supplier<LazyOptional<T>>?
if (subref == null) {
subref = SubRef(LazyOptional.empty<Any?>()) as SubRef<T>
subscriptions[capability] = subref
level?.once { updateTracked(capability) }
}
return subref
}
fun updateTracked() {
for (key in subscriptions.keys) {
// Concurrent Modification safety:
// we do not add nor remove keys from map, we only update values
updateTracked(key)
}
}
private fun updateTracked(capability: Capability<*>) {
if (isRemoved) return
val dir = blockRotation.side2Dir(side)
val chunk = level
?.chunkSource
?.getChunkNow(SectionPos.blockToSectionCoord(blockPos.x), SectionPos.blockToSectionCoord(blockPos.z))
if (chunk == null) {
subscriptions[capability]!!.unset()
level?.once { updateTracked(capability) }
return
}
val entity = chunk.getBlockEntity(blockPos + dir.normal)
if (entity == null) {
subscriptions[capability]!!.unset()
return
}
val new = entity.getCapability(capability, dir.opposite)
if (!new.isPresent) {
subscriptions[capability]!!.unset()
return
}
val subref = subscriptions[capability] as SubRef<Any?>
if (subref.value !== new) {
if (knownLOs.add(new)) {
val ref = WeakReference(this)
new.addListener {
ref.get()?.updateTracked(capability)
}
}
subref.value = new as LazyOptional<Any?>
}
}
val isEmpty get() = caps.isEmpty() && modeStates.isEmpty()
@ -453,6 +538,30 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
savetables.deserializeNBT(nbt)
}
@Suppress("OVERRIDE_DEPRECATION")
override fun setBlockState(pBlockState: BlockState) {
val old = blockRotation
@Suppress("DEPRECATION")
super.setBlockState(pBlockState)
val new = blockRotation
if (old != new) {
for (side in _sides.values) {
side.updateTracked()
side.invalidate()
}
}
for (side in _sides.values) {
side.revive()
}
}
fun neighborChanged(neighbour: Block, neighbourPos: BlockPos, movedByPiston: Boolean) {
val dir = vec2Dir[vecKey(neighbourPos - blockPos)] ?: return
_sides[blockRotation.dir2Side(dir)]!!.updateTracked()
}
protected fun tickOnce(func: Runnable) {
level?.oncePre { if (!isRemoved) func.run() }
}
@ -511,13 +620,31 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
synchronizer.defaultEndpoint.markUnused()
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
val old = this.level
super.setLevel(level)
unsubscribe()
_subCache = null
if (!p_155231_.isClientSide) {
if (!level.isClientSide) {
subscribe()
if (old != null) {
for (side in _sides.values) {
side.updateTracked()
side.invalidate()
}
for (side in _sides.values) {
side.revive()
}
} else {
level.once {
for (side in _sides.values) {
side.updateTracked()
}
}
}
}
}
@ -751,6 +878,24 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
private val playerMap = WeakHashMap<ServerLevel, Long2ObjectOpenHashMap<ChunkSubscribers>>()
private val tickingMap = WeakHashMap<ServerLevel, ArrayList<WeakReference<ChunkSubscribers>>>()
private val vec2Dir = Int2ObjectOpenHashMap<Direction>()
private fun vecKey(value: Vec3i): Int {
if (value.x !in -1 .. 1) return -1
if (value.y !in -1 .. 1) return -1
if (value.z !in -1 .. 1) return -1
val x = if (value.x < 0) 2 else value.x
val y = if (value.y < 0) 2 else value.y
val z = if (value.z < 0) 2 else value.z
return x or (y shl 4) or (z shl 8)
}
init {
for (dir in Direction.values()) {
vec2Dir[vecKey(dir.normal)] = dir
}
}
fun onLevelUnload(event: LevelEvent.Unload) {
val level = event.level as? ServerLevel ?: return
playerMap.remove(level)

View File

@ -85,8 +85,8 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery
private var sleepTicks = 4
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
sleepTicks = 4
}

View File

@ -133,10 +133,10 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
return matter
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
MatterNetworkGraph.discoverFull(this, matterNode)
}

View File

@ -1,20 +1,13 @@
package ru.dbotthepony.mc.otm.block.entity.matter
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.block.IDroppableContainer
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity
@ -30,8 +23,6 @@ import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.MATTER_CAPACITOR_BANK, p_155229_, p_155230_), IMatterGraphNode, IMatterStorage, IDroppableContainer {
var gaugeLevel by synchronizer.float()
@ -161,10 +152,10 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
matterNode.destroy(::MatterNetworkGraph)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
MatterNetworkGraph.discoverFull(this, matterNode)
}

View File

@ -204,10 +204,10 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
matterNode.destroy(::MatterNetworkGraph)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
MatterNetworkGraph.discoverFull(this, matterNode)
}

View File

@ -1,13 +1,11 @@
package ru.dbotthepony.mc.otm.block.entity.matter
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import java.util.HashMap
import java.util.UUID
@ -17,7 +15,6 @@ import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.matter.*
@ -54,10 +51,10 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
exposeGlobally(MatteryCapability.TASK, this)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
MatterNetworkGraph.discoverFull(this, matterNode)
}

View File

@ -106,10 +106,10 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
matterNode.destroy(::MatterNetworkGraph)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
MatterNetworkGraph.discoverFull(this, matterNode)
}

View File

@ -19,7 +19,6 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.ContainerHandler
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
@ -164,10 +163,10 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
matterNode.destroy(::MatterNetworkGraph)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
MatterNetworkGraph.discoverFull(this, matterNode)
}

View File

@ -169,10 +169,10 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
return null to IdleReason.ITEM
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel) {
if (level is ServerLevel) {
MatterNetworkGraph.discoverFull(this, matterNode)
}
}

View File

@ -8,20 +8,14 @@ import ru.dbotthepony.mc.otm.container.MatteryContainer
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.block.matter.PatternStorageBlock
import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.util.LazyOptional
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import ru.dbotthepony.mc.otm.menu.matter.PatternStorageMenu
import net.minecraft.MethodsReturnNonnullByDefault
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.Container
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import ru.dbotthepony.mc.otm.block.IDroppableContainer
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.matter.*
@ -29,9 +23,7 @@ import ru.dbotthepony.mc.otm.core.collect.iterator
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.ArrayList
import java.util.stream.Stream
@ -90,10 +82,10 @@ class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
private val itemHandler = container.handler { slot: Int, stack: ItemStack -> stack.getCapability(MatteryCapability.PATTERN).isPresent }
override val droppableContainer by ::container
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
MatterNetworkGraph.discoverFull(this, matterNode)
}

View File

@ -1,9 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity.storage
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
@ -11,9 +8,6 @@ import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
import ru.dbotthepony.mc.otm.capability.MatteryCapability
@ -21,11 +15,8 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MBlocks
import ru.dbotthepony.mc.otm.storage.*
class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
@ -59,10 +50,10 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
exposeGlobally(MatteryCapability.STORAGE_NODE, cell)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
StorageNetworkGraph.discoverFull(this, cell.storageNode)
}

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.block.entity.storage
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
@ -20,12 +19,9 @@ import net.minecraft.world.item.crafting.RecipeType
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.ForgeHooks
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.network.NetworkEvent
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
@ -42,7 +38,6 @@ import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.nbt.getEnum
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
import ru.dbotthepony.mc.otm.registry.MBlocks
import ru.dbotthepony.mc.otm.storage.*
import java.math.BigInteger
import java.util.*
@ -501,10 +496,10 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
return ItemMonitorMenu(containerID, inventory, this)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel)
if (level is ServerLevel)
StorageNetworkGraph.discoverFull(this, cell.storageNode)
}

View File

@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.player.Inventory
@ -14,7 +13,6 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.IItemHandler
@ -25,7 +23,6 @@ import ru.dbotthepony.mc.otm.block.entity.storage.AbstractStorageImportExport.Co
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom
@ -34,8 +31,6 @@ import ru.dbotthepony.mc.otm.core.math.getCapability
import ru.dbotthepony.mc.otm.core.math.isPositive
import ru.dbotthepony.mc.otm.core.math.isZero
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.GraphNodeListener
import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
@ -115,10 +110,10 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
savetable(::filter, FILTER_KEY)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel) {
if (level is ServerLevel) {
StorageNetworkGraph.discoverFull(this, cell.storageNode)
}

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.block.entity.storage
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.player.Inventory
@ -15,28 +14,22 @@ import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
import ru.dbotthepony.mc.otm.block.CableBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.rotationTwo
import ru.dbotthepony.mc.otm.core.math.toIntSafe
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 ru.dbotthepony.mc.otm.core.util.BESubscribeList
import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.GraphNodeListener
import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
@ -45,11 +38,15 @@ import ru.dbotthepony.mc.otm.menu.storage.StorageExporterMenu
import ru.dbotthepony.mc.otm.menu.storage.StorageImporterMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.storage.*
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import ru.dbotthepony.mc.otm.storage.StorageStackType
import ru.dbotthepony.mc.otm.storage.addStack
import java.math.BigInteger
import java.util.*
import java.util.stream.Stream
import kotlin.collections.HashSet
abstract class AbstractStorageImportExport<T>(
blockType: BlockEntityType<*>,
@ -85,42 +82,23 @@ abstract class AbstractStorageImportExport<T>(
exposeAllSidesExcept(RelativeSide.FRONT, MatteryCapability.STORAGE_NODE, cell)
}
override fun invalidateCaps() {
super.invalidateCaps()
target.invalidate()
}
override fun reviveCaps() {
super.reviveCaps()
target.revive()
}
override fun setRemoved() {
super.setRemoved()
cell.destroy(level)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel) {
if (level is ServerLevel) {
StorageNetworkGraph.discoverFull(this, cell.storageNode)
tickOnceServer(this::checkSurroundings)
}
}
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(BlockRotationFreedom.TWO.property).front
}
}
}
fun checkSurroundings() {
target.update()
front.track(targetCapability)
}
abstract val filter: ItemFilter
@ -206,7 +184,7 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState)
nextTick--
val target = target.firstOrNull()
val target = target.get().orNull()
if (nextTick <= 0 && target != null && enoughEnergy) {
val graph = cell.storageGraph ?: return
@ -318,7 +296,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
nextTick--
val target = target.firstOrNull()
val target = target.get().orNull()
if (nextTick <= 0 && target != null && enoughEnergy) {
val graph = cell.storageGraph ?: return

View File

@ -1,20 +1,12 @@
package ru.dbotthepony.mc.otm.block.entity.storage
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
@ -25,9 +17,6 @@ import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
import ru.dbotthepony.mc.otm.menu.storage.StoragePowerSupplierMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.set
class StoragePowerSupplierBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.STORAGE_POWER_SUPPLIER, blockPos, blockState) {
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
@ -51,10 +40,10 @@ class StoragePowerSupplierBlockEntity(blockPos: BlockPos, blockState: BlockState
savetables.decimal(::powerPassed, POWER_PASSED_KEY)
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
override fun setLevel(level: Level) {
super.setLevel(level)
if (p_155231_ is ServerLevel) {
if (level is ServerLevel) {
StorageNetworkGraph.discoverFull(this, cell.storageNode)
}
}

View File

@ -1,26 +1,28 @@
package ru.dbotthepony.mc.otm.block.entity.tech
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.energy.IEnergyStorage
import ru.dbotthepony.mc.otm.block.IDroppableContainer
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.capability.energyStoredMattery
import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.ImmutableList
import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.facingOne
import ru.dbotthepony.mc.otm.core.util.BESubscribeList
import ru.dbotthepony.mc.otm.menu.tech.BatteryBankMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
@ -202,28 +204,17 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
}
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
checkSurroundings()
}
private val consumers = object : BESubscribeList<IEnergyStorage>(this@BatteryBankBlockEntity, ForgeCapabilities.ENERGY) {
override fun test(t: Direction): Boolean {
return blockState.facingOne == t
}
}
fun checkSurroundings() = consumers.update(blockState.facingOne::equals)
private val consumers by front.track(ForgeCapabilities.ENERGY)
fun tick() {
if (redstoneControl.isBlockedByRedstone)
return
for (it in consumers) {
consumers.ifPresentK {
val (_, maxThroughput) = energy.getDistribution(false)
if (maxThroughput.isZero)
continue
return@ifPresentK
val diff = it.receiveEnergy(maxThroughput, true)

View File

@ -1,12 +1,10 @@
package ru.dbotthepony.mc.otm.block.entity.tech
import net.minecraft.core.BlockPos
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.ForgeConfigSpec
@ -18,7 +16,6 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.core.*
@ -29,9 +26,6 @@ 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.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.BESubscribeList
class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.CHEMICAL_GENERATOR, pos, state), IDroppableContainer {
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
@ -66,41 +60,18 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
savetable(::container, INVENTORY_KEY)
exposeEnergyGlobally(energy)
exposeItemsGlobally(itemHandler)
savetables.int(::workTicks, WORK_TICKS_KEY)
savetables.int(::workTicksTotal, WORK_TICKS_TOTAL_KEY)
}
private val consumers = BESubscribeList(this, ForgeCapabilities.ENERGY)
fun checkSurroundings() = consumers.update()
private val consumers = trackGlobally(ForgeCapabilities.ENERGY)
override fun setChangedLight() {
super.setChangedLight()
checkFuelSlot = true
}
override fun setLevel(p_155231_: Level) {
super.setLevel(p_155231_)
tickOnceServer(consumers::update)
}
override fun saveAdditional(nbt: CompoundTag) {
super.saveAdditional(nbt)
nbt[WORK_TICKS_KEY] = workTicks
nbt[WORK_TICKS_TOTAL_KEY] = workTicksTotal
}
override fun load(nbt: CompoundTag) {
super.load(nbt)
workTicks = nbt.getInt(WORK_TICKS_KEY)
workTicksTotal = nbt.getInt(WORK_TICKS_TOTAL_KEY)
}
override fun setBlockState(p_155251_: BlockState) {
super.setBlockState(p_155251_)
tickOnceServer(consumers::update)
}
var workTicks = 0
private set
@ -171,7 +142,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
}
for (consumer in consumers) {
workWithPower(consumer)
consumer.ifPresentK(::workWithPower)
}
}

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity.tech
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.IntTag
import net.minecraft.nbt.ListTag
@ -9,26 +8,21 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
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.block.tech.EnergyCounterBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.math.unaryMinus
import ru.dbotthepony.mc.otm.core.nbt.getByteArrayList
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.BESubscribeList
import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import java.util.*
@ -36,6 +30,10 @@ import java.util.*
class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) {
var passed by synchronizer.fraction()
override val blockRotation: BlockRotation get() {
return BlockRotation.of(blockState[EnergyCounterBlock.INPUT_DIRECTION])
}
private val history = Array(10 * 20) { Decimal.ZERO }
internal var historyTick = 0
@ -124,22 +122,8 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
private val energyInput = EnergyCounterCap(true)
private val energyOutput = EnergyCounterCap(false)
private val inputCapability = object : BESubscribeList<IEnergyStorage>(this@EnergyCounterBlockEntity, ForgeCapabilities.ENERGY) {
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) {
super.setLevel(p_155231_)
tickOnceServer(this::checkSurroundings)
}
private val inputCapability by front.track(ForgeCapabilities.ENERGY)
private val outputCapability by back.track(ForgeCapabilities.ENERGY)
private inner class EnergyCounterCap(isInput: Boolean) : IMatteryEnergyStorage {
override val energyFlow = FlowDirection.input(isInput)
@ -148,7 +132,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
if (redstoneControl.isBlockedByRedstone)
return Decimal.ZERO
val it = inputCapability.first
val it = inputCapability.orNull()
if (it != null) {
val diff: Decimal
@ -176,7 +160,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
if (redstoneControl.isBlockedByRedstone)
return Decimal.ZERO
val it = outputCapability.first
val it = outputCapability.orNull()
if (it != null) {
val diff: Decimal
@ -206,7 +190,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
override var batteryLevel: Decimal
get() {
if (energyFlow.input) {
val it = outputCapability.first
val it = outputCapability.orNull()
if (it != null) {
if (it is IMatteryEnergyStorage) {
@ -216,7 +200,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
return Decimal(it.energyStored)
}
} else {
val it = inputCapability.first
val it = inputCapability.orNull()
if (it != null) {
if (it is IMatteryEnergyStorage) {
@ -236,7 +220,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
override val maxBatteryLevel: Decimal
get() {
if (energyFlow.input) {
val it = outputCapability.first
val it = outputCapability.orNull()
if (it != null) {
if (it is IMatteryEnergyStorage) {
@ -246,7 +230,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
return Decimal(it.maxEnergyStored)
}
} else {
val it = inputCapability.first
val it = inputCapability.orNull()
if (it != null) {
if (it is IMatteryEnergyStorage) {
@ -263,7 +247,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
override val missingPower: Decimal
get() {
if (energyFlow.input) {
val it = outputCapability.first
val it = outputCapability.orNull()
if (it != null) {
if (it is IMatteryEnergyStorage) {
@ -273,7 +257,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
return Decimal((it.maxEnergyStored - it.energyStored).coerceAtLeast(0))
}
} else {
val it = inputCapability.first
val it = inputCapability.orNull()
if (it != null) {
if (it is IMatteryEnergyStorage) {
@ -288,86 +272,19 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
}
}
private var resolverInput = LazyOptional.of<IMatteryEnergyStorage> { energyInput }
private var resolverOutput = LazyOptional.of<IMatteryEnergyStorage> { energyOutput }
init {
front.Cap(ForgeCapabilities.ENERGY, energyInput)
front.Cap(MatteryCapability.ENERGY, energyInput)
private var resolverInputMekanism = if (isMekanismLoaded) LazyOptional.of { Mattery2MekanismEnergyWrapper(energyInput) } else null
private var resolverOutputMekanism = if (isMekanismLoaded) LazyOptional.of { Mattery2MekanismEnergyWrapper(energyOutput) } else null
private var valid = true
override fun invalidateCaps() {
super.invalidateCaps()
valid = false
resolverInput.invalidate()
resolverInputMekanism?.invalidate()
resolverOutput.invalidate()
resolverOutputMekanism?.invalidate()
}
override fun reviveCaps() {
super.reviveCaps()
valid = true
resolverInput = LazyOptional.of { energyInput }
resolverOutput = LazyOptional.of { energyOutput }
back.Cap(ForgeCapabilities.ENERGY, energyOutput)
back.Cap(MatteryCapability.ENERGY, energyOutput)
if (isMekanismLoaded) {
resolverInputMekanism = LazyOptional.of { Mattery2MekanismEnergyWrapper(energyInput) }
resolverOutputMekanism = LazyOptional.of { Mattery2MekanismEnergyWrapper(energyOutput) }
front.Cap(MatteryCapability.MEKANISM_ENERGY, Mattery2MekanismEnergyWrapper(energyInput))
back.Cap(MatteryCapability.MEKANISM_ENERGY, Mattery2MekanismEnergyWrapper(energyOutput))
}
}
@Suppress("deprecation", "OVERRIDE_DEPRECATION")
override fun setBlockState(new: BlockState) {
val old = blockState
super.setBlockState(new)
if (new !== old && new.getValue(EnergyCounterBlock.INPUT_DIRECTION) != old.getValue(EnergyCounterBlock.INPUT_DIRECTION)) {
resolverInput.invalidate()
resolverInputMekanism?.invalidate()
resolverOutput.invalidate()
resolverOutputMekanism?.invalidate()
resolverInput = LazyOptional.of { energyInput }
resolverOutput = LazyOptional.of { energyOutput }
if (isMekanismLoaded) {
resolverInputMekanism = LazyOptional.of { Mattery2MekanismEnergyWrapper(energyInput) }
resolverOutputMekanism = LazyOptional.of { Mattery2MekanismEnergyWrapper(energyOutput) }
}
checkSurroundings()
}
}
fun checkSurroundings() {
inputCapability.update((blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION))::equals)
outputCapability.update((-blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION))::equals)
}
override fun <T : Any> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (side == null || isRemoved)
return super.getCapability(cap, side)
if (valid) {
if (side == blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION)) {
if (cap == MatteryCapability.ENERGY || cap == ForgeCapabilities.ENERGY) {
return resolverInput.cast()
} else if (cap == MatteryCapability.MEKANISM_ENERGY) {
return resolverInputMekanism!!.cast()
}
} else if (side == blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION).opposite) {
if (cap == MatteryCapability.ENERGY || cap == ForgeCapabilities.ENERGY) {
return resolverOutput.cast()
} else if (cap == MatteryCapability.MEKANISM_ENERGY) {
return resolverOutputMekanism!!.cast()
}
}
}
return super.getCapability(cap, side)
}
fun tick() {
lastTick = history[historyTick]
historyTick = (historyTick + 1) % history.size

View File

@ -114,28 +114,6 @@ class StorageImporterBlock : RotatableMatteryBlock(), EntityBlock {
): VoxelShape {
return shapes[p_60555_]!!
}
@Suppress("OVERRIDE_DEPRECATION")
override fun neighborChanged(
state: BlockState,
level: Level,
pos: BlockPos,
neighbour: Block,
neighbourPos: BlockPos,
movedByPiston: Boolean
) {
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
if (!level.isClientSide) {
val tile = level.getBlockEntity(pos)
if (tile is StorageImporterBlockEntity) {
level.oncePre {
tile.checkSurroundings()
}
}
}
}
}
class StorageExporterBlock : RotatableMatteryBlock(), EntityBlock {
@ -218,26 +196,4 @@ class StorageExporterBlock : RotatableMatteryBlock(), EntityBlock {
): VoxelShape {
return shapes[p_60555_]!!
}
@Suppress("OVERRIDE_DEPRECATION")
override fun neighborChanged(
state: BlockState,
level: Level,
pos: BlockPos,
neighbour: Block,
neighbourPos: BlockPos,
movedByPiston: Boolean
) {
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
if (!level.isClientSide) {
val tile = level.getBlockEntity(pos)
if (tile is StorageExporterBlockEntity) {
level.oncePre {
tile.checkSurroundings()
}
}
}
}
}

View File

@ -58,17 +58,4 @@ class BatteryBankBlock : RotatableMatteryBlock(), EntityBlock {
override fun faceToPlayer(context: BlockPlaceContext): Boolean {
return false
}
override fun neighborChanged(
state: BlockState,
level: Level,
pos: BlockPos,
neighbour: Block,
neighbourPos: BlockPos,
movedByPiston: Boolean
) {
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
val blockEntity = level.getBlockEntity(pos) as? BatteryBankBlockEntity ?: return
level.oncePre { blockEntity.checkSurroundings() }
}
}

View File

@ -75,27 +75,6 @@ class ChemicalGeneratorBlock : RotatableMatteryBlock(), EntityBlock {
}
}
override fun neighborChanged(
state: BlockState,
level: Level,
pos: BlockPos,
neighbour: Block,
neighbourPos: BlockPos,
movedByPiston: Boolean
) {
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
if (!level.isClientSide) {
val tile = level.getBlockEntity(pos)
if (tile is ChemicalGeneratorBlockEntity) {
level.oncePre {
tile.checkSurroundings()
}
}
}
}
private val shapes = getShapeForEachState(rotationProperty) { BlockShapes.CHEMICAL_GENERATOR.rotateFromNorth(it[rotationProperty]).computeShape() }
@Suppress("override_deprecation")

View File

@ -63,23 +63,6 @@ class EnergyCounterBlock : MatteryBlock(), EntityBlock {
p_49915_.add(INPUT_DIRECTION, IF_DIRECTION)
}
override fun neighborChanged(
state: BlockState,
level: Level,
pos: BlockPos,
neighbour: Block,
neighbourPos: BlockPos,
movedByPiston: Boolean
) {
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
if (SERVER_IS_LIVE) {
level.once {
(level.getBlockEntity(pos) as? EnergyCounterBlockEntity)?.checkSurroundings()
}
}
}
private val SHAPES = HashMap<BlockState, VoxelShape>()
init {

View File

@ -66,6 +66,14 @@ interface GetterSetter<V> : Supplier<V>, Consumer<V>, ReadWriteProperty<Any?, V>
}
}
operator fun <T> Supplier<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
return get()
}
operator fun <T> Consumer<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
accept(value)
}
fun <V> KMutableProperty0<V>.asGetterSetter(watch: ((old: V, new: V) -> Unit)? = null): GetterSetter<V> {
return GetterSetter.of(this).let {
if (watch != null) {

View File

@ -1,303 +0,0 @@
package ru.dbotthepony.mc.otm.core.util
import net.minecraft.core.Direction
import net.minecraft.core.SectionPos
import net.minecraft.server.level.ServerLevel
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.SERVER_IS_LIVE
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
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.onceServer
import java.lang.ref.WeakReference
import java.util.EnumMap
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>(
val block: BlockEntity,
val capability: Capability<T>,
val alwaysValidate: Boolean = false
) : Predicate<Direction>, Iterable<T> {
private val knownCaps = WeakHashSet<LazyOptional<T>>()
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
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
}
final override fun iterator(): Iterator<T> {
if (updateQueued) {
update()
} else if (alwaysValidate) {
validate()
}
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)
} else if (!test(k)) {
iterator.remove()
unsubscribe(k, null)
updateQueued = true
}
}
}
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(direction: Direction) {
return update(direction::equals)
}
private fun calculateDirectionSet(): Int {
var set = 0
for (value in VALUES)
if (test(value))
set = set or value.flag
return set
}
/**
* 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)
*
* 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
fun update(predicate: Predicate<Direction>? = null) {
if (!valid || block.isRemoved)
return
val level = block.level as? ServerLevel ?: return // don't run on client
val sorse = level.chunkSource
var waitChunkLoad = false
var stream = Direction.stream()
if (predicate != null) {
stream = stream.filter(predicate)
} else {
updateQueued = false
lastDirectionSet = calculateDirectionSet()
}
stream = stream.filter(this)
firstKey = null
for (dir in stream) {
val pos = block.blockPos + dir
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, -dir)
val knownCap = trackedCaps[dir]?.get()
if (cap != null && cap.isPresent) {
if (knownCap !== cap) {
if (knownCaps.add(cap)) {
val ref = WeakReference(this)
cap.addListener {
val self = ref.get()
if (self != null) {
val current = self.trackedCaps[dir]
if (current?.get() === it) {
self.update()
}
}
}
}
trackedCaps[dir] = WeakReference(cap)
subscribe(dir, cap)
}
if (firstKey == null) {
firstKey = dir
}
} else {
trackedCaps.remove(dir)
unsubscribe(dir, knownCap)
}
}
}
if (waitChunkLoad && SERVER_IS_LIVE) {
onceServer { if (isWaitingOnChunk) update() }
isWaitingOnChunk = true
} else if (predicate == null) {
isWaitingOnChunk = false
}
}
companion object {
private val VALUES = Direction.values()
}
}