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();
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
|
|
||||||
public static OverdriveThatMatters INSTANCE;
|
public static OverdriveThatMatters INSTANCE;
|
||||||
private StorageStackType<ItemStackWrapper> ITEM_STORAGE;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public StorageStackType<ItemStackWrapper> ITEM_STORAGE() {
|
|
||||||
return Objects.requireNonNull(ITEM_STORAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ResourceLocation loc(String path) {
|
public static ResourceLocation loc(String path) {
|
||||||
return new ResourceLocation(MOD_ID, path);
|
return new ResourceLocation(MOD_ID, path);
|
||||||
}
|
}
|
||||||
@ -206,8 +199,6 @@ public final class OverdriveThatMatters {
|
|||||||
WeaponNetworkChannel.INSTANCE.register();
|
WeaponNetworkChannel.INSTANCE.register();
|
||||||
GenericNetworkChannel.INSTANCE.register();
|
GenericNetworkChannel.INSTANCE.register();
|
||||||
|
|
||||||
ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new Decimal("3.125"));
|
|
||||||
|
|
||||||
if (ModList.get().isLoaded("mekanism")) {
|
if (ModList.get().isLoaded("mekanism")) {
|
||||||
EVENT_BUS.addGenericListener(BlockEntity.class, EventPriority.NORMAL, QIOKt::attachCapabilities);
|
EVENT_BUS.addGenericListener(BlockEntity.class, EventPriority.NORMAL, QIOKt::attachCapabilities);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ public class MatteryCapability {
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@NotNull
|
@NotNull
|
||||||
public static final Capability<IMatteryDrive> DRIVE = CapabilityManager.get(new CapabilityToken<>() {});
|
public static final Capability<IMatteryDrive<?>> DRIVE = CapabilityManager.get(new CapabilityToken<>() {});
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity.storage
|
package ru.dbotthepony.mc.otm.block.entity.storage
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.server.level.ServerLevel
|
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||||
@ -11,13 +10,14 @@ import net.minecraft.world.level.block.state.BlockState
|
|||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.graph.storage.StorageNode
|
import ru.dbotthepony.mc.otm.graph.storage.StorageNode
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
|
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu
|
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu
|
||||||
import ru.dbotthepony.mc.otm.graph.storage.StorageGraph
|
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.storage.*
|
import ru.dbotthepony.mc.otm.storage.*
|
||||||
|
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||||
|
|
||||||
class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||||
MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, p_155229_, p_155230_) {
|
MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, p_155229_, p_155230_) {
|
||||||
@ -27,16 +27,19 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
||||||
super.setChanged(slot, new, old)
|
super.setChanged(slot, new, old)
|
||||||
|
|
||||||
|
// generics is going apeshit since storage types are invariant,
|
||||||
|
// but since we don't know generics of upvalue mattery drive, its storage type
|
||||||
|
// is defined as out variant
|
||||||
old.getCapability(MatteryCapability.DRIVE).ifPresent {
|
old.getCapability(MatteryCapability.DRIVE).ifPresent {
|
||||||
cell.computeIfAbsent(it.storageType) {
|
cell.computeIfAbsent(it.storageType as StorageStack.Type<ItemStorageStack>) {
|
||||||
PoweredVirtualComponent(it, energy)
|
PoweredVirtualComponent(VirtualComponent(it), ::energy)
|
||||||
}.remove(it)
|
}.remove(it as IMatteryDrive<ItemStorageStack>)
|
||||||
}
|
}
|
||||||
|
|
||||||
new.getCapability(MatteryCapability.DRIVE).ifPresent {
|
new.getCapability(MatteryCapability.DRIVE).ifPresent {
|
||||||
cell.computeIfAbsent(it.storageType) {
|
cell.computeIfAbsent(it.storageType as StorageStack.Type<ItemStorageStack>) {
|
||||||
PoweredVirtualComponent(it, energy)
|
PoweredVirtualComponent(VirtualComponent(it), ::energy)
|
||||||
}.add(it)
|
}.add(it as IMatteryDrive<ItemStorageStack>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.also(::addDroppableContainer)
|
}.also(::addDroppableContainer)
|
||||||
|
@ -18,6 +18,7 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig
|
|||||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu
|
import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu
|
||||||
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
|
|
||||||
class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_VIEWER, p_155229_, p_155230_) {
|
class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_VIEWER, p_155229_, p_155230_) {
|
||||||
override fun setChanged() {
|
override fun setChanged() {
|
||||||
@ -28,7 +29,7 @@ class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
|
|||||||
tickList.once {
|
tickList.once {
|
||||||
var state = blockState
|
var state = blockState
|
||||||
|
|
||||||
if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent && energy.batteryLevel >= OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation) {
|
if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent) {
|
||||||
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING)
|
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING)
|
||||||
} else {
|
} else {
|
||||||
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE)
|
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE)
|
||||||
|
@ -6,13 +6,11 @@ import net.minecraft.core.NonNullList
|
|||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.server.level.ServerLevel
|
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
import net.minecraft.world.Container
|
import net.minecraft.world.Container
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||||
import net.minecraft.world.inventory.CraftingContainer
|
|
||||||
import net.minecraft.world.inventory.TransientCraftingContainer
|
import net.minecraft.world.inventory.TransientCraftingContainer
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.item.crafting.CraftingRecipe
|
import net.minecraft.world.item.crafting.CraftingRecipe
|
||||||
@ -40,6 +38,7 @@ import ru.dbotthepony.mc.otm.core.nbt.mapString
|
|||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
|
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
|
||||||
import ru.dbotthepony.mc.otm.storage.*
|
import ru.dbotthepony.mc.otm.storage.*
|
||||||
|
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
@ -165,7 +164,7 @@ private fun takeOne(inventory: Inventory, item: ItemStack): Boolean {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun takeOne(id: UUID?, view: IStorageProvider<ItemStackWrapper>): Boolean {
|
private fun takeOne(id: UUID?, view: IStorageProvider<ItemStorageStack>): Boolean {
|
||||||
val extracted = view.extractStack(id ?: return false, BigInteger.ONE, false)
|
val extracted = view.extractStack(id ?: return false, BigInteger.ONE, false)
|
||||||
|
|
||||||
if (!extracted.isEmpty) {
|
if (!extracted.isEmpty) {
|
||||||
@ -175,10 +174,7 @@ private fun takeOne(id: UUID?, view: IStorageProvider<ItemStackWrapper>): Boolea
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, blockPos, blockState), IStorageEventConsumer<ItemStorageStack> {
|
||||||
MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, p_155229_, p_155230_),
|
|
||||||
IStorageEventConsumer<ItemStackWrapper> {
|
|
||||||
|
|
||||||
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.ITEM_MONITOR)
|
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.ITEM_MONITOR)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -186,18 +182,18 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
savetable(::energy, ENERGY_KEY)
|
savetable(::energy, ENERGY_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
var poweredView: PoweredVirtualComponent<ItemStackWrapper>? = null
|
var poweredView: PoweredVirtualComponent<ItemStorageStack>? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
val cell = object : StorageNode(energy) {
|
val cell = object : StorageNode(energy) {
|
||||||
override fun attachComponents(to: StorageGraph) {
|
override fun attachComponents(to: StorageGraph) {
|
||||||
super.attachComponents(to)
|
super.attachComponents(to)
|
||||||
poweredView = PoweredVirtualComponent(to.getVirtualComponent(ITEM_STORAGE), energy)
|
poweredView = PoweredVirtualComponent(to.getVirtualComponent(StorageStack.ITEMS), ::energy)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeComponents(from: StorageGraph) {
|
override fun removeComponents(from: StorageGraph) {
|
||||||
super.removeComponents(from)
|
super.removeComponents(from)
|
||||||
poweredView?.removeListeners()
|
// poweredView?.removeListeners()
|
||||||
poweredView = null
|
poweredView = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,7 +244,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
val item = craftingGrid[i]
|
val item = craftingGrid[i]
|
||||||
|
|
||||||
if (!item.isEmpty) {
|
if (!item.isEmpty) {
|
||||||
craftingGridTuples[i] = poweredView[ItemStackWrapper(item).key()]
|
craftingGridTuples[i] = poweredView[ItemStorageStack.unsafe(item)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,18 +265,18 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
|
|
||||||
private val craftingGridDummy = TransientCraftingContainer(craftingMenu, 3, 3)
|
private val craftingGridDummy = TransientCraftingContainer(craftingMenu, 3, 3)
|
||||||
|
|
||||||
override val storageType: StorageStackType<ItemStackWrapper>
|
override val storageType: StorageStack.Type<ItemStorageStack>
|
||||||
get() = ITEM_STORAGE
|
get() = StorageStack.ITEMS
|
||||||
|
|
||||||
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) {
|
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
|
||||||
// no op
|
// no op
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) {
|
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
|
||||||
// no op
|
// no op
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeStack(stack: ItemStackWrapper, id: UUID) {
|
override fun onStackRemoved(id: UUID) {
|
||||||
for (i in craftingGridTuples.indices) {
|
for (i in craftingGridTuples.indices) {
|
||||||
if (craftingGridTuples[i] == id) {
|
if (craftingGridTuples[i] == id) {
|
||||||
craftingGridTuples[i] = null
|
craftingGridTuples[i] = null
|
||||||
@ -393,7 +389,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
if (!remainder.isEmpty) {
|
if (!remainder.isEmpty) {
|
||||||
when (settings.resultTarget) {
|
when (settings.resultTarget) {
|
||||||
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, ItemMonitorPlayerSettings.ResultTarget.MIXED -> {
|
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, ItemMonitorPlayerSettings.ResultTarget.MIXED -> {
|
||||||
val remaining = poweredView?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder
|
val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
||||||
|
|
||||||
if (!remaining.isEmpty) {
|
if (!remaining.isEmpty) {
|
||||||
if (newItem.isEmpty) {
|
if (newItem.isEmpty) {
|
||||||
@ -409,7 +405,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
|
|
||||||
ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY -> {
|
ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY -> {
|
||||||
if (!craftingPlayer.inventory.add(remainder)) {
|
if (!craftingPlayer.inventory.add(remainder)) {
|
||||||
val remaining = poweredView?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder
|
val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
||||||
|
|
||||||
if (!remaining.isEmpty) {
|
if (!remaining.isEmpty) {
|
||||||
if (newItem.isEmpty) {
|
if (newItem.isEmpty) {
|
||||||
|
@ -2,8 +2,8 @@ package ru.dbotthepony.mc.otm.block.entity.storage
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
||||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.Direction
|
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||||
@ -12,7 +12,6 @@ import net.minecraft.world.level.Level
|
|||||||
import net.minecraft.world.level.block.Block
|
import net.minecraft.world.level.block.Block
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||||
import net.minecraftforge.common.util.LazyOptional
|
|
||||||
import net.minecraftforge.items.IItemHandler
|
import net.minecraftforge.items.IItemHandler
|
||||||
import ru.dbotthepony.mc.otm.*
|
import ru.dbotthepony.mc.otm.*
|
||||||
import ru.dbotthepony.mc.otm.block.CableBlock
|
import ru.dbotthepony.mc.otm.block.CableBlock
|
||||||
@ -23,23 +22,20 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
|||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||||
import ru.dbotthepony.mc.otm.core.*
|
import ru.dbotthepony.mc.otm.core.*
|
||||||
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||||
import ru.dbotthepony.mc.otm.core.math.getCapability
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.isPositive
|
import ru.dbotthepony.mc.otm.core.math.isPositive
|
||||||
import ru.dbotthepony.mc.otm.core.math.isZero
|
import ru.dbotthepony.mc.otm.core.math.toIntSafe
|
||||||
import ru.dbotthepony.mc.otm.core.math.plus
|
|
||||||
import ru.dbotthepony.mc.otm.graph.storage.StorageNode
|
import ru.dbotthepony.mc.otm.graph.storage.StorageNode
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.StorageBusMenu
|
import ru.dbotthepony.mc.otm.menu.storage.StorageBusMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.storage.*
|
import ru.dbotthepony.mc.otm.storage.*
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
private class SlotTuple(val slot: Int, val stack: ItemStack)
|
private data class SlotTuple(val slot: Int, val stack: ItemStack)
|
||||||
private class TrackedTuple(override val stack: ItemStackWrapper, override val id: UUID) : IStorageTuple<ItemStackWrapper> {
|
|
||||||
|
private class TrackedTuple(override var stack: ItemStorageStack, override val id: UUID) : IStorageTuple<ItemStorageStack> {
|
||||||
// do not use hash map because we need keys to be iterated in natural order
|
// do not use hash map because we need keys to be iterated in natural order
|
||||||
val children = Int2ObjectAVLTreeMap<SlotTuple>()
|
val children = Int2ObjectAVLTreeMap<SlotTuple>()
|
||||||
|
|
||||||
@ -128,51 +124,21 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
|||||||
const val MAX_FILTERS = 6 * 3
|
const val MAX_FILTERS = 6 * 3
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent<ItemStackWrapper> {
|
private inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent<ItemStorageStack> {
|
||||||
private inner class EventsSnapshot {
|
private fun updateTuple(tuple: TrackedTuple, diff: BigInteger) {
|
||||||
val index = HashMap<UUID, BigInteger>()
|
tuple.stack = tuple.stack.grow(diff)
|
||||||
|
|
||||||
fun change(key: UUID, diff: BigInteger) {
|
if (tuple.stack.isNotEmpty) {
|
||||||
if (diff.isZero)
|
for (listener in listeners) {
|
||||||
return
|
listener.onStackChanged(tuple)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
for (listener in listeners) {
|
||||||
fun apply() {
|
listener.onStackRemoved(tuple)
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>
|
private val listeners = ArrayList<IStorageEventConsumer<ItemStorageStack>>()
|
||||||
get() = ITEM_STORAGE
|
|
||||||
|
|
||||||
private val listeners = ArrayList<IStorageEventConsumer<ItemStackWrapper>>()
|
override fun addListener(listener: IStorageEventConsumer<ItemStorageStack>): Boolean {
|
||||||
|
|
||||||
override fun addListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
|
|
||||||
if (!listeners.contains(listener)) {
|
if (!listeners.contains(listener)) {
|
||||||
listeners.add(listener)
|
listeners.add(listener)
|
||||||
return true
|
return true
|
||||||
@ -256,139 +220,92 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
|
override fun removeListener(listener: IStorageEventConsumer<ItemStorageStack>): Boolean {
|
||||||
return listeners.remove(listener)
|
return listeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var scanned = arrayOfNulls<ItemStack>(0)
|
private var slot2itemStack = arrayOfNulls<ItemStack>(0)
|
||||||
private var scannedMap = arrayOfNulls<TrackedTuple>(0)
|
private var slot2tuple = arrayOfNulls<TrackedTuple>(0)
|
||||||
private val tuples = HashMap<ItemStackWrapper, TrackedTuple>()
|
private val items2tuples = Object2ObjectOpenCustomHashMap<ItemStorageStack, TrackedTuple>(StorageStack.Companion)
|
||||||
private val index = HashMap<UUID, TrackedTuple>()
|
private val id2tuples = HashMap<UUID, TrackedTuple>()
|
||||||
|
|
||||||
private fun removeTracked(slot: Int) {
|
private fun removeTracked(slot: Int) {
|
||||||
scanned[slot] = null
|
slot2itemStack[slot] = null
|
||||||
val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" }
|
val tuple = checkNotNull(slot2tuple[slot]) { "Not tracking slot $slot" }
|
||||||
this.scannedMap[slot] = null
|
slot2tuple[slot] = null
|
||||||
|
|
||||||
val item = scannedMap.children[slot] ?: throw IllegalStateException("${scannedMap.id} does not track $slot")
|
val slotTuple = tuple.children[slot] ?: throw IllegalStateException("${tuple.id} does not track $slot")
|
||||||
scannedMap.children.remove(slot)
|
tuple.children.remove(slot)
|
||||||
val count = scannedMap.stack.count
|
updateTuple(tuple, -slotTuple.stack.count.toBigInteger())
|
||||||
|
|
||||||
val snapshot = snapshot
|
|
||||||
|
|
||||||
if (snapshot != null) {
|
|
||||||
snapshot.change(scannedMap.id, -item.stack.count.toBigInteger())
|
|
||||||
} else {
|
|
||||||
scannedMap.stack.count -= item.stack.count.toBigInteger()
|
|
||||||
|
|
||||||
if (scannedMap.stack.count.isPositive) {
|
|
||||||
for (listener in listeners) {
|
|
||||||
listener.changeStack(scannedMap, count)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (listener in listeners) {
|
|
||||||
listener.removeStack(scannedMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
index.remove(scannedMap.id)
|
|
||||||
|
|
||||||
val key = scannedMap.stack.key()
|
|
||||||
tuples.remove(key) ?: throw IllegalStateException("Item tuple is not present for slot $slot at ${scannedMap.stack}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun diffTracked(slot: Int, diff: Int) {
|
private fun diffTracked(slot: Int, diff: Int) {
|
||||||
val scannedMap = checkNotNull(scannedMap[slot]) { "Not tracking slot $slot" }
|
val tuple = checkNotNull(slot2tuple[slot]) { "Not tracking slot $slot" }
|
||||||
val item = checkNotNull(scannedMap.children[slot]) { "${scannedMap.id} does not track $slot" }
|
val slotTuple = checkNotNull(tuple.children[slot]) { "${tuple.id} does not track $slot" }
|
||||||
|
slotTuple.stack.count += diff
|
||||||
val oldCount = scannedMap.stack.count
|
updateTuple(tuple, diff.toBigInteger())
|
||||||
item.stack.count += diff
|
|
||||||
|
|
||||||
val snapshot = snapshot
|
|
||||||
|
|
||||||
if (snapshot != null) {
|
|
||||||
snapshot.change(scannedMap.id, diff.toBigInteger())
|
|
||||||
} else {
|
|
||||||
scannedMap.stack.count += diff.toBigInteger()
|
|
||||||
|
|
||||||
for (listener in listeners) {
|
|
||||||
listener.changeStack(scannedMap.stack, scannedMap.id, oldCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addTracked(slot: Int, stack: ItemStack) {
|
private fun addTracked(slot: Int, stack: ItemStack) {
|
||||||
check(scannedMap[slot] == null) { "Already tracking slot $slot" }
|
check(slot2tuple[slot] == null) { "Already tracking slot $slot" }
|
||||||
|
|
||||||
val storageStack = ItemStackWrapper(stack)
|
val storageStack = ItemStorageStack(stack)
|
||||||
val key = storageStack.key()
|
var tuple = items2tuples[storageStack]
|
||||||
var tuple: TrackedTuple? = tuples[key]
|
|
||||||
val added = tuple == null
|
val added = tuple == null
|
||||||
var oldCount = BigInteger.ZERO
|
|
||||||
|
|
||||||
if (added) {
|
if (tuple == null) {
|
||||||
tuple = TrackedTuple(storageStack, UUID.randomUUID())
|
tuple = TrackedTuple(storageStack, UUID.randomUUID())
|
||||||
index[tuple.id] = tuple
|
id2tuples[tuple.id] = tuple
|
||||||
tuples[key] = tuple
|
items2tuples[storageStack] = tuple
|
||||||
} else {
|
} else {
|
||||||
oldCount = tuple!!.stack.count
|
tuple.stack = tuple.stack.grow(stack.count.toBigInteger())
|
||||||
|
|
||||||
if (snapshot == null)
|
|
||||||
tuple.stack.count += stack.count.toBigInteger()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tuple.children[slot] = SlotTuple(slot, stack.copy())
|
tuple.children[slot] = SlotTuple(slot, stack.copy())
|
||||||
scanned[slot] = tuple.children[slot].stack
|
slot2itemStack[slot] = tuple.children[slot].stack
|
||||||
scannedMap[slot] = tuple
|
slot2tuple[slot] = tuple
|
||||||
|
|
||||||
if (added) {
|
if (added) {
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.addStack(tuple.stack, tuple.id, this)
|
listener.onStackAdded(tuple.stack, tuple.id, this)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val snapshot = snapshot
|
for (listener in listeners) {
|
||||||
|
listener.onStackChanged(tuple.stack, tuple.id)
|
||||||
if (snapshot != null) {
|
|
||||||
snapshot.change(tuple.id, stack.count.toBigInteger())
|
|
||||||
} else {
|
|
||||||
for (listener in listeners) {
|
|
||||||
listener.changeStack(tuple.stack, tuple.id, oldCount)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sizeScan() {
|
private fun sizeScan() {
|
||||||
if (scanned.size != parent.slots) {
|
if (slot2itemStack.size != parent.slots) {
|
||||||
val old = scanned
|
val old = slot2itemStack
|
||||||
val oldMap = scannedMap
|
val oldMap = slot2tuple
|
||||||
|
|
||||||
if (scanned.size < parent.slots) {
|
if (slot2itemStack.size < parent.slots) {
|
||||||
// grow
|
// grow
|
||||||
scanned = arrayOfNulls(parent.slots)
|
slot2itemStack = arrayOfNulls(parent.slots)
|
||||||
scannedMap = arrayOfNulls(parent.slots)
|
slot2tuple = arrayOfNulls(parent.slots)
|
||||||
|
|
||||||
for ((i, item) in old.withIndex()) {
|
for ((i, item) in old.withIndex()) {
|
||||||
scanned[i] = item
|
slot2itemStack[i] = item
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((i, item) in oldMap.withIndex()) {
|
for ((i, item) in oldMap.withIndex()) {
|
||||||
scannedMap[i] = item
|
slot2tuple[i] = item
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// shrink
|
// shrink
|
||||||
for (i in parent.slots until scanned.size) {
|
for (i in parent.slots until slot2itemStack.size) {
|
||||||
if (scannedMap[i] != null)
|
if (slot2tuple[i] != null)
|
||||||
removeTracked(i)
|
removeTracked(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
scanned = arrayOfNulls(parent.slots)
|
slot2itemStack = arrayOfNulls(parent.slots)
|
||||||
scannedMap = arrayOfNulls(parent.slots)
|
slot2tuple = arrayOfNulls(parent.slots)
|
||||||
|
|
||||||
for (i in 0 until parent.slots) {
|
for (i in 0 until parent.slots) {
|
||||||
scanned[i] = old[i]
|
slot2itemStack[i] = old[i]
|
||||||
scannedMap[i] = oldMap[i]
|
slot2tuple[i] = oldMap[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,7 +313,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
|||||||
|
|
||||||
fun scan(slot: Int) {
|
fun scan(slot: Int) {
|
||||||
val current = parent[slot].let { if (it.isEmpty || !filter.match(it)) null else it }
|
val current = parent[slot].let { if (it.isEmpty || !filter.match(it)) null else it }
|
||||||
val last = scanned[slot]
|
val last = slot2itemStack[slot]
|
||||||
|
|
||||||
if (current == null && last != null) {
|
if (current == null && last != null) {
|
||||||
removeTracked(slot)
|
removeTracked(slot)
|
||||||
@ -415,42 +332,27 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
|||||||
fun scan() {
|
fun scan() {
|
||||||
sizeScan()
|
sizeScan()
|
||||||
|
|
||||||
snapshot = EventsSnapshot()
|
|
||||||
|
|
||||||
for (slot in 0 until parent.slots) {
|
for (slot in 0 until parent.slots) {
|
||||||
scan(slot)
|
scan(slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot!!.apply()
|
|
||||||
snapshot = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper {
|
override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack {
|
||||||
if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.item))
|
if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.toItemStack()))
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * stack.count
|
val required = StorageStack.ITEMS.energyPerInsert(stack)
|
||||||
val maxExtractEnergy = energy.extractEnergy(maxPossibleDemand, true)
|
if (energy.extractEnergy(required, true) != required) return stack
|
||||||
|
|
||||||
var leftover: ItemStackWrapper = stack.copy()
|
var leftover = stack
|
||||||
var additional: BigInteger = BigInteger.ZERO
|
|
||||||
|
|
||||||
if (maxExtractEnergy != maxPossibleDemand) {
|
for (slot in PrioritizedSlotIterator(items2tuples[stack])) {
|
||||||
additional = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole
|
|
||||||
leftover.count -= additional
|
|
||||||
}
|
|
||||||
|
|
||||||
for (slot in PrioritizedSlotIterator(tuples[stack.key()])) {
|
|
||||||
val oldCount = leftover.count
|
val oldCount = leftover.count
|
||||||
leftover = ItemStackWrapper(parent.insertItem(slot, leftover.stack, simulate))
|
leftover = ItemStorageStack.unsafe(parent.insertItem(slot, leftover.toItemStack(), simulate))
|
||||||
|
|
||||||
if (oldCount != leftover.count && !simulate) {
|
if (oldCount != leftover.count && !simulate) {
|
||||||
energy.extractEnergy(ITEM_STORAGE.energyPerOperation * (oldCount - leftover.count), false)
|
energy.extractEnergy(StorageStack.ITEMS.energyPerInsert(stack.copy(oldCount - leftover.count)), false)
|
||||||
|
if (slot2itemStack.size <= slot) sizeScan()
|
||||||
if (scanned.size <= slot) {
|
|
||||||
sizeScan()
|
|
||||||
}
|
|
||||||
|
|
||||||
scan(slot)
|
scan(slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,74 +361,52 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return leftover.also {
|
return leftover
|
||||||
if (additional != BigInteger.ZERO) {
|
|
||||||
it.count += additional
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(id: UUID): ItemStackWrapper {
|
override fun get(id: UUID): ItemStorageStack {
|
||||||
return index[id]?.stack ?: ItemStackWrapper.EMPTY
|
return id2tuples[id]?.stack ?: ItemStorageStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStackWrapper {
|
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStorageStack {
|
||||||
if (redstoneControl.isBlockedByRedstone || !amount.isPositive)
|
if (redstoneControl.isBlockedByRedstone || !amount.isPositive || !energy.batteryLevel.isPositive)
|
||||||
return ItemStackWrapper.EMPTY
|
return ItemStorageStack.EMPTY
|
||||||
|
|
||||||
val maxPossibleDemand = ITEM_STORAGE.energyPerOperation * amount
|
var total = BigInteger.ZERO
|
||||||
val maxExtractEnergy = energy.extractEnergy(maxPossibleDemand, true)
|
val tuple = id2tuples[id] ?: return ItemStorageStack.EMPTY
|
||||||
|
val slots = tuple.children.values.iterator()
|
||||||
|
val lstack = tuple.stack
|
||||||
|
|
||||||
@Suppress("NAME_SHADOWING")
|
while (amount < total && slots.hasNext() && energy.batteryLevel.isPositive) {
|
||||||
var amount = amount
|
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) {
|
if (extracted.isNotEmpty && tuple.stack.equalsWithoutCount(ItemStorageStack.unsafe(extracted)) && energy.extractEnergy(required, true) == required) {
|
||||||
amount = (maxExtractEnergy / ITEM_STORAGE.energyPerOperation).whole
|
if (simulate) {
|
||||||
}
|
total += extracted.count.toBigInteger()
|
||||||
|
} else {
|
||||||
|
val extracted2 = parent.extractItem(slot, extracted.count, false)
|
||||||
|
|
||||||
val intAmount = amount.toLong()
|
if (extracted2.count == extracted.count) {
|
||||||
val tuple = index[id] ?: return ItemStackWrapper.EMPTY
|
energy.extractEnergy(required, false)
|
||||||
var totalExtracted = 0L
|
} else {
|
||||||
|
energy.extractEnergy(StorageStack.ITEMS.energyPerExtract(ItemStorageStack.unsafe(extracted2)), false)
|
||||||
|
}
|
||||||
|
|
||||||
val copy = tuple.stack.copy()
|
total += extracted2.count.toBigInteger()
|
||||||
|
|
||||||
for (stack in tuple.children.values) {
|
|
||||||
val extracted = parent.extractItem(stack.slot, stack.stack.count.coerceAtMost((intAmount - totalExtracted).clamp()), true)
|
|
||||||
|
|
||||||
if (!extracted.isEmpty && tuple.stack.sameItem(extracted)) {
|
|
||||||
if (!simulate) {
|
|
||||||
parent.extractItem(stack.slot, extracted.count, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
totalExtracted += extracted.count
|
|
||||||
|
|
||||||
if (extracted.count != 0 && !simulate) {
|
|
||||||
energy.extractEnergy(ITEM_STORAGE.energyPerOperation * extracted.count, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalExtracted >= intAmount) {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalExtracted == 0L) {
|
return lstack.copy(total)
|
||||||
return ItemStackWrapper.EMPTY
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!simulate) {
|
|
||||||
scan()
|
|
||||||
}
|
|
||||||
|
|
||||||
copy.count = totalExtracted.toBigInteger()
|
|
||||||
return copy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val stacks: Stream<IStorageTuple<ItemStackWrapper>> get() {
|
override val stacks: Stream<IStorageTuple<ItemStorageStack>> get() {
|
||||||
val listing = ArrayList<IStorageTuple<ItemStackWrapper>>(index.size)
|
val listing = ArrayList<IStorageTuple<ItemStorageStack>>(id2tuples.size)
|
||||||
|
|
||||||
for (tuple in index.values) {
|
for (tuple in id2tuples.values) {
|
||||||
listing.add(StorageTuple(tuple.id, tuple.stack))
|
listing.add(IStorageTuple.I(tuple.id, tuple.stack))
|
||||||
}
|
}
|
||||||
|
|
||||||
return listing.stream()
|
return listing.stream()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity.storage
|
package ru.dbotthepony.mc.otm.block.entity.storage
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.Direction
|
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
@ -20,13 +19,12 @@ import ru.dbotthepony.mc.otm.block.CableBlock
|
|||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
|
|
||||||
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
|
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
|
||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||||
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||||
import ru.dbotthepony.mc.otm.core.math.toIntSafe
|
|
||||||
import ru.dbotthepony.mc.otm.core.orNull
|
import ru.dbotthepony.mc.otm.core.orNull
|
||||||
import ru.dbotthepony.mc.otm.graph.storage.StorageNode
|
import ru.dbotthepony.mc.otm.graph.storage.StorageNode
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.StorageExporterMenu
|
import ru.dbotthepony.mc.otm.menu.storage.StorageExporterMenu
|
||||||
@ -36,10 +34,8 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
|||||||
import ru.dbotthepony.mc.otm.registry.MNames
|
import ru.dbotthepony.mc.otm.registry.MNames
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
|
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageProvider
|
import ru.dbotthepony.mc.otm.storage.IStorageProvider
|
||||||
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE
|
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStackType
|
|
||||||
import ru.dbotthepony.mc.otm.storage.addStack
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
@ -112,7 +108,9 @@ abstract class AbstractStorageImportExport<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_IMPORTER, blockPos, blockState), IItemHandler {
|
class StorageImporterBlockEntity(
|
||||||
|
blockPos: BlockPos, blockState: BlockState
|
||||||
|
) : AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_IMPORTER, blockPos, blockState), IItemHandler {
|
||||||
override val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
|
override val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
|
||||||
setChangedLight()
|
setChangedLight()
|
||||||
}
|
}
|
||||||
@ -124,8 +122,6 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
|
|||||||
private var lastSlot = 0
|
private var lastSlot = 0
|
||||||
private var nextTick = INTERVAL
|
private var nextTick = INTERVAL
|
||||||
|
|
||||||
private val enoughEnergy get() = energy.batteryLevel >= ITEM_STORAGE.energyPerOperation
|
|
||||||
|
|
||||||
override val targetCapability: Capability<IItemHandler>
|
override val targetCapability: Capability<IItemHandler>
|
||||||
get() = ForgeCapabilities.ITEM_HANDLER
|
get() = ForgeCapabilities.ITEM_HANDLER
|
||||||
|
|
||||||
@ -141,23 +137,39 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
|
|||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun acceptItem(stack: ItemStack, simulate: Boolean): ItemStack {
|
||||||
|
val wrapped = ItemStorageStack(stack)
|
||||||
|
|
||||||
|
val view = cell.graph.getVirtualComponent(StorageStack.ITEMS)
|
||||||
|
val inserted = view.insertStack(wrapped, true)
|
||||||
|
if (inserted == wrapped) return stack
|
||||||
|
|
||||||
|
val required = StorageStack.ITEMS.energyPerInsert(wrapped)
|
||||||
|
|
||||||
|
if (energy.extractEnergy(required, true) == required) {
|
||||||
|
if (!simulate) {
|
||||||
|
val inserted2 = view.insertStack(wrapped, false)
|
||||||
|
|
||||||
|
if (inserted == inserted2) {
|
||||||
|
energy.extractEnergy(required, false)
|
||||||
|
return inserted.toItemStack()
|
||||||
|
} else {
|
||||||
|
energy.extractEnergy(StorageStack.ITEMS.energyPerInsert(inserted2), false)
|
||||||
|
return inserted2.toItemStack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inserted.toItemStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
|
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
|
||||||
if (redstoneControl.isBlockedByRedstone || !filter.match(stack))
|
if (redstoneControl.isBlockedByRedstone || !filter.match(stack))
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
val view = cell.graph.getVirtualComponent(ITEM_STORAGE)
|
return acceptItem(stack, simulate)
|
||||||
val maxMove = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, stack.count.toBigInteger(), true)
|
|
||||||
|
|
||||||
if (maxMove == BigInteger.ZERO)
|
|
||||||
return stack
|
|
||||||
|
|
||||||
val leftover = view.insertStack(ItemStackWrapper(stack).also { it.count = maxMove }, simulate)
|
|
||||||
|
|
||||||
if (simulate)
|
|
||||||
return leftover.stack
|
|
||||||
|
|
||||||
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, maxMove - leftover.count, false)
|
|
||||||
return leftover.stack
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
||||||
@ -184,28 +196,20 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
|
|||||||
|
|
||||||
val target = target.get().orNull()
|
val target = target.get().orNull()
|
||||||
|
|
||||||
if (nextTick <= 0 && target != null && enoughEnergy) {
|
if (nextTick <= 0 && target != null) {
|
||||||
val items = cell.graph.getVirtualComponent(ITEM_STORAGE)
|
|
||||||
|
|
||||||
if (lastSlot >= target.slots) {
|
if (lastSlot >= target.slots) {
|
||||||
lastSlot = 0
|
lastSlot = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val maxMove = energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, MAX_MOVE_PER_OPERATION, true)
|
val extracted = target.extractItem(lastSlot, MAX_MOVE_PER_OPERATION, true)
|
||||||
var extracted = target.extractItem(lastSlot, maxMove, true)
|
|
||||||
|
|
||||||
if (extracted.isEmpty || !filter.match(extracted)) {
|
if (extracted.isEmpty || !filter.match(extracted)) {
|
||||||
lastSlot++
|
lastSlot++
|
||||||
} else {
|
} else {
|
||||||
val leftOver = items.insertStack(ItemStackWrapper(extracted), true)
|
val accepted = acceptItem(extracted, true)
|
||||||
|
val extracted2 = target.extractItem(lastSlot, accepted.count, false)
|
||||||
if (leftOver.count.toInt() != extracted.count) {
|
acceptItem(extracted2, false)
|
||||||
extracted = target.extractItem(lastSlot, extracted.count - leftOver.count.toInt(), false)
|
nextTick += INTERVAL * 4
|
||||||
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, extracted.count, false)
|
|
||||||
items.insertStack(ItemStackWrapper(extracted), false)
|
|
||||||
} else {
|
|
||||||
nextTick += INTERVAL * 4
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +227,7 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
|
|||||||
|
|
||||||
class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||||
AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState),
|
AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState),
|
||||||
IStorageEventConsumer<ItemStackWrapper> {
|
IStorageEventConsumer<ItemStorageStack> {
|
||||||
override val defaultDisplayName: Component
|
override val defaultDisplayName: Component
|
||||||
get() = MACHINE_NAME
|
get() = MACHINE_NAME
|
||||||
|
|
||||||
@ -233,36 +237,36 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
|||||||
|
|
||||||
private val relevantTuples = HashSet<UUID>()
|
private val relevantTuples = HashSet<UUID>()
|
||||||
|
|
||||||
override val storageType: StorageStackType<ItemStackWrapper>
|
override val storageType: StorageStack.Type<ItemStorageStack>
|
||||||
get() = ITEM_STORAGE
|
get() = StorageStack.ITEMS
|
||||||
|
|
||||||
init {
|
init {
|
||||||
cell.addStorageComponent(this)
|
cell.addStorageComponent(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) {
|
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
|
||||||
if (!filter.match(stack.item)) {
|
if (!filter.match(stack.toItemStack())) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relevantTuples.add(id)
|
relevantTuples.add(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) {
|
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeStack(stack: ItemStackWrapper, id: UUID) {
|
override fun onStackRemoved(id: UUID) {
|
||||||
relevantTuples.remove(id)
|
relevantTuples.remove(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
|
override val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
|
||||||
relevantTuples.clear()
|
relevantTuples.clear()
|
||||||
|
|
||||||
val component = cell.graph.getVirtualComponent(ITEM_STORAGE)
|
val component = cell.graph.getVirtualComponent(StorageStack.ITEMS)
|
||||||
|
|
||||||
for (tuple in component.stacks) {
|
for (tuple in component.stacks) {
|
||||||
addStack(tuple, component)
|
onStackAdded(tuple, component)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSlot = 0
|
lastSlot = 0
|
||||||
@ -272,14 +276,13 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
|||||||
|
|
||||||
private var lastSlot = 0
|
private var lastSlot = 0
|
||||||
private var nextTick = INTERVAL
|
private var nextTick = INTERVAL
|
||||||
private val enoughEnergy get() = energy.batteryLevel >= ITEM_STORAGE.energyPerOperation
|
|
||||||
|
|
||||||
override val targetCapability: Capability<IItemHandler>
|
override val targetCapability: Capability<IItemHandler>
|
||||||
get() = ForgeCapabilities.ITEM_HANDLER
|
get() = ForgeCapabilities.ITEM_HANDLER
|
||||||
|
|
||||||
private val exportStacks: Stream<Pair<UUID, ItemStackWrapper>>
|
private val exportStacks: Stream<Pair<UUID, ItemStorageStack>>
|
||||||
get() {
|
get() {
|
||||||
val view = cell.graph.getVirtualComponent(ITEM_STORAGE)
|
val view = cell.graph.getVirtualComponent(StorageStack.ITEMS)
|
||||||
return relevantTuples.stream().map { it to view[it] }
|
return relevantTuples.stream().map { it to view[it] }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,53 +298,43 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
|||||||
|
|
||||||
val target = target.get().orNull()
|
val target = target.get().orNull()
|
||||||
|
|
||||||
if (nextTick <= 0 && target != null && enoughEnergy) {
|
if (nextTick <= 0 && target != null) {
|
||||||
val items = cell.graph.getVirtualComponent(ITEM_STORAGE)
|
val items = cell.graph.getVirtualComponent(StorageStack.ITEMS)
|
||||||
|
|
||||||
if (lastSlot >= target.slots) {
|
if (lastSlot >= target.slots) {
|
||||||
lastSlot = 0
|
lastSlot = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var hit = false
|
val any = exportStacks.anyMatch {
|
||||||
|
val (id, stack) = it
|
||||||
|
|
||||||
for (stack in exportStacks) {
|
if (!target.isItemValid(lastSlot, stack.toItemStack())) return@anyMatch false
|
||||||
if (!target.isItemValid(lastSlot, stack.second.item)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val exportAmountA = items.extractStack(
|
val extracted = items.extractStack(id, stack.count.coerceAtMost(MAX_MOVE_PER_OPERATION), true)
|
||||||
stack.first, stack.second.count.coerceAtMost(
|
if (extracted.isEmpty) return@anyMatch false
|
||||||
MAX_MOVE_PER_OPERATION
|
|
||||||
), true
|
|
||||||
).count
|
|
||||||
|
|
||||||
if (exportAmountA == BigInteger.ZERO) {
|
val required = StorageStack.ITEMS.energyPerOperation(extracted)
|
||||||
continue
|
if (energy.extractEnergy(required, true) != required) return@anyMatch false
|
||||||
}
|
|
||||||
|
|
||||||
var exportAmount =
|
val toInsert = extracted.toItemStack()
|
||||||
energy.extractEnergyExact(ITEM_STORAGE.energyPerOperation, exportAmountA, true).toIntSafe()
|
val leftover = target.insertItem(lastSlot, toInsert, true)
|
||||||
|
|
||||||
if (exportAmount == 0) {
|
if (leftover.count != toInsert.count) {
|
||||||
break
|
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) {
|
true
|
||||||
hit = true
|
} else {
|
||||||
exportAmount = items.extractStack(
|
false
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hit) {
|
if (!any) {
|
||||||
lastSlot++
|
lastSlot++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,27 +4,27 @@ import net.minecraft.nbt.CompoundTag
|
|||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageComponent
|
import ru.dbotthepony.mc.otm.storage.IStorageComponent
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageStack
|
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageTuple
|
import ru.dbotthepony.mc.otm.storage.IStorageTuple
|
||||||
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
|
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
||||||
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface IItemMatteryDrive : IMatteryDrive<ItemStackWrapper> {
|
interface IItemMatteryDrive : IMatteryDrive<ItemStorageStack> {
|
||||||
/**
|
/**
|
||||||
* @param item
|
* @param item
|
||||||
* @return all items belonging to passed class
|
* @return all items belonging to passed class
|
||||||
*/
|
*/
|
||||||
fun findItems(item: Item): Collection<IStorageTuple<ItemStackWrapper>>
|
fun findItems(item: Item): Collection<IStorageTuple<ItemStorageStack>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param stack
|
* @param stack
|
||||||
* @return [ItemStack] that match specified [stack] (item type, nbt tag)
|
* @return [ItemStack] that match specified [stack] (item type, nbt tag)
|
||||||
*/
|
*/
|
||||||
fun findItems(stack: ItemStack): IStorageTuple<ItemStackWrapper>?
|
fun findItems(stack: ItemStack): IStorageTuple<ItemStorageStack>?
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMatteryDrive<T : IStorageStack> : IStorageComponent<T> {
|
interface IMatteryDrive<T : StorageStack<T>> : IStorageComponent<T> {
|
||||||
val uuid: UUID
|
val uuid: UUID
|
||||||
|
|
||||||
var isDirty: Boolean
|
var isDirty: Boolean
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.capability.drive
|
package ru.dbotthepony.mc.otm.capability.drive
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
import kotlin.jvm.JvmOverloads
|
import kotlin.jvm.JvmOverloads
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@ -18,13 +20,13 @@ import java.math.BigInteger
|
|||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor(
|
abstract class AbstractMatteryDrive<T : StorageStack<T>> @JvmOverloads constructor(
|
||||||
override var driveCapacity: BigInteger,
|
override var driveCapacity: BigInteger,
|
||||||
override val uuid: UUID = UUID.randomUUID(),
|
override val uuid: UUID = UUID.randomUUID(),
|
||||||
var maxDifferentStacks: Int = 0xFFFF
|
var maxDifferentStacks: Int = 0xFFFF
|
||||||
) : IMatteryDrive<T> {
|
) : IMatteryDrive<T> {
|
||||||
protected val tuples = HashMap<T, IStorageTuple<T>>()
|
protected val stack2tuples = Object2ObjectOpenCustomHashMap<T, IStorageTuple.IMutable<T>>(StorageStack.Companion)
|
||||||
protected val tuplesByID: MutableMap<UUID, IStorageTuple<T>> = HashMap()
|
protected val id2tuples = Object2ObjectOpenHashMap<UUID, IStorageTuple.IMutable<T>>()
|
||||||
|
|
||||||
override var isDirty = false
|
override var isDirty = false
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -41,30 +43,25 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
override var storedCount: BigInteger = BigInteger.ZERO
|
override var storedCount: BigInteger = BigInteger.ZERO
|
||||||
protected set
|
protected set
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
|
||||||
override fun insertStack(stack: T, simulate: Boolean): T {
|
override fun insertStack(stack: T, simulate: Boolean): T {
|
||||||
val maxInsert = driveCapacity.minus(storedCount).coerceAtMost(stack.count)
|
val maxInsert = driveCapacity.minus(storedCount).coerceAtMost(stack.count)
|
||||||
if (maxInsert <= BigInteger.ZERO) return stack
|
if (maxInsert <= BigInteger.ZERO) return stack
|
||||||
|
|
||||||
val key = stack.key()
|
val tuple = stack2tuples[stack]
|
||||||
val tuple = tuples[key]
|
|
||||||
|
|
||||||
if (tuple != null) {
|
if (tuple != null) {
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
val oldCount = tuple.stack.count
|
tuple.grow(maxInsert)
|
||||||
tuple.stack.grow(maxInsert)
|
|
||||||
storedCount += maxInsert
|
storedCount += maxInsert
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.changeStack(tuple.stack, tuple.id, oldCount)
|
listener.onStackChanged(tuple.stack, tuple.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
isDirty = true
|
isDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val copy = stack.copy() as T
|
return stack.shrink(maxInsert)
|
||||||
copy.shrink(maxInsert)
|
|
||||||
return copy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storedDifferentStacks >= maxDifferentStacks) {
|
if (storedDifferentStacks >= maxDifferentStacks) {
|
||||||
@ -75,28 +72,22 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
storedDifferentStacks++
|
storedDifferentStacks++
|
||||||
storedCount = storedCount.plus(maxInsert)
|
storedCount = storedCount.plus(maxInsert)
|
||||||
|
|
||||||
val copy = stack.copy() as T
|
val state = IStorageTuple.M(UUID.randomUUID(), stack.copy(maxInsert))
|
||||||
copy.count = maxInsert
|
stack2tuples[stack] = state
|
||||||
|
id2tuples[state.id] = state
|
||||||
val state = StorageTuple(UUID.randomUUID(), copy)
|
|
||||||
tuples[key] = state
|
|
||||||
tuplesByID[state.id] = state
|
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.addStack(state.stack, state.id, this)
|
listener.onStackAdded(state.stack, state.id, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
isDirty = true
|
isDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val copy = stack.copy() as T
|
return stack.shrink(maxInsert)
|
||||||
copy.shrink(maxInsert)
|
|
||||||
return copy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
|
||||||
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
|
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
|
||||||
val get = tuplesByID[id] ?: return storageType.empty
|
val get = id2tuples[id] ?: return storageType.empty
|
||||||
|
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
var amount = amount
|
var amount = amount
|
||||||
@ -109,28 +100,25 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
if (amount <= BigInteger.ZERO)
|
if (amount <= BigInteger.ZERO)
|
||||||
return storageType.empty
|
return storageType.empty
|
||||||
|
|
||||||
val copy = get.stack.copy() as T
|
val copy = get.stack.copy(amount)
|
||||||
copy.count = amount
|
|
||||||
|
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
if (amount == get.stack.count) {
|
if (amount == get.stack.count) {
|
||||||
val key = get.stack.key()
|
stack2tuples.remove(get.stack) ?: throw IllegalStateException("Can't find storage key for ${get.stack}")
|
||||||
tuples.remove(key) ?: throw IllegalStateException("Can't find storage key for ${get.stack}")
|
|
||||||
|
|
||||||
storedDifferentStacks--
|
storedDifferentStacks--
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.removeStack(get.stack, get.id)
|
listener.onStackRemoved(get.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
storedCount -= amount
|
storedCount -= amount
|
||||||
val oldCount = get.stack.count
|
get.shrink(amount)
|
||||||
get.stack.shrink(amount)
|
|
||||||
|
|
||||||
if (get.stack.count.isPositive) {
|
if (get.stack.count.isPositive) {
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.changeStack(get.stack, get.id, oldCount)
|
listener.onStackChanged(get.stack, get.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +128,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun serializeStack(item: IStorageTuple<T>): CompoundTag?
|
protected abstract fun serializeStack(item: T): CompoundTag?
|
||||||
protected abstract fun deserializeStack(tag: CompoundTag): T?
|
protected abstract fun deserializeStack(tag: CompoundTag): T?
|
||||||
|
|
||||||
override fun serializeNBT(): CompoundTag {
|
override fun serializeNBT(): CompoundTag {
|
||||||
@ -152,8 +140,8 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
val list = ListTag()
|
val list = ListTag()
|
||||||
compound["items"] = list
|
compound["items"] = list
|
||||||
|
|
||||||
for (stack in tuples.values) {
|
for (tuple in stack2tuples.values) {
|
||||||
val serialized = serializeStack(stack)
|
val serialized = serializeStack(tuple.stack)
|
||||||
|
|
||||||
if (serialized != null) {
|
if (serialized != null) {
|
||||||
list.add(serialized)
|
list.add(serialized)
|
||||||
@ -165,13 +153,13 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
|
|
||||||
override fun deserializeNBT(nbt: CompoundTag) {
|
override fun deserializeNBT(nbt: CompoundTag) {
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
for (get in tuples.values) {
|
for (get in stack2tuples.values) {
|
||||||
listener.removeStack(get.stack, get.id)
|
listener.onStackRemoved(get.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tuples.clear()
|
stack2tuples.clear()
|
||||||
tuplesByID.clear()
|
id2tuples.clear()
|
||||||
storedCount = BigInteger.ZERO
|
storedCount = BigInteger.ZERO
|
||||||
storedDifferentStacks = 0
|
storedDifferentStacks = 0
|
||||||
// nextID = 0L
|
// nextID = 0L
|
||||||
@ -183,23 +171,23 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
if (entry is CompoundTag) {
|
if (entry is CompoundTag) {
|
||||||
val stack = deserializeStack(entry)
|
val stack = deserializeStack(entry)
|
||||||
|
|
||||||
if (stack != null) {
|
if (stack != null && stack.isNotEmpty) {
|
||||||
storedCount += stack.count
|
storedCount += stack.count
|
||||||
storedDifferentStacks++
|
storedDifferentStacks++
|
||||||
val tuple = StorageTuple(UUID.randomUUID(), stack)
|
val tuple = IStorageTuple.M(UUID.randomUUID(), stack)
|
||||||
tuples[tuple.stack.key()] = tuple
|
stack2tuples[tuple.stack] = tuple
|
||||||
tuplesByID[tuple.id] = tuple
|
id2tuples[tuple.id] = tuple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(id: UUID): T {
|
override fun get(id: UUID): T {
|
||||||
return tuplesByID[id]?.stack ?: storageType.empty
|
return id2tuples[id]?.stack ?: storageType.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
override val stacks: Stream<IStorageTuple<T>> get() {
|
override val stacks: Stream<IStorageTuple<T>> get() {
|
||||||
return ArrayList<IStorageTuple<T>>(tuples.size).also { it.addAll(tuples.values) }.stream()
|
return ArrayList<IStorageTuple<T>>(stack2tuples.size).also { it.addAll(stack2tuples.values) }.stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val listeners = ObjectArraySet<IStorageEventConsumer<T>>()
|
protected val listeners = ObjectArraySet<IStorageEventConsumer<T>>()
|
||||||
@ -211,8 +199,4 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
|
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
|
||||||
return listeners.remove(listener)
|
return listeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOGGER = LogManager.getLogger()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,69 +1,51 @@
|
|||||||
package ru.dbotthepony.mc.otm.capability.drive
|
package ru.dbotthepony.mc.otm.capability.drive
|
||||||
|
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.resources.ResourceLocation
|
|
||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.item.Items
|
|
||||||
import net.minecraftforge.registries.ForgeRegistries
|
|
||||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.BigInteger
|
import ru.dbotthepony.mc.otm.core.math.BigInteger
|
||||||
import ru.dbotthepony.mc.otm.core.math.serializeNBT
|
import ru.dbotthepony.mc.otm.core.math.serializeNBT
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageTuple
|
import ru.dbotthepony.mc.otm.storage.IStorageTuple
|
||||||
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
|
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStackType
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.key
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDrive {
|
class ItemMatteryDrive : AbstractMatteryDrive<ItemStorageStack>, IItemMatteryDrive {
|
||||||
constructor(capacity: BigInteger, max_different_stacks: Int) : super(capacity, maxDifferentStacks = max_different_stacks)
|
constructor(capacity: BigInteger, maxDifferentStacks: Int) : super(capacity, maxDifferentStacks = maxDifferentStacks)
|
||||||
constructor(capacity: BigInteger, uuid: UUID, max_different_stacks: Int) : super(capacity, uuid, max_different_stacks)
|
constructor(capacity: BigInteger, uuid: UUID, maxDifferentStacks: Int) : super(capacity, uuid, maxDifferentStacks)
|
||||||
constructor(capacity: BigInteger, uuid: UUID) : super(capacity, uuid)
|
constructor(capacity: BigInteger, uuid: UUID) : super(capacity, uuid)
|
||||||
constructor(capacity: BigInteger) : super(capacity)
|
constructor(capacity: BigInteger) : super(capacity)
|
||||||
|
|
||||||
override val storageType: StorageStackType<ItemStackWrapper> = OverdriveThatMatters.INSTANCE.ITEM_STORAGE()
|
override val storageType: StorageStack.Type<ItemStorageStack> = StorageStack.ITEMS
|
||||||
|
|
||||||
fun insertStack(item: ItemStack, simulate: Boolean): ItemStack {
|
fun insertStack(item: ItemStack, simulate: Boolean): ItemStack {
|
||||||
return insertStack(ItemStackWrapper(item), simulate).stack
|
return insertStack(ItemStorageStack(item), simulate).toItemStack()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serializeStack(item: IStorageTuple<ItemStackWrapper>): CompoundTag? {
|
override fun serializeStack(item: ItemStorageStack): CompoundTag {
|
||||||
val tag = CompoundTag()
|
val tag = CompoundTag()
|
||||||
val location = item.stack.registryName.toString()
|
tag["item"] = item.toItemStack(1).serializeNBT()
|
||||||
|
tag["count"] = item.count.serializeNBT()
|
||||||
tag["item"] = location
|
|
||||||
tag["count"] = item.stack.count.serializeNBT()
|
|
||||||
|
|
||||||
val itag = item.stack.item.tag
|
|
||||||
|
|
||||||
if (itag != null) {
|
|
||||||
tag["data"] = itag
|
|
||||||
}
|
|
||||||
|
|
||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deserializeStack(tag: CompoundTag): ItemStackWrapper? {
|
override fun deserializeStack(tag: CompoundTag): ItemStorageStack? {
|
||||||
val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(tag.getString("item")))
|
if ("item" in tag && "count" in tag) {
|
||||||
|
val item = tag["item"] as? CompoundTag ?: return null
|
||||||
if (item != null && item !== Items.AIR) {
|
|
||||||
val count = BigInteger(tag["count"])
|
val count = BigInteger(tag["count"])
|
||||||
val itemstack = ItemStack(item, 1)
|
return ItemStorageStack.unsafe(ItemStack.of(item), count)
|
||||||
itemstack.tag = tag["data"] as? CompoundTag
|
|
||||||
return ItemStackWrapper(itemstack, copy = false).also { it.count = count }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findItems(item: Item): List<IStorageTuple<ItemStackWrapper>> {
|
override fun findItems(item: Item): List<IStorageTuple<ItemStorageStack>> {
|
||||||
val list = ArrayList<IStorageTuple<ItemStackWrapper>>()
|
val list = ArrayList<IStorageTuple<ItemStorageStack>>()
|
||||||
|
|
||||||
for ((key, value) in tuples.entries) {
|
for ((key, value) in stack2tuples.entries) {
|
||||||
if (key.item.item === item) {
|
if (key.item === item) {
|
||||||
list.add(value)
|
list.add(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,8 +53,8 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findItems(stack: ItemStack): IStorageTuple<ItemStackWrapper>? {
|
override fun findItems(stack: ItemStack): IStorageTuple<ItemStorageStack>? {
|
||||||
return tuples[ItemStackWrapper(stack).key()]
|
return stack2tuples[ItemStorageStack.unsafe(stack)]
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -68,7 +68,7 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
|
|||||||
object : AbstractSlotPanel<DriveViewerScreen>(this@DriveViewerScreen, grid, 0f, 0f) {
|
object : AbstractSlotPanel<DriveViewerScreen>(this@DriveViewerScreen, grid, 0f, 0f) {
|
||||||
override val itemStack: ItemStack get() {
|
override val itemStack: ItemStack get() {
|
||||||
val index = i + scrollBar.scroll * GRID_WIDTH
|
val index = i + scrollBar.scroll * GRID_WIDTH
|
||||||
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY
|
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
||||||
|
@ -32,7 +32,7 @@ import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
|||||||
import ru.dbotthepony.mc.otm.core.util.formatReadableNumber
|
import ru.dbotthepony.mc.otm.core.util.formatReadableNumber
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatSiComponent
|
import ru.dbotthepony.mc.otm.core.util.formatSiComponent
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
|
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
|
||||||
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
|
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
|
||||||
|
|
||||||
@MouseTweaksDisableWheelTweak
|
@MouseTweaksDisableWheelTweak
|
||||||
@ -73,7 +73,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
|
|||||||
private val index get() = i + viewScrollBar.scroll * ITEM_GRID_WIDTH
|
private val index get() = i + viewScrollBar.scroll * ITEM_GRID_WIDTH
|
||||||
|
|
||||||
override val itemStack: ItemStack get() {
|
override val itemStack: ItemStack get() {
|
||||||
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY
|
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
||||||
@ -88,10 +88,10 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
|
|||||||
override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
|
override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||||
renderSlotBackground(graphics, mouseX, mouseY, partialTick)
|
renderSlotBackground(graphics, mouseX, mouseY, partialTick)
|
||||||
|
|
||||||
val itemstack = menu.networkedItemView.sortedView.getOrNull(index)?.stack ?: ITEM_STORAGE.empty
|
val itemstack = menu.networkedItemView.sortedView.getOrNull(index)?.stack ?: StorageStack.ITEMS.empty
|
||||||
val stack = graphics.pose()
|
val stack = graphics.pose()
|
||||||
|
|
||||||
renderRegular(graphics, itemstack.stack, "")
|
renderRegular(graphics, itemstack.toItemStack(), "")
|
||||||
|
|
||||||
if (!itemstack.isEmpty) {
|
if (!itemstack.isEmpty) {
|
||||||
stack.pushPose()
|
stack.pushPose()
|
||||||
|
@ -33,43 +33,43 @@ private val QIO_LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "item_s
|
|||||||
|
|
||||||
private class QIOTuple(
|
private class QIOTuple(
|
||||||
val mekanismItem: HashedItem,
|
val mekanismItem: HashedItem,
|
||||||
override val stack: ItemStackWrapper,
|
override var stack: ItemStorageStack,
|
||||||
override val id: UUID,
|
override val id: UUID,
|
||||||
var mark: Long
|
var mark: Long
|
||||||
) : IStorageTuple<ItemStackWrapper>
|
) : IStorageTuple.IMutable<ItemStorageStack>
|
||||||
|
|
||||||
private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<ItemStackWrapper> {
|
private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<ItemStorageStack> {
|
||||||
private var mark = 0L
|
private var mark = 0L
|
||||||
|
|
||||||
override val storageType: StorageStackType<ItemStackWrapper>
|
override val storageType: StorageStack.Type<ItemStorageStack>
|
||||||
get() = OverdriveThatMatters.INSTANCE.ITEM_STORAGE()
|
get() = StorageStack.ITEMS
|
||||||
|
|
||||||
private val index = HashMap<UUID, QIOTuple>()
|
private val index = HashMap<UUID, QIOTuple>()
|
||||||
private val tracked = HashMap<HashedItem, QIOTuple>()
|
private val tracked = HashMap<HashedItem, QIOTuple>()
|
||||||
private val listeners = ArrayList<IStorageEventConsumer<ItemStackWrapper>>()
|
private val listeners = ArrayList<IStorageEventConsumer<ItemStorageStack>>()
|
||||||
|
|
||||||
override fun get(id: UUID): ItemStackWrapper {
|
override fun get(id: UUID): ItemStorageStack {
|
||||||
return index[id]?.stack ?: ItemStackWrapper.EMPTY
|
return index[id]?.stack ?: ItemStorageStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
override val stacks: Stream<IStorageTuple<ItemStackWrapper>> get() {
|
override val stacks: Stream<IStorageTuple<ItemStorageStack>> get() {
|
||||||
return ArrayList<IStorageTuple<ItemStackWrapper>>(index.size).also { it.addAll(index.values) }.stream()
|
return ArrayList<IStorageTuple<ItemStorageStack>>(index.size).also { it.addAll(index.values) }.stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper {
|
override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack {
|
||||||
// Because there is no simulate method on QIO array, we have to simulate it by ourselves.
|
// Because there is no simulate method on QIO array, we have to simulate it by ourselves.
|
||||||
val hash = HashedItem.create(stack.item)
|
val hash = HashedItem.create(stack.toItemStack())
|
||||||
|
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
//val used = TransporterManager.getToUse(stack.stack, parent.addItem(stack.stack))
|
//val used = TransporterManager.getToUse(stack.stack, parent.addItem(stack.stack))
|
||||||
val used = parent.addItem(stack.stack)
|
val used = parent.addItem(stack.toItemStack())
|
||||||
|
|
||||||
if (used.count == stack.count.toInt()) {
|
if (used.count == stack.count.toInt()) {
|
||||||
return stack
|
return stack
|
||||||
}
|
}
|
||||||
|
|
||||||
scan(hash)
|
scan(hash)
|
||||||
return ItemStackWrapper(used)
|
return ItemStorageStack(used)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent.totalItemCount >= parent.totalItemCountCapacity) {
|
if (parent.totalItemCount >= parent.totalItemCountCapacity) {
|
||||||
@ -80,33 +80,31 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
|||||||
return stack
|
return stack
|
||||||
}
|
}
|
||||||
|
|
||||||
val inserted = stack.copy()
|
return stack.copy((parent.totalItemCountCapacity - parent.totalItemCount).toBigInteger().coerceAtMost(stack.count))
|
||||||
inserted.count = (parent.totalItemCountCapacity - parent.totalItemCount).toBigInteger().coerceAtMost(stack.count)
|
|
||||||
return inserted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStackWrapper {
|
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): ItemStorageStack {
|
||||||
// Because there is no simulate method on QIO array, we have to simulate it by ourselves.
|
// Because there is no simulate method on QIO array, we have to simulate it by ourselves.
|
||||||
// ASSUMPTION: We can ALWAYS remove items from QIO grid.
|
// ASSUMPTION: We can ALWAYS remove items from QIO grid.
|
||||||
|
|
||||||
if (!amount.isPositive)
|
if (!amount.isPositive)
|
||||||
return ItemStackWrapper.EMPTY
|
return ItemStorageStack.EMPTY
|
||||||
|
|
||||||
var local = index[id] ?: return ItemStackWrapper.EMPTY
|
var local = index[id] ?: return ItemStorageStack.EMPTY
|
||||||
scan(local.mekanismItem)
|
scan(local.mekanismItem)
|
||||||
local = index[id] ?: return ItemStackWrapper.EMPTY // unexpected...
|
local = index[id] ?: return ItemStorageStack.EMPTY // unexpected...
|
||||||
|
|
||||||
if (simulate) {
|
if (simulate) {
|
||||||
return local.stack.copy().also { it.count = it.count.coerceAtMost(amount) }
|
return local.stack.copy(local.stack.count.coerceAtMost(amount))
|
||||||
}
|
}
|
||||||
|
|
||||||
val removed = parent.removeByType(local.mekanismItem, amount.toInt())
|
val removed = parent.removeByType(local.mekanismItem, amount.toInt())
|
||||||
|
|
||||||
if (removed.isEmpty) {
|
if (removed.isEmpty) {
|
||||||
return ItemStackWrapper.EMPTY
|
return ItemStorageStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
val copy = ItemStackWrapper(removed)
|
val copy = ItemStorageStack(removed)
|
||||||
|
|
||||||
if (local.stack.count > copy.count) {
|
if (local.stack.count > copy.count) {
|
||||||
// expecting stack to be still present in QIO storage grid
|
// expecting stack to be still present in QIO storage grid
|
||||||
@ -118,7 +116,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.removeStack(local)
|
listener.onStackRemoved(local)
|
||||||
}
|
}
|
||||||
|
|
||||||
index.remove(local.id)
|
index.remove(local.id)
|
||||||
@ -128,7 +126,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
|||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
|
override fun addListener(listener: IStorageEventConsumer<ItemStorageStack>): Boolean {
|
||||||
if (!listeners.contains(listener)) {
|
if (!listeners.contains(listener)) {
|
||||||
listeners.add(listener)
|
listeners.add(listener)
|
||||||
return true
|
return true
|
||||||
@ -137,30 +135,29 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeListener(listener: IStorageEventConsumer<ItemStackWrapper>): Boolean {
|
override fun removeListener(listener: IStorageEventConsumer<ItemStorageStack>): Boolean {
|
||||||
return listeners.remove(listener)
|
return listeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scan(at: HashedItem, value: QIOItemTypeData = parent.itemDataMap[at] ?: throw IllegalArgumentException("$parent does not have item $at (${at.stack})")) {
|
private fun scan(at: HashedItem, value: QIOItemTypeData = parent.itemDataMap[at] ?: throw IllegalArgumentException("$parent does not have item $at (${at.internalStack})")) {
|
||||||
val local = tracked[at]
|
val local = tracked[at]
|
||||||
|
|
||||||
if (local != null) {
|
if (local != null) {
|
||||||
local.mark = mark
|
local.mark = mark
|
||||||
|
|
||||||
if (local.stack.count.toLong() != value.count) {
|
if (local.stack.count.toLong() != value.count) {
|
||||||
val oldCount = local.stack.count
|
local.stack = local.stack.copy(value.count.toBigInteger())
|
||||||
local.stack.count = value.count.toBigInteger()
|
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.changeStack(local, oldCount)
|
listener.onStackChanged(local)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val tuple = QIOTuple(at, ItemStackWrapper(at.stack).also { it.count = value.count.toBigInteger() }, UUID.randomUUID(), mark)
|
val tuple = QIOTuple(at, ItemStorageStack(at.internalStack, value.count.toBigInteger()), UUID.randomUUID(), mark)
|
||||||
index[tuple.id] = tuple
|
index[tuple.id] = tuple
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.addStack(tuple, this)
|
listener.onStackAdded(tuple, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
tracked[at] = tuple
|
tracked[at] = tuple
|
||||||
@ -192,7 +189,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
|||||||
val tuple = tracked.remove(key)!!
|
val tuple = tracked.remove(key)!!
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.removeStack(tuple)
|
listener.onStackRemoved(tuple)
|
||||||
}
|
}
|
||||||
|
|
||||||
index.remove(tuple.id)
|
index.remove(tuple.id)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.core.collect
|
package ru.dbotthepony.mc.otm.core.collect
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||||
import ru.dbotthepony.mc.otm.core.addAll
|
import ru.dbotthepony.mc.otm.core.addAll
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import java.util.function.BinaryOperator
|
import java.util.function.BinaryOperator
|
||||||
@ -123,6 +124,18 @@ class FlatMappingIterator<T, R>(private val parent: Iterator<T>, private val map
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> concatIterators(): MutableIterator<T> {
|
||||||
|
return ObjectIterators.EMPTY_ITERATOR as MutableIterator<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> concatIterators(a: Iterator<T>): MutableIterator<T> {
|
||||||
|
return a as MutableIterator<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> concatIterators(vararg iterators: Iterator<T>): MutableIterator<T> {
|
||||||
|
return iterators.iterator().flatMap { it }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limits amount of values returned by [parent] iterator to return at most [limit] values
|
* Limits amount of values returned by [parent] iterator to return at most [limit] values
|
||||||
*
|
*
|
||||||
|
@ -8,32 +8,22 @@ import ru.dbotthepony.mc.otm.storage.*
|
|||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
|
||||||
class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() {
|
class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() {
|
||||||
private val virtualComponents = Object2ObjectArrayMap<StorageStackType<*>, VirtualComponent<*>>()
|
private val virtualComponents = Object2ObjectArrayMap<StorageStack.Type<*>, VirtualComponent<*>>()
|
||||||
val powerDemandingNodes = LinkedList<IMatteryEnergyStorage>()
|
val powerDemandingNodes = LinkedList<IMatteryEnergyStorage>()
|
||||||
|
|
||||||
/**
|
fun <T : StorageStack<T>> getVirtualComponent(type: StorageStack.Type<T>): VirtualComponent<T> {
|
||||||
* Returns a [VirtualComponent] representing [type] storage
|
|
||||||
*/
|
|
||||||
fun <T : IStorageStack> getVirtualComponent(type: StorageStackType<T>): VirtualComponent<T> {
|
|
||||||
return virtualComponents.computeIfAbsent(type, Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<T>
|
return virtualComponents.computeIfAbsent(type, Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun <T : StorageStack<T>> add(storage: IStorage<T>) {
|
||||||
* Returns a [VirtualComponent] representing [type] storage
|
|
||||||
*/
|
|
||||||
fun <T : IStorageStack> getVirtualComponent(type: Class<T>): VirtualComponent<T> {
|
|
||||||
return virtualComponents.computeIfAbsent(StorageRegistry.get(type), Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : IStorageStack> add(storage: IStorage<T>) {
|
|
||||||
getVirtualComponent(storage.storageType).add(storage)
|
getVirtualComponent(storage.storageType).add(storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : IStorageStack> remove(storage: IStorage<T>) {
|
fun <T : StorageStack<T>> remove(storage: IStorage<T>) {
|
||||||
getVirtualComponent(storage.storageType).remove(storage)
|
getVirtualComponent(storage.storageType).remove(storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : IStorageStack> contains(storage: IStorage<T>): Boolean {
|
fun <T : StorageStack<T>> contains(storage: IStorage<T>): Boolean {
|
||||||
val virtual = virtualComponents[storage.storageType] ?: return false
|
val virtual = virtualComponents[storage.storageType] ?: return false
|
||||||
return (virtual as VirtualComponent<T>).contains(storage)
|
return (virtual as VirtualComponent<T>).contains(storage)
|
||||||
}
|
}
|
||||||
@ -45,5 +35,4 @@ class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() {
|
|||||||
override fun onNodeRemoved(node: StorageNode) {
|
override fun onNodeRemoved(node: StorageNode) {
|
||||||
node.removeComponents(this)
|
node.removeComponents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,12 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
|||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.graph.GraphNode
|
import ru.dbotthepony.mc.otm.graph.GraphNode
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorage
|
import ru.dbotthepony.mc.otm.storage.IStorage
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageStack
|
import ru.dbotthepony.mc.otm.storage.IVirtualStorageComponent
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStackType
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
|
import ru.dbotthepony.mc.otm.storage.VirtualComponent
|
||||||
|
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) {
|
open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) {
|
||||||
protected val components = ArrayList<IStorage<*>>()
|
protected val components = ArrayList<IStorage<*>>()
|
||||||
@ -72,71 +75,31 @@ open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
@Suppress("unchecked_cast")
|
||||||
fun <T : IStorageStack, U : IStorage<T>> computeIfAbsent(identity: StorageStackType<T>, provider: (StorageStackType<T>) -> U): U {
|
fun <T : StorageStack<T>, U : IStorage<T>> computeIfAbsent(type: StorageStack.Type<T>, provider: (StorageStack.Type<T>) -> U): U {
|
||||||
for (component in components) {
|
return components.firstOrNull { it.storageType == type } as U? ?: provider(type).also { addStorageComponent(it) }
|
||||||
if (component.storageType === identity) {
|
|
||||||
return component as U
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val factory = provider(identity)
|
|
||||||
addStorageComponent(factory)
|
|
||||||
return factory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addStorageComponent(component: IStorage<*>) {
|
fun addStorageComponent(component: IStorage<*>) {
|
||||||
for (component1 in components) {
|
if (!components.any { component === it || it.storageType === component.storageType }) {
|
||||||
if (component === component1 || component1.storageType === component.storageType) {
|
components.add(component)
|
||||||
return
|
|
||||||
}
|
if (isValid && !manualAttaching)
|
||||||
|
graph.add(component)
|
||||||
}
|
}
|
||||||
|
|
||||||
components.add(component)
|
|
||||||
|
|
||||||
if (isValid && !manualAttaching)
|
|
||||||
graph.add(component)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeStorageComponent(component: IStorage<*>) {
|
fun removeStorageComponent(component: IStorage<*>) {
|
||||||
var indexOf = -1
|
val indexOf = components.indexOfFirst { component === it || it.storageType === component.storageType }
|
||||||
|
if (indexOf == -1) return
|
||||||
for ((i, component1) in components.withIndex()) {
|
val self = components.removeAt(indexOf)
|
||||||
if (component === component1 || component1.storageType === component.storageType) {
|
if (isValid) graph.remove(self)
|
||||||
indexOf = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexOf == -1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val self = components[indexOf]
|
|
||||||
components.removeAt(indexOf)
|
|
||||||
|
|
||||||
if (isValid)
|
|
||||||
graph.remove(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeStorageComponent(component: StorageStackType<*>) {
|
fun removeStorageComponent(component: StorageStack.Type<*>) {
|
||||||
var indexOf = -1
|
val indexOf = components.indexOfFirst { it.storageType === component }
|
||||||
|
if (indexOf == -1) return
|
||||||
for ((i, component1) in components.withIndex()) {
|
val self = components.removeAt(indexOf)
|
||||||
if (component1.storageType === component) {
|
if (isValid) graph.remove(self)
|
||||||
indexOf = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexOf == -1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val self = components[indexOf]
|
|
||||||
components.removeAt(indexOf)
|
|
||||||
|
|
||||||
if (isValid && !manualAttaching)
|
|
||||||
graph.remove(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() {
|
override fun invalidate() {
|
||||||
|
@ -97,7 +97,8 @@ import ru.dbotthepony.mc.otm.network.GenericNetworkChannel
|
|||||||
import ru.dbotthepony.mc.otm.network.MatteryPacket
|
import ru.dbotthepony.mc.otm.network.MatteryPacket
|
||||||
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
|
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
|
||||||
import ru.dbotthepony.mc.otm.secondTime
|
import ru.dbotthepony.mc.otm.secondTime
|
||||||
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
|
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
||||||
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -1202,9 +1203,9 @@ object MatterManager {
|
|||||||
|
|
||||||
val drive = value.getCapability(MatteryCapability.DRIVE).orNull()
|
val drive = value.getCapability(MatteryCapability.DRIVE).orNull()
|
||||||
|
|
||||||
if (drive != null && drive.storageType == OverdriveThatMatters.INSTANCE.ITEM_STORAGE()) {
|
if (drive != null && drive.storageType == StorageStack.ITEMS) {
|
||||||
(drive as IMatteryDrive<ItemStackWrapper>).stacks
|
(drive as IMatteryDrive<ItemStorageStack>).stacks
|
||||||
.map { it.stack.stack }
|
.map { it.stack.toItemStack() }
|
||||||
.filter { !it.isEmpty }
|
.filter { !it.isEmpty }
|
||||||
.map { get(it, level + 1, true) }
|
.map { get(it, level + 1, true) }
|
||||||
.reduce(::reduce)
|
.reduce(::reduce)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.data
|
package ru.dbotthepony.mc.otm.menu.data
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.InputConstants
|
import com.mojang.blaze3d.platform.InputConstants
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.gui.screens.Screen
|
import net.minecraft.client.gui.screens.Screen
|
||||||
@ -75,11 +74,11 @@ object ClearItemViewPacket : MatteryPacket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStackWrapper) : MatteryPacket {
|
class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStorageStack) : MatteryPacket {
|
||||||
override fun write(buff: FriendlyByteBuf) {
|
override fun write(buff: FriendlyByteBuf) {
|
||||||
buff.writeInt(containerId)
|
buff.writeInt(containerId)
|
||||||
buff.writeInt(id)
|
buff.writeInt(id)
|
||||||
buff.writeBigItem(stack)
|
stack.write(buff)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||||
@ -127,7 +126,7 @@ class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStackWrap
|
|||||||
fun read(buffer: FriendlyByteBuf): StackAddPacket {
|
fun read(buffer: FriendlyByteBuf): StackAddPacket {
|
||||||
val containerId = buffer.readInt()
|
val containerId = buffer.readInt()
|
||||||
val id = buffer.readInt()
|
val id = buffer.readInt()
|
||||||
val item = buffer.readBigItem()
|
val item = StorageStack.ITEMS.read(buffer)
|
||||||
return StackAddPacket(containerId, id, item)
|
return StackAddPacket(containerId, id, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,7 +150,7 @@ class StackChangePacket(val id: Int, val stackID: Int, val newCount: BigInteger)
|
|||||||
val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
|
val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
|
||||||
val state = view.localState[stackID] ?: throw IllegalStateException("No such stack with id $stackID in $view")
|
val state = view.localState[stackID] ?: throw IllegalStateException("No such stack with id $stackID in $view")
|
||||||
|
|
||||||
state.stack.count = newCount
|
state.stack = state.stack.copy(newCount)
|
||||||
view.resort()
|
view.resort()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,11 +198,11 @@ class StackRemovePacket(val id: Int, val stackID: Int) : MatteryPacket {
|
|||||||
/**
|
/**
|
||||||
* Creates a virtual, slotless container for Player to interaction with.
|
* Creates a virtual, slotless container for Player to interaction with.
|
||||||
*/
|
*/
|
||||||
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer<ItemStackWrapper> {
|
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer<ItemStorageStack> {
|
||||||
data class NetworkedItem constructor(val id: Int, val stack: ItemStackWrapper, val upstreamId: UUID? = null)
|
data class NetworkedItem(val id: Int, var stack: ItemStorageStack, val upstreamId: UUID? = null)
|
||||||
|
|
||||||
override val storageType: StorageStackType<ItemStackWrapper>
|
override val storageType: StorageStack.Type<ItemStorageStack>
|
||||||
get() = ITEM_STORAGE
|
get() = StorageStack.ITEMS
|
||||||
|
|
||||||
// this (how client see and interact with)
|
// this (how client see and interact with)
|
||||||
val localState = Int2ObjectOpenHashMap<NetworkedItem>()
|
val localState = Int2ObjectOpenHashMap<NetworkedItem>()
|
||||||
@ -233,7 +232,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
|
|
||||||
fun resort() {
|
fun resort() {
|
||||||
sortedView.sortWith { a, b ->
|
sortedView.sortWith { a, b ->
|
||||||
return@sortWith sorter.compare(a.stack.item, b.stack.item)
|
return@sortWith sorter.compare(a.stack.toItemStack(), b.stack.toItemStack())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +242,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
|
|
||||||
operator fun get(id: Int): NetworkedItem? = localState[id]
|
operator fun get(id: Int): NetworkedItem? = localState[id]
|
||||||
|
|
||||||
var provider: IStorageComponent<ItemStackWrapper>? = null
|
var provider: IStorageComponent<ItemStorageStack>? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun mouseClick(index: Int, mouseButton: Int) {
|
fun mouseClick(index: Int, mouseButton: Int) {
|
||||||
@ -264,33 +263,33 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setComponent(provider: IStorageComponent<ItemStackWrapper>?) {
|
fun setComponent(provider: IStorageComponent<ItemStorageStack>?) {
|
||||||
if (provider === this.provider) return
|
if (provider === this.provider) return
|
||||||
|
|
||||||
this.provider?.removeListenerAuto(this)
|
this.provider?.removeListenerAndNotify(this)
|
||||||
this.provider = provider
|
this.provider = provider
|
||||||
provider?.addListenerAuto(this)
|
provider?.addListenerAndNotify(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removed() {
|
fun removed() {
|
||||||
provider?.removeListenerAuto(this)
|
provider?.removeListenerAndNotify(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
val itemCount get() = localState.values.size
|
val itemCount get() = localState.values.size
|
||||||
|
|
||||||
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) {
|
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
|
||||||
check(!upstreamState.containsKey(id)) { "Already tracking ItemStack with upstream id $id!" }
|
check(!upstreamState.containsKey(id)) { "Already tracking ItemStack with upstream id $id!" }
|
||||||
|
|
||||||
val state = NetworkedItem(nextItemID++, stack.copy(), id)
|
val state = NetworkedItem(nextItemID++, stack, id)
|
||||||
|
|
||||||
this.localState[state.id] = state
|
this.localState[state.id] = state
|
||||||
upstreamState[id] = state
|
upstreamState[id] = state
|
||||||
network { StackAddPacket(menu.containerId, state.id, state.stack) }
|
network { StackAddPacket(menu.containerId, state.id, state.stack) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) {
|
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
|
||||||
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
|
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
|
||||||
get.stack.count = stack.count
|
get.stack = stack
|
||||||
network { StackChangePacket(menu.containerId, get.id, stack.count) }
|
network { StackChangePacket(menu.containerId, get.id, stack.count) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +299,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeStack(stack: ItemStackWrapper, id: UUID) {
|
override fun onStackRemoved(id: UUID) {
|
||||||
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
|
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
|
||||||
upstreamState.remove(id)
|
upstreamState.remove(id)
|
||||||
localState.remove(get.id)
|
localState.remove(get.id)
|
||||||
@ -342,7 +341,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
if (stackId < 0 || !ply.abilities.instabuild) return
|
if (stackId < 0 || !ply.abilities.instabuild) return
|
||||||
|
|
||||||
val state = get(stackId) ?: return
|
val state = get(stackId) ?: return
|
||||||
val copy = state.stack.stack.also { it.count = it.maxStackSize }
|
val copy = state.stack.toItemStack().also { it.count = it.maxStackSize }
|
||||||
|
|
||||||
ply.containerMenu.carried = copy
|
ply.containerMenu.carried = copy
|
||||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(ply.containerMenu.carried))
|
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(ply.containerMenu.carried))
|
||||||
@ -353,17 +352,18 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
|
|
||||||
if (click == ClickType.QUICK_MOVE && stackId > -1) {
|
if (click == ClickType.QUICK_MOVE && stackId > -1) {
|
||||||
val state = get(stackId) ?: return
|
val state = get(stackId) ?: return
|
||||||
|
val stack = state.stack.toItemStack()
|
||||||
|
|
||||||
val amount =
|
val amount =
|
||||||
if (action == ClickAction.PRIMARY)
|
if (action == ClickAction.PRIMARY)
|
||||||
state.stack.item.maxStackSize
|
stack.maxStackSize
|
||||||
else
|
else
|
||||||
1.coerceAtLeast(state.stack.item.maxStackSize / 2)
|
1.coerceAtLeast(stack.maxStackSize / 2)
|
||||||
|
|
||||||
val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), true)
|
val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), true)
|
||||||
|
|
||||||
if (!extracted.isEmpty) {
|
if (!extracted.isEmpty) {
|
||||||
val remaining = menu.quickMoveToInventory(extracted.stack, false)
|
val remaining = menu.quickMoveToInventory(extracted.toItemStack(), false)
|
||||||
|
|
||||||
if (remaining.count != extracted.count.toInt()) {
|
if (remaining.count != extracted.count.toInt()) {
|
||||||
provider.extractStack(state.upstreamId, (extracted.count.toInt() - remaining.count).toBigInteger(), false)
|
provider.extractStack(state.upstreamId, (extracted.count.toInt() - remaining.count).toBigInteger(), false)
|
||||||
@ -380,8 +380,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
val amount = carried.count
|
val amount = carried.count
|
||||||
|
|
||||||
if (amount == carried.count) {
|
if (amount == carried.count) {
|
||||||
val stack = provider.insertStack(ItemStackWrapper(menu.carried), false).stack
|
menu.carried = provider.insertStack(ItemStorageStack(menu.carried), false).toItemStack()
|
||||||
menu.carried = stack
|
|
||||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
||||||
menu.setRemoteCarried(menu.carried.copy())
|
menu.setRemoteCarried(menu.carried.copy())
|
||||||
}
|
}
|
||||||
@ -389,7 +388,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
val copy = menu.carried.copy()
|
val copy = menu.carried.copy()
|
||||||
copy.count = 1
|
copy.count = 1
|
||||||
|
|
||||||
if (provider.insertStack(ItemStackWrapper(copy), false).isEmpty) {
|
if (provider.insertStack(ItemStorageStack(copy), false).isEmpty) {
|
||||||
menu.carried.shrink(1)
|
menu.carried.shrink(1)
|
||||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
||||||
menu.setRemoteCarried(menu.carried.copy())
|
menu.setRemoteCarried(menu.carried.copy())
|
||||||
@ -397,15 +396,15 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
}
|
}
|
||||||
} else if (stackId > -1) {
|
} else if (stackId > -1) {
|
||||||
val state = get(stackId) ?: return
|
val state = get(stackId) ?: return
|
||||||
|
val stack = state.stack.toItemStack()
|
||||||
|
|
||||||
val amount =
|
val amount =
|
||||||
if (action == ClickAction.PRIMARY)
|
if (action == ClickAction.PRIMARY)
|
||||||
state.stack.item.maxStackSize
|
stack.maxStackSize
|
||||||
else
|
else
|
||||||
(state.stack.stack.count / 2).coerceAtMost(state.stack.item.maxStackSize / 2).coerceAtLeast(1)
|
(stack.count / 2).coerceAtMost(stack.maxStackSize / 2).coerceAtLeast(1)
|
||||||
|
|
||||||
val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false)
|
menu.carried = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false).toItemStack()
|
||||||
menu.carried = extracted.stack
|
|
||||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
||||||
menu.setRemoteCarried(menu.carried.copy())
|
menu.setRemoteCarried(menu.carried.copy())
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@ import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
|
|||||||
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
||||||
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
||||||
import ru.dbotthepony.mc.otm.registry.MMenus
|
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||||
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE
|
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.PoweredVirtualComponent
|
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||||
|
|
||||||
class DriveViewerMenu @JvmOverloads constructor(
|
class DriveViewerMenu @JvmOverloads constructor(
|
||||||
containerID: Int,
|
containerID: Int,
|
||||||
@ -33,8 +33,8 @@ class DriveViewerMenu @JvmOverloads constructor(
|
|||||||
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
|
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
|
||||||
val driveSlot: MatterySlot
|
val driveSlot: MatterySlot
|
||||||
|
|
||||||
private val powered: PoweredVirtualComponent<ItemStackWrapper>?
|
private val powered: PoweredVirtualComponent<ItemStorageStack>?
|
||||||
private var lastDrive: IMatteryDrive<ItemStackWrapper>? = null
|
private var lastDrive: IMatteryDrive<ItemStorageStack>? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val container = tile?.container ?: SimpleContainer(1)
|
val container = tile?.container ?: SimpleContainer(1)
|
||||||
@ -47,8 +47,8 @@ class DriveViewerMenu @JvmOverloads constructor(
|
|||||||
|
|
||||||
if (tile != null) {
|
if (tile != null) {
|
||||||
powered = PoweredVirtualComponent(
|
powered = PoweredVirtualComponent(
|
||||||
ItemStackWrapper::class.java,
|
StorageStack.ITEMS,
|
||||||
tile.getCapability(MatteryCapability.ENERGY).resolve().get()
|
tile.getCapability(MatteryCapability.ENERGY).resolve()::get
|
||||||
)
|
)
|
||||||
|
|
||||||
this.networkedItemView.setComponent(powered)
|
this.networkedItemView.setComponent(powered)
|
||||||
@ -83,8 +83,8 @@ class DriveViewerMenu @JvmOverloads constructor(
|
|||||||
|
|
||||||
if (!itemStack.isEmpty) {
|
if (!itemStack.isEmpty) {
|
||||||
itemStack.getCapability(MatteryCapability.DRIVE).ifPresentK {
|
itemStack.getCapability(MatteryCapability.DRIVE).ifPresentK {
|
||||||
if (it.storageType === ITEM_STORAGE) {
|
if (it.storageType == StorageStack.ITEMS) {
|
||||||
lastDrive = it as IMatteryDrive<ItemStackWrapper>
|
lastDrive = it as IMatteryDrive<ItemStorageStack>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ class DriveViewerMenu @JvmOverloads constructor(
|
|||||||
if (lastDrive == null || powered == null)
|
if (lastDrive == null || powered == null)
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
|
|
||||||
val remaining = powered.insertStack(ItemStackWrapper(item), false)
|
val remaining = powered.insertStack(ItemStorageStack(item), false)
|
||||||
|
|
||||||
if (remaining.count.toInt() == item.count)
|
if (remaining.count.toInt() == item.count)
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
|
@ -139,7 +139,7 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
val leftover = networkedItemView.provider?.insertStack(ItemStackWrapper(slot.item), false)?.stack ?: slot.item
|
val leftover = networkedItemView.provider?.insertStack(ItemStorageStack(slot.item), false)?.toItemStack() ?: slot.item
|
||||||
|
|
||||||
if (leftover.count == slot.item.count) {
|
if (leftover.count == slot.item.count) {
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
@ -163,7 +163,7 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
remainder = networkedItemView.provider?.insertStack(ItemStackWrapper(remainder), false)?.stack ?: remainder
|
remainder = networkedItemView.provider?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
||||||
slots[slotIndex].set(remainder)
|
slots[slotIndex].set(remainder)
|
||||||
|
|
||||||
if (remainder.isEmpty) {
|
if (remainder.isEmpty) {
|
||||||
@ -229,8 +229,8 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
try {
|
try {
|
||||||
when (settings.resultTarget) {
|
when (settings.resultTarget) {
|
||||||
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM -> {
|
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM -> {
|
||||||
val wrapper = ItemStackWrapper(item)
|
val wrapper = ItemStorageStack(item)
|
||||||
var remaining = tile.poweredView?.insertStack(wrapper, true)?.stack ?: return ItemStack.EMPTY
|
var remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY
|
||||||
|
|
||||||
if (remaining.isEmpty) {
|
if (remaining.isEmpty) {
|
||||||
tile.poweredView!!.insertStack(wrapper, false)
|
tile.poweredView!!.insertStack(wrapper, false)
|
||||||
@ -241,7 +241,7 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
remaining = moveItemStackToSlots(remaining, playerInventorySlots, simulate = true)
|
remaining = moveItemStackToSlots(remaining, playerInventorySlots, simulate = true)
|
||||||
|
|
||||||
if (remaining.isEmpty) {
|
if (remaining.isEmpty) {
|
||||||
remaining = tile.poweredView!!.insertStack(wrapper, false).stack
|
remaining = tile.poweredView!!.insertStack(wrapper, false).toItemStack()
|
||||||
moveItemStackToSlots(remaining, playerInventorySlots, simulate = false)
|
moveItemStackToSlots(remaining, playerInventorySlots, simulate = false)
|
||||||
craftingResult.remove(item.count)
|
craftingResult.remove(item.count)
|
||||||
return item
|
return item
|
||||||
@ -259,12 +259,12 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
val wrapper = ItemStackWrapper(remaining)
|
val wrapper = ItemStorageStack(remaining)
|
||||||
remaining = tile.poweredView?.insertStack(wrapper, true)?.stack ?: return ItemStack.EMPTY
|
remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY
|
||||||
|
|
||||||
if (remaining.isEmpty) {
|
if (remaining.isEmpty) {
|
||||||
moveItemStackToSlots(item, playerInventorySlots, simulate = false)
|
moveItemStackToSlots(item, playerInventorySlots, simulate = false)
|
||||||
tile.poweredView!!.insertStack(wrapper, false).stack
|
tile.poweredView!!.insertStack(wrapper, false)
|
||||||
craftingResult.remove(item.count)
|
craftingResult.remove(item.count)
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ import ru.dbotthepony.mc.otm.matter.IMatterFunction
|
|||||||
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
|
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
|
||||||
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
|
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
|
||||||
import ru.dbotthepony.mc.otm.registry.objects.StripedColoredDecorativeBlock
|
import ru.dbotthepony.mc.otm.registry.objects.StripedColoredDecorativeBlock
|
||||||
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import ru.dbotthepony.mc.otm.triggers.AndroidBatteryTrigger
|
import ru.dbotthepony.mc.otm.triggers.AndroidBatteryTrigger
|
||||||
import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger
|
import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger
|
||||||
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
|
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
|
||||||
@ -274,6 +275,7 @@ object MRegistry {
|
|||||||
MItemFunctionTypes.register(bus)
|
MItemFunctionTypes.register(bus)
|
||||||
MLootItemConditions.register(bus)
|
MLootItemConditions.register(bus)
|
||||||
MRecipes.register(bus)
|
MRecipes.register(bus)
|
||||||
|
StorageStack.register(bus)
|
||||||
|
|
||||||
if (FMLEnvironment.dist == Dist.CLIENT) {
|
if (FMLEnvironment.dist == Dist.CLIENT) {
|
||||||
MBlockColors.register(bus)
|
MBlockColors.register(bus)
|
||||||
|
@ -4,69 +4,20 @@ import java.math.BigInteger
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a stack in storage system.
|
|
||||||
*
|
|
||||||
* In general there are next rules:
|
|
||||||
* 1. Once created, stack is immutable, except for it's [count], therefore [copy] is expected to do
|
|
||||||
* "shallow" copies (not performance taxing).
|
|
||||||
* 2. Due to condition above, and due to sublying storage most of time being
|
|
||||||
* mutable, it is expected you do defensive copies. Examples of when you should do
|
|
||||||
* them are described on related interfaces.
|
|
||||||
* 3. For storing stacks as [Map] keys, a stack with size of 1 is utilized (requiring [equals] and [hashCode] to return meaningful results, see [IStorageStack.key]).
|
|
||||||
* 4. For equality (INCLUDING count), regular [equals] is utilized.
|
|
||||||
*/
|
|
||||||
interface IStorageStack {
|
|
||||||
fun copy(): IStorageStack
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of this storage stack
|
|
||||||
*
|
|
||||||
* This is overriden in subclasses
|
|
||||||
*/
|
|
||||||
var count: BigInteger
|
|
||||||
val isEmpty: Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return max stack size for this stack object,
|
|
||||||
* null if unlimited (default)
|
|
||||||
*/
|
|
||||||
val maxStackSize: BigInteger? get() = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increase [count] by [amount]
|
|
||||||
*/
|
|
||||||
fun grow(amount: BigInteger) {
|
|
||||||
count += amount
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrease [count] by [amount]
|
|
||||||
*/
|
|
||||||
fun shrink(amount: BigInteger) {
|
|
||||||
count -= amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun <T : IStorageStack> T.key(): T {
|
|
||||||
return copy().also { it.count = BigInteger.ONE } as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage system root, along IStorageStack interface
|
* Storage system root, along IStorageStack interface
|
||||||
*/
|
*/
|
||||||
interface IStorage<T : IStorageStack> {
|
interface IStorage<T : StorageStack<T>> {
|
||||||
/**
|
/**
|
||||||
* @return Identity of this virtual component
|
* @return Identity of this virtual component
|
||||||
*/
|
*/
|
||||||
val storageType: StorageStackType<T>
|
val storageType: StorageStack.Type<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates events for [IStorageEventConsumer]
|
* Generates events for [IStorageEventConsumer]
|
||||||
*/
|
*/
|
||||||
interface IStorageEventProducer<T : IStorageStack> : IStorage<T> {
|
interface IStorageEventProducer<T : StorageStack<T>> : IStorage<T> {
|
||||||
/**
|
/**
|
||||||
* [listener] is [IStorageEventConsumer] which want to subscribe to our events
|
* [listener] is [IStorageEventConsumer] which want to subscribe to our events
|
||||||
*/
|
*/
|
||||||
@ -81,27 +32,39 @@ interface IStorageEventProducer<T : IStorageStack> : IStorage<T> {
|
|||||||
/**
|
/**
|
||||||
* Consumes events produced by [IStorageEventConsumer]
|
* Consumes events produced by [IStorageEventConsumer]
|
||||||
*/
|
*/
|
||||||
interface IStorageEventConsumer<T : IStorageStack> : IStorage<T> {
|
interface IStorageEventConsumer<T : StorageStack<T>> : IStorage<T> {
|
||||||
/**
|
/**
|
||||||
* Fired on whenever an object is added (to listener) we subscribed to
|
* Fired on whenever an object is added (to listener) we subscribed to
|
||||||
*/
|
*/
|
||||||
fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>)
|
fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fired on whenever an object is changes on listener we subscribed to
|
* Fired on whenever an object is changes on listener we subscribed to
|
||||||
*/
|
*/
|
||||||
fun changeStack(stack: T, id: UUID, oldCount: BigInteger)
|
fun onStackChanged(stack: T, id: UUID)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fired on whenever an object is removed from listener we subscribed to
|
* Fired on whenever an object is removed from listener we subscribed to
|
||||||
*/
|
*/
|
||||||
fun removeStack(stack: T, id: UUID)
|
fun onStackRemoved(id: UUID)
|
||||||
|
|
||||||
|
fun onStackChanged(tuple: IStorageTuple<T>) {
|
||||||
|
onStackChanged(tuple.stack, tuple.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStackAdded(tuple: IStorageTuple<T>, provider: IStorageProvider<T>) {
|
||||||
|
onStackAdded(tuple.stack, tuple.id, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStackRemoved(tuple: IStorageTuple<T>) {
|
||||||
|
onStackRemoved(tuple.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage acceptor, accepts (insert only) stacks provided via [insertStack] method.
|
* Storage acceptor, accepts (insert only) stacks provided via [insertStack] method.
|
||||||
*/
|
*/
|
||||||
interface IStorageAcceptor<T : IStorageStack> : IStorage<T> {
|
interface IStorageAcceptor<T : StorageStack<T>> : IStorage<T> {
|
||||||
/**
|
/**
|
||||||
* Inserts an item into system.
|
* Inserts an item into system.
|
||||||
* @return leftover, might equal to [stack] if no items were inserted
|
* @return leftover, might equal to [stack] if no items were inserted
|
||||||
@ -112,10 +75,7 @@ interface IStorageAcceptor<T : IStorageStack> : IStorage<T> {
|
|||||||
/**
|
/**
|
||||||
* Storage provider, provides (extract only) stacks to whoever needs them (the "warehouse").
|
* Storage provider, provides (extract only) stacks to whoever needs them (the "warehouse").
|
||||||
*
|
*
|
||||||
* [get] methods work as in bidirectional map.
|
* [get] methods work as in bidirectional map, with each stack mapping to unique [UUID]
|
||||||
*
|
|
||||||
* It is **required** for storage having **exactly one or none** of mappings of one stack [T]
|
|
||||||
* to one [UUID] (exactly one *UUID -> stack* and exactly one *stack -> UUID*).
|
|
||||||
*
|
*
|
||||||
* What this means is that [get] with [T] as an argument shall never experience a situation where
|
* What this means is that [get] with [T] as an argument shall never experience a situation where
|
||||||
* two stacks match provided key.
|
* two stacks match provided key.
|
||||||
@ -126,15 +86,14 @@ interface IStorageAcceptor<T : IStorageStack> : IStorage<T> {
|
|||||||
* which may or may not produce performance hit; [UUID]s are lightweight, semantically not bound to anything and are
|
* which may or may not produce performance hit; [UUID]s are lightweight, semantically not bound to anything and are
|
||||||
* very good for distributed ID generation (so nothing in game has to be bound to one sequential number generator).
|
* very good for distributed ID generation (so nothing in game has to be bound to one sequential number generator).
|
||||||
*/
|
*/
|
||||||
interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
|
interface IStorageProvider<T : StorageStack<T>> : IStorageEventProducer<T> {
|
||||||
/**
|
/**
|
||||||
* Attempts to retrieve stored stack by its [id].
|
* Attempts to retrieve stored stack by its [id].
|
||||||
*
|
*
|
||||||
* Returns stored stack as-is. If no stack with [id] exists, then
|
* If no stack with [id] exists, then "empty stack" is returned.
|
||||||
* "empty stack" is returned.
|
|
||||||
*
|
*
|
||||||
* @param id identifier of stack
|
* @param id identifier of stack
|
||||||
* @return stored object (not a copy). Do not edit it.
|
* @return stored object
|
||||||
*/
|
*/
|
||||||
operator fun get(id: UUID): T
|
operator fun get(id: UUID): T
|
||||||
|
|
||||||
@ -148,21 +107,13 @@ interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
|
|||||||
* @return UUID of stack if present
|
* @return UUID of stack if present
|
||||||
*/
|
*/
|
||||||
operator fun get(key: T): UUID? {
|
operator fun get(key: T): UUID? {
|
||||||
val key1 = key.let { if (it.count == BigInteger.ONE) it else it.key() }
|
return stacks.filter { it.stack.equalsWithoutCount(key) }.findFirst().orElse(null)?.id
|
||||||
|
|
||||||
return stacks.filter {
|
|
||||||
if (it.stack.count == BigInteger.ONE) {
|
|
||||||
return@filter it.stack == key1
|
|
||||||
} else {
|
|
||||||
return@filter it.stack.key() == key1
|
|
||||||
}
|
|
||||||
}.findFirst().orElse(null)?.id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val stacks: Stream<IStorageTuple<T>>
|
val stacks: Stream<IStorageTuple<T>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If tuple does not exist, returns empty stack
|
* If storage has no such stack, returns empty stack
|
||||||
*
|
*
|
||||||
* @param id identifier of stack to extract
|
* @param id identifier of stack to extract
|
||||||
* @param amount amount of units to extract
|
* @param amount amount of units to extract
|
||||||
@ -172,58 +123,35 @@ interface IStorageProvider<T : IStorageStack> : IStorageEventProducer<T> {
|
|||||||
fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T
|
fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : IStorageStack> IStorageProvider<T>.removeListenerAuto(listener: IStorageEventConsumer<T>): Boolean {
|
|
||||||
if (removeListener(listener)) {
|
|
||||||
for (stack in stacks) {
|
|
||||||
listener.removeStack(stack.stack, stack.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : IStorageStack> IStorageProvider<T>.addListenerAuto(listener: IStorageEventConsumer<T>): Boolean {
|
|
||||||
if (addListener(listener)) {
|
|
||||||
for (stack in stacks) {
|
|
||||||
listener.addStack(stack.stack, stack.id, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage component, which basically implement Input and Output
|
* Storage component, which basically implement Input and Output
|
||||||
*/
|
*/
|
||||||
interface IStorageComponent<T : IStorageStack> : IStorageProvider<T>, IStorageAcceptor<T>
|
interface IStorageComponent<T : StorageStack<T>> : IStorageProvider<T>, IStorageAcceptor<T>
|
||||||
|
|
||||||
fun <T : IStorageStack> IStorageEventConsumer<T>.changeStack(tuple: IStorageTuple<T>, oldCount: BigInteger) {
|
interface IStorageTuple<T : StorageStack<T>> {
|
||||||
changeStack(tuple.stack, tuple.id, oldCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : IStorageStack> IStorageEventConsumer<T>.addStack(tuple: IStorageTuple<T>, provider: IStorageProvider<T>) {
|
|
||||||
addStack(tuple.stack, tuple.id, provider)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : IStorageStack> IStorageEventConsumer<T>.removeStack(tuple: IStorageTuple<T>) {
|
|
||||||
removeStack(tuple.stack, tuple.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IStorageTuple<T : IStorageStack> {
|
|
||||||
val id: UUID
|
val id: UUID
|
||||||
val stack: T
|
val stack: T
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
* Component which (most time) proxy other components (combine their contents into single view)
|
||||||
*/
|
*/
|
||||||
interface IVirtualStorageComponent<T : IStorageStack> : IStorageComponent<T>, IStorageEventConsumer<T> {
|
interface IVirtualStorageComponent<T : StorageStack<T>> : IStorageComponent<T>, IStorageEventConsumer<T> {
|
||||||
fun add(identity: IStorage<T>)
|
fun add(identity: IStorage<T>)
|
||||||
fun remove(identity: IStorage<T>)
|
fun remove(identity: IStorage<T>)
|
||||||
fun contains(identity: IStorage<T>): Boolean
|
fun contains(identity: IStorage<T>): Boolean
|
||||||
|
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
|
package ru.dbotthepony.mc.otm.storage
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.isPositive
|
import ru.dbotthepony.mc.otm.core.math.isPositive
|
||||||
import ru.dbotthepony.mc.otm.core.math.isZero
|
import ru.dbotthepony.mc.otm.core.math.isZero
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.reflect.full.isSubclassOf
|
|
||||||
|
|
||||||
class RemoteTuple<T : IStorageStack>(
|
class RemoteTuple<T : StorageStack<T>>(
|
||||||
override val stack: T,
|
override var stack: T,
|
||||||
override val id: UUID,
|
override val id: UUID,
|
||||||
val provider: IStorageProvider<T>,
|
val parent: IStorageProvider<T>,
|
||||||
val local: LocalTuple<T>
|
val local: LocalTuple<T>
|
||||||
) : IStorageTuple<T> {
|
) : IStorageTuple<T> {
|
||||||
fun extract(amount: BigInteger, simulate: Boolean): T {
|
fun extract(amount: BigInteger, simulate: Boolean): T {
|
||||||
return provider.extractStack(id, amount, simulate)
|
return parent.extractStack(id, amount, simulate)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@ -29,21 +29,27 @@ class RemoteTuple<T : IStorageStack>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalTuple<T : IStorageStack>(override val stack: T, override val id: UUID, val tuples: ArrayList<RemoteTuple<T>>) : IStorageTuple<T>
|
class LocalTuple<T : StorageStack<T>>(override var stack: T, override val id: UUID) : IStorageTuple<T> {
|
||||||
|
val tuples = ArrayList<RemoteTuple<T>>(1)
|
||||||
|
|
||||||
open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVirtualStorageComponent<T> {
|
fun grow(amount: BigInteger) {
|
||||||
constructor(type: Class<T>) : this(StorageRegistry.get(type))
|
stack = stack.grow(amount)
|
||||||
|
}
|
||||||
|
|
||||||
override val storageType: StorageStackType<T> = type
|
fun shrink(amount: BigInteger) {
|
||||||
|
stack = stack.shrink(amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class VirtualComponent<T : StorageStack<T>>(override val storageType: StorageStack.Type<T>) : IVirtualStorageComponent<T> {
|
||||||
// удаленный UUID -> Кортеж
|
// удаленный UUID -> Кортеж
|
||||||
protected val remoteTuples = HashMap<UUID, RemoteTuple<T>>()
|
protected val remoteTuples = Object2ObjectOpenHashMap<UUID, RemoteTuple<T>>()
|
||||||
|
|
||||||
// локальный UUID -> Локальный кортеж
|
// локальный UUID -> Локальный кортеж
|
||||||
protected val localTuples = HashMap<UUID, LocalTuple<T>>()
|
protected val localTuples = Object2ObjectOpenHashMap<UUID, LocalTuple<T>>()
|
||||||
|
|
||||||
// Стак -> Локальный кортеж стака
|
// Стак -> Локальный кортеж стака
|
||||||
protected val hashedTuples = HashMap<T, LocalTuple<T>>()
|
protected val item2tuple = Object2ObjectOpenCustomHashMap<T, LocalTuple<T>>(StorageStack.Companion)
|
||||||
|
|
||||||
// ArrayList для скорости работы
|
// ArrayList для скорости работы
|
||||||
protected val listeners: MutableSet<IStorageEventConsumer<T>> = ObjectArraySet()
|
protected val listeners: MutableSet<IStorageEventConsumer<T>> = ObjectArraySet()
|
||||||
@ -54,18 +60,17 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
|
|||||||
protected open fun onRemove(identity: IStorage<T>) {}
|
protected open fun onRemove(identity: IStorage<T>) {}
|
||||||
|
|
||||||
override fun add(identity: IStorage<T>) {
|
override fun add(identity: IStorage<T>) {
|
||||||
if (!(identity.storageType::class.isSubclassOf(storageType::class)))
|
require(identity.storageType == storageType) { "Attempt to add component of type ${identity.storageType} to virtual component of type $storageType" }
|
||||||
throw ClassCastException("Reified Generics: ${identity.storageType::class.qualifiedName} can not be cast into ${storageType::class.qualifiedName}")
|
|
||||||
|
|
||||||
if (children.add(identity)) {
|
if (children.add(identity)) {
|
||||||
if (identity is IStorageProvider<T>) {
|
if (identity is IStorageProvider<T>) {
|
||||||
identity.addListenerAuto(this)
|
identity.addListenerAndNotify(this)
|
||||||
} else if (identity is IStorageEventProducer<T>) {
|
} else if (identity is IStorageEventProducer<T>) {
|
||||||
identity.addListener(this)
|
identity.addListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identity is IStorageEventConsumer<T>) {
|
if (identity is IStorageEventConsumer<T>) {
|
||||||
addListenerAuto(identity)
|
addListenerAndNotify(identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identity is IStorageAcceptor<T>) {
|
if (identity is IStorageAcceptor<T>) {
|
||||||
@ -77,18 +82,17 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun remove(identity: IStorage<T>) {
|
override fun remove(identity: IStorage<T>) {
|
||||||
if (!(identity.storageType::class.isSubclassOf(storageType::class)))
|
require(identity.storageType == storageType) { "Attempt to remove component of type ${identity.storageType} from virtual component of type $storageType" }
|
||||||
throw ClassCastException("Reified Generics: ${identity.storageType::class.qualifiedName} can not be cast into ${storageType::class.qualifiedName}")
|
|
||||||
|
|
||||||
if (children.remove(identity)) {
|
if (children.remove(identity)) {
|
||||||
if (identity is IStorageProvider<T>) {
|
if (identity is IStorageProvider<T>) {
|
||||||
identity.removeListenerAuto(this)
|
identity.removeListenerAndNotify(this)
|
||||||
} else if (identity is IStorageEventProducer<T>) {
|
} else if (identity is IStorageEventProducer<T>) {
|
||||||
identity.removeListener(this)
|
identity.removeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identity is IStorageEventConsumer<T>) {
|
if (identity is IStorageEventConsumer<T>) {
|
||||||
removeListenerAuto(identity)
|
removeListenerAndNotify(identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identity is IStorageAcceptor<T>) {
|
if (identity is IStorageAcceptor<T>) {
|
||||||
@ -99,101 +103,85 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun contains(identity: IStorage<T>): Boolean {
|
override fun contains(identity: IStorage<T>) = children.contains(identity)
|
||||||
return children.contains(identity)
|
override fun addListener(listener: IStorageEventConsumer<T>) = listeners.add(listener)
|
||||||
}
|
override fun removeListener(listener: IStorageEventConsumer<T>) = listeners.remove(listener)
|
||||||
|
|
||||||
override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
|
|
||||||
if (listeners.add(listener)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
|
|
||||||
return listeners.remove(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(id: UUID): T {
|
override fun get(id: UUID): T {
|
||||||
return localTuples[id]?.stack ?: this.storageType.empty
|
return localTuples[id]?.stack ?: this.storageType.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
override val stacks: Stream<IStorageTuple<T>> get() {
|
override val stacks: Stream<IStorageTuple<T>> get() {
|
||||||
return ArrayList<IStorageTuple<T>>(hashedTuples.size).also { it.addAll(hashedTuples.values) }.stream()
|
return ArrayList<IStorageTuple<T>>(item2tuple.size).also { it.addAll(item2tuple.values) }.stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(key: T): UUID? {
|
override fun get(key: T): UUID? {
|
||||||
return hashedTuples[key.key()]?.id
|
return item2tuple[key]?.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>) {
|
||||||
override fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>) {
|
|
||||||
check(!remoteTuples.containsKey(id)) { "Already tracking tuple with id $id" }
|
check(!remoteTuples.containsKey(id)) { "Already tracking tuple with id $id" }
|
||||||
|
|
||||||
val key = stack.key()
|
var local = item2tuple[stack]
|
||||||
var local: LocalTuple<T>? = hashedTuples[key]
|
|
||||||
var oldCount = BigInteger.ZERO
|
|
||||||
val added = local == null
|
val added = local == null
|
||||||
|
|
||||||
if (local == null) {
|
if (local == null) {
|
||||||
local = LocalTuple(stack.copy() as T, UUID.randomUUID(), ArrayList<RemoteTuple<T>>(1))
|
local = LocalTuple(stack, UUID.randomUUID())
|
||||||
localTuples[local.id] = local
|
localTuples[local.id] = local
|
||||||
hashedTuples[key] = local
|
item2tuple[stack] = local
|
||||||
} else {
|
} else {
|
||||||
oldCount = local.stack.count
|
local.grow(stack.count)
|
||||||
local.stack.grow(stack.count)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val remote = RemoteTuple(stack.copy() as T, id, provider, local)
|
val remote = RemoteTuple(stack, id, provider, local)
|
||||||
local.tuples.add(remote)
|
local.tuples.add(remote)
|
||||||
remoteTuples[id] = remote
|
remoteTuples[id] = remote
|
||||||
|
|
||||||
if (added) {
|
if (added) {
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.addStack(local.stack, local.id, this)
|
listener.onStackAdded(local.stack, local.id, this)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.changeStack(local.stack, local.id, oldCount)
|
listener.onStackChanged(local.stack, local.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun changeStack(stack: T, id: UUID, oldCount: BigInteger) {
|
override fun onStackChanged(stack: T, id: UUID) {
|
||||||
require(stack.count.isPositive)
|
require(stack.count.isPositive)
|
||||||
|
|
||||||
val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id")
|
val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id")
|
||||||
|
|
||||||
val diff = stack.count - tuple.stack.count
|
val diff = stack.count - tuple.stack.count
|
||||||
tuple.stack.count = stack.count
|
tuple.stack = stack
|
||||||
|
tuple.local.grow(diff)
|
||||||
@Suppress("NAME_SHADOWING") val oldCount = tuple.local.stack.count
|
|
||||||
tuple.local.stack.grow(diff)
|
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.changeStack(tuple.local.stack, tuple.local.id, oldCount)
|
listener.onStackChanged(tuple.local.stack, tuple.local.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeStack(stack: T, id: UUID) {
|
override fun onStackRemoved(id: UUID) {
|
||||||
val tuple = remoteTuples[id] ?: throw IllegalStateException("No such tuple with id $id")
|
val remote = remoteTuples.remove(id) ?: throw IllegalStateException("No such tuple with id $id")
|
||||||
|
val local = remote.local
|
||||||
|
|
||||||
tuple.local.stack.shrink(tuple.stack.count)
|
local.shrink(remote.stack.count)
|
||||||
tuple.local.tuples.remove(tuple)
|
check(local.tuples.remove(remote))
|
||||||
|
|
||||||
remoteTuples.remove(id)
|
val a = local.stack.isEmpty
|
||||||
|
val b = local.tuples.isEmpty()
|
||||||
val a = tuple.local.stack.count <= BigInteger.ZERO
|
|
||||||
val b = tuple.local.tuples.size == 0
|
|
||||||
|
|
||||||
if (a || b) {
|
if (a || b) {
|
||||||
check(a && b) { "View object is empty, but tuple list is not!" }
|
check(a && b) { "View stack is empty, but remote tuple list is not!" }
|
||||||
localTuples.remove(tuple.local.id)
|
localTuples.remove(local.id)
|
||||||
val key = stack.key()
|
checkNotNull(item2tuple.remove(remote.stack)) { "No such stack ${remote.stack}" }
|
||||||
checkNotNull(hashedTuples.remove(key)) { "No such stack $key" }
|
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.removeStack(tuple.local.stack, tuple.local.id)
|
listener.onStackRemoved(local.id)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (listener in listeners) {
|
||||||
|
listener.onStackChanged(local.stack, local.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,14 +200,13 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
|
|||||||
return leftover
|
return leftover
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
|
||||||
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
|
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
|
||||||
if (!amount.isPositive)
|
if (!amount.isPositive)
|
||||||
return this.storageType.empty
|
return this.storageType.empty
|
||||||
|
|
||||||
@Suppress("name_shadowing")
|
@Suppress("name_shadowing")
|
||||||
var amount = amount
|
var amount = amount
|
||||||
val tuple: LocalTuple<T>? = localTuples[id]
|
val tuple = localTuples[id]
|
||||||
|
|
||||||
if (tuple == null || amount.isZero)
|
if (tuple == null || amount.isZero)
|
||||||
return this.storageType.empty
|
return this.storageType.empty
|
||||||
@ -229,180 +216,19 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IVir
|
|||||||
|
|
||||||
val toExtract = tuple.stack.count.coerceAtMost(amount)
|
val toExtract = tuple.stack.count.coerceAtMost(amount)
|
||||||
var extracted = BigInteger.ZERO
|
var extracted = BigInteger.ZERO
|
||||||
val copy = tuple.stack.copy() as T
|
val copy = tuple.stack
|
||||||
|
|
||||||
for (remote_tuple in tuple.tuples.let { Array(it.size) { i -> it[i] } }) {
|
for (remote in ArrayList(tuple.tuples)) {
|
||||||
val extractedStack = remote_tuple.extract(toExtract - extracted, simulate)
|
extracted += remote.extract(toExtract - extracted, simulate).count
|
||||||
extracted += extractedStack.count
|
|
||||||
|
|
||||||
if (extracted >= toExtract)
|
if (extracted >= toExtract)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extracted.isPositive) {
|
if (extracted.isPositive) {
|
||||||
copy.count = extracted
|
return copy.copy(extracted)
|
||||||
return copy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.storageType.empty
|
return this.storageType.empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds energy demand to operations over [parent]
|
|
||||||
*/
|
|
||||||
open class PoweredComponent<T : IStorageStack>(open val parent: IStorageComponent<T>, val energyProvider: () -> IMatteryEnergyStorage) : IStorageComponent<T> {
|
|
||||||
override val storageType: StorageStackType<T>
|
|
||||||
get() = parent.storageType
|
|
||||||
|
|
||||||
private val subscribedThrough = ObjectArraySet<IStorageEventConsumer<T>>()
|
|
||||||
|
|
||||||
final override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
|
|
||||||
if (parent.addListener(listener)) {
|
|
||||||
subscribedThrough.add(listener)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
|
|
||||||
subscribedThrough.remove(listener)
|
|
||||||
return parent.removeListener(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeListeners() {
|
|
||||||
for (child in subscribedThrough) {
|
|
||||||
parent.removeListener(child)
|
|
||||||
}
|
|
||||||
|
|
||||||
subscribedThrough.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(key: T) = parent[key]
|
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
|
||||||
override fun insertStack(stack: T, simulate: Boolean): T {
|
|
||||||
val required = storageType.energyPerOperation * stack.count
|
|
||||||
val energy = energyProvider.invoke()
|
|
||||||
val extracted = energy.extractEnergy(required, true)
|
|
||||||
|
|
||||||
if (extracted.isZero) {
|
|
||||||
return stack.copy() as T
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extracted == required) {
|
|
||||||
val leftover = parent.insertStack(stack, simulate)
|
|
||||||
|
|
||||||
if (leftover.isEmpty) {
|
|
||||||
if (!simulate) {
|
|
||||||
energy.extractEnergy(required, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return leftover
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!simulate) {
|
|
||||||
val requiredNew = storageType.energyPerOperation * (stack.count - leftover.count)
|
|
||||||
energy.extractEnergy(requiredNew, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return leftover
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("name_shadowing")
|
|
||||||
val stack = stack.copy() as T
|
|
||||||
val oldCount = stack.count
|
|
||||||
stack.count = (extracted / storageType.energyPerOperation).whole
|
|
||||||
val diff = oldCount - stack.count
|
|
||||||
val newRequired = storageType.energyPerOperation * stack.count
|
|
||||||
val newExtracted = energy.extractEnergy(newRequired, true)
|
|
||||||
|
|
||||||
if (newExtracted == newRequired) {
|
|
||||||
val leftover = parent.insertStack(stack, simulate)
|
|
||||||
|
|
||||||
if (leftover.isEmpty) {
|
|
||||||
if (!simulate) {
|
|
||||||
energy.extractEnergy(newRequired, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
leftover.count = diff
|
|
||||||
return leftover
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!simulate) {
|
|
||||||
val requiredNew = storageType.energyPerOperation * (stack.count - leftover.count)
|
|
||||||
energy.extractEnergy(requiredNew, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
leftover.count += diff
|
|
||||||
return leftover
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(id: UUID) = parent[id]
|
|
||||||
|
|
||||||
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
|
|
||||||
val required = storageType.energyPerOperation * amount
|
|
||||||
val energy = energyProvider.invoke()
|
|
||||||
val extracted = energy.extractEnergy(required, true)
|
|
||||||
|
|
||||||
if (extracted.isZero) {
|
|
||||||
return storageType.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extracted == required) {
|
|
||||||
val extractedStack = parent.extractStack(id, amount, simulate)
|
|
||||||
|
|
||||||
if (extractedStack.isEmpty) {
|
|
||||||
return extractedStack
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!simulate) {
|
|
||||||
if (extractedStack.count == amount) {
|
|
||||||
energy.extractEnergy(required, false)
|
|
||||||
} else {
|
|
||||||
energy.extractEnergy(storageType.energyPerOperation * extractedStack.count, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return extractedStack
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("name_shadowing")
|
|
||||||
val amount = (required / storageType.energyPerOperation).whole
|
|
||||||
val extractedStack = parent.extractStack(id, amount, simulate)
|
|
||||||
|
|
||||||
if (extractedStack.isEmpty) {
|
|
||||||
return extractedStack
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!simulate) {
|
|
||||||
energy.extractEnergy(storageType.energyPerOperation * extractedStack.count, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return extractedStack
|
|
||||||
}
|
|
||||||
|
|
||||||
override val stacks get() = parent.stacks
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds energy demand to virtual component [parent]
|
|
||||||
*/
|
|
||||||
open class PoweredVirtualComponent<T : IStorageStack>(override val parent: IVirtualStorageComponent<T>, energyProvider: () -> IMatteryEnergyStorage)
|
|
||||||
: PoweredComponent<T>(parent, energyProvider), IVirtualStorageComponent<T> {
|
|
||||||
constructor(parent: IVirtualStorageComponent<T>, energy: IMatteryEnergyStorage) : this(parent, { energy })
|
|
||||||
constructor(parent: Class<T>, energy: IMatteryEnergyStorage) : this(VirtualComponent(parent), { energy })
|
|
||||||
constructor(parent: StorageStackType<T>, energy: IMatteryEnergyStorage) : this(VirtualComponent(parent), { energy })
|
|
||||||
|
|
||||||
override fun addStack(stack: T, id: UUID, provider: IStorageProvider<T>) = parent.addStack(stack, id, provider)
|
|
||||||
override fun changeStack(stack: T, id: UUID, oldCount: BigInteger) = parent.changeStack(stack, id, oldCount)
|
|
||||||
override fun removeStack(stack: T, id: UUID) = parent.removeStack(stack, id)
|
|
||||||
|
|
||||||
override fun add(identity: IStorage<T>) = parent.add(identity)
|
|
||||||
override fun remove(identity: IStorage<T>) = parent.remove(identity)
|
|
||||||
override fun contains(identity: IStorage<T>) = parent.contains(identity)
|
|
||||||
}
|
|
||||||
|
@ -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