Skip to content

Commit 0e25de2

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents 7efa4c6 + 8751505 commit 0e25de2

197 files changed

Lines changed: 9223 additions & 10444 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Screenshot Test PR Comment
2+
3+
# This workflow is designed to safely comment on PRs from forks
4+
# It uses pull_request_target which has higher permissions than pull_request
5+
# Security note: This workflow does NOT check out or execute code from the PR
6+
# It only monitors the status of the ScreenshotTests job and posts comments
7+
# (If this commenting was done in the main worflow it would not have the permissions
8+
# to create a comment)
9+
10+
on:
11+
pull_request_target:
12+
types: [opened, synchronize, reopened]
13+
14+
jobs:
15+
monitor-screenshot-tests:
16+
name: Monitor Screenshot Tests and Comment
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 60
19+
permissions:
20+
pull-requests: write
21+
contents: read
22+
steps:
23+
- name: Wait for GitHub to register the workflow run
24+
run: sleep 15
25+
26+
- name: Wait for Screenshot Tests to complete
27+
uses: lewagon/wait-on-check-action@v1.3.1
28+
with:
29+
ref: ${{ github.event.pull_request.head.sha }}
30+
check-name: 'Run Screenshot Tests'
31+
repo-token: ${{ secrets.GITHUB_TOKEN }}
32+
wait-interval: 10
33+
allowed-conclusions: success,skipped,failure
34+
- name: Check Screenshot Tests status
35+
id: check-status
36+
uses: actions/github-script@v6
37+
with:
38+
github-token: ${{ secrets.GITHUB_TOKEN }}
39+
script: |
40+
const { owner, repo } = context.repo;
41+
const ref = '${{ github.event.pull_request.head.sha }}';
42+
43+
// Get workflow runs for the PR
44+
const runs = await github.rest.actions.listWorkflowRunsForRepo({
45+
owner,
46+
repo,
47+
head_sha: ref
48+
});
49+
50+
// Find the ScreenshotTests job
51+
let screenshotTestRun = null;
52+
for (const run of runs.data.workflow_runs) {
53+
if (run.name === 'Build jMonkeyEngine') {
54+
const jobs = await github.rest.actions.listJobsForWorkflowRun({
55+
owner,
56+
repo,
57+
run_id: run.id
58+
});
59+
60+
for (const job of jobs.data.jobs) {
61+
if (job.name === 'Run Screenshot Tests') {
62+
screenshotTestRun = job;
63+
break;
64+
}
65+
}
66+
67+
if (screenshotTestRun) break;
68+
}
69+
}
70+
71+
if (!screenshotTestRun) {
72+
console.log('Screenshot test job not found');
73+
return;
74+
}
75+
76+
// Check if the job failed
77+
if (screenshotTestRun.conclusion === 'failure') {
78+
core.setOutput('failed', 'true');
79+
} else {
80+
core.setOutput('failed', 'false');
81+
}
82+
- name: Find Existing Comment
83+
uses: peter-evans/find-comment@v3
84+
id: existingCommentId
85+
with:
86+
issue-number: ${{ github.event.pull_request.number }}
87+
comment-author: 'github-actions[bot]'
88+
body-includes: Screenshot tests have failed.
89+
90+
- name: Comment on PR if tests fail
91+
if: steps.check-status.outputs.failed == 'true'
92+
uses: peter-evans/create-or-update-comment@v4
93+
with:
94+
issue-number: ${{ github.event.pull_request.number }}
95+
body: |
96+
🖼️ **Screenshot tests have failed.**
97+
98+
The purpose of these tests is to ensure that changes introduced in this PR don't break visual features. They are visual unit tests.
99+
100+
📄 **Where to find the report:**
101+
- Go to the (failed run) > Summary > Artifacts > screenshot-test-report
102+
- Download the zip and open jme3-screenshot-tests/build/reports/ScreenshotDiffReport.html
103+
104+
⚠️ **If you didn't expect to change anything visual:**
105+
Fix your changes so the screenshot tests pass.
106+
107+
✅ **If you did mean to change things:**
108+
Review the replacement images in jme3-screenshot-tests/build/changed-images to make sure they really are improvements and then replace and commit the replacement images at jme3-screenshot-tests/src/test/resources.
109+
110+
✨ **If you are creating entirely new tests:**
111+
Find the new images in jme3-screenshot-tests/build/changed-images and commit the new images at jme3-screenshot-tests/src/test/resources.
112+
113+
**Note;** it is very important that the committed reference images are created on the build pipeline, locally created images are not reliable. Similarly tests will fail locally but you can look at the report to check they are "visually similar".
114+
115+
See https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-screenshot-tests/README.md for more information
116+
edit-mode: replace
117+
comment-id: ${{ steps.existingCommentId.outputs.comment-id }}

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2009-2024 jMonkeyEngine.
1+
Copyright (c) 2009-2025 jMonkeyEngine.
22

33
Redistribution and use in source and binary forms, with or without
44
modification, are permitted provided that the following conditions

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Version number: Major.Minor.SubMinor (e.g. 3.3.0)
2-
jmeVersion = 3.8.0
2+
jmeVersion = 3.9.0
33

