To preface, my understanding of networking is mostly from Source Multiplayer Networking and 1000 Archers on a 28.8. For various reasons I have decided to go with a client-server model instead of peer-to-peer.
My C#/Unity multiplayer game is ruled by an authoritarian server, which uses UDP. Each network instruction has a time index, and is inserted into PlayoutBuffer's data collection. The buffer executes stored commands at the correct local time index, based on a fixed update loop.
The server is slightly ahead of time, and clients will pause if they have not received any regular update from the server, allowing them to catch up.
Each client will simulate object movement and this movement is corrected by the server (ten times a second). I've got the basic system working, but when I'm testing it in two windows on the same machine sometimes input arrives late, and is dropped.
Usually the late message is 1 time index too late. The fixed loop is 50 ticks per second. The server is 5 ahead of time, and commands are scheduled 5 ahead of time (assuming negligible lag, since ping hasn't been implemented yet and is on the same machine).
This is a problem. Inputs from server are grouped into a few specific channels: Ping, Serialise, Inputs, Criticals, Corrections.
Ping has yet to be implemented. Serialise packs and unpacks mostly pregame multiplayer lobby stuff. Inputs are player commands; select unit, issue move order, etc. Criticals are essential updates; create unit, shoot unit's weapon, damage unit, etc. Corrections are mostly movement updates.
All channels are unordered and with the exception of Corrections all channels are "Reliable" in Unity terms (see Unity documentation). Given that movement updates happen regularly, if a few are dropped or arrive late this is not a critical system failure. But if a critical is lost, this would effectively result in a desync.
How can I prevent and handle critical data arriving late from the server?
My first thought is to implement a custom ping. This is used to set the time index for the data relative to the time the last ping took to get back from the client, plus server time index.
But I doubt that would be enough... and if it fails? Should the client pause or catch up based on the difference between local time index and the time index in the late critical? Does the PlayoutBuffer need to become an AdaptivePlayoutBuffer to resolve these issues to handle CPU and network lag? If so, how so? Any other ideas welcome!
EDIT: The reason inputs were arriving one time index too late sometimes was because the buffer's for loop which read inputs counted from 0 to list.count, and removed items as it iterated... when I changed it to reading from list.count to 0, it fixed the issue and no inputs arrived late. Whoops.