How to modify the scene from another thread

Hello mates! I am attempting to modify a Character Control from the Spidermonkey message listener but I run across the "State was changed after rootNode.updateGeometricState() call. " error. Though the error is self explanatory, I don’t quite understand how to remedy it. I need the server to update the client constantly based on messages received, so I tried to edit the scene through the message listener, but obviously that didn’t work out too well. Here is an example of the code that causes this error:
[java] Users.get(Username).getControl(CharacterControl.class).setPhysicsLocation(userLocation);[/java]
Where Users is the string to node map.
Is there anyway I would be able to do this? Thanks for all your help

https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:multithreading

I have read and reread the Wiki page a few times but still cant understand it. How do I create a callable for:
[java] private void userUpdater(String Username, int Type, Vector3f Vector) {
if (Type == 1) {
Users.get(Username).getControl(CharacterControl.class).setPhysicsLocation(Vector);
System.out.println(Username + " IS LOCATED AT " + Vector);
} else if (Type == 2) {
Users.get(Username).getControl(CharacterControl.class).setWalkDirection(Vector);
System.out.println(Username + " IS WALKING TOWARDS " + Vector);
} else if (Type == 3) {
Users.get(Username).getControl(CharacterControl.class).setViewDirection(Vector);
System.out.println(Username + " IS ROTATED TOWARDS " + Vector);
}
}[/java]
Is there any full examples out there that I can access (excluding the wiki one of course)? I have searched the web but have found nothing.

if you don’t want the multithreading gain (for exemple if you don’t send a lot of messages through your network, or if wall these messages WILL modify the scene) you can use my class :

[java]
/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.
    */
    package real.common.tools;

import com.jme3.network.Message;
import com.jme3.network.MessageListener;
import com.jme3.network.base.MessageListenerRegistry;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import kapplication.KAppState;
import real.RealGame;

