Experience with determinism + floating points + multiplayer


I’ve a question if there is anybody who uses MonoGame and has implemented a deterministic float point (fixed point)?

For your background: Right now I’m working on a RTS prototype which uses server/client model where the server is authoritative. This works fine but is not often used in RTS because of network issues.

So either I live with it and you can’t build hundreds of units (The Tech of Planetary Annihilation: ChronoCam - ForrestTheWoods), or I need to change it to a lockstep model where only user-inputs are transfered to each player. This comes with the downside that everything must be deterministic. I wonder if anybody has implemented something like that in MonoGame/C# and can share the experience.

Thank you very much and have a nice day!

In an online game everything has to be deterministic anyway, as everything you see is basically some ms in the past and has to be extrapolated on screen - and as long as every client runs the same code it normally is.

So you either make an input delay to make sure every client will get commands for a near future timespot to be executed at the same time (that’s when older RTS do play some “yes sir” before the unit acutally moves)

Or you inter/extrapolate all movements based on command which was given in the past.

if at any point your game state is not the servers game state … or you surpass the predefined “command delay”, that’s when you normally get “syncronisation error” and get kicked of the session.

So yea, RTS require different netcode than a FPS. There was once a nice website which had indepth low-level information on how to do online RTS and how it’s done for older games. But that’s been some while since I read that …

Thank you for your response.

Probably you mean this post from Age Of Empires: 1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond

Well it has to be deterministic, but it’s very different if you use server/client approach or all clients run the exact same simulation. With server/client (where server is authoritative and client is “dumb”), floating points are no issue. In a simultaneous simulation approach (no authoritative server, clients run exact same simulation, often based on locksteps for RTS games), everything has to be 100% deterministic, so also floating points.
For example: We calculate some position for an unit. With a server/client approach, it doesn’t matter if one client uses 12.999999 or 13.000001. In a simultaneous simulation approach, this could lead to an out-of-sync. My question is if somebody has solved this in MonoGame/C# (with fixed points)?

I’ve found those topics but still I’m not sure about fixed points:

You can use either doubles to get close but the only real solution is using int64 then converting it to float, unit32/ fixed pt / fractional, when its appropriate for measurement or scaling to “real world” coordinates for and display and or some functions and caches. Problems is simply using doubles everywhere is slow for SIMD lanes, calcs, memory , caches, and such. but for hightly constrained dynamics, reversibility, machine agreement, and the persistent ground truth data model , this uint and fixed point concept mentioned earlier is key: In most practical cases for games, Floats using epsilons and other equality operators are usually ideal and rarely catastrophic, given some care. But see the bottom link comments and the catastrophic effects that can happen, missle crashes and such.

right to your point on RTS this might help:
This library might help GitHub - stormmuller/fixed-point-types: A C# library for fixed point numbers

or looking in the dotnet vault proposals… Vector128 and exisitng generalized maths if you want simd, burt you can get only vector2,3 4 in floats in .net But you can make your own vectors and arrrays 2d and such.

on a more general note

This problem of nondeterminism affects physics engines, neural networks and reversibilty as well as multiplayer games.
even with constraint solvers and simple Verlet Integration a 5-node pendulum can explode due to floating point errors. you have to damp energy artifiicially by doing 0.00000001 each integration step and carefully choose time steps and coordinate frames. real numbers and infinitesimals are not “real” , argueably, they are a construct and machines cannot agree on them nor can mathematicians. But there is a practical way and its not doubles everywhere, its a hybrid: see here an example a new paper from legendary jos stam , who put the smoke in the first games in the 90s

see this https://www.josstam.com/reversible
[2207.07695v1] An Exact Bitwise Reversible Integrator

So if you watch his demos and see this paper it’s very simple, and you can see how profoundly nondeterministic floats are. i used similar method in a CAD company long ago. So whille he representation and integration steps are are done usng the longs, its a hybrid method.

It doesn’t need any changes in monogame. because then when the determistic part is finished,

you simply divide the target int by a fixed point to get the float. that you can pass to monogame to draw. But would be nice if dotnet made it a bit easier and on par with other languages, theres lot of proposals and discussions in net8 and net 7 already has Vector128 w SIMD you can make stuff 27 x faster…

As far as libaries and such, its hard to find in c#, but Julia and even Swift all come with these homogenous vectors types , with Simd Intrinsics already , Vector2 3, 4 and the Matrix operators used of graphics and physics maths. but generalized Math is possible, and now I think Vector256 has some implemenation in the develop branch . in dotnet. But thats for batch calculation and conversion from uint to float and back.

this is a bit too far out, but Julia already has has Vector2 and in machine learning they use variable bit units now, burn chips with it from IL. For the sake of determinism, performance and reversibility. other radical paradigm changes are
Unum (number format) - Wikipedia(Unum (number format) - Wikipedia

If you want SIMD and this stuff:
if you look at netcore proposals, in the internals, Numerics, generalized math, , and Slik.Maths you might find something helpful. The proposals include some implementation but its looks like a big deal to decide on for the whole netcore, and or numberics or the comminty extensions. They do have a kind of generalized math interface but you have to implement the operators yourself.

In for monogame The compute branch has int32 surfaces now, that might be useful, if you want to do deterministic compute stuff on gpu.

. I can add that Autocads visionary John Walker decided to use doubles floats for its main data storage in .dwg files, since 1982, otherwise CAD drawings would mutate after many edits to where things dont fit , as do those of some of its competitors. Thats one reason why the 40-year old codebase still dominates. doubles or big ints are not high performance, so for display where determinism did not matter, we cached display lists as floats for drawing performance but kept the data model stored as doubles… if zooming too far we could rescale an make a new display list with a differnet scale. His famous solar system zoom was a bit much, but even in a large buildings, significant errors add up, after tons of edits on different machines.

now some of the other Media and game design products sufffer, even 3ds max suffers from drift since its using floats for data storage, from one early bad executive decision.

1 Like