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) {
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);

View File

@ -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<ItemStackWrapper>
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)) {
listeners.add(listener)
return true
@ -197,7 +199,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
return false
}
override fun removeListener(listener: IStorageListener<ItemStackWrapper>): Boolean {
override fun removeListener(listener: IStorageEventConsumer<ItemStackWrapper>): 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<IStorageTuple<ItemStackWrapper>> {
override val stacks: Stream<IStorageTuple<ItemStackWrapper>> get() {
val listing = ArrayList<IStorageTuple<ItemStackWrapper>>(index.size)
for (tuple in index.values) {
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.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<T>(
@ -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<IItemHandler>(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState),
IStorageListener<ItemStackWrapper> {
IStorageEventConsumer<ItemStackWrapper> {
override val defaultDisplayName: Component
get() = MACHINE_NAME
@ -275,7 +274,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
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)) {
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<Pair<UUID, ItemStackWrapper>>
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

View File

@ -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
}

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.IStorageTuple
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import java.math.BigInteger
import java.util.*
interface IItemMatteryDrive : IMatteryDrive<ItemStackWrapper> {
@ -25,19 +26,13 @@ interface IItemMatteryDrive : IMatteryDrive<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> {
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

View File

@ -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<T : IStorageStack> @JvmOverloads constructor(
override var driveCapacity: ImpreciseFraction,
override var driveCapacity: BigInteger,
override val uuid: UUID = UUID.randomUUID(),
var maxDifferentStacks: Int = 0xFFFF
) : IMatteryDrive<T> {
@ -34,13 +39,13 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @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<T : IStorageStack> @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<T : IStorageStack> @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<T : IStorageStack> @JvmOverloads constructor
}
}
override fun getStack(id: UUID): T {
override fun get(id: UUID): T {
return tuplesByID[id]?.stack ?: storageType.empty
}
override fun getStacks(): List<IStorageTuple<T>> {
return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) }
override val stacks: Stream<IStorageTuple<T>> get() {
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)
}
override fun removeListener(listener: IStorageListener<T>): Boolean {
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
return listeners.remove(listener)
}
}

View File

@ -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 {

View File

@ -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<ItemStackWrapper>, 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<ItemStackWrapper> = OverdriveThatMatters.INSTANCE.ITEM_STORAGE()
@ -49,7 +52,7 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, 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<ItemStackWrapper>, IItemMatteryDri
companion object {
@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.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<I
private val index = Object2ObjectAVLTreeMap<UUID, 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
}
override fun getStacks(): Collection<IStorageTuple<ItemStackWrapper>> {
return ArrayList<IStorageTuple<ItemStackWrapper>>(index.size).also { it.addAll(index.values) }
override val stacks: Stream<IStorageTuple<ItemStackWrapper>> get() {
return ArrayList<IStorageTuple<ItemStackWrapper>>(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<I
}
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
}
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.
// ASSUMPTION: We can ALWAYS remove items from QIO grid.
@ -128,7 +130,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
return copy
}
override fun addListener(listener: IStorageListener<ItemStackWrapper>): Boolean {
override fun addListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
if (!listeners.contains(listener)) {
listeners.add(listener)
return true
@ -137,7 +139,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
return false
}
override fun removeListener(listener: IStorageListener<ItemStackWrapper>): Boolean {
override fun removeListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
return listeners.remove(listener)
}
@ -149,14 +151,14 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
if (local.stack.count.toLong() != value.count) {
val oldCount = local.stack.count
local.stack.count = value.count.toImpreciseFraction()
local.stack.count = value.count.toBigInteger()
for (listener in listeners) {
listener.changeStack(local, oldCount)
}
}
} 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
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 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 {
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.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

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.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) })
}

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.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<ItemStackWrapper>).getStacks()) {
for (item in (drive as IMatteryDrive<ItemStackWrapper>).stacks) {
val tuple = getMatterValue(item.stack.stack, level + 1)
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.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<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)
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<ItemStackWrapper>) = 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<ItemStackWrapper>) = 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())

View File

