Skip to content

Latest commit

 

History

History
457 lines (362 loc) · 31.9 KB

File metadata and controls

457 lines (362 loc) · 31.9 KB

Troubleshooting

This is organized as a list of possible messages or signs there is a problem, followed by any ways that help resolve those problems.

This guide is a little new, and will be added to as new solutions are found for problems.

First things first, make sure you're using Java 17 or newer.

Seriously. Do this first. Many parts of a Gradle project will fail now if you are using too old of a JDK. You can check this version in IntelliJ IDEA under File -> Project Structure. If you don't have any JDKs that are version 17 or newer, go into SDKs in the sidebar, and click the + at the very top of the window. You can then download a JDK, such as Version: 21, Vendor: Eclipse Temurin, which is a solid choice. You can also download a GraalVM JDK if you want in this way, which enables using LWJGL3's native-image configuration much later on. You can have many JDKs installed if you want, of various versions.

You can go as new as Java 25, which is the latest at the time of writing, but the newest JDK released tends to take a few months for Gradle to support. Newer JDK versions have been making it harder to use some long-standing Java features, unfortunately, and will often emit warnings for things that have worked for decades until now. If you are experiencing issues with warnings that just became errors, consider downgrading your JDK to 17 or 21.

Installing Java 21 is currently recommended. Java 17 works well still, and was recommended for a long time as well. If you are planning on using Construo to distribute builds to users, you will have an easier time if you use JDK 21.

Some older docs mention Gradle tasks using a "desktop" module.

This would include docs telling you to run a Gradle task like desktop:run or desktop:dist. The solution is almost always to just change desktop to lwjgl3, since there is an lwjgl3 module in most Liftoff projects where gdx-setup would call it desktop. The reason for this is that Liftoff can generate modules using either lwjgl2, which is what gdx-setup used to call desktop, and lwjgl3, which is what it now calls desktop. Liftoff can even generate them both in one parent project, which can be handy for using gdx-tools in the lwjgl2 project.

Of the two, you almost always should be using LWJGL3 in new code; LWJGL2 hasn't been updated in 9 years, and won't see future updates. LWJGL3 also supports more target hardware, from ARM Linux machines like a Raspberry Pi, to M1 and newer Macs using Apple Silicon processors. You might need LWJGL2 to port older projects in specific cases; doing that prevents customers who have the latest Mac hardware from running your game natively. Those customers might still be able to use virtualization software to run Windows games on their Mac, though. LWJGL3 should work out of the box on Mac, thanks in part to the StartupHelper class distributed in new LWJGL3 projects that Liftoff creates.

When importing an Android project, an error mentions SeekableByteChannel.

Specifically, the error involves void org.apache.commons.compress.archivers.zip.ZipFile.<init>(java.nio.channels.SeekableByteChannel)'. The solution for this is to install the Android Build Tools, version 33.0.2 . I don't know if newer versions work as well, but you can have 33.0.2 installed in addition to other versions.

To install new build tools, go to Tools -> Android -> SDK Manager, SDK Tools tab, check "Show Package Details" at the bottom, select Build Tools version 33.0.2, and click Apply or OK. Then follow the installation steps.

When importing an iOS project, an error mentions SeekableByteChannel.

Like the above error, this involves void org.apache.commons.compress.archivers.zip.ZipFile.<init>(java.nio.channels.SeekableByteChannel)'. This has to do with the placement of the robovm plugin, which needs to be in ios/build.gradle, not the root build.gradle. This is automatically fixed in projects generated by gdx-liftoff 1.12.0.1 and later, but if you're for any reason using an older version, you can replace the top of ios/build.gradle with:

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath "com.mobidevelop.robovm:robovm-gradle-plugin:$robovmVersion"
  }
}
apply plugin: 'robovm'

