User defined slot filters on MatteryContainer level, streamlined slot-level filter interface, cargo crates now allow slots to be filtered
This commit is contained in:
parent
30f07e2fed
commit
3c61a03fd8
@ -678,6 +678,10 @@ private fun gui(provider: MatteryLanguageProvider) {
|
|||||||
|
|
||||||
gui("essence_capsule", "(Almost) Everything you ever knew is within")
|
gui("essence_capsule", "(Almost) Everything you ever knew is within")
|
||||||
gui("essence_capsule2", "This item can be recycled at Essence Servo")
|
gui("essence_capsule2", "This item can be recycled at Essence Servo")
|
||||||
|
|
||||||
|
gui("slot_filter.filtered", "This slot is filtered for automation")
|
||||||
|
gui("slot_filter.forbidden", "This slot is forbidden for automation mechanisms")
|
||||||
|
gui("slot_filter.hint", "To remove slot filter press Ctrl + LMB")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,6 +680,10 @@ private fun gui(provider: MatteryLanguageProvider) {
|
|||||||
|
|
||||||
gui("essence_capsule", "(Почти) Всё, что вы знали, хранится внутри")
|
gui("essence_capsule", "(Почти) Всё, что вы знали, хранится внутри")
|
||||||
gui("essence_capsule2", "Данный предмет может быть переработан внутри хранилища эссенции")
|
gui("essence_capsule2", "Данный предмет может быть переработан внутри хранилища эссенции")
|
||||||
|
|
||||||
|
gui("slot_filter.filtered", "Данный слот отфильтрован для автоматизации")
|
||||||
|
gui("slot_filter.forbidden", "Данный слот запрещён для взаимодействия средствами автоматизации")
|
||||||
|
gui("slot_filter.hint", "Для удаления фильтра нажмите Ctrl + ЛКМ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ class ItemMonitorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
|
|
||||||
// a lot of code is hardcoded to take CraftingContainer as it's input
|
// a lot of code is hardcoded to take CraftingContainer as it's input
|
||||||
// hence we are forced to work around this by providing proxy container
|
// hence we are forced to work around this by providing proxy container
|
||||||
val craftingGrid = object : MatteryContainer(this, 3 * 3) {
|
val craftingGrid = object : MatteryContainer(::setChangedLight, 3 * 3) {
|
||||||
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
||||||
super.setChanged(slot, new, old)
|
super.setChanged(slot, new, old)
|
||||||
craftingGridDummy[slot] = new
|
craftingGridDummy[slot] = new
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package ru.dbotthepony.mc.otm.capability
|
package ru.dbotthepony.mc.otm.capability
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||||
import net.minecraft.ChatFormatting
|
import net.minecraft.ChatFormatting
|
||||||
import net.minecraft.core.Direction
|
import net.minecraft.core.Direction
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.ListTag
|
import net.minecraft.nbt.ListTag
|
||||||
import net.minecraft.nbt.NumericTag
|
|
||||||
import net.minecraft.nbt.StringTag
|
import net.minecraft.nbt.StringTag
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
@ -20,7 +18,6 @@ import net.minecraft.world.entity.boss.wither.WitherBoss
|
|||||||
import net.minecraft.world.entity.monster.Phantom
|
import net.minecraft.world.entity.monster.Phantom
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.item.Item
|
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.item.Items
|
import net.minecraft.world.item.Items
|
||||||
import net.minecraft.world.item.ProjectileWeaponItem
|
import net.minecraft.world.item.ProjectileWeaponItem
|
||||||
@ -63,14 +60,11 @@ import ru.dbotthepony.mc.otm.core.math.Decimal
|
|||||||
import ru.dbotthepony.mc.otm.core.math.minus
|
import ru.dbotthepony.mc.otm.core.math.minus
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
|
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.getStringList
|
import ru.dbotthepony.mc.otm.core.nbt.getStringList
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.map
|
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
import ru.dbotthepony.mc.otm.core.util.IntValueCodec
|
import ru.dbotthepony.mc.otm.core.util.IntValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.ItemStackValueCodec
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
|
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.Savetables
|
import ru.dbotthepony.mc.otm.core.util.Savetables
|
||||||
import ru.dbotthepony.mc.otm.core.util.UUIDValueCodec
|
import ru.dbotthepony.mc.otm.core.util.UUIDValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
|
|
||||||
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
|
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
|
||||||
import ru.dbotthepony.mc.otm.network.*
|
import ru.dbotthepony.mc.otm.network.*
|
||||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||||
@ -144,10 +138,8 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
* For fields that need to be synchronized only to owning player
|
* For fields that need to be synchronized only to owning player
|
||||||
*
|
*
|
||||||
* Please mind if you really need to use this in your mod;
|
* Please mind if you really need to use this in your mod;
|
||||||
* don't forget to specify field names when you add them to synchronizer.
|
* any differences in field order/types/etc will break *everything*
|
||||||
*
|
*
|
||||||
* Even if other side does not have your field defined, both sides will negotiate
|
|
||||||
* and figure out how to deal with this situation as long as you specify field name.
|
|
||||||
*/
|
*/
|
||||||
val synchronizer = FieldSynchronizer()
|
val synchronizer = FieldSynchronizer()
|
||||||
|
|
||||||
@ -160,10 +152,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
* For fields that need to be synchronized to everyone
|
* For fields that need to be synchronized to everyone
|
||||||
*
|
*
|
||||||
* Please mind if you really need to use this in your mod;
|
* Please mind if you really need to use this in your mod;
|
||||||
* don't forget to specify field names when you add them to synchronizer.
|
* any differences in field order/types/etc will break *everything*
|
||||||
*
|
|
||||||
* Even if other side does not have your field defined, both sides will negotiate
|
|
||||||
* and figure out how to deal with this situation as long as you specify field name.
|
|
||||||
*/
|
*/
|
||||||
val publicSynchronizer = FieldSynchronizer()
|
val publicSynchronizer = FieldSynchronizer()
|
||||||
|
|
||||||
@ -192,13 +181,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
* If you want to properly extend Exopack suit capacity, add your value into this map
|
* If you want to properly extend Exopack suit capacity, add your value into this map
|
||||||
*/
|
*/
|
||||||
val exoPackSlotModifier = UUIDIntModifiersMap(observer = observer@{
|
val exoPackSlotModifier = UUIDIntModifiersMap(observer = observer@{
|
||||||
if (ply !is ServerPlayer)
|
|
||||||
return@observer
|
|
||||||
|
|
||||||
if (it < 0) {
|
if (it < 0) {
|
||||||
exoPackSlotCount = 0
|
exoPackContainer = PlayerMatteryContainer(0)
|
||||||
} else {
|
} else {
|
||||||
exoPackSlotCount = it
|
exoPackContainer = PlayerMatteryContainer(it)
|
||||||
}
|
}
|
||||||
}, backingMap = this.exoPackSlotModifierMap)
|
}, backingMap = this.exoPackSlotModifierMap)
|
||||||
|
|
||||||
@ -206,49 +192,28 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
synchronizer.Field(null, ItemValueCodec.nullable)
|
synchronizer.Field(null, ItemValueCodec.nullable)
|
||||||
}
|
}
|
||||||
|
|
||||||
val exoPackSlotFilters by synchronizer.Map(
|
|
||||||
keyCodec = VarIntValueCodec,
|
|
||||||
valueCodec = ItemValueCodec,
|
|
||||||
backingMap = Int2ObjectOpenHashMap()
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current slot count of Exopack
|
|
||||||
*
|
|
||||||
* For properly affecting this value please look at [exoPackSlotModifier]
|
|
||||||
*/
|
|
||||||
var exoPackSlotCount by publicSynchronizer.int(setter = setter@{ value, access, _ ->
|
|
||||||
require(value >= 0) { "Invalid slot count $value" }
|
|
||||||
|
|
||||||
if (value != access.read()) {
|
|
||||||
access.write(value)
|
|
||||||
_exoPackMenu = null
|
|
||||||
exoPackContainer = PlayerMatteryContainer(value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exopack container, which actually store items inside Exopack
|
* Exopack container, which actually store items inside Exopack
|
||||||
*/
|
*/
|
||||||
var exoPackContainer: MatteryContainer = PlayerMatteryContainer(0)
|
var exoPackContainer: MatteryContainer = PlayerMatteryContainer(0)
|
||||||
private set(value) {
|
private set(value) {
|
||||||
_exoPackMenu = null
|
_exoPackMenu = null
|
||||||
|
field.removeFilterSynchronizer()
|
||||||
|
|
||||||
@Suppress("SENSELESS_COMPARISON") // false positive - fields of player can easily be nulls, despite annotations saying otherwise
|
@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)) {
|
if (ply.containerMenu != null && (ply !is ServerPlayer || ply.connection != null)) {
|
||||||
ply.closeContainer()
|
ply.closeContainer()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until value.containerSize.coerceAtMost(field.containerSize)) {
|
|
||||||
if (!field[i].isEmpty) {
|
|
||||||
value[i] = field[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i in value.containerSize until field.containerSize) {
|
for (i in value.containerSize until field.containerSize) {
|
||||||
ply.spawnAtLocation(field[i])
|
if (ply is ServerPlayer)
|
||||||
|
ply.spawnAtLocation(field[i])
|
||||||
|
|
||||||
|
field[i] = ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value.deserializeNBT(field.serializeNBT())
|
||||||
|
value.addFilterSynchronizer(synchronizer)
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,15 +664,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tag["exoPackSlotFilters"] = ListTag().also {
|
|
||||||
for ((slot, filter) in exoPackSlotFilters) {
|
|
||||||
it.add(CompoundTag().also {
|
|
||||||
it["slot"] = slot
|
|
||||||
it["filter"] = filter.registryName!!.toString()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tag["regularSlotFilters"] = ListTag().also {
|
tag["regularSlotFilters"] = ListTag().also {
|
||||||
for (filter in regularSlotFilters) {
|
for (filter in regularSlotFilters) {
|
||||||
it.add(StringTag.valueOf(filter.value?.registryName?.toString() ?: ""))
|
it.add(StringTag.valueOf(filter.value?.registryName?.toString() ?: ""))
|
||||||
@ -724,17 +680,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
filter.value = null
|
filter.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
exoPackSlotFilters.clear()
|
|
||||||
|
|
||||||
for (elem in tag.getCompoundList("exoPackSlotFilters")) {
|
|
||||||
val slot = (elem["slot"] as? NumericTag)?.asInt ?: continue
|
|
||||||
val filter = (elem["filter"] as? StringTag)?.asString ?: continue
|
|
||||||
|
|
||||||
if (slot >= 0) {
|
|
||||||
exoPackSlotFilters[slot] = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(filter) ?: continue) ?: Items.AIR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val regularSlotFilters = tag.getStringList("regularSlotFilters")
|
val regularSlotFilters = tag.getStringList("regularSlotFilters")
|
||||||
|
|
||||||
for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) {
|
for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) {
|
||||||
@ -1046,7 +991,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until exoPackContainer.containerSize) {
|
for (i in 0 until exoPackContainer.containerSize) {
|
||||||
if (exoPackContainer[i].isEmpty && (exoPackSlotFilters[i] === null || exoPackSlotFilters[i] === stack.item)) {
|
if (exoPackContainer[i].isEmpty && exoPackContainer.testSlotFilter(i, stack)) {
|
||||||
exoPackContainer[i] = stack.copy()
|
exoPackContainer[i] = stack.copy()
|
||||||
exoPackContainer[i].popTime = 5
|
exoPackContainer[i].popTime = 5
|
||||||
stack.count = 0
|
stack.count = 0
|
||||||
@ -1100,7 +1045,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until exoPackContainer.containerSize) {
|
for (i in 0 until exoPackContainer.containerSize) {
|
||||||
if (exoPackContainer[i].isEmpty && exoPackSlotFilters[i] === stack.item) {
|
if (exoPackContainer[i].isEmpty && exoPackContainer.hasSlotFilter(i) && exoPackContainer.testSlotFilter(i, stack)) {
|
||||||
exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
|
exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
|
||||||
exoPackContainer[i].popTime = 5
|
exoPackContainer[i].popTime = 5
|
||||||
|
|
||||||
@ -1123,7 +1068,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until exoPackContainer.containerSize) {
|
for (i in 0 until exoPackContainer.containerSize) {
|
||||||
if (exoPackContainer[i].isEmpty && exoPackSlotFilters[i] === null) {
|
if (exoPackContainer[i].isEmpty && !exoPackContainer.hasSlotFilter(i)) {
|
||||||
exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
|
exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
|
||||||
exoPackContainer[i].popTime = 5
|
exoPackContainer[i].popTime = 5
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import ru.dbotthepony.mc.otm.client.render.sprite
|
|||||||
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilteredSlotPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
||||||
import ru.dbotthepony.mc.otm.client.setMousePos
|
import ru.dbotthepony.mc.otm.client.setMousePos
|
||||||
@ -83,7 +83,7 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
|
|||||||
mainInventoryLine.dock = Dock.BOTTOM
|
mainInventoryLine.dock = Dock.BOTTOM
|
||||||
|
|
||||||
for (slot in menu.playerHotbarSlots) {
|
for (slot in menu.playerHotbarSlots) {
|
||||||
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
|
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also {
|
||||||
it.dock = Dock.LEFT
|
it.dock = Dock.LEFT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,24 +18,16 @@ import net.minecraftforge.client.event.ContainerScreenEvent.Render.Foreground
|
|||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.common.MinecraftForge
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.GL11
|
||||||
import org.lwjgl.opengl.GL13
|
import org.lwjgl.opengl.GL13
|
||||||
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
|
||||||
import ru.dbotthepony.mc.otm.config.ClientConfig
|
import ru.dbotthepony.mc.otm.config.ClientConfig
|
||||||
import ru.dbotthepony.mc.otm.client.moveMousePosScaled
|
import ru.dbotthepony.mc.otm.client.moveMousePosScaled
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilteredSlotPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FoldableSlotPanel
|
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
|
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
|
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
|
||||||
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton
|
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.PlayerSlot
|
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
|
|
||||||
import ru.dbotthepony.mc.otm.network.SetInventoryFilterPacket
|
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,7 +161,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
|||||||
hotbarStrip.dock = Dock.BOTTOM
|
hotbarStrip.dock = Dock.BOTTOM
|
||||||
|
|
||||||
for (slot in menu.playerHotbarSlots) {
|
for (slot in menu.playerHotbarSlots) {
|
||||||
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
|
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also {
|
||||||
it.dock = Dock.LEFT
|
it.dock = Dock.LEFT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,7 +211,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
|||||||
hotbarStrip.dock = Dock.BOTTOM
|
hotbarStrip.dock = Dock.BOTTOM
|
||||||
|
|
||||||
for (slot in menu.playerHotbarSlots) {
|
for (slot in menu.playerHotbarSlots) {
|
||||||
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
|
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also {
|
||||||
it.dock = Dock.LEFT
|
it.dock = Dock.LEFT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,7 +254,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
|||||||
for (i in 0 .. (8).coerceAtMost(menu.playerCombinedInventorySlots.size - offset - 1)) {
|
for (i in 0 .. (8).coerceAtMost(menu.playerCombinedInventorySlots.size - offset - 1)) {
|
||||||
val slot = menu.playerCombinedInventorySlots[offset + i]
|
val slot = menu.playerCombinedInventorySlots[offset + i]
|
||||||
|
|
||||||
object : FilteredSlotPanel<MatteryScreen<*>, Slot>(this@MatteryScreen, canvas, slot) {
|
object : UserFilteredSlotPanel<MatteryScreen<*>, Slot>(this@MatteryScreen, canvas, slot) {
|
||||||
init {
|
init {
|
||||||
dock = Dock.LEFT
|
dock = Dock.LEFT
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
|||||||
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
|
||||||
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
|
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
|
||||||
|
|
||||||
class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Component) : MatteryScreen<CargoCrateMenu>(menu, inventory, title) {
|
class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Component) : MatteryScreen<CargoCrateMenu>(menu, inventory, title) {
|
||||||
@ -14,7 +15,7 @@ class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Compon
|
|||||||
val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6)
|
val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6)
|
||||||
|
|
||||||
for (slot in menu.storageSlots)
|
for (slot in menu.storageSlots)
|
||||||
SlotPanel(this, grid, slot)
|
UserFilteredSlotPanel.of(this, grid, slot)
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package ru.dbotthepony.mc.otm.client.screen.panels.slot
|
|||||||
|
|
||||||
import com.mojang.blaze3d.platform.InputConstants
|
import com.mojang.blaze3d.platform.InputConstants
|
||||||
import com.mojang.blaze3d.vertex.PoseStack
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import net.minecraft.ChatFormatting
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.world.inventory.Slot
|
import net.minecraft.world.inventory.Slot
|
||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
@ -16,9 +18,12 @@ import ru.dbotthepony.mc.otm.client.render.drawRect
|
|||||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||||
|
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||||
|
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.core.math.RGBAColor
|
import ru.dbotthepony.mc.otm.core.math.RGBAColor
|
||||||
|
import ru.dbotthepony.mc.otm.menu.UserFilteredSlot
|
||||||
|
|
||||||
abstract class FilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
|
abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
|
||||||
screen: S,
|
screen: S,
|
||||||
parent: EditablePanel<*>?,
|
parent: EditablePanel<*>?,
|
||||||
slot: T,
|
slot: T,
|
||||||
@ -39,9 +44,12 @@ abstract class FilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
|
|||||||
|
|
||||||
screen.renderItemStack(absoluteX, absoluteY, itemStack, null)
|
screen.renderItemStack(absoluteX, absoluteY, itemStack, null)
|
||||||
clearDepth(stack)
|
clearDepth(stack)
|
||||||
|
|
||||||
|
drawColor = SLOT_FILTER_COLOR
|
||||||
|
} else {
|
||||||
|
drawColor = SLOT_BLOCK_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
drawColor = SLOT_FILTER_COLOR
|
|
||||||
drawRect(stack, 0f, 0f, width, height)
|
drawRect(stack, 0f, 0f, width, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,12 +60,27 @@ abstract class FilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
|
|||||||
|
|
||||||
screen.renderComponentTooltip(
|
screen.renderComponentTooltip(
|
||||||
stack,
|
stack,
|
||||||
getItemStackTooltip(itemstack),
|
getItemStackTooltip(itemstack).toMutableList().also {
|
||||||
|
it.add(0, TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY))
|
||||||
|
it.add(1, TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
|
||||||
|
it.add(2, TextComponent(""))
|
||||||
|
},
|
||||||
mouseX.toInt(),
|
mouseX.toInt(),
|
||||||
mouseY.toInt(),
|
mouseY.toInt(),
|
||||||
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: screen.font,
|
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: screen.font,
|
||||||
itemstack
|
itemstack
|
||||||
)
|
)
|
||||||
|
} else if (isHovered && slotFilter === Items.AIR && itemStack.isEmpty) {
|
||||||
|
screen.renderComponentTooltip(
|
||||||
|
stack,
|
||||||
|
ArrayList<Component>().also {
|
||||||
|
it.add(TranslatableComponent("otm.gui.slot_filter.forbidden").withStyle(ChatFormatting.GRAY))
|
||||||
|
it.add(TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
|
||||||
|
},
|
||||||
|
mouseX.toInt(),
|
||||||
|
mouseY.toInt(),
|
||||||
|
screen.font
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
|
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
|
||||||
@ -93,6 +116,7 @@ abstract class FilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
|
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
|
||||||
|
val SLOT_BLOCK_COLOR = RGBAColor(219, 113, 113, 150)
|
||||||
|
|
||||||
fun <S : MatteryScreen<*>, T : Slot> of(
|
fun <S : MatteryScreen<*>, T : Slot> of(
|
||||||
screen: S,
|
screen: S,
|
||||||
@ -104,10 +128,27 @@ abstract class FilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
|
|||||||
height: Float = SIZE,
|
height: Float = SIZE,
|
||||||
noItemIcon: MatterySprite? = null,
|
noItemIcon: MatterySprite? = null,
|
||||||
filter: GetterSetter<Item?>
|
filter: GetterSetter<Item?>
|
||||||
): FilteredSlotPanel<S, T> {
|
): UserFilteredSlotPanel<S, T> {
|
||||||
return object : FilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height, noItemIcon) {
|
return object : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height, noItemIcon) {
|
||||||
override var slotFilter: Item? by filter
|
override var slotFilter: Item? by filter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <S : MatteryScreen<*>, T : UserFilteredSlot> of(
|
||||||
|
screen: S,
|
||||||
|
parent: EditablePanel<*>?,
|
||||||
|
slot: T,
|
||||||
|
x: Float = 0f,
|
||||||
|
y: Float = 0f,
|
||||||
|
width: Float = SIZE,
|
||||||
|
height: Float = SIZE,
|
||||||
|
noItemIcon: MatterySprite? = null
|
||||||
|
): UserFilteredSlotPanel<S, T> {
|
||||||
|
return object : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height, noItemIcon) {
|
||||||
|
override var slotFilter: Item?
|
||||||
|
get() = slot.filter?.get()
|
||||||
|
set(value) { slot.filter?.accept(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.container
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraftforge.common.util.LazyOptional
|
import net.minecraft.world.item.Items
|
||||||
import net.minecraftforge.items.IItemHandler
|
import net.minecraftforge.items.IItemHandler
|
||||||
|
|
||||||
class ContainerHandler @JvmOverloads internal constructor(
|
class ContainerHandler @JvmOverloads internal constructor(
|
||||||
@ -12,7 +12,7 @@ class ContainerHandler @JvmOverloads internal constructor(
|
|||||||
override fun getStackInSlot(slot: Int) = container[slot]
|
override fun getStackInSlot(slot: Int) = container[slot]
|
||||||
|
|
||||||
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
|
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
|
||||||
if (!filter.canInsert(slot, stack))
|
if (!container.testSlotFilter(slot, stack) || !filter.canInsert(slot, stack))
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
filter.preInsert(slot, stack, simulate)
|
filter.preInsert(slot, stack, simulate)
|
||||||
@ -47,11 +47,9 @@ class ContainerHandler @JvmOverloads internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
|
||||||
if (amount == 0)
|
if (amount <= 0 || container.isSlotForbiddenForAutomation(slot))
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
|
|
||||||
require(amount >= 0) { "Can not extract negative amount of items" }
|
|
||||||
|
|
||||||
filter.preExtract(slot, amount, simulate)
|
filter.preExtract(slot, amount, simulate)
|
||||||
|
|
||||||
val localStack = container.getItem(slot)
|
val localStack = container.getItem(slot)
|
||||||
@ -76,6 +74,6 @@ class ContainerHandler @JvmOverloads internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
|
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
|
||||||
return filter.canInsert(slot, stack)
|
return container.testSlotFilter(slot, stack) && filter.canInsert(slot, stack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,33 @@
|
|||||||
package ru.dbotthepony.mc.otm.container
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.ListTag
|
import net.minecraft.nbt.ListTag
|
||||||
import net.minecraft.nbt.Tag
|
import net.minecraft.nbt.Tag
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.minecraft.world.Container
|
import net.minecraft.world.Container
|
||||||
import kotlin.jvm.JvmOverloads
|
import kotlin.jvm.JvmOverloads
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.item.Items
|
||||||
import net.minecraftforge.common.util.INBTSerializable
|
import net.minecraftforge.common.util.INBTSerializable
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries
|
||||||
|
import ru.dbotthepony.mc.otm.core.forValidRefs
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.map
|
import ru.dbotthepony.mc.otm.core.nbt.map
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
|
import ru.dbotthepony.mc.otm.core.registryName
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
|
||||||
|
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||||
|
import ru.dbotthepony.mc.otm.network.IField
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED")
|
||||||
open class MatteryContainer(val watcher: Runnable, private val size: Int) : Container, Iterable<ItemStack>, INBTSerializable<Tag?> {
|
open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : Container, Iterable<ItemStack>, INBTSerializable<Tag?> {
|
||||||
constructor(watcher: BlockEntity, size: Int) : this(watcher::setChanged, size)
|
|
||||||
constructor(size: Int) : this({}, size)
|
constructor(size: Int) : this({}, size)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -26,6 +37,72 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont
|
|||||||
private var ignoreChangeNotifications = 0
|
private var ignoreChangeNotifications = 0
|
||||||
protected val slots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
|
protected val slots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
|
||||||
private val trackedSlots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
|
private val trackedSlots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
|
||||||
|
private val filters: Array<Item?> = arrayOfNulls(size)
|
||||||
|
private var filterSynchronizer: WeakReference<IField<MutableMap<Int, Item>>>? = null
|
||||||
|
|
||||||
|
fun clearSlotFilters() {
|
||||||
|
Arrays.fill(filters, null)
|
||||||
|
filterSynchronizer?.get()?.value?.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFilterSynchronizer() {
|
||||||
|
filterSynchronizer?.get()?.remove()
|
||||||
|
filterSynchronizer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addFilterSynchronizer(synchronizer: FieldSynchronizer): IField<MutableMap<Int, Item>> {
|
||||||
|
check(filterSynchronizer?.get() == null) { "Already has filter synchronizer" }
|
||||||
|
|
||||||
|
val field = synchronizer.Map(
|
||||||
|
keyCodec = VarIntValueCodec,
|
||||||
|
valueCodec = ItemValueCodec,
|
||||||
|
backingMap = Int2ObjectOpenHashMap(),
|
||||||
|
callback = {
|
||||||
|
for (change in it) {
|
||||||
|
change.map({ k, v -> filters[k] = v }, { k -> filters[k] = null }, { Arrays.fill(filters, null) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for ((i, filter) in filters.withIndex()) {
|
||||||
|
if (filter != null) {
|
||||||
|
field.value[i] = filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterSynchronizer = WeakReference(field)
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSlotFilter(slot: Int, filter: Item? = null) {
|
||||||
|
if (filters[slot] !== filter) {
|
||||||
|
filters[slot] = filter
|
||||||
|
|
||||||
|
filterSynchronizer?.get()?.let {
|
||||||
|
if (filter == null) {
|
||||||
|
it.value.remove(slot)
|
||||||
|
} else {
|
||||||
|
it.value[slot] = filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSlotFilter(slot: Int) = filters[slot]
|
||||||
|
fun hasSlotFilter(slot: Int) = filters[slot] !== null
|
||||||
|
fun isSlotForbiddenForAutomation(slot: Int) = filters[slot] === Items.AIR
|
||||||
|
|
||||||
|
fun testSlotFilter(slot: Int, itemStack: ItemStack): Boolean {
|
||||||
|
return testSlotFilter(slot, itemStack.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSlotFilter(slot: Int, item: Item): Boolean {
|
||||||
|
if (filters[slot] == null) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return filters[slot] === item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final override fun getContainerSize() = size
|
final override fun getContainerSize() = size
|
||||||
|
|
||||||
@ -44,42 +121,31 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont
|
|||||||
protected open fun startedIgnoringUpdates() {}
|
protected open fun startedIgnoringUpdates() {}
|
||||||
protected open fun stoppedIgnoringUpdates() {}
|
protected open fun stoppedIgnoringUpdates() {}
|
||||||
|
|
||||||
private fun deserializeNBT(tag: CompoundTag?) {
|
private fun deserializeNBT(tag: CompoundTag) {
|
||||||
Arrays.fill(slots, ItemStack.EMPTY)
|
|
||||||
|
|
||||||
if (tag == null) {
|
|
||||||
setChanged()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// нам не интересен размер
|
// нам не интересен размер
|
||||||
tag.map("items") { it: ListTag ->
|
tag.map("items") { it: ListTag ->
|
||||||
|
deserializeNBT(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.map("filters") { it: ListTag ->
|
||||||
|
val map = filterSynchronizer?.get()
|
||||||
|
|
||||||
for (i in 0 until it.size.coerceAtMost(size)) {
|
for (i in 0 until it.size.coerceAtMost(size)) {
|
||||||
slots[i] = ItemStack.of(it[i] as CompoundTag)
|
val nbt = it[i] as CompoundTag
|
||||||
|
val index = nbt.getInt("slotIndex")
|
||||||
|
filters[index] = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(nbt.getString("filter")) ?: continue)
|
||||||
|
|
||||||
|
if (filters[index] != null) {
|
||||||
|
map?.value?.put(index, filters[index]!!)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setChanged()
|
setChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deserializeNBT(tag: ListTag?) {
|
private fun deserializeNBT(tag: ListTag) {
|
||||||
Arrays.fill(slots, ItemStack.EMPTY)
|
if (tag.all { (it as CompoundTag).contains("slotIndex") }) {
|
||||||
|
|
||||||
if (tag == null) {
|
|
||||||
setChanged()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var isIndexed = true
|
|
||||||
|
|
||||||
for (i in 0 until tag.size.coerceAtMost(size)) {
|
|
||||||
if (!(tag[i] as CompoundTag).contains("slotIndex")) {
|
|
||||||
isIndexed = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isIndexed) {
|
|
||||||
val freeSlots = IntAVLTreeSet()
|
val freeSlots = IntAVLTreeSet()
|
||||||
|
|
||||||
for (i in 0 until size)
|
for (i in 0 until size)
|
||||||
@ -104,18 +170,21 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont
|
|||||||
slots[i] = ItemStack.of(tag[i] as CompoundTag)
|
slots[i] = ItemStack.of(tag[i] as CompoundTag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deserializeNBT(tag: Tag?) {
|
override fun deserializeNBT(tag: Tag?) {
|
||||||
|
Arrays.fill(slots, ItemStack.EMPTY)
|
||||||
|
Arrays.fill(filters, null)
|
||||||
|
filterSynchronizer?.get()?.value?.clear()
|
||||||
|
|
||||||
when (tag) {
|
when (tag) {
|
||||||
is CompoundTag -> deserializeNBT(tag)
|
is CompoundTag -> deserializeNBT(tag)
|
||||||
is ListTag -> deserializeNBT(tag)
|
is ListTag -> {
|
||||||
else -> {
|
deserializeNBT(tag)
|
||||||
Arrays.fill(slots, ItemStack.EMPTY)
|
|
||||||
setChanged()
|
setChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else -> setChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,13 +192,26 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont
|
|||||||
if (ignoreChangeNotifications == 0) watcher.run()
|
if (ignoreChangeNotifications == 0) watcher.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serializeNBT(): ListTag {
|
override fun serializeNBT(): CompoundTag {
|
||||||
return ListTag().also {
|
return CompoundTag().also {
|
||||||
for ((i, item) in slots.withIndex()) {
|
it["items"] = ListTag().also {
|
||||||
if (!item.isEmpty) {
|
for ((i, item) in slots.withIndex()) {
|
||||||
it.add(item.serializeNBT().also {
|
if (!item.isEmpty) {
|
||||||
it["slotIndex"] = i
|
it.add(item.serializeNBT().also {
|
||||||
})
|
it["slotIndex"] = i
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it["filters"] = ListTag().also {
|
||||||
|
for ((i, filter) in filters.withIndex()) {
|
||||||
|
if (filter != null) {
|
||||||
|
it.add(CompoundTag().also {
|
||||||
|
it["filter"] = filter.registryName!!.toString()
|
||||||
|
it["slotIndex"] = i
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,8 +173,8 @@ class ExoPackInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen
|
|||||||
return super.quickMoveStack(ply, slotIndex)
|
return super.quickMoveStack(ply, slotIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canTakeItemForPickAll(p_38908_: ItemStack, p_38909_: Slot): Boolean {
|
override fun canTakeItemForPickAll(itemStack: ItemStack, slot: Slot): Boolean {
|
||||||
return p_38909_.container != craftingResultContainer && super.canTakeItemForPickAll(p_38908_, p_38909_)
|
return slot.container != craftingResultContainer && super.canTakeItemForPickAll(itemStack, slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : ContainerSynchronizer {
|
companion object : ContainerSynchronizer {
|
||||||
|
@ -27,15 +27,18 @@ import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
|
|||||||
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
||||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||||
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
|
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
|
||||||
|
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||||
import ru.dbotthepony.mc.otm.core.util.BigDecimalValueCodec
|
import ru.dbotthepony.mc.otm.core.util.BigDecimalValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
|
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
|
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.IStreamCodec
|
import ru.dbotthepony.mc.otm.core.util.IStreamCodec
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.NullValueCodec
|
import ru.dbotthepony.mc.otm.core.util.NullValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
|
import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget
|
import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget
|
||||||
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||||
|
import ru.dbotthepony.mc.otm.network.IField
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryPacket
|
import ru.dbotthepony.mc.otm.network.MatteryPacket
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
|
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
|
||||||
import ru.dbotthepony.mc.otm.network.MenuFieldPacket
|
import ru.dbotthepony.mc.otm.network.MenuFieldPacket
|
||||||
@ -239,26 +242,22 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
return _matteryWidgets[index]
|
return _matteryWidgets[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
open inner class InventorySlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
|
open inner class InventorySlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : UserFilteredSlot(container, index, x, y) {
|
||||||
override fun mayPlace(itemStack: ItemStack): Boolean {
|
override fun mayPlace(itemStack: ItemStack): Boolean {
|
||||||
return super.mayPlace(itemStack) && !isInventorySlotLocked(index)
|
return !isInventorySlotLocked(index) && super.mayPlace(itemStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mayPickup(player: Player): Boolean {
|
override fun mayPickup(player: Player): Boolean {
|
||||||
return super.mayPickup(player) && !isInventorySlotLocked(index)
|
return !isInventorySlotLocked(index) && super.mayPickup(player)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isSameInventory(other: Slot): Boolean {
|
override fun isSameInventory(other: Slot): Boolean {
|
||||||
if (container === inventory || container === ply.matteryPlayer?.exoPackContainer)
|
if (container === inventory || container === ply.matteryPlayer?.exoPackContainer)
|
||||||
return other.container === inventory || other.container === ply.matteryPlayer?.exoPackContainer
|
return (other.container === inventory || other.container === ply.matteryPlayer?.exoPackContainer) && isSameFilter(other)
|
||||||
|
|
||||||
return super.isSameInventory(other)
|
return super.isSameInventory(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
// фильтр существует только для автоматизации
|
|
||||||
// игрок всё равно может класть предмет вручную, даже если он не соответствует фильтру
|
|
||||||
internal val filter: GetterSetter<Item?>?
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val mattery = ply.matteryPlayer
|
val mattery = ply.matteryPlayer
|
||||||
|
|
||||||
@ -270,7 +269,7 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
)
|
)
|
||||||
} else if (container === mattery.exoPackContainer) {
|
} else if (container === mattery.exoPackContainer) {
|
||||||
filter = GetterSetter.of(
|
filter = GetterSetter.of(
|
||||||
getter = { mattery.exoPackSlotFilters[slotIndex] },
|
getter = { mattery.exoPackContainer.getSlotFilter(slotIndex) },
|
||||||
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.EXOPACK, slotIndex, it)) }
|
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.EXOPACK, slotIndex, it)) }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -359,7 +358,7 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
val payload = mSynchronizer.collectNetworkPayload()
|
val payload = mSynchronizer.collectNetworkPayload()
|
||||||
|
|
||||||
if (payload != null) {
|
if (payload != null) {
|
||||||
MenuNetworkChannel.send(ply, MenuFieldPacket(payload))
|
MenuNetworkChannel.send(ply, MenuFieldPacket(containerId, payload))
|
||||||
}
|
}
|
||||||
|
|
||||||
super.broadcastChanges()
|
super.broadcastChanges()
|
||||||
@ -384,7 +383,7 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
val payload = mSynchronizer.collectNetworkPayload()
|
val payload = mSynchronizer.collectNetworkPayload()
|
||||||
|
|
||||||
if (payload != null) {
|
if (payload != null) {
|
||||||
MenuNetworkChannel.send(ply, MenuFieldPacket(payload))
|
MenuNetworkChannel.send(ply, MenuFieldPacket(containerId, payload))
|
||||||
}
|
}
|
||||||
|
|
||||||
super.broadcastFullState()
|
super.broadcastFullState()
|
||||||
@ -420,6 +419,23 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
return pSlot
|
return pSlot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pSlot is UserFilteredSlot && !pSlot.hasSetFilter) {
|
||||||
|
val container = pSlot.container
|
||||||
|
|
||||||
|
val input: PlayerInput<Item?>
|
||||||
|
val field: IField<Item?>
|
||||||
|
|
||||||
|
if (container is MatteryContainer) {
|
||||||
|
input = PlayerInput(ItemValueCodec.nullable, handler = { container.setSlotFilter(pSlot.slotIndex, it) })
|
||||||
|
field = mSynchronizer.ComputedField(getter = { container.getSlotFilter(pSlot.slotIndex) }, ItemValueCodec.nullable)
|
||||||
|
} else {
|
||||||
|
input = PlayerInput(ItemValueCodec.nullable, handler = { throw UnsupportedOperationException() })
|
||||||
|
field = mSynchronizer.ComputedField(getter = { null }, ItemValueCodec.nullable)
|
||||||
|
}
|
||||||
|
|
||||||
|
pSlot.filter = GetterSetter.of(getter = field::value, setter = input::input)
|
||||||
|
}
|
||||||
|
|
||||||
return super.addSlot(pSlot)
|
return super.addSlot(pSlot)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,7 +504,7 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
val copy = slot.item.copy()
|
val copy = slot.item.copy()
|
||||||
var any = false
|
var any = false
|
||||||
|
|
||||||
if (target.any { it.any { it is InventorySlot && it.filter != null } }) {
|
if (target.any { it.any { it is UserFilteredSlot && it.filter != null } }) {
|
||||||
for (collection in target) {
|
for (collection in target) {
|
||||||
if (moveItemStackTo(slot, collection, onlyFiltered = true)) {
|
if (moveItemStackTo(slot, collection, onlyFiltered = true)) {
|
||||||
any = true
|
any = true
|
||||||
@ -525,11 +541,11 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
return moveItemStackToSlots(stack, _playerInventorySlots, simulate = simulate)
|
return moveItemStackToSlots(stack, _playerInventorySlots, simulate = simulate)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canTakeItemForPickAll(p_38908_: ItemStack, p_38909_: Slot): Boolean {
|
override fun canTakeItemForPickAll(itemStack: ItemStack, slot: Slot): Boolean {
|
||||||
if (p_38909_ is EquipmentSlot)
|
if (slot is EquipmentSlot)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
return super.canTakeItemForPickAll(p_38908_, p_38909_) && (p_38909_ !is MatterySlot || p_38909_.canTakeItemForPickAll()) && !p_38909_.isCurioSlot
|
return super.canTakeItemForPickAll(itemStack, slot) && (slot !is MatterySlot || slot.canTakeItemForPickAll()) && !slot.isCurioSlot
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun moveItemStackTo(
|
override fun moveItemStackTo(
|
||||||
@ -579,9 +595,9 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
// first pass - stack with existing slots
|
// first pass - stack with existing slots
|
||||||
if (copy.isStackable) {
|
if (copy.isStackable) {
|
||||||
for (slot in slots) {
|
for (slot in slots) {
|
||||||
if (onlyFiltered && (slot !is InventorySlot || slot.filter == null || slot.filter.get() != item.item)) {
|
if (onlyFiltered && (slot !is UserFilteredSlot || slot.filter == null || slot.filter!!.get() != item.item)) {
|
||||||
continue
|
continue
|
||||||
} else if (!onlyFiltered && slot is InventorySlot && slot.filter != null && slot.filter.get() != null && slot.filter.get() != item.item) {
|
} else if (!onlyFiltered && slot is UserFilteredSlot && slot.filter != null && slot.filter!!.get() != null && slot.filter!!.get() != item.item) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,9 +622,9 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
|
|
||||||
// second pass - drop stack into first free slot
|
// second pass - drop stack into first free slot
|
||||||
for (slot in slots) {
|
for (slot in slots) {
|
||||||
if (onlyFiltered && (slot !is InventorySlot || slot.filter == null || slot.filter.get() != item.item)) {
|
if (onlyFiltered && (slot !is UserFilteredSlot || slot.filter == null || slot.filter!!.get() != item.item)) {
|
||||||
continue
|
continue
|
||||||
} else if (!onlyFiltered && slot is InventorySlot && slot.filter != null && slot.filter.get() != null && slot.filter.get() != item.item) {
|
} else if (!onlyFiltered && slot is UserFilteredSlot && slot.filter != null && slot.filter!!.get() != null && slot.filter!!.get() != item.item) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,13 @@ package ru.dbotthepony.mc.otm.menu
|
|||||||
import net.minecraft.world.Container
|
import net.minecraft.world.Container
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.Slot
|
import net.minecraft.world.inventory.Slot
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.capability.energy
|
import ru.dbotthepony.mc.otm.capability.energy
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
import ru.dbotthepony.mc.otm.client.minecraft
|
||||||
|
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||||
import ru.dbotthepony.mc.otm.runOnClient
|
import ru.dbotthepony.mc.otm.runOnClient
|
||||||
|
|
||||||
open class MatterySlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0) : Slot(container, index, x, y) {
|
open class MatterySlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0) : Slot(container, index, x, y) {
|
||||||
@ -26,6 +28,35 @@ open class MatterySlot @JvmOverloads constructor(container: Container, index: In
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
|
||||||
|
var hasSetFilter = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
var filter: GetterSetter<Item?>? = null
|
||||||
|
set(value) {
|
||||||
|
hasSetFilter = true
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canTakeItemForPickAll(): Boolean {
|
||||||
|
return filter?.get() == null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSameFilter(other: Slot): Boolean {
|
||||||
|
if (other !is UserFilteredSlot)
|
||||||
|
return filter?.get() == null
|
||||||
|
|
||||||
|
return (
|
||||||
|
(other.filter == null && filter == null) ||
|
||||||
|
(other.filter != null && filter != null && other.filter!!.get() == filter!!.get())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSameInventory(other: Slot): Boolean {
|
||||||
|
return isSameFilter(other) && super.isSameInventory(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open class MachineOutputSlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0, val onTake: () -> Unit = {}) : MatterySlot(container, index, x, y) {
|
open class MachineOutputSlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0, val onTake: () -> Unit = {}) : MatterySlot(container, index, x, y) {
|
||||||
override fun mayPlace(itemStack: ItemStack): Boolean {
|
override fun mayPlace(itemStack: ItemStack): Boolean {
|
||||||
return false
|
return false
|
||||||
|
@ -7,6 +7,7 @@ import ru.dbotthepony.mc.otm.core.immutableList
|
|||||||
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.MatterySlot
|
import ru.dbotthepony.mc.otm.menu.MatterySlot
|
||||||
|
import ru.dbotthepony.mc.otm.menu.UserFilteredSlot
|
||||||
import ru.dbotthepony.mc.otm.registry.MMenus
|
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||||
|
|
||||||
class CargoCrateMenu @JvmOverloads constructor(
|
class CargoCrateMenu @JvmOverloads constructor(
|
||||||
@ -14,7 +15,7 @@ class CargoCrateMenu @JvmOverloads constructor(
|
|||||||
inventory: Inventory,
|
inventory: Inventory,
|
||||||
tile: CargoCrateBlockEntity? = null
|
tile: CargoCrateBlockEntity? = null
|
||||||
) : MatteryMenu(MMenus.CARGO_CRATE, p_38852_, inventory, tile) {
|
) : MatteryMenu(MMenus.CARGO_CRATE, p_38852_, inventory, tile) {
|
||||||
val storageSlots: List<MatterySlot>
|
val storageSlots: List<UserFilteredSlot>
|
||||||
|
|
||||||
private val trackedPlayerOpen = !inventory.player.isSpectator
|
private val trackedPlayerOpen = !inventory.player.isSpectator
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ class CargoCrateMenu @JvmOverloads constructor(
|
|||||||
val container = tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY)
|
val container = tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY)
|
||||||
|
|
||||||
storageSlots = immutableList(CargoCrateBlockEntity.CAPACITY) {
|
storageSlots = immutableList(CargoCrateBlockEntity.CAPACITY) {
|
||||||
addStorageSlot(MatterySlot(container, it))
|
addStorageSlot(UserFilteredSlot(container, it))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackedPlayerOpen) {
|
if (trackedPlayerOpen) {
|
||||||
|
@ -57,6 +57,9 @@ sealed interface IField<V> : ReadOnlyProperty<Any?, V>, Supplier<V>, () -> V {
|
|||||||
fun markDirty()
|
fun markDirty()
|
||||||
fun markDirty(endpoint: FieldSynchronizer.Endpoint)
|
fun markDirty(endpoint: FieldSynchronizer.Endpoint)
|
||||||
val value: V
|
val value: V
|
||||||
|
val isRemoved: Boolean
|
||||||
|
|
||||||
|
fun remove()
|
||||||
|
|
||||||
fun write(stream: DataOutputStream, endpoint: FieldSynchronizer.Endpoint)
|
fun write(stream: DataOutputStream, endpoint: FieldSynchronizer.Endpoint)
|
||||||
fun read(stream: DataInputStream)
|
fun read(stream: DataInputStream)
|
||||||
@ -94,7 +97,23 @@ data class MapChangeset<out K, out V>(
|
|||||||
val action: MapAction,
|
val action: MapAction,
|
||||||
val key: K?,
|
val key: K?,
|
||||||
val value: V?
|
val value: V?
|
||||||
)
|
) {
|
||||||
|
inline fun map(add: (K, V) -> Unit, remove: (K) -> Unit) {
|
||||||
|
when (action) {
|
||||||
|
MapAction.ADD -> add.invoke(key!!, value!!)
|
||||||
|
MapAction.REMOVE -> remove.invoke(key!!)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun map(add: (K, V) -> Unit, remove: (K) -> Unit, clear: () -> Unit) {
|
||||||
|
when (action) {
|
||||||
|
MapAction.CLEAR -> clear.invoke()
|
||||||
|
MapAction.ADD -> add.invoke(key!!, value!!)
|
||||||
|
MapAction.REMOVE -> remove.invoke(key!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class MapAction {
|
enum class MapAction {
|
||||||
CLEAR, ADD, REMOVE
|
CLEAR, ADD, REMOVE
|
||||||
@ -109,7 +128,13 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
constructor() : this(Runnable {}, false)
|
constructor() : this(Runnable {}, false)
|
||||||
constructor(callback: Runnable) : this(callback, false)
|
constructor(callback: Runnable) : this(callback, false)
|
||||||
|
|
||||||
private val fields = ArrayList<AbstractField<*>>(0)
|
private var freeSlots = 0
|
||||||
|
// почему не удалять поля напрямую?
|
||||||
|
// чтоб не возникло проблем в состоянии гонки
|
||||||
|
// формируем пакет -> удаляем поле по обе стороны -> клиент принимает пакет -> клиент считывает неверные данные
|
||||||
|
// конечно, всё равно всё сломается если было удалено поле, которое находится в пакете
|
||||||
|
// но если поля нет в пакете, то всё окей
|
||||||
|
private val fields = ArrayList<AbstractField<*>?>(0)
|
||||||
private val observers = ArrayList<AbstractField<*>>(0)
|
private val observers = ArrayList<AbstractField<*>>(0)
|
||||||
|
|
||||||
private var nextFieldID = 0
|
private var nextFieldID = 0
|
||||||
@ -463,7 +488,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
|
|
||||||
fun markDirty() {
|
fun markDirty() {
|
||||||
for (field in fields) {
|
for (field in fields) {
|
||||||
field.markDirty(this)
|
field?.markDirty(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,6 +500,10 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
dirtyFields.add(field)
|
dirtyFields.add(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun removeDirtyField(field: AbstractField<*>) {
|
||||||
|
dirtyFields.remove(field)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun <K, V> getMapBacklog(map: Map<K, V>): LinkedList<Pair<Any?, (DataOutputStream) -> Unit>> {
|
internal fun <K, V> getMapBacklog(map: Map<K, V>): LinkedList<Pair<Any?, (DataOutputStream) -> Unit>> {
|
||||||
if (unused) {
|
if (unused) {
|
||||||
return LinkedList()
|
return LinkedList()
|
||||||
@ -521,10 +550,57 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
|
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
abstract inner class AbstractField<V> : IField<V> {
|
abstract inner class AbstractField<V> : IField<V> {
|
||||||
val id: Int = fields.size + 1
|
val id: Int
|
||||||
|
|
||||||
init {
|
init {
|
||||||
fields.add(this)
|
if (freeSlots > 0) {
|
||||||
|
var found = -1
|
||||||
|
|
||||||
|
for (i in fields.indices) {
|
||||||
|
if (fields[i] == null) {
|
||||||
|
fields[i] = this
|
||||||
|
found = i + 1
|
||||||
|
freeSlots--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found == -1) {
|
||||||
|
throw RuntimeException("freeSlots = $freeSlots but no null entries in field list!")
|
||||||
|
} else {
|
||||||
|
id = found
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fields.add(this)
|
||||||
|
id = fields.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final override var isRemoved = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun remove() {
|
||||||
|
if (isRemoved)
|
||||||
|
return
|
||||||
|
|
||||||
|
isRemoved = true
|
||||||
|
freeSlots++
|
||||||
|
fields[id - 1] = null
|
||||||
|
observers.remove(this)
|
||||||
|
|
||||||
|
while (fields[fields.size - 1] == null) {
|
||||||
|
fields.removeAt(fields.size - 1)
|
||||||
|
freeSlots--
|
||||||
|
}
|
||||||
|
|
||||||
|
forEachEndpoint {
|
||||||
|
it.removeDirtyField(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun markDirty(endpoint: Endpoint) {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
|
endpoint.addDirtyField(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,6 +640,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
override fun observe(): Boolean {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
if (!isDirty && !codec.compare(remote, field)) {
|
if (!isDirty && !codec.compare(remote, field)) {
|
||||||
notifyEndpoints(this@Field)
|
notifyEndpoints(this@Field)
|
||||||
isDirty = true
|
isDirty = true
|
||||||
@ -603,21 +680,20 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty() {
|
override fun markDirty() {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
notifyEndpoints(this@Field)
|
notifyEndpoints(this@Field)
|
||||||
isDirty = true
|
isDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty(endpoint: Endpoint) {
|
|
||||||
endpoint.addDirtyField(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
codec.write(stream, field)
|
codec.write(stream, field)
|
||||||
isDirty = false
|
isDirty = false
|
||||||
remote = codec.copy(field)
|
remote = codec.copy(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(stream: DataInputStream) {
|
override fun read(stream: DataInputStream) {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
val value = codec.read(stream)
|
val value = codec.read(stream)
|
||||||
val setter = this.setter
|
val setter = this.setter
|
||||||
|
|
||||||
@ -647,6 +723,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
override fun observe(): Boolean {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
if (!isDirty && (remote == null || !codec.compare(remote ?: throw ConcurrentModificationException(), value))) {
|
if (!isDirty && (remote == null || !codec.compare(remote ?: throw ConcurrentModificationException(), value))) {
|
||||||
notifyEndpoints(this)
|
notifyEndpoints(this)
|
||||||
isDirty = true
|
isDirty = true
|
||||||
@ -657,24 +734,23 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty() {
|
override fun markDirty() {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
notifyEndpoints(this)
|
notifyEndpoints(this)
|
||||||
isDirty = true
|
isDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty(endpoint: Endpoint) {
|
|
||||||
endpoint.addDirtyField(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val value: V
|
override val value: V
|
||||||
get() = clientValue ?: getter.invoke()
|
get() = clientValue ?: getter.invoke()
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
val value = value
|
val value = value
|
||||||
codec.write(stream, value)
|
codec.write(stream, value)
|
||||||
isDirty = false
|
isDirty = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(stream: DataInputStream) {
|
override fun read(stream: DataInputStream) {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
val newValue = codec.read(stream)
|
val newValue = codec.read(stream)
|
||||||
clientValue = newValue
|
clientValue = newValue
|
||||||
observer.invoke(newValue)
|
observer.invoke(newValue)
|
||||||
@ -715,6 +791,8 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
override fun observe(): Boolean {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
|
|
||||||
if (!isDirty && !codec.compare(remote, value)) {
|
if (!isDirty && !codec.compare(remote, value)) {
|
||||||
notifyEndpoints(this)
|
notifyEndpoints(this)
|
||||||
isDirty = true
|
isDirty = true
|
||||||
@ -725,21 +803,20 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty() {
|
override fun markDirty() {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
notifyEndpoints(this)
|
notifyEndpoints(this)
|
||||||
isDirty = true
|
isDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty(endpoint: Endpoint) {
|
|
||||||
endpoint.addDirtyField(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
val value = value
|
val value = value
|
||||||
codec.write(stream, value)
|
codec.write(stream, value)
|
||||||
isDirty = false
|
isDirty = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(stream: DataInputStream) {
|
override fun read(stream: DataInputStream) {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
this.value = codec.read(stream)
|
this.value = codec.read(stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -782,6 +859,8 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
override fun observe(): Boolean {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
|
|
||||||
if (isRemote) {
|
if (isRemote) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -815,6 +894,8 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty() {
|
override fun markDirty() {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
|
|
||||||
if (isRemote) {
|
if (isRemote) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -857,6 +938,8 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty(endpoint: Endpoint) {
|
override fun markDirty(endpoint: Endpoint) {
|
||||||
|
check(!isRemoved) { "Field was removed" }
|
||||||
|
|
||||||
if (isRemote) {
|
if (isRemote) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1025,7 +1108,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
*/
|
*/
|
||||||
fun invalidate() {
|
fun invalidate() {
|
||||||
for (field in fields) {
|
for (field in fields) {
|
||||||
field.markDirty()
|
field?.markDirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachEndpoint {
|
forEachEndpoint {
|
||||||
|
@ -544,12 +544,8 @@ class SetInventoryFilterPacket(val type: Type, val slot: Int, val item: Item?) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type.EXOPACK -> {
|
Type.EXOPACK -> {
|
||||||
if (slot in 0 until player.exoPackSlotCount) {
|
if (slot in 0 until player.exoPackContainer.containerSize) {
|
||||||
if (item == null) {
|
player.exoPackContainer.setSlotFilter(slot, item)
|
||||||
player.exoPackSlotFilters.remove(slot)
|
|
||||||
} else {
|
|
||||||
player.exoPackSlotFilters[slot] = item
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ import net.minecraft.world.item.ItemStack
|
|||||||
import net.minecraftforge.network.NetworkDirection
|
import net.minecraftforge.network.NetworkDirection
|
||||||
import net.minecraftforge.network.NetworkEvent
|
import net.minecraftforge.network.NetworkEvent
|
||||||
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorPlayerSettings
|
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorPlayerSettings
|
||||||
|
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
import ru.dbotthepony.mc.otm.client.minecraft
|
||||||
import ru.dbotthepony.mc.otm.compat.InventoryScrollPacket
|
import ru.dbotthepony.mc.otm.compat.InventoryScrollPacket
|
||||||
import ru.dbotthepony.mc.otm.container.ItemFilterSlotPacket
|
import ru.dbotthepony.mc.otm.container.ItemFilterSlotPacket
|
||||||
|
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.matter.CancelTaskPacket
|
import ru.dbotthepony.mc.otm.menu.matter.CancelTaskPacket
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.matter.PatternsChangePacket
|
import ru.dbotthepony.mc.otm.menu.matter.PatternsChangePacket
|
||||||
@ -22,24 +24,34 @@ import ru.dbotthepony.mc.otm.menu.data.StackRemovePacket
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
class MenuFieldPacket(val bytes: ByteArray, val length: Int) : MatteryPacket {
|
class MenuFieldPacket(val containerId: Int, val bytes: ByteArray, val length: Int) : MatteryPacket {
|
||||||
constructor(stream: FastByteArrayOutputStream) : this(stream.array, stream.length)
|
constructor(containerId: Int, stream: FastByteArrayOutputStream) : this(containerId, stream.array, stream.length)
|
||||||
|
|
||||||
override fun write(buff: FriendlyByteBuf) {
|
override fun write(buff: FriendlyByteBuf) {
|
||||||
|
buff.writeVarInt(containerId)
|
||||||
buff.writeBytes(bytes, 0, length)
|
buff.writeBytes(bytes, 0, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||||
context.packetHandled = true
|
context.packetHandled = true
|
||||||
context.get().enqueueWork {
|
|
||||||
(minecraft.player?.containerMenu as? MatteryMenu)?.mSynchronizer?.read(ByteArrayInputStream(bytes, 0, length))
|
context.enqueueWork {
|
||||||
|
if (containerId == ExoPackInventoryMenu.CONTAINER_ID) {
|
||||||
|
minecraft.player?.matteryPlayer?.exoPackMenu?.mSynchronizer?.read(ByteArrayInputStream(bytes, 0, length))
|
||||||
|
} else {
|
||||||
|
val menu = minecraft.player?.containerMenu as? MatteryMenu ?: return@enqueueWork
|
||||||
|
|
||||||
|
if (menu.containerId == containerId)
|
||||||
|
menu.mSynchronizer.read(ByteArrayInputStream(bytes, 0, length))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun read(buff: FriendlyByteBuf): MenuFieldPacket {
|
fun read(buff: FriendlyByteBuf): MenuFieldPacket {
|
||||||
|
val containerId = buff.readVarInt()
|
||||||
val readable = buff.readableBytes()
|
val readable = buff.readableBytes()
|
||||||
return MenuFieldPacket(ByteArray(readable).also(buff::readBytes), readable)
|
return MenuFieldPacket(containerId, ByteArray(readable).also(buff::readBytes), readable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +74,7 @@ class SetCarriedPacket(val item: ItemStack) : MatteryPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object MenuNetworkChannel : MatteryNetworkChannel(
|
object MenuNetworkChannel : MatteryNetworkChannel(
|
||||||
version = "2",
|
version = "3",
|
||||||
name = "menu"
|
name = "menu"
|
||||||
) {
|
) {
|
||||||
fun register() {
|
fun register() {
|
||||||
|
Loading…
Reference in New Issue
Block a user