javagradleminecraft-forgediscord-jda

Is there a way to use 2 versions of a library in the same JVM?


I'm trying to make a Minecraft Forge 1.16.5 mod that uses a library called JDA(Java Discord API). So I've made the mod and I put all the dependencies in the jar file, including JDA and its dependencies. But when I run the server, it says it can't find a class of a library JDA uses. This is the error I get:

[14okt2023 23:58:10.203] [Server thread/ERROR] [net.minecraft.server.MinecraftServer/]: Encountered an unexpected exception
java.lang.NoClassDefFoundError: gnu/trove/map/TLongObjectMap
    at net.dv8tion.jda.internal.utils.Checks.notBlank(Checks.java:98) ~[discord_statusbot:13.0]
    at net.dv8tion.jda.api.entities.Activity.playing(Activity.java:128) ~[discord_statusbot:13.0]
    at mopsy.productions.discord.statusbot.BotManger.regBot(BotManger.java:24) ~[discord_statusbot:13.0]
    at mopsy.productions.discord.statusbot.StatusbotMain.start(StatusbotMain.java:45) ~[discord_statusbot:13.0]
    at net.minecraftforge.eventbus.ASMEventHandler_0_StatusbotMain_start_FMLServerStartedEvent.invoke(.dynamic) ~[?:?]
    at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:85) ~[eventbus-4.0.0.jar:?]
    at net.minecraftforge.eventbus.EventBus.post(EventBus.java:302) ~[eventbus-4.0.0.jar:?]
    at net.minecraftforge.eventbus.EventBus.post(EventBus.java:283) ~[eventbus-4.0.0.jar:?]
    at net.minecraftforge.fml.server.ServerLifecycleHooks.handleServerStarted(ServerLifecycleHooks.java:106) ~[forge:?]
    at net.minecraft.server.MinecraftServer.func_240802_v_(MinecraftServer.java:622) ~[?:?]
    at net.minecraft.server.MinecraftServer.func_240783_a_(MinecraftServer.java:232) ~[?:?]
    at java.lang.Thread.run(Unknown Source) [?:1.8.0_311]
Caused by: java.lang.ClassNotFoundException: gnu.trove.map.TLongObjectMap
    at java.lang.ClassLoader.findClass(Unknown Source) ~[?:1.8.0_311]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[?:1.8.0_311]
    at cpw.mods.modlauncher.TransformingClassLoader.loadClass(TransformingClassLoader.java:94) ~[modlauncher-8.1.3.jar:?]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[?:1.8.0_311]
    ... 12 more

But that is strange, because both JDA and the libraries of JDA are present in the jar file (I've checked using a decompiler)

The only thing I could find is that in the server there is a folder called libraries which contains a much older version of the library. The only thing I can think of, is that Forge loads their libraries first and after that the new version of the library can't be loaded any more. I'm not certain how to solve this, is there anyone who has an idea? (I'm using Gradle to build my mod)

At first I tried using a feature of ForgeGradle called Jar-in-jar. But it couldn't even find JDA when I tried that. I read something about using another classloader, but I'm not sure how/if that would work.

It might be worth noticing that when I run the mod inside IntelliJ it doesn't throw the error.


Solution

  • I solved it by using the shadow plugin. After adding the shadow plugin to my project, I added the following code to my build.gradle file.

    shadowJar {
        relocate('gnu.trove', 'newpath.gnu.trove')
        //some more relocation of libraries conflicting with Forge.
        dependencies {
            include(dependency("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.3"))
            include(dependency("net.dv8tion:JDA:5.0.0-beta.13"))
            include(dependency("net.sf.trove4j:trove4j:3.0.3"))
            //Some more libraries that needed to be included.
        }
        classifier ''
    }
    shadowJar.finalizedBy('reobfShadowJar')
    reobf {
        shadowJar{}
    }
    

    The first part makes sure the conflicting library gets put into another package. Then with the dependencies part I include all library jars into the final jar. And finally the generated jar file needs to be re-obfuscated, because else it won't be able to work with the mappings of a Minecraft server/client.

    Eventually the re-obfuscated jar will be generated as projectdir/build/reobfShadowJar/output.jar