Importer and exporter menus and filters

This commit is contained in:
DBotThePony 2022-05-14 22:52:29 +07:00
parent 25916f920d
commit cce41508b8
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 223 additions and 18 deletions

View File

@ -2,6 +2,8 @@ package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
@ -15,21 +17,24 @@ import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.CapabilityItemHandler import net.minecraftforge.items.CapabilityItemHandler
import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.plus import ru.dbotthepony.mc.otm.core.plus
import ru.dbotthepony.mc.otm.core.toImpreciseFraction import ru.dbotthepony.mc.otm.core.toImpreciseFraction
import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
import ru.dbotthepony.mc.otm.orThrow import ru.dbotthepony.mc.otm.menu.StorageExporterMenu
import ru.dbotthepony.mc.otm.menu.StorageImporterMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities 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.IStorageTuple import ru.dbotthepony.mc.otm.storage.*
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE import java.util.*
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import java.util.stream.Stream
import ru.dbotthepony.mc.otm.unaryMinus import kotlin.collections.HashMap
import kotlin.collections.HashSet
abstract class AbstractStorageImportExport<T>( abstract class AbstractStorageImportExport<T>(
blockType: BlockEntityType<*>, blockType: BlockEntityType<*>,
@ -99,8 +104,12 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
override val defaultDisplayName: Component override val defaultDisplayName: Component
get() = MACHINE_NAME get() = MACHINE_NAME
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? { val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
return null
}
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return StorageImporterMenu(containerID, inventory, this)
} }
private var lastSlot = 0 private var lastSlot = 0
@ -111,6 +120,18 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
override val targetCapability: Capability<IItemHandler> override val targetCapability: Capability<IItemHandler>
get() = CapabilityItemHandler.ITEM_HANDLER_CAPABILITY get() = CapabilityItemHandler.ITEM_HANDLER_CAPABILITY
override fun saveAdditional(nbt: CompoundTag) {
super.saveAdditional(nbt)
nbt["filter"] = filter.serializeNBT()
}
override fun load(nbt: CompoundTag) {
super.load(nbt)
nbt.ifHas("filter", ListTag::class.java, filter::deserializeNBT)
}
fun tick() { fun tick() {
batteryChargeLoop() batteryChargeLoop()
cell.tickEnergyDemanding() cell.tickEnergyDemanding()
@ -130,7 +151,7 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
val maxMove = energy.extractStepInner(ITEM_STORAGE.energyPerOperation, MAX_MOVE_PER_OPERATION, true) val maxMove = energy.extractStepInner(ITEM_STORAGE.energyPerOperation, MAX_MOVE_PER_OPERATION, true)
var extracted = resolved.extractItem(lastSlot, maxMove, true) var extracted = resolved.extractItem(lastSlot, maxMove, true)
if (extracted.isEmpty) { if (extracted.isEmpty || !filter.match(extracted)) {
lastSlot++ lastSlot++
} else { } else {
val leftOver = items.insertStack(ItemStackWrapper(extracted), true) val leftOver = items.insertStack(ItemStackWrapper(extracted), true)
@ -154,17 +175,48 @@ class StorageImporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
const val MAX_MOVE_PER_OPERATION = 4 const val MAX_MOVE_PER_OPERATION = 4
private val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_IMPORTER}") private val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_IMPORTER}")
private const val INTERVAL = 5 private const val INTERVAL = 5
const val MAX_FILTERS = 6 * 3
} }
} }
class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState) { class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
AbstractStorageImportExport<IItemHandler>(MBlockEntities.STORAGE_EXPORTER, blockPos, blockState),
IStorageListener<ItemStackWrapper> {
override val defaultDisplayName: Component override val defaultDisplayName: Component
get() = MACHINE_NAME get() = MACHINE_NAME
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return null return StorageExporterMenu(containerID, inventory, this)
} }
private val relevantTuples = HashSet<UUID>()
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView<ItemStackWrapper>) {
if (!filter.match(stack.item)) {
return
}
relevantTuples.add(id)
}
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: ImpreciseFraction) {
// no-op
}
override fun removeStack(stack: ItemStackWrapper, id: UUID) {
relevantTuples.remove(id)
}
val filter = ItemFilter(MAX_FILTERS) { _, _, _ ->
relevantTuples.clear()
val component = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: return@ItemFilter
for (tuple in component.getStacks()) {
addStack(tuple, component)
}
}.also { it.isWhitelist = true }
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 private val enoughEnergy get() = energy.batteryLevel >= ITEM_STORAGE.energyPerOperation
@ -172,8 +224,23 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
override val targetCapability: Capability<IItemHandler> override val targetCapability: Capability<IItemHandler>
get() = CapabilityItemHandler.ITEM_HANDLER_CAPABILITY get() = CapabilityItemHandler.ITEM_HANDLER_CAPABILITY
private val exportStacks: List<IStorageTuple<ItemStackWrapper>> private val exportStacks: Stream<Pair<UUID, ItemStackWrapper>>
get() = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE)?.getStacks() ?: emptyList() get() {
val view = cell.storageGraph?.getVirtualComponent(ITEM_STORAGE) ?: return Stream.empty()
return relevantTuples.stream().map { it to view.getStack(it) }
}
override fun saveAdditional(nbt: CompoundTag) {
super.saveAdditional(nbt)
nbt["filter"] = filter.serializeNBT()
}
override fun load(nbt: CompoundTag) {
super.load(nbt)
nbt.ifHas("filter", ListTag::class.java, filter::deserializeNBT)
}
fun tick() { fun tick() {
batteryChargeLoop() batteryChargeLoop()
@ -194,7 +261,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
var hit = false var hit = false
for (stack in exportStacks) { for (stack in exportStacks) {
val exportAmountA = items.extractStack(stack.id, stack.stack.count.toInt().coerceAtMost(MAX_MOVE_PER_OPERATION).toImpreciseFraction(), true).count.toInt() val exportAmountA = items.extractStack(stack.first, stack.second.count.toInt().coerceAtMost(MAX_MOVE_PER_OPERATION).toImpreciseFraction(), true).count.toInt()
if (exportAmountA == 0) { if (exportAmountA == 0) {
continue continue
@ -206,12 +273,12 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
break break
} }
val leftover = resolved.insertItem(lastSlot, stack.stack.stack.also { it.count = exportAmount }, true) val leftover = resolved.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, true)
if (leftover.count != exportAmount) { if (leftover.count != exportAmount) {
hit = true hit = true
exportAmount = items.extractStack(stack.id, ImpreciseFraction(exportAmount - leftover.count), false).count.toInt() exportAmount = items.extractStack(stack.first, ImpreciseFraction(exportAmount - leftover.count), false).count.toInt()
resolved.insertItem(lastSlot, stack.stack.stack.also { it.count = exportAmount }, false) resolved.insertItem(lastSlot, stack.second.stack.also { it.count = exportAmount }, false)
energy.extractStepInner(ITEM_STORAGE.energyPerOperation, exportAmount, false) energy.extractStepInner(ITEM_STORAGE.energyPerOperation, exportAmount, false)
break break
} }
@ -231,5 +298,6 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) : A
const val MAX_MOVE_PER_OPERATION = 4 const val MAX_MOVE_PER_OPERATION = 4
private val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_EXPORTER}") private val MACHINE_NAME = TranslatableComponent("block.${OverdriveThatMatters.MOD_ID}.${MNames.STORAGE_EXPORTER}")
private const val INTERVAL = 5 private const val INTERVAL = 5
const val MAX_FILTERS = 6 * 3
} }
} }

