Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Forge for 1.17.1 does not include Kotlin Standard Library at runtime


MartinTheDragon
 Share

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
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.

 Share



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Are you using Fabric? I'm not sure if this forum would be the right place then.
    • server failed to start and gives me "Failed to load data packs, can't proceed with server load. You can either fix your data packs or reset to vanilla with --safeMode" error when i look in my logs. its updated to the latest version of forge(40.1.0), and I don't have any data packs installed as far as I know. And I will upload the full logs if asked as i dont know how to attach a document to this post.  
    • Pioneer Craft [modded] {1.18.2} {SMP} {Community} {Light Roleplay} We are Pioneer Craft and we seek you !!! We are building a community of passionate players to build, trade, and share some laughs with. Are you tired of playing alone? Or are you here seeking a server with dedicated members; a server where you are not overlooked and can voice your opinions? If you are interested in these things too, then come join us in Pioneer Craft! Our server is player built and driven. We have a custom mod pack with mods chosen by our community to suit our needs. We have designed our server for player driven roleplay! There will be four communities which will each be self-sufficient. We encourage and seek builders who want to create a beautiful world to live in. This is a whitelist server and we are looking for dedicated applicants and experienced builders. Applications are located on our website which can be accessed through the discord. Other than the application questions, we are requesting photos of your prior builds which will be taken into consideration. Please show photos of your builds in the share-your-work section of the discord. The server uses CurseForge to run. This server is for mature members (16+) . Discord (must join): https://discord.gg/c4uJvb29CN
    • Pioneer Craft [modded] {1.18.2} {SMP} {Community} {Light Roleplay} We are Pioneer Craft and we seek you !!! We are building a community of passionate players to build, trade, and share some laughs with. Are you tired of playing alone? Or are you here seeking a server with dedicated members; a server where you are not overlooked and can voice your opinions? If you are interested in these things too, then come join us in Pioneer Craft! Our server is player built and driven. We have a custom mod pack with mods chosen by our community to suit our needs. We have designed our server for player driven roleplay! There will be four communities which will each be self-sufficient. We encourage and seek builders who want to create a beautiful world to live in. This is a whitelist server and we are looking for dedicated applicants and experienced builders. Applications are located on our website which can be accessed through the discord. Other than the application questions, we are requesting photos of your prior builds which will be taken into consideration. Please show photos of your builds in the share-your-work section of the discord. The server uses CurseForge to run. This server is for mature members (16+) . Discord (must join): https://discord.gg/c4uJvb29CN 
    • Update: I didn't need capabilities. In fact, I didn't need to override applyEffectTick at all. But it seems like I need reflections. The increased hitbox size now works for both entity and block collision--with a little drawback. This is what it looks like so far: // this event handler checks every tick if the living entity has the "big" effect @SubscribeEvent public static void entitySizeChange(LivingEvent.LivingUpdateEvent event) { LivingEntity livingEntity = event.getEntityLiving(); if (livingEntity.getActiveEffectsMap() != null && livingEntity.hasEffect(ModEffects.BIG.get())) { EntityDimensions newDims = livingEntity.getDimensions(livingEntity.getPose()).scale(8.0F, 2.0F); try { // using reflection Field field = Entity.class.getDeclaredField("dimensions"); field.setAccessible(true); field.set(livingEntity, newDims); // setting the living entity's EntityDimensions EntityDimensions newEntityDimensions = (EntityDimensions) field.get(livingEntity); livingEntity.setBoundingBox(newEntityDimensions.makeBoundingBox( // setting the living entity's AABB livingEntity.getX(), livingEntity.getY(), livingEntity.getZ() )); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } }   The aforementioned drawback is that, when the effect wears off, the living entity's still keeps its bigger hitbox until it changes pose (i.e. my player crouching). Given that the EntityEvent.Size event fires whenever an entity changes pose, should I manually fire it with EVENT_BUS::post when that entity's effect wears off? If so, where? I tried manually firing EntityEvent.Size on my overridden removeAttributeModifiers(), but to no avail. 
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.