Android/Physics - Comparison Violates General Contract

Howdy friends,

I’ve been upgrading some of my old applications to the new Android SDK / JME 3.2.4. This error does not occur on PC.

I’ve gotten the following error on open for one of my games:

JNI DETECTED ERROR IN APPLICATION: JNI Throw called with pending exception java.lang.IllegalArgumentException: Comparison method violates its general contract!
at void java.util.TimSort.mergeHi(int, int, int, int) (TimSort.java:899)
at void java.util.TimSort.mergeAt(int) (TimSort.java:516)
at void java.util.TimSort.mergeCollapse() (TimSort.java:441)
at void java.util.TimSort.sort(java.lang.Object[], int, int, java.util.Comparator, java.lang.Object[], int, int) (TimSort.java:245)
at void java.util.Arrays.sort(java.lang.Object[], int, int, java.util.Comparator) (Arrays.java:1523)
at void java.util.Collections.sort(java.util.List, java.util.Comparator) (Collections.java:238)
at java.util.List com.jme3.bullet.PhysicsSpace.rayTest(com.jme3.math.Vector3f, com.jme3.math.Vector3f, java.util.List) (PhysicsSpace.java:1073)
at java.util.List com.jme3.bullet.PhysicsSpace.rayTest(com.jme3.math.Vector3f, com.jme3.math.Vector3f) (PhysicsSpace.java:1006)
at void com.jme3.bullet.control.BetterCharacterControl.checkOnGround() (BetterCharacterControl.java:503)
at void com.jme3.bullet.control.BetterCharacterControl.prePhysicsTick(com.jme3.bullet.PhysicsSpace, float) (BetterCharacterControl.java:200)
at void com.jme3.bullet.PhysicsSpace.preTick_native(float) (PhysicsSpace.java:300)
at void com.jme3.bullet.PhysicsSpace.stepSimulation(long, float, int, float) (PhysicsSpace.java:-2)
at void com.jme3.bullet.PhysicsSpace.update(float, int) (PhysicsSpace.java:480)
at void com.jme3.bullet.PhysicsSpace.update(float) (PhysicsSpace.java:465)
at void com.jme3.bullet.BulletAppState.render(com.jme3.renderer.RenderManager) (BulletAppState.java:398)
at void com.jme3.app.state.AppStateManager.render(com.jme3.renderer.RenderManager) (AppStateManager.java:300)
at void com.jme3.app.SimpleApplication.update() (SimpleApplication.java:250)
at void com.jme3.app.AndroidHarness.update() (AndroidHarness.java:496)
at void com.jme3.system.android.OGLESContext.onDrawFrame(javax.microedition.khronos.opengles.GL10) (OGLESContext.java:342)
at void android.opengl.GLSurfaceView$GLThread.guardedRun() (GLSurfaceView.java:1562)
at void android.opengl.GLSurfaceView$GLThread.run() (GLSurfaceView.java:1262)
in call to Throw
from void com.jme3.bullet.PhysicsSpace.stepSimulation(long, float, int, float)
at com.jme3.bullet.PhysicsSpace.stepSimulation(Native method)
at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:480)
at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:465)
at com.jme3.bullet.BulletAppState.render(BulletAppState.java:398)
at com.jme3.app.state.AppStateManager.render(AppStateManager.java:300)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:250)
at com.jme3.app.AndroidHarness.update(AndroidHarness.java:496)
at com.jme3.system.android.OGLESContext.onDrawFrame(OGLESContext.java:342)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1562)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1262)
art/runtime/runtime.cc:419] Runtime aborting…
art/runtime/runtime.cc:419] Aborting thread:

If this stack trace makes sense to anyone it help would be much appreciated.

Thanks for reading,

~BigBob

1 Like

It could be random so it’s possible that it happens on desktop and you’ve just never seen it.

It’s usually an indication that the criteria for the sort has changed during the sort… normally I’d suspect changing Geometries and stuff during the sort. In this case, it’s strange, though… since it’s part of sorting the Ray Test results.

Collections.sort(results, hitFractionComparator);

…so then the other case would be that the comparator returns inconsistent results. So compare(a, b) may return a < b but compare(b, a) returns b <= a.

Interestingly, an aside: it looks like we somehow let phroot checkin C#/VisualBasic style method names: https://github.com/jMonkeyEngine/jmonkeyengine/commit/614bffa9ae2d7a5bd70aafd6709dfd80aa5065bc

1 Like

Looking at the comparator:

Most likely cause is that one or the other hit fraction is NaN. Then comp will be NaN and comp > 0 will be false no matter which way r1 and r2 are compared.

1 Like
return comp != comp ? -1 : comp > 0 ? 1 : -1;

just me guessing?

No. Because then if you compare(r1, r2) you will get -1 and if you compare(r2, r1) you will also get -1… which is telling the sort() method that a < b and b < a… which is what it’s complaining about.

Probably, OP should figure out why collisions are resulting in NaNs… but the proper place to fix it is probably in the collision results which shouldn’t allow NaN in the first place… and maybe treat it like +Infinity instead.

1 Like

Hit fraction should always be between 0 and 1.