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.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))
}
}
}

View File

@ -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) {

View File

@ -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<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(
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)
}
}

View File

@ -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
}