Skip to content

Commit cda4b95

Browse files
committed
fix: add better error messages for a couple of error cases
- When using a generic INetworkSerializable+IEquatable combo and update documentation with workaround - When there are no initialized elements in the network prefab list (fix null reference exception)
1 parent 4b99f27 commit cda4b95

File tree

5 files changed

+107
-1
lines changed

5 files changed

+107
-1
lines changed

com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,3 +621,55 @@ public class TestFixedString : NetworkBehaviour
621621

622622
> [!NOTE]
623623
> The above example uses a pre-set list of strings to cycle through for example purposes only. If you have a predefined set of text strings as part of your actual design then you would not want to use a FixedString to handle synchronizing the changes to `m_TextString`. Instead, you would want to use a `uint` for the type `T` where the `uint` was the index of the string message to apply to `m_TextString`.
624+
625+
## Generic IEquatable network variables
626+
627+
Generic `INetworkSerializable` types with generic `IEquatable` are not supported, implemented as `public class NotSupported<T> : INetworkSerializable, IEquatable<NotSupported<T>>` where the type would be passed in during declaration like `NetworkVariable<NotSupported<int>> myVar;`.
628+
629+
The recommended workaround for this would be to create the generic class as usual but add a virtual method for handling the serialization of the type. Then wrap this generic `INetworkSerializable` in a derived class which then needs to have a serializable type defined where the implementation for the serialization is provided.
630+
631+
For example:
632+
633+
```csharp
634+
public class FirstGenClass<T> : INetworkSerializable
635+
{
636+
// This needs to be a serializable type according to what network variables support
637+
public T Data;
638+
639+
protected virtual void OnNetworkSerialize<T2>(BufferSerializer<T2> serializer) where T2 : IReaderWriter
640+
{
641+
}
642+
643+
public void NetworkSerialize<T2>(BufferSerializer<T2> serializer) where T2 : IReaderWriter
644+
{
645+
OnNetworkSerialize(serializer);
646+
}
647+
}
648+
649+
public class SecondGenWithLong : FirstGenClass<long>, IEquatable<SecondGenWithLong>
650+
{
651+
// Potential additional data
652+
public int AdditionalData;
653+
654+
protected virtual bool OnEquals(SecondGenWithLong other)
655+
{
656+
return other.Data.Equals(other);
657+
}
658+
public bool Equals(SecondGenWithLong other)
659+
{
660+
return OnEquals(other);
661+
}
662+
663+
protected override void OnNetworkSerialize<T2>(BufferSerializer<T2> serializer)
664+
{
665+
serializer.SerializeValue(ref AdditionalData);
666+
serializer.SerializeValue(ref Data);
667+
}
668+
}
669+
```
670+
671+
Then declare this network variable like so:
672+
673+
```csharp
674+
NetworkVariable<SecondGenWithLong> myVar = new NetworkVariable<SecondGenWithLong>();
675+
```

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,14 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
409409
}
410410
else
411411
{
412-
m_Diagnostics.AddError($"{type}: Managed type in NetworkVariable must implement IEquatable<{type}>");
412+
foreach (var typeInterface in type.Resolve().Interfaces)
413+
{
414+
if (typeInterface.InterfaceType.Name.Contains(typeof(IEquatable<>).Name) && typeInterface.InterfaceType.IsGenericInstance)
415+
{
416+
m_Diagnostics.AddError($"{type}: A generic IEquatable '{typeInterface.InterfaceType.FullName}' is not supported.");
417+
}
418+
}
419+
m_Diagnostics.AddError($"{type}: Managed type in NetworkVariable must implement IEquatable<{type}>.");
413420
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef);
414421
}
415422

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using Unity.Netcode.Editor.Configuration;
56
using UnityEditor;
67
using UnityEngine;
@@ -301,6 +302,10 @@ private void DisplayNetworkManagerProperties()
301302
{
302303
EditorGUILayout.HelpBox("You have no prefab list selected. You will have to add your prefabs manually at runtime for netcode to work.", MessageType.Warning);
303304
}
305+
else if (m_NetworkManager.NetworkConfig.Prefabs.NetworkPrefabsLists.All(x => x == null))
306+
{
307+
EditorGUILayout.HelpBox("All prefab lists selected are uninitialized. You will have to add your prefabs manually at runtime for netcode to work.", MessageType.Warning);
308+
}
304309
EditorGUILayout.PropertyField(m_PrefabsList);
305310
}
306311

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ internal void Shutdown()
8181
{
8282
foreach (var list in NetworkPrefabsLists)
8383
{
84+
if (list == null)
85+
{
86+
continue;
87+
}
8488
list.OnAdd -= AddTriggeredByNetworkPrefabList;
8589
list.OnRemove -= RemoveTriggeredByNetworkPrefabList;
8690
}
@@ -96,6 +100,10 @@ public void Initialize(bool warnInvalid = true)
96100
m_Prefabs.Clear();
97101
foreach (var list in NetworkPrefabsLists)
98102
{
103+
if (list == null)
104+
{
105+
continue;
106+
}
99107
list.OnAdd += AddTriggeredByNetworkPrefabList;
100108
list.OnRemove += RemoveTriggeredByNetworkPrefabList;
101109
}
@@ -109,6 +117,10 @@ public void Initialize(bool warnInvalid = true)
109117
{
110118
foreach (var list in NetworkPrefabsLists)
111119
{
120+
if (list == null)
121+
{
122+
continue;
123+
}
112124
prefabs.AddRange(list.PrefabList);
113125
}
114126
}

com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,36 @@ public void WhenModifyingPrefabListUsingPrefabsAPI_ModificationIsLocal()
294294
}
295295
}
296296

297+
[Test]
298+
public void WhenThereAreUninitializedElementsInPrefabsList_NoErrors()
299+
{
300+
// Setup
301+
var networkManagerObject = new GameObject();
302+
var networkManager = networkManagerObject.AddComponent<NetworkManager>();
303+
networkManager.NetworkConfig = new NetworkConfig
304+
{
305+
NetworkTransport = networkManager.gameObject.AddComponent<UnityTransport>()
306+
};
307+
308+
try
309+
{
310+
networkManager.NetworkConfig.Prefabs.NetworkPrefabsLists = new List<NetworkPrefabsList> { null };
311+
networkManager.Initialize(true);
312+
313+
Assert.IsTrue(networkManager.NetworkConfig.Prefabs.NetworkPrefabsLists.Count == 1);
314+
Assert.IsTrue(networkManager.NetworkConfig.Prefabs.NetworkPrefabsLists[0] == null);
315+
Assert.IsTrue(networkManager.NetworkConfig.Prefabs.Prefabs.Count == 0);
316+
}
317+
finally
318+
{
319+
networkManager.ShutdownInternal();
320+
// Shutdown doesn't get called correctly because we called Initialize()
321+
// instead of calling StartHost/StartClient/StartServer. See MTT-860 for
322+
// why.
323+
networkManager.NetworkConfig?.NetworkTransport.Shutdown();
324+
}
325+
}
326+
297327
[Test]
298328
public void WhenModifyingPrefabListUsingPrefabsListAPI_ModificationIsShared()
299329
{

0 commit comments

Comments
 (0)