Skip to content

Commit d90292d

Browse files
chore: migrate 2830 allow null references in NetworkBehaviourReference and NetworkObjectReference (#2874)
* initial commit * update adding changelog entry * update and style Fixing some style issues along with property type issues. * test Adding some generalized testing to validate both NetworkBehaviourReference and NetworkObjectReference can be created using a null and serialized when null. * update Updating the changed description. --------- Co-authored-by: Simone Guggiari <[email protected]>
1 parent 3ca359d commit d90292d

File tree

5 files changed

+119
-34
lines changed

5 files changed

+119
-34
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
3232

3333
### Changed
3434

35+
- Changed `NetworkObjectReference` and `NetworkBehaviourReference` to allow null references when constructing and serializing. (#2874)
3536
- Changed `NetworkAnimator` no longer requires the `Animator` component to exist on the same `GameObject`. (#2872)
3637
- Changed `NetworkTransform` to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810)
3738
- Changed `CustomMessageManager` so it no longer attempts to register or "unregister" a null or empty string and will log an error if this condition occurs. (#2807)

com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public struct NetworkBehaviourReference : INetworkSerializable, IEquatable<Netwo
1111
{
1212
private NetworkObjectReference m_NetworkObjectReference;
1313
private ushort m_NetworkBehaviourId;
14+
private static ushort s_NullId = ushort.MaxValue;
1415

1516
/// <summary>
1617
/// Creates a new instance of the <see cref="NetworkBehaviourReference{T}"/> struct.
@@ -21,7 +22,9 @@ public NetworkBehaviourReference(NetworkBehaviour networkBehaviour)
2122
{
2223
if (networkBehaviour == null)
2324
{
24-
throw new ArgumentNullException(nameof(networkBehaviour));
25+
m_NetworkObjectReference = new NetworkObjectReference((NetworkObject)null);
26+
m_NetworkBehaviourId = s_NullId;
27+
return;
2528
}
2629
if (networkBehaviour.NetworkObject == null)
2730
{
@@ -60,6 +63,10 @@ public bool TryGet<T>(out T networkBehaviour, NetworkManager networkManager = nu
6063
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6164
private static NetworkBehaviour GetInternal(NetworkBehaviourReference networkBehaviourRef, NetworkManager networkManager = null)
6265
{
66+
if (networkBehaviourRef.m_NetworkBehaviourId == s_NullId)
67+
{
68+
return null;
69+
}
6370
if (networkBehaviourRef.m_NetworkObjectReference.TryGet(out NetworkObject networkObject, networkManager))
6471
{
6572
return networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourRef.m_NetworkBehaviourId);

com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs

+15-6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Unity.Netcode
1010
public struct NetworkObjectReference : INetworkSerializable, IEquatable<NetworkObjectReference>
1111
{
1212
private ulong m_NetworkObjectId;
13+
private static ulong s_NullId = ulong.MaxValue;
1314

1415
/// <summary>
1516
/// The <see cref="NetworkObject.NetworkObjectId"/> of the referenced <see cref="NetworkObject"/>.
@@ -24,13 +25,13 @@ public ulong NetworkObjectId
2425
/// Creates a new instance of the <see cref="NetworkObjectReference"/> struct.
2526
/// </summary>
2627
/// <param name="networkObject">The <see cref="NetworkObject"/> to reference.</param>
27-
/// <exception cref="ArgumentNullException"></exception>
2828
/// <exception cref="ArgumentException"></exception>
2929
public NetworkObjectReference(NetworkObject networkObject)
3030
{
3131
if (networkObject == null)
3232
{
33-
throw new ArgumentNullException(nameof(networkObject));
33+
m_NetworkObjectId = s_NullId;
34+
return;
3435
}
3536

3637
if (networkObject.IsSpawned == false)
@@ -45,16 +46,20 @@ public NetworkObjectReference(NetworkObject networkObject)
4546
/// Creates a new instance of the <see cref="NetworkObjectReference"/> struct.
4647
/// </summary>
4748
/// <param name="gameObject">The GameObject from which the <see cref="NetworkObject"/> component will be referenced.</param>
48-
/// <exception cref="ArgumentNullException"></exception>
4949
/// <exception cref="ArgumentException"></exception>
5050
public NetworkObjectReference(GameObject gameObject)
5151
{
5252
if (gameObject == null)
5353
{
54-
throw new ArgumentNullException(nameof(gameObject));
54+
m_NetworkObjectId = s_NullId;
55+
return;
5556
}
5657

57-
var networkObject = gameObject.GetComponent<NetworkObject>() ?? throw new ArgumentException($"Cannot create {nameof(NetworkObjectReference)} from {nameof(GameObject)} without a {nameof(NetworkObject)} component.");
58+
var networkObject = gameObject.GetComponent<NetworkObject>();
59+
if (!networkObject)
60+
{
61+
throw new ArgumentException($"Cannot create {nameof(NetworkObjectReference)} from {nameof(GameObject)} without a {nameof(NetworkObject)} component.");
62+
}
5863
if (networkObject.IsSpawned == false)
5964
{
6065
throw new ArgumentException($"{nameof(NetworkObjectReference)} can only be created from spawned {nameof(NetworkObject)}s.");
@@ -80,10 +85,14 @@ public bool TryGet(out NetworkObject networkObject, NetworkManager networkManage
8085
/// </summary>
8186
/// <param name="networkObjectRef">The reference.</param>
8287
/// <param name="networkManager">The networkmanager. Uses <see cref="NetworkManager.Singleton"/> to resolve if null.</param>
83-
/// <returns>The resolves <see cref="NetworkObject"/>. Returns null if the networkobject was not found</returns>
88+
/// <returns>The resolved <see cref="NetworkObject"/>. Returns null if the networkobject was not found</returns>
8489
[MethodImpl(MethodImplOptions.AggressiveInlining)]
8590
private static NetworkObject Resolve(NetworkObjectReference networkObjectRef, NetworkManager networkManager = null)
8691
{
92+
if (networkObjectRef.m_NetworkObjectId == s_NullId)
93+
{
94+
return null;
95+
}
8796
networkManager = networkManager ?? NetworkManager.Singleton;
8897
networkManager.SpawnManager.SpawnedObjects.TryGetValue(networkObjectRef.m_NetworkObjectId, out NetworkObject networkObject);
8998

com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkBehaviourReferenceTests.cs

+39-9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public class NetworkBehaviourReferenceTests : IDisposable
1717
{
1818
private class TestNetworkBehaviour : NetworkBehaviour
1919
{
20+
public static bool ReceivedRPC;
21+
2022
public NetworkVariable<NetworkBehaviourReference> TestVariable = new NetworkVariable<NetworkBehaviourReference>();
2123

2224
public TestNetworkBehaviour RpcReceivedBehaviour;
@@ -25,6 +27,7 @@ private class TestNetworkBehaviour : NetworkBehaviour
2527
public void SendReferenceServerRpc(NetworkBehaviourReference value)
2628
{
2729
RpcReceivedBehaviour = (TestNetworkBehaviour)value;
30+
ReceivedRPC = true;
2831
}
2932
}
3033

@@ -57,8 +60,43 @@ public IEnumerator TestRpc()
5760
Assert.AreEqual(testNetworkBehaviour, testNetworkBehaviour.RpcReceivedBehaviour);
5861
}
5962

63+
[UnityTest]
64+
public IEnumerator TestSerializeNull([Values] bool initializeWithNull)
65+
{
66+
TestNetworkBehaviour.ReceivedRPC = false;
67+
using var networkObjectContext = UnityObjectContext.CreateNetworkObject();
68+
var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent<TestNetworkBehaviour>();
69+
networkObjectContext.Object.Spawn();
70+
71+
using var otherObjectContext = UnityObjectContext.CreateNetworkObject();
72+
otherObjectContext.Object.Spawn();
73+
74+
// If not initializing with null, then use the default constructor with no assigned NetworkBehaviour
75+
if (!initializeWithNull)
76+
{
77+
testNetworkBehaviour.SendReferenceServerRpc(new NetworkBehaviourReference());
78+
}
79+
else // Otherwise, initialize and pass in null as the reference
80+
{
81+
testNetworkBehaviour.SendReferenceServerRpc(new NetworkBehaviourReference(null));
82+
}
83+
84+
// wait for rpc completion
85+
float t = 0;
86+
while (!TestNetworkBehaviour.ReceivedRPC)
87+
{
88+
t += Time.deltaTime;
89+
if (t > 5f)
90+
{
91+
new AssertionException("RPC with NetworkBehaviour reference hasn't been received");
92+
}
6093

94+
yield return null;
95+
}
6196

97+
// validate
98+
Assert.AreEqual(null, testNetworkBehaviour.RpcReceivedBehaviour);
99+
}
62100

63101
[UnityTest]
64102
public IEnumerator TestRpcImplicitNetworkBehaviour()
@@ -89,6 +127,7 @@ public IEnumerator TestRpcImplicitNetworkBehaviour()
89127
Assert.AreEqual(testNetworkBehaviour, testNetworkBehaviour.RpcReceivedBehaviour);
90128
}
91129

130+
92131
[Test]
93132
public void TestNetworkVariable()
94133
{
@@ -131,15 +170,6 @@ public void FailSerializeGameObjectWithoutNetworkObject()
131170
});
132171
}
133172

134-
[Test]
135-
public void FailSerializeNullBehaviour()
136-
{
137-
Assert.Throws<ArgumentNullException>(() =>
138-
{
139-
NetworkBehaviourReference outReference = null;
140-
});
141-
}
142-
143173
public void Dispose()
144174
{
145175
//Stop, shutdown, and destroy

com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs

+56-18
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class NetworkObjectReferenceTests : IDisposable
1919
{
2020
private class TestNetworkBehaviour : NetworkBehaviour
2121
{
22+
public static bool ReceivedRPC;
2223
public NetworkVariable<NetworkObjectReference> TestVariable = new NetworkVariable<NetworkObjectReference>();
2324

2425
public NetworkObject RpcReceivedNetworkObject;
@@ -28,6 +29,7 @@ private class TestNetworkBehaviour : NetworkBehaviour
2829
[ServerRpc]
2930
public void SendReferenceServerRpc(NetworkObjectReference value)
3031
{
32+
ReceivedRPC = true;
3133
RpcReceivedGameObject = value;
3234
RpcReceivedNetworkObject = value;
3335
}
@@ -150,6 +152,60 @@ public void TestTryGet()
150152
Assert.AreEqual(networkObject, result);
151153
}
152154

155+
public enum NetworkObjectConstructorTypes
156+
{
157+
None,
158+
NullNetworkObject,
159+
NullGameObject
160+
}
161+
162+
[UnityTest]
163+
public IEnumerator TestSerializeNull([Values] NetworkObjectConstructorTypes networkObjectConstructorTypes)
164+
{
165+
TestNetworkBehaviour.ReceivedRPC = false;
166+
using var networkObjectContext = UnityObjectContext.CreateNetworkObject();
167+
var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent<TestNetworkBehaviour>();
168+
networkObjectContext.Object.Spawn();
169+
170+
switch (networkObjectConstructorTypes)
171+
{
172+
case NetworkObjectConstructorTypes.None:
173+
{
174+
testNetworkBehaviour.SendReferenceServerRpc(new NetworkObjectReference());
175+
break;
176+
}
177+
case NetworkObjectConstructorTypes.NullNetworkObject:
178+
{
179+
testNetworkBehaviour.SendReferenceServerRpc(new NetworkObjectReference((NetworkObject)null));
180+
break;
181+
}
182+
case NetworkObjectConstructorTypes.NullGameObject:
183+
{
184+
testNetworkBehaviour.SendReferenceServerRpc(new NetworkObjectReference((GameObject)null));
185+
break;
186+
}
187+
}
188+
189+
190+
// wait for rpc completion
191+
float t = 0;
192+
while (!TestNetworkBehaviour.ReceivedRPC)
193+
{
194+
195+
t += Time.deltaTime;
196+
if (t > 5f)
197+
{
198+
new AssertionException("RPC with NetworkBehaviour reference hasn't been received");
199+
}
200+
201+
yield return null;
202+
}
203+
204+
// validate
205+
Assert.AreEqual(null, testNetworkBehaviour.RpcReceivedNetworkObject);
206+
Assert.AreEqual(null, testNetworkBehaviour.RpcReceivedGameObject);
207+
}
208+
153209
[UnityTest]
154210
public IEnumerator TestRpc()
155211
{
@@ -305,24 +361,6 @@ public void FailSerializeGameObjectWithoutNetworkObject()
305361
});
306362
}
307363

308-
[Test]
309-
public void FailSerializeNullNetworkObject()
310-
{
311-
Assert.Throws<ArgumentNullException>(() =>
312-
{
313-
NetworkObjectReference outReference = (NetworkObject)null;
314-
});
315-
}
316-
317-
[Test]
318-
public void FailSerializeNullGameObject()
319-
{
320-
Assert.Throws<ArgumentNullException>(() =>
321-
{
322-
NetworkObjectReference outReference = (GameObject)null;
323-
});
324-
}
325-
326364
public void Dispose()
327365
{
328366
//Stop, shutdown, and destroy

0 commit comments

Comments
 (0)