[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'

(Starting with the line that sets encoding, everything else is the same.) You will also need to remove any mention of robovm plugin from the top of the root build.gradle file; this means a line that starts with classpath and a line apply plugin: 'robovm'.

A project that contains an Android module fails to run, saying Android Gradle plugin requires Java 17 to run.

Well, install Java 17 or higher and try again. You may need to set your JDK in your IDE. In IntelliJ IDEA, it's under File -> Project Structure; in that window, you can even download a JDK automatically in the SDKs tab by clicking + and then Download JDK. Android Studio doesn't provide as many ways to configure the JDK because it should include one that works with the latest Android tools already.

I feel like I covered this one already...

A Kotlin project that contains an Android module gives the message jvm target compatibility should be set to the same Java version.

This should be fixed in 1.12.0.1 by using toolchains, or in 1.12.1.7 using Kotlin's jvmTarget option; if that doesn't work for you, here are other options.

The simplest solution here is to set your JDK to a Java 21 JDK and to change java.sourceCompatibility and java.targetCompatibility to 11 each. You may need to set this for both Java and Kotlin, and they must use the same versions across the board. You can also set the release option to the same version as targetCompatibility to help with some incompatibilities between versions; this is only available if you are using Java 9 or later.

java.sourceCompatibility = 11
java.targetCompatibility = 11
if (JavaVersion.current().isJava9Compatible()) {
        compileJava.options.release.set(11)
}
kotlin.compilerOptions.jvmTarget.set(JvmTarget.JVM_11)

Another solution is to use toolchains. In your root build.gradle, you can try adding

kotlin {
  jvmToolchain(21)
}

(Probably adding it at the bottom of the file makes the most sense.) You still need to change sourceCompatibility and targetCompatibility to the same version as your toolchain; these affect Java, while the toolchain should help Kotlin.

Running a Kotlin launcher via IDEA's or Android Studio's "green triangle" button in the margin fails.

This can be identified by an error message appearing such as:

Error: Could not find or load main class some.liftoff.project.lwjgl3.Lwjgl3Launcher
Caused by: java.lang.ClassNotFoundException: some.liftoff.project.lwjgl3.Lwjgl3Launcher

The simplest solution I've found for this is to try it again in exactly the same way. You actually can expect a different result! The first failure seems to set something necessary up in the IDE, and so later attempts work. If this still fails on later tries, you can try reimporting/refreshing/syncing the Gradle project (I don't know what each IDE calls it, but the button may look like two circling arrows in the Gradle sidebar, may be in a bar that pops up at the top, or may be a tiny button with just those circling arrows). Then try it again (twice, if needed) and see if it works.

Why does this work at all? 🤷‍♂️

Some steps were taken to try to address this in gdx-liftoff 1.12.1.5, but they can't seem to fully eliminate this problem on the very first run. They may be what fixes it for the second and later runs, though.

Toolchains aren't working or are slow.

This is to be expected in 1.12.1.4, because some configuration was missing for Kotlin projects. That absence has been fixed in 1.12.1.5. In that version and in 1.12.1.6, Java also uses toolchains, the same way Kotlin does. This means there can be a long download for the first time you launch a gdx-liftoff project, but the download will get a JDK and keep it for any future projects to use (as long as they need the same version). This can be useful if someone wants to build your project but doesn't necessarily start with the right JVM version -- toolchains ensure they get the right one.

A good option for cross-platform building is to keep the language level on 11 (supported by everything except RoboVM). This works even on Android; even with its requirements for Java 17 in other places, using a toolchain JDK 11 seems to keep away from those requirements. A JDK 17 or higher may still be needed for other parts of an Android build.

Version 1.12.1.7 does not use toolchains because some simpler ways to accomplish the same things became feasible.

Graal Native Image isn't working (in any of various ways).

First, ensure that you changed enableGraalNative=false to enableGraalNative=true in gradle.properties. This enables the rest of the Graal code, including downloading dependencies once you re-sync the project.

