Multiplayer, Real-time Networking Problems

Hi jMonkeys,

I’ve recently started trying out some networked programming and everything is working well, except now I want to make it work smoothly (instead of seeing other characters “jump” as they walk, since updates are only occurring a few times per second).

What I did was combine the HelloTerrain and HelloCollisions tutorials, so your character is walking around on some terrain. The client also spawns another thread, which connects to the server I have made. Both sides communicate using a BufferedReader and PrintWriter (communicating with Strings)




The client connection logic is:

  1. Handshake
  2. While(isRunning)

    a. Get location (x, y, z) and rotation of every player on the server

    b. Send my location and rotation to server

    c. Flag “isUpdated” variable to true (so that the simpleUpdate(float tpf) function knows to update all other player positions)




    Note: I have tried extrapolating the positions of other players between updates, but it doesn’t work very well.




    The server holds a HashMap of all Player objects, which gets modified synchronously by the different connection threads.

    The server logic is:
  3. While(true)

    a. Listen for a new connection using a ServerSocket

    b. Spawn a thread for the new client (I named the class “ConnectionHandler”)




    The ConnectionHandler does this:
  4. Handshake
  5. While(inputMessage is not “EXIT”)

    a. IF(inputMessage is “GetPlayers”) THEN Send location and rotation of every player

    ELSE IF(inputMessage is “PositionUpdate”) THEN Read in update of new player and make the change to the server’s HashMap

    ELSE IF(inputMessage is “RemovePlayer”) THEN Read in the player name and remove it from the server’s HashMap

    b. Read the “inputMessage” from the client




    Now as you can probably guess, this is fairly slow, since updates are only sent by the server when the client requests them (using “GetPlayers” as the command). The location of all players in the world only really updates on average every 500ms (determined by a friend who lives a few suburbs away), this clearly isn’t very scalable either.




    My questions essentially are:
  6. I believe the correct way is for the server to just keep sending updates and the client to read them and vice versa, but what if for example the buffer starts filling up on the client side? This would mean that client is behind, still reading information from a few updates ago. How would you regulate the speed so that this doesn’t happen, or what is an alternative approach (avoiding race conditions)?
  7. My Player class has everything broken down into primitives: floats x, y, z for position, floats x-Dir, yDir, zDir for the movement vector etc. It would be preferable to store Vector3f and Quaternion objects in the Player class, however these classes aren’t serializable, so I can’t use Java’s ObjectInputStream and ObjectOutputStream classes to send whole Player objects (which I think would be a fair bit faster). I tried looking through the tutorials and forums for how to use SpiderMonkey to do something like this, but couldn’t figure it out. I found (THIS) but it only describes how to upgrade your code to the new version. Are there any good tutorials for this, or am I better off building my own solution, due to the possibility of the SpiderMonkey project being dumped?




    Apologies for the monster post.

    Thanks - slothee

a) “Both sides communicate using a BufferedReader and PrintWriter (communicating with Strings)” Don’t do that. :slight_smile: You will waste a lot of bandwidth when you could have been sending smaller, tighter messages. And you waste a little processing time formatting and parsing.



b) JME has a networking API that handles a lot of the standard crud for you. https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:networking

It might make some things easier.



You can also look at the game MonkeyZone which is a JME example that has networking.



And if you are really keen on the subject, these articles are good:

Source Multiplayer Networking - Valve Developer Community

Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization - Valve Developer Community



The basic advice, keep your messages as small as possible and use unreliable (and fast) UDP communication when you can get away with it. TCP is completely wasted when the next message makes the previous one obsolete.

2 Likes

Thanks for the advice. I’ll try reading over that first link again, don’t quite understand how to implement it (can you put ANY object in a Message object?).

The “ticks” idea seems good, essentially making sure the client isn’t flooded with messages from the server.

You can put any object in a message that has a serializer or can be marked with @Serializable and registered with Serializer.

I might give a go at “the crud” myself (coding UDP), since lines in the tutorial such as:

[java]Client client = Network.connectToServer(host, port);[/java]



Don’t work unless I update to one of the nightly builds (I have updated everything through Help → Check for Updates in the IDE), but then other chunks of my code don’t work due to changes. I’d prefer not to have to go back and re-write networking code when the next stable update comes out.

