diff --git a/src/main/java/pm/c7/scout/mixin/PlayerScreenHandlerTransformer.java b/src/main/java/pm/c7/scout/mixin/PlayerScreenHandlerTransformer.java index 4835e06..fa68c4b 100644 --- a/src/main/java/pm/c7/scout/mixin/PlayerScreenHandlerTransformer.java +++ b/src/main/java/pm/c7/scout/mixin/PlayerScreenHandlerTransformer.java @@ -24,7 +24,7 @@ public class PlayerScreenHandlerTransformer implements ClassNodeTransformer { for (var mn : node.methods) { // that other comment was a half truth, you can transform mixins :^) - if (mn.name.equals("handler$zei000$trinkets$quickMove") || mn.name.equals(quickMove)) { + if (mn.name.endsWith("trinkets$quickMove") || mn.name.equals(quickMove)) { for (var insn : mn.instructions) { if (insn instanceof VarInsnNode vin) { if (vin.getOpcode() == ASTORE && vin.var == 4) { diff --git a/src/main/java/pm/c7/scout/mixin/ScreenHandlerMixin.java b/src/main/java/pm/c7/scout/mixin/ScreenHandlerMixin.java index 3411e88..2f90eac 100644 --- a/src/main/java/pm/c7/scout/mixin/ScreenHandlerMixin.java +++ b/src/main/java/pm/c7/scout/mixin/ScreenHandlerMixin.java @@ -6,18 +6,21 @@ import net.minecraft.item.ItemStack; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.util.collection.DefaultedList; + import org.jetbrains.annotations.Nullable; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import pm.c7.scout.ScoutMixin.Transformer; import pm.c7.scout.ScoutUtil; -@Mixin(ScreenHandler.class) +@Mixin(value = ScreenHandler.class, priority = 950) @Transformer(ScreenHandlerTransformer.class) public abstract class ScreenHandlerMixin { @Inject(method = "getSlot", at = @At("HEAD"), cancellable = true) @@ -55,6 +58,15 @@ public abstract class ScreenHandlerMixin { } } + @Redirect(method = "internalOnSlotClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;get(I)Ljava/lang/Object;", ordinal = 5)) + public Object scout$fixSlotIndexing(DefaultedList self, int index, int slotIndex, int button, SlotActionType actionType, PlayerEntity player) { + if (ScoutUtil.isBagSlot(index)) { + return ScoutUtil.getBagSlot(index, player.playerScreenHandler); + } else { + return self.get(index); + } + } + @Shadow public static boolean canInsertItemIntoSlot(@Nullable Slot slot, ItemStack stack, boolean allowOverflow) { return false; diff --git a/src/main/java/pm/c7/scout/mixin/ScreenHandlerTransformer.java b/src/main/java/pm/c7/scout/mixin/ScreenHandlerTransformer.java index 7d06a56..21a91fc 100644 --- a/src/main/java/pm/c7/scout/mixin/ScreenHandlerTransformer.java +++ b/src/main/java/pm/c7/scout/mixin/ScreenHandlerTransformer.java @@ -8,6 +8,8 @@ import pm.c7.scout.mixinsupport.ClassNodeTransformer; import static org.objectweb.asm.Opcodes.*; public class ScreenHandlerTransformer implements ClassNodeTransformer { + //private static final Logger LOGGER = LoggerFactory.getLogger("Scout:ScreenHandlerTransformer"); + @Override public void transform(String name, ClassNode node) { var resolver = QuiltLoader.getMappingResolver(); @@ -25,6 +27,11 @@ public class ScreenHandlerTransformer implements ClassNodeTransformer { var LSlot = L(Slot); var DefaultedList = slash(resolver.mapClassName(namespace, "net.minecraft.class_2371")); + //var LDefaultedList = L(DefaultedList); + + //var slots = resolver.mapFieldName(namespace, name, "field_7761", LDefaultedList); + + int ordinal = 0; for (var mn : node.methods) { if (mn.name.equals(internalOnSlotClick)) { @@ -32,20 +39,92 @@ public class ScreenHandlerTransformer implements ClassNodeTransformer { if (insn instanceof VarInsnNode vin) { if (vin.getOpcode() == ILOAD && vin.var == 1) { if (insn.getNext() instanceof JumpInsnNode nextInsn && nextInsn.getOpcode() == IFGE) { + // `if (slotIndex < 0) return` -> `if (slotIndex < 0 && !isBagSlot(slotIndex)) return` var jumpTo = nextInsn.label; mn.instructions.insert(nextInsn, insns( ILOAD(1), INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"), IFNE(jumpTo) )); + } else if (insn.getPrevious() instanceof JumpInsnNode prevInsn && prevInsn.getOpcode() == IFEQ && insn.getNext() instanceof JumpInsnNode nextInsn && nextInsn.getOpcode() == IFLT) { + // skip creative duping, it uses same signature and i dont feel like overcomplicating the check + if (ordinal != 1) { + ordinal++; + continue; + } + + // fix dropping from bags not working + LabelNode Lcheck = new LabelNode(); + nextInsn.label = Lcheck; + nextInsn.setOpcode(IFGE); + mn.instructions.insert(nextInsn, insns( + ILOAD(1), + INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"), + IFNE(Lcheck), + RETURN(), + Lcheck + )); } - } else if (vin.getOpcode() == ASTORE && vin.var == 7) { + } else if (vin.getOpcode() == ASTORE && (vin.var == 6 || vin.var == 7)) { + // fix most but not all calls to `slots.get` if (vin.getPrevious() instanceof TypeInsnNode prevInsn && prevInsn.getOpcode() == CHECKCAST && prevInsn.desc.equals(Slot)) { if (prevInsn.getPrevious() instanceof MethodInsnNode prevPrevInsn && prevPrevInsn.getOpcode() == INVOKEVIRTUAL) { if(prevPrevInsn.owner.equals(DefaultedList)) { + var insertPoint = prevPrevInsn.getPrevious(); + + if (insertPoint.getOpcode() == ILOAD) { + var beforeInsert = insertPoint.getPrevious(); + + if (beforeInsert != null && beforeInsert.getPrevious() != null){ + if (beforeInsert.getOpcode() == GETFIELD && beforeInsert.getPrevious().getOpcode() == ALOAD) { + insertPoint = beforeInsert.getPrevious(); + } else { + continue; + } + } + + LabelNode LnotBag = new LabelNode(); + LabelNode Lend = (LabelNode) vin.getNext(); + + mn.instructions.insertBefore(insertPoint, insns( + ILOAD(1), + INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"), + IFEQ(LnotBag), + ILOAD(1), + ALOAD(4), + GETFIELD(PlayerEntity, playerScreenHandler, LPlayerScreenHandler), + INVOKESTATIC("pm/c7/scout/ScoutUtil", "getBagSlot", "(I" + LPlayerScreenHandler + ")" + LSlot), + CHECKCAST(Slot), + ASTORE(vin.var), + GOTO(Lend), + LnotBag + )); + } + } + } + } + } + } + } + } else if (mn.name.endsWith("debugify$handleCtrlQCrafting")) { // ughghghhghghghghgh + for (var insn : mn.instructions) { + if (insn instanceof VarInsnNode vin && vin.getOpcode() == ASTORE && vin.var == 6) { + if (vin.getPrevious() instanceof TypeInsnNode prevInsn && prevInsn.getOpcode() == CHECKCAST && prevInsn.desc.equals(Slot)) { + if (prevInsn.getPrevious() instanceof MethodInsnNode prevPrevInsn && prevPrevInsn.getOpcode() == INVOKEVIRTUAL) { + if(prevPrevInsn.owner.equals(DefaultedList)) { + var insertPoint = prevPrevInsn.getPrevious(); + + if (insertPoint.getOpcode() == ILOAD) { + var beforeInsert = insertPoint.getPrevious(); + + if (beforeInsert != null && beforeInsert.getPrevious() != null && beforeInsert.getOpcode() == GETFIELD && beforeInsert.getPrevious().getOpcode() == ALOAD) { + insertPoint = beforeInsert.getPrevious(); + } + LabelNode LnotBag = new LabelNode(); - LabelNode Lend = new LabelNode(); - mn.instructions.insertBefore(prevPrevInsn.getPrevious().getPrevious().getPrevious(), insns( + LabelNode Lend = (LabelNode) vin.getNext(); + + mn.instructions.insertBefore(insertPoint, insns( ILOAD(1), INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"), IFEQ(LnotBag), @@ -58,9 +137,6 @@ public class ScreenHandlerTransformer implements ClassNodeTransformer { GOTO(Lend), LnotBag )); - mn.instructions.insert(vin, insns( - Lend - )); } } } @@ -71,6 +147,76 @@ public class ScreenHandlerTransformer implements ClassNodeTransformer { } } + // debug, keeping here for future use + /*private static List allOpcodes = Arrays.asList(Opcodes.class.getFields()); + + private String getOpcodeName(int v) { + Optional opcode = allOpcodes.stream() + .filter(f -> f.getType() == int.class) + .filter(f -> { + if (f.getName().startsWith("F_")) { + return f.getName().equals("F_NEW"); + } else { + return !f.getName().startsWith("V") + && !f.getName().startsWith("ASM") + && !f.getName().startsWith("SOURCE_") + && !f.getName().startsWith("ACC_") + && !f.getName().startsWith("H_") + && !f.getName().startsWith("T_"); + } + }) + .filter(f -> { + try { + var field = f.get(Opcodes.class); + //LOGGER.info("{} {} | {}", f.getName(), f.getType(), field); + return field.equals(v); + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + }) + .findFirst(); + + if (opcode.isEmpty()) { + return ""; + } + + return opcode.get().getName(); + } + + private void printNode(AbstractInsnNode node) { + var name = getOpcodeName(node.getOpcode()); + String val = ""; + + if (node instanceof VarInsnNode vin) { + val = String.valueOf(vin.var); + } else if(node instanceof FieldInsnNode fin) { + val = fin.owner + "." + fin.name + ":" + fin.desc; + } else if(node instanceof MethodInsnNode min) { + val = min.owner + "." + min.name + ":" + min.desc; + } else if (node instanceof TypeInsnNode tin) { + val = tin.desc; + } else if (node instanceof JumpInsnNode jin) { + val = jin.label.toString(); + } else if (node instanceof LabelNode label) { + name = "L"; + val = label.toString(); + } + + LOGGER.info("{} {}", name, val); + } + + private void dumpInstructions(InsnList insns, int start, int end) { + for (var i = start; i < end + 1; i++) { + printNode(insns.get(i)); + } + }*/ + private String slash(String clazz) { return clazz.replaceAll("\\.", "/"); } @@ -105,13 +251,13 @@ public class ScreenHandlerTransformer implements ClassNodeTransformer { private static FieldInsnNode GETFIELD(String owner, String name, String desc) { return new FieldInsnNode(GETFIELD, owner, name, desc); } - private static JumpInsnNode IFNULL(LabelNode v) { - return new JumpInsnNode(IFNULL, v); - } private static TypeInsnNode CHECKCAST(String desc) { return new TypeInsnNode(CHECKCAST, desc); } private static JumpInsnNode GOTO(LabelNode v) { return new JumpInsnNode(GOTO, v); } + private static InsnNode RETURN() { + return new InsnNode(RETURN); + } }