From d4aa8cd7ee9adeba831bef143cee8931f36dbddd Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 21 Mar 2022 19:57:09 +0700 Subject: [PATCH] Storage bus test, storage api improvements --- .../mc/otm/capability/MatteryCapability.java | 3 +- src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt | 2 + .../mc/otm/block/ChemicalGeneratorBlock.kt | 2 +- .../mc/otm/block/StorageBusBlock.kt | 60 +++ .../otm/block/entity/DriveRackBlockEntity.kt | 11 +- .../block/entity/ItemMonitorBlockEntity.kt | 1 - .../otm/block/entity/StorageBusBlockEntity.kt | 366 ++++++++++++++++++ .../capability/drive/AbstractMatteryDrive.kt | 10 +- .../mc/otm/core/ImpreciseFraction.kt | 7 + .../graph/storage/BasicStorageGraphNode.kt | 64 ++- .../mc/otm/menu/data/NetworkedItemView.kt | 4 +- .../mc/otm/registry/MBlockEntities.kt | 2 + .../ru/dbotthepony/mc/otm/registry/MBlocks.kt | 2 + .../ru/dbotthepony/mc/otm/registry/MItems.kt | 3 + .../ru/dbotthepony/mc/otm/registry/MNames.kt | 2 + .../ru/dbotthepony/mc/otm/storage/API.kt | 8 +- .../mc/otm/storage/ItemStackWrapper.kt | 4 +- .../mc/otm/storage/VirtualComponent.kt | 24 +- 18 files changed, 546 insertions(+), 29 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/StorageBusBlock.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java b/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java index 8643c35f8..75e5246f0 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java +++ b/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java @@ -9,6 +9,7 @@ import ru.dbotthepony.mc.otm.capability.matter.IMatterTaskProvider; import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage; import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode; import ru.dbotthepony.mc.otm.graph.storage.IStorageGraphNode; +import ru.dbotthepony.mc.otm.storage.IStorageStack; public class MatteryCapability { public static final Capability ENERGY = CapabilityManager.get(new CapabilityToken<>() {}); @@ -17,7 +18,7 @@ public class MatteryCapability { public static final Capability MATTER_NODE = CapabilityManager.get(new CapabilityToken<>() {}); public static final Capability PATTERN = CapabilityManager.get(new CapabilityToken<>() {}); public static final Capability TASK = CapabilityManager.get(new CapabilityToken<>() {}); - public static final Capability DRIVE = CapabilityManager.get(new CapabilityToken<>() {}); + public static final Capability> DRIVE = CapabilityManager.get(new CapabilityToken<>() {}); public static final Capability STORAGE_NODE = CapabilityManager.get(new CapabilityToken<>() {}); public static final Capability MEKANISM_ENERGY = CapabilityManager.get(new CapabilityToken<>() {}); diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt index 80accaab6..1706f3cf3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt @@ -21,6 +21,7 @@ import net.minecraft.world.item.ItemStack import net.minecraft.world.phys.Vec3 import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.energy.CapabilityEnergy +import net.minecraftforge.items.IItemHandler import ru.dbotthepony.mc.otm.capability.MatteryCapability import java.util.* import java.util.function.Consumer @@ -112,6 +113,7 @@ val CompoundTag.uuids get() = CompoundTagUUID(this) operator fun Container.set(index: Int, value: ItemStack) = setItem(index, value) operator fun Container.get(index: Int): ItemStack = getItem(index) +operator fun IItemHandler.get(index: Int): ItemStack = getStackInSlot(index) operator fun JsonObject.set(s: String, value: JsonElement) = add(s, value) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/ChemicalGeneratorBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/ChemicalGeneratorBlock.kt index 99943fcdb..dc5c59116 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/ChemicalGeneratorBlock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/ChemicalGeneratorBlock.kt @@ -51,7 +51,7 @@ class ChemicalGeneratorBlock : RotatableMatteryBlock(), EntityBlock { ) { super.neighborChanged(state, level, pos, sender, sender_pos, flag) - if (!level.isClientSide && level is ServerLevel) { + if (!level.isClientSide) { val tile = level.getBlockEntity(pos) if (tile is ChemicalGeneratorBlockEntity) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/StorageBusBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/StorageBusBlock.kt new file mode 100644 index 000000000..c6103aff4 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/StorageBusBlock.kt @@ -0,0 +1,60 @@ +package ru.dbotthepony.mc.otm.block + +import net.minecraft.core.BlockPos +import net.minecraft.world.item.context.BlockPlaceContext +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.Block +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.ChemicalGeneratorBlockEntity +import ru.dbotthepony.mc.otm.block.entity.StorageBusBlockEntity +import ru.dbotthepony.mc.otm.registry.MBlockEntities +import ru.dbotthepony.mc.otm.tickOnceServer +import ru.dbotthepony.mc.otm.unaryMinus + +class StorageBusBlock : RotatableMatteryBlock(), EntityBlock { + override val hasFreeRotation: Boolean get() = true + + override fun newBlockEntity(p_153215_: BlockPos, p_153216_: BlockState): BlockEntity { + return StorageBusBlockEntity(p_153215_, p_153216_) + } + + override fun getStateForPlacement(context: BlockPlaceContext): BlockState? { + return super.getStateForPlacement(context)?.setValue(FACING_FULL, -context.clickedFace) + } + + override fun getTicker( + p_153212_: Level, + p_153213_: BlockState, + p_153214_: BlockEntityType + ): BlockEntityTicker? { + if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.STORAGE_BUS) + return null + + return BlockEntityTicker { _, _, _, tile -> if (tile is StorageBusBlockEntity) tile.tick() } + } + + override fun neighborChanged( + state: BlockState, + level: Level, + pos: BlockPos, + sender: Block, + sender_pos: BlockPos, + flag: Boolean + ) { + super.neighborChanged(state, level, pos, sender, sender_pos, flag) + + if (!level.isClientSide) { + val tile = level.getBlockEntity(pos) + + if (tile is StorageBusBlockEntity) { + tickOnceServer(level) { + tile.checkSurroundings() + } + } + } + } +} 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 0f4f7ae67..1ce2a7c15 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 @@ -24,7 +24,10 @@ import ru.dbotthepony.mc.otm.menu.DriveRackMenu import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph import ru.dbotthepony.mc.otm.registry.MBlockEntities +import ru.dbotthepony.mc.otm.storage.IStorageStack +import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.PoweredVirtualComponent +import ru.dbotthepony.mc.otm.storage.StorageStackType class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, p_155229_, p_155230_) { @@ -36,11 +39,15 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : super.setChanged(slot, new, old) old.getCapability(MatteryCapability.DRIVE).ifPresent { - cell.computeIfAbsent(it.storageType) {PoweredVirtualComponent(it, energy)}.remove(it) + cell.computeIfAbsent(it.storageType) { + PoweredVirtualComponent(it, energy) + }.remove(it) } new.getCapability(MatteryCapability.DRIVE).ifPresent { - cell.computeIfAbsent(it.storageType) {PoweredVirtualComponent(it, energy)}.add(it) + cell.computeIfAbsent(it.storageType) { + PoweredVirtualComponent(it, energy) + }.add(it) } } } 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 45350a258..c0468440f 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,7 +22,6 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, p_155229_, p_155230_) { - @JvmField val cell = BasicStorageGraphNode() override val energy = WorkerEnergyStorage(this) 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 new file mode 100644 index 000000000..30c71a681 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt @@ -0,0 +1,366 @@ +package ru.dbotthepony.mc.otm.block.entity + +import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap +import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap +import it.unimi.dsi.fastutil.longs.Long2ObjectFunction +import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +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.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 net.minecraftforge.items.CapabilityItemHandler +import net.minecraftforge.items.IItemHandler +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock +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.core.plus +import ru.dbotthepony.mc.otm.get +import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode +import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph +import ru.dbotthepony.mc.otm.orThrow +import ru.dbotthepony.mc.otm.registry.MBlockEntities +import ru.dbotthepony.mc.otm.registry.MNames +import ru.dbotthepony.mc.otm.storage.* +import java.lang.ref.WeakReference +import java.util.* +import kotlin.collections.ArrayList + +private class SlotTuple(val slot: Int, val stack: ItemStack) +private class TrackedTuple(val stack: ItemStackWrapper, val id: UUID) { + val children = Int2ObjectAVLTreeMap() +} + +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 partitioned = Long2ObjectAVLTreeMap>() + private val index = Object2ObjectAVLTreeMap() + + 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.stack, scannedMap.id, count) + } + } else { + for (listener in listeners) { + listener.removeStack(scannedMap.stack, scannedMap.id) + } + + index.remove(scannedMap.id) + } + } + + 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 listing = partitioned.computeIfAbsent(ItemStackWrapper.partitionKey(stack.item), Long2ObjectFunction { ArrayList() }) + var tuple: TrackedTuple? = null + + for (storedTuple in listing) { + if (ItemStack.isSameItemSameTags(stack, storedTuple.stack.stack)) { + tuple = storedTuple + break + } + } + + val added = tuple == null + var oldCount = ImpreciseFraction.ZERO + + if (added) { + val storageStack = ItemStackWrapper(stack.copy()) + tuple = TrackedTuple(storageStack, UUID.randomUUID()) + index[tuple.id] = tuple + listing.add(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.toInt() + val tuple = index[id] ?: return ItemStackWrapper.EMPTY + var totalExtracted = 0 + + 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), true) + + if (extracted.isEmpty) { + // dummy condition + continue + } else if (ItemStack.isSameItemSameTags(extracted, tuple.stack.stack)) { + if (!simulate) { + parent.extractItem(stack.slot, stack.stack.count.coerceAtMost(intAmount - totalExtracted), false) + } + + totalExtracted += extracted.count + + if (extracted.count != 0 && !simulate) { + scan(stack.slot) + } + + if (totalExtracted >= intAmount) { + break + } + } else { + // O SHI~ + scan(stack.slot) + } + } + + if (totalExtracted == 0) { + return ItemStackWrapper.EMPTY + } + + copy.setCount(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 + + override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? { + return null + } + + override val energy = WorkerEnergyStorage(this) + + val cell = BasicStorageGraphNode() + + override fun setLevel(p_155231_: Level) { + super.setLevel(p_155231_) + + if (p_155231_ is ServerLevel) { + StorageNetworkGraph.discoverFull(this, cell.storageNode) + } + + tickOnceServer(this::checkSurroundings) + } + + override fun getCapability(cap: Capability, side: Direction?): LazyOptional { + return if (cap === MatteryCapability.STORAGE_NODE) { + cell.get().cast() + } else super.getCapability(cap, side) + } + + override fun invalidateCaps() { + super.invalidateCaps() + cell.invalidate() + } + + override fun reviveCaps() { + super.reviveCaps() + cell.revive() + } + + private var neighbour: LazyOptional? = null + private var component: ItemHandlerComponent? = null + + fun tick() { + component?.scan() + } + + fun checkSurroundings() { + val front = blockPos + blockState.getValue(RotatableMatteryBlock.FACING_FULL) + val storage = level?.getBlockEntity(front)?.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)?.let { if (it.isPresent) it else null } + + if (neighbour != storage) { + neighbour = storage + + if (storage != null) { + val ref = WeakReference(this) + + storage.addListener { + val self = ref.get() ?: return@addListener + + if (self.neighbour === it) + self.checkSurroundings() + } + + component?.let(cell::removeStorageComponent) + component = ItemHandlerComponent(storage.orThrow()) + component!!.let(cell::addStorageComponent) + } else { + component?.let(cell::removeStorageComponent) + component = null + } + } + } + + companion object { + private val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_BUS}") + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/AbstractMatteryDrive.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/AbstractMatteryDrive.kt index 5de2addf3..c08248f23 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/AbstractMatteryDrive.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/AbstractMatteryDrive.kt @@ -48,11 +48,12 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor for (state in listing) { if (state.stack.sameItem(stack)) { if (!simulate) { + val oldCount = state.stack.count state.stack.grow(maxInsert) storedCount += maxInsert for (listener in listeners) { - listener.changeStack(state.id, state.stack.count) + listener.changeStack(state.stack, state.id, oldCount) } isDirty = true @@ -115,7 +116,7 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor storedDifferentStacks-- for (listener in listeners) { - listener.removeStack(get.id) + listener.removeStack(get.stack, get.id) } if (listing.size == 0) { @@ -123,12 +124,13 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor } } - storedCount = storedCount.minus(amount) + storedCount -= amount + val oldCount = get.stack.count get.stack.shrink(amount) if (!get.stack.count.isZero) { for (listener in listeners) { - listener.changeStack(get.id, get.stack.count) + listener.changeStack(get.stack, get.id, oldCount) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/ImpreciseFraction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/ImpreciseFraction.kt index 38ac50454..7c075716d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/ImpreciseFraction.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/ImpreciseFraction.kt @@ -327,6 +327,13 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do return 31 * (decimal - decimal % EPSILON).hashCode() + whole.hashCode() } + fun floor(): ImpreciseFraction { + if (decimal == 0.0) + return this + + return ImpreciseFraction(whole) + } + fun toString(decimals: Int): String { if (isNaN) return "NaN" 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 7987c28c8..fe2a08682 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 @@ -39,21 +39,79 @@ open class BasicStorageGraphNode : IStorageGraphNode { } components.add(component) - storageGraph?.add(component) + + if (valid) + storageGraph?.add(component) + } + + fun removeStorageComponent(component: IStorage<*>) { + var indexOf = -1 + + for ((i, component1) in components.withIndex()) { + if (component === component1 || component1.storageType === component.storageType) { + indexOf = i + break + } + } + + if (indexOf == -1) { + return + } + + val self = components[indexOf] + components.removeAt(indexOf) + + if (valid) + storageGraph?.remove(self) + } + + fun removeStorageComponent(component: StorageStackType<*>) { + var indexOf = -1 + + for ((i, component1) in components.withIndex()) { + if (component1.storageType === component) { + indexOf = i + break + } + } + + if (indexOf == -1) { + return + } + + val self = components[indexOf] + components.removeAt(indexOf) + + if (valid) + storageGraph?.remove(self) } fun invalidate() { if (!valid) return valid = false resolver.invalidate() - // detachStorage(); + + val storageGraph = storageGraph + + if (storageGraph != null) { + for (component in components) { + storageGraph.remove(component) + } + } } fun revive() { if (valid) return valid = true resolver = LazyOptional.of { this } - // attachStorage(); + + val storageGraph = storageGraph + + if (storageGraph != null) { + for (component in components) { + storageGraph.add(component) + } + } } fun get(): LazyOptional { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.kt index 847dde066..a63df295d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.kt @@ -78,7 +78,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: } override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView) = addObject(stack.stack, id) - override fun changeStack(id: UUID, newCount: ImpreciseFraction) = changeObject(id, newCount.toInt()) + override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: ImpreciseFraction) = changeObject(id, stack.count.toInt()) protected fun network(fn: () -> Any) { if (!remote) { @@ -86,7 +86,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: } } - override fun removeStack(id: UUID) { + override fun removeStack(stack: ItemStackWrapper, id: UUID) { val get = upstream_state[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!") upstream_state.remove(id) state.remove(get.id) 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 de422b4bb..ccc2f03d5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt @@ -39,6 +39,8 @@ object MBlockEntities { val GRAVITATION_STABILIZER: BlockEntityType<*> by registry.register(MNames.GRAVITATION_STABILIZER) { BlockEntityType.Builder.of(::GravitationStabilizerBlockEntity, MBlocks.GRAVITATION_STABILIZER).build(null) } 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 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 986339ed6..05563fc95 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt @@ -55,6 +55,8 @@ object MBlocks { val PLATE_PRESS: Block by registry.register(MNames.PLATE_PRESS) { PlatePressBlock() } val MATTER_RECYCLER: Block by registry.register(MNames.MATTER_RECYCLER) { MatterRecyclerBlock() } + val STORAGE_BUS: Block by registry.register(MNames.STORAGE_BUS) { StorageBusBlock() } + 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 9e004f40c..0db5603d2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -48,6 +48,9 @@ object MItems { val CHEMICAL_GENERATOR: Item by registry.register(MNames.CHEMICAL_GENERATOR) { BlockItem(MBlocks.CHEMICAL_GENERATOR, DEFAULT_PROPERTIES) } val PLATE_PRESS: Item by registry.register(MNames.PLATE_PRESS) { BlockItem(MBlocks.PLATE_PRESS, DEFAULT_PROPERTIES) } val MATTER_RECYCLER: Item by registry.register(MNames.MATTER_RECYCLER) { BlockItem(MBlocks.MATTER_RECYCLER, DEFAULT_PROPERTIES) } + + val STORAGE_BUS: Item by registry.register(MNames.STORAGE_BUS) { BlockItem(MBlocks.STORAGE_BUS, DEFAULT_PROPERTIES) } + val GRAVITATION_STABILIZER: Item by registry.register(MNames.GRAVITATION_STABILIZER) { object : BlockItem(MBlocks.GRAVITATION_STABILIZER, DEFAULT_PROPERTIES) { override fun appendHoverText( 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 08a0b87ed..92a2232fa 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -31,6 +31,8 @@ object MNames { const val GRAVITATION_STABILIZER_LENS = "gravitation_stabilizer_lens" const val CARGO_CRATE = "cargo_crate" // нужен рецепт? + const val STORAGE_BUS = "storage_bus" + // building blocks const val TRITANIUM_BLOCK = "tritanium_block" const val TRITANIUM_STRIPED_BLOCK = "tritanium_striped_block" diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt index 245459f11..aad0b615f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt @@ -92,6 +92,8 @@ interface IStorageView : IStorageTrigger { fun getStack(id: UUID): T /** + * If tuple does not exist, returns empty stack + * * @param id identifier of stack to extract * @param amount amount of units to extract * @param simulate whenever to simulate the action or not @@ -134,7 +136,7 @@ interface IStorageView : IStorageTrigger { fun removeListenerAuto(listener: IStorageListener): Boolean { if (removeListener(listener)) { for (stack in getStacks()) { - listener.removeStack(stack.id) + listener.removeStack(stack.stack, stack.id) } return true @@ -153,12 +155,12 @@ interface IStorageListener { /** * Fired on whenever an object is changes on listener we subscribed to */ - fun changeStack(id: UUID, newCount: ImpreciseFraction) + fun changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction) /** * Fired on whenever an object is removed from listener we subscribed to */ - fun removeStack(id: UUID) + fun removeStack(stack: T, id: UUID) } interface IStorageTuple { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt index 2dcf73036..7a3d817f6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt @@ -9,7 +9,7 @@ import ru.dbotthepony.mc.otm.core.ImpreciseFraction class ItemStackWrapper(val stack: ItemStack) : IStorageStack { operator fun component1() = stack - override fun copy(): IStorageStack { + override fun copy(): ItemStackWrapper { return ItemStackWrapper(stack.copy()) } @@ -40,6 +40,6 @@ class ItemStackWrapper(val stack: ItemStack) : IStorageStack { @JvmField val EMPTY = ItemStackWrapper(ItemStack.EMPTY) - fun partitionKey(item: Item) = (ForgeRegistries.ITEMS as ForgeRegistry?)?.getID(item)?.toLong() ?: System.identityHashCode(item).toLong() + fun partitionKey(item: Item) = (ForgeRegistries.ITEMS as? ForgeRegistry)?.getID(item)?.toLong() ?: System.identityHashCode(item).toLong() } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt index db64f2cf4..c194381a3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt @@ -99,9 +99,11 @@ open class VirtualComponent(type: StorageStackType) : ISto check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" } val items = partitions.computeIfAbsent(stack.partitionKey, Long2ObjectFunction { ArrayList() }) var local: LocalTuple? = null + var oldCount = ImpreciseFraction.ZERO for (item in items) { if (item.stack.sameItem(stack)) { + oldCount = item.stack.count item.stack.grow(stack.count) local = item break @@ -126,25 +128,27 @@ open class VirtualComponent(type: StorageStackType) : ISto } } else { for (listener in listeners) { - listener.changeStack(local.id, local.stack.count) + listener.changeStack(local.stack, local.id, oldCount) } } } - override fun changeStack(id: UUID, newCount: ImpreciseFraction) { - assert(newCount > ImpreciseFraction.ZERO) + override fun changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction) { + require(stack.count.isPositive) val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") - val diff = newCount - tuple.obj.count - tuple.obj.count = newCount + val diff = stack.count - tuple.obj.count + tuple.obj.count = stack.count + + @Suppress("NAME_SHADOWING") val oldCount = tuple.local.stack.count tuple.local.stack.grow(diff) for (listener in listeners) { - listener.changeStack(tuple.local.id, tuple.local.stack.count) + listener.changeStack(tuple.local.stack, tuple.local.id, oldCount) } } - override fun removeStack(id: UUID) { + override fun removeStack(stack: T, id: UUID) { val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") val key = tuple.local.stack.partitionKey @@ -162,7 +166,7 @@ open class VirtualComponent(type: StorageStackType) : ISto partitions[key]!!.remove(tuple.local) for (listener in listeners) { - listener.removeStack(tuple.local.id) + listener.removeStack(tuple.local.stack, tuple.local.id) } } } @@ -200,7 +204,7 @@ open class VirtualComponent(type: StorageStackType) : ISto var extracted = ImpreciseFraction.ZERO val copy = tuple.stack.copy() as T - for (remote_tuple in tuple.tuples) { + for (remote_tuple in tuple.tuples.let { Array(it.size) { i -> it[i] } }) { extracted += remote_tuple.extractCount(toExtract - extracted, simulate) if (extracted >= toExtract) @@ -219,7 +223,7 @@ open class VirtualComponent(type: StorageStackType) : ISto open class PoweredVirtualComponent(type: StorageStackType, val energyProvider: () -> IMatteryEnergyStorage) : VirtualComponent(type) { constructor(type: Class, energyStorage: IMatteryEnergyStorage) : this(StorageRegistry.get(type), {energyStorage}) constructor(type: StorageStackType, energyStorage: IMatteryEnergyStorage) : this(type, {energyStorage}) - constructor(other: VirtualComponent, energyStorage: IMatteryEnergyStorage) : this(other.storageType.identity, energyStorage) { + constructor(other: IStorage, energyStorage: IMatteryEnergyStorage) : this(other.storageType.identity, energyStorage) { add(other) }