Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,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. (#3744)
- The `NetworkManager` functions `GetTransportIdFromClientId` and `GetClientIdFromTransportId` will now return `ulong.MaxValue` when the clientId or transportId do not exist. (#3721)
- Changed minimum Unity version supported to 2022.3 LTS

Expand All @@ -25,6 +26,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Fixed

- Multiple disconnect events from the same transport will no longer disconnect the host. (#3721)
- Exception when the network prefab list in the network manager has uninitialized elements. (#3744)

### 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

Netcode for GameObjects doesn't support generic `INetworkSerializable` types with generic `IEquatable`s when implemented as `public class NotSupported<T> : INetworkSerializable, IEquatable<NotSupported<T>>`, where the type is passed in during declaration like `NetworkVariable<NotSupported<int>> myVar;`.

You can work around this limitation by creating the generic class as normal and adding a virtual method for handling the serialization of the type. Then wrap the generic `INetworkSerializable` in a derived class that has 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 @@ -408,7 +408,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
Expand Up @@ -4,6 +4,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 @@ -539,6 +540,10 @@ private void DrawPrefabListField()
{
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);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,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 @@ -282,6 +282,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
1 change: 1 addition & 0 deletions pvpExceptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
"Unity.Netcode.EditorTests.NetworkManagerConfigurationTests: void WhenCallingInitializeAfterAddingAPrefabUsingPrefabsAPI_ThePrefabStillExists(): undocumented",
"Unity.Netcode.EditorTests.NetworkManagerConfigurationTests: void WhenShuttingDownAndReinitializingPrefabs_RuntimeAddedPrefabsStillExists(): undocumented",
"Unity.Netcode.EditorTests.NetworkManagerConfigurationTests: void WhenCallingInitializeMultipleTimes_NothingBreaks(): undocumented",
"Unity.Netcode.EditorTests.NetworkManagerConfigurationTests: void WhenThereAreUninitializedElementsInPrefabsList_NoErrors(): undocumented",
"Unity.Netcode.EditorTests.NetworkManagerConfigurationTests.NetworkObjectPlacement: undocumented",
"Unity.Netcode.EditorTests.NetworkManagerConfigurationTests.NetworkObjectPlacement: Root: undocumented",
"Unity.Netcode.EditorTests.NetworkManagerConfigurationTests.NetworkObjectPlacement: Child: undocumented",
Expand Down