Skip to content

Commit c032b55

Browse files
fix: networkvariablebase not being reinitialized if networkobject persists between sessions (#3217)
* fix Back-port of #3181 final fix. * test The test to validate this PR. * update Only log something if we have something to log. * test remove verbose mode. * update adding changelog entry
1 parent 0d8d95a commit c032b55

File tree

6 files changed

+518
-20
lines changed

6 files changed

+518
-20
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1212

1313
### Fixed
1414

15+
- Fixed issue where `NetworkVariableBase` derived classes were not being re-initialized if the associated `NetworkObject` instance was not destroyed and re-spawned. (#3217)
1516
- 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)
1617
- Fixed issue where `NetworkRigidBody2D` was still using the deprecated `isKinematic` property in Unity versions 2022.3 and newer. (#3199)
1718
- Fixed issue where an exception was thrown when calling `NetworkManager.Shutdown` after calling `UnityTransport.Shutdown`. (#3118)

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/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)

0 commit comments

Comments
 (0)