Finely optimize MatteryContainer iterator() and isEmpty(), add Cache<T> to container

This commit is contained in:
DBotThePony 2023-08-02 16:12:46 +07:00
parent 0878bd9a7e
commit 8653dd343f
Signed by: DBot
GPG Key ID: DCC23B5715498507
3 changed files with 156 additions and 57 deletions

View File

@ -1,59 +1,42 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.objects.ObjectIterators
import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.collect.ContainerItemStackEntry
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.isNotEmpty
class ContainerIterator(private val container: Container, initialPosition: Int = 0) : ObjectIterators.AbstractIndexBasedListIterator<ItemStack>(0, initialPosition), MutableListIterator<ItemStack> {
init {
require(initialPosition in 0 .. container.containerSize) { "Invalid initial position: $initialPosition" }
class ContainerIterator(private val container: Container) : MutableIterator<ItemStack> {
private var index = 0
private var lastIndex = -1
override fun hasNext(): Boolean {
while (index < container.containerSize) {
if (container[index].isNotEmpty) {
return true
}
index++
}
return false
}
override fun add(location: Int, k: ItemStack) {
throw UnsupportedOperationException()
override fun next(): ItemStack {
if (!hasNext()) {
throw NoSuchElementException()
}
lastIndex = index
return container[index++]
}
override fun set(location: Int, k: ItemStack) {
container[location] = k
}
override fun remove() {
if (lastIndex == -1) {
throw NoSuchElementException()
}
override fun remove(location: Int) {
pos++
container[location] = ItemStack.EMPTY
}
override fun get(location: Int): ItemStack {
return container[location]
}
override fun getMaxPos(): Int {
return container.containerSize
container[lastIndex] = ItemStack.EMPTY
lastIndex = -1
}
}
class AwareContainerIterator(private val container: Container, initialPosition: Int = 0) : ObjectIterators.AbstractIndexBasedIterator<AwareItemStack>(0, initialPosition), Iterator<AwareItemStack> {
init {
require(initialPosition in 0 .. container.containerSize) { "Invalid initial position: $initialPosition" }
}
override fun remove(location: Int) {
pos++
container[location] = ItemStack.EMPTY
}
override fun get(location: Int): AwareItemStack {
return ContainerItemStackEntry(location, container)
}
override fun getMaxPos(): Int {
return container.containerSize
}
}
@JvmOverloads
fun Container.iterator(initialPosition: Int = 0) = ContainerIterator(this, initialPosition)
@JvmOverloads
fun Container.awareIterator(initialPosition: Int = 0) = AwareContainerIterator(this, initialPosition)
fun Container.iterator() = ContainerIterator(this)

View File

@ -2,6 +2,9 @@ package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.ints.IntComparators
import it.unimi.dsi.fastutil.objects.ObjectIterators
import net.minecraft.world.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
@ -14,6 +17,8 @@ import net.minecraft.world.item.Item
import net.minecraft.world.item.Items
import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.core.addSorted
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.registryName
@ -23,6 +28,8 @@ import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.synchronizer.IField
import java.lang.ref.WeakReference
import java.util.*
import java.util.function.Supplier
import kotlin.NoSuchElementException
@Suppress("UNUSED")
open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : Container, Iterable<ItemStack>, INBTSerializable<Tag?> {
@ -32,11 +39,40 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
require(size >= 0) { "Invalid container size $size" }
}
protected val slots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
private val slots = Array(size) { ItemStack.EMPTY }
private val nonEmptyFlags = BitSet()
private val nonEmptyIndices = IntArrayList()
private val trackedSlots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
private val filters: Array<Item?> = arrayOfNulls(size)
private var filterSynchronizer: WeakReference<IField<MutableMap<Int, Item>>>? = null
var changeset = 0
private set
inner class Cache<T>(private val supplier: (MatteryContainer) -> T) : Supplier<T> {
private var thisChangeset = -1
private var value: T? = null
private var isDisabled = false
fun disable() {
isDisabled = true
}
fun enable() {
isDisabled = false
}
override fun get(): T {
if (thisChangeset == changeset && !isDisabled) {
return value as T
}
value = supplier.invoke(this@MatteryContainer)
thisChangeset = changeset
return value as T
}
}
fun clearSlotFilters() {
Arrays.fill(filters, null)
filterSynchronizer?.get()?.value?.clear()
@ -159,6 +195,9 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
override fun deserializeNBT(tag: Tag?) {
Arrays.fill(slots, ItemStack.EMPTY)
Arrays.fill(filters, null)
nonEmptyFlags.clear()
nonEmptyIndices.clear()
filterSynchronizer?.get()?.value?.clear()
when (tag) {
@ -244,6 +283,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
val old = slotStack.copy()
slotStack.count = newCount
trackedSlots[slot] = slotStack.copy()
changeset++
setChanged(slot, slotStack, old)
if (popTime != null) {
@ -269,7 +309,11 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
if (!simulate) {
val copyToPut = copy.copy()
copyToPut.count = diff
this[slot] = copyToPut
slots[slot] = copyToPut
trackedSlots[slot] = copyToPut.copy()
updateEmptyFlag(slot)
changeset++
setChanged(slot, copyToPut, ItemStack.EMPTY)
if (popTime != null) {
copyToPut.popTime = popTime
@ -327,11 +371,14 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
}
override fun isEmpty(): Boolean {
return slots.all { it.isEmpty }
return nonEmptyIndices.isEmpty
}
operator fun get(slot: Int) = getItem(slot)
operator fun set(slot: Int, stack: ItemStack) = setItem(slot, stack)
@Suppress("nothing_to_inline")
inline operator fun get(slot: Int) = getItem(slot)
@Suppress("nothing_to_inline")
inline operator fun set(slot: Int, stack: ItemStack) = setItem(slot, stack)
operator fun contains(other: ItemStack): Boolean {
for (i in 0 until size) {
@ -346,10 +393,19 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
final override fun getItem(slot: Int): ItemStack {
val item = slots[slot]
if (item.isEmpty)
if (item.isEmpty) {
if (nonEmptyFlags[slot]) {
setChanged(slot)
}
return ItemStack.EMPTY
else
} else {
if (!nonEmptyFlags[slot]) {
setChanged(slot)
}
return item
}
}
final override fun removeItem(slot: Int, amount: Int): ItemStack {
@ -359,6 +415,8 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
val old = slots[slot].copy()
val split = slots[slot].split(amount)
trackedSlots[slot] = slots[slot].copy()
changeset++
updateEmptyFlag(slot)
setChanged(slot, if (slots[slot].isEmpty) ItemStack.EMPTY else slots[slot], old)
return split
@ -368,6 +426,12 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
val old = slots[slot]
slots[slot] = ItemStack.EMPTY
trackedSlots[slot] = ItemStack.EMPTY
if (old.isNotEmpty) {
updateEmptyFlag(slot)
changeset++
}
return old
}
@ -376,8 +440,11 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
return
val old = slots[slot]
slots[slot] = stack
slots[slot] = if (stack.isEmpty) ItemStack.EMPTY else stack
trackedSlots[slot] = if (stack.isEmpty) ItemStack.EMPTY else stack.copy()
updateEmptyFlag(slot)
changeset++
setChanged(slot, stack, old)
}
@ -389,17 +456,38 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
fun setChanged(slot: Int) {
if (!slots[slot].equals(trackedSlots[slot], false)) {
setChanged(slot, slots[slot], trackedSlots[slot])
trackedSlots[slot] = slots[slot].copy()
updateEmptyFlag(slot)
changeset++
setChanged(slot, slots[slot], trackedSlots[slot])
// mojang соси))0)0))0)))))0)
}
}
private fun updateEmptyFlag(slot: Int) {
if (slots[slot].isEmpty) {
if (nonEmptyFlags[slot]) {
nonEmptyFlags[slot] = false
nonEmptyIndices.removeInt(slot)
}
} else {
if (!nonEmptyFlags[slot]) {
nonEmptyFlags[slot] = true
nonEmptyIndices.addSorted(slot, IntComparators.NATURAL_COMPARATOR)
}
}
}
override fun stillValid(player: Player): Boolean {
return true
}
final override fun clearContent() {
nonEmptyFlags.clear()
nonEmptyIndices.clear()
Arrays.fill(trackedSlots, ItemStack.EMPTY)
for (slot in 0 until size) {
if (!slots[slot].isEmpty) {
val old = slots[slot]
@ -407,11 +495,37 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
setChanged(slot, ItemStack.EMPTY, old)
}
}
}
Arrays.fill(trackedSlots, ItemStack.EMPTY)
private inner class Iterator : MutableIterator<ItemStack> {
private val parent = nonEmptyIndices.intIterator()
private var lastIndex = -1
override fun hasNext(): Boolean {
return parent.hasNext()
}
override fun next(): ItemStack {
lastIndex = parent.nextInt()
return getItem(lastIndex)
}
override fun remove() {
if (lastIndex == -1) {
throw NoSuchElementException()
}
parent.remove()
setItem(lastIndex, ItemStack.EMPTY)
lastIndex = -1
}
}
final override fun iterator(): MutableIterator<ItemStack> {
return ContainerIterator(this)
if (isEmpty) {
return ObjectIterators.EMPTY_ITERATOR as MutableIterator<ItemStack>
}
return Iterator()
}
}

View File

@ -376,6 +376,7 @@ fun <E> List<E>.searchInsertionIndex(element: E, comparator: Comparator<E>, from
return fromIndex
}
// линейный поиск если границы маленькие
if (toIndex - fromIndex <= 10) {
for (i in fromIndex + 1 until toIndex) {
val compare = comparator.compare(element, this[i])
@ -387,6 +388,7 @@ fun <E> List<E>.searchInsertionIndex(element: E, comparator: Comparator<E>, from
return size
} else {
// двоичный поиск
var lower = fromIndex
var upper = toIndex - 1