Updating both independent client windows

In a class extending SimpleApplication I’ve introduced a function like this.

//In Window extends SimpleApplication
    public void moveOpponent(String command) {...}

I’m trying to call moveOpponent from an observer in main, that is in another file. The observer definitely gets notified of network activity and I’m sure I reach it, but does not seem to call moveOpponent(). The calling code:

//Field:
private static Window window;

//in main:
window = new Window();
try{ //due to network
                    @Override
                    public void onObservableChanged(String commandReceived) {
                        System.out.println("This prints");
                        window.moveOpponent(commandReceived); //Nothing happens, why?
                    }
                };
} catch() {}

I was dumb and forgot to send over the network, I solved the above problem.

Now another problem! I’m running two jmonkey windows started in IntelliJ IDEA I see that if I do something in one window, the other window doesn’t update the new position of the player until I switch to the other window, then the player jumps into position. I’d like to immediately see the result in the other window.
Maybe this has to do with that I’m running on the main thread. I think I do at least, since I start the jmonkey window fram main. Perhaps this can be alleviated by running the window on a Thread? Any ideas what’s wrong? (I also have another thread listening (by blocking I think) for network but that should not affect this, right?)

Ok, I moved around in my two windows some more and got this lovely error.

Uncaught exception thrown in Thread[jME3 Main,5,main]
IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call. 
Make sure you do not modify the scene from another thread!
Problem spatial name: Root Node

Hmm, how would I tell the Window (from another class) to modify the position of opponent? Btw my simpleUpdate method is empty, I’m not doing anything there.

You should be able to change this functionality with this method you can call in the SimpleApplication

setPauseOnLostFocus(false);

2 Likes

The pause on lost focus is what you need. When JME window losses focus, it basically stops updating. You need to prevent that since it is on by default.

2 Likes

Thanks, I actually managed to see my player move in both windows for a short time, but then the error came back.

Uncaught exception thrown in Thread[jME3 Main,5,main]
IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call. 
Make sure you do not modify the scene from another thread!
Problem spatial name: Root Node

I’m having trouble understanding how I am modifying it from another thread. But, yeah the network listening code is on another thread. That network code calls an observer in the main class. In the observer I access the field member SimpleWindow to change the position of an opponent. Is that troublesome?

Yeah if you want to do anything that modifies, adds, or removes a scene objects thats attached to the rootNode or guiNode, then you need to enqueue those changes back to the main thread, or you will get that error.
Here’s a link to the wiki for a better explanation than I could probably give

So the easy way to solve this is to surround the code that is changing the position of a scene object with an app.enqueue call like this

//message receieved on networking thread {
      app.enqueue(() -> {
           yourSimpleWindow.setLocalTranslation(newPosition);                   
       });
2 Likes

Are you doing this to learn the basics of networking or is your intent to actually get to a working networked game?

If the latter then I recommend starting with tech that already works, see a demo of one such tech here:

If the former then you could still poke around at those examples to see what your future looks like if you want real time networking someday.

3 Likes

Thanks for the recommendation, but yes, the goal is to learn network programming and that is why I’m not using the network facilities of jME. I managed to get the server to control both players.

It’s worth reading these articles for a foundation of what you are in for:
https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization
https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking

You are dipping your toes into the shallow end of a very very deep pool so it pays to take a depth every now and then. It will also help you recognize the pitfalls when you find them.

Probably you are using TCP to start with and sending reliable commands. (From your code, I would guess that those are even strings maybe.)

Short term bumps you might hit are disabling “Nagle’s algorithm” to avoid outbound blocking/delays.

Later you will hit the “tyranny of the reliable connection”: that all packets will always be delivered… even if they are stale and even if the connection paused for 2-3 seconds.

For anything short of turn-based games, that can ruin your day.

3 Likes

Thanks for the link and the words of wisdom. Yes, I use TCP to send strings. The scope of my current challenge is not that extensive, so I’m fine with a simplified approach. We’ll see what the future holds…

Following your example and documentation, I managed to also delete a piece of geometry from the scene, proud. However, changing the label text is challenging.

I’ve followed the Lemur Getting started example and now have a situation similar to the “full source example” at the bottom of the page in the above link.

In jME’s simpleInitApp() I create the GUI:

        GuiGlobals.initialize(this);
        BaseStyles.loadGlassStyle();
        GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");
        Container myUiElements = new Container();
        myUiElements.setName("myUiElements");
        guiNode.attachChild(myUiElements);
        myUiElements.setLocalTranslation(0, 480, 0);
        Label myLabel = new Label("Initial label text");
        myLabel.setName("myLabel");
        myUiElements.addChild(myLabel);

Then, from my own networking code, which is outside of the class that extends jME’s SimpleApplication, I would like to modify the label’s text.

I tried

        final Node tempNode = (Node) clientWindow.getGuiNode().getChild("myUiElements");
        final Label tempLabel = tempNode.getChild("myLabel"); //This does not give me access to Label's .setText() method.

And, do I also have to “enqueue” it?

Whenever you modify the scene graph, you have to do it from the render/update thread. Lemur is part of the scene graph so that applies there also.

Your thing receiving network messages will need a reference to Application in order to enqueue things or you will have to have a more fleshed out threading architecture to deal with it.

My code does have a reference to the class extending SimpleApplication, I have already enqueued things twice with your enqueue example above. My problem now is referencing the label since. This does not give me access to Label’s .setText() method.

    final Node tempNode = (Node) clientWindow.getGuiNode().getChild("myUiElements");
    final Label tempLabel = tempNode.getChild("myLabel");

I don’t understand the question.

tempLabel.setText()

Either you are asking a very basic Java question or there is something about your setup that we don’t understand based on the pinhole view of your code.