Refine storage api, make it compile

This commit is contained in:
DBotThePony 2023-08-06 23:13:15 +07:00
parent 48d367b705
commit 64d5f1b336
Signed by: DBot
GPG Key ID: DCC23B5715498507
32 changed files with 824 additions and 1120 deletions

View File

@ -75,13 +75,6 @@ public final class OverdriveThatMatters {
private static final Logger LOGGER = LogManager.getLogger(); private static final Logger LOGGER = LogManager.getLogger();
public static OverdriveThatMatters INSTANCE; public static OverdriveThatMatters INSTANCE;
private StorageStackType<ItemStackWrapper> ITEM_STORAGE;
@NotNull
public StorageStackType<ItemStackWrapper> ITEM_STORAGE() {
return Objects.requireNonNull(ITEM_STORAGE);
}
public static ResourceLocation loc(String path) { public static ResourceLocation loc(String path) {
return new ResourceLocation(MOD_ID, path); return new ResourceLocation(MOD_ID, path);
} }
@ -206,8 +199,6 @@ public final class OverdriveThatMatters {
WeaponNetworkChannel.INSTANCE.register(); WeaponNetworkChannel.INSTANCE.register();
GenericNetworkChannel.INSTANCE.register(); GenericNetworkChannel.INSTANCE.register();
ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new Decimal("3.125"));
if (ModList.get().isLoaded("mekanism")) { if (ModList.get().isLoaded("mekanism")) {
EVENT_BUS.addGenericListener(BlockEntity.class, EventPriority.NORMAL, QIOKt::attachCapabilities); EVENT_BUS.addGenericListener(BlockEntity.class, EventPriority.NORMAL, QIOKt::attachCapabilities);
} }

View File

@ -45,7 +45,7 @@ public class MatteryCapability {
@Nonnull @Nonnull
@NotNull @NotNull
public static final Capability<IMatteryDrive> DRIVE = CapabilityManager.get(new CapabilityToken<>() {}); public static final Capability<IMatteryDrive<?>> DRIVE = CapabilityManager.get(new CapabilityToken<>() {});
@Nonnull @Nonnull
@NotNull @NotNull

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity.storage package ru.dbotthepony.mc.otm.block.entity.storage
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
@ -11,13 +10,14 @@ import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.graph.storage.StorageNode import ru.dbotthepony.mc.otm.graph.storage.StorageNode
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.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu
import ru.dbotthepony.mc.otm.graph.storage.StorageGraph
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, p_155229_, p_155230_) { MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, p_155229_, p_155230_) {
@ -27,16 +27,19 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old) super.setChanged(slot, new, old)
// generics is going apeshit since storage types are invariant,
// but since we don't know generics of upvalue mattery drive, its storage type
// is defined as out variant
old.getCapability(MatteryCapability.DRIVE).ifPresent { old.getCapability(MatteryCapability.DRIVE).ifPresent {
cell.computeIfAbsent(it.storageType) { cell.computeIfAbsent(it.storageType as StorageStack.Type<ItemStorageStack>) {
PoweredVirtualComponent(it, energy) PoweredVirtualComponent(VirtualComponent(it), ::energy)
}.remove(it) }.remove(it as IMatteryDrive<ItemStorageStack>)
} }
new.getCapability(MatteryCapability.DRIVE).ifPresent { new.getCapability(MatteryCapability.DRIVE).ifPresent {
cell.computeIfAbsent(it.storageType) { cell.computeIfAbsent(it.storageType as StorageStack.Type<ItemStorageStack>) {
PoweredVirtualComponent(it, energy) PoweredVirtualComponent(VirtualComponent(it), ::energy)
}.add(it) }.add(it as IMatteryDrive<ItemStorageStack>)
} }
} }
}.also(::addDroppableContainer) }.also(::addDroppableContainer)

View File

