schteppe / cannon.js

A lightweight 3D physics engine written in JavaScript.
http://schteppe.github.com/cannon.js
MIT License
4.63k stars 700 forks source link

Is simulation consistent across browsers & hardware platfoms. #112

Closed nouknouk closed 9 years ago

nouknouk commented 10 years ago

Hi,

This is not an issue, rather a question about the library's capabilities:

I plan to do a turn-based multiplayer game including physics. The requirements could be similar to a pool game: 1- it's turn based, and so there won't be concurrent interaction between players at the same time. 2- the physics involved during each turn must provide exactly the same results for each client running it.

Would cannon.js guarantee the point #2, for:

Thanks in advance for your advices and for the amazing work done with cannon.js :-)

schteppe commented 10 years ago

Hi! The Cannon.js logic should be consistent in between platforms. If JavaScript numbers and arithmetic ops are handled equally in each browser on every platform, then the answer is yes. But I'm not sure if that is the case.

nouknouk commented 10 years ago

Hi, thanks for your answer. That's the point: i suspect the arithmetic ops not being handled exactly the same on all platforms (especially when the CPU architecture is different), that's why I'm asking. If I remember well I had troubles in the past between some specific 10^-12 roundings that were different for cos() sin() and sqrt() functions between x86 and an old (floating-point-less) MIPS architecture. I don't know if it's the same for ARM vs x86, and I don't know if it's also the same for different implementations of the arithmetics in Javascript interpreters (SpiderMonkey vs Google V8 for example).

So, I'm a bit stuck ; I don't really know where to look for more detailed information on that topic.

If you've got any idea...

erichlof commented 10 years ago

Hi nouknouk,

If I may add some advice to help solve your problem; I believe this is more of a networking issue, rather than a physics engine issue. To explain, if what you want in the end is all machines that are networked together running your game simulation to see the EXACT same outcome once they make their move/turn, then IMO you must approach this problem from a server/client architecture standpoint.

Looking at it from a bird's-eye generalized view, you pick one computer to be the server. In a peer-to-peer 2 player game like pool/billiards, this could be the computer that started the game first. In a true multiplayer though, you need a server computer running 24/7 to host the ongoing game and clients can come and go as they choose (not sure of which would suit your game type).

Anyway, the server would run Cannon.js and it HAS to be the undisputed master or referee. What it says that happened with each player's turn is law. You could have the clients running their own Cannon.js as well, but they need to check in with the server master referee a couple of times each second as the simulation is running, to make sure that things have not gotten too far out of sync.

If things have gotten out of sync, not to worry - just snap the client back into place according to where the server says it needs to be at a certain time-stamp. The only downside to this 'snapping' is that if there is a big interruption on the client's simulation (garbage collection) or network hiccups, those players might see their simulation appear to be jerky or choppy for a second, as they are snapped back into correct position.

Other than that one downside, this approach ensures that every player gets the same exact experience, regardless of browser/system specs. Plus, if something is not working/feeling right with the game, you know where to look - the server's code running Cannon.js. Any changes here will immediately affect the clients' game experience.

Doing this in real-time like QuakeLive is a real art and there are a lot of articles out there on the internet dealing with "Dead-Reckoning" and 'interpolation' for smoothing out the 'snaps' when the clients occasionally get out of sync. But it sounds like with your turn-based game, this won't be much of an issue.

Hope this helps! -Erich

nouknouk commented 10 years ago

Hi Erich, and thanks for your feedback.

I'm quite comfortable with the game networking state of the art ; the major issue with your approach is the scalability: running physics simulation on server side involves a more cpu and/or network intensive server need for each client running the game.

I already have developped a game running on several platforms (*) with a custom basic physics engine. My current approach is to run the physics on each client (and a system of "complain" raised by clients in case of suspicious discrepencies to avoid cheats), but this approach can only make players play against other players running the same platform.

The game has already more than 400 players during peak hours ; in such context, running one physics engine for each live game is not really an option for my little (free) game and my little server. If I cannot find another workaround, I think I will adapt my current physics engine and work with fixed-point arithmetics.

(*) there is one version available on a dedicated french TV set top box called freebox and a (dirty, old) web version running over a java applet (the one I want to re-write in plain HTML5 and make it compatible with the freebox's version) ; You can take some look and have a try of the web version on the game's website: bouncebox.fr

erichlof commented 10 years ago

Hi again, sorry I didn't know your level of expertise regarding multiplayer. Sorry if I sounded like I was trying to teach. Thanks for the links - I will check out your game project tonight when I return!

I have similar aspirations of making a multiplayer game but my design will be 2-4 players max at one time. You have raised an interesting issue about how to deal with lots of concurrent connections. I can see how it would be impossible to scale my approach up to 400 players. That's getting into MMORPG territory.! I will see if I can locate some online solutions to this same problem.

I like your ideas about using fixed-point calculations. What small amount you lose in physically-real accuracy, you would surely gain back in game responsiveness and fun factor! If you do find a solution please post back here. I'll be interested to know what workaround is possible. I'll do the same if I find anything on gamedev or similar community.

All the best! Erich

erichlof commented 10 years ago

Ah, I just visited the freebox website and I see now what you mean by up to 400 players. Although, each individual game is for 2 players, like a real game of billiards no? This is a slightly different problem than I was imagining ( I was thinking you had 400 players playing against each other on the same game instance! - which is more MMORPG ).

So your problem is essentially how to deal with possibly 200 simultaneous games (times 2 players each game = 400 possible connections) running physics either on their own machines or on some master server computers?

I have an idea to cut down on bandwidth and reduce discrepancies between clients running Cannon.js on different platforms, but Stefan (schteppe) might need to help me out because I'm no math whiz.

Anyway, here it goes - using the players' initial input turn data (such as force, angle, position, etc.) I believe there is a way to pre-calculate where the balls will end up ahead of time, using Cannon.js to do the calculations. The thing is that the simulation for each particular shot or turn would have to run its course in extreme fast-forward, in a split second, (with a very small time delta value) behind the scenes. After the balls' resting places are determined, the 2 clients compare their end results which ran on both of their personal device's hardware.

If, and only if, there is a large enough discrepancy (a threshold that could be fine-tuned) between each client's answers, they must defer to the server which acts as referee. This means that they send the initial state of the balls, the turn data (what the player gave as input for the shot) over the network, then the master server is obliged to run that particular shot on its own hardware (in extreme fast-forward of course). Whichever client is furthest from the server's indisputable answer, gets slightly snapped into position at the end of the simulation.

All this happens hopefully in a split-second (without the players knowing) and then the shot plays out as normal on each client's machine in real-time, so players can see the results of their efforts. The only downside being if there is indeed floating point errors vs. the master server, that client has to get snapped and might see a slight jerkiness. But in the end, everyone agrees about the outcome.

This is the only way IMO that you can guarantee that each client will see the same result. It also prevents cheating, because if someone plays with your code, their answers will be always the farthest from the master server's answers, and eventually they will be caught because the server is constantly having to correct their wild measurements on every turn.

The question is, Stefan, can Cannon.js be used in this manner to do a quick calculation (in extreme fast-forward)? It probably depends on the complexity and realness of the billiard simulation I suppose.

Just some thoughts. I love thinking about problems like this. :)

schteppe commented 10 years ago

Yes, Cannon.js could be used for such solution. "Fast forward" is just running world.step(dt) in a synchronous loop instead of requestAnimationFrame. This should be quite fast for a sphere/plane simulation, which I guess your scene is. However, running Cannon like this will probably use just as much CPU as normal usage.

The trick is to serialize the scene and unserialize it on the server properly so the physics don't break. Apart from your scene setup and settings, what needs to be transferred from client to server is position, quaternion, velocity and angularVelocity of each body. The .collisionMatrix and .collisionMatrixPrevious in the World are also needed. Using this data, server can compute all trajectories of the balls and send back all or some of the data to the client.

I note that the computations can be done independently for each game. Why not make a simple Node.js cloud service that computes ball trajectories given the input state? If you need more computing power, just add some virtual machines :)

dirkk0 commented 10 years ago

Also - you might want to consider compressing the communication like so: http://buildnewgames.com/optimizing-websockets-bandwidth/