예제 #1
0
 public void PutIncomingPacketSize(int size)
 {
     _inPacketSizes.Put(size);
 }
예제 #2
0
 public void PutOutgoingPacketSize(int size)
 {
     _outPacketSizes.Put(size);
 }
예제 #3
0
        /// <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);
        }