Skip to content

Commit 6eb88aa

Browse files
larusNoelStephensUnityEmandM
authored
fix: add better error messages for a couple of error cases (#3739)
* 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) * update changelog * style Removing single whitespace at end of sentence. * style Keeping the most recent changelog entry chronologically ordered (ordered by PR number). * Apply suggestions from code review Co-authored-by: Emma <[email protected]> * add missing pr number * move to inetworkserializable section * switch to testing NetworkConfig directly * remove uninitialized from list instead of ignoring --------- Co-authored-by: Noel Stephens <[email protected]> Co-authored-by: Emma <[email protected]>
1 parent f0b8a92 commit 6eb88aa

File tree

6 files changed

+84
-1
lines changed

6 files changed

+84
-1
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1616

1717
### Changed
1818

19+
- Better error message when using generic IEquatable in a generic INetworkSerializable class and updated documentation with workaround. (#3739)
1920
- The `NetworkManager` functions `GetTransportIdFromClientId` and `GetClientIdFromTransportId` will now return `ulong.MaxValue` when the clientId or transportId do not exist. (#3707)
2021
- Changed NetworkShow to send a message at the end of the frame and force a NetworkVariable synchronization prior to generating the CreateObjectMessage as opposed to waiting until the next network tick to synchronize the show with the update to NetworkVariables. (#3664)
2122
- Changed NetworkTransform now synchronizes `NetworkTransform.SwitchTransformSpaceWhenParented` when it is updated by the motion model authority. (#3664)
@@ -37,6 +38,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
3738
- Fixed NetworkTransform state synchronization issue when `NetworkTransform.SwitchTransformSpaceWhenParented` is enabled and the associated NetworkObject is parented multiple times in a single frame or within a couple of frames. (#3664)
3839
- Fixed issue when spawning, parenting, and immediately re-parenting when `NetworkTransform.SwitchTransformSpaceWhenParented` is enabled. (#3664)
3940
- Fixed issue where the disconnect event and provided message was too generic to know why the disconnect occurred. (#3551)
41+
- Exception when the network prefab list in the network manager has uninitialized elements. (#3739)
4042

4143
### Security
4244

com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializable.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,55 @@ public struct MyStructB : MyStructA
181181
}
182182
}
183183
```
184+
185+
## Generic IEquatable network variables
186+
187+
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;`.
188+
189+
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.
190+
191+
For example:
192+
193+
```csharp
194+
public class MyGameData<T> : INetworkSerializable
195+
{
196+
// This needs to be a serializable type according to what network variables support
197+
public T Data;
198+
199+
protected virtual void OnNetworkSerialize<T2>(BufferSerializer<T2> serializer) where T2 : IReaderWriter
200+
{
201+
}
202+
203+
public void NetworkSerialize<T2>(BufferSerializer<T2> serializer) where T2 : IReaderWriter
204+
{
205+
OnNetworkSerialize(serializer);
206+
}
207+
}
208+
209+
public class GameDataWithLong : MyGameData<long>, IEquatable<GameDataWithLong>
210+
{
211+
// Potential additional data
212+
public int AdditionalData;
213+
214+
protected virtual bool OnEquals(GameDataWithLong other)
215+
{
216+
return other.Data.Equals(other);
217+
}
218+
public bool Equals(GameDataWithLong other)
219+
{
220+
return OnEquals(other);
221+
}
222+
223+
protected override void OnNetworkSerialize<T2>(BufferSerializer<T2> serializer)
224+
{
225+
serializer.SerializeValue(ref AdditionalData);
226+
serializer.SerializeValue(ref Data);
227+
}
228+
}
229+
```
230+
231+
Then declare this network variable like so:
232+
233+
```csharp
234+
NetworkVariable<GameDataWithLong> myVar = new NetworkVariable<GameDataWithLong>();
235+
```

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ internal void Shutdown()
9494
public void Initialize(bool warnInvalid = true)
9595
{
9696
m_Prefabs.Clear();
97+
NetworkPrefabsLists.RemoveAll(x => x == null);
9798
foreach (var list in NetworkPrefabsLists)
9899
{
99100
list.OnAdd += AddTriggeredByNetworkPrefabList;

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

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

297+
[Test]
298+
public void WhenThereAreUninitializedElementsInPrefabsList_NoErrors()
299+
{
300+
var networkConfig = new NetworkConfig();
301+
302+
networkConfig.Prefabs.NetworkPrefabsLists = new List<NetworkPrefabsList> { null };
303+
304+
networkConfig.InitializePrefabs();
305+
306+
// Null elements will be removed from the list so it should be empty
307+
Assert.IsTrue(networkConfig.Prefabs.NetworkPrefabsLists.Count == 0);
308+
Assert.IsTrue(networkConfig.Prefabs.Prefabs.Count == 0);
309+
310+
networkConfig.Prefabs.Shutdown();
311+
}
312+
297313
[Test]
298314
public void WhenModifyingPrefabListUsingPrefabsListAPI_ModificationIsShared()
299315
{

0 commit comments

Comments
 (0)