Fix dropping from bags not working

This commit is contained in:
Cynthia Foxwell 2024-03-14 19:44:00 -06:00
parent a5f7fd9fb0
commit 8712c28b0e
3 changed files with 169 additions and 11 deletions

View file

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

View file

@ -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<Slot> 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;

View file

@ -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<Field> allOpcodes = Arrays.asList(Opcodes.class.getFields());
private String getOpcodeName(int v) {
Optional<Field> 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 "<unknown: " + String.valueOf(v) + ">";
}
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);
}
}