/// <summary> /// Adds a timeline to the manager. /// </summary> public void Add(TimelineBase timeline) { if (timeline._manager == this) { return; } if (timeline._manager != null) { timeline._manager.Remove(timeline); } timeline._manager = this; timeline._index = _nextTimelineIndex; timeline._now = _now; lock (_timelineLock) { _timelinesById[timeline._id] = timeline; _timelinesByIndex[timeline._index] = timeline; } Connect(timeline); if (_nextTimelineIndex == ushort.MaxValue) { _nextTimelineIndex = 0; } else { _nextTimelineIndex++; } }
/// <summary> /// Increments the local time and process messages. /// </summary> /// <param name="deltaTime">The time elapsed since the last step.</param> /// public void Step(double deltaTime) { // Used to prevent overcorrection. var totalError = _targetOffset - _timeOffset; // Used to prevent backwards flow of time. var minAllowedCorrection = -deltaTime; // Speed of correction is based on the amount of error. // This results in an inverse-exponential rate of convergence. var correctionRate = totalError * ClockSyncCorrectionFactor; // Make sure that the rate of correction is never too low, // so as to avoid prolonged periods of "close-but-not-quite". // We do this by always correcting "as if" the error is at least a certain size. correctionRate = Math.Sign(correctionRate) * Math.Max(MinClockSyncCorrectionRate, Math.Abs(correctionRate)); var correction = correctionRate * deltaTime; correction = Math.Max(Math.Min(correction, totalError), minAllowedCorrection); _now += deltaTime + correction; _timeOffset += correction; lock (_timelineLock) { var timelines = new TimelineBase[_timelinesById.Count]; _timelinesById.Values.CopyTo(timelines, 0); foreach (TimelineBase timeline in timelines) { timeline._now = _now; timeline.Step(); } } }
/// <summary> /// Returns the timeline with a given ID. If it does not exist, null is returned. /// </summary> public TimelineBase Get(byte[] id) { TimelineBase timeline = null; lock (_timelineLock) { _timelinesById.TryGetValue(id, out timeline); return(timeline); } }
/// <summary> /// Disconnects a timeline from the network. For internal use only. /// </summary> internal void Disconnect(TimelineBase timeline) { timeline._isConnected = false; byte[] data = new byte[sizeof(ushort)]; BinaryWriter writer = new BinaryWriter(new MemoryStream(data)); writer.Write(timeline._index); writer.Close(); EnqueueOutgoingMessage(new TimelineMessage(TimelineMessageType.DisconnectTimeline, data)); }
/// <summary> /// Removes a timeline from the manager. /// </summary> public void Remove(TimelineBase timeline) { if (timeline == null || timeline._manager != this) { return; } Disconnect(timeline); lock (_timelineLock) { _timelinesById.Remove(timeline._id); _timelinesByIndex.Remove(timeline._index); } timeline._manager = null; timeline._index = 0; }
/// <summary> /// Connects a timeline to the network. For internal use only. /// </summary> internal void Connect(TimelineBase timeline) { byte[] data = new byte[sizeof(ushort) + timeline._id.Length * sizeof(byte)]; BinaryWriter writer = new BinaryWriter(new MemoryStream(data)); writer.Write(timeline._index); writer.Write(timeline._id); writer.Close(); EnqueueOutgoingMessage(new TimelineMessage(TimelineMessageType.ConnectTimeline, data)); timeline._isConnected = true; // Send a cache size message if the timeline wants a cache size different from the default. if (timeline._cacheSize != TimelineBase._defaultCacheSize) { timeline.CacheSize = timeline._cacheSize; } }
/// <summary> /// Returns an array of timelines from the given object which belong to this manager. /// </summary> /// <param name="obj">The object to inspect.</param> /// <returns>An array of timelines.</returns> public TimelineBase[] GetTimelines(object obj) { List <TimelineBase> timelines = new List <TimelineBase>(); Type objType = obj.GetType(); // Loop through each field... foreach (FieldInfo field in objType.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { Type fieldType = field.FieldType; // Only generate timelines for timeline fields. if (!fieldType.IsSubclassOf(typeof(TimelineBase))) { continue; } // Fetch the timeline from the field. TimelineBase timeline = (TimelineBase)field.GetValue(obj); // Don't add null timelines or timelines which belong to other managers. if (timeline == null || timeline._manager != this) { continue; } // Don't add timelines specifically marked with Ignore. if (field.GetCustomAttributes(typeof(IgnoreAttribute), false).Length != 0) { continue; } // Add the timeline to the list of timelines to return. timelines.Add(timeline); } return(timelines.ToArray()); }
/// <summary> /// Remove all Timelines from the Timeline Manager /// </summary> public void Clear() { lock (_timelineLock) { TimelineBase[] timelines = new TimelineBase[_timelinesById.Count]; _timelinesById.Values.CopyTo(timelines, 0); foreach (var timeline in timelines) { if (timeline._manager != this) { return; } Disconnect(timeline); timeline._manager = null; timeline._index = 0; } _timelinesById.Clear(); _timelinesByIndex.Clear(); } }
/// <summary> /// Add an object's timeline fields. /// </summary> /// <param name="obj">The object whose timelines to add.</param> /// <param name="generateId">The ID generator to use.</param> /// <returns>An array containing the generated timelines.</returns> public TimelineBase[] AddTimelines(object obj, TimelineIDGenerator generateId = null) { List <TimelineBase> timelines = new List <TimelineBase>(); Type objType = obj.GetType(); // Loop through each field... foreach (FieldInfo field in objType.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { Type fieldType = field.FieldType; // Only generate timelines for timeline fields. if (!fieldType.IsSubclassOf(typeof(TimelineBase))) { continue; } // Fetch the timeline from the field. TimelineBase timeline = (TimelineBase)field.GetValue(obj); // Don't add null timelines or timelines which already have managers. if (timeline == null || timeline._manager != null) { continue; } // Don't add timelines specifically marked with Ignore. if (field.GetCustomAttributes(typeof(IgnoreAttribute), false).Length != 0) { continue; } timeline.Owner = obj; var tagsAttrs = field.GetCustomAttributes(typeof(TimelineTagsAttribute), false); // Save timeline tags. if (tagsAttrs.Length != 0) { var tagsAttr = (TimelineTagsAttribute)tagsAttrs[0]; foreach (var tag in tagsAttr.Tags) { timeline._tags.Add(tag); } } // Generate an ID for the timeline if it doesn't already have one. if (timeline._id == null) { timeline._id = generateId(field.Name); } // Add the timeline to this manager. Add(timeline); // Add the timeline to the list of timelines to return. timelines.Add(timeline); } return(timelines.ToArray()); }