@ -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 : 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>
}
interface IStorageTrigger<T : IStorageStack> : IStorage<T> {
/**
* [listener] is [IStorageListener] which want to subscribe to our events
/**
* Generates events for [IStorageEventConsumer]
*/
fun addListener(listener: IStorageListener<T>): Boolean
interface IStorageEventProducer<T : IStorageStack> : IStorage<T> {
/**
* [listener] is [IStorageEventConsumer] which want to subscribe to our events
*/
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.
* @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
}
interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
/**
* @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<IStorageTuple<T>>
/**
* 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
* @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<IStorageTuple<T>>
fun addListenerAuto(listener: IStorageListener<T>): Boolean {
fun addListenerAuto(listener: IStorageEventConsumer<T>): 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<T : IStorageStack> : IStorageTrigger<T> {
return false
}
fun removeListenerAuto(listener: IStorageListener<T>): Boolean {
fun removeListenerAuto(listener: IStorageEventConsumer<T>): Boolean {
if (removeListener(listener)) {
for (stack in getStacks()) {
for (stack in stacks) {
listener.removeStack(stack.stack, stack.id)
}
@ -158,33 +150,16 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
}
}
interface IStorageListener<T : IStorageStack> {
/**
* 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>) {
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<T>, oldCount: ImpreciseFraction) {
fun <T : IStorageStack> IStorageEventConsumer<T>.changeStack(tuple: IStorageTuple<T>, oldCount: BigInteger) {
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 <T : IStorageStack> IStorageEventConsumer<T>.addStack(tuple: IStorageTuple<T>, provider: IStorageProvider<T>) {
addStack(tuple.stack, tuple.id, provider)
}
fun removeStack(tuple: IStorageTuple<T>) {
fun <T : IStorageStack> IStorageEventConsumer<T>.removeStack(tuple: IStorageTuple<T>) {
removeStack(tuple.stack, tuple.id)
}
}
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>
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)
*/
interface IVirtualStorageComponent<T : IStorageStack> : IStorageComponent<T>, IStorageListener<T> {
interface IVirtualStorageComponent<T : IStorageStack> : IStorageComponent<T>, IStorageEventConsumer<T> {
fun add(identity: IStorage<T>)
fun remove(identity: IStorage<T>)
fun contains(identity: IStorage<T>): Boolean

View File

@ -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

View File

@ -13,24 +13,9 @@ open class StorageStackType<T : IStorageStack>(
* 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 <T : IStorageStack> register(
identity: Class<T>,
empty: T,
energyPerOperation: ImpreciseFraction,
fractional: Boolean
energyPerOperation: ImpreciseFraction
): StorageStackType<T> {
return register(StorageStackType(identity, empty, energyPerOperation, fractional)) as StorageStackType<T>
return register(StorageStackType(identity, empty, energyPerOperation)) as StorageStackType<T>
}
@JvmStatic

View File

@ -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<T : IStorageStack>(val obj: T, val remote_id: UUID, val provider: IStorageView<T>, val local: LocalTuple<T>) {
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<T : IStorageStack>(
override val stack: T,
override val id: UUID,
val provider: IStorageProvider<T>,
val local: LocalTuple<T>
) : IStorageTuple<T> {
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<T : IStorageStack>(type: StorageStackType<T>) : IVir
override val storageType: StorageStackType<T> = type
// удаленный UUID -> Кортеж
protected val remoteByUUID = HashMap<UUID, RemoteTuple<T>>()
protected val remoteByUUID: MutableMap<UUID, RemoteTuple<T>> = HashMap()
// локальный 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 для скорости работы
protected val listeners = ArrayList<IStorageListener<T>>()
protected val set = ObjectArraySet<Any>()
protected val consumers = ArrayList<IStorageConsumer<T>>()
protected val listeners: MutableSet<IStorageEventConsumer<T>> = ObjectArraySet()
protected val set: MutableSet<IStorage<T>> = ObjectArraySet()
protected val consumers: MutableSet<IStorageAcceptor<T>> = ObjectArraySet()
protected open fun onAdd(identity: IStorage<T>) {}
protected open fun onRemove(identity: IStorage<T>) {}
override fun add(identity: IStorage<T>) {
if (set.add(identity)) {
if (identity is IStorageView<T>) {
if (identity is IStorageProvider<T>) {
identity.addListenerAuto(this)
} else if (identity is IStorageTrigger<T>) {
} else if (identity is IStorageEventProducer<T>) {
identity.addListener(this)
}
if (identity is IStorageConsumer<T>) {
if (identity is IStorageAcceptor<T>) {
consumers.add(identity)
}
@ -69,13 +71,13 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
override fun remove(identity: IStorage<T>) {
if (set.remove(identity)) {
if (identity is IStorageView<T>) {
if (identity is IStorageProvider<T>) {
identity.removeListenerAuto(this)
} else if (identity is IStorageTrigger<T>) {
} else if (identity is IStorageEventProducer<T>) {
identity.removeListener(this)
}
if (identity is IStorageConsumer<T>) {
if (identity is IStorageAcceptor<T>) {
consumers.remove(identity)
}
@ -87,7 +89,7 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
return set.contains(identity)
}
override fun addListener(listener: IStorageListener<T>): Boolean {
override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
if (!listeners.contains(listener)) {
listeners.add(listener)
return true
@ -96,31 +98,31 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
return false
}
override fun removeListener(listener: IStorageListener<T>): Boolean {
override fun removeListener(listener: IStorageEventConsumer<T>): 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<IStorageTuple<T>> {
return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) }
override val stacks: Stream<IStorageTuple<T>> get() {
return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) }.stream()
}
@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" }
val key = stack.key()
var local: LocalTuple<T>? = 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<RemoteTuple<T>>(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<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)
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<T : IStorageStack>(type: StorageStackType<T>) : 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<T : IStorageStack>(type: StorageStackType<T>) : 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<T : IStorageStack>(type: StorageStackType<T>) : 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<T : IStorageStack>(open val parent: IStorageComponen
override val storageType: StorageStackType<T>
get() = parent.storageType
override fun addListener(listener: IStorageListener<T>) = parent.addListener(listener)
override fun removeListener(listener: IStorageListener<T>) = parent.removeListener(listener)
override fun addListener(listener: IStorageEventConsumer<T>) = parent.addListener(listener)
override fun removeListener(listener: IStorageEventConsumer<T>) = parent.removeListener(listener)
@Suppress("unchecked_cast")
override fun insertStack(stack: T, simulate: Boolean): T {
@ -273,7 +273,7 @@ open class PoweredComponent<T : IStorageStack>(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<T : IStorageStack>(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<T : IStorageStack>(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<T : IStorageStack>(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<T : IStorageStack>(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<T : IStorageStack>(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<T : IStorageStack>(override val parent: IVirt
constructor(parent: Class<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 changeStack(stack: T, id: UUID, oldCount: ImpreciseFraction) = parent.changeStack(stack, id, oldCount)
override fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>) = 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<T>) = parent.add(identity)