If you are not building against the nightlies then you will have a painful upgrade path in general. Not to mention all of the fixed bugs that you are bound to hit at some point… but I guess that’s your choice. :slight_smile:



If you upgrade to the nightly then you’ll be able to use already written network code, you won’t have to rewrite your network code when the next stable build comes out, AND you won’t have to run around and fix all of the other things that “don’t work due to changes” when the next stable build comes out.



…just sayin’. :wink:

Fair point, I think I might upgrade to the nightly, it’s just a few things which need to be fixed (couldn’t find some imports which were probably just moved around). My progress into UDP networking is good so far, using a “PlayerStatus” serializable class I made, which is a packet containing the player’s name (unique identifier), location, movement, rotation and current animation. My implementation is going to be using the “ticks” idea from the Valve Source multiplayer networking link you posted.




Thanks for pointing me in the right direction xD

== NEW QUESTION ==




My client/server setup right now sends UDP packets from the client to the server on one port and from the server to the client on another port. The clients connect to the server using my global IP address (not my local one - 192.168.0.13). I have opened the correct ports on my router and the server works - I can connect to it and send/receive information because it has the required ports open (same computer, even though packets are leaving the router and coming back in). The client/server setup also works fine when I try using 2 computers on my network and connecting using the local IP address, since the information never leaves the router.

The client connects to the server on another port using TCP to do a handshake, which works regardless.




My problem is that computers without the required ports open can only send UDP packets but not receive them (TCP packets can be sent and received without problems). My question is, how then do FPS, racing games etc. send UDP packets from their servers to their clients (since they would be using UDP)??? When I install a game like Battlefield 2, it doesn’t require me to go to my router homepage and open ports. Do they do something dodgy like send UDP packets through port 80?




Thanks - slothee

No, nothing dodgy. If you setup the UDP connections properly it will work. For example, SpiderMonkey deals with this just fine.



The code is there if you want to look at it though you would save yourself a lot of trouble just to use it.



Anyway… the general approach is that the client opens a UDP connection without a port so one is chosen for it. It sends a UDP packet to the server which then looks at the packet to figure out what the return address and port are. This handles the case where a router is using proper UDP forwarding.



A client behind a firewall cannot use a fixed UDP port or you will require forwarding. No games do this anyway because it would mean you could only run one client at a time on an address, ie: one per firewall. Only the server needs a fixed port.

Thanks!!!

I might have a look at the SpiderMonkey code and end up using it, the whole UDP thing is a bit of a headache but something I wanted to learn.

I have to say I really like the SpiderMonkey so far, after figuring out how to use it, it’s saving me days of work.

Just a few questions and I should be on my way xD




  1. 1. Should the message I'm going to send be a class that extends AbstractMessage or just Message (I noticed the deprecated strike-through when I extend message, so I guess AbstractMessage must be the right one)?

  2. 2. Out of curiosity: A UDP packet doesn't need to be received on the same port it was sent from, whereas TCP does, right?

  3. 3. If I always want a particular message sent over UDP, can I just call
    [java]this.setReliable(false);[/java]
    in the constructor?




Thanks again champ xD
slothee said:
I have to say I really like the SpiderMonkey so far, after figuring out how to use it, it's saving me days of work.
Just a few questions and I should be on my way xD



  1. 1. Should the message I'm going to send be a class that extends AbstractMessage or just Message (I noticed the deprecated strike-through when I extend message, so I guess AbstractMessage must be the right one)?


Yes, extend AbstractMessage.

  • 2. Out of curiosity: A UDP packet doesn't need to be received on the same port it was sent from, whereas TCP does, right?


  • You don't control the TCP packets but even there the port is almost always different on each end since the outbound socket creates an etherial port for communication. With SpiderMonkey you shouldn't have to worry about that. Create a server with the ports you want and just connect the clients to it and let the networking layer pick ports for them.

  • 3. If I always want a particular message sent over UDP, can I just call
    [java]this.setReliable(false);[/java]
    in the constructor?




  • Thanks again champ xD


    Yes... that's very common.