diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerIterator.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerIterator.kt index 197633e58..56041d368 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerIterator.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerIterator.kt @@ -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(0, initialPosition), MutableListIterator { - init { - require(initialPosition in 0 .. container.containerSize) { "Invalid initial position: $initialPosition" } +class ContainerIterator(private val container: Container) : MutableIterator { + 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(0, initialPosition), Iterator { - 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) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt index 4b342f692..a18991525 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt @@ -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, INBTSerializable { @@ -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 = Array(size) { ItemStack.EMPTY } + private val slots = Array(size) { ItemStack.EMPTY } + private val nonEmptyFlags = BitSet() + private val nonEmptyIndices = IntArrayList() private val trackedSlots: Array = Array(size) { ItemStack.EMPTY } private val filters: Array = arrayOfNulls(size) private var filterSynchronizer: WeakReference>>? = null + var changeset = 0 + private set + + inner class Cache(private val supplier: (MatteryContainer) -> T) : Supplier { + 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 { + 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 { - return ContainerIterator(this) + if (isEmpty) { + return ObjectIterators.EMPTY_ITERATOR as MutableIterator + } + + return Iterator() } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 6387b3a8e..a0a46e119 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -376,6 +376,7 @@ fun List.searchInsertionIndex(element: E, comparator: Comparator, 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 List.searchInsertionIndex(element: E, comparator: Comparator, from return size } else { + // двоичный поиск var lower = fromIndex var upper = toIndex - 1