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
This commit is contained in:
DBotThePony 2022-06-07 18:45:26 +07:00
parent 4682d8116d
commit cb5cb44848
Signed by: DBot
GPG Key ID: DCC23B5715498507
19 changed files with 279 additions and 263 deletions

View File

@ -103,7 +103,7 @@ public final class OverdriveThatMatters {
private void setup(final FMLCommonSetupEvent event) { private void setup(final FMLCommonSetupEvent event) {
MatteryNetworking.register(); 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")) { if (ModList.get().isLoaded("mekanism")) {
MinecraftForge.EVENT_BUS.register(QIOKt.class); MinecraftForge.EVENT_BUS.register(QIOKt.class);

View File

@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.server.level.ServerLevel 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.capability.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.ImpreciseFraction 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.core.plus
import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.GraphNodeListener 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.registry.MNames
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.math.BigInteger
import java.util.* import java.util.*
import java.util.stream.Stream
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashMap import kotlin.collections.HashMap
@ -186,9 +188,9 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
override val storageType: StorageStackType<ItemStackWrapper> override val storageType: StorageStackType<ItemStackWrapper>
get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE()
private val listeners = ArrayList<IStorageListener<ItemStackWrapper>>() private val listeners = ArrayList<IStorageEventConsumer<ItemStackWrapper>>()
override fun addListener(listener: IStorageListener<ItemStackWrapper>): Boolean { override fun addListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
if (!listeners.contains(listener)) { if (!listeners.contains(listener)) {
listeners.add(listener) listeners.add(listener)
return true return true
@ -197,7 +199,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
return false return false
} }
override fun removeListener(listener: IStorageListener<ItemStackWrapper>): Boolean { override fun removeListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
return listeners.remove(listener) 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") val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot")
scannedMap.children.remove(slot) scannedMap.children.remove(slot)
val count = scannedMap.stack.count val count = scannedMap.stack.count
scannedMap.stack.count -= item.stack.count scannedMap.stack.count -= item.stack.count.toBigInteger()
if (scannedMap.stack.count.isPositive) { if (scannedMap.stack.count.isPositive) {
for (listener in listeners) { for (listener in listeners) {
@ -238,7 +240,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
val oldCount = scannedMap.stack.count val oldCount = scannedMap.stack.count
item.stack.count += diff item.stack.count += diff
scannedMap.stack.count += diff scannedMap.stack.count += diff.toBigInteger()
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(scannedMap.stack, scannedMap.id, oldCount) listener.changeStack(scannedMap.stack, scannedMap.id, oldCount)
@ -252,7 +254,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
val key = storageStack.key() val key = storageStack.key()
var tuple: TrackedTuple? = tuples[key] var tuple: TrackedTuple? = tuples[key]
val added = tuple == null val added = tuple == null
var oldCount = ImpreciseFraction.ZERO var oldCount = BigInteger.ZERO
if (added) { if (added) {
tuple = TrackedTuple(storageStack, UUID.randomUUID()) tuple = TrackedTuple(storageStack, UUID.randomUUID())
@ -260,7 +262,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
tuples[key] = tuple tuples[key] = tuple
} else { } else {
oldCount = tuple!!.stack.count oldCount = tuple!!.stack.count
tuple.stack.count += stack.count tuple.stack.count += stack.count.toBigInteger()
} }
tuple.children[slot] = SlotTuple(slot, stack.copy()) 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)) if (energy.batteryLevel.isZero || !filter.match(stack.item))
return stack return stack
val maxPossibleDemand = stack.count * ITEM_STORAGE.energyPerOperation val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * stack.count
val maxExtractEnergy = energy.extractEnergyInner(maxPossibleDemand, true) val maxExtractEnergy = energy.extractEnergyInner(maxPossibleDemand, true)
var leftover: ItemStackWrapper var leftover: ItemStackWrapper
@ -352,7 +354,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
leftover = stack.copy() leftover = stack.copy()
} else { } else {
leftover = stack.copy().also { 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)) leftover = ItemStackWrapper(parent.insertItem(slot, leftover.stack, simulate))
if (oldCount != leftover.count && !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) scan(slot)
} }
@ -373,22 +375,22 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
return leftover return leftover
} }
override fun getStack(id: UUID): ItemStackWrapper { override fun get(id: UUID): ItemStackWrapper {
return index[id]?.stack ?: ItemStackWrapper.EMPTY return index[id]?.stack ?: ItemStackWrapper.EMPTY
} }
override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): ItemStackWrapper { override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStackWrapper {
@Suppress("NAME_SHADOWING")
var amount = amount.floor()
if (!amount.isPositive) if (!amount.isPositive)
return ItemStackWrapper.EMPTY return ItemStackWrapper.EMPTY
val maxPossibleDemand = amount * OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * amount
val maxExtractEnergy = energy.extractEnergyInner(maxPossibleDemand, true) val maxExtractEnergy = energy.extractEnergyInner(maxPossibleDemand, true)
@Suppress("NAME_SHADOWING")
var amount = amount
if (maxPossibleDemand != maxExtractEnergy) { if (maxPossibleDemand != maxExtractEnergy) {
amount = (maxExtractEnergy / OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation).floor() amount = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole
} }
val intAmount = amount.toLong() val intAmount = amount.toLong()
@ -433,18 +435,18 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
return ItemStackWrapper.EMPTY return ItemStackWrapper.EMPTY
} }
copy.count = ImpreciseFraction(totalExtracted) copy.count = totalExtracted.toBigInteger()
return copy return copy
} }
override fun getStacks(): Collection<IStorageTuple<ItemStackWrapper>> { override val stacks: Stream<IStorageTuple<ItemStackWrapper>> get() {
val listing = ArrayList<IStorageTuple<ItemStackWrapper>>(index.size) val listing = ArrayList<IStorageTuple<ItemStackWrapper>>(index.size)
for (tuple in index.values) { for (tuple in index.values) {
listing.add(StorageTuple(tuple.id, tuple.stack)) listing.add(StorageTuple(tuple.id, tuple.stack))
} }
return listing return listing.stream()
} }
} }
} }

View File

