Refine storage api, make it compile
This commit is contained in:
parent
48d367b705
commit
64d5f1b336
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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++
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
25
src/main/kotlin/ru/dbotthepony/mc/otm/storage/Helpers.kt
Normal file
25
src/main/kotlin/ru/dbotthepony/mc/otm/storage/Helpers.kt
Normal 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
|
||||
}
|
@ -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)
|
@ -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)
|
||||
}
|
||||
}
|
@ -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()
|
127
src/main/kotlin/ru/dbotthepony/mc/otm/storage/StorageStack.kt
Normal file
127
src/main/kotlin/ru/dbotthepony/mc/otm/storage/StorageStack.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user