Well, I've come up with an unfortunate problem that I guess I should have realized would be a potential problem. ConcurrentModificationExceptions occurring because jME-Physics makes updates to jME within its thread make it impossible at the current time to do multithreading with jME-Physics. I wrote this to try to support multithreading in a powerful way in jME-Physics and got slapped:
package test;
import java.util.concurrent.locks.*;
import java.util.logging.*;
import com.jme.util.*;
import com.jmex.game.state.*;
import com.jmex.physics.*;
/**
* <code>PhysicsGameState</code> provides physics encapsulation into a GameState.
*
* @author Matthew D. Hicks
*/
public class PhysicsGameState extends BasicGameState {
private PhysicsSpace physics;
private PhysicsThread thread;
public PhysicsGameState(String name) {
super(name);
physics = PhysicsSpace.create();
thread = new PhysicsThread(physics, 60);
new Thread(thread).start();
}
public PhysicsSpace getPhysicsSpace() {
return physics;
}
public void setActive(boolean active) {
super.setActive(active);
thread.setEnabled(active);
}
public void shutdown() {
thread.shutdown();
}
public void lock() {
thread.lock();
}
public void unlock() {
thread.unlock();
}
}
class PhysicsThread implements Runnable {
private PhysicsSpace physics;
private long preferredTicksPerFrame;
private boolean enabled;
private boolean keepAlive;
private Timer timer;
private boolean limitUpdates;
private Lock updateLock;
public PhysicsThread(PhysicsSpace physics, int desiredUpdatesPerSecond) {
this.physics = physics;
enabled = false;
keepAlive = true;
updateLock = new ReentrantLock(true); // Make our lock be fair (first come, first serve)
timer = new NanoTimer();
if (desiredUpdatesPerSecond == -1) {
limitUpdates = false;
} else {
preferredTicksPerFrame = Math.round((float)timer.getResolution() / (float)desiredUpdatesPerSecond);
limitUpdates = true;
}
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void run() {
// We have to lock it up while we're starting
lock();
long frameStartTick = 0;
long frameDurationTicks = 0;
float tpf;
while (keepAlive) {
if (limitUpdates) {
frameStartTick = timer.getTime();
}
timer.update();
tpf = timer.getTimePerFrame();
update(tpf);
if ((limitUpdates) && (preferredTicksPerFrame >= 0)) {
frameDurationTicks = timer.getTime() - frameStartTick;
while (frameDurationTicks < preferredTicksPerFrame) {
long sleepTime = ((preferredTicksPerFrame - frameDurationTicks) * 1000) / timer.getResolution();
try {
Thread.sleep(sleepTime);
} catch (InterruptedException exc) {
LoggingSystem.getLogger().log(Level.SEVERE, "Interrupted while sleeping in fixed-framerate",
exc);
}
frameDurationTicks = timer.getTime() - frameStartTick;
}
}
}
}
public void update(float tpf) {
if (!enabled) return;
// Open the lock up for any work that needs to be done
unlock();
// Now lock up again so nothing can happen while we're updating
lock();
physics.update(tpf);
}
public void lock() {
updateLock.lock();
}
public void unlock() {
updateLock.unlock();
}
public void shutdown() {
keepAlive = false;
}
}
Essentially what has to happen is either maybe have jME-Physics provide an interface that can be called before and after a change is going to be made to the jME scenegraph, or it needs to be able to somehow pass the changes through GameTaskQueue (which SimpleGame does support directly now BTW).
I'd love to be able to use this code I've written as it would make the game operate SO much smoother on hyperthreaded/multicore/multiprocessor machines.