Update MatteryPlayer to use Enhanced containers instead of Mattery containers

This commit is contained in:
DBotThePony 2025-03-14 18:11:39 +07:00
parent d57371ca13
commit a2263c5725
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 156 additions and 65 deletions

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

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

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

@ -0,0 +1,21 @@
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
class ExopackContainer(size: Int, val player: MatteryPlayer) : EnhancedContainer(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

@ -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
@ -76,12 +80,16 @@ 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.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IContainer
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 +103,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,10 +167,10 @@ 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) {
private inner class PlayerMatteryContainer(size: Int) : EnhancedContainer(size) {
override fun notifySlotChanged(slot: Int, old: ItemStack) {
if (ply is ServerPlayer) {
val item = new.copy()
val item = this[slot].copy()
tickList.once {
MatteryInventoryChangeTrigger.trigger(ply, combinedInventory, item)
@ -249,22 +258,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 +284,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 +304,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 +315,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 = object : IEnhancedContainer by wrappedInventory {
override fun getContainerSize(): Int {
return 36
}
@ -955,11 +933,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 +952,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 +1314,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
}
}