public void PutIncomingPacketSize(int size) { _inPacketSizes.Put(size); }
public void PutOutgoingPacketSize(int size) { _outPacketSizes.Put(size); }
/// <summary> /// Takes care of client side TSS synchronization logic. /// </summary> protected override FrameCommand UnwrapDataForReceive(SessionDataEventArgs e) { var args = (ClientDataEventArgs)e; var type = (TssControllerMessage)args.Data.ReadByte(); switch (type) { case TssControllerMessage.Command: { // Normal command, forward it. var command = base.UnwrapDataForReceive(e); // Test if we got the message from the server, to mark the command accordingly. command.IsAuthoritative = args.IsAuthoritative; // Return the deserialized command. return(command); } case TssControllerMessage.Synchronize: { // Answer to a synchronization request. // Only accept these when they come from the server, and disregard if // we're waiting for a snapshot of the simulation. if (args.IsAuthoritative && !Tss.WaitingForSynchronization) { // This calculation follows algorithm described here: // http://www.mine-control.com/zack/timesync/timesync.html var sentFrame = args.Data.ReadInt64(); var serverFrame = args.Data.ReadInt64(); // We also adjust the game speed to accommodate slow // machines. That's the speed we get in this step. AdjustedSpeed = args.Data.ReadSingle(); var latency = (Tss.CurrentFrame - sentFrame) / 2; var clientServerDelta = (serverFrame - Tss.CurrentFrame); var frameDelta = clientServerDelta + latency / 2; _frameDiff.Put((int)frameDelta); var median = _frameDiff.Median(); var stdDev = _frameDiff.StandardDeviation(); if (System.Math.Abs(frameDelta) > 1 && frameDelta < (int)(median + stdDev)) { Logger.Trace("Correcting for {0} frames.", frameDelta); // Adjust the current frame of the simulation. ScheduleFrameskip(frameDelta); } } break; } case TssControllerMessage.HashCheck: { // Only accept these when they come from the server. if (args.IsAuthoritative) { // Get the frame this hash data is for. var hashFrame = args.Data.ReadInt64(); Debug.Assert(hashFrame > _lastServerHashedFrame); _lastServerHashedFrame = hashFrame; // Read hash values. var hashValue = args.Data.ReadUInt32(); // And perform hash check. PerformHashCheck(hashValue, hashFrame, Tss.TrailingFrame < _lastServerHashedFrame); } break; } case TssControllerMessage.GameState: { // Got a simulation snap shot (normally after requesting it due to // our simulation going out of scope for an older event). // Only accept these when they come from the server. if (args.IsAuthoritative) { // Read data. var serverHash = args.Data.ReadUInt32(); args.Data.ReadPacketizableInto(Tss); // Validate the data we got. var hasher = new Hasher(); hasher.Write(Tss.TrailingSimulation); if (hasher.Value != serverHash) { Logger.Error("Hash mismatch after deserialization."); Session.Leave(); } // Run to current frame to avoid slow interpolation to current frame. // Take into account the time we need to get there. var delta = 0L; do { // Remember when we started, see below. var started = DateTime.UtcNow; // Do the actual update, run to where we want to be. Tss.RunToFrame(Tss.CurrentFrame + delta); // See how long we took for this update, and compute the number // of updates we'd otherwise have made in that time. delta = (long)((DateTime.UtcNow - started).TotalMilliseconds / TargetElapsedMilliseconds); // Continue until we're close enough with the *actual* current // frame to what we want to be at. } while (delta > 10); } break; } case TssControllerMessage.RemoveGameObject: { // Only accept these when they come from the server. if (args.IsAuthoritative) { var removeFrame = args.Data.ReadInt64(); var entityUid = args.Data.ReadInt32(); Tss.RemoveEntity(entityUid, removeFrame); } break; } } return(null); }