Compare commits

...

12 Commits

64 changed files with 291 additions and 1299 deletions

View File

@ -29,7 +29,6 @@ import net.minecraft.world.phys.Vec3
import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer

View File

@ -19,7 +19,6 @@ import ru.dbotthepony.mc.otm.capability.fluid.BlockMatteryFluidHandler
import ru.dbotthepony.mc.otm.capability.moveFluid
import ru.dbotthepony.mc.otm.config.ItemsConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters

View File

@ -19,7 +19,6 @@ import net.neoforged.neoforge.fluids.FluidStack
import net.neoforged.neoforge.fluids.capability.IFluidHandler
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList

View File

@ -21,7 +21,6 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot

View File

@ -17,7 +17,6 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList

View File

@ -22,7 +22,6 @@ import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot

View File

@ -26,7 +26,6 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal

View File

@ -20,7 +20,6 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer

View File

@ -14,7 +14,6 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu

View File

@ -18,7 +18,6 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
@ -30,7 +29,7 @@ class DriveViewerBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyUpdated, MachinesConfig.DRIVE_VIEWER))
val energyConfig = ConfigurableEnergy(energy)
val container: EnhancedContainer = object : EnhancedContainer(1) {
val container: EnhancedContainer.Simple = object : EnhancedContainer.Simple(1) {
override fun notifySlotChanged(slot: Int, old: ItemStack) {
super.notifySlotChanged(slot, old)
markDirtyFast()

View File

@ -31,7 +31,6 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IEnhancedCraftingContainer
import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer
import ru.dbotthepony.mc.otm.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.collect.map
@ -219,7 +218,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
fun howMuchPlayerCrafted(ply: Player): Int = craftingAmount.getInt(ply)
fun lastCraftingRecipe(ply: Player) = lastCraftingRecipe[ply]
val craftingGrid = IEnhancedCraftingContainer.Wrapper(EnhancedContainer.withListener(3 * 3) {
val craftingGrid = IEnhancedCraftingContainer.Wrapper(EnhancedContainer.WithListener(3 * 3) {
markDirtyFast()
if (!inProcessOfCraft) {

View File

@ -20,7 +20,6 @@ import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.transcieveEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer

View File

@ -12,7 +12,6 @@ import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer

View File

@ -11,7 +11,6 @@ import ru.dbotthepony.mc.otm.block.entity.ItemJob
import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer

View File

@ -17,7 +17,6 @@ import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.RelativeSide

View File

@ -19,7 +19,6 @@ import ru.dbotthepony.mc.otm.capability.energy.CombinedProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.RelativeSide

View File

@ -16,7 +16,6 @@ import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot

View File

@ -25,7 +25,6 @@ import ru.dbotthepony.mc.otm.capability.item.CombinedItemHandler
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
@ -71,7 +70,7 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
}
val capsuleContainer = SlottedContainer.simple(1, ::CapsuleSlot, ::markDirtyFast)
val servoContainer = EnhancedContainer.withListener(1, ::markDirtyFast)
val servoContainer = EnhancedContainer.WithListener(1, ::markDirtyFast)
val mendingContainer = SlottedContainer.simple(1, ::MendingSlot, ::markDirtyFast).also(::addDroppableContainer)
private var mending: Holder<Enchantment>? = null

View File

@ -10,7 +10,6 @@ import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag

View File

@ -16,7 +16,6 @@ import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.capability.moveMatter
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal

View File

@ -18,7 +18,6 @@ import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters

View File

@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.capability.item
import com.google.common.collect.ImmutableList
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
import ru.dbotthepony.mc.otm.container.ContainerHandler
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import java.util.stream.Stream
@ -12,7 +11,7 @@ class CombinedItemHandler(val handlers: ImmutableList<IItemHandler>) : IItemHand
constructor(handlers: Collection<IItemHandler>) : this(ImmutableList.copyOf(handlers))
constructor(vararg handlers: IItemHandler) : this(ImmutableList.copyOf(handlers))
private val needsChecking = handlers.any { it !is ContainerHandler && it !is SlottedContainer }
private val needsChecking = handlers.any { it !is SlottedContainer }
private val lastSizes = IntArray(this.handlers.size)
private var totalSize = 0
private val mappings = ArrayList<Mapping>()

View File

@ -11,7 +11,7 @@ import ru.dbotthepony.mc.otm.menu.makeSlots
class MatteryChestMenu(
type: MenuType<*>, containerId: Int,
inventory: Inventory, override val rows: Int, override val columns: Int,
container: Container = EnhancedContainer(rows * columns)
container: Container = EnhancedContainer.Simple(rows * columns)
) : AbstractVanillaChestMenu(type, containerId, inventory, container) {
override val containerSlots = makeSlots(container, ::MatteryMenuSlot)

View File

@ -10,7 +10,7 @@ import ru.dbotthepony.mc.otm.menu.makeSlots
class MatteryShulkerBoxMenu(
containerId: Int,
inventory: Inventory,
container: Container = EnhancedContainer(27)
container: Container = EnhancedContainer.Simple(27)
) : AbstractVanillaChestMenu(VanillaMenuTypes.SHULKER_BOX, containerId, inventory, container) {
override val containerSlots = makeSlots(container, ::Slot)
override val rows: Int

View File

@ -16,11 +16,11 @@ object PlayerConfig : AbstractConfig("player") {
val REGENERATE_ENERGY_EFFICIENCY_FOOD: Double by builder
.comment("How much % of food points to convert to energy")
.defineInRange("REGENERATE_ENERGY_EFFICIENCY_FOOD", 0.75, 0.0)
.defineInRange("REGENERATE_ENERGY_EFFICIENCY_FOOD", 0.4, 0.0)
val REGENERATE_ENERGY_EFFICIENCY_SATURATION: Double by builder
.comment("How much % of food saturation points to convert to energy")
.defineInRange("REGENERATE_ENERGY_EFFICIENCY_SATURATION", 0.4, 0.0)
.defineInRange("REGENERATE_ENERGY_EFFICIENCY_SATURATION", 0.2, 0.0)
val REGENERATE_ENERGY_IN_PEACEFUL: Boolean by builder
.comment("Regenerate energy while in peaceful")
@ -39,6 +39,28 @@ object PlayerConfig : AbstractConfig("player") {
.comment("for android players, since 'hunger' (for compatibility) is managed by mod in such case")
.defineInRange("TIME_BETWEEN_NATURAL_REGENERATION", 120, 0, Int.MAX_VALUE)
val ANDROID_ENERGY_PER_HUNGER_POINT by builder.defineDecimal("energyPerHunger", Decimal(2000), Decimal.ZERO)
val ANDROID_MAX_ENERGY by builder
.comment("Internal battery of every android has this much storage")
.comment("Keep in mind that already existing players won't get their value changed since it is", "stored inside their savedata")
.defineDecimal("capacity", Decimal(80_000), Decimal.ZERO)
val NIGHT_VISION_POWER_DRAW by builder.defineDecimal("nightVisionPowerDraw", Decimal(8), Decimal.ZERO)
val FALL_DAMAGE_REDUCTION_PER_LEVEL_P: Double by builder
.comment("In percent. Level of feature is multiplied by this")
.comment("First, fall damage is reduced by flat resistance, then reduced by percentage resistance (this)")
.defineInRange("FALL_DAMAGE_REDUCTION_PER_LEVEL_P", 0.2, 0.01, 1.0)
val FALL_DAMAGE_REDUCTION_PER_LEVEL_F: Double by builder
.comment("In flat half of hearts. Level of feature is multiplied by this")
.comment("First, fall damage is reduced by flat resistance (this), then reduced by percentage resistance")
.defineInRange("FALL_DAMAGE_REDUCTION_PER_LEVEL_F", 1.5, 0.0, Float.MAX_VALUE.toDouble())
val SWIM_BOOSTERS: Double by builder
.comment("Increase per level")
.defineInRange("SWIM_BOOSTERS", 0.25, 0.0, Float.MAX_VALUE.toDouble())
init {
builder.pop()
}
@ -71,27 +93,6 @@ object PlayerConfig : AbstractConfig("player") {
}
}
val ANDROID_ENERGY_PER_HUNGER_POINT by builder.defineDecimal("energyPerHunger", Decimal(2000), Decimal.ZERO)
val ANDROID_MAX_ENERGY by builder
.comment("Internal battery of every android has this much storage")
.comment("Keep in mind that already existing players won't get their value changed since it is", "stored inside their savedata")
.defineDecimal("capacity", Decimal(80_000), Decimal.ZERO)
val NIGHT_VISION_POWER_DRAW by builder.defineDecimal("nightVisionPowerDraw", Decimal(8), Decimal.ZERO)
val FALL_DAMAGE_REDUCTION_PER_LEVEL_P: Double by builder
.comment("In percent. Level of feature is multiplied by this")
.comment("First, fall damage is reduced by flat resistance, then reduced by percentage resistance (this)")
.defineInRange("FALL_DAMAGE_REDUCTION_PER_LEVEL_P", 0.2, 0.01, 1.0)
val FALL_DAMAGE_REDUCTION_PER_LEVEL_F: Double by builder
.comment("In flat half of hearts. Level of feature is multiplied by this")
.comment("First, fall damage is reduced by flat resistance (this), then reduced by percentage resistance")
.defineInRange("FALL_DAMAGE_REDUCTION_PER_LEVEL_F", 1.5, 0.0, Float.MAX_VALUE.toDouble())
val SWIM_BOOSTERS: Double by builder
.comment("Increase per level")
.defineInRange("SWIM_BOOSTERS", 0.25, 0.0, Float.MAX_VALUE.toDouble())
object EnderTeleporter {
init {
builder.comment("Ender Teleporter ability").push("EnderTeleporter")

View File

@ -23,9 +23,9 @@ import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.stream
import java.util.stream.Stream
class CombinedContainer(containers: Stream<Pair<IEnhancedContainer, Iterable<Int>>>) : ISlottedContainer, IMatteryContainer {
constructor(vararg containers: IEnhancedContainer) : this(containers.stream().map { it to (0 until it.containerSize) })
constructor(containers: Collection<IEnhancedContainer>) : this(containers.stream().map { it to (0 until it.containerSize) })
class CombinedContainer(containers: Stream<Pair<IEnhancedContainer<*>, Iterable<Int>>>) : ISlottedContainer<IContainerSlot> {
constructor(vararg containers: IEnhancedContainer<*>) : this(containers.stream().map { it to (0 until it.containerSize) })
constructor(containers: Collection<IEnhancedContainer<*>>) : this(containers.stream().map { it to (0 until it.containerSize) })
private val slots: ImmutableList<IContainerSlot>
private val slotsMap: ImmutableMap<Container, List<IContainerSlot>>
@ -116,28 +116,12 @@ class CombinedContainer(containers: Stream<Pair<IEnhancedContainer, Iterable<Int
)
}
override fun slotIterator(): Iterator<IFilteredContainerSlot> {
return slots.iterator().map {
if (it is IFilteredContainerSlot) it else IFilteredContainerSlot.Dummy(it)
}
override fun slotIterator(): Iterator<IContainerSlot> {
return slots.iterator()
}
override fun containerSlot(slot: Int): IFilteredContainerSlot {
val getSlot = slots[slot]
if (getSlot is IFilteredContainerSlot) return getSlot
return IFilteredContainerSlot.Dummy(getSlot)
}
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return super<ISlottedContainer>.getMaxStackSize(slot, itemStack)
}
override fun getSlotFilter(slot: Int): Item? {
return containerSlot(slot).filter
}
override fun clearSlotFilters() {
override fun containerSlot(slot: Int): IContainerSlot {
return slots[slot]
}
override fun setChanged(slot: Int) {
@ -145,7 +129,7 @@ class CombinedContainer(containers: Stream<Pair<IEnhancedContainer, Iterable<Int
}
class Builder {
private val values = ArrayList<Pair<IEnhancedContainer, Iterable<Int>>>()
private val values = ArrayList<Pair<IEnhancedContainer<*>, Iterable<Int>>>()
fun add(container: Container): Builder {
return add(IEnhancedContainer.wrap(container))
@ -167,27 +151,27 @@ class CombinedContainer(containers: Stream<Pair<IEnhancedContainer, Iterable<Int
return add(IEnhancedContainer.wrap(container), slots)
}
fun add(container: IEnhancedContainer): Builder {
fun add(container: IEnhancedContainer<*>): Builder {
values.add(container to container.slotRange)
return this
}
fun add(container: IEnhancedContainer, slots: Iterator<Int>): Builder {
fun add(container: IEnhancedContainer<*>, slots: Iterator<Int>): Builder {
values.add(container to IntArrayList(slots))
return this
}
fun add(container: IEnhancedContainer, slot: Int): Builder {
fun add(container: IEnhancedContainer<*>, slot: Int): Builder {
values.add(container to intArrayOf(slot).asIterable())
return this
}
fun add(container: IEnhancedContainer, from: Int, to: Int): Builder {
fun add(container: IEnhancedContainer<*>, from: Int, to: Int): Builder {
values.add(container to (from .. to))
return this
}
fun add(container: IEnhancedContainer, slots: Iterable<Int>): Builder {
fun add(container: IEnhancedContainer<*>, slots: Iterable<Int>): Builder {
values.add(container to slots)
return this
}

View File

@ -1,89 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
class ContainerHandler(
private val container: IMatteryContainer,
private val filter: HandlerFilter = HandlerFilter.Both,
) : IItemHandler {
override fun getSlots() = container.containerSize
override fun getStackInSlot(slot: Int) = container[slot]
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
if (!container.testSlotFilter(slot, stack) || !filter.canInsert(slot, stack))
return stack
filter.preInsert(slot, stack, simulate)
val localStack = container[slot]
var amount = filter.modifyInsertCount(slot, stack, localStack, simulate)
if (amount <= 0)
return stack
if (localStack.isEmpty) {
amount = stack.count.coerceAtMost(container.getMaxStackSize(slot, stack)).coerceAtMost(amount)
if (!simulate) {
container.setItem(slot, stack.copyWithCount(amount))
}
if (stack.count <= amount) {
return ItemStack.EMPTY
} else {
return stack.copyWithCount(stack.count - amount)
}
} else if (localStack.isStackable && container.getMaxStackSize(slot, localStack) > localStack.count && ItemStack.isSameItemSameComponents(localStack, stack)) {
val newCount = container.getMaxStackSize(slot, localStack).coerceAtMost(localStack.count + stack.count.coerceAtMost(amount))
val diff = newCount - localStack.count
if (diff != 0) {
if (!simulate) {
localStack.grow(diff)
container.setChanged(slot)
}
val copy = stack.copy()
copy.shrink(diff)
return copy
}
}
return stack
}
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
if (amount <= 0 || container.isSlotForbiddenForAutomation(slot))
return ItemStack.EMPTY
val localStack = container.getItem(slot)
if (localStack.isEmpty) return ItemStack.EMPTY
@Suppress("name_shadowing")
val amount = filter.modifyExtractCount(slot, amount, simulate)
if (amount <= 0) return ItemStack.EMPTY
if (!filter.canExtract(slot, amount, localStack)) return ItemStack.EMPTY
filter.preExtract(slot, amount, simulate)
val minimal = amount.coerceAtMost(localStack.count)
val copy = localStack.copy()
copy.count = minimal
if (!simulate) {
localStack.shrink(minimal)
container.setChanged(slot)
}
return copy
}
override fun getSlotLimit(slot: Int): Int {
return container.maxStackSize
}
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return container.testSlotFilter(slot, stack) && filter.canInsert(slot, stack)
}
}

View File

@ -1,81 +0,0 @@
package ru.dbotthepony.mc.otm.container
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 java.util.function.Predicate
import java.util.function.Supplier
/**
* because mods tend to do crazy shit
*/
class DynamicallyProxiedContainer(private val toProxy: Supplier<Container>) : IContainer {
override fun clearContent() {
return toProxy.get().clearContent()
}
override fun getContainerSize(): Int {
return toProxy.get().containerSize
}
override fun isEmpty(): Boolean {
return toProxy.get().isEmpty
}
override fun getItem(slot: Int): ItemStack {
return toProxy.get().getItem(slot)
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
return toProxy.get().removeItem(slot, amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
return toProxy.get().removeItemNoUpdate(slot)
}
override fun setItem(slot: Int, itemStack: ItemStack) {
return toProxy.get().setItem(slot, itemStack)
}
override fun setChanged() {
return toProxy.get().setChanged()
}
override fun stillValid(player: Player): Boolean {
return toProxy.get().stillValid(player)
}
override fun getMaxStackSize(): Int {
return toProxy.get().getMaxStackSize()
}
override fun startOpen(player: Player) {
toProxy.get().startOpen(player)
}
override fun stopOpen(player: Player) {
toProxy.get().stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return toProxy.get().canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return toProxy.get().canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return toProxy.get().countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return toProxy.get().hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return toProxy.get().hasAnyMatching(predicate)
}
}

View File

@ -23,15 +23,9 @@ import ru.dbotthepony.mc.otm.core.nbt.set
* This is supposed to be counterpart to [SimpleContainer] of Minecraft itself, with more features
* and improved performance (inside [IEnhancedContainer] defined methods).
*/
open class EnhancedContainer(private val size: Int) : IEnhancedContainer, INBTSerializable<CompoundTag> {
abstract class EnhancedContainer<S : IContainerSlot>(private val size: Int) : IEnhancedContainer<S>, INBTSerializable<CompoundTag> {
private val items = Array(size) { ItemStack.EMPTY }
private val observedItems = Array(size) { ItemStack.EMPTY }
private val slots by lazy(LazyThreadSafetyMode.PUBLICATION) { Array(size) { IContainerSlot.Simple(it, this) } }
// can be safely overridden in subclasses, very little memory will be wasted
override fun containerSlot(slot: Int): IContainerSlot {
return slots[slot]
}
protected open fun notifySlotChanged(slot: Int, old: ItemStack) {}
@ -184,16 +178,22 @@ open class EnhancedContainer(private val size: Int) : IEnhancedContainer, INBTSe
}
}
companion object {
private val LOGGER = LogManager.getLogger()
open class Simple(size: Int) : EnhancedContainer<IContainerSlot>(size) {
private val slots by lazy(LazyThreadSafetyMode.PUBLICATION) { Array(size) { IContainerSlot.Simple(it, this) } }
fun withListener(slots: Int, listener: Runnable): EnhancedContainer {
return object : EnhancedContainer(slots) {
override fun notifySlotChanged(slot: Int, old: ItemStack) {
super.notifySlotChanged(slot, old)
listener.run()
}
}
override fun containerSlot(slot: Int): IContainerSlot {
return slots[slot]
}
}
open class WithListener(size: Int, private val listener: Runnable) : Simple(size) {
override fun notifySlotChanged(slot: Int, old: ItemStack) {
super.notifySlotChanged(slot, old)
listener.run()
}
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -10,9 +10,7 @@ import net.neoforged.neoforge.items.IItemHandlerModifiable
* Reinforced [ISlottedContainer] which slots are [IAutomatedContainerSlot]s, which
* subsequently allow this container to implement [IItemHandler]
*/
interface IAutomatedContainer : ISlottedContainer, IItemHandlerModifiable {
override fun containerSlot(slot: Int): IAutomatedContainerSlot
interface IAutomatedContainer<S : IAutomatedContainerSlot> : ISlottedContainer<S>, IItemHandlerModifiable {
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return containerSlot(slot).canAutomationPlaceItem(itemStack)
}

View File

@ -1,95 +0,0 @@
package ru.dbotthepony.mc.otm.container
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 java.util.function.Predicate
// passthrough all default methods to fix Kotlin bug related to implementation delegation not properly working on Java interfaces
// and also to give params proper names
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
interface IContainer : Container {
override fun getMaxStackSize(): Int {
return super.getMaxStackSize()
}
override fun startOpen(player: Player) {
super.startOpen(player)
}
override fun stopOpen(player: Player) {
super.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return super.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return super.canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return super.countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return super.hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return super.hasAnyMatching(predicate)
}
override fun clearContent()
override fun getContainerSize(): Int
override fun isEmpty(): Boolean
override fun getItem(slot: Int): ItemStack
override fun removeItem(slot: Int, amount: Int): ItemStack
override fun removeItemNoUpdate(slot: Int): ItemStack
override fun setItem(slot: Int, itemStack: ItemStack)
override fun setChanged()
override fun stillValid(player: Player): Boolean
companion object {
fun wrap(container: Container): IContainer {
if (container is IContainer)
return container
else
return object : IContainer, Container by container {
override fun getMaxStackSize(): Int {
return container.getMaxStackSize()
}
override fun startOpen(player: Player) {
container.startOpen(player)
}
override fun stopOpen(player: Player) {
container.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return container.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return container.canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return container.countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return container.hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return container.hasAnyMatching(predicate)
}
}
}
}
}

View File

@ -19,12 +19,41 @@ import java.util.stream.Stream
import java.util.stream.StreamSupport
/**
* "Backward-compatible" enhanced container interface, where all methods can be derived/emulated from existing [IContainer] ([Container]) code
* "Backward-compatible" enhanced container interface, where all methods can be derived/emulated from existing [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>, StackedContentsCompatible {
interface IEnhancedContainer<S : IContainerSlot> : Container, RecipeInput, Iterable<ItemStack>, StackedContentsCompatible {
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
override fun getMaxStackSize(): Int {
return super.getMaxStackSize()
}
override fun startOpen(player: Player) {
super.startOpen(player)
}
override fun stopOpen(player: Player) {
super.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return super.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return super.canTakeItem(container, slot, itemStack)
}
override fun clearContent()
override fun getContainerSize(): Int
override fun getItem(slot: Int): ItemStack
override fun removeItem(slot: Int, amount: Int): ItemStack
override fun removeItemNoUpdate(slot: Int): ItemStack
override fun setItem(slot: Int, itemStack: ItemStack)
override fun setChanged()
// provide non-ambiguous get and set operators
operator fun get(slot: Int): ItemStack {
return getItem(slot)
@ -34,9 +63,7 @@ interface IEnhancedContainer : IContainer, RecipeInput, Iterable<ItemStack>, Sta
setItem(slot, value)
}
fun containerSlot(slot: Int): IContainerSlot {
return IContainerSlot.Simple(slot, this)
}
fun containerSlot(slot: Int): S
override fun fillStackedContents(contents: StackedContents) {
forEach { contents.accountStack(it) }
@ -45,11 +72,11 @@ interface IEnhancedContainer : IContainer, RecipeInput, Iterable<ItemStack>, Sta
/**
* Returns iterator over **all** slots this container has
*/
fun slotIterator(): Iterator<IContainerSlot> {
fun slotIterator(): Iterator<S> {
return (0 until containerSize).iterator().map { containerSlot(it) }
}
fun nonEmptySlotIterator(): Iterator<IContainerSlot> {
fun nonEmptySlotIterator(): Iterator<S> {
return slotIterator().filter { it.isNotEmpty }
}
@ -307,7 +334,11 @@ interface IEnhancedContainer : IContainer, RecipeInput, Iterable<ItemStack>, Sta
return StreamSupport.stream(spliterator(), false)
}
private class Wrapper(private val parent: Container) : IEnhancedContainer {
private class Wrapper(private val parent: Container) : IEnhancedContainer<IContainerSlot> {
override fun containerSlot(slot: Int): IContainerSlot {
return IContainerSlot.Simple(slot, parent)
}
override fun clearContent() {
return parent.clearContent()
}
@ -378,8 +409,8 @@ interface IEnhancedContainer : IContainer, RecipeInput, Iterable<ItemStack>, Sta
}
companion object {
fun wrap(other: Container): IEnhancedContainer {
if (other is IEnhancedContainer)
fun wrap(other: Container): IEnhancedContainer<*> {
if (other is IEnhancedContainer<*>)
return other
return Wrapper(other)

View File

@ -4,7 +4,7 @@ import net.minecraft.world.entity.player.StackedContents
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.item.ItemStack
interface IEnhancedCraftingContainer : IEnhancedContainer, CraftingContainer {
interface IEnhancedCraftingContainer<S : IContainerSlot> : IEnhancedContainer<S>, CraftingContainer {
override fun getItems(): MutableList<ItemStack> {
return toList()
}
@ -13,7 +13,7 @@ interface IEnhancedCraftingContainer : IEnhancedContainer, CraftingContainer {
forEach { contents.accountSimpleStack(it) }
}
class Wrapper<C : IEnhancedContainer>(val parent: C, private val width: Int, private val height: Int) : IEnhancedCraftingContainer, IEnhancedContainer by parent {
class Wrapper<C : IEnhancedContainer<S>, S : IContainerSlot>(val parent: C, private val width: Int, private val height: Int) : IEnhancedCraftingContainer<S>, IEnhancedContainer<S> by parent {
init {
require(width * height == parent.containerSize) { "Crafting container dimensions ($width x $height) do not match container size provided (${parent.containerSize})" }
}

View File

@ -1,203 +0,0 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntIterable
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.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty
interface IMatteryContainer : IEnhancedContainer {
fun getSlotFilter(slot: Int): Item?
/**
* @return whenever the filter was set. Returns false only if container can't be filtered.
*/
fun setSlotFilter(slot: Int, filter: Item? = null): Boolean {
return false
}
fun clearSlotFilters()
override fun isEmpty(): Boolean
override fun size(): Int {
return containerSize
}
/**
* Iterates over non-empty itemstacks of this container
*/
override fun iterator(): Iterator<ItemStack> {
return iterator(true)
}
/**
* Iterates non-empty slots of this container
*/
override fun slotIterator(): Iterator<IFilteredContainerSlot> {
return slotIterator(true)
}
fun iterator(nonEmpty: Boolean): Iterator<ItemStack> {
if (nonEmpty) {
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
} else {
return (0 until containerSize).iterator().map { this[it] }
}
}
/**
* Iterates either non-empty slots of container or all slots of container
*/
fun slotIterator(nonEmpty: Boolean): Iterator<IFilteredContainerSlot> {
if (nonEmpty) {
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { containerSlot(it) }
} else {
return (0 until containerSize).iterator().map { containerSlot(it) }
}
}
override fun containerSlot(slot: Int): IFilteredContainerSlot
fun hasSlotFilter(slot: Int) = getSlotFilter(slot) !== null
fun isSlotForbiddenForAutomation(slot: Int) = getSlotFilter(slot) === Items.AIR
fun testSlotFilter(slot: Int, itemStack: ItemStack): Boolean {
return testSlotFilter(slot, itemStack.item)
}
fun testSlotFilter(slot: Int, item: Item): Boolean {
if (getSlotFilter(slot) == null) {
return true
} else {
return getSlotFilter(slot) === item
}
}
override fun getMaxStackSize(slot: Int, itemStack: ItemStack) = maxStackSize.coerceAtMost(itemStack.maxStackSize)
private fun addItem(stack: ItemStack, simulate: Boolean, filterPass: Boolean, slots: IntIterable, onlyIntoExisting: Boolean, popTime: Int?, ignoreFilters: Boolean): ItemStack {
if (stack.isEmpty)
return stack
// двигаем в одинаковые слоты
var i = slots.intIterator()
while (i.hasNext()) {
val slot = i.nextInt()
if (
(ignoreFilters || !isSlotForbiddenForAutomation(slot)) &&
ItemStack.isSameItemSameComponents(getItem(slot), stack) &&
(ignoreFilters || !filterPass && !hasSlotFilter(slot) || filterPass && hasSlotFilter(slot) && testSlotFilter(slot, stack))
) {
val slotStack = getItem(slot)
val slotLimit = getMaxStackSize(slot, slotStack)
if (slotStack.count < slotLimit) {
val newCount = (slotStack.count + stack.count).coerceAtMost(slotLimit)
val diff = newCount - slotStack.count
if (!simulate) {
slotStack.count = newCount
setChanged(slot)
if (popTime != null) {
slotStack.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty) {
return stack
}
}
}
}
if (!onlyIntoExisting) {
i = slots.intIterator()
// двигаем в пустые слоты
while (i.hasNext()) {
val slot = i.nextInt()
if (
getItem(slot).isEmpty &&
(ignoreFilters || !isSlotForbiddenForAutomation(slot)) &&
(ignoreFilters || !filterPass && !hasSlotFilter(slot) || filterPass && hasSlotFilter(slot) && testSlotFilter(slot, stack))
) {
val diff = stack.count.coerceAtMost(getMaxStackSize(slot, stack))
if (!simulate) {
val copyToPut = stack.copy()
copyToPut.count = diff
setItem(slot, copyToPut)
if (popTime != null) {
copyToPut.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty) {
return stack
}
}
}
}
return stack
}
fun addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): ItemStack {
if (stack.isEmpty)
return stack
if (ignoreFilters) {
return addItem(stack.copy(), simulate, true, slots, onlyIntoExisting, popTime, true)
} else {
var copy = addItem(stack.copy(), simulate, true, slots, onlyIntoExisting, popTime, false)
copy = addItem(copy, simulate, false, slots, onlyIntoExisting, popTime, false)
return copy
}
}
fun handler(filter: HandlerFilter = HandlerFilter.Both): ContainerHandler {
return ContainerHandler(this, filter)
}
/**
* Unlike [addItem], modifies original [stack]
*
* @return Whenever [stack] was modified
*/
fun consumeItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): Boolean {
if (stack.isEmpty)
return false
val result = addItem(stack, simulate, slots, onlyIntoExisting, popTime, ignoreFilters)
if (result.count != stack.count) {
if (!simulate) {
stack.count = result.count
}
return true
}
return false
}
fun fullyAddItem(stack: ItemStack, slots: IntIterable = slotRange, ignoreFilters: Boolean = false): Boolean {
if (!addItem(stack, true, slots, ignoreFilters).isEmpty)
return false
return addItem(stack, false, slots, ignoreFilters).isEmpty
}
}

View File

@ -8,7 +8,7 @@ import ru.dbotthepony.kommons.collect.any
/**
* Skeletal implementation for containers which revolve around [IContainerSlot]
*/
interface ISlottedContainer : IEnhancedContainer {
interface ISlottedContainer<S : IContainerSlot> : IEnhancedContainer<S> {
override fun setChanged(slot: Int) {
containerSlot(slot).setChanged()
}

View File

@ -1,498 +0,0 @@
package ru.dbotthepony.mc.otm.container
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.ints.IntComparators
import it.unimi.dsi.fastutil.ints.IntSpliterator
import it.unimi.dsi.fastutil.objects.ObjectSpliterators
import net.minecraft.core.HolderLookup
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.entity.player.StackedContents
import net.minecraft.world.inventory.StackedContentsCompatible
import net.minecraft.world.item.Item
import net.minecraft.world.item.Items
import net.neoforged.neoforge.common.util.INBTSerializable
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.mc.otm.core.addSorted
import ru.dbotthepony.mc.otm.core.collect.any
import ru.dbotthepony.mc.otm.core.collect.count
import ru.dbotthepony.mc.otm.core.collect.emptyIterator
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.network.StreamCodecs
import ru.dbotthepony.mc.otm.network.syncher.ISynchable
import ru.dbotthepony.mc.otm.network.syncher.SynchableObservedDelegate
import java.util.*
import java.util.function.Consumer
import java.util.function.Predicate
import java.util.function.Supplier
import java.util.stream.Stream
import java.util.stream.StreamSupport
import kotlin.collections.ArrayList
@Suppress("UNUSED")
open class MatteryContainer(var listener: ContainerListener, private val size: Int) : IMatteryContainer, INBTSerializable<Tag?>, StackedContentsCompatible {
constructor(watcher: Runnable, size: Int) : this({ _, _, _ -> watcher.run() }, size)
constructor(size: Int) : this(EmptyListener, size)
fun interface ContainerListener {
fun setChanged(slot: Int, new: ItemStack, old: ItemStack)
}
object EmptyListener : ContainerListener {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {}
}
init {
require(size >= 0) { "Invalid container size $size" }
}
private val slots = Array(size) { ItemStack.EMPTY }
private val nonEmptyFlags = BitSet()
private var nonEmptyIndices = IntArrayList()
private var indicesReferenced = false
private data class Update(val slot: Int, val new: ItemStack, val old: ItemStack)
private val queuedUpdates = ArrayList<Update>()
private var queueUpdates = false
private fun cowIndices() {
if (indicesReferenced) {
nonEmptyIndices = IntArrayList(nonEmptyIndices)
indicesReferenced = false
}
}
private val trackedSlots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
private val filters: Array<Item?> = arrayOfNulls(size)
var changeset = 0
private set
override fun clearSlotFilters() {
Arrays.fill(filters, null)
}
val synchableFilters by lazy {
immutableList<ISynchable> {
for (i in 0 until size) {
accept(SynchableObservedDelegate(Delegate.Of({ filters[i] }, { filters[i] = it }), StreamCodecs.ITEM_TYPE_NULLABLE))
}
}
}
final override fun setSlotFilter(slot: Int, filter: Item?): Boolean {
filters[slot] = filter
return true
}
final override fun getSlotFilter(slot: Int) = filters[slot]
final override fun hasSlotFilter(slot: Int) = filters[slot] !== null
final override fun isSlotForbiddenForAutomation(slot: Int) = filters[slot] === Items.AIR
final override fun testSlotFilter(slot: Int, itemStack: ItemStack): Boolean {
return testSlotFilter(slot, itemStack.item)
}
final override fun testSlotFilter(slot: Int, item: Item): Boolean {
if (filters[slot] == null) {
return true
} else {
return filters[slot] === item
}
}
final override fun getContainerSize() = size
protected open fun startedIgnoringUpdates() {}
protected open fun stoppedIgnoringUpdates() {}
private data class SerializedItem(val item: ItemStack, val slot: Int) {
companion object {
val CODEC: Codec<SerializedItem> = RecordCodecBuilder.create {
it.group(
ItemStack.OPTIONAL_CODEC.fieldOf("item").forGetter { it.item },
Codec.INT.minRange(0).fieldOf("slot").forGetter { it.slot },
).apply(it, ::SerializedItem)
}
}
}
private data class SerializedFilter(val item: Item, val slot: Int) {
companion object {
val CODEC: Codec<SerializedFilter> = RecordCodecBuilder.create {
it.group(
BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter { it.item },
Codec.INT.minRange(0).fieldOf("slot").forGetter { it.slot },
).apply(it, ::SerializedFilter)
}
}
}
private data class SerializedState(
val items: List<SerializedItem>,
val filters: List<SerializedFilter>
) {
companion object {
val CODEC: Codec<SerializedState> = RecordCodecBuilder.create {
it.group(
Codec.list(SerializedItem.CODEC).fieldOf("items").forGetter { it.items },
Codec.list(SerializedFilter.CODEC).fieldOf("filters").forGetter { it.filters },
).apply(it, ::SerializedState)
}
}
}
override fun deserializeNBT(registries: HolderLookup.Provider, tag: Tag?) {
Arrays.fill(slots, ItemStack.EMPTY)
Arrays.fill(filters, null)
nonEmptyFlags.clear()
nonEmptyIndices = IntArrayList()
if (tag != null) {
SerializedState.CODEC.parse(registries.createSerializationContext(NbtOps.INSTANCE), tag)
.resultOrPartial { LOGGER.error("Error deserializing container: $it") }
.ifPresent {
val freeSlots = IntAVLTreeSet()
for (i in 0 until size)
freeSlots.add(i)
for ((item, slotID) in it.items) {
if (item.isEmpty)
continue
if (freeSlots.remove(slotID)) {
slots[slotID] = item
// trackedSlots[slotID] = item.copy()
} else if (freeSlots.isEmpty()) {
break
} else {
val slotID = freeSlots.firstInt()
freeSlots.remove(slotID)
slots[slotID] = item
// trackedSlots[slotID] = item.copy()
}
}
for ((item, index) in it.filters) {
if (index in 0 until size)
filters[index] = item
}
setChanged()
}
}
}
private fun internalSetChanged(slot: Int, new: ItemStack, old: ItemStack) {
if (queueUpdates) {
queuedUpdates.add(Update(slot, new, old))
} else {
setChanged(slot, new, old)
}
}
private fun runUpdates() {
for ((slot, new, old) in queuedUpdates) {
setChanged(slot, new, old)
}
queuedUpdates.clear()
}
protected open fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
listener.setChanged(slot, new, old)
}
override fun serializeNBT(registries: HolderLookup.Provider): CompoundTag {
val state = SerializedState(
slotIterator(true).map { SerializedItem(it.item, it.slot) }.toList(size),
filters.withIndex().iterator().filter { it.value != null }.map { SerializedFilter(it.value!!, it.index) }.toList()
)
return SerializedState.CODEC.encodeStart(registries.createSerializationContext(NbtOps.INSTANCE), state)
.resultOrPartial { throw RuntimeException("Failed to encode container contents: $it") }.get() as CompoundTag
}
final override fun isEmpty(): Boolean {
return nonEmptyIndices.isEmpty
}
operator fun contains(other: ItemStack): Boolean {
for (i in 0 until size) {
if (ItemStack.isSameItemSameComponents(this[i], other)) {
return true
}
}
return false
}
final override fun getItem(slot: Int): ItemStack {
val item = slots[slot]
if (item.isEmpty) {
if (nonEmptyFlags[slot]) {
setChanged(slot)
}
return ItemStack.EMPTY
} else {
if (!nonEmptyFlags[slot]) {
setChanged(slot)
}
return item
}
}
override fun fillStackedContents(contents: StackedContents) {
for (item in iterator()) {
contents.accountStack(item)
}
}
final override fun removeItem(slot: Int, amount: Int): ItemStack {
if (amount <= 0 || slot < 0 || slot >= size || slots[slot].isEmpty)
return ItemStack.EMPTY
val old = slots[slot].copy()
val split = slots[slot].split(amount)
trackedSlots[slot] = slots[slot].copy()
changeset++
updateEmptyFlag(slot)
internalSetChanged(slot, if (slots[slot].isEmpty) ItemStack.EMPTY else slots[slot], old)
return split
}
final override fun removeItemNoUpdate(slot: Int): ItemStack {
val old = slots[slot]
slots[slot] = ItemStack.EMPTY
trackedSlots[slot] = ItemStack.EMPTY
if (old.isNotEmpty) {
updateEmptyFlag(slot)
changeset++
}
return old
}
final override fun setItem(slot: Int, itemStack: ItemStack) {
if (slots[slot].isEmpty && itemStack.isEmpty || itemStack === slots[slot])
return
val old = slots[slot]
slots[slot] = if (itemStack.isEmpty) ItemStack.EMPTY else itemStack
trackedSlots[slot] = if (itemStack.isEmpty) ItemStack.EMPTY else itemStack.copy()
updateEmptyFlag(slot)
changeset++
internalSetChanged(slot, itemStack, old)
}
final override fun setChanged() {
queueUpdates = true
try {
for (slot in 0 until size) {
setChanged(slot)
}
runUpdates()
} finally {
queuedUpdates.clear()
queueUpdates = false
}
}
final override fun setChanged(slot: Int) {
if (!ItemStack.isSameItemSameComponents(slots[slot], trackedSlots[slot])) {
trackedSlots[slot] = slots[slot].copy()
updateEmptyFlag(slot)
changeset++
internalSetChanged(slot, slots[slot], trackedSlots[slot])
// mojang соси))0)0))0)))))0)
}
}
private fun updateEmptyFlag(slot: Int) {
if (slots[slot].isEmpty) {
if (nonEmptyFlags[slot]) {
nonEmptyFlags[slot] = false
cowIndices()
nonEmptyIndices.rem(slot)
}
} else {
if (!nonEmptyFlags[slot]) {
nonEmptyFlags[slot] = true
cowIndices()
nonEmptyIndices.addSorted(slot, IntComparators.NATURAL_COMPARATOR)
}
}
}
override fun stillValid(player: Player): Boolean {
return true
}
final override fun clearContent() {
nonEmptyFlags.clear()
nonEmptyIndices = IntArrayList()
Arrays.fill(trackedSlots, ItemStack.EMPTY)
for (slot in 0 until size) {
if (!slots[slot].isEmpty) {
val old = slots[slot]
slots[slot] = ItemStack.EMPTY
internalSetChanged(slot, ItemStack.EMPTY, old)
}
}
}
private inner class Iterator : kotlin.collections.Iterator<ItemStack> {
init {
indicesReferenced = true
}
private val parent = nonEmptyIndices.intIterator()
private var lastIndex = -1
override fun hasNext(): Boolean {
return parent.hasNext()
}
override fun next(): ItemStack {
lastIndex = parent.nextInt()
return getItem(lastIndex)
}
}
private inner class Spliterator(private val parent: IntSpliterator) : java.util.Spliterator<ItemStack> {
override fun tryAdvance(action: Consumer<in ItemStack>): Boolean {
return parent.tryAdvance {
action.accept(getItem(it))
}
}
override fun trySplit(): java.util.Spliterator<ItemStack>? {
return parent.trySplit()?.let(::Spliterator)
}
override fun estimateSize(): Long {
return parent.estimateSize()
}
override fun characteristics(): Int {
return parent.characteristics()
}
}
final override fun iterator(): kotlin.collections.Iterator<ItemStack> {
if (isEmpty) {
return emptyIterator()
}
return Iterator()
}
final override fun iterator(nonEmpty: Boolean): kotlin.collections.Iterator<ItemStack> {
if (!nonEmpty) {
return (0 until size).iterator().map { slots[it] }
} else if (isEmpty) {
return emptyIterator()
} else {
return Iterator()
}
}
inner class Slot(val slot: Int) : IFilteredContainerSlot {
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 maxStackSize(item: ItemStack): Int {
return getMaxStackSize(slot, item)
}
override fun setChanged() {
setChanged(slot)
}
}
final override fun slotIterator(): kotlin.collections.Iterator<Slot> {
indicesReferenced = true
return nonEmptyIndices.iterator().map { Slot(it) }
}
final override fun slotIterator(nonEmpty: Boolean): kotlin.collections.Iterator<Slot> {
if (!nonEmpty) {
return (0 until size).iterator().map { Slot(it) }
} else if (isEmpty) {
return emptyIterator()
} else {
indicesReferenced = true
return nonEmptyIndices.iterator().map { Slot(it) }
}
}
final override fun containerSlot(slot: Int): Slot {
return Slot(slot)
}
final override fun countItem(item: Item): Int {
return iterator().filter { it.item == item }.count().toInt()
}
final override fun hasAnyOf(items: Set<Item>): Boolean {
return iterator().any { it.item in items }
}
final override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return iterator().any(predicate)
}
final override fun spliterator(): java.util.Spliterator<ItemStack> {
if (isEmpty) {
return ObjectSpliterators.emptySpliterator()
}
indicesReferenced = true
return Spliterator(nonEmptyIndices.intSpliterator())
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -1,21 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.item.ItemStack
open class MatteryCraftingContainer(listener: ContainerListener, private val width: Int, private val height: Int) : MatteryContainer(listener, width * height), CraftingContainer {
constructor(listener: () -> Unit, width: Int, height: Int) : this({ _, _, _ -> listener.invoke() }, width, height)
constructor(width: Int, height: Int) : this(EmptyListener, width, height)
final override fun getWidth(): Int {
return width
}
final override fun getHeight(): Int {
return height
}
final override fun getItems(): MutableList<ItemStack> {
return toList()
}
}

View File

@ -1,47 +0,0 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack
class ShadowContainer(private val parent: Container) : IContainer by IContainer.wrap(parent) {
private val shadowed = Int2ObjectArrayMap<ItemStack>(0)
override fun clearContent() {
shadowed.clear()
parent.clearContent()
}
override fun isEmpty(): Boolean {
return parent.isEmpty && shadowed.isEmpty()
}
override fun getItem(slot: Int): ItemStack {
return shadowed[slot] ?: parent.getItem(slot)
}
override fun removeItem(slot: Int, count: Int): ItemStack {
val shadow = shadowed[slot] ?: return parent.removeItem(slot, count)
val copy = shadow.copyWithCount(shadow.count.coerceAtLeast(count))
shadow.split(count)
if (shadow.isEmpty) shadowed[slot] = ItemStack.EMPTY
return copy
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
shadowed[slot] ?: return parent.removeItemNoUpdate(slot)
val old = shadowed[slot]
shadowed[slot] = ItemStack.EMPTY
return old!!
}
override fun setItem(slot: Int, item: ItemStack) {
shadowed[slot] = item
}
companion object {
fun shadow(container: Container, slot: Int, itemStack: ItemStack): Container {
return ShadowContainer(container).also { it[slot] = itemStack }
}
}
}

View File

@ -1,33 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.entity.player.StackedContents
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.container.util.iterator
import ru.dbotthepony.mc.otm.core.collect.toList
class ShadowCraftingContainer(private val parent: CraftingContainer) : IContainer by ShadowContainer(parent), CraftingContainer {
override fun fillStackedContents(contents: StackedContents) {
for (item in iterator()) {
contents.accountStack(item)
}
}
override fun getWidth(): Int {
return parent.width
}
override fun getHeight(): Int {
return parent.height
}
override fun getItems(): MutableList<ItemStack> {
return iterator().toList()
}
companion object {
fun shadow(container: CraftingContainer, slot: Int, itemStack: ItemStack): CraftingContainer {
return ShadowCraftingContainer(container).also { it[slot] = itemStack }
}
}
}

View File

@ -17,7 +17,7 @@ class UpgradeContainer(
val allowedUpgrades: Set<UpgradeType> = UpgradeType.ALL,
val shouldLockUpgradeSlots: BooleanSupplier = BooleanSupplier { false },
private val listener: Runnable = Runnable {}
) : EnhancedContainer(slotCount), IMatteryUpgrade {
) : EnhancedContainer.Simple(slotCount), IMatteryUpgrade {
override fun notifySlotChanged(slot: Int, old: ItemStack) {
super.notifySlotChanged(slot, old)
listener.run()

View File

@ -40,7 +40,7 @@ class SlottedContainer(
slots: Collection<MarkedSlotProvider<*>>,
private val stillValid: Predicate<Player>,
private val globalChangeListeners: Array<Runnable>
) : IAutomatedContainer, INBTSerializable<Tag> {
) : IAutomatedContainer<ContainerSlot>, INBTSerializable<Tag> {
interface ISlotGroup<T : ContainerSlot> : List<T> {
/**
* @see IAutomatedContainer.addItem
@ -140,7 +140,7 @@ class SlottedContainer(
}
}
override fun containerSlot(slot: Int): IAutomatedContainerSlot {
override fun containerSlot(slot: Int): ContainerSlot {
return slots[slot]
}

View File

@ -6,14 +6,13 @@ 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
fun Container.containerSlot(slot: Int): IContainerSlot {
if (this is IEnhancedContainer) {
if (this is IEnhancedContainer<*>) {
return containerSlot(slot)
} else {
return IContainerSlot.Simple(slot, this)
@ -24,7 +23,7 @@ fun Container.containerSlot(slot: Int): IContainerSlot {
* Returns [IContainerSlot] only if this container is [IEnhancedContainer]
*/
fun Container.containerSlotOrNull(slot: Int): IContainerSlot? {
if (this is IEnhancedContainer) {
if (this is IEnhancedContainer<*>) {
return containerSlot(slot)
} else {
return null
@ -40,7 +39,7 @@ fun Slot.containerSlotOrNull(): IContainerSlot? {
}
operator fun Container.iterator(): Iterator<ItemStack> {
if (this is IEnhancedContainer) {
if (this is IEnhancedContainer<*>) {
return iterator()
} else {
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
@ -48,7 +47,7 @@ operator fun Container.iterator(): Iterator<ItemStack> {
}
fun Container.slotIterator(): Iterator<IContainerSlot> {
if (this is IEnhancedContainer) {
if (this is IEnhancedContainer<*>) {
return slotIterator()
} else {
return (0 until containerSize).iterator().map { IContainerSlot.Simple(it, this) }
@ -56,7 +55,7 @@ fun Container.slotIterator(): Iterator<IContainerSlot> {
}
fun Container.nonEmptySlotIterator(): Iterator<IContainerSlot> {
if (this is IEnhancedContainer) {
if (this is IEnhancedContainer<*>) {
return nonEmptySlotIterator()
} else {
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { IContainerSlot.Simple(it, this) }

View File

@ -23,7 +23,6 @@ import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.map

View File

@ -66,7 +66,7 @@ class MatterDustItem : Item(Properties().stacksTo(64)), IMatterItem {
return MatterValue(value, 1.0)
}
fun moveIntoContainer(matterValue: Decimal, container: IEnhancedContainer, mainSlot: Int, stackingSlot: Int): Decimal {
fun moveIntoContainer(matterValue: Decimal, container: IEnhancedContainer<*>, mainSlot: Int, stackingSlot: Int): Decimal {
@Suppress("name_shadowing")
var matterValue = matterValue

View File

@ -77,7 +77,8 @@ import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IEnhancedCraftingContainer
import ru.dbotthepony.mc.otm.container.util.stream
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.TextComponent
@ -500,7 +501,7 @@ object MatterManager {
height = it.value.ingredients.size.coerceAtLeast(height)
}
val container = MatteryCraftingContainer(width, height)
val container = IEnhancedCraftingContainer.Wrapper(EnhancedContainer.Simple(width * height), width, height)
val realIngredients = ArrayList<ArrayList<RecipeEntry>>()
for (c in it.value.ingredients.indices) {

View File

@ -21,7 +21,6 @@ import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IEnhancedContainer
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.IMatteryContainer
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.util.containerSlotOrNull
@ -65,8 +64,8 @@ open class MatteryMenuSlot(container: Container, index: Int, x: Int = 0, y: Int
}
override fun setChanged() {
if (container is IEnhancedContainer) {
(container as IEnhancedContainer).setChanged(containerSlot)
if (container is IEnhancedContainer<*>) {
(container as IEnhancedContainer<*>).setChanged(containerSlot)
} else {
super.setChanged()
}
@ -83,7 +82,7 @@ open class MatteryMenuSlot(container: Container, index: Int, x: Int = 0, y: Int
override fun getMaxStackSize(): Int {
val container = container
if (container is IEnhancedContainer) {
if (container is IEnhancedContainer<*>) {
return container.getMaxStackSize(slotIndex, ItemStack.EMPTY)
} else {
return super.getMaxStackSize()
@ -93,7 +92,7 @@ open class MatteryMenuSlot(container: Container, index: Int, x: Int = 0, y: Int
override fun getMaxStackSize(itemStack: ItemStack): Int {
val container = container
if (container is IEnhancedContainer) {
if (container is IEnhancedContainer<*>) {
return container.getMaxStackSize(slotIndex, itemStack)
} else {
return super.getMaxStackSize(itemStack)
@ -278,7 +277,7 @@ fun MatteryMenu.makeUpgradeSlots(count: Int, container: UpgradeContainer?): Upgr
allowedTypes[value] = BooleanSupplier { b.get() }
}
val syncContainer = container ?: EnhancedContainer(count)
val syncContainer = container ?: EnhancedContainer.Simple(count)
val isOpen = InstantBooleanInput(this)
return UpgradeSlots(

View File

@ -10,7 +10,6 @@ import net.minecraft.world.item.crafting.RecipeType
import net.minecraft.world.item.crafting.SingleRecipeInput
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.block.entity.decorative.GrillBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList

View File

@ -16,7 +16,6 @@ import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.block.entity.decorative.PainterBlockEntity
import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.addAll

View File

@ -21,7 +21,7 @@ class MatterRecyclerMenu @JvmOverloads constructor(
inventory: Inventory,
tile: MatterRecyclerBlockEntity? = null
) : MatteryPoweredMenu(MMenus.MATTER_RECYCLER, containerID, inventory, tile) {
val input = object : MatteryMenuSlot(tile?.container ?: EnhancedContainer(1), 0) {
val input = object : MatteryMenuSlot(tile?.container ?: EnhancedContainer.Simple(1), 0) {
override fun mayPlace(itemStack: ItemStack): Boolean {
return itemStack.item is MatterDustItem && (itemStack.item as MatterDustItem).getMatterValue(itemStack) != null
}

View File

@ -35,7 +35,7 @@ class MatterReplicatorMenu @JvmOverloads constructor(
val upgrades = makeUpgradeSlots(3, tile?.upgrades)
init {
val container = CombinedContainer(tile?.outputContainer ?: EnhancedContainer(3), tile?.dustContainer ?: EnhancedContainer(2))
val container = CombinedContainer(tile?.outputContainer ?: EnhancedContainer.Simple(3), tile?.dustContainer ?: EnhancedContainer.Simple(2))
storageSlots = immutableList(5) {
addStorageSlot(OutputMenuSlot(container, it, onTake = {

View File

@ -31,7 +31,7 @@ class MatterScannerMenu(
val upgrades = makeUpgradeSlots(2, tile?.upgrades)
init {
val container = tile?.container ?: EnhancedContainer(1)
val container = tile?.container ?: EnhancedContainer.Simple(1)
input = object : MatteryMenuSlot(container, 0, 64, 38) {
override fun mayPlace(itemStack: ItemStack) = MatterManager.canDecompose(itemStack)

View File

@ -32,7 +32,7 @@ class PatternStorageMenu @JvmOverloads constructor(
})
}
val patterns = tile?.container ?: EnhancedContainer(2 * 4)
val patterns = tile?.container ?: EnhancedContainer.Simple(2 * 4)
storageSlots = immutableList(patterns.containerSize) {
addStorageSlot(PatternMenuSlot(patterns, it))

View File

@ -19,7 +19,7 @@ class DriveRackMenu @JvmOverloads constructor(
inventory: Inventory,
tile: DriveRackBlockEntity? = null
) : MatteryPoweredMenu(MMenus.DRIVE_RACK, containerId, inventory, tile) {
val storageSlots = makeSlots(tile?.container ?: EnhancedContainer(4), ::DriveMenuSlot)
val storageSlots = makeSlots(tile?.container ?: EnhancedContainer.Simple(4), ::DriveMenuSlot)
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
val insertPriority = IntInputWithFeedback(this, tile?.let { it::insertPriority })

View File

@ -37,7 +37,7 @@ class DriveViewerMenu(
) : MatteryPoweredMenu(MMenus.DRIVE_VIEWER, containerID, inventory, tile), INetworkedItemViewProvider {
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
val driveSlot = object : MatteryMenuSlot(tile?.container ?: EnhancedContainer(1), 0) {
val driveSlot = object : MatteryMenuSlot(tile?.container ?: EnhancedContainer.Simple(1), 0) {
override fun mayPlace(itemStack: ItemStack): Boolean {
return itemStack.getCapability(MatteryCapability.CONDENSATION_DRIVE) != null
}

View File

@ -38,7 +38,7 @@ class ChemicalGeneratorMenu @JvmOverloads constructor(id: Int, inv: Inventory, t
}
}
val residueSlot = object : MatteryMenuSlot(tile?.residueContainer ?: EnhancedContainer(1), 0) {
val residueSlot = object : MatteryMenuSlot(tile?.residueContainer ?: EnhancedContainer.Simple(1), 0) {
override fun mayPlace(itemStack: ItemStack): Boolean {
return false
}

View File

@ -17,7 +17,7 @@ class CobblerMenu(
inventory: Inventory,
tile: CobblerBlockEntity? = null
) : MatteryMenu(MMenus.COBBLESTONE_GENERATOR, p_38852_, inventory, tile) {
val storageSlots = makeSlots(tile?.container ?: EnhancedContainer(CobblerBlockEntity.CONTAINER_SIZE), ::OutputMenuSlot)
val storageSlots = makeSlots(tile?.container ?: EnhancedContainer.Simple(CobblerBlockEntity.CONTAINER_SIZE), ::OutputMenuSlot)
val redstone = EnumInputWithFeedback<RedstoneSetting>(this)
val itemConfig = ItemConfigPlayerInput(this)

View File

@ -34,13 +34,13 @@ class EssenceStorageMenu(
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
val fluidConfig = FluidConfigPlayerInput(this, tile?.fluidConfig)
val capsuleSlot = object : MatteryMenuSlot(tile?.capsuleContainer ?: EnhancedContainer(1), 0) {
val capsuleSlot = object : MatteryMenuSlot(tile?.capsuleContainer ?: EnhancedContainer.Simple(1), 0) {
override fun mayPlace(itemStack: ItemStack): Boolean {
return itemStack.item is EssenceCapsuleItem && super.mayPlace(itemStack)
}
}
val servoSlot = object : MatteryMenuSlot(tile?.servoContainer ?: EnhancedContainer(1), 0) {
val servoSlot = object : MatteryMenuSlot(tile?.servoContainer ?: EnhancedContainer.Simple(1), 0) {
override fun mayPlace(itemStack: ItemStack): Boolean {
return itemStack.item == MItems.ESSENCE_SERVO && super.mayPlace(itemStack)
}

View File

@ -31,7 +31,7 @@ class PlatePressMenu(
isTwin: Boolean
) : AbstractProcessingMachineMenu(type, containerID, inventory, tile) {
val inputSlots = makeSlots(tile?.inputContainer ?: SlottedContainer.filtered(if (isTwin) 2 else 1), ::MatteryMenuSlot)
val outputSlots = makeSlots(tile?.outputContainer ?: EnhancedContainer(if (isTwin) 2 else 1)) { a, b -> OutputMenuSlot(a, b) { tile?.experience?.popExperience(player as ServerPlayer) } }
val outputSlots = makeSlots(tile?.outputContainer ?: EnhancedContainer.Simple(if (isTwin) 2 else 1)) { a, b -> OutputMenuSlot(a, b) { tile?.experience?.popExperience(player as ServerPlayer) } }
val gauges = immutableList(if (isTwin) 2 else 1) { ProgressGaugeWidget(this, tile?.jobEventLoops?.get(it)) }
override val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true)

View File

@ -40,7 +40,7 @@ class PoweredFurnaceMenu(
// we can't make these slots to reject non-smeltable items
// since mods may add obscure recipes/ingredients which never test true on client
val inputSlots = makeSlots(tile?.inputs ?: SlottedContainer.filtered(2), ::UserFilteredMenuSlot)
val outputSlots = makeSlots(tile?.outputs ?: EnhancedContainer(2)) { c, s -> OutputMenuSlot(c, s) { tile?.experience?.popExperience(player as ServerPlayer) } }
val outputSlots = makeSlots(tile?.outputs ?: EnhancedContainer.Simple(2)) { c, s -> OutputMenuSlot(c, s) { tile?.experience?.popExperience(player as ServerPlayer) } }
val progressGauge = immutableList(2) { ProgressGaugeWidget(this, tile?.jobEventLoops?.get(it)) }
override val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true)

View File

@ -0,0 +1,22 @@
package ru.dbotthepony.mc.otm.player
import net.minecraft.world.item.Item
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IContainerSlot
import ru.dbotthepony.mc.otm.container.IEnhancedContainer
class ExopackContainer(size: Int, val player: MatteryPlayer) : EnhancedContainer<IPlayerInventorySlot>(size) {
private inner class Slot(slot: Int) : IContainerSlot.Simple(slot, this@ExopackContainer), IPlayerInventorySlot {
override var shouldCharge: Boolean
get() = (PlayerInventoryWrapper.SLOTS + slot) in player.slotsChargeFlag
set(value) { if (value) player.slotsChargeFlag.add(PlayerInventoryWrapper.SLOTS + slot) else player.slotsChargeFlag.remove(PlayerInventoryWrapper.SLOTS + slot) }
override var filter: Item?
get() = player.slotFilters[PlayerInventoryWrapper.SLOTS + slot]
set(value) { if (value == null) player.slotFilters.remove(PlayerInventoryWrapper.SLOTS + slot) else player.slotFilters[PlayerInventoryWrapper.SLOTS + slot] = value }
}
override fun containerSlot(slot: Int): IPlayerInventorySlot {
return Slot(slot)
}
}

View File

@ -0,0 +1,7 @@
package ru.dbotthepony.mc.otm.player
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
interface IPlayerInventorySlot : IFilteredContainerSlot {
var shouldCharge: Boolean
}

View File

@ -69,21 +69,20 @@ class MatteryFoodData(private var player: Player) : FoodData() {
private var energyToDrain = Decimal.ZERO
private fun tickExhaustion() {
if (exhaustionLevel >= EXHAUSTION_PER_HUNGER_POINT) {
if (player.matteryPlayer.isAndroid && exhaustionLevel > 0f) {
energyToDrain += PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * (exhaustionLevel / EXHAUSTION_PER_HUNGER_POINT)
exhaustionLevel = 0f
} else if (exhaustionLevel >= EXHAUSTION_PER_HUNGER_POINT) {
var points = (exhaustionLevel / EXHAUSTION_PER_HUNGER_POINT).toInt()
exhaustionLevel %= EXHAUSTION_PER_HUNGER_POINT
if (player.matteryPlayer.isAndroid) {
energyToDrain += PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * points
} else {
if (saturationLevel > 0f) {
val satisfied = min(saturationLevel.roundToInt(), points)
points -= satisfied
saturationLevel -= satisfied
}
foodLevel = max(0, foodLevel - points)
if (saturationLevel > 0f) {
val satisfied = min(saturationLevel.roundToInt(), points)
points -= satisfied
saturationLevel -= satisfied
}
foodLevel = max(0, foodLevel - points)
}
}
@ -111,9 +110,13 @@ class MatteryFoodData(private var player: Player) : FoodData() {
if (player.matteryPlayer.isAndroid) {
if (energyToDrain > Decimal.ZERO)
energyToDrain -= player.matteryPlayer.androidEnergy.extractEnergy(energyToDrain, false)
else if (energyToDrain < Decimal.ZERO)
else if (energyToDrain < Decimal.ZERO) {
energyToDrain += player.matteryPlayer.androidEnergy.receiveEnergy(-energyToDrain, false)
if (player.matteryPlayer.androidEnergy.missingPower <= Decimal.ZERO)
energyToDrain = Decimal.ZERO
}
if (player.level().difficulty == Difficulty.PEACEFUL && PlayerConfig.REGENERATE_ENERGY_IN_PEACEFUL)
player.matteryPlayer.androidEnergy.receiveEnergy(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)

View File

@ -2,6 +2,9 @@ package ru.dbotthepony.mc.otm.player
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.ints.IntSet
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
@ -12,6 +15,7 @@ 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.StringTag
import net.minecraft.network.chat.Component
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
@ -75,13 +79,13 @@ import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.config.PlayerConfig
import ru.dbotthepony.mc.otm.config.ExopackConfig
import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.DynamicallyProxiedContainer
import ru.dbotthepony.mc.otm.container.IContainer
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IContainerSlot
import ru.dbotthepony.mc.otm.container.IEnhancedContainer
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.IMatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.container.util.slotIterator
import ru.dbotthepony.mc.otm.container.vanishCursedItems
import ru.dbotthepony.mc.otm.core.*
@ -95,6 +99,7 @@ import ru.dbotthepony.mc.otm.core.nbt.getStringList
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.Savetables
import ru.dbotthepony.mc.otm.core.util.TickList
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import ru.dbotthepony.mc.otm.menu.IItemStackSortingSettings
import ru.dbotthepony.mc.otm.network.*
@ -158,18 +163,6 @@ class MatteryPlayer(val ply: Player) {
val level: Level get() = capability.ply.level()
}
private inner class PlayerMatteryContainer(size: Int) : MatteryContainer(size) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
if (ply is ServerPlayer) {
val item = new.copy()
tickList.once {
MatteryInventoryChangeTrigger.trigger(ply, combinedInventory, item)
}
}
}
}
/**
* For fields that need to be synchronized only to owning player
*
@ -249,22 +242,20 @@ class MatteryPlayer(val ply: Player) {
* If you want to properly extend Exopack suit capacity, add your value into this map
*/
val exopackSlotModifier = UUIDIntModifiersMap(observer = observer@{
if (it < 0) {
exopackContainer = PlayerMatteryContainer(0)
} else {
exopackContainer = PlayerMatteryContainer(it)
}
exopackContainer = ExopackContainer(it.coerceAtLeast(0), this)
}, backingMap = this.exopackSlotModifierMap.delegate)
val regularSlotFilters = immutableList(Inventory.INVENTORY_SIZE) {
syncher.add(null, StreamCodecs.ITEM_TYPE.nullable())
}
val slotsChargeFlag = syncher.set(
backing = ListenableSet(IntAVLTreeSet()),
codec = StreamCodecs.VAR_INT,
).delegate
val slotFilters = syncher.map(
backing = ListenableMap(Int2ObjectOpenHashMap()),
keyCodec = StreamCodecs.VAR_INT,
valueCodec = StreamCodecs.ITEM_TYPE
).delegate
private fun slotChargeToDefault() {
// броня
slotsChargeFlag.add(36)
@ -277,15 +268,12 @@ class MatteryPlayer(val ply: Player) {
slotChargeToDefault()
}
private var exopackContainerSynchedFilters: List<Closeable> = listOf()
/**
* Exopack container, which actually store items inside Exopack
*/
var exopackContainer: MatteryContainer = PlayerMatteryContainer(0)
var exopackContainer: ExopackContainer = ExopackContainer(0, this)
private set(value) {
_exoPackMenu = null
exopackContainerSynchedFilters.forEach { it.close() }
@Suppress("SENSELESS_COMPARISON") // false positive - fields of player can easily be nulls, despite annotations saying otherwise
if (ply.containerMenu != null && (ply !is ServerPlayer || ply.connection != null)) {
@ -300,7 +288,6 @@ class MatteryPlayer(val ply: Player) {
}
value.deserializeNBT(ply.level().registryAccess(), field.serializeNBT(ply.level().registryAccess()))
exopackContainerSynchedFilters = value.synchableFilters.map { syncher.add0(it) }
field = value
_combinedInventory = null
@ -312,34 +299,9 @@ class MatteryPlayer(val ply: Player) {
private var _combinedInventory2: CombinedContainer? = null
private var _combinedInventory3: CombinedContainer? = null
val wrappedInventory: IMatteryContainer = object : IMatteryContainer, IContainer by DynamicallyProxiedContainer(ply::getInventory) {
override fun getSlotFilter(slot: Int): Item? {
return regularSlotFilters.getOrNull(slot)?.get()
}
val wrappedInventory = PlayerInventoryWrapper(this)
override fun setSlotFilter(slot: Int, filter: Item?): Boolean {
regularSlotFilters.getOrNull(slot)?.accept(filter)
return true
}
override fun setChanged(slot: Int) {
ply.inventory.setChanged()
}
override fun clearSlotFilters() {
regularSlotFilters.forEach { it.accept(null) }
}
override fun containerSlot(slot: Int): IFilteredContainerSlot {
return object : IContainerSlot.Simple(slot, this), IFilteredContainerSlot {
override var filter: Item?
get() = regularSlotFilters.getOrNull(slot)?.get()
set(value) { regularSlotFilters.getOrNull(slot)?.accept(value) }
}
}
}
val wrappedItemInventory: IMatteryContainer = object : IMatteryContainer by wrappedInventory {
val wrappedItemInventory: IEnhancedContainer<IPlayerInventorySlot> = object : IEnhancedContainer<IPlayerInventorySlot> by wrappedInventory {
override fun getContainerSize(): Int {
return 36
}
@ -543,8 +505,8 @@ class MatteryPlayer(val ply: Player) {
}
}
val input = MatteryContainer(Runnable { notify(IdleReason.ITEM) }, 1)
val output = MatteryContainer(Runnable { notify(IdleReason.ITEM) }, 1)
val input = EnhancedContainer.WithListener(1) { notify(IdleReason.ITEM) }
val output = EnhancedContainer.WithListener(1) { notify(IdleReason.ITEM) }
init {
savetables.stateful(::input, "exopack_smelter_input_$index")
@ -573,7 +535,7 @@ class MatteryPlayer(val ply: Player) {
*/
val exopackEnergy = ProfiledEnergyStorage(BatteryBackedEnergyStorage(ply, syncher, Decimal.ZERO, ExopackConfig.ENERGY_CAPACITY, false, onChange = { for (v in smelters) v.notify(MachineJobEventLoop.IdleReason.POWER) }))
val exopackChargeSlots = MatteryContainer(4)
val exopackChargeSlots = EnhancedContainer.Simple(4)
var acceptExopackChargeFromWirelessCharger = true
init {
@ -955,11 +917,8 @@ class MatteryPlayer(val ply: Player) {
}
}
tag["regularSlotFilters"] = ListTag().also {
for (filter in regularSlotFilters) {
it.add(StringTag.valueOf(filter.value?.registryName?.toString() ?: ""))
}
}
tag["slotFilters"] = filtersCodec.encodeStart(registry.createSerializationContext(NbtOps.INSTANCE), slotFilters.entries.map { it.key to it.value })
.getOrThrow { IllegalStateException("Unable to serialize slot filters: $it") }
tag.putIntArray("slotsChargeFlag", slotsChargeFlag.toIntArray())
return tag
@ -977,18 +936,16 @@ class MatteryPlayer(val ply: Player) {
ExopackSlotsExpandedTrigger.trigger(ply, 0, exopackContainer.containerSize)
}
for (filter in regularSlotFilters) {
filter.value = null
}
slotFilters.clear()
slotsChargeFlag.clear()
val regularSlotFilters = tag.getStringList("regularSlotFilters")
for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) {
val path = regularSlotFilters[i].asString
if (path == "") continue
this.regularSlotFilters[i].value = BuiltInRegistries.ITEM.get(ResourceLocation.tryParse(path) ?: continue)
if ("slotFilters" in tag) {
filtersCodec.decode(registry.createSerializationContext(NbtOps.INSTANCE), tag["slotFilters"])
.ifError { LOGGER.error("Unable to deserialize slot filters: ${it.message()}") }
.resultOrPartial()
.ifPresent {
it.first.forEach { (a, b) -> slotFilters[a] = b }
}
}
if ("slotsChargeFlag" in tag) {
@ -1341,6 +1298,15 @@ class MatteryPlayer(val ply: Player) {
@Suppress("unused")
companion object {
private val filtersCodec: Codec<List<Pair<Int, Item>>> = Codec.list(
RecordCodecBuilder.create {
it.group(
Codec.INT.minRange(0).fieldOf("slot").forGetter { it.first },
BuiltInRegistries.ITEM.byNameCodec().fieldOf("filter").forGetter { it.second }
).apply(it, ::Pair)
}
)
private val offhandSlotRange = IntSet.of(40)
init {

View File

@ -0,0 +1,73 @@
package ru.dbotthepony.mc.otm.player
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.container.ISlottedContainer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.set
class PlayerInventoryWrapper(val player: MatteryPlayer) : ISlottedContainer<IPlayerInventorySlot> {
val inventory: Inventory
get() = player.ply.inventory
private inner class Slot(val slot: Int) : IPlayerInventorySlot {
override fun setChanged() {
inventory.setChanged()
}
override var filter: Item?
get() = player.slotFilters[slot]
set(value) { if (value == null) player.slotFilters.remove(slot) else player.slotFilters[slot] = value }
override var shouldCharge: Boolean
get() = slot in player.slotsChargeFlag
set(value) { if (value) player.slotsChargeFlag.add(slot) else player.slotsChargeFlag.remove(slot) }
override var item: ItemStack
get() = inventory[slot]
set(value) { inventory[slot] = value }
override val maxStackSize: Int
get() = inventory.maxStackSize
override fun maxStackSize(item: ItemStack): Int {
return inventory.getMaxStackSize(item)
}
override fun remove(): ItemStack {
return inventory.removeItemNoUpdate(slot)
}
override fun remove(count: Int): ItemStack {
return inventory.removeItem(slot, count)
}
}
private val slots = Array(SLOTS) { Slot(it) }
override val hasFilterableSlots: Boolean
get() = true
override fun containerSlot(slot: Int): IPlayerInventorySlot {
return slots[slot]
}
override fun clearContent() {
slots.forEach { it.remove() }
}
override fun setChanged() {
inventory.setChanged()
}
override fun getContainerSize(): Int {
return slots.size
}
override fun stillValid(player: Player): Boolean {
return inventory.stillValid(player)
}
companion object {
const val SLOTS = 41
}
}