View File

@ -0,0 +1,32 @@
package ru.dbotthepony.mc.otm.client.screen
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.client.screen.panels.CheckBoxLabelInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.FilterSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.menu.StorageExporterMenu
class StorageExporterScreen(menu: StorageExporterMenu, inventory: Inventory, title: Component) :
MatteryScreen<StorageExporterMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel {
val frame = super.makeMainFrame()!!
PowerGaugePanel(this, frame, menu.powerWidget, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
SlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
for (row in 0 .. 2) {
for (column in 0 .. 5) {
FilterSlotPanel(this, frame, menu.busFilterSlots[row + column * 3], 55f + 18f * column, 17f + 18f * row)
}
}
CheckBoxLabelInputPanel(this, frame, menu.busFilterState, TranslatableComponent("otm.gui.filter.is_whitelist"), 59f, 78f, width = 170f)
return frame
}
}

View File

@ -0,0 +1,32 @@
package ru.dbotthepony.mc.otm.client.screen
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.client.screen.panels.CheckBoxLabelInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.FilterSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.menu.StorageImporterMenu
class StorageImporterScreen(menu: StorageImporterMenu, inventory: Inventory, title: Component) :
MatteryScreen<StorageImporterMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel {
val frame = super.makeMainFrame()!!
PowerGaugePanel(this, frame, menu.powerWidget, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
SlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
for (row in 0 .. 2) {
for (column in 0 .. 5) {
FilterSlotPanel(this, frame, menu.busFilterSlots[row + column * 3], 55f + 18f * column, 17f + 18f * row)
}
}
CheckBoxLabelInputPanel(this, frame, menu.busFilterState, TranslatableComponent("otm.gui.filter.is_whitelist"), 59f, 78f, width = 170f)
return frame
}
}

