diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt index fdbee4c49..0722ba944 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt @@ -360,6 +360,9 @@ object DataGen { tile(MBlocks.BATTERY_BANK, TileNbtCopy("container")) tile(MBlocks.DRIVE_VIEWER, TileNbtCopy("energy"), TileNbtCopy("container"), TileNbtCopy("battery_container")) + tile(MBlocks.STORAGE_BUS, TileNbtCopy("energy"), TileNbtCopy("battery_container")) + tile(MBlocks.STORAGE_POWER_SUPPLIER, TileNbtCopy("energy"), TileNbtCopy("battery_container"), TileNbtCopy("power_supplied")) + tile(MBlocks.MATTER_DECOMPOSER, TileNbtCopy("container"), TileNbtCopy("matter"), *workerTags) tile(MBlocks.MATTER_REPLICATOR, TileNbtCopy("container"), TileNbtCopy("matter"), *workerTags) tile(MBlocks.MATTER_RECYCLER, TileNbtCopy("container"), TileNbtCopy("matter"), *workerTags) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/StoragePowerSupplierBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/StoragePowerSupplierBlock.kt new file mode 100644 index 000000000..50595834d --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/StoragePowerSupplierBlock.kt @@ -0,0 +1,28 @@ +package ru.dbotthepony.mc.otm.block + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityTicker +import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.block.entity.StoragePowerSupplierBlockEntity +import ru.dbotthepony.mc.otm.registry.MBlockEntities + +class StoragePowerSupplierBlock : RotatableMatteryBlock(), EntityBlock { + override fun newBlockEntity(p_153215_: BlockPos, p_153216_: BlockState): BlockEntity { + return StoragePowerSupplierBlockEntity(p_153215_, p_153216_) + } + + override fun getTicker( + p_153212_: Level, + p_153213_: BlockState, + p_153214_: BlockEntityType + ): BlockEntityTicker? { + if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.STORAGE_POWER_SUPPLIER) + return null + + return BlockEntityTicker { _, _, _, tile -> if (tile is StoragePowerSupplierBlockEntity) tile.tick() } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/DriveRackBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/DriveRackBlockEntity.kt index f98f10760..e4c363718 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/DriveRackBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/DriveRackBlockEntity.kt @@ -49,7 +49,7 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : } } - val cell = BasicStorageGraphNode() + val cell = BasicStorageGraphNode(energy) override fun load(nbt: CompoundTag) { super.load(nbt) @@ -73,6 +73,7 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : fun tick() { batteryChargeLoop() + cell.tickEnergyDemanding() } override val defaultDisplayName: Component diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/ItemMonitorBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/ItemMonitorBlockEntity.kt index fe8f29334..fa5120509 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/ItemMonitorBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/ItemMonitorBlockEntity.kt @@ -22,11 +22,12 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, p_155229_, p_155230_) { - val cell = BasicStorageGraphNode() override val energy = WorkerEnergyStorage(this) + val cell = BasicStorageGraphNode(energy) fun tick() { batteryChargeLoop() + cell.tickEnergyDemanding() } override val defaultDisplayName: Component diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt index b7d741407..a12a28c5b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt @@ -1,7 +1,6 @@ package ru.dbotthepony.mc.otm.block.entity import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap -import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.network.chat.Component @@ -50,248 +49,6 @@ private fun Long.clamp(): Int { return this.toInt() } -private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent { - override val storageType: StorageStackType - get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() - - private val listeners = ArrayList>() - - override fun addListener(listener: IStorageListener): Boolean { - if (!listeners.contains(listener)) { - listeners.add(listener) - return true - } - - return false - } - - override fun removeListener(listener: IStorageListener): Boolean { - return listeners.remove(listener) - } - - private var scanned = arrayOfNulls(0) - private var scannedMap = arrayOfNulls(0) - private val tuples = HashMap() - private val index = HashMap() - - private fun removeTracked(slot: Int) { - scanned[slot] = null - val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" } - this.scannedMap[slot] = null - - val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot") - scannedMap.children.remove(slot) - val count = scannedMap.stack.count - scannedMap.stack.count -= item.stack.count - - if (scannedMap.stack.count.isPositive) { - for (listener in listeners) { - listener.changeStack(scannedMap, count) - } - } else { - for (listener in listeners) { - listener.removeStack(scannedMap) - } - - index.remove(scannedMap.id) - - val key = scannedMap.stack.key() - tuples.remove(key) ?: throw IllegalStateException("Item tuple is not present for slot $slot at ${scannedMap.stack}") - } - } - - private fun diffTracked(slot: Int, diff: Int) { - val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" } - val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot") - - val oldCount = scannedMap.stack.count - item.stack.count += diff - scannedMap.stack.count += diff - - for (listener in listeners) { - listener.changeStack(scannedMap.stack, scannedMap.id, oldCount) - } - } - - private fun addTracked(slot: Int, stack: ItemStack) { - check(scannedMap[slot] == null) { "Already tracking slot $slot" } - - val storageStack = ItemStackWrapper(stack) - val key = storageStack.key() - var tuple: TrackedTuple? = tuples[key] - val added = tuple == null - var oldCount = ImpreciseFraction.ZERO - - if (added) { - tuple = TrackedTuple(storageStack, UUID.randomUUID()) - index[tuple.id] = tuple - tuples[key] = tuple - } else { - oldCount = tuple!!.stack.count - tuple.stack.count += stack.count - } - - tuple.children[slot] = SlotTuple(slot, stack.copy()) - scanned[slot] = tuple.children[slot].stack - scannedMap[slot] = tuple - - if (added) { - for (listener in listeners) { - listener.addStack(tuple.stack, tuple.id, this) - } - } else { - for (listener in listeners) { - listener.changeStack(tuple.stack, tuple.id, oldCount) - } - } - } - - private fun sizeScan() { - if (scanned.size != parent.slots) { - val old = scanned - val oldMap = scannedMap - - if (scanned.size < parent.slots) { - // grow - scanned = arrayOfNulls(parent.slots) - scannedMap = arrayOfNulls(parent.slots) - - for ((i, item) in old.withIndex()) { - scanned[i] = item - } - - for ((i, item) in oldMap.withIndex()) { - scannedMap[i] = item - } - } else { - // shrink - for (i in parent.slots until scanned.size) { - removeTracked(i) - } - - scanned = arrayOfNulls(parent.slots) - scannedMap = arrayOfNulls(parent.slots) - - for (i in 0 until parent.slots) { - scanned[i] = old[i] - scannedMap[i] = oldMap[i] - } - } - } - } - - fun scan(slot: Int) { - val current = parent[slot].let { if (it.isEmpty) null else it } - val last = scanned[slot] - - if (current == null && last != null) { - removeTracked(slot) - } else if (current != null && last == null) { - addTracked(slot, current) - } else if (current != null && last != null) { - if (!ItemStack.isSameItemSameTags(current, last)) { - removeTracked(slot) - addTracked(slot, current) - } else if (current.count != last.count) { - diffTracked(slot, current.count - last.count) - } - } - } - - fun scan() { - sizeScan() - - for (slot in 0 until parent.slots) { - scan(slot) - } - } - - override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper { - var leftover = stack.copy() - - for (slot in 0 until parent.slots) { - val oldCount = leftover.count - leftover = ItemStackWrapper(parent.insertItem(slot, leftover.stack, simulate)) - - if (oldCount != leftover.count && !simulate) { - scan(slot) - } - - if (leftover.isEmpty) { - break - } - } - - return leftover - } - - override fun getStack(id: UUID): ItemStackWrapper { - return index[id]?.stack ?: ItemStackWrapper.EMPTY - } - - override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): ItemStackWrapper { - @Suppress("NAME_SHADOWING") - val amount = amount.floor() - - if (!amount.isPositive) - return ItemStackWrapper.EMPTY - - val intAmount = amount.toLong() - val tuple = index[id] ?: return ItemStackWrapper.EMPTY - var totalExtracted = 0L - - val iter = tuple.children.values.iterator() - val listCopy = Array(tuple.children.values.size) { - iter.next() - } - - val copy = tuple.stack.copy() - - for (stack in listCopy) { - val extracted = parent.extractItem(stack.slot, stack.stack.count.coerceAtMost((intAmount - totalExtracted).clamp()), true) - - if (extracted.isEmpty) { - // dummy condition - continue - } else if (tuple.stack.sameItem(extracted)) { - if (!simulate) { - parent.extractItem(stack.slot, stack.stack.count.coerceAtMost((intAmount - totalExtracted).clamp()), false) - } - - totalExtracted += extracted.count - - if (extracted.count != 0 && !simulate) { - scan(stack.slot) - } - - if (totalExtracted >= intAmount) { - break - } - } else { - // O SHI~ - scan(stack.slot) - } - } - - if (totalExtracted == 0L) { - return ItemStackWrapper.EMPTY - } - - copy.count = ImpreciseFraction(totalExtracted) - return copy - } - - override fun getStacks(): Collection> { - val listing = ArrayList>(index.size) - - for (tuple in index.values) { - listing.add(StorageTuple(tuple.id, tuple.stack)) - } - - return listing - } -} - class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.STORAGE_BUS, blockPos, blockState) { override val defaultDisplayName: Component get() = MACHINE_NAME @@ -302,7 +59,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter override val energy = WorkerEnergyStorage(this) - val cell = BasicStorageGraphNode() + val cell = BasicStorageGraphNode(energy) override fun setLevel(p_155231_: Level) { super.setLevel(p_155231_) @@ -314,8 +71,10 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter tickOnceServer(this::checkSurroundings) } + private var valid = true + override fun getCapability(cap: Capability, side: Direction?): LazyOptional { - return if (cap === MatteryCapability.STORAGE_NODE) { + return if (valid && cap === MatteryCapability.STORAGE_NODE) { cell.get().cast() } else super.getCapability(cap, side) } @@ -323,23 +82,28 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter override fun invalidateCaps() { super.invalidateCaps() cell.invalidate() + valid = false } override fun reviveCaps() { super.reviveCaps() cell.revive() + valid = true } private var neighbour: LazyOptional? = null private var component: ItemHandlerComponent? = null fun tick() { + batteryChargeLoop() component?.scan() + cell.tickEnergyDemanding() } override fun setRemoved() { super.setRemoved() cell.destroy(level) + valid = false } fun checkSurroundings() { @@ -376,4 +140,269 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter companion object { private val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_BUS}") } + + private inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent { + override val storageType: StorageStackType + get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() + + private val listeners = ArrayList>() + + override fun addListener(listener: IStorageListener): Boolean { + if (!listeners.contains(listener)) { + listeners.add(listener) + return true + } + + return false + } + + override fun removeListener(listener: IStorageListener): Boolean { + return listeners.remove(listener) + } + + private var scanned = arrayOfNulls(0) + private var scannedMap = arrayOfNulls(0) + private val tuples = HashMap() + private val index = HashMap() + + private fun removeTracked(slot: Int) { + scanned[slot] = null + val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" } + this.scannedMap[slot] = null + + val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot") + scannedMap.children.remove(slot) + val count = scannedMap.stack.count + scannedMap.stack.count -= item.stack.count + + if (scannedMap.stack.count.isPositive) { + for (listener in listeners) { + listener.changeStack(scannedMap, count) + } + } else { + for (listener in listeners) { + listener.removeStack(scannedMap) + } + + index.remove(scannedMap.id) + + val key = scannedMap.stack.key() + tuples.remove(key) ?: throw IllegalStateException("Item tuple is not present for slot $slot at ${scannedMap.stack}") + } + } + + private fun diffTracked(slot: Int, diff: Int) { + val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" } + val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot") + + val oldCount = scannedMap.stack.count + item.stack.count += diff + scannedMap.stack.count += diff + + for (listener in listeners) { + listener.changeStack(scannedMap.stack, scannedMap.id, oldCount) + } + } + + private fun addTracked(slot: Int, stack: ItemStack) { + check(scannedMap[slot] == null) { "Already tracking slot $slot" } + + val storageStack = ItemStackWrapper(stack) + val key = storageStack.key() + var tuple: TrackedTuple? = tuples[key] + val added = tuple == null + var oldCount = ImpreciseFraction.ZERO + + if (added) { + tuple = TrackedTuple(storageStack, UUID.randomUUID()) + index[tuple.id] = tuple + tuples[key] = tuple + } else { + oldCount = tuple!!.stack.count + tuple.stack.count += stack.count + } + + tuple.children[slot] = SlotTuple(slot, stack.copy()) + scanned[slot] = tuple.children[slot].stack + scannedMap[slot] = tuple + + if (added) { + for (listener in listeners) { + listener.addStack(tuple.stack, tuple.id, this) + } + } else { + for (listener in listeners) { + listener.changeStack(tuple.stack, tuple.id, oldCount) + } + } + } + + private fun sizeScan() { + if (scanned.size != parent.slots) { + val old = scanned + val oldMap = scannedMap + + if (scanned.size < parent.slots) { + // grow + scanned = arrayOfNulls(parent.slots) + scannedMap = arrayOfNulls(parent.slots) + + for ((i, item) in old.withIndex()) { + scanned[i] = item + } + + for ((i, item) in oldMap.withIndex()) { + scannedMap[i] = item + } + } else { + // shrink + for (i in parent.slots until scanned.size) { + removeTracked(i) + } + + scanned = arrayOfNulls(parent.slots) + scannedMap = arrayOfNulls(parent.slots) + + for (i in 0 until parent.slots) { + scanned[i] = old[i] + scannedMap[i] = oldMap[i] + } + } + } + } + + fun scan(slot: Int) { + val current = parent[slot].let { if (it.isEmpty) null else it } + val last = scanned[slot] + + if (current == null && last != null) { + removeTracked(slot) + } else if (current != null && last == null) { + addTracked(slot, current) + } else if (current != null && last != null) { + if (!ItemStack.isSameItemSameTags(current, last)) { + removeTracked(slot) + addTracked(slot, current) + } else if (current.count != last.count) { + diffTracked(slot, current.count - last.count) + } + } + } + + fun scan() { + sizeScan() + + for (slot in 0 until parent.slots) { + scan(slot) + } + } + + override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper { + if (energy.batteryLevel.isZero) + return stack + + val maxPossibleDemand = stack.count * OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation + val maxExtractEnergy = energy.extractEnergyInner(maxPossibleDemand, true) + + var leftover: ItemStackWrapper + + if (maxExtractEnergy == maxPossibleDemand) { + leftover = stack.copy() + } else { + leftover = stack.copy().also { + it.count = (maxExtractEnergy / OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation).floor() + } + } + + for (slot in 0 until parent.slots) { + val oldCount = leftover.count + leftover = ItemStackWrapper(parent.insertItem(slot, leftover.stack, simulate)) + + if (oldCount != leftover.count && !simulate) { + energy.extractEnergyInner((oldCount - leftover.count) * OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation, false) + scan(slot) + } + + if (leftover.isEmpty) { + break + } + } + + return leftover + } + + override fun getStack(id: UUID): ItemStackWrapper { + return index[id]?.stack ?: ItemStackWrapper.EMPTY + } + + override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): ItemStackWrapper { + @Suppress("NAME_SHADOWING") + var amount = amount.floor() + + if (!amount.isPositive) + return ItemStackWrapper.EMPTY + + val maxPossibleDemand = amount * OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation + val maxExtractEnergy = energy.extractEnergyInner(maxPossibleDemand, true) + + if (maxPossibleDemand != maxExtractEnergy) { + amount = (maxExtractEnergy / OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation).floor() + } + + val intAmount = amount.toLong() + val tuple = index[id] ?: return ItemStackWrapper.EMPTY + var totalExtracted = 0L + + val iter = tuple.children.values.iterator() + val listCopy = Array(tuple.children.values.size) { + iter.next() + } + + val copy = tuple.stack.copy() + + for (stack in listCopy) { + val extracted = parent.extractItem(stack.slot, stack.stack.count.coerceAtMost((intAmount - totalExtracted).clamp()), true) + + if (extracted.isEmpty) { + // dummy condition + continue + } else if (tuple.stack.sameItem(extracted)) { + if (!simulate) { + parent.extractItem(stack.slot, extracted.count, false) + } + + totalExtracted += extracted.count + + if (extracted.count != 0 && !simulate) { + scan(stack.slot) + energy.extractEnergyInner(OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation * extracted.count, false) + } + + if (totalExtracted >= intAmount) { + break + } + } else { + // O SHI~ + scan(stack.slot) + } + } + + if (totalExtracted == 0L) { + return ItemStackWrapper.EMPTY + } + + copy.count = ImpreciseFraction(totalExtracted) + return copy + } + + override fun getStacks(): Collection> { + val listing = ArrayList>(index.size) + + for (tuple in index.values) { + listing.add(StorageTuple(tuple.id, tuple.stack)) + } + + return listing + } + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StoragePowerSupplierBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StoragePowerSupplierBlockEntity.kt new file mode 100644 index 000000000..0ae5d5c38 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StoragePowerSupplierBlockEntity.kt @@ -0,0 +1,130 @@ +package ru.dbotthepony.mc.otm.block.entity + +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +import net.minecraft.nbt.CompoundTag +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.TranslatableComponent +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.capability.MatteryCapability +import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage +import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode +import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph +import ru.dbotthepony.mc.otm.registry.MBlockEntities +import ru.dbotthepony.mc.otm.registry.MNames +import ru.dbotthepony.mc.otm.set + +class StoragePowerSupplierBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.STORAGE_POWER_SUPPLIER, blockPos, blockState) { + override val defaultDisplayName: Component + get() = MACHINE_NAME + + override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? { + return null + } + + val cell = BasicStorageGraphNode() + + var powerSupplied = ImpreciseFraction.ZERO + private set + + override fun setLevel(p_155231_: Level) { + super.setLevel(p_155231_) + + if (p_155231_ is ServerLevel) { + StorageNetworkGraph.discoverFull(this, cell.storageNode) + } + } + + private var valid = true + + override fun getCapability(cap: Capability, side: Direction?): LazyOptional { + return if (valid && cap === MatteryCapability.STORAGE_NODE) { + cell.get().cast() + } else super.getCapability(cap, side) + } + + override fun invalidateCaps() { + super.invalidateCaps() + cell.invalidate() + valid = false + } + + override fun reviveCaps() { + super.reviveCaps() + cell.revive() + valid = true + } + + override fun setRemoved() { + super.setRemoved() + cell.destroy(level) + valid = false + } + + fun tick() { + batteryChargeLoop() + + if (energy.batteryLevel.isZero) + return + + val graph = cell.storageGraph ?: return + + if (graph.powerDemandingNodes.isEmpty()) + return + + var demand = ImpreciseFraction.ZERO + var i = 0 + + val available = energy.batteryLevel.coerceAtMost(MAX_IO) + + for (demanding in graph.powerDemandingNodes) { + val received = demanding.receiveEnergyOuter(available, true) + + if (received.isPositive) { + demand += received + i++ + } + } + + if (demand.isZero) { + return + } else if (demand < available) { + for (demanding in graph.powerDemandingNodes) { + powerSupplied += energy.transferInner(demanding, available, false) + } + } else { + val forEach = available / i + + for (demanding in graph.powerDemandingNodes) { + powerSupplied += energy.transferInner(demanding, forEach, false) + } + } + } + + override val energy = WorkerEnergyStorage(this, MAX_POWER, MAX_IO) + + override fun saveAdditional(nbt: CompoundTag) { + super.saveAdditional(nbt) + nbt["power_supplied"] = powerSupplied.serializeNBT() + } + + override fun load(nbt: CompoundTag) { + super.load(nbt) + nbt["power_supplied"]?.let { powerSupplied = ImpreciseFraction.deserializeNBT(it) } + } + + companion object { + private val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_POWER_SUPPLIER}") + private val MAX_POWER = ImpreciseFraction(100_000) + private val MAX_IO = ImpreciseFraction(320) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IMatteryEnergyStorage.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IMatteryEnergyStorage.kt index a7e062541..c9c192a55 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IMatteryEnergyStorage.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IMatteryEnergyStorage.kt @@ -179,4 +179,30 @@ interface IMatteryEnergyStorage : IEnergyStorage { override fun canReceive(): Boolean { return receiveEnergyOuter(ImpreciseFraction.ONE, true) > ImpreciseFraction.ZERO } -} \ No newline at end of file + + fun transferInner(other: IMatteryEnergyStorage, amount: ImpreciseFraction, simulate: Boolean): ImpreciseFraction { + if (!amount.isPositive) + return ImpreciseFraction.ZERO + + val extracted = extractEnergyInner(amount, true) + val received = other.receiveEnergyOuter(extracted, simulate) + + if (!simulate) + extractEnergyInner(received, false) + + return received + } + + fun transferOuter(other: IMatteryEnergyStorage, amount: ImpreciseFraction, simulate: Boolean): ImpreciseFraction { + if (!amount.isPositive) + return ImpreciseFraction.ZERO + + val extracted = extractEnergyOuter(amount, true) + val received = other.receiveEnergyOuter(extracted, simulate) + + if (!simulate) + extractEnergyInner(received, false) + + return received + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/BasicStorageGraphNode.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/BasicStorageGraphNode.kt index e43feb3b2..c03431184 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/BasicStorageGraphNode.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/BasicStorageGraphNode.kt @@ -2,13 +2,14 @@ package ru.dbotthepony.mc.otm.graph.storage import net.minecraft.world.level.Level import net.minecraftforge.common.util.LazyOptional +import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.storage.IStorage import ru.dbotthepony.mc.otm.storage.IStorageStack import ru.dbotthepony.mc.otm.storage.StorageStackType import java.util.* -open class BasicStorageGraphNode : IStorageGraphNode { +open class BasicStorageGraphNode(private val energyDemander: IMatteryEnergyStorage? = null) : IStorageGraphNode { private var resolver = LazyOptional.of { this } private var valid = true protected val components = ArrayList>() @@ -25,7 +26,16 @@ open class BasicStorageGraphNode : IStorageGraphNode { @Suppress("LeakingThis") final override val storageNode = Graph6Node(this) + private var demandingEnergy = false + override fun attachComponents(to: StorageNetworkGraph) { + if (energyDemander != null) { + if (!demandingEnergy && energyDemander.missingPower.isPositive) { + demandingEnergy = true + to.powerDemandingNodes.add(energyDemander) + } + } + if (manualAttaching) { return } @@ -35,10 +45,27 @@ open class BasicStorageGraphNode : IStorageGraphNode { } } + fun tickEnergyDemanding() { + energyDemander ?: throw IllegalStateException("No energy demander") + + if (!demandingEnergy && storageGraph != null && energyDemander.missingPower.isPositive) { + demandingEnergy = true + storageGraph!!.powerDemandingNodes.add(energyDemander) + } else if (demandingEnergy && storageGraph != null && !energyDemander.missingPower.isPositive) { + demandingEnergy = false + storageGraph!!.powerDemandingNodes.remove(energyDemander) + } + } + override fun removeComponents(from: StorageNetworkGraph) { for (component in components) { from.remove(component) } + + if (energyDemander != null) { + from.powerDemandingNodes.remove(energyDemander) + demandingEnergy = false + } } @Suppress("unchecked_cast") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt index 9a47dc0c2..f40fd67b2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt @@ -7,11 +7,13 @@ import net.minecraft.world.level.Level import net.minecraft.world.level.block.entity.BlockEntity import ru.dbotthepony.mc.otm.addPreServerTicker import ru.dbotthepony.mc.otm.addPreWorldTicker +import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.graph.Abstract6Graph import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.orNull import ru.dbotthepony.mc.otm.storage.* +import java.util.LinkedList class StorageNetworkGraph(private val level: Level) : Abstract6Graph() { private val virtualComponents = Object2ObjectArrayMap, VirtualComponent<*>>() @@ -58,6 +60,8 @@ class StorageNetworkGraph(private val level: Level) : Abstract6Graph() + companion object { @JvmStatic fun discoverFull(tile: BlockEntity, node: Graph6Node) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt index ccc2f03d5..1af8bd6fb 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt @@ -40,6 +40,7 @@ object MBlockEntities { val MATTER_RECYCLER: BlockEntityType<*> by registry.register(MNames.MATTER_RECYCLER) { BlockEntityType.Builder.of(::MatterRecyclerBlockEntity, MBlocks.MATTER_RECYCLER).build(null) } val STORAGE_BUS: BlockEntityType<*> by registry.register(MNames.STORAGE_BUS) { BlockEntityType.Builder.of(::StorageBusBlockEntity, MBlocks.STORAGE_BUS).build(null) } + val STORAGE_POWER_SUPPLIER: BlockEntityType<*> by registry.register(MNames.STORAGE_POWER_SUPPLIER) { BlockEntityType.Builder.of(::StoragePowerSupplierBlockEntity, MBlocks.STORAGE_POWER_SUPPLIER).build(null) } val DEBUG_EXPLOSION_SMALL: BlockEntityType<*> by registry.register(MNames.DEBUG_EXPLOSION_SMALL) { BlockEntityType.Builder.of(::BlockEntityExplosionDebugger, MBlocks.DEBUG_EXPLOSION_SMALL).build(null) } val DEBUG_SPHERE_POINTS: BlockEntityType<*> by registry.register(MNames.DEBUG_SPHERE_POINTS) { BlockEntityType.Builder.of(::BlockEntitySphereDebugger, MBlocks.DEBUG_SPHERE_POINTS).build(null) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt index f3e4971af..a685bc545 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt @@ -57,6 +57,7 @@ object MBlocks { val DRIVE_RACK: Block by registry.register(MNames.DRIVE_RACK) { DriveRackBlock() } val ITEM_MONITOR: Block by registry.register(MNames.ITEM_MONITOR) { ItemMonitorBlock() } val STORAGE_CABLE: Block by registry.register(MNames.STORAGE_CABLE) { StorageCableBlock() } + val STORAGE_POWER_SUPPLIER: Block by registry.register(MNames.STORAGE_POWER_SUPPLIER) { StoragePowerSupplierBlock() } val DEBUG_EXPLOSION_SMALL: Block by registry.register(MNames.DEBUG_EXPLOSION_SMALL) { BlockExplosionDebugger() } val DEBUG_SPHERE_POINTS: Block by registry.register(MNames.DEBUG_SPHERE_POINTS) { BlockSphereDebugger() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt index 8b18daa7f..231d11d7d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -51,6 +51,7 @@ object MItems { val DRIVE_RACK: Item by registry.register(MNames.DRIVE_RACK) { BlockItem(MBlocks.DRIVE_RACK, DEFAULT_PROPERTIES) } val ITEM_MONITOR: Item by registry.register(MNames.ITEM_MONITOR) { BlockItem(MBlocks.ITEM_MONITOR, DEFAULT_PROPERTIES) } val STORAGE_CABLE: Item by registry.register(MNames.STORAGE_CABLE) { BlockItem(MBlocks.STORAGE_CABLE, DEFAULT_PROPERTIES) } + val STORAGE_POWER_SUPPLIER: Item by registry.register(MNames.STORAGE_POWER_SUPPLIER) { BlockItem(MBlocks.STORAGE_POWER_SUPPLIER, DEFAULT_PROPERTIES) } val GRAVITATION_STABILIZER: Item by registry.register(MNames.GRAVITATION_STABILIZER) { object : BlockItem(MBlocks.GRAVITATION_STABILIZER, DEFAULT_PROPERTIES) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt index 9b9893b4c..f24619f39 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -24,6 +24,7 @@ object MNames { const val MATTER_RECYCLER = "matter_recycler" // нужен рецепт const val STORAGE_CABLE = "storage_cable" // нужен рецепт + const val STORAGE_POWER_SUPPLIER = "storage_power_supplier" // нужен рецепт const val DEBUG_EXPLOSION_SMALL = "debug_explosion_small" const val DEBUG_SPHERE_POINTS = "debug_sphere_points" diff --git a/src/main/resources/assets/overdrive_that_matters/lang/en_us.json b/src/main/resources/assets/overdrive_that_matters/lang/en_us.json index e9a6fda87..a5e84724a 100644 --- a/src/main/resources/assets/overdrive_that_matters/lang/en_us.json +++ b/src/main/resources/assets/overdrive_that_matters/lang/en_us.json @@ -236,6 +236,8 @@ "block.overdrive_that_matters.tritanium_raw_block": "Raw Tritanium Block", "block.overdrive_that_matters.storage_cable": "Storage Cable", + "block.overdrive_that_matters.storage_power_supplier": "Storage Power Supplier", + "block.overdrive_that_matters.storage_bus": "Storage Bus", "item.overdrive_that_matters.pill_android": "Android Pill", "item.overdrive_that_matters.pill_humane": "Humane Pill",