Switch Powered furnaces to SlottedContainer, make them reject items which are not used in any recipe (in automation)

This commit is contained in:
DBotThePony 2025-03-06 22:56:26 +07:00
parent fbd34f3414
commit 3e086dcacf
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 81 additions and 23 deletions

View File

@ -29,33 +29,60 @@ 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.config.WorkerBalanceValues import ru.dbotthepony.mc.otm.config.WorkerBalanceValues
import ru.dbotthepony.mc.otm.container.CombinedContainer import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.balance import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.core.collect.maybe import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.SimpleCache
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.util.ItemStackKey
import ru.dbotthepony.mc.otm.core.util.asKey
import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu
import ru.dbotthepony.mc.otm.recipe.MatteryCookingRecipe import ru.dbotthepony.mc.otm.recipe.MatteryCookingRecipe
import ru.dbotthepony.mc.otm.recipe.MicrowaveRecipe import ru.dbotthepony.mc.otm.recipe.MicrowaveRecipe
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.time.Duration
sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : MatteryCookingRecipe>( sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : MatteryCookingRecipe>(
type: BlockEntityType<*>, type: BlockEntityType<*>,
blockPos: BlockPos, blockPos: BlockPos,
blockState: BlockState, blockState: BlockState,
val recipeType: RecipeType<P>,
val secondaryRecipeType: RecipeType<S>?,
val config: WorkerBalanceValues,
maxJobs: Int = 2 maxJobs: Int = 2
) : MatteryWorkerBlockEntity<ItemJob>(type, blockPos, blockState, ItemJob.CODEC, maxJobs) { ) : MatteryWorkerBlockEntity<ItemJob>(type, blockPos, blockState, ItemJob.CODEC, maxJobs) {
abstract val recipeType: RecipeType<P>
abstract val secondaryRecipeType: RecipeType<S>?
abstract val config: WorkerBalanceValues
final override val upgrades = makeUpgrades(2, UpgradeType.BASIC_PROCESSING) final override val upgrades = makeUpgrades(2, UpgradeType.BASIC_PROCESSING)
final override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(config))) final override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(config)))
val inputs = MatteryContainer(this::itemContainerUpdated, maxJobs) private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
val outputs = MatteryContainer(this::itemContainerUpdated, maxJobs) override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
val level = level ?: return true
return acceptableItems.get(itemStack.asKey()) {
val input = SingleRecipeInput(itemStack)
val secondaryRecipeType = secondaryRecipeType
if (secondaryRecipeType != null && level.recipeManager.byType(secondaryRecipeType).any { it.value.matches(input, level) })
return@get true
return@get level.recipeManager.byType(recipeType).any { it.value.matches(input, level) }
}
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
val inputs = SlottedContainer.simple(maxJobs, ::InputSlot, ::itemContainerUpdated)
val outputs = SlottedContainer.simple(maxJobs, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated)
init { init {
addDroppableContainer(inputs) addDroppableContainer(inputs)
@ -76,8 +103,8 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
val experience = ExperienceStorage(config::maxExperienceStored).also(::addNeighbourListener) val experience = ExperienceStorage(config::maxExperienceStored).also(::addNeighbourListener)
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
input = inputs.handler(HandlerFilter.OnlyIn), input = inputs,
output = outputs.handler(HandlerFilter.OnlyOut), output = outputs,
battery = batteryContainer battery = batteryContainer
) )
@ -127,13 +154,13 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
return JobContainer.noItem() return JobContainer.noItem()
val level = level as? ServerLevel ?: return JobContainer.failure() val level = level as? ServerLevel ?: return JobContainer.failure()
val secondaryRecipeType = secondaryRecipeType
if (secondaryRecipeType != null) { if (secondaryRecipeType != null) {
val recipe = level.recipeManager val recipe = level.recipeManager
.byType(secondaryRecipeType) .byType(secondaryRecipeType)
.iterator() .firstOrNull { it.value.matches(SingleRecipeInput(inputs[id]), level) }
.filter { it.value.matches(SingleRecipeInput(inputs[id]), level) } ?.value
.maybe()?.value
if (recipe != null) { if (recipe != null) {
val toProcess = inputs[id].count.coerceAtMost(1 + upgrades.processingItems) val toProcess = inputs[id].count.coerceAtMost(1 + upgrades.processingItems)
@ -172,24 +199,47 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
)) ))
}.orElse(JobContainer.noItem()) }.orElse(JobContainer.noItem())
} }
companion object {
// shared by all furnace instances, so cache should be large enough
private val acceptableItems = SimpleCache<ItemStackKey, Boolean>(16384L, Duration.ofMinutes(1))
}
} }
class PoweredFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmeltingRecipe, MatteryCookingRecipe>( class PoweredFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmeltingRecipe, MatteryCookingRecipe>(MBlockEntities.POWERED_FURNACE, blockPos, blockState) {
MBlockEntities.POWERED_FURNACE, blockPos, blockState, RecipeType.SMELTING, null, MachinesConfig.POWERED_FURNACE) { override val recipeType: RecipeType<SmeltingRecipe>
get() = RecipeType.SMELTING
override val secondaryRecipeType: RecipeType<MatteryCookingRecipe>?
get() = null
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.furnace(containerID, inventory, this) return PoweredFurnaceMenu.furnace(containerID, inventory, this)
} }
} }
class PoweredBlastFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<BlastingRecipe, MatteryCookingRecipe>( class PoweredBlastFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<BlastingRecipe, MatteryCookingRecipe>(MBlockEntities.POWERED_BLAST_FURNACE, blockPos, blockState) {
MBlockEntities.POWERED_BLAST_FURNACE, blockPos, blockState, RecipeType.BLASTING, null, MachinesConfig.POWERED_FURNACE) { override val recipeType: RecipeType<BlastingRecipe>
get() = RecipeType.BLASTING
override val secondaryRecipeType: RecipeType<MatteryCookingRecipe>?
get() = null
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.blasting(containerID, inventory, this) return PoweredFurnaceMenu.blasting(containerID, inventory, this)
} }
} }
class PoweredSmokerBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmokingRecipe, MicrowaveRecipe>( class PoweredSmokerBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmokingRecipe, MicrowaveRecipe>(MBlockEntities.POWERED_SMOKER, blockPos, blockState) {
MBlockEntities.POWERED_SMOKER, blockPos, blockState, RecipeType.SMOKING, MRecipes.MICROWAVE, MachinesConfig.POWERED_FURNACE) { override val recipeType: RecipeType<SmokingRecipe>
get() = RecipeType.SMOKING
override val secondaryRecipeType: RecipeType<MicrowaveRecipe>
get() = MRecipes.MICROWAVE
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.smoking(containerID, inventory, this) return PoweredFurnaceMenu.smoking(containerID, inventory, this)
} }

