From 0efc5207826d19df901e45bc444acfe87ab94653 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 15 Mar 2025 00:18:02 +0700 Subject: [PATCH] Implement faster lookup methods for Slotted Container as well --- .../otm/container/BitmapTrackingContainer.kt | 61 +++++++++++++++++++ .../mc/otm/container/EnhancedContainer.kt | 51 +--------------- .../mc/otm/container/slotted/ContainerSlot.kt | 4 +- .../otm/container/slotted/SlottedContainer.kt | 11 +++- 4 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/container/BitmapTrackingContainer.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/BitmapTrackingContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/BitmapTrackingContainer.kt new file mode 100644 index 000000000..136990472 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/BitmapTrackingContainer.kt @@ -0,0 +1,61 @@ +package ru.dbotthepony.mc.otm.container + +import it.unimi.dsi.fastutil.ints.IntCollection +import net.minecraft.world.item.ItemStack +import ru.dbotthepony.kommons.collect.iterateClearBits +import ru.dbotthepony.kommons.collect.iterateSetBits +import ru.dbotthepony.mc.otm.core.collect.IntRange2Set +import ru.dbotthepony.mc.otm.core.collect.map +import java.util.* + +abstract class BitmapTrackingContainer : IEnhancedContainer { + protected val bitmap = BitSet() + + final override fun isEmpty(): Boolean { + return bitmap.isEmpty + } + + final override fun nextEmptySlot(startIndex: Int): Int { + if (startIndex >= containerSize) + return -1 + else if (startIndex < 0) + return bitmap.nextClearBit(0) + else + return bitmap.nextClearBit(startIndex) + } + + final override fun nextNonEmptySlot(startIndex: Int): Int { + if (startIndex >= containerSize) + return -1 + else if (startIndex < 0) + return bitmap.nextSetBit(0) + else + return bitmap.nextSetBit(startIndex) + } + + final override fun iterator(): Iterator { + return bitmap.iterateSetBits(containerSize).map { this[it] } + } + + final override fun nonEmptySlotIndexIterator(): IntIterator { + return bitmap.iterateSetBits(containerSize) + } + + final override fun emptySlotIndexIterator(): IntIterator { + return bitmap.iterateClearBits(containerSize) + } + + final override fun emptySlotIndexIterator(allowedSlots: IntCollection): IntIterator { + if (allowedSlots is IntRange2Set && allowedSlots.isNotEmpty()) + return bitmap.iterateClearBits(allowedSlots.firstInt(), allowedSlots.lastInt() + 1) + + return super.emptySlotIndexIterator(allowedSlots) + } + + final override fun nonEmptySlotIndexIterator(allowedSlots: IntCollection): IntIterator { + if (allowedSlots is IntRange2Set && allowedSlots.isNotEmpty()) + return bitmap.iterateSetBits(allowedSlots.firstInt(), allowedSlots.lastInt() + 1) + + return super.nonEmptySlotIndexIterator(allowedSlots) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/EnhancedContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/EnhancedContainer.kt index f504d831c..6ce4277a9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/EnhancedContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/EnhancedContainer.kt @@ -29,58 +29,9 @@ import java.util.BitSet * This is supposed to be counterpart to [SimpleContainer] of Minecraft itself, with more features * and improved performance (inside [IEnhancedContainer] defined methods). */ -abstract class EnhancedContainer(private val size: Int) : IEnhancedContainer, INBTSerializable { +abstract class EnhancedContainer(private val size: Int) : BitmapTrackingContainer(), INBTSerializable { private val items = Array(size) { ItemStack.EMPTY } private val observedItems = Array(size) { ItemStack.EMPTY } - private val bitmap = BitSet(size) - - final override fun isEmpty(): Boolean { - return bitmap.isEmpty - } - - final override fun nextEmptySlot(startIndex: Int): Int { - if (startIndex >= size) - return -1 - else if (startIndex < 0) - return bitmap.nextClearBit(0) - else - return bitmap.nextClearBit(startIndex) - } - - final override fun nextNonEmptySlot(startIndex: Int): Int { - if (startIndex >= size) - return -1 - else if (startIndex < 0) - return bitmap.nextSetBit(0) - else - return bitmap.nextSetBit(startIndex) - } - - final override fun iterator(): Iterator { - return bitmap.iterateSetBits(size).map { this[it] } - } - - final override fun nonEmptySlotIndexIterator(): IntIterator { - return bitmap.iterateSetBits(size) - } - - final override fun emptySlotIndexIterator(): IntIterator { - return bitmap.iterateClearBits(size) - } - - final override fun emptySlotIndexIterator(allowedSlots: IntCollection): IntIterator { - if (allowedSlots is IntRange2Set && allowedSlots.isNotEmpty()) - return bitmap.iterateClearBits(allowedSlots.firstInt(), allowedSlots.lastInt() + 1) - - return super.emptySlotIndexIterator(allowedSlots) - } - - final override fun nonEmptySlotIndexIterator(allowedSlots: IntCollection): IntIterator { - if (allowedSlots is IntRange2Set && allowedSlots.isNotEmpty()) - return bitmap.iterateSetBits(allowedSlots.firstInt(), allowedSlots.lastInt() + 1) - - return super.nonEmptySlotIndexIterator(allowedSlots) - } protected open fun notifySlotChanged(slot: Int, old: ItemStack) {} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/ContainerSlot.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/ContainerSlot.kt index 62780ee8c..7d4821597 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/ContainerSlot.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/ContainerSlot.kt @@ -56,12 +56,12 @@ open class ContainerSlot( notifyChanged(observedItem) observedItem = ItemStack.EMPTY _item = ItemStack.EMPTY - container.notifyChanged() + container.notifyChanged(slot) return true } else if (observedItem.count != item.count || !ItemStack.isSameItemSameComponents(item, observedItem)) { notifyChanged(observedItem) observedItem = item.copy() - container.notifyChanged() + container.notifyChanged(slot) return true } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/SlottedContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/SlottedContainer.kt index 1b5a66420..2f013f4d6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/SlottedContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/slotted/SlottedContainer.kt @@ -17,6 +17,7 @@ import net.minecraft.world.item.ItemStack import net.neoforged.neoforge.common.util.INBTSerializable import org.apache.logging.log4j.LogManager import ru.dbotthepony.kommons.util.Either +import ru.dbotthepony.mc.otm.container.BitmapTrackingContainer import ru.dbotthepony.mc.otm.container.EnhancedContainer import ru.dbotthepony.mc.otm.container.IAutomatedContainer import ru.dbotthepony.mc.otm.container.IAutomatedContainerSlot @@ -25,6 +26,7 @@ import ru.dbotthepony.mc.otm.container.balance import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.data.codec.minRange +import java.util.BitSet import java.util.function.Predicate import kotlin.reflect.KClass @@ -40,7 +42,7 @@ class SlottedContainer( slots: Collection>, private val stillValid: Predicate, private val globalChangeListeners: Array -) : IAutomatedContainer, INBTSerializable { +) : BitmapTrackingContainer(), IAutomatedContainer, INBTSerializable { interface ISlotGroup : List { /** * @see IAutomatedContainer.addItem @@ -130,6 +132,7 @@ class SlottedContainer( private var suppressListeners = false override fun clearContent() { + bitmap.clear() suppressListeners = true try { @@ -149,6 +152,11 @@ class SlottedContainer( globalChangeListeners.forEach { it.run() } } + fun notifyChanged(slot: Int) { + notifyChanged() + bitmap[slot] = slots[slot].isNotEmpty + } + // called by outside code (vanilla and other unaware mods) override fun setChanged() { suppressListeners = true @@ -253,6 +261,7 @@ class SlottedContainer( override fun deserializeNBT(provider: HolderLookup.Provider, nbt: Tag) { lostItems.clear() slots.forEach { it.clear() } + bitmap.clear() if (nbt is CompoundTag) { // legacy container