Implement faster lookup methods for Slotted Container as well

This commit is contained in:
DBotThePony 2025-03-15 00:18:02 +07:00
parent 8b38504a26
commit 0efc520782
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 74 additions and 53 deletions

View File

@ -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<out S : IContainerSlot> : IEnhancedContainer<S> {
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<ItemStack> {
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)
}
}

View File

@ -29,58 +29,9 @@ import java.util.BitSet
* This is supposed to be counterpart to [SimpleContainer] of Minecraft itself, with more features * This is supposed to be counterpart to [SimpleContainer] of Minecraft itself, with more features
* and improved performance (inside [IEnhancedContainer] defined methods). * and improved performance (inside [IEnhancedContainer] defined methods).
*/ */
abstract class EnhancedContainer<out S : IContainerSlot>(private val size: Int) : IEnhancedContainer<S>, INBTSerializable<CompoundTag> { abstract class EnhancedContainer<out S : IContainerSlot>(private val size: Int) : BitmapTrackingContainer<S>(), INBTSerializable<CompoundTag> {
private val items = Array(size) { ItemStack.EMPTY } private val items = Array(size) { ItemStack.EMPTY }
private val observedItems = 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<ItemStack> {
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) {} protected open fun notifySlotChanged(slot: Int, old: ItemStack) {}

View File

@ -56,12 +56,12 @@ open class ContainerSlot(
notifyChanged(observedItem) notifyChanged(observedItem)
observedItem = ItemStack.EMPTY observedItem = ItemStack.EMPTY
_item = ItemStack.EMPTY _item = ItemStack.EMPTY
container.notifyChanged() container.notifyChanged(slot)
return true return true
} else if (observedItem.count != item.count || !ItemStack.isSameItemSameComponents(item, observedItem)) { } else if (observedItem.count != item.count || !ItemStack.isSameItemSameComponents(item, observedItem)) {
notifyChanged(observedItem) notifyChanged(observedItem)
observedItem = item.copy() observedItem = item.copy()
container.notifyChanged() container.notifyChanged(slot)
return true return true
} }

View File

@ -17,6 +17,7 @@ import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.common.util.INBTSerializable import net.neoforged.neoforge.common.util.INBTSerializable
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.util.Either 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.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IAutomatedContainer import ru.dbotthepony.mc.otm.container.IAutomatedContainer
import ru.dbotthepony.mc.otm.container.IAutomatedContainerSlot 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.isNotEmpty
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.data.codec.minRange import ru.dbotthepony.mc.otm.data.codec.minRange
import java.util.BitSet
import java.util.function.Predicate import java.util.function.Predicate
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -40,7 +42,7 @@ class SlottedContainer(
slots: Collection<MarkedSlotProvider<*>>, slots: Collection<MarkedSlotProvider<*>>,
private val stillValid: Predicate<Player>, private val stillValid: Predicate<Player>,
private val globalChangeListeners: Array<Runnable> private val globalChangeListeners: Array<Runnable>
) : IAutomatedContainer<ContainerSlot>, INBTSerializable<Tag> { ) : BitmapTrackingContainer<ContainerSlot>(), IAutomatedContainer<ContainerSlot>, INBTSerializable<Tag> {
interface ISlotGroup<T : ContainerSlot> : List<T> { interface ISlotGroup<T : ContainerSlot> : List<T> {
/** /**
* @see IAutomatedContainer.addItem * @see IAutomatedContainer.addItem
@ -130,6 +132,7 @@ class SlottedContainer(
private var suppressListeners = false private var suppressListeners = false
override fun clearContent() { override fun clearContent() {
bitmap.clear()
suppressListeners = true suppressListeners = true
try { try {
@ -149,6 +152,11 @@ class SlottedContainer(
globalChangeListeners.forEach { it.run() } globalChangeListeners.forEach { it.run() }
} }
fun notifyChanged(slot: Int) {
notifyChanged()
bitmap[slot] = slots[slot].isNotEmpty
}
// called by outside code (vanilla and other unaware mods) // called by outside code (vanilla and other unaware mods)
override fun setChanged() { override fun setChanged() {
suppressListeners = true suppressListeners = true
@ -253,6 +261,7 @@ class SlottedContainer(
override fun deserializeNBT(provider: HolderLookup.Provider, nbt: Tag) { override fun deserializeNBT(provider: HolderLookup.Provider, nbt: Tag) {
lostItems.clear() lostItems.clear()
slots.forEach { it.clear() } slots.forEach { it.clear() }
bitmap.clear()
if (nbt is CompoundTag) { if (nbt is CompoundTag) {
// legacy container // legacy container