Skip to content

Commit

Permalink
Error screen handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Lassebq committed Oct 12, 2024
1 parent 3b724b4 commit 2e1ccbb
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ public class MicroMixinTweak extends Tweak {
private static final boolean ACCESS_FIXER = Boolean.parseBoolean(System.getProperty("launchwrapper.accessfixer", "false"));
public static final boolean DEV = Boolean.parseBoolean(System.getProperty("launchwrapper.dev", "false"));

private List<MixinMod> mods = new ArrayList<MixinMod>();
private Tweak baseTweak;
protected List<MixinMod> mods = new ArrayList<MixinMod>();
protected Tweak baseTweak;
protected MicroMixinInjection microMixinInjection = new MicroMixinInjection(mods, this);

public MicroMixinTweak(LaunchConfig launch, Tweak tweak) {
super(launch);
baseTweak = tweak;
Expand Down Expand Up @@ -91,7 +93,7 @@ public List<Injection> getInjections() {
}
}
injects.addAll(baseTweak.getInjections());
injects.add(new MicroMixinInjection(mods, this));
injects.add(microMixinInjection);
return injects;
}

Expand All @@ -105,6 +107,10 @@ public List<LazyTweaker> getLazyTweakers() {
return list;
}

public boolean handleError(LaunchClassLoader loader, Throwable t) {
return baseTweak.handleError(loader, microMixinInjection.exception == null ? t : microMixinInjection.exception);
}

@Override
public LaunchTarget getLaunchTarget() {
return baseTweak.getLaunchTarget();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class MicroMixinInjection implements Injection {

private Collection<MixinMod> mixinMods;
private MicroMixinTweak mixinTweak;

public Exception exception;

public MicroMixinInjection(Collection<MixinMod> mixinMods, MicroMixinTweak mixinTweak) {
this.mixinMods = mixinMods;
Expand Down Expand Up @@ -113,7 +115,7 @@ public boolean apply(ClassNodeSource source, LaunchConfig config) {
// remapper2.remapNode(mixin, sharedSB);
source.overrideClass(mixin);
} catch (Exception e) {
e.printStackTrace();
exception = e;
return false;
}
}
Expand All @@ -128,7 +130,12 @@ public boolean apply(ClassNodeSource source, LaunchConfig config) {
}
}

mixinTweak.transformer.addMixin(source, mixinConfig);
try {
mixinTweak.transformer.addMixin(source, mixinConfig);
} catch (IllegalStateException e) {
exception = e;
return false;
}
}

