Add pickItem hook for exosuit inventory

Fixes #93
This commit is contained in:
DBotThePony 2022-10-12 09:23:27 +07:00
parent ba52af00fb
commit 994058b187
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 187 additions and 27 deletions

View File

@ -43,6 +43,7 @@ import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearchManager import ru.dbotthepony.mc.otm.android.AndroidResearchManager
import ru.dbotthepony.mc.otm.android.AndroidResearchType import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature 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.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu 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))
}
} }
} }

View File

@ -10,6 +10,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.* import net.minecraft.world.inventory.*
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability 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.cos.cosmeticArmorSlots
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
import ru.dbotthepony.mc.otm.container.iterator 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) { 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) { fun sendCarriedChange(container: ExoSuitInventoryMenu, itemStack: ItemStack) {

View File

@ -3,7 +3,11 @@ package ru.dbotthepony.mc.otm.network
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket
import net.minecraft.resources.ResourceLocation 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.minecraft.world.item.ItemStack
import net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT import net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT
import net.minecraftforge.network.NetworkDirection.PLAY_TO_SERVER 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.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.MatteryGUI import ru.dbotthepony.mc.otm.client.MatteryGUI
import ru.dbotthepony.mc.otm.client.minecraft 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.AndroidStationMenu
import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu
import ru.dbotthepony.mc.otm.registry.AndroidFeatures import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MRegistry import ru.dbotthepony.mc.otm.registry.MRegistry
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
@ -237,6 +244,11 @@ class ExoSuitSlotPacket(val slotId: Int, val itemStack: ItemStack, val container
return@enqueueWork 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.slots[slotId].set(itemStack)
mattery.exoSuitMenu.stateId = containerState 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<NetworkEvent.Context>) {
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( object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
version = "1", version = "1",
name = "player" name = "player"
@ -401,5 +455,7 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
add(TriggerShockwavePacket::class, { TriggerShockwavePacket }, PLAY_TO_SERVER) add(TriggerShockwavePacket::class, { TriggerShockwavePacket }, PLAY_TO_SERVER)
add(TriggerJumpBoostPacket::class, { TriggerJumpBoostPacket }, PLAY_TO_SERVER) add(TriggerJumpBoostPacket::class, { TriggerJumpBoostPacket }, PLAY_TO_SERVER)
add(PickItemFromInventoryPacket::class, PickItemFromInventoryPacket.Companion::read, PLAY_TO_SERVER)
} }
} }

View File

@ -510,40 +510,34 @@ var returnCodes = [
opcodesRemapped.lreturn, 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) { function injectAtTail(path, instructions) {
return method(path, function(node) { return method(path, function(node) {
for (var instr = node.instructions.size() - 1; instr >= 0; instr--) { for (var instr = node.instructions.size() - 1; instr >= 0; instr--) {
if (includes(returnCodes, node.instructions.get(instr).getOpcode())) { if (includes(returnCodes, node.instructions.get(instr).getOpcode())) {
var last = node.instructions.get(instr > 0 ? --instr : instr) putInstructions(node, node.instructions.get(instr > 0 ? --instr : instr), instructions)
var next
for (var i = 0; i < instructions.length; i++) {
next = instructions[i]
node.instructions.insert(last, next)
last = next
}
return node return node
} }
} }
print('[Overdrive That Matters Coremod] Unable to find tail of ' + path + ', injecting at beginning!') print('[Overdrive That Matters Coremod] Unable to find tail of ' + path + ', injecting at beginning!')
putInstructions(node, undefined, instructions)
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
}
return node return node
}) })
} }
@ -614,6 +608,21 @@ function patchBlendFunc(node) {
return 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() { function initializeCoreMod() {
return { return {
'Inventory#dropAll': injectAtTail( 'Inventory#dropAll': injectAtTail(
@ -700,6 +709,74 @@ function initializeCoreMod() {
'transformer': patchBlendFunc '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': { 'Inventory#add patch': {
'target': { 'target': {
'type': 'METHOD', 'type': 'METHOD',
@ -812,7 +889,7 @@ function initializeCoreMod() {
injectInventoryInsertHook(node.instructions, determinedOffset, injectInventoryInsertHook(node.instructions, determinedOffset,
'inventoryAddDamagedItemHook', 'inventoryAddDamagedItemHook',
'(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V', '(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 break
} }