Skip to content

Commit b9fe0d2

Browse files
authored
feat: Added support for client anticipation in NetworkVariables and NetworkTransform and support for throttling functionality in NetworkVariables (#2820)
1 parent 1fa39e1 commit b9fe0d2

35 files changed

+2584
-25
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ Additional documentation and release notes are available at [Multiplayer Documen
99
## [Unreleased]
1010

1111
### Added
12+
-Added AnticipatedNetworkVariable<T>, which adds support for client anticipation of NetworkVariable values, allowing for more responsive gameplay (#2820)
13+
- Added AnticipatedNetworkTransform, which adds support for client anticipation of NetworkTransforms (#2820)
14+
- 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) (#2820)
15+
- 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. (#2820)
16+
- Added virtual method NetworkVariableBase.OnInitialize() which can be used by NetworkVariable subclasses to add initialization code (#2820)
17+
- Added virtual method NetworkVariableBase.Update(), which is called once per frame to support behaviors such as interpolation between an anticipated value and an authoritative one. (#2820)
18+
- Added NetworkTime.TickWithPartial, which represents the current tick as a double that includes the fractional/partial tick value. (#2820)
19+
- 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. (#2820)
1220

1321
### Fixed
1422

com.unity.netcode.gameobjects/Components/AnticipatedNetworkTransform.cs

Lines changed: 500 additions & 0 deletions
Large diffs are not rendered by default.

com.unity.netcode.gameobjects/Components/AnticipatedNetworkTransform.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

com.unity.netcode.gameobjects/Components/NetworkTransform.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,10 +2047,15 @@ private bool ApplyTransformToNetworkStateWithInfo(ref NetworkTransformState netw
20472047
return isDirty;
20482048
}
20492049

2050+
protected virtual void OnTransformUpdated()
2051+
{
2052+
2053+
}
2054+
20502055
/// <summary>
20512056
/// Applies the authoritative state to the transform
20522057
/// </summary>
2053-
private void ApplyAuthoritativeState()
2058+
protected internal void ApplyAuthoritativeState()
20542059
{
20552060
var networkState = m_LocalAuthoritativeNetworkState;
20562061
// The m_CurrentPosition, m_CurrentRotation, and m_CurrentScale values are continually updated
@@ -2221,6 +2226,7 @@ private void ApplyAuthoritativeState()
22212226
}
22222227
transform.localScale = m_CurrentScale;
22232228
}
2229+
OnTransformUpdated();
22242230
}
22252231

22262232
/// <summary>
@@ -2418,6 +2424,7 @@ private void ApplyTeleportingState(NetworkTransformState newState)
24182424
{
24192425
AddLogEntry(ref newState, NetworkObject.OwnerClientId);
24202426
}
2427+
OnTransformUpdated();
24212428
}
24222429

24232430
/// <summary>
@@ -2586,6 +2593,11 @@ protected virtual void OnNetworkTransformStateUpdated(ref NetworkTransformState
25862593

25872594
}
25882595

2596+
protected virtual void OnBeforeUpdateTransformState()
2597+
{
2598+
2599+
}
2600+
25892601
private NetworkTransformState m_OldState = new NetworkTransformState();
25902602

25912603
/// <summary>
@@ -2609,6 +2621,8 @@ private void OnNetworkStateChanged(NetworkTransformState oldState, NetworkTransf
26092621
// Get the time when this new state was sent
26102622
newState.SentTime = new NetworkTime(m_CachedNetworkManager.NetworkConfig.TickRate, newState.NetworkTick).Time;
26112623

2624+
OnBeforeUpdateTransformState();
2625+
26122626
// Apply the new state
26132627
ApplyUpdatedState(newState);
26142628

@@ -3315,7 +3329,7 @@ private static void RegisterForTickUpdate(NetworkTransform networkTransform)
33153329

33163330
/// <summary>
33173331
/// If a NetworkTransformTickRegistration exists for the NetworkManager instance, then this will
3318-
/// remove the NetworkTransform instance from the single tick update entry point.
3332+
/// remove the NetworkTransform instance from the single tick update entry point.
33193333
/// </summary>
33203334
/// <param name="networkTransform"></param>
33213335
private static void DeregisterForTickUpdate(NetworkTransform networkTransform)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Unity.Netcode.Components;
2+
using UnityEditor;
3+
4+
namespace Unity.Netcode.Editor
5+
{
6+
/// <summary>
7+
/// The <see cref="CustomEditor"/> for <see cref="AnticipatedNetworkTransform"/>
8+
/// </summary>
9+
[CustomEditor(typeof(AnticipatedNetworkTransform), true)]
10+
public class AnticipatedNetworkTransformEditor : NetworkTransformEditor
11+
{
12+
public override bool HideInterpolateValue => true;
13+
}
14+
}

com.unity.netcode.gameobjects/Editor/AnticipatedNetworkTransformEditor.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
408408
}
409409
else
410410
{
411+
m_Diagnostics.AddError($"{type}: Managed type in NetworkVariable must implement IEquatable<{type}>");
411412
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef);
412413
}
413414

com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public class NetworkTransformEditor : UnityEditor.Editor
3737
private static GUIContent s_RotationLabel = EditorGUIUtility.TrTextContent("Rotation");
3838
private static GUIContent s_ScaleLabel = EditorGUIUtility.TrTextContent("Scale");
3939

40+
public virtual bool HideInterpolateValue => false;
41+
4042
/// <inheritdoc/>
4143
public void OnEnable()
4244
{
@@ -137,7 +139,11 @@ public override void OnInspectorGUI()
137139
EditorGUILayout.Space();
138140
EditorGUILayout.LabelField("Configurations", EditorStyles.boldLabel);
139141
EditorGUILayout.PropertyField(m_InLocalSpaceProperty);
140-
EditorGUILayout.PropertyField(m_InterpolateProperty);
142+
if (!HideInterpolateValue)
143+
{
144+
EditorGUILayout.PropertyField(m_InterpolateProperty);
145+
}
146+
141147
EditorGUILayout.PropertyField(m_SlerpPosition);
142148
EditorGUILayout.PropertyField(m_UseQuaternionSynchronization);
143149
if (m_UseQuaternionSynchronization.boolValue)

com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,15 @@ internal void DisconnectEventHandler(ulong transportClientId)
510510
// as the client ID is no longer valid.
511511
NetworkManager.Shutdown(true);
512512
}
513+
514+
if (NetworkManager.IsServer)
515+
{
516+
MessageManager.ClientDisconnected(clientId);
517+
}
518+
else
519+
{
520+
MessageManager.ClientDisconnected(NetworkManager.ServerClientId);
521+
}
513522
#if DEVELOPMENT_BUILD || UNITY_EDITOR
514523
s_TransportDisconnect.End();
515524
#endif

com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -815,19 +815,35 @@ internal void PostNetworkVariableWrite(bool forced = false)
815815
// during OnNetworkSpawn has been sent and needs to be cleared
816816
for (int i = 0; i < NetworkVariableFields.Count; i++)
817817
{
818-
NetworkVariableFields[i].ResetDirty();
818+
var networkVariable = NetworkVariableFields[i];
819+
if (networkVariable.IsDirty())
820+
{
821+
if (networkVariable.CanSend())
822+
{
823+
networkVariable.UpdateLastSentTime();
824+
networkVariable.ResetDirty();
825+
networkVariable.SetDirty(false);
826+
}
827+
}
819828
}
820829
}
821830
else
822831
{
823832
// mark any variables we wrote as no longer dirty
824833
for (int i = 0; i < NetworkVariableIndexesToReset.Count; i++)
825834
{
826-
NetworkVariableFields[NetworkVariableIndexesToReset[i]].ResetDirty();
835+
var networkVariable = NetworkVariableFields[NetworkVariableIndexesToReset[i]];
836+
if (networkVariable.IsDirty())
837+
{
838+
if (networkVariable.CanSend())
839+
{
840+
networkVariable.UpdateLastSentTime();
841+
networkVariable.ResetDirty();
842+
networkVariable.SetDirty(false);
843+
}
844+
}
827845
}
828846
}
829-
830-
MarkVariablesDirty(false);
831847
}
832848

833849
internal void PreVariableUpdate()
@@ -836,7 +852,6 @@ internal void PreVariableUpdate()
836852
{
837853
InitializeVariables();
838854
}
839-
840855
PreNetworkVariableWrite();
841856
}
842857

@@ -863,7 +878,10 @@ private void NetworkVariableUpdate(ulong targetClientId, int behaviourIndex)
863878
var networkVariable = NetworkVariableFields[k];
864879
if (networkVariable.IsDirty() && networkVariable.CanClientRead(targetClientId))
865880
{
866-
shouldSend = true;
881+
if (networkVariable.CanSend())
882+
{
883+
shouldSend = true;
884+
}
867885
break;
868886
}
869887
}
@@ -904,9 +922,16 @@ private bool CouldHaveDirtyNetworkVariables()
904922
// TODO: There should be a better way by reading one dirty variable vs. 'n'
905923
for (int i = 0; i < NetworkVariableFields.Count; i++)
906924
{
907-
if (NetworkVariableFields[i].IsDirty())
925+
var networkVariable = NetworkVariableFields[i];
926+
if (networkVariable.IsDirty())
908927
{
909-
return true;
928+
if (networkVariable.CanSend())
929+
{
930+
return true;
931+
}
932+
// If it's dirty but can't be sent yet, we have to keep monitoring it until one of the
933+
// conditions blocking its send changes.
934+
NetworkManager.BehaviourUpdater.AddForUpdate(NetworkObject);
910935
}
911936
}
912937

@@ -1063,6 +1088,11 @@ protected virtual void OnSynchronize<T>(ref BufferSerializer<T> serializer) wher
10631088

10641089
}
10651090

1091+
public virtual void OnReanticipate(double lastRoundTripTime)
1092+
{
1093+
1094+
}
1095+
10661096
/// <summary>
10671097
/// The relative client identifier targeted for the serialization of this <see cref="NetworkBehaviour"/> instance.
10681098
/// </summary>

0 commit comments

Comments
 (0)