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();
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) {
return new ResourceLocation(MOD_ID, path);
}
@ -206,8 +199,6 @@ public final class OverdriveThatMatters {
WeaponNetworkChannel.INSTANCE.register();
GenericNetworkChannel.INSTANCE.register();
ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new Decimal("3.125"));
if (ModList.get().isLoaded("mekanism")) {
EVENT_BUS.addGenericListener(BlockEntity.class, EventPriority.NORMAL, QIOKt::attachCapabilities);
}

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.core.collect
import it.unimi.dsi.fastutil.objects.ObjectIterators
import ru.dbotthepony.mc.otm.core.addAll
import java.util.Optional
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
*

View File

@ -8,32 +8,22 @@ import ru.dbotthepony.mc.otm.storage.*
import java.util.LinkedList
class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() {
private val virtualComponents = Object2ObjectArrayMap<StorageStackType<*>, VirtualComponent<*>>()
private val virtualComponents = Object2ObjectArrayMap<StorageStack.Type<*>, VirtualComponent<*>>()
val powerDemandingNodes = LinkedList<IMatteryEnergyStorage>()
/**
* Returns a [VirtualComponent] representing [type] storage
*/
fun <T : IStorageStack> getVirtualComponent(type: StorageStackType<T>): VirtualComponent<T> {
fun <T : StorageStack<T>> getVirtualComponent(type: StorageStack.Type<T>): VirtualComponent<T> {
return virtualComponents.computeIfAbsent(type, Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<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>) {
fun <T : StorageStack<T>> add(storage: IStorage<T>) {
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)
}
fun <T : IStorageStack> contains(storage: IStorage<T>): Boolean {
fun <T : StorageStack<T>> contains(storage: IStorage<T>): Boolean {
val virtual = virtualComponents[storage.storageType] ?: return false
return (virtual as VirtualComponent<T>).contains(storage)
}
@ -45,5 +35,4 @@ class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() {
override fun onNodeRemoved(node: StorageNode) {
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.graph.GraphNode
import ru.dbotthepony.mc.otm.storage.IStorage
import ru.dbotthepony.mc.otm.storage.IStorageStack
import ru.dbotthepony.mc.otm.storage.StorageStackType
import ru.dbotthepony.mc.otm.storage.IVirtualStorageComponent
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.storage.VirtualComponent
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
import java.util.*
import java.util.function.Supplier
open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) {
protected val components = ArrayList<IStorage<*>>()
@ -72,71 +75,31 @@ open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null
}
@Suppress("unchecked_cast")
fun <T : IStorageStack, U : IStorage<T>> computeIfAbsent(identity: StorageStackType<T>, provider: (StorageStackType<T>) -> U): U {
for (component in components) {
if (component.storageType === identity) {
return component as U
}
}
val factory = provider(identity)
addStorageComponent(factory)
return factory
fun <T : StorageStack<T>, U : IStorage<T>> computeIfAbsent(type: StorageStack.Type<T>, provider: (StorageStack.Type<T>) -> U): U {
return components.firstOrNull { it.storageType == type } as U? ?: provider(type).also { addStorageComponent(it) }
}
fun addStorageComponent(component: IStorage<*>) {
for (component1 in components) {
if (component === component1 || component1.storageType === component.storageType) {
return
}
if (!components.any { component === it || it.storageType === component.storageType }) {
components.add(component)
if (isValid && !manualAttaching)
graph.add(component)
}
components.add(component)
if (isValid && !manualAttaching)
graph.add(component)
}
fun removeStorageComponent(component: IStorage<*>) {
var indexOf = -1
for ((i, component1) in components.withIndex()) {
if (component === component1 || component1.storageType === component.storageType) {
indexOf = i
break
}
}
if (indexOf == -1) {
return
}
val self = components[indexOf]
components.removeAt(indexOf)
if (isValid)
graph.remove(self)
val indexOf = components.indexOfFirst { component === it || it.storageType === component.storageType }
if (indexOf == -1) return
val self = components.removeAt(indexOf)
if (isValid) graph.remove(self)
}
fun removeStorageComponent(component: StorageStackType<*>) {
var indexOf = -1
for ((i, component1) in components.withIndex()) {
if (component1.storageType === component) {
indexOf = i
break
}
}
if (indexOf == -1) {
return
}
val self = components[indexOf]
components.removeAt(indexOf)
if (isValid && !manualAttaching)
graph.remove(self)
fun removeStorageComponent(component: StorageStack.Type<*>) {
val indexOf = components.indexOfFirst { it.storageType === component }
if (indexOf == -1) return
val self = components.removeAt(indexOf)
if (isValid) graph.remove(self)
}
override fun invalidate() {

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

View File

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

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

View File

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

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

View File

@ -4,69 +4,20 @@ import java.math.BigInteger
import java.util.*
import java.util.stream.Stream
/**
* Represents a stack in storage system.
*
* In general there are next rules:
* 1. Once created, stack is immutable, except for it's [count], therefore [copy] is expected to do
* "shallow" copies (not performance taxing).
* 2. Due to condition above, and due to sublying storage most of time being
* mutable, it is expected you do defensive copies. Examples of when you should do
* them are described on related interfaces.
* 3. For storing stacks as [Map] keys, a stack with size of 1 is utilized (requiring [equals] and [hashCode] to return meaningful results, see [IStorageStack.key]).
* 4. For equality (INCLUDING count), regular [equals] is utilized.
*/
interface IStorageStack {
fun copy(): IStorageStack
/**
* Size of this storage stack
*
* This is overriden in subclasses
*/
var count: BigInteger
val isEmpty: Boolean
/**
* @return max stack size for this stack object,
* null if unlimited (default)
*/
val maxStackSize: BigInteger? get() = null
/**
* Increase [count] by [amount]
*/
fun grow(amount: BigInteger) {
count += amount
}
/**
* Decrease [count] by [amount]
*/
fun shrink(amount: BigInteger) {
count -= amount
}
}
@Suppress("UNCHECKED_CAST")
fun <T : IStorageStack> T.key(): T {
return copy().also { it.count = BigInteger.ONE } as T
}
/**
* Storage system root, along IStorageStack interface
*/
interface IStorage<T : IStorageStack> {
interface IStorage<T : StorageStack<T>> {
/**
* @return Identity of this virtual component
*/
val storageType: StorageStackType<T>
val storageType: StorageStack.Type<T>
}
/**
* 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
*/
@ -81,27 +32,39 @@ interface IStorageEventProducer<T : IStorageStack> : IStorage<T> {
/**
* 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
*/
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
*/
fun changeStack(stack: T, id: UUID, oldCount: BigInteger)
fun onStackChanged(stack: T, id: UUID)
/**
* Fired on whenever an object is removed from listener we subscribed to
*/
fun removeStack(stack: T, id: UUID)
fun onStackRemoved(id: UUID)
fun onStackChanged(tuple: IStorageTuple<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.
*/
interface IStorageAcceptor<T : IStorageStack> : IStorage<T> {
interface IStorageAcceptor<T : StorageStack<T>> : IStorage<T> {
/**
* Inserts an item into system.
* @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").
*
* [get] methods work as in bidirectional map.
*
* It is **required** for storage having **exactly one or none** of mappings of one stack [T]
* to one [UUID] (exactly one *UUID -> stack* and exactly one *stack -> UUID*).
* [get] methods work as in bidirectional map, with each stack mapping to unique [UUID]
*
* What this means is that [get] with [T] as an argument shall never experience a situation where
* two stacks match provided key.
@ -126,15 +86,14 @@ interface IStorageAcceptor<T : IStorageStack> : IStorage<T> {
* which may or may not produce performance hit; [UUID]s are lightweight, semantically not bound to anything and are
* very good for distributed ID generation (so nothing in game has to be bound to one sequential number generator).
*/
interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
interface IStorageProvider<T : StorageStack<T>> : IStorageEventProducer<T> {
/**
* Attempts to retrieve stored stack by its [id].
*
* Returns stored stack as-is. If no stack with [id] exists, then
* "empty stack" is returned.
* If no stack with [id] exists, then "empty stack" is returned.
*
* @param id identifier of stack
* @return stored object (not a copy). Do not edit it.
* @return stored object
*/
operator fun get(id: UUID): T
@ -148,21 +107,13 @@ interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
* @return UUID of stack if present
*/
operator fun get(key: T): UUID? {
val key1 = key.let { if (it.count == BigInteger.ONE) it else it.key() }
return stacks.filter {
if (it.stack.count == BigInteger.ONE) {
return@filter it.stack == key1
} else {
return@filter it.stack.key() == key1
}
}.findFirst().orElse(null)?.id
return stacks.filter { it.stack.equalsWithoutCount(key) }.findFirst().orElse(null)?.id
}
val stacks: Stream<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 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 <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
*/
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) {
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> {
interface IStorageTuple<T : StorageStack<T>> {
val id: UUID
val stack: T
}
class StorageTuple<T : IStorageStack>(override val id: UUID, override val stack: T) : IStorageTuple<T>
interface IMutable<T : StorageStack<T>> : IStorageTuple<T> {
override var stack: T
fun grow(amount: BigInteger) {
stack = stack.grow(amount)
}
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)
*/
interface IVirtualStorageComponent<T : IStorageStack> : IStorageComponent<T>, IStorageEventConsumer<T> {
interface IVirtualStorageComponent<T : StorageStack<T>> : IStorageComponent<T>, IStorageEventConsumer<T> {
fun add(identity: IStorage<T>)
fun remove(identity: IStorage<T>)
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
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.core.math.isPositive
import ru.dbotthepony.mc.otm.core.math.isZero
import java.math.BigInteger
import java.util.*
import java.util.stream.Stream
import kotlin.collections.HashMap
import kotlin.reflect.full.isSubclassOf
import kotlin.collections.ArrayList
class RemoteTuple<T : IStorageStack>(
override val stack: T,
class RemoteTuple<T : StorageStack<T>>(
override var stack: T,
override val id: UUID,
val provider: IStorageProvider<T>,
val parent: IStorageProvider<T>,
val local: LocalTuple<T>
) : IStorageTuple<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 {
@ -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> {
constructor(type: Class<T>) : this(StorageRegistry.get(type))
fun grow(amount: BigInteger) {
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 -> Кортеж
protected val remoteTuples = HashMap<UUID, RemoteTuple<T>>()
protected val remoteTuples = Object2ObjectOpenHashMap<UUID, RemoteTuple<T>>()
// локальный 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 для скорости работы
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>) {}
override fun add(identity: IStorage<T>) {
if (!(identity.storageType::class.isSubclassOf(storageType::class)))
throw ClassCastException("Reified Generics: ${identity.storageType::class.qualifiedName} can not be cast into ${storageType::class.qualifiedName}")
require(identity.storageType == storageType) { "Attempt to add component of type ${identity.storageType} to virtual component of type $storageType" }
if (children.add(identity)) {
if (identity is IStorageProvider<T>) {
identity.addListenerAuto(this)
identity.addListenerAndNotify(this)
} else if (identity is IStorageEventProducer<T>) {
identity.addListener(this)
}
if (identity is IStorageEventConsumer<T>) {
addListenerAuto(identity)
addListenerAndNotify(identity)
}
if (identity is IStorageAcceptor<T>) {
@ -77,18 +82,17 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
}
override fun remove(identity: IStorage<T>) {
if (!(identity.storageType::class.isSubclassOf(storageType::class)))
throw ClassCastException("Reified Generics: ${identity.storageType::class.qualifiedName} can not be cast into ${storageType::class.qualifiedName}")
require(identity.storageType == storageType) { "Attempt to remove component of type ${identity.storageType} from virtual component of type $storageType" }
if (children.remove(identity)) {
if (identity is IStorageProvider<T>) {
identity.removeListenerAuto(this)
identity.removeListenerAndNotify(this)
} else if (identity is IStorageEventProducer<T>) {
identity.removeListener(this)
}
if (identity is IStorageEventConsumer<T>) {
removeListenerAuto(identity)
removeListenerAndNotify(identity)
}
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 {
return children.contains(identity)
}
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 contains(identity: IStorage<T>) = children.contains(identity)
override fun addListener(listener: IStorageEventConsumer<T>) = listeners.add(listener)
override fun removeListener(listener: IStorageEventConsumer<T>) = listeners.remove(listener)
override fun get(id: UUID): T {
return localTuples[id]?.stack ?: this.storageType.empty
}
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? {
return hashedTuples[key.key()]?.id
return item2tuple[key]?.id
}
@Suppress("unchecked_cast")
override fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>) {
override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>) {
check(!remoteTuples.containsKey(id)) { "Already tracking tuple with id $id" }
val key = stack.key()
var local: LocalTuple<T>? = hashedTuples[key]
var oldCount = BigInteger.ZERO
var local = item2tuple[stack]
val added = local == null
if (local == null) {
local = LocalTuple(stack.copy() as T, UUID.randomUUID(), ArrayList<RemoteTuple<T>>(1))
local = LocalTuple(stack, UUID.randomUUID())
localTuples[local.id] = local
hashedTuples[key] = local
item2tuple[stack] = local
} else {
oldCount = local.stack.count
local.stack.grow(stack.count)
local.grow(stack.count)
}
val remote = RemoteTuple(stack.copy() as T, id, provider, local)
val remote = RemoteTuple(stack, id, provider, local)
local.tuples.add(remote)
remoteTuples[id] = remote
if (added) {
for (listener in listeners) {
listener.addStack(local.stack, local.id, this)
listener.onStackAdded(local.stack, local.id, this)
}
} else {
for (listener in listeners) {
listener.changeStack(local.stack, local.id, oldCount)
listener.onStackChanged(local.stack, local.id)
}
}
}
override fun changeStack(stack: T, id: UUID, oldCount: BigInteger) {
override fun onStackChanged(stack: T, id: UUID) {
require(stack.count.isPositive)
val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id")
val diff = stack.count - tuple.stack.count
tuple.stack.count = stack.count
@Suppress("NAME_SHADOWING") val oldCount = tuple.local.stack.count
tuple.local.stack.grow(diff)
tuple.stack = stack
tuple.local.grow(diff)
for (listener in listeners) {
listener.changeStack(tuple.local.stack, tuple.local.id, oldCount)
listener.onStackChanged(tuple.local.stack, tuple.local.id)
}
}
override fun removeStack(stack: T, id: UUID) {
val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id")
override fun onStackRemoved(id: UUID) {
val remote = remoteTuples.remove(id) ?: throw IllegalStateException("No such tuple with id $id")
val local = remote.local
tuple.local.stack.shrink(tuple.stack.count)
tuple.local.tuples.remove(tuple)
local.shrink(remote.stack.count)
check(local.tuples.remove(remote))
remoteTuples.remove(id)
val a = tuple.local.stack.count <= BigInteger.ZERO
val b = tuple.local.tuples.size == 0
val a = local.stack.isEmpty
val b = local.tuples.isEmpty()
if (a || b) {
check(a && b) { "View object is empty, but tuple list is not!" }
localTuples.remove(tuple.local.id)
val key = stack.key()
checkNotNull(hashedTuples.remove(key)) { "No such stack $key" }
check(a && b) { "View stack is empty, but remote tuple list is not!" }
localTuples.remove(local.id)
checkNotNull(item2tuple.remove(remote.stack)) { "No such stack ${remote.stack}" }
for (listener in listeners) {
listener.removeStack(tuple.local.stack, tuple.local.id)
listener.onStackRemoved(local.id)
}
} else {
for (listener in listeners) {
listener.onStackChanged(local.stack, local.id)
}
}
}
@ -212,14 +200,13 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
return leftover
}
@Suppress("unchecked_cast")
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
if (!amount.isPositive)
return this.storageType.empty
@Suppress("name_shadowing")
var amount = amount
val tuple: LocalTuple<T>? = localTuples[id]
val tuple = localTuples[id]
if (tuple == null || amount.isZero)
return this.storageType.empty
@ -229,180 +216,19 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
val toExtract = tuple.stack.count.coerceAtMost(amount)
var extracted = BigInteger.ZERO
val copy = tuple.stack.copy() as T
val copy = tuple.stack
for (remote_tuple in tuple.tuples.let { Array(it.size) { i -> it[i] } }) {
val extractedStack = remote_tuple.extract(toExtract - extracted, simulate)
extracted += extractedStack.count
for (remote in ArrayList(tuple.tuples)) {
extracted += remote.extract(toExtract - extracted, simulate).count
if (extracted >= toExtract)
break
}
if (extracted.isPositive) {
copy.count = extracted
return copy
return copy.copy(extracted)
}
return this.storageType.empty
}
}
/**
* Adds energy demand to operations over [parent]
*/
open class PoweredComponent<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)
}