Skip to content

Commit 023efd7

Browse files
fix: [Up-Port] handle NetworkManager Awake being overridden (#4244)
* fix: handle NetworkManager Awake being overridden (cherry picked from commit e6693e9) * Fix the XML doc and the double destroy (cherry picked from commit 6f907ed) * Fix malformed XML doc and simplify logger API (cherry picked from commit ddc074e) * dotnet-fix (cherry picked from commit 5fe1822) * Bump to minor version due to new API * Update CHANGELOG * Fix compilation error * Update package version in ValidationExceptions.json Updating validation exceptions NGO version number to match that of the package. --------- Co-authored-by: Noel Stephens <noel.stephens@unity3d.com>
1 parent d7534e5 commit 023efd7

5 files changed

Lines changed: 102 additions & 12 deletions

File tree

‎com.unity.netcode.gameobjects/CHANGELOG.md‎

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

1919
### Fixed
2020

21+
- Fixed a NullReferenceException that would be thrown if a MonoBehaviour derived from NetworkManager defines it's own Awake or OnEnable. (#4244)
2122
- Issue where the unified bootstrap created a second world that ran a duplicate of every default system next to the default world. (#4243)
2223
- Ensured a useful and human readable error is logged when a NetworkVariable is trying to serialize a private or protected type. (#4239)
2324
- Issue where `NetworkAnimator` logged an error for a trigger-conditioned transition into an Exit node, and a client that joined during that transition, or during one into a sub-state machine, did not synchronize with it. Transitions that pass through Exit, Entry or sub-state machine nodes now resolve to the state they reach, and a late-joining client cross-fades from the matching originating state when several states transition into the same destination. (#4238)

‎com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs‎

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,11 +1100,18 @@ public void SetSingleton()
11001100
OnSingletonReady?.Invoke();
11011101
}
11021102

1103-
private void Awake()
1103+
/// <summary>
1104+
/// Unity calls Awake when loading an instance of a script component.
1105+
/// </summary>
1106+
/// <remarks>
1107+
/// Override this method to implement custom initialization logic for a derived <see cref="NetworkManager"/>.
1108+
/// Ensure you call <c>base.Awake()</c> in your override to allow the base initialization to happen.
1109+
/// </remarks>
1110+
protected virtual void Awake()
11041111
{
11051112
if (Log == null)
11061113
{
1107-
Log = new ContextualLogger(this, false);
1114+
Log = new ContextualLogger(this);
11081115
}
11091116

11101117
NetworkConfig?.InitializePrefabs();
@@ -1124,7 +1131,14 @@ private void Awake()
11241131
}
11251132
}
11261133

1127-
private void OnEnable()
1134+
/// <summary>
1135+
/// Called when a component of an active GameObject is first enabled.
1136+
/// </summary>
1137+
/// <remarks>
1138+
/// Override this method to implement custom initialization logic for a derived <see cref="NetworkManager"/>.
1139+
/// Ensure you call <c>base.OnEnable()</c> in your override to allow the base initialization to happen.
1140+
/// </remarks>
1141+
protected virtual void OnEnable()
11281142
{
11291143
if (RunInBackground)
11301144
{
@@ -1485,6 +1499,11 @@ private void UnifiedApplyNetcodeConfig()
14851499
/// <returns>returns true if <see cref="NetworkManager"/> started in server mode successfully; otherwise false</returns>
14861500
public bool StartServer()
14871501
{
1502+
if (Log == null)
1503+
{
1504+
Log = new ContextualLogger(this);
1505+
Log.Warning(new Context(LogLevel.Normal, "No logger defined. Ensure base.Awake() and base.OnEnable is called in any derived NetworkManager classes."));
1506+
}
14881507
Log.CaptureFunctionCall();
14891508

14901509
if (!CanStart(StartType.Server))
@@ -1578,6 +1597,11 @@ internal bool InternalStartServer()
15781597
/// <returns>(<see cref="true"/>/<see cref="false"/>) returns true if <see cref="NetworkManager"/> started in client mode successfully.</returns>
15791598
public bool StartClient()
15801599
{
1600+
if (Log == null)
1601+
{
1602+
Log = new ContextualLogger(this);
1603+
Log.Warning(new Context(LogLevel.Normal, "No logger defined. Ensure base.Awake() and base.OnEnable is called in any derived NetworkManager classes."));
1604+
}
15811605
Log.CaptureFunctionCall();
15821606

15831607
if (!CanStart(StartType.Client))
@@ -1663,6 +1687,11 @@ internal bool InternalStartClient()
16631687
/// <returns>(<see cref="true"/>/<see cref="false"/>) returns true if <see cref="NetworkManager"/> started in host mode successfully.</returns>
16641688
public bool StartHost()
16651689
{
1690+
if (Log == null)
1691+
{
1692+
Log = new ContextualLogger(this);
1693+
Log.Warning(new Context(LogLevel.Normal, "No logger defined. Ensure base.Awake() and base.OnEnable is called in any derived NetworkManager classes."));
1694+
}
16661695
Log.CaptureFunctionCall();
16671696

16681697
if (!CanStart(StartType.Host))
@@ -1848,7 +1877,7 @@ private void OnSceneUnloaded(Scene scene)
18481877
{
18491878
if (gameObject != null && scene == gameObject.scene)
18501879
{
1851-
OnDestroy();
1880+
DestroyInternal();
18521881
}
18531882
}
18541883

@@ -1991,7 +2020,7 @@ internal void ShutdownInternal()
19912020
}
19922021
}
19932022

1994-
// Ensures that the NetworkManager is cleaned up before OnDestroy is run on NetworkObjects and NetworkBehaviours when quitting the application.
2023+
// Ensures that the NetworkManager is cleaned up before DestroyInternal is run on NetworkObjects and NetworkBehaviours when quitting the application.
19952024
private void OnApplicationQuit()
19962025
{
19972026
// Abrupt shutdown (or immediate exit of play mode).
@@ -2005,7 +2034,7 @@ private void OnApplicationQuit()
20052034
{
20062035
return;
20072036
}
2008-
OnDestroy();
2037+
DestroyInternal();
20092038
#if UNITY_EDITOR
20102039
if (Singleton != null)
20112040
{
@@ -2016,15 +2045,34 @@ private void OnApplicationQuit()
20162045

20172046
private bool m_IsDestroyed = false;
20182047

2048+
/// <summary>
2049+
/// Called when a GameObject or component is about to be destroyed.
2050+
/// </summary>
2051+
/// <remarks>
2052+
/// Override this method to implement custom teardown logic for a derived <see cref="NetworkManager"/>.
2053+
/// Ensure you call <c>base.OnDestroy()</c> in your override to allow the base teardown to happen.
2054+
/// </remarks>
2055+
protected virtual void OnDestroy()
2056+
{
2057+
DestroyInternal();
2058+
}
2059+
2060+
// Internal destroy method that can't be overridden by derived classes.
20192061
// Note that this gets also called manually by OnSceneUnloaded and OnApplicationQuit
2020-
private void OnDestroy()
2062+
private void DestroyInternal()
20212063
{
20222064
if (m_IsDestroyed)
20232065
{
20242066
return;
20252067
}
20262068
m_IsDestroyed = true;
20272069

2070+
if (Log == null)
2071+
{
2072+
Log = new ContextualLogger(this);
2073+
Log.Warning(new Context(LogLevel.Normal, "No logger defined. Ensure base.Awake() and base.OnEnable is called in any derived NetworkManager classes."));
2074+
}
2075+
20282076
try
20292077
{
20302078
ShutdownInternal();
@@ -2120,7 +2168,7 @@ internal void OnValidate()
21202168

21212169
if (Log == null)
21222170
{
2123-
Log = new ContextualLogger(this, false);
2171+
Log = new ContextualLogger(this);
21242172
}
21252173

21262174
// Do a validation pass on NetworkConfig properties

‎com.unity.netcode.gameobjects/ValidationExceptions.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
{
44
"ValidationTest": "NDA Keyword Validation",
55
"ExceptionMessage": "#if UNITY_7000_0_OR_NEWER",
6-
"PackageVersion": "3.2.2"
6+
"PackageVersion": "3.3.0"
77
},
88
{
99
"ValidationTest": "NDA Keyword Validation",
1010
"ExceptionMessage": "File 'Editor/InScenePlacedProcessor.cs' contains deny-listed keyword pattern '(?<!BestPracticeUnderstandingPerformanceIn)unity[ _]*7' It must not be published",
11-
"PackageVersion": "3.2.2"
11+
"PackageVersion": "3.3.0"
1212
}
1313
],
1414
"WarningExceptions": []

‎com.unity.netcode.gameobjects/package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "com.unity.netcode.gameobjects",
33
"displayName": "Netcode for GameObjects",
44
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
5-
"version": "3.2.2",
5+
"version": "3.3.0",
66
"unity": "6000.7",
77
"unityRelease": "0b8",
88
"dependencies": {
@@ -17,4 +17,4 @@
1717
"path": "Samples~/Bootstrap"
1818
}
1919
]
20-
}
20+
}

‎testproject/Assets/Tests/Runtime/NetworkManagerTests.cs‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,45 @@ private void OnServerStopped(bool obj)
198198
m_ServerNetworkManager.Shutdown();
199199
}
200200
}
201+
202+
internal class DerivedNetworkManagerTests
203+
{
204+
private class TestNetworkManager : NetworkManager
205+
{
206+
public void Awake() { }
207+
public void OnEnable() { }
208+
}
209+
210+
[UnityTest]
211+
public IEnumerator ValidateDerivedNetworkManager()
212+
{
213+
var gameObject = new GameObject("TestNetworkManager");
214+
var server = gameObject.AddComponent<TestNetworkManager>();
215+
var mockTransport = server.gameObject.AddComponent<MockTransport>();
216+
server.NetworkConfig ??= new NetworkConfig();
217+
server.NetworkConfig.NetworkTransport = mockTransport;
218+
var client = Object.Instantiate(server);
219+
220+
var isListening = server.StartServer();
221+
Assert.That(isListening, Is.True, "Server failed to start!");
222+
223+
client.StartClient();
224+
225+
var timeoutHelper = new TimeoutHelper();
226+
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => client.IsListening, timeoutHelper);
227+
Assert.That(timeoutHelper.TimedOut, Is.False, "Client timed out waiting to connect!");
228+
229+
Object.Destroy(client.gameObject);
230+
231+
server.Shutdown();
232+
timeoutHelper = new TimeoutHelper();
233+
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => !server.IsListening, timeoutHelper);
234+
Assert.That(timeoutHelper.TimedOut, Is.False, "Timed out waiting for server to shutdown!");
235+
236+
isListening = server.StartHost();
237+
Assert.That(isListening, Is.True, "Host failed to start!");
238+
239+
Object.Destroy(gameObject);
240+
}
241+
}
201242
}

0 commit comments

Comments
 (0)