@ -18,6 +18,7 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu
import ru.dbotthepony.mc.otm.storage.StorageStack
class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_VIEWER, p_155229_, p_155230_) { class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_VIEWER, p_155229_, p_155230_) {
override fun setChanged() { override fun setChanged() {
@ -28,7 +29,7 @@ class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
tickList.once { tickList.once {
var state = blockState var state = blockState
if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent && energy.batteryLevel >= OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation) { if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent) {
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING) state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING)
} else { } else {
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE) state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE)

View File

@ -6,13 +6,11 @@ import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.inventory.TransientCraftingContainer import net.minecraft.world.inventory.TransientCraftingContainer
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.CraftingRecipe import net.minecraft.world.item.crafting.CraftingRecipe
@ -40,6 +38,7 @@ import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
import java.math.BigInteger import java.math.BigInteger
import java.util.* import java.util.*
import java.util.function.Supplier import java.util.function.Supplier
@ -165,7 +164,7 @@ private fun takeOne(inventory: Inventory, item: ItemStack): Boolean {
return false return false
} }
private fun takeOne(id: UUID?, view: IStorageProvider<ItemStackWrapper>): Boolean { private fun takeOne(id: UUID?, view: IStorageProvider<ItemStorageStack>): Boolean {
val extracted = view.extractStack(id ?: return false, BigInteger.ONE, false) val extracted = view.extractStack(id ?: return false, BigInteger.ONE, false)
if (!extracted.isEmpty) { if (!extracted.isEmpty) {
@ -175,10 +174,7 @@ private fun takeOne(id: UUID?, view: IStorageProvider<ItemStackWrapper>): Boolea
return false return false
} }
class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, blockPos, blockState), IStorageEventConsumer<ItemStorageStack> {
MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, p_155229_, p_155230_),
IStorageEventConsumer<ItemStackWrapper> {
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.ITEM_MONITOR) val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.ITEM_MONITOR)
init { init {
@ -186,18 +182,18 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
savetable(::energy, ENERGY_KEY) savetable(::energy, ENERGY_KEY)
} }
var poweredView: PoweredVirtualComponent<ItemStackWrapper>? = null var poweredView: PoweredVirtualComponent<ItemStorageStack>? = null
private set private set
val cell = object : StorageNode(energy) { val cell = object : StorageNode(energy) {
override fun attachComponents(to: StorageGraph) { override fun attachComponents(to: StorageGraph) {
super.attachComponents(to) super.attachComponents(to)
poweredView = PoweredVirtualComponent(to.getVirtualComponent(ITEM_STORAGE), energy) poweredView = PoweredVirtualComponent(to.getVirtualComponent(StorageStack.ITEMS), ::energy)
} }
override fun removeComponents(from: StorageGraph) { override fun removeComponents(from: StorageGraph) {
super.removeComponents(from) super.removeComponents(from)
poweredView?.removeListeners() // poweredView?.removeListeners()
poweredView = null poweredView = null
} }
} }
@ -248,7 +244,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
val item = craftingGrid[i] val item = craftingGrid[i]
if (!item.isEmpty) { if (!item.isEmpty) {
craftingGridTuples[i] = poweredView[ItemStackWrapper(item).key()] craftingGridTuples[i] = poweredView[ItemStorageStack.unsafe(item)]
} }
} }
} }
@ -269,18 +265,18 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
private val craftingGridDummy = TransientCraftingContainer(craftingMenu, 3, 3) private val craftingGridDummy = TransientCraftingContainer(craftingMenu, 3, 3)
override val storageType: StorageStackType<ItemStackWrapper> override val storageType: StorageStack.Type<ItemStorageStack>
get() = ITEM_STORAGE get() = StorageStack.ITEMS
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) { override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
// no op // no op
} }
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) { override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
// no op // no op
} }
override fun removeStack(stack: ItemStackWrapper, id: UUID) { override fun onStackRemoved(id: UUID) {
for (i in craftingGridTuples.indices) { for (i in craftingGridTuples.indices) {
if (craftingGridTuples[i] == id) { if (craftingGridTuples[i] == id) {
craftingGridTuples[i] = null craftingGridTuples[i] = null
@ -393,7 +389,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
if (!remainder.isEmpty) { if (!remainder.isEmpty) {
when (settings.resultTarget) { when (settings.resultTarget) {
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, ItemMonitorPlayerSettings.ResultTarget.MIXED -> { ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, ItemMonitorPlayerSettings.ResultTarget.MIXED -> {
val remaining = poweredView?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
if (!remaining.isEmpty) { if (!remaining.isEmpty) {
if (newItem.isEmpty) { if (newItem.isEmpty) {
@ -409,7 +405,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY -> { ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY -> {
if (!craftingPlayer.inventory.add(remainder)) { if (!craftingPlayer.inventory.add(remainder)) {
val remaining = poweredView?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
if (!remaining.isEmpty) { if (!remaining.isEmpty) {
if (newItem.isEmpty) { if (newItem.isEmpty) {

View File

@ -2,8 +2,8 @@ package ru.dbotthepony.mc.otm.block.entity.storage
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
@ -12,7 +12,6 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.* import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.block.CableBlock import ru.dbotthepony.mc.otm.block.CableBlock
@ -23,23 +22,20 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.getCapability
import ru.dbotthepony.mc.otm.core.math.isPositive import ru.dbotthepony.mc.otm.core.math.isPositive
import ru.dbotthepony.mc.otm.core.math.isZero import ru.dbotthepony.mc.otm.core.math.toIntSafe
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.graph.storage.StorageNode import ru.dbotthepony.mc.otm.graph.storage.StorageNode
import ru.dbotthepony.mc.otm.menu.storage.StorageBusMenu import ru.dbotthepony.mc.otm.menu.storage.StorageBusMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
import java.lang.ref.WeakReference
import java.math.BigInteger import java.math.BigInteger
import java.util.* import java.util.*
import java.util.stream.Stream import java.util.stream.Stream
private class SlotTuple(val slot: Int, val stack: ItemStack) private data class SlotTuple(val slot: Int, val stack: ItemStack)
private class TrackedTuple(override val stack: ItemStackWrapper, override val id: UUID) : IStorageTuple<ItemStackWrapper> {
private class TrackedTuple(override var stack: ItemStorageStack, override val id: UUID) : IStorageTuple<ItemStorageStack> {
// do not use hash map because we need keys to be iterated in natural order // do not use hash map because we need keys to be iterated in natural order
val children = Int2ObjectAVLTreeMap<SlotTuple>() val children = Int2ObjectAVLTreeMap<SlotTuple>()
@ -128,51 +124,21 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
const val MAX_FILTERS = 6 * 3 const val MAX_FILTERS = 6 * 3
} }
private inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent<ItemStackWrapper> { private inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent<ItemStorageStack> {
private inner class EventsSnapshot { private fun updateTuple(tuple: TrackedTuple, diff: BigInteger) {
val index = HashMap<UUID, BigInteger>() tuple.stack = tuple.stack.grow(diff)
fun change(key: UUID, diff: BigInteger) { if (tuple.stack.isNotEmpty) {
if (diff.isZero)
return
val value = index[key]
if (value == null) {
index[key] = diff
} else {
val newvalue = value + diff
if (newvalue.isZero) {
index.remove(key)
} else {
index[key] = newvalue
}
}
}
fun apply() {
for ((key, value) in index) {
val tuple = checkNotNull(this@ItemHandlerComponent.index[key]) { "Tuple with ID $key is missing!" }
val count = tuple.stack.count
tuple.stack.count += value
if (tuple.stack.count.isPositive) {
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(tuple, count) listener.onStackChanged(tuple)
} }
} else { } else {
for (listener in listeners) { for (listener in listeners) {
listener.removeStack(tuple) listener.onStackRemoved(tuple)
} }
this@ItemHandlerComponent.index.remove(tuple.id) id2tuples.remove(tuple.id)
items2tuples.remove(tuple.stack) ?: throw IllegalStateException("Cross-reference integrity check failed for $tuple")
val tuplekey = tuple.stack.key()
tuples.remove(tuplekey) ?: throw IllegalStateException("Cross-reference integrity check failed for $tuple")
}
}
} }
} }
@ -240,14 +206,12 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
} }
} }
private var snapshot: EventsSnapshot? = null override val storageType: StorageStack.Type<ItemStorageStack>
get() = StorageStack.ITEMS
override val storageType: StorageStackType<ItemStackWrapper> private val listeners = ArrayList<IStorageEventConsumer<ItemStorageStack>>()
get() = ITEM_STORAGE
private val listeners = ArrayList<IStorageEventConsumer<ItemStackWrapper>>() override fun addListener(listener: IStorageEventConsumer<ItemStorageStack>): 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
@ -256,139 +220,92 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
return false return false
} }
override fun removeListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean { override fun removeListener(listener: IStorageEventConsumer<ItemStorageStack>): Boolean {
return listeners.remove(listener) return listeners.remove(listener)
} }
private var scanned = arrayOfNulls<ItemStack>(0) private var slot2itemStack = arrayOfNulls<ItemStack>(0)
private var scannedMap = arrayOfNulls<TrackedTuple>(0) private var slot2tuple = arrayOfNulls<TrackedTuple>(0)
private val tuples = HashMap<ItemStackWrapper, TrackedTuple>() private val items2tuples = Object2ObjectOpenCustomHashMap<ItemStorageStack, TrackedTuple>(StorageStack.Companion)
private val index = HashMap<UUID, TrackedTuple>() private val id2tuples = HashMap<UUID, TrackedTuple>()
private fun removeTracked(slot: Int) { private fun removeTracked(slot: Int) {
scanned[slot] = null slot2itemStack[slot] = null
val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" } val tuple = checkNotNull(slot2tuple[slot]) { "Not tracking slot $slot" }
this.scannedMap[slot] = null slot2tuple[slot] = null
val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot") val slotTuple = tuple.children[slot] ?: throw IllegalStateException("${tuple.id} does not track $slot")
scannedMap.children.remove(slot) tuple.children.remove(slot)
val count = scannedMap.stack.count updateTuple(tuple, -slotTuple.stack.count.toBigInteger())
val snapshot = snapshot
if (snapshot != null) {
snapshot.change(scannedMap.id, -item.stack.count.toBigInteger())
} else {
scannedMap.stack.count -= item.stack.count.toBigInteger()
if (scannedMap.stack.count.isPositive) {
for (listener in listeners) {
listener.changeStack(scannedMap, count)
}
} else {
for (listener in listeners) {
listener.removeStack(scannedMap)
}
index.remove(scannedMap.id)
val key = scannedMap.stack.key()
tuples.remove(key) ?: throw IllegalStateException("Item tuple is not present for slot $slot at ${scannedMap.stack}")
}
}
} }
private fun diffTracked(slot: Int, diff: Int) { private fun diffTracked(slot: Int, diff: Int) {
val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" } val tuple = checkNotNull(slot2tuple[slot]) { "Not tracking slot $slot" }
val item = checkNotNull(scannedMap.children[slot]) { "${scannedMap.id} does not track $slot" } val slotTuple = checkNotNull(tuple.children[slot]) { "${tuple.id} does not track $slot" }
slotTuple.stack.count += diff
val oldCount = scannedMap.stack.count updateTuple(tuple, diff.toBigInteger())
item.stack.count += diff
val snapshot = snapshot
if (snapshot != null) {
snapshot.change(scannedMap.id, diff.toBigInteger())
} else {
scannedMap.stack.count += diff.toBigInteger()
for (listener in listeners) {
listener.changeStack(scannedMap.stack, scannedMap.id, oldCount)
}
}
} }
private fun addTracked(slot: Int, stack: ItemStack) { private fun addTracked(slot: Int, stack: ItemStack) {
check(scannedMap[slot] == null) { "Already tracking slot $slot" } check(slot2tuple[slot] == null) { "Already tracking slot $slot" }
val storageStack = ItemStackWrapper(stack) val storageStack = ItemStorageStack(stack)
val key = storageStack.key() var tuple = items2tuples[storageStack]
var tuple: TrackedTuple? = tuples[key]
val added = tuple == null val added = tuple == null
var oldCount = BigInteger.ZERO
if (added) { if (tuple == null) {
tuple = TrackedTuple(storageStack, UUID.randomUUID()) tuple = TrackedTuple(storageStack, UUID.randomUUID())
index[tuple.id] = tuple id2tuples[tuple.id] = tuple
tuples[key] = tuple items2tuples[storageStack] = tuple
} else { } else {
oldCount = tuple!!.stack.count tuple.stack = tuple.stack.grow(stack.count.toBigInteger())
if (snapshot == null)
tuple.stack.count += stack.count.toBigInteger()
} }
tuple.children[slot] = SlotTuple(slot, stack.copy()) tuple.children[slot] = SlotTuple(slot, stack.copy())
scanned[slot] = tuple.children[slot].stack slot2itemStack[slot] = tuple.children[slot].stack
scannedMap[slot] = tuple slot2tuple[slot] = tuple
if (added) { if (added) {
for (listener in listeners) { for (listener in listeners) {
listener.addStack(tuple.stack, tuple.id, this) listener.onStackAdded(tuple.stack, tuple.id, this)
} }
} else {
val snapshot = snapshot
if (snapshot != null) {
snapshot.change(tuple.id, stack.count.toBigInteger())
} else { } else {
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(tuple.stack, tuple.id, oldCount) listener.onStackChanged(tuple.stack, tuple.id)
}
} }
} }
} }
private fun sizeScan() { private fun sizeScan() {
if (scanned.size != parent.slots) { if (slot2itemStack.size != parent.slots) {
val old = scanned val old = slot2itemStack
val oldMap = scannedMap val oldMap = slot2tuple
if (scanned.size < parent.slots) { if (slot2itemStack.size < parent.slots) {
// grow // grow
scanned = arrayOfNulls(parent.slots) slot2itemStack = arrayOfNulls(parent.slots)
scannedMap = arrayOfNulls(parent.slots) slot2tuple = arrayOfNulls(parent.slots)
for ((i, item) in old.withIndex()) { for ((i, item) in old.withIndex()) {
scanned[i] = item slot2itemStack[i] = item
} }
for ((i, item) in oldMap.withIndex()) { for ((i, item) in oldMap.withIndex()) {
scannedMap[i] = item slot2tuple[i] = item
} }
} else { } else {
// shrink // shrink
for (i in parent.slots until scanned.size) { for (i in parent.slots until slot2itemStack.size) {
if (scannedMap[i] != null) if (slot2tuple[i] != null)
removeTracked(i) removeTracked(i)
} }
scanned = arrayOfNulls(parent.slots) slot2itemStack = arrayOfNulls(parent.slots)
scannedMap = arrayOfNulls(parent.slots) slot2tuple = arrayOfNulls(parent.slots)
for (i in 0 until parent.slots) { for (i in 0 until parent.slots) {
scanned[i] = old[i] slot2itemStack[i] = old[i]
scannedMap[i] = oldMap[i] slot2tuple[i] = oldMap[i]
} }
} }
} }
@ -396,7 +313,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
fun scan(slot: Int) { fun scan(slot: Int) {
val current = parent[slot].let { if (it.isEmpty || !filter.match(it)) null else it } val current = parent[slot].let { if (it.isEmpty || !filter.match(it)) null else it }
val last = scanned[slot] val last = slot2itemStack[slot]
if (current == null && last != null) { if (current == null && last != null) {
removeTracked(slot) removeTracked(slot)
@ -415,42 +332,27 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
fun scan() { fun scan() {
sizeScan() sizeScan()
snapshot = EventsSnapshot()
for (slot in 0 until parent.slots) { for (slot in 0 until parent.slots) {
scan(slot) scan(slot)
} }
snapshot!!.apply()
snapshot = null
} }
override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper { override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack {
if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.item)) if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.toItemStack()))
return stack return stack
val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * stack.count val required = StorageStack.ITEMS.energyPerInsert(stack)
val maxExtractEnergy = energy.extractEnergy(maxPossibleDemand, true) if (energy.extractEnergy(required, true) != required) return stack
var leftover: ItemStackWrapper = stack.copy() var leftover = stack
var additional: BigInteger = BigInteger.ZERO
if (maxExtractEnergy != maxPossibleDemand) { for (slot in PrioritizedSlotIterator(items2tuples[stack])) {
additional = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole
leftover.count -= additional
}
for (slot in PrioritizedSlotIterator(tuples[stack.key()])) {
val oldCount = leftover.count val oldCount = leftover.count
leftover = ItemStackWrapper(parent.insertItem(slot, leftover.stack, simulate)) leftover = ItemStorageStack.unsafe(parent.insertItem(slot, leftover.toItemStack(), simulate))
if (oldCount != leftover.count && !simulate) { if (oldCount != leftover.count && !simulate) {
energy.extractEnergy(ITEM_STORAGE.energyPerOperation * (oldCount - leftover.count), false) energy.extractEnergy(StorageStack.ITEMS.energyPerInsert(stack.copy(oldCount - leftover.count)), false)
if (slot2itemStack.size <= slot) sizeScan()
if (scanned.size <= slot) {
sizeScan()
}
scan(slot) scan(slot)
} }
@ -459,74 +361,52 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
} }
} }
return leftover.also { return leftover
if (additional != BigInteger.ZERO) { }
it.count += additional
override fun get(id: UUID): ItemStorageStack {
return id2tuples[id]?.stack ?: ItemStorageStack.EMPTY
}
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStorageStack {
if (redstoneControl.isBlockedByRedstone || !amount.isPositive || !energy.batteryLevel.isPositive)
return ItemStorageStack.EMPTY
var total = BigInteger.ZERO
val tuple = id2tuples[id] ?: return ItemStorageStack.EMPTY
val slots = tuple.children.values.iterator()
val lstack = tuple.stack
while (amount < total && slots.hasNext() && energy.batteryLevel.isPositive) {
val (slot, stack) = slots.next()
val extracted = parent.extractItem(slot, stack.count.coerceAtMost(amount.toIntSafe()), true)
val required = StorageStack.ITEMS.energyPerExtract(ItemStorageStack.unsafe(extracted))
if (extracted.isNotEmpty && tuple.stack.equalsWithoutCount(ItemStorageStack.unsafe(extracted)) && energy.extractEnergy(required, true) == required) {
if (simulate) {
total += extracted.count.toBigInteger()
} else {
val extracted2 = parent.extractItem(slot, extracted.count, false)
if (extracted2.count == extracted.count) {
energy.extractEnergy(required, false)
} else {
energy.extractEnergy(StorageStack.ITEMS.energyPerExtract(ItemStorageStack.unsafe(extracted2)), false)
}
total += extracted2.count.toBigInteger()
} }
} }
} }
override fun get(id: UUID): ItemStackWrapper { return lstack.copy(total)
return index[id]?.stack ?: ItemStackWrapper.EMPTY
} }
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStackWrapper { override val stacks: Stream<IStorageTuple<ItemStorageStack>> get() {
if (redstoneControl.isBlockedByRedstone || !amount.isPositive) val listing = ArrayList<IStorageTuple<ItemStorageStack>>(id2tuples.size)
return ItemStackWrapper.EMPTY
val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * amount for (tuple in id2tuples.values) {
val maxExtractEnergy = energy.extractEnergy(maxPossibleDemand, true) listing.add(IStorageTuple.I(tuple.id, tuple.stack))
@Suppress("NAME_SHADOWING")
var amount = amount
if (maxPossibleDemand != maxExtractEnergy) {
amount = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole
}
val intAmount = amount.toLong()
val tuple = index[id] ?: return ItemStackWrapper.EMPTY
var totalExtracted = 0L
val copy = tuple.stack.copy()
for (stack in tuple.children.values) {
val extracted = parent.extractItem(stack.slot, stack.stack.count.coerceAtMost((intAmount - totalExtracted).clamp()), true)
if (!extracted.isEmpty && tuple.stack.sameItem(extracted)) {
if (!simulate) {
parent.extractItem(stack.slot, extracted.count, false)
}
totalExtracted += extracted.count
if (extracted.count != 0 && !simulate) {
energy.extractEnergy(ITEM_STORAGE.energyPerOperation * extracted.count, false)
}
if (totalExtracted >= intAmount) {
break
}
}
}
if (totalExtracted == 0L) {
return ItemStackWrapper.EMPTY
}
if (!simulate) {
scan()
}
copy.count = totalExtracted.toBigInteger()
return copy
}
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.stream() return listing.stream()

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity.storage package ru.dbotthepony.mc.otm.block.entity.storage
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
@ -20,13 +19,12 @@ import ru.dbotthepony.mc.otm.block.CableBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.toIntSafe
import ru.dbotthepony.mc.otm.core.orNull import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.graph.storage.StorageNode import ru.dbotthepony.mc.otm.graph.storage.StorageNode
import ru.dbotthepony.mc.otm.menu.storage.StorageExporterMenu import ru.dbotthepony.mc.otm.menu.storage.StorageExporterMenu
@ -36,10 +34,8 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
import ru.dbotthepony.mc.otm.storage.IStorageProvider import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE import ru.dbotthepony.mc.otm.storage.ItemStorageStack
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.storage.StorageStackType
import ru.dbotthepony.mc.otm.storage.addStack
import java.math.BigInteger import java.math.BigInteger
import java.util.* import java.util.*
import java.util.stream.Stream import java.util.stream.Stream
@ -112,7 +108,9 @@ abstract class AbstractStorageImportExport<T>(
} }
} }
class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_IMPORTER, blockPos, blockState), IItemHandler { class StorageImporterBlockEntity(
blockPos: BlockPos, blockState: BlockState
) : AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_IMPORTER, blockPos, blockState), IItemHandler {
override val filter = ItemFilter(MAX_FILTERS) { _, _, _ -> override val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
setChangedLight() setChangedLight()
} }
@ -124,8 +122,6 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
private var lastSlot = 0 private var lastSlot = 0
private var nextTick = INTERVAL private var nextTick = INTERVAL
private val enoughEnergy get() = energy.batteryLevel >= ITEM_STORAGE.energyPerOperation
override val targetCapability: Capability<IItemHandler> override val targetCapability: Capability<IItemHandler>
get() = ForgeCapabilities.ITEM_HANDLER get() = ForgeCapabilities.ITEM_HANDLER
@ -141,23 +137,39 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
return ItemStack.EMPTY return ItemStack.EMPTY
} }
private fun acceptItem(stack: ItemStack, simulate: Boolean): ItemStack {
val wrapped = ItemStorageStack(stack)
val view = cell.graph.getVirtualComponent(StorageStack.ITEMS)
val inserted = view.insertStack(wrapped, true)
if (inserted == wrapped) return stack
val required = StorageStack.ITEMS.energyPerInsert(wrapped)
if (energy.extractEnergy(required, true) == required) {
if (!simulate) {
val inserted2 = view.insertStack(wrapped, false)
if (inserted == inserted2) {
energy.extractEnergy(required, false)
return inserted.toItemStack()
} else {
energy.extractEnergy(StorageStack.ITEMS.energyPerInsert(inserted2), false)
return inserted2.toItemStack()
}
}
return inserted.toItemStack()
}
return stack
}
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack { override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
if (redstoneControl.isBlockedByRedstone || !filter.match(stack)) if (redstoneControl.isBlockedByRedstone || !filter.match(stack))
return stack return stack
val view = cell.graph.getVirtualComponent(ITEM_STORAGE) return acceptItem(stack, simulate)
val maxMove = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, stack.count.toBigInteger(), true)
if (maxMove == BigInteger.ZERO)
return stack
val leftover = view.insertStack(ItemStackWrapper(stack).also { it.count = maxMove }, simulate)
if (simulate)
return leftover.stack
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, maxMove - leftover.count, false)
return leftover.stack
} }
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack { override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
@ -184,30 +196,22 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
val target = target.get().orNull() val target = target.get().orNull()
if (nextTick <= 0 && target != null && enoughEnergy) { if (nextTick <= 0 && target != null) {
val items = cell.graph.getVirtualComponent(ITEM_STORAGE)
if (lastSlot >= target.slots) { if (lastSlot >= target.slots) {
lastSlot = 0 lastSlot = 0
} }
val maxMove = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, MAX_MOVE_PER_OPERATION, true) val extracted = target.extractItem(lastSlot, MAX_MOVE_PER_OPERATION, true)
var extracted = target.extractItem(lastSlot, maxMove, true)
if (extracted.isEmpty || !filter.match(extracted)) { if (extracted.isEmpty || !filter.match(extracted)) {
lastSlot++ lastSlot++
} else { } else {
val leftOver = items.insertStack(ItemStackWrapper(extracted), true) val accepted = acceptItem(extracted, true)
val extracted2 = target.extractItem(lastSlot, accepted.count, false)
if (leftOver.count.toInt() != extracted.count) { acceptItem(extracted2, false)
extracted = target.extractItem(lastSlot, extracted.count - leftOver.count.toInt(), false)
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, extracted.count, false)
items.insertStack(ItemStackWrapper(extracted), false)
} else {
nextTick += INTERVAL * 4 nextTick += INTERVAL * 4
} }
} }
}
if (nextTick <= 0) { if (nextTick <= 0) {
nextTick = INTERVAL nextTick = INTERVAL
@ -223,7 +227,7 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
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),
IStorageEventConsumer<ItemStackWrapper> { IStorageEventConsumer<ItemStorageStack> {
override val defaultDisplayName: Component override val defaultDisplayName: Component
get() = MACHINE_NAME get() = MACHINE_NAME
@ -233,36 +237,36 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
private val relevantTuples = HashSet<UUID>() private val relevantTuples = HashSet<UUID>()
override val storageType: StorageStackType<ItemStackWrapper> override val storageType: StorageStack.Type<ItemStorageStack>
get() = ITEM_STORAGE get() = StorageStack.ITEMS
init { init {
cell.addStorageComponent(this) cell.addStorageComponent(this)
} }
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) { override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
if (!filter.match(stack.item)) { if (!filter.match(stack.toItemStack())) {
return return
} }
relevantTuples.add(id) relevantTuples.add(id)
} }
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) { override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
// no-op // no-op
} }
override fun removeStack(stack: ItemStackWrapper, id: UUID) { override fun onStackRemoved(id: UUID) {
relevantTuples.remove(id) relevantTuples.remove(id)
} }
override val filter = ItemFilter(MAX_FILTERS) { _, _, _ -> override val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
relevantTuples.clear() relevantTuples.clear()
val component = cell.graph.getVirtualComponent(ITEM_STORAGE) val component = cell.graph.getVirtualComponent(StorageStack.ITEMS)
for (tuple in component.stacks) { for (tuple in component.stacks) {
addStack(tuple, component) onStackAdded(tuple, component)
} }
lastSlot = 0 lastSlot = 0
@ -272,14 +276,13 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
private var lastSlot = 0 private var lastSlot = 0
private var nextTick = INTERVAL private var nextTick = INTERVAL
private val enoughEnergy get() = energy.batteryLevel >= ITEM_STORAGE.energyPerOperation
override val targetCapability: Capability<IItemHandler> override val targetCapability: Capability<IItemHandler>
get() = ForgeCapabilities.ITEM_HANDLER get() = ForgeCapabilities.ITEM_HANDLER
private val exportStacks: Stream<Pair<UUID, ItemStackWrapper>> private val exportStacks: Stream<Pair<UUID, ItemStorageStack>>
get() { get() {
val view = cell.graph.getVirtualComponent(ITEM_STORAGE) val view = cell.graph.getVirtualComponent(StorageStack.ITEMS)
return relevantTuples.stream().map { it to view[it] } return relevantTuples.stream().map { it to view[it] }
} }
@ -295,53 +298,43 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
val target = target.get().orNull() val target = target.get().orNull()
if (nextTick <= 0 && target != null && enoughEnergy) { if (nextTick <= 0 && target != null) {
val items = cell.graph.getVirtualComponent(ITEM_STORAGE) val items = cell.graph.getVirtualComponent(StorageStack.ITEMS)
if (lastSlot >= target.slots) { if (lastSlot >= target.slots) {
lastSlot = 0 lastSlot = 0
} }
var hit = false val any = exportStacks.anyMatch {
val (id, stack) = it
for (stack in exportStacks) { if (!target.isItemValid(lastSlot, stack.toItemStack())) return@anyMatch false
if (!target.isItemValid(lastSlot, stack.second.item)) {
continue val extracted = items.extractStack(id, stack.count.coerceAtMost(MAX_MOVE_PER_OPERATION), true)
if (extracted.isEmpty) return@anyMatch false
val required = StorageStack.ITEMS.energyPerOperation(extracted)
if (energy.extractEnergy(required, true) != required) return@anyMatch false
val toInsert = extracted.toItemStack()
val leftover = target.insertItem(lastSlot, toInsert, true)
if (leftover.count != toInsert.count) {
val extracted2 = items.extractStack(id, (toInsert.count - leftover.count).toBigInteger(), false)
energy.extractEnergy(StorageStack.ITEMS.energyPerOperation(extracted2), false)
val leftover2 = target.insertItem(lastSlot, extracted2.toItemStack(), false)
if (leftover2.isNotEmpty) {
items.insertStack(ItemStorageStack.unsafe(leftover2), false)
} }
val exportAmountA = items.extractStack( true
stack.first, stack.second.count.coerceAtMost( } else {
MAX_MOVE_PER_OPERATION
), true
).count
if (exportAmountA == BigInteger.ZERO) {
continue
}
var exportAmount =
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmountA, true).toIntSafe()
if (exportAmount == 0) {
break
}
val leftover = target.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, true)
if (leftover.count != exportAmount) {
hit = true
exportAmount = items.extractStack(
stack.first,
(exportAmount - leftover.count).toBigInteger(),
false false
).count.toInt()
target.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, false)
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmount, false)
break
} }
} }
if (!hit) { if (!any) {
lastSlot++ lastSlot++
} }
} }

View File

@ -4,27 +4,27 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.storage.IStorageComponent 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.IStorageTuple
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStorageStack
import ru.dbotthepony.mc.otm.storage.StorageStack
import java.math.BigInteger import java.math.BigInteger
import java.util.* import java.util.*
interface IItemMatteryDrive : IMatteryDrive<ItemStackWrapper> { interface IItemMatteryDrive : IMatteryDrive<ItemStorageStack> {
/** /**
* @param item * @param item
* @return all items belonging to passed class * @return all items belonging to passed class
*/ */
fun findItems(item: Item): Collection<IStorageTuple<ItemStackWrapper>> fun findItems(item: Item): Collection<IStorageTuple<ItemStorageStack>>
/** /**
* @param stack * @param stack
* @return [ItemStack] that match specified [stack] (item type, nbt tag) * @return [ItemStack] that match specified [stack] (item type, nbt tag)
*/ */
fun findItems(stack: ItemStack): IStorageTuple<ItemStackWrapper>? fun findItems(stack: ItemStack): IStorageTuple<ItemStorageStack>?
} }
interface IMatteryDrive<T : IStorageStack> : IStorageComponent<T> { interface IMatteryDrive<T : StorageStack<T>> : IStorageComponent<T> {
val uuid: UUID val uuid: UUID
var isDirty: Boolean var isDirty: Boolean

View File

@ -1,5 +1,7 @@
package ru.dbotthepony.mc.otm.capability.drive package ru.dbotthepony.mc.otm.capability.drive
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet import it.unimi.dsi.fastutil.objects.ObjectArraySet
import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmOverloads
import java.util.UUID import java.util.UUID
@ -18,13 +20,13 @@ import java.math.BigInteger
import java.util.ArrayList import java.util.ArrayList
import java.util.stream.Stream import java.util.stream.Stream
abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor( abstract class AbstractMatteryDrive<T : StorageStack<T>> @JvmOverloads constructor(
override var driveCapacity: BigInteger, 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> {
protected val tuples = HashMap<T, IStorageTuple<T>>() protected val stack2tuples = Object2ObjectOpenCustomHashMap<T, IStorageTuple.IMutable<T>>(StorageStack.Companion)
protected val tuplesByID: MutableMap<UUID, IStorageTuple<T>> = HashMap() protected val id2tuples = Object2ObjectOpenHashMap<UUID, IStorageTuple.IMutable<T>>()
override var isDirty = false override var isDirty = false
set(value) { set(value) {
@ -41,30 +43,25 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
override var storedCount: BigInteger = BigInteger.ZERO override var storedCount: BigInteger = BigInteger.ZERO
protected set protected set
@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 <= BigInteger.ZERO) return stack if (maxInsert <= BigInteger.ZERO) return stack
val key = stack.key() val tuple = stack2tuples[stack]
val tuple = tuples[key]
if (tuple != null) { if (tuple != null) {
if (!simulate) { if (!simulate) {
val oldCount = tuple.stack.count tuple.grow(maxInsert)
tuple.stack.grow(maxInsert)
storedCount += maxInsert storedCount += maxInsert
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(tuple.stack, tuple.id, oldCount) listener.onStackChanged(tuple.stack, tuple.id)
} }
isDirty = true isDirty = true
} }
val copy = stack.copy() as T return stack.shrink(maxInsert)
copy.shrink(maxInsert)
return copy
} }
if (storedDifferentStacks >= maxDifferentStacks) { if (storedDifferentStacks >= maxDifferentStacks) {
@ -75,28 +72,22 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
storedDifferentStacks++ storedDifferentStacks++
storedCount = storedCount.plus(maxInsert) storedCount = storedCount.plus(maxInsert)
val copy = stack.copy() as T val state = IStorageTuple.M(UUID.randomUUID(), stack.copy(maxInsert))
copy.count = maxInsert stack2tuples[stack] = state
id2tuples[state.id] = state
val state = StorageTuple(UUID.randomUUID(), copy)
tuples[key] = state
tuplesByID[state.id] = state
for (listener in listeners) { for (listener in listeners) {
listener.addStack(state.stack, state.id, this) listener.onStackAdded(state.stack, state.id, this)
} }
isDirty = true isDirty = true
} }
val copy = stack.copy() as T return stack.shrink(maxInsert)
copy.shrink(maxInsert)
return copy
} }
@Suppress("unchecked_cast")
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
val get = tuplesByID[id] ?: return storageType.empty val get = id2tuples[id] ?: return storageType.empty
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
var amount = amount var amount = amount
@ -109,28 +100,25 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
if (amount <= BigInteger.ZERO) if (amount <= BigInteger.ZERO)
return storageType.empty return storageType.empty
val copy = get.stack.copy() as T val copy = get.stack.copy(amount)
copy.count = amount
if (!simulate) { if (!simulate) {
if (amount == get.stack.count) { if (amount == get.stack.count) {
val key = get.stack.key() stack2tuples.remove(get.stack) ?: throw IllegalStateException("Can't find storage key for ${get.stack}")
tuples.remove(key) ?: throw IllegalStateException("Can't find storage key for ${get.stack}")
storedDifferentStacks-- storedDifferentStacks--
for (listener in listeners) { for (listener in listeners) {
listener.removeStack(get.stack, get.id) listener.onStackRemoved(get.id)
} }
} }
storedCount -= amount storedCount -= amount
val oldCount = get.stack.count get.shrink(amount)
get.stack.shrink(amount)
if (get.stack.count.isPositive) { if (get.stack.count.isPositive) {
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(get.stack, get.id, oldCount) listener.onStackChanged(get.stack, get.id)
} }
} }
@ -140,7 +128,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
return copy return copy
} }
protected abstract fun serializeStack(item: IStorageTuple<T>): CompoundTag? protected abstract fun serializeStack(item: T): CompoundTag?
protected abstract fun deserializeStack(tag: CompoundTag): T? protected abstract fun deserializeStack(tag: CompoundTag): T?
override fun serializeNBT(): CompoundTag { override fun serializeNBT(): CompoundTag {
@ -152,8 +140,8 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
val list = ListTag() val list = ListTag()
compound["items"] = list compound["items"] = list
for (stack in tuples.values) { for (tuple in stack2tuples.values) {
val serialized = serializeStack(stack) val serialized = serializeStack(tuple.stack)
if (serialized != null) { if (serialized != null) {
list.add(serialized) list.add(serialized)
@ -165,13 +153,13 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
override fun deserializeNBT(nbt: CompoundTag) { override fun deserializeNBT(nbt: CompoundTag) {
for (listener in listeners) { for (listener in listeners) {
for (get in tuples.values) { for (get in stack2tuples.values) {
listener.removeStack(get.stack, get.id) listener.onStackRemoved(get.id)
} }
} }
tuples.clear() stack2tuples.clear()
tuplesByID.clear() id2tuples.clear()
storedCount = BigInteger.ZERO storedCount = BigInteger.ZERO
storedDifferentStacks = 0 storedDifferentStacks = 0
// nextID = 0L // nextID = 0L
@ -183,23 +171,23 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
if (entry is CompoundTag) { if (entry is CompoundTag) {
val stack = deserializeStack(entry) val stack = deserializeStack(entry)
if (stack != null) { if (stack != null && stack.isNotEmpty) {
storedCount += stack.count storedCount += stack.count
storedDifferentStacks++ storedDifferentStacks++
val tuple = StorageTuple(UUID.randomUUID(), stack) val tuple = IStorageTuple.M(UUID.randomUUID(), stack)
tuples[tuple.stack.key()] = tuple stack2tuples[tuple.stack] = tuple
tuplesByID[tuple.id] = tuple id2tuples[tuple.id] = tuple
} }
} }
} }
} }
override fun get(id: UUID): T { override fun get(id: UUID): T {
return tuplesByID[id]?.stack ?: storageType.empty return id2tuples[id]?.stack ?: storageType.empty
} }
override val stacks: Stream<IStorageTuple<T>> get() { override val stacks: Stream<IStorageTuple<T>> get() {
return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) }.stream() return ArrayList<IStorageTuple<T>>(stack2tuples.size).also { it.addAll(stack2tuples.values) }.stream()
} }
protected val listeners = ObjectArraySet<IStorageEventConsumer<T>>() protected val listeners = ObjectArraySet<IStorageEventConsumer<T>>()
@ -211,8 +199,4 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean { override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
return listeners.remove(listener) return listeners.remove(listener)
} }
companion object {
private val LOGGER = LogManager.getLogger()
}
} }

View File

@ -1,69 +1,51 @@
package ru.dbotthepony.mc.otm.capability.drive package ru.dbotthepony.mc.otm.capability.drive
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.math.BigInteger import ru.dbotthepony.mc.otm.core.math.BigInteger
import ru.dbotthepony.mc.otm.core.math.serializeNBT import ru.dbotthepony.mc.otm.core.math.serializeNBT
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.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.ItemStorageStack
import ru.dbotthepony.mc.otm.storage.StorageStackType import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.storage.key
import java.math.BigInteger import java.math.BigInteger
import java.util.* import java.util.*
import kotlin.collections.ArrayList
class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDrive { class ItemMatteryDrive : AbstractMatteryDrive<ItemStorageStack>, IItemMatteryDrive {
constructor(capacity: BigInteger, max_different_stacks: Int) : super(capacity, maxDifferentStacks = max_different_stacks) constructor(capacity: BigInteger, maxDifferentStacks: Int) : super(capacity, maxDifferentStacks = maxDifferentStacks)
constructor(capacity: BigInteger, uuid: UUID, max_different_stacks: Int) : super(capacity, uuid, max_different_stacks) constructor(capacity: BigInteger, uuid: UUID, maxDifferentStacks: Int) : super(capacity, uuid, maxDifferentStacks)
constructor(capacity: BigInteger, uuid: UUID) : super(capacity, uuid) constructor(capacity: BigInteger, uuid: UUID) : super(capacity, uuid)
constructor(capacity: BigInteger) : super(capacity) constructor(capacity: BigInteger) : super(capacity)
override val storageType: StorageStackType<ItemStackWrapper> = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() override val storageType: StorageStack.Type<ItemStorageStack> = StorageStack.ITEMS
fun insertStack(item: ItemStack, simulate: Boolean): ItemStack { fun insertStack(item: ItemStack, simulate: Boolean): ItemStack {
return insertStack(ItemStackWrapper(item), simulate).stack return insertStack(ItemStorageStack(item), simulate).toItemStack()
} }
override fun serializeStack(item: IStorageTuple<ItemStackWrapper>): CompoundTag? { override fun serializeStack(item: ItemStorageStack): CompoundTag {
val tag = CompoundTag() val tag = CompoundTag()
val location = item.stack.registryName.toString() tag["item"] = item.toItemStack(1).serializeNBT()
tag["count"] = item.count.serializeNBT()
tag["item"] = location
tag["count"] = item.stack.count.serializeNBT()
val itag = item.stack.item.tag
if (itag != null) {
tag["data"] = itag
}
return tag return tag
} }
override fun deserializeStack(tag: CompoundTag): ItemStackWrapper? { override fun deserializeStack(tag: CompoundTag): ItemStorageStack? {
val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(tag.getString("item"))) if ("item" in tag && "count" in tag) {
val item = tag["item"] as? CompoundTag ?: return null
if (item != null && item !== Items.AIR) {
val count = BigInteger(tag["count"]) val count = BigInteger(tag["count"])
val itemstack = ItemStack(item, 1) return ItemStorageStack.unsafe(ItemStack.of(item), count)
itemstack.tag = tag["data"] as? CompoundTag
return ItemStackWrapper(itemstack, copy = false).also { it.count = count }
} }
return null return null
} }
override fun findItems(item: Item): List<IStorageTuple<ItemStackWrapper>> { override fun findItems(item: Item): List<IStorageTuple<ItemStorageStack>> {
val list = ArrayList<IStorageTuple<ItemStackWrapper>>() val list = ArrayList<IStorageTuple<ItemStorageStack>>()
for ((key, value) in tuples.entries) { for ((key, value) in stack2tuples.entries) {
if (key.item.item === item) { if (key.item === item) {
list.add(value) list.add(value)
} }
} }
@ -71,8 +53,8 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
return list return list
} }
override fun findItems(stack: ItemStack): IStorageTuple<ItemStackWrapper>? { override fun findItems(stack: ItemStack): IStorageTuple<ItemStorageStack>? {
return tuples[ItemStackWrapper(stack).key()] return stack2tuples[ItemStorageStack.unsafe(stack)]
} }
companion object { companion object {

View File

@ -68,7 +68,7 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
object : AbstractSlotPanel<DriveViewerScreen>(this@DriveViewerScreen, grid, 0f, 0f) { object : AbstractSlotPanel<DriveViewerScreen>(this@DriveViewerScreen, grid, 0f, 0f) {
override val itemStack: ItemStack get() { override val itemStack: ItemStack get() {
val index = i + scrollBar.scroll * GRID_WIDTH val index = i + scrollBar.scroll * GRID_WIDTH
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY
} }
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {

View File

@ -32,7 +32,7 @@ import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.core.util.formatReadableNumber import ru.dbotthepony.mc.otm.core.util.formatReadableNumber
import ru.dbotthepony.mc.otm.core.util.formatSiComponent import ru.dbotthepony.mc.otm.core.util.formatSiComponent
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE import ru.dbotthepony.mc.otm.storage.StorageStack
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
@MouseTweaksDisableWheelTweak @MouseTweaksDisableWheelTweak
@ -73,7 +73,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
private val index get() = i + viewScrollBar.scroll * ITEM_GRID_WIDTH private val index get() = i + viewScrollBar.scroll * ITEM_GRID_WIDTH
override val itemStack: ItemStack get() { override val itemStack: ItemStack get() {
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY
} }
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
@ -88,10 +88,10 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
renderSlotBackground(graphics, mouseX, mouseY, partialTick) renderSlotBackground(graphics, mouseX, mouseY, partialTick)
val itemstack = menu.networkedItemView.sortedView.getOrNull(index)?.stack ?: ITEM_STORAGE.empty val itemstack = menu.networkedItemView.sortedView.getOrNull(index)?.stack ?: StorageStack.ITEMS.empty
val stack = graphics.pose() val stack = graphics.pose()
renderRegular(graphics, itemstack.stack, "") renderRegular(graphics, itemstack.toItemStack(), "")
if (!itemstack.isEmpty) { if (!itemstack.isEmpty) {
stack.pushPose() stack.pushPose()

View File

@ -33,43 +33,43 @@ private val QIO_LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "item_s
private class QIOTuple( private class QIOTuple(
val mekanismItem: HashedItem, val mekanismItem: HashedItem,
override val stack: ItemStackWrapper, override var stack: ItemStorageStack,
override val id: UUID, override val id: UUID,
var mark: Long var mark: Long
) : IStorageTuple<ItemStackWrapper> ) : IStorageTuple.IMutable<ItemStorageStack>
private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<ItemStackWrapper> { private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<ItemStorageStack> {
private var mark = 0L private var mark = 0L
override val storageType: StorageStackType<ItemStackWrapper> override val storageType: StorageStack.Type<ItemStorageStack>
get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE() get() = StorageStack.ITEMS
private val index = HashMap<UUID, QIOTuple>() private val index = HashMap<UUID, QIOTuple>()
private val tracked = HashMap<HashedItem, QIOTuple>() private val tracked = HashMap<HashedItem, QIOTuple>()
private val listeners = ArrayList<IStorageEventConsumer<ItemStackWrapper>>() private val listeners = ArrayList<IStorageEventConsumer<ItemStorageStack>>()
override fun get(id: UUID): ItemStackWrapper { override fun get(id: UUID): ItemStorageStack {
return index[id]?.stack ?: ItemStackWrapper.EMPTY return index[id]?.stack ?: ItemStorageStack.EMPTY
} }
override val stacks: Stream<IStorageTuple<ItemStackWrapper>> get() { override val stacks: Stream<IStorageTuple<ItemStorageStack>> get() {
return ArrayList<IStorageTuple<ItemStackWrapper>>(index.size).also { it.addAll(index.values) }.stream() return ArrayList<IStorageTuple<ItemStorageStack>>(index.size).also { it.addAll(index.values) }.stream()
} }
override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper { override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack {
// Because there is no simulate method on QIO array, we have to simulate it by ourselves. // Because there is no simulate method on QIO array, we have to simulate it by ourselves.
val hash = HashedItem.create(stack.item) val hash = HashedItem.create(stack.toItemStack())
if (!simulate) { if (!simulate) {
//val used = TransporterManager.getToUse(stack.stack, parent.addItem(stack.stack)) //val used = TransporterManager.getToUse(stack.stack, parent.addItem(stack.stack))
val used = parent.addItem(stack.stack) val used = parent.addItem(stack.toItemStack())
if (used.count == stack.count.toInt()) { if (used.count == stack.count.toInt()) {
return stack return stack
} }
scan(hash) scan(hash)
return ItemStackWrapper(used) return ItemStorageStack(used)
} }
if (parent.totalItemCount >= parent.totalItemCountCapacity) { if (parent.totalItemCount >= parent.totalItemCountCapacity) {
@ -80,33 +80,31 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
return stack return stack
} }
val inserted = stack.copy() return stack.copy((parent.totalItemCountCapacity - parent.totalItemCount).toBigInteger().coerceAtMost(stack.count))
inserted.count = (parent.totalItemCountCapacity - parent.totalItemCount).toBigInteger().coerceAtMost(stack.count)
return inserted
} }
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStackWrapper { override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStorageStack {
// 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.
if (!amount.isPositive) if (!amount.isPositive)
return ItemStackWrapper.EMPTY return ItemStorageStack.EMPTY
var local = index[id] ?: return ItemStackWrapper.EMPTY var local = index[id] ?: return ItemStorageStack.EMPTY
scan(local.mekanismItem) scan(local.mekanismItem)
local = index[id] ?: return ItemStackWrapper.EMPTY // unexpected... local = index[id] ?: return ItemStorageStack.EMPTY // unexpected...
if (simulate) { if (simulate) {
return local.stack.copy().also { it.count = it.count.coerceAtMost(amount) } return local.stack.copy(local.stack.count.coerceAtMost(amount))
} }
val removed = parent.removeByType(local.mekanismItem, amount.toInt()) val removed = parent.removeByType(local.mekanismItem, amount.toInt())
if (removed.isEmpty) { if (removed.isEmpty) {
return ItemStackWrapper.EMPTY return ItemStorageStack.EMPTY
} }
val copy = ItemStackWrapper(removed) val copy = ItemStorageStack(removed)
if (local.stack.count > copy.count) { if (local.stack.count > copy.count) {
// expecting stack to be still present in QIO storage grid // expecting stack to be still present in QIO storage grid
@ -118,7 +116,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
} }
for (listener in listeners) { for (listener in listeners) {
listener.removeStack(local) listener.onStackRemoved(local)
} }
index.remove(local.id) index.remove(local.id)
@ -128,7 +126,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
return copy return copy
} }
override fun addListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean { override fun addListener(listener: IStorageEventConsumer<ItemStorageStack>): Boolean {
if (!listeners.contains(listener)) { if (!listeners.contains(listener)) {
listeners.add(listener) listeners.add(listener)
return true return true
@ -137,30 +135,29 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
return false return false
} }
override fun removeListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean { override fun removeListener(listener: IStorageEventConsumer<ItemStorageStack>): Boolean {
return listeners.remove(listener) return listeners.remove(listener)
} }
private fun scan(at: HashedItem, value: QIOItemTypeData = parent.itemDataMap[at] ?: throw IllegalArgumentException("$parent does not have item $at (${at.stack})")) { private fun scan(at: HashedItem, value: QIOItemTypeData = parent.itemDataMap[at] ?: throw IllegalArgumentException("$parent does not have item $at (${at.internalStack})")) {
val local = tracked[at] val local = tracked[at]
if (local != null) { if (local != null) {
local.mark = mark local.mark = mark
if (local.stack.count.toLong() != value.count) { if (local.stack.count.toLong() != value.count) {
val oldCount = local.stack.count local.stack = local.stack.copy(value.count.toBigInteger())
local.stack.count = value.count.toBigInteger()
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(local, oldCount) listener.onStackChanged(local)
} }
} }
} else { } else {
val tuple = QIOTuple(at, ItemStackWrapper(at.stack).also { it.count = value.count.toBigInteger() }, UUID.randomUUID(), mark) val tuple = QIOTuple(at, ItemStorageStack(at.internalStack, value.count.toBigInteger()), UUID.randomUUID(), mark)
index[tuple.id] = tuple index[tuple.id] = tuple
for (listener in listeners) { for (listener in listeners) {
listener.addStack(tuple, this) listener.onStackAdded(tuple, this)
} }
tracked[at] = tuple tracked[at] = tuple
@ -192,7 +189,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
val tuple = tracked.remove(key)!! val tuple = tracked.remove(key)!!
for (listener in listeners) { for (listener in listeners) {
listener.removeStack(tuple) listener.onStackRemoved(tuple)
} }
index.remove(tuple.id) index.remove(tuple.id)

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.core.collect package ru.dbotthepony.mc.otm.core.collect
import it.unimi.dsi.fastutil.objects.ObjectIterators
import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.addAll
import java.util.Optional import java.util.Optional
import java.util.function.BinaryOperator import java.util.function.BinaryOperator
@ -123,6 +124,18 @@ class FlatMappingIterator<T, R>(private val parent: Iterator<T>, private val map
} }
} }
fun <T> concatIterators(): MutableIterator<T> {
return ObjectIterators.EMPTY_ITERATOR as MutableIterator<T>
}
fun <T> concatIterators(a: Iterator<T>): MutableIterator<T> {
return a as MutableIterator<T>
}
fun <T> concatIterators(vararg iterators: Iterator<T>): MutableIterator<T> {
return iterators.iterator().flatMap { it }
}
/** /**
* Limits amount of values returned by [parent] iterator to return at most [limit] values * Limits amount of values returned by [parent] iterator to return at most [limit] values
* *

View File

@ -8,32 +8,22 @@ import ru.dbotthepony.mc.otm.storage.*
import java.util.LinkedList import java.util.LinkedList
class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() { class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() {
private val virtualComponents = Object2ObjectArrayMap<StorageStackType<*>, VirtualComponent<*>>() private val virtualComponents = Object2ObjectArrayMap<StorageStack.Type<*>, VirtualComponent<*>>()
val powerDemandingNodes = LinkedList<IMatteryEnergyStorage>() val powerDemandingNodes = LinkedList<IMatteryEnergyStorage>()
/** fun <T : StorageStack<T>> getVirtualComponent(type: StorageStack.Type<T>): VirtualComponent<T> {
* Returns a [VirtualComponent] representing [type] storage
*/
fun <T : IStorageStack> getVirtualComponent(type: StorageStackType<T>): VirtualComponent<T> {
return virtualComponents.computeIfAbsent(type, Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<T> return virtualComponents.computeIfAbsent(type, Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<T>
} }
/** fun <T : StorageStack<T>> add(storage: IStorage<T>) {
* Returns a [VirtualComponent] representing [type] storage
*/
fun <T : IStorageStack> getVirtualComponent(type: Class<T>): VirtualComponent<T> {
return virtualComponents.computeIfAbsent(StorageRegistry.get(type), Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<T>
}
fun <T : IStorageStack> add(storage: IStorage<T>) {
getVirtualComponent(storage.storageType).add(storage) getVirtualComponent(storage.storageType).add(storage)
} }
fun <T : IStorageStack> remove(storage: IStorage<T>) { fun <T : StorageStack<T>> remove(storage: IStorage<T>) {
getVirtualComponent(storage.storageType).remove(storage) getVirtualComponent(storage.storageType).remove(storage)
} }
fun <T : IStorageStack> contains(storage: IStorage<T>): Boolean { fun <T : StorageStack<T>> contains(storage: IStorage<T>): Boolean {
val virtual = virtualComponents[storage.storageType] ?: return false val virtual = virtualComponents[storage.storageType] ?: return false
return (virtual as VirtualComponent<T>).contains(storage) return (virtual as VirtualComponent<T>).contains(storage)
} }
@ -45,5 +35,4 @@ class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() {
override fun onNodeRemoved(node: StorageNode) { override fun onNodeRemoved(node: StorageNode) {
node.removeComponents(this) node.removeComponents(this)
} }
} }

View File

@ -5,9 +5,12 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.graph.GraphNode import ru.dbotthepony.mc.otm.graph.GraphNode
import ru.dbotthepony.mc.otm.storage.IStorage import ru.dbotthepony.mc.otm.storage.IStorage
import ru.dbotthepony.mc.otm.storage.IStorageStack import ru.dbotthepony.mc.otm.storage.IVirtualStorageComponent
import ru.dbotthepony.mc.otm.storage.StorageStackType import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.storage.VirtualComponent
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
import java.util.* import java.util.*
import java.util.function.Supplier
open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) { open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) {
protected val components = ArrayList<IStorage<*>>() protected val components = ArrayList<IStorage<*>>()
@ -72,71 +75,31 @@ open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
fun <T : IStorageStack, U : IStorage<T>> computeIfAbsent(identity: StorageStackType<T>, provider: (StorageStackType<T>) -> U): U { fun <T : StorageStack<T>, U : IStorage<T>> computeIfAbsent(type: StorageStack.Type<T>, provider: (StorageStack.Type<T>) -> U): U {
for (component in components) { return components.firstOrNull { it.storageType == type } as U? ?: provider(type).also { addStorageComponent(it) }
if (component.storageType === identity) {
return component as U
}
}
val factory = provider(identity)
addStorageComponent(factory)
return factory
} }
fun addStorageComponent(component: IStorage<*>) { fun addStorageComponent(component: IStorage<*>) {
for (component1 in components) { if (!components.any { component === it || it.storageType === component.storageType }) {
if (component === component1 || component1.storageType === component.storageType) {
return
}
}
components.add(component) components.add(component)
if (isValid && !manualAttaching) if (isValid && !manualAttaching)
graph.add(component) graph.add(component)
} }
}
fun removeStorageComponent(component: IStorage<*>) { fun removeStorageComponent(component: IStorage<*>) {
var indexOf = -1 val indexOf = components.indexOfFirst { component === it || it.storageType === component.storageType }
if (indexOf == -1) return
for ((i, component1) in components.withIndex()) { val self = components.removeAt(indexOf)
if (component === component1 || component1.storageType === component.storageType) { if (isValid) graph.remove(self)
indexOf = i
break
}
} }
if (indexOf == -1) { fun removeStorageComponent(component: StorageStack.Type<*>) {
return val indexOf = components.indexOfFirst { it.storageType === component }
} if (indexOf == -1) return
val self = components.removeAt(indexOf)
val self = components[indexOf] if (isValid) graph.remove(self)
components.removeAt(indexOf)
if (isValid)
graph.remove(self)
}
fun removeStorageComponent(component: StorageStackType<*>) {
var indexOf = -1
for ((i, component1) in components.withIndex()) {
if (component1.storageType === component) {
indexOf = i
break
}
}
if (indexOf == -1) {
return
}
val self = components[indexOf]
components.removeAt(indexOf)
if (isValid && !manualAttaching)
graph.remove(self)
} }
override fun invalidate() { override fun invalidate() {

View File

@ -97,7 +97,8 @@ import ru.dbotthepony.mc.otm.network.GenericNetworkChannel
import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.registry.RegistryDelegate import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import ru.dbotthepony.mc.otm.secondTime import ru.dbotthepony.mc.otm.secondTime
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStorageStack
import ru.dbotthepony.mc.otm.storage.StorageStack
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import java.io.File import java.io.File
@ -1202,9 +1203,9 @@ object MatterManager {
val drive = value.getCapability(MatteryCapability.DRIVE).orNull() val drive = value.getCapability(MatteryCapability.DRIVE).orNull()
if (drive != null && drive.storageType == OverdriveThatMatters.INSTANCE.ITEM_STORAGE()) { if (drive != null && drive.storageType == StorageStack.ITEMS) {
(drive as IMatteryDrive<ItemStackWrapper>).stacks (drive as IMatteryDrive<ItemStorageStack>).stacks
.map { it.stack.stack } .map { it.stack.toItemStack() }
.filter { !it.isEmpty } .filter { !it.isEmpty }
.map { get(it, level + 1, true) } .map { get(it, level + 1, true) }
.reduce(::reduce) .reduce(::reduce)

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.menu.data package ru.dbotthepony.mc.otm.menu.data
import com.mojang.blaze3d.platform.InputConstants import com.mojang.blaze3d.platform.InputConstants
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
@ -75,11 +74,11 @@ object ClearItemViewPacket : MatteryPacket {
} }
} }
class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStackWrapper) : MatteryPacket { class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStorageStack) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) { override fun write(buff: FriendlyByteBuf) {
buff.writeInt(containerId) buff.writeInt(containerId)
buff.writeInt(id) buff.writeInt(id)
buff.writeBigItem(stack) stack.write(buff)
} }
override fun play(context: Supplier<NetworkEvent.Context>) { override fun play(context: Supplier<NetworkEvent.Context>) {
@ -127,7 +126,7 @@ class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStackWrap
fun read(buffer: FriendlyByteBuf): StackAddPacket { fun read(buffer: FriendlyByteBuf): StackAddPacket {
val containerId = buffer.readInt() val containerId = buffer.readInt()
val id = buffer.readInt() val id = buffer.readInt()
val item = buffer.readBigItem() val item = StorageStack.ITEMS.read(buffer)
return StackAddPacket(containerId, id, item) return StackAddPacket(containerId, id, item)
} }
} }
@ -151,7 +150,7 @@ class StackChangePacket(val id: Int, val stackID: Int, val newCount: BigInteger)
val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id") val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
val state = view.localState[stackID] ?: throw IllegalStateException("No such stack with id $stackID in $view") val state = view.localState[stackID] ?: throw IllegalStateException("No such stack with id $stackID in $view")
state.stack.count = newCount state.stack = state.stack.copy(newCount)
view.resort() view.resort()
} }
} }
@ -199,11 +198,11 @@ class StackRemovePacket(val id: Int, val stackID: Int) : MatteryPacket {
/** /**
* Creates a virtual, slotless container for Player to interaction with. * Creates a virtual, slotless container for Player to interaction with.
*/ */
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer<ItemStackWrapper> { open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer<ItemStorageStack> {
data class NetworkedItem constructor(val id: Int, val stack: ItemStackWrapper, val upstreamId: UUID? = null) data class NetworkedItem(val id: Int, var stack: ItemStorageStack, val upstreamId: UUID? = null)
override val storageType: StorageStackType<ItemStackWrapper> override val storageType: StorageStack.Type<ItemStorageStack>
get() = ITEM_STORAGE get() = StorageStack.ITEMS
// this (how client see and interact with) // this (how client see and interact with)
val localState = Int2ObjectOpenHashMap<NetworkedItem>() val localState = Int2ObjectOpenHashMap<NetworkedItem>()
@ -233,7 +232,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
fun resort() { fun resort() {
sortedView.sortWith { a, b -> sortedView.sortWith { a, b ->
return@sortWith sorter.compare(a.stack.item, b.stack.item) return@sortWith sorter.compare(a.stack.toItemStack(), b.stack.toItemStack())
} }
} }
@ -243,7 +242,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
operator fun get(id: Int): NetworkedItem? = localState[id] operator fun get(id: Int): NetworkedItem? = localState[id]
var provider: IStorageComponent<ItemStackWrapper>? = null var provider: IStorageComponent<ItemStorageStack>? = null
private set private set
fun mouseClick(index: Int, mouseButton: Int) { fun mouseClick(index: Int, mouseButton: Int) {
@ -264,33 +263,33 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
) )
} }
fun setComponent(provider: IStorageComponent<ItemStackWrapper>?) { fun setComponent(provider: IStorageComponent<ItemStorageStack>?) {
if (provider === this.provider) return if (provider === this.provider) return
this.provider?.removeListenerAuto(this) this.provider?.removeListenerAndNotify(this)
this.provider = provider this.provider = provider
provider?.addListenerAuto(this) provider?.addListenerAndNotify(this)
} }
fun removed() { fun removed() {
provider?.removeListenerAuto(this) provider?.removeListenerAndNotify(this)
} }
val itemCount get() = localState.values.size val itemCount get() = localState.values.size
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) { override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
check(!upstreamState.containsKey(id)) { "Already tracking ItemStack with upstream id $id!" } check(!upstreamState.containsKey(id)) { "Already tracking ItemStack with upstream id $id!" }
val state = NetworkedItem(nextItemID++, stack.copy(), id) val state = NetworkedItem(nextItemID++, stack, id)
this.localState[state.id] = state this.localState[state.id] = state
upstreamState[id] = state upstreamState[id] = state
network { StackAddPacket(menu.containerId, state.id, state.stack) } network { StackAddPacket(menu.containerId, state.id, state.stack) }
} }
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) { override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!") val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
get.stack.count = stack.count get.stack = stack
network { StackChangePacket(menu.containerId, get.id, stack.count) } network { StackChangePacket(menu.containerId, get.id, stack.count) }
} }
@ -300,7 +299,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
} }
} }
override fun removeStack(stack: ItemStackWrapper, id: UUID) { override fun onStackRemoved(id: UUID) {
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!") val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
upstreamState.remove(id) upstreamState.remove(id)
localState.remove(get.id) localState.remove(get.id)
@ -342,7 +341,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
if (stackId < 0 || !ply.abilities.instabuild) return if (stackId < 0 || !ply.abilities.instabuild) return
val state = get(stackId) ?: return val state = get(stackId) ?: return
val copy = state.stack.stack.also { it.count = it.maxStackSize } val copy = state.stack.toItemStack().also { it.count = it.maxStackSize }
ply.containerMenu.carried = copy ply.containerMenu.carried = copy
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(ply.containerMenu.carried)) MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(ply.containerMenu.carried))
@ -353,17 +352,18 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
if (click == ClickType.QUICK_MOVE && stackId > -1) { if (click == ClickType.QUICK_MOVE && stackId > -1) {
val state = get(stackId) ?: return val state = get(stackId) ?: return
val stack = state.stack.toItemStack()
val amount = val amount =
if (action == ClickAction.PRIMARY) if (action == ClickAction.PRIMARY)
state.stack.item.maxStackSize stack.maxStackSize
else else
1.coerceAtLeast(state.stack.item.maxStackSize / 2) 1.coerceAtLeast(stack.maxStackSize / 2)
val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), 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.toItemStack(), false)
if (remaining.count != extracted.count.toInt()) { if (remaining.count != extracted.count.toInt()) {
provider.extractStack(state.upstreamId, (extracted.count.toInt() - remaining.count).toBigInteger(), false) provider.extractStack(state.upstreamId, (extracted.count.toInt() - remaining.count).toBigInteger(), false)
@ -380,8 +380,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
val amount = carried.count val amount = carried.count
if (amount == carried.count) { if (amount == carried.count) {
val stack = provider.insertStack(ItemStackWrapper(menu.carried), false).stack menu.carried = provider.insertStack(ItemStorageStack(menu.carried), false).toItemStack()
menu.carried = stack
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
menu.setRemoteCarried(menu.carried.copy()) menu.setRemoteCarried(menu.carried.copy())
} }
@ -389,7 +388,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
val copy = menu.carried.copy() val copy = menu.carried.copy()
copy.count = 1 copy.count = 1
if (provider.insertStack(ItemStackWrapper(copy), false).isEmpty) { if (provider.insertStack(ItemStorageStack(copy), false).isEmpty) {
menu.carried.shrink(1) menu.carried.shrink(1)
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
menu.setRemoteCarried(menu.carried.copy()) menu.setRemoteCarried(menu.carried.copy())
@ -397,15 +396,15 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
} }
} else if (stackId > -1) { } else if (stackId > -1) {
val state = get(stackId) ?: return val state = get(stackId) ?: return
val stack = state.stack.toItemStack()
val amount = val amount =
if (action == ClickAction.PRIMARY) if (action == ClickAction.PRIMARY)
state.stack.item.maxStackSize stack.maxStackSize
else else
(state.stack.stack.count / 2).coerceAtMost(state.stack.item.maxStackSize / 2).coerceAtLeast(1) (stack.count / 2).coerceAtMost(stack.maxStackSize / 2).coerceAtLeast(1)
val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false) menu.carried = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false).toItemStack()
menu.carried = extracted.stack
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
menu.setRemoteCarried(menu.carried.copy()) menu.setRemoteCarried(menu.carried.copy())
} }

View File

@ -19,9 +19,9 @@ import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.registry.MMenus import ru.dbotthepony.mc.otm.registry.MMenus
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE import ru.dbotthepony.mc.otm.storage.ItemStorageStack
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.storage.PoweredVirtualComponent import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
class DriveViewerMenu @JvmOverloads constructor( class DriveViewerMenu @JvmOverloads constructor(
containerID: Int, containerID: Int,
@ -33,8 +33,8 @@ class DriveViewerMenu @JvmOverloads constructor(
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null) override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
val driveSlot: MatterySlot val driveSlot: MatterySlot
private val powered: PoweredVirtualComponent<ItemStackWrapper>? private val powered: PoweredVirtualComponent<ItemStorageStack>?
private var lastDrive: IMatteryDrive<ItemStackWrapper>? = null private var lastDrive: IMatteryDrive<ItemStorageStack>? = null
init { init {
val container = tile?.container ?: SimpleContainer(1) val container = tile?.container ?: SimpleContainer(1)
@ -47,8 +47,8 @@ class DriveViewerMenu @JvmOverloads constructor(
if (tile != null) { if (tile != null) {
powered = PoweredVirtualComponent( powered = PoweredVirtualComponent(
ItemStackWrapper::class.java, StorageStack.ITEMS,
tile.getCapability(MatteryCapability.ENERGY).resolve().get() tile.getCapability(MatteryCapability.ENERGY).resolve()::get
) )
this.networkedItemView.setComponent(powered) this.networkedItemView.setComponent(powered)
@ -83,8 +83,8 @@ class DriveViewerMenu @JvmOverloads constructor(
if (!itemStack.isEmpty) { if (!itemStack.isEmpty) {
itemStack.getCapability(MatteryCapability.DRIVE).ifPresentK { itemStack.getCapability(MatteryCapability.DRIVE).ifPresentK {
if (it.storageType === ITEM_STORAGE) { if (it.storageType == StorageStack.ITEMS) {
lastDrive = it as IMatteryDrive<ItemStackWrapper> lastDrive = it as IMatteryDrive<ItemStorageStack>
} }
} }
} }
@ -146,7 +146,7 @@ class DriveViewerMenu @JvmOverloads constructor(
if (lastDrive == null || powered == null) if (lastDrive == null || powered == null)
return ItemStack.EMPTY return ItemStack.EMPTY
val remaining = powered.insertStack(ItemStackWrapper(item), false) val remaining = powered.insertStack(ItemStorageStack(item), false)
if (remaining.count.toInt() == item.count) if (remaining.count.toInt() == item.count)
return ItemStack.EMPTY return ItemStack.EMPTY

View File

@ -139,7 +139,7 @@ class ItemMonitorMenu @JvmOverloads constructor(
return ItemStack.EMPTY return ItemStack.EMPTY
} }
val leftover = networkedItemView.provider?.insertStack(ItemStackWrapper(slot.item), false)?.stack ?: slot.item val leftover = networkedItemView.provider?.insertStack(ItemStorageStack(slot.item), false)?.toItemStack() ?: slot.item
if (leftover.count == slot.item.count) { if (leftover.count == slot.item.count) {
return ItemStack.EMPTY return ItemStack.EMPTY
@ -163,7 +163,7 @@ class ItemMonitorMenu @JvmOverloads constructor(
return item return item
} }
remainder = networkedItemView.provider?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder remainder = networkedItemView.provider?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
slots[slotIndex].set(remainder) slots[slotIndex].set(remainder)
if (remainder.isEmpty) { if (remainder.isEmpty) {
@ -229,8 +229,8 @@ class ItemMonitorMenu @JvmOverloads constructor(
try { try {
when (settings.resultTarget) { when (settings.resultTarget) {
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM -> { ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM -> {
val wrapper = ItemStackWrapper(item) val wrapper = ItemStorageStack(item)
var remaining = tile.poweredView?.insertStack(wrapper, true)?.stack ?: return ItemStack.EMPTY var remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY
if (remaining.isEmpty) { if (remaining.isEmpty) {
tile.poweredView!!.insertStack(wrapper, false) tile.poweredView!!.insertStack(wrapper, false)
@ -241,7 +241,7 @@ class ItemMonitorMenu @JvmOverloads constructor(
remaining = moveItemStackToSlots(remaining, playerInventorySlots, simulate = true) remaining = moveItemStackToSlots(remaining, playerInventorySlots, simulate = true)
if (remaining.isEmpty) { if (remaining.isEmpty) {
remaining = tile.poweredView!!.insertStack(wrapper, false).stack remaining = tile.poweredView!!.insertStack(wrapper, false).toItemStack()
moveItemStackToSlots(remaining, playerInventorySlots, simulate = false) moveItemStackToSlots(remaining, playerInventorySlots, simulate = false)
craftingResult.remove(item.count) craftingResult.remove(item.count)
return item return item
@ -259,12 +259,12 @@ class ItemMonitorMenu @JvmOverloads constructor(
return item return item
} }
val wrapper = ItemStackWrapper(remaining) val wrapper = ItemStorageStack(remaining)
remaining = tile.poweredView?.insertStack(wrapper, true)?.stack ?: return ItemStack.EMPTY remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY
if (remaining.isEmpty) { if (remaining.isEmpty) {
moveItemStackToSlots(item, playerInventorySlots, simulate = false) moveItemStackToSlots(item, playerInventorySlots, simulate = false)
tile.poweredView!!.insertStack(wrapper, false).stack tile.poweredView!!.insertStack(wrapper, false)
craftingResult.remove(item.count) craftingResult.remove(item.count)
return item return item
} }

View File

@ -43,6 +43,7 @@ import ru.dbotthepony.mc.otm.matter.IMatterFunction
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.StripedColoredDecorativeBlock import ru.dbotthepony.mc.otm.registry.objects.StripedColoredDecorativeBlock
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.triggers.AndroidBatteryTrigger import ru.dbotthepony.mc.otm.triggers.AndroidBatteryTrigger
import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
@ -274,6 +275,7 @@ object MRegistry {
MItemFunctionTypes.register(bus) MItemFunctionTypes.register(bus)
MLootItemConditions.register(bus) MLootItemConditions.register(bus)
MRecipes.register(bus) MRecipes.register(bus)
StorageStack.register(bus)
if (FMLEnvironment.dist == Dist.CLIENT) { if (FMLEnvironment.dist == Dist.CLIENT) {
MBlockColors.register(bus) MBlockColors.register(bus)

View File

@ -4,69 +4,20 @@ import java.math.BigInteger
import java.util.* import java.util.*
import java.util.stream.Stream import java.util.stream.Stream
/**
* Represents a stack in storage system.
*
* In general there are next rules:
* 1. Once created, stack is immutable, except for it's [count], therefore [copy] is expected to do
* "shallow" copies (not performance taxing).
* 2. Due to condition above, and due to sublying storage most of time being
* mutable, it is expected you do defensive copies. Examples of when you should do
* them are described on related interfaces.
* 3. For storing stacks as [Map] keys, a stack with size of 1 is utilized (requiring [equals] and [hashCode] to return meaningful results, see [IStorageStack.key]).
* 4. For equality (INCLUDING count), regular [equals] is utilized.
*/
interface IStorageStack {
fun copy(): IStorageStack
/**
* Size of this storage stack
*
* This is overriden in subclasses
*/
var count: BigInteger
val isEmpty: Boolean
/**
* @return max stack size for this stack object,
* null if unlimited (default)
*/
val maxStackSize: BigInteger? get() = null
/**
* Increase [count] by [amount]
*/
fun grow(amount: BigInteger) {
count += amount
}
/**
* Decrease [count] by [amount]
*/
fun shrink(amount: BigInteger) {
count -= amount
}
}
@Suppress("UNCHECKED_CAST")
fun <T : IStorageStack> T.key(): T {
return copy().also { it.count = BigInteger.ONE } as T
}
/** /**
* Storage system root, along IStorageStack interface * Storage system root, along IStorageStack interface
*/ */
interface IStorage<T : IStorageStack> { interface IStorage<T : StorageStack<T>> {
/** /**
* @return Identity of this virtual component * @return Identity of this virtual component
*/ */
val storageType: StorageStackType<T> val storageType: StorageStack.Type<T>
} }
/** /**
* Generates events for [IStorageEventConsumer] * Generates events for [IStorageEventConsumer]
*/ */
interface IStorageEventProducer<T : IStorageStack> : IStorage<T> { interface IStorageEventProducer<T : StorageStack<T>> : IStorage<T> {
/** /**
* [listener] is [IStorageEventConsumer] which want to subscribe to our events * [listener] is [IStorageEventConsumer] which want to subscribe to our events
*/ */
@ -81,27 +32,39 @@ interface IStorageEventProducer<T : IStorageStack> : IStorage<T> {
/** /**
* Consumes events produced by [IStorageEventConsumer] * Consumes events produced by [IStorageEventConsumer]
*/ */
interface IStorageEventConsumer<T : IStorageStack> : IStorage<T> { interface IStorageEventConsumer<T : StorageStack<T>> : IStorage<T> {
/** /**
* Fired on whenever an object is added (to listener) we subscribed to * Fired on whenever an object is added (to listener) we subscribed to
*/ */
fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>) fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>)
/** /**
* Fired on whenever an object is changes on listener we subscribed to * Fired on whenever an object is changes on listener we subscribed to
*/ */
fun changeStack(stack: T, id: UUID, oldCount: BigInteger) fun onStackChanged(stack: T, id: UUID)
/** /**
* Fired on whenever an object is removed from listener we subscribed to * Fired on whenever an object is removed from listener we subscribed to
*/ */
fun removeStack(stack: T, id: UUID) fun onStackRemoved(id: UUID)
fun onStackChanged(tuple: IStorageTuple<T>) {
onStackChanged(tuple.stack, tuple.id)
}
fun onStackAdded(tuple: IStorageTuple<T>, provider: IStorageProvider<T>) {
onStackAdded(tuple.stack, tuple.id, provider)
}
fun onStackRemoved(tuple: IStorageTuple<T>) {
onStackRemoved(tuple.id)
}
} }
/** /**
* Storage acceptor, accepts (insert only) stacks provided via [insertStack] method. * Storage acceptor, accepts (insert only) stacks provided via [insertStack] method.
*/ */
interface IStorageAcceptor<T : IStorageStack> : IStorage<T> { interface IStorageAcceptor<T : StorageStack<T>> : 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
@ -112,10 +75,7 @@ interface IStorageAcceptor<T : IStorageStack> : IStorage<T> {
/** /**
* Storage provider, provides (extract only) stacks to whoever needs them (the "warehouse"). * Storage provider, provides (extract only) stacks to whoever needs them (the "warehouse").
* *
* [get] methods work as in bidirectional map. * [get] methods work as in bidirectional map, with each stack mapping to unique [UUID]
*
* It is **required** for storage having **exactly one or none** of mappings of one stack [T]
* to one [UUID] (exactly one *UUID -> stack* and exactly one *stack -> UUID*).
* *
* What this means is that [get] with [T] as an argument shall never experience a situation where * What this means is that [get] with [T] as an argument shall never experience a situation where
* two stacks match provided key. * two stacks match provided key.
@ -126,15 +86,14 @@ interface IStorageAcceptor<T : IStorageStack> : IStorage<T> {
* which may or may not produce performance hit; [UUID]s are lightweight, semantically not bound to anything and are * which may or may not produce performance hit; [UUID]s are lightweight, semantically not bound to anything and are
* very good for distributed ID generation (so nothing in game has to be bound to one sequential number generator). * very good for distributed ID generation (so nothing in game has to be bound to one sequential number generator).
*/ */
interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> { interface IStorageProvider<T : StorageStack<T>> : IStorageEventProducer<T> {
/** /**
* Attempts to retrieve stored stack by its [id]. * Attempts to retrieve stored stack by its [id].
* *
* Returns stored stack as-is. If no stack with [id] exists, then * If no stack with [id] exists, then "empty stack" is returned.
* "empty stack" is returned.
* *
* @param id identifier of stack * @param id identifier of stack
* @return stored object (not a copy). Do not edit it. * @return stored object
*/ */
operator fun get(id: UUID): T operator fun get(id: UUID): T
@ -148,21 +107,13 @@ interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
* @return UUID of stack if present * @return UUID of stack if present
*/ */
operator fun get(key: T): UUID? { operator fun get(key: T): UUID? {
val key1 = key.let { if (it.count == BigInteger.ONE) it else it.key() } return stacks.filter { it.stack.equalsWithoutCount(key) }.findFirst().orElse(null)?.id
return stacks.filter {
if (it.stack.count == BigInteger.ONE) {
return@filter it.stack == key1
} else {
return@filter it.stack.key() == key1
}
}.findFirst().orElse(null)?.id
} }
val stacks: Stream<IStorageTuple<T>> val stacks: Stream<IStorageTuple<T>>
/** /**
* If tuple does not exist, returns empty stack * If storage has no such stack, returns empty stack
* *
* @param id identifier of stack to extract * @param id identifier of stack to extract
* @param amount amount of units to extract * @param amount amount of units to extract
@ -172,58 +123,35 @@ interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T
} }
fun <T : IStorageStack> IStorageProvider<T>.removeListenerAuto(listener: IStorageEventConsumer<T>): Boolean {
if (removeListener(listener)) {
for (stack in stacks) {
listener.removeStack(stack.stack, stack.id)
}
return true
}
return false
}
fun <T : IStorageStack> IStorageProvider<T>.addListenerAuto(listener: IStorageEventConsumer<T>): Boolean {
if (addListener(listener)) {
for (stack in stacks) {
listener.addStack(stack.stack, stack.id, this)
}
return true
}
return false
}
/** /**
* Storage component, which basically implement Input and Output * Storage component, which basically implement Input and Output
*/ */
interface IStorageComponent<T : IStorageStack> : IStorageProvider<T>, IStorageAcceptor<T> interface IStorageComponent<T : StorageStack<T>> : IStorageProvider<T>, IStorageAcceptor<T>
fun <T : IStorageStack> IStorageEventConsumer<T>.changeStack(tuple: IStorageTuple<T>, oldCount: BigInteger) { interface IStorageTuple<T : StorageStack<T>> {
changeStack(tuple.stack, tuple.id, oldCount)
}
fun <T : IStorageStack> IStorageEventConsumer<T>.addStack(tuple: IStorageTuple<T>, provider: IStorageProvider<T>) {
addStack(tuple.stack, tuple.id, provider)
}
fun <T : IStorageStack> IStorageEventConsumer<T>.removeStack(tuple: IStorageTuple<T>) {
removeStack(tuple.stack, tuple.id)
}
interface IStorageTuple<T : IStorageStack> {
val id: UUID val id: UUID
val stack: T val stack: T
interface IMutable<T : StorageStack<T>> : IStorageTuple<T> {
override var stack: T
fun grow(amount: BigInteger) {
stack = stack.grow(amount)
} }
class StorageTuple<T : IStorageStack>(override val id: UUID, override val stack: T) : IStorageTuple<T> fun shrink(amount: BigInteger) {
stack = stack.shrink(amount)
}
}
data class I<T : StorageStack<T>>(override val id: UUID, override val stack: T) : IStorageTuple<T>
data class M<T : StorageStack<T>>(override val id: UUID, override var stack: T) : IMutable<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>, IStorageEventConsumer<T> { interface IVirtualStorageComponent<T : StorageStack<T>> : 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

@ -0,0 +1,25 @@
package ru.dbotthepony.mc.otm.storage
fun <T : StorageStack<T>> IStorageProvider<T>.removeListenerAndNotify(listener: IStorageEventConsumer<T>): Boolean {
if (removeListener(listener)) {
for (stack in stacks) {
listener.onStackRemoved(stack.id)
}
return true
}
return false
}
fun <T : StorageStack<T>> IStorageProvider<T>.addListenerAndNotify(listener: IStorageEventConsumer<T>): Boolean {
if (addListener(listener)) {
for (stack in stacks) {
listener.onStackAdded(stack.stack, stack.id, this)
}
return true
}
return false
}

View File

@ -1,107 +0,0 @@
package ru.dbotthepony.mc.otm.storage
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.ItemStack
import org.jetbrains.annotations.ApiStatus
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.isPositive
import ru.dbotthepony.mc.otm.core.math.toIntSafe
import ru.dbotthepony.mc.otm.core.registryName
import java.math.BigInteger
/**
* constructors always copy its input.
*/
class ItemStackWrapper : IStorageStack {
/**
* [ItemStack] representing what item is this.
*
* In most cases you want to use [stack] instead.
*/
@ApiStatus.Internal
val item: ItemStack
val registryName get() = item.item.registryName!!
private val hash: Int
override var count: BigInteger
/**
* [copy] as false is used internally for fast index construction, do not specify
* it unless you know what you are doing!
*/
@JvmOverloads
constructor(item: ItemStack, copy: Boolean = true) {
if (copy) {
this.item = item.copy()
} else {
this.item = item
}
this.count = BigInteger.valueOf(item.count.toLong())
if (copy) {
this.item.count = 1
}
this.hash = item.tag.hashCode() xor item.item.hashCode()
}
constructor(item: ItemStackWrapper) {
this.item = item.item
this.count = item.count
this.item.count = 1
this.hash = item.hash
}
override fun copy(): ItemStackWrapper {
return ItemStackWrapper(this)
}
fun sameItem(other: ItemStack) = ItemStack.isSameItemSameTags(item, other)
override fun equals(other: Any?): Boolean {
if (other === this)
return true
if (other is ItemStackWrapper)
return other.hash == hash && count == other.count && ItemStack.isSameItemSameTags(item, other.item)
return false
}
override fun hashCode(): Int {
return hash * 31 + count.hashCode()
}
override val maxStackSize: BigInteger get() = BigInteger.valueOf(item.maxStackSize.toLong())
override val isEmpty: Boolean get() = item.isEmpty || !count.isPositive
/**
* [ItemStack] with valid amount and which can be modified after
*/
val stack: ItemStack get() = item.copy().also { it.count = count.toIntSafe() }
override fun toString(): String {
return "ItemStackWrapper[$count $registryName]"
}
fun write(buff: FriendlyByteBuf) {
buff.writeItem(item)
buff.writeBigInteger(count)
}
companion object {
@JvmField
val EMPTY = ItemStackWrapper(ItemStack.EMPTY)
fun read(buff: FriendlyByteBuf): ItemStackWrapper {
val item = buff.readItem()
val count = buff.readBigInteger()
return ItemStackWrapper(item, copy = false).also { it.count = count }
}
}
}
fun FriendlyByteBuf.writeBigItem(value: ItemStackWrapper) = value.write(this)
fun FriendlyByteBuf.readBigItem() = ItemStackWrapper.read(this)

View File

@ -0,0 +1,60 @@
package ru.dbotthepony.mc.otm.storage
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.math.toIntSafe
import java.math.BigInteger
class ItemStorageStack private constructor(private val stack: ItemStack, count: BigInteger, mark: Nothing?) : StorageStack<ItemStorageStack>(count) {
constructor(stack: ItemStack) : this(stack.copy(), BigInteger.valueOf(stack.count.toLong()), null)
constructor(stack: ItemStack, count: Int) : this(stack.copy(), BigInteger.valueOf(count.toLong()), null)
constructor(stack: ItemStack, count: Long) : this(stack.copy(), BigInteger.valueOf(count), null)
constructor(stack: ItemStack, count: BigInteger) : this(stack.copy(), count, null)
override val type: Type<ItemStorageStack>
get() = ITEMS
override fun copy(newCount: BigInteger): ItemStorageStack {
if (isEmpty)
return ITEMS.empty
if (newCount == count)
return this
return ItemStorageStack(stack, newCount, null)
}
override fun equals(other: Any?): Boolean {
return other is ItemStorageStack && other.count == count && ItemStack.isSameItemSameTags(stack, other.stack)
}
override fun hashCode(): Int {
return Integer.rotateLeft(count.hashCode(), 12) xor (stack.item.hashCode() * 31 + stack.tag.hashCode())
}
override fun equalsWithoutCount(other: StorageStack<*>): Boolean {
return other is ItemStorageStack && ItemStack.isSameItemSameTags(stack, other.stack)
}
override fun hashCodeWithoutCount(): Int {
return stack.item.hashCode() * 31 + stack.tag.hashCode()
}
override val isEmpty: Boolean = stack.isEmpty || super.isEmpty
override val maxStackSize: BigInteger = BigInteger.valueOf(stack.maxStackSize.toLong())
fun toItemStack(count: Int = this.count.toIntSafe()): ItemStack {
return stack.copyWithCount(count)
}
val item: Item get() = stack.item
companion object {
val EMPTY get() = ITEMS.empty
fun unsafe(stack: ItemStack) = ItemStorageStack(stack, BigInteger.valueOf(stack.count.toLong()), null)
fun unsafe(stack: ItemStack, count: Int) = ItemStorageStack(stack, BigInteger.valueOf(count.toLong()), null)
fun unsafe(stack: ItemStack, count: Long) = ItemStorageStack(stack, BigInteger.valueOf(count), null)
fun unsafe(stack: ItemStack, count: BigInteger) = ItemStorageStack(stack, count, null)
}
}

View File

@ -1,60 +0,0 @@
package ru.dbotthepony.mc.otm.storage
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.math.Decimal
import java.util.IdentityHashMap
open class StorageStackType<T : IStorageStack>(
val identity: Class<T>,
open val empty: T,
/**
* Speculated energy required per operation on stack with size of 1
*/
open val energyPerOperation: Decimal,
) {
open fun energyPerOperation(stack: T): Decimal {
return energyPerOperation * stack.count
}
open fun energyPerStorage(stack: T) = energyPerOperation(stack)
open fun energyPerExtraction(stack: T) = energyPerOperation(stack)
open fun energyForUpkeep(stack: T) = Decimal.ZERO
}
@Suppress("unchecked_cast")
object StorageRegistry {
private val REGISTRY = IdentityHashMap<Class<*>, StorageStackType<*>>()
@JvmStatic
fun register(type: StorageStackType<*>): StorageStackType<*> {
check(!REGISTRY.containsKey(type.identity)) { "Already have storage stack type for ${type.identity}" }
REGISTRY[type.identity] = type
return type
}
/**
* Refer to [StorageStackType] for explanation of flags.
*/
@JvmStatic
fun <T : IStorageStack> register(
identity: Class<T>,
empty: T,
energyPerOperation: Decimal
): StorageStackType<T> {
return register(StorageStackType(identity, empty, energyPerOperation)) as StorageStackType<T>
}
@JvmStatic
fun <T : IStorageStack> get(identity: Class<T>): StorageStackType<T> {
val get = REGISTRY[identity] ?: throw NoSuchElementException("Registry does not contain $identity")
return get as StorageStackType<T>
}
@JvmStatic
fun <T : IStorageStack> getOrNull(identity: Class<T>): StorageStackType<T>? {
return REGISTRY[identity] as StorageStackType<T>?
}
}
inline val ITEM_STORAGE: StorageStackType<ItemStackWrapper> get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE()

View File

@ -0,0 +1,127 @@
package ru.dbotthepony.mc.otm.storage
import it.unimi.dsi.fastutil.Hash
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.ItemStack
import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.registries.DeferredRegister
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.readBigInteger
import ru.dbotthepony.mc.otm.core.writeBigInteger
import ru.dbotthepony.mc.otm.core.writeItemType
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import java.math.BigInteger
abstract class StorageStack<S : StorageStack<S>>(val count: BigInteger) {
init {
require(count >= BigInteger.ZERO) { "Negative amount of things in stack: $count" }
}
abstract val type: Type<S>
abstract fun copy(newCount: BigInteger): S
abstract fun equalsWithoutCount(other: StorageStack<*>): Boolean
abstract fun hashCodeWithoutCount(): Int
open val isEmpty: Boolean get() = count == BigInteger.ZERO
inline val isNotEmpty get() = !isEmpty
open val maxStackSize: BigInteger? get() = null
abstract override fun equals(other: Any?): Boolean
abstract override fun hashCode(): Int
fun grow(amount: BigInteger): S {
if (amount == BigInteger.ZERO) return this as S
val newCount = count + amount
if (newCount <= BigInteger.ZERO)
return type.empty
return copy(newCount)
}
fun shrink(amount: BigInteger): S {
if (amount == BigInteger.ZERO) return this as S
val newCount = count - amount
if (newCount <= BigInteger.ZERO)
return type.empty
return copy(newCount)
}
fun write(buff: FriendlyByteBuf) {
type.write(buff, this as S)
}
interface Type<T : StorageStack<*>> {
val empty: T
fun read(buff: FriendlyByteBuf): T
fun write(buff: FriendlyByteBuf, value: T)
fun energyPerUpkeep(stack: T): Decimal = Decimal.ZERO
fun energyPerInsert(stack: T): Decimal = energyPerOperation(stack)
fun energyPerExtract(stack: T): Decimal = energyPerOperation(stack)
fun energyPerOperation(stack: T): Decimal
}
class SimpleType<T : StorageStack<*>>(
val energyPerOperation: Decimal,
override val empty: T,
private val read: (FriendlyByteBuf) -> T,
private val write: (FriendlyByteBuf, T) -> Unit
) : Type<T> {
override fun read(buff: FriendlyByteBuf): T {
return read.invoke(buff)
}
override fun write(buff: FriendlyByteBuf, value: T) {
write.invoke(buff, value)
}
override fun energyPerOperation(stack: T): Decimal {
return energyPerOperation * stack.count
}
}
companion object : Hash.Strategy<StorageStack<*>?> {
override fun equals(a: StorageStack<*>?, b: StorageStack<*>?): Boolean {
if (a == null && b == null) return true
if (a != null && b != null) return a.equalsWithoutCount(b)
return false
}
override fun hashCode(o: StorageStack<*>?): Int {
return o?.hashCodeWithoutCount() ?: 0
}
private val delegate = RegistryDelegate<Type<*>>("stack_type") { disableSaving() }
val REGISTRY by delegate
val REGISTRY_KEY by delegate::key
private val registrar = DeferredRegister.create(REGISTRY_KEY, OverdriveThatMatters.MOD_ID)
val ITEMS: Type<ItemStorageStack> by registrar.register("items") {
SimpleType(
Decimal("4"),
ItemStorageStack(ItemStack.EMPTY),
{
ItemStorageStack.unsafe(it.readItem(), it.readBigInteger())
},
{ buff, v ->
buff.writeItem(v.toItemStack())
buff.writeBigInteger(v.count)
}
)
}
internal fun register(bus: IEventBus) {
bus.addListener(delegate::build)
registrar.register(bus)
}
}
}

View File

@ -1,23 +1,23 @@
package ru.dbotthepony.mc.otm.storage package ru.dbotthepony.mc.otm.storage
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet import it.unimi.dsi.fastutil.objects.ObjectArraySet
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.core.math.isPositive import ru.dbotthepony.mc.otm.core.math.isPositive
import ru.dbotthepony.mc.otm.core.math.isZero import ru.dbotthepony.mc.otm.core.math.isZero
import java.math.BigInteger 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.ArrayList
import kotlin.reflect.full.isSubclassOf
class RemoteTuple<T : IStorageStack>( class RemoteTuple<T : StorageStack<T>>(
override val stack: T, override var stack: T,
override val id: UUID, override val id: UUID,
val provider: IStorageProvider<T>, val parent: IStorageProvider<T>,
val local: LocalTuple<T> val local: LocalTuple<T>
) : IStorageTuple<T> { ) : IStorageTuple<T> {
fun extract(amount: BigInteger, simulate: Boolean): T { fun extract(amount: BigInteger, simulate: Boolean): T {
return provider.extractStack(id, amount, simulate) return parent.extractStack(id, amount, simulate)
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -29,21 +29,27 @@ class RemoteTuple<T : IStorageStack>(
} }
} }
class LocalTuple<T : IStorageStack>(override val stack: T, override val id: UUID, val tuples: ArrayList<RemoteTuple<T>>) : IStorageTuple<T> class LocalTuple<T : StorageStack<T>>(override var stack: T, override val id: UUID) : IStorageTuple<T> {
val tuples = ArrayList<RemoteTuple<T>>(1)
open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVirtualStorageComponent<T> { fun grow(amount: BigInteger) {
constructor(type: Class<T>) : this(StorageRegistry.get(type)) stack = stack.grow(amount)
}
override val storageType: StorageStackType<T> = type fun shrink(amount: BigInteger) {
stack = stack.shrink(amount)
}
}
open class VirtualComponent<T : StorageStack<T>>(override val storageType: StorageStack.Type<T>) : IVirtualStorageComponent<T> {
// удаленный UUID -> Кортеж // удаленный UUID -> Кортеж
protected val remoteTuples = HashMap<UUID, RemoteTuple<T>>() protected val remoteTuples = Object2ObjectOpenHashMap<UUID, RemoteTuple<T>>()
// локальный UUID -> Локальный кортеж // локальный UUID -> Локальный кортеж
protected val localTuples = HashMap<UUID, LocalTuple<T>>() protected val localTuples = Object2ObjectOpenHashMap<UUID, LocalTuple<T>>()
// Стак -> Локальный кортеж стака // Стак -> Локальный кортеж стака
protected val hashedTuples = HashMap<T, LocalTuple<T>>() protected val item2tuple = Object2ObjectOpenCustomHashMap<T, LocalTuple<T>>(StorageStack.Companion)
// ArrayList для скорости работы // ArrayList для скорости работы
protected val listeners: MutableSet<IStorageEventConsumer<T>> = ObjectArraySet() protected val listeners: MutableSet<IStorageEventConsumer<T>> = ObjectArraySet()
@ -54,18 +60,17 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
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 (!(identity.storageType::class.isSubclassOf(storageType::class))) require(identity.storageType == storageType) { "Attempt to add component of type ${identity.storageType} to virtual component of type $storageType" }
throw ClassCastException("Reified Generics: ${identity.storageType::class.qualifiedName} can not be cast into ${storageType::class.qualifiedName}")
if (children.add(identity)) { if (children.add(identity)) {
if (identity is IStorageProvider<T>) { if (identity is IStorageProvider<T>) {
identity.addListenerAuto(this) identity.addListenerAndNotify(this)
} else if (identity is IStorageEventProducer<T>) { } else if (identity is IStorageEventProducer<T>) {
identity.addListener(this) identity.addListener(this)
} }
if (identity is IStorageEventConsumer<T>) { if (identity is IStorageEventConsumer<T>) {
addListenerAuto(identity) addListenerAndNotify(identity)
} }
if (identity is IStorageAcceptor<T>) { if (identity is IStorageAcceptor<T>) {
@ -77,18 +82,17 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
} }
override fun remove(identity: IStorage<T>) { override fun remove(identity: IStorage<T>) {
if (!(identity.storageType::class.isSubclassOf(storageType::class))) require(identity.storageType == storageType) { "Attempt to remove component of type ${identity.storageType} from virtual component of type $storageType" }
throw ClassCastException("Reified Generics: ${identity.storageType::class.qualifiedName} can not be cast into ${storageType::class.qualifiedName}")
if (children.remove(identity)) { if (children.remove(identity)) {
if (identity is IStorageProvider<T>) { if (identity is IStorageProvider<T>) {
identity.removeListenerAuto(this) identity.removeListenerAndNotify(this)
} else if (identity is IStorageEventProducer<T>) { } else if (identity is IStorageEventProducer<T>) {
identity.removeListener(this) identity.removeListener(this)
} }
if (identity is IStorageEventConsumer<T>) { if (identity is IStorageEventConsumer<T>) {
removeListenerAuto(identity) removeListenerAndNotify(identity)
} }
if (identity is IStorageAcceptor<T>) { if (identity is IStorageAcceptor<T>) {
@ -99,101 +103,85 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
} }
} }
override fun contains(identity: IStorage<T>): Boolean { override fun contains(identity: IStorage<T>) = children.contains(identity)
return children.contains(identity) override fun addListener(listener: IStorageEventConsumer<T>) = listeners.add(listener)
} override fun removeListener(listener: IStorageEventConsumer<T>) = listeners.remove(listener)
override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
if (listeners.add(listener)) {
return true
}
return false
}
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
return listeners.remove(listener)
}
override fun get(id: UUID): T { override fun get(id: UUID): T {
return localTuples[id]?.stack ?: this.storageType.empty return localTuples[id]?.stack ?: this.storageType.empty
} }
override val stacks: Stream<IStorageTuple<T>> get() { override val stacks: Stream<IStorageTuple<T>> get() {
return ArrayList<IStorageTuple<T>>(hashedTuples.size).also { it.addAll(hashedTuples.values) }.stream() return ArrayList<IStorageTuple<T>>(item2tuple.size).also { it.addAll(item2tuple.values) }.stream()
} }
override fun get(key: T): UUID? { override fun get(key: T): UUID? {
return hashedTuples[key.key()]?.id return item2tuple[key]?.id
} }
@Suppress("unchecked_cast") override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>) {
override fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>) {
check(!remoteTuples.containsKey(id)) { "Already tracking tuple with id $id" } check(!remoteTuples.containsKey(id)) { "Already tracking tuple with id $id" }
val key = stack.key() var local = item2tuple[stack]
var local: LocalTuple<T>? = hashedTuples[key]
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, UUID.randomUUID())
localTuples[local.id] = local localTuples[local.id] = local
hashedTuples[key] = local item2tuple[stack] = local
} else { } else {
oldCount = local.stack.count local.grow(stack.count)
local.stack.grow(stack.count)
} }
val remote = RemoteTuple(stack.copy() as T, id, provider, local) val remote = RemoteTuple(stack, id, provider, local)
local.tuples.add(remote) local.tuples.add(remote)
remoteTuples[id] = remote remoteTuples[id] = remote
if (added) { if (added) {
for (listener in listeners) { for (listener in listeners) {
listener.addStack(local.stack, local.id, this) listener.onStackAdded(local.stack, local.id, this)
} }
} else { } else {
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(local.stack, local.id, oldCount) listener.onStackChanged(local.stack, local.id)
} }
} }
} }
override fun changeStack(stack: T, id: UUID, oldCount: BigInteger) { override fun onStackChanged(stack: T, id: UUID) {
require(stack.count.isPositive) require(stack.count.isPositive)
val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id") val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id")
val diff = stack.count - tuple.stack.count val diff = stack.count - tuple.stack.count
tuple.stack.count = stack.count tuple.stack = stack
tuple.local.grow(diff)
@Suppress("NAME_SHADOWING") val oldCount = tuple.local.stack.count
tuple.local.stack.grow(diff)
for (listener in listeners) { for (listener in listeners) {
listener.changeStack(tuple.local.stack, tuple.local.id, oldCount) listener.onStackChanged(tuple.local.stack, tuple.local.id)
} }
} }
override fun removeStack(stack: T, id: UUID) { override fun onStackRemoved(id: UUID) {
val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id") val remote = remoteTuples.remove(id) ?: throw IllegalStateException("No such tuple with id $id")
val local = remote.local
tuple.local.stack.shrink(tuple.stack.count) local.shrink(remote.stack.count)
tuple.local.tuples.remove(tuple) check(local.tuples.remove(remote))
remoteTuples.remove(id) val a = local.stack.isEmpty
val b = local.tuples.isEmpty()
val a = tuple.local.stack.count <= BigInteger.ZERO
val b = tuple.local.tuples.size == 0
if (a || b) { if (a || b) {
check(a && b) { "View object is empty, but tuple list is not!" } check(a && b) { "View stack is empty, but remote tuple list is not!" }
localTuples.remove(tuple.local.id) localTuples.remove(local.id)
val key = stack.key() checkNotNull(item2tuple.remove(remote.stack)) { "No such stack ${remote.stack}" }
checkNotNull(hashedTuples.remove(key)) { "No such stack $key" }
for (listener in listeners) { for (listener in listeners) {
listener.removeStack(tuple.local.stack, tuple.local.id) listener.onStackRemoved(local.id)
}
} else {
for (listener in listeners) {
listener.onStackChanged(local.stack, local.id)
} }
} }
} }
@ -212,14 +200,13 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
return leftover return leftover
} }
@Suppress("unchecked_cast")
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T { override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
if (!amount.isPositive) if (!amount.isPositive)
return this.storageType.empty return this.storageType.empty
@Suppress("name_shadowing") @Suppress("name_shadowing")
var amount = amount var amount = amount
val tuple: LocalTuple<T>? = localTuples[id] val tuple = localTuples[id]
if (tuple == null || amount.isZero) if (tuple == null || amount.isZero)
return this.storageType.empty return this.storageType.empty
@ -229,180 +216,19 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
val toExtract = tuple.stack.count.coerceAtMost(amount) val toExtract = tuple.stack.count.coerceAtMost(amount)
var extracted = BigInteger.ZERO var extracted = BigInteger.ZERO
val copy = tuple.stack.copy() as T val copy = tuple.stack
for (remote_tuple in tuple.tuples.let { Array(it.size) { i -> it[i] } }) { for (remote in ArrayList(tuple.tuples)) {
val extractedStack = remote_tuple.extract(toExtract - extracted, simulate) extracted += remote.extract(toExtract - extracted, simulate).count
extracted += extractedStack.count
if (extracted >= toExtract) if (extracted >= toExtract)
break break
} }
if (extracted.isPositive) { if (extracted.isPositive) {
copy.count = extracted return copy.copy(extracted)
return copy
} }
return this.storageType.empty return this.storageType.empty
} }
} }
/**
* Adds energy demand to operations over [parent]
*/
open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponent<T>, val energyProvider: () -> IMatteryEnergyStorage) : IStorageComponent<T> {
override val storageType: StorageStackType<T>
get() = parent.storageType
private val subscribedThrough = ObjectArraySet<IStorageEventConsumer<T>>()
final override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
if (parent.addListener(listener)) {
subscribedThrough.add(listener)
return true
}
return false
}
final override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
subscribedThrough.remove(listener)
return parent.removeListener(listener)
}
fun removeListeners() {
for (child in subscribedThrough) {
parent.removeListener(child)
}
subscribedThrough.clear()
}
override fun get(key: T) = parent[key]
@Suppress("unchecked_cast")
override fun insertStack(stack: T, simulate: Boolean): T {
val required = storageType.energyPerOperation * stack.count
val energy = energyProvider.invoke()
val extracted = energy.extractEnergy(required, true)
if (extracted.isZero) {
return stack.copy() as T
}
if (extracted == required) {
val leftover = parent.insertStack(stack, simulate)
if (leftover.isEmpty) {
if (!simulate) {
energy.extractEnergy(required, false)
}
return leftover
}
if (!simulate) {
val requiredNew = storageType.energyPerOperation * (stack.count - leftover.count)
energy.extractEnergy(requiredNew, false)
}
return leftover
}
@Suppress("name_shadowing")
val stack = stack.copy() as T
val oldCount = stack.count
stack.count = (extracted / storageType.energyPerOperation).whole
val diff = oldCount - stack.count
val newRequired = storageType.energyPerOperation * stack.count
val newExtracted = energy.extractEnergy(newRequired, true)
if (newExtracted == newRequired) {
val leftover = parent.insertStack(stack, simulate)
if (leftover.isEmpty) {
if (!simulate) {
energy.extractEnergy(newRequired, false)
}
leftover.count = diff
return leftover
}
if (!simulate) {
val requiredNew = storageType.energyPerOperation * (stack.count - leftover.count)
energy.extractEnergy(requiredNew, false)
}
leftover.count += diff
return leftover
}
return stack
}
override fun get(id: UUID) = parent[id]
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
val required = storageType.energyPerOperation * amount
val energy = energyProvider.invoke()
val extracted = energy.extractEnergy(required, true)
if (extracted.isZero) {
return storageType.empty
}
if (extracted == required) {
val extractedStack = parent.extractStack(id, amount, simulate)
if (extractedStack.isEmpty) {
return extractedStack
}
if (!simulate) {
if (extractedStack.count == amount) {
energy.extractEnergy(required, false)
} else {
energy.extractEnergy(storageType.energyPerOperation * extractedStack.count, false)
}
}
return extractedStack
}
@Suppress("name_shadowing")
val amount = (required / storageType.energyPerOperation).whole
val extractedStack = parent.extractStack(id, amount, simulate)
if (extractedStack.isEmpty) {
return extractedStack
}
if (!simulate) {
energy.extractEnergy(storageType.energyPerOperation * extractedStack.count, false)
}
return extractedStack
}
override val stacks get() = parent.stacks
}
/**
* Adds energy demand to virtual component [parent]
*/
open class PoweredVirtualComponent<T : IStorageStack>(override val parent: IVirtualStorageComponent<T>, energyProvider: () -> IMatteryEnergyStorage)
: PoweredComponent<T>(parent, energyProvider), IVirtualStorageComponent<T> {
constructor(parent: IVirtualStorageComponent<T>, energy: IMatteryEnergyStorage) : this(parent, { energy })
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: 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)
override fun remove(identity: IStorage<T>) = parent.remove(identity)
override fun contains(identity: IStorage<T>) = parent.contains(identity)
}

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.storage.powered
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.storage.IStorageAcceptor
import ru.dbotthepony.mc.otm.storage.IStorageComponent
import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.StorageStack
import java.util.function.Supplier
class PoweredComponent<T : StorageStack<T>>(
val parent: IStorageComponent<T>,
energy: Supplier<IMatteryEnergyStorage>
) : IStorageComponent<T>, IStorageProvider<T> by PoweredStorageProvider(parent, energy), IStorageAcceptor<T> by PoweredStorageAcceptor(parent, energy) {
override val storageType: StorageStack.Type<T>
get() = parent.storageType
}

View File

@ -0,0 +1,34 @@
package ru.dbotthepony.mc.otm.storage.powered
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.storage.IStorageAcceptor
import ru.dbotthepony.mc.otm.storage.StorageStack
import java.util.function.Supplier
class PoweredStorageAcceptor<T : StorageStack<T>>(val parent: IStorageAcceptor<T>, val energy: Supplier<IMatteryEnergyStorage>) : IStorageAcceptor<T> by parent {
override fun insertStack(stack: T, simulate: Boolean): T {
val leftover = parent.insertStack(stack, true)
if (leftover == stack) return stack
val energy = energy.get()
val required = storageType.energyPerInsert(stack.shrink(leftover.count))
if (energy.extractEnergy(required, true) == required) {
if (!simulate) {
val leftover2 = parent.insertStack(stack, false)
if (leftover2.count != leftover.count) {
energy.extractEnergy(storageType.energyPerInsert(stack.shrink(leftover2.count)), false)
} else {
energy.extractEnergy(required, false)
}
return leftover2
}
return leftover
}
return stack
}
}

View File

@ -0,0 +1,36 @@
package ru.dbotthepony.mc.otm.storage.powered
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.StorageStack
import java.math.BigInteger
import java.util.*
import java.util.function.Supplier
class PoweredStorageProvider<T : StorageStack<T>>(val parent: IStorageProvider<T>, val energy: Supplier<IMatteryEnergyStorage>) : IStorageProvider<T> by parent {
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
val extracted = parent.extractStack(id, amount, simulate)
if (extracted.isEmpty) return extracted
val energy = energy.get()
val required = storageType.energyPerExtract(extracted)
if (energy.extractEnergy(required, true) == required) {
if (!simulate) {
val extracted2 = parent.extractStack(id, amount, false)
if (extracted.count != extracted2.count) {
energy.extractEnergy(storageType.energyPerExtract(extracted2), false)
} else {
energy.extractEnergy(required, false)
}
return extracted2
}
return extracted
}
return storageType.empty
}
}

View File

@ -0,0 +1,25 @@
package ru.dbotthepony.mc.otm.storage.powered
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.storage.IStorage
import ru.dbotthepony.mc.otm.storage.IStorageComponent
import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.IVirtualStorageComponent
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.storage.VirtualComponent
import java.util.*
import java.util.function.Supplier
class PoweredVirtualComponent<T : StorageStack<T>>(
val parent: IVirtualStorageComponent<T>,
energy: Supplier<IMatteryEnergyStorage>
) : IVirtualStorageComponent<T>, IStorageComponent<T> by PoweredComponent(parent, energy) {
constructor(type: StorageStack.Type<T>, energy: Supplier<IMatteryEnergyStorage>) : this(VirtualComponent(type), energy)
override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>) = parent.onStackAdded(stack, id, provider)
override fun onStackChanged(stack: T, id: UUID) = parent.onStackChanged(stack, id)
override fun onStackRemoved(id: UUID) = parent.onStackRemoved(id)
override fun add(identity: IStorage<T>) = parent.add(identity)
override fun remove(identity: IStorage<T>) = parent.remove(identity)
override fun contains(identity: IStorage<T>) = parent.contains(identity)
}