Third-party java libraries when creating minecraft mods

136 Views Asked by At

I am creating a simple mod for minecraft version 1.19.2. I downloaded forge 1.19.2-43.0 and decided to add a dependency of a third-party java library. or rather, the twitch4j library (https://github.com/twitch4j )

the main idea of my mod is to output messages from the live chat of my twitch channel to the minecraft chat.

I have one class in the project:

package net.alerto.minetwitch;

import com.github.philippheuer.credentialmanager.domain.OAuth2Credential;
import com.github.twitch4j.TwitchClient;
import com.github.twitch4j.TwitchClientBuilder;
import com.github.twitch4j.chat.events.channel.ChannelMessageEvent;
import com.mojang.logging.LogUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.slf4j.Logger;

@Mod(MineTwitch.MOD_ID)
public class MineTwitch {

    public static final String MOD_ID = "minetwitch";

    private static final Logger LOGGER = LogUtils.getLogger();

    public MineTwitch() {
        TwitchClient twitchClient = TwitchClientBuilder.builder()
                .withChatAccount(new OAuth2Credential("twitch", "YOUR_TWITCH_OAUTH_TOKEN"))
                .withEnableChat(true)
                .build();

        twitchClient.getEventManager().onEvent(ChannelMessageEvent.class, this::handleChannelMessage);


        IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
        modEventBus.addListener(this::commonSetup);
        MinecraftForge.EVENT_BUS.register(this);
    }

    private void commonSetup(final FMLCommonSetupEvent event) {}

    private void handleChannelMessage(ChannelMessageEvent event) {
        String username = event.getUser().getName();
        String message = event.getMessage();
        
        Minecraft.getInstance().player.displayClientMessage(Component.literal("[" + username + "]: " + message), true);
    }
}

and here's the build.gradle:

plugins {
    id 'eclipse'
    id 'idea'
    id 'maven-publish'
    id 'net.minecraftforge.gradle' version '[6.0,6.2)'
    id 'com.github.johnrengelman.shadow' version '8.1.1'
}

version = '0.1-1.19.2'
group = 'net.alerto.minetwitch'

base {
    archivesName = 'minetwitch'
}

java.toolchain.languageVersion = JavaLanguageVersion.of(17)

println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}"
minecraft {
    mappings channel: mapping_channel, version: mapping_version

    copyIdeResources = true

    runs {
        configureEach {
            workingDirectory project.file('run')

            property 'forge.logging.markers', 'REGISTRIES'

            property 'forge.logging.console.level', 'debug'

            property 'forge.enabledGameTestNamespaces', 'minetwitch'

            mods {
                "${'minetwitch'}" {
                    source sourceSets.main
                }
            }
        }

        client {
            property 'forge.enabledGameTestNamespaces', 'minetwitch'
        }

        server {
            property 'forge.enabledGameTestNamespaces', 'minetwitch'
            args '--nogui'
        }

        gameTestServer {
            property 'forge.enabledGameTestNamespaces', 'minetwitch'
        }

        data {
            workingDirectory project.file('run-data')

            args '--mod', 'minetwitch', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
        }
    }
}

sourceSets.main.resources { srcDir 'src/generated/resources' }

repositories {
    mavenCentral()
}

dependencies {
    minecraft 'net.minecraftforge:forge:1.19.2-43.3.0'
    implementation 'com.github.twitch4j:twitch4j:1.19.0'
    implementation 'com.github.philippheuer.events4j:events4j-api:0.12.1'
    implementation 'com.github.philippheuer.events4j:events4j-core:0.12.1'
}

tasks.named('processResources', ProcessResources).configure {
    var replaceProperties = [
            minecraft_version: minecraft_version, minecraft_version_range: minecraft_version_range,
            forge_version: forge_version, forge_version_range: forge_version_range,
            loader_version_range: loader_version_range,
            'minetwitch': 'minetwitch', mod_name: mod_name, mod_license: mod_license, mod_version: mod_version,
            mod_authors: mod_authors, mod_description: mod_description,
    ]
    inputs.properties replaceProperties

    filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) {
        expand replaceProperties + [project: project]
    }
}