View File

@ -1,19 +1,24 @@
package ru.dbotthepony.mc.otm.menu.tech package ru.dbotthepony.mc.otm.menu.tech
import com.google.common.collect.ImmutableList
import mezz.jei.api.constants.RecipeTypes import mezz.jei.api.constants.RecipeTypes
import mezz.jei.api.recipe.RecipeType import mezz.jei.api.recipe.RecipeType
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.MenuType import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.crafting.Recipe
import net.minecraft.world.item.crafting.SingleRecipeInput
import ru.dbotthepony.mc.otm.block.entity.tech.AbstractPoweredFurnaceBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.AbstractPoweredFurnaceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.PoweredBlastFurnaceBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.PoweredBlastFurnaceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.PoweredFurnaceBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.PoweredFurnaceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.PoweredSmokerBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.PoweredSmokerBlockEntity
import ru.dbotthepony.mc.otm.compat.jei.MicrowaveRecipeCategory import ru.dbotthepony.mc.otm.compat.jei.MicrowaveRecipeCategory
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.menu.OutputMenuSlot import ru.dbotthepony.mc.otm.menu.OutputMenuSlot
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.UserFilteredMenuSlot
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
@ -23,6 +28,7 @@ import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.TakeExperienceWidget import ru.dbotthepony.mc.otm.menu.widget.TakeExperienceWidget
import ru.dbotthepony.mc.otm.registry.game.MMenus import ru.dbotthepony.mc.otm.registry.game.MMenus
import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.util.function.Supplier import java.util.function.Supplier
class PoweredFurnaceMenu( class PoweredFurnaceMenu(
@ -31,8 +37,10 @@ class PoweredFurnaceMenu(
inventory: Inventory, inventory: Inventory,
tile: AbstractPoweredFurnaceBlockEntity<*, *>? = null tile: AbstractPoweredFurnaceBlockEntity<*, *>? = null
) : AbstractProcessingMachineMenu(type, containerID, inventory, tile) { ) : AbstractProcessingMachineMenu(type, containerID, inventory, tile) {
val inputSlots = makeSlots(tile?.inputs ?: SimpleContainer(2), ::MatteryMenuSlot) // we can't make these slots to reject non-smeltable items
val outputSlots = makeSlots(tile?.outputs ?: SimpleContainer(2)) { c, s -> OutputMenuSlot(c, s) { tile?.experience?.popExperience(player as ServerPlayer) } } // since mods may add obscure recipes/ingredients which never test true on client
val inputSlots = makeSlots(tile?.inputs ?: SlottedContainer.filtered(2), ::UserFilteredMenuSlot)
val outputSlots = makeSlots(tile?.outputs ?: EnhancedContainer(2)) { c, s -> OutputMenuSlot(c, s) { tile?.experience?.popExperience(player as ServerPlayer) } }
val progressGauge = immutableList(2) { ProgressGaugeWidget(this, tile?.jobEventLoops?.get(it)) } val progressGauge = immutableList(2) { ProgressGaugeWidget(this, tile?.jobEventLoops?.get(it)) }
override val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true) override val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true)