-
Notifications
You must be signed in to change notification settings - Fork 13
Game Synchronizer
The MDGameSynchronizer has three main features.
- Attempts to detect the offset of OS.GetTicksMSec() between clients so commands can be executed at the same time on all clients.
- Track ping to other clients
- Synchronize joining so we can ensure new clients are completely synched
The MDGameSynchronizer will by default automatically pause the game when a new player joins, then wait for the new player to synchronize all networked nodes before resuming. Network nodes that need special handling can implement the IMDSynchronizedNode interface to tell the MDGameSynchronizer when they have completed synchronization.
Once the all players have been synchronized a resume command will be sent to all clients using the estimated OS.GetTicksMsec() for each client. This ensures all the clients unpauses the game at almost the same time.
Example usage of most events can be found in the BasicNetworkLobbyExample.
This event is called when synchronization is started. This is intended to be used for showing UI or taking any custom actions requrired when synchronization is started.
- IsPaused - Will be true if auto pause is active, false if not
If auto pause is one this event will be called when we are about to resume the game.
- ResumeGameIn - Time until game is resumed in seconds.
Intended to be used to update UI with player synchronization progress.
- PeerId - The ID of the peer this status update is for.
- ProgressPercentage - The synchronization progress as a percentage value from 0 to 1.
If you want to do something when the player completes synching you can subscribe to this event and do something like this
protected bool AlreadySynched = false;
protected void SynchStatusUpdate(int PeerId, float ProgressPercentage)
{
if (AlreadySynched)
{
return;
}
// Float compares are annoying
if (PeerId == MDStatics.GetPeerId() && ProgressPercentage >= 0.99f)
{
// Do your stuff here
AlreadySynched = true;
}
}
Sent every time we calculate a new estimated ping for a given peer. Used by the GameClock to automatically adjust ticks based on player ping.
- PeerId - The ID of the peer that the ping update is for.
- Ping - The average ping of this player.
Returns the ping of the given peer or -1 if the peer does not exist
- PeerId - The ID of the peer you want the ping for.
- Return - int value indicating the player average ping.
Get the highest ping of any player in the game.
- Return - int value containing the highest ping of currently connected players.
Returns the estimated OS.GetTicksMsec for the given peer or 0 if the peer does not exist.
- PeerId - The ID of the peer you want information for.
- Return - uint value indicating the estimated current tick for the given peer.
By using the command this.GetPlayerTicksMsec(PeerId) from any node you can get the estimated OS.GetTicksMSec() value for the peer. This allows for all clients to execute commands at the same time. Take this code from the PredictiveSynchronizationExample.
foreach (int peerid in this.GetGameSession().GetAllPeerIds())
{
if (peerid == MDStatics.GetPeerId())
{
// Set our start time
StartAt = OS.GetTicksMsec() + delay;
}
else
{
// Set start time for other clients
RpcId(peerid, nameof(RpcSetStartTime), this.GetPlayerTicksMsec(peerid) + delay);
}
}
This code sets a start time to some time in the future to be executed at almost the same time on all clients, under normal network conditions the estimations are usually within 20 ms of each other. So even if the rpc call arrives a few second apart on all clients as long as it arrives before the execution time all clients can perform the action at the same time. The ping that the synchronizer provides can be used to estimate how far in the future a command execution needs to be set to ensure all clients have recieved the signal.
The configuration methods are virtual methods that you can override in your own implementation of the MDGameSynchronizer. To make the framework load your custom class you will also need to override GetGameSynchronizerType() in MDGameInstance
(Default: True)
If set to true the game will pause when a new player joins. Allowing the game to pause will allow for better synchronization between players.
(Default: False)
If set to true it will delay MDReplicator from sending any data unti all nodes are synched.
(Default: 2 seconds)
How long to countdown before resuming the game after synchronization is complete. This is mainly to allow all players to unpause at the same time because the unpause signal needs some time to be sent to all players.
(Default: half second)
Decides how often we ping other clients, while lower values causes ping signals to be sent more often it also allows the GameClock to adjust it's offset faster in case ping increases or decreases.
(Default: 10)
Player ping is calculated as an average over the last X ping results. This value decides the size of that X.
(Default: true)
If this is set the GameSynchronizer will continuously ping other players during gameplay to get updated ping values. This is required for the GameClock to function properly.
(Default: 20)
This decides how many times we go back and forth to establish the OS.GetTicksMsec offset for each client. 20 seems to be a decent amount of times to get a fairly accurate value. Lowering this value will mean less accurate synching between clients. Increasing this value too much will mean longer initial synchronization as the game will not unpause until a minimum amount of measurements have been made.
(Default: True)
Sets if we should use the GameClock or not, this requires IsActivePingEnabled to be true else the GameClock will not function properly.
(Default: GetInitialMeasurementCount() / 2)
If IsPauseOnJoin() is enabled we will wait for at least this level of security for TicksMsec before we resume the game.