V3.5.0 beta testing

jMonkeyEngine version 3.5.0-beta4 is available from Maven Central and ready for testing.

Before using the new version, please read the release notes.


To upgrade existing projects from an earlier release…

  1. Replace all JME version strings in the build scripts with “3.5.0-beta4”. For instance:
ext.jme3Version = '3.5.0-beta4'
implementation 'org.jmonkeyengine:jme3-core:3.5.0-beta4'
  1. If your build scripts specify Java 7 target compatibility, change to Java 8 or higher. For instance:
targetCompatibility = JavaVersion.VERSION_1_8
  1. If built using Ant, download the latest JARs from https://repo1.maven.org/maven2/org/jmonkeyengine/.

Updated javadoc is available for browsing at https://javadoc.jmonkeyengine.org/v3.5.0-beta4/.


Most of the tests in jme3-examples can be run using the “jME3.5.0-beta4.zip” asset from GitHub:

$ unzip jME3.5.0-beta4.zip 
$ java -jar jMonkeyEngine3.jar

However, that only tests LWJGL v2. To test LWJGL v3 (or run some of the more sophisticated example apps), clone the repo and build the Engine from source:

$ git clone https://github.com/jmonkeyengine/jmonkeyengine.git
$ cd jmonkeyengine
$ git checkout -b latest v3.5.0-beta4
$ ./gradlew run

To switch between LWJGL v2 and v3, edit lines 22-23 of the file “jme3-examples/build.gradle” and rebuild.


Any issues or difficulties with v3.5.0-beta4 may be discussed here. If you believe you’ve discovered a new bug, you may also open an issue at GitHub.

6 Likes

One thing I find annoying about v3.5: ALSOFT warning messages during app startup, like these:

[ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1)
[ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1)

I don’t think they indicate a serious problem, but short of reverting to LWJGL v3.2.3, I don’t see how to disable them.

1 Like

Nuisance Warning Line: `[ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1)` · Issue #554 · kcat/openal-soft · GitHub. Probably ok in the next LWJGL release. Minor nuisance.

OpenKeeper at least works fine with jME 3.5 beta LWJGL 3. Maybe a tad faster. So no serious regressions :slight_smile:

2 Likes

I think I’ve found a bug in 3.5 beta. A call to SimpleApplication.stop(boolean waitFor) with waitFor=true causes the application to freeze. This doesn’t happen on 3.4.1-stable, where the app stops as expected.

The following code shows the issue:

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;

public class StopAppTest extends SimpleApplication {

    private float time = 0.0f;

    public static void main(String... args) {
        final Application app = new StopAppTest();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        final DirectionalLight light = new DirectionalLight(new Vector3f(-.5f, -.5f, -.5f), ColorRGBA.White);
        rootNode.addLight(light);

        final Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        mat.setColor("Diffuse", ColorRGBA.Blue);
        mat.setBoolean("UseMaterialColors", true);

        final Spatial cube = new Geometry("Cube", new Box(1, 1, 1));
        cube.setMaterial(mat);

        rootNode.attachChild(cube);
    }

    @Override
    public void simpleUpdate(float tpf) {
        rootNode.getChild("Cube").rotate(tpf, tpf, tpf);

        time += tpf;
        System.out.println(time);

        // stops after 10 seconds
        if (time >= 10) {
            this.stop(true); // THIS LINE HANGS THE APP
        }
    }
}

Edit: running on openSUSE Tumbleweed and openjdk 17.0.1.

2 Likes

@fba can you open up an issue on the JME Github page?

Also, are you using LWJGL3? If so, maybe this can be related to

Edit:

I could reproduce this issue as well in Linux Mint with LWJGL3.

Edit2:

I noticed in the above PR (submitted by @kevinba99 ?) the newly created thread is never set to mainThread field at the below line, so mainThread field value will be null.

thus will be evaluated to null here:

Setting the mainThread at line 513 fixed it for me.

2 Likes

Sure, issue 1721 opened to keep track of it. Yes, I was using LWJGL3 as you could confirm on your tests. Thank you for taking a look this issue!

2 Likes

Thanks for opening the issue, I’m going to submit a patch soon.

1 Like

Ok, submitted the PR:

2 Likes

When I update my projects to 3.5.0-beta4, they fail to locate the native LWJGL(3) libraries during startup.
I tried to run them from the IDE (IntelliJ) on both, Windows 10 and Linux Kubuntu,
AdoptOpenJDK 11 and GraalVM 17.

Dec 28, 2021 4:37:32 PM com.jme3.system.JmeDesktopSystem initialize
INFO: Running on jMonkeyEngine 3.5.0-beta4
 * Branch: HEAD
 * Git Hash: b10d6d4
 * Build Date: 2021-12-17
