Sign in to follow this  
kloffy

How often should you send updates?

Recommended Posts

I'm writing a multiplayer Pong clone to get some practise with network programming. At first I wrote all of the network code myself, using Winsock. However, every action had a pretty noticable delay before it appeared on the client. I thought this was because of my code, so I started over using RakNet. But, unfortunately I still get the same problem. I think it might have something to do with the fact that I send too many updates. When I reduced the number of updates to 1 each 100ms, the response was much quicker. On the other hand, the movement isn't as smooth as it used to be. (But I know there are methods of exter-/interpolating movement. I was hoping I wouldn't need such more advanced techniques for a simple game like Pong. ) I'd like to hear some advice, so how often should you send updates?

Share this post


Link to post
Share on other sites
For an action game, the going rate seems to be about 20-30 times a second. For an RPG, anything between 1/s and 10/s seems to be common.

Also, if you're using TCP, make sure you're turning on the SOL_SOCKET/TCP_NODELAY socket option.

Share this post


Link to post
Share on other sites
Alright, I'm now sending 1 message each 50ms (20 messages/second). I'm also sending them as UNRELIABLE_SEQUENCED packets, which according to the RakNet manual are sent via UDP. Only the newest packet is accepted.
It works alright with one client, but as soon as multiple clients connect, I still get delays. The last client to connect is affected the most.
I uploaded the current version, so you can see what I mean. (Download)

Controls:
x - Start Server
c - Start Client

w,a,s,d - Move Player

Edit: The clients automatically connect to 127.0.0.1.

Share this post


Link to post
Share on other sites
What does your CPU usage look like? It may be that the processing you are trying to do to create the update message takes more than 50ms, although it really shouldn't, which would cause youe CPU to be at near 100%. If the CPU is very low, it may be that your call to Send() is blocking until it gets confirmation that the packet was sent (though it shouldn't if it's via UDP); there may be an option to disable that.

Share this post


Link to post
Share on other sites
My CPU usage is at 100%, but that's because the main loop (input, rendering, physics) takes all the cpu power it can get. But the FPS are much higher than 20. I check how much time passed since the last Send() and if more than 50ms passed I call Send() again. Even if the programm couldn't send the messages this fast, I wouldn't expect to experience the kind of behavior it's showing. I would expect less messages to arrive, making the movement a little "jumpier". However, this is what i get (o is an object moving to the right):
Server:                 o->
Client1: o->
Client2: o->
...


[Edited by - kloffy on March 6, 2006 6:23:44 PM]

Share this post


Link to post
Share on other sites
How large are the packets? Are you maxing out your (upstream) bandwidth? Can you measure packet loss?

Also, if you have two clients, can you measure whether the CPU usage is actually causing delays?

Share this post


Link to post
Share on other sites
If your CPU is 100%, and you're trying to run two clients on the same machine as the server, you're quite likely to get lags simply because your OS has to work out who to give timeslices to. Try cutting the framerate of your physics to reduce CPU load and see if that fixes it.

Share this post


Link to post
Share on other sites
You will need smoothing/interpolation/prediction for all cases if you want smooth motion (for a pong game, a simple velocity-based prediction and an IIR filter should work OK). For better results, simulate the game (visible) ball and the networked ball, or "ghost" (not normally drawn). For debugging, you'll draw both: the ghost is updated immediately from network data, and continues to run via the network-sent velocity. The game ball is smoothly IIR filtered to the ghost ball position (gameBallPos = lerp(alpha,gameBallPos,ghostBallPos), where alpha is < 1 (smaller values = smoother motion, but with more delay). Collision detection is performed for both balls.

Sending data at a fixed rate is a bad idea for at least two reasons. The first, is that under high network congestion situations, a constant send rate will prevent the network from quickly reaching stability (equilibrium). In some situations, if the data streams cannot throttle back, the network system will never recover (and the clients will be disconnected due to time out).

The second reason, is that without an adaptive and efficient send rate, the client experience will be diminished. A fast network can support extremely high send rates, providing users with little to no perceivable lag. A slow network requires updates to be throttled back, otherwise the network connection will collapse and have an effective data rate much lower than what would be possible had the stream been throttled back.

The history and theory of throttled network transmission is explained well in Congestion Avoidance and Control. While a game developer may not at first think they need to worry about congestion, it should be apparent after reading the paper that network game performance can be improved on both fast and slow networks by using the concepts and algorithms presented in the paper (I have used these concepts since 2001 (Xbox launch title) and the real-world results are excellent). If RakNet has an adaptive UDP send system, use it. If not, ask the developer if there are plans to implement one (otherwise, you can implement your own system, though it does take a bit of time and testing to get a good, working solution).

If you are using TCP, you'll need to monitor the TCP send queue (TCP will take care of throttled send rate for you). Thus, you can prevent the TCP send queue from overflowing by throttling back your sends (and increasing send rate when the queue level is low).

For testing network client+server code on a single machine: add a Sleep(0) or even a Sleep(1) to yield the thread+process (Win32 multitasking is not highly preemptive).

Share this post


Link to post
Share on other sites
Thanks everyone! I'll try to implement your suggestions.
I've already been able to improve the program a lot using the techniques from "Targeting - A variation of dead reckoning". (That is probably what you meant by "simple velocity-based prediction"). The movement definately needs to be smoother though. The method of using two positions (ghost/real) sounds really good. Do you know where I could find more information on that?
The whole system pretty much breaks down with more than 4 players. I guess that's because of the CPU usage, since I'm running all cients and the server on the same computer. I added a Sleep(1) to my main loop, but a single instance of the program still uses 100% of my CPU as soon as the client/server is running. (Before starting a server/client, it uses about 70%)

Edit: @hplus0603:
A single packet is 77 Byte. I'm sending out one for each player and one for the ball. I'm not sure about packet loss or maxing out the upstream, where can I check that?

[Edited by - kloffy on March 6, 2006 5:59:21 PM]

Share this post


Link to post
Share on other sites
I've added a ghost and it further improved the game. I figured it out myself, so it may not be the best way of doing it, but it works. However, I still have to limit the number of messages. If I send as many as possible, the clients seem to "overflow". It looks like they can't keep up with all the messages. They lag behind the server, and it gets worse the longer the program runs. It was especially bad when I connected with an older PC.
I think this is really weird because I thought only the newest packet would get accepted anyways.
Here's the recent version, if anyone wants to have a look.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Well its not surpising that the older data is consumed. As the buffer fills the data on the buffer is older than the incoming data. When the buffer is full, it throws away newer data. So as your program reads the buffer its getting the oldest data. Your app proably isn't updating as fast on an older machine which would percpitate the problem. You might want to implement a 2nd thread which pulls data from the buffer asap and put the packets onto your own queue. That way you can pull the latest state.

Good Luck!

-ddn

Share this post


Link to post
Share on other sites
Alright, I replaced:
if(packet=client->Receive()){ //Update }
with:
while(packet=client->Receive()){ //Update }
Now my older PC can play as well. I also don't have to limit the number of packets anymore. Thanks guys!


Btw.: Since I'm using RakNet, I don't think it would be a good idea to implement send/receive as threads.
Quote:
RakNet FAQ
I want to, or should I call Send and Receive from multiple threads.
Those functions are not currently threadsafe. There is no benefit to calling them from threads, as they are already optimized to run asynchronously with the main network thread.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this