From 2f7188b9693b0ef6674b8a433af49d1ae211fb64 Mon Sep 17 00:00:00 2001 From: Space Walker Date: Sun, 21 Sep 2025 12:31:23 +0200 Subject: [PATCH] implement entrypoint patch for pre-classic --- .../game/minecraft/patch/EntrypointPatch.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/minecraft/src/main/java/net/fabricmc/loader/impl/game/minecraft/patch/EntrypointPatch.java b/minecraft/src/main/java/net/fabricmc/loader/impl/game/minecraft/patch/EntrypointPatch.java index dc83f5231..f8f2e9fa4 100644 --- a/minecraft/src/main/java/net/fabricmc/loader/impl/game/minecraft/patch/EntrypointPatch.java +++ b/minecraft/src/main/java/net/fabricmc/loader/impl/game/minecraft/patch/EntrypointPatch.java @@ -82,6 +82,7 @@ public void process(FabricLauncher launcher, Function classSo // Main -> Game entrypoint search // // -- CLIENT -- + // pre-classic: find init() invocation before "Failed to start RubyDung" log message // pre-1.6 (seems to hold to 0.0.11!): find the only non-static non-java-packaged Object field // 1.6.1+: [client].start() [INVOKEVIRTUAL] // 19w04a: [client]. [INVOKESPECIAL] -> Thread.start() @@ -209,6 +210,62 @@ public void process(FabricLauncher launcher, Function classSo } } + if (type == EnvType.CLIENT && !isApplet && gmCandidate.name.equals("run")) { + // For pre-classic, try to find the "Failed to start RubyDung" log message + // that is shown if the init() method throws an exception, then patch said + // init() method. + + MethodInsnNode potentialInitInsn = null; + boolean hasFailedToStartLog = false; + + for (AbstractInsnNode insn : gmCandidate.instructions) { + if (insn instanceof MethodInsnNode && potentialInitInsn == null) { + MethodInsnNode methodInsn = (MethodInsnNode) insn; + + if (methodInsn.getOpcode() == Opcodes.INVOKEVIRTUAL && methodInsn.owner.equals(gameClass.name)) { + potentialInitInsn = methodInsn; + } else { + // first method insn is not init(), this is not pre-classic! + break; + } + } + + if (insn instanceof LdcInsnNode && !hasFailedToStartLog) { + if (potentialInitInsn == null) { + // found LDC before init() invocation, this is not pre-classic! + break; + } + + Object cst = ((LdcInsnNode) insn).cst; + + if (cst instanceof String) { + String s = (String) cst; + + if (s.equals("Failed to start RubyDung")) { + hasFailedToStartLog = true; + } + } + + if (!hasFailedToStartLog) { + // first LDC insn is not the expected log message, this is not pre-classic! + break; + } + } + + if (potentialInitInsn != null && hasFailedToStartLog) { + // found log message and init() invocation, now get the init() method node + for (MethodNode gm : gameClass.methods) { + if (gm.name.equals(potentialInitInsn.name) && gm.desc.equals(potentialInitInsn.desc)) { + gameMethod = gm; + gameMethodQuality = 2; + + break; + } + } + } + } + } + if (type == EnvType.CLIENT && !isApplet && gameMethodQuality < 2) { // Try to find a method with an LDC string "LWJGL Version: ". // This is the "init()" method, or as of 19w38a is the constructor, or called somewhere in that vicinity, @@ -510,6 +567,17 @@ public void process(FabricLauncher launcher, Function classSo break; } } + + // TODO: better handling of run directory for pre-classic + if (!patched && gameMethod != gameConstructor) { + ListIterator it = gameMethod.instructions.iterator(); + + it.add(new InsnNode(Opcodes.ACONST_NULL)); + it.add(new VarInsnNode(Opcodes.ALOAD, 0)); + finishEntrypoint(type, it); + + patched = true; + } } if (!patched) {