public DeltaUpdate ReadDeltaUpdate() { if (!_playing) { return(null); } if (_playbackDeltaUpdates.Count == 0) { return(null); } DeltaUpdate deltaUpdate = _playbackDeltaUpdates.Peek(); // If this update is newer than the current playback time, bail if (deltaUpdate.timestamp > _playbackTime) { return(null); } // Dequeue _playbackDeltaUpdates.Dequeue(); return(deltaUpdate); }
public bool ReadDeltaUpdate(double playbackTime, DeltaUpdate deltaUpdate) { return(_fileStream.ReadDeltaUpdate(playbackTime, ref deltaUpdate.timestamp, ref deltaUpdate.sender, ref deltaUpdate.data, ref deltaUpdate.reliable, ref deltaUpdate.updateID, ref deltaUpdate.incoming)); }
private static bool AdjustOutgoingUnreliableDeltaUpdateTimestamp(double playbackTime, PlaybackStream playbackStream, DeltaUpdate deltaUpdate) { // Adjust the timestamp double timestamp = deltaUpdate.timestamp + playbackStream.sendTimestampOffset; // If the update is now newer than the current playback time, then bail. if (timestamp > playbackTime) { return(false); } // Adjust update timestamp deltaUpdate.timestamp += playbackStream.sendTimestampOffset; return(true); }
private static bool AdjustPlaybackStreamSendTimestampOffsetWithOutgoingReliableDeltaUpdate(PlaybackStream playbackStream, Dictionary <uint, DeltaUpdate> incomingReliableUpdates, DeltaUpdate outgoingReliableDeltaUpdate) { // Check for a matching reliable delta update. If we haven't received it yet, then bail, it's not time to decode this update yet. DeltaUpdate incomingReliableDeltaUpdate; if (incomingReliableUpdates == null || !incomingReliableUpdates.TryGetValue(outgoingReliableDeltaUpdate.updateID, out incomingReliableDeltaUpdate)) { return(false); } // Found a matching reliable update. Use the round trip time to adjust outgoing unreliable updates. playbackStream.sendTimestampOffset = incomingReliableDeltaUpdate.timestamp - outgoingReliableDeltaUpdate.timestamp; // Remove from incoming reliable updates incomingReliableUpdates.Remove(outgoingReliableDeltaUpdate.updateID); return(true); }
public void PlaybackTick(double deltaTime) { if (!_playing) { return; } // Increment playback time _playbackTime += deltaTime; // Keep track of whether this frame produced any updates for playback bool didAddDeltaUpdateToPlaybackQueue = false; // Read delta updates from primary playback stream DeltaUpdate deltaUpdate = new DeltaUpdate(); while (_primaryPlaybackStream.ReadDeltaUpdate(_playbackTime, deltaUpdate)) { // Add all outgoing updates to the queue to be processed if (deltaUpdate.outgoing) { _primaryPlaybackDeltaUpdatesToBeProcessed.Enqueue(deltaUpdate); } // If it's an incoming reliable update, keep track so we can use the timestamp to calculate timestamp offsets. if (deltaUpdate.incoming && deltaUpdate.reliable) { // If this is an incoming reliable update, add it directly to the queue of updates to play back. It doesn't need any timestamp adjustments or anything. _playbackDeltaUpdates.Enqueue(deltaUpdate); didAddDeltaUpdateToPlaybackQueue = true; // Retrieve dictionary for this sender. Create a new one if needed. Dictionary <uint, DeltaUpdate> incomingReliableUpdates; if (!_clientToIncomingReliableUpdatesMap.TryGetValue(deltaUpdate.sender, out incomingReliableUpdates)) { incomingReliableUpdates = new Dictionary <uint, DeltaUpdate>(); _clientToIncomingReliableUpdatesMap.Add(deltaUpdate.sender, incomingReliableUpdates); } // Add the update incomingReliableUpdates.Add(deltaUpdate.updateID, deltaUpdate); } // Create a new update for the next loop deltaUpdate = new DeltaUpdate(); } // Adjust the timestamps for outgoing unreliable messages by measuring the roundtrip time on reliable messages. // We're using incoming reliable updates for playback (because the server assigns uniqueIDs and things), but unreliable updates are recorded when they're sent. // That means that the reliable updates will have timestamps that are later than when they were actually sent. To fix this, we adjust the timestamps of the // unreliable updates to account for the roundtrip delay that we're seeing. // Grab the incoming reliable updates for the primary stream's client ID Dictionary <uint, DeltaUpdate> primaryPlaybackClientIncomingReliableUpdates; _clientToIncomingReliableUpdatesMap.TryGetValue(_primaryPlaybackStream.clientID, out primaryPlaybackClientIncomingReliableUpdates); // Loop through updates and process them until we run out or we hit one that requires us to wait for more time to pass / more frames to be decoded while (_primaryPlaybackDeltaUpdatesToBeProcessed.Count > 0) { // Peek at update deltaUpdate = _primaryPlaybackDeltaUpdatesToBeProcessed.Peek(); // Outgoing unreliable update. if (deltaUpdate.outgoing && deltaUpdate.unreliable) { bool success = AdjustOutgoingUnreliableDeltaUpdateTimestamp(_playbackTime, _primaryPlaybackStream, deltaUpdate); // If we were unable to successfully adjust the update, break out of the loop. We'll try again next frame after more time has passed. if (!success) { break; } // Add to outgoing updates _playbackDeltaUpdates.Enqueue(deltaUpdate); didAddDeltaUpdateToPlaybackQueue = true; } // Outgoing reliable update. Find the matching incoming reliable update and use it to adjust the unreliableTimestampOffset else if (deltaUpdate.outgoing && deltaUpdate.reliable) { bool success = AdjustPlaybackStreamSendTimestampOffsetWithOutgoingReliableDeltaUpdate(_primaryPlaybackStream, primaryPlaybackClientIncomingReliableUpdates, deltaUpdate); // If we were unable to successfully adjust the send timestamp offset, break out of the loop. We'll try again next frame after more time has passed. if (!success) { break; } } // If we hit this point, the update has been processed, dequeue it and move onto the next one. _primaryPlaybackDeltaUpdatesToBeProcessed.Dequeue(); } // Loop through each secondary stream. Perform the timestamp adjustments and add them to the playback queue. foreach (KeyValuePair <int, PlaybackStream> secondaryPlaybackStream in _secondaryPlaybackStreams) { int clientID = secondaryPlaybackStream.Key; PlaybackStream playbackStream = secondaryPlaybackStream.Value; // Grab the incoming reliable updates for this stream's clientID Dictionary <uint, DeltaUpdate> incomingReliableUpdates; _clientToIncomingReliableUpdatesMap.TryGetValue(clientID, out incomingReliableUpdates); // Read updates up until playbackTime processing them as we go. Stop if we run out of updates or we hit an update that we can't process yet. deltaUpdate = new DeltaUpdate(); while (playbackStream.ReadDeltaUpdate(_playbackTime, deltaUpdate)) { // Outgoing unreliable update. if (deltaUpdate.outgoing && deltaUpdate.unreliable) { bool success = AdjustOutgoingUnreliableDeltaUpdateTimestamp(_playbackTime, playbackStream, deltaUpdate); // If we were unable to successfully adjust the update, break out of the loop. We'll try again next frame after more time has passed. if (!success) { break; } // Add to outgoing updates _playbackDeltaUpdates.Enqueue(deltaUpdate); didAddDeltaUpdateToPlaybackQueue = true; } // Outgoing reliable update. Find the matching incoming reliable update and use it to adjust the unreliableTimestampOffset else if (deltaUpdate.outgoing && deltaUpdate.reliable) { bool success = AdjustPlaybackStreamSendTimestampOffsetWithOutgoingReliableDeltaUpdate(playbackStream, incomingReliableUpdates, deltaUpdate); // If we were unable to successfully adjust the send timestamp offset, break out of the loop. We'll try again next frame after more time has passed. if (!success) { break; } } } } // If we added any updates to the playback queue, sort it to make sure that they're in order after timestamp adjustments were made. if (didAddDeltaUpdateToPlaybackQueue) { _playbackDeltaUpdates = new Queue <DeltaUpdate>(_playbackDeltaUpdates.OrderBy(playbackDeltaUpdate => playbackDeltaUpdate.timestamp)); } // If we've finished reading updates, we're done playing. _playing = _primaryPlaybackStream.reading; }