How do you keep a game in sync? (multiplayer)

Started by
36 comments, last by Memir 20 years, 8 months ago
I''ve got a problem that has been plaguing my Online Snooker (Pool) game ever since I''ve begun six months ago... When people play online and take shots, sometimes one of the players/watchers in the table experiences a different outcome. We''ve been thru the code and combed out all the bugs that could cause an incorrect simulation. Eventually, we found out that when you do simple floating point calculations e.g. y = a+b; Certain computers calculate the result ever-so slightly differently. e.g. 5*5 = 25.000000 on one person''s processor, and on another''s 5*5 = 25.0000001. Now that small difference becomes bigger and bigger, eventually resulting in one player seeing the balls in different positions. Our current solution is to update all the balls'' positions at the end of each shot. That solves the problem of players going out of sync and staying out of sync. However, it also causes people to say "Hey this game has bugs, cause the balls are moving after the shot has been completed". What I want to know is is there a way to use floating point math without causing this problem? Is there any ''Emulation'' mode floating point that I can use which gaurantees that all players across the world, regardless of CPU/FPU/Hardware/OS will carry out floating point calcs the EXACT same way. The only other solution I can think of is changing the ***WHOLE*** thing over to fixed point, and thats gonna be a mission. Especially figuring out how to convert the 4x4 Matrix calcuations, 3D ball/table detections etc. over to Fixed point. There''s gotta be an easy way out? Anyway, if you''re curious to see what my Snooker game looks like click the following url & download, you can play as a guest for free: TheSnookerClub /Memir
Flash Pool - Pool/Billiards game for Facebook (developed by Memir).
Advertisement
hm...thats weird. how can 5*5 = 25.000000000001? anyways, the best answer i can give you (i have virtually no idea of networking) is to keep original values so as to keep the effects of erroneous values from adding up... sorry for lame answer but its the best i can give you...


Check out our website!
Clicky!

I am Ciph, and that is what I am
I eat heart attacks
What about having one computer designated as server and one as client.. and one does the calculations and sends the results to the other.. or perhaps they alternate?
Disclaimer: "I am in no way qualified to present advice on any topic concerning anything and can not be held responsible for any damages that my advice may incurr (due to neither my negligence nor yours)"
Problem is... this is a Snooker game, i.e. like Yahoo Pool! ...

Means there are a lot of balls that collide into eachother when someone breaks & smashes the pack. Not only does the person taking the shot see what''s going on live, but his/her opponent and all the people watching in the game see this live.
Now I''m not gonna send over the coordinates of 20-odd balls * float(X,Y,Z) * 60*say 5 seconds (20*12*300 bytes) to all the players watching me take a shot! - At present I only send over the strike vector and target ball, aswell as a 32-bit CRC value of the current table state (present ball coordinates) - i.e. to detect out of syncs.

I read an article on Gamasutra about Reproducibility, by Peter Lindcroft (dude who helped develop X-Wing vs TIE Fighter). His answer was the easy "Just use fixed point math" OR "Do a complete update every now and then".

Looks like no one knows how to "beat the float" head on.

If the game was a simple Snooker/Pool game like the rest of the online games, then I would''ve just coded it all fixed point (especially if I knew that floats+networking doesn''t mix), but this game has a lot of complex vector & matrix calculations as we tried to make it as realistic as possible.

I guess I''ll have to just go over the code line by line, function by function changing it.

/Memir
Flash Pool - Pool/Billiards game for Facebook (developed by Memir).
Just do the shots like you normally do, then at the end of the turn, sync all the ball positions to that of one computer. If you do this every turn, the balls will only move minutely after the turn is done.

This will avoid having to send constant details about the motion of the balls, and it will keep the game in-sync.
I suspect a bigger problem is timing differences between computers. Would a different frame rate result in different collision results or different integration results (because the time-step is different)? If so, then fixed-point won''t make a difference.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
glassJAw: That''s what we do already! - The thing about this problem is that it doesn''t always come out, it''s pretty rare, and most times when it does come out, it only causes the balls to move a little at the end of the shot. But sometimes, you can clearly see that the balls have move a lot. It really depends on the shot, and how the balls interact with eachother. So when someone smashes the pack, theres lots of ball collisions, meaning a small error can quickly propagate up, and become quite noticable.

JohnBolton: The timing differences doesn''t matter in this game, coz it''s like a "You take a turn, Then I''ll take a turn" game. And everyone waits until everyone has completed the simulation of the shot.

At one point a few months ago, we did find a solution to the floating point problem, and that was to simplify the operations.

e.g. instead of having y = (a+b)*c;
we would do y = a;
then y+=b;
then y*=c;

and somehow doing this made all math processsors (FPU) produce the same result.

I think it has something to do with the fact that the FPU does calculations at 80bit. and these values are stored as 32bit floats.
so when you''re doing:

1) y= (a+b)*c [32bit = FPU((a+b)*c)]
2) y= a; [32bit = CPU(a)]
y+=b; [32bit+= CPU(b)]
y*=c; [32bit*= CPU(c)]
Basically I think the compiler realises that a complex one liner, will be calculated faster by converting the 32-bit floats into 80bit FPU values, then calcuated by the FPU, then converted back into 32-bit floats.
Whereas simple operations on seperate lines, can be evaluated faster just using the CPU''s own math processor, rather than converting the 32bit values into 80bit then firing up the FPU then converting back to 32bit.

It''s a small problem in my game, not one of the high priorities, as it doesn''t happen that often, and when it does, it usually only happens to one person in the game. But nevertheless I''d like to get rid of it completely, without having to just avoid floats.

/Memir
Flash Pool - Pool/Billiards game for Facebook (developed by Memir).
quote:Original post by Memir

JohnBolton: The timing differences doesn't matter in this game, coz it's like a "You take a turn, Then I'll take a turn" game. And everyone waits until everyone has completed the simulation of the shot.


Hmmm, that sounds extremly strange I think that timing does not affect the movement... Unless you use frame dependent animation, the timing will of course affect the movments, even if you have a turned based game. That it is turned based has nothing to do with it.

However, maybe I have misunderstood something here, but the fact that one computer might calc a collision 3 times over 60 ms (1 time each 20 ms) and another 2 times over 60 ms (1 time each 30 ms) will of course affect all the calculations due to round offs. Of course, in general, a ball that moves from A to B with a certain speed and direction and acceleration/deacceleration will move from A and end up in B even if the ball moves in 2 steps or 3 steps over 60 ms... However, in your case where you have a lot of floats you will get a lot of round offs and those round off will differ if you use 1, 2, 3, 4....n steps and will in the end cause different results. So timing is for sure an issue here I think.

However, is you lock your framerate to for example 30 FPS and do calcs each frame then all should be in sync. But I assume you to calculations based on time that has passed and then you will be affected by the above facts.




[edited by - calvincoder on July 27, 2003 8:37:14 AM]
Try using doubles instead of floats.

If push comes to shove do all the ball calculations in assembly using the highest precision registers and the least amount of conversions.

------------
- outRider -
What I can suggest is to have one computer designated as the computational master doing all of the floating point math ahead of time and to then relay that information to the other computers.

Besides that, I can say you could try using doubles (you might end up with the same problem). However, if you check regularly to make sure the game is in sync then at most the balls would move like a single pixel. It shouldn''t be detected by the human eye.
OpenGL Revolutions http://students.hightechhigh.org/~jjensen/

This topic is closed to new replies.

Advertisement