Skip to content
Merged
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Changed

- Better error message when using generic IEquatable in a generic INetworkSerializable class and updated documentation with workaround. (#3739)
- The `NetworkManager` functions `GetTransportIdFromClientId` and `GetClientIdFromTransportId` will now return `ulong.MaxValue` when the clientId or transportId do not exist. (#3707)
- 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)
- Changed NetworkTransform now synchronizes `NetworkTransform.SwitchTransformSpaceWhenParented` when it is updated by the motion model authority. (#3664)
Expand All @@ -37,6 +38,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
- 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)
- Fixed issue when spawning, parenting, and immediately re-parenting when `NetworkTransform.SwitchTransformSpaceWhenParented` is enabled. (#3664)
- Fixed issue where the disconnect event and provided message was too generic to know why the disconnect occurred. (#3551)
- Exception when the network prefab list in the network manager has uninitialized elements. (#3739)

### Security

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,55 @@ public struct MyStructB : MyStructA
}
}
```

## Generic IEquatable network variables

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;`.

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.

For example:

```csharp
public class MyGameData<T> : INetworkSerializable
{
// This needs to be a serializable type according to what network variables support
public T Data;

protected virtual void OnNetworkSerialize<T2>(BufferSerializer<T2> serializer) where T2 : IReaderWriter
{
}

public void NetworkSerialize<T2>(BufferSerializer<T2> serializer) where T2 : IReaderWriter
{
OnNetworkSerialize(serializer);
}
}

public class GameDataWithLong : MyGameData<long>, IEquatable<GameDataWithLong>
{
// Potential additional data
public int AdditionalData;

protected virtual bool OnEquals(GameDataWithLong other)
{
return other.Data.Equals(other);
}
public bool Equals(GameDataWithLong other)
{
return OnEquals(other);
}

protected override void OnNetworkSerialize<T2>(BufferSerializer<T2> serializer)
{
serializer.SerializeValue(ref AdditionalData);
serializer.SerializeValue(ref Data);
}
}
```

Then declare this network variable like so:

```csharp
NetworkVariable<GameDataWithLong> myVar = new NetworkVariable<GameDataWithLong>();
```
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,14 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
}
else
{
m_Diagnostics.AddError($"{type}: Managed type in NetworkVariable must implement IEquatable<{type}>");
foreach (var typeInterface in type.Resolve().Interfaces)
{
if (typeInterface.InterfaceType.Name.Contains(typeof(IEquatable<>).Name) && typeInterface.InterfaceType.IsGenericInstance)
{
m_Diagnostics.AddError($"{type}: A generic IEquatable '{typeInterface.InterfaceType.FullName}' is not supported.");
}
}
m_Diagnostics.AddError($"{type}: Managed type in NetworkVariable must implement IEquatable<{type}>.");
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.Netcode.Editor.Configuration;
using UnityEditor;
using UnityEngine;
Expand Down Expand Up @@ -301,6 +302,10 @@ private void DisplayNetworkManagerProperties()
{
EditorGUILayout.HelpBox("You have no prefab list selected. You will have to add your prefabs manually at runtime for netcode to work.", MessageType.Warning);
}
else if (m_NetworkManager.NetworkConfig.Prefabs.NetworkPrefabsLists.All(x => x == null))
{
EditorGUILayout.HelpBox("All prefab lists selected are uninitialized. You will have to add your prefabs manually at runtime for netcode to work.", MessageType.Warning);
}
EditorGUILayout.PropertyField(m_PrefabsList);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ internal void Shutdown()
public void Initialize(bool warnInvalid = true)
{
m_Prefabs.Clear();
NetworkPrefabsLists.RemoveAll(x => x == null);
foreach (var list in NetworkPrefabsLists)
{
list.OnAdd += AddTriggeredByNetworkPrefabList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,22 @@ public void WhenModifyingPrefabListUsingPrefabsAPI_ModificationIsLocal()
}
}

[Test]
public void WhenThereAreUninitializedElementsInPrefabsList_NoErrors()
{
var networkConfig = new NetworkConfig();

networkConfig.Prefabs.NetworkPrefabsLists = new List<NetworkPrefabsList> { null };

networkConfig.InitializePrefabs();

// Null elements will be removed from the list so it should be empty
Assert.IsTrue(networkConfig.Prefabs.NetworkPrefabsLists.Count == 0);
Assert.IsTrue(networkConfig.Prefabs.Prefabs.Count == 0);

networkConfig.Prefabs.Shutdown();
}

[Test]
public void WhenModifyingPrefabListUsingPrefabsListAPI_ModificationIsShared()
{
Expand Down