using System; using Unity.Profiling; namespace Unity.Netcode { /// /// Provides discretized time. /// This is useful for games that require ticks happening at regular interval on the server and clients. /// public class NetworkTickSystem { #if DEVELOPMENT_BUILD || UNITY_EDITOR private static ProfilerMarker s_Tick = new ProfilerMarker($"{nameof(NetworkTickSystem)}.Tick"); #endif /// /// Special value to indicate "No tick information" /// public const int NoTick = int.MinValue; /// /// The TickRate of the tick system. This is used to decide how often a fixed network tick is run. /// public uint TickRate { get; internal set; } /// /// The current local time. This is the time at which predicted or client authoritative objects move. /// This value is accurate when called in Update or during the event but does not work correctly for FixedUpdate. /// public NetworkTime LocalTime { get; internal set; } /// /// The current server time. This value is mostly used for internal purposes and to interpolate state received from the server. /// This value is accurate when called in Update or during the event but does not work correctly for FixedUpdate. /// public NetworkTime ServerTime { get; internal set; } /// /// Gets invoked before every network tick. /// public event Action Tick; /// /// Creates a new instance of the class. /// /// The tick rate /// The initial local time to start at. /// The initial server time to start at. public NetworkTickSystem(uint tickRate, double localTimeSec, double serverTimeSec) { if (tickRate == 0) { throw new ArgumentException("Tickrate must be a positive value.", nameof(tickRate)); } TickRate = tickRate; Tick = null; LocalTime = new NetworkTime(tickRate, localTimeSec); ServerTime = new NetworkTime(tickRate, serverTimeSec); } /// /// Resets the tick system to the given network time. /// /// The local time in seconds. /// The server time in seconds. public void Reset(double localTimeSec, double serverTimeSec) { LocalTime = new NetworkTime(TickRate, localTimeSec); ServerTime = new NetworkTime(TickRate, serverTimeSec); } /// /// Called after advancing the time system to run ticks based on the difference in time. /// /// The local time in seconds /// The server time in seconds public void UpdateTick(double localTimeSec, double serverTimeSec) { // store old local tick to know how many fixed ticks passed var previousLocalTick = LocalTime.Tick; LocalTime = new NetworkTime(TickRate, localTimeSec); ServerTime = new NetworkTime(TickRate, serverTimeSec); // cache times here so that we can adjust them to temporary values while simulating ticks. var cacheLocalTime = LocalTime; var cacheServerTime = ServerTime; var currentLocalTick = LocalTime.Tick; var localToServerDifference = currentLocalTick - ServerTime.Tick; for (int i = previousLocalTick + 1; i <= currentLocalTick; i++) { // set exposed time values to correct fixed values LocalTime = new NetworkTime(TickRate, i); ServerTime = new NetworkTime(TickRate, i - localToServerDifference); #if DEVELOPMENT_BUILD || UNITY_EDITOR s_Tick.Begin(); #endif Tick?.Invoke(); #if DEVELOPMENT_BUILD || UNITY_EDITOR s_Tick.End(); #endif } // Set exposed time to values from tick system LocalTime = cacheLocalTime; ServerTime = cacheServerTime; } } }