From 30f07e2fedfaf168e73e5354c690dd83a717a87a Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 16 Mar 2023 07:56:48 +0700 Subject: [PATCH] Inventory filters preview --- .../mc/otm/mixin/MixinInventory.java | 49 +++ .../otm/capability/MatteryPlayerCapability.kt | 282 +++++++++++++++--- .../capability/energy/AndroidPowerSource.kt | 5 +- .../client/screen/ExoPackInventoryScreen.kt | 15 +- .../mc/otm/client/screen/MatteryScreen.kt | 26 +- .../screen/panels/slot/AbstractSlotPanel.kt | 3 +- .../screen/panels/slot/FilteredSlotPanel.kt | 113 +++++++ .../kotlin/ru/dbotthepony/mc/otm/core/Ext.kt | 18 +- .../mc/otm/core/util/DataStreams.kt | 29 +- .../ru/dbotthepony/mc/otm/menu/MatteryMenu.kt | 117 ++++++-- .../network/MatteryPlayerNetworkChannel.kt | 56 +++- .../resources/META-INF/accesstransformer.cfg | 2 + src/main/resources/coremods/code_injector.js | 162 ---------- .../overdrive_that_matters.mixins.json | 1 + 14 files changed, 615 insertions(+), 263 deletions(-) create mode 100644 src/main/java/ru/dbotthepony/mc/otm/mixin/MixinInventory.java create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/FilteredSlotPanel.kt diff --git a/src/main/java/ru/dbotthepony/mc/otm/mixin/MixinInventory.java b/src/main/java/ru/dbotthepony/mc/otm/mixin/MixinInventory.java new file mode 100644 index 000000000..a8f494406 --- /dev/null +++ b/src/main/java/ru/dbotthepony/mc/otm/mixin/MixinInventory.java @@ -0,0 +1,49 @@ +package ru.dbotthepony.mc.otm.mixin; + +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +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 net.minecraftforge.registries.ForgeRegistries; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import ru.dbotthepony.mc.otm.capability.MatteryCapability; + +@Mixin(Inventory.class) +public class MixinInventory { + @Shadow Player player; + + @Inject( + method = "add(ILnet/minecraft/world/item/ItemStack;)Z", + at = @At("HEAD"), + cancellable = true + ) + private void add(int pSlot, ItemStack pStack, CallbackInfoReturnable hook) { + if (pStack.isEmpty()) { + return; + } + + if (pSlot == -1) { + this.player.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresent(it -> { + try { + hook.setReturnValue(it.inventoryAddImpl(pStack)); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.forThrowable(throwable, "Adding item to inventory (Overdrive That Matters detour)"); + CrashReportCategory crashreportcategory = crashreport.addCategory("Item being added"); + crashreportcategory.setDetail("Registry Name", () -> String.valueOf(ForgeRegistries.ITEMS.getKey(pStack.getItem()))); + crashreportcategory.setDetail("Item Class", () -> pStack.getItem().getClass().getName()); + crashreportcategory.setDetail("Item ID", Item.getId(pStack.getItem())); + crashreportcategory.setDetail("Item data", pStack.getDamageValue()); + crashreportcategory.setDetail("Item name", () -> pStack.getHoverName().getString()); + throw new ReportedException(crashreport); + } + }); + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt index 448ca8554..27895053c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt @@ -1,10 +1,12 @@ package ru.dbotthepony.mc.otm.capability +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import net.minecraft.ChatFormatting import net.minecraft.core.Direction import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag +import net.minecraft.nbt.NumericTag import net.minecraft.nbt.StringTag import net.minecraft.network.chat.Component import net.minecraft.resources.ResourceLocation @@ -18,7 +20,9 @@ import net.minecraft.world.entity.boss.wither.WitherBoss import net.minecraft.world.entity.monster.Phantom 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 net.minecraft.world.item.Items import net.minecraft.world.item.ProjectileWeaponItem import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse import net.minecraft.world.level.Level @@ -58,11 +62,15 @@ import ru.dbotthepony.mc.otm.core.collect.nonEmpty import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.minus import ru.dbotthepony.mc.otm.core.nbt.getCompoundList +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.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.Savetables 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.network.* import ru.dbotthepony.mc.otm.registry.AndroidFeatures @@ -108,6 +116,19 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial val level: Level get() = capability.ply.level } + /** + * This event is fired on main event bus when [Inventory.add] was called and entire [ItemStack] can not be stored + * (both in [Inventory] and [MatteryPlayerCapability] exopack due to inventory filters or no free space). + */ + data class ItemStackLeftoverEvent(val stack: ItemStack, val capability: MatteryPlayerCapability) : Event() { + override fun isCancelable() = false + override fun hasResult() = false + override fun setResult(value: Result) {} + + val player get() = capability.ply + val level: Level get() = capability.ply.level + } + private inner class PlayerMatteryContainer(size: Int) : MatteryContainer(size) { override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { super.setChanged(slot, new, old) @@ -181,6 +202,16 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } }, backingMap = this.exoPackSlotModifierMap) + val regularSlotFilters = immutableList(Inventory.INVENTORY_SIZE) { + synchronizer.Field(null, ItemValueCodec.nullable) + } + + val exoPackSlotFilters by synchronizer.Map( + keyCodec = VarIntValueCodec, + valueCodec = ItemValueCodec, + backingMap = Int2ObjectOpenHashMap() + ) + /** * Current slot count of Exopack * @@ -291,15 +322,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial private var wasInLiquid = false private var lastDimension = ResourceLocation("overworld") - init { - savetables.int(::ticksIExist) - savetables.int(::iteration) - savetables.bool(::shouldSendIteration) - savetables.bool(::wasInLiquid) - savetables.vector(::lastOutsideLiquid) - savetables.location(::lastDimension) - } - /** * Whenever player should become an Android once transformation conditions are met (e.g. player dies or sleeps in bed) */ @@ -324,6 +346,26 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial */ val androidEnergy = AndroidPowerSource(ply, synchronizer, AndroidConfig.ANDROID_MAX_ENERGY, AndroidConfig.ANDROID_MAX_ENERGY) + init { + savetables.int(::ticksIExist) + savetables.int(::iteration) + + savetables.bool(::shouldSendIteration) + savetables.bool(::wasInLiquid) + savetables.bool(::isAndroid) + savetables.bool(::willBecomeAndroid) + savetables.bool(::hasExoPack, "hasExoSuit") + savetables.bool(::displayExoPack, "displayExoSuit") + savetables.bool(::isExoPackCraftingUpgraded, "isExoSuitCraftingUpgraded") + + savetables.vector(::lastOutsideLiquid) + savetables.location(::lastDimension) + + savetables.stateful(::exoPackSlotModifier, "exoSuitSlotCountModifiers") + savetables.stateful(::exoPackContainer, "exoSuitContainer") + savetables.stateful(::androidEnergy) + } + fun invalidateNetworkState() { synchronizer.invalidate() publicSynchronizer.invalidate() @@ -641,36 +683,36 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } } - // exosuit - tag["hasExoSuit"] = hasExoPack - tag["displayExoSuit"] = displayExoPack - tag["exoSuitContainer"] = exoPackContainer.serializeNBT() - tag["isExoSuitCraftingUpgraded"] = isExoPackCraftingUpgraded - tag["exoSuitSlotCountModifiers"] = exoPackSlotModifier.serializeNBT() - - // android - tag["androidEnergy"] = androidEnergy.serializeNBT() - - tag["isAndroid"] = isAndroid - tag["willBecomeAndroid"] = willBecomeAndroid - - val featureList = ListTag() - val researchList = ListTag() - - for (feature in featureMap.values) { - featureList.add(feature.serializeNBT().also { - it["id"] = feature.type.registryName!!.toString() - }) + tag["features"] = ListTag().also { + for (feature in featureMap.values) { + it.add(feature.serializeNBT().also { + it["id"] = feature.type.registryName!!.toString() + }) + } } - for ((type, instance) in research) { - researchList.add(instance.serializeNBT().also { - it["id"] = type.id.toString() - }) + tag["research"] = ListTag().also { + for ((type, instance) in research) { + it.add(instance.serializeNBT().also { + it["id"] = type.id.toString() + }) + } } - tag["features"] = featureList - tag["research"] = researchList + 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 { + for (filter in regularSlotFilters) { + it.add(StringTag.valueOf(filter.value?.registryName?.toString() ?: "")) + } + } return tag } @@ -678,6 +720,29 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial override fun deserializeNBT(tag: CompoundTag) { savetables.deserializeNBT(tag) + for (filter in regularSlotFilters) { + 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") + + for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) { + val path = regularSlotFilters[i].asString + if (path == "") continue + this.regularSlotFilters[i].value = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(path) ?: continue) ?: Items.AIR + } + // iterations deathLog.clear() @@ -689,20 +754,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } } - // exosuit - hasExoPack = tag.getBoolean("hasExoSuit") - displayExoPack = tag.getBoolean("displayExoSuit") - tag.map("exoSuitSlotCountModifiers", exoPackSlotModifier::deserializeNBT) - - exoPackContainer.deserializeNBT(tag["exoSuitContainer"]) - isExoPackCraftingUpgraded = tag.getBoolean("isExoSuitCraftingUpgraded") - - // android - isAndroid = tag.getBoolean("isAndroid") - willBecomeAndroid = tag.getBoolean("willBecomeAndroid") - - tag.map("androidEnergy", androidEnergy::deserializeNBT) - featureMap.clear() research.clear() @@ -977,8 +1028,141 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } else LazyOptional.empty() } + /** + * This re-implement [Inventory.add] logic (original method is redirected to this) + */ + fun inventoryAddImpl(stack: ItemStack): Boolean { + val items = ply.inventory.items + val exoPackContainer = exoPackContainer + + if (!stack.isStackable) { + for (i in items.indices) { + if (items[i].isEmpty && (regularSlotFilters[i].value === null || regularSlotFilters[i].value === stack.item)) { + items[i] = stack.copy() + items[i].popTime = 5 + stack.count = 0 + return true + } + } + + for (i in 0 until exoPackContainer.containerSize) { + if (exoPackContainer[i].isEmpty && (exoPackSlotFilters[i] === null || exoPackSlotFilters[i] === stack.item)) { + exoPackContainer[i] = stack.copy() + exoPackContainer[i].popTime = 5 + stack.count = 0 + return true + } + } + + MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this)) + + if (ply.abilities.instabuild) { + stack.count = 0 + } + + return !stack.isEmpty + } + + // двигаем в существующие слоты + items[ply.inventory.selected].also { stackStacks(it, stack) } + if (stack.isEmpty) return true + ply.inventory.offhand[0].also { stackStacks(it, stack) } + if (stack.isEmpty) return true + + for (i in items.indices) { + if (stackStacks(items[i], stack) && stack.isEmpty) { + return true + } + } + + for (i in 0 until exoPackContainer.containerSize) { + if (stackStacks(exoPackContainer[i], stack)) { + exoPackContainer.setChanged(i) + + if (stack.isEmpty) { + return true + } + } + } + + // двигаем в пустые слоты + + // двигаем в отфильтрованные слоты + for (i in items.indices) { + if (items[i].isEmpty && regularSlotFilters[i].value === stack.item) { + items[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize)) + items[i].popTime = 5 + + if (stack.isEmpty) { + return true + } + } + } + + for (i in 0 until exoPackContainer.containerSize) { + if (exoPackContainer[i].isEmpty && exoPackSlotFilters[i] === stack.item) { + exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize)) + exoPackContainer[i].popTime = 5 + + if (stack.isEmpty) { + return true + } + } + } + + // двигаем в обычные слоты + for (i in items.indices) { + if (items[i].isEmpty && regularSlotFilters[i].value === null) { + items[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize)) + items[i].popTime = 5 + + if (stack.isEmpty) { + return true + } + } + } + + for (i in 0 until exoPackContainer.containerSize) { + if (exoPackContainer[i].isEmpty && exoPackSlotFilters[i] === null) { + exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize)) + exoPackContainer[i].popTime = 5 + + if (stack.isEmpty) { + return true + } + } + } + + MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this)) + + if (ply.abilities.instabuild) { + stack.count = 0 + } + + return !stack.isEmpty + } + @Suppress("unused") companion object { + private fun stackStacks(it: ItemStack, stack: ItemStack): Boolean { + if ( + !it.isEmpty && + it.maxStackSize > it.count && + it.isStackable && + ItemStack.isSameItemSameTags(it, stack) + ) { + val new = (it.count + stack.count).coerceAtMost(it.maxStackSize) + val diff = new - it.count + it.count = new + stack.count -= diff + it.popTime = 5 + + return true + } + + return false + } + init { WitherBoss.TARGETING_CONDITIONS.selector(WitherBoss.TARGETING_CONDITIONS.selector!!.and { it.matteryPlayer?.isAndroid != true }) WitherBoss.LIVING_ENTITY_SELECTOR = WitherBoss.LIVING_ENTITY_SELECTOR.and { it.matteryPlayer?.isAndroid != true } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/AndroidPowerSource.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/AndroidPowerSource.kt index 82f17061f..f7c63478c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/AndroidPowerSource.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/AndroidPowerSource.kt @@ -23,7 +23,7 @@ class AndroidPowerSource( synchronizer: FieldSynchronizer, initialCharge: Decimal, maxCharge: Decimal -) : IMatteryEnergyStorage, INBTSerializable { +) : IMatteryEnergyStorage, INBTSerializable { override val energyFlow: FlowDirection get() = FlowDirection.INPUT @@ -46,7 +46,8 @@ class AndroidPowerSource( } } - override fun deserializeNBT(tag: CompoundTag) { + override fun deserializeNBT(tag: CompoundTag?) { + tag ?: return battery = tag.getDecimal("battery") maxBattery = tag.getDecimal("maxBattery") item = tag.getItemStack("item") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExoPackInventoryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExoPackInventoryScreen.kt index 5c2d28a0c..fdea69675 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExoPackInventoryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExoPackInventoryScreen.kt @@ -10,8 +10,8 @@ import ru.dbotthepony.mc.otm.client.render.sprite 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.slot.AbstractSlotPanel +import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilteredSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel -import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel import ru.dbotthepony.mc.otm.client.setMousePos import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory @@ -48,10 +48,10 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen> { val frame = FramePanel(this, width = 200f, height = FRAME_BASE_HEIGHT + inventoryRows * AbstractSlotPanel.SIZE, title = this.title) - val toolbeltLine = EditablePanel(this, frame, height = 18f) - toolbeltLine.dock = Dock.BOTTOM + val hotbarStrip = EditablePanel(this, frame, height = 18f) + hotbarStrip.dock = Dock.BOTTOM - toolbeltLine.setDockMargin(top = 3f) + hotbarStrip.setDockMargin(top = 3f) scrollPanel = DiscreteScrollBarPanel(this, null, maxScroll = { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) }, scrollCallback = { @@ -83,11 +83,12 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen(menu: T, inventory: Inventory, tit hotbarStrip.dock = Dock.BOTTOM for (slot in menu.playerHotbarSlots) { - SlotPanel(this, hotbarStrip, slot).also { it.dock = Dock.LEFT } + FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also { + it.dock = Dock.LEFT + } } val canvas = EditablePanel(this, inventoryFrame) @@ -211,7 +219,9 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit hotbarStrip.dock = Dock.BOTTOM for (slot in menu.playerHotbarSlots) { - SlotPanel(this, hotbarStrip, slot).also { it.dock = Dock.LEFT } + FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also { + it.dock = Dock.LEFT + } } inventoryScrollbar.parent = slotListCanvas @@ -250,13 +260,19 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit } for (i in 0 .. (8).coerceAtMost(menu.playerCombinedInventorySlots.size - offset - 1)) { - val slot = object : SlotPanel, Slot>(this@MatteryScreen, canvas, menu.playerCombinedInventorySlots[offset + i]) { + val slot = menu.playerCombinedInventorySlots[offset + i] + + object : FilteredSlotPanel, Slot>(this@MatteryScreen, canvas, slot) { + init { + dock = Dock.LEFT + } + override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { return false } - } - slot.dock = Dock.LEFT + override var slotFilter: Item? by slot.filter!! + } } return@Int2ObjectFunction canvas diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/AbstractSlotPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/AbstractSlotPanel.kt index 37422cba5..9c5b1516f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/AbstractSlotPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/AbstractSlotPanel.kt @@ -59,7 +59,7 @@ abstract class AbstractSlotPanel> @JvmOverloads constru if (!itemstack.isEmpty) { // val font = RenderProperties.get(itemstack).getFont(itemstack) - val font = (itemstack.item.renderPropertiesInternal as? IClientItemExtensions)?.getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) + val font = IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) // TODO: WHERE???????????? // GuiUtils.preItemToolTip(itemstack); @@ -84,5 +84,6 @@ abstract class AbstractSlotPanel> @JvmOverloads constru val SLOT_HIGHLIGHT_DRAG = RGBAColor(200, 200, 200, 150) const val SIZE = 18f val SLOT_BACKGROUND = WidgetLocation.MISC.sprite(0f, 0f, SIZE, SIZE) + val FILTERED_SLOT_BACKGROUND = WidgetLocation.MISC.sprite(46f, 0f, SIZE, SIZE) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/FilteredSlotPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/FilteredSlotPanel.kt new file mode 100644 index 000000000..b031c0d88 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/slot/FilteredSlotPanel.kt @@ -0,0 +1,113 @@ +package ru.dbotthepony.mc.otm.client.screen.panels.slot + +import com.mojang.blaze3d.platform.InputConstants +import com.mojang.blaze3d.vertex.PoseStack +import net.minecraft.world.inventory.Slot +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraftforge.client.extensions.common.IClientItemExtensions +import ru.dbotthepony.mc.otm.client.isCtrlDown +import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.client.playGuiClickSound +import ru.dbotthepony.mc.otm.client.render.MatterySprite +import ru.dbotthepony.mc.otm.client.render.drawColor +import ru.dbotthepony.mc.otm.client.render.drawRect +import ru.dbotthepony.mc.otm.client.screen.MatteryScreen +import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel +import ru.dbotthepony.mc.otm.core.GetterSetter +import ru.dbotthepony.mc.otm.core.math.RGBAColor + +abstract class FilteredSlotPanel, out T : Slot>( + screen: S, + parent: EditablePanel<*>?, + slot: T, + x: Float = 0f, + y: Float = 0f, + width: Float = SIZE, + height: Float = SIZE, + noItemIcon: MatterySprite? = null +) : SlotPanel(screen, parent, slot, x, y, width, height, noItemIcon) { + abstract var slotFilter: Item? + + override fun renderSlotBackground(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { + super.renderSlotBackground(stack, mouseX, mouseY, partialTick) + + if (slotFilter != null) { + if (slotFilter !== Items.AIR) { + val itemStack = ItemStack(slotFilter!!, 1) + + screen.renderItemStack(absoluteX, absoluteY, itemStack, null) + clearDepth(stack) + } + + drawColor = SLOT_FILTER_COLOR + drawRect(stack, 0f, 0f, width, height) + } + } + + override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { + if (isHovered && slotFilter != null && slotFilter !== Items.AIR && itemStack.isEmpty) { + val itemstack = ItemStack(slotFilter!!, 1) + + screen.renderComponentTooltip( + stack, + getItemStackTooltip(itemstack), + mouseX.toInt(), + mouseY.toInt(), + IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: screen.font, + itemstack + ) + } + + return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick) + } + + override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { + if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) { + if (slotFilter === null) { + if (screen.menu.carried.isEmpty) { + slotFilter = slot.item.item + } else { + slotFilter = screen.menu.carried.item + } + } else { + slotFilter = null + } + + playGuiClickSound() + + return true + } else { + return super.mouseClickedInner(x, y, button) + } + } + + override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean { + if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) { + return true + } + + return super.mouseReleasedInner(x, y, button) + } + + companion object { + val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150) + + fun , T : Slot> of( + screen: S, + parent: EditablePanel<*>?, + slot: T, + x: Float = 0f, + y: Float = 0f, + width: Float = SIZE, + height: Float = SIZE, + noItemIcon: MatterySprite? = null, + filter: GetterSetter + ): FilteredSlotPanel { + return object : FilteredSlotPanel(screen, parent, slot, x, y, width, height, noItemIcon) { + override var slotFilter: Item? by filter + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 906cdda22..181419a23 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -11,6 +11,7 @@ import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import net.minecraft.core.BlockPos import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtAccounter import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.ComponentContents import net.minecraft.network.chat.contents.TranslatableContents @@ -18,6 +19,7 @@ import net.minecraft.resources.ResourceLocation import net.minecraft.world.entity.Entity import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.level.BlockGetter import net.minecraft.world.level.block.state.BlockState @@ -32,7 +34,11 @@ import net.minecraftforge.registries.ForgeRegistry import net.minecraftforge.registries.IForgeRegistry import ru.dbotthepony.mc.otm.core.math.Vector import ru.dbotthepony.mc.otm.core.util.readInt +import ru.dbotthepony.mc.otm.core.util.readVarIntLE +import ru.dbotthepony.mc.otm.core.util.writeInt +import ru.dbotthepony.mc.otm.core.util.writeVarIntLE import java.io.InputStream +import java.io.OutputStream import java.lang.ref.Reference import java.math.BigInteger import java.util.Arrays @@ -167,12 +173,16 @@ fun FriendlyByteBuf.writeItemType(value: Item) { writeInt(ForgeRegistries.ITEMS.getID(value)) } -fun FriendlyByteBuf.readItemType(): Item? { - return ForgeRegistries.ITEMS.getValue(readInt()) +fun OutputStream.writeItemType(value: Item) { + writeVarIntLE(ForgeRegistries.ITEMS.getID(value)) } -fun InputStream.readItemType(): Item? { - return ForgeRegistries.ITEMS.getValue(readInt()) +fun FriendlyByteBuf.readItemType(): Item { + return ForgeRegistries.ITEMS.getValue(readInt()) ?: Items.AIR +} + +fun InputStream.readItemType(sizeLimit: NbtAccounter? = null): Item { + return ForgeRegistries.ITEMS.getValue(readVarIntLE(sizeLimit)) ?: Items.AIR } operator fun > StateHolder<*, *>.get(property: Property): T { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/DataStreams.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/DataStreams.kt index 15fb51b18..b5b81aa6d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/DataStreams.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/DataStreams.kt @@ -10,6 +10,8 @@ import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.core.immutableMap import ru.dbotthepony.mc.otm.core.math.readDecimal import ru.dbotthepony.mc.otm.core.math.writeDecimal +import ru.dbotthepony.mc.otm.core.readItemType +import ru.dbotthepony.mc.otm.core.writeItemType import java.io.DataInput import java.io.DataInputStream import java.io.DataOutput @@ -60,6 +62,30 @@ class StreamCodec( comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b } ) : this({ stream, sizeLimit -> sizeLimit.accountBytes(payloadSize); reader.invoke(stream) }, writer, copier, comparator) + val nullable = object : IStreamCodec { + override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): V? { + sizeLimit.accountBytes(1L) + return if (stream.read() == 0) null else reader.invoke(stream, sizeLimit) + } + + override fun write(stream: DataOutputStream, value: V?) { + if (value === null) stream.write(0) else { + stream.write(1) + writer.invoke(stream, value) + } + } + + override fun copy(value: V?): V? { + return if (value === null) null else copier.invoke(value) + } + + override fun compare(a: V?, b: V?): Boolean { + if (a === null && b === null) return true + if (a === null || b === null) return false + return comparator.invoke(a, b) + } + } + override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): V { return reader.invoke(stream, sizeLimit) } @@ -86,6 +112,7 @@ val LongValueCodec = StreamCodec(DataInputStream::readLong, 8L, DataOutputStream val FloatValueCodec = StreamCodec(DataInputStream::readFloat, 4L, DataOutputStream::writeFloat) val DoubleValueCodec = StreamCodec(DataInputStream::readDouble, 8L, DataOutputStream::writeDouble) val ItemStackValueCodec = StreamCodec(DataInputStream::readItem, DataOutputStream::writeItem, ItemStack::copy) { a, b -> a.equals(b, true) } +val ItemValueCodec = StreamCodec(DataInputStream::readItemType, DataOutputStream::writeItemType) val DecimalValueCodec = StreamCodec(DataInputStream::readDecimal, DataOutputStream::writeDecimal) val BigDecimalValueCodec = StreamCodec(DataInputStream::readBigDecimal, DataOutputStream::writeBigDecimal) val UUIDValueCodec = StreamCodec({ s, a -> a.accountBytes(8L); UUID(s.readLong(), s.readLong()) }, { s, v -> s.writeLong(v.mostSignificantBits); s.writeLong(v.leastSignificantBits) }) @@ -331,7 +358,7 @@ fun OutputStream.writeBinaryString(input: String) { private data class IndexedStreamCodec( val condition: Predicate, val id: Int, - val codec: StreamCodec + val codec: IStreamCodec ) { fun read(stream: DataInputStream, sizeLimit: NbtAccounter = NbtAccounter(1L shl 18 /* 256 KiB */)): T { return codec.read(stream, sizeLimit) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt index be6448075..0aab8f4b5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt @@ -14,6 +14,7 @@ import net.minecraft.world.Container import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.* +import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraft.world.item.enchantment.EnchantmentHelper.hasBindingCurse import net.minecraft.world.level.block.entity.BlockEntity @@ -26,6 +27,7 @@ import ru.dbotthepony.mc.otm.compat.curios.curiosSlots import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot +import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.util.BigDecimalValueCodec import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec @@ -35,8 +37,10 @@ import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget import ru.dbotthepony.mc.otm.network.FieldSynchronizer import ru.dbotthepony.mc.otm.network.MatteryPacket +import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel import ru.dbotthepony.mc.otm.network.MenuFieldPacket import ru.dbotthepony.mc.otm.network.MenuNetworkChannel +import ru.dbotthepony.mc.otm.network.SetInventoryFilterPacket import ru.dbotthepony.mc.otm.network.enqueueWork import ru.dbotthepony.mc.otm.network.packetHandled import ru.dbotthepony.mc.otm.network.sender @@ -70,12 +74,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor( private val _matteryWidgets = ArrayList() val matteryWidgets: List = Collections.unmodifiableList(_matteryWidgets) - private val _playerInventorySlots = ArrayList() - private val _playerInventorySlots2 = ArrayList() - private val _playerHotbarSlots = ArrayList() - private val _playerMainSlots = ArrayList() - private val _playerExoSuitSlots = ArrayList() - private val _playerCombinedInventorySlots = ArrayList() + private val _playerInventorySlots = ArrayList() + private val _playerInventorySlots2 = ArrayList() + private val _playerHotbarSlots = ArrayList() + private val _playerMainSlots = ArrayList() + private val _playerExoSuitSlots = ArrayList() + private val _playerCombinedInventorySlots = ArrayList() private val playerInputs = ArrayList>() @@ -155,32 +159,32 @@ abstract class MatteryMenu @JvmOverloads protected constructor( /** * inventory + exosuit + hotbar (in this order) */ - val playerInventorySlots: List = Collections.unmodifiableList(_playerInventorySlots) + val playerInventorySlots: List = Collections.unmodifiableList(_playerInventorySlots) /** * hotbar + inventory + exosuit (in this order) */ - val playerInventorySlots2: List = Collections.unmodifiableList(_playerInventorySlots2) + val playerInventorySlots2: List = Collections.unmodifiableList(_playerInventorySlots2) /** * hotbar only */ - val playerHotbarSlots: List = Collections.unmodifiableList(_playerHotbarSlots) + val playerHotbarSlots: List = Collections.unmodifiableList(_playerHotbarSlots) /** * inventory only */ - val playerMainSlots: List = Collections.unmodifiableList(_playerMainSlots) + val playerMainSlots: List = Collections.unmodifiableList(_playerMainSlots) /** * exosuit only */ - val playerExoSuitSlots: List = Collections.unmodifiableList(_playerExoSuitSlots) + val playerExoSuitSlots: List = Collections.unmodifiableList(_playerExoSuitSlots) /** * inventory + exosuit (in this order) */ - val playerCombinedInventorySlots: List = Collections.unmodifiableList(_playerCombinedInventorySlots) + val playerCombinedInventorySlots: List = Collections.unmodifiableList(_playerCombinedInventorySlots) var autoCreateInventoryFrame = true private set @@ -250,6 +254,32 @@ abstract class MatteryMenu @JvmOverloads protected constructor( return super.isSameInventory(other) } + + // фильтр существует только для автоматизации + // игрок всё равно может класть предмет вручную, даже если он не соответствует фильтру + internal val filter: GetterSetter? + + init { + val mattery = ply.matteryPlayer + + if (mattery != null) { + if (container === inventory && slotIndex in mattery.regularSlotFilters.indices) { + filter = GetterSetter.of( + getter = { mattery.regularSlotFilters[slotIndex].value }, + setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.INVENTORY, slotIndex, it)) } + ) + } else if (container === mattery.exoPackContainer) { + filter = GetterSetter.of( + getter = { mattery.exoPackSlotFilters[slotIndex] }, + setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.EXOPACK, slotIndex, it)) } + ) + } else { + filter = null + } + } else { + filter = null + } + } } open inner class EquipmentSlot(container: Container, index: Int, val type: net.minecraft.world.entity.EquipmentSlot, x: Int = 0, y: Int = 0) : InventorySlot(container, index, x, y) { @@ -458,6 +488,18 @@ abstract class MatteryMenu @JvmOverloads protected constructor( val copy = slot.item.copy() var any = false + if (target.any { it.any { it is InventorySlot && it.filter != null } }) { + for (collection in target) { + if (moveItemStackTo(slot, collection, onlyFiltered = true)) { + any = true + + if (!slot.hasItem()) { + return copy + } + } + } + } + for (collection in target) { if (moveItemStackTo(slot, collection)) { any = true @@ -506,25 +548,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor( return true } - fun moveItemStackTo( - item: ItemStack, - slots: Collection - ): Boolean { - val remainder = moveItemStackToSlots(item, slots) - - if (remainder.count == item.count) { - return false - } - - item.count = remainder.count - return true - } - fun moveItemStackTo( source: Slot, - slots: Collection + slots: Collection, + onlyFiltered: Boolean = false ): Boolean { - val remainder = moveItemStackToSlots(source.item, slots) + val remainder = moveItemStackToSlots(source.item, slots, onlyFiltered = onlyFiltered) if (remainder.count == source.item.count) { return false @@ -540,15 +569,25 @@ abstract class MatteryMenu @JvmOverloads protected constructor( return true } - fun moveItemStackToSlots(item: ItemStack, slots: Collection, simulate: Boolean = false): ItemStack { + fun moveItemStackToSlots(item: ItemStack, slots: Collection, simulate: Boolean = false, onlyFiltered: Boolean = false): ItemStack { + if (item.isEmpty) { + return ItemStack.EMPTY + } + val copy = item.copy() // first pass - stack with existing slots if (copy.isStackable) { for (slot in slots) { + if (onlyFiltered && (slot !is InventorySlot || slot.filter == null || slot.filter.get() != item.item)) { + continue + } else if (!onlyFiltered && slot is InventorySlot && slot.filter != null && slot.filter.get() != null && slot.filter.get() != item.item) { + continue + } + val limit = slot.getMaxStackSize(copy) - if (limit > slot.item.count && ItemStack.isSameItemSameTags(slot.item, copy) && slot.mayPlace(item)) { + if (limit > slot.item.count && slot.mayPlace(item) && ItemStack.isSameItemSameTags(slot.item, copy)) { val newCount = (slot.item.count + copy.count).coerceAtMost(limit) val diff = newCount - slot.item.count copy.count -= diff @@ -567,6 +606,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor( // second pass - drop stack into first free slot for (slot in slots) { + if (onlyFiltered && (slot !is InventorySlot || slot.filter == null || slot.filter.get() != item.item)) { + continue + } else if (!onlyFiltered && slot is InventorySlot && slot.filter != null && slot.filter.get() != null && slot.filter.get() != item.item) { + continue + } + val limit = slot.getMaxStackSize(copy) if (!slot.hasItem() && slot.mayPlace(item)) { @@ -597,9 +642,19 @@ abstract class MatteryMenu @JvmOverloads protected constructor( require(finalSlot < slots.size) { "Final slot $finalSlot is bigger than total size of array of ${slots.size}" } val slots = ArrayList(finalSlot - initialSlot + 1) + var filters = false for (i in (if (inverse) finalSlot downTo initialSlot else initialSlot .. finalSlot)) { - slots.add(slots[i]) + val slot = slots[i] + slots.add(slot) + + if (slot is InventorySlot && slot.filter != null) { + filters = true + } + } + + if (filters) { + return moveItemStackToSlots(moveItemStackToSlots(item, slots, simulate, onlyFiltered = true), slots, simulate, onlyFiltered = false) } return moveItemStackToSlots(item, slots, simulate) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt index 6d709eda3..c8ed89fc6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt @@ -6,7 +6,9 @@ import net.minecraft.network.chat.Component import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket import net.minecraft.resources.ResourceLocation import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items import net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT import net.minecraftforge.network.NetworkDirection.PLAY_TO_SERVER import net.minecraftforge.network.NetworkEvent @@ -27,6 +29,8 @@ import ru.dbotthepony.mc.otm.client.render.ShockwaveRenderer import ru.dbotthepony.mc.otm.container.get import ru.dbotthepony.mc.otm.container.set import ru.dbotthepony.mc.otm.core.math.Vector +import ru.dbotthepony.mc.otm.core.readItemType +import ru.dbotthepony.mc.otm.core.writeItemType import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu import ru.dbotthepony.mc.otm.registry.AndroidFeatures @@ -510,8 +514,56 @@ object HideExosuitPacket : MatteryPacket { } } +class SetInventoryFilterPacket(val type: Type, val slot: Int, val item: Item?) : MatteryPacket { + enum class Type { + INVENTORY, + EXOPACK + } + + override fun write(buff: FriendlyByteBuf) { + buff.writeEnum(type) + buff.writeVarInt(slot) + + if (item == null) { + buff.writeByte(0) + } else { + buff.writeByte(1) + buff.writeItemType(item) + } + } + + override fun play(context: Supplier) { + context.packetHandled = true + val player = context.sender?.matteryPlayer ?: return + + when (type) { + Type.INVENTORY -> { + if (slot in 0 until player.regularSlotFilters.size) { + player.regularSlotFilters[slot].value = item + } + } + + Type.EXOPACK -> { + if (slot in 0 until player.exoPackSlotCount) { + if (item == null) { + player.exoPackSlotFilters.remove(slot) + } else { + player.exoPackSlotFilters[slot] = item + } + } + } + } + } + + companion object { + fun read(buff: FriendlyByteBuf): SetInventoryFilterPacket { + return SetInventoryFilterPacket(buff.readEnum(Type::class.java), buff.readVarInt(), if (buff.readByte() > 0) buff.readItemType() else null) + } + } +} + object MatteryPlayerNetworkChannel : MatteryNetworkChannel( - version = "2", + version = "3", name = "player" ) { fun register() { @@ -543,5 +595,7 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel( add(DisplayExosuitPacket::class, { DisplayExosuitPacket }, PLAY_TO_SERVER) add(HideExosuitPacket::class, { HideExosuitPacket }, PLAY_TO_SERVER) + + add(SetInventoryFilterPacket::class, SetInventoryFilterPacket.Companion::read, PLAY_TO_SERVER) } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 4000db026..4c3be4b7f 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -2,6 +2,8 @@ public-f net.minecraft.client.gui.screens.Screen f_96539_ # title public net.minecraft.server.MinecraftServer f_129744_ # storageSource +public net.minecraft.world.entity.player.Inventory f_150070_ # SELECTION_SIZE + # for accessing and setting from MatteryScreen class public net.minecraft.client.gui.screens.inventory.AbstractContainerScreen f_169600_ public net.minecraft.client.gui.screens.inventory.AbstractContainerScreen f_169605_ diff --git a/src/main/resources/coremods/code_injector.js b/src/main/resources/coremods/code_injector.js index f733d2489..5de443d1f 100644 --- a/src/main/resources/coremods/code_injector.js +++ b/src/main/resources/coremods/code_injector.js @@ -533,46 +533,6 @@ function injectAtTail(path, instructions) { }) } -function injectInventoryInsertHook( - instructions, - determinedOffset, - hookName, - hookDesc, - pre -) { - var last = instructions.get(determinedOffset) - - // print('Patching Inventory#add at instruction ' + determinedOffset + ' to add ' + hookName + hookDesc) - - // loading this (Inventory) to stack - - if (pre != undefined) { - instructions.insert(last, pre) - last = pre - } - - var next = new VarInsnNode(opcodesRemapped.aload, 0) - instructions.insert(last, next) - - last = next - // loading itemstack to stack - next = new VarInsnNode(opcodesRemapped.aload, 2) - - instructions.insert(last, next) - - last = next - // dispatching hook method - next = new MethodInsnNode( - opcodesRemapped.invokestatic, - 'ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability', - hookName, - hookDesc, - false - ) - - instructions.insert(last, next) -} - function patchBlendFunc(node) { var last = new MethodInsnNode( opcodesRemapped.invokestatic, @@ -795,128 +755,6 @@ function initializeCoreMod() { return node }), - 'Inventory#add patch': { - 'target': { - 'type': 'METHOD', - 'class': 'net.minecraft.world.entity.player.Inventory', - 'methodName': ASMAPI.mapMethod('m_36040_'), // add - 'methodDesc': '(ILnet/minecraft/world/item/ItemStack;)Z' - }, - - 'transformer': function(node) { - // If item is not "damaged": - // 113: invokevirtual #144 // Method addResource:(ILnet/minecraft/world/item/ItemStack;)I - // 116: invokevirtual #158 // Method net/minecraft/world/item/ItemStack.setCount:(I)V - // 119: aload_2 - // 120: invokevirtual #57 // Method net/minecraft/world/item/ItemStack.isEmpty:()Z - // 123: ifne 134 - // 126: aload_2 - // 127: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I - // 130: iload_3 - // 131: if_icmplt 87 - // <-- our target - // 134: aload_2 - // 135: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I - // 138: iload_3 - // 139: if_icmpne 162 - // 142: aload_0 - - // If item returned that it is damaged: - // 10: invokevirtual #97 // Method net/minecraft/world/item/ItemStack.isDamaged:()Z - // 13: ifeq 87 - // 16: iload_1 - // 17: iconst_m1 - // 18: if_icmpne 26 - // 21: aload_0 - // 22: invokevirtual #86 // Method getFreeSlot:()I - // 25: istore_1 - // <-- our target - // 26: iload_1 - // 27: iflt 65 - // 30: aload_0 - // 31: getfield #19 // Field items:Lnet/minecraft/core/NonNullList; - // 34: iload_1 - // 35: aload_2 - - var i - - // 83: iconst_1 - // 84: ireturn - // 85: iconst_0 - // 86: ireturn - // 87: aload_2 - // 88: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I - - // patch "not damaged" before loop - for (i = 0; i < node.instructions.size(); i++) { - var determinedOffset = test([ - opcodesRemapped.iconst_1, - opcodesRemapped.ireturn, - opcodesRemapped.iconst_0, - opcodesRemapped.ireturn, - opcodesRemapped.aload, - opcodesRemapped.invokevirtual, - ], node.instructions, i) - - if (determinedOffset != -1) { - injectInventoryInsertHook(node.instructions, determinedOffset - 1, - 'inventoryAddItemHookPre', - '(Lnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V') - - break - } - } - - // patch "not damaged" after loop - for (i = 0; i < node.instructions.size(); i++) { - var determinedOffset = test([ - opcodesRemapped.invokevirtual, - opcodesRemapped.invokevirtual, - opcodesRemapped.aload, - opcodesRemapped.invokevirtual, - opcodesRemapped.ifne, - opcodesRemapped.aload, - opcodesRemapped.invokevirtual, - opcodesRemapped.iload, - opcodesRemapped.if_icmplt, - ], node.instructions, i) - - if (determinedOffset != -1) { - injectInventoryInsertHook(node.instructions, determinedOffset, - 'inventoryAddItemHook', - '(Lnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V') - - break - } - } - - // patch "damaged" - for (i = 0; i < node.instructions.size(); i++) { - var determinedOffset = test([ - opcodesRemapped.invokevirtual, - opcodesRemapped.ifeq, - opcodesRemapped.iload, - opcodesRemapped.iconst_m1, - opcodesRemapped.if_icmpne, - opcodesRemapped.aload, - opcodesRemapped.invokevirtual, - opcodesRemapped.istore, - ], node.instructions, i) - - if (determinedOffset != -1) { - injectInventoryInsertHook(node.instructions, determinedOffset, - 'inventoryAddDamagedItemHook', - '(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V', - new VarInsnNode(opcodesRemapped.iload, node.instructions.get(determinedOffset - 1)['var'])) - - break - } - } - - return node - } - }, - 'GameRenderer#render hook': { 'target': { 'type': 'METHOD', diff --git a/src/main/resources/overdrive_that_matters.mixins.json b/src/main/resources/overdrive_that_matters.mixins.json index c0527a2d9..cbdb3b6c4 100644 --- a/src/main/resources/overdrive_that_matters.mixins.json +++ b/src/main/resources/overdrive_that_matters.mixins.json @@ -9,6 +9,7 @@ "MixinPatchProjectileFinder", "MixinLivingEntity", "MixinAnvilBlock", + "MixinInventory", "MixinAbstractHurtingProjectile" ] }