I started working with the Spidermonkey API, and I created a server and a client classes. When two clients join, the game crashes due to a NullPointerException. I did some testing, and it appears that the bug is random…
Basically, I have the Client1 and Client2. The Client1 gets notified about a new player, and it adds a model for him. Client2 receives a list of all player connected, and does the same thing. When I send the position (and the rotation and color, since i’m using random colors) of Client1, the Client2 receives it. The problem is that Client2 is also receiving info from Client2. But is not like organized. For example, receives from client1, client2, client1, client1, client2, client1, client2… I looked for the code 3 days, checked if Filters.notEqualTo(client) was there, and already tried two different solutions. I cant fix this.
So, I’m asking if someone can look on my code and see where I am messing up. The project is still not big, but analyzing the code may take hours and even days, so I’ll wait. I just ask if someone starts to debug the code, to please inform me, so I know if this topic is being watched or is dead.
The project folder (the code is in portuguese. if you need an english translation, ask me ;))
Well, it basically just returns a NullPointerException, because the client receives his own info. That is not supposed to happen, so I don’t have any class variables that register that info, and at the moment of using the information, it crashes (i can’t explain it very well… as I said, the bug seems to be random…).
Mai 13, 2015 6:57:27 PM com.jme3.network.base.DefaultClient handleError SEVERE: Termining connection due to unhandled error java.lang.NullPointerException at controlos.InimigosControl.jogadorPos(InimigosControl.java:70) at appstate.GamePlayAppState.messageReceived(GamePlayAppState.java:127) at appstate.GamePlayAppState.messageReceived(GamePlayAppState.java:30) at com.jme3.network.base.MessageListenerRegistry.messageReceived(MessageListenerRegistry.java:73) at com.jme3.network.base.DefaultClient.dispatch(DefaultClient.java:408) at com.jme3.network.base.DefaultClient$Redispatch.messageReceived(DefaultClient.java:416) at com.jme3.network.base.ConnectorAdapter.dispatch(ConnectorAdapter.java:132) at com.jme3.network.base.ConnectorAdapter.run(ConnectorAdapter.java:174)
When I say random, it’s like: the client1 receives the position from client2, client1, client2, client2, client1, client2, client1, client1, etc…
And, if only one client is connected, no messages are received.
I have 3 types of messages (the forth in the source code is not being used yet): PlayerConnectedMessage, PlayerDisconnectedMessage and PlayerPosMessage.
The PlayerConnectedMessage is sent when a new player connects. Then, on the Client, I attach a new EnemyControl and add it to a HashMap with Strings (the usernames) and EnemyControls.
The EnemyControl is the one that sends the PlayerPosMessage. The server will broadcast that messsage to every client but the one that sent the message. The message will have the username and trought the HashMap, I can access the corresponding EnemyControl and update his position.
PlayerDisconnectedMessage is used to remove the EnemyControl of that player and detaching it from the scene.
The problem is that the client receives information from himself. And, since i’m not an enemy (from my point of view, i am the player and the others are the enemies), I don’t have a corresponding EnemyControl on the Map. I think that is the thing that causes the NullPointerException.
Just looking at that line in your code looks like you are getting a null pointer because the item in ‘inimigos’ doesn’t exist when you call this update. I’m not sure how you are sending the packets but my guess is that the creation message didn’t arrive before the first update and you attempted to update something that didn’t exist. Network programming causes all sorts of synchronization problems. This is also why it appears “random”.
You mean the order?
I’m not 100% sure since I don’t use spidermonkey, but if you don’t mark the message as reliable it should send it over udp that doesn’t ensures the correct receiving order.
This also could be a problem (still not 100% sure) but spidermonkey should use a thread per client, so if both client are trying to edit the hashmap at the same time, you could end in data inconsistency problems.
Firstly, the ‘inimigos’ HashMap is created when the control is attached, which means it can’t be null. And the Map is not a static variable: it’s has an instance for every class (in this case, for every Client).
I am setting the PlayerPosMessage to not reliable, since it’s about the player position ,which is sent in every loop.
Now, I did some testing, and I found an interesting thing:
The Client1 detects a new player and adds him to the Map. When I printed the control, it doesn’t return null.
However, on the Client2, it looks like the client tries to position the player BEFORE he is added to the map. It’s like the simpleUpdate() being called before the simpleInitApp(). This is very weird, because it doesn’t happen on the first client. I suppose the server is running in a separate Thread. If that is the case, what should I do?
I haven’t looked at your code but just based on the discussion so far…
Unreliable messages can come at any time in any order and you need to account for that. So if the create message comes reliably and the position message comes unreliably then you might see those in any order, positions before create, create before position, anything is possible. Think of them as being sent on completely different channels (because they are). And if both are sent unreliably then they can still come in any order… because that’s UDP.
Network messages are delivered on a background thread, not on the main update thread… you need to account for that also.
So, after thinking and thinking, I discovered the problem: the InimigosControl that receives the position was sending his position again. I know this doesn’t make sense. The server finally works!
The problem now is that because the server is running in another thread (and for positioning the players i need access to the scene graph), I need to learn multithreading. But I can’t understand the tutorial here in the JME Wiki. Can someone please explain me in a simple way how to do multithreading?
Just copy this code where you want, replace your_application with the reference to your jme Application and replace
// the code that edits the scene
with your code.
The enqueue method will add the callable object that you have just generated to a queue inside the application and it will be executed only once on the next update then automatically removed from the queue.
So, you really have nothing to do but copy the code above and replace the stuff i said.
Just keep in mind that enqueue won’t block your thread, if you need to wait the execution of the callable you have to use the java.util.concurrent.Future object returned by the enqueue method.