libgdx/gdx-liftoff

Opening directory dialog crashes liftoff

ChristianPervoelz opened this issue · 6 comments

System
OS: Windows 11
Java: OpenJDK 16
gdx-liftoff: 1.11.0.1 (and above)

Steps

  1. Start gdx-liftoff
  2. Click on the folder icon to open an empty project directory

Result

java -jar gdx-liftoff-1.11.0.1.jar
[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.
[LWJGL] Enable debug mode with -Dorg.lwjgl.util.Debug=true for better diagnostics.
[LWJGL] Enable the SharedLibraryLoader debug mode with -Dorg.lwjgl.util.DebugLoader=true for better diagnostics.
Exception in thread "main" java.lang.RuntimeException: Actor: VisImageButton
|  Image: icon-folder
        at com.badlogic.gdx.scenes.scene2d.Actor.notify(Actor.java:192)
        at com.badlogic.gdx.scenes.scene2d.Actor.fire(Actor.java:152)
        at com.badlogic.gdx.scenes.scene2d.ui.Button.setChecked(Button.java:125)
        at com.badlogic.gdx.scenes.scene2d.ui.Button$1.clicked(Button.java:93)
        at com.badlogic.gdx.scenes.scene2d.utils.ClickListener.touchUp(ClickListener.java:88)
        at com.badlogic.gdx.scenes.scene2d.InputListener.handle(InputListener.java:71)
        at com.badlogic.gdx.scenes.scene2d.Stage.touchUp(Stage.java:355)
        at com.badlogic.gdx.InputEventQueue.drain(InputEventQueue.java:70)
        at com.badlogic.gdx.backends.lwjgl3.DefaultLwjgl3Input.update(DefaultLwjgl3Input.java:189)
        at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window.update(Lwjgl3Window.java:378)
        at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.loop(Lwjgl3Application.java:192)
        at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.<init>(Lwjgl3Application.java:166)
        at gdx.liftoff.MainKt.main(main.kt:106)
        at gdx.liftoff.MainKt.main(main.kt)
Caused by: com.badlogic.gdx.utils.GdxRuntimeException: Unable to invoke method: com.badlogic.gdx.utils.reflect.Method@38600b of object: gdx.liftoff.views.MainView@669d2b1b
        at com.github.czyzby.lml.parser.impl.action.MethodActorConsumer.consume(MethodActorConsumer.java:41)
        at com.github.czyzby.lml.parser.impl.attribute.OnChangeLmlAttribute$1.changed(OnChangeLmlAttribute.java:29)
        at com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.handle(ChangeListener.java:28)
        at com.badlogic.gdx.scenes.scene2d.Actor.notify(Actor.java:188)
        ... 13 more
Caused by: com.badlogic.gdx.utils.reflect.ReflectionException: Exception occurred in method: chooseDirectory
        at com.badlogic.gdx.utils.reflect.Method.invoke(Method.java:114)
        at com.github.czyzby.kiwi.util.gdx.reflection.Reflection.invokeMethod(Reflection.java:130)
        at com.github.czyzby.lml.parser.impl.action.MethodActorConsumer.consume(MethodActorConsumer.java:38)
        ... 16 more
Caused by: java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:567)
        at com.badlogic.gdx.utils.reflect.Method.invoke(Method.java:108)
        ... 18 more
Caused by: java.util.MissingResourceException: Can't find bundle key desktop
        at com.badlogic.gdx.utils.I18NBundle.get(I18NBundle.java:435)
        at com.badlogic.gdx.utils.I18NBundle.get(I18NBundle.java:432)
        at com.kotcrab.vis.ui.widget.file.internal.FileChooserText.get(FileChooserText.java:106)
        at com.kotcrab.vis.ui.widget.file.FileChooser.createShortcutsMainPanel(FileChooser.java:630)
        at com.kotcrab.vis.ui.widget.file.FileChooser.init(FileChooser.java:240)
        at com.kotcrab.vis.ui.widget.file.FileChooser.<init>(FileChooser.java:181)
        at com.kotcrab.vis.ui.widget.file.FileChooser.<init>(FileChooser.java:164)
        at gdx.liftoff.views.MainView.pickDirectory(MainView.kt:140)
        at gdx.liftoff.views.MainView.chooseDirectory(MainView.kt:71)
        ... 23 more

Note
Up to version 1.11.0.0 (including) liftoff works absolutely fine. This problem occurs first with 1.11.0.1 and then in all subsequent releases.

Spooky timing! crashinvaders/gdx-texture-packer-gui#138 was created just today - very similar.

I'll take a look... Hmm...

This looks like there's an I18N issue with VisUI's FileChooser, which I don't think 1.11.0.1 even uses (it uses NFD, Native File Dialogs). Maybe on Windows 11 (which I don't have), NFD can't work and falls back to VisUI's FileChooser, but that seems unlikely...