@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.server.level.ServerLevel 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.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
import java.math.BigInteger
import java.util.* import java.util.*
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.collections.HashMap
import kotlin.collections.HashSet import kotlin.collections.HashSet
abstract class AbstractStorageImportExport<T>( abstract class AbstractStorageImportExport<T>(
@ -190,17 +189,17 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState)
return stack return stack
val view = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: 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 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) if (simulate)
return leftover.stack 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 return leftover.stack
} }
@ -265,7 +264,7 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState)
class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState), AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState),
IStorageListener<ItemStackWrapper> { IStorageEventConsumer<ItemStackWrapper> {
override val defaultDisplayName: Component override val defaultDisplayName: Component
get() = MACHINE_NAME get() = MACHINE_NAME
@ -275,7 +274,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
private val relevantTuples = HashSet<UUID>() private val relevantTuples = HashSet<UUID>()
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView<ItemStackWrapper>) { override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) {
if (!filter.match(stack.item)) { if (!filter.match(stack.item)) {
return return
} }
@ -283,7 +282,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
relevantTuples.add(id) relevantTuples.add(id)
} }
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: ImpreciseFraction) { override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) {
// no-op // no-op
} }
@ -296,7 +295,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
val component = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: return@ItemFilter val component = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: return@ItemFilter
for (tuple in component.getStacks()) { for (tuple in component.stacks) {
addStack(tuple, component) addStack(tuple, component)
} }
@ -315,7 +314,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
private val exportStacks: Stream<Pair<UUID, ItemStackWrapper>> private val exportStacks: Stream<Pair<UUID, ItemStackWrapper>>
get() { get() {
val view = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: return Stream.empty() 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) { override fun saveAdditional(nbt: CompoundTag) {
@ -353,9 +352,9 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
continue 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 continue
} }
@ -369,7 +368,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
if (leftover.count != exportAmount) { if (leftover.count != exportAmount) {
hit = true 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) resolved.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, false)
energy.extractStepInner(ITEM_STORAGE.energyPerOperation, exportAmount, false) energy.extractStepInner(ITEM_STORAGE.energyPerOperation, exportAmount, false)
break break
@ -387,7 +386,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
} }
companion object { 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 val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_EXPORTER}")
private const val INTERVAL = 5 private const val INTERVAL = 5
const val MAX_FILTERS = 6 * 3 const val MAX_FILTERS = 6 * 3

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.capability
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import java.math.BigInteger
// IEnergyStorage for direct compat with Forge Energy // IEnergyStorage for direct compat with Forge Energy
interface IMatteryEnergyStorage : IEnergyStorage { interface IMatteryEnergyStorage : IEnergyStorage {
@ -212,3 +213,27 @@ fun IMatteryEnergyStorage.extractStepInner(base: ImpreciseFraction, multiplier:
fun IMatteryEnergyStorage.extractStepOuter(base: ImpreciseFraction, multiplier: Int, simulate: Boolean): Int { fun IMatteryEnergyStorage.extractStepOuter(base: ImpreciseFraction, multiplier: Int, simulate: Boolean): Int {
return (extractEnergyOuter(base * multiplier, simulate) / base).toInt() 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
}

View File

@ -9,6 +9,7 @@ import ru.dbotthepony.mc.otm.storage.IStorageComponent
import ru.dbotthepony.mc.otm.storage.IStorageStack import ru.dbotthepony.mc.otm.storage.IStorageStack
import ru.dbotthepony.mc.otm.storage.IStorageTuple import ru.dbotthepony.mc.otm.storage.IStorageTuple
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import java.math.BigInteger
import java.util.* import java.util.*
interface IItemMatteryDrive : IMatteryDrive<ItemStackWrapper> { interface IItemMatteryDrive : IMatteryDrive<ItemStackWrapper> {
@ -25,19 +26,13 @@ interface IItemMatteryDrive : IMatteryDrive<ItemStackWrapper> {
fun findItems(stack: ItemStack): IStorageTuple<ItemStackWrapper>? fun findItems(stack: ItemStack): IStorageTuple<ItemStackWrapper>?
} }
interface IItemViewListener {
fun addViewItem(stack: ItemStack, id_upstream: UUID)
fun changeViewItem(id_upstream: UUID, new_count: Int)
fun removeViewItem(id_upstream: UUID)
}
interface IMatteryDrive<T : IStorageStack> : IStorageComponent<T> { interface IMatteryDrive<T : IStorageStack> : IStorageComponent<T> {
val uuid: UUID val uuid: UUID
var isDirty: Boolean var isDirty: Boolean
val storedCount: ImpreciseFraction val storedCount: BigInteger
val driveCapacity: ImpreciseFraction val driveCapacity: BigInteger
// not extending INBTSerializable to avoid serializing it as forgecaps // not extending INBTSerializable to avoid serializing it as forgecaps
fun serializeNBT(): CompoundTag fun serializeNBT(): CompoundTag

View File

@ -1,21 +1,26 @@
package ru.dbotthepony.mc.otm.capability.drive 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.Object2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmOverloads
import java.util.UUID import java.util.UUID
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag 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.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.ifHas
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
import java.math.BigInteger
import java.util.ArrayList import java.util.ArrayList
import java.util.HashSet import java.util.HashSet
import java.util.stream.Stream
abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor( abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor(
override var driveCapacity: ImpreciseFraction, override var driveCapacity: BigInteger,
override val uuid: UUID = UUID.randomUUID(), override val uuid: UUID = UUID.randomUUID(),
var maxDifferentStacks: Int = 0xFFFF var maxDifferentStacks: Int = 0xFFFF
) : IMatteryDrive<T> { ) : IMatteryDrive<T> {
@ -34,13 +39,13 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
var storedDifferentStacks = 0 var storedDifferentStacks = 0
protected set protected set
override var storedCount = ImpreciseFraction.ZERO override var storedCount: BigInteger = BigInteger.ZERO
protected set protected set
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
override fun insertStack(stack: T, simulate: Boolean): T { override fun insertStack(stack: T, simulate: Boolean): T {
val maxInsert = driveCapacity.minus(storedCount).coerceAtMost(stack.count) 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 key = stack.key()
val tuple = tuples[key] val tuple = tuples[key]
@ -91,20 +96,18 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T { override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
@Suppress("NAME_SHADOWING")
var amount = amount
val get = tuplesByID[id] ?: return storageType.empty val get = tuplesByID[id] ?: return storageType.empty
if (!storageType.fractional) @Suppress("NAME_SHADOWING")
amount = amount.floor() var amount = amount
if (!amount.isPositive) if (!amount.isPositive)
amount = get.stack.maxStackSize ?: get.stack.count amount = get.stack.maxStackSize ?: get.stack.count
amount = amount.coerceAtMost(get.stack.count) amount = amount.coerceAtMost(get.stack.count)
if (amount <= ImpreciseFraction.ZERO) if (amount <= BigInteger.ZERO)
return storageType.empty return storageType.empty
val copy = get.stack.copy() as T val copy = get.stack.copy() as T
@ -165,11 +168,11 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
override fun deserializeNBT(nbt: CompoundTag) { override fun deserializeNBT(nbt: CompoundTag) {
tuples.clear() tuples.clear()
tuplesByID.clear() tuplesByID.clear()
storedCount = ImpreciseFraction.ZERO storedCount = BigInteger.ZERO
storedDifferentStacks = 0 storedDifferentStacks = 0
nbt.ifHas("capacity") { nbt.ifHas("capacity") {
driveCapacity = ImpreciseFraction.deserializeNBT(it) driveCapacity = BigInteger(it)
} }
maxDifferentStacks = nbt.getInt("max_different_stacks") maxDifferentStacks = nbt.getInt("max_different_stacks")
@ -191,21 +194,21 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
} }
} }
override fun getStack(id: UUID): T { override fun get(id: UUID): T {
return tuplesByID[id]?.stack ?: storageType.empty return tuplesByID[id]?.stack ?: storageType.empty
} }
override fun getStacks(): List<IStorageTuple<T>> { override val stacks: Stream<IStorageTuple<T>> get() {
return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) } return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) }.stream()
} }
protected val listeners = HashSet<IStorageListener<T>>() protected val listeners = ObjectArraySet<IStorageEventConsumer<T>>()
override fun addListener(listener: IStorageListener<T>): Boolean { override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
return listeners.add(listener) return listeners.add(listener)
} }
override fun removeListener(listener: IStorageListener<T>): Boolean { override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
return listeners.remove(listener) return listeners.remove(listener)
} }
} }

View File

@ -28,13 +28,15 @@ import java.util.ArrayList
* Why? * Why?
* *
* There are several reasons: * 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 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 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 * 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" * 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 * 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 * 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") @Suppress("unused")
object DrivePool { object DrivePool {

View File

@ -8,20 +8,23 @@ import net.minecraft.world.item.Items
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import net.minecraftforge.registries.RegistryManager import net.minecraftforge.registries.RegistryManager
import ru.dbotthepony.mc.otm.OverdriveThatMatters 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.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.serializeNBT
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
import ru.dbotthepony.mc.otm.storage.IStorageTuple import ru.dbotthepony.mc.otm.storage.IStorageTuple
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import ru.dbotthepony.mc.otm.storage.StorageStackType import ru.dbotthepony.mc.otm.storage.StorageStackType
import ru.dbotthepony.mc.otm.storage.key import ru.dbotthepony.mc.otm.storage.key
import java.math.BigInteger
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDrive { class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDrive {
constructor(capacity: ImpreciseFraction, max_different_stacks: Int) : super(capacity, maxDifferentStacks = max_different_stacks) constructor(capacity: BigInteger, 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: BigInteger, uuid: UUID, max_different_stacks: Int) : super(capacity, uuid, max_different_stacks)
constructor(capacity: ImpreciseFraction, uuid: UUID) : super(capacity, uuid) constructor(capacity: BigInteger, uuid: UUID) : super(capacity, uuid)
constructor(capacity: ImpreciseFraction) : super(capacity) constructor(capacity: BigInteger) : super(capacity)
override val storageType: StorageStackType<ItemStackWrapper> = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() override val storageType: StorageStackType<ItemStackWrapper> = OverdriveThatMatters.INSTANCE.ITEM_STORAGE()
@ -49,7 +52,7 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(tag.getString("item"))) val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(tag.getString("item")))
if (item != null && item !== Items.AIR) { if (item != null && item !== Items.AIR) {
val count = ImpreciseFraction.deserializeNBT(tag["count"]) val count = BigInteger(tag["count"])
val itemstack = ItemStack(item, 1) val itemstack = ItemStack(item, 1)
itemstack.tag = tag["data"] as? CompoundTag itemstack.tag = tag["data"] as? CompoundTag
return ItemStackWrapper(itemstack, copy = false).also { it.count = count } return ItemStackWrapper(itemstack, copy = false).also { it.count = count }
@ -76,6 +79,6 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
companion object { companion object {
@JvmField @JvmField
val DUMMY = ItemMatteryDrive(ImpreciseFraction(0), UUID(0L, 0L), 0) val DUMMY = ItemMatteryDrive(BigInteger.ZERO, UUID(0L, 0L), 0)
} }
} }

View File

@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.compat.mekanism
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import mekanism.api.math.MathUtils
import mekanism.common.content.qio.QIOFrequency import mekanism.common.content.qio.QIOFrequency
import mekanism.common.content.qio.QIOFrequency.QIOItemTypeData import mekanism.common.content.qio.QIOFrequency.QIOItemTypeData
import mekanism.common.lib.frequency.Frequency 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.MatteryCapability
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
import ru.dbotthepony.mc.otm.core.ImpreciseFraction 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.core.toImpreciseFraction
import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
import java.math.BigInteger
import java.util.UUID import java.util.UUID
import java.util.stream.Stream
private val QIO_LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "item_storage") private val QIO_LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "item_storage")
@ -46,14 +48,14 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
private val index = Object2ObjectAVLTreeMap<UUID, QIOTuple>() private val index = Object2ObjectAVLTreeMap<UUID, QIOTuple>()
private val tracked = HashMap<HashedItem, QIOTuple>() private val tracked = HashMap<HashedItem, QIOTuple>()
private val listeners = ArrayList<IStorageListener<ItemStackWrapper>>() private val listeners = ArrayList<IStorageEventConsumer<ItemStackWrapper>>()
override fun getStack(id: UUID): ItemStackWrapper { override fun get(id: UUID): ItemStackWrapper {
return index[id]?.stack ?: ItemStackWrapper.EMPTY return index[id]?.stack ?: ItemStackWrapper.EMPTY
} }
override fun getStacks(): Collection<IStorageTuple<ItemStackWrapper>> { override val stacks: Stream<IStorageTuple<ItemStackWrapper>> get() {
return ArrayList<IStorageTuple<ItemStackWrapper>>(index.size).also { it.addAll(index.values) } return ArrayList<IStorageTuple<ItemStackWrapper>>(index.size).also { it.addAll(index.values) }.stream()
} }
override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper { override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper {
@ -81,11 +83,11 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
} }
val inserted = stack.copy() val inserted = stack.copy()
inserted.count = (parent.totalItemCountCapacity - parent.totalItemCount).toImpreciseFraction().coerceAtMost(stack.count) inserted.count = (parent.totalItemCountCapacity - parent.totalItemCount).toBigInteger().coerceAtMost(stack.count)
return inserted return inserted
} }
override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): ItemStackWrapper { override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStackWrapper {
// Because there is no simulate method on QIO array, we have to simulate it by ourselves. // Because there is no simulate method on QIO array, we have to simulate it by ourselves.
// ASSUMPTION: We can ALWAYS remove items from QIO grid. // ASSUMPTION: We can ALWAYS remove items from QIO grid.
@ -128,7 +130,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
return copy return copy
} }
override fun addListener(listener: IStorageListener<ItemStackWrapper>): Boolean { override fun addListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
if (!listeners.contains(listener)) { if (!listeners.contains(listener)) {
listeners.add(listener) listeners.add(listener)
return true return true
@ -137,7 +139,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
return false return false
} }
override fun removeListener(listener: IStorageListener<ItemStackWrapper>): Boolean { override fun removeListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
return listeners.remove(listener) return listeners.remove(listener)
} }
@ -149,14 +151,14 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
if (local.stack.count.toLong() != value.count) { if (local.stack.count.toLong() != value.count) {
val oldCount = local.stack.count val oldCount = local.stack.count
local.stack.count = value.count.toImpreciseFraction() local.stack.count = value.count.toBigInteger()
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(local, oldCount) listener.changeStack(local, oldCount)
} }
} }
} else { } else {
val tuple = QIOTuple(at, ItemStackWrapper(at.stack).also { it.count = value.count.toImpreciseFraction() }, UUID.randomUUID(), mark) val tuple = QIOTuple(at, ItemStackWrapper(at.stack).also { it.count = value.count.toBigInteger() }, UUID.randomUUID(), mark)
index[tuple.id] = tuple index[tuple.id] = tuple
for (listener in listeners) { for (listener in listeners) {

View File

@ -318,6 +318,11 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
operator fun times(other: Long): ImpreciseFraction = times(ImpreciseFraction(other)) operator fun times(other: Long): ImpreciseFraction = times(ImpreciseFraction(other))
operator fun div(other: Long): ImpreciseFraction = div(ImpreciseFraction(other)) operator fun div(other: Long): ImpreciseFraction = div(ImpreciseFraction(other))
operator fun plus(other: BigInteger): ImpreciseFraction = plus(ImpreciseFraction(other))
operator fun minus(other: BigInteger): ImpreciseFraction = minus(ImpreciseFraction(other))
operator fun times(other: BigInteger): ImpreciseFraction = times(ImpreciseFraction(other))
operator fun div(other: BigInteger): ImpreciseFraction = div(ImpreciseFraction(other))
operator fun unaryMinus(): ImpreciseFraction { operator fun unaryMinus(): ImpreciseFraction {
return ImpreciseFraction(-whole, -decimal) return ImpreciseFraction(-whole, -decimal)
} }

View File

@ -0,0 +1,20 @@
package ru.dbotthepony.mc.otm.core
import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.Tag
import java.math.BigInteger
inline val BigInteger.isZero get() = this == BigInteger.ZERO
inline val BigInteger.isPositive get() = this > 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)
}

View File

@ -5,7 +5,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity 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.addPreWorldTicker
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability

View File

@ -28,15 +28,12 @@ import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.ifHas import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
import java.math.BigInteger
import java.util.* import java.util.*
class PortableCondensationDriveItem(capacity: Int) : class PortableCondensationDriveItem(capacity: Int) :
Item(Properties().stacksTo(1).tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB)) { Item(Properties().stacksTo(1).tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB)) {
val capacity: ImpreciseFraction val capacity: BigInteger = capacity.toBigInteger()
init {
this.capacity = ImpreciseFraction(capacity)
}
private inner class DriveCapability(private val stack: ItemStack) : ICapabilityProvider { private inner class DriveCapability(private val stack: ItemStack) : ICapabilityProvider {
private var uuid: UUID? = null private var uuid: UUID? = null
@ -48,10 +45,10 @@ class PortableCondensationDriveItem(capacity: Int) :
val uuid = uuid val uuid = uuid
DrivePool.get(uuid!!, { tag: CompoundTag? -> return@of DrivePool.get(uuid!!, { tag: CompoundTag? ->
val drive = ItemMatteryDrive(capacity, uuid) val drive = ItemMatteryDrive(capacity, uuid)
drive.deserializeNBT(tag!!) drive.deserializeNBT(tag!!)
drive return@get drive
}, { ItemMatteryDrive(capacity, uuid) }) }, { ItemMatteryDrive(capacity, uuid) })
} }

View File

@ -20,12 +20,14 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
import ru.dbotthepony.mc.otm.core.ImpreciseFraction 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.readImpreciseFraction
import ru.dbotthepony.mc.otm.core.writeImpreciseFraction import ru.dbotthepony.mc.otm.core.writeImpreciseFraction
import ru.dbotthepony.mc.otm.menu.FormattingHelper import ru.dbotthepony.mc.otm.menu.FormattingHelper
import ru.dbotthepony.mc.otm.network.MatteryNetworking import ru.dbotthepony.mc.otm.network.MatteryNetworking
import ru.dbotthepony.mc.otm.orNull import ru.dbotthepony.mc.otm.orNull
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import java.math.BigInteger
import java.util.function.Supplier import java.util.function.Supplier
internal var building = false internal var building = false
@ -174,7 +176,7 @@ fun canDecompose(stack: ItemStack): Boolean {
return canDecompose(stack.item) && return canDecompose(stack.item) &&
(stack.getCapability(MatteryCapability.MATTER).orNull()?.storedMatter ?: ImpreciseFraction.ZERO).isZero && (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 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() val drive = stack.getCapability(MatteryCapability.DRIVE).orNull()
if (drive != null && drive.storageType === OverdriveThatMatters.INSTANCE.ITEM_STORAGE()) { if (drive != null && drive.storageType === OverdriveThatMatters.INSTANCE.ITEM_STORAGE()) {
for (item in (drive as IMatteryDrive<ItemStackWrapper>).getStacks()) { for (item in (drive as IMatteryDrive<ItemStackWrapper>).stacks) {
val tuple = getMatterValue(item.stack.stack, level + 1) val tuple = getMatterValue(item.stack.stack, level + 1)
if (!tuple.isZero) { if (!tuple.isZero) {

View File

@ -18,9 +18,10 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.network.MatteryNetworking import ru.dbotthepony.mc.otm.network.MatteryNetworking
import ru.dbotthepony.mc.otm.network.SetCarriedPacket import ru.dbotthepony.mc.otm.network.SetCarriedPacket
import ru.dbotthepony.mc.otm.storage.IStorageComponent import ru.dbotthepony.mc.otm.storage.IStorageComponent
import ru.dbotthepony.mc.otm.storage.IStorageListener import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
import ru.dbotthepony.mc.otm.storage.IStorageView import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import java.math.BigInteger
import java.util.* import java.util.*
import java.util.function.Supplier 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. * Creates a virtual, slotless container for Player to interaction with.
*/ */
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageListener<ItemStackWrapper> { open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer<ItemStackWrapper> {
data class NetworkedItem constructor(val id: Int, val stack: ItemStack, val upstreamId: UUID? = null) data class NetworkedItem constructor(val id: Int, val stack: ItemStack, val upstreamId: UUID? = null)
protected var nextStackID = 0 protected var nextStackID = 0
@ -230,8 +231,8 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
return localState.values.size return localState.values.size
} }
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView<ItemStackWrapper>) = addObject(stack.stack, id) override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) = addObject(stack.stack, id)
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: ImpreciseFraction) = changeObject(id, stack.count.toInt()) override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) = changeObject(id, stack.count.toInt())
protected fun network(fn: () -> Any) { protected fun network(fn: () -> Any) {
if (!remote) { if (!remote) {
@ -310,13 +311,13 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
else else
Math.max(1, state.stack.maxStackSize / 2) 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) { if (!extracted.isEmpty) {
val (_, remaining) = menu.quickMoveToInventory(extracted.stack, false) val (_, remaining) = menu.quickMoveToInventory(extracted.stack, false)
if (remaining.count != extracted.count.toInt()) { 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 else
(state.stack.count / 2).coerceAtMost(state.stack.maxStackSize / 2).coerceAtLeast(1) (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 menu.carried = extracted.stack
MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
menu.setRemoteCarried(menu.carried.copy()) menu.setRemoteCarried(menu.carried.copy())

View File

@ -1,7 +1,9 @@
package ru.dbotthepony.mc.otm.storage package ru.dbotthepony.mc.otm.storage
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import java.math.BigInteger
import java.util.* import java.util.*
import java.util.stream.Stream
/** /**
* Represents a stack in storage system. * Represents a stack in storage system.
@ -19,51 +21,36 @@ interface IStorageStack {
fun copy(): IStorageStack fun copy(): IStorageStack
/** /**
* Despite count being [ImpreciseFraction], far not all items can be "fraction" * Size of this storage stack
* into pieces (e.g. ItemStacks).
* *
* Consult [StorageStackType.fractional] to see if item has actual fractions. * This is overriden in subclasses
*
* 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.
*/ */
var count: ImpreciseFraction var count: BigInteger
val isEmpty: Boolean val isEmpty: Boolean
/** /**
* @return max stack size for this stack object, * @return max stack size for this stack object,
* null if unlimited (default) * null if unlimited (default)
*/ */
val maxStackSize: ImpreciseFraction? get() = null val maxStackSize: BigInteger? get() = null
/** /**
* Increase [count] by [amount] * Increase [count] by [amount]
*/ */
fun grow(amount: ImpreciseFraction) { fun grow(amount: BigInteger) {
count += amount count += amount
} }
/** /**
* Decrease [count] by [amount] * Decrease [count] by [amount]
*/ */
fun shrink(amount: ImpreciseFraction) { fun shrink(amount: BigInteger) {
count -= amount 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 : IStorageStack> T.key(): T { fun <T : IStorageStack> 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<T : IStorageStack> {
val storageType: StorageStackType<T> val storageType: StorageStackType<T>
} }
interface IStorageTrigger<T : IStorageStack> : IStorage<T> { /**
* Generates events for [IStorageEventConsumer]
*/
interface IStorageEventProducer<T : IStorageStack> : IStorage<T> {
/** /**
* [listener] is [IStorageListener] which want to subscribe to our events * [listener] is [IStorageEventConsumer] which want to subscribe to our events
*/ */
fun addListener(listener: IStorageListener<T>): Boolean fun addListener(listener: IStorageEventConsumer<T>): 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<T>): Boolean fun removeListener(listener: IStorageEventConsumer<T>): Boolean
} }
interface IStorageConsumer<T : IStorageStack> : IStorage<T> { /**
* Consumes events produced by [IStorageEventConsumer]
*/
interface IStorageEventConsumer<T : IStorageStack> {
/**
* Fired on whenever an object is added (to listener) we subscribed to
*/
fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>)
/**
* 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<T : IStorageStack> : IStorage<T> {
/** /**
* Inserts an item into system. * Inserts an item into system.
* @return leftover, might equal to [stack] if no items were inserted * @return leftover, might equal to [stack] if no items were inserted
@ -96,12 +106,14 @@ interface IStorageConsumer<T : IStorageStack> : IStorage<T> {
fun insertStack(stack: T, simulate: Boolean): T fun insertStack(stack: T, simulate: Boolean): T
} }
interface IStorageView<T : IStorageStack> : IStorageTrigger<T> { interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
/** /**
* @param id identifier of stack * @param id identifier of stack
* @return stored object (not a copy). Do not edit it. * @return stored object (not a copy). Do not edit it.
*/ */
fun getStack(id: UUID): T operator fun get(id: UUID): T
val stacks: Stream<IStorageTuple<T>>
/** /**
* If tuple does not exist, returns empty stack * If tuple does not exist, returns empty stack
@ -111,31 +123,11 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
* @param simulate whenever to simulate the action or not * @param simulate whenever to simulate the action or not
* @return copy of object, with amount of units actually extracted * @return copy of object, with amount of units actually extracted
*/ */
fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T fun extractStack(id: UUID, amount: BigInteger, 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 addListenerAuto(listener: IStorageEventConsumer<T>): Boolean {
* 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<IStorageTuple<T>>
fun addListenerAuto(listener: IStorageListener<T>): Boolean {
if (addListener(listener)) { if (addListener(listener)) {
for (stack in getStacks()) { for (stack in stacks) {
listener.addStack(stack.stack, stack.id, this) listener.addStack(stack.stack, stack.id, this)
} }
@ -145,9 +137,9 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
return false return false
} }
fun removeListenerAuto(listener: IStorageListener<T>): Boolean { fun removeListenerAuto(listener: IStorageEventConsumer<T>): Boolean {
if (removeListener(listener)) { if (removeListener(listener)) {
for (stack in getStacks()) { for (stack in stacks) {
listener.removeStack(stack.stack, stack.id) listener.removeStack(stack.stack, stack.id)
} }
@ -158,33 +150,16 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
} }
} }
interface IStorageListener<T : IStorageStack> { fun <T : IStorageStack> IStorageEventConsumer<T>.changeStack(tuple: IStorageTuple<T>, oldCount: BigInteger) {
/** changeStack(tuple.stack, tuple.id, oldCount)
* Fired on whenever an object is added (to listener) we subscribed to }
*/
fun addStack(stack: T, id: UUID, provider: IStorageView<T>)
fun addStack(tuple: IStorageTuple<T>, provider: IStorageView<T>) { fun <T : IStorageStack> IStorageEventConsumer<T>.addStack(tuple: IStorageTuple<T>, provider: IStorageProvider<T>) {
addStack(tuple.stack, tuple.id, provider) addStack(tuple.stack, tuple.id, provider)
} }
/** fun <T : IStorageStack> IStorageEventConsumer<T>.removeStack(tuple: IStorageTuple<T>) {
* Fired on whenever an object is changes on listener we subscribed to removeStack(tuple.stack, tuple.id)
*/
fun changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction)
fun changeStack(tuple: IStorageTuple<T>, 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<T>) {
removeStack(tuple.stack, tuple.id)
}
} }
interface IStorageTuple<T : IStorageStack> { interface IStorageTuple<T : IStorageStack> {
@ -193,12 +168,13 @@ interface IStorageTuple<T : IStorageStack> {
} }
class StorageTuple<T : IStorageStack>(override val id: UUID, override val stack: T) : IStorageTuple<T> class StorageTuple<T : IStorageStack>(override val id: UUID, override val stack: T) : IStorageTuple<T>
interface IStorageComponent<T : IStorageStack> : IStorageView<T>, IStorageConsumer<T>
interface IStorageComponent<T : IStorageStack> : IStorageProvider<T>, IStorageAcceptor<T>
/** /**
* Component which (most time) proxy other components (combine their contents into single view) * Component which (most time) proxy other components (combine their contents into single view)
*/ */
interface IVirtualStorageComponent<T : IStorageStack> : IStorageComponent<T>, IStorageListener<T> { interface IVirtualStorageComponent<T : IStorageStack> : IStorageComponent<T>, IStorageEventConsumer<T> {
fun add(identity: IStorage<T>) fun add(identity: IStorage<T>)
fun remove(identity: IStorage<T>) fun remove(identity: IStorage<T>)
fun contains(identity: IStorage<T>): Boolean fun contains(identity: IStorage<T>): Boolean

View File

@ -6,6 +6,8 @@ import net.minecraftforge.registries.ForgeRegistries
import net.minecraftforge.registries.ForgeRegistry import net.minecraftforge.registries.ForgeRegistry
import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.ApiStatus
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.isPositive
import java.math.BigInteger
/** /**
* constructors always copy its input. * constructors always copy its input.
@ -21,8 +23,7 @@ class ItemStackWrapper : IStorageStack {
val registryName get() = item.item.registryName!! val registryName get() = item.item.registryName!!
private val hash: Int private val hash: Int
override var count: ImpreciseFraction override var count: BigInteger
set(value) { field = value.floor() }
/** /**
* [copy] as false is used internally for fast index construction, do not specify * [copy] as false is used internally for fast index construction, do not specify
@ -36,7 +37,7 @@ class ItemStackWrapper : IStorageStack {
this.item = item this.item = item
} }
this.count = ImpreciseFraction(item.count) this.count = BigInteger.valueOf(item.count.toLong())
if (copy) { if (copy) {
this.item.count = 1 this.item.count = 1
@ -72,7 +73,7 @@ class ItemStackWrapper : IStorageStack {
return hash * 31 + count.hashCode() 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 override val isEmpty: Boolean get() = item.isEmpty || !count.isPositive

View File

@ -13,24 +13,9 @@ open class StorageStackType<T : IStorageStack>(
* Speculated energy required per operation on stack with size of 1 * Speculated energy required per operation on stack with size of 1
*/ */
open val energyPerOperation: ImpreciseFraction, 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 { open fun energyPerOperation(stack: T): ImpreciseFraction {
return stack.count * energyPerOperation return energyPerOperation * stack.count
} }
open fun energyPerStorage(stack: T) = energyPerOperation(stack) open fun energyPerStorage(stack: T) = energyPerOperation(stack)
@ -56,10 +41,9 @@ object StorageRegistry {
fun <T : IStorageStack> register( fun <T : IStorageStack> register(
identity: Class<T>, identity: Class<T>,
empty: T, empty: T,
energyPerOperation: ImpreciseFraction, energyPerOperation: ImpreciseFraction
fractional: Boolean
): StorageStackType<T> { ): StorageStackType<T> {
return register(StorageStackType(identity, empty, energyPerOperation, fractional)) as StorageStackType<T> return register(StorageStackType(identity, empty, energyPerOperation)) as StorageStackType<T>
} }
@JvmStatic @JvmStatic

View File

@ -1,29 +1,31 @@
package ru.dbotthepony.mc.otm.storage 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 it.unimi.dsi.fastutil.objects.ObjectArraySet
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.core.ImpreciseFraction 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.*
import java.util.stream.Stream
import kotlin.collections.HashMap import kotlin.collections.HashMap
class RemoteTuple<T : IStorageStack>(val obj: T, val remote_id: UUID, val provider: IStorageView<T>, val local: LocalTuple<T>) { class RemoteTuple<T : IStorageStack>(
fun extract(amount: ImpreciseFraction, simulate: Boolean): T { override val stack: T,
return provider.extractStack(remote_id, amount, simulate) override val id: UUID,
} val provider: IStorageProvider<T>,
val local: LocalTuple<T>
fun extractCount(amount: ImpreciseFraction, simulate: Boolean): ImpreciseFraction { ) : IStorageTuple<T> {
return provider.extractStackCount(remote_id, amount, simulate) fun extract(amount: BigInteger, simulate: Boolean): T {
return provider.extractStack(id, amount, simulate)
} }
override fun equals(other: Any?): Boolean { 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 { override fun hashCode(): Int {
return remote_id.hashCode() return id.hashCode()
} }
} }
@ -35,31 +37,31 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
override val storageType: StorageStackType<T> = type override val storageType: StorageStackType<T> = type
// удаленный UUID -> Кортеж // удаленный UUID -> Кортеж
protected val remoteByUUID = HashMap<UUID, RemoteTuple<T>>() protected val remoteByUUID: MutableMap<UUID, RemoteTuple<T>> = HashMap()
// локальный UUID -> Локальный кортеж // локальный UUID -> Локальный кортеж
protected val localByUUID = HashMap<UUID, LocalTuple<T>>() protected val localByUUID: MutableMap<UUID, LocalTuple<T>> = HashMap()
// Стак -> Локальный кортеж стака // Стак -> Локальный кортеж стака
protected val tuples = HashMap<T, LocalTuple<T>>() protected val tuples: MutableMap<T, LocalTuple<T>> = HashMap()
// ArrayList для скорости работы // ArrayList для скорости работы
protected val listeners = ArrayList<IStorageListener<T>>() protected val listeners: MutableSet<IStorageEventConsumer<T>> = ObjectArraySet()
protected val set = ObjectArraySet<Any>() protected val set: MutableSet<IStorage<T>> = ObjectArraySet()
protected val consumers = ArrayList<IStorageConsumer<T>>() protected val consumers: MutableSet<IStorageAcceptor<T>> = ObjectArraySet()
protected open fun onAdd(identity: IStorage<T>) {} protected open fun onAdd(identity: IStorage<T>) {}
protected open fun onRemove(identity: IStorage<T>) {} protected open fun onRemove(identity: IStorage<T>) {}
override fun add(identity: IStorage<T>) { override fun add(identity: IStorage<T>) {
if (set.add(identity)) { if (set.add(identity)) {
if (identity is IStorageView<T>) { if (identity is IStorageProvider<T>) {
identity.addListenerAuto(this) identity.addListenerAuto(this)
} else if (identity is IStorageTrigger<T>) { } else if (identity is IStorageEventProducer<T>) {
identity.addListener(this) identity.addListener(this)
} }
if (identity is IStorageConsumer<T>) { if (identity is IStorageAcceptor<T>) {
consumers.add(identity) consumers.add(identity)
} }
@ -69,13 +71,13 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
override fun remove(identity: IStorage<T>) { override fun remove(identity: IStorage<T>) {
if (set.remove(identity)) { if (set.remove(identity)) {
if (identity is IStorageView<T>) { if (identity is IStorageProvider<T>) {
identity.removeListenerAuto(this) identity.removeListenerAuto(this)
} else if (identity is IStorageTrigger<T>) { } else if (identity is IStorageEventProducer<T>) {
identity.removeListener(this) identity.removeListener(this)
} }
if (identity is IStorageConsumer<T>) { if (identity is IStorageAcceptor<T>) {
consumers.remove(identity) consumers.remove(identity)
} }
@ -87,7 +89,7 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
return set.contains(identity) return set.contains(identity)
} }
override fun addListener(listener: IStorageListener<T>): Boolean { override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
if (!listeners.contains(listener)) { if (!listeners.contains(listener)) {
listeners.add(listener) listeners.add(listener)
return true return true
@ -96,31 +98,31 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
return false return false
} }
override fun removeListener(listener: IStorageListener<T>): Boolean { override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
return listeners.remove(listener) return listeners.remove(listener)
} }
override fun getStack(id: UUID): T { override fun get(id: UUID): T {
return localByUUID[id]?.stack ?: this.storageType.empty return localByUUID[id]?.stack ?: this.storageType.empty
} }
override fun getStacks(): List<IStorageTuple<T>> { override val stacks: Stream<IStorageTuple<T>> get() {
return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) } return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) }.stream()
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
override fun addStack(stack: T, id: UUID, provider: IStorageView<T>) { override fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>) {
check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" } check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" }
val key = stack.key() val key = stack.key()
var local: LocalTuple<T>? = tuples[key] var local: LocalTuple<T>? = tuples[key]
var oldCount = ImpreciseFraction.ZERO var oldCount = BigInteger.ZERO
val added = local == null val added = local == null
if (local == null) { if (local == null) {
local = LocalTuple(stack.copy() as T, UUID.randomUUID(), ArrayList<RemoteTuple<T>>(1)) local = LocalTuple(stack.copy() as T, UUID.randomUUID(), ArrayList<RemoteTuple<T>>(1))
localByUUID[local.id] = local localByUUID[local.id] = local
tuples[key as T] = local tuples[key] = local
} else { } else {
oldCount = local.stack.count oldCount = local.stack.count
local.stack.grow(stack.count) local.stack.grow(stack.count)
@ -141,12 +143,12 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
} }
} }
override fun changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction) { override fun changeStack(stack: T, id: UUID, oldCount: BigInteger) {
require(stack.count.isPositive) require(stack.count.isPositive)
val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id")
val diff = stack.count - tuple.obj.count val diff = stack.count - tuple.stack.count
tuple.obj.count = stack.count tuple.stack.count = stack.count
@Suppress("NAME_SHADOWING") val oldCount = tuple.local.stack.count @Suppress("NAME_SHADOWING") val oldCount = tuple.local.stack.count
tuple.local.stack.grow(diff) tuple.local.stack.grow(diff)
@ -159,12 +161,12 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
override fun removeStack(stack: T, id: UUID) { override fun removeStack(stack: T, id: UUID) {
val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") 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) tuple.local.tuples.remove(tuple)
remoteByUUID.remove(id) 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 val b = tuple.local.tuples.size == 0
if (a || b) { if (a || b) {
@ -194,8 +196,8 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T { override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
if (amount.isZero) if (!amount.isPositive)
return this.storageType.empty return this.storageType.empty
@Suppress("name_shadowing") @Suppress("name_shadowing")
@ -205,18 +207,16 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
if (tuple == null || amount.isZero) if (tuple == null || amount.isZero)
return this.storageType.empty return this.storageType.empty
if (!storageType.fractional)
amount = amount.floor()
if (!amount.isPositive) if (!amount.isPositive)
amount = tuple.stack.maxStackSize ?: tuple.stack.count amount = tuple.stack.maxStackSize ?: tuple.stack.count
val toExtract = tuple.stack.count.coerceAtMost(amount) val toExtract = tuple.stack.count.coerceAtMost(amount)
var extracted = ImpreciseFraction.ZERO var extracted = BigInteger.ZERO
val copy = tuple.stack.copy() as T val copy = tuple.stack.copy() as T
for (remote_tuple in tuple.tuples.let { Array(it.size) { i -> it[i] } }) { 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) if (extracted >= toExtract)
break break
@ -238,8 +238,8 @@ open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponen
override val storageType: StorageStackType<T> override val storageType: StorageStackType<T>
get() = parent.storageType get() = parent.storageType
override fun addListener(listener: IStorageListener<T>) = parent.addListener(listener) override fun addListener(listener: IStorageEventConsumer<T>) = parent.addListener(listener)
override fun removeListener(listener: IStorageListener<T>) = parent.removeListener(listener) override fun removeListener(listener: IStorageEventConsumer<T>) = parent.removeListener(listener)
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
override fun insertStack(stack: T, simulate: Boolean): T { override fun insertStack(stack: T, simulate: Boolean): T {
@ -273,7 +273,7 @@ open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponen
@Suppress("name_shadowing") @Suppress("name_shadowing")
val stack = stack.copy() as T val stack = stack.copy() as T
val oldCount = stack.count val oldCount = stack.count
stack.count = extracted / storageType.energyPerOperation stack.count = (extracted / storageType.energyPerOperation).whole
val diff = oldCount - stack.count val diff = oldCount - stack.count
val newRequired = storageType.energyPerOperation * stack.count val newRequired = storageType.energyPerOperation * stack.count
val newExtracted = energy.extractEnergyInner(newRequired, true) val newExtracted = energy.extractEnergyInner(newRequired, true)
@ -291,7 +291,7 @@ open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponen
} }
if (!simulate) { if (!simulate) {
val requiredNew = (stack.count - leftover.count) * storageType.energyPerOperation val requiredNew = storageType.energyPerOperation * (stack.count - leftover.count)
energy.extractEnergyInner(requiredNew, false) energy.extractEnergyInner(requiredNew, false)
} }
@ -302,9 +302,9 @@ open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponen
return stack 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 required = storageType.energyPerOperation * amount
val energy = energyProvider.invoke() val energy = energyProvider.invoke()
val extracted = energy.extractEnergyInner(required, true) val extracted = energy.extractEnergyInner(required, true)
@ -324,7 +324,7 @@ open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponen
if (extractedStack.count == amount) { if (extractedStack.count == amount) {
energy.extractEnergyInner(required, false) energy.extractEnergyInner(required, false)
} else { } else {
energy.extractEnergyInner(extractedStack.count * storageType.energyPerOperation, false) energy.extractEnergyInner(storageType.energyPerOperation * extractedStack.count, false)
} }
} }
@ -332,7 +332,7 @@ open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponen
} }
@Suppress("name_shadowing") @Suppress("name_shadowing")
val amount = required / storageType.energyPerOperation val amount = (required / storageType.energyPerOperation).whole
val extractedStack = parent.extractStack(id, amount, simulate) val extractedStack = parent.extractStack(id, amount, simulate)
if (extractedStack.isEmpty) { if (extractedStack.isEmpty) {
@ -340,13 +340,13 @@ open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponen
} }
if (!simulate) { if (!simulate) {
energy.extractEnergyInner(extractedStack.count * storageType.energyPerOperation, false) energy.extractEnergyInner(storageType.energyPerOperation * extractedStack.count, false)
} }
return extractedStack return extractedStack
} }
override fun getStacks() = parent.getStacks() override val stacks get() = parent.stacks
} }
/** /**
@ -358,8 +358,8 @@ open class PoweredVirtualComponent<T : IStorageStack>(override val parent: IVirt
constructor(parent: Class<T>, energy: IMatteryEnergyStorage) : this(VirtualComponent(parent), { energy }) constructor(parent: Class<T>, energy: IMatteryEnergyStorage) : this(VirtualComponent(parent), { energy })
constructor(parent: StorageStackType<T>, energy: IMatteryEnergyStorage) : this(VirtualComponent(parent), { energy }) constructor(parent: StorageStackType<T>, energy: IMatteryEnergyStorage) : this(VirtualComponent(parent), { energy })
override fun addStack(stack: T, id: UUID, provider: IStorageView<T>) = parent.addStack(stack, id, provider) override fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>) = parent.addStack(stack, id, provider)
override fun changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction) = parent.changeStack(stack, id, oldCount) 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 removeStack(stack: T, id: UUID) = parent.removeStack(stack, id)
override fun add(identity: IStorage<T>) = parent.add(identity) override fun add(identity: IStorage<T>) = parent.add(identity)