[LWJGL] Version: 3.3.0 build 21
[LWJGL] 	 OS: Linux v5.11.0-41-generic
[LWJGL] 	JRE: Linux amd64 17.0.1
[LWJGL] 	JVM: OpenJDK 64-Bit Server VM v17.0.1+12-jvmci-21.3-b05 by GraalVM Community
[LWJGL] Loading JNI library: lwjgl
[LWJGL] 	Module: org.lwjgl
[LWJGL] 	linux/x64/org/lwjgl/liblwjgl.so not found in java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib
[LWJGL] 	liblwjgl.so not found in java.library.path
[LWJGL] Failed to load a library. Possible solutions:
	a) Add the directory that contains the shared library to -Djava.library.path or -Dorg.lwjgl.librarypath.
	b) Add the JAR that contains the shared library to the classpath.
Dec 28, 2021 4:37:32 PM com.jme3.app.LegacyApplication handleError
SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.UnsatisfiedLinkError: Failed to locate library: liblwjgl.so
	at org.lwjgl.system.Library.loadSystem(Library.java:164)
	at org.lwjgl.system.Library.loadSystem(Library.java:63)
	at org.lwjgl.system.Library.<clinit>(Library.java:51)
	at org.lwjgl.system.MemoryUtil.<clinit>(MemoryUtil.java:100)
	at org.lwjgl.system.Pointer$Default.<clinit>(Pointer.java:67)
	at org.lwjgl.system.Callback.<clinit>(Callback.java:40)
	at com.jme3.system.lwjgl.LwjglWindow.createContext(LwjglWindow.java:191)
	at com.jme3.system.lwjgl.LwjglWindow.initInThread(LwjglWindow.java:524)
	at com.jme3.system.lwjgl.LwjglWindow.run(LwjglWindow.java:659)
	at java.base/java.lang.Thread.run(Thread.java:833)


Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "jME3 Main"

I’m using Gradle and the build scripts are all similar to this one:

After the update, I couldn’t find any lwjgl-xxxx-3.3.0-natives-xxxx.jar files inside ~/.gradle.
These do exist for previous LWJGL versions (3.2.3).
The 3.3.0 files are downloaded when I add the dependencies manually, e.g.:

runtimeOnly "org.lwjgl:lwjgl:3.3.0:natives-linux"
runtimeOnly "org.lwjgl:lwjgl-glfw:3.3.0:natives-linux"
runtimeOnly "org.lwjgl:lwjgl-opengl:3.3.0:natives-linux"
runtimeOnly "org.lwjgl:lwjgl-openal:3.3.0:natives-linux"
runtimeOnly "org.lwjgl:lwjgl-jemalloc:3.3.0:natives-linux"

I see the actual *.so files are extracted to some directory in /tmp.
This works for Linux and Windows and the program runs without issues so far.

Updating to previous versions was a breeze and only included changing the jme-version string defined in the Gradle script. But since the error seems obvious and you don’t seem to encounter the same, I assume the issue lies in my Gradle script or maybe the Gradle version (6.1).
Any hints?

1 Like

Thanks for the trouble report.

When I run applications containing “jme3-lwjgl3-3.5.0-beta4.jar” on 64-bit OpenJDK version 11 on Linux, 5 native libraries get written to the “/tmp/lwjglsgold/3.3.0-build-21” directory, and that same directory gets added to the “org.lwjgl.librarypath” system property. I’m unsure why your apps are behaving differently.

The extracted files have the following names and properties:

