Cache recipes in Matter Entangler

This commit is contained in:
DBotThePony 2025-03-05 15:23:27 +07:00
parent 45a32dce3c
commit 83d11d531d
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 90 additions and 8 deletions

View File

@ -1,13 +1,18 @@
package ru.dbotthepony.mc.otm.block.entity.matter
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Scheduler
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.Util
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.CraftingInput
import net.minecraft.world.item.crafting.RecipeManager
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities
@ -28,13 +33,21 @@ 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.collect.filter
import ru.dbotthepony.mc.otm.core.collect.forEach
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.ItemStackKey
import ru.dbotthepony.mc.otm.core.util.asKey
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.graph.matter.MatterNode
import ru.dbotthepony.mc.otm.menu.matter.MatterEntanglerMenu
import ru.dbotthepony.mc.otm.recipe.IMatterEntanglerRecipe
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.time.Duration
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) {
@ -60,20 +73,47 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
val experience = ExperienceStorage(MachinesConfig.MATTER_ENTANGLER::maxExperienceStored).also(::addNeighbourListener)
val energyConfig = ConfigurableEnergy(energy)
private var recipeCache: Collection<IMatterEntanglerRecipe> = listOf()
private var seenRecipeManager: RecipeManager? = null
private fun getRecipes(): Collection<IMatterEntanglerRecipe> {
val level = level!!
val manager = level.recipeManager
if (seenRecipeManager !== manager) {
seenRecipeManager = manager
val input = inputs.asPositionedCraftInput()
recipeCache = manager.byType(MRecipes.MATTER_ENTANGLER)
.iterator()
.map { it.value }
.filter { it.preemptivelyMatches(input, level, 3, 3) }
.toList()
}
return recipeCache
}
private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
val insertCache: Cache<ItemStackKey, Boolean> = Caffeine.newBuilder()
.maximumSize(1024L)
.expireAfterWrite(Duration.ofMinutes(1L))
.scheduler(Scheduler.systemScheduler())
.executor(Util.backgroundExecutor())
.build()
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) }
return insertCache.get(itemStack.asKey()) {
val list = container.toList()
list[slot] = itemStack
val shadow = CraftingInput.ofPositioned(3, 3, list)
getRecipes().any { it.preemptivelyMatches(shadow, level, 3, 3) }
}
}
override fun canAutomationTakeItem(desired: Int): Boolean {
@ -84,7 +124,14 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
get() = 1
}
val inputs = IEnhancedCraftingContainer.Wrapper(SlottedContainer.simple(3 * 3, ::InputSlot, ::setChanged), 3, 3)
private fun inputsChanged() {
inputs.slotIterator().forEach { (it as InputSlot).insertCache.invalidateAll() }
seenRecipeManager = null
recipeCache = listOf()
setChanged()
}
val inputs = IEnhancedCraftingContainer.Wrapper(SlottedContainer.simple(3 * 3, ::InputSlot, ::inputsChanged), 3, 3)
val output = SlottedContainer.simple(1, AutomationFilters.ONLY_OUT.unlimitedSimpleProvider, ::markDirtyFast)
val itemConfig = ConfigurableItemHandler(

View File

@ -0,0 +1,35 @@
package ru.dbotthepony.mc.otm.core.util
import it.unimi.dsi.fastutil.HashCommon
import net.minecraft.core.component.DataComponentMap
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
class ItemStackKey(val item: Item, val components: DataComponentMap) {
// make copy of original itemstack because there is no copy() method on DataComponentMap, which is returned by ItemStack#getComponents
constructor(itemStack: ItemStack) : this(itemStack.item, itemStack.copy().components)
private var hashComputed = false
private var hash = 0
override fun equals(other: Any?): Boolean {
return this === other || other is ItemStackKey && other.item === item && other.components == components
}
override fun hashCode(): Int {
if (!hashComputed) {
hash = HashCommon.murmurHash3(item.hashCode().toLong().shl(32) or components.hashCode().toLong()).toInt()
hashComputed = true
}
return hash
}
override fun toString(): String {
return "ItemStackKey[$item, $components]"
}
}
fun ItemStack.asKey(): ItemStackKey {
return ItemStackKey(this)
}