jMonkey and JLink

I’m playing around to package my game in modern way using JLink.

Googling about JMonkey and JLink shows some shallow evidences of this tandem.

But I’ve faced error "jme3-core-3.3.0-stable.jar does not have a module-info.java file. So it can't be linked." when tried to build my project with jlink (and apropriate maven plugin).

[INFO] --- maven-jlink-plugin:3.0.0-alpha-1:jlink (default) @ crawler2020 ---
[INFO] Toolchain in maven-jlink-plugin: jlink [ C:\apps\openjdk-11.28_x64\bin\jlink.exe ]
[INFO] The following dependencies will be linked into the runtime image:
[INFO]  -> module: com.fasterxml.jackson.databind ( C:\Users\kkolyan\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.10.3\jackson-databind-2.10.3.jar )
[INFO]  -> module: com.fasterxml.jackson.annotation ( C:\Users\kkolyan\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.10.3\jackson-annotations-2.10.3.jar )
[INFO]  -> module: com.fasterxml.jackson.core ( C:\Users\kkolyan\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.10.3\jackson-core-2.10.3.jar )
[INFO]  -> module: com.fasterxml.jackson.dataformat.yaml ( C:\Users\kkolyan\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-yaml\2.10.3\jackson-dataformat-yaml-2.10.3.jar )
[INFO]  -> module: org.yaml.snakeyaml ( C:\Users\kkolyan\.m2\repository\org\yaml\snakeyaml\1.24\snakeyaml-1.24.jar )
[INFO]  -> module: spring.context ( C:\Users\kkolyan\.m2\repository\org\springframework\spring-context\5.2.0.RELEASE\spring-context-5.2.0.RELEASE.jar )
[INFO]  -> module: spring.aop ( C:\Users\kkolyan\.m2\repository\org\springframework\spring-aop\5.2.0.RELEASE\spring-aop-5.2.0.RELEASE.jar )
[INFO]  -> module: spring.beans ( C:\Users\kkolyan\.m2\repository\org\springframework\spring-beans\5.2.0.RELEASE\spring-beans-5.2.0.RELEASE.jar )
[INFO]  -> module: spring.core ( C:\Users\kkolyan\.m2\repository\org\springframework\spring-core\5.2.0.RELEASE\spring-core-5.2.0.RELEASE.jar )
[INFO]  -> module: spring.jcl ( C:\Users\kkolyan\.m2\repository\org\springframework\spring-jcl\5.2.0.RELEASE\spring-jcl-5.2.0.RELEASE.jar )
[INFO]  -> module: spring.expression ( C:\Users\kkolyan\.m2\repository\org\springframework\spring-expression\5.2.0.RELEASE\spring-expression-5.2.0.RELEASE.jar )
[ERROR] The given dependency C:\Users\kkolyan\.m2\repository\org\jmonkeyengine\jme3-core\3.3.0-stable\jme3-core-3.3.0-stable.jar does not have a module-info.java file. So it can't be linked.

Please share any knowledge about it or at least hints.

JLink is only able to link Java modules into the static image, since the images must be self-contained and there is no way to know (from jlink’s perspective) what non-modular dependencies non-modular code has. So in short… until jME gets module-info files added, there is no way to jlink an image containing jME.

However, the good news is that a jlinked image can still load and run code from the classpath at runtime, so you jlink any modules you want into your base image and then distribute and run classpath dependencies as normal with that jlinked VM image.

1 Like

Hi. Actually, when I said about “jme+jlink evidences”, I meant exactly your messages :slight_smile: They are the only relevant googlable information.

So, looks like there are no any successful attempts to use this tandem. I read many articles describing how we can just generate automatic module-info and inject it to legacy jars and it works for many libs (Kotlin, Guava, Apache Commons). Is it possible for jME jars for your opinion?

Now I’m on the first stage, when I’m looking for some magic maven plugin that does everything for me. But looks like I need to dive deeper to have any results, because there are no out-of-the-box tools (actually moditect looks releavant, but I haven’t chance to try it yet).

1 Like

I do use jME + a jlinked runtime for the MyWorld client - I’m just not able to jlink everything together into a single image. The build is very simple, and with a trivial launcher script it’s quite clean and maintainable.

This is an area I’ve never done anything in, so this is just a guess… but usually it’s reflection that trips up automated tools (Graal native image, for example, can require some auxiliary information beyond the source code for reflection-heavy code), but so far as I know jME only uses reflection in native resource freeing, the networking engine, the serialization library, and maybe one or two other places that I’m not thinking of right now. Reflection is a tricky area with modularized code though, because of access to non-public class members across module boundaries. There are ways around it (opening a module to another), but they require a bit of legwork. Any such issues would come up at runtime for each person using the modularized serialization, but it may not be much of an issue in practice if the reflection is only touching public members. This is an area I haven’t delved into personally. TLDR, I think this stands a reasonable shot at working, but may require some manual intervention.

1 Like

How does it look like? MyWorld.exe (your code and JRE as one executable) and a couple of jme3-*.jar files in the same directory?

/MyWorldClientRoot
   MyWorld.sh <- MyWorld.bat on Windows
   vmArgs.txt <- consolidated, easy place to share all x-plat VM args
   MyWorld-Client.jar < - this could also go in ./lib/, as it is just another classpath dependency as far as the JVM is concerned
   lib/ <- all classpath dependencies go here
   runtime/ <- jlink output goes here
   ...other directories & files not related to running from jLinked image
2 Likes

Thanks for detailed listing. And how does it look as built (and distributable) artifact?

Same structure stored in a zip. You can easily build a native installer from this structure too, which I don’t currently do because the current JDK tools for that are still bleeding edge and gave me some trouble.

1 Like

I have been experimenting with using github to build distributions.

I was sidetracked into wiki stuff so didn’t get past building a minimal test but you can use this to get an idea what I was up to.

This does both modular and non modular builds using gradle.
https://badass-jlink-plugin.beryx.org/releases/latest/

Heres the workflow I wrote that compiled on all three platforms.

Here is an example that you can use to get an idea of how to implement if you need it.

3 Likes

Jpackage says an app can only be installed into

    On Linux, the default is /opt/application-name
    On macOS, the default is /Applications/application-name
    On Windows, the default is c:\Program Files\application-name; if the --win-per-user-install option is used, the default is C:\Users\user-name\AppData\Local\application-name

allowing you to only change the dir name inside those destinations.

Doesn’t this render jpackage useless for gamming because by restricting to those folders you cannot write to disk?

I can’t seem to find the trick that allows other programs to run updates to their apps from those folders.

I don’t know much about jpackage, but if you sell your game through stores like Steam or itch.io you don’t have to worry about permissions, let them take care of it for you.

This and you can still write on the users folders (Electron does exactly that)