FabricMC / fabric-loader

Fabric's mostly-version-independent mod loader.
Apache License 2.0
612 stars 257 forks source link

AppletLoader should not assume that all "applets" are true Applets #367

Open PhoenixVX opened 3 years ago

PhoenixVX commented 3 years ago

This is something that most people will not notice, but in the case of old versions such as rd-132211, the Applet is not a "true applet," but instead a Runnable like so.

public class MinecraftApplet implements Runnable {...}

AppletLoader assumes that the object that is passed in is an Applet and will fail because of invalid cast. Such as the error below:

[19:53:26] [main/INFO] (Fabric|Loader) [FabricLoader] Loading 4 mods: minecraft@132211, java@15, fabricloader@0.11.0, modid@1.0.0
[19:53:26] [main/INFO] (mixin) SpongePowered MIXIN Subsystem Version=0.8.2 Source=file:/C:/Users/Zer0/.gradle/caches/modules-2/files-2.1/net.fabricmc/sponge-mixin/0.8.2+build.24/e5a741445d0f425bf3cce946c6cf8c7fb8376b2/sponge-mixin-0.8.2+build.24.jar Service=Knot/Fabric Env=CLIENT
[19:53:26] [main/INFO] (Fabric|MixinBootstrap) Loaded Fabric development mappings for mixin remapper!
Hello Fabric world!
Exception in thread "main" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at net.fabricmc.loader.game.MinecraftGameProvider.launch(MinecraftGameProvider.java:228)
    at net.fabricmc.loader.launch.knot.Knot.init(Knot.java:139)
    at net.fabricmc.loader.launch.knot.KnotClient.main(KnotClient.java:27)
    at net.fabricmc.devlaunchinjector.Main.main(Main.java:86)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at net.fabricmc.loader.game.MinecraftGameProvider.launch(MinecraftGameProvider.java:226)
    ... 3 more
Caused by: java.lang.ClassCastException: class com.mojang.minecraft.MinecraftApplet cannot be cast to class java.applet.Applet (com.mojang.minecraft.MinecraftApplet is in unnamed module of loader net.fabricmc.loader.launch.knot.KnotClassLoader @46cdf8bd; java.applet.Applet is in module java.desktop of loader 'bootstrap')
    at net.fabricmc.loader.entrypoint.applet.AppletLauncher.<init>(AppletLauncher.java:64)
    at net.fabricmc.loader.entrypoint.applet.AppletFrame.launch(AppletFrame.java:91)
    at net.fabricmc.loader.entrypoint.applet.AppletMain.main(AppletMain.java:37)
    ... 8 more

The lucky part about this is that the onInitialize method has a chance to call the Applet as a Runnable like so:

// This is a workaround to let loader load the class.
try {
    MinecraftApplet clazz = (MinecraftApplet) FabricLauncherBase.getLauncher().getTargetClassLoader().loadClass(EntrypointTransformer.appletMainClass).getDeclaredConstructor().newInstance();
    clazz.run();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
    e.printStackTrace();
}

Honestly, should not assume that it's a "true applet" or have a fallback to a generic Runnable runner.

PhoenixVX commented 3 years ago

The problem with the lucky part above is that the "Applet" is called again after each closing and requires external intervention to stop the program. And it's hacky, so I don't like it.