File size: 3,483 Bytes
24010a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Set;
import jdk.internal.org.objectweb.asm.*;

/** Log the exception vanilla 1.19 discards when player saving fails. */
public final class PlayerSaveDiagnostics {
    public static void premain(String args, Instrumentation instrumentation) {
        instrumentation.addTransformer(new ClassFileTransformer() {
            public byte[] transform(ClassLoader loader, String name, Class<?> type,
                                    ProtectionDomain domain, byte[] bytes) {
                if (!"net/minecraft/class_29".equals(name)) return null;
                try {
                    ClassReader reader = new ClassReader(bytes);
                    ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
                    int[] injected = {0};
                    reader.accept(new ClassVisitor(Opcodes.ASM8, writer) {
                        public MethodVisitor visitMethod(int access, String method, String desc,
                                                         String signature, String[] exceptions) {
                            MethodVisitor delegate = super.visitMethod(access, method, desc, signature, exceptions);
                            if (!"method_262".equals(method)) return delegate;
                            return new MethodVisitor(Opcodes.ASM8, delegate) {
                                final Set<Label> handlers = new HashSet<>();
                                boolean pending;
                                public void visitTryCatchBlock(Label start, Label end, Label handler, String caught) {
                                    if ("java/lang/Exception".equals(caught)) handlers.add(handler);
                                    super.visitTryCatchBlock(start, end, handler, caught);
                                }
                                public void visitLabel(Label label) {
                                    super.visitLabel(label);
                                    if (handlers.contains(label)) pending = true;
                                }
                                public void visitVarInsn(int opcode, int variable) {
                                    if (pending && opcode == Opcodes.ASTORE) {
                                        // Preserve the original exception and catch behavior.
                                        super.visitInsn(Opcodes.DUP);
                                        super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable",
                                                              "printStackTrace", "()V", false);
                                        pending = false;
                                        injected[0]++;
                                    }
                                    super.visitVarInsn(opcode, variable);
                                }
                            };
                        }
                    }, 0);
                    System.err.println("[player-save-diagnostics] exception logging hooks=" + injected[0]);
                    return injected[0] == 0 ? null : writer.toByteArray();
                } catch (Throwable failure) {
                    System.err.println("[player-save-diagnostics] skipped: " + failure);
                    return null;
                }
            }
        });
    }
}