From 64d5f1b33680294594535f260b54468354ea8ae0 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 6 Aug 2023 23:13:15 +0700 Subject: [PATCH] Refine storage api, make it compile --- .../mc/otm/OverdriveThatMatters.java | 9 - .../mc/otm/capability/MatteryCapability.java | 2 +- .../entity/storage/DriveRackBlockEntity.kt | 19 +- .../entity/storage/DriveViewerBlockEntity.kt | 3 +- .../entity/storage/ItemMonitorBlockEntity.kt | 32 +- .../entity/storage/StorageBusBlockEntity.kt | 324 ++++++------------ .../block/entity/storage/StorageInterfaces.kt | 155 ++++----- .../mc/otm/capability/drive/API.kt | 12 +- .../capability/drive/AbstractMatteryDrive.kt | 82 ++--- .../otm/capability/drive/ItemMatteryDrive.kt | 58 ++-- .../screen/storage/DriveViewerScreen.kt | 2 +- .../screen/storage/ItemMonitorScreen.kt | 8 +- .../dbotthepony/mc/otm/compat/mekanism/QIO.kt | 65 ++-- .../mc/otm/core/collect/StreamyIterator.kt | 13 + .../mc/otm/graph/storage/StorageGraph.kt | 21 +- .../mc/otm/graph/storage/StorageNode.kt | 79 ++--- .../mc/otm/matter/MatterManager.kt | 9 +- .../mc/otm/menu/data/NetworkedItemView.kt | 61 ++-- .../mc/otm/menu/storage/DriveViewerMenu.kt | 20 +- .../mc/otm/menu/storage/ItemMonitorMenu.kt | 16 +- .../dbotthepony/mc/otm/registry/MRegistry.kt | 2 + .../ru/dbotthepony/mc/otm/storage/API.kt | 160 +++------ .../ru/dbotthepony/mc/otm/storage/Helpers.kt | 25 ++ .../mc/otm/storage/ItemStackWrapper.kt | 107 ------ .../mc/otm/storage/ItemStorageStack.kt | 60 ++++ .../ru/dbotthepony/mc/otm/storage/Registry.kt | 60 ---- .../mc/otm/storage/StorageStack.kt | 127 +++++++ .../mc/otm/storage/VirtualComponent.kt | 302 ++++------------ .../otm/storage/powered/PoweredComponent.kt | 16 + .../storage/powered/PoweredStorageAcceptor.kt | 34 ++ .../storage/powered/PoweredStorageProvider.kt | 36 ++ .../powered/PoweredVirtualComponent.kt | 25 ++ 32 files changed, 824 insertions(+), 1120 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/Helpers.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStorageStack.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/Registry.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/StorageStack.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredComponent.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredStorageAcceptor.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredStorageProvider.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredVirtualComponent.kt diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index b034e27e3..f729571bb 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -75,13 +75,6 @@ public final class OverdriveThatMatters { private static final Logger LOGGER = LogManager.getLogger(); public static OverdriveThatMatters INSTANCE; - private StorageStackType ITEM_STORAGE; - - @NotNull - public StorageStackType ITEM_STORAGE() { - return Objects.requireNonNull(ITEM_STORAGE); - } - public static ResourceLocation loc(String path) { return new ResourceLocation(MOD_ID, path); } @@ -206,8 +199,6 @@ public final class OverdriveThatMatters { WeaponNetworkChannel.INSTANCE.register(); GenericNetworkChannel.INSTANCE.register(); - ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new Decimal("3.125")); - if (ModList.get().isLoaded("mekanism")) { EVENT_BUS.addGenericListener(BlockEntity.class, EventPriority.NORMAL, QIOKt::attachCapabilities); } 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 37a8aba97..93c227c62 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java +++ b/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java @@ -45,7 +45,7 @@ public class MatteryCapability { @Nonnull @NotNull - public static final Capability DRIVE = CapabilityManager.get(new CapabilityToken<>() {}); + public static final Capability> DRIVE = CapabilityManager.get(new CapabilityToken<>() {}); @Nonnull @NotNull diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/DriveRackBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/DriveRackBlockEntity.kt index ab72fdbbc..c46d9cd0a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/DriveRackBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/DriveRackBlockEntity.kt @@ -1,7 +1,6 @@ package ru.dbotthepony.mc.otm.block.entity.storage import net.minecraft.core.BlockPos -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 @@ -11,13 +10,14 @@ import net.minecraft.world.level.block.state.BlockState import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity import ru.dbotthepony.mc.otm.graph.storage.StorageNode import ru.dbotthepony.mc.otm.capability.MatteryCapability +import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive 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.graph.storage.StorageGraph import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.storage.* +import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, p_155229_, p_155230_) { @@ -27,16 +27,19 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { super.setChanged(slot, new, old) + // generics is going apeshit since storage types are invariant, + // but since we don't know generics of upvalue mattery drive, its storage type + // is defined as out variant old.getCapability(MatteryCapability.DRIVE).ifPresent { - cell.computeIfAbsent(it.storageType) { - PoweredVirtualComponent(it, energy) - }.remove(it) + cell.computeIfAbsent(it.storageType as StorageStack.Type) { + PoweredVirtualComponent(VirtualComponent(it), ::energy) + }.remove(it as IMatteryDrive) } new.getCapability(MatteryCapability.DRIVE).ifPresent { - cell.computeIfAbsent(it.storageType) { - PoweredVirtualComponent(it, energy) - }.add(it) + cell.computeIfAbsent(it.storageType as StorageStack.Type) { + PoweredVirtualComponent(VirtualComponent(it), ::energy) + }.add(it as IMatteryDrive) } } }.also(::addDroppableContainer) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/DriveViewerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/DriveViewerBlockEntity.kt index 62e7060dc..1dd82c294 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/DriveViewerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/DriveViewerBlockEntity.kt @@ -18,6 +18,7 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu +import ru.dbotthepony.mc.otm.storage.StorageStack class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_VIEWER, p_155229_, p_155230_) { override fun setChanged() { @@ -28,7 +29,7 @@ class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte tickList.once { var state = blockState - if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent && energy.batteryLevel >= OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation) { + if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent) { state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING) } else { state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/ItemMonitorBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/ItemMonitorBlockEntity.kt index 7934932b4..2f33be061 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/ItemMonitorBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/ItemMonitorBlockEntity.kt @@ -6,13 +6,11 @@ import net.minecraft.core.NonNullList import net.minecraft.nbt.CompoundTag import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.Component -import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerPlayer 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.inventory.CraftingContainer import net.minecraft.world.inventory.TransientCraftingContainer import net.minecraft.world.item.ItemStack import net.minecraft.world.item.crafting.CraftingRecipe @@ -40,6 +38,7 @@ import ru.dbotthepony.mc.otm.core.nbt.mapString import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu import ru.dbotthepony.mc.otm.storage.* +import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent import java.math.BigInteger import java.util.* import java.util.function.Supplier @@ -165,7 +164,7 @@ private fun takeOne(inventory: Inventory, item: ItemStack): Boolean { return false } -private fun takeOne(id: UUID?, view: IStorageProvider): Boolean { +private fun takeOne(id: UUID?, view: IStorageProvider): Boolean { val extracted = view.extractStack(id ?: return false, BigInteger.ONE, false) if (!extracted.isEmpty) { @@ -175,10 +174,7 @@ private fun takeOne(id: UUID?, view: IStorageProvider): Boolea return false } -class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : - MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, p_155229_, p_155230_), - IStorageEventConsumer { - +class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, blockPos, blockState), IStorageEventConsumer { val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.ITEM_MONITOR) init { @@ -186,18 +182,18 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : savetable(::energy, ENERGY_KEY) } - var poweredView: PoweredVirtualComponent? = null + var poweredView: PoweredVirtualComponent? = null private set val cell = object : StorageNode(energy) { override fun attachComponents(to: StorageGraph) { super.attachComponents(to) - poweredView = PoweredVirtualComponent(to.getVirtualComponent(ITEM_STORAGE), energy) + poweredView = PoweredVirtualComponent(to.getVirtualComponent(StorageStack.ITEMS), ::energy) } override fun removeComponents(from: StorageGraph) { super.removeComponents(from) - poweredView?.removeListeners() + // poweredView?.removeListeners() poweredView = null } } @@ -248,7 +244,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : val item = craftingGrid[i] if (!item.isEmpty) { - craftingGridTuples[i] = poweredView[ItemStackWrapper(item).key()] + craftingGridTuples[i] = poweredView[ItemStorageStack.unsafe(item)] } } } @@ -269,18 +265,18 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : private val craftingGridDummy = TransientCraftingContainer(craftingMenu, 3, 3) - override val storageType: StorageStackType - get() = ITEM_STORAGE + override val storageType: StorageStack.Type + get() = StorageStack.ITEMS - override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider) { + override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider) { // no op } - override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) { + override fun onStackChanged(stack: ItemStorageStack, id: UUID) { // no op } - override fun removeStack(stack: ItemStackWrapper, id: UUID) { + override fun onStackRemoved(id: UUID) { for (i in craftingGridTuples.indices) { if (craftingGridTuples[i] == id) { craftingGridTuples[i] = null @@ -393,7 +389,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : if (!remainder.isEmpty) { when (settings.resultTarget) { ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, ItemMonitorPlayerSettings.ResultTarget.MIXED -> { - val remaining = poweredView?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder + val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder if (!remaining.isEmpty) { if (newItem.isEmpty) { @@ -409,7 +405,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY -> { if (!craftingPlayer.inventory.add(remainder)) { - val remaining = poweredView?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder + val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder if (!remaining.isEmpty) { if (newItem.isEmpty) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/StorageBusBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/StorageBusBlockEntity.kt index a352411dc..57a8530ca 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/StorageBusBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/StorageBusBlockEntity.kt @@ -2,8 +2,8 @@ package ru.dbotthepony.mc.otm.block.entity.storage import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap import it.unimi.dsi.fastutil.ints.IntAVLTreeSet +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap 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 @@ -12,7 +12,6 @@ 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.ForgeCapabilities -import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.items.IItemHandler import ru.dbotthepony.mc.otm.* import ru.dbotthepony.mc.otm.block.CableBlock @@ -23,23 +22,20 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.config.MachinesConfig 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.math.RelativeSide -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.math.toIntSafe import ru.dbotthepony.mc.otm.graph.storage.StorageNode import ru.dbotthepony.mc.otm.menu.storage.StorageBusMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.storage.* -import java.lang.ref.WeakReference import java.math.BigInteger import java.util.* import java.util.stream.Stream -private class SlotTuple(val slot: Int, val stack: ItemStack) -private class TrackedTuple(override val stack: ItemStackWrapper, override val id: UUID) : IStorageTuple { +private data class SlotTuple(val slot: Int, val stack: ItemStack) + +private class TrackedTuple(override var stack: ItemStorageStack, override val id: UUID) : IStorageTuple { // do not use hash map because we need keys to be iterated in natural order val children = Int2ObjectAVLTreeMap() @@ -128,51 +124,21 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter const val MAX_FILTERS = 6 * 3 } - private inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent { - private inner class EventsSnapshot { - val index = HashMap() + private inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent { + private fun updateTuple(tuple: TrackedTuple, diff: BigInteger) { + tuple.stack = tuple.stack.grow(diff) - fun change(key: UUID, diff: BigInteger) { - if (diff.isZero) - return - - val value = index[key] - - if (value == null) { - index[key] = diff - } else { - val newvalue = value + diff - - if (newvalue.isZero) { - index.remove(key) - } else { - index[key] = newvalue - } + if (tuple.stack.isNotEmpty) { + for (listener in listeners) { + listener.onStackChanged(tuple) } - } - - fun apply() { - for ((key, value) in index) { - val tuple = checkNotNull(this@ItemHandlerComponent.index[key]) { "Tuple with ID $key is missing!" } - - val count = tuple.stack.count - tuple.stack.count += value - - if (tuple.stack.count.isPositive) { - for (listener in listeners) { - listener.changeStack(tuple, count) - } - } else { - for (listener in listeners) { - listener.removeStack(tuple) - } - - this@ItemHandlerComponent.index.remove(tuple.id) - - val tuplekey = tuple.stack.key() - tuples.remove(tuplekey) ?: throw IllegalStateException("Cross-reference integrity check failed for $tuple") - } + } else { + for (listener in listeners) { + listener.onStackRemoved(tuple) } + + id2tuples.remove(tuple.id) + items2tuples.remove(tuple.stack) ?: throw IllegalStateException("Cross-reference integrity check failed for $tuple") } } @@ -240,14 +206,12 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter } } - private var snapshot: EventsSnapshot? = null + override val storageType: StorageStack.Type + get() = StorageStack.ITEMS - override val storageType: StorageStackType - get() = ITEM_STORAGE + private val listeners = ArrayList>() - private val listeners = ArrayList>() - - override fun addListener(listener: IStorageEventConsumer): Boolean { + override fun addListener(listener: IStorageEventConsumer): Boolean { if (!listeners.contains(listener)) { listeners.add(listener) return true @@ -256,139 +220,92 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter return false } - override fun removeListener(listener: IStorageEventConsumer): Boolean { + override fun removeListener(listener: IStorageEventConsumer): Boolean { return listeners.remove(listener) } - private var scanned = arrayOfNulls(0) - private var scannedMap = arrayOfNulls(0) - private val tuples = HashMap() - private val index = HashMap() + private var slot2itemStack = arrayOfNulls(0) + private var slot2tuple = arrayOfNulls(0) + private val items2tuples = Object2ObjectOpenCustomHashMap(StorageStack.Companion) + private val id2tuples = HashMap() private fun removeTracked(slot: Int) { - scanned[slot] = null - val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" } - this.scannedMap[slot] = null + slot2itemStack[slot] = null + val tuple = checkNotNull(slot2tuple[slot]) { "Not tracking slot $slot" } + slot2tuple[slot] = null - val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot") - scannedMap.children.remove(slot) - val count = scannedMap.stack.count - - val snapshot = snapshot - - if (snapshot != null) { - snapshot.change(scannedMap.id, -item.stack.count.toBigInteger()) - } else { - scannedMap.stack.count -= item.stack.count.toBigInteger() - - 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}") - } - } + val slotTuple = tuple.children[slot] ?: throw IllegalStateException("${tuple.id} does not track $slot") + tuple.children.remove(slot) + updateTuple(tuple, -slotTuple.stack.count.toBigInteger()) } private fun diffTracked(slot: Int, diff: Int) { - val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" } - val item = checkNotNull(scannedMap.children[slot]) { "${scannedMap.id} does not track $slot" } - - val oldCount = scannedMap.stack.count - item.stack.count += diff - - val snapshot = snapshot - - if (snapshot != null) { - snapshot.change(scannedMap.id, diff.toBigInteger()) - } else { - scannedMap.stack.count += diff.toBigInteger() - - for (listener in listeners) { - listener.changeStack(scannedMap.stack, scannedMap.id, oldCount) - } - } + val tuple = checkNotNull(slot2tuple[slot]) { "Not tracking slot $slot" } + val slotTuple = checkNotNull(tuple.children[slot]) { "${tuple.id} does not track $slot" } + slotTuple.stack.count += diff + updateTuple(tuple, diff.toBigInteger()) } private fun addTracked(slot: Int, stack: ItemStack) { - check(scannedMap[slot] == null) { "Already tracking slot $slot" } + check(slot2tuple[slot] == null) { "Already tracking slot $slot" } - val storageStack = ItemStackWrapper(stack) - val key = storageStack.key() - var tuple: TrackedTuple? = tuples[key] + val storageStack = ItemStorageStack(stack) + var tuple = items2tuples[storageStack] val added = tuple == null - var oldCount = BigInteger.ZERO - if (added) { + if (tuple == null) { tuple = TrackedTuple(storageStack, UUID.randomUUID()) - index[tuple.id] = tuple - tuples[key] = tuple + id2tuples[tuple.id] = tuple + items2tuples[storageStack] = tuple } else { - oldCount = tuple!!.stack.count - - if (snapshot == null) - tuple.stack.count += stack.count.toBigInteger() + tuple.stack = tuple.stack.grow(stack.count.toBigInteger()) } tuple.children[slot] = SlotTuple(slot, stack.copy()) - scanned[slot] = tuple.children[slot].stack - scannedMap[slot] = tuple + slot2itemStack[slot] = tuple.children[slot].stack + slot2tuple[slot] = tuple if (added) { for (listener in listeners) { - listener.addStack(tuple.stack, tuple.id, this) + listener.onStackAdded(tuple.stack, tuple.id, this) } } else { - val snapshot = snapshot - - if (snapshot != null) { - snapshot.change(tuple.id, stack.count.toBigInteger()) - } else { - for (listener in listeners) { - listener.changeStack(tuple.stack, tuple.id, oldCount) - } + for (listener in listeners) { + listener.onStackChanged(tuple.stack, tuple.id) } } } private fun sizeScan() { - if (scanned.size != parent.slots) { - val old = scanned - val oldMap = scannedMap + if (slot2itemStack.size != parent.slots) { + val old = slot2itemStack + val oldMap = slot2tuple - if (scanned.size < parent.slots) { + if (slot2itemStack.size < parent.slots) { // grow - scanned = arrayOfNulls(parent.slots) - scannedMap = arrayOfNulls(parent.slots) + slot2itemStack = arrayOfNulls(parent.slots) + slot2tuple = arrayOfNulls(parent.slots) for ((i, item) in old.withIndex()) { - scanned[i] = item + slot2itemStack[i] = item } for ((i, item) in oldMap.withIndex()) { - scannedMap[i] = item + slot2tuple[i] = item } } else { // shrink - for (i in parent.slots until scanned.size) { - if (scannedMap[i] != null) + for (i in parent.slots until slot2itemStack.size) { + if (slot2tuple[i] != null) removeTracked(i) } - scanned = arrayOfNulls(parent.slots) - scannedMap = arrayOfNulls(parent.slots) + slot2itemStack = arrayOfNulls(parent.slots) + slot2tuple = arrayOfNulls(parent.slots) for (i in 0 until parent.slots) { - scanned[i] = old[i] - scannedMap[i] = oldMap[i] + slot2itemStack[i] = old[i] + slot2tuple[i] = oldMap[i] } } } @@ -396,7 +313,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter fun scan(slot: Int) { val current = parent[slot].let { if (it.isEmpty || !filter.match(it)) null else it } - val last = scanned[slot] + val last = slot2itemStack[slot] if (current == null && last != null) { removeTracked(slot) @@ -415,42 +332,27 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter fun scan() { sizeScan() - snapshot = EventsSnapshot() - for (slot in 0 until parent.slots) { scan(slot) } - - snapshot!!.apply() - snapshot = null } - override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper { - if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.item)) + override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack { + if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.toItemStack())) return stack - val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * stack.count - val maxExtractEnergy = energy.extractEnergy(maxPossibleDemand, true) + val required = StorageStack.ITEMS.energyPerInsert(stack) + if (energy.extractEnergy(required, true) != required) return stack - var leftover: ItemStackWrapper = stack.copy() - var additional: BigInteger = BigInteger.ZERO + var leftover = stack - if (maxExtractEnergy != maxPossibleDemand) { - additional = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole - leftover.count -= additional - } - - for (slot in PrioritizedSlotIterator(tuples[stack.key()])) { + for (slot in PrioritizedSlotIterator(items2tuples[stack])) { val oldCount = leftover.count - leftover = ItemStackWrapper(parent.insertItem(slot, leftover.stack, simulate)) + leftover = ItemStorageStack.unsafe(parent.insertItem(slot, leftover.toItemStack(), simulate)) if (oldCount != leftover.count && !simulate) { - energy.extractEnergy(ITEM_STORAGE.energyPerOperation * (oldCount - leftover.count), false) - - if (scanned.size <= slot) { - sizeScan() - } - + energy.extractEnergy(StorageStack.ITEMS.energyPerInsert(stack.copy(oldCount - leftover.count)), false) + if (slot2itemStack.size <= slot) sizeScan() scan(slot) } @@ -459,74 +361,52 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter } } - return leftover.also { - if (additional != BigInteger.ZERO) { - it.count += additional - } - } + return leftover } - override fun get(id: UUID): ItemStackWrapper { - return index[id]?.stack ?: ItemStackWrapper.EMPTY + override fun get(id: UUID): ItemStorageStack { + return id2tuples[id]?.stack ?: ItemStorageStack.EMPTY } - override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStackWrapper { - if (redstoneControl.isBlockedByRedstone || !amount.isPositive) - return ItemStackWrapper.EMPTY + override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStorageStack { + if (redstoneControl.isBlockedByRedstone || !amount.isPositive || !energy.batteryLevel.isPositive) + return ItemStorageStack.EMPTY - val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * amount - val maxExtractEnergy = energy.extractEnergy(maxPossibleDemand, true) + var total = BigInteger.ZERO + val tuple = id2tuples[id] ?: return ItemStorageStack.EMPTY + val slots = tuple.children.values.iterator() + val lstack = tuple.stack - @Suppress("NAME_SHADOWING") - var amount = amount + while (amount < total && slots.hasNext() && energy.batteryLevel.isPositive) { + val (slot, stack) = slots.next() + val extracted = parent.extractItem(slot, stack.count.coerceAtMost(amount.toIntSafe()), true) + val required = StorageStack.ITEMS.energyPerExtract(ItemStorageStack.unsafe(extracted)) - if (maxPossibleDemand != maxExtractEnergy) { - amount = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole - } + if (extracted.isNotEmpty && tuple.stack.equalsWithoutCount(ItemStorageStack.unsafe(extracted)) && energy.extractEnergy(required, true) == required) { + if (simulate) { + total += extracted.count.toBigInteger() + } else { + val extracted2 = parent.extractItem(slot, extracted.count, false) - val intAmount = amount.toLong() - val tuple = index[id] ?: return ItemStackWrapper.EMPTY - var totalExtracted = 0L + if (extracted2.count == extracted.count) { + energy.extractEnergy(required, false) + } else { + energy.extractEnergy(StorageStack.ITEMS.energyPerExtract(ItemStorageStack.unsafe(extracted2)), false) + } - val copy = tuple.stack.copy() - - for (stack in tuple.children.values) { - val extracted = parent.extractItem(stack.slot, stack.stack.count.coerceAtMost((intAmount - totalExtracted).clamp()), true) - - if (!extracted.isEmpty && tuple.stack.sameItem(extracted)) { - if (!simulate) { - parent.extractItem(stack.slot, extracted.count, false) - } - - totalExtracted += extracted.count - - if (extracted.count != 0 && !simulate) { - energy.extractEnergy(ITEM_STORAGE.energyPerOperation * extracted.count, false) - } - - if (totalExtracted >= intAmount) { - break + total += extracted2.count.toBigInteger() } } } - if (totalExtracted == 0L) { - return ItemStackWrapper.EMPTY - } - - if (!simulate) { - scan() - } - - copy.count = totalExtracted.toBigInteger() - return copy + return lstack.copy(total) } - override val stacks: Stream> get() { - val listing = ArrayList>(index.size) + override val stacks: Stream> get() { + val listing = ArrayList>(id2tuples.size) - for (tuple in index.values) { - listing.add(StorageTuple(tuple.id, tuple.stack)) + for (tuple in id2tuples.values) { + listing.add(IStorageTuple.I(tuple.id, tuple.stack)) } return listing.stream() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/StorageInterfaces.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/StorageInterfaces.kt index a2dc766ca..e5348811e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/StorageInterfaces.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/StorageInterfaces.kt @@ -1,7 +1,6 @@ package ru.dbotthepony.mc.otm.block.entity.storage import net.minecraft.core.BlockPos -import net.minecraft.core.Direction import net.minecraft.network.chat.Component import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player @@ -20,13 +19,12 @@ import ru.dbotthepony.mc.otm.block.CableBlock import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity 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.EnergyBalanceValues import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.math.RelativeSide -import ru.dbotthepony.mc.otm.core.math.toIntSafe import ru.dbotthepony.mc.otm.core.orNull import ru.dbotthepony.mc.otm.graph.storage.StorageNode import ru.dbotthepony.mc.otm.menu.storage.StorageExporterMenu @@ -36,10 +34,8 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MNames 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 ru.dbotthepony.mc.otm.storage.ItemStorageStack +import ru.dbotthepony.mc.otm.storage.StorageStack import java.math.BigInteger import java.util.* import java.util.stream.Stream @@ -112,7 +108,9 @@ abstract class AbstractStorageImportExport( } } -class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractStorageImportExport(MBlockEntities.STORAGE_IMPORTER, blockPos, blockState), IItemHandler { +class StorageImporterBlockEntity( + blockPos: BlockPos, blockState: BlockState +) : AbstractStorageImportExport(MBlockEntities.STORAGE_IMPORTER, blockPos, blockState), IItemHandler { override val filter = ItemFilter(MAX_FILTERS) { _, _, _ -> setChangedLight() } @@ -124,8 +122,6 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A private var lastSlot = 0 private var nextTick = INTERVAL - private val enoughEnergy get() = energy.batteryLevel >= ITEM_STORAGE.energyPerOperation - override val targetCapability: Capability get() = ForgeCapabilities.ITEM_HANDLER @@ -141,23 +137,39 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A return ItemStack.EMPTY } + private fun acceptItem(stack: ItemStack, simulate: Boolean): ItemStack { + val wrapped = ItemStorageStack(stack) + + val view = cell.graph.getVirtualComponent(StorageStack.ITEMS) + val inserted = view.insertStack(wrapped, true) + if (inserted == wrapped) return stack + + val required = StorageStack.ITEMS.energyPerInsert(wrapped) + + if (energy.extractEnergy(required, true) == required) { + if (!simulate) { + val inserted2 = view.insertStack(wrapped, false) + + if (inserted == inserted2) { + energy.extractEnergy(required, false) + return inserted.toItemStack() + } else { + energy.extractEnergy(StorageStack.ITEMS.energyPerInsert(inserted2), false) + return inserted2.toItemStack() + } + } + + return inserted.toItemStack() + } + + return stack + } + override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack { if (redstoneControl.isBlockedByRedstone || !filter.match(stack)) return stack - val view = cell.graph.getVirtualComponent(ITEM_STORAGE) - val maxMove = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, stack.count.toBigInteger(), true) - - if (maxMove == BigInteger.ZERO) - return stack - - val leftover = view.insertStack(ItemStackWrapper(stack).also { it.count = maxMove }, simulate) - - if (simulate) - return leftover.stack - - energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, maxMove - leftover.count, false) - return leftover.stack + return acceptItem(stack, simulate) } override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack { @@ -184,28 +196,20 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A val target = target.get().orNull() - if (nextTick <= 0 && target != null && enoughEnergy) { - val items = cell.graph.getVirtualComponent(ITEM_STORAGE) - + if (nextTick <= 0 && target != null) { if (lastSlot >= target.slots) { lastSlot = 0 } - val maxMove = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, MAX_MOVE_PER_OPERATION, true) - var extracted = target.extractItem(lastSlot, maxMove, true) + val extracted = target.extractItem(lastSlot, MAX_MOVE_PER_OPERATION, true) if (extracted.isEmpty || !filter.match(extracted)) { lastSlot++ } else { - val leftOver = items.insertStack(ItemStackWrapper(extracted), true) - - if (leftOver.count.toInt() != extracted.count) { - extracted = target.extractItem(lastSlot, extracted.count - leftOver.count.toInt(), false) - energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, extracted.count, false) - items.insertStack(ItemStackWrapper(extracted), false) - } else { - nextTick += INTERVAL * 4 - } + val accepted = acceptItem(extracted, true) + val extracted2 = target.extractItem(lastSlot, accepted.count, false) + acceptItem(extracted2, false) + nextTick += INTERVAL * 4 } } @@ -223,7 +227,7 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractStorageImportExport(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState), - IStorageEventConsumer { + IStorageEventConsumer { override val defaultDisplayName: Component get() = MACHINE_NAME @@ -233,36 +237,36 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : private val relevantTuples = HashSet() - override val storageType: StorageStackType - get() = ITEM_STORAGE + override val storageType: StorageStack.Type + get() = StorageStack.ITEMS init { cell.addStorageComponent(this) } - override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider) { - if (!filter.match(stack.item)) { + override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider) { + if (!filter.match(stack.toItemStack())) { return } relevantTuples.add(id) } - override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) { + override fun onStackChanged(stack: ItemStorageStack, id: UUID) { // no-op } - override fun removeStack(stack: ItemStackWrapper, id: UUID) { + override fun onStackRemoved(id: UUID) { relevantTuples.remove(id) } override val filter = ItemFilter(MAX_FILTERS) { _, _, _ -> relevantTuples.clear() - val component = cell.graph.getVirtualComponent(ITEM_STORAGE) + val component = cell.graph.getVirtualComponent(StorageStack.ITEMS) for (tuple in component.stacks) { - addStack(tuple, component) + onStackAdded(tuple, component) } lastSlot = 0 @@ -272,14 +276,13 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : private var lastSlot = 0 private var nextTick = INTERVAL - private val enoughEnergy get() = energy.batteryLevel >= ITEM_STORAGE.energyPerOperation override val targetCapability: Capability get() = ForgeCapabilities.ITEM_HANDLER - private val exportStacks: Stream> + private val exportStacks: Stream> get() { - val view = cell.graph.getVirtualComponent(ITEM_STORAGE) + val view = cell.graph.getVirtualComponent(StorageStack.ITEMS) return relevantTuples.stream().map { it to view[it] } } @@ -295,53 +298,43 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : val target = target.get().orNull() - if (nextTick <= 0 && target != null && enoughEnergy) { - val items = cell.graph.getVirtualComponent(ITEM_STORAGE) + if (nextTick <= 0 && target != null) { + val items = cell.graph.getVirtualComponent(StorageStack.ITEMS) if (lastSlot >= target.slots) { lastSlot = 0 } - var hit = false + val any = exportStacks.anyMatch { + val (id, stack) = it - for (stack in exportStacks) { - if (!target.isItemValid(lastSlot, stack.second.item)) { - continue - } + if (!target.isItemValid(lastSlot, stack.toItemStack())) return@anyMatch false - val exportAmountA = items.extractStack( - stack.first, stack.second.count.coerceAtMost( - MAX_MOVE_PER_OPERATION - ), true - ).count + val extracted = items.extractStack(id, stack.count.coerceAtMost(MAX_MOVE_PER_OPERATION), true) + if (extracted.isEmpty) return@anyMatch false - if (exportAmountA == BigInteger.ZERO) { - continue - } + val required = StorageStack.ITEMS.energyPerOperation(extracted) + if (energy.extractEnergy(required, true) != required) return@anyMatch false - var exportAmount = - energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmountA, true).toIntSafe() + val toInsert = extracted.toItemStack() + val leftover = target.insertItem(lastSlot, toInsert, true) - if (exportAmount == 0) { - break - } + if (leftover.count != toInsert.count) { + val extracted2 = items.extractStack(id, (toInsert.count - leftover.count).toBigInteger(), false) + energy.extractEnergy(StorageStack.ITEMS.energyPerOperation(extracted2), false) + val leftover2 = target.insertItem(lastSlot, extracted2.toItemStack(), false) - val leftover = target.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, true) + if (leftover2.isNotEmpty) { + items.insertStack(ItemStorageStack.unsafe(leftover2), false) + } - if (leftover.count != exportAmount) { - hit = true - exportAmount = items.extractStack( - stack.first, - (exportAmount - leftover.count).toBigInteger(), - false - ).count.toInt() - target.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, false) - energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmount, false) - break + true + } else { + false } } - if (!hit) { + if (!any) { lastSlot++ } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/API.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/API.kt index 2c31379ba..689056cb4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/API.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/API.kt @@ -4,27 +4,27 @@ import net.minecraft.nbt.CompoundTag import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.storage.IStorageComponent -import ru.dbotthepony.mc.otm.storage.IStorageStack import ru.dbotthepony.mc.otm.storage.IStorageTuple -import ru.dbotthepony.mc.otm.storage.ItemStackWrapper +import ru.dbotthepony.mc.otm.storage.ItemStorageStack +import ru.dbotthepony.mc.otm.storage.StorageStack import java.math.BigInteger import java.util.* -interface IItemMatteryDrive : IMatteryDrive { +interface IItemMatteryDrive : IMatteryDrive { /** * @param item * @return all items belonging to passed class */ - fun findItems(item: Item): Collection> + fun findItems(item: Item): Collection> /** * @param stack * @return [ItemStack] that match specified [stack] (item type, nbt tag) */ - fun findItems(stack: ItemStack): IStorageTuple? + fun findItems(stack: ItemStack): IStorageTuple? } -interface IMatteryDrive : IStorageComponent { +interface IMatteryDrive> : IStorageComponent { val uuid: UUID var isDirty: Boolean 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 fec2c6b25..f8e72f168 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 @@ -1,5 +1,7 @@ package ru.dbotthepony.mc.otm.capability.drive +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectArraySet import kotlin.jvm.JvmOverloads import java.util.UUID @@ -18,13 +20,13 @@ import java.math.BigInteger import java.util.ArrayList import java.util.stream.Stream -abstract class AbstractMatteryDrive @JvmOverloads constructor( +abstract class AbstractMatteryDrive> @JvmOverloads constructor( override var driveCapacity: BigInteger, override val uuid: UUID = UUID.randomUUID(), var maxDifferentStacks: Int = 0xFFFF ) : IMatteryDrive { - protected val tuples = HashMap>() - protected val tuplesByID: MutableMap> = HashMap() + protected val stack2tuples = Object2ObjectOpenCustomHashMap>(StorageStack.Companion) + protected val id2tuples = Object2ObjectOpenHashMap>() override var isDirty = false set(value) { @@ -41,30 +43,25 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor override var storedCount: BigInteger = BigInteger.ZERO protected set - @Suppress("unchecked_cast") override fun insertStack(stack: T, simulate: Boolean): T { val maxInsert = driveCapacity.minus(storedCount).coerceAtMost(stack.count) if (maxInsert <= BigInteger.ZERO) return stack - val key = stack.key() - val tuple = tuples[key] + val tuple = stack2tuples[stack] if (tuple != null) { if (!simulate) { - val oldCount = tuple.stack.count - tuple.stack.grow(maxInsert) + tuple.grow(maxInsert) storedCount += maxInsert for (listener in listeners) { - listener.changeStack(tuple.stack, tuple.id, oldCount) + listener.onStackChanged(tuple.stack, tuple.id) } isDirty = true } - val copy = stack.copy() as T - copy.shrink(maxInsert) - return copy + return stack.shrink(maxInsert) } if (storedDifferentStacks >= maxDifferentStacks) { @@ -75,28 +72,22 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor storedDifferentStacks++ storedCount = storedCount.plus(maxInsert) - val copy = stack.copy() as T - copy.count = maxInsert - - val state = StorageTuple(UUID.randomUUID(), copy) - tuples[key] = state - tuplesByID[state.id] = state + val state = IStorageTuple.M(UUID.randomUUID(), stack.copy(maxInsert)) + stack2tuples[stack] = state + id2tuples[state.id] = state for (listener in listeners) { - listener.addStack(state.stack, state.id, this) + listener.onStackAdded(state.stack, state.id, this) } isDirty = true } - val copy = stack.copy() as T - copy.shrink(maxInsert) - return copy + return stack.shrink(maxInsert) } - @Suppress("unchecked_cast") override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { - val get = tuplesByID[id] ?: return storageType.empty + val get = id2tuples[id] ?: return storageType.empty @Suppress("NAME_SHADOWING") var amount = amount @@ -109,28 +100,25 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor if (amount <= BigInteger.ZERO) return storageType.empty - val copy = get.stack.copy() as T - copy.count = amount + val copy = get.stack.copy(amount) if (!simulate) { if (amount == get.stack.count) { - val key = get.stack.key() - tuples.remove(key) ?: throw IllegalStateException("Can't find storage key for ${get.stack}") + stack2tuples.remove(get.stack) ?: throw IllegalStateException("Can't find storage key for ${get.stack}") storedDifferentStacks-- for (listener in listeners) { - listener.removeStack(get.stack, get.id) + listener.onStackRemoved(get.id) } } storedCount -= amount - val oldCount = get.stack.count - get.stack.shrink(amount) + get.shrink(amount) if (get.stack.count.isPositive) { for (listener in listeners) { - listener.changeStack(get.stack, get.id, oldCount) + listener.onStackChanged(get.stack, get.id) } } @@ -140,7 +128,7 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor return copy } - protected abstract fun serializeStack(item: IStorageTuple): CompoundTag? + protected abstract fun serializeStack(item: T): CompoundTag? protected abstract fun deserializeStack(tag: CompoundTag): T? override fun serializeNBT(): CompoundTag { @@ -152,8 +140,8 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor val list = ListTag() compound["items"] = list - for (stack in tuples.values) { - val serialized = serializeStack(stack) + for (tuple in stack2tuples.values) { + val serialized = serializeStack(tuple.stack) if (serialized != null) { list.add(serialized) @@ -165,13 +153,13 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor override fun deserializeNBT(nbt: CompoundTag) { for (listener in listeners) { - for (get in tuples.values) { - listener.removeStack(get.stack, get.id) + for (get in stack2tuples.values) { + listener.onStackRemoved(get.id) } } - tuples.clear() - tuplesByID.clear() + stack2tuples.clear() + id2tuples.clear() storedCount = BigInteger.ZERO storedDifferentStacks = 0 // nextID = 0L @@ -183,23 +171,23 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor if (entry is CompoundTag) { val stack = deserializeStack(entry) - if (stack != null) { + if (stack != null && stack.isNotEmpty) { storedCount += stack.count storedDifferentStacks++ - val tuple = StorageTuple(UUID.randomUUID(), stack) - tuples[tuple.stack.key()] = tuple - tuplesByID[tuple.id] = tuple + val tuple = IStorageTuple.M(UUID.randomUUID(), stack) + stack2tuples[tuple.stack] = tuple + id2tuples[tuple.id] = tuple } } } } override fun get(id: UUID): T { - return tuplesByID[id]?.stack ?: storageType.empty + return id2tuples[id]?.stack ?: storageType.empty } override val stacks: Stream> get() { - return ArrayList>(tuples.size).also { it.addAll(tuples.values) }.stream() + return ArrayList>(stack2tuples.size).also { it.addAll(stack2tuples.values) }.stream() } protected val listeners = ObjectArraySet>() @@ -211,8 +199,4 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor override fun removeListener(listener: IStorageEventConsumer): Boolean { return listeners.remove(listener) } - - companion object { - private val LOGGER = LogManager.getLogger() - } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/ItemMatteryDrive.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/ItemMatteryDrive.kt index 4ff1b972b..a5fb59159 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/ItemMatteryDrive.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/ItemMatteryDrive.kt @@ -1,69 +1,51 @@ package ru.dbotthepony.mc.otm.capability.drive import net.minecraft.nbt.CompoundTag -import net.minecraft.resources.ResourceLocation import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack -import net.minecraft.world.item.Items -import net.minecraftforge.registries.ForgeRegistries -import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.core.math.BigInteger import ru.dbotthepony.mc.otm.core.math.serializeNBT import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.storage.IStorageTuple -import ru.dbotthepony.mc.otm.storage.ItemStackWrapper -import ru.dbotthepony.mc.otm.storage.StorageStackType -import ru.dbotthepony.mc.otm.storage.key +import ru.dbotthepony.mc.otm.storage.ItemStorageStack +import ru.dbotthepony.mc.otm.storage.StorageStack import java.math.BigInteger import java.util.* -import kotlin.collections.ArrayList -class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDrive { - constructor(capacity: BigInteger, max_different_stacks: Int) : super(capacity, maxDifferentStacks = max_different_stacks) - constructor(capacity: BigInteger, uuid: UUID, max_different_stacks: Int) : super(capacity, uuid, max_different_stacks) +class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDrive { + constructor(capacity: BigInteger, maxDifferentStacks: Int) : super(capacity, maxDifferentStacks = maxDifferentStacks) + constructor(capacity: BigInteger, uuid: UUID, maxDifferentStacks: Int) : super(capacity, uuid, maxDifferentStacks) constructor(capacity: BigInteger, uuid: UUID) : super(capacity, uuid) constructor(capacity: BigInteger) : super(capacity) - override val storageType: StorageStackType = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() + override val storageType: StorageStack.Type = StorageStack.ITEMS fun insertStack(item: ItemStack, simulate: Boolean): ItemStack { - return insertStack(ItemStackWrapper(item), simulate).stack + return insertStack(ItemStorageStack(item), simulate).toItemStack() } - override fun serializeStack(item: IStorageTuple): CompoundTag? { + override fun serializeStack(item: ItemStorageStack): CompoundTag { val tag = CompoundTag() - val location = item.stack.registryName.toString() - - tag["item"] = location - tag["count"] = item.stack.count.serializeNBT() - - val itag = item.stack.item.tag - - if (itag != null) { - tag["data"] = itag - } - + tag["item"] = item.toItemStack(1).serializeNBT() + tag["count"] = item.count.serializeNBT() return tag } - override fun deserializeStack(tag: CompoundTag): ItemStackWrapper? { - val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(tag.getString("item"))) - - if (item != null && item !== Items.AIR) { + override fun deserializeStack(tag: CompoundTag): ItemStorageStack? { + if ("item" in tag && "count" in tag) { + val item = tag["item"] as? CompoundTag ?: return null val count = BigInteger(tag["count"]) - val itemstack = ItemStack(item, 1) - itemstack.tag = tag["data"] as? CompoundTag - return ItemStackWrapper(itemstack, copy = false).also { it.count = count } + return ItemStorageStack.unsafe(ItemStack.of(item), count) } return null } - override fun findItems(item: Item): List> { - val list = ArrayList>() + override fun findItems(item: Item): List> { + val list = ArrayList>() - for ((key, value) in tuples.entries) { - if (key.item.item === item) { + for ((key, value) in stack2tuples.entries) { + if (key.item === item) { list.add(value) } } @@ -71,8 +53,8 @@ class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDri return list } - override fun findItems(stack: ItemStack): IStorageTuple? { - return tuples[ItemStackWrapper(stack).key()] + override fun findItems(stack: ItemStack): IStorageTuple? { + return stack2tuples[ItemStorageStack.unsafe(stack)] } companion object { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/DriveViewerScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/DriveViewerScreen.kt index f08354adb..29ceb9604 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/DriveViewerScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/DriveViewerScreen.kt @@ -68,7 +68,7 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp object : AbstractSlotPanel(this@DriveViewerScreen, grid, 0f, 0f) { override val itemStack: ItemStack get() { val index = i + scrollBar.scroll * GRID_WIDTH - return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY + return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY } override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/ItemMonitorScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/ItemMonitorScreen.kt index f139c43da..4215dc9cf 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/ItemMonitorScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/ItemMonitorScreen.kt @@ -32,7 +32,7 @@ import ru.dbotthepony.mc.otm.core.math.integerDivisionDown import ru.dbotthepony.mc.otm.core.util.formatReadableNumber import ru.dbotthepony.mc.otm.core.util.formatSiComponent import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu -import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE +import ru.dbotthepony.mc.otm.storage.StorageStack import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak @MouseTweaksDisableWheelTweak @@ -73,7 +73,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp private val index get() = i + viewScrollBar.scroll * ITEM_GRID_WIDTH override val itemStack: ItemStack get() { - return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY + return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY } override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { @@ -88,10 +88,10 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { renderSlotBackground(graphics, mouseX, mouseY, partialTick) - val itemstack = menu.networkedItemView.sortedView.getOrNull(index)?.stack ?: ITEM_STORAGE.empty + val itemstack = menu.networkedItemView.sortedView.getOrNull(index)?.stack ?: StorageStack.ITEMS.empty val stack = graphics.pose() - renderRegular(graphics, itemstack.stack, "") + renderRegular(graphics, itemstack.toItemStack(), "") if (!itemstack.isEmpty) { stack.pushPose() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/mekanism/QIO.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/mekanism/QIO.kt index b63a601e7..d05f2bf3a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/mekanism/QIO.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/mekanism/QIO.kt @@ -33,43 +33,43 @@ private val QIO_LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "item_s private class QIOTuple( val mekanismItem: HashedItem, - override val stack: ItemStackWrapper, + override var stack: ItemStorageStack, override val id: UUID, var mark: Long -) : IStorageTuple +) : IStorageTuple.IMutable -private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent { +private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent { private var mark = 0L - override val storageType: StorageStackType - get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() + override val storageType: StorageStack.Type + get() = StorageStack.ITEMS private val index = HashMap() private val tracked = HashMap() - private val listeners = ArrayList>() + private val listeners = ArrayList>() - override fun get(id: UUID): ItemStackWrapper { - return index[id]?.stack ?: ItemStackWrapper.EMPTY + override fun get(id: UUID): ItemStorageStack { + return index[id]?.stack ?: ItemStorageStack.EMPTY } - override val stacks: Stream> get() { - return ArrayList>(index.size).also { it.addAll(index.values) }.stream() + override val stacks: Stream> get() { + return ArrayList>(index.size).also { it.addAll(index.values) }.stream() } - override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper { + override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack { // Because there is no simulate method on QIO array, we have to simulate it by ourselves. - val hash = HashedItem.create(stack.item) + val hash = HashedItem.create(stack.toItemStack()) if (!simulate) { //val used = TransporterManager.getToUse(stack.stack, parent.addItem(stack.stack)) - val used = parent.addItem(stack.stack) + val used = parent.addItem(stack.toItemStack()) if (used.count == stack.count.toInt()) { return stack } scan(hash) - return ItemStackWrapper(used) + return ItemStorageStack(used) } if (parent.totalItemCount >= parent.totalItemCountCapacity) { @@ -80,33 +80,31 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent copy.count) { // expecting stack to be still present in QIO storage grid @@ -118,7 +116,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent): Boolean { + override fun addListener(listener: IStorageEventConsumer): Boolean { if (!listeners.contains(listener)) { listeners.add(listener) return true @@ -137,30 +135,29 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent): Boolean { + override fun removeListener(listener: IStorageEventConsumer): Boolean { return listeners.remove(listener) } - private fun scan(at: HashedItem, value: QIOItemTypeData = parent.itemDataMap[at] ?: throw IllegalArgumentException("$parent does not have item $at (${at.stack})")) { + private fun scan(at: HashedItem, value: QIOItemTypeData = parent.itemDataMap[at] ?: throw IllegalArgumentException("$parent does not have item $at (${at.internalStack})")) { val local = tracked[at] if (local != null) { local.mark = mark if (local.stack.count.toLong() != value.count) { - val oldCount = local.stack.count - local.stack.count = value.count.toBigInteger() + local.stack = local.stack.copy(value.count.toBigInteger()) for (listener in listeners) { - listener.changeStack(local, oldCount) + listener.onStackChanged(local) } } } else { - val tuple = QIOTuple(at, ItemStackWrapper(at.stack).also { it.count = value.count.toBigInteger() }, UUID.randomUUID(), mark) + val tuple = QIOTuple(at, ItemStorageStack(at.internalStack, value.count.toBigInteger()), UUID.randomUUID(), mark) index[tuple.id] = tuple for (listener in listeners) { - listener.addStack(tuple, this) + listener.onStackAdded(tuple, this) } tracked[at] = tuple @@ -192,7 +189,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent(private val parent: Iterator, private val map } } +fun concatIterators(): MutableIterator { + return ObjectIterators.EMPTY_ITERATOR as MutableIterator +} + +fun concatIterators(a: Iterator): MutableIterator { + return a as MutableIterator +} + +fun concatIterators(vararg iterators: Iterator): MutableIterator { + return iterators.iterator().flatMap { it } +} + /** * Limits amount of values returned by [parent] iterator to return at most [limit] values * diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageGraph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageGraph.kt index 11ce5f853..ab8221d28 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageGraph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageGraph.kt @@ -8,32 +8,22 @@ import ru.dbotthepony.mc.otm.storage.* import java.util.LinkedList class StorageGraph : GraphNodeList() { - private val virtualComponents = Object2ObjectArrayMap, VirtualComponent<*>>() + private val virtualComponents = Object2ObjectArrayMap, VirtualComponent<*>>() val powerDemandingNodes = LinkedList() - /** - * Returns a [VirtualComponent] representing [type] storage - */ - fun getVirtualComponent(type: StorageStackType): VirtualComponent { + fun > getVirtualComponent(type: StorageStack.Type): VirtualComponent { return virtualComponents.computeIfAbsent(type, Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent } - /** - * Returns a [VirtualComponent] representing [type] storage - */ - fun getVirtualComponent(type: Class): VirtualComponent { - return virtualComponents.computeIfAbsent(StorageRegistry.get(type), Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent - } - - fun add(storage: IStorage) { + fun > add(storage: IStorage) { getVirtualComponent(storage.storageType).add(storage) } - fun remove(storage: IStorage) { + fun > remove(storage: IStorage) { getVirtualComponent(storage.storageType).remove(storage) } - fun contains(storage: IStorage): Boolean { + fun > contains(storage: IStorage): Boolean { val virtual = virtualComponents[storage.storageType] ?: return false return (virtual as VirtualComponent).contains(storage) } @@ -45,5 +35,4 @@ class StorageGraph : GraphNodeList() { override fun onNodeRemoved(node: StorageNode) { node.removeComponents(this) } - } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNode.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNode.kt index b06493c0c..bc0f21f30 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNode.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNode.kt @@ -5,9 +5,12 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.graph.GraphNode import ru.dbotthepony.mc.otm.storage.IStorage -import ru.dbotthepony.mc.otm.storage.IStorageStack -import ru.dbotthepony.mc.otm.storage.StorageStackType +import ru.dbotthepony.mc.otm.storage.IVirtualStorageComponent +import ru.dbotthepony.mc.otm.storage.StorageStack +import ru.dbotthepony.mc.otm.storage.VirtualComponent +import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent import java.util.* +import java.util.function.Supplier open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode(::StorageGraph) { protected val components = ArrayList>() @@ -72,71 +75,31 @@ open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null } @Suppress("unchecked_cast") - fun > computeIfAbsent(identity: StorageStackType, provider: (StorageStackType) -> U): U { - for (component in components) { - if (component.storageType === identity) { - return component as U - } - } - - val factory = provider(identity) - addStorageComponent(factory) - return factory + fun , U : IStorage> computeIfAbsent(type: StorageStack.Type, provider: (StorageStack.Type) -> U): U { + return components.firstOrNull { it.storageType == type } as U? ?: provider(type).also { addStorageComponent(it) } } fun addStorageComponent(component: IStorage<*>) { - for (component1 in components) { - if (component === component1 || component1.storageType === component.storageType) { - return - } + if (!components.any { component === it || it.storageType === component.storageType }) { + components.add(component) + + if (isValid && !manualAttaching) + graph.add(component) } - - components.add(component) - - if (isValid && !manualAttaching) - graph.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 (isValid) - graph.remove(self) + val indexOf = components.indexOfFirst { component === it || it.storageType === component.storageType } + if (indexOf == -1) return + val self = components.removeAt(indexOf) + if (isValid) graph.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 (isValid && !manualAttaching) - graph.remove(self) + fun removeStorageComponent(component: StorageStack.Type<*>) { + val indexOf = components.indexOfFirst { it.storageType === component } + if (indexOf == -1) return + val self = components.removeAt(indexOf) + if (isValid) graph.remove(self) } override fun invalidate() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt index 298cf1383..eb7598029 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt @@ -97,7 +97,8 @@ import ru.dbotthepony.mc.otm.network.GenericNetworkChannel import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.registry.RegistryDelegate import ru.dbotthepony.mc.otm.secondTime -import ru.dbotthepony.mc.otm.storage.ItemStackWrapper +import ru.dbotthepony.mc.otm.storage.ItemStorageStack +import ru.dbotthepony.mc.otm.storage.StorageStack import java.io.DataInputStream import java.io.DataOutputStream import java.io.File @@ -1202,9 +1203,9 @@ object MatterManager { val drive = value.getCapability(MatteryCapability.DRIVE).orNull() - if (drive != null && drive.storageType == OverdriveThatMatters.INSTANCE.ITEM_STORAGE()) { - (drive as IMatteryDrive).stacks - .map { it.stack.stack } + if (drive != null && drive.storageType == StorageStack.ITEMS) { + (drive as IMatteryDrive).stacks + .map { it.stack.toItemStack() } .filter { !it.isEmpty } .map { get(it, level + 1, true) } .reduce(::reduce) 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 251540c89..ec1205e43 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 @@ -1,7 +1,6 @@ package ru.dbotthepony.mc.otm.menu.data import com.mojang.blaze3d.platform.InputConstants -import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import net.minecraft.client.Minecraft import net.minecraft.client.gui.screens.Screen @@ -75,11 +74,11 @@ object ClearItemViewPacket : MatteryPacket { } } -class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStackWrapper) : MatteryPacket { +class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStorageStack) : MatteryPacket { override fun write(buff: FriendlyByteBuf) { buff.writeInt(containerId) buff.writeInt(id) - buff.writeBigItem(stack) + stack.write(buff) } override fun play(context: Supplier) { @@ -127,7 +126,7 @@ class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStackWrap fun read(buffer: FriendlyByteBuf): StackAddPacket { val containerId = buffer.readInt() val id = buffer.readInt() - val item = buffer.readBigItem() + val item = StorageStack.ITEMS.read(buffer) return StackAddPacket(containerId, id, item) } } @@ -151,7 +150,7 @@ class StackChangePacket(val id: Int, val stackID: Int, val newCount: BigInteger) val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id") val state = view.localState[stackID] ?: throw IllegalStateException("No such stack with id $stackID in $view") - state.stack.count = newCount + state.stack = state.stack.copy(newCount) view.resort() } } @@ -199,11 +198,11 @@ class StackRemovePacket(val id: Int, val stackID: Int) : MatteryPacket { /** * Creates a virtual, slotless container for Player to interaction with. */ -open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer { - data class NetworkedItem constructor(val id: Int, val stack: ItemStackWrapper, val upstreamId: UUID? = null) +open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer { + data class NetworkedItem(val id: Int, var stack: ItemStorageStack, val upstreamId: UUID? = null) - override val storageType: StorageStackType - get() = ITEM_STORAGE + override val storageType: StorageStack.Type + get() = StorageStack.ITEMS // this (how client see and interact with) val localState = Int2ObjectOpenHashMap() @@ -233,7 +232,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: fun resort() { sortedView.sortWith { a, b -> - return@sortWith sorter.compare(a.stack.item, b.stack.item) + return@sortWith sorter.compare(a.stack.toItemStack(), b.stack.toItemStack()) } } @@ -243,7 +242,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: operator fun get(id: Int): NetworkedItem? = localState[id] - var provider: IStorageComponent? = null + var provider: IStorageComponent? = null private set fun mouseClick(index: Int, mouseButton: Int) { @@ -264,33 +263,33 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: ) } - fun setComponent(provider: IStorageComponent?) { + fun setComponent(provider: IStorageComponent?) { if (provider === this.provider) return - this.provider?.removeListenerAuto(this) + this.provider?.removeListenerAndNotify(this) this.provider = provider - provider?.addListenerAuto(this) + provider?.addListenerAndNotify(this) } fun removed() { - provider?.removeListenerAuto(this) + provider?.removeListenerAndNotify(this) } val itemCount get() = localState.values.size - override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider) { + override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider) { check(!upstreamState.containsKey(id)) { "Already tracking ItemStack with upstream id $id!" } - val state = NetworkedItem(nextItemID++, stack.copy(), id) + val state = NetworkedItem(nextItemID++, stack, id) this.localState[state.id] = state upstreamState[id] = state network { StackAddPacket(menu.containerId, state.id, state.stack) } } - override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) { + override fun onStackChanged(stack: ItemStorageStack, id: UUID) { val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!") - get.stack.count = stack.count + get.stack = stack network { StackChangePacket(menu.containerId, get.id, stack.count) } } @@ -300,7 +299,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: } } - override fun removeStack(stack: ItemStackWrapper, id: UUID) { + override fun onStackRemoved(id: UUID) { val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!") upstreamState.remove(id) localState.remove(get.id) @@ -342,7 +341,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: if (stackId < 0 || !ply.abilities.instabuild) return val state = get(stackId) ?: return - val copy = state.stack.stack.also { it.count = it.maxStackSize } + val copy = state.stack.toItemStack().also { it.count = it.maxStackSize } ply.containerMenu.carried = copy MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(ply.containerMenu.carried)) @@ -353,17 +352,18 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: if (click == ClickType.QUICK_MOVE && stackId > -1) { val state = get(stackId) ?: return + val stack = state.stack.toItemStack() val amount = if (action == ClickAction.PRIMARY) - state.stack.item.maxStackSize + stack.maxStackSize else - 1.coerceAtLeast(state.stack.item.maxStackSize / 2) + 1.coerceAtLeast(stack.maxStackSize / 2) val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), true) if (!extracted.isEmpty) { - val remaining = menu.quickMoveToInventory(extracted.stack, false) + val remaining = menu.quickMoveToInventory(extracted.toItemStack(), false) if (remaining.count != extracted.count.toInt()) { provider.extractStack(state.upstreamId, (extracted.count.toInt() - remaining.count).toBigInteger(), false) @@ -380,8 +380,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: val amount = carried.count if (amount == carried.count) { - val stack = provider.insertStack(ItemStackWrapper(menu.carried), false).stack - menu.carried = stack + menu.carried = provider.insertStack(ItemStorageStack(menu.carried), false).toItemStack() MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) menu.setRemoteCarried(menu.carried.copy()) } @@ -389,7 +388,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: val copy = menu.carried.copy() copy.count = 1 - if (provider.insertStack(ItemStackWrapper(copy), false).isEmpty) { + if (provider.insertStack(ItemStorageStack(copy), false).isEmpty) { menu.carried.shrink(1) MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) menu.setRemoteCarried(menu.carried.copy()) @@ -397,15 +396,15 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: } } else if (stackId > -1) { val state = get(stackId) ?: return + val stack = state.stack.toItemStack() val amount = if (action == ClickAction.PRIMARY) - state.stack.item.maxStackSize + stack.maxStackSize else - (state.stack.stack.count / 2).coerceAtMost(state.stack.item.maxStackSize / 2).coerceAtLeast(1) + (stack.count / 2).coerceAtMost(stack.maxStackSize / 2).coerceAtLeast(1) - val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false) - menu.carried = extracted.stack + menu.carried = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false).toItemStack() MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) menu.setRemoteCarried(menu.carried.copy()) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt index bd39f67dd..89e4e664c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt @@ -19,9 +19,9 @@ import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.registry.MMenus -import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE -import ru.dbotthepony.mc.otm.storage.ItemStackWrapper -import ru.dbotthepony.mc.otm.storage.PoweredVirtualComponent +import ru.dbotthepony.mc.otm.storage.ItemStorageStack +import ru.dbotthepony.mc.otm.storage.StorageStack +import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent class DriveViewerMenu @JvmOverloads constructor( containerID: Int, @@ -33,8 +33,8 @@ class DriveViewerMenu @JvmOverloads constructor( override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null) val driveSlot: MatterySlot - private val powered: PoweredVirtualComponent? - private var lastDrive: IMatteryDrive? = null + private val powered: PoweredVirtualComponent? + private var lastDrive: IMatteryDrive? = null init { val container = tile?.container ?: SimpleContainer(1) @@ -47,8 +47,8 @@ class DriveViewerMenu @JvmOverloads constructor( if (tile != null) { powered = PoweredVirtualComponent( - ItemStackWrapper::class.java, - tile.getCapability(MatteryCapability.ENERGY).resolve().get() + StorageStack.ITEMS, + tile.getCapability(MatteryCapability.ENERGY).resolve()::get ) this.networkedItemView.setComponent(powered) @@ -83,8 +83,8 @@ class DriveViewerMenu @JvmOverloads constructor( if (!itemStack.isEmpty) { itemStack.getCapability(MatteryCapability.DRIVE).ifPresentK { - if (it.storageType === ITEM_STORAGE) { - lastDrive = it as IMatteryDrive + if (it.storageType == StorageStack.ITEMS) { + lastDrive = it as IMatteryDrive } } } @@ -146,7 +146,7 @@ class DriveViewerMenu @JvmOverloads constructor( if (lastDrive == null || powered == null) return ItemStack.EMPTY - val remaining = powered.insertStack(ItemStackWrapper(item), false) + val remaining = powered.insertStack(ItemStorageStack(item), false) if (remaining.count.toInt() == item.count) return ItemStack.EMPTY diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt index c5941db23..d7714883c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt @@ -139,7 +139,7 @@ class ItemMonitorMenu @JvmOverloads constructor( return ItemStack.EMPTY } - val leftover = networkedItemView.provider?.insertStack(ItemStackWrapper(slot.item), false)?.stack ?: slot.item + val leftover = networkedItemView.provider?.insertStack(ItemStorageStack(slot.item), false)?.toItemStack() ?: slot.item if (leftover.count == slot.item.count) { return ItemStack.EMPTY @@ -163,7 +163,7 @@ class ItemMonitorMenu @JvmOverloads constructor( return item } - remainder = networkedItemView.provider?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder + remainder = networkedItemView.provider?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder slots[slotIndex].set(remainder) if (remainder.isEmpty) { @@ -229,8 +229,8 @@ class ItemMonitorMenu @JvmOverloads constructor( try { when (settings.resultTarget) { ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM -> { - val wrapper = ItemStackWrapper(item) - var remaining = tile.poweredView?.insertStack(wrapper, true)?.stack ?: return ItemStack.EMPTY + val wrapper = ItemStorageStack(item) + var remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY if (remaining.isEmpty) { tile.poweredView!!.insertStack(wrapper, false) @@ -241,7 +241,7 @@ class ItemMonitorMenu @JvmOverloads constructor( remaining = moveItemStackToSlots(remaining, playerInventorySlots, simulate = true) if (remaining.isEmpty) { - remaining = tile.poweredView!!.insertStack(wrapper, false).stack + remaining = tile.poweredView!!.insertStack(wrapper, false).toItemStack() moveItemStackToSlots(remaining, playerInventorySlots, simulate = false) craftingResult.remove(item.count) return item @@ -259,12 +259,12 @@ class ItemMonitorMenu @JvmOverloads constructor( return item } - val wrapper = ItemStackWrapper(remaining) - remaining = tile.poweredView?.insertStack(wrapper, true)?.stack ?: return ItemStack.EMPTY + val wrapper = ItemStorageStack(remaining) + remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY if (remaining.isEmpty) { moveItemStackToSlots(item, playerInventorySlots, simulate = false) - tile.poweredView!!.insertStack(wrapper, false).stack + tile.poweredView!!.insertStack(wrapper, false) craftingResult.remove(item.count) return item } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt index fd51b3838..2101c8ab0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt @@ -43,6 +43,7 @@ import ru.dbotthepony.mc.otm.matter.IMatterFunction import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock import ru.dbotthepony.mc.otm.registry.objects.StripedColoredDecorativeBlock +import ru.dbotthepony.mc.otm.storage.StorageStack import ru.dbotthepony.mc.otm.triggers.AndroidBatteryTrigger import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger @@ -274,6 +275,7 @@ object MRegistry { MItemFunctionTypes.register(bus) MLootItemConditions.register(bus) MRecipes.register(bus) + StorageStack.register(bus) if (FMLEnvironment.dist == Dist.CLIENT) { MBlockColors.register(bus) 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 6081246ed..8c5b0953a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt @@ -4,69 +4,20 @@ import java.math.BigInteger import java.util.* import java.util.stream.Stream -/** - * Represents a stack in storage system. - * - * In general there are next rules: - * 1. Once created, stack is immutable, except for it's [count], therefore [copy] is expected to do - * "shallow" copies (not performance taxing). - * 2. Due to condition above, and due to sublying storage most of time being - * mutable, it is expected you do defensive copies. Examples of when you should do - * them are described on related interfaces. - * 3. For storing stacks as [Map] keys, a stack with size of 1 is utilized (requiring [equals] and [hashCode] to return meaningful results, see [IStorageStack.key]). - * 4. For equality (INCLUDING count), regular [equals] is utilized. - */ -interface IStorageStack { - fun copy(): IStorageStack - - /** - * Size of this storage stack - * - * This is overriden in subclasses - */ - var count: BigInteger - val isEmpty: Boolean - - /** - * @return max stack size for this stack object, - * null if unlimited (default) - */ - val maxStackSize: BigInteger? get() = null - - /** - * Increase [count] by [amount] - */ - fun grow(amount: BigInteger) { - count += amount - } - - /** - * Decrease [count] by [amount] - */ - fun shrink(amount: BigInteger) { - count -= amount - } -} - -@Suppress("UNCHECKED_CAST") -fun T.key(): T { - return copy().also { it.count = BigInteger.ONE } as T -} - /** * Storage system root, along IStorageStack interface */ -interface IStorage { +interface IStorage> { /** * @return Identity of this virtual component */ - val storageType: StorageStackType + val storageType: StorageStack.Type } /** * Generates events for [IStorageEventConsumer] */ -interface IStorageEventProducer : IStorage { +interface IStorageEventProducer> : IStorage { /** * [listener] is [IStorageEventConsumer] which want to subscribe to our events */ @@ -81,27 +32,39 @@ interface IStorageEventProducer : IStorage { /** * Consumes events produced by [IStorageEventConsumer] */ -interface IStorageEventConsumer : IStorage { +interface IStorageEventConsumer> : IStorage { /** * Fired on whenever an object is added (to listener) we subscribed to */ - fun addStack(stack: T, id: UUID, provider: IStorageProvider) + fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider) /** * Fired on whenever an object is changes on listener we subscribed to */ - fun changeStack(stack: T, id: UUID, oldCount: BigInteger) + fun onStackChanged(stack: T, id: UUID) /** * Fired on whenever an object is removed from listener we subscribed to */ - fun removeStack(stack: T, id: UUID) + fun onStackRemoved(id: UUID) + + fun onStackChanged(tuple: IStorageTuple) { + onStackChanged(tuple.stack, tuple.id) + } + + fun onStackAdded(tuple: IStorageTuple, provider: IStorageProvider) { + onStackAdded(tuple.stack, tuple.id, provider) + } + + fun onStackRemoved(tuple: IStorageTuple) { + onStackRemoved(tuple.id) + } } /** * Storage acceptor, accepts (insert only) stacks provided via [insertStack] method. */ -interface IStorageAcceptor : IStorage { +interface IStorageAcceptor> : IStorage { /** * Inserts an item into system. * @return leftover, might equal to [stack] if no items were inserted @@ -112,10 +75,7 @@ interface IStorageAcceptor : IStorage { /** * Storage provider, provides (extract only) stacks to whoever needs them (the "warehouse"). * - * [get] methods work as in bidirectional map. - * - * It is **required** for storage having **exactly one or none** of mappings of one stack [T] - * to one [UUID] (exactly one *UUID -> stack* and exactly one *stack -> UUID*). + * [get] methods work as in bidirectional map, with each stack mapping to unique [UUID] * * What this means is that [get] with [T] as an argument shall never experience a situation where * two stacks match provided key. @@ -126,15 +86,14 @@ interface IStorageAcceptor : IStorage { * which may or may not produce performance hit; [UUID]s are lightweight, semantically not bound to anything and are * very good for distributed ID generation (so nothing in game has to be bound to one sequential number generator). */ -interface IStorageProvider : IStorageEventProducer { +interface IStorageProvider> : IStorageEventProducer { /** * Attempts to retrieve stored stack by its [id]. * - * Returns stored stack as-is. If no stack with [id] exists, then - * "empty stack" is returned. + * If no stack with [id] exists, then "empty stack" is returned. * * @param id identifier of stack - * @return stored object (not a copy). Do not edit it. + * @return stored object */ operator fun get(id: UUID): T @@ -148,21 +107,13 @@ interface IStorageProvider : IStorageEventProducer { * @return UUID of stack if present */ operator fun get(key: T): UUID? { - val key1 = key.let { if (it.count == BigInteger.ONE) it else it.key() } - - return stacks.filter { - if (it.stack.count == BigInteger.ONE) { - return@filter it.stack == key1 - } else { - return@filter it.stack.key() == key1 - } - }.findFirst().orElse(null)?.id + return stacks.filter { it.stack.equalsWithoutCount(key) }.findFirst().orElse(null)?.id } val stacks: Stream> /** - * If tuple does not exist, returns empty stack + * If storage has no such stack, returns empty stack * * @param id identifier of stack to extract * @param amount amount of units to extract @@ -172,58 +123,35 @@ interface IStorageProvider : IStorageEventProducer { fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T } -fun IStorageProvider.removeListenerAuto(listener: IStorageEventConsumer): Boolean { - if (removeListener(listener)) { - for (stack in stacks) { - listener.removeStack(stack.stack, stack.id) - } - - return true - } - - return false -} - -fun IStorageProvider.addListenerAuto(listener: IStorageEventConsumer): Boolean { - if (addListener(listener)) { - for (stack in stacks) { - listener.addStack(stack.stack, stack.id, this) - } - - return true - } - - return false -} - /** * Storage component, which basically implement Input and Output */ -interface IStorageComponent : IStorageProvider, IStorageAcceptor +interface IStorageComponent> : IStorageProvider, IStorageAcceptor -fun IStorageEventConsumer.changeStack(tuple: IStorageTuple, oldCount: BigInteger) { - changeStack(tuple.stack, tuple.id, oldCount) -} - -fun IStorageEventConsumer.addStack(tuple: IStorageTuple, provider: IStorageProvider) { - addStack(tuple.stack, tuple.id, provider) -} - -fun IStorageEventConsumer.removeStack(tuple: IStorageTuple) { - removeStack(tuple.stack, tuple.id) -} - -interface IStorageTuple { +interface IStorageTuple> { val id: UUID val stack: T -} -class StorageTuple(override val id: UUID, override val stack: T) : IStorageTuple + interface IMutable> : IStorageTuple { + override var stack: T + + fun grow(amount: BigInteger) { + stack = stack.grow(amount) + } + + fun shrink(amount: BigInteger) { + stack = stack.shrink(amount) + } + } + + data class I>(override val id: UUID, override val stack: T) : IStorageTuple + data class M>(override val id: UUID, override var stack: T) : IMutable +} /** * Component which (most time) proxy other components (combine their contents into single view) */ -interface IVirtualStorageComponent : IStorageComponent, IStorageEventConsumer { +interface IVirtualStorageComponent> : IStorageComponent, IStorageEventConsumer { fun add(identity: IStorage) fun remove(identity: IStorage) fun contains(identity: IStorage): Boolean diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Helpers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Helpers.kt new file mode 100644 index 000000000..a19e4bb61 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Helpers.kt @@ -0,0 +1,25 @@ +package ru.dbotthepony.mc.otm.storage + +fun > IStorageProvider.removeListenerAndNotify(listener: IStorageEventConsumer): Boolean { + if (removeListener(listener)) { + for (stack in stacks) { + listener.onStackRemoved(stack.id) + } + + return true + } + + return false +} + +fun > IStorageProvider.addListenerAndNotify(listener: IStorageEventConsumer): Boolean { + if (addListener(listener)) { + for (stack in stacks) { + listener.onStackAdded(stack.stack, stack.id, this) + } + + return true + } + + return false +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt deleted file mode 100644 index 0c9402dd4..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt +++ /dev/null @@ -1,107 +0,0 @@ -package ru.dbotthepony.mc.otm.storage - -import net.minecraft.network.FriendlyByteBuf -import net.minecraft.world.item.ItemStack -import org.jetbrains.annotations.ApiStatus -import ru.dbotthepony.mc.otm.core.* -import ru.dbotthepony.mc.otm.core.math.isPositive -import ru.dbotthepony.mc.otm.core.math.toIntSafe -import ru.dbotthepony.mc.otm.core.registryName -import java.math.BigInteger - -/** - * constructors always copy its input. - */ -class ItemStackWrapper : IStorageStack { - /** - * [ItemStack] representing what item is this. - * - * In most cases you want to use [stack] instead. - */ - @ApiStatus.Internal - val item: ItemStack - val registryName get() = item.item.registryName!! - private val hash: Int - - override var count: BigInteger - - /** - * [copy] as false is used internally for fast index construction, do not specify - * it unless you know what you are doing! - */ - @JvmOverloads - constructor(item: ItemStack, copy: Boolean = true) { - if (copy) { - this.item = item.copy() - } else { - this.item = item - } - - this.count = BigInteger.valueOf(item.count.toLong()) - - if (copy) { - this.item.count = 1 - } - - this.hash = item.tag.hashCode() xor item.item.hashCode() - } - - constructor(item: ItemStackWrapper) { - this.item = item.item - this.count = item.count - this.item.count = 1 - this.hash = item.hash - } - - override fun copy(): ItemStackWrapper { - return ItemStackWrapper(this) - } - - fun sameItem(other: ItemStack) = ItemStack.isSameItemSameTags(item, other) - - override fun equals(other: Any?): Boolean { - if (other === this) - return true - - if (other is ItemStackWrapper) - return other.hash == hash && count == other.count && ItemStack.isSameItemSameTags(item, other.item) - - return false - } - - override fun hashCode(): Int { - return hash * 31 + count.hashCode() - } - - override val maxStackSize: BigInteger get() = BigInteger.valueOf(item.maxStackSize.toLong()) - - override val isEmpty: Boolean get() = item.isEmpty || !count.isPositive - - /** - * [ItemStack] with valid amount and which can be modified after - */ - val stack: ItemStack get() = item.copy().also { it.count = count.toIntSafe() } - - override fun toString(): String { - return "ItemStackWrapper[$count $registryName]" - } - - fun write(buff: FriendlyByteBuf) { - buff.writeItem(item) - buff.writeBigInteger(count) - } - - companion object { - @JvmField - val EMPTY = ItemStackWrapper(ItemStack.EMPTY) - - fun read(buff: FriendlyByteBuf): ItemStackWrapper { - val item = buff.readItem() - val count = buff.readBigInteger() - return ItemStackWrapper(item, copy = false).also { it.count = count } - } - } -} - -fun FriendlyByteBuf.writeBigItem(value: ItemStackWrapper) = value.write(this) -fun FriendlyByteBuf.readBigItem() = ItemStackWrapper.read(this) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStorageStack.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStorageStack.kt new file mode 100644 index 000000000..a76046db9 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStorageStack.kt @@ -0,0 +1,60 @@ +package ru.dbotthepony.mc.otm.storage + +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import ru.dbotthepony.mc.otm.core.math.toIntSafe +import java.math.BigInteger + +class ItemStorageStack private constructor(private val stack: ItemStack, count: BigInteger, mark: Nothing?) : StorageStack(count) { + constructor(stack: ItemStack) : this(stack.copy(), BigInteger.valueOf(stack.count.toLong()), null) + constructor(stack: ItemStack, count: Int) : this(stack.copy(), BigInteger.valueOf(count.toLong()), null) + constructor(stack: ItemStack, count: Long) : this(stack.copy(), BigInteger.valueOf(count), null) + constructor(stack: ItemStack, count: BigInteger) : this(stack.copy(), count, null) + + override val type: Type + get() = ITEMS + + override fun copy(newCount: BigInteger): ItemStorageStack { + if (isEmpty) + return ITEMS.empty + + if (newCount == count) + return this + + return ItemStorageStack(stack, newCount, null) + } + + override fun equals(other: Any?): Boolean { + return other is ItemStorageStack && other.count == count && ItemStack.isSameItemSameTags(stack, other.stack) + } + + override fun hashCode(): Int { + return Integer.rotateLeft(count.hashCode(), 12) xor (stack.item.hashCode() * 31 + stack.tag.hashCode()) + } + + override fun equalsWithoutCount(other: StorageStack<*>): Boolean { + return other is ItemStorageStack && ItemStack.isSameItemSameTags(stack, other.stack) + } + + override fun hashCodeWithoutCount(): Int { + return stack.item.hashCode() * 31 + stack.tag.hashCode() + } + + override val isEmpty: Boolean = stack.isEmpty || super.isEmpty + override val maxStackSize: BigInteger = BigInteger.valueOf(stack.maxStackSize.toLong()) + + fun toItemStack(count: Int = this.count.toIntSafe()): ItemStack { + return stack.copyWithCount(count) + } + + val item: Item get() = stack.item + + companion object { + val EMPTY get() = ITEMS.empty + + fun unsafe(stack: ItemStack) = ItemStorageStack(stack, BigInteger.valueOf(stack.count.toLong()), null) + fun unsafe(stack: ItemStack, count: Int) = ItemStorageStack(stack, BigInteger.valueOf(count.toLong()), null) + fun unsafe(stack: ItemStack, count: Long) = ItemStorageStack(stack, BigInteger.valueOf(count), null) + fun unsafe(stack: ItemStack, count: BigInteger) = ItemStorageStack(stack, count, null) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Registry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Registry.kt deleted file mode 100644 index c8e6c54a0..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Registry.kt +++ /dev/null @@ -1,60 +0,0 @@ -package ru.dbotthepony.mc.otm.storage - -import ru.dbotthepony.mc.otm.OverdriveThatMatters -import ru.dbotthepony.mc.otm.core.math.Decimal -import java.util.IdentityHashMap - -open class StorageStackType( - val identity: Class, - open val empty: T, - - /** - * Speculated energy required per operation on stack with size of 1 - */ - open val energyPerOperation: Decimal, -) { - open fun energyPerOperation(stack: T): Decimal { - return energyPerOperation * stack.count - } - - open fun energyPerStorage(stack: T) = energyPerOperation(stack) - open fun energyPerExtraction(stack: T) = energyPerOperation(stack) - open fun energyForUpkeep(stack: T) = Decimal.ZERO -} - -@Suppress("unchecked_cast") -object StorageRegistry { - private val REGISTRY = IdentityHashMap, StorageStackType<*>>() - - @JvmStatic - fun register(type: StorageStackType<*>): StorageStackType<*> { - check(!REGISTRY.containsKey(type.identity)) { "Already have storage stack type for ${type.identity}" } - REGISTRY[type.identity] = type - return type - } - - /** - * Refer to [StorageStackType] for explanation of flags. - */ - @JvmStatic - fun register( - identity: Class, - empty: T, - energyPerOperation: Decimal - ): StorageStackType { - return register(StorageStackType(identity, empty, energyPerOperation)) as StorageStackType - } - - @JvmStatic - fun get(identity: Class): StorageStackType { - val get = REGISTRY[identity] ?: throw NoSuchElementException("Registry does not contain $identity") - return get as StorageStackType - } - - @JvmStatic - fun getOrNull(identity: Class): StorageStackType? { - return REGISTRY[identity] as StorageStackType? - } -} - -inline val ITEM_STORAGE: StorageStackType get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/StorageStack.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/StorageStack.kt new file mode 100644 index 000000000..501ff399b --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/StorageStack.kt @@ -0,0 +1,127 @@ +package ru.dbotthepony.mc.otm.storage + +import it.unimi.dsi.fastutil.Hash +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.world.item.ItemStack +import net.minecraftforge.eventbus.api.IEventBus +import net.minecraftforge.registries.DeferredRegister +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.core.getValue +import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.readBigInteger +import ru.dbotthepony.mc.otm.core.writeBigInteger +import ru.dbotthepony.mc.otm.core.writeItemType +import ru.dbotthepony.mc.otm.registry.RegistryDelegate +import java.math.BigInteger + +abstract class StorageStack>(val count: BigInteger) { + init { + require(count >= BigInteger.ZERO) { "Negative amount of things in stack: $count" } + } + + abstract val type: Type + abstract fun copy(newCount: BigInteger): S + + abstract fun equalsWithoutCount(other: StorageStack<*>): Boolean + abstract fun hashCodeWithoutCount(): Int + + open val isEmpty: Boolean get() = count == BigInteger.ZERO + inline val isNotEmpty get() = !isEmpty + + open val maxStackSize: BigInteger? get() = null + + abstract override fun equals(other: Any?): Boolean + abstract override fun hashCode(): Int + + fun grow(amount: BigInteger): S { + if (amount == BigInteger.ZERO) return this as S + val newCount = count + amount + + if (newCount <= BigInteger.ZERO) + return type.empty + + return copy(newCount) + } + + fun shrink(amount: BigInteger): S { + if (amount == BigInteger.ZERO) return this as S + val newCount = count - amount + + if (newCount <= BigInteger.ZERO) + return type.empty + + return copy(newCount) + } + + fun write(buff: FriendlyByteBuf) { + type.write(buff, this as S) + } + + interface Type> { + val empty: T + + fun read(buff: FriendlyByteBuf): T + fun write(buff: FriendlyByteBuf, value: T) + + fun energyPerUpkeep(stack: T): Decimal = Decimal.ZERO + fun energyPerInsert(stack: T): Decimal = energyPerOperation(stack) + fun energyPerExtract(stack: T): Decimal = energyPerOperation(stack) + fun energyPerOperation(stack: T): Decimal + } + + class SimpleType>( + val energyPerOperation: Decimal, + override val empty: T, + private val read: (FriendlyByteBuf) -> T, + private val write: (FriendlyByteBuf, T) -> Unit + ) : Type { + override fun read(buff: FriendlyByteBuf): T { + return read.invoke(buff) + } + + override fun write(buff: FriendlyByteBuf, value: T) { + write.invoke(buff, value) + } + + override fun energyPerOperation(stack: T): Decimal { + return energyPerOperation * stack.count + } + } + + companion object : Hash.Strategy?> { + override fun equals(a: StorageStack<*>?, b: StorageStack<*>?): Boolean { + if (a == null && b == null) return true + if (a != null && b != null) return a.equalsWithoutCount(b) + return false + } + + override fun hashCode(o: StorageStack<*>?): Int { + return o?.hashCodeWithoutCount() ?: 0 + } + + private val delegate = RegistryDelegate>("stack_type") { disableSaving() } + val REGISTRY by delegate + val REGISTRY_KEY by delegate::key + + private val registrar = DeferredRegister.create(REGISTRY_KEY, OverdriveThatMatters.MOD_ID) + + val ITEMS: Type by registrar.register("items") { + SimpleType( + Decimal("4"), + ItemStorageStack(ItemStack.EMPTY), + { + ItemStorageStack.unsafe(it.readItem(), it.readBigInteger()) + }, + { buff, v -> + buff.writeItem(v.toItemStack()) + buff.writeBigInteger(v.count) + } + ) + } + + internal fun register(bus: IEventBus) { + bus.addListener(delegate::build) + registrar.register(bus) + } + } +} 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 a8dd38f74..2742d6b09 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt @@ -1,23 +1,23 @@ package ru.dbotthepony.mc.otm.storage +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectArraySet -import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.core.math.isPositive import ru.dbotthepony.mc.otm.core.math.isZero import java.math.BigInteger import java.util.* import java.util.stream.Stream -import kotlin.collections.HashMap -import kotlin.reflect.full.isSubclassOf +import kotlin.collections.ArrayList -class RemoteTuple( - override val stack: T, +class RemoteTuple>( + override var stack: T, override val id: UUID, - val provider: IStorageProvider, + val parent: IStorageProvider, val local: LocalTuple ) : IStorageTuple { fun extract(amount: BigInteger, simulate: Boolean): T { - return provider.extractStack(id, amount, simulate) + return parent.extractStack(id, amount, simulate) } override fun equals(other: Any?): Boolean { @@ -29,21 +29,27 @@ class RemoteTuple( } } -class LocalTuple(override val stack: T, override val id: UUID, val tuples: ArrayList>) : IStorageTuple +class LocalTuple>(override var stack: T, override val id: UUID) : IStorageTuple { + val tuples = ArrayList>(1) -open class VirtualComponent(type: StorageStackType) : IVirtualStorageComponent { - constructor(type: Class) : this(StorageRegistry.get(type)) + fun grow(amount: BigInteger) { + stack = stack.grow(amount) + } - override val storageType: StorageStackType = type + fun shrink(amount: BigInteger) { + stack = stack.shrink(amount) + } +} +open class VirtualComponent>(override val storageType: StorageStack.Type) : IVirtualStorageComponent { // удаленный UUID -> Кортеж - protected val remoteTuples = HashMap>() + protected val remoteTuples = Object2ObjectOpenHashMap>() // локальный UUID -> Локальный кортеж - protected val localTuples = HashMap>() + protected val localTuples = Object2ObjectOpenHashMap>() // Стак -> Локальный кортеж стака - protected val hashedTuples = HashMap>() + protected val item2tuple = Object2ObjectOpenCustomHashMap>(StorageStack.Companion) // ArrayList для скорости работы protected val listeners: MutableSet> = ObjectArraySet() @@ -54,18 +60,17 @@ open class VirtualComponent(type: StorageStackType) : IVir protected open fun onRemove(identity: IStorage) {} override fun add(identity: IStorage) { - if (!(identity.storageType::class.isSubclassOf(storageType::class))) - throw ClassCastException("Reified Generics: ${identity.storageType::class.qualifiedName} can not be cast into ${storageType::class.qualifiedName}") + require(identity.storageType == storageType) { "Attempt to add component of type ${identity.storageType} to virtual component of type $storageType" } if (children.add(identity)) { if (identity is IStorageProvider) { - identity.addListenerAuto(this) + identity.addListenerAndNotify(this) } else if (identity is IStorageEventProducer) { identity.addListener(this) } if (identity is IStorageEventConsumer) { - addListenerAuto(identity) + addListenerAndNotify(identity) } if (identity is IStorageAcceptor) { @@ -77,18 +82,17 @@ open class VirtualComponent(type: StorageStackType) : IVir } override fun remove(identity: IStorage) { - if (!(identity.storageType::class.isSubclassOf(storageType::class))) - throw ClassCastException("Reified Generics: ${identity.storageType::class.qualifiedName} can not be cast into ${storageType::class.qualifiedName}") + require(identity.storageType == storageType) { "Attempt to remove component of type ${identity.storageType} from virtual component of type $storageType" } if (children.remove(identity)) { if (identity is IStorageProvider) { - identity.removeListenerAuto(this) + identity.removeListenerAndNotify(this) } else if (identity is IStorageEventProducer) { identity.removeListener(this) } if (identity is IStorageEventConsumer) { - removeListenerAuto(identity) + removeListenerAndNotify(identity) } if (identity is IStorageAcceptor) { @@ -99,101 +103,85 @@ open class VirtualComponent(type: StorageStackType) : IVir } } - override fun contains(identity: IStorage): Boolean { - return children.contains(identity) - } - - override fun addListener(listener: IStorageEventConsumer): Boolean { - if (listeners.add(listener)) { - return true - } - - return false - } - - override fun removeListener(listener: IStorageEventConsumer): Boolean { - return listeners.remove(listener) - } + override fun contains(identity: IStorage) = children.contains(identity) + override fun addListener(listener: IStorageEventConsumer) = listeners.add(listener) + override fun removeListener(listener: IStorageEventConsumer) = listeners.remove(listener) override fun get(id: UUID): T { return localTuples[id]?.stack ?: this.storageType.empty } override val stacks: Stream> get() { - return ArrayList>(hashedTuples.size).also { it.addAll(hashedTuples.values) }.stream() + return ArrayList>(item2tuple.size).also { it.addAll(item2tuple.values) }.stream() } override fun get(key: T): UUID? { - return hashedTuples[key.key()]?.id + return item2tuple[key]?.id } - @Suppress("unchecked_cast") - override fun addStack(stack: T, id: UUID, provider: IStorageProvider) { + override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider) { check(!remoteTuples.containsKey(id)) { "Already tracking tuple with id $id" } - val key = stack.key() - var local: LocalTuple? = hashedTuples[key] - var oldCount = BigInteger.ZERO + var local = item2tuple[stack] val added = local == null if (local == null) { - local = LocalTuple(stack.copy() as T, UUID.randomUUID(), ArrayList>(1)) + local = LocalTuple(stack, UUID.randomUUID()) localTuples[local.id] = local - hashedTuples[key] = local + item2tuple[stack] = local } else { - oldCount = local.stack.count - local.stack.grow(stack.count) + local.grow(stack.count) } - val remote = RemoteTuple(stack.copy() as T, id, provider, local) + val remote = RemoteTuple(stack, id, provider, local) local.tuples.add(remote) remoteTuples[id] = remote if (added) { for (listener in listeners) { - listener.addStack(local.stack, local.id, this) + listener.onStackAdded(local.stack, local.id, this) } } else { for (listener in listeners) { - listener.changeStack(local.stack, local.id, oldCount) + listener.onStackChanged(local.stack, local.id) } } } - override fun changeStack(stack: T, id: UUID, oldCount: BigInteger) { + override fun onStackChanged(stack: T, id: UUID) { require(stack.count.isPositive) + val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id") - val diff = stack.count - tuple.stack.count - tuple.stack.count = stack.count - - @Suppress("NAME_SHADOWING") val oldCount = tuple.local.stack.count - tuple.local.stack.grow(diff) + tuple.stack = stack + tuple.local.grow(diff) for (listener in listeners) { - listener.changeStack(tuple.local.stack, tuple.local.id, oldCount) + listener.onStackChanged(tuple.local.stack, tuple.local.id) } } - override fun removeStack(stack: T, id: UUID) { - val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id") + override fun onStackRemoved(id: UUID) { + val remote = remoteTuples.remove(id) ?: throw IllegalStateException("No such tuple with id $id") + val local = remote.local - tuple.local.stack.shrink(tuple.stack.count) - tuple.local.tuples.remove(tuple) + local.shrink(remote.stack.count) + check(local.tuples.remove(remote)) - remoteTuples.remove(id) - - val a = tuple.local.stack.count <= BigInteger.ZERO - val b = tuple.local.tuples.size == 0 + val a = local.stack.isEmpty + val b = local.tuples.isEmpty() if (a || b) { - check(a && b) { "View object is empty, but tuple list is not!" } - localTuples.remove(tuple.local.id) - val key = stack.key() - checkNotNull(hashedTuples.remove(key)) { "No such stack $key" } + check(a && b) { "View stack is empty, but remote tuple list is not!" } + localTuples.remove(local.id) + checkNotNull(item2tuple.remove(remote.stack)) { "No such stack ${remote.stack}" } for (listener in listeners) { - listener.removeStack(tuple.local.stack, tuple.local.id) + listener.onStackRemoved(local.id) + } + } else { + for (listener in listeners) { + listener.onStackChanged(local.stack, local.id) } } } @@ -212,14 +200,13 @@ open class VirtualComponent(type: StorageStackType) : IVir return leftover } - @Suppress("unchecked_cast") override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { if (!amount.isPositive) return this.storageType.empty @Suppress("name_shadowing") var amount = amount - val tuple: LocalTuple? = localTuples[id] + val tuple = localTuples[id] if (tuple == null || amount.isZero) return this.storageType.empty @@ -229,180 +216,19 @@ open class VirtualComponent(type: StorageStackType) : IVir val toExtract = tuple.stack.count.coerceAtMost(amount) var extracted = BigInteger.ZERO - val copy = tuple.stack.copy() as T + val copy = tuple.stack - for (remote_tuple in tuple.tuples.let { Array(it.size) { i -> it[i] } }) { - val extractedStack = remote_tuple.extract(toExtract - extracted, simulate) - extracted += extractedStack.count + for (remote in ArrayList(tuple.tuples)) { + extracted += remote.extract(toExtract - extracted, simulate).count if (extracted >= toExtract) break } if (extracted.isPositive) { - copy.count = extracted - return copy + return copy.copy(extracted) } return this.storageType.empty } } - -/** - * Adds energy demand to operations over [parent] - */ -open class PoweredComponent(open val parent: IStorageComponent, val energyProvider: () -> IMatteryEnergyStorage) : IStorageComponent { - override val storageType: StorageStackType - get() = parent.storageType - - private val subscribedThrough = ObjectArraySet>() - - final override fun addListener(listener: IStorageEventConsumer): Boolean { - if (parent.addListener(listener)) { - subscribedThrough.add(listener) - return true - } - - return false - } - - final override fun removeListener(listener: IStorageEventConsumer): Boolean { - subscribedThrough.remove(listener) - return parent.removeListener(listener) - } - - fun removeListeners() { - for (child in subscribedThrough) { - parent.removeListener(child) - } - - subscribedThrough.clear() - } - - override fun get(key: T) = parent[key] - - @Suppress("unchecked_cast") - override fun insertStack(stack: T, simulate: Boolean): T { - val required = storageType.energyPerOperation * stack.count - val energy = energyProvider.invoke() - val extracted = energy.extractEnergy(required, true) - - if (extracted.isZero) { - return stack.copy() as T - } - - if (extracted == required) { - val leftover = parent.insertStack(stack, simulate) - - if (leftover.isEmpty) { - if (!simulate) { - energy.extractEnergy(required, false) - } - - return leftover - } - - if (!simulate) { - val requiredNew = storageType.energyPerOperation * (stack.count - leftover.count) - energy.extractEnergy(requiredNew, false) - } - - return leftover - } - - @Suppress("name_shadowing") - val stack = stack.copy() as T - val oldCount = stack.count - stack.count = (extracted / storageType.energyPerOperation).whole - val diff = oldCount - stack.count - val newRequired = storageType.energyPerOperation * stack.count - val newExtracted = energy.extractEnergy(newRequired, true) - - if (newExtracted == newRequired) { - val leftover = parent.insertStack(stack, simulate) - - if (leftover.isEmpty) { - if (!simulate) { - energy.extractEnergy(newRequired, false) - } - - leftover.count = diff - return leftover - } - - if (!simulate) { - val requiredNew = storageType.energyPerOperation * (stack.count - leftover.count) - energy.extractEnergy(requiredNew, false) - } - - leftover.count += diff - return leftover - } - - return stack - } - - override fun get(id: UUID) = parent[id] - - override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { - val required = storageType.energyPerOperation * amount - val energy = energyProvider.invoke() - val extracted = energy.extractEnergy(required, true) - - if (extracted.isZero) { - return storageType.empty - } - - if (extracted == required) { - val extractedStack = parent.extractStack(id, amount, simulate) - - if (extractedStack.isEmpty) { - return extractedStack - } - - if (!simulate) { - if (extractedStack.count == amount) { - energy.extractEnergy(required, false) - } else { - energy.extractEnergy(storageType.energyPerOperation * extractedStack.count, false) - } - } - - return extractedStack - } - - @Suppress("name_shadowing") - val amount = (required / storageType.energyPerOperation).whole - val extractedStack = parent.extractStack(id, amount, simulate) - - if (extractedStack.isEmpty) { - return extractedStack - } - - if (!simulate) { - energy.extractEnergy(storageType.energyPerOperation * extractedStack.count, false) - } - - return extractedStack - } - - override val stacks get() = parent.stacks -} - -/** - * Adds energy demand to virtual component [parent] - */ -open class PoweredVirtualComponent(override val parent: IVirtualStorageComponent, energyProvider: () -> IMatteryEnergyStorage) - : PoweredComponent(parent, energyProvider), IVirtualStorageComponent { - constructor(parent: IVirtualStorageComponent, energy: IMatteryEnergyStorage) : this(parent, { energy }) - constructor(parent: Class, energy: IMatteryEnergyStorage) : this(VirtualComponent(parent), { energy }) - constructor(parent: StorageStackType, energy: IMatteryEnergyStorage) : this(VirtualComponent(parent), { energy }) - - override fun addStack(stack: T, id: UUID, provider: IStorageProvider) = parent.addStack(stack, id, provider) - override fun changeStack(stack: T, id: UUID, oldCount: BigInteger) = parent.changeStack(stack, id, oldCount) - override fun removeStack(stack: T, id: UUID) = parent.removeStack(stack, id) - - override fun add(identity: IStorage) = parent.add(identity) - override fun remove(identity: IStorage) = parent.remove(identity) - override fun contains(identity: IStorage) = parent.contains(identity) -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredComponent.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredComponent.kt new file mode 100644 index 000000000..eb5f65ab8 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredComponent.kt @@ -0,0 +1,16 @@ +package ru.dbotthepony.mc.otm.storage.powered + +import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage +import ru.dbotthepony.mc.otm.storage.IStorageAcceptor +import ru.dbotthepony.mc.otm.storage.IStorageComponent +import ru.dbotthepony.mc.otm.storage.IStorageProvider +import ru.dbotthepony.mc.otm.storage.StorageStack +import java.util.function.Supplier + +class PoweredComponent>( + val parent: IStorageComponent, + energy: Supplier +) : IStorageComponent, IStorageProvider by PoweredStorageProvider(parent, energy), IStorageAcceptor by PoweredStorageAcceptor(parent, energy) { + override val storageType: StorageStack.Type + get() = parent.storageType +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredStorageAcceptor.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredStorageAcceptor.kt new file mode 100644 index 000000000..c058020c7 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredStorageAcceptor.kt @@ -0,0 +1,34 @@ +package ru.dbotthepony.mc.otm.storage.powered + +import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage +import ru.dbotthepony.mc.otm.storage.IStorageAcceptor +import ru.dbotthepony.mc.otm.storage.StorageStack +import java.util.function.Supplier + +class PoweredStorageAcceptor>(val parent: IStorageAcceptor, val energy: Supplier) : IStorageAcceptor by parent { + override fun insertStack(stack: T, simulate: Boolean): T { + val leftover = parent.insertStack(stack, true) + if (leftover == stack) return stack + + val energy = energy.get() + val required = storageType.energyPerInsert(stack.shrink(leftover.count)) + + if (energy.extractEnergy(required, true) == required) { + if (!simulate) { + val leftover2 = parent.insertStack(stack, false) + + if (leftover2.count != leftover.count) { + energy.extractEnergy(storageType.energyPerInsert(stack.shrink(leftover2.count)), false) + } else { + energy.extractEnergy(required, false) + } + + return leftover2 + } + + return leftover + } + + return stack + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredStorageProvider.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredStorageProvider.kt new file mode 100644 index 000000000..bae9d933d --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredStorageProvider.kt @@ -0,0 +1,36 @@ +package ru.dbotthepony.mc.otm.storage.powered + +import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage +import ru.dbotthepony.mc.otm.storage.IStorageProvider +import ru.dbotthepony.mc.otm.storage.StorageStack +import java.math.BigInteger +import java.util.* +import java.util.function.Supplier + +class PoweredStorageProvider>(val parent: IStorageProvider, val energy: Supplier) : IStorageProvider by parent { + override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { + val extracted = parent.extractStack(id, amount, simulate) + if (extracted.isEmpty) return extracted + + val energy = energy.get() + val required = storageType.energyPerExtract(extracted) + + if (energy.extractEnergy(required, true) == required) { + if (!simulate) { + val extracted2 = parent.extractStack(id, amount, false) + + if (extracted.count != extracted2.count) { + energy.extractEnergy(storageType.energyPerExtract(extracted2), false) + } else { + energy.extractEnergy(required, false) + } + + return extracted2 + } + + return extracted + } + + return storageType.empty + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredVirtualComponent.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredVirtualComponent.kt new file mode 100644 index 000000000..5f2391019 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/powered/PoweredVirtualComponent.kt @@ -0,0 +1,25 @@ +package ru.dbotthepony.mc.otm.storage.powered + +import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage +import ru.dbotthepony.mc.otm.storage.IStorage +import ru.dbotthepony.mc.otm.storage.IStorageComponent +import ru.dbotthepony.mc.otm.storage.IStorageProvider +import ru.dbotthepony.mc.otm.storage.IVirtualStorageComponent +import ru.dbotthepony.mc.otm.storage.StorageStack +import ru.dbotthepony.mc.otm.storage.VirtualComponent +import java.util.* +import java.util.function.Supplier + +class PoweredVirtualComponent>( + val parent: IVirtualStorageComponent, + energy: Supplier +) : IVirtualStorageComponent, IStorageComponent by PoweredComponent(parent, energy) { + constructor(type: StorageStack.Type, energy: Supplier) : this(VirtualComponent(type), energy) + + override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider) = parent.onStackAdded(stack, id, provider) + override fun onStackChanged(stack: T, id: UUID) = parent.onStackChanged(stack, id) + override fun onStackRemoved(id: UUID) = parent.onStackRemoved(id) + override fun add(identity: IStorage) = parent.add(identity) + override fun remove(identity: IStorage) = parent.remove(identity) + override fun contains(identity: IStorage) = parent.contains(identity) +}