Insert and extract priorities in storage system

This commit is contained in:
DBotThePony 2023-08-10 17:36:31 +07:00
parent 5784b3346f
commit b3249cdcd7
Signed by: DBot
GPG Key ID: DCC23B5715498507
14 changed files with 351 additions and 53 deletions

View File

@ -10,33 +10,41 @@ import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.graph.storage.StorageNode
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.storage.*
import ru.dbotthepony.mc.otm.storage.powered.PoweredComponent
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
import ru.dbotthepony.mc.otm.storage.optics.priority
import ru.dbotthepony.mc.otm.storage.optics.powered
class DriveRackBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, blockPos, blockState) {
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.DRIVE_RACK)
val cell = StorageNode(energy)
var insertPriority = 0
set(value) {
field = value
setChangedLight()
}
var extractPriority = 0
set(value) {
field = value
setChangedLight()
}
val container: MatteryContainer = object : MatteryContainer(this::setChanged, 4) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
// generics is going apeshit since storage types are invariant,
// but since we don't know generics of upvalue mattery drive, its storage type
// is defined as out variant
old.getCapability(MatteryCapability.DRIVE).ifPresent {
cell.removeStorageComponent(PoweredComponent(it, ::energy))
old.getCapability(MatteryCapability.DRIVE).ifPresentK {
cell.removeStorageComponent(it.priority(::insertPriority, ::extractPriority).powered(energy))
}
new.getCapability(MatteryCapability.DRIVE).ifPresent {
cell.addStorageComponent(PoweredComponent(it, ::energy))
new.getCapability(MatteryCapability.DRIVE).ifPresentK {
cell.addStorageComponent(it.priority(::insertPriority, ::extractPriority).powered(energy))
}
}
}.also(::addDroppableContainer)
@ -46,6 +54,8 @@ class DriveRackBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
savetable(::container, INVENTORY_KEY)
exposeEnergyGlobally(energy)
exposeGlobally(MatteryCapability.STORAGE_NODE, cell)
savetables.int(::insertPriority)
savetables.int(::extractPriority)
}
override fun setLevel(level: Level) {

View File

@ -81,9 +81,23 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
private var component: ItemHandlerComponent? = null
var insertPriority = 0
set(value) {
field = value
setChangedLight()
}
var extractPriority = 0
set(value) {
field = value
setChangedLight()
}
init {
exposeEnergyGlobally(energy)
savetable(::energy, ENERGY_KEY)
savetables.int(::insertPriority)
savetables.int(::extractPriority)
exposeGlobally(MatteryCapability.STORAGE_NODE, cell) { it != RelativeSide.FRONT }
side(RelativeSide.FRONT).track(ForgeCapabilities.ITEM_HANDLER).addListener {
@ -213,6 +227,11 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
private val listeners = ArrayList<IStorageEventConsumer<ItemStorageStack>>()
override val insertPriority: Int
get() = this@StorageBusBlockEntity.insertPriority
override val extractPriority: Int
get() = this@StorageBusBlockEntity.extractPriority
override fun addListener(listener: IStorageEventConsumer<ItemStorageStack>): Boolean {
if (!listeners.contains(listener)) {
listeners.add(listener)

View File

@ -7,6 +7,7 @@ import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.CheckBoxLabelInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.input.TextInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilterSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel
@ -31,6 +32,28 @@ class StorageBusScreen(menu: StorageBusMenu, inventory: Inventory, title: Compon
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig)
/*object : TextInputPanel<StorageBusScreen>(this@StorageBusScreen, frame) {
init {
allowNumbersAndSign()
dock = Dock.BOTTOM
}
override fun tickInner() {
super.tickInner()
if (!hasHierarchicalFocus()) {
text = menu.insertPriority.value.toString()
}
}
override fun onTextChanged(new: String, old: String) {
if (hasHierarchicalFocus()) {
val i = new.toIntOrNull()
if (i != null) menu.insertPriority.accept(i)
}
}
}*/
return frame
}
}

View File

@ -0,0 +1,31 @@
package ru.dbotthepony.mc.otm.menu.input
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import java.util.function.IntSupplier
import kotlin.reflect.KMutableProperty0
class IntInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean = false) : AbstractPlayerInputWithFeedback<Int>() {
override val input = menu.intInput(allowSpectators) { consumer?.invoke(it) }
override val field = menu.mSynchronizer.computedInt(IntSupplier { supplier?.invoke() ?: 0 })
constructor(menu: MatteryMenu, allowSpectators: Boolean, state: KMutableProperty0<Int>?) : this(menu, allowSpectators) {
if (state != null)
with(state)
}
constructor(menu: MatteryMenu, allowSpectators: Boolean, state: GetterSetter<Int>?) : this(menu, allowSpectators) {
if (state != null)
with(state)
}
constructor(menu: MatteryMenu, state: KMutableProperty0<Int>?) : this(menu) {
if (state != null)
with(state)
}
constructor(menu: MatteryMenu, state: GetterSetter<Int>?) : this(menu) {
if (state != null)
with(state)
}
}

View File

@ -1,30 +1,34 @@
package ru.dbotthepony.mc.otm.menu.storage
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.Slot
import ru.dbotthepony.mc.otm.block.entity.storage.StorageBusBlockEntity
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.IntInputWithFeedback
import ru.dbotthepony.mc.otm.registry.MMenus
class StorageBusMenu @JvmOverloads constructor(
p_38852_: Int,
containerId: Int,
inventory: Inventory,
tile: StorageBusBlockEntity? = null
) : MatteryPoweredMenu(
MMenus.STORAGE_BUS, p_38852_, inventory, tile
) {
) : MatteryPoweredMenu(MMenus.STORAGE_BUS, containerId, inventory, tile) {
val busFilterSlots: List<ItemFilterNetworkSlot>
val busFilterState: BooleanInputWithFeedback
val insertPriority: IntInputWithFeedback
val extractPriority: IntInputWithFeedback
init {
if (tile != null) {
busFilterSlots = addFilterSlots(tile.filter)
busFilterState = BooleanInputWithFeedback(this, tile.filter::isWhitelist)
insertPriority = IntInputWithFeedback(this, tile::insertPriority)
extractPriority = IntInputWithFeedback(this, tile::extractPriority)
} else {
busFilterSlots = addFilterSlots(StorageBusBlockEntity.MAX_FILTERS)
busFilterState = BooleanInputWithFeedback(this)
insertPriority = IntInputWithFeedback(this)
extractPriority = IntInputWithFeedback(this)
}
addStorageSlot(batterySlot)

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.storage
import java.math.BigInteger
import java.util.*
import java.util.stream.Stream
import kotlin.Comparator
/**
* Storage system root, along IStorageStack interface
@ -70,6 +71,14 @@ interface IStorageAcceptor<T : StorageStack<T>> : IStorage<T> {
* @return leftover, might equal to [stack] if no items were inserted
*/
fun insertStack(stack: T, simulate: Boolean): T
val insertPriority: Int get() = 0
companion object : Comparator<IStorageAcceptor<*>> {
override fun compare(o1: IStorageAcceptor<*>, o2: IStorageAcceptor<*>): Int {
return o2.insertPriority.compareTo(o1.insertPriority)
}
}
}
/**
@ -125,12 +134,43 @@ interface IStorageProvider<T : StorageStack<T>> : IStorageEventProducer<T> {
fun extractStack(id: T, amount: BigInteger, simulate: Boolean): T {
return extractStack(get(id) ?: return storageType.empty, amount, simulate)
}
val extractPriority: Int get() = 0
companion object : Comparator<IStorageProvider<*>> {
override fun compare(o1: IStorageProvider<*>, o2: IStorageProvider<*>): Int {
return o2.extractPriority.compareTo(o1.extractPriority)
}
}
}
/**
* Storage component, which basically implement Input and Output
*/
interface IStorageComponent<T : StorageStack<T>> : IStorageProvider<T>, IStorageAcceptor<T>
interface IStorageComponent<T : StorageStack<T>> : IStorageProvider<T>, IStorageAcceptor<T> {
class C<T : StorageStack<T>>(private val provider: IStorageProvider<T>, private val acceptor: IStorageAcceptor<T>) : IStorageComponent<T>, IStorageProvider<T> by provider, IStorageAcceptor<T> by acceptor {
constructor(acceptor: IStorageAcceptor<T>, provider: IStorageProvider<T>) : this(provider, acceptor)
init {
require(provider.storageType == acceptor.storageType) { "Provider and Accept have different storage types: ${provider.storageType} != ${acceptor.storageType}" }
}
override val storageType: StorageStack.Type<T>
get() = provider.storageType
override fun equals(other: Any?): Boolean {
return other is C<*> && provider == other.provider && acceptor == other.acceptor
}
override fun hashCode(): Int {
return (provider.hashCode() * 31) xor acceptor.hashCode()
}
override fun toString(): String {
return "IStorageComponent[$provider; $acceptor]"
}
}
}
interface IStorageTuple<T : StorageStack<T>> {
val id: UUID

View File

@ -1,7 +1,10 @@
package ru.dbotthepony.mc.otm.storage
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
import ru.dbotthepony.mc.otm.core.math.isPositive
@ -16,7 +19,11 @@ class RemoteTuple<T : StorageStack<T>>(
override val id: UUID,
val parent: IStorageProvider<T>,
val local: LocalTuple<T>
) : IStorageTuple<T> {
) : IStorageTuple<T>, Comparable<RemoteTuple<T>> {
override fun compareTo(other: RemoteTuple<T>): Int {
return parent.extractPriority.compareTo(other.parent.extractPriority)
}
fun extract(amount: BigInteger, simulate: Boolean): T {
return parent.extractStack(id, amount, simulate)
}
@ -181,6 +188,8 @@ class VirtualComponent<T : StorageStack<T>>(override val storageType: StorageSta
override fun insertStack(stack: T, simulate: Boolean): T {
var leftover = stack
val consumers = ObjectArrayList(consumers)
consumers.sortWith(IStorageAcceptor.Companion)
for (consumer in consumers) {
leftover = consumer.insertStack(leftover, simulate)
@ -210,8 +219,10 @@ class VirtualComponent<T : StorageStack<T>>(override val storageType: StorageSta
val toExtract = tuple.stack.count.coerceAtMost(amount)
var extracted = BigInteger.ZERO
val copy = tuple.stack
val tuples = ArrayList(tuple.tuples)
tuples.sort()
for (remote in ArrayList(tuple.tuples)) {
for (remote in tuples) {
extracted += remote.extract(toExtract - extracted, simulate).count
if (extracted >= toExtract)

View File

@ -0,0 +1,31 @@
package ru.dbotthepony.mc.otm.storage.optics
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.StorageStack
import java.util.function.IntSupplier
class ExtractPriority<S : StorageStack<S>>(private val parent: IStorageProvider<S>, private val priority: IntSupplier) : IStorageProvider<S> by parent {
override val extractPriority: Int
get() = priority.asInt
override fun addListener(listener: IStorageEventConsumer<S>): Boolean {
return parent.addListener(ListenerProxy(listener, this))
}
override fun removeListener(listener: IStorageEventConsumer<S>): Boolean {
return parent.removeListener(ListenerProxy(listener, this))
}
override fun equals(other: Any?): Boolean {
return other is ExtractPriority<*> && parent == other.parent
}
override fun hashCode(): Int {
return parent.hashCode()
}
override fun toString(): String {
return "ExtractPriority[$parent with $priority]"
}
}

View File

@ -0,0 +1,22 @@
package ru.dbotthepony.mc.otm.storage.optics
import ru.dbotthepony.mc.otm.storage.IStorageAcceptor
import ru.dbotthepony.mc.otm.storage.StorageStack
import java.util.function.IntSupplier
class InsertPriority<S : StorageStack<S>>(private val parent: IStorageAcceptor<S>, private val priority: IntSupplier) : IStorageAcceptor<S> by parent {
override val insertPriority: Int
get() = priority.asInt
override fun equals(other: Any?): Boolean {
return other is InsertPriority<*> && parent == other.parent
}
override fun hashCode(): Int {
return parent.hashCode()
}
override fun toString(): String {
return "InsertPriority[$parent with $priority]"
}
}

View File

@ -0,0 +1,29 @@
package ru.dbotthepony.mc.otm.storage.optics
import it.unimi.dsi.fastutil.HashCommon
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.StorageStack
import java.util.*
class ListenerProxy<T : StorageStack<T>>(private val consumer: IStorageEventConsumer<T>, private val provider: IStorageProvider<T>) : IStorageEventConsumer<T> by consumer {
init {
require(consumer.storageType == provider.storageType) { "Consumer storage type does not match provider's: ${consumer.storageType} != ${provider.storageType}" }
}
override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>) {
consumer.onStackAdded(stack, id, this.provider)
}
override fun toString(): String {
return "ListenerProxy[$consumer from $provider]"
}
override fun equals(other: Any?): Boolean {
return other is ListenerProxy<*> && consumer == other.consumer && provider == other.provider
}
override fun hashCode(): Int {
return HashCommon.mix(consumer.hashCode() * 31 + provider.hashCode())
}
}

View File

@ -0,0 +1,28 @@
package ru.dbotthepony.mc.otm.storage.optics
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.IntSupplier
class ModifyPriority<S : StorageStack<S>>(
private val parent: IStorageComponent<S>,
insertPriority: IntSupplier,
extractPriority: IntSupplier
) : IStorageComponent<S>, IStorageAcceptor<S> by InsertPriority(parent, insertPriority), IStorageProvider<S> by ExtractPriority(parent, extractPriority) {
override fun equals(other: Any?): Boolean {
return other is ModifyPriority<*> && parent == other.parent
}
override fun hashCode(): Int {
return parent.hashCode()
}
override fun toString(): String {
return "ModifyPriority[$parent with $insertPriority and $extractPriority]"
}
override val storageType: StorageStack.Type<S>
get() = parent.storageType
}

View File

@ -0,0 +1,78 @@
package ru.dbotthepony.mc.otm.storage.optics
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.IVirtualStorageComponent
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.storage.powered.PoweredComponent
import ru.dbotthepony.mc.otm.storage.powered.PoweredStorageAcceptor
import ru.dbotthepony.mc.otm.storage.powered.PoweredStorageProvider
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
import java.util.function.IntSupplier
import java.util.function.Supplier
fun <S : StorageStack<S>> IStorageAcceptor<S>.insertPriority(priority: IntSupplier): IStorageAcceptor<S> {
return InsertPriority(this, priority)
}
fun <S : StorageStack<S>> IStorageAcceptor<S>.insertPriority(priority: Int): IStorageAcceptor<S> {
return InsertPriority(this) { priority }
}
fun <S : StorageStack<S>> IStorageProvider<S>.extractPriority(priority: IntSupplier): IStorageProvider<S> {
return ExtractPriority(this, priority)
}
fun <S : StorageStack<S>> IStorageProvider<S>.extractPriority(priority: Int): IStorageProvider<S> {
return ExtractPriority(this) { priority }
}
fun <S : StorageStack<S>> IStorageComponent<S>.priority(priority: IntSupplier): IStorageComponent<S> {
return ModifyPriority(this, priority, priority)
}
fun <S : StorageStack<S>> IStorageComponent<S>.priority(priority: Int): IStorageComponent<S> {
return ModifyPriority(this, { priority }, { priority })
}
fun <S : StorageStack<S>> IStorageComponent<S>.priority(insertPriority: IntSupplier, extractPriority: IntSupplier): IStorageComponent<S> {
return ModifyPriority(this, insertPriority, extractPriority)
}
fun <S : StorageStack<S>> IStorageComponent<S>.priority(insertPriority: Int, extractPriority: Int): IStorageComponent<S> {
return ModifyPriority(this, { insertPriority }, { extractPriority })
}
fun <S : StorageStack<S>> IStorageAcceptor<S>.powered(energy: Supplier<IMatteryEnergyStorage>): IStorageAcceptor<S> {
return PoweredStorageAcceptor(this, energy)
}
fun <S : StorageStack<S>> IStorageAcceptor<S>.powered(energy: IMatteryEnergyStorage): IStorageAcceptor<S> {
return PoweredStorageAcceptor(this) { energy }
}
fun <S : StorageStack<S>> IStorageProvider<S>.powered(energy: Supplier<IMatteryEnergyStorage>): IStorageProvider<S> {
return PoweredStorageProvider(this, energy)
}
fun <S : StorageStack<S>> IStorageProvider<S>.powered(energy: IMatteryEnergyStorage): IStorageProvider<S> {
return PoweredStorageProvider(this) { energy }
}
fun <S : StorageStack<S>> IStorageComponent<S>.powered(energy: Supplier<IMatteryEnergyStorage>): IStorageComponent<S> {
return PoweredComponent(this, energy)
}
fun <S : StorageStack<S>> IStorageComponent<S>.powered(energy: IMatteryEnergyStorage): IStorageComponent<S> {
return PoweredComponent(this) { energy }
}
fun <S : StorageStack<S>> IVirtualStorageComponent<S>.powered(energy: Supplier<IMatteryEnergyStorage>): IVirtualStorageComponent<S> {
return PoweredVirtualComponent(this, energy)
}
fun <S : StorageStack<S>> IVirtualStorageComponent<S>.powered(energy: IMatteryEnergyStorage): IVirtualStorageComponent<S> {
return PoweredVirtualComponent(this) { energy }
}

View File

@ -11,6 +11,9 @@ 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) {
constructor(provider: IStorageProvider<T>, acceptor: IStorageAcceptor<T>, energy: Supplier<IMatteryEnergyStorage>) : this(IStorageComponent.C(provider, acceptor), energy)
constructor(acceptor: IStorageAcceptor<T>, provider: IStorageProvider<T>, energy: Supplier<IMatteryEnergyStorage>) : this(IStorageComponent.C(provider, acceptor), energy)
override val storageType: StorageStack.Type<T>
get() = parent.storageType

View File

@ -1,12 +1,10 @@
package ru.dbotthepony.mc.otm.storage.powered
import it.unimi.dsi.fastutil.HashCommon.mix
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
import ru.dbotthepony.mc.otm.storage.IStorageProvider
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.storage.optics.ListenerProxy
import java.math.BigInteger
import java.util.*
import java.util.function.Supplier
@ -24,41 +22,12 @@ class PoweredStorageProvider<T : StorageStack<T>>(val parent: IStorageProvider<T
return "PoweredStorageProvider[$parent]"
}
private class Proxy<T : StorageStack<T>>(private val parent: IStorageEventConsumer<T>, private val powered: PoweredStorageProvider<T>) : IStorageEventConsumer<T> {
override val storageType: StorageStack.Type<T>
get() = parent.storageType
override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>) {
parent.onStackAdded(stack, id, powered)
}
override fun onStackChanged(stack: T, id: UUID) {
parent.onStackChanged(stack, id)
}
override fun onStackRemoved(id: UUID) {
parent.onStackRemoved(id)
}
override fun toString(): String {
return "PoweredStorageProvider.Proxy[$parent with $powered]"
}
override fun equals(other: Any?): Boolean {
return other is Proxy<*> && parent == other.parent && powered == other.powered
}
override fun hashCode(): Int {
return mix(parent.hashCode() * 31 + powered.hashCode())
}
}
override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
return parent.addListener(Proxy(listener, this))
return parent.addListener(ListenerProxy(listener, this))
}
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
return parent.removeListener(Proxy(listener, this))
return parent.removeListener(ListenerProxy(listener, this))
}
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {