Improving a realtime 3D multiplayer physics browser game

Started by
5 comments, last by vertices 8 years ago

Hi all,

I have been trying to create a realtime 3D multiplayer physics game (using Node.js/CannonJS). So far, it works 'smooth' (tested it with 2 players only though :P ) although I feel that there are still many things that can be improved.

My current implementation:

  • The client side loop runs at 60 ticks (including physics). It however only sends input data (whether a key W/A/S/D is pressed 10 times per second to the server.
  • The server side runs (at least smoothly) on 10 ticks per second (including physics)
  • The server sends 10 times a second to all clients the position and velocity of all objects. (Velocity is the reason why these objects are moving smoothly rather than abruptly 10 times a second)
  • The client adjusts receives this information and applied it to the objects.

My concerns/questions:

  • I find 10 ticks still a bit too much. Especially since I am planning (or my goal) is to run multiple games on a single server. My goal is that a single game should have 8 people. Is 10 ticks server side too much? Or can I even put it to 20/30 ticks? A strange question: how can I determine this? What are most games doing in this case? (if they either run multiple game instances or just a single one). I don't think my server (1GHz, 512 MB RAM) can keep up with 30 ticks for let's say 10 game instances.
  • I am not directly executing commands on the client side. For some strange reason that I have yet to solve, the client then is not smooth enough. I understand that for a realtime multiplayer game these commands need to be executed on the client side directly?
  • How can I simulate other players the best to test the network latency and smoothness of the game? How can this be done without having to get actual people that test the game?

Thanks!

Advertisement
First, 10 frames per second for physics is WAY too little for most kinds of games. You'll end up with tunneling, and oscillation (or over-dampening) and "impossible" jumps that just feel wrong.
Second, running a different step rate on servers and clients is a recipe for clients always being out-of-sync. 6 steps of 1/60th second is not the same as 1 step of 1/10th second for most integrators.
Third, if number of players per machine actually matters to you, write the server in C/C++ and use ODE or Bullet or something like that for physics.
However, unless CannonJS is vastly inefficient (or you're doing something inefficient,) you should be able to do several games with several players per game at 60 Hz even in Node, so before you end up short-changing your physics, I suggest you actually profile/measure whether there is a problem, and how big it is.

I don't think my server (1GHz, 512 MB RAM) can keep up with 30 ticks for let's say 10 game instances.


Two things:
1) The only way to know is to actually measure. Output the number of ticks per second per game to a log file, and keep adding games until the value starts going below the target value.
2) It sounds like you're using a virtual server/instance/VM. This is generally a bad idea for physics games, because the virtualization service will time-slice between instances on the order of seconds, rather than milliseconds, when load goes up across all the machines on the same host.
Thus, your program will seem like the CPU suddenly stopped for a second, which will be a bad user experience.
Larger virtual servers/hosts/instances suffer less from this, but the best practice is to put physics based games on "bare" hardware.
(Check out "self-managed servers" or "root servers" from some hosting providers for options.)
enum Bool { True, False, FileNotFound };

Hi hplus0603, thank you for your answer!


First, 10 frames per second for physics is WAY too little for most kinds of games. You'll end up with tunneling, and oscillation (or over-dampening) and "impossible" jumps that just feel wrong.
Second, running a different step rate on servers and clients is a recipe for clients always being out-of-sync. 6 steps of 1/60th second is not the same as 1 step of 1/10th second for most integrators.

Interesting. I thought that the server (loop) would always run slower (by skipping a few steps) than the client's loop. Just like this example: http://www.gabrielgambetta.com/fpm_live.html.

Hence, I thought the server can always skip some steps.

In CannonJS, I can specify the time between each tick, which I believe would compensate for the speed. That is why currently, I am getting an 'ok' smoothness when playing the game. However, I do have to admit that when I put my server loop tick to 60, it is a lot smoother. But I assume that if I do that, I have to send data to the client every let's say 100 milliseconds (rather than 1000/60 milliseconds)?

So now from my understanding of your post, I should put the server loop (and client loop) at 60 ticks equally. I think I underestimate the power of those machines, because I thought 60 ticks (for only 1 game) is quite a lot (which is like 600 ticks a second for only 10 games, or is this a bad example to see it?).


It sounds like you're using a virtual server/instance/VM. This is generally a bad idea for physics games, because the virtualization service will time-slice between instances on the order of seconds, rather than milliseconds, when load goes up across all the machines on the same host.
Thus, your program will seem like the CPU suddenly stopped for a second, which will be a bad user experience.

Very good point. I honestly haven't even thought of that!

Thank you :)

But I assume that if I do that, I have to send data to the client every let's say 100 milliseconds (rather than 1000/60 milliseconds)?


There are three "rates" involved:

1) The physics simulation rate.
2) The client rendering rate.
3) The network packet rate.

These should not be coupled. You can render faster than you simulate by using extrapolation. You can simulate faster than you render by doing multiple simulation steps per render frame. You can send fewer network packets than simulation steps by packing multiple updates into a single network packet.

In general, the lower level for action games is 10 network packets per second. High-end action games (like competitive Counter-Strike) may run as fast as 60 Hz network rate.

Quake game servers ran games with multiple players at 60 Hz 20 years ago, and I think computers have gotten a fair bit faster since then :-)
The main question is whether CannonJS is massively inefficient compared to a C++ implementation.
You can easily find this out for yourself by simply benchmarking your server, either by adding more games until it breaks, or by looking at CPU idle while it's running a known fixed load.
enum Bool { True, False, FileNotFound };

Thank you for your comment. Haha, clear!

I did a bit of testing a couple of hours ago and added more objects (around 400 static boxes floating in the air) to the physics engine. Unfortunately, it went quite slow for only 1 single game. This of course can also be some terrible implementation on my part (most likely :D ). But it seems to me that indeed CannonJS is a bottleneck at the moment (I don't know/think of any better 3D physics engine performance wise).

Perhaps a 3D multiplayer browser physics game is a bit ambitious at the moment. I don't know. I'm going to a 2D physics engine now I think, haha :).

For sure, using a language like C++ and some 3D physics library like Bullet may performance wise be the best.

Thanks again!

There is also Bulletjs/ammojs. Or using C/C++ and compiling for a browser with emscripten.
enum Bool { True, False, FileNotFound };

Thanks for the reply again. I think I will keep CannonJS. I've seen it perform better than my own implementation (even on client side alone with 250 boxes, my implementation lags quite hard). Most likely, I did something wrong.

Based on your advice, I am planning to run the physics on the server and client exactly the same (60 time steps) and will send out client data periodically (10-20 times a second) while rendering the game on the client as much as the client can do. Although my current implementation has this kind of setup (All separate loops :) ), the physics timesteps were not the same. I will try to rent a dedicated server and do some tests there (running multiple instances of the game while maximize the amount of players as well).

Thanks again hplus0603!

This topic is closed to new replies.

Advertisement