Aware items stream

This commit is contained in:
DBotThePony 2022-09-25 21:00:36 +07:00
parent 35ee672696
commit 3f8a96661f
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 378 additions and 86 deletions

View File

@ -12,6 +12,7 @@ import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.capability.awareItemsStream
import ru.dbotthepony.mc.otm.capability.itemsStream import ru.dbotthepony.mc.otm.capability.itemsStream
import ru.dbotthepony.mc.otm.client.render.SkinElement import ru.dbotthepony.mc.otm.client.render.SkinElement
import ru.dbotthepony.mc.otm.container.iterator import ru.dbotthepony.mc.otm.container.iterator
@ -136,20 +137,11 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
for ((tag, count) in type.items) { for ((tag, count) in type.items) {
var required = count var required = count
val iterator = capability.ply.inventory.iterator().nonEmpty()
for (invItem in iterator) { for (invItem in ply.awareItemsStream(false)) {
if (tag.test(invItem)) { if (tag.test(invItem.itemStack)) {
val toExtract = required.coerceAtMost(invItem.count) val toExtract = required.coerceAtMost(invItem.itemStack.count)
required -= toExtract required -= invItem.extract(toExtract, simulate).count
if (!simulate) {
if (toExtract == invItem.count) {
iterator.remove()
} else {
invItem.count -= toExtract
}
}
if (required <= 0) { if (required <= 0) {
break break
@ -336,11 +328,11 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
for ((tag, count) in type.items) { for ((tag, count) in type.items) {
var required = count var required = count
val iterator = capability.ply.inventory.iterator().nonEmpty()
for (invItem in iterator) { for (invItem in ply.awareItemsStream(false)) {
if (tag.test(invItem)) { if (tag.test(invItem.itemStack)) {
required -= required.coerceAtMost(invItem.count) val toExtract = required.coerceAtMost(invItem.itemStack.count)
required -= invItem.extract(toExtract, true).count
if (required <= 0) { if (required <= 0) {
break break

View File

@ -9,15 +9,19 @@ import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import net.minecraftforge.fml.ModList import net.minecraftforge.fml.ModList
import ru.dbotthepony.mc.otm.compat.curios.curiosAwareStream
import ru.dbotthepony.mc.otm.compat.curios.curiosStream import ru.dbotthepony.mc.otm.compat.curios.curiosStream
import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded
import ru.dbotthepony.mc.otm.compat.mekanism.getMekanismEnergySided import ru.dbotthepony.mc.otm.compat.mekanism.getMekanismEnergySided
import ru.dbotthepony.mc.otm.compat.mekanism.mekanismEnergy import ru.dbotthepony.mc.otm.compat.mekanism.mekanismEnergy
import ru.dbotthepony.mc.otm.container.iterator import ru.dbotthepony.mc.otm.container.awareStream
import ru.dbotthepony.mc.otm.container.stream import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.AwareItemStack
import ru.dbotthepony.mc.otm.core.ContainerItemStackEntry
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.orNull import ru.dbotthepony.mc.otm.core.orNull
import java.util.IdentityHashMap import java.util.IdentityHashMap
import java.util.LinkedList
import java.util.stream.Stream import java.util.stream.Stream
val ICapabilityProvider.matteryPlayer: MatteryPlayerCapability? get() = getCapability(MatteryCapability.MATTERY_PLAYER).orNull() val ICapabilityProvider.matteryPlayer: MatteryPlayerCapability? get() = getCapability(MatteryCapability.MATTERY_PLAYER).orNull()
@ -156,7 +160,7 @@ fun ICapabilityProvider.getMatteryEnergySided(side: Direction? = null): LazyOpti
* Contains all items that player might carry * Contains all items that player might carry
*/ */
fun Player.itemsStream(includeCosmetics: Boolean = true): Stream<out ItemStack> { fun Player.itemsStream(includeCosmetics: Boolean = true): Stream<out ItemStack> {
val streams = ArrayList<Stream<out ItemStack>>() val streams = LinkedList<Stream<out ItemStack>>()
streams.add(inventory.stream()) streams.add(inventory.stream())
matteryPlayer?.let { matteryPlayer?.let {
@ -178,8 +182,8 @@ fun Player.itemsStream(includeCosmetics: Boolean = true): Stream<out ItemStack>
* Contains all items that player might see/access ([itemsStream] + open container's items) * Contains all items that player might see/access ([itemsStream] + open container's items)
*/ */
fun Player.allItemsStream(includeCosmetics: Boolean = true): Stream<out ItemStack> { fun Player.allItemsStream(includeCosmetics: Boolean = true): Stream<out ItemStack> {
if (containerMenu == inventoryMenu) { if (containerMenu == inventoryMenu || containerMenu == matteryPlayer?.exoSuitMenu) {
return itemsStream() return itemsStream(includeCosmetics)
} }
val seen = IdentityHashMap<ItemStack, Unit>(containerMenu.slots.size) val seen = IdentityHashMap<ItemStack, Unit>(containerMenu.slots.size)
@ -192,3 +196,47 @@ fun Player.allItemsStream(includeCosmetics: Boolean = true): Stream<out ItemStac
itemsStream(includeCosmetics).filter { it.isEmpty || it !in seen }, itemsStream(includeCosmetics).filter { it.isEmpty || it !in seen },
containerMenu.slots.stream().map { it.item }) containerMenu.slots.stream().map { it.item })
} }
/**
* Modify returned [ItemStack]s through [AwareItemStack.extract]
*
* Contains all items that player might carry
*/
fun Player.awareItemsStream(includeCosmetics: Boolean = true): Stream<out AwareItemStack> {
val streams = LinkedList<Stream<out AwareItemStack>>()
streams.add(inventory.awareStream())
matteryPlayer?.let {
if (it.hasExoSuit) {
streams.add(it.exoSuitContainer.awareStream())
}
}
if (isCuriosLoaded) {
streams.add(curiosAwareStream(includeCosmetics))
}
return Streams.concat(*streams.toTypedArray())
}
/**
* Modify returned [ItemStack]s through [AwareItemStack.extract]
*
* Contains all items that player might see/access ([itemsStream] + open container's items)
*/
fun Player.awareAllItemsStream(includeCosmetics: Boolean = true): Stream<out AwareItemStack> {
if (containerMenu == inventoryMenu || containerMenu == matteryPlayer?.exoSuitMenu) {
return awareItemsStream(includeCosmetics)
}
val seen = HashSet<AwareItemStack>(containerMenu.slots.size)
for (slot in containerMenu.slots) {
seen.add(ContainerItemStackEntry(slot.slotIndex, slot.container))
}
return Streams.concat(
awareItemsStream(includeCosmetics).filter { it !in seen },
seen.stream())
}

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.compat.curios package ru.dbotthepony.mc.otm.compat.curios
import com.google.common.collect.Iterators
import com.google.common.collect.Streams import com.google.common.collect.Streams
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
@ -7,11 +8,18 @@ import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.fml.ModList import net.minecraftforge.fml.ModList
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.container.awareIterator
import ru.dbotthepony.mc.otm.container.awareStream
import ru.dbotthepony.mc.otm.container.iterator
import ru.dbotthepony.mc.otm.container.stream import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.AwareItemStack
import ru.dbotthepony.mc.otm.core.EmptyMutableIterator
import ru.dbotthepony.mc.otm.core.IAwareItemStackIterator
import ru.dbotthepony.mc.otm.core.orNull import ru.dbotthepony.mc.otm.core.orNull
import top.theillusivec4.curios.api.CuriosApi import top.theillusivec4.curios.api.CuriosApi
import top.theillusivec4.curios.common.inventory.CosmeticCurioSlot import top.theillusivec4.curios.common.inventory.CosmeticCurioSlot
import top.theillusivec4.curios.common.inventory.CurioSlot import top.theillusivec4.curios.common.inventory.CurioSlot
import java.util.LinkedList
import java.util.stream.Stream import java.util.stream.Stream
val isCuriosLoaded by lazy { val isCuriosLoaded by lazy {
@ -67,7 +75,7 @@ val Player.curiosSlots: Collection<Pair<Slot, Slot?>> get() {
private fun Player.curiosStreamImpl(includeCosmetics: Boolean): Stream<out ItemStack> { private fun Player.curiosStreamImpl(includeCosmetics: Boolean): Stream<out ItemStack> {
val handler = getCapability(MatteryCapability.CURIOS_INVENTORY).orNull() ?: return Stream.empty() val handler = getCapability(MatteryCapability.CURIOS_INVENTORY).orNull() ?: return Stream.empty()
val result = ArrayList<Stream<out ItemStack>>() val result = LinkedList<Stream<out ItemStack>>()
for ((identifier, curio) in handler.curios) { for ((identifier, curio) in handler.curios) {
result.add(curio.stacks.stream()) result.add(curio.stacks.stream())
@ -87,3 +95,27 @@ fun Player.curiosStream(includeCosmetics: Boolean = true): Stream<out ItemStack>
return curiosStreamImpl(includeCosmetics) return curiosStreamImpl(includeCosmetics)
} }
private fun Player.curiosAwareStreamImpl(includeCosmetics: Boolean): Stream<out AwareItemStack> {
val handler = getCapability(MatteryCapability.CURIOS_INVENTORY).orNull() ?: return Stream.empty()
val result = LinkedList<Stream<out AwareItemStack>>()
for ((identifier, curio) in handler.curios) {
result.add(curio.stacks.awareStream())
if (includeCosmetics && curio.hasCosmetic()) {
result.add(curio.cosmeticStacks.awareStream())
}
}
return Streams.concat(*result.toTypedArray())
}
fun Player.curiosAwareStream(includeCosmetics: Boolean = true): Stream<out AwareItemStack> {
if (!isCuriosLoaded) {
return Stream.empty()
}
return curiosAwareStreamImpl(includeCosmetics)
}

View File

@ -1,30 +1,58 @@
package ru.dbotthepony.mc.otm.container package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.objects.ObjectIterators
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.ContainerItemStackEntry
import ru.dbotthepony.mc.otm.core.IAwareItemStackIterator
import ru.dbotthepony.mc.otm.core.AwareItemStack
class ContainerIterator(private val container: Container) : MutableIterator<ItemStack> { class ContainerIterator(private val container: Container, initialPosition: Int = 0) : ObjectIterators.AbstractIndexBasedListIterator<ItemStack>(0, initialPosition), MutableListIterator<ItemStack> {
private var index = 0 init {
require(initialPosition in 0 .. container.containerSize) { "Invalid initial position: $initialPosition" }
override fun hasNext(): Boolean {
return index < container.containerSize
} }
override fun next(): ItemStack { override fun add(location: Int, k: ItemStack) {
if (index >= container.containerSize) { throw UnsupportedOperationException()
throw IllegalStateException("Already finished iterating")
} }
return container[index++] override fun set(location: Int, k: ItemStack) {
container[location] = k
} }
override fun remove() { override fun remove(location: Int) {
if (index == 0) { container[location] = ItemStack.EMPTY
throw IllegalStateException("Never called next()")
} }
container[index - 1] = ItemStack.EMPTY override fun get(location: Int): ItemStack {
return container[location]
}
override fun getMaxPos(): Int {
return container.containerSize
} }
} }
fun Container.iterator() = ContainerIterator(this) class AwareContainerIterator(private val container: Container, initialPosition: Int = 0) : ObjectIterators.AbstractIndexBasedIterator<AwareItemStack>(0, initialPosition), IAwareItemStackIterator {
init {
require(initialPosition in 0 .. container.containerSize) { "Invalid initial position: $initialPosition" }
}
override fun remove(location: Int) {
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)

View File

@ -4,6 +4,8 @@ import it.unimi.dsi.fastutil.objects.ObjectSpliterator
import it.unimi.dsi.fastutil.objects.ObjectSpliterators import it.unimi.dsi.fastutil.objects.ObjectSpliterators
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.AwareItemStack
import ru.dbotthepony.mc.otm.core.ContainerItemStackEntry
import java.util.stream.Stream import java.util.stream.Stream
import java.util.stream.StreamSupport import java.util.stream.StreamSupport
@ -26,5 +28,26 @@ class ContainerSpliterator(private val container: Container, offset: Int = 0, pr
} }
} }
class AwareContainerSpliterator(private val container: Container, offset: Int = 0, private val maxPos: Int = container.containerSize) : ObjectSpliterators.AbstractIndexBasedSpliterator<AwareItemStack>(offset) {
init {
require(offset >= 0) { "Invalid offset $offset" }
require(offset + maxPos <= container.containerSize) { "$offset -> $maxPos while having only size of ${container.containerSize}!" }
}
override fun get(location: Int): AwareItemStack {
return ContainerItemStackEntry(location, container)
}
override fun getMaxPos(): Int {
return maxPos
}
override fun makeForSplit(pos: Int, maxPos: Int): ObjectSpliterator<AwareItemStack> {
return AwareContainerSpliterator(container, pos, maxPos)
}
}
fun Container.spliterator() = ContainerSpliterator(this) fun Container.spliterator() = ContainerSpliterator(this)
fun Container.awareSpliterator() = AwareContainerSpliterator(this)
fun Container.stream(): Stream<out ItemStack> = StreamSupport.stream(spliterator(), false) fun Container.stream(): Stream<out ItemStack> = StreamSupport.stream(spliterator(), false)
fun Container.awareStream(): Stream<out AwareItemStack> = StreamSupport.stream(awareSpliterator(), false)

View File

@ -4,6 +4,8 @@ import it.unimi.dsi.fastutil.objects.ObjectSpliterator
import it.unimi.dsi.fastutil.objects.ObjectSpliterators import it.unimi.dsi.fastutil.objects.ObjectSpliterators
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.core.AwareItemStack
import ru.dbotthepony.mc.otm.core.ItemHandlerItemStackEntry
import java.util.Spliterator import java.util.Spliterator
import java.util.stream.Stream import java.util.stream.Stream
import java.util.stream.StreamSupport import java.util.stream.StreamSupport
@ -27,5 +29,26 @@ class ItemHandlerSpliterator(private val handler: IItemHandler, offset: Int = 0,
} }
} }
class ItemHandlerAwareSpliterator(private val handler: IItemHandler, offset: Int = 0, private val maxPos: Int = handler.slots) : ObjectSpliterators.AbstractIndexBasedSpliterator<AwareItemStack>(offset) {
init {
require(offset >= 0) { "Invalid offset $offset" }
require(offset + maxPos <= handler.slots) { "$offset -> $maxPos while having only size of ${handler.slots}!" }
}
override fun get(location: Int): AwareItemStack {
return ItemHandlerItemStackEntry(location, handler)
}
override fun getMaxPos(): Int {
return maxPos
}
override fun makeForSplit(pos: Int, maxPos: Int): ObjectSpliterator<AwareItemStack> {
return ItemHandlerAwareSpliterator(handler, pos, maxPos)
}
}
fun IItemHandler.spliterator(): Spliterator<out ItemStack> = ItemHandlerSpliterator(this) fun IItemHandler.spliterator(): Spliterator<out ItemStack> = ItemHandlerSpliterator(this)
fun IItemHandler.awareSpliterator(): Spliterator<out AwareItemStack> = ItemHandlerAwareSpliterator(this)
fun IItemHandler.stream(): Stream<out ItemStack> = StreamSupport.stream(spliterator(), false) fun IItemHandler.stream(): Stream<out ItemStack> = StreamSupport.stream(spliterator(), false)
fun IItemHandler.awareStream(): Stream<out AwareItemStack> = StreamSupport.stream(awareSpliterator(), false)

View File

@ -0,0 +1,56 @@
package ru.dbotthepony.mc.otm.core
import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack
import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.set
/**
* Allows to see the contents of container and extract the item from it
*/
interface AwareItemStack {
operator fun component0(): ItemStack
val itemStack: ItemStack get() = component0()
/**
* Extracts item from underlying container
*/
fun extract(amount: Int, simulate: Boolean = true): ItemStack
}
typealias IAwareItemStackIterator = Iterator<AwareItemStack>
data class ContainerItemStackEntry(val index: Int, val container: Container) : AwareItemStack {
override fun component0(): ItemStack {
return container[index]
}
override fun extract(amount: Int, simulate: Boolean): ItemStack {
if (simulate) {
return container[index].let { if (it.isEmpty) it else it.copy().also { it.count = it.count.coerceAtMost(amount) } }
}
val item = container[index]
val newCount = (item.count - amount).coerceAtLeast(0)
if (newCount == 0) {
container[index] = ItemStack.EMPTY
return item
}
val copy = item.copy().also { it.count = amount }
container[index] = item.copy().also { it.count -= amount }
return copy
}
}
data class ItemHandlerItemStackEntry(val index: Int, val handler: IItemHandler) : AwareItemStack {
override fun component0(): ItemStack {
return handler[index]
}
override fun extract(amount: Int, simulate: Boolean): ItemStack {
return handler.extractItem(index, amount, simulate)
}
}

View File

@ -0,0 +1,44 @@
package ru.dbotthepony.mc.otm.core
object EmptyMutableIterator : MutableIterator<@UnsafeVariance Nothing>, MutableListIterator<@UnsafeVariance Nothing> {
override fun hasPrevious(): Boolean {
return false
}
override fun nextIndex(): Int {
return 0
}
override fun previous(): Nothing {
throw NoSuchElementException()
}
override fun previousIndex(): Int {
return -1
}
override fun add(element: Nothing) {
throw UnsupportedOperationException()
}
override fun hasNext(): Boolean {
return false
}
override fun next(): Nothing {
throw NoSuchElementException()
}
override fun remove() {
throw NoSuchElementException()
}
override fun set(element: Nothing) {
throw UnsupportedOperationException()
}
@Suppress("unchecked_cast")
fun <T> cast(): MutableListIterator<T> {
return this as MutableListIterator<T>
}
}

View File

@ -2,50 +2,5 @@ package ru.dbotthepony.mc.otm.core
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
open class NonEmptyItemStackIterator(protected open val parent: Iterator<ItemStack>) : Iterator<ItemStack> { fun Iterator<ItemStack>.nonEmpty() = PredicateIterator(this) { !it.isEmpty }
private var itemStack: ItemStack? = null fun MutableIterator<ItemStack>.nonEmpty() = MutablePredicateIterator(this) { !it.isEmpty }
private var searched = false
private fun search() {
searched = true
if (itemStack != null) {
return
}
for (stack in parent) {
if (!stack.isEmpty) {
itemStack = stack
return
}
}
}
override fun hasNext(): Boolean {
if (!searched) {
search()
}
return itemStack != null
}
override fun next(): ItemStack {
if (!searched) {
search()
}
val itemStack = itemStack ?: throw IllegalStateException("No next element")
this.itemStack = null
this.searched = false
return itemStack
}
}
class NonEmptyMutableItemStackIterator(override val parent: MutableIterator<ItemStack>) : NonEmptyItemStackIterator(parent), MutableIterator<ItemStack> {
override fun remove() {
parent.remove()
}
}
fun Iterator<ItemStack>.nonEmpty() = NonEmptyItemStackIterator(this)
fun MutableIterator<ItemStack>.nonEmpty() = NonEmptyMutableItemStackIterator(this)

View File

@ -0,0 +1,91 @@
package ru.dbotthepony.mc.otm.core
import java.util.function.Consumer
import java.util.function.Predicate
class PredicateIterator<T> : Iterator<T> {
private val parent: Iterator<T>
private val predicate: Predicate<in T>
private val consumer: Consumer<in T>?
constructor(parent: Iterator<T>, predicate: Predicate<in T>) {
this.parent = parent
this.predicate = predicate
this.consumer = null
}
constructor(parent: Iterator<T>, predicate: Predicate<in T>, consumer: Consumer<in T>) {
this.parent = parent
this.predicate = predicate
this.consumer = consumer
}
private var foundValue: Any? = Companion
override fun hasNext(): Boolean {
if (foundValue === Companion) {
while (parent.hasNext()) {
val next = parent.next()
if (predicate.test(next)) {
foundValue = next
return true
}
}
return false
}
return true
}
@Suppress("unchecked_cast")
override fun next(): T {
if (!hasNext()) {
throw NoSuchElementException()
}
val foundValue = foundValue
if (foundValue === Companion) {
throw ConcurrentModificationException()
}
this.foundValue = Companion
consumer?.accept(foundValue as T)
return foundValue as T
}
private companion object
}
fun <T> Iterator<T>.filter(condition: Predicate<in T>) = PredicateIterator(this, condition)
class MutablePredicateIterator<T> : MutableIterator<T> {
private val parent: MutableIterator<T>
private val predicateParent: PredicateIterator<T>
constructor(parent: MutableIterator<T>, predicate: Predicate<in T>) {
this.parent = parent
this.predicateParent = PredicateIterator(parent, predicate)
}
constructor(parent: MutableIterator<T>, predicate: Predicate<in T>, consumer: Consumer<in T>) {
this.parent = parent
this.predicateParent = PredicateIterator(parent, predicate, consumer)
}
override fun hasNext(): Boolean {
return predicateParent.hasNext()
}
override fun next(): T {
return predicateParent.next()
}
override fun remove() {
return parent.remove()
}
}
fun <T> MutableIterator<T>.filter(condition: Predicate<in T>) = MutablePredicateIterator(this, condition)