From cb5cb448485ab69760acbfccde77362138de10a2 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Tue, 7 Jun 2022 18:45:26 +0700 Subject: [PATCH] Migrate storage system to use bigintegers since there are zero known real cases of storage units which use fractions and are not singleton units (e.g. energy) fluid is still millibuckets despite having cauldrons and etc --- .../mc/otm/OverdriveThatMatters.java | 2 +- .../otm/block/entity/StorageBusBlockEntity.kt | 44 +++--- .../mc/otm/block/entity/StorageInterfaces.kt | 29 ++-- .../otm/capability/IMatteryEnergyStorage.kt | 25 ++++ .../mc/otm/capability/drive/API.kt | 11 +- .../capability/drive/AbstractMatteryDrive.kt | 39 ++--- .../mc/otm/capability/drive/DrivePool.kt | 10 +- .../otm/capability/drive/ItemMatteryDrive.kt | 15 +- .../dbotthepony/mc/otm/compat/mekanism/QIO.kt | 24 +-- .../mc/otm/core/ImpreciseFraction.kt | 5 + .../kotlin/ru/dbotthepony/mc/otm/core/Math.kt | 20 +++ .../otm/graph/storage/StorageNetworkGraph.kt | 1 - .../otm/item/PortableCondensationDriveItem.kt | 11 +- .../mc/otm/matter/MatterRegistry.kt | 6 +- .../mc/otm/menu/data/NetworkedItemView.kt | 17 ++- .../ru/dbotthepony/mc/otm/storage/API.kt | 140 ++++++++---------- .../mc/otm/storage/ItemStackWrapper.kt | 9 +- .../ru/dbotthepony/mc/otm/storage/Registry.kt | 22 +-- .../mc/otm/storage/VirtualComponent.kt | 112 +++++++------- 19 files changed, 279 insertions(+), 263 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/Math.kt diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index 084f2b459..628672e74 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -103,7 +103,7 @@ public final class OverdriveThatMatters { private void setup(final FMLCommonSetupEvent event) { MatteryNetworking.register(); - ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new ImpreciseFraction("3.125"), false); + ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new ImpreciseFraction("3.125")); if (ModList.get().isLoaded("mekanism")) { MinecraftForge.EVENT_BUS.register(QIOKt.class); diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt index 153b8ef01..e256fb9d2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageBusBlockEntity.kt @@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.ListTag import net.minecraft.network.chat.Component import net.minecraft.network.chat.TranslatableComponent import net.minecraft.server.level.ServerLevel @@ -26,6 +25,7 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.isPositive import ru.dbotthepony.mc.otm.core.plus import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.graph.GraphNodeListener @@ -36,7 +36,9 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.storage.* import java.lang.ref.WeakReference +import java.math.BigInteger import java.util.* +import java.util.stream.Stream import kotlin.collections.ArrayList import kotlin.collections.HashMap @@ -186,9 +188,9 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter override val storageType: StorageStackType get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() - private val listeners = ArrayList>() + private val listeners = ArrayList>() - override fun addListener(listener: IStorageListener): Boolean { + override fun addListener(listener: IStorageEventConsumer): Boolean { if (!listeners.contains(listener)) { listeners.add(listener) return true @@ -197,7 +199,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter return false } - override fun removeListener(listener: IStorageListener): Boolean { + override fun removeListener(listener: IStorageEventConsumer): Boolean { return listeners.remove(listener) } @@ -214,7 +216,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot") scannedMap.children.remove(slot) val count = scannedMap.stack.count - scannedMap.stack.count -= item.stack.count + scannedMap.stack.count -= item.stack.count.toBigInteger() if (scannedMap.stack.count.isPositive) { for (listener in listeners) { @@ -238,7 +240,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter val oldCount = scannedMap.stack.count item.stack.count += diff - scannedMap.stack.count += diff + scannedMap.stack.count += diff.toBigInteger() for (listener in listeners) { listener.changeStack(scannedMap.stack, scannedMap.id, oldCount) @@ -252,7 +254,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter val key = storageStack.key() var tuple: TrackedTuple? = tuples[key] val added = tuple == null - var oldCount = ImpreciseFraction.ZERO + var oldCount = BigInteger.ZERO if (added) { tuple = TrackedTuple(storageStack, UUID.randomUUID()) @@ -260,7 +262,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter tuples[key] = tuple } else { oldCount = tuple!!.stack.count - tuple.stack.count += stack.count + tuple.stack.count += stack.count.toBigInteger() } tuple.children[slot] = SlotTuple(slot, stack.copy()) @@ -343,7 +345,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter if (energy.batteryLevel.isZero || !filter.match(stack.item)) return stack - val maxPossibleDemand = stack.count * ITEM_STORAGE.energyPerOperation + val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * stack.count val maxExtractEnergy = energy.extractEnergyInner(maxPossibleDemand, true) var leftover: ItemStackWrapper @@ -352,7 +354,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter leftover = stack.copy() } else { leftover = stack.copy().also { - it.count = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).floor() + it.count = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole } } @@ -361,7 +363,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter leftover = ItemStackWrapper(parent.insertItem(slot, leftover.stack, simulate)) if (oldCount != leftover.count && !simulate) { - energy.extractEnergyInner((oldCount - leftover.count) * ITEM_STORAGE.energyPerOperation, false) + energy.extractEnergyInner(ITEM_STORAGE.energyPerOperation * (oldCount - leftover.count), false) scan(slot) } @@ -373,22 +375,22 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter return leftover } - override fun getStack(id: UUID): ItemStackWrapper { + override fun get(id: UUID): ItemStackWrapper { return index[id]?.stack ?: ItemStackWrapper.EMPTY } - override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): ItemStackWrapper { - @Suppress("NAME_SHADOWING") - var amount = amount.floor() - + override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStackWrapper { if (!amount.isPositive) return ItemStackWrapper.EMPTY - val maxPossibleDemand = amount * OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation + val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * amount val maxExtractEnergy = energy.extractEnergyInner(maxPossibleDemand, true) + @Suppress("NAME_SHADOWING") + var amount = amount + if (maxPossibleDemand != maxExtractEnergy) { - amount = (maxExtractEnergy / OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation).floor() + amount = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole } val intAmount = amount.toLong() @@ -433,18 +435,18 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter return ItemStackWrapper.EMPTY } - copy.count = ImpreciseFraction(totalExtracted) + copy.count = totalExtracted.toBigInteger() return copy } - override fun getStacks(): Collection> { + override val stacks: Stream> get() { val listing = ArrayList>(index.size) for (tuple in index.values) { listing.add(StorageTuple(tuple.id, tuple.stack)) } - return listing + return listing.stream() } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageInterfaces.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageInterfaces.kt index 231f0d7a6..4537ec517 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageInterfaces.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/StorageInterfaces.kt @@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.block.entity import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.ListTag import net.minecraft.network.chat.Component import net.minecraft.network.chat.TranslatableComponent import net.minecraft.server.level.ServerLevel @@ -36,9 +35,9 @@ import ru.dbotthepony.mc.otm.menu.StorageImporterMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.storage.* +import java.math.BigInteger import java.util.* import java.util.stream.Stream -import kotlin.collections.HashMap import kotlin.collections.HashSet abstract class AbstractStorageImportExport( @@ -190,17 +189,17 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) return stack val view = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: return stack - val maxMove = energy.extractStepInner(ITEM_STORAGE.energyPerOperation, stack.count, true) + val maxMove = energy.extractStepInnerBi(ITEM_STORAGE.energyPerOperation, stack.count, true) - if (maxMove == 0) + if (maxMove == BigInteger.ZERO) return stack - val leftover = view.insertStack(ItemStackWrapper(stack).also { it.count = maxMove.toImpreciseFraction() }, simulate) + val leftover = view.insertStack(ItemStackWrapper(stack).also { it.count = maxMove }, simulate) if (simulate) return leftover.stack - energy.extractStepInner(ITEM_STORAGE.energyPerOperation, maxMove - leftover.count.toInt(), false) + energy.extractStepInner(ITEM_STORAGE.energyPerOperation, maxMove - leftover.count, false) return leftover.stack } @@ -265,7 +264,7 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractStorageImportExport(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState), - IStorageListener { + IStorageEventConsumer { override val defaultDisplayName: Component get() = MACHINE_NAME @@ -275,7 +274,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : private val relevantTuples = HashSet() - override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView) { + override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider) { if (!filter.match(stack.item)) { return } @@ -283,7 +282,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : relevantTuples.add(id) } - override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: ImpreciseFraction) { + override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) { // no-op } @@ -296,7 +295,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : val component = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: return@ItemFilter - for (tuple in component.getStacks()) { + for (tuple in component.stacks) { addStack(tuple, component) } @@ -315,7 +314,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : private val exportStacks: Stream> get() { val view = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: return Stream.empty() - return relevantTuples.stream().map { it to view.getStack(it) } + return relevantTuples.stream().map { it to view[it] } } override fun saveAdditional(nbt: CompoundTag) { @@ -353,9 +352,9 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : continue } - val exportAmountA = items.extractStack(stack.first, stack.second.count.toInt().coerceAtMost(MAX_MOVE_PER_OPERATION).toImpreciseFraction(), true).count.toInt() + val exportAmountA = items.extractStack(stack.first, stack.second.count.coerceAtMost(MAX_MOVE_PER_OPERATION), true).count - if (exportAmountA == 0) { + if (exportAmountA == BigInteger.ZERO) { continue } @@ -369,7 +368,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : if (leftover.count != exportAmount) { hit = true - exportAmount = items.extractStack(stack.first, ImpreciseFraction(exportAmount - leftover.count), false).count.toInt() + exportAmount = items.extractStack(stack.first, (exportAmount - leftover.count).toBigInteger(), false).count.toInt() resolved.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, false) energy.extractStepInner(ITEM_STORAGE.energyPerOperation, exportAmount, false) break @@ -387,7 +386,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : } companion object { - const val MAX_MOVE_PER_OPERATION = 4 + val MAX_MOVE_PER_OPERATION: BigInteger = BigInteger.valueOf(4L) private val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_EXPORTER}") private const val INTERVAL = 5 const val MAX_FILTERS = 6 * 3 diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IMatteryEnergyStorage.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IMatteryEnergyStorage.kt index 102ba2d0f..97071dba8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IMatteryEnergyStorage.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IMatteryEnergyStorage.kt @@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.capability import net.minecraftforge.energy.IEnergyStorage import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import java.math.BigInteger // IEnergyStorage for direct compat with Forge Energy interface IMatteryEnergyStorage : IEnergyStorage { @@ -212,3 +213,27 @@ fun IMatteryEnergyStorage.extractStepInner(base: ImpreciseFraction, multiplier: fun IMatteryEnergyStorage.extractStepOuter(base: ImpreciseFraction, multiplier: Int, simulate: Boolean): Int { return (extractEnergyOuter(base * multiplier, simulate) / base).toInt() } + +fun IMatteryEnergyStorage.extractStepInnerBi(base: ImpreciseFraction, multiplier: Int, simulate: Boolean): BigInteger { + return (extractEnergyInner(base * multiplier, simulate) / base).whole +} + +fun IMatteryEnergyStorage.extractStepOuterBi(base: ImpreciseFraction, multiplier: Int, simulate: Boolean): BigInteger { + return (extractEnergyOuter(base * multiplier, simulate) / base).whole +} + +fun IMatteryEnergyStorage.extractStepInner(base: ImpreciseFraction, multiplier: BigInteger, simulate: Boolean): Int { + return (extractEnergyInner(base * multiplier, simulate) / base).toInt() +} + +fun IMatteryEnergyStorage.extractStepOuter(base: ImpreciseFraction, multiplier: BigInteger, simulate: Boolean): Int { + return (extractEnergyOuter(base * multiplier, simulate) / base).toInt() +} + +fun IMatteryEnergyStorage.extractStepInnerBi(base: ImpreciseFraction, multiplier: BigInteger, simulate: Boolean): BigInteger { + return (extractEnergyInner(base * multiplier, simulate) / base).whole +} + +fun IMatteryEnergyStorage.extractStepOuterBi(base: ImpreciseFraction, multiplier: BigInteger, simulate: Boolean): BigInteger { + return (extractEnergyOuter(base * multiplier, simulate) / base).whole +} 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 2e2027d7e..fc4db5c62 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 @@ -9,6 +9,7 @@ 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 java.math.BigInteger import java.util.* interface IItemMatteryDrive : IMatteryDrive { @@ -25,19 +26,13 @@ interface IItemMatteryDrive : IMatteryDrive { fun findItems(stack: ItemStack): IStorageTuple? } -interface IItemViewListener { - fun addViewItem(stack: ItemStack, id_upstream: UUID) - fun changeViewItem(id_upstream: UUID, new_count: Int) - fun removeViewItem(id_upstream: UUID) -} - interface IMatteryDrive : IStorageComponent { val uuid: UUID var isDirty: Boolean - val storedCount: ImpreciseFraction - val driveCapacity: ImpreciseFraction + val storedCount: BigInteger + val driveCapacity: BigInteger // not extending INBTSerializable to avoid serializing it as forgecaps fun serializeNBT(): CompoundTag 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 e64e7a5c7..916e50b9c 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,21 +1,26 @@ package ru.dbotthepony.mc.otm.capability.drive -import it.unimi.dsi.fastutil.longs.Long2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap +import it.unimi.dsi.fastutil.objects.ObjectArraySet import kotlin.jvm.JvmOverloads import java.util.UUID import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag import net.minecraft.nbt.Tag +import ru.dbotthepony.mc.otm.core.BigInteger import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.isPositive +import ru.dbotthepony.mc.otm.core.serializeNBT import ru.dbotthepony.mc.otm.ifHas import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.storage.* +import java.math.BigInteger import java.util.ArrayList import java.util.HashSet +import java.util.stream.Stream abstract class AbstractMatteryDrive @JvmOverloads constructor( - override var driveCapacity: ImpreciseFraction, + override var driveCapacity: BigInteger, override val uuid: UUID = UUID.randomUUID(), var maxDifferentStacks: Int = 0xFFFF ) : IMatteryDrive { @@ -34,13 +39,13 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor var storedDifferentStacks = 0 protected set - override var storedCount = ImpreciseFraction.ZERO + 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 <= ImpreciseFraction.ZERO) return stack + if (maxInsert <= BigInteger.ZERO) return stack val key = stack.key() val tuple = tuples[key] @@ -91,20 +96,18 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor } @Suppress("unchecked_cast") - override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T { - @Suppress("NAME_SHADOWING") - var amount = amount + override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { val get = tuplesByID[id] ?: return storageType.empty - if (!storageType.fractional) - amount = amount.floor() + @Suppress("NAME_SHADOWING") + var amount = amount if (!amount.isPositive) amount = get.stack.maxStackSize ?: get.stack.count amount = amount.coerceAtMost(get.stack.count) - if (amount <= ImpreciseFraction.ZERO) + if (amount <= BigInteger.ZERO) return storageType.empty val copy = get.stack.copy() as T @@ -165,11 +168,11 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor override fun deserializeNBT(nbt: CompoundTag) { tuples.clear() tuplesByID.clear() - storedCount = ImpreciseFraction.ZERO + storedCount = BigInteger.ZERO storedDifferentStacks = 0 nbt.ifHas("capacity") { - driveCapacity = ImpreciseFraction.deserializeNBT(it) + driveCapacity = BigInteger(it) } maxDifferentStacks = nbt.getInt("max_different_stacks") @@ -191,21 +194,21 @@ abstract class AbstractMatteryDrive @JvmOverloads constructor } } - override fun getStack(id: UUID): T { + override fun get(id: UUID): T { return tuplesByID[id]?.stack ?: storageType.empty } - override fun getStacks(): List> { - return ArrayList>(tuples.size).also { it.addAll(tuples.values) } + override val stacks: Stream> get() { + return ArrayList>(tuples.size).also { it.addAll(tuples.values) }.stream() } - protected val listeners = HashSet>() + protected val listeners = ObjectArraySet>() - override fun addListener(listener: IStorageListener): Boolean { + override fun addListener(listener: IStorageEventConsumer): Boolean { return listeners.add(listener) } - override fun removeListener(listener: IStorageListener): Boolean { + override fun removeListener(listener: IStorageEventConsumer): Boolean { return listeners.remove(listener) } } \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/DrivePool.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/DrivePool.kt index 1c6066356..bcb4e7bfb 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/DrivePool.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/DrivePool.kt @@ -28,13 +28,15 @@ import java.util.ArrayList * Why? * * There are several reasons: - * 0. This data can get very large very quickly, even when playing singleplayer (and much quicker and bigger when hosting a dedicated server) - * 1. This data can not be stored inside ItemStack.ForgeCaps due to it's size - * 2. This data can not be stored inside unshared (server only) nbt tag because, again, mods prone to use and interact with + * 1. This data can get very large very quickly, even when playing singleplayer (and much quicker and bigger when hosting a dedicated server) + * 2. This data can not be stored inside ItemStack.ForgeCaps due to it's size + * 3. This data can not be stored inside unshared (server only) nbt tag because, again, mods prone to use and interact with * it wrong, causing loss of stored data or mods exposing full content of a drive inside their own tag (which cause very real "NBT size too large" * network kicks, often locking players out of server/singleplayer worlds - * 3. net.minecraft.world.level.saveddata.SaveData is for storing everything inside one dat file, which + * 4. net.minecraft.world.level.saveddata.SaveData is for storing everything inside one dat file, which * is performance tanking, because we have to write *entire* NBT on each save, not the data of Drives that are dirty + * 5. Mods which check items for being stack-able even with stack size of 1 gonna compare nbt tag, + * which will be performance tanking due to clause 1. */ @Suppress("unused") object DrivePool { 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 1d5a11c9d..74a66ad21 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 @@ -8,20 +8,23 @@ import net.minecraft.world.item.Items import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.RegistryManager import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.core.BigInteger import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.serializeNBT import ru.dbotthepony.mc.otm.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 java.math.BigInteger import java.util.* import kotlin.collections.ArrayList class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDrive { - constructor(capacity: ImpreciseFraction, max_different_stacks: Int) : super(capacity, maxDifferentStacks = max_different_stacks) - constructor(capacity: ImpreciseFraction, uuid: UUID, max_different_stacks: Int) : super(capacity, uuid, max_different_stacks) - constructor(capacity: ImpreciseFraction, uuid: UUID) : super(capacity, uuid) - constructor(capacity: ImpreciseFraction) : super(capacity) + 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) + constructor(capacity: BigInteger, uuid: UUID) : super(capacity, uuid) + constructor(capacity: BigInteger) : super(capacity) override val storageType: StorageStackType = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() @@ -49,7 +52,7 @@ class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDri val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(tag.getString("item"))) if (item != null && item !== Items.AIR) { - val count = ImpreciseFraction.deserializeNBT(tag["count"]) + 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 } @@ -76,6 +79,6 @@ class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDri companion object { @JvmField - val DUMMY = ItemMatteryDrive(ImpreciseFraction(0), UUID(0L, 0L), 0) + val DUMMY = ItemMatteryDrive(BigInteger.ZERO, UUID(0L, 0L), 0) } } 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 0aa17b7bf..af0926648 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 @@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.compat.mekanism import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap import it.unimi.dsi.fastutil.objects.Object2ObjectFunction -import mekanism.api.math.MathUtils import mekanism.common.content.qio.QIOFrequency import mekanism.common.content.qio.QIOFrequency.QIOItemTypeData import mekanism.common.lib.frequency.Frequency @@ -23,11 +22,14 @@ import ru.dbotthepony.mc.otm.addPostServerTickerOnce import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.isMekanismLoaded import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.isPositive import ru.dbotthepony.mc.otm.core.toImpreciseFraction import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph import ru.dbotthepony.mc.otm.storage.* +import java.math.BigInteger import java.util.UUID +import java.util.stream.Stream private val QIO_LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "item_storage") @@ -46,14 +48,14 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent() private val tracked = HashMap() - private val listeners = ArrayList>() + private val listeners = ArrayList>() - override fun getStack(id: UUID): ItemStackWrapper { + override fun get(id: UUID): ItemStackWrapper { return index[id]?.stack ?: ItemStackWrapper.EMPTY } - override fun getStacks(): Collection> { - return ArrayList>(index.size).also { it.addAll(index.values) } + override val stacks: Stream> get() { + return ArrayList>(index.size).also { it.addAll(index.values) }.stream() } override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper { @@ -81,11 +83,11 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent): Boolean { + override fun addListener(listener: IStorageEventConsumer): Boolean { if (!listeners.contains(listener)) { listeners.add(listener) return true @@ -137,7 +139,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent): Boolean { + override fun removeListener(listener: IStorageEventConsumer): Boolean { return listeners.remove(listener) } @@ -149,14 +151,14 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent BigInteger.ZERO +inline val BigInteger.isNegative get() = this < BigInteger.ZERO + +fun BigInteger.serializeNBT(): ByteArrayTag { + return ByteArrayTag(toByteArray()) +} + +fun BigInteger(tag: Tag?): BigInteger { + if (tag !is ByteArrayTag) + return BigInteger.ZERO + + return BigInteger(tag.asByteArray) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt index 107b83c68..d53d90d52 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt @@ -5,7 +5,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import net.minecraft.server.level.ServerLevel import net.minecraft.world.level.Level import net.minecraft.world.level.block.entity.BlockEntity -import ru.dbotthepony.mc.otm.addPreServerTicker import ru.dbotthepony.mc.otm.addPreWorldTicker import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.MatteryCapability diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/PortableCondensationDriveItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/PortableCondensationDriveItem.kt index a9c04884b..435974578 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/PortableCondensationDriveItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/PortableCondensationDriveItem.kt @@ -28,15 +28,12 @@ import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.ifHas import ru.dbotthepony.mc.otm.set +import java.math.BigInteger import java.util.* class PortableCondensationDriveItem(capacity: Int) : Item(Properties().stacksTo(1).tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB)) { - val capacity: ImpreciseFraction - - init { - this.capacity = ImpreciseFraction(capacity) - } + val capacity: BigInteger = capacity.toBigInteger() private inner class DriveCapability(private val stack: ItemStack) : ICapabilityProvider { private var uuid: UUID? = null @@ -48,10 +45,10 @@ class PortableCondensationDriveItem(capacity: Int) : val uuid = uuid - DrivePool.get(uuid!!, { tag: CompoundTag? -> + return@of DrivePool.get(uuid!!, { tag: CompoundTag? -> val drive = ItemMatteryDrive(capacity, uuid) drive.deserializeNBT(tag!!) - drive + return@get drive }, { ItemMatteryDrive(capacity, uuid) }) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterRegistry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterRegistry.kt index c26cc1a2e..ccb932af2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterRegistry.kt @@ -20,12 +20,14 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.isZero import ru.dbotthepony.mc.otm.core.readImpreciseFraction import ru.dbotthepony.mc.otm.core.writeImpreciseFraction import ru.dbotthepony.mc.otm.menu.FormattingHelper import ru.dbotthepony.mc.otm.network.MatteryNetworking import ru.dbotthepony.mc.otm.orNull import ru.dbotthepony.mc.otm.storage.ItemStackWrapper +import java.math.BigInteger import java.util.function.Supplier internal var building = false @@ -174,7 +176,7 @@ fun canDecompose(stack: ItemStack): Boolean { return canDecompose(stack.item) && (stack.getCapability(MatteryCapability.MATTER).orNull()?.storedMatter ?: ImpreciseFraction.ZERO).isZero && - (stack.getCapability(MatteryCapability.DRIVE).orNull()?.storedCount ?: ImpreciseFraction.ZERO).isZero + (stack.getCapability(MatteryCapability.DRIVE).orNull()?.storedCount ?: BigInteger.ZERO).isZero } private const val MAX_NESTING = 100 @@ -203,7 +205,7 @@ private fun getMatterValue(stack: ItemStack, level: Int): MatterTuple { val drive = stack.getCapability(MatteryCapability.DRIVE).orNull() if (drive != null && drive.storageType === OverdriveThatMatters.INSTANCE.ITEM_STORAGE()) { - for (item in (drive as IMatteryDrive).getStacks()) { + for (item in (drive as IMatteryDrive).stacks) { val tuple = getMatterValue(item.stack.stack, level + 1) if (!tuple.isZero) { 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 60042b532..9eba10f71 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 @@ -18,9 +18,10 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.network.MatteryNetworking import ru.dbotthepony.mc.otm.network.SetCarriedPacket import ru.dbotthepony.mc.otm.storage.IStorageComponent -import ru.dbotthepony.mc.otm.storage.IStorageListener -import ru.dbotthepony.mc.otm.storage.IStorageView +import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer +import ru.dbotthepony.mc.otm.storage.IStorageProvider import ru.dbotthepony.mc.otm.storage.ItemStackWrapper +import java.math.BigInteger import java.util.* import java.util.function.Supplier @@ -155,7 +156,7 @@ class StackRemovePacket(val id: Int, val stackID: Int) { /** * Creates a virtual, slotless container for Player to interaction with. */ -open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageListener { +open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer { data class NetworkedItem constructor(val id: Int, val stack: ItemStack, val upstreamId: UUID? = null) protected var nextStackID = 0 @@ -230,8 +231,8 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: return localState.values.size } - override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView) = addObject(stack.stack, id) - override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: ImpreciseFraction) = changeObject(id, stack.count.toInt()) + override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider) = addObject(stack.stack, id) + override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) = changeObject(id, stack.count.toInt()) protected fun network(fn: () -> Any) { if (!remote) { @@ -310,13 +311,13 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: else Math.max(1, state.stack.maxStackSize / 2) - val extracted = provider.extractStack(state.upstreamId!!, amount, true) + val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), true) if (!extracted.isEmpty) { val (_, remaining) = menu.quickMoveToInventory(extracted.stack, false) if (remaining.count != extracted.count.toInt()) { - provider.extractStack(state.upstreamId, extracted.count.toInt() - remaining.count, false) + provider.extractStack(state.upstreamId, (extracted.count.toInt() - remaining.count).toBigInteger(), false) } } @@ -354,7 +355,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: else (state.stack.count / 2).coerceAtMost(state.stack.maxStackSize / 2).coerceAtLeast(1) - val extracted = provider.extractStack(state.upstreamId!!, amount, false) + val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false) menu.carried = extracted.stack MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) menu.setRemoteCarried(menu.carried.copy()) 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 f5e677d25..aee94f2c3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt @@ -1,7 +1,9 @@ package ru.dbotthepony.mc.otm.storage import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import java.math.BigInteger import java.util.* +import java.util.stream.Stream /** * Represents a stack in storage system. @@ -19,51 +21,36 @@ interface IStorageStack { fun copy(): IStorageStack /** - * Despite count being [ImpreciseFraction], far not all items can be "fraction" - * into pieces (e.g. ItemStacks). + * Size of this storage stack * - * Consult [StorageStackType.fractional] to see if item has actual fractions. - * - * Implementation MUST [ImpreciseFraction.floor] received value if fractions - * are not supported, and NOT throw any errors. Writing fractional value is a correct - * behavior, and is used in base mod. - * - * Behavior of writing negative value is undefined. + * This is overriden in subclasses */ - var count: ImpreciseFraction + var count: BigInteger val isEmpty: Boolean /** * @return max stack size for this stack object, * null if unlimited (default) */ - val maxStackSize: ImpreciseFraction? get() = null + val maxStackSize: BigInteger? get() = null /** * Increase [count] by [amount] */ - fun grow(amount: ImpreciseFraction) { + fun grow(amount: BigInteger) { count += amount } /** * Decrease [count] by [amount] */ - fun shrink(amount: ImpreciseFraction) { + fun shrink(amount: BigInteger) { count -= amount } } -/** - * (Supposedly shallow) Copies this object and sets it's count to 1 for use as map key. - * - * Equals to next code: - * ```kotlin - * this.copy().also { it.count = ImpreciseFraction.ONE } - * ``` - */ fun T.key(): T { - return copy().also { it.count = ImpreciseFraction.ONE } as T + return copy().also { it.count = BigInteger.ONE } as T } /** @@ -76,19 +63,42 @@ interface IStorage { val storageType: StorageStackType } -interface IStorageTrigger : IStorage { +/** + * Generates events for [IStorageEventConsumer] + */ +interface IStorageEventProducer : IStorage { /** - * [listener] is [IStorageListener] which want to subscribe to our events + * [listener] is [IStorageEventConsumer] which want to subscribe to our events */ - fun addListener(listener: IStorageListener): Boolean + fun addListener(listener: IStorageEventConsumer): Boolean /** - * [listener] is [IStorageListener] which want to unsubscribe from our events + * [listener] is [IStorageEventConsumer] which want to unsubscribe from our events */ - fun removeListener(listener: IStorageListener): Boolean + fun removeListener(listener: IStorageEventConsumer): Boolean } -interface IStorageConsumer : IStorage { +/** + * Consumes events produced by [IStorageEventConsumer] + */ +interface IStorageEventConsumer { + /** + * Fired on whenever an object is added (to listener) we subscribed to + */ + fun addStack(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) + + /** + * Fired on whenever an object is removed from listener we subscribed to + */ + fun removeStack(stack: T, id: UUID) +} + +interface IStorageAcceptor : IStorage { /** * Inserts an item into system. * @return leftover, might equal to [stack] if no items were inserted @@ -96,12 +106,14 @@ interface IStorageConsumer : IStorage { fun insertStack(stack: T, simulate: Boolean): T } -interface IStorageView : IStorageTrigger { +interface IStorageProvider : IStorageEventProducer { /** * @param id identifier of stack * @return stored object (not a copy). Do not edit it. */ - fun getStack(id: UUID): T + operator fun get(id: UUID): T + + val stacks: Stream> /** * If tuple does not exist, returns empty stack @@ -111,31 +123,11 @@ interface IStorageView : IStorageTrigger { * @param simulate whenever to simulate the action or not * @return copy of object, with amount of units actually extracted */ - fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T - fun extractStack(id: UUID, amount: Int, simulate: Boolean): T = extractStack(id, ImpreciseFraction(amount), simulate) - fun extractStack(id: UUID, amount: Long, simulate: Boolean): T = extractStack(id, ImpreciseFraction(amount), simulate) + fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T - /** - * Designed for views, for extraction with less computation overhead caused by - * copying stack extracted - * - * @param id identifier of stack to extract - * @param amount desired amount to extract - * @param simulate whenever to simulate the action or not - * @return amount extracted - */ - fun extractStackCount(id: UUID, amount: ImpreciseFraction, simulate: Boolean): ImpreciseFraction { - return extractStack(id, amount, simulate).count - } - - fun extractStackCount(id: UUID, amount: Int, simulate: Boolean): ImpreciseFraction = extractStackCount(id, ImpreciseFraction(amount), simulate) - fun extractStackCount(id: UUID, amount: Long, simulate: Boolean): ImpreciseFraction = extractStackCount(id, ImpreciseFraction(amount), simulate) - - fun getStacks(): Collection> - - fun addListenerAuto(listener: IStorageListener): Boolean { + fun addListenerAuto(listener: IStorageEventConsumer): Boolean { if (addListener(listener)) { - for (stack in getStacks()) { + for (stack in stacks) { listener.addStack(stack.stack, stack.id, this) } @@ -145,9 +137,9 @@ interface IStorageView : IStorageTrigger { return false } - fun removeListenerAuto(listener: IStorageListener): Boolean { + fun removeListenerAuto(listener: IStorageEventConsumer): Boolean { if (removeListener(listener)) { - for (stack in getStacks()) { + for (stack in stacks) { listener.removeStack(stack.stack, stack.id) } @@ -158,33 +150,16 @@ interface IStorageView : IStorageTrigger { } } -interface IStorageListener { - /** - * Fired on whenever an object is added (to listener) we subscribed to - */ - fun addStack(stack: T, id: UUID, provider: IStorageView) +fun IStorageEventConsumer.changeStack(tuple: IStorageTuple, oldCount: BigInteger) { + changeStack(tuple.stack, tuple.id, oldCount) +} - fun addStack(tuple: IStorageTuple, provider: IStorageView) { - addStack(tuple.stack, tuple.id, provider) - } +fun IStorageEventConsumer.addStack(tuple: IStorageTuple, provider: IStorageProvider) { + addStack(tuple.stack, tuple.id, provider) +} - /** - * Fired on whenever an object is changes on listener we subscribed to - */ - fun changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction) - - fun changeStack(tuple: IStorageTuple, oldCount: ImpreciseFraction) { - changeStack(tuple.stack, tuple.id, oldCount) - } - - /** - * Fired on whenever an object is removed from listener we subscribed to - */ - fun removeStack(stack: T, id: UUID) - - fun removeStack(tuple: IStorageTuple) { - removeStack(tuple.stack, tuple.id) - } +fun IStorageEventConsumer.removeStack(tuple: IStorageTuple) { + removeStack(tuple.stack, tuple.id) } interface IStorageTuple { @@ -193,12 +168,13 @@ interface IStorageTuple { } class StorageTuple(override val id: UUID, override val stack: T) : IStorageTuple -interface IStorageComponent : IStorageView, IStorageConsumer + +interface IStorageComponent : IStorageProvider, IStorageAcceptor /** * Component which (most time) proxy other components (combine their contents into single view) */ -interface IVirtualStorageComponent : IStorageComponent, IStorageListener { +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/ItemStackWrapper.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt index 0015aeecf..e9852735c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/ItemStackWrapper.kt @@ -6,6 +6,8 @@ import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistry import org.jetbrains.annotations.ApiStatus import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.isPositive +import java.math.BigInteger /** * constructors always copy its input. @@ -21,8 +23,7 @@ class ItemStackWrapper : IStorageStack { val registryName get() = item.item.registryName!! private val hash: Int - override var count: ImpreciseFraction - set(value) { field = value.floor() } + override var count: BigInteger /** * [copy] as false is used internally for fast index construction, do not specify @@ -36,7 +37,7 @@ class ItemStackWrapper : IStorageStack { this.item = item } - this.count = ImpreciseFraction(item.count) + this.count = BigInteger.valueOf(item.count.toLong()) if (copy) { this.item.count = 1 @@ -72,7 +73,7 @@ class ItemStackWrapper : IStorageStack { return hash * 31 + count.hashCode() } - override val maxStackSize get() = ImpreciseFraction(item.maxStackSize) + override val maxStackSize: BigInteger get() = BigInteger.valueOf(item.maxStackSize.toLong()) override val isEmpty: Boolean get() = item.isEmpty || !count.isPositive diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Registry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Registry.kt index 1ad3bd955..a4cd6584f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Registry.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/Registry.kt @@ -13,24 +13,9 @@ open class StorageStackType( * Speculated energy required per operation on stack with size of 1 */ open val energyPerOperation: ImpreciseFraction, - - /** - * Whenever is this stack supports fractional part (e.g. 0.5). - * - * Keep in mind fractions are imprecise and can lead to rounding errors. - * [ImpreciseFraction] class attempts to negate most of the issues - * (e.g. 0.1 + 0.2 eventually getting its 0....4 part into whole part), - * but that is about it. - * - * On design side note, storage system could have been using [Fraction], but there is an issue: - * they are **precise**. Under precise, means that anything that continuously divide/multiply them - * they become more and more "irrational", greatly slowing down operations. Worst case scenario: - * value is getting divided by [Long.MAX_VALUE] again and again, creating insanely huge divisor. - */ - open val fractional: Boolean ) { open fun energyPerOperation(stack: T): ImpreciseFraction { - return stack.count * energyPerOperation + return energyPerOperation * stack.count } open fun energyPerStorage(stack: T) = energyPerOperation(stack) @@ -56,10 +41,9 @@ object StorageRegistry { fun register( identity: Class, empty: T, - energyPerOperation: ImpreciseFraction, - fractional: Boolean + energyPerOperation: ImpreciseFraction ): StorageStackType { - return register(StorageStackType(identity, empty, energyPerOperation, fractional)) as StorageStackType + return register(StorageStackType(identity, empty, energyPerOperation)) as StorageStackType } @JvmStatic 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 0fafaf82c..23a207be5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt @@ -1,29 +1,31 @@ package ru.dbotthepony.mc.otm.storage -import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap -import it.unimi.dsi.fastutil.longs.Long2ObjectFunction -import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap import it.unimi.dsi.fastutil.objects.ObjectArraySet import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.isPositive +import ru.dbotthepony.mc.otm.core.isZero +import java.math.BigInteger import java.util.* +import java.util.stream.Stream import kotlin.collections.HashMap -class RemoteTuple(val obj: T, val remote_id: UUID, val provider: IStorageView, val local: LocalTuple) { - fun extract(amount: ImpreciseFraction, simulate: Boolean): T { - return provider.extractStack(remote_id, amount, simulate) - } - - fun extractCount(amount: ImpreciseFraction, simulate: Boolean): ImpreciseFraction { - return provider.extractStackCount(remote_id, amount, simulate) +class RemoteTuple( + override val stack: T, + override val id: UUID, + val provider: IStorageProvider, + val local: LocalTuple +) : IStorageTuple { + fun extract(amount: BigInteger, simulate: Boolean): T { + return provider.extractStack(id, amount, simulate) } override fun equals(other: Any?): Boolean { - return other is RemoteTuple<*> && other.remote_id == remote_id || other is UUID && other == remote_id + return other is RemoteTuple<*> && other.id == id || other is UUID && other == id } override fun hashCode(): Int { - return remote_id.hashCode() + return id.hashCode() } } @@ -35,31 +37,31 @@ open class VirtualComponent(type: StorageStackType) : IVir override val storageType: StorageStackType = type // удаленный UUID -> Кортеж - protected val remoteByUUID = HashMap>() + protected val remoteByUUID: MutableMap> = HashMap() // локальный UUID -> Локальный кортеж - protected val localByUUID = HashMap>() + protected val localByUUID: MutableMap> = HashMap() // Стак -> Локальный кортеж стака - protected val tuples = HashMap>() + protected val tuples: MutableMap> = HashMap() // ArrayList для скорости работы - protected val listeners = ArrayList>() - protected val set = ObjectArraySet() - protected val consumers = ArrayList>() + protected val listeners: MutableSet> = ObjectArraySet() + protected val set: MutableSet> = ObjectArraySet() + protected val consumers: MutableSet> = ObjectArraySet() protected open fun onAdd(identity: IStorage) {} protected open fun onRemove(identity: IStorage) {} override fun add(identity: IStorage) { if (set.add(identity)) { - if (identity is IStorageView) { + if (identity is IStorageProvider) { identity.addListenerAuto(this) - } else if (identity is IStorageTrigger) { + } else if (identity is IStorageEventProducer) { identity.addListener(this) } - if (identity is IStorageConsumer) { + if (identity is IStorageAcceptor) { consumers.add(identity) } @@ -69,13 +71,13 @@ open class VirtualComponent(type: StorageStackType) : IVir override fun remove(identity: IStorage) { if (set.remove(identity)) { - if (identity is IStorageView) { + if (identity is IStorageProvider) { identity.removeListenerAuto(this) - } else if (identity is IStorageTrigger) { + } else if (identity is IStorageEventProducer) { identity.removeListener(this) } - if (identity is IStorageConsumer) { + if (identity is IStorageAcceptor) { consumers.remove(identity) } @@ -87,7 +89,7 @@ open class VirtualComponent(type: StorageStackType) : IVir return set.contains(identity) } - override fun addListener(listener: IStorageListener): Boolean { + override fun addListener(listener: IStorageEventConsumer): Boolean { if (!listeners.contains(listener)) { listeners.add(listener) return true @@ -96,31 +98,31 @@ open class VirtualComponent(type: StorageStackType) : IVir return false } - override fun removeListener(listener: IStorageListener): Boolean { + override fun removeListener(listener: IStorageEventConsumer): Boolean { return listeners.remove(listener) } - override fun getStack(id: UUID): T { + override fun get(id: UUID): T { return localByUUID[id]?.stack ?: this.storageType.empty } - override fun getStacks(): List> { - return ArrayList>(tuples.size).also { it.addAll(tuples.values) } + override val stacks: Stream> get() { + return ArrayList>(tuples.size).also { it.addAll(tuples.values) }.stream() } @Suppress("unchecked_cast") - override fun addStack(stack: T, id: UUID, provider: IStorageView) { + override fun addStack(stack: T, id: UUID, provider: IStorageProvider) { check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" } val key = stack.key() var local: LocalTuple? = tuples[key] - var oldCount = ImpreciseFraction.ZERO + var oldCount = BigInteger.ZERO val added = local == null if (local == null) { local = LocalTuple(stack.copy() as T, UUID.randomUUID(), ArrayList>(1)) localByUUID[local.id] = local - tuples[key as T] = local + tuples[key] = local } else { oldCount = local.stack.count local.stack.grow(stack.count) @@ -141,12 +143,12 @@ open class VirtualComponent(type: StorageStackType) : IVir } } - override fun changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction) { + override fun changeStack(stack: T, id: UUID, oldCount: BigInteger) { require(stack.count.isPositive) val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") - val diff = stack.count - tuple.obj.count - tuple.obj.count = stack.count + 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) @@ -159,12 +161,12 @@ open class VirtualComponent(type: StorageStackType) : IVir override fun removeStack(stack: T, id: UUID) { val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") - tuple.local.stack.shrink(tuple.obj.count) + tuple.local.stack.shrink(tuple.stack.count) tuple.local.tuples.remove(tuple) remoteByUUID.remove(id) - val a = tuple.local.stack.count <= ImpreciseFraction.ZERO + val a = tuple.local.stack.count <= BigInteger.ZERO val b = tuple.local.tuples.size == 0 if (a || b) { @@ -194,8 +196,8 @@ open class VirtualComponent(type: StorageStackType) : IVir } @Suppress("unchecked_cast") - override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T { - if (amount.isZero) + override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { + if (!amount.isPositive) return this.storageType.empty @Suppress("name_shadowing") @@ -205,18 +207,16 @@ open class VirtualComponent(type: StorageStackType) : IVir if (tuple == null || amount.isZero) return this.storageType.empty - if (!storageType.fractional) - amount = amount.floor() - if (!amount.isPositive) amount = tuple.stack.maxStackSize ?: tuple.stack.count val toExtract = tuple.stack.count.coerceAtMost(amount) - var extracted = ImpreciseFraction.ZERO + var extracted = BigInteger.ZERO val copy = tuple.stack.copy() as T for (remote_tuple in tuple.tuples.let { Array(it.size) { i -> it[i] } }) { - extracted += remote_tuple.extractCount(toExtract - extracted, simulate) + val extractedStack = remote_tuple.extract(toExtract - extracted, simulate) + extracted += extractedStack.count if (extracted >= toExtract) break @@ -238,8 +238,8 @@ open class PoweredComponent(open val parent: IStorageComponen override val storageType: StorageStackType get() = parent.storageType - override fun addListener(listener: IStorageListener) = parent.addListener(listener) - override fun removeListener(listener: IStorageListener) = parent.removeListener(listener) + override fun addListener(listener: IStorageEventConsumer) = parent.addListener(listener) + override fun removeListener(listener: IStorageEventConsumer) = parent.removeListener(listener) @Suppress("unchecked_cast") override fun insertStack(stack: T, simulate: Boolean): T { @@ -273,7 +273,7 @@ open class PoweredComponent(open val parent: IStorageComponen @Suppress("name_shadowing") val stack = stack.copy() as T val oldCount = stack.count - stack.count = extracted / storageType.energyPerOperation + stack.count = (extracted / storageType.energyPerOperation).whole val diff = oldCount - stack.count val newRequired = storageType.energyPerOperation * stack.count val newExtracted = energy.extractEnergyInner(newRequired, true) @@ -291,7 +291,7 @@ open class PoweredComponent(open val parent: IStorageComponen } if (!simulate) { - val requiredNew = (stack.count - leftover.count) * storageType.energyPerOperation + val requiredNew = storageType.energyPerOperation * (stack.count - leftover.count) energy.extractEnergyInner(requiredNew, false) } @@ -302,9 +302,9 @@ open class PoweredComponent(open val parent: IStorageComponen return stack } - override fun getStack(id: UUID) = parent.getStack(id) + override fun get(id: UUID) = parent[id] - override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T { + override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { val required = storageType.energyPerOperation * amount val energy = energyProvider.invoke() val extracted = energy.extractEnergyInner(required, true) @@ -324,7 +324,7 @@ open class PoweredComponent(open val parent: IStorageComponen if (extractedStack.count == amount) { energy.extractEnergyInner(required, false) } else { - energy.extractEnergyInner(extractedStack.count * storageType.energyPerOperation, false) + energy.extractEnergyInner(storageType.energyPerOperation * extractedStack.count, false) } } @@ -332,7 +332,7 @@ open class PoweredComponent(open val parent: IStorageComponen } @Suppress("name_shadowing") - val amount = required / storageType.energyPerOperation + val amount = (required / storageType.energyPerOperation).whole val extractedStack = parent.extractStack(id, amount, simulate) if (extractedStack.isEmpty) { @@ -340,13 +340,13 @@ open class PoweredComponent(open val parent: IStorageComponen } if (!simulate) { - energy.extractEnergyInner(extractedStack.count * storageType.energyPerOperation, false) + energy.extractEnergyInner(storageType.energyPerOperation * extractedStack.count, false) } return extractedStack } - override fun getStacks() = parent.getStacks() + override val stacks get() = parent.stacks } /** @@ -358,8 +358,8 @@ open class PoweredVirtualComponent(override val parent: IVirt 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: IStorageView) = parent.addStack(stack, id, provider) - override fun changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction) = parent.changeStack(stack, id, oldCount) + 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)