return true;
Expand Down
17 changes: 13 additions & 4 deletions src/main/java/org/mcphackers/launchwrapper/Launch.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,18 @@ public void launch() {
return;
}
mainTweak.prepare(loader);
if(!mainTweak.transform(loader)) {
LOGGER.logErr("Tweak could not be applied");
return;
try {
if(!mainTweak.transform(loader)) {
LOGGER.logErr("Tweak could not be applied");
if(!mainTweak.handleError(loader, null)) { // if handled successfully, continue
return;
}
}
} catch(Throwable t) {
LOGGER.logErr("Tweak could not be applied", t);
if(!mainTweak.handleError(loader, t)) { // if handled successfully, continue
return;
}
}
mainTweak.transformResources(loader);
LaunchTarget target = mainTweak.getLaunchTarget();
Expand Down Expand Up @@ -80,7 +89,7 @@ public void logErr(String format, Object... args) {
System.err.println("[LaunchWrapper] " + String.format(format, args));
}

public void logErr(String msg, Exception e) {
public void logErr(String msg, Throwable e) {
System.err.println("[LaunchWrapper] " + msg);
e.printStackTrace(System.err);
}
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/org/mcphackers/launchwrapper/tweak/LegacyTweak.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.mcphackers.launchwrapper.Launch;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler;
import org.mcphackers.launchwrapper.protocol.URLStreamHandlerProxy;
import org.mcphackers.launchwrapper.target.AppletLaunchTarget;
Expand Down Expand Up @@ -58,6 +59,7 @@ public class LegacyTweak extends Tweak {
"com/mojang/minecraft/MinecraftApplet"
};
protected LegacyTweakContext context = new LegacyTweakContext();
protected ClassicCrashScreen crashPatch = new ClassicCrashScreen(context);

public LegacyTweak(LaunchConfig config) {
super(config);
Expand All @@ -66,7 +68,7 @@ public LegacyTweak(LaunchConfig config) {
public List<Injection> getInjections() {
return Arrays.asList(
context,
new ClassicCrashScreen(context),
crashPatch,
new ClassicLoadingFix(context),
new UnlicensedCopyText(context),
new FixSplashScreen(context),
Expand All @@ -79,7 +81,7 @@ public List<Injection> getInjections() {
new MouseFix(context),
new ReplaceGameDir(context),
new OptionsLoadFix(context),
new FixClassicSession(context),
// new FixClassicSession(context),
new GameModeSwitch(context),
new AddMain(context),
new ForgeVersionCheck(),
Expand All @@ -91,6 +93,13 @@ public List<LazyTweaker> getLazyTweakers() {
return Collections.<LazyTweaker>singletonList(new Java5LazyTweaker());
}

public boolean handleError(LaunchClassLoader loader, Throwable t) {
if(config.isom.get()) {
return false;
}
return crashPatch.injectErrorAtInit(loader, t);
}

private void downloadServer() {
if(config.serverURL.get() == null || config.gameDir.get() == null) {
return;
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/mcphackers/launchwrapper/tweak/Tweak.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public void transformResources(ResourceSource source) {
// Any classpath changes required for transformers can be set here
public void prepare(LaunchClassLoader loader) {
}

public boolean handleError(LaunchClassLoader loader, Throwable t) {
if(t != null) {
t.printStackTrace();
}
return false;
}

public final boolean transform(ClassNodeSource source) {
if(!clean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;

import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler;
import org.mcphackers.launchwrapper.protocol.URLStreamHandlerProxy;
import org.mcphackers.launchwrapper.target.LaunchTarget;
Expand All @@ -20,6 +21,7 @@ public class VanillaTweak extends Tweak {
public static final String MAIN_CLASS = "net/minecraft/client/main/Main";

protected VanillaTweakContext context = new VanillaTweakContext();
protected ClassicCrashScreen crashPatch = new ClassicCrashScreen(context);

public VanillaTweak(LaunchConfig launch) {
super(launch);
Expand All @@ -28,13 +30,17 @@ public VanillaTweak(LaunchConfig launch) {
public List<Injection> getInjections() {
return Arrays.<Injection>asList(
context,
crashPatch,
new OutOfFocusFullscreen(context),
new ClassicCrashScreen(context),
new OneSixAssetsFix(context),
new ChangeBrand()
);
}

public boolean handleError(LaunchClassLoader loader, Throwable t) {
return crashPatch.injectErrorAtInit(loader, t);
}

public LaunchTarget getLaunchTarget() {
URLStreamHandlerProxy.setURLStreamHandler("http", new LegacyURLStreamHandler(config));
URLStreamHandlerProxy.setURLStreamHandler("https", new LegacyURLStreamHandler(config));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface MinecraftGetter {
MethodNode getRun();

FieldNode getIsRunning();

MethodNode getInit();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.tweak.injection.InjectionWithContext;
import org.mcphackers.launchwrapper.tweak.injection.MinecraftGetter;
import org.mcphackers.launchwrapper.util.ClassNodeSource;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
Expand All @@ -17,9 +18,9 @@
/**
* Fixes Z-fighting issues on certain graphics drivers by increasing bit depth to 24
*/
public class BitDepthFix extends InjectionWithContext<LegacyTweakContext> {
public class BitDepthFix extends InjectionWithContext<MinecraftGetter> {

public BitDepthFix(LegacyTweakContext storage) {
public BitDepthFix(MinecraftGetter storage) {
super(storage);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
* Note that some crashes related to rendering may break Tesselator which will make it stuck in an exception loop
*/
public class ClassicCrashScreen extends InjectionWithContext<MinecraftGetter> {

private ClassNode errScreen;
private ClassNode button;

public ClassicCrashScreen(MinecraftGetter context) {
super(context);
}
Expand All @@ -49,7 +51,7 @@ public boolean required() {
return false;
}

public MethodNode getOpenScreen() {
private MethodNode getOpenScreen() {
ClassNode minecraft = context.getMinecraft();

for(MethodNode m : minecraft.methods) {
Expand Down Expand Up @@ -85,6 +87,72 @@ && compareInsn(insns2[3], INVOKEVIRTUAL, minecraft.name, null, null)) {
return null;
}

public boolean injectErrorAtInit(ClassNodeSource source, Throwable t) {
if(errScreen == null) {
// Error was either caused too early or we couldn't patch error screen
return false;
}
MethodNode openScreen = getOpenScreen();
if(openScreen == null) {
return false;
}
MethodNode initScreen = null;
for(MethodNode m : errScreen.methods) {
if(!m.desc.equals("()V") || m.name.equals("<init>")) {
continue;
}
initScreen = m;
break;
}
if(initScreen == null) {
return false;
}
ClassNode minecraft = context.getMinecraft();
MethodNode init = context.getInit();
MethodNode run = context.getRun();
FieldInsnNode screen = null;
for(AbstractInsnNode insn = openScreen.instructions.getFirst(); insn != null; insn = nextInsn(insn)) {
if(compareInsn(insn, ALOAD, 1)
&& compareInsn(nextInsn(insn), PUTFIELD, minecraft.name, null, "L" + errScreen.superName + ";")) {
screen = (FieldInsnNode)nextInsn(insn);
}
}
for(AbstractInsnNode insn = run.instructions.getFirst(); insn != null; insn = nextInsn(insn)) {
if(compareInsn(insn, INVOKEVIRTUAL, minecraft.name, init.name, init.desc)
|| compareInsn(insn, INVOKESPECIAL, minecraft.name, init.name, init.desc)) {
InsnList handle = new InsnList();
handle.add(new VarInsnNode(ALOAD, 0));
handle.add(new TypeInsnNode(NEW, errScreen.name));
handle.add(new InsnNode(DUP));
handle.add(new LdcInsnNode("LaunchWrapper error"));
handle.add(new LdcInsnNode(t == null ? "Required tweaks failed to apply" : "[" + t.getClass().getName() + "]"));
handle.add(new MethodInsnNode(INVOKESPECIAL, errScreen.name, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"));
handle.add(new MethodInsnNode(INVOKEVIRTUAL, minecraft.name, openScreen.name, openScreen.desc));
run.instructions.insert(insn, handle);
LabelNode skip = new LabelNode();
handle = new InsnList();
handle.add(new VarInsnNode(ALOAD, 0));
handle.add(new FieldInsnNode(GETFIELD, minecraft.name, screen.name, screen.desc));
handle.add(new JumpInsnNode(IFNULL, skip));
handle.add(new VarInsnNode(ALOAD, 0));
handle.add(new FieldInsnNode(GETFIELD, minecraft.name, screen.name, screen.desc));
handle.add(new TypeInsnNode(INSTANCEOF, errScreen.name));
handle.add(new JumpInsnNode(IFEQ, skip));
handle.add(new InsnNode(RETURN));
handle.add(skip);
openScreen.instructions.insert(handle);
handle = new InsnList();
handle.add(new InsnNode(RETURN));
initScreen.instructions = handle;
return true;
}
if(insn.getType() == AbstractInsnNode.METHOD_INSN) {
return false;
}
}
return false;
}

@Override
public boolean apply(ClassNodeSource source, LaunchConfig config) {
// TODO catch MinecraftServer crashes in 1.3+
Expand Down Expand Up @@ -153,7 +221,6 @@ && compareInsn(testInsn2, INVOKEVIRTUAL, minecraft.name, null, "()V")) {
MethodNode setWorld = getWorldSetter(putWorld);

ClassNode titleScreen = null;
ClassNode errScreen = null;
MethodNode openScreen = getOpenScreen();
if(openScreen == null) {
return false;
Expand Down Expand Up @@ -251,6 +318,8 @@ && compareInsn(insns[2], GOTO)) {
insn = nextInsn(insn);
}
}
MethodInsnNode translateInvoke = null;

if(errScreen == null && titleScreen != null) {
ClassNode worldSelectScreen = null;
methods:
Expand Down Expand Up @@ -315,6 +384,12 @@ && compareInsn(insns[3], INVOKEVIRTUAL, null, null, "()V")) {
for(AbstractInsnNode insn2 = m2.instructions.getFirst(); insn2 != null; insn2 = nextInsn(insn2)) {
if(compareInsn(insn2, LDC, "selectWorld.unable_to_load")
|| compareInsn(insn2, LDC, "Unable to load worlds")) {
AbstractInsnNode[] insns3 = fill(nextInsn(insn2), 3);
if(compareInsn(insns3[0], ICONST_0)
&& compareInsn(insns3[1], ANEWARRAY)
&& compareInsn(insns3[2], INVOKESTATIC, null, null, "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;")) {
translateInvoke = (MethodInsnNode)insns3[2];
}
AbstractInsnNode[] insns2 = fillBackwards(insn2, 3);
if(compareInsn(insns2[0], NEW)
&& compareInsn(insns2[1], DUP)) {
Expand All @@ -334,7 +409,7 @@ && compareInsn(insns2[1], DUP)) {
if(errScreen == null) {
return false;
}
if(patchErrorScreen(source, errScreen, openScreen) && instanceOf != null) {
if(patchErrorScreen(source, errScreen, openScreen, translateInvoke) && instanceOf != null) {
openScreen.instructions.set(instanceOf, new InsnNode(ICONST_0));
}
if(setWorld == null && cleanup == null) {
Expand Down Expand Up @@ -437,7 +512,7 @@ && compareInsn(insns[3], INVOKESPECIAL, minecraft.name, null, null)) {
return setWorld;
}

public boolean patchErrorScreen(ClassNodeSource source, ClassNode errScreen, MethodNode openScreen) {
public boolean patchErrorScreen(ClassNodeSource source, ClassNode errScreen, MethodNode openScreen, MethodInsnNode translate) {
boolean needCancelButton = true;
String[] fields = {"message", "description"};
MethodNode init = NodeHelper.getMethod(errScreen, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
Expand Down Expand Up @@ -543,7 +618,7 @@ && compareInsn(insns[3], CHECKCAST)) {
buttonClicked = m;
break;
}
ClassNode button = source.getClass(buttonType);
button = source.getClass(buttonType);
if(button != null) {
if(NodeHelper.getMethod(button, "<init>", "(IIILjava/lang/String;)V") == null) {
break cancelButton;
Expand Down Expand Up @@ -609,7 +684,14 @@ && compareInsn(insns[5], GETFIELD, null, null, "I")) {
list.add(intInsn(100));
list.add(new InsnNode(ISUB));
list.add(intInsn(140));
list.add(new LdcInsnNode("Cancel")); // TODO translate string?
if(translate != null) {
list.add(new LdcInsnNode("gui.cancel"));
list.add(new InsnNode(ICONST_0));
list.add(new InsnNode(ANEWARRAY));
list.add(new MethodInsnNode(INVOKESTATIC, translate.owner, translate.name, translate.desc));
} else {
list.add(new LdcInsnNode("Cancel"));
}
list.add(new MethodInsnNode(INVOKESPECIAL, buttonType, "<init>", "(IIILjava/lang/String;)V"));
list.add(new MethodInsnNode(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z"));
list.add(new InsnNode(POP));
Expand Down
Loading

0 comments on commit 2e1ccbb

Please sign in to comment.