From 3910e38addcad4682dc6683ee539502bba981b57 Mon Sep 17 00:00:00 2001
From: DBotThePony <dbotthepony@yandex.ru>
Date: Wed, 5 Mar 2025 13:17:57 +0700
Subject: [PATCH] Move Matter Entangler to Slotted container

---
 .../matter/MatterEntanglerBlockEntity.kt      | 66 +++++++++----------
 .../container/slotted/AutomationFilters.kt    | 20 +++++-
 .../mc/otm/menu/matter/MatterEntanglerMenu.kt | 33 +++-------
 3 files changed, 61 insertions(+), 58 deletions(-)

diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterEntanglerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterEntanglerBlockEntity.kt
index a09006236..e66525d66 100644
--- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterEntanglerBlockEntity.kt
+++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterEntanglerBlockEntity.kt
@@ -24,9 +24,10 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
 import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
 import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
 import ru.dbotthepony.mc.otm.config.MachinesConfig
-import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer
-import ru.dbotthepony.mc.otm.container.HandlerFilter
-import ru.dbotthepony.mc.otm.container.MatteryContainer
+import ru.dbotthepony.mc.otm.container.IEnhancedCraftingContainer
+import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
+import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
+import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
 import ru.dbotthepony.mc.otm.core.math.Decimal
 import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
 import ru.dbotthepony.mc.otm.data.codec.minRange
@@ -35,8 +36,7 @@ import ru.dbotthepony.mc.otm.menu.matter.MatterEntanglerMenu
 import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
 import ru.dbotthepony.mc.otm.registry.game.MRecipes
 
