I am making a game with Unity, and am having problems attempting network synchronisation. Deterministic simulation is impossible because Unity's internal logic is float based. I am trying to make a solution which works on an authoritative client-server model, in which the position of every soldier is corrected by the server 5-10 times a second. This is achieved by sending UDP datagrams, and measures will be implemented to ensure delivery. Between corrections the client simulates the game as best it can.
Soldiers do not often move in straight lines. They engage in flocking and will encounter many obstacles which they have to navigate. Soldiers exist as individuals and do not exist as part of a squad object.
There are things which can be done to reduce bandwidth consumption which are not the focus here, because they are at best temporary improvements. Like only sending the positions of soldiers who are moving and/or observed, and compressing the data before transmission.
The problem is simple. If I have ten soldiers running about that's fine, but if I have one thousand then bandwidth use becomes unreasonable. At the very least we need to send a soldier id (ushort) and position (float, float, float) per soldier. Right now that's 2 + 4 + 4 + 4 = 14 bytes. More data maybe required, for instance the direction of facing (soldiers never look up or down).
14 * 10 * 1000 = 140kb/s ... way too much!
The question is this:
How can we reduce the number of bytes sent per soldier to an absolute minimum and still have a synchronised result?
1. One idea I had was that since a soldier's max speed will not change, this allows us to correct the position of any soldier by an absolute value relative to the last known position. But that may suffer from desync too.
Instead of sending a float for the XYZ axis of the position, we send a sbyte. Since this is -128 to 127, we can read/write it to say that the new position is a percent of the soldier's max speed from the last position. If it worked that'd reduce the data sent from 14 bytes to 5. But even 36% is still 50kb/s for 1000 soldiers.
Potentially that could be reduced to 4 bytes some of the time, as soldiers do not always move up or down (Y axis). But that is, like ignoring soldiers who are not moving, not the issue.
2. Another idea I had was to reduce the XYZ axis to 12 bits (1.5 bytes). Each axis has 4 bits, 3 of which are used to find a distance value. 3 bits allows us to count from 0 to 8. There are six values between either end of zero or maximum speed. We have a range of values which can be used as they are "close enough". The other bit determines whether this is positive or negative. The server then forces a correction upon soldiers before it sends their positions to the client, forcing their distance from the last position to conform to one of the "close enough" values.
Additionally, if there won't be more than a thousand soldiers alive at any one time, we can assign soldiers to a living ID, and so shorten the network ID from 16 bits to 10, allowing us to count to 1024.
In total this would reduce the data sent per soldier from 14 bytes to 22 bits (2.75 bytes). I suspect this would be even less likely than the first idea to maintain synchronisation. The result may simply be too jittery. If it did work it'd be 20% of the original bandwidth at 28kb/s.
Any ideas are welcome at this point. I've been focusing on making smaller and smaller vector3's, but there may be a much better way of doing this which does something completely different.