Major Threadproblem with Gamestate

Hi, i have a really frustrating problem :frowning:



I have a client that should react to events and add objects to the gameworld.

Here is the code for the Gamestate (the important part is BOLD)

package de.mbws.client.state;

import java.util.HashMap;

import com.jme.app.StandardGameState;
import com.jme.bounding.BoundingBox;
import com.jme.input.ChaseCamera;
import com.jme.input.InputHandler;
import com.jme.input.MouseInput;
import com.jme.input.thirdperson.ThirdPersonMouseLook;
import com.jme.light.DirectionalLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.shape.Box;
import com.jme.scene.state.LightState;
import com.jme.system.DisplaySystem;

import de.mbws.client.data.ClientPlayerData;
import de.mbws.client.data.ObjectNode;
import de.mbws.client.state.handler.TestGameHandler;
import de.mbws.common.eventdata.generated.WorldObject;

public class TestGameState extends StandardGameState {

private Node player;

Node player2;

// private ChaseCamera chaser;
protected InputHandler input;

protected DisplaySystem display;

protected Skybox skybox;

// The chase camera, this will follow our player as he zooms around the
// level
private ChaseCamera chaser;

public volatile boolean add = false;

public volatile int i = 0;

public TestGameState(String name) {
super(name);
this.display = DisplaySystem.getDisplaySystem();

// Light the world
buildLighting();
// Build the player
buildPlayer();

// build the chase cam
buildChaseCamera();
// build the player input
buildInput();
// addPlayer();
// just for testing we added some box as secondary player
// addPlayer();
// update the scene graph for rendering
rootNode.updateGeometricState(0.0f, true);
rootNode.updateRenderState();

TestThread t = new TestThread();
t.start();

}

private void buildInput() {
input = new TestGameHandler(player, null);
}

/**
* we are going to build the player object here. For now, we will use a box
* as a place holder. This is a good demonstration that you don't always
* need your graphics in place before you can start working on your
* application.
*
*/
private void buildPlayer() {
// box stand in
Box b = new Box("box", new Vector3f(), 0.35f, 0.25f, 0.5f);
b.setModelBound(new BoundingBox());
b.updateModelBound();

player = new Node("Player Node");
// player.setLocalTranslation(new Vector3f(100, 0, 100));
Vector3f location = new Vector3f(ClientPlayerData.getInstance()
.getCharacterData().getCharacterStatus().getCoordinateX(),
ClientPlayerData.getInstance().getCharacterData()
.getCharacterStatus().getCoordinateY(),
ClientPlayerData.getInstance().getCharacterData()
.getCharacterStatus().getCoordinateZ());
player.setLocalTranslation(location);

rootNode.attachChild(player);
player.attachChild(b);
player.updateWorldBound();
}

public void updatePlayer() {
Vector3f location = new Vector3f(ClientPlayerData.getInstance()
.getCharacterData().getCharacterStatus().getCoordinateX(),
ClientPlayerData.getInstance().getCharacterData()
.getCharacterStatus().getCoordinateY(),
ClientPlayerData.getInstance().getCharacterData()
.getCharacterStatus().getCoordinateZ());
player.setLocalTranslation(location);

}

// TODO REMOVE BELOW
public void addPlayer() {
// box stand in
Box b = new Box("box", new Vector3f(), 0.35f, 0.5f, 0.25f);
b.setDefaultColor(new ColorRGBA(ColorRGBA.white));
b.setModelBound(new BoundingBox());
b.updateModelBound();

player2 = new Node("Player2 Node");
player2.setLocalTranslation(new Vector3f(100, 0, 100));

rootNode.attachChild(player2);
player2.attachChild(b);
player2.updateWorldBound();
}


public void movePlayer(Vector3f newLocation) {
player2.setLocalTranslation(newLocation);
} // TODO: REMOVE ABOVE

/**
* creates a light for the terrain.
*/
private void buildLighting() {
/** Set up a basic, default light. */
DirectionalLight light = new DirectionalLight();
light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
light.setDirection(new Vector3f(1, -1, 0));
light.setEnabled(true);

/** Attach the light to a lightState and the lightState to rootNode. */
LightState lightState = display.getRenderer().createLightState();
lightState.setEnabled(true);
lightState.attach(light);
rootNode.setRenderState(lightState);
}

/**
* set the basic parameters of the chase camera. This includes the offset.
* We want to be behind the vehicle and a little above it. So we will the
* offset as 0 for x and z, but be 1.5 times higher than the node.
*
* We then set the roll out parameters (2 units is the closest the camera
* can get, and 5 is the furthest).
*
*/
private void buildChaseCamera() {
Vector3f targetOffset = new Vector3f();
targetOffset.y = ((BoundingBox) player.getWorldBound()).yExtent * 1.5f;
HashMap props = new HashMap();
props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "40");
props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "3");
props.put(ChaseCamera.PROP_TARGETOFFSET, targetOffset);
chaser = new ChaseCamera(cam, player, props);
chaser.setActionSpeed(100f);
}

/**
* This is where derived classes are supposed to put their game logic. Gets
* called between the input.update and rootNode.updateGeometricState calls.
*
* <p>
* Much like the structure of <code>SimpleGame</code>.
* </p>
*
* @param tpf
*            The time since the last frame.
*/
protected void stateUpdate(float tpf) {
// update the keyboard input (move the player around)
input.update(tpf);
// update the chase camera to handle the player moving around.
chaser.update(tpf);

float camMinHeightPlayer = player.getWorldTranslation().y + 2f;
cam.getLocation().y = camMinHeightPlayer;
cam.update();

if (add) {
addPlayer();
add = false;
}


rootNode.updateGeometricState(tpf, true);
}