-class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity<MatterEntanglerBlockEntity.Job>(
-	MBlockEntities.MATTER_ENTANGLER, blockPos, blockState, Job.CODEC) {
+class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity<MatterEntanglerBlockEntity.Job>(MBlockEntities.MATTER_ENTANGLER, blockPos, blockState, Job.CODEC) {
 	class Job(itemStack: ItemStack, val matter: Decimal, ticks: Double, experience: Float) : ItemJob(itemStack, ticks, MachinesConfig.MATTER_ENTANGLER.energyConsumption, experience = experience) {
 		val matterPerTick = matter / ticks
 
@@ -60,36 +60,36 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
 	val experience = ExperienceStorage(MachinesConfig.MATTER_ENTANGLER::maxExperienceStored).also(::addNeighbourListener)
 	val energyConfig = ConfigurableEnergy(energy)
 
-	val inputs = object : MatteryCraftingContainer(::itemContainerUpdated, 3, 3) {
-		override fun getMaxStackSize(): Int {
-			return 1
+	private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
+		override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
+			if (!super.canAutomationPlaceItem(itemStack))
+				return false
+
+			val level = level ?: return false
+			val list = container.toList()
+			list[slot] = itemStack
+			val shadow = CraftingInput.ofPositioned(3, 3, list)
+
+			return level
+				.recipeManager
+				.byType(MRecipes.MATTER_ENTANGLER)
+				.any { it.value.preemptivelyMatches(shadow, level, 3, 3) }
 		}
+
+		override fun canAutomationTakeItem(desired: Int): Boolean {
+			return false
+		}
+
+		override val maxStackSize: Int
+			get() = 1
 	}
 
-	val output = object : MatteryContainer(::itemContainerUpdated, 1) {
-		override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
-			return Int.MAX_VALUE
-		}
-	}
+	val inputs = IEnhancedCraftingContainer.Wrapper(SlottedContainer.simple(3 * 3, ::InputSlot, ::setChanged), 3, 3)
+	val output = SlottedContainer.simple(1, AutomationFilters.ONLY_OUT.unlimitedSimpleProvider, ::markDirtyFast)
 
 	val itemConfig = ConfigurableItemHandler(
-		input = inputs.handler(object : HandlerFilter {
-			override fun canInsert(slot: Int, stack: ItemStack): Boolean {
-				val list = inputs.toList()
-				list[slot] = stack
-				val shadow = CraftingInput.ofPositioned(3, 3, list)
-
-				return (level ?: return false)
-					.recipeManager
-					.byType(MRecipes.MATTER_ENTANGLER)
-					.any { it.value.preemptivelyMatches(shadow, level!!, 3, 3) }
-			}
-
-			override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
-				return false
-			}
-		}),
-		output = output.handler(HandlerFilter.OnlyOut)
+		input = inputs.parent,
+		output = output
 	)
 
 	init {
@@ -98,7 +98,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
 		savetables.stateful(::energy, ENERGY_KEY)
 		savetables.stateful(::matter, MATTER_STORAGE_KEY)
 		savetables.stateful(::upgrades)
-		savetables.stateful(::inputs)
+		savetables.stateful(inputs::parent, "inputs")
 		savetables.stateful(::output)
 		savetables.stateful(::experience)
 
@@ -124,7 +124,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
 		val required = status.job.matterPerTick * status.ticksAdvanced
 
 		if (matter.storedMatter < required) {
-			matter.receiveMatter(node.graph.extractMatter(status.job.matterPerTick.coerceAtLeast(Decimal.TEN).coerceAtMost(matter.missingMatter), false), false)
+			matter.receiveMatter(node.graph.extractMatter(status.job.matterPerTick.coerceIn(Decimal.TEN, matter.missingMatter), false), false)
 		}
 
 		status.scale(matter.extractMatter(required, false) / required)
@@ -150,7 +150,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
 		if (!energy.batteryLevel.isPositive)
 			return JobContainer.noEnergy()
 
-		val inputs = CraftingInput.of(3, 3, inputs.toList())
+		val inputs = this.inputs.asCraftInput()
 
 		val recipe = (level ?: return JobContainer.failure())
 			.recipeManager
diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/AutomationFilters.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/AutomationFilters.kt
index 46dd44472..702cd4a54 100644
--- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/AutomationFilters.kt
+++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/AutomationFilters.kt
@@ -136,11 +136,27 @@ enum class AutomationFilters : AutomationFilter<ContainerSlot> {
 		}
 	};
 
-	val simpleProvider: SlottedContainer.SlotProvider<ContainerSlot> by lazy {
+	val simpleProvider: SlottedContainer.SlotProvider<ContainerSlot> by lazy(LazyThreadSafetyMode.PUBLICATION) {
 		ContainerSlot.Simple(filter = this)
 	}
 
-	val filteredProvider: SlottedContainer.SlotProvider<FilteredContainerSlot> by lazy {
+	val filteredProvider: SlottedContainer.SlotProvider<FilteredContainerSlot> by lazy(LazyThreadSafetyMode.PUBLICATION) {
 		FilteredContainerSlot.Simple(filter = this)
 	}
+
+	val unlimitedSimpleProvider: SlottedContainer.SlotProvider<ContainerSlot> by lazy(LazyThreadSafetyMode.PUBLICATION) {
+		ContainerSlot.Simple(filter = this, maxStackSize = Int.MAX_VALUE)
+	}
+
+	val unlimitedFilteredProvider: SlottedContainer.SlotProvider<FilteredContainerSlot> by lazy(LazyThreadSafetyMode.PUBLICATION) {
+		FilteredContainerSlot.Simple(filter = this, maxStackSize = Int.MAX_VALUE)
+	}
+
+	val limitedSimpleProvider: SlottedContainer.SlotProvider<ContainerSlot> by lazy(LazyThreadSafetyMode.PUBLICATION) {
+		ContainerSlot.Simple(filter = this, maxStackSize = 1)
+	}
+
+	val limitedFilteredProvider: SlottedContainer.SlotProvider<FilteredContainerSlot> by lazy(LazyThreadSafetyMode.PUBLICATION) {
+		FilteredContainerSlot.Simple(filter = this, maxStackSize = 1)
+	}
 }
diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterEntanglerMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterEntanglerMenu.kt
index 9ecd5d33d..62eaf1120 100644
--- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterEntanglerMenu.kt
+++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/matter/MatterEntanglerMenu.kt
@@ -1,22 +1,19 @@
 package ru.dbotthepony.mc.otm.menu.matter
 
 import net.minecraft.server.level.ServerPlayer
-import net.minecraft.world.Container
-import net.minecraft.world.SimpleContainer
 import net.minecraft.world.entity.player.Inventory
 import net.minecraft.world.entity.player.Player
 import net.minecraft.world.item.ItemStack
 import net.minecraft.world.item.crafting.CraftingInput
 import ru.dbotthepony.mc.otm.block.entity.matter.MatterEntanglerBlockEntity
-import ru.dbotthepony.mc.otm.container.MatteryContainer
-import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer
-import ru.dbotthepony.mc.otm.container.get
 import ru.dbotthepony.mc.otm.container.set
+import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
+import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
 import ru.dbotthepony.mc.otm.core.isNotEmpty
 import ru.dbotthepony.mc.otm.item.IQuantumLinked
-import ru.dbotthepony.mc.otm.menu.OutputMenuSlot
-import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
 import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
+import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
+import ru.dbotthepony.mc.otm.menu.OutputMenuSlot
 import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
 import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
 import ru.dbotthepony.mc.otm.menu.makeSlots
@@ -39,11 +36,7 @@ class MatterEntanglerMenu(
 
 	val progress = ProgressGaugeWidget(this, tile?.jobEventLoops?.get(0))
 
-	val inputs: List<MatteryMenuSlot> = makeSlots(tile?.inputs ?: object : MatteryCraftingContainer(3, 3) {
-		override fun getMaxStackSize(): Int {
-			return 1
-		}
-	}) { it, i ->
+	val inputs: List<MatteryMenuSlot> = makeSlots(tile?.inputs ?: SlottedContainer.simple(3 * 3, AutomationFilters.ALLOW.limitedFilteredProvider)) { it, i ->
 		object : MatteryMenuSlot(it, i) {
 			override fun mayPlace(itemStack: ItemStack): Boolean {
 				val list = it.toList()
@@ -59,21 +52,15 @@ class MatterEntanglerMenu(
 		}
 	}
 
-	val outputs = makeSlots(tile?.output ?: SimpleContainer(1)) { a, b -> OutputMenuSlot(a, b) { tile?.experience?.popExperience(player as ServerPlayer) } }
+	val outputs = makeSlots(tile?.output ?: SlottedContainer.simple(1, AutomationFilters.ONLY_OUT.unlimitedSimpleProvider)) { a, b -> OutputMenuSlot(a, b) { tile?.experience?.popExperience(player as ServerPlayer) } }
 	val upgrades = makeUpgradeSlots(3, tile?.upgrades)
 	val experience = TakeExperienceWidget(this, tile?.experience)
 
-	private val entangling: Container = if (tile == null) object : SimpleContainer(2) {
-		override fun getMaxStackSize(): Int {
-			return 1
-		}
-	} else object : MatteryContainer(::rescan, 2) {
-		override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
-			return 1
-		}
-	}
+	private val entangling = SlottedContainer.simple(2, AutomationFilters.ALLOW.limitedSimpleProvider)
 
 	private fun rescan() {
+		if (tile == null) return
+
 		if (player is ServerPlayer && entangling[0].item is IQuantumLinked && entangling[1].item == entangling[0].item && entangling[0].isNotEmpty && entangling[1].isNotEmpty) {
 			val result = (entangling[0].item as IQuantumLinked).merge(entangling[0], entangling[1])
 			entanglingC.container[0] = result
@@ -90,7 +77,7 @@ class MatterEntanglerMenu(
 
 	val entanglingA: MatteryMenuSlot = EntanglingInputMenuSlot(0)
 	val entanglingB: MatteryMenuSlot = EntanglingInputMenuSlot(1)
-	val entanglingC: MatteryMenuSlot = object : MatteryMenuSlot(MatteryContainer(1), 0) {
+	val entanglingC: MatteryMenuSlot = object : MatteryMenuSlot(SlottedContainer.simple(1), 0) {
 		override fun mayPlace(itemStack: ItemStack): Boolean {
 			return false
 		}