Solution as stated by the developer: "
Hi, I developed the game client so I can tell you exactly whats going on.
When the server first racks up the balls it truncates each ball's position to a 5 digit fixed-point number before sending the positions to the clients (each new game will have the balls at slightly randomized positions so that all breaks are unique)
This way each client will have the balls at the same position regardless of cpu architecture and floating point implementation at game start.
When a player takes a shot, the power, angle, and spin is also truncated to a 5 digit fixed number and sent as an integer to the server. The server then records this and relays the information to the other client.
Now both clients start to resolve the physics locally using floating point math. When the physics are resolved (balls are no longer moving) each client again truncates the position of each ball to a fixed point number and sends it to the server.
When the server has received the shot outcome from both clients it compares the result. If the clients agree on the outcome then everything is good and the server passes the turn and allows the next player to shoot. If however the two clients disagree the server will simply close the game (yes it was a quick-fix). This will happen every once in a while because even though the two clients are in agreement about the positions of the balls and in agreement about the power, angle and spin of the shot, the difference in floating point implementations may still cause the shot outcome to be vastly different. For instance, on one client two balls might just miss each other while on the other client they collide and cause a completely different outcome.
However, in the wild, we saw that this very rarely happened because we keep resetting the positions to a fixed-number between each shot. It was so rare that we didn't bother implementing the physics on the server to let that resolve the shot in case of a conflict.
Hope it makes sense