Initial code for new containers
This commit is contained in:
parent
dfc35f393f
commit
ca6ff30414
@ -9,6 +9,7 @@ import it.unimi.dsi.fastutil.ints.IntIterator
|
|||||||
import it.unimi.dsi.fastutil.ints.IntList
|
import it.unimi.dsi.fastutil.ints.IntList
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet
|
import it.unimi.dsi.fastutil.ints.IntSet
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSortedSet
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
@ -22,6 +23,7 @@ import ru.dbotthepony.mc.otm.container.util.ItemStackHashStrategy
|
|||||||
import ru.dbotthepony.mc.otm.container.util.containerSlot
|
import ru.dbotthepony.mc.otm.container.util.containerSlot
|
||||||
import ru.dbotthepony.mc.otm.container.util.slotIterator
|
import ru.dbotthepony.mc.otm.container.util.slotIterator
|
||||||
import ru.dbotthepony.mc.otm.core.addAll
|
import ru.dbotthepony.mc.otm.core.addAll
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.IntRange2Set
|
||||||
import ru.dbotthepony.mc.otm.core.collect.filter
|
import ru.dbotthepony.mc.otm.core.collect.filter
|
||||||
import ru.dbotthepony.mc.otm.core.collect.toList
|
import ru.dbotthepony.mc.otm.core.collect.toList
|
||||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
@ -38,89 +40,12 @@ inline operator fun Container.get(index: Int): ItemStack = getItem(index)
|
|||||||
@Suppress("nothing_to_inline")
|
@Suppress("nothing_to_inline")
|
||||||
inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index)
|
inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index)
|
||||||
|
|
||||||
val Container.slotRange: IntIterable get() {
|
val Container.slotRange: IntRange2Set get() {
|
||||||
return IntIterable {
|
return IntRange2Set.openEnded(0, containerSize)
|
||||||
val i = (0 until containerSize).iterator()
|
|
||||||
|
|
||||||
object : IntIterator {
|
|
||||||
override fun hasNext(): Boolean {
|
|
||||||
return i.hasNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove() {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun nextInt(): Int {
|
|
||||||
return i.nextInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Container.addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange): ItemStack {
|
fun Container.addItem(stack: ItemStack, simulate: Boolean, slots: IntSortedSet = slotRange): ItemStack {
|
||||||
if (this is IMatteryContainer) {
|
return IEnhancedContainer.wrap(this).addItem(stack, simulate, slots)
|
||||||
return this.addItem(stack, simulate, slots)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stack.isEmpty)
|
|
||||||
return stack
|
|
||||||
|
|
||||||
val copy = stack.copy()
|
|
||||||
|
|
||||||
// двигаем в одинаковые слоты
|
|
||||||
var i = slots.intIterator()
|
|
||||||
|
|
||||||
while (i.hasNext()) {
|
|
||||||
val slot = i.nextInt()
|
|
||||||
|
|
||||||
if (ItemStack.isSameItemSameComponents(this[slot], copy)) {
|
|
||||||
val slotStack = this[slot]
|
|
||||||
val slotLimit = maxStackSize.coerceAtMost(slotStack.maxStackSize)
|
|
||||||
|
|
||||||
if (slotStack.count < slotLimit) {
|
|
||||||
val newCount = (slotStack.count + copy.count).coerceAtMost(slotLimit)
|
|
||||||
val diff = newCount - slotStack.count
|
|
||||||
|
|
||||||
if (!simulate) {
|
|
||||||
slotStack.count = newCount
|
|
||||||
setChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
copy.shrink(diff)
|
|
||||||
|
|
||||||
if (copy.isEmpty) {
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// двигаем в пустые слоты
|
|
||||||
i = slots.intIterator()
|
|
||||||
|
|
||||||
while (i.hasNext()) {
|
|
||||||
val slot = i.nextInt()
|
|
||||||
|
|
||||||
if (this[slot].isEmpty) {
|
|
||||||
val diff = copy.count.coerceAtMost(maxStackSize.coerceAtMost(copy.maxStackSize))
|
|
||||||
|
|
||||||
if (!simulate) {
|
|
||||||
val copyToPut = copy.copy()
|
|
||||||
copyToPut.count = diff
|
|
||||||
this[slot] = copyToPut
|
|
||||||
setChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
copy.shrink(diff)
|
|
||||||
|
|
||||||
if (copy.isEmpty) {
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Container.vanishCursedItems() {
|
fun Container.vanishCursedItems() {
|
||||||
@ -312,12 +237,18 @@ fun Container.sortWithIndices(sortedSlots: IntCollection) {
|
|||||||
if (value in 0 until containerSize && seen.add(value)) {
|
if (value in 0 until containerSize && seen.add(value)) {
|
||||||
val slot = containerSlot(value)
|
val slot = containerSlot(value)
|
||||||
|
|
||||||
if (
|
val condition: Boolean
|
||||||
slot.isNotEmpty &&
|
|
||||||
!slot.isForbiddenForAutomation &&
|
if (slot is IFilteredContainerSlot) {
|
||||||
slot.item.count <= slot.getMaxStackSize() &&
|
condition = slot.isNotEmpty &&
|
||||||
(!slot.hasFilter || slot.getFilter() != slot.item.item || slot.getMaxStackSize() > 1)
|
!slot.isForbiddenForAutomation &&
|
||||||
) {
|
slot.item.count <= slot.maxStackSize(slot.item) &&
|
||||||
|
(!slot.hasFilter || slot.filter != slot.item.item || slot.maxStackSize(slot.item) > 1)
|
||||||
|
} else {
|
||||||
|
condition = slot.isNotEmpty && slot.item.count <= slot.maxStackSize(slot.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
valid.add(slot)
|
valid.add(slot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,7 +266,7 @@ fun Container.computeSortedIndices(comparator: Comparator<ItemStack> = ItemStack
|
|||||||
if (isEmpty)
|
if (isEmpty)
|
||||||
return IntList.of()
|
return IntList.of()
|
||||||
|
|
||||||
val slots = slotIterator().filter { !it.isForbiddenForAutomation && it.getMaxStackSize() >= it.item.count }.toList()
|
val slots = slotIterator().filter { (it !is IFilteredContainerSlot || !it.isForbiddenForAutomation) && it.maxStackSize(it.item) >= it.item.count }.toList()
|
||||||
|
|
||||||
if (slots.isEmpty())
|
if (slots.isEmpty())
|
||||||
return IntList.of()
|
return IntList.of()
|
||||||
|
171
src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerSlot.kt
Normal file
171
src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerSlot.kt
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import net.minecraft.core.HolderLookup
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.nbt.NbtOps
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
|
import ru.dbotthepony.mc.otm.core.registryName
|
||||||
|
import ru.dbotthepony.mc.otm.data.getOrNull
|
||||||
|
|
||||||
|
open class ContainerSlot(
|
||||||
|
final override val container: SlottedContainer,
|
||||||
|
final override val slot: Int
|
||||||
|
) : ISlottedContainerSlot, INBTSerializable<CompoundTag> {
|
||||||
|
private var _item: ItemStack = ItemStack.EMPTY
|
||||||
|
|
||||||
|
final override var item: ItemStack
|
||||||
|
get() = _item
|
||||||
|
set(value) {
|
||||||
|
_item = value
|
||||||
|
setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var observedItem = ItemStack.EMPTY
|
||||||
|
|
||||||
|
// called from inside setChanged
|
||||||
|
protected open fun notifyChanged(old: ItemStack) {}
|
||||||
|
|
||||||
|
final override fun setChanged() {
|
||||||
|
observeChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when slot needs to be cleared of any data present
|
||||||
|
*/
|
||||||
|
open fun clear() {
|
||||||
|
_item = ItemStack.EMPTY
|
||||||
|
|
||||||
|
if (observedItem.isNotEmpty) {
|
||||||
|
notifyChanged(observedItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
observedItem = ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observeChanges(): Boolean {
|
||||||
|
if (observedItem.count != item.count || !ItemStack.isSameItemSameComponents(item, observedItem)) {
|
||||||
|
notifyChanged(observedItem)
|
||||||
|
observedItem = item.copy()
|
||||||
|
container.notifyChanged()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(): ItemStack {
|
||||||
|
val item = item
|
||||||
|
|
||||||
|
if (item.isEmpty) {
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
} else {
|
||||||
|
this.item = ItemStack.EMPTY
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val maxStackSize: Int
|
||||||
|
get() = Item.DEFAULT_MAX_STACK_SIZE
|
||||||
|
|
||||||
|
override fun remove(count: Int): ItemStack {
|
||||||
|
val item = item
|
||||||
|
|
||||||
|
if (item.isEmpty) {
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.count >= count) {
|
||||||
|
this.item = ItemStack.EMPTY
|
||||||
|
return item
|
||||||
|
} else {
|
||||||
|
val split = item.split(count)
|
||||||
|
setChanged()
|
||||||
|
return split
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serializeNBT(provider: HolderLookup.Provider): CompoundTag {
|
||||||
|
return CompoundTag().also {
|
||||||
|
it["item"] = ItemStack.OPTIONAL_CODEC.encodeStart(provider.createSerializationContext(NbtOps.INSTANCE), item)
|
||||||
|
.getOrThrow { RuntimeException("Unable to serialize $item in slot $slot: $it") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeNBT(provider: HolderLookup.Provider, nbt: CompoundTag) {
|
||||||
|
_item = ItemStack.OPTIONAL_CODEC.decode(provider.createSerializationContext(NbtOps.INSTANCE), nbt["item"])
|
||||||
|
.ifError { LOGGER.error("Unable to deserialize item at slot $slot: ${it.message()}") }
|
||||||
|
.getOrNull()?.first ?: ItemStack.EMPTY
|
||||||
|
|
||||||
|
observedItem = item.copy()
|
||||||
|
notifyChanged(ItemStack.EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Simple(
|
||||||
|
protected val listener: (new: ItemStack, old: ItemStack) -> Unit,
|
||||||
|
protected val maxStackSize: Int = Item.DEFAULT_MAX_STACK_SIZE,
|
||||||
|
) : SlottedContainerBuilder.SlotProvider {
|
||||||
|
protected open inner class Instance(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
|
||||||
|
override val maxStackSize: Int
|
||||||
|
get() = this@Simple.maxStackSize
|
||||||
|
|
||||||
|
override fun notifyChanged(old: ItemStack) {
|
||||||
|
super.notifyChanged(old)
|
||||||
|
listener(item, old)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(container: SlottedContainer, index: Int): ContainerSlot {
|
||||||
|
return Instance(container, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Filtered(
|
||||||
|
listener: (new: ItemStack, old: ItemStack) -> Unit,
|
||||||
|
maxStackSize: Int = Item.DEFAULT_MAX_STACK_SIZE,
|
||||||
|
) : Simple(listener, maxStackSize) {
|
||||||
|
protected open inner class Instance(container: SlottedContainer, slot: Int) : Simple.Instance(container, slot), IFilteredSlottedContainerSlot {
|
||||||
|
override var filter: Item? = null
|
||||||
|
set(value) {
|
||||||
|
if (field !== value) {
|
||||||
|
field = value
|
||||||
|
container.notifyChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
super.clear()
|
||||||
|
filter = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serializeNBT(provider: HolderLookup.Provider): CompoundTag {
|
||||||
|
return super.serializeNBT(provider).also {
|
||||||
|
if (filter != null)
|
||||||
|
it["filter"] = filter!!.registryName!!.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeNBT(provider: HolderLookup.Provider, nbt: CompoundTag) {
|
||||||
|
super.deserializeNBT(provider, nbt)
|
||||||
|
|
||||||
|
if ("filter" in nbt) {
|
||||||
|
filter = BuiltInRegistries.ITEM.get(ResourceLocation.parse(nbt.getString("filter")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(container: SlottedContainer, index: Int): ContainerSlot {
|
||||||
|
return Instance(container, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.container
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
import net.minecraft.world.Container
|
import net.minecraft.world.Container
|
||||||
import net.minecraft.world.item.Item
|
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.item.Items
|
|
||||||
import ru.dbotthepony.kommons.util.Delegate
|
import ru.dbotthepony.kommons.util.Delegate
|
||||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
|
|
||||||
@ -15,54 +13,67 @@ interface IContainerSlot : Delegate<ItemStack> {
|
|||||||
val slot: Int
|
val slot: Int
|
||||||
val container: Container
|
val container: Container
|
||||||
|
|
||||||
|
fun setChanged()
|
||||||
|
var item: ItemStack
|
||||||
|
|
||||||
operator fun component1() = slot
|
operator fun component1() = slot
|
||||||
operator fun component2() = item
|
operator fun component2() = item
|
||||||
|
|
||||||
fun getMaxStackSize(item: ItemStack = this.item): Int {
|
/**
|
||||||
return container.maxStackSize
|
* Max stack size regardless of item
|
||||||
}
|
*
|
||||||
|
* Prefer to use ItemStack version instead
|
||||||
val isForbiddenForAutomation: Boolean get() {
|
*/
|
||||||
return getFilter() === Items.AIR
|
val maxStackSize: Int
|
||||||
}
|
|
||||||
|
|
||||||
fun getFilter(): Item?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whenever the filter was set. Returns false only if container can't be filtered.
|
* Max amount of [item] that can be stored in this slot.
|
||||||
|
*
|
||||||
|
* This may be larger or smaller than value returned [ItemStack.getMaxStackSize],
|
||||||
|
* and as such returned value by this method should be ground truth
|
||||||
*/
|
*/
|
||||||
fun setFilter(filter: Item? = null): Boolean
|
fun maxStackSize(item: ItemStack): Int {
|
||||||
|
return maxStackSize.coerceAtMost(item.maxStackSize)
|
||||||
val hasFilter: Boolean
|
|
||||||
get() = getFilter() != null
|
|
||||||
|
|
||||||
fun remove() {
|
|
||||||
container[slot] = ItemStack.EMPTY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remove(count: Int): ItemStack {
|
fun remove(): ItemStack
|
||||||
return container.removeItem(slot, count)
|
fun remove(count: Int): ItemStack
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(): ItemStack {
|
override fun get(): ItemStack {
|
||||||
return container[slot]
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(t: ItemStack) {
|
override fun accept(t: ItemStack) {
|
||||||
container[slot] = t
|
item = t
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setChanged() {
|
|
||||||
container.setChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
var item: ItemStack
|
|
||||||
get() = container[slot]
|
|
||||||
set(value) { container[slot] = value }
|
|
||||||
|
|
||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
get() = container[slot].isEmpty
|
get() = item.isEmpty
|
||||||
|
|
||||||
val isNotEmpty: Boolean
|
val isNotEmpty: Boolean
|
||||||
get() = container[slot].isNotEmpty
|
get() = item.isNotEmpty
|
||||||
|
|
||||||
|
class Simple(override val slot: Int, override val container: Container) : IContainerSlot {
|
||||||
|
override fun setChanged() {
|
||||||
|
container.setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override var item: ItemStack
|
||||||
|
get() = container[slot]
|
||||||
|
set(value) { container[slot] = value }
|
||||||
|
override val maxStackSize: Int
|
||||||
|
get() = container.maxStackSize
|
||||||
|
|
||||||
|
override fun remove(count: Int): ItemStack {
|
||||||
|
return container.removeItem(slot, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(): ItemStack {
|
||||||
|
return container.removeItemNoUpdate(slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun maxStackSize(item: ItemStack): Int {
|
||||||
|
return maxStackSize.coerceAtMost(item.maxStackSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,338 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSet
|
||||||
|
import net.minecraft.world.Container
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.minecraft.world.item.Items
|
||||||
|
import net.minecraft.world.item.crafting.RecipeInput
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.any
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.filter
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Backward-compatible" enhanced container interface, where all methods can be derived/emulated from existing [IContainer] ([Container]) code
|
||||||
|
*
|
||||||
|
* This is useful because it allows to interact with actually enhanced and regular containers through unified interface,
|
||||||
|
* and actual implementations of this interface are likely to provide efficient method implementations in place of derived/emulated ones.
|
||||||
|
*/
|
||||||
|
interface IEnhancedContainer : IContainer, RecipeInput, Iterable<ItemStack> {
|
||||||
|
fun containerSlot(slot: Int): IContainerSlot {
|
||||||
|
return IContainerSlot.Simple(slot, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun slotIterator(): Iterator<IContainerSlot> {
|
||||||
|
return (0 until containerSize).iterator().map { containerSlot(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nonEmptySlotIterator(): Iterator<IContainerSlot> {
|
||||||
|
return (0 until containerSize).iterator().map { containerSlot(it) }.filter { it.isNotEmpty }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun slotIterator(allowedSlots: IntSet, predicate: Predicate<ItemStack>): IntIterator {
|
||||||
|
return object : IntIterator() {
|
||||||
|
private val parent = allowedSlots.intIterator()
|
||||||
|
private var foundNext = false
|
||||||
|
private var next = -1
|
||||||
|
|
||||||
|
private fun findNext() {
|
||||||
|
if (!foundNext) {
|
||||||
|
foundNext = true
|
||||||
|
next = -1
|
||||||
|
|
||||||
|
while (parent.hasNext()) {
|
||||||
|
val i = parent.nextInt()
|
||||||
|
|
||||||
|
if (predicate.test(this@IEnhancedContainer[i])) {
|
||||||
|
next = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun nextInt(): Int {
|
||||||
|
findNext()
|
||||||
|
|
||||||
|
if (next == -1)
|
||||||
|
throw NoSuchElementException()
|
||||||
|
|
||||||
|
foundNext = false
|
||||||
|
val next = next
|
||||||
|
this.next = -1
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
findNext()
|
||||||
|
return next != -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emptySlotIterator(allowedSlots: IntSet = slotRange): IntIterator {
|
||||||
|
return slotIterator(allowedSlots) { it.isEmpty }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nonEmptySlotIterator(allowedSlots: IntSet = slotRange): IntIterator {
|
||||||
|
return slotIterator(allowedSlots) { it.isNotEmpty }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun slotWithItemIterator(item: Item, allowedSlots: IntSet = slotRange): IntIterator {
|
||||||
|
return slotIterator(allowedSlots) { it.isNotEmpty && it.item === item }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextEmptySlot(startIndex: Int): Int {
|
||||||
|
for (i in startIndex until containerSize) {
|
||||||
|
if (this[i].isEmpty) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextNonEmptySlot(startIndex: Int): Int {
|
||||||
|
for (i in startIndex until containerSize) {
|
||||||
|
if (this[i].isNotEmpty) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextSlotWithItem(startIndex: Int, item: Item): Int {
|
||||||
|
var find = nextNonEmptySlot(startIndex)
|
||||||
|
|
||||||
|
while (find != -1 && this[find].item !== item)
|
||||||
|
find = nextNonEmptySlot(find + 1)
|
||||||
|
|
||||||
|
return find
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setChanged(slot: Int) {
|
||||||
|
return setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
|
||||||
|
return maxStackSize
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<ItemStack> {
|
||||||
|
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return super.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
val hasEmptySlots: Boolean get() {
|
||||||
|
for (i in 0 until containerSize) {
|
||||||
|
if (this[i].isEmpty) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun size(): Int {
|
||||||
|
return containerSize
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun countItem(item: Item): Int {
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
for (stack in this) {
|
||||||
|
if (stack.item === item) {
|
||||||
|
count += stack.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasAnyOf(items: Set<Item>): Boolean {
|
||||||
|
if (Items.AIR in items && hasEmptySlots)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return iterator().any { it.item in items }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
|
||||||
|
if (predicate.test(ItemStack.EMPTY) && hasEmptySlots)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return iterator().any(predicate)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toList(): MutableList<ItemStack> {
|
||||||
|
val list = ArrayList<ItemStack>(containerSize)
|
||||||
|
|
||||||
|
for (i in 0 until containerSize) {
|
||||||
|
list.add(this[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addItem(stack: ItemStack, simulate: Boolean, slots: IntSet = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null): ItemStack {
|
||||||
|
if (stack.isEmpty || slots.isEmpty())
|
||||||
|
return stack
|
||||||
|
|
||||||
|
val copy = stack.copy()
|
||||||
|
|
||||||
|
// двигаем в одинаковые слоты
|
||||||
|
for (slot in slotWithItemIterator(stack.item, slots)) {
|
||||||
|
if (ItemStack.isSameItemSameComponents(this[slot], copy)) {
|
||||||
|
val slotStack = this[slot]
|
||||||
|
val slotLimit = getMaxStackSize(slot, slotStack)
|
||||||
|
|
||||||
|
if (slotStack.count < slotLimit) {
|
||||||
|
val newCount = (slotStack.count + copy.count).coerceAtMost(slotLimit)
|
||||||
|
val diff = newCount - slotStack.count
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
slotStack.count = newCount
|
||||||
|
setChanged(slot)
|
||||||
|
|
||||||
|
if (popTime != null) {
|
||||||
|
slotStack.popTime = popTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy.shrink(diff)
|
||||||
|
|
||||||
|
if (copy.isEmpty) {
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!onlyIntoExisting) {
|
||||||
|
// двигаем в пустые слоты
|
||||||
|
for (slot in emptySlotIterator(slots)) {
|
||||||
|
val diff = copy.count.coerceAtMost(getMaxStackSize(slot, stack))
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
val copyToPut = copy.copy()
|
||||||
|
copyToPut.count = diff
|
||||||
|
this[slot] = copyToPut
|
||||||
|
setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
copy.shrink(diff)
|
||||||
|
|
||||||
|
if (copy.isEmpty)
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlike [addItem], modifies original [stack]
|
||||||
|
*
|
||||||
|
* @return Whenever [stack] was modified
|
||||||
|
*/
|
||||||
|
fun consumeItem(stack: ItemStack, simulate: Boolean, slots: IntSet = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null): Boolean {
|
||||||
|
if (stack.isEmpty || slots.isEmpty())
|
||||||
|
return false
|
||||||
|
|
||||||
|
val result = addItem(stack, simulate, slots, onlyIntoExisting, popTime)
|
||||||
|
if (!simulate) stack.count = result.count
|
||||||
|
return result.count != stack.count
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fullyAddItem(stack: ItemStack, slots: IntSet = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null): Boolean {
|
||||||
|
if (!addItem(stack, true, slots, onlyIntoExisting, popTime).isEmpty)
|
||||||
|
return false
|
||||||
|
|
||||||
|
return addItem(stack, false, slots, onlyIntoExisting, popTime).isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Wrapper(private val parent: Container) : IEnhancedContainer {
|
||||||
|
override fun clearContent() {
|
||||||
|
return parent.clearContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setChanged() {
|
||||||
|
return parent.setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getContainerSize(): Int {
|
||||||
|
return parent.containerSize
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(slot: Int): ItemStack {
|
||||||
|
return parent.getItem(slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeItem(slot: Int, amount: Int): ItemStack {
|
||||||
|
return parent.removeItem(slot, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeItemNoUpdate(slot: Int): ItemStack {
|
||||||
|
return parent.removeItemNoUpdate(slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setItem(slot: Int, itemStack: ItemStack) {
|
||||||
|
return parent.setItem(slot, itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stillValid(player: Player): Boolean {
|
||||||
|
return parent.stillValid(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMaxStackSize(): Int {
|
||||||
|
return parent.maxStackSize
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startOpen(player: Player) {
|
||||||
|
parent.startOpen(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stopOpen(player: Player) {
|
||||||
|
parent.stopOpen(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
|
||||||
|
return parent.canPlaceItem(slot, itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
|
||||||
|
return parent.canTakeItem(container, slot, itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return parent.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
|
||||||
|
return parent.hasAnyMatching(predicate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasAnyOf(items: Set<Item>): Boolean {
|
||||||
|
return parent.hasAnyOf(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun countItem(item: Item): Int {
|
||||||
|
return parent.countItem(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun wrap(other: Container): IEnhancedContainer {
|
||||||
|
if (other is IEnhancedContainer)
|
||||||
|
return other
|
||||||
|
|
||||||
|
return Wrapper(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.minecraft.world.item.Items
|
||||||
|
|
||||||
|
interface IFilteredContainerSlot : IContainerSlot {
|
||||||
|
var filter: Item?
|
||||||
|
|
||||||
|
val isForbiddenForAutomation: Boolean get() {
|
||||||
|
return filter === Items.AIR
|
||||||
|
}
|
||||||
|
|
||||||
|
val hasFilter: Boolean
|
||||||
|
get() = filter != null
|
||||||
|
|
||||||
|
|
||||||
|
fun testSlotFilter(itemStack: ItemStack): Boolean {
|
||||||
|
return testSlotFilter(itemStack.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSlotFilter(item: Item): Boolean {
|
||||||
|
if (filter == null) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return filter === item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
|
||||||
|
interface IFilteredSlottedContainerSlot : IFilteredContainerSlot, ISlottedContainerSlot {
|
||||||
|
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
|
||||||
|
return super.canAutomationPlaceItem(itemStack) && testSlotFilter(itemStack)
|
||||||
|
}
|
||||||
|
}
|
@ -50,27 +50,6 @@ interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class ContainerSlot(override val slot: Int, override val container: IMatteryContainer) : IContainerSlot {
|
|
||||||
override val isForbiddenForAutomation: Boolean
|
|
||||||
get() = container.isSlotForbiddenForAutomation(slot)
|
|
||||||
|
|
||||||
override fun getFilter(): Item? {
|
|
||||||
return container.getSlotFilter(slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setFilter(filter: Item?): Boolean {
|
|
||||||
return container.setSlotFilter(slot, filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getMaxStackSize(item: ItemStack): Int {
|
|
||||||
return container.getMaxStackSize(slot, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setChanged() {
|
|
||||||
container.setChanged(slot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates either non-empty slots of container or all slots of container
|
* Iterates either non-empty slots of container or all slots of container
|
||||||
*/
|
*/
|
||||||
@ -83,7 +62,7 @@ interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun containerSlot(slot: Int): IContainerSlot {
|
fun containerSlot(slot: Int): IContainerSlot {
|
||||||
return ContainerSlot(slot, this)
|
return IContainerSlot.Simple(slot, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasSlotFilter(slot: Int) = getSlotFilter(slot) !== null
|
fun hasSlotFilter(slot: Int) = getSlotFilter(slot) !== null
|
||||||
@ -234,22 +213,4 @@ interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
|
|||||||
|
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shrink(slot: Int, amount: Int): Boolean {
|
|
||||||
if (slot < 0 || slot > size())
|
|
||||||
return false
|
|
||||||
|
|
||||||
val item = this[slot]
|
|
||||||
if (item.isEmpty)
|
|
||||||
return false
|
|
||||||
|
|
||||||
if (item.count <= amount) {
|
|
||||||
this[slot] = ItemStack.EMPTY
|
|
||||||
} else {
|
|
||||||
item.shrink(amount)
|
|
||||||
setChanged(slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,220 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSet
|
||||||
|
import net.minecraft.world.Container
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.neoforged.neoforge.items.IItemHandler
|
||||||
|
import ru.dbotthepony.kommons.collect.any
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.filter
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container which revolve around embedding slot objects rather than providing direct item access,
|
||||||
|
* and subsequently fully implement [IItemHandler]
|
||||||
|
*/
|
||||||
|
interface ISlottedContainer : IEnhancedContainer, IItemHandler {
|
||||||
|
override fun containerSlot(slot: Int): ISlottedContainerSlot
|
||||||
|
|
||||||
|
override fun slotIterator(): Iterator<ISlottedContainerSlot> {
|
||||||
|
return (0 until containerSize).iterator().map { containerSlot(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun nonEmptySlotIterator(): Iterator<ISlottedContainerSlot> {
|
||||||
|
return (0 until containerSize).iterator().map { containerSlot(it) }.filter { it.isNotEmpty }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setChanged(slot: Int) {
|
||||||
|
containerSlot(slot).setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
|
||||||
|
return containerSlot(slot).maxStackSize(itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(slot: Int): ItemStack {
|
||||||
|
return containerSlot(slot).item
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeItem(slot: Int, amount: Int): ItemStack {
|
||||||
|
return containerSlot(slot).remove(amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeItemNoUpdate(slot: Int): ItemStack {
|
||||||
|
return containerSlot(slot).remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setItem(slot: Int, itemStack: ItemStack) {
|
||||||
|
containerSlot(slot).item = itemStack
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
|
||||||
|
return containerSlot(slot).canAutomationPlaceItem(itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
|
||||||
|
return containerSlot(slot).canAutomationTakeItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSlots() = containerSize
|
||||||
|
override fun getStackInSlot(slot: Int) = containerSlot(slot).item
|
||||||
|
|
||||||
|
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
|
||||||
|
return containerSlot(slot).insertItem(stack, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
||||||
|
return containerSlot(slot).extractItem(amount, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSlotLimit(slot: Int): Int {
|
||||||
|
return containerSlot(slot).maxStackSize
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
|
||||||
|
return canPlaceItem(slot, stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addItem(stack: ItemStack, simulate: Boolean, filterPass: Boolean, slots: IntSet, onlyIntoExisting: Boolean, popTime: Int?, ignoreFilters: Boolean): ItemStack {
|
||||||
|
if (stack.isEmpty || slots.isEmpty())
|
||||||
|
return stack
|
||||||
|
|
||||||
|
// двигаем в одинаковые слоты
|
||||||
|
for (i in slotWithItemIterator(stack.item, slots)) {
|
||||||
|
val slot = containerSlot(i)
|
||||||
|
|
||||||
|
val condition: Boolean
|
||||||
|
|
||||||
|
if (slot is IFilteredContainerSlot) {
|
||||||
|
condition = (ignoreFilters || !slot.isForbiddenForAutomation) &&
|
||||||
|
ItemStack.isSameItemSameComponents(slot.item, stack) &&
|
||||||
|
(ignoreFilters || !filterPass && !slot.hasFilter || filterPass && slot.hasFilter && slot.testSlotFilter(stack))
|
||||||
|
} else {
|
||||||
|
condition = (ignoreFilters || !filterPass) && ItemStack.isSameItemSameComponents(slot.item, stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
val slotLimit = slot.maxStackSize(slot.item)
|
||||||
|
|
||||||
|
if (slot.item.count < slotLimit) {
|
||||||
|
val newCount = (slot.item.count + stack.count).coerceAtMost(slotLimit)
|
||||||
|
val diff = newCount - slot.item.count
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
slot.item.count = newCount
|
||||||
|
slot.setChanged()
|
||||||
|
|
||||||
|
if (popTime != null) {
|
||||||
|
slot.item.popTime = popTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.shrink(diff)
|
||||||
|
|
||||||
|
if (stack.isEmpty)
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!onlyIntoExisting) {
|
||||||
|
for (i in emptySlotIterator(slots)) {
|
||||||
|
val slot = containerSlot(i)
|
||||||
|
|
||||||
|
val condition: Boolean
|
||||||
|
|
||||||
|
if (slot is IFilteredContainerSlot) {
|
||||||
|
condition = (ignoreFilters || !slot.isForbiddenForAutomation) &&
|
||||||
|
(ignoreFilters || !filterPass && !slot.hasFilter || filterPass && slot.hasFilter && slot.testSlotFilter(stack))
|
||||||
|
} else {
|
||||||
|
condition = ignoreFilters || !filterPass
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
val diff = stack.count.coerceAtMost(slot.maxStackSize(stack))
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
val copyToPut = stack.copy()
|
||||||
|
copyToPut.count = diff
|
||||||
|
slot.item = copyToPut
|
||||||
|
|
||||||
|
if (popTime != null) {
|
||||||
|
copyToPut.popTime = popTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.shrink(diff)
|
||||||
|
|
||||||
|
if (stack.isEmpty)
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hint used internally by [ISlottedContainer] to potentially speed up default method implementations
|
||||||
|
*/
|
||||||
|
val hasFilterableSlots: Boolean
|
||||||
|
get() = slotIterator().any { it is IFilteredContainerSlot }
|
||||||
|
|
||||||
|
fun addItem(stack: ItemStack, simulate: Boolean, slots: IntSet = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean): ItemStack {
|
||||||
|
if (stack.isEmpty || slots.isEmpty())
|
||||||
|
return stack
|
||||||
|
|
||||||
|
if (ignoreFilters || !hasFilterableSlots) {
|
||||||
|
return addItem(stack.copy(), simulate, filterPass = true, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = true)
|
||||||
|
} else {
|
||||||
|
var copy = addItem(stack.copy(), simulate, filterPass = true, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = false)
|
||||||
|
copy = addItem(copy, simulate, filterPass = false, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = false)
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlike [addItem], modifies original [stack]
|
||||||
|
*
|
||||||
|
* @return Whenever [stack] was modified
|
||||||
|
*/
|
||||||
|
fun consumeItem(stack: ItemStack, simulate: Boolean, slots: IntSet = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean): Boolean {
|
||||||
|
if (stack.isEmpty)
|
||||||
|
return false
|
||||||
|
|
||||||
|
val result = addItem(stack, simulate, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = ignoreFilters)
|
||||||
|
if (!simulate) stack.count = result.count
|
||||||
|
return result.count != stack.count
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fullyAddItem(stack: ItemStack, slots: IntSet = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean): Boolean {
|
||||||
|
if (!addItem(stack, true, slots, popTime = popTime, onlyIntoExisting = onlyIntoExisting, ignoreFilters = ignoreFilters).isEmpty)
|
||||||
|
return false
|
||||||
|
|
||||||
|
return addItem(stack, false, slots, popTime = popTime, onlyIntoExisting = onlyIntoExisting, ignoreFilters = ignoreFilters).isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addItem(
|
||||||
|
stack: ItemStack,
|
||||||
|
simulate: Boolean,
|
||||||
|
slots: IntSet,
|
||||||
|
onlyIntoExisting: Boolean,
|
||||||
|
popTime: Int?
|
||||||
|
): ItemStack {
|
||||||
|
return addItem(stack, simulate, slots, onlyIntoExisting, popTime, ignoreFilters = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun consumeItem(
|
||||||
|
stack: ItemStack,
|
||||||
|
simulate: Boolean,
|
||||||
|
slots: IntSet,
|
||||||
|
onlyIntoExisting: Boolean,
|
||||||
|
popTime: Int?
|
||||||
|
): Boolean {
|
||||||
|
return consumeItem(stack, simulate, slots, onlyIntoExisting, popTime, ignoreFilters = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fullyAddItem(stack: ItemStack, slots: IntSet, onlyIntoExisting: Boolean, popTime: Int?): Boolean {
|
||||||
|
return fullyAddItem(stack, slots, onlyIntoExisting, popTime, ignoreFilters = false)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.neoforged.neoforge.items.IItemHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slot of [ISlottedContainer], with additional methods to implement interaction behavior for both for players and mechanisms
|
||||||
|
*/
|
||||||
|
interface ISlottedContainerSlot : IContainerSlot {
|
||||||
|
override val container: ISlottedContainer
|
||||||
|
|
||||||
|
fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canAutomationTakeItem(desired: Int = item.count): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun modifyAutomationPlaceCount(itemStack: ItemStack): Int {
|
||||||
|
return itemStack.count
|
||||||
|
}
|
||||||
|
|
||||||
|
fun modifyAutomationExtractionCount(desired: Int): Int {
|
||||||
|
return desired
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slot-specific implementation for [IItemHandler.insertItem]
|
||||||
|
*/
|
||||||
|
fun insertItem(stack: ItemStack, simulate: Boolean): ItemStack {
|
||||||
|
if (!canAutomationPlaceItem(stack))
|
||||||
|
return stack
|
||||||
|
|
||||||
|
var amount = modifyAutomationPlaceCount(stack)
|
||||||
|
|
||||||
|
if (amount <= 0)
|
||||||
|
return stack
|
||||||
|
|
||||||
|
if (item.isEmpty) {
|
||||||
|
amount = stack.count.coerceAtMost(maxStackSize(stack)).coerceAtMost(amount)
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
item = stack.copyWithCount(amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack.count <= amount) {
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
} else {
|
||||||
|
return stack.copyWithCount(stack.count - amount)
|
||||||
|
}
|
||||||
|
} else if (item.isStackable && maxStackSize(item) > item.count && ItemStack.isSameItemSameComponents(item, stack)) {
|
||||||
|
val newCount = maxStackSize(item).coerceAtMost(item.count + stack.count.coerceAtMost(amount))
|
||||||
|
val diff = newCount - item.count
|
||||||
|
|
||||||
|
if (diff != 0) {
|
||||||
|
if (!simulate) {
|
||||||
|
item.grow(diff)
|
||||||
|
setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
val copy = stack.copy()
|
||||||
|
copy.shrink(diff)
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extractItem(amount: Int, simulate: Boolean): ItemStack {
|
||||||
|
if (amount <= 0 || !canAutomationTakeItem(amount) || item.isEmpty)
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
|
||||||
|
@Suppress("name_shadowing")
|
||||||
|
val amount = modifyAutomationExtractionCount(amount)
|
||||||
|
if (amount <= 0 || !canAutomationTakeItem(amount)) return ItemStack.EMPTY
|
||||||
|
|
||||||
|
val minimal = amount.coerceAtMost(item.count)
|
||||||
|
val copy = item.copy()
|
||||||
|
copy.count = minimal
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
if (item.count == minimal) {
|
||||||
|
item = ItemStack.EMPTY
|
||||||
|
} else {
|
||||||
|
item.shrink(minimal)
|
||||||
|
}
|
||||||
|
|
||||||
|
setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
}
|
@ -426,22 +426,32 @@ open class MatteryContainer(var listener: ContainerListener, private val size: I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class Slot(override val slot: Int) : IContainerSlot {
|
private inner class Slot(override val slot: Int) : IFilteredContainerSlot {
|
||||||
override val container: Container
|
override val container: Container
|
||||||
get() = this@MatteryContainer
|
get() = this@MatteryContainer
|
||||||
|
|
||||||
|
override var item: ItemStack
|
||||||
|
get() = this@MatteryContainer[slot]
|
||||||
|
set(value) { this@MatteryContainer[slot] = value }
|
||||||
|
override val maxStackSize: Int
|
||||||
|
get() = this@MatteryContainer.maxStackSize
|
||||||
|
|
||||||
|
override fun remove(): ItemStack {
|
||||||
|
return removeItemNoUpdate(slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(count: Int): ItemStack {
|
||||||
|
return removeItem(slot, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
override var filter: Item?
|
||||||
|
get() = getSlotFilter(slot)
|
||||||
|
set(value) { setSlotFilter(slot, value) }
|
||||||
|
|
||||||
override val isForbiddenForAutomation: Boolean
|
override val isForbiddenForAutomation: Boolean
|
||||||
get() = isSlotForbiddenForAutomation(slot)
|
get() = isSlotForbiddenForAutomation(slot)
|
||||||
|
|
||||||
override fun getFilter(): Item? {
|
override fun maxStackSize(item: ItemStack): Int {
|
||||||
return getSlotFilter(slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setFilter(filter: Item?): Boolean {
|
|
||||||
return setSlotFilter(slot, filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getMaxStackSize(item: ItemStack): Int {
|
|
||||||
return getMaxStackSize(slot, item)
|
return getMaxStackSize(slot, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,12 +460,12 @@ open class MatteryContainer(var listener: ContainerListener, private val size: I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final override fun slotIterator(): kotlin.collections.Iterator<IContainerSlot> {
|
final override fun slotIterator(): kotlin.collections.Iterator<IFilteredContainerSlot> {
|
||||||
indicesReferenced = true
|
indicesReferenced = true
|
||||||
return nonEmptyIndices.iterator().map { Slot(it) }
|
return nonEmptyIndices.iterator().map { Slot(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
final override fun slotIterator(nonEmpty: Boolean): kotlin.collections.Iterator<IContainerSlot> {
|
final override fun slotIterator(nonEmpty: Boolean): kotlin.collections.Iterator<IFilteredContainerSlot> {
|
||||||
if (!nonEmpty) {
|
if (!nonEmpty) {
|
||||||
return (0 until size).iterator().map { Slot(it) }
|
return (0 until size).iterator().map { Slot(it) }
|
||||||
} else if (isEmpty) {
|
} else if (isEmpty) {
|
||||||
@ -466,7 +476,7 @@ open class MatteryContainer(var listener: ContainerListener, private val size: I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final override fun containerSlot(slot: Int): IContainerSlot {
|
final override fun containerSlot(slot: Int): IFilteredContainerSlot {
|
||||||
return Slot(slot)
|
return Slot(slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,177 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||||
|
import net.minecraft.core.HolderLookup
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.nbt.ListTag
|
||||||
|
import net.minecraft.nbt.NbtOps
|
||||||
|
import net.minecraft.nbt.Tag
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import ru.dbotthepony.mc.otm.data.codec.minRange
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
class SlottedContainer(
|
||||||
|
slots: Collection<SlottedContainerBuilder.SlotProvider>,
|
||||||
|
private val stillValid: Predicate<Player>,
|
||||||
|
private val globalChangeListeners: Array<Runnable>
|
||||||
|
) : ISlottedContainer, INBTSerializable<Tag> {
|
||||||
|
private val slots: Array<ContainerSlot>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val itr = slots.iterator()
|
||||||
|
this.slots = Array(slots.size) { itr.next().create(this, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val hasFilterableSlots: Boolean = this.slots.any { it is IFilteredContainerSlot }
|
||||||
|
private var suppressListeners = false
|
||||||
|
|
||||||
|
override fun clearContent() {
|
||||||
|
suppressListeners = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
slots.forEach { it.remove() }
|
||||||
|
notifyChanged()
|
||||||
|
} finally {
|
||||||
|
suppressListeners = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containerSlot(slot: Int): ISlottedContainerSlot {
|
||||||
|
return slots[slot]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyChanged() {
|
||||||
|
if (suppressListeners) return
|
||||||
|
globalChangeListeners.forEach { it.run() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by outside code (vanilla and other unaware mods)
|
||||||
|
override fun setChanged() {
|
||||||
|
suppressListeners = true
|
||||||
|
var hasChanges = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
slots.forEach { hasChanges = it.observeChanges() || hasChanges }
|
||||||
|
} finally {
|
||||||
|
suppressListeners = false
|
||||||
|
|
||||||
|
if (hasChanges)
|
||||||
|
notifyChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getContainerSize(): Int {
|
||||||
|
return slots.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stillValid(player: Player): Boolean {
|
||||||
|
return stillValid.test(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class LegacySerializedItem(val item: ItemStack, val slot: Int) {
|
||||||
|
companion object {
|
||||||
|
val CODEC: Codec<LegacySerializedItem> = RecordCodecBuilder.create {
|
||||||
|
it.group(
|
||||||
|
ItemStack.OPTIONAL_CODEC.fieldOf("item").forGetter { it.item },
|
||||||
|
Codec.INT.minRange(0).fieldOf("slot").forGetter { it.slot },
|
||||||
|
).apply(it, ::LegacySerializedItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class LegacySerializedFilter(val item: Item, val slot: Int) {
|
||||||
|
companion object {
|
||||||
|
val CODEC: Codec<LegacySerializedFilter> = RecordCodecBuilder.create {
|
||||||
|
it.group(
|
||||||
|
BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter { it.item },
|
||||||
|
Codec.INT.minRange(0).fieldOf("slot").forGetter { it.slot },
|
||||||
|
).apply(it, ::LegacySerializedFilter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class LegacySerializedState(
|
||||||
|
val items: List<LegacySerializedItem>,
|
||||||
|
val filters: List<LegacySerializedFilter>
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val CODEC: Codec<LegacySerializedState> = RecordCodecBuilder.create {
|
||||||
|
it.group(
|
||||||
|
Codec.list(LegacySerializedItem.CODEC).fieldOf("items").forGetter { it.items },
|
||||||
|
Codec.list(LegacySerializedFilter.CODEC).fieldOf("filters").forGetter { it.filters },
|
||||||
|
).apply(it, ::LegacySerializedState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val lostItems = ArrayList<CompoundTag>()
|
||||||
|
|
||||||
|
override fun serializeNBT(provider: HolderLookup.Provider): ListTag {
|
||||||
|
return ListTag().also {
|
||||||
|
for (slot in slots) {
|
||||||
|
it.add(slot.serializeNBT(provider))
|
||||||
|
}
|
||||||
|
|
||||||
|
it.addAll(lostItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeNBT(provider: HolderLookup.Provider, nbt: Tag) {
|
||||||
|
lostItems.clear()
|
||||||
|
slots.forEach { it.clear() }
|
||||||
|
|
||||||
|
if (nbt is CompoundTag) {
|
||||||
|
// legacy container
|
||||||
|
LegacySerializedState.CODEC.decode(provider.createSerializationContext(NbtOps.INSTANCE), nbt)
|
||||||
|
.resultOrPartial { LOGGER.error("Error deserializing container: $it") }
|
||||||
|
.ifPresent {
|
||||||
|
val (items, filters) = it.first
|
||||||
|
|
||||||
|
// excessive items will be lost
|
||||||
|
for ((item, slot) in items) {
|
||||||
|
if (slot in 0 until containerSize) {
|
||||||
|
slots[slot].item = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((filter, slot) in filters) {
|
||||||
|
if (slot in 0 until containerSize) {
|
||||||
|
val getSlot = slots[slot]
|
||||||
|
|
||||||
|
if (getSlot is IFilteredContainerSlot) {
|
||||||
|
getSlot.filter = filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (nbt is ListTag) {
|
||||||
|
// normal container
|
||||||
|
for ((i, element) in nbt.withIndex()) {
|
||||||
|
if (element !is CompoundTag) {
|
||||||
|
LOGGER.error("Deserializing mattery container: Expected compound tag at $i, got $element")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i in 0 until containerSize) {
|
||||||
|
slots[i].deserializeNBT(provider, element)
|
||||||
|
} else {
|
||||||
|
lostItems.add(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.error("Unable to deserialize mattery container, expected CompoundTag or ListTag, got $nbt")
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
class SlottedContainerBuilder {
|
||||||
|
fun interface SlotProvider {
|
||||||
|
fun create(container: SlottedContainer, index: Int): ContainerSlot
|
||||||
|
}
|
||||||
|
|
||||||
|
private val slots = ArrayList<SlotProvider>()
|
||||||
|
private var stillValid = Predicate<Player> { true }
|
||||||
|
private val globalChangeListeners = ArrayList<Runnable>()
|
||||||
|
|
||||||
|
fun add(slot: SlotProvider): SlottedContainerBuilder {
|
||||||
|
slots.add(slot)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(amount: Int, provider: SlotProvider): SlottedContainerBuilder {
|
||||||
|
for (i in 0 until amount)
|
||||||
|
slots.add(provider)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stillValid(predicate: Predicate<Player>): SlottedContainerBuilder {
|
||||||
|
this.stillValid = predicate
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onChanged(listener: Runnable): SlottedContainerBuilder {
|
||||||
|
globalChangeListeners.add(listener)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copy(): SlottedContainerBuilder {
|
||||||
|
val copy = SlottedContainerBuilder()
|
||||||
|
copy.slots.addAll(slots)
|
||||||
|
copy.globalChangeListeners.addAll(globalChangeListeners)
|
||||||
|
copy.stillValid = stillValid
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): SlottedContainer {
|
||||||
|
return SlottedContainer(slots, stillValid, globalChangeListeners.toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
@ -4,52 +4,41 @@ import net.minecraft.world.Container
|
|||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import ru.dbotthepony.mc.otm.container.IContainerSlot
|
import ru.dbotthepony.mc.otm.container.IContainerSlot
|
||||||
|
import ru.dbotthepony.mc.otm.container.IEnhancedContainer
|
||||||
import ru.dbotthepony.mc.otm.container.IMatteryContainer
|
import ru.dbotthepony.mc.otm.container.IMatteryContainer
|
||||||
import ru.dbotthepony.mc.otm.container.get
|
import ru.dbotthepony.mc.otm.container.get
|
||||||
import ru.dbotthepony.mc.otm.core.collect.filter
|
import ru.dbotthepony.mc.otm.core.collect.filter
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
|
|
||||||
class SimpleContainerSlot(override val slot: Int, override val container: Container) : IContainerSlot {
|
|
||||||
init {
|
|
||||||
require(slot in 0 until container.containerSize) { "Slot out of bounds: $slot (container: $container with size ${container.containerSize})" }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFilter(): Item? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setFilter(filter: Item?): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Container.containerSlot(slot: Int): IContainerSlot {
|
fun Container.containerSlot(slot: Int): IContainerSlot {
|
||||||
if (this is IMatteryContainer) {
|
if (this is IEnhancedContainer) {
|
||||||
return containerSlot(slot)
|
return containerSlot(slot)
|
||||||
} else {
|
} else {
|
||||||
return SimpleContainerSlot(slot, this)
|
return IContainerSlot.Simple(slot, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun Container.iterator() = iterator(true)
|
operator fun Container.iterator(): Iterator<ItemStack> {
|
||||||
|
if (this is IEnhancedContainer) {
|
||||||
fun Container.iterator(nonEmpty: Boolean): Iterator<ItemStack> {
|
return iterator()
|
||||||
if (this is IMatteryContainer) {
|
} else {
|
||||||
return iterator(nonEmpty)
|
|
||||||
} else if (nonEmpty) {
|
|
||||||
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
|
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
|
||||||
} else {
|
|
||||||
return (0 until containerSize).iterator().map { this[it] }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Container.slotIterator(nonEmpty: Boolean = true): Iterator<IContainerSlot> {
|
fun Container.slotIterator(): Iterator<IContainerSlot> {
|
||||||
if (this is IMatteryContainer) {
|
if (this is IEnhancedContainer) {
|
||||||
return slotIterator(nonEmpty)
|
return slotIterator()
|
||||||
} else if (nonEmpty) {
|
|
||||||
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { SimpleContainerSlot(it, this) }
|
|
||||||
} else {
|
} else {
|
||||||
return (0 until containerSize).iterator().map { SimpleContainerSlot(it, this) }
|
return (0 until containerSize).iterator().map { IContainerSlot.Simple(it, this) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Container.nonEmptySlotIterator(): Iterator<IContainerSlot> {
|
||||||
|
if (this is IEnhancedContainer) {
|
||||||
|
return nonEmptySlotIterator()
|
||||||
|
} else {
|
||||||
|
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { IContainerSlot.Simple(it, this) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,206 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.collect
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.HashCommon
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntCollection
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntComparator
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntIterators
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSet
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSortedSet
|
||||||
|
|
||||||
|
class IntRange2Set private constructor(private val first: Int, private val last: Int) : IntSortedSet {
|
||||||
|
constructor(range: IntRange) : this(range.first, range.last) {
|
||||||
|
require(range.step == 1) { "Provided range has non-standard step of ${range.step}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun add(element: Int): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun addAll(c: IntCollection?): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun addAll(elements: Collection<Int>): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun clear() {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(fromElement: Int): IntBidirectionalIterator {
|
||||||
|
if (isEmpty() || fromElement > last)
|
||||||
|
return IntIterators.EMPTY_ITERATOR
|
||||||
|
else if (fromElement <= first)
|
||||||
|
return IntIterators.fromTo(first, last + 1)
|
||||||
|
else
|
||||||
|
return IntIterators.fromTo(fromElement, last + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): IntBidirectionalIterator {
|
||||||
|
if (isEmpty())
|
||||||
|
return IntIterators.EMPTY_ITERATOR
|
||||||
|
|
||||||
|
return IntIterators.fromTo(first, last + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun remove(k: Int): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun removeAll(c: IntCollection?): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun removeAll(elements: Collection<Int>): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun retainAll(c: IntCollection?): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Not supported", level = DeprecationLevel.ERROR)
|
||||||
|
override fun retainAll(elements: Collection<Int>): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun contains(key: Int): Boolean {
|
||||||
|
return key in first .. last
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containsAll(c: IntCollection): Boolean {
|
||||||
|
return c.ktIterator().all { it in first .. last }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containsAll(elements: Collection<Int>): Boolean {
|
||||||
|
return elements.all { it in first .. last }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return last > first
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toArray(a: IntArray?): IntArray {
|
||||||
|
if (a == null || a.size < size)
|
||||||
|
return toIntArray()
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
|
||||||
|
for (i in first .. last) {
|
||||||
|
a[index++] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toIntArray(): IntArray {
|
||||||
|
if (isEmpty())
|
||||||
|
return EMPTY_ARRAY
|
||||||
|
|
||||||
|
return IntArray(last - first + 1) { first + it }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun comparator(): IntComparator? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun subSet(fromElement: Int, toElement: Int): IntSortedSet {
|
||||||
|
if (fromElement <= first && toElement > last)
|
||||||
|
return this
|
||||||
|
else if (fromElement <= first)
|
||||||
|
return IntRange2Set(first, toElement - 1)
|
||||||
|
else if (toElement > last)
|
||||||
|
return IntRange2Set(fromElement, last)
|
||||||
|
else
|
||||||
|
return EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun headSet(toElement: Int): IntSortedSet {
|
||||||
|
if (isEmpty() || toElement <= first)
|
||||||
|
return EMPTY
|
||||||
|
else if (toElement < last)
|
||||||
|
return this
|
||||||
|
else
|
||||||
|
return IntRange2Set(first, toElement - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tailSet(fromElement: Int): IntSortedSet {
|
||||||
|
if (isEmpty() || fromElement > last)
|
||||||
|
return EMPTY
|
||||||
|
else if (fromElement < first)
|
||||||
|
return this
|
||||||
|
else
|
||||||
|
return IntRange2Set(fromElement, last)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun firstInt(): Int {
|
||||||
|
if (isEmpty())
|
||||||
|
throw NoSuchElementException("Range is empty")
|
||||||
|
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lastInt(): Int {
|
||||||
|
if (isEmpty())
|
||||||
|
throw NoSuchElementException("Range is empty")
|
||||||
|
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = if (last > first) 0 else last - first + 1
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
if (isEmpty())
|
||||||
|
return "IntRange2Set[EMPTY]"
|
||||||
|
|
||||||
|
return "IntRange2Set[$first .. $last]"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return this === other ||
|
||||||
|
other is IntRange2Set && (isEmpty() == other.isEmpty() || first == other.first && last == other.last) ||
|
||||||
|
other is IntSet && other.size == size && containsAll(other) ||
|
||||||
|
other is Set<*> && other.size == size && other.all { it is Int && it in first .. last }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
if (isEmpty())
|
||||||
|
return EMPTY_HASH
|
||||||
|
|
||||||
|
return HashCommon.murmurHash3(last - first)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Returns set containing values beginning from [from] (inclusive) to [to] (inclusive)
|
||||||
|
*/
|
||||||
|
fun closed(from: Int, to: Int): IntRange2Set {
|
||||||
|
if (from > to)
|
||||||
|
return EMPTY
|
||||||
|
|
||||||
|
return IntRange2Set(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns set containing values beginning from [from] (inclusive) to [to] (exclusive)
|
||||||
|
*/
|
||||||
|
fun openEnded(from: Int, to: Int): IntRange2Set {
|
||||||
|
return closed(from, to - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val EMPTY_ARRAY = IntArray(0)
|
||||||
|
val EMPTY = IntRange2Set(0, -1)
|
||||||
|
private val EMPTY_HASH = HashCommon.murmurHash3(0)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package ru.dbotthepony.mc.otm.core.collect
|
package ru.dbotthepony.mc.otm.core.collect
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntCollection
|
||||||
import it.unimi.dsi.fastutil.ints.IntIterable
|
import it.unimi.dsi.fastutil.ints.IntIterable
|
||||||
import it.unimi.dsi.fastutil.ints.IntIterator
|
import it.unimi.dsi.fastutil.ints.IntIterator
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSortedSet
|
||||||
|
|
||||||
fun IntRange.asIterable(): IntIterable {
|
fun IntRange.asIterable(): IntIterable {
|
||||||
return IntIterable {
|
return IntIterable {
|
||||||
@ -22,3 +24,31 @@ fun IntRange.asIterable(): IntIterable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun IntCollection.ktIterator(): kotlin.collections.IntIterator {
|
||||||
|
return object : kotlin.collections.IntIterator() {
|
||||||
|
private val parent = this@ktIterator.intIterator()
|
||||||
|
|
||||||
|
override fun nextInt(): Int {
|
||||||
|
return parent.nextInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return parent.hasNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IntSortedSet.ktIterator(fromElement: Int): kotlin.collections.IntIterator {
|
||||||
|
return object : kotlin.collections.IntIterator() {
|
||||||
|
private val parent = this@ktIterator.iterator(fromElement)
|
||||||
|
|
||||||
|
override fun nextInt(): Int {
|
||||||
|
return parent.nextInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return parent.hasNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user