/**
* This is where derived classes are supposed to put their render logic.
* Gets called before the rootNode gets rendered.
*
* <p>
* Much like the structure of <code>SimpleGame</code>.
* </p>
*
* @param tpf
*            The time since the last frame.
*/
protected void stateRender(float tpf) {
display.getRenderer().clearBuffers();
super.stateRender(tpf);
}

/**
* @see com.jme.app.StandardGameState#onActivate()
*/
public void onActivate() {
display.setTitle("Test Game State System - Game State");
// TODO: SUCK MY DICK WHAT A HECK OF A BUG !!!! Take that out
// later when all works with a correct mouse
MouseInput.get().setCursorVisible(false);
super.onActivate();
}

public ObjectNode addObject(WorldObject objectInfo) {
Box b = new Box("box2", new Vector3f(), 0.35f, 0.25f, 0.5f);
b.setDefaultColor(new ColorRGBA(ColorRGBA.white));
b.setModelBound(new BoundingBox());
b.updateModelBound();
ObjectNode n = new ObjectNode("test");// +objectInfo.getObjectID());

// IntVector3D l = objectInfo.getLocation();
Vector3f location = new Vector3f(new Vector3f(100, 0, 100));// l.getX(),l.getY(),l.getZ());
n.setLocalTranslation(location);

// IntVector3D h = objectInfo.getHeading();
// Vector3f rotation = new Vector3f(h.getX(),h.getY(),h.getZ());
// n.setLocalTranslation(rotation);

rootNode.attachChild(n);
n.attachChild(b);
n.updateWorldBound();
rootNode.updateGeometricState(0.0f, true);
rootNode.updateRenderState();
// display.getRenderer().clearBuffers();
// display.getRenderer().draw(rootNode);

return n;
}

public void deleteObject(ObjectNode node) {
rootNode.detachChild(node);
}

class TestThread extends Thread {
int count = 0;

public void run() {
try {

while (add != true) {
Thread.sleep(20);

count++;
if (count == 1000) {

add = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}


}

As you can see i use an internal threadclass to set a boolean variable. If that is true a new box is inserted into the scene.
This works !!

Now if i take that threadclass and put it in another thread (an eventlistener used for networkevents triggering such a spawning) then strange things happen.
Here the modified code of the threadclass:

class TestThread extends Thread {
int count = 0;

public void run() {
try {
boolean add = false;
while (add != true) {
Thread.sleep(20);
TestGameState gameState = (TestGameState) GameStateManager
.getInstance().getChild("game");
count++;
if (count == 1000) {
gameState.add = true;
add = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}


Now what happens is the following:
in BOTH cases add is set to true and in BOTH cases addPlayer gets called by the stateUpdate method in TestGameState.
In BOTH cases the debug messages tell me that the second box was added.

But in the second case the box NEVER appears on the screen !!!

I simply have arrived at the point where i cant seem to see any further hint at what the problem could be :(

Any help appreciated.

as an alternative i tried to modify the internal Thread (first example) like this:


static class TestThread extends Thread {
int count = 0;

public void run() {
try {
boolean add = false;
while (add != true) {
Thread.sleep(20);
TestGameState gameState = (TestGameState) GameStateManager
.getInstance().getChild("game");

count++;
if (count == 1000) {
gameState.add = true;
add = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}


Since it is static i need a reference to the testgamestate by calling gamestatemanager.
And here the problem ALSO occurs. The debug tells me the node is there !
But it doesnt appear.

OpenGL is not threadsafe, so there's little point in LWJGL being threadsafe, which is why jME is not threadsafe.



See here: http://www.jmonkeyengine.com/jmeforum/index.php?topic=2428.0



It's not too hard to synchronize an action like yours to run in the jME thread. I'll give you an example of how you could do this.



This is assumes you use SimpleGame, but you can replace SimpleGame's simpleUpdate() etc. with the regular update calls. Put this code in your (Simple)Game derived class:



public static Thread jMEThread;
LinkeList runlist = new LinkedList();

public static boolean execInMainThread(Runnable runnable) {
 if (Thread.getCurrent() == jMEThread) return false;
 synchronized(this.runlist) {
 

Ok, my fault :wink:



YOu see my problem initially is NOT to have some Thread that loops and does something.

My problem is a different one.

Initially i tried to modify the rootNode in the Gamestate through a different Thread related to Networking. Each time I got an event i tried to get the rootNode and manipulated it. Didnt work of course because as someone mentioned here in the forum, one should modify everything in the state.



So i thought for a test i only use a "flag".



So now every time i get the event i set a flag "add".

And the GameStateThread ITSELF looks for that flag and then adds the box to the rootNode.



So actually there is no interference from another Thread when it comes to the scene itself.



Now what i do not understand is why the Gamestate actually ACCEPTS the change and tells me it does add a box (remember i dont call the Gamestate from anywhere, its looping on its own) and doesnt display it !





Or to put it differently.



If i have a Game and i want to insert new objects, delete them or move them based on events i get through a different Thread … how do i do that ?

Obviously flags dont seem to work, direkt calls to methods dont work either.



So how would you do that ?

Do i need some synchronized Queue ?

You could use the code I showed you, combined with an anonymous inner class that extends runnable.

I solved the problem…

Actually all that code was working wonderfully fine …



It was one of the "obvious mistakes".



In order to start the gamestate i was listening to another event and set a boolean.

And my MenuState (listening for that boolean) simply created 3 GameStates instead of of one because the updatemethod was called and i had forgotton to set the boolean to false again. Thats why it didnt work.





Actually i am almost embarassed :-o