Jump to content

Forge for 1.17.1 does not include Kotlin Standard Library at runtime


MartinTheDragon

Recommended Posts

The issue

Updating from 1.16.5 to 1.17.1, a problem with the availability of the Kotlin stdlib at runtime emerged. Up until now, the following build script setup worked without issue (same thing, just different versions specified):

Getting the Kotlin Gradle plugin and applying it

buildscript {
    repositories {
        maven { url = 'https://maven.minecraftforge.net' }
        mavenCentral()
    }
    dependencies {
        classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // Kotlin version: 1.6.0
    }
}

apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'kotlin'

// Some other things

java.toolchain.languageVersion = JavaLanguageVersion.of(16) // This is new

Including the Standard Library as a dependency (actually unnecessary)

dependencies {
    minecraft "net.minecraftforge:forge:1.17.1-37.0.126" // Used to call for a Forge version for 1.16.5
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // This being jdk8 is not an issue

    // JEI would be here too
}

Packaging the Standard Library into the jar

jar {
    dependsOn(classes)
    duplicatesStrategy(DuplicatesStrategy.INCLUDE)
  
    // Manifest here

    configurations {
        kotlinstdlib
    }
    dependencies {
        kotlinstdlib "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    }
    from {
        configurations.kotlinstdlib.collect() { it.isDirectory() ? it : zipTree(it) }
    }
}

Setting the Kotlin JVM target version to 16 (used to be 1.8)

def kotlinCompilerArgs = ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]

compileKotlin {
    kotlinOptions {
        jvmTarget = "16"
        freeCompilerArgs += kotlinCompilerArgs
    }
}
compileTestKotlin {
    kotlinOptions {
        jvmTarget = "16"
        freeCompilerArgs += kotlinCompilerArgs
    }
}

This still compiles the project correctly, running it however throws the following exception as soon as the mod annotated class gets loaded (when a Logger instance is requested to be more specific, probably does a null check under the hood - because Kotlin):

java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics

This class is part of the kotlin-stdlib-1.6.0.jar file. (Not kotlin-stdlib-jdk8-1.6.0.jar)
It shows up under "External Libraries", but appears to be missing at runtime for the workspace. Running 'build' outputs a jar that gets loaded correctly though, so the problem only appears when running in IDE using 'runClient'.

JEI does seem to get loaded though, only the Kotlin Standard Library does not.

Versions

Gradle - 7.2
ForgeGradle - 5.1.X
Java for building: 11 (1.8 when testing on a different computer)
Java for compiling and running: 16
Kotlin: 1.6.0
Minecraft: 1.17.1
Forge: 37.0.126 (Currently latest)

IDE is IntelliJ

What I already tried

  • Using kotlin-stdlib instead of kotlin-stdlib-jdk8
  • Setting the Java version to 16 for the 1.16.5 version of the project (worked, but not the goal)
  • Using a different PC (different caches, same output)
  • Running the 'clean' task
  • Using 'api, 'compileOnly' and 'runtimeOnly' in the dependencies block instead of 'implementation'.
  • Applying the Kotlin plugin using the plugins block
  • Creating a raw Kotlin project with JVM 16 target (not Forge; worked)
  • Creating a raw Forge mod project using the Forge MDK, adding the Kotlin plugin to it, and converting the ExampleMod class to Kotlin (failed with same output)
  • Removing the implementation line for the Standard Library in both the 1.16.5 version and the 1.17.1 (the 1.17.1 version failed, the 1.16.5 version ran successfully, which means that this line does not have any effect)
  • This abomination
    compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    runtimeOnly fg.deobf("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version")
    runtimeOnly fg.deobf("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
    runtimeOnly fg.deobf("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version")
    runtimeOnly fg.deobf("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
  • Combinations of the things mentioned above.

My guess

As this problem only appears with a difference in the build script of the mappings and Forge version, the only thing I guess would be left is Forge having an issue itself. I do not know how Forge loads mods and libraries in the development environment, I have checked what System.getenv("CLASSPATH") outputs using a breakpoint, and it returned null. It is remapping and including JEI correctly though, so it confuses me why the Kotlin Standard Library would not be present. This makes me a bit unsure whether it is Forge's fault, but again, the only real change I did for the build script was changing the mappings and Forge version (and adding the Java 16 line, but I have already checked doing that for 1.16.5). It is also not an incompatibility between Java versions because building a mod jar and running Minecraft Forge 1.17.1 with it normally (outside of IDE) works.

Are there any workarounds/fixes for this? Of course, any help much appreciated!

Link to comment
Share on other sites

By looking at the log output for command 'gradlew --debug runData', it appears that the Standard Library jars get passed to Forge via the '-cp' command line parameter correctly in both versions. Trying to create a workaround earlier revealed that the module resolver is aware of the Standard Library's exports, and therefore conflicts with any attempt to manually copy stdlib over to 'build/classes/kotlin/main'. This means I cannot create a workaround that way. I have also switched to 1.18, and this issue still persists. This is most likely an issue regarding JVM options, which I do not have enough knowledge about.

The build.gradle snippets shown above (remember switching jvmTarget in kotlinOptions to 17 when building for 1.18), alongside with the following piece of code for a mod class, should be enough to reproduce:

File: src/main/kotlin/com/example/examplemod/ExampleMod.kt

package com.example.examplemod

import net.minecraftforge.fml.common.Mod
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger

@Mod("examplemod")
class ExampleMod {
    init {
        LOGGER.info("Hello World!")
    }

    companion object {
        private val LOGGER: Logger = LogManager.getLogger()
    }
}

Note that removing the explicit type Logger (so it becomes 'private val LOGGER = LogManager.getLogger()') loads successfully, because when there is no explicit type specified, Koltin implicitly infers a so-called platform type, shown in IDE as 'Logger!' (note the exclamation mark). This platform type does not get null checked by default, so there are no references to 'kotlin.jvm.internal.Intrinsics' for null checks. Specifying the type explicitly as not null however creates a null check under the hood, because in Kotlin, for a type to be nullable, it has to be suffixed with a question mark (Logger?). Otherwise it is guaranteed to be not null.

Explanation for those not knowing Kotlin:
The 'init' block is essentially a constructor block. The Java equivalent would be 'public ExampleMod() { LOGGER.info("Hello World!"); }'. Declarations inside 'companion object' basically behave like static declarations, so the Java equivalent would be 'private static Logger LOGGER = LogManager.getLogger();'.

Looking forward to a fix! Thank you for your time.

Edited by MartinTheDragon
Clarification
Link to comment
Share on other sites

Further testing revealed that Forge will not successfully load any library in the IDE at all, if the library is not a mod. This means this is either a Forge bug, or the way to include a library at runtime has been changed.

Tested with a random library for Java not included in Minecraft or Forge:

build.gradle

dependencies {
    minecraft 'net.minecraftforge:forge:1.18-38.0.8'
    implementation 'com.github.ben-manes.caffeine:caffeine:3.0.4'
}

ExampleMod.java

package com.example.examplemod;

import com.github.benmanes.caffeine.cache.Caffeine;
import net.minecraftforge.fml.common.Mod;

@Mod("examplemod")
public class ExampleMod {
    public ExampleMod() {
        Caffeine<Object, Object> graphs = Caffeine.newBuilder();
    }
}

Will result in

java.lang.NoClassDefFoundError: com/github/benmanes/caffeine/cache/Caffeine

Should I file a bug report?

Link to comment
Share on other sites

  • 1 month later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.