This repository has been archived on 2025-04-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs
Unity Technologies c813386c5c com.unity.netcode.gameobjects@2.0.0-pre.2
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [2.0.0-pre.2] - 2024-06-17

### Added

- Added `AnticipatedNetworkVariable<T>`, which adds support for client anticipation of `NetworkVariable` values, allowing for more responsive gameplay. (#2957)
- Added `AnticipatedNetworkTransform`, which adds support for client anticipation of NetworkTransforms. (#2957)
- Added `NetworkVariableBase.ExceedsDirtinessThreshold` to allow network variables to throttle updates by only sending updates when the difference between the current and previous values exceeds a threshold. (This is exposed in `NetworkVariable<T>` with the callback `NetworkVariable<T>.CheckExceedsDirtinessThreshold`). (#2957)
- Added `NetworkVariableUpdateTraits`, which add additional throttling support: `MinSecondsBetweenUpdates` will prevent the `NetworkVariable` from sending updates more often than the specified time period (even if it exceeds the dirtiness threshold), while `MaxSecondsBetweenUpdates` will force a dirty `NetworkVariable` to send an update after the specified time period even if it has not yet exceeded the dirtiness threshold. (#2957)
- Added virtual method `NetworkVariableBase.OnInitialize` which can be used by `NetworkVariable` subclasses to add initialization code. (#2957)
- Added `NetworkTime.TickWithPartial`, which represents the current tick as a double that includes the fractional/partial tick value. (#2957)
- Added `NetworkTickSystem.AnticipationTick`, which can be helpful with implementation of client anticipation. This value represents the tick the current local client was at at the beginning of the most recent network round trip, which enables it to correlate server update ticks with the client tick that may have triggered them. (#2957)
- Added event `NetworkManager.OnSessionOwnerPromoted` that is invoked when a new session owner promotion occurs. (#2948)
- Added `NetworkRigidBodyBase.GetLinearVelocity` and `NetworkRigidBodyBase.SetLinearVelocity` convenience/helper methods. (#2948)
- Added `NetworkRigidBodyBase.GetAngularVelocity` and `NetworkRigidBodyBase.SetAngularVelocity` convenience/helper methods. (#2948)

### Fixed

- Fixed issue when `NetworkTransform` half float precision is enabled and ownership changes the current base position was not being synchronized. (#2948)
- Fixed issue where `OnClientConnected` not being invoked on the session owner when connecting to a new distributed authority session. (#2948)
- Fixed issue where Rigidbody micro-motion (i.e. relatively small velocities) would result in non-authority instances slightly stuttering as the body would come to a rest (i.e. no motion). Now, the threshold value can increase at higher velocities and can decrease slightly below the provided threshold to account for this. (#2948)

### Changed

- Changed `NetworkAnimator` no longer requires the `Animator` component to exist on the same `GameObject`. (#2957)
- Changed `NetworkObjectReference` and `NetworkBehaviourReference` to allow null references when constructing and serializing. (#2957)
- Changed the client's owned objects is now returned (`NetworkClient` and `NetworkSpawnManager`) as an array as opposed to a list for performance purposes. (#2948)
- Changed `NetworkTransfrom.TryCommitTransformToServer` to be internal as it will be removed by the final 2.0.0 release. (#2948)
- Changed `NetworkTransformEditor.OnEnable` to a virtual method to be able to customize a `NetworkTransform` derived class by creating a derived editor control from `NetworkTransformEditor`. (#2948)
2024-06-17 00:00:00 +00:00

255 lines
11 KiB
C#

using System;
using Unity.Profiling;
namespace Unity.Netcode
{
/// <summary>
/// <see cref="NetworkTimeSystem"/> is a standalone system which can be used to run a network time simulation.
/// The network time system maintains both a local and a server time. The local time is based on the server time
/// as last received from the server plus an offset based on the current RTT - in other words, it is a best-guess
/// effort at predicting what the server tick will be when a given network action is processed on the server.
/// </summary>
public class NetworkTimeSystem
{
/// <remarks>
/// This was the original comment when it lived in NetworkManager:
/// todo talk with UX/Product, find good default value for this
/// </remarks>
private const float k_DefaultBufferSizeSec = 0.05f;
/// <summary>
/// Time synchronization frequency defaults to 1 synchronization message per second
/// </summary>
private const double k_TimeSyncFrequency = 1.0d;
/// <summary>
/// The threshold, in seconds, used to force a hard catchup of network time
/// </summary>
private const double k_HardResetThresholdSeconds = 0.2d;
/// <summary>
/// Default adjustment ratio
/// </summary>
private const double k_DefaultAdjustmentRatio = 0.01d;
#if DEVELOPMENT_BUILD || UNITY_EDITOR
private static ProfilerMarker s_SyncTime = new ProfilerMarker($"{nameof(NetworkManager)}.SyncTime");
#endif
private double m_TimeSec;
private double m_CurrentLocalTimeOffset;
private double m_DesiredLocalTimeOffset;
private double m_CurrentServerTimeOffset;
private double m_DesiredServerTimeOffset;
/// <summary>
/// Gets or sets the amount of time in seconds the server should buffer incoming client messages.
/// This increases the difference between local and server time so that messages arrive earlier on the server.
/// </summary>
public double LocalBufferSec { get; set; }
/// <summary>
/// Gets or sets the amount of the time in seconds the client should buffer incoming messages from the server. This increases server time.
/// A higher value increases latency but makes the game look more smooth in bad networking conditions.
/// This value must be higher than the tick length client side.
/// </summary>
public double ServerBufferSec { get; set; }
/// <summary>
/// Gets or sets a threshold in seconds used to force a hard catchup of network time.
/// </summary>
public double HardResetThresholdSec { get; set; }
/// <summary>
/// Gets or sets the ratio at which the NetworkTimeSystem speeds up or slows down time.
/// </summary>
public double AdjustmentRatio { get; set; }
/// <summary>
/// The current local time with the local time offset applied
/// </summary>
public double LocalTime => m_TimeSec + m_CurrentLocalTimeOffset;
/// <summary>
/// The current server time with the server time offset applied
/// </summary>
public double ServerTime => m_TimeSec + m_CurrentServerTimeOffset;
internal double LastSyncedServerTimeSec { get; private set; }
internal double LastSyncedRttSec { get; private set; }
private NetworkConnectionManager m_ConnectionManager;
private NetworkTransport m_NetworkTransport;
private NetworkTickSystem m_NetworkTickSystem;
private NetworkManager m_NetworkManager;
/// <summary>
/// <see cref="k_TimeSyncFrequency"/>
/// </summary>
private int m_TimeSyncFrequencyTicks;
/// <summary>
/// The constructor class for <see cref="NetworkTickSystem"/>
/// </summary>
/// <param name="localBufferSec">The amount of time, in seconds, the server should buffer incoming client messages.</param>
/// <param name="serverBufferSec">The amount of the time in seconds the client should buffer incoming messages from the server.</param>
/// <param name="hardResetThresholdSec">The threshold, in seconds, used to force a hard catchup of network time.</param>
/// <param name="adjustmentRatio">The ratio at which the NetworkTimeSystem speeds up or slows down time.</param>
public NetworkTimeSystem(double localBufferSec, double serverBufferSec = k_DefaultBufferSizeSec, double hardResetThresholdSec = k_HardResetThresholdSeconds, double adjustmentRatio = k_DefaultAdjustmentRatio)
{
LocalBufferSec = localBufferSec;
ServerBufferSec = serverBufferSec;
HardResetThresholdSec = hardResetThresholdSec;
AdjustmentRatio = adjustmentRatio;
}
/// <summary>
/// The primary time system is initialized when a server-host or client is started
/// </summary>
internal NetworkTickSystem Initialize(NetworkManager networkManager)
{
m_NetworkManager = networkManager;
m_ConnectionManager = networkManager.ConnectionManager;
m_NetworkTransport = networkManager.NetworkConfig.NetworkTransport;
m_TimeSyncFrequencyTicks = (int)(k_TimeSyncFrequency * networkManager.NetworkConfig.TickRate);
m_NetworkTickSystem = new NetworkTickSystem(networkManager.NetworkConfig.TickRate, 0, 0);
// Only the server side needs to register for tick based time synchronization
if (m_ConnectionManager.LocalClient.IsServer)
{
m_NetworkTickSystem.Tick += OnTickSyncTime;
}
return m_NetworkTickSystem;
}
internal void UpdateTime()
{
// As a client wait to run the time system until we are connected.
// As a client or server don't worry about the time system if we are no longer processing messages
if (!m_ConnectionManager.LocalClient.IsServer && !m_ConnectionManager.LocalClient.IsConnected)
{
return;
}
// Only update RTT here, server time is updated by time sync messages
var reset = Advance(m_NetworkManager.RealTimeProvider.UnscaledDeltaTime);
if (reset)
{
m_NetworkTickSystem.Reset(LocalTime, ServerTime);
}
m_NetworkTickSystem.UpdateTick(LocalTime, ServerTime);
if (!m_ConnectionManager.LocalClient.IsServer)
{
Sync(LastSyncedServerTimeSec + m_NetworkManager.RealTimeProvider.UnscaledDeltaTime, m_NetworkTransport.GetCurrentRtt(NetworkManager.ServerClientId) / 1000d);
}
}
/// <summary>
/// Server-Side:
/// Synchronizes time with clients based on the given <see cref="m_TimeSyncFrequencyTicks"/>.
/// Also: <see cref="k_TimeSyncFrequency"/>
/// </summary>
/// <remarks>
/// The default is to send 1 time synchronization message per second
/// </remarks>
private void OnTickSyncTime()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_SyncTime.Begin();
#endif
// Check if we need to send a time synchronization message, and if so send it
if (m_ConnectionManager.LocalClient.IsServer && m_NetworkTickSystem.ServerTime.Tick % m_TimeSyncFrequencyTicks == 0)
{
var message = new TimeSyncMessage
{
Tick = m_NetworkTickSystem.ServerTime.Tick
};
m_ConnectionManager.SendMessage(ref message, NetworkDelivery.Unreliable, m_ConnectionManager.ConnectedClientIds);
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_SyncTime.End();
#endif
}
/// <summary>
/// Invoke when shutting down the NetworkManager
/// </summary>
internal void Shutdown()
{
if (m_ConnectionManager.LocalClient.IsServer)
{
m_NetworkTickSystem.Tick -= OnTickSyncTime;
}
}
/// <summary>
/// Creates a new instance of the <see cref="NetworkTimeSystem"/> class for a server instance.
/// The server will not apply any buffer values which ensures that local time equals server time.
/// </summary>
/// <returns>The instance.</returns>
public static NetworkTimeSystem ServerTimeSystem()
{
return new NetworkTimeSystem(0, 0, double.MaxValue);
}
/// <summary>
/// Advances the time system by a certain amount of time. Should be called once per frame with Time.unscaledDeltaTime or similar.
/// </summary>
/// <param name="deltaTimeSec">The amount of time to advance. The delta time which passed since Advance was last called.</param>
/// <returns></returns>
public bool Advance(double deltaTimeSec)
{
m_TimeSec += deltaTimeSec;
if (Math.Abs(m_DesiredLocalTimeOffset - m_CurrentLocalTimeOffset) > HardResetThresholdSec || Math.Abs(m_DesiredServerTimeOffset - m_CurrentServerTimeOffset) > HardResetThresholdSec)
{
m_TimeSec += m_DesiredServerTimeOffset;
m_DesiredLocalTimeOffset -= m_DesiredServerTimeOffset;
m_CurrentLocalTimeOffset = m_DesiredLocalTimeOffset;
m_DesiredServerTimeOffset = 0;
m_CurrentServerTimeOffset = 0;
return true;
}
m_CurrentLocalTimeOffset += deltaTimeSec * (m_DesiredLocalTimeOffset > m_CurrentLocalTimeOffset ? AdjustmentRatio : -AdjustmentRatio);
m_CurrentServerTimeOffset += deltaTimeSec * (m_DesiredServerTimeOffset > m_CurrentServerTimeOffset ? AdjustmentRatio : -AdjustmentRatio);
return false;
}
/// <summary>
/// Resets the time system to a time based on the given network parameters.
/// </summary>
/// <param name="serverTimeSec">The most recent server time value received in seconds.</param>
/// <param name="rttSec">The current RTT in seconds. Can be an averaged or a raw value.</param>
public void Reset(double serverTimeSec, double rttSec)
{
Sync(serverTimeSec, rttSec);
Advance(0);
}
/// <summary>
/// Synchronizes the time system with up-to-date network statistics but does not change any time values or advance the time.
/// </summary>
/// <param name="serverTimeSec">The most recent server time value received in seconds.</param>
/// <param name="rttSec">The current RTT in seconds. Can be an averaged or a raw value.</param>
public void Sync(double serverTimeSec, double rttSec)
{
LastSyncedRttSec = rttSec;
LastSyncedServerTimeSec = serverTimeSec;
var timeDif = serverTimeSec - m_TimeSec;
m_DesiredServerTimeOffset = timeDif - ServerBufferSec;
m_DesiredLocalTimeOffset = timeDif + rttSec + LocalBufferSec;
}
}
}