diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000000..da87bd8a58
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,27 @@
+FROM registry.fedoraproject.org/fedora:43
+
+RUN dnf -y update && \
+ dnf -y install \
+ git curl unzip which procps-ng psmisc findutils \
+ java-21-openjdk-devel \
+ # X + desktop
+ xorg-x11-server-Xvfb xterm dbus-x11 \
+ xfce4-session xfce4-panel xfce4-settings xfce4-terminal thunar \
+ xfwm4 \
+ feh \
+ # VNC + web VNC
+ x11vnc novnc python3-websockify \
+ # OpenGL (Mesa) + GLVND + tools (glxinfo)
+ mesa-dri-drivers mesa-libGL mesa-libEGL mesa-libGLU \
+ libglvnd-glx libglvnd-egl \
+ glx-utils \
+ && dnf clean all
+
+
+RUN dnf -y install weston xorg-x11-server-Xwayland wayland-utils \
+ && dnf clean all
+
+
+COPY assets/wallpaper.jpg /root/wallpaper.jpg
+COPY assets/novnc-index.html /usr/share/novnc/index.html
+
\ No newline at end of file
diff --git a/.devcontainer/assets/novnc-index.html b/.devcontainer/assets/novnc-index.html
new file mode 100644
index 0000000000..1dd03f733b
--- /dev/null
+++ b/.devcontainer/assets/novnc-index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ noVNC
+
+
+
+ Warming up noVNC...
+
+
+
\ No newline at end of file
diff --git a/.devcontainer/assets/wallpaper.jpg b/.devcontainer/assets/wallpaper.jpg
new file mode 100644
index 0000000000..ddbd1cbb09
Binary files /dev/null and b/.devcontainer/assets/wallpaper.jpg differ
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000..d5d735f429
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,36 @@
+{
+ "name": "jME env (SERVER)",
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
+ "forwardPorts": [6080],
+ "portsAttributes": {
+ "6080": {
+ "label": "jME Desktop",
+ "onAutoForward": "openPreview"
+ }
+ },
+ "containerEnv": {
+ "JME_DEV_ENVIRONMENT": "yesReally",
+ "LIBGL_ALWAYS_SOFTWARE": "1",
+ "MESA_LOADER_DRIVER_OVERRIDE": "llvmpipe",
+ "GALLIUM_DRIVER": "llvmpipe",
+ "WAYLAND_DISPLAY": "",
+ "WAYLAND_SOCKET": "",
+ "XDG_RUNTIME_DIR": "",
+ "GDK_BACKEND": "x11",
+ "SDL_BACKEND": "x11",
+ "GLFW_PLATFORM": "x11",
+ "DISPLAY": ":99"
+ },
+ "postCreateCommand": "bash .devcontainer/scripts/setup.sh",
+ "postStartCommand": "bash .devcontainer/scripts/start-desktop.sh",
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "workbench.colorTheme": "GitHub Dark Dimmed"
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/.devcontainer/gpu-nvidia/devcontainer.json b/.devcontainer/gpu-nvidia/devcontainer.json
new file mode 100644
index 0000000000..798f24fd1f
--- /dev/null
+++ b/.devcontainer/gpu-nvidia/devcontainer.json
@@ -0,0 +1,25 @@
+{
+ "name": "jME env (NVIDIA)",
+ "build": {
+ "dockerfile": "../Dockerfile",
+ "context": ".."
+ },
+ "runArgs": [
+ "--gpus=all"
+ ],
+ "containerEnv": {
+ "LIBGL_ALWAYS_SOFTWARE": "0",
+ "NVIDIA_VISIBLE_DEVICES": "all",
+ "NVIDIA_DRIVER_CAPABILITIES": "graphics,utility,compute",
+ "__GL_THREADED_OPTIMIZATIONS": "0",
+ "JME3_DIALOGS_FACTORY": ""
+ },
+ "postCreateCommand": "bash .devcontainer/scripts/setup.sh",
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "workbench.colorTheme": "GitHub Dark Dimmed"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/.devcontainer/gup-soft/devcontainer.json b/.devcontainer/gup-soft/devcontainer.json
new file mode 100644
index 0000000000..74154b90f9
--- /dev/null
+++ b/.devcontainer/gup-soft/devcontainer.json
@@ -0,0 +1,24 @@
+{
+ "name": "jME env",
+ "build": {
+ "dockerfile": "../Dockerfile",
+ "context": ".."
+ },
+ "runArgs": [
+ "--device=/dev/dri",
+ "--group-add=video",
+ "--group-add=render"
+ ],
+ "containerEnv": {
+ "LIBGL_ALWAYS_SOFTWARE": "0",
+ "JME3_DIALOGS_FACTORY": ""
+ },
+ "postCreateCommand": "bash .devcontainer/scripts/setup.sh",
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "workbench.colorTheme": "GitHub Dark Dimmed"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/.devcontainer/scripts/setup.sh b/.devcontainer/scripts/setup.sh
new file mode 100644
index 0000000000..68754c4b53
--- /dev/null
+++ b/.devcontainer/scripts/setup.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+
+# Optional: warm Gradle wrapper so first run is less painful
+if [ -f ./gradlew ]; then
+ ./gradlew --version || true
+fi
\ No newline at end of file
diff --git a/.devcontainer/scripts/start-desktop.sh b/.devcontainer/scripts/start-desktop.sh
new file mode 100755
index 0000000000..90eabffd7f
--- /dev/null
+++ b/.devcontainer/scripts/start-desktop.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+set -euo pipefail
+if [ "$JME_DEV_ENVIRONMENT" != "yesReally" ];
+then
+ echo "Not running! This script is meant to be run in the jMonkeyEngine dev container environment, and may do unexpected things if run elsewhere."
+ exit 1
+fi
+
+export DISPLAY="${DISPLAY:-:99}"
+unset WAYLAND_DISPLAY WAYLAND_SOCKET XDG_RUNTIME_DIR
+export GDK_BACKEND=x11
+export SDL_VIDEODRIVER=x11
+export GLFW_PLATFORM=x11
+
+# Fedora noVNC web root path
+NOVNC_WEB="/usr/share/novnc"
+if [ ! -d "$NOVNC_WEB" ]; then
+ echo "ERROR: noVNC web root not found at $NOVNC_WEB"
+ echo "Try checking where novnc installed files are:"
+ rpm -ql novnc | sed -n '1,120p' || true
+ exit 1
+fi
+
+# Start virtual X server
+if ! pgrep -f "Xvfb ${DISPLAY}" >/dev/null 2>&1; then
+ nohup Xvfb "${DISPLAY}" -screen 0 1440x900x24 +extension GLX +render -noreset >/tmp/xvfb.log 2>&1 &
+fi
+
+# Start a dbus session (XFCE wants it)
+if ! pgrep -u "$(id -u)" -f "dbus-daemon.*--session" >/dev/null 2>&1; then
+ # shellcheck disable=SC2046
+ eval "$(dbus-launch --sh-syntax)"
+fi
+
+
+
+if [ "$JME_DEV_ENVIRONMENT" != "yesReally" ];
+then
+ echo "Not running! This script is meant to be run in the jMonkeyEngine dev container environment, and may do unexpected things if run elsewhere."
+ exit 1
+fi
+
+# Init home
+mkdir -p "$HOME/.config" "$HOME/.cache" "$HOME/.local/share"
+
+
+# Start window manager
+if ! pgrep -u "$(id -u)" -x xfwm4 >/dev/null 2>&1; then
+ nohup xfwm4 --compositor=off --daemon >/tmp/xfwm4.log 2>&1 &
+fi
+
+
+# Start XFCE session
+if ! pgrep -u "$(id -u)" -x xfce4-session >/dev/null 2>&1; then
+ nohup xfce4-session >/tmp/xfce4-session.log 2>&1 &
+fi
+
+# Set wallpaper
+feh --no-fehbg --bg-fill "/root/wallpaper.jpg"
+
+# Start VNC server (no password; fine for Codespaces)
+if ! pgrep -f "x11vnc.*-rfbport 5900" >/dev/null 2>&1; then
+ nohup x11vnc -display "${DISPLAY}" -nopw -forever -shared -cursor arrow -rfbport 5900 >/tmp/x11vnc.log 2>&1 &
+fi
+
+# Start noVNC (websockify)
+if ! pgrep -f "websockify.*6080" >/dev/null 2>&1; then
+ python3 -m websockify --web="${NOVNC_WEB}" 6080 localhost:5900
+fi
+
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
deleted file mode 100644
index 6ff2d099fa..0000000000
--- a/.github/workflows/format.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-name: auto-format
-on:
- push:
-
-jobs:
- format:
- runs-on: ubuntu-latest
- if: ${{ false }}
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
- - name: Prettify code
- uses: creyD/prettier_action@v4.3
- with:
- prettier_options: --tab-width 4 --print-width 110 --write **/**/*.java
- prettier_version: "2.8.8"
- only_changed: True
- commit_message: "auto-format"
- prettier_plugins: "prettier-plugin-java"
diff --git a/.gitignore b/.gitignore
index 121fd33ac3..acb22783d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,4 +50,5 @@ javadoc_deploy.pub
!.vscode/settings.json
!.vscode/JME_style.xml
!.vscode/extensions.json
+!.vscode/java.code-snippets
joysticks-*.txt
\ No newline at end of file
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index a1538208b3..e7e0525d52 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,6 +1,13 @@
{
- "recommendations": [
- "vscjava.vscode-java-pack",
- "slevesque.shader"
- ]
-}
+ "recommendations": [
+ "vscjava.vscode-java-pack",
+ "slevesque.shader",
+ // "github.vscode-github-actions",
+ "Anthropic.claude-code",
+ "ms-vscode-remote.remote-containers",
+ // "GitHub.codespaces",
+ "GitHub.copilot-chat",
+ "ms-vscode.remote-server",
+ // "HarryHopkinson.vim-theme"
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/java.code-snippets b/.vscode/java.code-snippets
new file mode 100644
index 0000000000..7c67647e00
--- /dev/null
+++ b/.vscode/java.code-snippets
@@ -0,0 +1,54 @@
+{
+
+ "logger":{
+ "prefix":"logger",
+ "body": [
+ "private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(${1:${TM_FILENAME_BASE}}.class.getName());"
+ ],
+ "description":"Add java.util Logger"
+ },
+ "log":{
+ "prefix": "log",
+ "body":[
+ "if(logger.isLoggable(java.util.logging.Level.$1 )){\n\tlogger.log(java.util.logging.Level.$1, \"$2\");\n}"
+ ],
+
+ },
+ "logfinest":{
+ "prefix": "logfinest",
+ "body":[
+ "logger.finest($1);"
+ ]
+ },
+ "logfiner":{
+ "prefix": "logfiner",
+ "body":[
+ "logger.finer($1);"
+ ]
+ },
+ "logfine":{
+ "prefix": "logfine",
+ "body":[
+ "logger.fine($1);"
+ ]
+ },
+ "loginfo":{
+ "prefix": "loginfo",
+ "body":[
+ "logger.info($1);"
+ ]
+ },
+ "logwarning":{
+ "prefix": "logwarning",
+ "body":[
+ "logger.warning($1);"
+ ]
+ },
+ "logsevere":{
+ "prefix": "logsevere",
+ "body":[
+ "logger.severe($1);"
+ ]
+ }
+
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 89d691dd52..a092e2bc52 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,15 +1,9 @@
{
"java.configuration.updateBuildConfiguration": "automatic",
"java.compile.nullAnalysis.mode": "automatic",
- "java.refactor.renameFromFileExplorer": "prompt",
"java.format.settings.url": "./.vscode/JME_style.xml",
- "editor.formatOnPaste": true,
+ "editor.formatOnPaste": false,
"editor.formatOnType": false,
- "editor.formatOnSave": true,
- "editor.formatOnSaveMode": "modifications" ,
-
- "prettier.tabWidth": 4,
- "prettier.printWidth": 110,
- "prettier.enable": true,
- "prettier.resolveGlobalModules": true
+ "editor.formatOnSave": false,
+ "editor.formatOnSaveMode": "modifications",
}
diff --git a/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java b/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java
index ec89b9df06..887f7348e6 100644
--- a/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java
@@ -129,7 +129,7 @@ private void initOpenAL() {
if (!alc.isCreated()) {
alc.createALC();
}
- } catch (UnsatisfiedLinkError ex) {
+ } catch (Exception ex) {
logger.log(Level.SEVERE, "Failed to load audio library (OpenAL). Audio will be disabled.", ex);
audioDisabled = true;
return;
diff --git a/jme3-core/src/main/java/com/jme3/system/AppSettings.java b/jme3-core/src/main/java/com/jme3/system/AppSettings.java
index 92354ff91e..8c154b46ea 100644
--- a/jme3-core/src/main/java/com/jme3/system/AppSettings.java
+++ b/jme3-core/src/main/java/com/jme3/system/AppSettings.java
@@ -296,12 +296,12 @@ public final class AppSettings extends HashMap {
static {
defaults.put("Display", 0);
defaults.put("CenterWindow", true);
- defaults.put("Width", 640);
- defaults.put("Height", 480);
+ defaults.put("Width", 1440);
+ defaults.put("Height", 900);
defaults.put("WindowWidth", Integer.MIN_VALUE);
defaults.put("WindowHeight", Integer.MIN_VALUE);
defaults.put("BitsPerPixel", 24);
- defaults.put("Frequency", 60);
+ defaults.put("Frequency", 0);
defaults.put("DepthBits", 24);
defaults.put("StencilBits", 0);
defaults.put("Samples", 0);
@@ -317,7 +317,7 @@ public final class AppSettings extends HashMap {
defaults.put("MinHeight", 0);
defaults.put("MinWidth", 0);
defaults.put("GammaCorrection", true);
- defaults.put("Resizable", false);
+ defaults.put("Resizable", true);
defaults.put("SwapBuffers", true);
defaults.put("OpenCL", false);
defaults.put("OpenCLPlatformChooser", DefaultPlatformChooser.class.getName());
diff --git a/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java b/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java
index c9d8b79182..3f31c7adb6 100644
--- a/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java
+++ b/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java
@@ -66,7 +66,13 @@ public abstract class JmeSystemDelegate {
protected Consumer errorMessageHandler = (message) -> {
JmeDialogsFactory dialogFactory = null;
try {
- dialogFactory = (JmeDialogsFactory)Class.forName("com.jme3.system.JmeDialogsFactoryImpl").getConstructor().newInstance();
+ String dialogFactoryClass = System.getenv("JME3_DIALOGS_FACTORY");
+ if(dialogFactoryClass == null) {
+ dialogFactoryClass = System.getProperty("jme3.dialogsFactory","com.jme3.system.JmeDialogsFactoryImpl");
+ }
+ if(dialogFactoryClass != null && !dialogFactoryClass.trim().isEmpty()){
+ dialogFactory = (JmeDialogsFactory)Class.forName(dialogFactoryClass).getConstructor().newInstance();
+ }
} catch(ClassNotFoundException e){
logger.warning("JmeDialogsFactory implementation not found.");
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
@@ -79,7 +85,14 @@ public abstract class JmeSystemDelegate {
protected BiFunction settingsHandler = (settings,loadFromRegistry) -> {
JmeDialogsFactory dialogFactory = null;
try {
- dialogFactory = (JmeDialogsFactory)Class.forName("com.jme3.system.JmeDialogsFactoryImpl").getConstructor().newInstance();
+ String dialogFactoryClass = System.getenv("JME3_DIALOGS_FACTORY");
+ if(dialogFactoryClass == null) {
+ dialogFactoryClass = System.getProperty("jme3.dialogsFactory","com.jme3.system.JmeDialogsFactoryImpl");
+ }
+ if(dialogFactoryClass != null && !dialogFactoryClass.trim().isEmpty()){
+ dialogFactory = (JmeDialogsFactory)Class.forName(dialogFactoryClass).getConstructor().newInstance();
+ }
+ dialogFactory = (JmeDialogsFactory)Class.forName(dialogFactoryClass).getConstructor().newInstance();
} catch(ClassNotFoundException e){
logger.warning("JmeDialogsFactory implementation not found.");
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {