Ejemplo n.º 1
0
        /// <summary>
        /// Reads and overwrites this entity snapshot with data from a binary source, but only if the binary source represents a newer snapshot.
        /// Returns true if this snapshot was actually updated from the deserialized binary source.
        /// </summary>
        public bool DeserializeIfNewer(EntitySnapshot previousEntitySnapshot, IReader reader)
        {
            int newServerFrameTick = reader.ReadInt32();

            if (newServerFrameTick <= this.ServerFrameTick)
            {
                return(false);
            }

            this.ServerFrameTick = newServerFrameTick;
            this.EntityArray.Deserialize(previousEntitySnapshot?.EntityArray, reader);
            return(true);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Returns the oldest entity snapshot that exists in the history buffer.
        /// </summary>
        private EntitySnapshot getOldestHistoryEntitySnapshot()
        {
            // The snapshot history isn't in any order so we need to check every snapshot
            EntitySnapshot oldestEntitySnapshot = this.entitySnapshotHistory[0];

            foreach (EntitySnapshot entitySnapshot in this.entitySnapshotHistory)
            {
                if (entitySnapshot.ServerFrameTick < oldestEntitySnapshot.ServerFrameTick)
                {
                    oldestEntitySnapshot = entitySnapshot;
                }
            }
            return(oldestEntitySnapshot);
        }
        /// <summary>
        /// Deserializes a server update (entity snapshot and client-specific data) based on the given reader, basing incoming data on a previous snapshot's data.
        /// Only overwrites if the reader contains a newer server update. Returns true if the entity snapshot was actually updated from the reader.
        /// </summary>
        public static bool DeserializeIfNewer(IReader reader, EntitySnapshot[] previousEntitySnapshots, EntitySnapshot latestEntitySnapshot, out int latestClientTickAcknowledgedByServer, out int clientCommandingEntityID)
        {
            latestClientTickAcknowledgedByServer = reader.ReadInt32();
            clientCommandingEntityID             = reader.ReadInt32();
            int            previousServerFrameTick = reader.ReadInt32();
            EntitySnapshot previousEntitySnapshot  = null;

            foreach (EntitySnapshot entitySnapshot in previousEntitySnapshots)
            {
                if (entitySnapshot.ServerFrameTick == previousServerFrameTick)
                {
                    previousEntitySnapshot = entitySnapshot;
                    break;
                }
            }
            return(latestEntitySnapshot.DeserializeIfNewer(previousEntitySnapshot, reader));
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Updates the server state by processing client input, updating entities, and sending state to clients.
        /// </summary>
        public void Update()
        {
            this.FrameTick++;

            foreach (ClientProxy client in this.clients)
            {
                int newCommandingEntityID = this.updateCommandingEntityID.Invoke(client.IsConnected, client.CommandingEntityID);
                if (client.IsConnected)
                {
                    client.CommandingEntityID = newCommandingEntityID;
                    client.ReceiveClientCommands(this.EntityArray);
                }
                else
                {
                    client.Reset();
                }
            }

            this.SystemArray.ServerUpdate(this.EntityArray);

            // Take a snapshot of the latest entity state and add it to the snapshot history buffer (overwriting an old snapshot)
            EntitySnapshot newEntitySnapshot = this.entitySnapshotHistory.Dequeue();

            newEntitySnapshot.Update(this.FrameTick, this.EntityArray);
            this.entitySnapshotHistory.Enqueue(newEntitySnapshot);

            if (this.FrameTick % this.NetworkSendRate == 0)
            {
                foreach (ClientProxy client in this.clients)
                {
                    if (client.IsConnected)
                    {
                        client.SendServerUpdate(newEntitySnapshot);
                    }
                }
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Updates this client's commanding entity so that it responds immediately to client commands (rather than waiting for the server to acknowledge
        /// the input).
        /// </summary>
        private void updatePrediction()
        {
            if (this.ShouldPredictInput && this.HasRenderingStarted && this.CommandingEntityID != -1 && this.RenderedSnapshot.EntityArray.TryGetEntity(this.CommandingEntityID, out Entity predictedEntity))
            {
                // Start at the latest server update and apply all client commands that have been taken after that
                EntitySnapshot latestHistoryEntitySnapshot = this.getLatestHistoryEntitySnapshot();
                if (latestHistoryEntitySnapshot.EntityArray.TryGetEntity(this.CommandingEntityID, out Entity latestHistoryEntity))
                {
                    latestHistoryEntity.CopyTo(predictedEntity);
                    foreach (ClientCommand <TCommandData> clientCommand in this.clientCommandHistory)
                    {
                        // If the command was already received by the server then it shouldn't be predicted (the results are in the updates sent to us), if the command was for a different
                        // entity than what we are currently commanding then we can't predict at all so ignore it
                        if (!clientCommand.HasData || clientCommand.ClientFrameTick <= this.LatestFrameTickAcknowledgedByServer || clientCommand.CommandingEntityID != this.CommandingEntityID)
                        {
                            continue;
                        }

                        // Reapply all the commands we've sent that the server hasn't processed yet to get us to where we predict we should be
                        this.SystemArray.PredictClientCommand(this.RenderedSnapshot.EntityArray, predictedEntity, clientCommand.CommandData);
                    }
                }
            }
        }
Ejemplo n.º 6
0
            /// <summary>
            /// Sends the client an update of the given entity state and other information.
            /// </summary>
            public void SendServerUpdate(EntitySnapshot latestEntitySnapshot)
            {
                EntitySnapshot previousEntitySnapshot = this.parentServer.getEntitySnapshotForServerFrameTick(this.LatestFrameTickAcknowledgedByClient);

                ServerUpdateSerializer.Send(this.clientNetworkConnection, previousEntitySnapshot, latestEntitySnapshot, this.LatestClientTickReceived, this.CommandingEntityID);
            }
Ejemplo n.º 7
0
 /// <summary>
 /// Reads and overwrites this entity snapshot with data from a binary source, basing incoming data on a previous snapshot's data.
 /// </summary>
 public void Deserialize(EntitySnapshot previousEntitySnapshot, IReader reader)
 {
     this.ServerFrameTick = reader.ReadInt32();
     this.EntityArray.Deserialize(previousEntitySnapshot?.EntityArray, reader);
 }
Ejemplo n.º 8
0
 /// <summary>
 /// Writes this entity snapshot's data to a binary source, only writing data that has changed from a previous snapshot.
 /// </summary>
 public void Serialize(EntitySnapshot previousEntitySnapshot, IWriter writer)
 {
     writer.Write(this.ServerFrameTick);
     this.EntityArray.Serialize(previousEntitySnapshot?.EntityArray, writer);
 }
Ejemplo n.º 9
0
 /// <summary>
 /// Copies all entity and meta-data to another entity snapshot.
 /// </summary>
 public void CopyTo(EntitySnapshot other)
 {
     other.Update(this.ServerFrameTick, this.EntityArray);
 }
Ejemplo n.º 10
0
        /// <summary>
        /// Updates the rendered snapshot with latest data from the server, interpolating as necessary.
        /// </summary>
        private void updateRenderedSnapshot()
        {
            int renderedFrameTick = this.FrameTick - this.InterpolationRenderDelay;

            if (!this.HasInterpolationStarted)
            {
                // We haven't received enough data from the server yet to start interpolation rendering,
                // so keep polling until we get enough data, once we have enough data we can begin rendering.
                // The snapshot history isn't in any order so we need to check every snapshot for the closest ticks in both directions (start and end)
                EntitySnapshot newInterpolationStartSnapshot = null;
                EntitySnapshot newInterpolationEndSnapshot   = null;
                foreach (EntitySnapshot entitySnapshot in this.entitySnapshotHistory)
                {
                    if (entitySnapshot.ServerFrameTick == -1)
                    {
                        continue;
                    }

                    if (entitySnapshot.ServerFrameTick <= renderedFrameTick && (newInterpolationStartSnapshot == null || entitySnapshot.ServerFrameTick > newInterpolationStartSnapshot.ServerFrameTick))
                    {
                        newInterpolationStartSnapshot = entitySnapshot;
                    }
                    if (entitySnapshot.ServerFrameTick > renderedFrameTick && (newInterpolationEndSnapshot == null || entitySnapshot.ServerFrameTick < newInterpolationEndSnapshot.ServerFrameTick))
                    {
                        newInterpolationEndSnapshot = entitySnapshot;
                    }
                }
                if (newInterpolationStartSnapshot != null && newInterpolationEndSnapshot != null)
                {
                    newInterpolationStartSnapshot.CopyTo(this.InterpolationStartSnapshot);
                    newInterpolationEndSnapshot.CopyTo(this.InterpolationEndSnapshot);
                }
            }

            // Check to see if we still can't interpolate after going through the latest received updates
            if (!this.HasInterpolationStarted)
            {
                return;
            }

            if (renderedFrameTick > this.InterpolationEndSnapshot.ServerFrameTick)
            {
                // Find the next closest entity snapshot to start interpolating to
                // The snapshot history isn't in any order so we need to check every snapshot closest next tick
                EntitySnapshot newInterpolationEndSnapshot = null;
                foreach (EntitySnapshot entitySnapshot in this.entitySnapshotHistory)
                {
                    if (entitySnapshot.ServerFrameTick > renderedFrameTick && (newInterpolationEndSnapshot == null || entitySnapshot.ServerFrameTick < newInterpolationEndSnapshot.ServerFrameTick))
                    {
                        newInterpolationEndSnapshot = entitySnapshot;
                    }
                }

                if (newInterpolationEndSnapshot != null)
                {
                    this.RenderedSnapshot.CopyTo(this.InterpolationStartSnapshot);
                    newInterpolationEndSnapshot.CopyTo(this.InterpolationEndSnapshot);
                }
            }

            if (this.ShouldInterpolate)
            {
                // Clamp the interpolation frame tick to the maximum number of frames we are allowed to extrapolate, then render that
                // Always make sure the rendered state has the correct rendered frame tick number, even if extrapolation was clamped
                int interpolationFrameTick = Math.Min(renderedFrameTick, this.InterpolationEndSnapshot.ServerFrameTick + this.MaxExtrapolationTicks);
                this.RenderedSnapshot.Interpolate(this.InterpolationStartSnapshot, this.InterpolationEndSnapshot, interpolationFrameTick, renderedFrameTick);

                if (interpolationFrameTick < renderedFrameTick)
                {
                    this.NumberOfNoInterpolationFrames++;
                }
                else if (this.InterpolationEndSnapshot.ServerFrameTick < renderedFrameTick)
                {
                    this.NumberOfExtrapolatedFrames++;
                }
            }
            else
            {
                // Even though we aren't interpolating, we still want to use whatever the user picked as the render delay so
                // use the end interpolation state and just snap to it
                this.InterpolationEndSnapshot.CopyTo(this.RenderedSnapshot);
            }
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Serializes the given server update (entity snapshot and client-specific data) to the given writer, only writing data that has changed from a previous snapshot.
 /// </summary>
 public static void Serialize(IWriter writer, EntitySnapshot previousEntitySnapshot, EntitySnapshot latestEntitySnapshot, int latestClientTickReceived, int clientCommandingEntityID)
 {
     writer.Write(latestClientTickReceived);
     writer.Write(clientCommandingEntityID);
     writer.Write((previousEntitySnapshot == null) ? -1 : previousEntitySnapshot.ServerFrameTick);
     latestEntitySnapshot.Serialize(previousEntitySnapshot, writer);
 }
Ejemplo n.º 12
0
        /// <summary>
        /// Serializes the given server update (entity snapshot and client-specific data) and immediately sends a packet, only writing data that has changed from a previous snapshot..
        /// </summary>
        public static void Send(INetworkConnection clientNetworkConnection, EntitySnapshot previousEntitySnapshot, EntitySnapshot latestEntitySnapshot, int latestClientTickReceived, int clientCommandingEntityID)
        {
            OutgoingMessage outgoingMessage = clientNetworkConnection.GetOutgoingMessageToSend();

            ServerUpdateSerializer.Serialize(outgoingMessage, previousEntitySnapshot, latestEntitySnapshot, latestClientTickReceived, clientCommandingEntityID);
            clientNetworkConnection.SendMessage(outgoingMessage);
        }