On Windows, you also have to make sure a (rather recent) Visual Studio is installed and has C++ tools installed. The C++ tools aren't checked by default when first installing Visual Studio. There may be issues with some non-US locales when the Native Image tools try to locate Visual Studio programs/scripts. If you encounter these and have a non-English-language and/or non-US locale, you may want to try this StackOverflow answer's solution. You can also launch the Visual Studio x64 Native Tools command prompt (it's in the start menu by default), navigate to your Liftoff project, and launch gradlew lwjgl3:nativeCompile from there, which may work better.

The asset-location code was subtly broken in Liftoff 1.12.1.3 in some cases, namely when assets were in subfolders. This should be fixed in 1.12.1.4; if you are updating a 1.12.1.3 project that uses Graal Native Image, I recommend copying in the whole nativeimage.gradle file to replace the existing one (unless you have edited it) from either a new 1.12.1.4 or higher project or the same file from the generated demo.

There are probably a lot of ways Graal projects can have issues that I don't know about yet. There aren't many users for Graal Native Image in the Java gamedev space right now, but it's a good option for releasing small executables that are relatively hard to decompile (though not impossible). If more people start using Graal Native Image, this section will likely grow.

You receive compile-time errors in a GWT project made with Liftoff 1.12.1.6 or newer.

Liftoff 1.12.1.6 is the first to use GWT 2.11.0, which is almost entirely backwards-compatible... at an API level, at least. It has different dependencies for gwt-user, both because the version is newer, and that JAR is provided in a different "group" -- org.gwtproject:gwt-user:2.11.0 instead of the older com.google.gwt:gwt-user:2.8.2. This isn't a problem in libGDX 1.13.0 or newer, since they use GWT 2.11.0, but the fixes here won't cause problems if they remain in a project updated to a newer libGDX version.

Before I start with the general solution, there's often a much easier one: for libraries that Liftoff has in its third-party extensions, any that need special treatment for GWT should already have that taken care of. The rest of this is only needed if a library you want to use isn't known to Liftoff and depends on an older GWT version.

Any library dependencies, not installed via a checkbox in Liftoff, that have code specifically for GWT, and depend either directly on gwt-user (or gwt-dev) or indirectly via any version of com.badlogicgames.gdx:gdx-backend-gwt before 1.13.0, can trigger version conflicts from two different releases of GWT. That means if you are trying to use 2.11.0, but 2.10.1, 2.10.0, 2.9.0, 2.8.2, or 2.8.0 gets into the dependencies, because the groups are different, the older version won't be replaced by the newer one. The solution is to exclude gwt-user (or the gdx-backend-gwt in the com.badlogicsgames.gdx group) from any dependencies that themselves depend on the older GWT. You can identify which dependencies these are by running the Gradle task gradlew html:dependencies, and searching through it for com.google.gwt:gwt-user . The output you're trying to find looks like this:

+--- com.crashinvaders.basisu:basisu-gdx-gwt:1.0.0
|    +--- com.crashinvaders.basisu:basisu-gdx:1.0.0 (*)
|    \--- com.badlogicgames.gdx:gdx-backend-gwt:1.12.0
|         +--- com.badlogicgames.gdx:gdx:1.12.0 -> 1.14.0 (*)
|         \--- com.google.gwt:gwt-user:2.8.2
|              +--- com.google.jsinterop:jsinterop-annotations:1.0.2 -> 2.0.2
|              +--- javax.validation:validation-api:1.0.0.GA
|              +--- javax.servlet:javax.servlet-api:3.1.0
|              \--- org.w3c.css:sac:1.3

Right in the middle there is gwt-user 2.8.2 . Once you've found all the places that depend on old gwt-user, you'll need to add an "exclude" to each dependency that was trying to obtain the old gwt-user. The new one should be kept; it's in org.gwtproject:gwt-user. The change to add an exclude block isn't too hard, but there's a lot of Gradle syntax involved. You find an existing dependency, such as:

implementation "com.crashinvaders.basisu:basisu-gdx-gwt:$gdxBasisUniversalVersion:sources"

Then make sure the quoted String is also in parentheses (I don't know why this is needed), and after that add a curly brace block to configure that line.

implementation("com.crashinvaders.basisu:basisu-gdx-gwt:$gdxBasisUniversalVersion:sources") {
  exclude group: "com.badlogicgames.gdx", module: "gdx-backend-gwt"
}

It is approximately equivalent to exclude the older gwt-user directly, and this may be better for some libraries that don't depend on libGDX, but do depend on GWT. This step could still be required even in the next version of libGDX, if libraries don't see updates. You could do that with this block instead of the one above:

implementation("com.crashinvaders.basisu:basisu-gdx-gwt:$gdxBasisUniversalVersion:sources") {
  exclude group: "com.google.gwt", module: "gwt-user"
}

If a dependency has two parts that both depend on GWT, typically one with a :sources classifier at the end and one without :sources, then both need to be given the same exclude block in curly braces.

Once every dependency on older GWT has been excluded, you should be able to run html:superDev or html:dist without any issues. From this, anyway.

Libraries known to be affected by this include Artemis-ODB, the official Box2D, gdx-facebook, the official gdx-controllers, the unofficial gdx-controllerutils, Guacamole, and GdxBasisUniversal. There are probably more that are not in gdx-liftoff's known extension list. If you can get one of these using an extensions checkbox in Liftoff, that way is strongly recommended.

(There's probably a more elegant way to solve this using Gradle's dependency constraints; I just haven't figured it out yet... If anyone wants to flex their Gradle muscles and provide a way for the old gwt-user and gdx-backend-gwt dependencies to be automatically excluded, that would be very welcome!)

Selecting MOE causes the project to not generate correctly, and errors are logged.

The iOS backend using Multi-OS Engine (MOE) seems to have some incompatibility with some Gradle versions, and they go back almost to 8.3. I'm not exactly clear on the nature of the incompatibility, other than it's supposed to be fixed in an upcoming Gradle release. Because that hadn't happened yet, MOE was temporarily removed from Liftoff 1.12.1.7; it won't work in earlier versions unless you go back to 1.12.0.4 or downgrade Gradle yourself to 8.3. Gradle 8.10 appears to work with MOE again, and the latest Liftoff releases (starting with 1.12.1.16) should have it. See also "Where is MOE? What is MOE?" several issues below.

In 1.12.1.8 through 1.13.1.2, the default icons for Android projects look terrible!

SUPERSEDED BY:

In 1.13.1.3 and later, the default icons just say "CHANGE THIS" in bold letters!

This is all according to plan. The plan being, you should never be submitting an app to the Google Play Store with any default title, default description, or default icon, because that makes you look like a duplicate of any other app with those default properties. And duplicate apps get suspended for plagiarism! In earlier versions, a hideous default icon was randomly generated to make sure a duplicate icon would never be submitted. People apparently didn't like having a hideous icon at any point, so in 1.13.1.3 and later, the icon says, bluntly, "CHANGE THIS" to remind you that you really, really, should change the icon before even making a testing release to Google Play! If you don't, well, you can blame the people who complained about the hideous random icons all you like, but the only person who deserves that blame would be, well, you. So please, just change your Android icons before touching the Google Play Store. Your icon is a huge part of your app's identity, and being lazy with it just marks your app as low-effort.

In short, you should really pay attention to your icon when you are submitting to Google Play Store, and you should always "CHANGE THIS" icon that is generated!

In 1.12.1.9 and later, jpackage configuration is gone; how do I produce .exe and other apps?

While jpackage may be gone from Liftoff projects out of the box, in that box, in its place, is Construo! This is a relatively new project by @fourlastor. It acts like the earlier Packr project, but works in more places and can target most widely-used OS-architecture combinations (notably ARM Macs, such as those with an M1 or newer processor, aren't supported by Packr but are supported by Construo). Like with Packr, and unlike with jpackage, you can build for any target from any OS that can run Construo Gradle tasks. These tasks are typically grouped under lwjgl3/construo/ , and the ones you actually want to run have names that start with "package". Currently, that means lwjgl3:packageLinuxX64 , lwjgl3:packageMacM1 , lwjgl3:packageMacX64 , and lwjgl3:packageWinX64 . Each of these will output a working executable for that OS in the lwjgl3/build/construo/ folder. For Windows, the .exe is in the winX64/ subfolder, and a ready-to-upload .zip file is in dist/. Other platforms place their executables in different folders, such as linuxX64/, but still place .zip files in dist/. There will be several other folders created in the process of shrinking down the release's size, but they usually won't affect you.

Construo has some uncommon advantages, such as how it tries to run on discrete graphics instead of integrated graphics if both are available (such as on many laptops). It also has some pitfalls, though not many. It requires a Java 17 or newer JDK to build the project, though not necessarily to run the resulting game, even if compiled to a JAR. It uses jdeps to determine which parts of the JDK to use, and jlink to create a JRE without any parts the game doesn't use. The jdeps/jlink step can go wrong if certain deprecated parts of the JDK are used, which is the case with the commonly-used dependency VisUI up to its version 1.5.3 . VisUI recently released a new stable version, and Liftoff updated to it; so can your project. That release is version 1.5.7, and uses this dependency:

implementation "com.kotcrab.vis:vis-ui:1.5.7"

You could instead change your dependency on VisUI to a known-working commit on JitPack if you encounter any issues:

implementation "com.github.kotcrab.vis-ui:vis-ui:1f8b37a24b"

Which gets a recent commit built by JitPack, and that code has fixes from after 1.5.7 that make it compatible with libGDX 1.14.0 and Construo. You may need to call VisUI.setSkipGdxVersionCheck(true); before calling VisUI.load().

Other dependencies may also have issues, but no one has found them yet.

Opening a folder picker crashes on macOS or Linux!

This was a bug in 1.12.1.13 and possibly some earlier versions, and it has been fixed in 1.12.1.14 (probably). It was caused by opening the file picker dialog on its own thread, which other OSes don't mind at all, but macOS can't handle at all. It causes a crash in native code (which can't be caught with try/catch). The fix turns out to be to just lock input to the rest of the Liftoff program as we were doing before, but permit the file dialog to do its thing, and re-enable input when the dialog closes.

1.12.1.15 uses a newer version of the file dialog library (we went from LWJGL 3.3.1's NFD binding to LWJGL 3.3.4's NFDe binding). This had some bug fixes, but also had all kinds of new bugs, so many releases were back to using 3.3.1. If anyone is using an older Liftoff version that uses NFDe from LWJGL 3.3.4 or 3.3.5, and encounters issues with that (maintained) code, you can send bug reports to NFDe. Thankfully, LWJGL 3.4.0 came out in mid-January 2026, and with it an updated NFDe that seems to have fixed some bugs. It's used by gdx-liftoff 1.14.0.6 and up, except on Linux, where the VisUI fallback is now used instead. NFDe seems to stil have native-code crashing bugs on (at least some GNOME-using) Linux systems. The VisUI fallback file picker isn't optimal either, but it at least doesn't crash, and doesn't freeze anymore. 1.14.0.5 used a broken VisUI release, but 1.14.0.6 and up use the (working) latest commit, because there still hasn't been a VisUI release yet.

The native distributions for macOS won't run how they should!

First off, if you can run a .jar file normally, you should try that before any of the following steps. The runnable .jar file is always provided with Liftoff releases. The native distribution is meant for cases when you don't have a JDK installed at the system level (such as if your IDE has its own JDK, and you haven't added one).

This is a bit of a tricky issue, because the native distributions are built (currently) on Windows 11, which doesn't have the same ways it can mark files as macOS. Liftoff version 1.12.1.15 and newer does mark the right file as executable, but still has to deal with how macOS browsers like to put downloaded files in "quarantine." Once you have downloaded the macOS M1 or macOS x64 distribution, you can (on macOS) run this command from the same directory as gdx-liftoff.app :

xattr -cr gdx-liftoff.app

This removes gdx-liftoff.app from quarantine. After running that command, the .app should be normally runnable via double-click. If that doesn't work, you may have an older Liftoff distribution (1.12.1.14 or older), which if you need to use that version for some reason, needs an extra step after the above one:

chmod +x gdx-liftoff.app/Contents/MacOS/gdx-liftoff

These were found by JojoIce in this issue thread; more future discoveries could be in that thread or in other issues. As far as I can tell, the first command clears any attributes on the downloaded .app, which removes any quarantine settings, and the second command sets the entry point inside the .app to be executable. The second command isn't needed anymore thanks to changes in Liftoff 1.12.1.15, which uses construo 1.4.1 (the current version uses 2.0.2). Since the quarantine settings are applied to the .app when it is downloaded, there isn't really any change that could be made to how Liftoff is built that would make it somehow avoid being quarantined. The only ways I know of that handle quarantine settings are to a) download gdx-liftoff.app via a tool such as curl, or b) download it however you want and use the above xattr command to remove the file from quarantine. Using curl will simply not apply quarantine to any files it downloads, but I haven't used it in a while (and never on recent macOS), so the exact command-line syntax is something you'd have to look up.

Weird Gradle issues with "Cannot call Task.usesService(Provider)..."

These have been reported intermittently with Gradle 8.10.1, which Liftoff 1.12.1.15 uses, but that version of Liftoff also includes one of the recommended workarounds for that Gradle version - disabling Gradle's daemon service. It is entirely likely that the way the daemon is disabled might not work, because Gradle really likes to try to use the daemon even when it is broken. Disabling the daemon doesn't appear to have much performance impact, especially since Gradle 8.10.1 is mostly meant to fix a performance regression in Gradle 8.10, and so could be faster either with or without the daemon. If you are encountering this, you should upgrade Gradle to 8.10.2 or (better) 8.14.3, either of which fixes the bug:

  • Open the file gradle/wrapper/gradle-wrapper.properties in your project.
  • Find where it has the version string 8.10.1 ; this is on a line that starts with distributionUrl .
  • Change 8.10.1 to 8.14.3 .
  • Save and reload your Gradle project.
    • If you don't know how to reload your Gradle project, it is done automatically by Android Studio, so you don't need to do anything but save there. Otherwise, in IDEA:
    • Click the sideways Gradle tab on the right side of the window.
    • Click the two circling arrows ("Reload All Gradle Projects") in the pane that appears.
    • This clears out most IDE-specific configuration you may have done via "Project Structure"; that configuration is meant to be controlled by Gradle, so Gradle changes will take priority. Usually Project Structure changes beyond the JDK setting aren't needed, and the JDK settings won't be cleared out.

Where is MOE? What is MOE?

The Multi-OS Engine target, which lets you build libGDX games/apps for iOS using a different method than RoboVM, wasn't working for a while, and was removed from Liftoff's options while any combination of Gradle, Gradle plugins, and MOE itself were experiencing bugs. This should be resolved as of 1.12.1.16 1.12.1.17 ! You can now select MOE in the Platforms selection box. How well could it work? That depends on what you want to do with it. You need a macOS machine at some point to deploy to iOS, but unlike RoboVM, MOE should be able to allow you to write code on Windows or Linux and send it to a (possibly remote) macOS machine to build it. The latest MOE release posting is here, and that forum is also where to go for MOE-specific questions.

How do I change the Construo JDK download links, so I can bundle a JDK other than JDK 21?

The links in lwjgl3/build.gradle and its construo configuration block all point to Adoptium OpenJDK links to JDK 21, at the time of writing, 21.0.10_7, which should only include small bug-fixes to the original JDK 21 release. But, if you want to bundle a different JDK version with your application, you have some important steps to take.

First, make sure Gradle itself uses the JDK version you want to bundle. In IDEA and AS, navigate to: File | Settings | Build, Execution, Deployment | Build Tools | Gradle and select the version you want in the bottom drop-down menu. If I want to build with and bundle Java 25, for example, I would select a Java 25 version from there, like "Eclipse Temurin 25" or "BellSoft Liberica 25". If you don't have any Java installation of the version you want, Navigate to File | Project Structure | SDKs, Click the + at top, and Download JDK to get the version you want. If you just downloaded a JDK, you can do the first step again and actually select your wanted version this time. Click Apply or OK once you have selected your wanted version in the drop-down.

Second, you'll need to make the links in lwjgl3/build.gradle match the JDK you selected, with the same major version and hopefully the most recent minor version you can manage. The links currently used by default in Construo are taken from https://github.com/adoptium/temurin21-binaries/releases , on the only section with blue links listed inside it. If you want the recent Java 25, you'll want to look in https://github.com/adoptium/temurin25-binaries/releases instead (note, the only change is from "21" to "25"). Other versions are similar; 17 is just about the first version of JDK that Construo should work with, and is definitely the earliest one still receiving support from any OpenJDK vendor. The actual links probably don't matter too much, but you may want to copy the link matching a description like OpenJDK25U-jdk_x64_windows_hotspot_25.0.2_10.zip to make sure everything matches. The reason the link doesn't matter is that you can usually just change the version from 21.0.10 to 25.0.2 (in this case). Then, change the last number where it appears to match as well (7 to 10), which is after an _ underscore in one place and after the escape %2B in another place. You should probably try clicking the link to make sure it's valid.

Once you've changed the link for each platform you want to support (probably Windows, maybe also Linux, and unlikely also macOS, AARCH64 and x64), you can run the Construo package task for that platform, such as lwjgl3:packageWinX64 . Then you're done!

Why is GWT so different in recent versions?

Liftoff 1.14.0.3 includes an update to Gradle 9.x, specifically 9.2.1 . This release has several breaking changes, but most of them seem to break older plugins more than user code. The GWT Gradle plugin we used was quite old (1.1.29 had been what we were using for quite a while), and its version 1.x isn't compatible with Gradle 9. So updating to the 2.x line of the GWT Gradle plugin (2.2.7) turned out to have some other advantages. Even though Gretty was not playing nicely with GWT at first, it turns out the 2.x plugin doesn't actually need Gretty anymore to serve webpages, which was a long-standing oddity of the GWT code.

So, the new usage of GWT projects uses the same Gradle tasks as before, dist to build a standalone, deployable page (which is still grouped under "other" in task lists), and superDev to build while changing the source code, saving, and seeing the changes as soon as the page re-compiles (which is still grouped under "gwt"). The differences here are that dist runs a lot more quickly in the latest version, and that (for reasons I haven't fully figured out) superDev now depends on running dist when it first starts up. More importantly, superDev pops up a Swing window (using GWT itself to do so) that provides a link to your re-load-able page. It isn't hidden in the Gradle output text anymore, which is nice. You may need to wait a few seconds for the link to appear near the top of the Gradle window, and you can also copy the URL into a browser of your choice.

Why isn't TeaVM compiling to WASM?

In versions 1.14.0.0 through 1.14.0.2, a change in how TeaVM uses its configuration led to WASM never getting generated by TeaVM, even if it appeared to be selected. This has been fixed in 1.14.0.3, and the syntax changed for TeaVM config in 1.14.0.7. In the current version of Liftoff, which uses TeaVM 1.5.3 or newer, there should be a commented line in TeaVMBuilder.java or TeaVMBuilder.kt :

//                .setWebAssembly(true) // Uncomment this line to use WASM output instead of JavaScript output.

As you would expect, uncomment that line to enable WASM generation.

In earlier versions of Liftoff projects, use:

teaBuildConfiguration.targetType = TeaVMTargetType.WEBASSEMBLY_GC;

before the line containing TeaBuilder.config(teaBuildConfiguration) . This works for both Java and Kotlin.

You can get by fine without WASM, but the generated JavaScript by default isn't nearly as fast as the WASM on some kinds of code, in particular any math involving a JVM long. That also affects some libGDX data structures (maps and sets), which rely on long math to mix hash codes adequately, and so very large maps and sets are faster (in general) on WASM than JS. However, WASM also doesn't run, or at least doesn't run as well, on mobile browsers. It can be worthwhile to build both TeaVM builds with and without WASM. If graphical effects are your game's performance bottleneck, the choice between JS and WASM is nearly irrelevant for speed, and JS can be preferred because it works better on mobile browsers.