Initial code for new containers

This commit is contained in:
DBotThePony 2025-02-27 19:19:33 +07:00
parent dfc35f393f
commit ca6ff30414
Signed by: DBot
GPG Key ID: DCC23B5715498507
15 changed files with 1430 additions and 205 deletions

View File

@ -9,6 +9,7 @@ import it.unimi.dsi.fastutil.ints.IntIterator
import it.unimi.dsi.fastutil.ints.IntList
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
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.Object2ObjectOpenCustomHashMap
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.slotIterator
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.toList
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")
inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index)
val Container.slotRange: IntIterable get() {
return IntIterable {
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()
}
}
}
val Container.slotRange: IntRange2Set get() {
return IntRange2Set.openEnded(0, containerSize)
}
fun Container.addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange): ItemStack {
if (this is IMatteryContainer) {
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.addItem(stack: ItemStack, simulate: Boolean, slots: IntSortedSet = slotRange): ItemStack {
return IEnhancedContainer.wrap(this).addItem(stack, simulate, slots)
}
fun Container.vanishCursedItems() {
@ -312,12 +237,18 @@ fun Container.sortWithIndices(sortedSlots: IntCollection) {
if (value in 0 until containerSize && seen.add(value)) {
val slot = containerSlot(value)
if (
slot.isNotEmpty &&
!slot.isForbiddenForAutomation &&
slot.item.count <= slot.getMaxStackSize() &&
(!slot.hasFilter || slot.getFilter() != slot.item.item || slot.getMaxStackSize() > 1)
) {
val condition: Boolean
if (slot is IFilteredContainerSlot) {
condition = slot.isNotEmpty &&
!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)
}
}
@ -335,7 +266,7 @@ fun Container.computeSortedIndices(comparator: Comparator<ItemStack> = ItemStack
if (isEmpty)
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())
return IntList.of()

View 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()
}
}

View File

@ -1,9 +1,7 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -15,54 +13,67 @@ interface IContainerSlot : Delegate<ItemStack> {
val slot: Int
val container: Container
fun setChanged()
var item: ItemStack
operator fun component1() = slot
operator fun component2() = item
fun getMaxStackSize(item: ItemStack = this.item): Int {
return container.maxStackSize
}
val isForbiddenForAutomation: Boolean get() {
return getFilter() === Items.AIR
}
fun getFilter(): Item?
/**
* Max stack size regardless of item
*
* Prefer to use ItemStack version instead
*/
val maxStackSize: Int
/**
* @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
val hasFilter: Boolean
get() = getFilter() != null
fun remove() {
container[slot] = ItemStack.EMPTY
fun maxStackSize(item: ItemStack): Int {
return maxStackSize.coerceAtMost(item.maxStackSize)
}
fun remove(count: Int): ItemStack {
return container.removeItem(slot, count)
}
fun remove(): ItemStack
fun remove(count: Int): ItemStack
override fun get(): ItemStack {
return container[slot]
return item
}
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
get() = container[slot].isEmpty
get() = item.isEmpty
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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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
}
}
}

View File

@ -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)
}
}

View File

@ -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
*/
@ -83,7 +62,7 @@ interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
}
fun containerSlot(slot: Int): IContainerSlot {
return ContainerSlot(slot, this)
return IContainerSlot.Simple(slot, this)
}
fun hasSlotFilter(slot: Int) = getSlotFilter(slot) !== null
@ -234,22 +213,4 @@ interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
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
get() = isSlotForbiddenForAutomation(slot)
override fun getFilter(): Item? {
return getSlotFilter(slot)
}
override fun setFilter(filter: Item?): Boolean {
return setSlotFilter(slot, filter)
}
override fun getMaxStackSize(item: ItemStack): Int {
override fun maxStackSize(item: ItemStack): Int {
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
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) {
return (0 until size).iterator().map { Slot(it) }
} 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)
}

View File

@ -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()
}
}

View File

@ -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())
}
}

View File

@ -4,52 +4,41 @@ import net.minecraft.world.Container
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
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.get
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
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 {
if (this is IMatteryContainer) {
if (this is IEnhancedContainer) {
return containerSlot(slot)
} else {
return SimpleContainerSlot(slot, this)
return IContainerSlot.Simple(slot, this)
}
}
operator fun Container.iterator() = iterator(true)
fun Container.iterator(nonEmpty: Boolean): Iterator<ItemStack> {
if (this is IMatteryContainer) {
return iterator(nonEmpty)
} else if (nonEmpty) {
operator fun Container.iterator(): Iterator<ItemStack> {
if (this is IEnhancedContainer) {
return iterator()
} else {
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> {
if (this is IMatteryContainer) {
return slotIterator(nonEmpty)
} else if (nonEmpty) {
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { SimpleContainerSlot(it, this) }
fun Container.slotIterator(): Iterator<IContainerSlot> {
if (this is IEnhancedContainer) {
return slotIterator()
} 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) }
}
}

View File

@ -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)
}
}

View File

@ -1,7 +1,9 @@
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.IntIterator
import it.unimi.dsi.fastutil.ints.IntSortedSet
fun IntRange.asIterable(): 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()
}
}
}