Minecraftforge: Support for Kotlin's object instances

Created on 9 Feb 2019  路  11Comments  路  Source: MinecraftForge/MinecraftForge

Hey. In Kotlin, there are no static methods/fields, meaning you can not possibly create a class with field called INSTANCE and assign this to it in the constructor. Instead, they introduced a concept of objects. In short, these become regular java classes with private constructors and static INSTANCE field in it, so basically regular Java singletons. No, you can not declare your own public constructor for kotlin objects.

The problem

You need an instance of your mod sometimes (like when opening a GUI, or holding your itemGroup (creativeTab) in it), and the only option in Kotlin is an object, but Forge crashes when trying to load the mod. Log: https://gist.github.com/autaut03/1ee47ba4abbcb64b9f308a5fac33e108

Why it happens

This is where exception is thrown. It finds the class, tries to instantiate it, which triggers Kotlin's init block of my mod object, responsible for registering listeners in the event bus, but at the time of doing this activeContainer is still null. Init blocks for kotlin objects are basically static {} blocks from Java.

But even if init {} block is completely removed from my mod object, there's still a problem. During CONSTRUCT phase, constructMod method is called, which attempts to trigger mod class constructor. Since the only constructor is private, it fails.

Code problems

  1. Class.forName with second parameter set to true is not suitable for init blocks of Kotlin objects.
  2. class.newInstance() is not suitable for Kotlin objects' private constructors.

Possible solution

If Forge teams decides to support Kotlin, the following can be done:

  1. Move class resolving (problem #1) into a separate protected method
  2. Make FMLModTarget a separate class with public access
  3. Move string "net.minecraftforge.fml.javafmlmod.FMLModContainer" into a separate protected method inside of FMLModTarget
  4. Inherit FMLModContainer, overriding method that resolves the class
  5. Inherit FMLTarget, overriding method that returns the string given above
  6. In FMLJavaModLanguageProvider, create a factory method for FMLTarget
  7. Finally, inherit FMLJavaModLanguageProvider, overriding factory method for FMLTarget

In the end, we will have three additional classes with "Kotlin" prefixes, allowing for easy use of Kotlin in the modding community.

1.13

All 11 comments

The mod loading system was designed in such a way that the community can easily provide their own loaders for mods with source languages other than Java.

See Scorge for an example of this with Scala, which is in a situation similar to Kotlin's.

An officially endorsed loader for Kotlin would certainly be an option, but this would never become a core part of Forge.

I'll leave this here: https://github.com/shadowfacts/Forgelin

First of all, Forgelin only supports 1.12. It will require a full rewrite in order for it to work for 1.13.
Secondly, it requires a full kotlin library, which means developers can't choose kotlin version they want to work with. It also adds additional unneeded overhead for mod users.

The mod loading system was designed in such a way that the community can easily provide their own loaders for mods with source languages other than Java.

See Scorge for an example of this with Scala, which is in a situation similar to Kotlin's.

An officially endorsed loader for Kotlin would certainly be an option, but this would never become a core part of Forge.

Thanks for the link, but I still don't think that copy-pasting 300+ lines of code is a good solution. I'm cool with it being a separate mod, but I guess it would be a lot easier if the files above maintained a better structure, allowing for easier inheritance.

The system certainly has room for improvement, cpw relies on the feedback of implementors for other languages to make it workable.

Regarding your comment about Forgelin: Pinning a certain version of the library with a central mod has several advantages:

  1. Reduced file sizes for all mods that want to use Kotlin (admittedly, you add an extra dependency people will face to get, but in a world where the Twitch launcher exists, that's not really an issue)
  2. Mods depending on different versions of Kotlin may lead to incompatibilities and classloading issues, and not everybody is aware of the need for relocation of the library to a different package to avoid these conflicts
  3. Even if you relocate, you may experience issues due to the nature of some of the Kotlin libraries. In my own testing, I've experienced issues with the reflection library, which certainly would have been resolvable, but I'd rather not go through the hassle.

I also think that major language updates are rare enough that a library mod can easily keep up while modders get access to the features they want.

Hm, for some reason I thought Forge's class loader (presumably TransformingClassLoader from today's cpw.modlauncher) will deal with different dependency versions himself. However, after just testing it, it appears it doesn't work this way. In this case, Forgelin's way of doing this should be fine, as we don't have a choice.

I'll PR into Forgelin then, but doing first 3 things of my list would still be majorly appreciated, as it would help avoid lots of boilerplate code, which will 100% lead to major bugs when Forge decides to change any of that.

I am the maintainer of Forgelin.

Yes, for 1.13, Forgelin would require a rewrite. I would be happy to turn it into a Forge project (similar to Scorge) or have it superseded by a Forge project that provides the same functionality. But, if that doesn't happen, I will continue to maintain and update Forgelin as an independent project.

As for your complaints about Forgelin, PaleoCrafter addressed them well. Maybe the situation isn't ideal, but it's a reasonable compromise/balance given the constraints and goals of the project.

The situation is definitely not ideal, but that's the most we can do as of right now.

About rewrite: have you already looked at what can be done with new Forge regarding Kotlin support? Duplicating triggerMap and all of the relevant methods with almost no changes is kind of strange, but as of current Forge state that's the only option. Same goes for FMLJavaModLanguageProvider's getFileVisitor and FMLModTarget.loadMod. I'm asking your opinion as Forgelin maintainer, and would be happy to contribute to your project.

While waiting for Forgelin to be updated to 1.13, I created this project, it's not perfect especially the main class of your mod can't be an object.
(I'm French, sorry if my English is bad)

I might be missing something, but my mod works good if main class is not an object, only with minor tweaks to build.gradle, done automatically by IDEA.

This is a feature that would be covered by a kotlin language plugin, not forge.

Was this page helpful?
0 / 5 - 0 ratings