Compare commits

...

5 Commits

6 changed files with 131 additions and 33 deletions

View File

@ -22,7 +22,7 @@ mixin_version=0.8.5
neogradle.subsystems.parchment.minecraftVersion=1.21.1 neogradle.subsystems.parchment.minecraftVersion=1.21.1
neogradle.subsystems.parchment.mappingsVersion=2024.11.17 neogradle.subsystems.parchment.mappingsVersion=2024.11.17
kommons_version=3.5.2 kommons_version=3.6.0
caffeine_cache_version=3.1.5 caffeine_cache_version=3.1.5
jei_version=19.16.4.171 jei_version=19.16.4.171

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

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.container package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntCollection
import it.unimi.dsi.fastutil.ints.IntOpenHashSet import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import net.minecraft.core.HolderLookup.Provider import net.minecraft.core.HolderLookup.Provider
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
@ -12,9 +13,14 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack 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.collect.iterateClearBits
import ru.dbotthepony.kommons.collect.iterateSetBits
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.collect.IntRange2Set
import ru.dbotthepony.mc.otm.core.collect.map
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 java.util.BitSet
/** /**
* Flexible base implementation of [IEnhancedContainer], designed to be inherited, or used as-is * Flexible base implementation of [IEnhancedContainer], designed to be inherited, or used as-is
@ -23,7 +29,7 @@ import ru.dbotthepony.mc.otm.core.nbt.set
* 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 }
@ -36,9 +42,11 @@ abstract class EnhancedContainer<out S : IContainerSlot>(private val size: Int)
if (items[slot].isEmpty) { if (items[slot].isEmpty) {
items[slot] = ItemStack.EMPTY items[slot] = ItemStack.EMPTY
observedItems[slot] = ItemStack.EMPTY observedItems[slot] = ItemStack.EMPTY
bitmap[slot] = false
} else { } else {
notifySlotChanged(slot, observedItems[slot]) notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = items[slot].copy() observedItems[slot] = items[slot].copy()
bitmap[slot] = true
} }
} }
} }
@ -50,6 +58,7 @@ abstract class EnhancedContainer<out S : IContainerSlot>(private val size: Int)
override fun clearContent() { override fun clearContent() {
items.fill(ItemStack.EMPTY) items.fill(ItemStack.EMPTY)
observedItems.fill(ItemStack.EMPTY) observedItems.fill(ItemStack.EMPTY)
bitmap.clear()
} }
override fun setChanged() { override fun setChanged() {
@ -146,6 +155,7 @@ abstract class EnhancedContainer<out S : IContainerSlot>(private val size: Int)
val copy = observedItems.copyOf() val copy = observedItems.copyOf()
items.fill(ItemStack.EMPTY) items.fill(ItemStack.EMPTY)
observedItems.fill(ItemStack.EMPTY) observedItems.fill(ItemStack.EMPTY)
bitmap.clear()
val seenSlots = IntOpenHashSet() val seenSlots = IntOpenHashSet()
val ops = provider.createSerializationContext(NbtOps.INSTANCE) val ops = provider.createSerializationContext(NbtOps.INSTANCE)
@ -166,6 +176,7 @@ abstract class EnhancedContainer<out S : IContainerSlot>(private val size: Int)
if (it.isNotEmpty) { if (it.isNotEmpty) {
items[slot] = it items[slot] = it
observedItems[slot] = it.copy() observedItems[slot] = it.copy()
bitmap[slot] = true
if (it.count != copy[slot].count || !ItemStack.isSameItemSameComponents(it, copy[slot])) if (it.count != copy[slot].count || !ItemStack.isSameItemSameComponents(it, copy[slot]))
notifySlotChanged(slot, copy[slot]) notifySlotChanged(slot, copy[slot])

View File

@ -76,10 +76,22 @@ interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, I
} }
fun nonEmptySlotIterator(): Iterator<S> { fun nonEmptySlotIterator(): Iterator<S> {
return slotIterator().filter { it.isNotEmpty } return nonEmptySlotIndexIterator().map { containerSlot(it) }
} }
private fun slotIterator(allowedSlots: IntCollection, predicate: Predicate<ItemStack>): IntIterator { fun emptySlotIterator(): Iterator<S> {
return emptySlotIndexIterator().map { containerSlot(it) }
}
fun nonEmptySlotIterator(allowedSlots: IntCollection): Iterator<S> {
return nonEmptySlotIndexIterator(allowedSlots).map { containerSlot(it) }
}
fun emptySlotIterator(allowedSlots: IntCollection): Iterator<S> {
return emptySlotIndexIterator(allowedSlots).map { containerSlot(it) }
}
private fun slotIndexWalker(allowedSlots: IntCollection, predicate: Predicate<ItemStack>): IntIterator {
return object : IntIterator() { return object : IntIterator() {
private val parent = allowedSlots.intIterator() private val parent = allowedSlots.intIterator()
private var foundNext = false private var foundNext = false
@ -120,6 +132,14 @@ interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, I
} }
} }
fun emptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
return slotIndexWalker(allowedSlots) { it.isEmpty }
}
fun nonEmptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
return slotIndexWalker(allowedSlots) { it.isNotEmpty }
}
fun emptySlotIndexIterator(): IntIterator { fun emptySlotIndexIterator(): IntIterator {
return emptySlotIndexIterator(slotRange) return emptySlotIndexIterator(slotRange)
} }
@ -128,20 +148,30 @@ interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, I
return nonEmptySlotIndexIterator(slotRange) return nonEmptySlotIndexIterator(slotRange)
} }
fun emptySlotIndexIterator(allowedSlots: IntCollection): IntIterator { fun slotIndexWithItemIterator(item: Item): IntIterator {
return slotIterator(allowedSlots) { it.isEmpty } return slotIndexWithItemIterator(item, slotRange)
} }
fun nonEmptySlotIndexIterator(allowedSlots: IntCollection): IntIterator { fun slotIndexWithItemIterator(item: Item, allowedSlots: IntCollection): IntIterator {
return slotIterator(allowedSlots) { it.isNotEmpty } val parent = nonEmptySlotIndexIterator(allowedSlots).filter { this[it].isNotEmpty && this[it].item === item }
return object : IntIterator() {
override fun nextInt(): Int {
return parent.next()
}
override fun hasNext(): Boolean {
return parent.hasNext()
}
}
} }
fun slotWithItemIterator(item: Item): IntIterator { fun slotWithItemIterator(item: Item): Iterator<S> {
return slotWithItemIterator(item, slotRange) return slotWithItemIterator(item, slotRange)
} }
fun slotWithItemIterator(item: Item, allowedSlots: IntCollection): IntIterator { fun slotWithItemIterator(item: Item, allowedSlots: IntCollection): Iterator<S> {
return slotIterator(allowedSlots) { it.isNotEmpty && it.item === item } return slotIndexWithItemIterator(item, allowedSlots).map { containerSlot(it) }
} }
fun nextEmptySlot(startIndex: Int): Int { fun nextEmptySlot(startIndex: Int): Int {
@ -189,17 +219,11 @@ interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, I
} }
override fun isEmpty(): Boolean { override fun isEmpty(): Boolean {
return super.isEmpty() return nextNonEmptySlot(0) != -1
} }
val hasEmptySlots: Boolean get() { val hasEmptySlots: Boolean get() {
for (i in 0 until containerSize) { return nextEmptySlot(0) != -1
if (this[i].isEmpty) {
return true
}
}
return false
} }
override fun size(): Int { override fun size(): Int {
@ -209,11 +233,8 @@ interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, I
override fun countItem(item: Item): Int { override fun countItem(item: Item): Int {
var count = 0 var count = 0
for (stack in this) { for (slot in slotWithItemIterator(item))
if (stack.item === item) { count += slot.item.count
count += stack.count
}
}
return count return count
} }
@ -257,9 +278,7 @@ interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, I
return stack return stack
// двигаем в одинаковые слоты // двигаем в одинаковые слоты
for (i in slotWithItemIterator(stack.item, slots)) { for (slot in slotWithItemIterator(stack.item, slots)) {
val slot = containerSlot(i)
val condition: Boolean val condition: Boolean
if (slot is IFilteredContainerSlot) { if (slot is IFilteredContainerSlot) {
@ -295,9 +314,7 @@ interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, I
} }
if (!onlyIntoExisting) { if (!onlyIntoExisting) {
for (i in emptySlotIndexIterator(slots)) { for (slot in emptySlotIterator(slots)) {
val slot = containerSlot(i)
val condition: Boolean val condition: Boolean
if (slot is IFilteredContainerSlot) { if (slot is IFilteredContainerSlot) {

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