View File

@ -0,0 +1,34 @@
package ru.dbotthepony.mc.otm.menu
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.block.entity.StorageBusBlockEntity
import ru.dbotthepony.mc.otm.block.entity.StorageExporterBlockEntity
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
import ru.dbotthepony.mc.otm.menu.widget.BooleanPlayerInputWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class StorageExporterMenu @JvmOverloads constructor(
p_38852_: Int,
inventory: Inventory,
tile: StorageExporterBlockEntity? = null
) : MatteryPoweredMenu(
MMenus.STORAGE_EXPORTER, p_38852_, inventory, tile
) {
val busFilterSlots: List<ItemFilterNetworkSlot>
val busFilterState: BooleanPlayerInputWidget
init {
if (tile != null) {
busFilterSlots = addFilterSlots(tile.filter)
busFilterState = BooleanPlayerInputWidget(this, tile.filter::isWhitelist)
} else {
busFilterSlots = addFilterSlots(StorageExporterBlockEntity.MAX_FILTERS)
busFilterState = BooleanPlayerInputWidget(this).asClient()
}
addInventorySlots()
}
override fun getWorkingSlotStart() = 0
override fun getWorkingSlotEnd() = 1
}

View File

@ -0,0 +1,35 @@
package ru.dbotthepony.mc.otm.menu
import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.block.entity.StorageBusBlockEntity
import ru.dbotthepony.mc.otm.block.entity.StorageExporterBlockEntity
import ru.dbotthepony.mc.otm.block.entity.StorageImporterBlockEntity
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
import ru.dbotthepony.mc.otm.menu.widget.BooleanPlayerInputWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class StorageImporterMenu @JvmOverloads constructor(
p_38852_: Int,
inventory: Inventory,
tile: StorageImporterBlockEntity? = null
) : MatteryPoweredMenu(
MMenus.STORAGE_IMPORTER, p_38852_, inventory, tile
) {
val busFilterSlots: List<ItemFilterNetworkSlot>
val busFilterState: BooleanPlayerInputWidget
init {
if (tile != null) {
busFilterSlots = addFilterSlots(tile.filter)
busFilterState = BooleanPlayerInputWidget(this, tile.filter::isWhitelist)
} else {
busFilterSlots = addFilterSlots(StorageImporterBlockEntity.MAX_FILTERS)
busFilterState = BooleanPlayerInputWidget(this).asClient()
}
addInventorySlots()
}
override fun getWorkingSlotStart() = 0
override fun getWorkingSlotEnd() = 1
}

View File

@ -32,6 +32,8 @@ object MMenus {
val MATTER_RECYCLER: MenuType<*> by registry.register(MNames.MATTER_RECYCLER) { MenuType(::MatterRecyclerMenu) } val MATTER_RECYCLER: MenuType<*> by registry.register(MNames.MATTER_RECYCLER) { MenuType(::MatterRecyclerMenu) }
val STORAGE_BUS: MenuType<*> by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu) } val STORAGE_BUS: MenuType<*> by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu) }
val STORAGE_EXPORTER: MenuType<*> by registry.register(MNames.STORAGE_EXPORTER) { MenuType(::StorageExporterMenu) }
val STORAGE_IMPORTER: MenuType<*> by registry.register(MNames.STORAGE_IMPORTER) { MenuType(::StorageImporterMenu) }
internal fun register() { internal fun register() {
registry.register(FMLJavaModLoadingContext.get().modEventBus) registry.register(FMLJavaModLoadingContext.get().modEventBus)
@ -59,6 +61,8 @@ object MMenus {
MenuScreens.register(PLATE_PRESS as MenuType<PlatePressMenu>, ::PlatePressScreen) MenuScreens.register(PLATE_PRESS as MenuType<PlatePressMenu>, ::PlatePressScreen)
MenuScreens.register(MATTER_RECYCLER as MenuType<MatterRecyclerMenu>, ::MatterRecyclerScreen) MenuScreens.register(MATTER_RECYCLER as MenuType<MatterRecyclerMenu>, ::MatterRecyclerScreen)
MenuScreens.register(STORAGE_BUS as MenuType<StorageBusMenu>, ::StorageBusScreen) MenuScreens.register(STORAGE_BUS as MenuType<StorageBusMenu>, ::StorageBusScreen)
MenuScreens.register(STORAGE_EXPORTER as MenuType<StorageExporterMenu>, ::StorageExporterScreen)
MenuScreens.register(STORAGE_IMPORTER as MenuType<StorageImporterMenu>, ::StorageImporterScreen)
} }
} }
} }