sgold:~/Git/BasicGame-on-Gradle$ ls -l /tmp/lwjglsgold/*
total 2436
-rw-rw-r-- 1 sgold sgold 373744 Dec 28 08:07 libglfw.so
-rw-rw-r-- 1 sgold sgold 371600 Dec 28 08:07 libjemalloc.so
-rw-rw-r-- 1 sgold sgold 354072 Dec 28 08:07 liblwjgl_opengl.so
-rw-rw-r-- 1 sgold sgold 391728 Dec 28 08:07 liblwjgl.so
-rw-rw-r-- 1 sgold sgold 991320 Dec 28 08:07 libopenal.so

JME uses NativeLibraryLoader.loadNativeLibrary() to extract native libraries:

The “org.lwjgl.librarypath” property gets set in line 667. I wonder whether that code gets executed on your system. Could you step through the method in a debugger or add some System.out.println() calls?

Edit: Looking at your “build.gradle”, I don’t see where the repositories get set, nor where the jmonkeyengineVersion variable gets set.

How about using
implementation ‘org.jmonkeyengine:jme3-lwjgl3:’ + jmonkeyengineVersion

That is the difference I can see as opposed to my working setup.

I have a working app that lists “jme3-lwjgl3” as a runtimeOnly dependency.

Thanks for your suggestions. I updated Gradle (through the Gradle wrapper) from 6.1.1 to 6.9.1 and now it seems to work as it should.
And I can confirm that it also works with runtimeOnly for jme3-lwjgl3.

It seems to me that Gradle somehow didn’t retrieve all transitive dependencies, even though ./gradlew dependencies listed them all… I think. It didn’t say anything about the native libraries specifically.
The JARs containing the .so/.dll were missing, so there was also nothing to extract.

2 Likes

With Gradle v6.1.1, I was able to reproduce the UnsatisfiedLinkError. I’ll investigate further.

Edit: Whatever the Gradle bug was, it was fixed between v6.3-rc-2 and v6.3-rc-3. I looked at the release notes for both releases, but nothing jumped out at me. Very interesting!

Edit^2: I haven’t found a better workaround than simply upgrading Gradle, so I added an item to the 3.5.0-beta4 release notes:

  • to use the “jme3-lwjgl3” library in a Gradle build, you’ll need Gradle v6.3 or later
2 Likes

Okay, fixes for issue 1721 (and a few other bugs) have now been released:

Testers, please upgrade to v3.5.0-beta5 ASAP.

3 Likes

Thanks. I’ve updated the code.

2 Likes

I started to get this exception (alpha1 → beta5):

java.lang.NullPointerException: Cannot invoke "com.jme3.anim.tween.action.Action.setMask(com.jme3.anim.AnimationMask)" because "this.currentAction" is null
        at com.jme3.anim.AnimLayer.update(AnimLayer.java:209)
        at com.jme3.anim.AnimComposer.controlUpdate(AnimComposer.java:391)
        at com.jme3.scene.control.AbstractControl.update(AbstractControl.java:118)
        at com.jme3.scene.Spatial.runControlUpdate(Spatial.java:743)
        at com.jme3.scene.Spatial.updateLogicalState(Spatial.java:890)
        at com.jme3.scene.Node.updateLogicalState(Node.java:228)
        at com.jme3.scene.Node.updateLogicalState(Node.java:239)
        at com.jme3.app.SimpleApplication.update(SimpleApplication.java:262)
        at com.jme3.system.lwjgl.LwjglWindow.runLoop(LwjglWindow.java:578)
        at com.jme3.system.lwjgl.LwjglWindow.run(LwjglWindow.java:667)
        at java.base/java.lang.Thread.run(Thread.java:833)

I will debug it now and come back with more info.
Looking for recent changes that might be related I found this: convert inner class Layer to a top-level class by stephengold · Pull Request #1656 · jMonkeyEngine/jmonkeyengine · GitHub
Could that have broken something?

EDIT: Yes. It’s because that change, I need to go now but I’ll explain it when I’m back in a few hours.

4 Likes

I think the currentAction should be copied into a local variable inside Layer.update():

because it might be changed (removed or replcaed with new action) in:

2 Likes

Good catch! I hope we can fix this soon.

2 Likes

I took a test myself and encountered the same exception. I confirm @Ali_RS’s analysis.

If it helps, this is the previous controlUpdate method of the AnimComposer class from jme-3.4.0

public class AnimComposer {

	...
	
	@Override
	protected void controlUpdate(float tpf) {
		for (Layer layer : layers.values()) {
			Action currentAction = layer.currentAction;
			if (currentAction == null) {
				continue;
			}
			layer.advance(tpf);

			currentAction.setMask(layer.mask);
			boolean running = currentAction.interpolate(layer.time);
			currentAction.setMask(null);

			if (!running) {
				layer.time = 0;
			}
		}
	}
}

Here the possible solution in the AnimLayer class:

public class AnimLayer {

	...
	
	void update(float appDeltaTimeInSeconds) {
		Action runningAction = currentAction;
		if (runningAction == null) {
			return;
		}

		double speedup = runningAction.getSpeed() * composer.getGlobalSpeed();
		double scaledDeltaTime = speedup * appDeltaTimeInSeconds;
		time += scaledDeltaTime;

		// wrap negative times to the [0, length] range:
		if (time < 0.0) {
			double length = runningAction.getLength();
			time = (time % length + length) % length;
		}

		// update the current Action, filtered by this layer's mask:
		runningAction.setMask(mask);
		boolean running = runningAction.interpolate(time);
		runningAction.setMask(null);

		if (!running) { // went past the end of the current Action
			time = 0.0;
		}
	}
}
2 Likes