com.unity.netcode.gameobjects@1.5.1

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.5.1] - 2023-06-07

### Added

- Added support for serializing `NativeArray<>` and `NativeList<>` in `FastBufferReader`/`FastBufferWriter`, `BufferSerializer`, `NetworkVariable`, and RPCs. (To use `NativeList<>`, add `UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT` to your Scripting Define Symbols in `Project Settings > Player`) (#2375)
- The location of the automatically-created default network prefab list can now be configured (#2544)
- Added: Message size limits (max single message and max fragmented message) can now be set using NetworkManager.MaximumTransmissionUnitSize and NetworkManager.MaximumFragmentedMessageSize for transports that don't work with the default values (#2530)
- Added `NetworkObject.SpawnWithObservers` property (default is true) that when set to false will spawn a `NetworkObject` with no observers and will not be spawned on any client until `NetworkObject.NetworkShow` is invoked. (#2568)

### Fixed

- Fixed: Fixed a null reference in codegen in some projects (#2581)
- Fixed issue where the `OnClientDisconnected` client identifier was incorrect after a pending client connection was denied. (#2569)
- Fixed warning "Runtime Network Prefabs was not empty at initialization time." being erroneously logged when no runtime network prefabs had been added (#2565)
- Fixed issue where some temporary debug console logging was left in a merged PR. (#2562)
- Fixed the "Generate Default Network Prefabs List" setting not loading correctly and always reverting to being checked. (#2545)
- Fixed issue where users could not use NetworkSceneManager.VerifySceneBeforeLoading to exclude runtime generated scenes from client synchronization. (#2550)
- Fixed missing value on `NetworkListEvent` for `EventType.RemoveAt` events.  (#2542,#2543)
- Fixed issue where parenting a NetworkTransform under a transform with a scale other than Vector3.one would result in incorrect values on non-authoritative instances. (#2538)
- Fixed issue where a server would include scene migrated and then despawned NetworkObjects to a client that was being synchronized. (#2532)
- Fixed the inspector throwing exceptions when attempting to render `NetworkVariable`s of enum types. (#2529)
- Making a `NetworkVariable` with an `INetworkSerializable` type that doesn't meet the `new()` constraint will now create a compile-time error instead of an editor crash (#2528)
- Fixed Multiplayer Tools package installation docs page link on the NetworkManager popup. (#2526)
- Fixed an exception and error logging when two different objects are shown and hidden on the same frame (#2524)
- Fixed a memory leak in `UnityTransport` that occurred if `StartClient` failed. (#2518)
- Fixed issue where a client could throw an exception if abruptly disconnected from a network session with one or more spawned `NetworkObject`(s). (#2510)
- Fixed issue where invalid endpoint addresses were not being detected and returning false from NGO UnityTransport. (#2496)
- Fixed some errors that could occur if a connection is lost and the loss is detected when attempting to write to the socket. (#2495)

## Changed

- Adding network prefabs before NetworkManager initialization is now supported. (#2565)
- Connecting clients being synchronized now switch to the server's active scene before spawning and synchronizing NetworkObjects. (#2532)
- Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.4. (#2533)
- Improved performance of NetworkBehaviour initialization by replacing reflection when initializing NetworkVariables with compile-time code generation, which should help reduce hitching during additive scene loads. (#2522)
This commit is contained in:
Unity Technologies
2023-06-07 00:00:00 +00:00
parent b5abc3ff7c
commit 4d70c198bd
119 changed files with 11328 additions and 3164 deletions

View File

@@ -37,10 +37,17 @@ namespace Unity.Netcode.RuntimeTests
return ServerAuthority;
}
public static NetworkTransformTestComponent AuthorityInstance;
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
if (CanCommitToTransform)
{
AuthorityInstance = this;
}
ReadyToReceivePositionUpdate = true;
}
@@ -59,31 +66,38 @@ namespace Unity.Netcode.RuntimeTests
/// <summary>
/// Helper component for NetworkTransform parenting tests
/// </summary>
public class ChildObjectComponent : NetworkBehaviour
public class ChildObjectComponent : NetworkTransform
{
public static readonly List<ChildObjectComponent> Instances = new List<ChildObjectComponent>();
public static ChildObjectComponent ServerInstance { get; internal set; }
public static ChildObjectComponent AuthorityInstance { get; internal set; }
public static readonly Dictionary<ulong, NetworkObject> ClientInstances = new Dictionary<ulong, NetworkObject>();
public static void Reset()
{
ServerInstance = null;
AuthorityInstance = null;
ClientInstances.Clear();
Instances.Clear();
}
public bool ServerAuthority;
protected override bool OnIsServerAuthoritative()
{
return ServerAuthority;
}
public override void OnNetworkSpawn()
{
if (IsServer)
base.OnNetworkSpawn();
if (CanCommitToTransform)
{
ServerInstance = this;
AuthorityInstance = this;
}
else
{
ClientInstances.Add(NetworkManager.LocalClientId, NetworkObject);
Instances.Add(this);
}
Instances.Add(this);
base.OnNetworkSpawn();
ClientInstances.Add(NetworkManager.LocalClientId, NetworkObject);
}
}
@@ -101,7 +115,8 @@ namespace Unity.Netcode.RuntimeTests
{
private NetworkObject m_AuthoritativePlayer;
private NetworkObject m_NonAuthoritativePlayer;
private NetworkObject m_ChildObjectToBeParented;
private NetworkObject m_ChildObject;
private NetworkObject m_ParentObject;
private NetworkTransformTestComponent m_AuthoritativeTransform;
private NetworkTransformTestComponent m_NonAuthoritativeTransform;
@@ -133,6 +148,13 @@ namespace Unity.Netcode.RuntimeTests
Quaternion
}
public enum RotationCompression
{
None,
QuaternionCompress
}
public enum TransformSpace
{
World,
@@ -190,6 +212,7 @@ namespace Unity.Netcode.RuntimeTests
protected override void OnInlineSetup()
{
NetworkTransformTestComponent.AuthorityInstance = null;
m_Precision = Precision.Full;
ChildObjectComponent.Reset();
}
@@ -209,17 +232,22 @@ namespace Unity.Netcode.RuntimeTests
protected override void OnServerAndClientsCreated()
{
var childObject = CreateNetworkObjectPrefab("ChildObject");
childObject.AddComponent<ChildObjectComponent>();
var childNetworkTransform = childObject.AddComponent<NetworkTransform>();
childNetworkTransform.InLocalSpace = true;
m_ChildObjectToBeParented = childObject.GetComponent<NetworkObject>();
var childNetworkTransform = childObject.AddComponent<ChildObjectComponent>();
childNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
m_ChildObject = childObject.GetComponent<NetworkObject>();
var parentObject = CreateNetworkObjectPrefab("ParentObject");
var parentNetworkTransform = parentObject.AddComponent<NetworkTransformTestComponent>();
parentNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
m_ParentObject = parentObject.GetComponent<NetworkObject>();
// Now apply local transform values
m_ChildObjectToBeParented.transform.position = m_ChildObjectLocalPosition;
var childRotation = m_ChildObjectToBeParented.transform.rotation;
m_ChildObject.transform.position = m_ChildObjectLocalPosition;
var childRotation = m_ChildObject.transform.rotation;
childRotation.eulerAngles = m_ChildObjectLocalRotation;
m_ChildObjectToBeParented.transform.rotation = childRotation;
m_ChildObjectToBeParented.transform.localScale = m_ChildObjectLocalScale;
m_ChildObject.transform.rotation = childRotation;
m_ChildObject.transform.localScale = m_ChildObjectLocalScale;
if (m_EnableVerboseDebug)
{
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
@@ -268,7 +296,7 @@ namespace Unity.Netcode.RuntimeTests
/// <returns></returns>
private bool AllChildObjectInstancesAreSpawned()
{
if (ChildObjectComponent.ServerInstance == null)
if (ChildObjectComponent.AuthorityInstance == null)
{
return false;
}
@@ -306,21 +334,34 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
private bool AllInstancesKeptLocalTransformValues()
{
var authorityObjectLocalPosition = m_AuthorityChildObject.transform.localPosition;
var authorityObjectLocalRotation = m_AuthorityChildObject.transform.localRotation.eulerAngles;
var authorityObjectLocalScale = m_AuthorityChildObject.transform.localScale;
foreach (var childInstance in ChildObjectComponent.Instances)
{
var childLocalPosition = childInstance.transform.localPosition;
var childLocalRotation = childInstance.transform.localRotation.eulerAngles;
var childLocalScale = childInstance.transform.localScale;
if (!Approximately(childLocalPosition, m_ChildObjectLocalPosition))
// Adjust approximation based on precision
if (m_Precision == Precision.Half)
{
m_CurrentHalfPrecision = k_HalfPrecisionPosScale;
}
if (!Approximately(childLocalPosition, authorityObjectLocalPosition))
{
return false;
}
if (!ApproximatelyEuler(childLocalRotation, m_ChildObjectLocalRotation))
if (!Approximately(childLocalScale, authorityObjectLocalScale))
{
return false;
}
if (!Approximately(childLocalScale, m_ChildObjectLocalScale))
// Adjust approximation based on precision
if (m_Precision == Precision.Half)
{
m_CurrentHalfPrecision = k_HalfPrecisionRot;
}
if (!ApproximatelyEuler(childLocalRotation, authorityObjectLocalRotation))
{
return false;
}
@@ -333,68 +374,133 @@ namespace Unity.Netcode.RuntimeTests
/// If not, it generates a message containing the axial values that did not match
/// the target/start local space values.
/// </summary>
private void WaitForAllChildrenLocalTransformValuesToMatch()
private void AllChildrenLocalTransformValuesMatch()
{
var success = WaitForConditionOrTimeOutWithTimeTravel(AllInstancesKeptLocalTransformValues);
var infoMessage = string.Empty;
if (s_GlobalTimeoutHelper.TimedOut)
//TimeTravelToNextTick();
var infoMessage = new System.Text.StringBuilder($"Timed out waiting for all children to have the correct local space values:\n");
var authorityObjectLocalPosition = m_AuthorityChildObject.transform.localPosition;
var authorityObjectLocalRotation = m_AuthorityChildObject.transform.localRotation.eulerAngles;
var authorityObjectLocalScale = m_AuthorityChildObject.transform.localScale;
if (s_GlobalTimeoutHelper.TimedOut || !success)
{
foreach (var childInstance in ChildObjectComponent.Instances)
{
var childLocalPosition = childInstance.transform.localPosition;
var childLocalRotation = childInstance.transform.localRotation.eulerAngles;
var childLocalScale = childInstance.transform.localScale;
// Adjust approximation based on precision
if (m_Precision == Precision.Half)
{
m_CurrentHalfPrecision = k_HalfPrecisionPosScale;
}
if (!Approximately(childLocalPosition, authorityObjectLocalPosition))
{
infoMessage.AppendLine($"[{childInstance.name}] Child's Local Position ({childLocalPosition}) | Authority Local Position ({authorityObjectLocalPosition})");
success = false;
}
if (!Approximately(childLocalScale, authorityObjectLocalScale))
{
infoMessage.AppendLine($"[{childInstance.name}] Child's Local Scale ({childLocalScale}) | Authority Local Scale ({authorityObjectLocalScale})");
success = false;
}
if (!Approximately(childLocalPosition, m_ChildObjectLocalPosition))
// Adjust approximation based on precision
if (m_Precision == Precision.Half)
{
infoMessage += $"[{childInstance.name}] Child's Local Position ({childLocalPosition}) | Original Local Position ({m_ChildObjectLocalPosition})\n";
m_CurrentHalfPrecision = k_HalfPrecisionRot;
}
if (!ApproximatelyEuler(childLocalRotation, m_ChildObjectLocalRotation))
if (!ApproximatelyEuler(childLocalRotation, authorityObjectLocalRotation))
{
infoMessage += $"[{childInstance.name}] Child's Local Rotation ({childLocalRotation}) | Original Local Rotation ({m_ChildObjectLocalRotation})\n";
}
if (!Approximately(childLocalScale, m_ChildObjectLocalScale))
{
infoMessage += $"[{childInstance.name}] Child's Local Scale ({childLocalScale}) | Original Local Rotation ({m_ChildObjectLocalScale})\n";
infoMessage.AppendLine($"[{childInstance.name}] Child's Local Rotation ({childLocalRotation}) | Authority Local Rotation ({authorityObjectLocalRotation})");
success = false;
}
}
Assert.True(success, $"Timed out waiting for all children to have the correct local space values:\n {infoMessage}");
if (!success)
{
Assert.True(success, infoMessage.ToString());
}
}
}
private NetworkObject m_AuthorityParentObject;
private NetworkTransformTestComponent m_AuthorityParentNetworkTransform;
private NetworkObject m_AuthorityChildObject;
private ChildObjectComponent m_AuthorityChildNetworkTransform;
/// <summary>
/// Validates that local space transform values remain the same when a NetworkTransform is
/// parented under another NetworkTransform
/// Validates that transform values remain the same when a NetworkTransform is
/// parented under another NetworkTransform under all of the possible axial conditions
/// as well as when the parent has a varying scale.
/// </summary>
[Test]
public void NetworkTransformParentedLocalSpaceTest([Values] Interpolation interpolation)
public void ParentedNetworkTransformTest([Values] Precision precision, [Values] Rotation rotation,
[Values] RotationCompression rotationCompression, [Values] Interpolation interpolation, [Values] bool worldPositionStays,
[Values(0.5f, 1.0f, 5.0f)] float scale)
{
m_AuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
m_NonAuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
var authoritativeChildObject = SpawnObject(m_ChildObjectToBeParented.gameObject, m_AuthoritativeTransform.NetworkManager);
// Set the precision being used for threshold adjustments
m_Precision = precision;
// Assure all of the child object instances are spawned
// Get the NetworkManager that will have authority in order to spawn with the correct authority
var isServerAuthority = m_Authority == Authority.ServerAuthority;
var authorityNetworkManager = m_ServerNetworkManager;
if (!isServerAuthority)
{
authorityNetworkManager = m_ClientNetworkManagers[0];
}
// Spawn a parent and child object
var serverSideParent = SpawnObject(m_ParentObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
var serverSideChild = SpawnObject(m_ChildObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
// Assure all of the child object instances are spawned before proceeding to parenting
var success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesAreSpawned);
Assert.True(success, "Timed out waiting for all child instances to be spawned!");
// Just a sanity check as it should have timed out before this check
Assert.IsNotNull(ChildObjectComponent.ServerInstance, $"The server-side {nameof(ChildObjectComponent)} instance is null!");
// This determines which parent on the server side should be the parent
if (m_AuthoritativeTransform.IsServerAuthoritative())
{
Assert.True(ChildObjectComponent.ServerInstance.NetworkObject.TrySetParent(m_AuthoritativeTransform.transform, false), "[Authoritative] Failed to parent the child object!");
}
else
{
Assert.True(ChildObjectComponent.ServerInstance.NetworkObject.TrySetParent(m_NonAuthoritativeTransform.transform, false), "[Non-Authoritative] Failed to parent the child object!");
}
// Get the authority parent and child instances
m_AuthorityParentObject = NetworkTransformTestComponent.AuthorityInstance.NetworkObject;
m_AuthorityChildObject = ChildObjectComponent.AuthorityInstance.NetworkObject;
// The child NetworkTransform will use world space when world position stays and
// local space when world position does not stay when parenting.
ChildObjectComponent.AuthorityInstance.InLocalSpace = !worldPositionStays;
ChildObjectComponent.AuthorityInstance.UseHalfFloatPrecision = precision == Precision.Half;
ChildObjectComponent.AuthorityInstance.UseQuaternionSynchronization = rotation == Rotation.Quaternion;
ChildObjectComponent.AuthorityInstance.UseQuaternionCompression = rotationCompression == RotationCompression.QuaternionCompress;
// Set whether we are interpolating or not
m_AuthorityParentNetworkTransform = m_AuthorityParentObject.GetComponent<NetworkTransformTestComponent>();
m_AuthorityParentNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
m_AuthorityChildNetworkTransform = m_AuthorityChildObject.GetComponent<ChildObjectComponent>();
m_AuthorityChildNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
// Apply a scale to the parent object to make sure the scale on the child is properly updated on
// non-authority instances.
m_AuthorityParentObject.transform.localScale = new Vector3(scale, scale, scale);
// Allow one tick for authority to update these changes
TimeTravelToNextTick();
// Parent the child under the parent with the current world position stays setting
Assert.True(serverSideChild.TrySetParent(serverSideParent.transform, worldPositionStays), "[Server-Side Child] Failed to set child's parent!");
// This waits for all child instances to be parented
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesHaveChild);
Assert.True(success, "Timed out waiting for all instances to have parented a child!");
// This validates each child instance has preserved their local space values
WaitForAllChildrenLocalTransformValuesToMatch();
AllChildrenLocalTransformValuesMatch();
// Verify that a late joining client will synchronize to the parented NetworkObjects properly
CreateAndStartNewClientWithTimeTravel();
// Assure all of the child object instances are spawned (basically for the newly connected client)
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesAreSpawned);
Assert.True(success, "Timed out waiting for all child instances to be spawned!");
// Assure the newly connected client's child object's transform values are correct
AllChildrenLocalTransformValuesMatch();
}
/// <summary>