44
# Leave empty to autogenerate
55
# (use -PjmeVersionName="myVersion" from commandline to specify a custom version name )

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[versions]
44

55
checkstyle = "9.3"
6-
lwjgl3 = "3.3.3"
6+
lwjgl3 = "3.3.6"
77
nifty = "1.4.3"
88

99
[libraries]
@@ -14,7 +14,7 @@ androidx-annotation = "androidx.annotation:annotation:1.3.0"
1414
androidx-lifecycle-common = "androidx.lifecycle:lifecycle-common:2.4.0"
1515
gradle-git = "org.ajoberstar:gradle-git:1.2.0"
1616
gradle-retrolambda = "me.tatarka:gradle-retrolambda:3.7.1"
17-
groovy-test = "org.codehaus.groovy:groovy-test:3.0.22"
17+
groovy-test = "org.codehaus.groovy:groovy-test:3.0.24"
1818
gson = "com.google.code.gson:gson:2.9.1"
1919
j-ogg-vorbis = "com.github.stephengold:j-ogg-vorbis:1.0.6"
2020
jbullet = "com.github.stephengold:jbullet:1.0.3"
@@ -23,7 +23,7 @@ jna = "net.java.dev.jna:jna:5.10.0"
2323
jnaerator-runtime = "com.nativelibs4java:jnaerator-runtime:0.12"
2424
junit4 = "junit:junit:4.13.2"
2525
lwjgl2 = "org.jmonkeyengine:lwjgl:2.9.5"
26-
lwjgl3-awt = "org.lwjglx:lwjgl3-awt:0.1.8"
26+
lwjgl3-awt = "org.lwjglx:lwjgl3-awt:0.2.3"
2727

2828
lwjgl3-base = { module = "org.lwjgl:lwjgl", version.ref = "lwjgl3" }
2929
lwjgl3-glfw = { module = "org.lwjgl:lwjgl-glfw", version.ref = "lwjgl3" }

jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2023 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -247,10 +247,19 @@ protected void deinitInThread() {
247247
}
248248

249249
listener.destroy();
250-
250+
// releases the view holder from the Android Input Resources
251+
// releasing the view enables the context instance to be
252+
// reclaimed by the GC.
253+
// if not released; it leads to a weak reference leak
254+
// disabling the destruction of the Context View Holder.
255+
androidInput.setView(null);
256+
257+
// nullifying the references
258+
// signals their memory to be reclaimed
251259
listener = null;
252260
renderer = null;
253261
timer = null;
262+
androidInput = null;
254263

255264
// do android specific cleaning here
256265
logger.fine("Display destroyed.");

jme3-android/src/main/java/com/jme3/view/surfaceview/JmeSurfaceView.java

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2022 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -239,11 +239,7 @@ public void startRenderer(int delayMillis) {
239239
}
240240

241241
private void removeGLSurfaceView() {
242-
((Activity) getContext()).runOnUiThread(() -> {
243-
if (glSurfaceView != null) {
244-
JmeSurfaceView.this.removeView(glSurfaceView);
245-
}
246-
});
242+
((Activity) getContext()).runOnUiThread(() -> JmeSurfaceView.this.removeView(glSurfaceView));
247243
}
248244