/**
*

public MessageDispatcher()
{
messageListenerRegistry = new MessageListenerRegistry<>();
enqueuedMessages = new ConcurrentLinkedDeque<>();
listeners = new ArrayList<>();
}

public void addMessageListener(MessageListener<S> listener)
{
this.messageListenerRegistry.addMessageListener(listener);
this.listeners.add(listener);
}

public void addMessageListener(MessageListener<S> listener, Class … classes)
{
this.messageListenerRegistry.addMessageListener(listener, classes);
this.listeners.add(listener);
}

public void removeMessageListener(MessageListener<? super S> listener)
{
this.messageListenerRegistry.removeMessageListener(listener);
this.listeners.remove(listener);
}

public void removeMessageListener(MessageListener<? super S> listener, Class … classes)
{
this.messageListenerRegistry.removeMessageListener(listener, classes);
this.listeners.remove(listener);
}

public void addMessageReceiver(AbstractMessageReceiver<?, S, A> receiver)
{
receiver.setApplication(application());
addMessageListener(receiver, receiver.messageClass());
}

@Override
public void messageReceived(S source, Message m)
{
EnqueuedMessage em = new EnqueuedMessage();
em.source = source;
em.message = m;

enqueuedMessages.addLast(em);

}

@Override
public void update(float tpf)
{
while(! enqueuedMessages.isEmpty())
{
EnqueuedMessage em = enqueuedMessages.removeFirst();
messageListenerRegistry.messageReceived(em.source, em.message);
}
}

public void clear()
{
while(!listeners.isEmpty())
{
MessageListener listener = listeners.get(0);
removeMessageListener(listener);
}
}

private class EnqueuedMessage
{
private Message message;
private S source;
}
}
[/java]

You’ll have to adapt it.
So, for the record :
KAppState is basically an appstate, i think you can replace it directly (it only holds a reference to the application).
RealGame is the abstract class for RealClient and RealServer, but it’s more or less a SimpleApplication.
MessageReceiver is MessageListener dedicated to one message type (where a messagelistener can handle a lot of messages). You can remove it, likely.

The general idea is : when a message arrive, you enqueue it in the message dispatcher (so the message dispatcher is the only object that listen the network directly. Where you have a lot (or just some)

[java]
client.addMessageListener(aMessageListener, someClass);
[/java]

You have now one
[java]
client.addMessageListener(theMessageDispatcher);

//and somewhere
stateManager.addState(theMessageDispatcher)
[/java]
And a lot of

[java]
theMessageDispatcher.addMessageListener(aMessageListener, someClass)
[/java]
For this part, it doesn’t change a lot of thing for you. But what it does is this : every messages are enqueued to the message dispatcher, and during the main’s loop, the update method of the message dispatcher is called and this method flush the list and perform the enqueued message. So, you can now do whatever you want to the scene, as you ARE in the main loop.

Note that you still can have some messagelistener outside of this mechanism, if they don’t need to access the scene (just don’t add them to the message dispatcher, just add them to the client/server, as before).

EDIT : @pspeed and @normen or @ anyone else working on the core of jme : this kind of situation is a perfect example why i would love to have an “Enqueueable” interface with only one method “enqueue” in it, and have all threads (the main loop, the physics thread, the network thread and anything like that that could come later) implementing it. In that way, if i need to modify the scenegraph, the network and the physics part i could have something like that:

[java]

void methodInitiallyCalled()
{
//do some stuff

application.enqueue(this);
bullet.enqueue(this);
network.enqueue(this);
}

void physicsJob()
{
//do physic stuff
}

void networkJob()
{
//etc.
}

// etc.

Future call()
{
if ( currentThreadIsBullet() )
{
physicsJob()
}
else if ( currentThreadIsNetwork() )
{
networkJob()
}
etc.
}
[/java]

You can even have a case where the enqueue on the scene is done only in the physicsJob, so i can modify then sequentially.
I know that it’s a “deep” modification (as it implies modification on 3 major class) but the modification is only : add an interface and give a simple implementation for it (i.e. enqueue the callable etc.) then call enqueued callables at the end of the loop of the thread. It will not break anything (i think), it’s not something very hard to do and it offers an easy way to do things in many thread in a thread-safe way.

1 Like

That would probably be something like
[java]
private void userUpdater(final String Username, final int Type, final Vector3f Vector) {
app.enqueue(new Callable() {
public Object call() {
if (Type == 1) {
Users.get(Username).getControl(CharacterControl.class).setPhysicsLocation(Vector);
System.out.println(Username + " IS LOCATED AT " + Vector);
} else if (Type == 2) {
Users.get(Username).getControl(CharacterControl.class).setWalkDirection(Vector);
System.out.println(Username + " IS WALKING TOWARDS " + Vector);
} else if (Type == 3) {
Users.get(Username).getControl(CharacterControl.class).setViewDirection(Vector);
System.out.println(Username + " IS ROTATED TOWARDS " + Vector);
}
return null;
}
});
}
[/java]
I’m not sure if there’s a better way to handle the type of the call-function and to avoid the “return null;”, but it should work this way. app should be your SimpleApplication.

2 Likes

I like Bubuche’s method quite a bit but I am having trouble implementing it. In the meantime, as a temp fix while I work out the kinks, I am trying to implement Toboi’s method but I receive the following error: ”non-static method <V>enqueue(Callable<V>) cannot be referenced from a static context Where <V> is a type-variable”
Any ideas on how to remedy it?

EDIT: It turns out, all I had to do was change the simple app reference to “this”. Thanks for al your help guys!

@IdreesInc said: I like Bubuche's method quite a bit but I am having trouble implementing it. In the meantime, as a temp fix while I work out the kinks, I am trying to implement Toboi's method but I receive the following error: ”non-static method <V>enqueue(Callable<V>) cannot be referenced from a static context Where <V> is a type-variable” Any ideas on how to remedy it?

Well, it sounds like you are running it from a strange place… so we would have to see the code. Like, you are trying to run enqueue as a static method or something.

Edit: ninja’ed by OP.

Haha thanks for trying to help anyway mate!