The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.0.0-pre.10] - 2022-06-21 ### Added - Added a new `OnTransportFailure` callback to `NetworkManager`. This callback is invoked when the manager's `NetworkTransport` encounters an unrecoverable error. Transport failures also cause the `NetworkManager` to shut down. Currently, this is only used by `UnityTransport` to signal a timeout of its connection to the Unity Relay servers. (#1994) - Added `NetworkEvent.TransportFailure`, which can be used by implementations of `NetworkTransport` to signal to `NetworkManager` that an unrecoverable error was encountered. (#1994) - Added test to ensure a warning occurs when nesting NetworkObjects in a NetworkPrefab (#1969) - Added `NetworkManager.RemoveNetworkPrefab(...)` to remove a prefab from the prefabs list (#1950) ### Changed - Updated `UnityTransport` dependency on `com.unity.transport` to 1.1.0. (#2025) - (API Breaking) `ConnectionApprovalCallback` is no longer an `event` and will not allow more than 1 handler registered at a time. Also, `ConnectionApprovalCallback` is now a `Func<>` taking `ConnectionApprovalRequest` in and returning `ConnectionApprovalResponse` back out (#1972) ### Removed ### Fixed - Fixed issue where dynamically spawned `NetworkObject`s could throw an exception if the scene of origin handle was zero (0) and the `NetworkObject` was already spawned. (#2017) - Fixed issue where `NetworkObject.Observers` was not being cleared when despawned. (#2009) - Fixed `NetworkAnimator` could not run in the server authoritative mode. (#2003) - Fixed issue where late joining clients would get a soft synchronization error if any in-scene placed NetworkObjects were parented under another `NetworkObject`. (#1985) - Fixed issue where `NetworkBehaviourReference` would throw a type cast exception if using `NetworkBehaviourReference.TryGet` and the component type was not found. (#1984) - Fixed `NetworkSceneManager` was not sending scene event notifications for the currently active scene and any additively loaded scenes when loading a new scene in `LoadSceneMode.Single` mode. (#1975) - Fixed issue where one or more clients disconnecting during a scene event would cause `LoadEventCompleted` or `UnloadEventCompleted` to wait until the `NetworkConfig.LoadSceneTimeOut` period before being triggered. (#1973) - Fixed issues when multiple `ConnectionApprovalCallback`s were registered (#1972) - Fixed a regression in serialization support: `FixedString`, `Vector2Int`, and `Vector3Int` types can now be used in NetworkVariables and RPCs again without requiring a `ForceNetworkSerializeByMemcpy<>` wrapper. (#1961) - Fixed generic types that inherit from NetworkBehaviour causing crashes at compile time. (#1976) - Fixed endless dialog boxes when adding a `NetworkBehaviour` to a `NetworkManager` or vice-versa. (#1947) - Fixed `NetworkAnimator` issue where it was only synchronizing parameters if the layer or state changed or was transitioning between states. (#1946) - Fixed `NetworkAnimator` issue where when it did detect a parameter had changed it would send all parameters as opposed to only the parameters that changed. (#1946) - Fixed `NetworkAnimator` issue where it was not always disposing the `NativeArray` that is allocated when spawned. (#1946) - Fixed `NetworkAnimator` issue where it was not taking the animation speed or state speed multiplier into consideration. (#1946) - Fixed `NetworkAnimator` issue where it was not properly synchronizing late joining clients if they joined while `Animator` was transitioning between states. (#1946) - Fixed `NetworkAnimator` issue where the server was not relaying changes to non-owner clients when a client was the owner. (#1946) - Fixed issue where the `PacketLoss` metric for tools would return the packet loss over a connection lifetime instead of a single frame. (#2004)
234 lines
11 KiB
C#
234 lines
11 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
using NUnit.Framework;
|
|
using Unity.Collections;
|
|
using Unity.Netcode.TestHelpers.Runtime;
|
|
|
|
namespace Unity.Netcode.RuntimeTests
|
|
{
|
|
public class NetworkObjectSceneSerializationTests
|
|
{
|
|
|
|
/// <summary>
|
|
/// The purpose behind this test is to assure that in-scene NetworkObjects
|
|
/// that are serialized into a single stream (approval or switch scene this happens)
|
|
/// will continue to be processed even if one of the NetworkObjects is invalid.
|
|
/// </summary>
|
|
[Test]
|
|
public void NetworkObjectSceneSerializationFailure()
|
|
{
|
|
var networkObjectsToTest = new List<GameObject>();
|
|
|
|
var writer = new FastBufferWriter(1300, Allocator.Temp, 4096000);
|
|
var invalidNetworkObjectOffsets = new List<long>();
|
|
var invalidNetworkObjectIdCount = new List<int>();
|
|
var invalidNetworkObjects = new List<GameObject>();
|
|
var invalidNetworkObjectFrequency = 3;
|
|
using (writer)
|
|
{
|
|
// Construct 50 NetworkObjects
|
|
for (int i = 0; i < 50; i++)
|
|
{
|
|
// Inject an invalid NetworkObject every [invalidNetworkObjectFrequency] entry
|
|
if ((i % invalidNetworkObjectFrequency) == 0)
|
|
{
|
|
// Create the invalid NetworkObject
|
|
var gameObject = new GameObject($"InvalidTestObject{i}");
|
|
|
|
Assert.IsNotNull(gameObject);
|
|
|
|
var networkObject = gameObject.AddComponent<NetworkObject>();
|
|
|
|
Assert.IsNotNull(networkObject);
|
|
|
|
var networkVariableComponent = gameObject.AddComponent<NetworkBehaviourWithNetworkVariables>();
|
|
Assert.IsNotNull(networkVariableComponent);
|
|
|
|
// Add invalid NetworkObject's starting position before serialization to handle trapping for the Debug.LogError message
|
|
// that we know will be thrown
|
|
invalidNetworkObjectOffsets.Add(writer.Position);
|
|
|
|
networkObject.GlobalObjectIdHash = (uint)(i);
|
|
invalidNetworkObjectIdCount.Add(i);
|
|
|
|
invalidNetworkObjects.Add(gameObject);
|
|
|
|
writer.WriteValueSafe((int)networkObject.GetSceneOriginHandle());
|
|
// Serialize the invalid NetworkObject
|
|
var sceneObject = networkObject.GetMessageSceneObject(0);
|
|
var prePosition = writer.Position;
|
|
sceneObject.Serialize(writer);
|
|
|
|
Debug.Log(
|
|
$"Invalid {nameof(NetworkObject)} Size {writer.Position - prePosition}");
|
|
|
|
// Now adjust how frequent we will inject invalid NetworkObjects
|
|
invalidNetworkObjectFrequency = Random.Range(2, 5);
|
|
|
|
}
|
|
else
|
|
{
|
|
// Create a valid NetworkObject
|
|
var gameObject = new GameObject($"TestObject{i}");
|
|
|
|
Assert.IsNotNull(gameObject);
|
|
|
|
var networkObject = gameObject.AddComponent<NetworkObject>();
|
|
|
|
var networkVariableComponent = gameObject.AddComponent<NetworkBehaviourWithNetworkVariables>();
|
|
Assert.IsNotNull(networkVariableComponent);
|
|
|
|
Assert.IsNotNull(networkObject);
|
|
|
|
networkObject.GlobalObjectIdHash = (uint)(i + 4096);
|
|
|
|
networkObjectsToTest.Add(gameObject);
|
|
|
|
writer.WriteValueSafe(networkObject.GetSceneOriginHandle());
|
|
|
|
// Handle populating the scenes loaded list
|
|
var scene = networkObject.gameObject.scene;
|
|
|
|
if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey(
|
|
scene.handle))
|
|
{
|
|
NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded
|
|
.Add(scene.handle, scene);
|
|
}
|
|
var handle = networkObject.GetSceneOriginHandle();
|
|
// Since this is a unit test, we will fake the server to client handle lookup by just adding the same handle key and value
|
|
if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle
|
|
.ContainsKey(handle))
|
|
{
|
|
NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle
|
|
.Add(handle, handle);
|
|
}
|
|
|
|
// Serialize the valid NetworkObject
|
|
var sceneObject = networkObject.GetMessageSceneObject(0);
|
|
sceneObject.Serialize(writer);
|
|
|
|
if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.ContainsKey(
|
|
networkObject.GlobalObjectIdHash))
|
|
{
|
|
NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add(
|
|
networkObject.GlobalObjectIdHash, new Dictionary<int, NetworkObject>());
|
|
}
|
|
|
|
// Add this valid NetworkObject into the ScenePlacedObjects list
|
|
NetworkManagerHelper.NetworkManagerObject.SceneManager
|
|
.ScenePlacedObjects[networkObject.GlobalObjectIdHash]
|
|
.Add(SceneManager.GetActiveScene().handle, networkObject);
|
|
}
|
|
}
|
|
|
|
var totalBufferSize = writer.Position;
|
|
|
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
|
using (reader)
|
|
{
|
|
|
|
var networkObjectsDeSerialized = new List<NetworkObject>();
|
|
var currentLogLevel = NetworkManager.Singleton.LogLevel;
|
|
var invalidNetworkObjectCount = 0;
|
|
while (reader.Position != totalBufferSize)
|
|
{
|
|
// If we reach the point where we expect it to fail, then make sure we let TestRunner know it should expect this log error message
|
|
if (invalidNetworkObjectOffsets.Count > 0 &&
|
|
reader.Position == invalidNetworkObjectOffsets[0])
|
|
{
|
|
invalidNetworkObjectOffsets.RemoveAt(0);
|
|
|
|
// Turn off Network Logging to avoid other errors that we know will happen after the below LogAssert.Expect message occurs.
|
|
NetworkManager.Singleton.LogLevel = LogLevel.Nothing;
|
|
|
|
// Trap for this specific error message so we don't make Test Runner think we failed (it will fail on Debug.LogError)
|
|
UnityEngine.TestTools.LogAssert.Expect(LogType.Error,
|
|
$"Failed to spawn {nameof(NetworkObject)} for Hash {invalidNetworkObjectIdCount[invalidNetworkObjectCount]}.");
|
|
|
|
invalidNetworkObjectCount++;
|
|
}
|
|
|
|
|
|
reader.ReadValueSafe(out int handle);
|
|
NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(handle);
|
|
var sceneObject = new NetworkObject.SceneObject();
|
|
sceneObject.Deserialize(reader);
|
|
|
|
var deserializedNetworkObject = NetworkObject.AddSceneObject(sceneObject, reader,
|
|
NetworkManagerHelper.NetworkManagerObject);
|
|
if (deserializedNetworkObject != null)
|
|
{
|
|
networkObjectsDeSerialized.Add(deserializedNetworkObject);
|
|
}
|
|
else
|
|
{
|
|
// Under this condition, we are expecting null (i.e. no NetworkObject instantiated)
|
|
// and will set our log level back to the original value to assure the valid NetworkObjects
|
|
// aren't causing any log Errors to occur
|
|
NetworkManager.Singleton.LogLevel = currentLogLevel;
|
|
}
|
|
}
|
|
|
|
// Now validate all NetworkObjects returned against the original NetworkObjects we created
|
|
// after they validate, destroy the objects
|
|
foreach (var entry in networkObjectsToTest)
|
|
{
|
|
var entryNetworkObject = entry.GetComponent<NetworkObject>();
|
|
Assert.IsTrue(networkObjectsDeSerialized.Contains(entryNetworkObject));
|
|
Object.Destroy(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Destroy the invalid network objects
|
|
foreach (var entry in invalidNetworkObjects)
|
|
{
|
|
Object.Destroy(entry);
|
|
}
|
|
}
|
|
|
|
[SetUp]
|
|
public void Setup()
|
|
{
|
|
// Create, instantiate, and host
|
|
NetworkManagerHelper.StartNetworkManager(out NetworkManager networkManager, NetworkManagerHelper.NetworkManagerOperatingMode.None);
|
|
networkManager.NetworkConfig.EnableSceneManagement = true;
|
|
networkManager.StartHost();
|
|
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
// Stop, shutdown, and destroy
|
|
NetworkManagerHelper.ShutdownNetworkManager();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A simple test class that will provide varying NetworkBuffer stream sizes
|
|
/// when the NetworkVariable is serialized
|
|
/// </summary>
|
|
public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour
|
|
{
|
|
private const uint k_MinDataBlocks = 1;
|
|
private const uint k_MaxDataBlocks = 64;
|
|
|
|
public NetworkList<ulong> NetworkVariableData;
|
|
|
|
private void Awake()
|
|
{
|
|
var dataBlocksAssigned = new List<ulong>();
|
|
var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks);
|
|
for (var i = 0; i < numberDataBlocks; i++)
|
|
{
|
|
dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue));
|
|
}
|
|
|
|
NetworkVariableData = new NetworkList<ulong>(dataBlocksAssigned);
|
|
}
|
|
}
|
|
}
|