From 994058b18789302666d28224ed7cfb6140006143 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 12 Oct 2022 09:23:27 +0700 Subject: [PATCH] Add pickItem hook for exosuit inventory Fixes #93 --- .../otm/capability/MatteryPlayerCapability.kt | 25 ++++ .../mc/otm/menu/ExoSuitInventoryMenu.kt | 4 +- .../network/MatteryPlayerNetworkChannel.kt | 56 ++++++++ src/main/resources/coremods/code_injector.js | 129 ++++++++++++++---- 4 files changed, 187 insertions(+), 27 deletions(-) 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 64a7990b2..93092b63d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt @@ -43,6 +43,7 @@ import ru.dbotthepony.mc.otm.android.AndroidResearch import ru.dbotthepony.mc.otm.android.AndroidResearchManager import ru.dbotthepony.mc.otm.android.AndroidResearchType import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature +import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu @@ -920,5 +921,29 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } } } + + @JvmStatic + fun pickBlockHook(determinedSlot: Int, itemStack: ItemStack) { + val player = minecraft.player ?: return + + if (player.abilities.instabuild || determinedSlot != -1 || itemStack.isEmpty) { + return + } + + val matteryPlayer = player.matteryPlayer ?: return + + if (!matteryPlayer.hasExoSuit) { + return + } + + val targetSlot = player.inventory.suitableHotbarSlot + val itemSlot = matteryPlayer.exoSuitContainer.indexOfFirst { !it.isEmpty && ItemStack.isSameItemSameTags(itemStack, it) } + + if (itemSlot == -1) { + return + } + + MatteryPlayerNetworkChannel.sendToServer(PickItemFromInventoryPacket(targetSlot, itemSlot)) + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExoSuitInventoryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExoSuitInventoryMenu.kt index d3a31b0f5..6f13df36d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExoSuitInventoryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ExoSuitInventoryMenu.kt @@ -10,6 +10,7 @@ import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.* import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability +import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots import ru.dbotthepony.mc.otm.compat.curios.curiosSlots import ru.dbotthepony.mc.otm.container.iterator @@ -213,7 +214,8 @@ class ExoSuitInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen } fun sendSlotChange(container: ExoSuitInventoryMenu, slotId: Int, itemStack: ItemStack) { - MatteryPlayerNetworkChannel.send(container.ply as ServerPlayer, ExoSuitSlotPacket(slotId, itemStack, container.stateId)) + if (container.slots[slotId].container != container.ply.inventory || container.ply.containerMenu is ExoSuitInventoryMenu) + MatteryPlayerNetworkChannel.send(container.ply as ServerPlayer, ExoSuitSlotPacket(slotId, itemStack, container.stateId)) } fun sendCarriedChange(container: ExoSuitInventoryMenu, itemStack: ItemStack) { 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 d91f64944..fde821a2a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt @@ -3,7 +3,11 @@ package ru.dbotthepony.mc.otm.network import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.Component +import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.entity.player.Player import net.minecraft.world.item.ItemStack import net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT import net.minecraftforge.network.NetworkDirection.PLAY_TO_SERVER @@ -19,7 +23,10 @@ import ru.dbotthepony.mc.otm.android.feature.TriggerShockwavePacket import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.client.MatteryGUI import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.container.get +import ru.dbotthepony.mc.otm.container.set import ru.dbotthepony.mc.otm.menu.AndroidStationMenu +import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu import ru.dbotthepony.mc.otm.registry.AndroidFeatures import ru.dbotthepony.mc.otm.registry.MRegistry import java.io.ByteArrayInputStream @@ -237,6 +244,11 @@ class ExoSuitSlotPacket(val slotId: Int, val itemStack: ItemStack, val container return@enqueueWork } + // don't duplicate data + // really. + if (mattery.exoSuitMenu.slots[slotId].container == minecraft.player?.inventory && minecraft.player?.containerMenu !is ExoSuitInventoryMenu) + return@enqueueWork + mattery.exoSuitMenu.slots[slotId].set(itemStack) mattery.exoSuitMenu.stateId = containerState } @@ -376,6 +388,48 @@ class ActivateAndroidFeaturePacket(val type: AndroidFeatureType<*>) : MatteryPac } } +class PickItemFromInventoryPacket( + val targetHotbarSlot: Int, + val sourceExosuitSlot: Int +) : MatteryPacket { + override fun write(buff: FriendlyByteBuf) { + buff.writeVarInt(targetHotbarSlot) + buff.writeVarInt(sourceExosuitSlot) + } + + override fun play(context: Supplier) { + context.packetHandled = true + + context.enqueueWork { + val player = context.sender ?: return@enqueueWork + val mattery = player.matteryPlayer ?: return@enqueueWork + + if (!mattery.hasExoSuit || sourceExosuitSlot !in 0 until mattery.exoSuitContainer.containerSize) { + return@enqueueWork + } + + if (!Inventory.isHotbarSlot(targetHotbarSlot)) { + return@enqueueWork + } + + player.inventory.selected = targetHotbarSlot + val existingItem = player.inventory[targetHotbarSlot].copy() + val inventoryItem = mattery.exoSuitContainer[sourceExosuitSlot].copy() + + player.inventory[targetHotbarSlot] = if (inventoryItem.isEmpty) ItemStack.EMPTY else inventoryItem + mattery.exoSuitContainer[sourceExosuitSlot] = if (existingItem.isEmpty) ItemStack.EMPTY else existingItem + + player.connection.send(ClientboundSetCarriedItemPacket(targetHotbarSlot)) + } + } + + companion object { + fun read(buff: FriendlyByteBuf): PickItemFromInventoryPacket { + return PickItemFromInventoryPacket(buff.readVarInt(), buff.readVarInt()) + } + } +} + object MatteryPlayerNetworkChannel : MatteryNetworkChannel( version = "1", name = "player" @@ -401,5 +455,7 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel( add(TriggerShockwavePacket::class, { TriggerShockwavePacket }, PLAY_TO_SERVER) add(TriggerJumpBoostPacket::class, { TriggerJumpBoostPacket }, PLAY_TO_SERVER) + + add(PickItemFromInventoryPacket::class, PickItemFromInventoryPacket.Companion::read, PLAY_TO_SERVER) } } diff --git a/src/main/resources/coremods/code_injector.js b/src/main/resources/coremods/code_injector.js index 08cf490fd..8df3c51d2 100644 --- a/src/main/resources/coremods/code_injector.js +++ b/src/main/resources/coremods/code_injector.js @@ -510,40 +510,34 @@ var returnCodes = [ opcodesRemapped.lreturn, ] +function putInstructions(node, after, instructions) { + var last = after + var next + + for (var i = 0; i < instructions.length; i++) { + next = instructions[i] + + if (last === undefined) { + node.instructions.insert(next) + } else { + node.instructions.insert(last, next) + } + + last = next + } +} + function injectAtTail(path, instructions) { return method(path, function(node) { for (var instr = node.instructions.size() - 1; instr >= 0; instr--) { if (includes(returnCodes, node.instructions.get(instr).getOpcode())) { - var last = node.instructions.get(instr > 0 ? --instr : instr) - var next - - for (var i = 0; i < instructions.length; i++) { - next = instructions[i] - node.instructions.insert(last, next) - last = next - } - + putInstructions(node, node.instructions.get(instr > 0 ? --instr : instr), instructions) return node } } print('[Overdrive That Matters Coremod] Unable to find tail of ' + path + ', injecting at beginning!') - - var last = undefined - var next - - for (var i = 0; i < instructions.length; i++) { - next = instructions[i] - - if (last === undefined) { - node.instructions.insert(next) - } else { - node.instructions.insert(last, next) - } - - last = next - } - + putInstructions(node, undefined, instructions) return node }) } @@ -614,6 +608,21 @@ function patchBlendFunc(node) { return node } +function backtrack(instructions, from, opcode, skipAmount) { + for (var i = from; i >= 0; i--) { + if (instructions.get(i).getOpcode() === opcode) { + if (skipAmount > 0) { + skipAmount-- + continue + } + + return instructions.get(i) + } + } + + return null +} + function initializeCoreMod() { return { 'Inventory#dropAll': injectAtTail( @@ -700,6 +709,74 @@ function initializeCoreMod() { 'transformer': patchBlendFunc }, + 'Minecraft#pickBlock patch for exosuit': + method('net.minecraft.client.Minecraft.m_91280_()V', function(node) { + // 275: invokevirtual #7672 // Method net/minecraft/world/entity/Entity.getType:()Lnet/minecraft/world/entity/EntityType; + // 278: invokevirtual #7667 // Method net/minecraft/core/DefaultedRegistry.getKey:(Ljava/lang/Object;)Lnet/minecraft/resources/ResourceLocation; + // 281: invokevirtual #4475 // Method net/minecraft/resources/ResourceLocation.toString:()Ljava/lang/String; + // 284: astore 5 + // 286: getstatic #5986 // Field LOGGER:Lorg/slf4j/Logger; + // 289: ldc_w #4484 // String Picking on: [{}] {} gave null item + // 292: aload_3 + // 293: aload 5 + // 295: invokeinterface #3145, 4 // InterfaceMethod org/slf4j/Logger.warn:(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V + // 300: goto 405 + // 303: aload_0 + // 304: getfield #6654 // Field player:Lnet/minecraft/client/player/LocalPlayer; + // 307: invokevirtual #7409 // Method net/minecraft/client/player/LocalPlayer.getInventory:()Lnet/minecraft/world/entity/player/Inventory; + // 310: astore 5 + // 312: aload_2 + // 313: ifnull 324 + // 316: aload_0 + // 317: aload 4 + // 319: aload_2 + // 320: invokevirtual #7675 // Method addCustomNbtData:(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/world/item/ItemStack; + // 323: pop + // 324: aload 5 + // 326: aload 4 + // 328: invokevirtual #7678 // Method net/minecraft/world/entity/player/Inventory.findSlotMatchingItem:(Lnet/minecraft/world/item/ItemStack;)I + // 331: istore 6 + // <-- Our target + // 333: iload_1 + // 334: ifeq 372 + // 337: aload 5 + // 339: aload 4 + // 341: invokevirtual #7681 // Method net/minecraft/world/entity/player/Inventory.setPickedItem:(Lnet/minecraft/world/item/ItemStack;)V + // 344: aload_0 + + for (var i = 0; i < node.instructions.size(); i++) { + var determinedOffset = test([ + opcodesRemapped.aload, + opcodesRemapped.getfield, + opcodesRemapped.invokevirtual, + opcodesRemapped.astore, + opcodesRemapped.aload, + opcodesRemapped.ifnull, + opcodesRemapped.aload, + opcodesRemapped.aload, + opcodesRemapped.aload, + opcodesRemapped.invokevirtual, + opcodesRemapped.pop, + opcodesRemapped.aload, + opcodesRemapped.aload, + opcodesRemapped.invokevirtual, + opcodesRemapped.istore, + ], node.instructions, i) + + if (determinedOffset != -1) { + putInstructions(node, node.instructions.get(determinedOffset), [ + new VarInsnNode(opcodesRemapped.iload, backtrack(node.instructions, determinedOffset, opcodesRemapped.istore, 0)['var']), + new VarInsnNode(opcodesRemapped.aload, backtrack(node.instructions, determinedOffset, opcodesRemapped.aload, 0)['var']), + new MethodInsnNode(opcodesRemapped.invokestatic, 'ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability', 'pickBlockHook', '(ILnet/minecraft/world/item/ItemStack;)V', false) + ]) + + break + } + } + + return node + }), + 'Inventory#add patch': { 'target': { 'type': 'METHOD', @@ -812,7 +889,7 @@ function initializeCoreMod() { injectInventoryInsertHook(node.instructions, determinedOffset, 'inventoryAddDamagedItemHook', '(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V', - new VarInsnNode(opcodesRemapped.iload, 1)) + new VarInsnNode(opcodesRemapped.iload, node.instructions.get(determinedOffset - 1)['var'])) break }