All I can tell is that NFD is crashing (which has never happened to me, but I'm on Windows 10), so VisUI's FileChooser is used instead, and that looks for the I18N string desktop, which isn't defined because it caused some kind of oddities back in 2019 or earlier. I've brought desktop back into the I18N files, and so here's a prerelease JAR (in a zip, must be extracted etc.) that has the latest changes. This doesn't fix the NFD crash that starts the whole process, because I have no idea what would cause that.
gdx-liftoff-1.11.0.5-SNAPSHOT.jar.zip

Yeah, the patch works fine.

Aaaaaand... I found the reason for this issue!

The problem seems to be located in the lwjgl handling when two applications are open, that both uses NFD.
I had TexturePacker open and then started liftoff and by trying to open the dialog it crashes.

Interesting observation:
If both apps are running, it depends on which one opened the file dialog first. The other one will be faced with the problem then.

Here's a debug output, that shows what happens. At the end there is an AccessDeniedException, because a file should be deleted, but of course cannot, as it is in use.

java.lang.RuntimeException:     Failed to extract lwjgl_nfd library
        at org.lwjgl.system.SharedLibraryLoader.load(SharedLibraryLoader.java:93)
        at org.lwjgl.system.Library.loadSystem(Library.java:123)
        at org.lwjgl.util.nfd.LibNFD.<clinit>(LibNFD.java:17)
        at org.lwjgl.util.nfd.NativeFileDialog.<clinit>(NativeFileDialog.java:65)
        at gdx.liftoff.views.MainView.pickDirectory(MainView.kt:120)
        at gdx.liftoff.views.MainView.chooseDirectory(MainView.kt:71)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:567)
        at com.badlogic.gdx.utils.reflect.Method.invoke(Method.java:108)
        at com.github.czyzby.kiwi.util.gdx.reflection.Reflection.invokeMethod(Reflection.java:130)
        at com.github.czyzby.lml.parser.impl.action.MethodActorConsumer.consume(MethodActorConsumer.java:38)
        at com.github.czyzby.lml.parser.impl.attribute.OnChangeLmlAttribute$1.changed(OnChangeLmlAttribute.java:29)
        at com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.handle(ChangeListener.java:28)
        at com.badlogic.gdx.scenes.scene2d.Actor.notify(Actor.java:188)
        at com.badlogic.gdx.scenes.scene2d.Actor.fire(Actor.java:152)
        at com.badlogic.gdx.scenes.scene2d.ui.Button.setChecked(Button.java:125)
        at com.badlogic.gdx.scenes.scene2d.ui.Button$1.clicked(Button.java:93)
        at com.badlogic.gdx.scenes.scene2d.utils.ClickListener.touchUp(ClickListener.java:88)
        at com.badlogic.gdx.scenes.scene2d.InputListener.handle(InputListener.java:71)
        at com.badlogic.gdx.scenes.scene2d.Stage.touchUp(Stage.java:355)
        at com.badlogic.gdx.InputEventQueue.drain(InputEventQueue.java:70)
        at com.badlogic.gdx.backends.lwjgl3.DefaultLwjgl3Input.update(DefaultLwjgl3Input.java:189)
        at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window.update(Lwjgl3Window.java:378)
        at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.loop(Lwjgl3Application.java:192)
        at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.<init>(Lwjgl3Application.java:166)
        at gdx.liftoff.MainKt.main(main.kt:116)
        at gdx.liftoff.MainKt.main(main.kt)
Caused by: java.nio.file.AccessDeniedException: C:\Users\AUser\AppData\Local\Temp\lwjglAUser\3.3.1-SNAPSHOT\lwjgl_nfd.dll
        at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:89)
        at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
        at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
        at java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:275)
        at java.base/sun.nio.fs.AbstractFileSystemProvider.deleteIfExists(AbstractFileSystemProvider.java:110)
        at java.base/java.nio.file.Files.deleteIfExists(Files.java:1185)
        at java.base/java.nio.file.Files.copy(Files.java:3132)
        at org.lwjgl.system.SharedLibraryLoader.extract(SharedLibraryLoader.java:225)
        at org.lwjgl.system.SharedLibraryLoader.load(SharedLibraryLoader.java:91)
        ... 28 more

I don't think there's anything I can do about this, since it seems to be on LWJGL3's side. I can't even reproduce it by opening and closing NFD-using apps (I am on Windows 10, using I think Java 16). We do know now that a potential solution is to only use one app that uses NFD at a time, which isn't ideal, but I'm glad you found it. I added some logging code so if users are running from the command line, they should see the NFD stack trace and also get a message about trying to avoid using multiple LWJGL3 apps at once.

I'll close this because I don't think Liftoff's code can do any more, but I might open an issue with LWJGL3 if I can reproduce. You can open an issue there instead, and since you actually can encounter the bug and maybe get more info in the process, that might help them figure out a solution.