tasks.named('jar', Jar).configure {
    manifest {
        attributes([
                'Specification-Title'     : 'minetwitch',
                'Specification-Vendor'    : mod_authors,
                'Specification-Version'   : '1',
                'Implementation-Title'    : project.name,
                'Implementation-Version'  : project.jar.archiveVersion,
                'Implementation-Vendor'   : mod_authors,
                'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
        ])
    }
}

publishing {
    publications {
        register('mavenJava', MavenPublication) {
            artifact jar
        }
    }
    repositories {
        mavenCentral()
        maven {
            url "file://${project.projectDir}/mcmodsrepo"
        }
        maven { url = 'https://jitpack.io' }
    }
}

tasks.withType(JavaCompile).configureEach {
    options.encoding = 'UTF-8'
}

tasks.jar {
    doFirst {
        zip64 true
    }
}

jar {
    from {
        duplicatesStrategy DuplicatesStrategy.INCLUDE
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

I'm using:

intellij idea 2021.3

java 17

twitch4j 1.19.0

forge 1.19.2-43.0

everything works in intellij, all dependencies are lifted and I can write the code calmly, there are no errors, I also build in gradle without errors. but when I run the run Client or copy the compiled jar to the mods folder, minecraft starts with the mod error, here are the logs:

---- Minecraft Crash Report ----
// Surprise! Haha. Well, this is awkward.

Time: 2024-01-27 21:32:20
Description: Mod loading error has occurred

java.lang.Exception: Mod Loading has failed
    at net.minecraftforge.logging.CrashReportExtender.dumpModLoadingCrashReport(CrashReportExtender.java:55) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23187%23194!/:?] {re:classloading}
    at net.minecraftforge.client.loading.ClientModLoader.completeModLoading(ClientModLoader.java:167) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23187%23194!/:?] {re:classloading,pl:runtimedistcleaner:A}
    at net.minecraft.client.Minecraft.lambda$new$2(Minecraft.java:585) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}
    at net.minecraft.Util.ifElse(Util.java:438) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading}
    at net.minecraft.client.Minecraft.lambda$new$3(Minecraft.java:579) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}
    at net.minecraft.client.gui.screens.LoadingOverlay.render(LoadingOverlay.java:135) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading,pl:runtimedistcleaner:A}
    at net.minecraft.client.renderer.GameRenderer.render(GameRenderer.java:885) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}
    at net.minecraft.client.Minecraft.runTick(Minecraft.java:1115) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}
    at net.minecraft.client.Minecraft.run(Minecraft.java:700) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}
    at net.minecraft.client.main.Main.run(Main.java:212) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading,pl:runtimedistcleaner:A}
    at net.minecraft.client.main.Main.main(Main.java:51) ~[forge-1.19.2-43.3.0_mapped_official_1.19.2-recomp.jar%23188!/:?] {re:classloading,pl:runtimedistcleaner:A}
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?] {}
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?] {}
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?] {}
    at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?] {}
    at net.minecraftforge.fml.loading.targets.ForgeClientUserdevLaunchHandler.lambda$launchService$0(ForgeClientUserdevLaunchHandler.java:25) ~[fmlloader-1.19.2-43.3.0.jar%23101!/:?] {}
    at cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:30) [modlauncher-10.0.9.jar%23114!/:?] {}
    at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:53) [modlauncher-10.0.9.jar%23114!/:?] {}
    at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:71) [modlauncher-10.0.9.jar%23114!/:?] {}
    at cpw.mods.modlauncher.Launcher.run(Launcher.java:108) [modlauncher-10.0.9.jar%23114!/:?] {}
    at cpw.mods.modlauncher.Launcher.main(Launcher.java:78) [modlauncher-10.0.9.jar%23114!/:?] {}
    at cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26) [modlauncher-10.0.9.jar%23114!/:?] {}
    at cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23) [modlauncher-10.0.9.jar%23114!/:?] {}
    at cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141) [bootstraplauncher-1.1.2.jar:?] {}


A detailed walkthrough of the error, its code path and all known details is as follows:
---------------------------------------------------------------------------------------

-- Head --
Thread: Render thread
Stacktrace:
    at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[?:?] {}