249245
@Override
@@ -265,19 +261,34 @@ public void handleError(String errorMsg, Throwable throwable) {
265261
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
266262
switch (event) {
267263
case ON_DESTROY:
268-
/*destroy only if the policy flag is enabled*/
269-
if (destructionPolicy == DestructionPolicy.DESTROY_WHEN_FINISH) {
270-
legacyApplication.stop(!isGLThreadPaused());
271-
}
264+
// activity is off the foreground stack
265+
// activity is being destructed completely as a result of Activity#finish()
266+
// this is a killable automata state!
267+
jmeSurfaceViewLogger.log(Level.INFO, "Hosting Activity has been destructed.");
272268
break;
273269
case ON_PAUSE:
274-
loseFocus();
270+
// activity is still on the foreground stack but not
271+
// on the topmost level or before transition to stopped/hidden or destroyed state
272+
// as a result of dispatch to Activity#finish()
273+
// activity is no longer visible and is out of foreground
274+
if (((Activity) getContext()).isFinishing()) {
275+
if (destructionPolicy == DestructionPolicy.DESTROY_WHEN_FINISH) {
276+
legacyApplication.stop(!isGLThreadPaused());
277+
} else if (destructionPolicy == DestructionPolicy.KEEP_WHEN_FINISH) {
278+
jmeSurfaceViewLogger.log(Level.INFO, "Context stops, but game is still running.");
279+
}
280+
} else {
281+
loseFocus();
282+
}
275283
break;
276284
case ON_RESUME:
285+
// activity is back to the topmost of the
286+
// foreground stack
277287
gainFocus();
278288
break;
279289
case ON_STOP:
280-
jmeSurfaceViewLogger.log(Level.INFO, "Context stops, but game is still running");
290+
// activity is out off the foreground stack or being destructed by a finishing dispatch
291+
// this is a killable automata state!
281292
break;
282293
}
283294
}
@@ -404,13 +415,13 @@ public void loseFocus() {
404415

405416
@Override
406417
public void destroy() {
407-
/*skip the destroy block if the invoking instance is null*/
408-
if (legacyApplication == null) {
409-
return;
418+
if (glSurfaceView != null) {
419+
removeGLSurfaceView();
420+
}
421+
if (legacyApplication != null) {
422+
legacyApplication.destroy();
410423
}
411-
removeGLSurfaceView();
412-
legacyApplication.destroy();
413-
/*help the Dalvik Garbage collector to destruct the pointers, by making them nullptr*/
424+
/*help the Dalvik Garbage collector to destruct the objects, by releasing their references*/
414425
/*context instances*/
415426
legacyApplication = null;
416427
appSettings = null;
@@ -430,10 +441,10 @@ public void destroy() {
430441
onRendererCompleted = null;
431442
onExceptionThrown = null;
432443
onLayoutDrawn = null;
433-
/*nullifying the static memory (pushing zero to registers to prepare for a clean use)*/
434444
GameState.setLegacyApplication(null);
435445
GameState.setFirstUpdatePassed(false);
436-
jmeSurfaceViewLogger.log(Level.INFO, "Context and Game have been destructed");
446+
JmeAndroidSystem.setView(null);
447+
jmeSurfaceViewLogger.log(Level.INFO, "Context and Game have been destructed.");
437448
}
438449

439450
@Override
@@ -516,11 +527,13 @@ public void bindAppStateToActivityLifeCycle(final boolean condition) {
516527
/*register this Ui Component as an observer to the context of jmeSurfaceView only if this context is a LifeCycleOwner*/
517528
if (getContext() instanceof LifecycleOwner) {
518529
((LifecycleOwner) getContext()).getLifecycle().addObserver(JmeSurfaceView.this);
530+
jmeSurfaceViewLogger.log(Level.INFO, "Command binding SurfaceView to the Activity Lifecycle.");
519531
}
520532
} else {
521533
/*un-register this Ui Component as an observer to the context of jmeSurfaceView only if this context is a LifeCycleOwner*/
522534
if (getContext() instanceof LifecycleOwner) {
523535
((LifecycleOwner) getContext()).getLifecycle().removeObserver(JmeSurfaceView.this);
536+
jmeSurfaceViewLogger.log(Level.INFO, "Command removing SurfaceView from the Activity Lifecycle.");
524537
}
525538
}
526539
}
@@ -917,7 +930,7 @@ public void setShowErrorDialog(boolean showErrorDialog) {
917930
}
918931

919932
/**
920-
* Determines whether the app context would be destructed
933+
* Determines whether the app context would be destructed as a result of dispatching {@link Activity#finish()}
921934
* with the holder activity context in case of {@link DestructionPolicy#DESTROY_WHEN_FINISH} or be
922935
* spared for a second use in case of {@link DestructionPolicy#KEEP_WHEN_FINISH}.
923936
* Default value is : {@link DestructionPolicy#DESTROY_WHEN_FINISH}.
@@ -926,12 +939,14 @@ public void setShowErrorDialog(boolean showErrorDialog) {
926939
*/
927940
public enum DestructionPolicy {
928941
/**
929-
* Finishes the game context with the activity context (ignores the static memory {@link GameState#legacyApplication}).
942+
* Finishes the game context with the activity context (ignores the static memory {@link GameState#legacyApplication})
943+
* as a result of dispatching {@link Activity#finish()}.
930944
*/
931945
DESTROY_WHEN_FINISH,
932946
/**
933947
* Spares the game context inside a static memory {@link GameState#legacyApplication}
934-
* when the activity context is destroyed, but the app stills in the background.
948+
* when the activity context is destroyed dispatching {@link Activity#finish()}, but the {@link android.app.Application}
949+
* stills in the background.
935950
*/
936951
KEEP_WHEN_FINISH
937952
}

jme3-core/src/main/java/com/jme3/anim/AnimComposer.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2024 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -313,24 +313,21 @@ public Action removeAction(String name) {
313313
/**
314314
* Add a layer to this composer.
315315
*
316-
* @param name The desired name for the new layer
317-
* @param mask The desired mask for the new layer (alias created)
318-
* @return a new layer
316+
* @param name the desired name for the new layer
317+
* @param mask the desired mask for the new layer (alias created)
319318
*/
320-
public AnimLayer makeLayer(String name, AnimationMask mask) {
319+
public void makeLayer(String name, AnimationMask mask) {
321320
AnimLayer l = new AnimLayer(name, mask);
322321
layers.put(name, l);
323-
return l;
324322
}
325323

326324
/**
327325
* Remove specified layer. This will stop the current action on this layer.
328326
*
329327
* @param name The name of the layer to remove.
330-
* @return The removed layer.
331328
*/
332-
public AnimLayer removeLayer(String name) {
333-
return layers.remove(name);
329+
public void removeLayer(String name) {
330+
layers.remove(name);
334331
}
335332

336333
/**

0 commit comments

Comments
 (0)