Skip to content

Commit 53ec291

Browse files
Merge branch 'develop' into chore/add_auto_labeler
2 parents 1d2828b + d9a09a3 commit 53ec291

File tree

11 files changed

+577
-50
lines changed

11 files changed

+577
-50
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,20 @@ Additional documentation and release notes are available at [Multiplayer Documen
1212

1313
### Fixed
1414

15+
### Changed
16+
17+
18+
## [1.12.2] - 2025-01-17
19+
20+
### Fixed
21+
22+
- Fixed issue where `NetworkVariableBase` derived classes were not being re-initialized if the associated `NetworkObject` instance was not destroyed and re-spawned. (#3217)
1523
- Fixed issue where a spawned `NetworkObject` that was registered with a prefab handler and owned by a client would invoke destroy more than once on the host-server side if the client disconnected while the `NetworkObject` was still spawned. (#3202)
1624
- Fixed issue where `NetworkRigidBody2D` was still using the deprecated `isKinematic` property in Unity versions 2022.3 and newer. (#3199)
1725
- Fixed issue where an exception was thrown when calling `NetworkManager.Shutdown` after calling `UnityTransport.Shutdown`. (#3118)
1826

19-
### Changed
27+
28+
- Changed the `NetworkTimeSystem.Sync` method to use half RTT to calculate the desired local time offset as opposed to the full RTT. (#3206)
2029

2130
## [1.12.0] - 2024-11-19
2231

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,13 @@ internal void InternalOnNetworkDespawn()
755755
{
756756
Debug.LogException(e);
757757
}
758+
759+
// Deinitialize all NetworkVariables in the event the associated
760+
// NetworkObject is recylced (in-scene placed or pooled).
761+
for (int i = 0; i < NetworkVariableFields.Count; i++)
762+
{
763+
NetworkVariableFields[i].Deinitialize();
764+
}
758765
}
759766

760767
/// <summary>
@@ -858,6 +865,22 @@ internal void InitializeVariables()
858865
{
859866
if (m_VarInit)
860867
{
868+
// If the primary initialization has already been done, then go ahead
869+
// and re-initialize each NetworkVariable in the event it is an in-scene
870+
// placed NetworkObject in an already loaded scene that has already been
871+
// used within a network session =or= if this is a pooled NetworkObject
872+
// that is being repurposed.
873+
for (int i = 0; i < NetworkVariableFields.Count; i++)
874+
{
875+
// If already initialized, then skip
876+
if (NetworkVariableFields[i].HasBeenInitialized)
877+
{
878+
continue;
879+
}
880+
NetworkVariableFields[i].Initialize(this);
881+
}
882+
// Exit early as we don't need to run through the rest of this initialization
883+
// process
861884
return;
862885
}
863886

com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public abstract class NetworkVariableBase : IDisposable
3434
private protected NetworkBehaviour m_NetworkBehaviour;
3535
private NetworkManager m_InternalNetworkManager;
3636

37+
// Determines if this NetworkVariable has been "initialized" to prevent initializing more than once which can happen when first
38+
// instantiated and spawned. If this NetworkVariable instance is on an in-scene placed NetworkObject =or= a pooled NetworkObject
39+
// that can persist between sessions and/or be recycled we need to reset the LastUpdateSent value prior to spawning otherwise
40+
// this NetworkVariableBase property instance will not update until the last session time used.
41+
internal bool HasBeenInitialized { get; private set; }
42+
3743
public NetworkBehaviour GetBehaviour()
3844
{
3945
return m_NetworkBehaviour;
@@ -49,37 +55,82 @@ internal void LogWritePermissionError()
4955
Debug.LogError(GetWritePermissionError());
5056
}
5157

52-
private protected NetworkManager m_NetworkManager
53-
{
54-
get
55-
{
56-
if (m_InternalNetworkManager == null && m_NetworkBehaviour && m_NetworkBehaviour.NetworkObject?.NetworkManager)
57-
{
58-
m_InternalNetworkManager = m_NetworkBehaviour.NetworkObject?.NetworkManager;
59-
}
60-
return m_InternalNetworkManager;
61-
}
62-
}
58+
private protected NetworkManager m_NetworkManager => m_InternalNetworkManager;
6359

6460
/// <summary>
6561
/// Initializes the NetworkVariable
6662
/// </summary>
6763
/// <param name="networkBehaviour">The NetworkBehaviour the NetworkVariable belongs to</param>
6864
public void Initialize(NetworkBehaviour networkBehaviour)
6965
{
70-
m_InternalNetworkManager = null;
66+
// If we have already been initialized, then exit early.
67+
// This can happen on the very first instantiation and spawning of the associated NetworkObject
68+
if (HasBeenInitialized)
69+
{
70+
return;
71+
}
72+
73+
// Throw an exception if there is an invalid NetworkBehaviour parameter
74+
if (!networkBehaviour)
75+
{
76+
throw new Exception($"[{GetType().Name}][Initialize] {nameof(NetworkBehaviour)} parameter passed in is null!");
77+
}
7178
m_NetworkBehaviour = networkBehaviour;
72-
if (m_NetworkBehaviour && m_NetworkBehaviour.NetworkObject?.NetworkManager)
79+
80+
// Throw an exception if there is no NetworkManager available
81+
if (!m_NetworkBehaviour.NetworkManager)
7382
{
74-
m_InternalNetworkManager = m_NetworkBehaviour.NetworkObject?.NetworkManager;
83+
// Exit early if there has yet to be a NetworkManager assigned.
84+
// This is ok because Initialize is invoked multiple times until
85+
// it is considered "initialized".
86+
return;
87+
}
7588

76-
if (m_NetworkBehaviour.NetworkManager.NetworkTimeSystem != null)
77-
{
78-
UpdateLastSentTime();
79-
}
89+
if (!m_NetworkBehaviour.NetworkObject)
90+
{
91+
// Exit early if there has yet to be a NetworkObject assigned.
92+
// This is ok because Initialize is invoked multiple times until
93+
// it is considered "initialized".
94+
return;
8095
}
8196

97+
if (!m_NetworkBehaviour.NetworkObject.NetworkManagerOwner)
98+
{
99+
// Exit early if there has yet to be a NetworkManagerOwner assigned
100+
// to the NetworkObject. This is ok because Initialize is invoked
101+
// multiple times until it is considered "initialized".
102+
return;
103+
}
104+
m_InternalNetworkManager = m_NetworkBehaviour.NetworkObject.NetworkManagerOwner;
105+
82106
OnInitialize();
107+
108+
// Some unit tests don't operate with a running NetworkManager.
109+
// Only update the last time if there is a NetworkTimeSystem.
110+
if (m_InternalNetworkManager.NetworkTimeSystem != null)
111+
{
112+
// Update our last sent time relative to when this was initialized
113+
UpdateLastSentTime();
114+
115+
// At this point, this instance is considered initialized
116+
HasBeenInitialized = true;
117+
}
118+
else if (m_InternalNetworkManager.LogLevel == LogLevel.Developer)
119+
{
120+
Debug.LogWarning($"[{m_NetworkBehaviour.name}][{m_NetworkBehaviour.GetType().Name}][{GetType().Name}][Initialize] {nameof(NetworkManager)} has no {nameof(NetworkTimeSystem)} assigned!");
121+
}
122+
}
123+
124+
/// <summary>
125+
/// Deinitialize is invoked when a NetworkObject is despawned.
126+
/// This allows for a recyled NetworkObject (in-scene or pooled)
127+
/// to be properly initialized upon the next use/spawn.
128+
/// </summary>
129+
internal void Deinitialize()
130+
{
131+
// When despawned, reset the HasBeenInitialized so if the associated NetworkObject instance
132+
// is recylced (i.e. in-scene placed or pooled) it will re-initialize the LastUpdateSent time.
133+
HasBeenInitialized = false;
83134
}
84135

85136
/// <summary>

com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,9 @@ public void Sync(double serverTimeSec, double rttSec)
248248
var timeDif = serverTimeSec - m_TimeSec;
249249

250250
m_DesiredServerTimeOffset = timeDif - ServerBufferSec;
251-
m_DesiredLocalTimeOffset = timeDif + rttSec + LocalBufferSec;
251+
// We adjust our desired local time offset to be half RTT since the delivery of
252+
// the TimeSyncMessage should only take half of the RTT time (legacy was using 1 full RTT)
253+
m_DesiredLocalTimeOffset = timeDif + (rttSec * 0.5d) + LocalBufferSec;
252254
}
253255
}
254256
}

com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,13 @@ public override void DisconnectLocalClient()
11671167
/// <param name="clientId">The client to disconnect</param>
11681168
public override void DisconnectRemoteClient(ulong clientId)
11691169
{
1170-
Debug.Assert(m_State == State.Listening, "DisconnectRemoteClient should be called on a listening server");
1170+
#if DEBUG
1171+
if (m_State != State.Listening)
1172+
{
1173+
Debug.LogWarning($"{nameof(DisconnectRemoteClient)} should only be called on a listening server!");
1174+
return;
1175+
}
1176+
#endif
11711177

11721178
if (m_State == State.Listening)
11731179
{
@@ -1239,7 +1245,13 @@ public NetworkEndpoint GetEndpoint(ulong clientId)
12391245
/// <param name="networkManager">The NetworkManager that initialized and owns the transport</param>
12401246
public override void Initialize(NetworkManager networkManager = null)
12411247
{
1242-
Debug.Assert(sizeof(ulong) == UnsafeUtility.SizeOf<NetworkConnection>(), "Netcode connection id size does not match UTP connection id size");
1248+
#if DEBUG
1249+
if (sizeof(ulong) != UnsafeUtility.SizeOf<NetworkConnection>())
1250+
{
1251+
Debug.LogWarning($"Netcode connection id size {sizeof(ulong)} does not match UTP connection id size {UnsafeUtility.SizeOf<NetworkConnection>()}!");
1252+
return;
1253+
}
1254+
#endif
12431255

12441256
NetworkManager = networkManager;
12451257

com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,8 +1550,12 @@ private void UnloadRemainingScenes()
15501550

15511551
private void LogWaitForMessages()
15521552
{
1553-
VerboseDebug(m_WaitForLog.ToString());
1554-
m_WaitForLog.Clear();
1553+
// If there is nothing to log, then don't log anything
1554+
if (m_WaitForLog.Length > 0)
1555+
{
1556+
VerboseDebug(m_WaitForLog.ToString());
1557+
m_WaitForLog.Clear();
1558+
}
15551559
}
15561560

15571561
private IEnumerator WaitForTickAndFrames(NetworkManager networkManager, int tickCount, float targetFrames)

com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,16 @@ public class ClientNetworkTimeSystemTests
1818
public void StableRttTest()
1919
{
2020
double receivedServerTime = 2;
21-
22-
var timeSystem = new NetworkTimeSystem(0.05d, 0.05d, 0.1d);
21+
var baseRtt = 0.1f;
22+
var halfRtt = 0.05f;
23+
var timeSystem = new NetworkTimeSystem(0.05d, 0.05d, baseRtt);
2324
timeSystem.Reset(receivedServerTime, 0.15);
2425
var tickSystem = new NetworkTickSystem(60, timeSystem.LocalTime, timeSystem.ServerTime);
2526

2627
Assert.True(timeSystem.LocalTime > 2);
2728

28-
var steps = TimingTestHelper.GetRandomTimeSteps(100f, 0.01f, 0.1f, 42);
29-
var rttSteps = TimingTestHelper.GetRandomTimeSteps(1000f, 0.095f, 0.105f, 42); // 10ms jitter
29+
var steps = TimingTestHelper.GetRandomTimeSteps(100f, 0.01f, baseRtt, 42);
30+
var rttSteps = TimingTestHelper.GetRandomTimeSteps(1000f, baseRtt - 0.05f, baseRtt + 0.05f, 42); // 10ms jitter
3031

3132
// run for a while so that we reach regular RTT offset
3233
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
@@ -37,10 +38,11 @@ public void StableRttTest()
3738
});
3839

3940
// check how we close we are to target time.
40-
var expectedRtt = 0.1d;
41-
var offsetToTarget = (timeSystem.LocalTime - timeSystem.ServerTime) - expectedRtt - timeSystem.ServerBufferSec - timeSystem.LocalBufferSec;
41+
var offsetToTarget = (timeSystem.LocalTime - timeSystem.ServerTime) - halfRtt - timeSystem.ServerBufferSec - timeSystem.LocalBufferSec;
4242
Debug.Log($"offset to target time after running for a while: {offsetToTarget}");
43-
Assert.IsTrue(Math.Abs(offsetToTarget) < k_AcceptableRttOffset);
43+
44+
// server speedup/slowdowns should not be affected by RTT
45+
Assert.True(Math.Abs(offsetToTarget) < k_AcceptableRttOffset, $"Expected offset time to be less than {k_AcceptableRttOffset}ms but it was {offsetToTarget}!");
4446

4547
// run again, test that we never need to speed up or slow down under stable RTT
4648
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
@@ -51,9 +53,10 @@ public void StableRttTest()
5153
});
5254

5355
// check again to ensure we are still close to the target
54-
var newOffsetToTarget = (timeSystem.LocalTime - timeSystem.ServerTime) - expectedRtt - timeSystem.ServerBufferSec - timeSystem.LocalBufferSec;
56+
var newOffsetToTarget = (timeSystem.LocalTime - timeSystem.ServerTime) - halfRtt - timeSystem.ServerBufferSec - timeSystem.LocalBufferSec;
5557
Debug.Log($"offset to target time after running longer: {newOffsetToTarget}");
56-
Assert.IsTrue(Math.Abs(newOffsetToTarget) < k_AcceptableRttOffset);
58+
// server speedup/slowdowns should not be affected by RTT
59+
Assert.True(Math.Abs(offsetToTarget) < k_AcceptableRttOffset, $"Expected offset time to be less than {k_AcceptableRttOffset}ms but it was {offsetToTarget}!");
5760

5861
// difference between first and second offset should be minimal
5962
var dif = offsetToTarget - newOffsetToTarget;
@@ -67,13 +70,14 @@ public void StableRttTest()
6770
public void RttCatchupSlowdownTest()
6871
{
6972
double receivedServerTime = 2;
70-
71-
var timeSystem = new NetworkTimeSystem(0.05d, 0.05d, 0.1d);
73+
var baseRtt = 0.1f;
74+
var halfRtt = 0.05f;
75+
var timeSystem = new NetworkTimeSystem(0.05d, 0.05d, baseRtt);
7276
timeSystem.Reset(receivedServerTime, 0.15);
7377
var tickSystem = new NetworkTickSystem(60, timeSystem.LocalTime, timeSystem.ServerTime);
7478

75-
var steps = TimingTestHelper.GetRandomTimeSteps(100f, 0.01f, 0.1f, 42);
76-
var rttSteps = TimingTestHelper.GetRandomTimeSteps(1000f, 0.095f, 0.105f, 42); // 10ms jitter
79+
var steps = TimingTestHelper.GetRandomTimeSteps(100f, 0.01f, baseRtt, 42);
80+
var rttSteps = TimingTestHelper.GetRandomTimeSteps(1000f, baseRtt - 0.05f, baseRtt + 0.05f, 42); // 10ms jitter
7781

7882
// run for a while so that we reach regular RTT offset
7983
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
@@ -102,11 +106,14 @@ public void RttCatchupSlowdownTest()
102106

103107
// speed up of 0.1f expected
104108
Debug.Log($"Total local speed up time catch up: {totalLocalSpeedUpTime}");
105-
Assert.True(Math.Abs(totalLocalSpeedUpTime - 0.1) < k_AcceptableRttOffset);
106-
Assert.True(Math.Abs(totalServerSpeedUpTime) < k_AcceptableRttOffset); // server speedup/slowdowns should not be affected by RTT
109+
var expectedSpeedUpTime = Math.Abs(totalLocalSpeedUpTime - halfRtt);
110+
var expectedServerSpeedUpTime = Math.Abs(totalServerSpeedUpTime);
111+
Assert.True(expectedSpeedUpTime < k_AcceptableRttOffset, $"Expected local speed up time to be less than {k_AcceptableRttOffset}ms but it was {expectedSpeedUpTime}!");
112+
// server speedup/slowdowns should not be affected by RTT
113+
Assert.True(Math.Abs(totalServerSpeedUpTime) < k_AcceptableRttOffset, $"Expected server speed up time to be less than {k_AcceptableRttOffset}ms but it was {expectedServerSpeedUpTime}!");
107114

108115

109-
// run again with RTT ~100ms and see whether we slow down by -0.1f
116+
// run again with RTT ~100ms and see whether we slow down by -halfRtt
110117
unscaledLocalTime = timeSystem.LocalTime;
111118
unscaledServerTime = timeSystem.ServerTime;
112119

@@ -121,13 +128,13 @@ public void RttCatchupSlowdownTest()
121128

122129
totalLocalSpeedUpTime = timeSystem.LocalTime - unscaledLocalTime;
123130
totalServerSpeedUpTime = timeSystem.ServerTime - unscaledServerTime;
124-
125-
// slow down of 0.1f expected
131+
// slow down of half halfRtt expected
126132
Debug.Log($"Total local speed up time slow down: {totalLocalSpeedUpTime}");
127-
Assert.True(Math.Abs(totalLocalSpeedUpTime + 0.1) < k_AcceptableRttOffset);
128-
Assert.True(Math.Abs(totalServerSpeedUpTime) < k_AcceptableRttOffset); // server speedup/slowdowns should not be affected by RTT
129-
130-
133+
expectedSpeedUpTime = Math.Abs(totalLocalSpeedUpTime + halfRtt);
134+
expectedServerSpeedUpTime = Math.Abs(totalServerSpeedUpTime);
135+
Assert.True(expectedSpeedUpTime < k_AcceptableRttOffset, $"Expected local speed up time to be less than {k_AcceptableRttOffset}ms but it was {expectedSpeedUpTime}!");
136+
// server speedup/slowdowns should not be affected by RTT
137+
Assert.True(Math.Abs(totalServerSpeedUpTime) < k_AcceptableRttOffset, $"Expected server speed up time to be less than {k_AcceptableRttOffset}ms but it was {expectedServerSpeedUpTime}!");
131138
}
132139

133140
/// <summary>
@@ -172,8 +179,8 @@ public void ResetTest()
172179
receivedServerTime += steps[step];
173180
timeSystem.Sync(receivedServerTime, rttSteps2[step]);
174181

175-
// after hard reset time should stay close to rtt
176-
var expectedRtt = 0.5d;
182+
// after hard reset time should stay close to half rtt
183+
var expectedRtt = 0.25d;
177184
Assert.IsTrue(Math.Abs((timeSystem.LocalTime - timeSystem.ServerTime) - expectedRtt - timeSystem.ServerBufferSec - timeSystem.LocalBufferSec) < k_AcceptableRttOffset);
178185

179186
});

0 commit comments

Comments
 (0)