-- MOD minetwitch --
Details:
    Caused by 0: java.lang.reflect.InvocationTargetException
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?] {}
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?] {}
        at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?] {}
        at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?] {}
        at java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[?:?] {}
        at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:68) ~[javafmllanguage-1.19.2-43.3.0.jar%23189!/:?] {}
        at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$10(ModContainer.java:121) ~[fmlcore-1.19.2-43.3.0.jar%23192!/:?] {}
        at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?] {}
        at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) ~[?:?] {}
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {}
        at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {}
        at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {}
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {}
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {}

    Caused by 1: java.lang.NoClassDefFoundError: com/github/twitch4j/TwitchClientBuilder
        at net.alerto.minetwitch.MineTwitch.<init>(MineTwitch.java:28) ~[%23193!/:?] {re:classloading}
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?] {}
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?] {}
        at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?] {}
        at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?] {}
        at java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[?:?] {}
        at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:68) ~[javafmllanguage-1.19.2-43.3.0.jar%23189!/:?] {}
        at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$10(ModContainer.java:121) ~[fmlcore-1.19.2-43.3.0.jar%23192!/:?] {}
        at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?] {}
        at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) ~[?:?] {}
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {}
        at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {}
        at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {}
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {}
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {}

    Mod File: /E:/Java projects/Orther/Mine_Twitch/build/resources/main/
    Failure message: Example Mod (minetwitch) has failed to load correctly
        java.lang.reflect.InvocationTargetException: null
    Mod Version: 0.1-1.19.2
    Mod Issue URL: NOT PROVIDED
    Exception message: java.lang.ClassNotFoundException: com.github.twitch4j.TwitchClientBuilder
Stacktrace:
    at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[?:?] {}
    at java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[?:?] {}
    at cpw.mods.cl.ModuleClassLoader.loadClass(ModuleClassLoader.java:137) ~[securejarhandler-2.1.4.jar:?] {}
    at java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[?:?] {}
    at cpw.mods.cl.ModuleClassLoader.loadClass(ModuleClassLoader.java:137) ~[securejarhandler-2.1.4.jar:?] {}
    at java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[?:?] {}
    at net.alerto.minetwitch.MineTwitch.<init>(MineTwitch.java:28) ~[%23193!/:?] {re:classloading}
    at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?] {}
    at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?] {}
    at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?] {}
    at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?] {}
    at java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[?:?] {}
    at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:68) ~[javafmllanguage-1.19.2-43.3.0.jar%23189!/:?] {}
    at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$10(ModContainer.java:121) ~[fmlcore-1.19.2-43.3.0.jar%23192!/:?] {}
    at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?] {}
    at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) ~[?:?] {}
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {}
    at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {}
    at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {}
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {}
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {}


What could be the problem? It is the TwitchClientBuilder class that does not find it. Which I have already done. I have added a plugin for building with johnrengelman shadow libraries. what can be done if the jar is created correctly and with all libraries, but minecraft itself does not see it.

1

There are 1 best solutions below

0
GuiTaek On

In the following the solution, that worked for me. Keep in mind, I developed in fabric so it might not work in forge. Because of fabric api, I can't use the shadow library (essentially the api is included twice), but maybe in your case, this is enough and the error is somewhere else. I assume that minecraft is included twice for forge with shadow, though.

In the build.gradle-file, in add to the configurations-block:

includedJars

Which gives the following configurations block if not already present:

configurations {
    includedJars
}

For every dependency you need (in your case: com.github.twitch4j:twitch4j:1.19.0), add

includedJars <dependency>

to the dependency block (that always exists in a minecraft mod), so in your case:

dependency {
    <other dependencies>
    includedJars 'com.github.twitch4j:twitch4j:1.19.0'
}

To the jar-Block add the following and create the jar block if not already present:

from configurations.includedJars.collect {
    it.isDirectory() ? it : zipTree(it)
}

giving the following jar:

jar {
    <rest from jar>
    from configurations.includedJars.collect {
        it.isDirectory() ? it : zipTree(it)
    }
}

For further information on that question, type in "gradle fat jar" in google.

edit:

you can change every occurance of includedJars with any name you want (excluding keywords of course)

edit2:

if it is difficult to follow, what I said, maybe you find this article from baeldung better: https://www.baeldung.com/gradle-fat-jar