This repository has been archived on 2025-04-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs
Unity Technologies 0f7a30d285 com.unity.netcode.gameobjects@1.0.0-pre.10
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)
2022-06-21 00:00:00 +00:00

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);
}
}
}