package de.aeromc.capes; import java.lang.instrument.ClassFileTransformer; import java.security.ProtectionDomain; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * Baut YggdrasilMinecraftSessionService beim Laden um: vor jedem "return" der * Textur-Methode wird das Ergebnis an den Nachschlage-Dienst gereicht, der den * AeroMC-Umhang einsetzt. * * authlib 5+ (Minecraft 1.20.2 … 26.x): * MinecraftProfileTextures unpackTextures(Property) * authlib 1–4 (Minecraft 1.8 … 1.20.1): * Map<Type, MinecraftProfileTexture> getTextures(GameProfile, boolean) * * Eingefügt wird nur geradliniger Code ohne Sprünge – die vorhandenen * Stack-Map-Frames bleiben gültig, es muss nichts neu berechnet werden. */ final class Umbau implements ClassFileTransformer { static final String ZIEL = "com/mojang/authlib/yggdrasil/YggdrasilMinecraftSessionService"; @Override public byte[] transform(ClassLoader loader, String name, Class umdefiniert, ProtectionDomain pd, byte[] klasse) { if (!ZIEL.equals(name) || umdefiniert != null) return null; try { ClassReader leser = new ClassReader(klasse); ClassWriter schreiber = new ClassWriter(leser, ClassWriter.COMPUTE_MAXS); final int[] umgebaut = {0}; leser.accept(new ClassVisitor(Opcodes.ASM9, schreiber) { @Override public MethodVisitor visitMethod(int zugriff, String mname, String desc, String sig, String[] ausn) { MethodVisitor mv = super.visitMethod(zugriff, mname, desc, sig, ausn); if ((zugriff & Opcodes.ACC_STATIC) != 0) return mv; String rueckgabe = null; if ("unpackTextures".equals(mname) && desc.startsWith("(Lcom/mojang/authlib/properties/Property;)L")) { rueckgabe = desc.substring(desc.indexOf(")L") + 2, desc.length() - 1); } else if ("getTextures".equals(mname) && desc.equals("(Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;")) { rueckgabe = "java/util/Map"; } if (rueckgabe == null) return mv; umgebaut[0]++; final String typ = rueckgabe; return new MethodVisitor(Opcodes.ASM9, mv) { @Override public void visitInsn(int op) { if (op == Opcodes.ARETURN) { // Stapel: [ergebnis] -> [dienst, ergebnis, argument] -> apply -> [neu] super.visitVarInsn(Opcodes.ALOAD, 1); super.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "getProperties", "()Ljava/util/Properties;", false); super.visitLdcInsn(Agent.SCHLUESSEL); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/Properties", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", false); super.visitTypeInsn(Opcodes.CHECKCAST, "java/util/function/BiFunction"); super.visitInsn(Opcodes.DUP_X2); super.visitInsn(Opcodes.POP); super.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/function/BiFunction", "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true); super.visitTypeInsn(Opcodes.CHECKCAST, typ); } super.visitInsn(op); } }; } }, 0); if (umgebaut[0] == 0) return null; System.out.println("[AeroMC] authlib fuer Umhaenge vorbereitet (" + umgebaut[0] + " Methode)"); return schreiber.toByteArray(); } catch (Throwable t) { System.out.println("[AeroMC] authlib konnte nicht angepasst werden: " + t); return null; } } }