Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3bd4727ab | ||
|
|
0581a42b70 |
33
CHANGELOG.md
33
CHANGELOG.md
@@ -6,6 +6,39 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
|
|
||||||
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
|
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
|
||||||
|
|
||||||
|
## [1.6.0] - 2023-08-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added a protected virtual method `NetworkTransform.OnInitialize(ref NetworkTransformState replicatedState)` that just returns the replicated state reference.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed issue where invoking `NetworkManager.Shutdown` within `NetworkManager.OnClientStopped` or `NetworkManager.OnServerStopped` would force `NetworkManager.ShutdownInProgress` to remain true after completing the shutdown process. (#2661)
|
||||||
|
- Fixed issue with client synchronization of position when using half precision and the delta position reaches the maximum value and is collapsed on the host prior to being forwarded to the non-owner clients. (#2636)
|
||||||
|
- Fixed issue with scale not synchronizing properly depending upon the spawn order of NetworkObjects. (#2636)
|
||||||
|
- Fixed issue position was not properly transitioning between ownership changes with an owner authoritative NetworkTransform. (#2636)
|
||||||
|
- Fixed issue where a late joining non-owner client could update an owner authoritative NetworkTransform if ownership changed without any updates to position prior to the non-owner client joining. (#2636)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
## [1.5.2] - 2023-07-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed issue where `NetworkClient.OwnedObjects` was not returning any owned objects due to the `NetworkClient.IsConnected` not being properly set. (#2631)
|
||||||
|
- Fixed a crash when calling TrySetParent with a null Transform (#2625)
|
||||||
|
- Fixed issue where a `NetworkTransform` using full precision state updates was losing transform state updates when interpolation was enabled. (#2624)
|
||||||
|
- Fixed issue where `NetworkObject.SpawnWithObservers` was not being honored for late joining clients. (#2623)
|
||||||
|
- Fixed issue where invoking `NetworkManager.Shutdown` multiple times, depending upon the timing, could cause an exception. (#2622)
|
||||||
|
- Fixed issue where removing ownership would not notify the server that it gained ownership. This also resolves the issue where an owner authoritative NetworkTransform would not properly initialize upon removing ownership from a remote client. (#2618)
|
||||||
|
- Fixed ILPP issues when using CoreCLR and for certain dedicated server builds. (#2614)
|
||||||
|
- Fixed an ILPP compile error when creating a generic NetworkBehaviour singleton with a static T instance. (#2603)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
## [1.5.1] - 2023-06-07
|
## [1.5.1] - 2023-06-07
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Unity.Netcode.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The half float precision value of the z-axis as a <see cref="half"/>.
|
/// The half float precision value of the z-axis as a <see cref="half"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public half Z => Axis.x;
|
public half Z => Axis.z;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to store the half float precision values as a <see cref="half3"/>
|
/// Used to store the half float precision values as a <see cref="half3"/>
|
||||||
@@ -39,6 +39,17 @@ namespace Unity.Netcode.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool3 AxisToSynchronize;
|
public bool3 AxisToSynchronize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Directly sets each axial value to the passed in full precision values
|
||||||
|
/// that are converted to half precision
|
||||||
|
/// </summary>
|
||||||
|
internal void Set(float x, float y, float z)
|
||||||
|
{
|
||||||
|
Axis.x = math.half(x);
|
||||||
|
Axis.y = math.half(y);
|
||||||
|
Axis.z = math.half(z);
|
||||||
|
}
|
||||||
|
|
||||||
private void SerializeWrite(FastBufferWriter writer)
|
private void SerializeWrite(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Length; i++)
|
for (int i = 0; i < Length; i++)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Unity.Collections;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -49,6 +50,11 @@ namespace Unity.Netcode.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public OnClientRequestChangeDelegate OnClientRequestChange;
|
public OnClientRequestChangeDelegate OnClientRequestChange;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When set each state update will contain a state identifier
|
||||||
|
/// </summary>
|
||||||
|
internal static bool TrackByStateId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data structure used to synchronize the <see cref="NetworkTransform"/>
|
/// Data structure used to synchronize the <see cref="NetworkTransform"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -71,9 +77,16 @@ namespace Unity.Netcode.Components
|
|||||||
private const int k_UseHalfFloats = 0x00004000; // Persists between state updates (authority dictates if this is set)
|
private const int k_UseHalfFloats = 0x00004000; // Persists between state updates (authority dictates if this is set)
|
||||||
private const int k_Synchronization = 0x00008000;
|
private const int k_Synchronization = 0x00008000;
|
||||||
private const int k_PositionSlerp = 0x00010000; // Persists between state updates (authority dictates if this is set)
|
private const int k_PositionSlerp = 0x00010000; // Persists between state updates (authority dictates if this is set)
|
||||||
|
private const int k_IsParented = 0x00020000; // When parented and synchronizing, we need to have both lossy and local scale due to varying spawn order
|
||||||
|
private const int k_TrackStateId = 0x10000000; // (Internal Debugging) When set each state update will contain a state identifier
|
||||||
|
|
||||||
// Stores persistent and state relative flags
|
// Stores persistent and state relative flags
|
||||||
private uint m_Bitset;
|
private uint m_Bitset;
|
||||||
|
internal uint BitSet
|
||||||
|
{
|
||||||
|
get { return m_Bitset; }
|
||||||
|
set { m_Bitset = value; }
|
||||||
|
}
|
||||||
|
|
||||||
// Used to store the tick calculated sent time
|
// Used to store the tick calculated sent time
|
||||||
internal double SentTime;
|
internal double SentTime;
|
||||||
@@ -98,6 +111,7 @@ namespace Unity.Netcode.Components
|
|||||||
// Used for half precision scale
|
// Used for half precision scale
|
||||||
internal HalfVector3 HalfVectorScale;
|
internal HalfVector3 HalfVectorScale;
|
||||||
internal Vector3 Scale;
|
internal Vector3 Scale;
|
||||||
|
internal Vector3 LossyScale;
|
||||||
|
|
||||||
// Used for half precision quaternion
|
// Used for half precision quaternion
|
||||||
internal HalfVector4 HalfVectorRotation;
|
internal HalfVector4 HalfVectorRotation;
|
||||||
@@ -118,7 +132,6 @@ namespace Unity.Netcode.Components
|
|||||||
internal int NetworkTick;
|
internal int NetworkTick;
|
||||||
|
|
||||||
// Used when tracking by state ID is enabled
|
// Used when tracking by state ID is enabled
|
||||||
internal bool TrackByStateId;
|
|
||||||
internal int StateId;
|
internal int StateId;
|
||||||
|
|
||||||
// Used during serialization
|
// Used during serialization
|
||||||
@@ -416,6 +429,24 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal bool IsParented
|
||||||
|
{
|
||||||
|
get => GetFlag(k_IsParented);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetFlag(value, k_IsParented);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool TrackByStateId
|
||||||
|
{
|
||||||
|
get => GetFlag(k_TrackStateId);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetFlag(value, k_TrackStateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool GetFlag(int flag)
|
private bool GetFlag(int flag)
|
||||||
{
|
{
|
||||||
@@ -534,6 +565,8 @@ namespace Unity.Netcode.Components
|
|||||||
return NetworkTick;
|
return NetworkTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal HalfVector3 HalfEulerRotation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializes this <see cref="NetworkTransformState"/>
|
/// Serializes this <see cref="NetworkTransformState"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -553,23 +586,6 @@ namespace Unity.Netcode.Components
|
|||||||
positionStart = m_Reader.Position;
|
positionStart = m_Reader.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TrackByStateId)
|
|
||||||
{
|
|
||||||
var stateId = StateId;
|
|
||||||
if (IsSynchronizing)
|
|
||||||
{
|
|
||||||
StateId = -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (serializer.IsWriter)
|
|
||||||
{
|
|
||||||
StateId++;
|
|
||||||
}
|
|
||||||
serializer.SerializeValue(ref StateId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synchronize State Flags and Network Tick
|
// Synchronize State Flags and Network Tick
|
||||||
{
|
{
|
||||||
if (isWriting)
|
if (isWriting)
|
||||||
@@ -589,11 +605,22 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If debugging states and track by state identifier is enabled, serialize the current state identifier
|
||||||
|
if (TrackByStateId)
|
||||||
|
{
|
||||||
|
serializer.SerializeValue(ref StateId);
|
||||||
|
}
|
||||||
|
|
||||||
// Synchronize Position
|
// Synchronize Position
|
||||||
if (HasPositionChange)
|
if (HasPositionChange)
|
||||||
{
|
{
|
||||||
if (UseHalfFloatPrecision)
|
if (UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
|
// Apply which axis should be updated for both write/read (teleporting, synchronizing, or just updating)
|
||||||
|
NetworkDeltaPosition.HalfVector3.AxisToSynchronize[0] = HasPositionX;
|
||||||
|
NetworkDeltaPosition.HalfVector3.AxisToSynchronize[1] = HasPositionY;
|
||||||
|
NetworkDeltaPosition.HalfVector3.AxisToSynchronize[2] = HasPositionZ;
|
||||||
|
|
||||||
if (IsTeleportingNextFrame)
|
if (IsTeleportingNextFrame)
|
||||||
{
|
{
|
||||||
// **Always use full precision when teleporting and UseHalfFloatPrecision is enabled**
|
// **Always use full precision when teleporting and UseHalfFloatPrecision is enabled**
|
||||||
@@ -604,7 +631,7 @@ namespace Unity.Netcode.Components
|
|||||||
serializer.SerializeValue(ref DeltaPosition);
|
serializer.SerializeValue(ref DeltaPosition);
|
||||||
if (!isWriting)
|
if (!isWriting)
|
||||||
{
|
{
|
||||||
NetworkDeltaPosition = new NetworkDeltaPosition(Vector3.zero, 0, math.bool3(HasPositionX, HasPositionY, HasPositionZ));
|
NetworkDeltaPosition.NetworkTick = NetworkTick;
|
||||||
NetworkDeltaPosition.NetworkSerialize(serializer);
|
NetworkDeltaPosition.NetworkSerialize(serializer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -617,7 +644,7 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
if (!isWriting)
|
if (!isWriting)
|
||||||
{
|
{
|
||||||
NetworkDeltaPosition = new NetworkDeltaPosition(Vector3.zero, 0, math.bool3(HasPositionX, HasPositionY, HasPositionZ));
|
NetworkDeltaPosition.NetworkTick = NetworkTick;
|
||||||
NetworkDeltaPosition.NetworkSerialize(serializer);
|
NetworkDeltaPosition.NetworkSerialize(serializer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -626,9 +653,8 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Legacy Position Synchronization
|
else // Full precision axis specific position synchronization
|
||||||
{
|
{
|
||||||
// Position Values
|
|
||||||
if (HasPositionX)
|
if (HasPositionX)
|
||||||
{
|
{
|
||||||
serializer.SerializeValue(ref PositionX);
|
serializer.SerializeValue(ref PositionX);
|
||||||
@@ -703,11 +729,21 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
if (HasRotAngleChange)
|
if (HasRotAngleChange)
|
||||||
{
|
{
|
||||||
var halfPrecisionRotation = new HalfVector3(RotAngleX, RotAngleY, RotAngleZ, math.bool3(HasRotAngleX, HasRotAngleY, HasRotAngleZ));
|
// Apply which axis should be updated for both write/read
|
||||||
serializer.SerializeValue(ref halfPrecisionRotation);
|
HalfEulerRotation.AxisToSynchronize[0] = HasRotAngleX;
|
||||||
|
HalfEulerRotation.AxisToSynchronize[1] = HasRotAngleY;
|
||||||
|
HalfEulerRotation.AxisToSynchronize[2] = HasRotAngleZ;
|
||||||
|
|
||||||
|
if (isWriting)
|
||||||
|
{
|
||||||
|
HalfEulerRotation.Set(RotAngleX, RotAngleY, RotAngleZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializer.SerializeValue(ref HalfEulerRotation);
|
||||||
|
|
||||||
if (!isWriting)
|
if (!isWriting)
|
||||||
{
|
{
|
||||||
var eulerRotation = halfPrecisionRotation.ToVector3();
|
var eulerRotation = HalfEulerRotation.ToVector3();
|
||||||
if (HasRotAngleX)
|
if (HasRotAngleX)
|
||||||
{
|
{
|
||||||
RotAngleX = eulerRotation.x;
|
RotAngleX = eulerRotation.x;
|
||||||
@@ -749,6 +785,12 @@ namespace Unity.Netcode.Components
|
|||||||
// Synchronize Scale
|
// Synchronize Scale
|
||||||
if (HasScaleChange)
|
if (HasScaleChange)
|
||||||
{
|
{
|
||||||
|
// If we are teleporting (which includes synchronizing) and the associated NetworkObject has a parent
|
||||||
|
// then we want to serialize the LossyScale since NetworkObject spawn order is not guaranteed
|
||||||
|
if (IsTeleportingNextFrame && IsParented)
|
||||||
|
{
|
||||||
|
serializer.SerializeValue(ref LossyScale);
|
||||||
|
}
|
||||||
// Half precision scale synchronization
|
// Half precision scale synchronization
|
||||||
if (UseHalfFloatPrecision)
|
if (UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
@@ -758,9 +800,19 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Apply which axis should be updated for both write/read
|
||||||
|
HalfVectorScale.AxisToSynchronize[0] = HasScaleX;
|
||||||
|
HalfVectorScale.AxisToSynchronize[1] = HasScaleY;
|
||||||
|
HalfVectorScale.AxisToSynchronize[2] = HasScaleZ;
|
||||||
|
|
||||||
// For scale, when half precision is enabled we can still only send the axis with deltas
|
// For scale, when half precision is enabled we can still only send the axis with deltas
|
||||||
HalfVectorScale = new HalfVector3(Scale, math.bool3(HasScaleX, HasScaleY, HasScaleZ));
|
if (isWriting)
|
||||||
|
{
|
||||||
|
HalfVectorScale.Set(Scale[0], Scale[1], Scale[2]);
|
||||||
|
}
|
||||||
|
|
||||||
serializer.SerializeValue(ref HalfVectorScale);
|
serializer.SerializeValue(ref HalfVectorScale);
|
||||||
|
|
||||||
if (!isWriting)
|
if (!isWriting)
|
||||||
{
|
{
|
||||||
Scale = HalfVectorScale.ToVector3();
|
Scale = HalfVectorScale.ToVector3();
|
||||||
@@ -1029,26 +1081,6 @@ namespace Unity.Netcode.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected NetworkManager m_CachedNetworkManager; // Note: we no longer use this and are only keeping it until we decide to deprecate it
|
protected NetworkManager m_CachedNetworkManager; // Note: we no longer use this and are only keeping it until we decide to deprecate it
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// We have two internal NetworkVariables.
|
|
||||||
/// One for server authoritative and one for "client/owner" authoritative.
|
|
||||||
/// </summary>
|
|
||||||
private readonly NetworkVariable<NetworkTransformState> m_ReplicatedNetworkStateServer = new NetworkVariable<NetworkTransformState>(new NetworkTransformState(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
|
|
||||||
private readonly NetworkVariable<NetworkTransformState> m_ReplicatedNetworkStateOwner = new NetworkVariable<NetworkTransformState>(new NetworkTransformState(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
|
||||||
|
|
||||||
internal NetworkVariable<NetworkTransformState> ReplicatedNetworkState
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!IsServerAuthoritative())
|
|
||||||
{
|
|
||||||
return m_ReplicatedNetworkStateOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_ReplicatedNetworkStateServer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper method that returns the space relative position of the transform.
|
/// Helper method that returns the space relative position of the transform.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1149,6 +1181,8 @@ namespace Unity.Netcode.Components
|
|||||||
// This represents the most recent local authoritative state.
|
// This represents the most recent local authoritative state.
|
||||||
private NetworkTransformState m_LocalAuthoritativeNetworkState;
|
private NetworkTransformState m_LocalAuthoritativeNetworkState;
|
||||||
|
|
||||||
|
internal NetworkTransformState LocalAuthoritativeNetworkState => m_LocalAuthoritativeNetworkState;
|
||||||
|
|
||||||
private ClientRpcParams m_ClientRpcParams = new ClientRpcParams() { Send = new ClientRpcSendParams() };
|
private ClientRpcParams m_ClientRpcParams = new ClientRpcParams() { Send = new ClientRpcSendParams() };
|
||||||
private List<ulong> m_ClientIds = new List<ulong>() { 0 };
|
private List<ulong> m_ClientIds = new List<ulong>() { 0 };
|
||||||
|
|
||||||
@@ -1159,8 +1193,14 @@ namespace Unity.Netcode.Components
|
|||||||
// Non-Authoritative's current position, scale, and rotation that is used to assure the non-authoritative side cannot make adjustments to
|
// Non-Authoritative's current position, scale, and rotation that is used to assure the non-authoritative side cannot make adjustments to
|
||||||
// the portions of the transform being synchronized.
|
// the portions of the transform being synchronized.
|
||||||
private Vector3 m_CurrentPosition;
|
private Vector3 m_CurrentPosition;
|
||||||
|
private Vector3 m_TargetPosition;
|
||||||
private Vector3 m_CurrentScale;
|
private Vector3 m_CurrentScale;
|
||||||
|
private Vector3 m_TargetScale;
|
||||||
private Quaternion m_CurrentRotation;
|
private Quaternion m_CurrentRotation;
|
||||||
|
private Vector3 m_TargetRotation;
|
||||||
|
|
||||||
|
// Used to for each instance to uniquely identify the named message
|
||||||
|
private string m_MessageName;
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -1255,7 +1295,13 @@ namespace Unity.Netcode.Components
|
|||||||
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
|
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
|
||||||
{
|
{
|
||||||
var targetClientId = m_TargetIdBeingSynchronized;
|
var targetClientId = m_TargetIdBeingSynchronized;
|
||||||
var synchronizationState = new NetworkTransformState();
|
var synchronizationState = new NetworkTransformState()
|
||||||
|
{
|
||||||
|
HalfEulerRotation = new HalfVector3(),
|
||||||
|
HalfVectorRotation = new HalfVector4(),
|
||||||
|
HalfVectorScale = new HalfVector3(),
|
||||||
|
NetworkDeltaPosition = new NetworkDeltaPosition(),
|
||||||
|
};
|
||||||
|
|
||||||
if (serializer.IsWriter)
|
if (serializer.IsWriter)
|
||||||
{
|
{
|
||||||
@@ -1283,6 +1329,7 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
m_LocalAuthoritativeNetworkState = synchronizationState;
|
m_LocalAuthoritativeNetworkState = synchronizationState;
|
||||||
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
|
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
|
||||||
|
m_LocalAuthoritativeNetworkState.IsSynchronizing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1363,11 +1410,11 @@ namespace Unity.Netcode.Components
|
|||||||
// If the transform has deltas (returns dirty) then...
|
// If the transform has deltas (returns dirty) then...
|
||||||
if (ApplyTransformToNetworkStateWithInfo(ref m_LocalAuthoritativeNetworkState, ref transformToCommit, synchronize))
|
if (ApplyTransformToNetworkStateWithInfo(ref m_LocalAuthoritativeNetworkState, ref transformToCommit, synchronize))
|
||||||
{
|
{
|
||||||
m_LocalAuthoritativeNetworkState.LastSerializedSize = ReplicatedNetworkState.Value.LastSerializedSize;
|
m_LocalAuthoritativeNetworkState.LastSerializedSize = m_OldState.LastSerializedSize;
|
||||||
OnAuthorityPushTransformState(ref m_LocalAuthoritativeNetworkState);
|
OnAuthorityPushTransformState(ref m_LocalAuthoritativeNetworkState);
|
||||||
|
|
||||||
// "push"/commit the state
|
// Update the state
|
||||||
ReplicatedNetworkState.Value = m_LocalAuthoritativeNetworkState;
|
UpdateTransformState();
|
||||||
|
|
||||||
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
|
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
|
||||||
}
|
}
|
||||||
@@ -1648,7 +1695,43 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only if we are not synchronizing...
|
// For scale, we need to check for parenting when synchronizing and/or teleporting
|
||||||
|
if (isSynchronization || networkState.IsTeleportingNextFrame)
|
||||||
|
{
|
||||||
|
// This all has to do with complex nested hierarchies and how it impacts scale
|
||||||
|
// when set for the first time and depending upon whether the NetworkObject is parented
|
||||||
|
// (or not parented) at the time the scale values are applied.
|
||||||
|
var hasParentNetworkObject = false;
|
||||||
|
|
||||||
|
// If the NetworkObject belonging to this NetworkTransform instance has a parent
|
||||||
|
// (i.e. this handles nested NetworkTransforms under a parent at some layer above)
|
||||||
|
if (NetworkObject.transform.parent != null)
|
||||||
|
{
|
||||||
|
var parentNetworkObject = NetworkObject.transform.parent.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
|
// In-scene placed NetworkObjects parented under a GameObject with no
|
||||||
|
// NetworkObject preserve their lossyScale when synchronizing.
|
||||||
|
if (parentNetworkObject == null && NetworkObject.IsSceneObject != false)
|
||||||
|
{
|
||||||
|
hasParentNetworkObject = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Or if the relative NetworkObject has a parent NetworkObject
|
||||||
|
hasParentNetworkObject = parentNetworkObject != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
networkState.IsParented = hasParentNetworkObject;
|
||||||
|
// If we are synchronizing and the associated NetworkObject has a parent then we want to send the
|
||||||
|
// LossyScale if the NetworkObject has a parent since NetworkObject spawn order is not guaranteed
|
||||||
|
if (hasParentNetworkObject)
|
||||||
|
{
|
||||||
|
networkState.LossyScale = transform.lossyScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking scale deltas when not synchronizing
|
||||||
if (!isSynchronization)
|
if (!isSynchronization)
|
||||||
{
|
{
|
||||||
if (!UseHalfFloatPrecision)
|
if (!UseHalfFloatPrecision)
|
||||||
@@ -1679,7 +1762,7 @@ namespace Unity.Netcode.Components
|
|||||||
var previousScale = networkState.Scale;
|
var previousScale = networkState.Scale;
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
if (Mathf.Abs(Mathf.DeltaAngle(previousScale[i], scale[i])) >= ScaleThreshold || networkState.IsTeleportingNextFrame)
|
if (Mathf.Abs(scale[i] - previousScale[i]) >= ScaleThreshold || networkState.IsTeleportingNextFrame)
|
||||||
{
|
{
|
||||||
isScaleDirty = true;
|
isScaleDirty = true;
|
||||||
networkState.Scale[i] = scale[i];
|
networkState.Scale[i] = scale[i];
|
||||||
@@ -1688,46 +1771,18 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // If we are synchronizing then we need to determine which scale to use
|
else // Just apply the full local scale when synchronizing
|
||||||
if (SynchronizeScale)
|
if (SynchronizeScale)
|
||||||
{
|
{
|
||||||
// This all has to do with complex nested hierarchies and how it impacts scale
|
|
||||||
// when set for the first time.
|
|
||||||
var hasParentNetworkObject = false;
|
|
||||||
|
|
||||||
// If the NetworkObject belonging to this NetworkTransform instance has a parent
|
|
||||||
// (i.e. this handles nested NetworkTransforms under a parent at some layer above)
|
|
||||||
if (NetworkObject.transform.parent != null)
|
|
||||||
{
|
|
||||||
var parentNetworkObject = NetworkObject.transform.parent.GetComponent<NetworkObject>();
|
|
||||||
|
|
||||||
// In-scene placed NetworkObjects parented under a GameObject with no
|
|
||||||
// NetworkObject preserve their lossyScale when synchronizing.
|
|
||||||
if (parentNetworkObject == null && NetworkObject.IsSceneObject != false)
|
|
||||||
{
|
|
||||||
hasParentNetworkObject = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Or if the relative NetworkObject has a parent NetworkObject
|
|
||||||
hasParentNetworkObject = parentNetworkObject != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If world position stays is set and the relative NetworkObject is parented under a NetworkObject
|
|
||||||
// then we want to use the lossy scale for the initial synchronization.
|
|
||||||
var useLossy = NetworkObject.WorldPositionStays() && hasParentNetworkObject;
|
|
||||||
var scaleToUse = useLossy ? transform.lossyScale : transform.localScale;
|
|
||||||
|
|
||||||
if (!UseHalfFloatPrecision)
|
if (!UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
networkState.ScaleX = scaleToUse.x;
|
networkState.ScaleX = transform.localScale.x;
|
||||||
networkState.ScaleY = scaleToUse.y;
|
networkState.ScaleY = transform.localScale.y;
|
||||||
networkState.ScaleZ = scaleToUse.z;
|
networkState.ScaleZ = transform.localScale.z;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
networkState.Scale = scaleToUse;
|
networkState.Scale = transform.localScale;
|
||||||
}
|
}
|
||||||
networkState.HasScaleX = true;
|
networkState.HasScaleX = true;
|
||||||
networkState.HasScaleY = true;
|
networkState.HasScaleY = true;
|
||||||
@@ -2009,6 +2064,7 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_CurrentPosition = currentPosition;
|
m_CurrentPosition = currentPosition;
|
||||||
|
m_TargetPosition = currentPosition;
|
||||||
|
|
||||||
// Apply the position
|
// Apply the position
|
||||||
if (newState.InLocalSpace)
|
if (newState.InLocalSpace)
|
||||||
@@ -2023,32 +2079,45 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
if (newState.HasScaleChange)
|
if (newState.HasScaleChange)
|
||||||
{
|
{
|
||||||
|
bool shouldUseLossy = false;
|
||||||
|
if (newState.IsParented)
|
||||||
|
{
|
||||||
|
if (transform.parent == null)
|
||||||
|
{
|
||||||
|
shouldUseLossy = NetworkObject.WorldPositionStays();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shouldUseLossy = !NetworkObject.WorldPositionStays();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (UseHalfFloatPrecision)
|
if (UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
currentScale = newState.Scale;
|
currentScale = shouldUseLossy ? newState.LossyScale : newState.Scale;
|
||||||
m_CurrentScale = currentScale;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Adjust based on which axis changed
|
// Adjust based on which axis changed
|
||||||
if (newState.HasScaleX)
|
if (newState.HasScaleX)
|
||||||
{
|
{
|
||||||
currentScale.x = newState.ScaleX;
|
currentScale.x = shouldUseLossy ? newState.LossyScale.x : newState.ScaleX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newState.HasScaleY)
|
if (newState.HasScaleY)
|
||||||
{
|
{
|
||||||
currentScale.y = newState.ScaleY;
|
currentScale.y = shouldUseLossy ? newState.LossyScale.y : newState.ScaleY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newState.HasScaleZ)
|
if (newState.HasScaleZ)
|
||||||
{
|
{
|
||||||
currentScale.z = newState.ScaleZ;
|
currentScale.z = shouldUseLossy ? newState.LossyScale.z : newState.ScaleZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_CurrentScale = currentScale;
|
m_CurrentScale = currentScale;
|
||||||
|
m_TargetScale = currentScale;
|
||||||
m_ScaleInterpolator.ResetTo(currentScale, sentTime);
|
m_ScaleInterpolator.ResetTo(currentScale, sentTime);
|
||||||
|
|
||||||
// Apply the adjusted scale
|
// Apply the adjusted scale
|
||||||
@@ -2082,6 +2151,7 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_CurrentRotation = currentRotation;
|
m_CurrentRotation = currentRotation;
|
||||||
|
m_TargetRotation = currentRotation.eulerAngles;
|
||||||
m_RotationInterpolator.ResetTo(currentRotation, sentTime);
|
m_RotationInterpolator.ResetTo(currentRotation, sentTime);
|
||||||
|
|
||||||
if (InLocalSpace)
|
if (InLocalSpace)
|
||||||
@@ -2107,7 +2177,7 @@ namespace Unity.Netcode.Components
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Only non-authoritative instances should invoke this
|
/// Only non-authoritative instances should invoke this
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private void UpdateState(NetworkTransformState oldState, NetworkTransformState newState)
|
private void ApplyUpdatedState(NetworkTransformState newState)
|
||||||
{
|
{
|
||||||
// Set the transforms's synchronization modes
|
// Set the transforms's synchronization modes
|
||||||
InLocalSpace = newState.InLocalSpace;
|
InLocalSpace = newState.InLocalSpace;
|
||||||
@@ -2137,8 +2207,9 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
// assure our local NetworkDeltaPosition state is updated
|
// assure our local NetworkDeltaPosition state is updated
|
||||||
m_HalfPositionState.HalfVector3.Axis = m_LocalAuthoritativeNetworkState.NetworkDeltaPosition.HalfVector3.Axis;
|
m_HalfPositionState.HalfVector3.Axis = m_LocalAuthoritativeNetworkState.NetworkDeltaPosition.HalfVector3.Axis;
|
||||||
// and update our current position
|
// and update our target position
|
||||||
m_LocalAuthoritativeNetworkState.CurrentPosition = m_HalfPositionState.ToVector3(newState.NetworkTick);
|
m_TargetPosition = m_HalfPositionState.ToVector3(newState.NetworkTick);
|
||||||
|
m_LocalAuthoritativeNetworkState.CurrentPosition = m_TargetPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Interpolate)
|
if (!Interpolate)
|
||||||
@@ -2149,37 +2220,33 @@ namespace Unity.Netcode.Components
|
|||||||
// Apply axial changes from the new state
|
// Apply axial changes from the new state
|
||||||
// Either apply the delta position target position or the current state's delta position
|
// Either apply the delta position target position or the current state's delta position
|
||||||
// depending upon whether UsePositionDeltaCompression is enabled
|
// depending upon whether UsePositionDeltaCompression is enabled
|
||||||
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasPositionChange)
|
if (m_LocalAuthoritativeNetworkState.HasPositionChange)
|
||||||
{
|
{
|
||||||
if (m_LocalAuthoritativeNetworkState.UseHalfFloatPrecision)
|
if (!m_LocalAuthoritativeNetworkState.UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
UpdatePositionInterpolator(m_LocalAuthoritativeNetworkState.CurrentPosition, sentTime);
|
var newTargetPosition = m_TargetPosition;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var currentPosition = GetSpaceRelativePosition();
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasPositionX)
|
if (m_LocalAuthoritativeNetworkState.HasPositionX)
|
||||||
{
|
{
|
||||||
currentPosition.x = m_LocalAuthoritativeNetworkState.PositionX;
|
newTargetPosition.x = m_LocalAuthoritativeNetworkState.PositionX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasPositionY)
|
if (m_LocalAuthoritativeNetworkState.HasPositionY)
|
||||||
{
|
{
|
||||||
currentPosition.y = m_LocalAuthoritativeNetworkState.PositionY;
|
newTargetPosition.y = m_LocalAuthoritativeNetworkState.PositionY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasPositionZ)
|
if (m_LocalAuthoritativeNetworkState.HasPositionZ)
|
||||||
{
|
{
|
||||||
currentPosition.z = m_LocalAuthoritativeNetworkState.PositionZ;
|
newTargetPosition.z = m_LocalAuthoritativeNetworkState.PositionZ;
|
||||||
}
|
}
|
||||||
UpdatePositionInterpolator(currentPosition, sentTime);
|
m_TargetPosition = newTargetPosition;
|
||||||
}
|
}
|
||||||
|
UpdatePositionInterpolator(m_TargetPosition, sentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasScaleChange)
|
if (m_LocalAuthoritativeNetworkState.HasScaleChange)
|
||||||
{
|
{
|
||||||
var currentScale = transform.localScale;
|
var currentScale = m_TargetScale;
|
||||||
if (UseHalfFloatPrecision)
|
if (UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
@@ -2207,6 +2274,7 @@ namespace Unity.Netcode.Components
|
|||||||
currentScale.z = m_LocalAuthoritativeNetworkState.ScaleZ;
|
currentScale.z = m_LocalAuthoritativeNetworkState.ScaleZ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_TargetScale = currentScale;
|
||||||
m_ScaleInterpolator.AddMeasurement(currentScale, sentTime);
|
m_ScaleInterpolator.AddMeasurement(currentScale, sentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2221,7 +2289,9 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
currentEulerAngles = m_TargetRotation;
|
||||||
// Adjust based on which axis changed
|
// Adjust based on which axis changed
|
||||||
|
// (both half precision and full precision apply Eulers to the RotAngle properties when reading the update)
|
||||||
if (m_LocalAuthoritativeNetworkState.HasRotAngleX)
|
if (m_LocalAuthoritativeNetworkState.HasRotAngleX)
|
||||||
{
|
{
|
||||||
currentEulerAngles.x = m_LocalAuthoritativeNetworkState.RotAngleX;
|
currentEulerAngles.x = m_LocalAuthoritativeNetworkState.RotAngleX;
|
||||||
@@ -2236,6 +2306,7 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
currentEulerAngles.z = m_LocalAuthoritativeNetworkState.RotAngleZ;
|
currentEulerAngles.z = m_LocalAuthoritativeNetworkState.RotAngleZ;
|
||||||
}
|
}
|
||||||
|
m_TargetRotation = currentEulerAngles;
|
||||||
currentRotation.eulerAngles = currentEulerAngles;
|
currentRotation.eulerAngles = currentEulerAngles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2253,6 +2324,8 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NetworkTransformState m_OldState = new NetworkTransformState();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Only non-authoritative instances should invoke this method
|
/// Only non-authoritative instances should invoke this method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2266,8 +2339,8 @@ namespace Unity.Netcode.Components
|
|||||||
// Get the time when this new state was sent
|
// Get the time when this new state was sent
|
||||||
newState.SentTime = new NetworkTime(NetworkManager.NetworkConfig.TickRate, newState.NetworkTick).Time;
|
newState.SentTime = new NetworkTime(NetworkManager.NetworkConfig.TickRate, newState.NetworkTick).Time;
|
||||||
|
|
||||||
// Update the state
|
// Apply the new state
|
||||||
UpdateState(oldState, newState);
|
ApplyUpdatedState(newState);
|
||||||
|
|
||||||
// Provide notifications when the state has been updated
|
// Provide notifications when the state has been updated
|
||||||
OnNetworkTransformStateUpdated(ref oldState, ref newState);
|
OnNetworkTransformStateUpdated(ref oldState, ref newState);
|
||||||
@@ -2350,10 +2423,19 @@ namespace Unity.Netcode.Components
|
|||||||
internal void OnUpdateAuthoritativeState(ref Transform transformSource)
|
internal void OnUpdateAuthoritativeState(ref Transform transformSource)
|
||||||
{
|
{
|
||||||
// If our replicated state is not dirty and our local authority state is dirty, clear it.
|
// If our replicated state is not dirty and our local authority state is dirty, clear it.
|
||||||
if (!ReplicatedNetworkState.IsDirty() && m_LocalAuthoritativeNetworkState.IsDirty && !m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame)
|
if (m_LocalAuthoritativeNetworkState.IsDirty && !m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame)
|
||||||
{
|
{
|
||||||
// Now clear our bitset and prepare for next network tick state update
|
// Now clear our bitset and prepare for next network tick state update
|
||||||
m_LocalAuthoritativeNetworkState.ClearBitSetForNextTick();
|
m_LocalAuthoritativeNetworkState.ClearBitSetForNextTick();
|
||||||
|
if (TrackByStateId)
|
||||||
|
{
|
||||||
|
m_LocalAuthoritativeNetworkState.TrackByStateId = true;
|
||||||
|
m_LocalAuthoritativeNetworkState.StateId++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_LocalAuthoritativeNetworkState.TrackByStateId = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AxisChangedDeltaPositionCheck();
|
AxisChangedDeltaPositionCheck();
|
||||||
@@ -2384,20 +2466,31 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void OnNetworkSpawn()
|
public override void OnNetworkSpawn()
|
||||||
{
|
{
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
// NOTE: Legacy and no longer used (candidates for deprecation)
|
// NOTE: Legacy and no longer used (candidates for deprecation)
|
||||||
m_CachedIsServer = IsServer;
|
m_CachedIsServer = IsServer;
|
||||||
m_CachedNetworkManager = NetworkManager;
|
m_CachedNetworkManager = NetworkManager;
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Register a custom named message specifically for this instance
|
||||||
|
m_MessageName = $"NTU_{NetworkObjectId}_{NetworkBehaviourId}";
|
||||||
|
NetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(m_MessageName, TransformStateUpdate);
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void OnNetworkDespawn()
|
public override void OnNetworkDespawn()
|
||||||
{
|
{
|
||||||
ReplicatedNetworkState.OnValueChanged -= OnNetworkStateChanged;
|
if (!NetworkManager.ShutdownInProgress && NetworkManager.CustomMessagingManager != null)
|
||||||
|
{
|
||||||
|
NetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler(m_MessageName);
|
||||||
|
}
|
||||||
CanCommitToTransform = false;
|
CanCommitToTransform = false;
|
||||||
if (NetworkManager != null && NetworkManager.NetworkTickSystem != null)
|
if (NetworkManager != null && NetworkManager.NetworkTickSystem != null)
|
||||||
{
|
{
|
||||||
@@ -2414,9 +2507,6 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
CanCommitToTransform = false;
|
CanCommitToTransform = false;
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
m_ReplicatedNetworkStateServer.Dispose();
|
|
||||||
m_ReplicatedNetworkStateOwner.Dispose();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -2443,7 +2533,20 @@ namespace Unity.Netcode.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when first spawned and when ownership changes.
|
/// Invoked when first spawned and when ownership changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="replicatedState">the <see cref="NetworkVariable{T}"/> replicated <see cref="NetworkTransformState"/></param>
|
/// <param name="replicatedState">the current <see cref="NetworkTransformState"/> after initializing</param>
|
||||||
|
protected virtual void OnInitialize(ref NetworkTransformState replicatedState)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An owner read and owner write NetworkVariable so it doesn't generate any messages
|
||||||
|
/// </summary>
|
||||||
|
private NetworkVariable<NetworkTransformState> m_InternalStatNetVar = new NetworkVariable<NetworkTransformState>(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Owner);
|
||||||
|
/// <summary>
|
||||||
|
/// This method is only invoked by the owner
|
||||||
|
/// Use: OnInitialize(ref NetworkTransformState replicatedState) to be notified on all instances
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="replicatedState"></param>
|
||||||
protected virtual void OnInitialize(ref NetworkVariable<NetworkTransformState> replicatedState)
|
protected virtual void OnInitialize(ref NetworkVariable<NetworkTransformState> replicatedState)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -2460,7 +2563,6 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
CanCommitToTransform = IsServerAuthoritative() ? IsServer : IsOwner;
|
CanCommitToTransform = IsServerAuthoritative() ? IsServer : IsOwner;
|
||||||
var replicatedState = ReplicatedNetworkState;
|
|
||||||
var currentPosition = GetSpaceRelativePosition();
|
var currentPosition = GetSpaceRelativePosition();
|
||||||
var currentRotation = GetSpaceRelativeRotation();
|
var currentRotation = GetSpaceRelativeRotation();
|
||||||
|
|
||||||
@@ -2480,21 +2582,27 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Sanity check to assure we only subscribe to OnValueChanged once
|
|
||||||
replicatedState.OnValueChanged -= OnNetworkStateChanged;
|
|
||||||
replicatedState.OnValueChanged += OnNetworkStateChanged;
|
|
||||||
|
|
||||||
// Assure we no longer subscribe to the tick event
|
// Assure we no longer subscribe to the tick event
|
||||||
NetworkManager.NetworkTickSystem.Tick -= NetworkTickSystem_Tick;
|
NetworkManager.NetworkTickSystem.Tick -= NetworkTickSystem_Tick;
|
||||||
|
|
||||||
ResetInterpolatedStateToCurrentAuthoritativeState();
|
ResetInterpolatedStateToCurrentAuthoritativeState();
|
||||||
|
|
||||||
m_CurrentPosition = currentPosition;
|
m_CurrentPosition = currentPosition;
|
||||||
|
m_TargetPosition = currentPosition;
|
||||||
m_CurrentScale = transform.localScale;
|
m_CurrentScale = transform.localScale;
|
||||||
|
m_TargetScale = transform.localScale;
|
||||||
m_CurrentRotation = currentRotation;
|
m_CurrentRotation = currentRotation;
|
||||||
|
m_TargetRotation = currentRotation.eulerAngles;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
OnInitialize(ref m_LocalAuthoritativeNetworkState);
|
||||||
|
|
||||||
OnInitialize(ref replicatedState);
|
if (IsOwner)
|
||||||
|
{
|
||||||
|
m_InternalStatNetVar.Value = m_LocalAuthoritativeNetworkState;
|
||||||
|
OnInitialize(ref m_InternalStatNetVar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -2649,7 +2757,7 @@ namespace Unity.Netcode.Components
|
|||||||
var serverTime = NetworkManager.ServerTime;
|
var serverTime = NetworkManager.ServerTime;
|
||||||
var cachedDeltaTime = NetworkManager.RealTimeProvider.DeltaTime;
|
var cachedDeltaTime = NetworkManager.RealTimeProvider.DeltaTime;
|
||||||
var cachedServerTime = serverTime.Time;
|
var cachedServerTime = serverTime.Time;
|
||||||
// TODO: Investigate Further
|
|
||||||
// With owner authoritative mode, non-authority clients can lag behind
|
// With owner authoritative mode, non-authority clients can lag behind
|
||||||
// by more than 1 tick period of time. The current "solution" for now
|
// by more than 1 tick period of time. The current "solution" for now
|
||||||
// is to make their cachedRenderTime run 2 ticks behind.
|
// is to make their cachedRenderTime run 2 ticks behind.
|
||||||
@@ -2677,12 +2785,6 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have not received any additional state updates since the very
|
|
||||||
// initial synchronization, then exit early.
|
|
||||||
if (m_LocalAuthoritativeNetworkState.IsSynchronizing)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Apply the current authoritative state
|
// Apply the current authoritative state
|
||||||
ApplyAuthoritativeState();
|
ApplyAuthoritativeState();
|
||||||
}
|
}
|
||||||
@@ -2725,6 +2827,103 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
return OnIsServerAuthoritative();
|
return OnIsServerAuthoritative();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Receives the <see cref="NetworkTransformState"/> named message updates
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="senderId">authority of the transform</param>
|
||||||
|
/// <param name="messagePayload">serialzied <see cref="NetworkTransformState"/></param>
|
||||||
|
private void TransformStateUpdate(ulong senderId, FastBufferReader messagePayload)
|
||||||
|
{
|
||||||
|
// Forward owner authoritative messages before doing anything else
|
||||||
|
if (IsServer && !OnIsServerAuthoritative())
|
||||||
|
{
|
||||||
|
ForwardStateUpdateMessage(messagePayload);
|
||||||
|
}
|
||||||
|
// Store the previous/old state
|
||||||
|
m_OldState = m_LocalAuthoritativeNetworkState;
|
||||||
|
|
||||||
|
// Deserialize the message
|
||||||
|
messagePayload.ReadNetworkSerializableInPlace(ref m_LocalAuthoritativeNetworkState);
|
||||||
|
|
||||||
|
// Apply the message
|
||||||
|
OnNetworkStateChanged(m_OldState, m_LocalAuthoritativeNetworkState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forwards owner authoritative state updates when received by the server
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messagePayload">the owner state message payload</param>
|
||||||
|
private unsafe void ForwardStateUpdateMessage(FastBufferReader messagePayload)
|
||||||
|
{
|
||||||
|
var currentPosition = messagePayload.Position;
|
||||||
|
var messageSize = messagePayload.Length - currentPosition;
|
||||||
|
var writer = new FastBufferWriter(messageSize, Allocator.Temp);
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteBytesSafe(messagePayload.GetUnsafePtr(), messageSize, currentPosition);
|
||||||
|
|
||||||
|
var clientCount = NetworkManager.ConnectionManager.ConnectedClientsList.Count;
|
||||||
|
for (int i = 0; i < clientCount; i++)
|
||||||
|
{
|
||||||
|
var clientId = NetworkManager.ConnectionManager.ConnectedClientsList[i].ClientId;
|
||||||
|
if (!OnIsServerAuthoritative() && (NetworkManager.ServerClientId == clientId || clientId == OwnerClientId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
NetworkManager.CustomMessagingManager.SendNamedMessage(m_MessageName, clientId, writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messagePayload.Seek(currentPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends <see cref="NetworkTransformState"/> named message updates by the authority of the transform
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateTransformState()
|
||||||
|
{
|
||||||
|
if (NetworkManager.ShutdownInProgress)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isServerAuthoritative = OnIsServerAuthoritative();
|
||||||
|
if (isServerAuthoritative && !IsServer)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Server authoritative {nameof(NetworkTransform)} can only be updated by the server!");
|
||||||
|
}
|
||||||
|
else if (!isServerAuthoritative && !IsServer && !IsOwner)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Owner authoritative {nameof(NetworkTransform)} can only be updated by the owner!");
|
||||||
|
}
|
||||||
|
var customMessageManager = NetworkManager.CustomMessagingManager;
|
||||||
|
|
||||||
|
var writer = new FastBufferWriter(128, Allocator.Temp);
|
||||||
|
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteNetworkSerializable(m_LocalAuthoritativeNetworkState);
|
||||||
|
// Server-host always sends updates to all clients (but itself)
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
var clientCount = NetworkManager.ConnectionManager.ConnectedClientsList.Count;
|
||||||
|
for (int i = 0; i < clientCount; i++)
|
||||||
|
{
|
||||||
|
var clientId = NetworkManager.ConnectionManager.ConnectedClientsList[i].ClientId;
|
||||||
|
if (NetworkManager.ServerClientId == clientId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
customMessageManager.SendNamedMessage(m_MessageName, clientId, writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Clients (owner authoritative) send messages to the server-host
|
||||||
|
customMessageManager.SendNamedMessage(m_MessageName, NetworkManager.ServerClientId, writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface INetworkTransformLogStateEntry
|
internal interface INetworkTransformLogStateEntry
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
public static bool IsSubclassOf(this TypeDefinition typeDefinition, string classTypeFullName)
|
public static bool IsSubclassOf(this TypeDefinition typeDefinition, string classTypeFullName)
|
||||||
{
|
{
|
||||||
if (!typeDefinition.IsClass)
|
if (typeDefinition == null || !typeDefinition.IsClass)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -154,6 +154,10 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
public static bool IsSubclassOf(this TypeReference typeReference, TypeReference baseClass)
|
public static bool IsSubclassOf(this TypeReference typeReference, TypeReference baseClass)
|
||||||
{
|
{
|
||||||
|
if (typeReference == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
var type = typeReference.Resolve();
|
var type = typeReference.Resolve();
|
||||||
if (type?.BaseType == null || type.BaseType.Name == nameof(Object))
|
if (type?.BaseType == null || type.BaseType.Name == nameof(Object))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Mono.Cecil.Cil;
|
|||||||
using Mono.Cecil.Rocks;
|
using Mono.Cecil.Rocks;
|
||||||
using Unity.CompilationPipeline.Common.Diagnostics;
|
using Unity.CompilationPipeline.Common.Diagnostics;
|
||||||
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
||||||
|
using UnityEngine;
|
||||||
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
|
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
|
||||||
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
||||||
|
|
||||||
@@ -101,6 +102,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
private ModuleDefinition m_NetcodeModule;
|
private ModuleDefinition m_NetcodeModule;
|
||||||
private PostProcessorAssemblyResolver m_AssemblyResolver;
|
private PostProcessorAssemblyResolver m_AssemblyResolver;
|
||||||
|
|
||||||
|
private MethodReference m_RuntimeInitializeOnLoadAttribute_Ctor;
|
||||||
|
|
||||||
private MethodReference m_MessageManager_ReceiveMessage_MethodRef;
|
private MethodReference m_MessageManager_ReceiveMessage_MethodRef;
|
||||||
private MethodReference m_MessageManager_CreateMessageAndGetVersion_MethodRef;
|
private MethodReference m_MessageManager_CreateMessageAndGetVersion_MethodRef;
|
||||||
private TypeReference m_MessageManager_MessageWithHandler_TypeRef;
|
private TypeReference m_MessageManager_MessageWithHandler_TypeRef;
|
||||||
@@ -125,6 +128,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
// (i.e., there's no #if UNITY_EDITOR in them that could create invalid IL code)
|
// (i.e., there's no #if UNITY_EDITOR in them that could create invalid IL code)
|
||||||
TypeDefinition typeTypeDef = moduleDefinition.ImportReference(typeof(Type)).Resolve();
|
TypeDefinition typeTypeDef = moduleDefinition.ImportReference(typeof(Type)).Resolve();
|
||||||
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();
|
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();
|
||||||
|
m_RuntimeInitializeOnLoadAttribute_Ctor = moduleDefinition.ImportReference(typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new Type[] { }));
|
||||||
|
|
||||||
TypeDefinition messageHandlerTypeDef = null;
|
TypeDefinition messageHandlerTypeDef = null;
|
||||||
TypeDefinition versionGetterTypeDef = null;
|
TypeDefinition versionGetterTypeDef = null;
|
||||||
@@ -232,25 +236,6 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodDefinition GetOrCreateStaticConstructor(TypeDefinition typeDefinition)
|
|
||||||
{
|
|
||||||
var staticCtorMethodDef = typeDefinition.GetStaticConstructor();
|
|
||||||
if (staticCtorMethodDef == null)
|
|
||||||
{
|
|
||||||
staticCtorMethodDef = new MethodDefinition(
|
|
||||||
".cctor", // Static Constructor (constant-constructor)
|
|
||||||
MethodAttributes.HideBySig |
|
|
||||||
MethodAttributes.SpecialName |
|
|
||||||
MethodAttributes.RTSpecialName |
|
|
||||||
MethodAttributes.Static,
|
|
||||||
typeDefinition.Module.TypeSystem.Void);
|
|
||||||
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
|
||||||
typeDefinition.Methods.Add(staticCtorMethodDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
return staticCtorMethodDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateInstructionsToRegisterType(ILProcessor processor, List<Instruction> instructions, TypeReference type, MethodReference receiveMethod, MethodReference versionMethod)
|
private void CreateInstructionsToRegisterType(ILProcessor processor, List<Instruction> instructions, TypeReference type, MethodReference receiveMethod, MethodReference versionMethod)
|
||||||
{
|
{
|
||||||
// NetworkMessageManager.__network_message_types.Add(new NetworkMessageManager.MessageWithHandler{MessageType=typeof(type), Handler=type.Receive});
|
// NetworkMessageManager.__network_message_types.Add(new NetworkMessageManager.MessageWithHandler{MessageType=typeof(type), Handler=type.Receive});
|
||||||
@@ -295,15 +280,19 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
// https://web.archive.org/web/20100212140402/http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
|
// https://web.archive.org/web/20100212140402/http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
|
||||||
private void CreateModuleInitializer(AssemblyDefinition assembly, List<TypeDefinition> networkMessageTypes)
|
private void CreateModuleInitializer(AssemblyDefinition assembly, List<TypeDefinition> networkMessageTypes)
|
||||||
{
|
{
|
||||||
foreach (var typeDefinition in assembly.MainModule.Types)
|
var typeDefinition = new TypeDefinition("__GEN", "INetworkMessageHelper", TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit, assembly.MainModule.TypeSystem.Object);
|
||||||
{
|
|
||||||
if (typeDefinition.FullName == "<Module>")
|
|
||||||
{
|
|
||||||
var staticCtorMethodDef = GetOrCreateStaticConstructor(typeDefinition);
|
|
||||||
|
|
||||||
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
var staticCtorMethodDef = new MethodDefinition(
|
||||||
|
$"InitializeMessages",
|
||||||
|
MethodAttributes.Assembly |
|
||||||
|
MethodAttributes.Static,
|
||||||
|
assembly.MainModule.TypeSystem.Void);
|
||||||
|
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_RuntimeInitializeOnLoadAttribute_Ctor));
|
||||||
|
typeDefinition.Methods.Add(staticCtorMethodDef);
|
||||||
|
|
||||||
var instructions = new List<Instruction>();
|
var instructions = new List<Instruction>();
|
||||||
|
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
||||||
|
|
||||||
foreach (var type in networkMessageTypes)
|
foreach (var type in networkMessageTypes)
|
||||||
{
|
{
|
||||||
@@ -315,9 +304,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
|
|
||||||
instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
|
instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
|
||||||
break;
|
|
||||||
}
|
assembly.MainModule.Types.Add(typeDefinition);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,25 +109,6 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
|
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodDefinition GetOrCreateStaticConstructor(TypeDefinition typeDefinition)
|
|
||||||
{
|
|
||||||
var staticCtorMethodDef = typeDefinition.GetStaticConstructor();
|
|
||||||
if (staticCtorMethodDef == null)
|
|
||||||
{
|
|
||||||
staticCtorMethodDef = new MethodDefinition(
|
|
||||||
".cctor", // Static Constructor (constant-constructor)
|
|
||||||
MethodAttributes.HideBySig |
|
|
||||||
MethodAttributes.SpecialName |
|
|
||||||
MethodAttributes.RTSpecialName |
|
|
||||||
MethodAttributes.Static,
|
|
||||||
typeDefinition.Module.TypeSystem.Void);
|
|
||||||
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
|
||||||
typeDefinition.Methods.Add(staticCtorMethodDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
return staticCtorMethodDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsMemcpyableType(TypeReference type)
|
private bool IsMemcpyableType(TypeReference type)
|
||||||
{
|
{
|
||||||
foreach (var supportedType in BaseSupportedTypes)
|
foreach (var supportedType in BaseSupportedTypes)
|
||||||
@@ -156,15 +137,20 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly)
|
private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly)
|
||||||
{
|
{
|
||||||
foreach (var typeDefinition in assembly.MainModule.Types)
|
var typeDefinition = new TypeDefinition("__GEN", "NetworkVariableSerializationHelper", TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit, assembly.MainModule.TypeSystem.Object);
|
||||||
{
|
|
||||||
if (typeDefinition.FullName == "<Module>")
|
var staticCtorMethodDef = new MethodDefinition(
|
||||||
{
|
$"InitializeSerialization",
|
||||||
var staticCtorMethodDef = GetOrCreateStaticConstructor(typeDefinition);
|
MethodAttributes.Assembly |
|
||||||
|
MethodAttributes.Static,
|
||||||
|
assembly.MainModule.TypeSystem.Void);
|
||||||
|
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_RuntimeInitializeOnLoadAttribute_Ctor));
|
||||||
|
typeDefinition.Methods.Add(staticCtorMethodDef);
|
||||||
|
|
||||||
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
|
||||||
|
|
||||||
var instructions = new List<Instruction>();
|
var instructions = new List<Instruction>();
|
||||||
|
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
||||||
|
|
||||||
foreach (var type in m_WrappedNetworkVariableTypes)
|
foreach (var type in m_WrappedNetworkVariableTypes)
|
||||||
{
|
{
|
||||||
@@ -325,9 +311,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
|
|
||||||
instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
|
instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
|
||||||
break;
|
|
||||||
}
|
assembly.MainModule.Types.Add(typeDefinition);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModuleDefinition m_MainModule;
|
private ModuleDefinition m_MainModule;
|
||||||
@@ -396,6 +381,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
#endif
|
#endif
|
||||||
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef;
|
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef;
|
||||||
|
|
||||||
|
private MethodReference m_RuntimeInitializeOnLoadAttribute_Ctor;
|
||||||
|
|
||||||
private MethodReference m_ExceptionCtorMethodReference;
|
private MethodReference m_ExceptionCtorMethodReference;
|
||||||
private MethodReference m_List_NetworkVariableBase_Add;
|
private MethodReference m_List_NetworkVariableBase_Add;
|
||||||
|
|
||||||
@@ -509,6 +496,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_RuntimeInitializeOnLoadAttribute_Ctor = moduleDefinition.ImportReference(typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new Type[] { }));
|
||||||
|
|
||||||
TypeDefinition networkManagerTypeDef = null;
|
TypeDefinition networkManagerTypeDef = null;
|
||||||
TypeDefinition networkBehaviourTypeDef = null;
|
TypeDefinition networkBehaviourTypeDef = null;
|
||||||
TypeDefinition networkVariableBaseTypeDef = null;
|
TypeDefinition networkVariableBaseTypeDef = null;
|
||||||
@@ -1200,19 +1189,14 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
if (rpcHandlers.Count > 0 || rpcNames.Count > 0)
|
if (rpcHandlers.Count > 0 || rpcNames.Count > 0)
|
||||||
{
|
{
|
||||||
var staticCtorMethodDef = typeDefinition.GetStaticConstructor();
|
var staticCtorMethodDef = new MethodDefinition(
|
||||||
if (staticCtorMethodDef == null)
|
$"InitializeRPCS_{typeDefinition.Name}",
|
||||||
{
|
MethodAttributes.Assembly |
|
||||||
staticCtorMethodDef = new MethodDefinition(
|
|
||||||
".cctor", // Static Constructor (constant-constructor)
|
|
||||||
MethodAttributes.HideBySig |
|
|
||||||
MethodAttributes.SpecialName |
|
|
||||||
MethodAttributes.RTSpecialName |
|
|
||||||
MethodAttributes.Static,
|
MethodAttributes.Static,
|
||||||
typeDefinition.Module.TypeSystem.Void);
|
typeDefinition.Module.TypeSystem.Void);
|
||||||
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_RuntimeInitializeOnLoadAttribute_Ctor));
|
||||||
typeDefinition.Methods.Add(staticCtorMethodDef);
|
typeDefinition.Methods.Add(staticCtorMethodDef);
|
||||||
}
|
|
||||||
|
|
||||||
var instructions = new List<Instruction>();
|
var instructions = new List<Instruction>();
|
||||||
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
||||||
@@ -1254,7 +1238,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
baseGetTypeNameMethod.ReturnType)
|
baseGetTypeNameMethod.ReturnType)
|
||||||
{
|
{
|
||||||
ImplAttributes = baseGetTypeNameMethod.ImplAttributes,
|
ImplAttributes = baseGetTypeNameMethod.ImplAttributes,
|
||||||
SemanticsAttributes = baseGetTypeNameMethod.SemanticsAttributes
|
SemanticsAttributes = baseGetTypeNameMethod.SemanticsAttributes,
|
||||||
|
IsFamilyOrAssembly = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var processor = newGetTypeNameMethod.Body.GetILProcessor();
|
var processor = newGetTypeNameMethod.Body.GetILProcessor();
|
||||||
@@ -2225,6 +2210,12 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
field = new FieldReference(fieldDefinition.Name, fieldDefinition.FieldType, genericType);
|
field = new FieldReference(fieldDefinition.Name, fieldDefinition.FieldType, genericType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field.FieldType.Resolve() == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!field.FieldType.IsArray && !field.FieldType.Resolve().IsArray && field.FieldType.IsSubclassOf(m_NetworkVariableBase_TypeRef))
|
if (!field.FieldType.IsArray && !field.FieldType.Resolve().IsArray && field.FieldType.IsSubclassOf(m_NetworkVariableBase_TypeRef))
|
||||||
{
|
{
|
||||||
// if({variable} == null) {
|
// if({variable} == null) {
|
||||||
|
|||||||
@@ -98,6 +98,14 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
fieldDefinition.IsPublic = true;
|
fieldDefinition.IsPublic = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var nestedTypeDefinition in typeDefinition.NestedTypes)
|
||||||
|
{
|
||||||
|
if (nestedTypeDefinition.Name == nameof(NetworkManager.RpcReceiveHandler))
|
||||||
|
{
|
||||||
|
nestedTypeDefinition.IsNestedPublic = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
|
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
|
||||||
@@ -114,7 +122,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
{
|
{
|
||||||
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_exec_stage) || fieldDefinition.Name == nameof(NetworkBehaviour.NetworkVariableFields))
|
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_exec_stage) || fieldDefinition.Name == nameof(NetworkBehaviour.NetworkVariableFields))
|
||||||
{
|
{
|
||||||
fieldDefinition.IsFamily = true;
|
fieldDefinition.IsFamilyOrAssembly = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +138,11 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
{
|
{
|
||||||
methodDefinition.IsFamily = true;
|
methodDefinition.IsFamily = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (methodDefinition.Name == nameof(NetworkBehaviour.__getTypeName))
|
||||||
|
{
|
||||||
|
methodDefinition.IsFamilyOrAssembly = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -345,8 +345,9 @@ namespace Unity.Netcode.Editor
|
|||||||
/// <param name="networkObjectRemoved">used internally</param>
|
/// <param name="networkObjectRemoved">used internally</param>
|
||||||
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
|
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
|
||||||
{
|
{
|
||||||
// If there are no NetworkBehaviours or no gameObject, then exit early
|
// If there are no NetworkBehaviours or gameObjects then exit early
|
||||||
if (gameObject == null || (gameObject.GetComponent<NetworkBehaviour>() == null && gameObject.GetComponentInChildren<NetworkBehaviour>() == null))
|
// If we are in play mode and a user is inspecting something then exit early (we don't add NetworkObjects to something when in play mode)
|
||||||
|
if (EditorApplication.isPlaying || gameObject == null || (gameObject.GetComponent<NetworkBehaviour>() == null && gameObject.GetComponentInChildren<NetworkBehaviour>() == null))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,11 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ClientId of the NetworkClient
|
/// The ClientId of the NetworkClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// TODO-2023-Q2: Determine if we want to make this property a public get and internal/private set
|
|
||||||
// There is no reason for a user to want to set this, but this will fail the package-validation-suite
|
|
||||||
public ulong ClientId;
|
public ulong ClientId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The PlayerObject of the Client
|
/// The PlayerObject of the Client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// TODO-2023-Q2: Determine if we want to make this property a public get and internal/private set
|
|
||||||
// There is no reason for a user to want to set this, but this will fail the package-validation-suite
|
|
||||||
public NetworkObject PlayerObject;
|
public NetworkObject PlayerObject;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ namespace Unity.Netcode
|
|||||||
/// - Processing <see cref="NetworkEvent"/>s.
|
/// - Processing <see cref="NetworkEvent"/>s.
|
||||||
/// - Client Disconnection
|
/// - Client Disconnection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// TODO 2023-Q2: Discuss what kind of public API exposure we want for this
|
|
||||||
public sealed class NetworkConnectionManager
|
public sealed class NetworkConnectionManager
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
@@ -628,6 +627,8 @@ namespace Unity.Netcode
|
|||||||
};
|
};
|
||||||
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
|
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
|
||||||
{
|
{
|
||||||
|
// Update the observed spawned NetworkObjects for the newly connected player when scene management is disabled
|
||||||
|
NetworkManager.SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
|
||||||
if (NetworkManager.SpawnManager.SpawnedObjectsList.Count != 0)
|
if (NetworkManager.SpawnManager.SpawnedObjectsList.Count != 0)
|
||||||
{
|
{
|
||||||
message.SpawnedObjectsList = NetworkManager.SpawnManager.SpawnedObjectsList;
|
message.SpawnedObjectsList = NetworkManager.SpawnManager.SpawnedObjectsList;
|
||||||
@@ -651,12 +652,13 @@ namespace Unity.Netcode
|
|||||||
SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
|
SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
|
||||||
message.MessageVersions.Dispose();
|
message.MessageVersions.Dispose();
|
||||||
|
|
||||||
// If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization
|
// If scene management is disabled, then we are done and notify the local host-server the client is connected
|
||||||
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
|
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
|
||||||
{
|
{
|
||||||
|
NetworkManager.ConnectedClients[ownerClientId].IsConnected = true;
|
||||||
InvokeOnClientConnectedCallback(ownerClientId);
|
InvokeOnClientConnectedCallback(ownerClientId);
|
||||||
}
|
}
|
||||||
else
|
else // Otherwise, let NetworkSceneManager handle the initial scene and NetworkObject synchronization
|
||||||
{
|
{
|
||||||
NetworkManager.SceneManager.SynchronizeNetworkObjects(ownerClientId);
|
NetworkManager.SceneManager.SynchronizeNetworkObjects(ownerClientId);
|
||||||
}
|
}
|
||||||
@@ -665,6 +667,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
LocalClient = client;
|
LocalClient = client;
|
||||||
NetworkManager.SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
|
NetworkManager.SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
|
||||||
|
LocalClient.IsConnected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.CreatePlayerObject || (response.PlayerPrefabHash == null && NetworkManager.NetworkConfig.PlayerPrefab == null))
|
if (!response.CreatePlayerObject || (response.PlayerPrefabHash == null && NetworkManager.NetworkConfig.PlayerPrefab == null))
|
||||||
@@ -730,12 +733,10 @@ namespace Unity.Netcode
|
|||||||
internal NetworkClient AddClient(ulong clientId)
|
internal NetworkClient AddClient(ulong clientId)
|
||||||
{
|
{
|
||||||
var networkClient = LocalClient;
|
var networkClient = LocalClient;
|
||||||
if (clientId != NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
networkClient = new NetworkClient();
|
networkClient = new NetworkClient();
|
||||||
networkClient.SetRole(isServer: false, isClient: true, NetworkManager);
|
networkClient.SetRole(clientId == NetworkManager.ServerClientId, isClient: true, NetworkManager);
|
||||||
networkClient.ClientId = clientId;
|
networkClient.ClientId = clientId;
|
||||||
}
|
|
||||||
|
|
||||||
ConnectedClients.Add(clientId, networkClient);
|
ConnectedClients.Add(clientId, networkClient);
|
||||||
ConnectedClientsList.Add(networkClient);
|
ConnectedClientsList.Add(networkClient);
|
||||||
@@ -799,7 +800,6 @@ namespace Unity.Netcode
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Handle changing ownership and prefab handlers
|
// Handle changing ownership and prefab handlers
|
||||||
// TODO-2023: Look into whether in-scene placed NetworkObjects could be destroyed if ownership changes to a client
|
|
||||||
for (int i = clientOwnedObjects.Count - 1; i >= 0; i--)
|
for (int i = clientOwnedObjects.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var ownedObject = clientOwnedObjects[i];
|
var ownedObject = clientOwnedObjects[i];
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ namespace Unity.Netcode
|
|||||||
Server = 1,
|
Server = 1,
|
||||||
Client = 2
|
Client = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NetworkBehaviourILPP will override this in derived classes to return the name of the concrete type
|
// NetworkBehaviourILPP will override this in derived classes to return the name of the concrete type
|
||||||
internal virtual string __getTypeName() => nameof(NetworkBehaviour);
|
internal virtual string __getTypeName() => nameof(NetworkBehaviour);
|
||||||
|
|
||||||
@@ -98,7 +96,6 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
bufferWriter.Dispose();
|
bufferWriter.Dispose();
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
||||||
{
|
{
|
||||||
@@ -230,7 +227,6 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
bufferWriter.Dispose();
|
bufferWriter.Dispose();
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ namespace Unity.Netcode
|
|||||||
m_NetworkManager.NetworkTickSystem.Tick -= NetworkBehaviourUpdater_Tick;
|
m_NetworkManager.NetworkTickSystem.Tick -= NetworkBehaviourUpdater_Tick;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 2023-Q2: Order of operations requires NetworkVariable updates first then showing NetworkObjects
|
// Order of operations requires NetworkVariable updates first then showing NetworkObjects
|
||||||
private void NetworkBehaviourUpdater_Tick()
|
private void NetworkBehaviourUpdater_Tick()
|
||||||
{
|
{
|
||||||
// First update NetworkVariables
|
// First update NetworkVariables
|
||||||
|
|||||||
@@ -59,13 +59,12 @@ namespace Unity.Netcode
|
|||||||
// Metrics update needs to be driven by NetworkConnectionManager's update to assure metrics are dispatched after the send queue is processed.
|
// Metrics update needs to be driven by NetworkConnectionManager's update to assure metrics are dispatched after the send queue is processed.
|
||||||
MetricsManager.UpdateMetrics();
|
MetricsManager.UpdateMetrics();
|
||||||
|
|
||||||
// TODO 2023-Q2: Determine a better way to handle this
|
// TODO: Determine a better way to handle this
|
||||||
NetworkObject.VerifyParentingStatus();
|
NetworkObject.VerifyParentingStatus();
|
||||||
|
|
||||||
// This is "ok" to invoke when not processing messages since it is just cleaning up messages that never got handled within their timeout period.
|
// This is "ok" to invoke when not processing messages since it is just cleaning up messages that never got handled within their timeout period.
|
||||||
DeferredMessageManager.CleanupStaleTriggers();
|
DeferredMessageManager.CleanupStaleTriggers();
|
||||||
|
|
||||||
// TODO 2023-Q2: Determine a better way to handle this
|
|
||||||
if (m_ShuttingDown)
|
if (m_ShuttingDown)
|
||||||
{
|
{
|
||||||
ShutdownInternal();
|
ShutdownInternal();
|
||||||
@@ -587,7 +586,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="size"></param>
|
/// <param name="size"></param>
|
||||||
public int MaximumTransmissionUnitSize
|
public int MaximumTransmissionUnitSize
|
||||||
{
|
{
|
||||||
set => MessageManager.NonFragmentedMessageMaxSize = value;
|
set => MessageManager.NonFragmentedMessageMaxSize = value & ~7; // Round down to nearest word aligned size
|
||||||
get => MessageManager.NonFragmentedMessageMaxSize;
|
get => MessageManager.NonFragmentedMessageMaxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,9 +833,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConnectionManager.LocalClient.SetRole(true, true, this);
|
ConnectionManager.LocalClient.SetRole(true, true, this);
|
||||||
|
|
||||||
Initialize(true);
|
Initialize(true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IsListening = NetworkConfig.NetworkTransport.StartServer();
|
IsListening = NetworkConfig.NetworkTransport.StartServer();
|
||||||
@@ -942,11 +939,17 @@ namespace Unity.Netcode
|
|||||||
if (IsServer || IsClient)
|
if (IsServer || IsClient)
|
||||||
{
|
{
|
||||||
m_ShuttingDown = true;
|
m_ShuttingDown = true;
|
||||||
|
if (MessageManager != null)
|
||||||
|
{
|
||||||
MessageManager.StopProcessing = discardMessageQueue;
|
MessageManager.StopProcessing = discardMessageQueue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkConfig != null && NetworkConfig.NetworkTransport != null)
|
||||||
|
{
|
||||||
NetworkConfig.NetworkTransport.OnTransportEvent -= ConnectionManager.HandleNetworkEvent;
|
NetworkConfig.NetworkTransport.OnTransportEvent -= ConnectionManager.HandleNetworkEvent;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensures that the NetworkManager is cleaned up before OnDestroy is run on NetworkObjects and NetworkBehaviours when unloading a scene with a NetworkManager
|
// Ensures that the NetworkManager is cleaned up before OnDestroy is run on NetworkObjects and NetworkBehaviours when unloading a scene with a NetworkManager
|
||||||
private void OnSceneUnloaded(Scene scene)
|
private void OnSceneUnloaded(Scene scene)
|
||||||
@@ -1010,6 +1013,9 @@ namespace Unity.Netcode
|
|||||||
OnServerStopped?.Invoke(ConnectionManager.LocalClient.IsClient);
|
OnServerStopped?.Invoke(ConnectionManager.LocalClient.IsClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In the event shutdown is invoked within OnClientStopped or OnServerStopped, set it to false again
|
||||||
|
m_ShuttingDown = false;
|
||||||
|
|
||||||
// Reset the client's roles
|
// Reset the client's roles
|
||||||
ConnectionManager.LocalClient.SetRole(false, false);
|
ConnectionManager.LocalClient.SetRole(false, false);
|
||||||
|
|
||||||
@@ -1029,6 +1035,8 @@ namespace Unity.Netcode
|
|||||||
// Ensures that the NetworkManager is cleaned up before OnDestroy is run on NetworkObjects and NetworkBehaviours when quitting the application.
|
// Ensures that the NetworkManager is cleaned up before OnDestroy is run on NetworkObjects and NetworkBehaviours when quitting the application.
|
||||||
private void OnApplicationQuit()
|
private void OnApplicationQuit()
|
||||||
{
|
{
|
||||||
|
// Make sure ShutdownInProgress returns true during this time
|
||||||
|
m_ShuttingDown = true;
|
||||||
OnDestroy();
|
OnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -733,6 +733,12 @@ namespace Unity.Netcode
|
|||||||
/// <returns>Whether or not reparenting was successful.</returns>
|
/// <returns>Whether or not reparenting was successful.</returns>
|
||||||
public bool TrySetParent(Transform parent, bool worldPositionStays = true)
|
public bool TrySetParent(Transform parent, bool worldPositionStays = true)
|
||||||
{
|
{
|
||||||
|
// If we are removing ourself from a parent
|
||||||
|
if (parent == null)
|
||||||
|
{
|
||||||
|
return TrySetParent((NetworkObject)null, worldPositionStays);
|
||||||
|
}
|
||||||
|
|
||||||
var networkObject = parent.GetComponent<NetworkObject>();
|
var networkObject = parent.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
// If the parent doesn't have a NetworkObjet then return false, otherwise continue trying to parent
|
// If the parent doesn't have a NetworkObjet then return false, otherwise continue trying to parent
|
||||||
@@ -1192,7 +1198,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
NetworkLog.LogError($"{nameof(NetworkBehaviour)} index {index} was out of bounds for {name}. NetworkBehaviours must be the same, and in the same order, between server and client.");
|
NetworkLog.LogError($"{nameof(NetworkBehaviour)} index {index} was out of bounds for {name}. NetworkBehaviours must be the same, and in the same order, between server and client.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
var currentKnownChildren = new System.Text.StringBuilder();
|
var currentKnownChildren = new System.Text.StringBuilder();
|
||||||
@@ -1205,7 +1210,6 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
NetworkLog.LogInfo(currentKnownChildren.ToString());
|
NetworkLog.LogInfo(currentKnownChildren.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,12 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
|
if (!networkManager.ShutdownInProgress)
|
||||||
{
|
{
|
||||||
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(Hash, context.SenderId, m_ReceiveData, context.SerializedHeaderSize);
|
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(Hash, context.SenderId, m_ReceiveData, context.SerializedHeaderSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
networkVariable.WriteDelta(writer);
|
networkVariable.WriteDelta(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
||||||
TargetClientId,
|
TargetClientId,
|
||||||
NetworkBehaviour.NetworkObject,
|
NetworkBehaviour.NetworkObject,
|
||||||
@@ -207,7 +206,6 @@ namespace Unity.Netcode
|
|||||||
networkBehaviour.__getTypeName(),
|
networkBehaviour.__getTypeName(),
|
||||||
context.MessageSize);
|
context.MessageSize);
|
||||||
|
|
||||||
|
|
||||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
if (m_ReceivedNetworkVariableData.Position > (readStartPos + varSize))
|
if (m_ReceivedNetworkVariableData.Position > (readStartPos + varSize))
|
||||||
|
|||||||
@@ -72,6 +72,14 @@ namespace Unity.Netcode
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogException(new Exception("Unhandled RPC exception!", ex));
|
Debug.LogException(new Exception("Unhandled RPC exception!", ex));
|
||||||
|
if (networkManager.LogLevel == LogLevel.Developer)
|
||||||
|
{
|
||||||
|
Debug.Log($"RPC Table Contents");
|
||||||
|
foreach (var entry in NetworkManager.__rpc_func_table)
|
||||||
|
{
|
||||||
|
Debug.Log($"{entry.Key} | {entry.Value.Method.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort Magic;
|
public ushort Magic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of messages in the batch.
|
||||||
|
/// </summary>
|
||||||
|
public ushort BatchCount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total number of bytes in the batch.
|
/// Total number of bytes in the batch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -22,9 +27,5 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong BatchHash;
|
public ulong BatchHash;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total number of messages in the batch.
|
|
||||||
/// </summary>
|
|
||||||
public ushort BatchCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace Unity.Netcode
|
|||||||
return m_MessageTypes[t];
|
return m_MessageTypes[t];
|
||||||
}
|
}
|
||||||
|
|
||||||
public const int DefaultNonFragmentedMessageMaxSize = 1300;
|
public const int DefaultNonFragmentedMessageMaxSize = 1300 & ~7; // Round down to nearest word aligned size (1296)
|
||||||
public int NonFragmentedMessageMaxSize = DefaultNonFragmentedMessageMaxSize;
|
public int NonFragmentedMessageMaxSize = DefaultNonFragmentedMessageMaxSize;
|
||||||
public int FragmentedMessageMaxSize = int.MaxValue;
|
public int FragmentedMessageMaxSize = int.MaxValue;
|
||||||
|
|
||||||
@@ -517,16 +517,19 @@ namespace Unity.Netcode
|
|||||||
internal int GetMessageVersion(Type type, ulong clientId, bool forReceive = false)
|
internal int GetMessageVersion(Type type, ulong clientId, bool forReceive = false)
|
||||||
{
|
{
|
||||||
if (!m_PerClientMessageVersions.TryGetValue(clientId, out var versionMap))
|
if (!m_PerClientMessageVersions.TryGetValue(clientId, out var versionMap))
|
||||||
|
{
|
||||||
|
var networkManager = NetworkManager.Singleton;
|
||||||
|
if (networkManager != null && networkManager.LogLevel == LogLevel.Developer)
|
||||||
{
|
{
|
||||||
if (forReceive)
|
if (forReceive)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Trying to receive {type.Name} from client {clientId} which is not in a connected state.");
|
NetworkLog.LogWarning($"Trying to receive {type.Name} from client {clientId} which is not in a connected state.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Trying to send {type.Name} to client {clientId} which is not in a connected state.");
|
NetworkLog.LogWarning($"Trying to send {type.Name} to client {clientId} which is not in a connected state.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -826,11 +829,17 @@ namespace Unity.Netcode
|
|||||||
// Skipping the Verify and sneaking the write mark in because we know it's fine.
|
// Skipping the Verify and sneaking the write mark in because we know it's fine.
|
||||||
queueItem.Writer.Handle->AllowedWriteMark = sizeof(NetworkBatchHeader);
|
queueItem.Writer.Handle->AllowedWriteMark = sizeof(NetworkBatchHeader);
|
||||||
#endif
|
#endif
|
||||||
queueItem.BatchHeader.BatchHash = XXHash.Hash64(queueItem.Writer.GetUnsafePtr() + sizeof(NetworkBatchHeader), queueItem.Writer.Length - sizeof(NetworkBatchHeader));
|
|
||||||
|
|
||||||
queueItem.BatchHeader.BatchSize = queueItem.Writer.Length;
|
|
||||||
|
var alignedLength = (queueItem.Writer.Length + 7) & ~7;
|
||||||
|
queueItem.Writer.TryBeginWrite(alignedLength);
|
||||||
|
|
||||||
|
queueItem.BatchHeader.BatchHash = XXHash.Hash64(queueItem.Writer.GetUnsafePtr() + sizeof(NetworkBatchHeader), alignedLength - sizeof(NetworkBatchHeader));
|
||||||
|
|
||||||
|
queueItem.BatchHeader.BatchSize = alignedLength;
|
||||||
|
|
||||||
queueItem.Writer.WriteValue(queueItem.BatchHeader);
|
queueItem.Writer.WriteValue(queueItem.BatchHeader);
|
||||||
|
queueItem.Writer.Seek(alignedLength);
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -2191,6 +2191,10 @@ namespace Unity.Netcode
|
|||||||
ClientId = clientId
|
ClientId = clientId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// At this point the client is considered fully "connected"
|
||||||
|
NetworkManager.ConnectedClients[clientId].IsConnected = true;
|
||||||
|
|
||||||
|
// All scenes are synchronized, let the server know we are done synchronizing
|
||||||
OnSynchronizeComplete?.Invoke(clientId);
|
OnSynchronizeComplete?.Invoke(clientId);
|
||||||
|
|
||||||
// At this time the client is fully synchronized with all loaded scenes and
|
// At this time the client is fully synchronized with all loaded scenes and
|
||||||
|
|||||||
@@ -113,12 +113,6 @@ namespace Unity.Netcode
|
|||||||
// Remove the previous owner's entry
|
// Remove the previous owner's entry
|
||||||
OwnershipToObjectsTable[previousOwner].Remove(networkObject.NetworkObjectId);
|
OwnershipToObjectsTable[previousOwner].Remove(networkObject.NetworkObjectId);
|
||||||
|
|
||||||
// Server or Host alway invokes the lost ownership notification locally
|
|
||||||
if (NetworkManager.IsServer)
|
|
||||||
{
|
|
||||||
networkObject.InvokeBehaviourOnLostOwnership();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are removing the entry (i.e. despawning or client lost ownership)
|
// If we are removing the entry (i.e. despawning or client lost ownership)
|
||||||
if (isRemoving)
|
if (isRemoving)
|
||||||
{
|
{
|
||||||
@@ -143,12 +137,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
// Add the new ownership entry
|
// Add the new ownership entry
|
||||||
OwnershipToObjectsTable[newOwner].Add(networkObject.NetworkObjectId, networkObject);
|
OwnershipToObjectsTable[newOwner].Add(networkObject.NetworkObjectId, networkObject);
|
||||||
|
|
||||||
// Server or Host always invokes the gained ownership notification locally
|
|
||||||
if (NetworkManager.IsServer)
|
|
||||||
{
|
|
||||||
networkObject.InvokeBehaviourOnGainedOwnership();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (isRemoving)
|
else if (isRemoving)
|
||||||
{
|
{
|
||||||
@@ -227,43 +215,6 @@ namespace Unity.Netcode
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveOwnership(NetworkObject networkObject)
|
|
||||||
{
|
|
||||||
if (!NetworkManager.IsServer)
|
|
||||||
{
|
|
||||||
throw new NotServerException("Only the server can change ownership");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!networkObject.IsSpawned)
|
|
||||||
{
|
|
||||||
throw new SpawnStateException("Object is not spawned");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we made it here then we are the server and if the server is determined to already be the owner
|
|
||||||
// then ignore the RemoveOwnership invocation.
|
|
||||||
if (networkObject.OwnerClientId == NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.OwnerClientId = NetworkManager.ServerClientId;
|
|
||||||
|
|
||||||
// Server removes the entry and takes over ownership before notifying
|
|
||||||
UpdateOwnershipTable(networkObject, NetworkManager.ServerClientId, true);
|
|
||||||
|
|
||||||
var message = new ChangeOwnershipMessage
|
|
||||||
{
|
|
||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
|
||||||
OwnerClientId = networkObject.OwnerClientId
|
|
||||||
};
|
|
||||||
var size = NetworkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
|
||||||
|
|
||||||
foreach (var client in NetworkManager.ConnectedClients)
|
|
||||||
{
|
|
||||||
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper function to get a network client for a clientId from the NetworkManager.
|
/// Helper function to get a network client for a clientId from the NetworkManager.
|
||||||
/// On the server this will check the <see cref="NetworkManager.ConnectedClients"/> list.
|
/// On the server this will check the <see cref="NetworkManager.ConnectedClients"/> list.
|
||||||
@@ -289,6 +240,11 @@ namespace Unity.Netcode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void RemoveOwnership(NetworkObject networkObject)
|
||||||
|
{
|
||||||
|
ChangeOwnership(networkObject, NetworkManager.ServerClientId);
|
||||||
|
}
|
||||||
|
|
||||||
internal void ChangeOwnership(NetworkObject networkObject, ulong clientId)
|
internal void ChangeOwnership(NetworkObject networkObject, ulong clientId)
|
||||||
{
|
{
|
||||||
if (!NetworkManager.IsServer)
|
if (!NetworkManager.IsServer)
|
||||||
@@ -301,14 +257,21 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is not spawned");
|
throw new SpawnStateException("Object is not spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign the new owner
|
||||||
networkObject.OwnerClientId = clientId;
|
networkObject.OwnerClientId = clientId;
|
||||||
|
|
||||||
|
// Always notify locally on the server when ownership is lost
|
||||||
|
networkObject.InvokeBehaviourOnLostOwnership();
|
||||||
|
|
||||||
networkObject.MarkVariablesDirty(true);
|
networkObject.MarkVariablesDirty(true);
|
||||||
NetworkManager.BehaviourUpdater.AddForUpdate(networkObject);
|
NetworkManager.BehaviourUpdater.AddForUpdate(networkObject);
|
||||||
|
|
||||||
// Server adds entries for all client ownership
|
// Server adds entries for all client ownership
|
||||||
UpdateOwnershipTable(networkObject, networkObject.OwnerClientId);
|
UpdateOwnershipTable(networkObject, networkObject.OwnerClientId);
|
||||||
|
|
||||||
|
// Always notify locally on the server when a new owner is assigned
|
||||||
|
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||||
|
|
||||||
var message = new ChangeOwnershipMessage
|
var message = new ChangeOwnershipMessage
|
||||||
{
|
{
|
||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
NetworkObjectId = networkObject.NetworkObjectId,
|
||||||
@@ -952,27 +915,35 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates all spawned <see cref="NetworkObject.Observers"/> for the specified client
|
/// Updates all spawned <see cref="NetworkObject.Observers"/> for the specified newly connected client
|
||||||
/// Note: if the clientId is the server then it is observable to all spawned <see cref="NetworkObject"/>'s
|
/// Note: if the clientId is the server then it is observable to all spawned <see cref="NetworkObject"/>'s
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method is to only to be used for newly connected clients in order to update the observers list for
|
||||||
|
/// each NetworkObject instance.
|
||||||
|
/// </remarks>
|
||||||
internal void UpdateObservedNetworkObjects(ulong clientId)
|
internal void UpdateObservedNetworkObjects(ulong clientId)
|
||||||
{
|
{
|
||||||
foreach (var sobj in SpawnedObjectsList)
|
foreach (var sobj in SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
|
// If the NetworkObject has no visibility check then prepare to add this client as an observer
|
||||||
if (sobj.CheckObjectVisibility == null)
|
if (sobj.CheckObjectVisibility == null)
|
||||||
{
|
{
|
||||||
if (!sobj.Observers.Contains(clientId))
|
// If the client is not part of the observers and spawn with observers is enabled on this instance or the clientId is the server
|
||||||
|
if (!sobj.Observers.Contains(clientId) && (sobj.SpawnWithObservers || clientId == NetworkManager.ServerClientId))
|
||||||
{
|
{
|
||||||
sobj.Observers.Add(clientId);
|
sobj.Observers.Add(clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// CheckObject visibility overrides SpawnWithObservers under this condition
|
||||||
if (sobj.CheckObjectVisibility(clientId))
|
if (sobj.CheckObjectVisibility(clientId))
|
||||||
{
|
{
|
||||||
sobj.Observers.Add(clientId);
|
sobj.Observers.Add(clientId);
|
||||||
}
|
}
|
||||||
else if (sobj.Observers.Contains(clientId))
|
else // Otherwise, if the observers contains the clientId (shouldn't happen) then remove it since CheckObjectVisibility returned false
|
||||||
|
if (sobj.Observers.Contains(clientId))
|
||||||
{
|
{
|
||||||
sobj.Observers.Remove(clientId);
|
sobj.Observers.Remove(clientId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NetworkTimeSystem
|
public class NetworkTimeSystem
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// TODO 2023-Q2: Not sure if this just needs to go away, but there is nothing that ever replaces this
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This was the original comment when it lived in NetworkManager:
|
/// This was the original comment when it lived in NetworkManager:
|
||||||
/// todo talk with UX/Product, find good default value for this
|
/// todo talk with UX/Product, find good default value for this
|
||||||
|
|||||||
@@ -121,9 +121,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
LogAssert.Expect(LogType.Error, "Invalid network endpoint: 127.0.0.:4242.");
|
LogAssert.Expect(LogType.Error, "Invalid network endpoint: 127.0.0.:4242.");
|
||||||
LogAssert.Expect(LogType.Error, "Network listen address (127.0.0.) is Invalid!");
|
LogAssert.Expect(LogType.Error, "Network listen address (127.0.0.) is Invalid!");
|
||||||
#if UTP_TRANSPORT_2_0_ABOVE
|
|
||||||
LogAssert.Expect(LogType.Error, "Socket creation failed (error Unity.Baselib.LowLevel.Binding+Baselib_ErrorState: Invalid argument (0x01000003) <argument name stripped>");
|
|
||||||
#endif
|
|
||||||
transport.SetConnectionData("127.0.0.1", 4242, "127.0.0.1");
|
transport.SetConnectionData("127.0.0.1", 4242, "127.0.0.1");
|
||||||
Assert.True(transport.StartServer());
|
Assert.True(transport.StartServer());
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
}
|
}
|
||||||
|
|
||||||
Assert.True(observer.Found);
|
Assert.True(observer.Found);
|
||||||
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead, observer.Value);
|
Assert.AreEqual(((FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead) + 7) & ~7, observer.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
@@ -61,8 +61,6 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
writer.Dispose();
|
writer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var nbFrames = 0;
|
var nbFrames = 0;
|
||||||
while (!observer.Found || nbFrames < 10)
|
while (!observer.Found || nbFrames < 10)
|
||||||
{
|
{
|
||||||
@@ -71,7 +69,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
}
|
}
|
||||||
|
|
||||||
Assert.True(observer.Found);
|
Assert.True(observer.Found);
|
||||||
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead, observer.Value);
|
Assert.AreEqual(((FastBufferWriter.GetWriteSize(messageName) + k_MessageOverhead) + 7) & ~7, observer.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TotalBytesObserver : IMetricObserver
|
private class TotalBytesObserver : IMetricObserver
|
||||||
@@ -89,12 +87,22 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
|
|
||||||
public long Value { get; private set; }
|
public long Value { get; private set; }
|
||||||
|
|
||||||
|
private int m_BytesFoundCounter;
|
||||||
|
private long m_TotalBytes;
|
||||||
|
|
||||||
public void Observe(MetricCollection collection)
|
public void Observe(MetricCollection collection)
|
||||||
{
|
{
|
||||||
if (collection.TryGetCounter(m_MetricInfo.Id, out var counter) && counter.Value > 0)
|
if (collection.TryGetCounter(m_MetricInfo.Id, out var counter) && counter.Value > 0)
|
||||||
|
{
|
||||||
|
// Don't assign another observed value once one is already observed
|
||||||
|
if (!Found)
|
||||||
{
|
{
|
||||||
Found = true;
|
Found = true;
|
||||||
Value = counter.Value;
|
Value = counter.Value;
|
||||||
|
m_TotalBytes += ((counter.Value + 7) & ~7);
|
||||||
|
m_BytesFoundCounter++;
|
||||||
|
UnityEngine.Debug.Log($"[{m_BytesFoundCounter}] Bytes Observed {counter.Value} | Total Bytes Observed: {m_TotalBytes}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
// Set the child object to be inactive in the hierarchy
|
// Set the child object to be inactive in the hierarchy
|
||||||
childObject.SetActive(false);
|
childObject.SetActive(false);
|
||||||
|
|
||||||
LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during ownership assignment!");
|
|
||||||
LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during spawn!");
|
LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during spawn!");
|
||||||
|
|
||||||
parentNetworkObject.Spawn();
|
parentNetworkObject.Spawn();
|
||||||
|
|||||||
@@ -61,8 +61,26 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assures the <see cref="ObserverSpawnTests"/> late joining client has all
|
||||||
|
/// NetworkPrefabs required to connect.
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnNewClientCreated(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
|
||||||
|
{
|
||||||
|
if (!networkManager.NetworkConfig.Prefabs.Contains(networkPrefab.Prefab))
|
||||||
|
{
|
||||||
|
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base.OnNewClientCreated(networkManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This test validates <see cref="NetworkObject.SpawnWithObservers"/> property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="observerTestTypes">whether to spawn with or without observers</param>
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator ObserverSpawnTests([Values] ObserverTestTypes observerTestTypes)
|
public IEnumerator ObserverSpawnTests([Values] ObserverTestTypes observerTestTypes)
|
||||||
{
|
{
|
||||||
@@ -92,6 +110,23 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
m_ObserverTestType = ObserverTestTypes.WithObservers;
|
m_ObserverTestType = ObserverTestTypes.WithObservers;
|
||||||
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
|
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
|
||||||
AssertOnTimeout($"{k_WithObserversError} {k_ObserverTestObjName} object!");
|
AssertOnTimeout($"{k_WithObserversError} {k_ObserverTestObjName} object!");
|
||||||
|
|
||||||
|
// Validate that a late joining client does not see the NetworkObject when it spawns
|
||||||
|
yield return CreateAndStartNewClient();
|
||||||
|
|
||||||
|
m_ObserverTestType = ObserverTestTypes.WithoutObservers;
|
||||||
|
// Just give a little time to make sure nothing spawned
|
||||||
|
yield return s_DefaultWaitForTick;
|
||||||
|
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
|
||||||
|
AssertOnTimeout($"{(withoutObservers ? k_WithoutObserversError : k_WithObserversError)} {k_ObserverTestObjName} object!");
|
||||||
|
|
||||||
|
// Now validate that we can make the NetworkObject visible to the newly joined client
|
||||||
|
m_ObserverTestNetworkObject.NetworkShow(m_ClientNetworkManagers[NumberOfClients].LocalClientId);
|
||||||
|
|
||||||
|
// Validate the NetworkObject is visible to all connected clients (including the recently joined client)
|
||||||
|
m_ObserverTestType = ObserverTestTypes.WithObservers;
|
||||||
|
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
|
||||||
|
AssertOnTimeout($"{k_WithObserversError} {k_ObserverTestObjName} object!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
public NetworkObjectOwnershipTests(HostOrServer hostOrServer) : base(hostOrServer) { }
|
public NetworkObjectOwnershipTests(HostOrServer hostOrServer) : base(hostOrServer) { }
|
||||||
|
|
||||||
|
public enum OwnershipChecks
|
||||||
|
{
|
||||||
|
Change,
|
||||||
|
Remove
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnServerAndClientsCreated()
|
protected override void OnServerAndClientsCreated()
|
||||||
{
|
{
|
||||||
m_OwnershipPrefab = CreateNetworkObjectPrefab("OnwershipPrefab");
|
m_OwnershipPrefab = CreateNetworkObjectPrefab("OnwershipPrefab");
|
||||||
@@ -62,7 +68,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator TestOwnershipCallbacks()
|
public IEnumerator TestOwnershipCallbacks([Values] OwnershipChecks ownershipChecks)
|
||||||
{
|
{
|
||||||
m_OwnershipObject = SpawnObject(m_OwnershipPrefab, m_ServerNetworkManager);
|
m_OwnershipObject = SpawnObject(m_OwnershipPrefab, m_ServerNetworkManager);
|
||||||
m_OwnershipNetworkObject = m_OwnershipObject.GetComponent<NetworkObject>();
|
m_OwnershipNetworkObject = m_OwnershipObject.GetComponent<NetworkObject>();
|
||||||
@@ -109,7 +115,17 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
serverComponent.ResetFlags();
|
serverComponent.ResetFlags();
|
||||||
clientComponent.ResetFlags();
|
clientComponent.ResetFlags();
|
||||||
|
|
||||||
|
if (ownershipChecks == OwnershipChecks.Change)
|
||||||
|
{
|
||||||
|
// Validates that when ownership is changed back to the server it will get an OnGainedOwnership notification
|
||||||
serverObject.ChangeOwnership(NetworkManager.ServerClientId);
|
serverObject.ChangeOwnership(NetworkManager.ServerClientId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Validates that when ownership is removed the server gets an OnGainedOwnership notification
|
||||||
|
serverObject.RemoveOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
yield return s_DefaultWaitForTick;
|
yield return s_DefaultWaitForTick;
|
||||||
|
|
||||||
Assert.That(serverComponent.OnGainedOwnershipFired);
|
Assert.That(serverComponent.OnGainedOwnershipFired);
|
||||||
@@ -125,7 +141,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
/// Verifies that switching ownership between several clients works properly
|
/// Verifies that switching ownership between several clients works properly
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator TestOwnershipCallbacksSeveralClients()
|
public IEnumerator TestOwnershipCallbacksSeveralClients([Values] OwnershipChecks ownershipChecks)
|
||||||
{
|
{
|
||||||
// Build our message hook entries tables so we can determine if all clients received spawn or ownership messages
|
// Build our message hook entries tables so we can determine if all clients received spawn or ownership messages
|
||||||
var messageHookEntriesForSpawn = new List<MessageHookEntry>();
|
var messageHookEntriesForSpawn = new List<MessageHookEntry>();
|
||||||
@@ -247,8 +263,17 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
previousClientComponent = currentClientComponent;
|
previousClientComponent = currentClientComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now change ownership back to the server
|
if (ownershipChecks == OwnershipChecks.Change)
|
||||||
|
{
|
||||||
|
// Validates that when ownership is changed back to the server it will get an OnGainedOwnership notification
|
||||||
serverObject.ChangeOwnership(NetworkManager.ServerClientId);
|
serverObject.ChangeOwnership(NetworkManager.ServerClientId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Validates that when ownership is removed the server gets an OnGainedOwnership notification
|
||||||
|
serverObject.RemoveOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
yield return WaitForConditionOrTimeOut(ownershipMessageHooks);
|
yield return WaitForConditionOrTimeOut(ownershipMessageHooks);
|
||||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for all clients to receive the {nameof(ChangeOwnershipMessage)} message (back to server).");
|
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for all clients to receive the {nameof(ChangeOwnershipMessage)} message (back to server).");
|
||||||
|
|
||||||
@@ -269,5 +294,69 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
serverComponent.ResetFlags();
|
serverComponent.ResetFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const int k_NumberOfSpawnedObjects = 5;
|
||||||
|
|
||||||
|
private bool AllClientsHaveCorrectObjectCount()
|
||||||
|
{
|
||||||
|
|
||||||
|
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||||
|
{
|
||||||
|
if (clientNetworkManager.LocalClient.OwnedObjects.Count < k_NumberOfSpawnedObjects)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ServerHasCorrectClientOwnedObjectCount()
|
||||||
|
{
|
||||||
|
// Only check when we are the host
|
||||||
|
if (m_ServerNetworkManager.IsHost)
|
||||||
|
{
|
||||||
|
if (m_ServerNetworkManager.LocalClient.OwnedObjects.Count < k_NumberOfSpawnedObjects)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var connectedClient in m_ServerNetworkManager.ConnectedClients)
|
||||||
|
{
|
||||||
|
if (connectedClient.Value.OwnedObjects.Count < k_NumberOfSpawnedObjects)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TestOwnedObjectCounts()
|
||||||
|
{
|
||||||
|
if (m_ServerNetworkManager.IsHost)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
SpawnObject(m_OwnershipPrefab, m_ServerNetworkManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
SpawnObject(m_OwnershipPrefab, clientNetworkManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return WaitForConditionOrTimeOut(AllClientsHaveCorrectObjectCount);
|
||||||
|
AssertOnTimeout($"Not all clients spawned {k_NumberOfSpawnedObjects} {nameof(NetworkObject)}s!");
|
||||||
|
|
||||||
|
yield return WaitForConditionOrTimeOut(ServerHasCorrectClientOwnedObjectCount);
|
||||||
|
AssertOnTimeout($"Server does not have the correct count for all clients spawned {k_NumberOfSpawnedObjects} {nameof(NetworkObject)}s!");
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,93 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
base.OnServerAndClientsCreated();
|
base.OnServerAndClientsCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clients created during a test need to have their prefabs list updated to
|
||||||
|
/// match the server's prefab list.
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnNewClientCreated(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
|
||||||
|
{
|
||||||
|
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnNewClientCreated(networkManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ClientIsOwner()
|
||||||
|
{
|
||||||
|
var clientId = m_ClientNetworkManagers[0].LocalClientId;
|
||||||
|
if (!VerifyObjectIsSpawnedOnClient.GetClientsThatSpawnedThisPrefab().Contains(clientId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (VerifyObjectIsSpawnedOnClient.GetClientInstance(clientId).OwnerClientId != clientId)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This test verifies a late joining client cannot change the transform when:
|
||||||
|
/// - A NetworkObject is spawned with a host and one or more connected clients
|
||||||
|
/// - The NetworkTransform is owner authoritative and spawned with the host as the owner
|
||||||
|
/// - The host does not change the transform values
|
||||||
|
/// - One of the already connected clients gains ownership of the spawned NetworkObject
|
||||||
|
/// - The new client owner does not change the transform values
|
||||||
|
/// - A new late joining client connects and is synchronized
|
||||||
|
/// - The newly connected late joining client tries to change the transform of the NetworkObject
|
||||||
|
/// it does not own
|
||||||
|
/// </summary>
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator LateJoinedNonOwnerClientCannotChangeTransform()
|
||||||
|
{
|
||||||
|
// Spawn the m_ClientNetworkTransformPrefab with the host starting as the owner
|
||||||
|
var hostInstance = SpawnObject(m_ClientNetworkTransformPrefab, m_ServerNetworkManager);
|
||||||
|
|
||||||
|
// Wait for the client to spawn it
|
||||||
|
yield return WaitForConditionOrTimeOut(() => VerifyObjectIsSpawnedOnClient.GetClientsThatSpawnedThisPrefab().Contains(m_ClientNetworkManagers[0].LocalClientId));
|
||||||
|
|
||||||
|
// Change the ownership to the connectd client
|
||||||
|
hostInstance.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
||||||
|
|
||||||
|
// Wait until the client gains ownership
|
||||||
|
yield return WaitForConditionOrTimeOut(ClientIsOwner);
|
||||||
|
|
||||||
|
// Spawn a new client
|
||||||
|
yield return CreateAndStartNewClient();
|
||||||
|
|
||||||
|
// Get the instance of the object relative to the newly joined client
|
||||||
|
var newClientObjectInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(m_ClientNetworkManagers[1].LocalClientId);
|
||||||
|
|
||||||
|
// Attempt to change the transform values
|
||||||
|
var currentPosition = newClientObjectInstance.transform.position;
|
||||||
|
newClientObjectInstance.transform.position = GetRandomVector3(0.5f, 10.0f);
|
||||||
|
var rotation = newClientObjectInstance.transform.rotation;
|
||||||
|
var currentRotation = rotation.eulerAngles;
|
||||||
|
rotation.eulerAngles = GetRandomVector3(1.0f, 180.0f);
|
||||||
|
var currentScale = newClientObjectInstance.transform.localScale;
|
||||||
|
newClientObjectInstance.transform.localScale = GetRandomVector3(0.25f, 4.0f);
|
||||||
|
|
||||||
|
// Wait one frame so the NetworkTransform can apply the owner's last state received on the late joining client side
|
||||||
|
// (i.e. prevent the non-owner from changing the transform)
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
// Get the owner instance
|
||||||
|
var ownerInstance = VerifyObjectIsSpawnedOnClient.GetClientInstance(m_ClientNetworkManagers[0].LocalClientId);
|
||||||
|
|
||||||
|
// Verify that the non-owner instance transform values are the same before they were changed last frame
|
||||||
|
Assert.True(Approximately(currentPosition, newClientObjectInstance.transform.position), $"Non-owner instance was able to change the position!");
|
||||||
|
Assert.True(Approximately(currentRotation, newClientObjectInstance.transform.rotation.eulerAngles), $"Non-owner instance was able to change the rotation!");
|
||||||
|
Assert.True(Approximately(currentScale, newClientObjectInstance.transform.localScale), $"Non-owner instance was able to change the scale!");
|
||||||
|
|
||||||
|
// Verify that the non-owner instance transform is still the same as the owner instance transform
|
||||||
|
Assert.True(Approximately(ownerInstance.transform.position, newClientObjectInstance.transform.position), "Non-owner and owner instance position values are not the same!");
|
||||||
|
Assert.True(Approximately(ownerInstance.transform.rotation.eulerAngles, newClientObjectInstance.transform.rotation.eulerAngles), "Non-owner and owner instance rotation values are not the same!");
|
||||||
|
Assert.True(Approximately(ownerInstance.transform.localScale, newClientObjectInstance.transform.localScale), "Non-owner and owner instance scale values are not the same!");
|
||||||
|
}
|
||||||
|
|
||||||
public enum StartingOwnership
|
public enum StartingOwnership
|
||||||
{
|
{
|
||||||
HostStartsAsOwner,
|
HostStartsAsOwner,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using NUnit.Framework;
|
|||||||
using Unity.Netcode.Components;
|
using Unity.Netcode.Components;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
namespace Unity.Netcode.RuntimeTests
|
namespace Unity.Netcode.RuntimeTests
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -89,6 +90,125 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
networkTransform.SyncPositionX || networkTransform.SyncPositionY || networkTransform.SyncPositionZ;
|
networkTransform.SyncPositionX || networkTransform.SyncPositionY || networkTransform.SyncPositionZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void NetworkTransformStateFlags()
|
||||||
|
{
|
||||||
|
var indexValues = new System.Collections.Generic.List<uint>();
|
||||||
|
var currentFlag = (uint)0x00000001;
|
||||||
|
for (int j = 0; j < 18; j++)
|
||||||
|
{
|
||||||
|
indexValues.Add(currentFlag);
|
||||||
|
currentFlag = currentFlag << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackByStateId is unique
|
||||||
|
indexValues.Add(0x10000000);
|
||||||
|
|
||||||
|
var boolSet = new System.Collections.Generic.List<bool>();
|
||||||
|
var transformState = new NetworkTransform.NetworkTransformState();
|
||||||
|
// Test setting one at a time.
|
||||||
|
for (int j = 0; j < 19; j++)
|
||||||
|
{
|
||||||
|
boolSet = new System.Collections.Generic.List<bool>();
|
||||||
|
for (int i = 0; i < 19; i++)
|
||||||
|
{
|
||||||
|
if (i == j)
|
||||||
|
{
|
||||||
|
boolSet.Add(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boolSet.Add(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transformState = new NetworkTransform.NetworkTransformState()
|
||||||
|
{
|
||||||
|
InLocalSpace = boolSet[0],
|
||||||
|
HasPositionX = boolSet[1],
|
||||||
|
HasPositionY = boolSet[2],
|
||||||
|
HasPositionZ = boolSet[3],
|
||||||
|
HasRotAngleX = boolSet[4],
|
||||||
|
HasRotAngleY = boolSet[5],
|
||||||
|
HasRotAngleZ = boolSet[6],
|
||||||
|
HasScaleX = boolSet[7],
|
||||||
|
HasScaleY = boolSet[8],
|
||||||
|
HasScaleZ = boolSet[9],
|
||||||
|
IsTeleportingNextFrame = boolSet[10],
|
||||||
|
UseInterpolation = boolSet[11],
|
||||||
|
QuaternionSync = boolSet[12],
|
||||||
|
QuaternionCompression = boolSet[13],
|
||||||
|
UseHalfFloatPrecision = boolSet[14],
|
||||||
|
IsSynchronizing = boolSet[15],
|
||||||
|
UsePositionSlerp = boolSet[16],
|
||||||
|
IsParented = boolSet[17],
|
||||||
|
TrackByStateId = boolSet[18],
|
||||||
|
};
|
||||||
|
Assert.True((transformState.BitSet & indexValues[j]) == indexValues[j], $"[FlagTest][Individual] Set flag value {indexValues[j]} at index {j}, but BitSet value did not match!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test setting all flag values
|
||||||
|
boolSet = new System.Collections.Generic.List<bool>();
|
||||||
|
for (int i = 0; i < 19; i++)
|
||||||
|
{
|
||||||
|
boolSet.Add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
transformState = new NetworkTransform.NetworkTransformState()
|
||||||
|
{
|
||||||
|
InLocalSpace = boolSet[0],
|
||||||
|
HasPositionX = boolSet[1],
|
||||||
|
HasPositionY = boolSet[2],
|
||||||
|
HasPositionZ = boolSet[3],
|
||||||
|
HasRotAngleX = boolSet[4],
|
||||||
|
HasRotAngleY = boolSet[5],
|
||||||
|
HasRotAngleZ = boolSet[6],
|
||||||
|
HasScaleX = boolSet[7],
|
||||||
|
HasScaleY = boolSet[8],
|
||||||
|
HasScaleZ = boolSet[9],
|
||||||
|
IsTeleportingNextFrame = boolSet[10],
|
||||||
|
UseInterpolation = boolSet[11],
|
||||||
|
QuaternionSync = boolSet[12],
|
||||||
|
QuaternionCompression = boolSet[13],
|
||||||
|
UseHalfFloatPrecision = boolSet[14],
|
||||||
|
IsSynchronizing = boolSet[15],
|
||||||
|
UsePositionSlerp = boolSet[16],
|
||||||
|
IsParented = boolSet[17],
|
||||||
|
TrackByStateId = boolSet[18],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int j = 0; j < 19; j++)
|
||||||
|
{
|
||||||
|
Assert.True((transformState.BitSet & indexValues[j]) == indexValues[j], $"[FlagTest][All] All flag values are set but failed to detect flag value {indexValues[j]}!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test getting all flag values
|
||||||
|
transformState = new NetworkTransform.NetworkTransformState();
|
||||||
|
for (int i = 0; i < 19; i++)
|
||||||
|
{
|
||||||
|
transformState.BitSet |= indexValues[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.True(transformState.InLocalSpace, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.InLocalSpace)}!");
|
||||||
|
Assert.True(transformState.HasPositionX, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasPositionX)}!");
|
||||||
|
Assert.True(transformState.HasPositionY, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasPositionY)}!");
|
||||||
|
Assert.True(transformState.HasPositionZ, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasPositionZ)}!");
|
||||||
|
Assert.True(transformState.HasRotAngleX, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasRotAngleX)}!");
|
||||||
|
Assert.True(transformState.HasRotAngleY, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasRotAngleY)}!");
|
||||||
|
Assert.True(transformState.HasRotAngleZ, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasRotAngleZ)}!");
|
||||||
|
Assert.True(transformState.HasScaleX, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasScaleX)}!");
|
||||||
|
Assert.True(transformState.HasScaleY, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasScaleY)}!");
|
||||||
|
Assert.True(transformState.HasScaleZ, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.HasScaleZ)}!");
|
||||||
|
Assert.True(transformState.IsTeleportingNextFrame, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.IsTeleportingNextFrame)}!");
|
||||||
|
Assert.True(transformState.UseInterpolation, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.UseInterpolation)}!");
|
||||||
|
Assert.True(transformState.QuaternionSync, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.QuaternionSync)}!");
|
||||||
|
Assert.True(transformState.QuaternionCompression, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.QuaternionCompression)}!");
|
||||||
|
Assert.True(transformState.UseHalfFloatPrecision, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.UseHalfFloatPrecision)}!");
|
||||||
|
Assert.True(transformState.IsSynchronizing, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.IsSynchronizing)}!");
|
||||||
|
Assert.True(transformState.UsePositionSlerp, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.UsePositionSlerp)}!");
|
||||||
|
Assert.True(transformState.IsParented, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.IsParented)}!");
|
||||||
|
Assert.True(transformState.TrackByStateId, $"[FlagTest][Get] Failed to detect {nameof(NetworkTransform.NetworkTransformState.TrackByStateId)}!");
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Values] SyncAxis syncAxis)
|
public void TestSyncAxes([Values] SynchronizationType synchronizationType, [Values] SyncAxis syncAxis)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Unity.Netcode.Components;
|
using Unity.Netcode.Components;
|
||||||
using Unity.Netcode.TestHelpers.Runtime;
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
@@ -63,24 +64,50 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper component for NetworkTransform parenting tests when
|
||||||
|
/// a child is a parent of another child (i.e. "sub child")
|
||||||
|
/// </summary>
|
||||||
|
public class SubChildObjectComponent : ChildObjectComponent
|
||||||
|
{
|
||||||
|
protected override bool IsSubChild()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper component for NetworkTransform parenting tests
|
/// Helper component for NetworkTransform parenting tests
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChildObjectComponent : NetworkTransform
|
public class ChildObjectComponent : NetworkTransform
|
||||||
{
|
{
|
||||||
public static readonly List<ChildObjectComponent> Instances = new List<ChildObjectComponent>();
|
public static readonly List<ChildObjectComponent> Instances = new List<ChildObjectComponent>();
|
||||||
|
public static readonly List<ChildObjectComponent> SubInstances = new List<ChildObjectComponent>();
|
||||||
public static ChildObjectComponent AuthorityInstance { get; internal set; }
|
public static ChildObjectComponent AuthorityInstance { get; internal set; }
|
||||||
|
public static ChildObjectComponent AuthoritySubInstance { get; internal set; }
|
||||||
public static readonly Dictionary<ulong, NetworkObject> ClientInstances = new Dictionary<ulong, NetworkObject>();
|
public static readonly Dictionary<ulong, NetworkObject> ClientInstances = new Dictionary<ulong, NetworkObject>();
|
||||||
|
public static readonly Dictionary<ulong, NetworkObject> ClientSubChildInstances = new Dictionary<ulong, NetworkObject>();
|
||||||
|
|
||||||
|
public static bool HasSubChild;
|
||||||
|
|
||||||
public static void Reset()
|
public static void Reset()
|
||||||
{
|
{
|
||||||
AuthorityInstance = null;
|
AuthorityInstance = null;
|
||||||
|
AuthoritySubInstance = null;
|
||||||
|
HasSubChild = false;
|
||||||
ClientInstances.Clear();
|
ClientInstances.Clear();
|
||||||
|
ClientSubChildInstances.Clear();
|
||||||
Instances.Clear();
|
Instances.Clear();
|
||||||
|
SubInstances.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ServerAuthority;
|
public bool ServerAuthority;
|
||||||
|
|
||||||
|
protected virtual bool IsSubChild()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnIsServerAuthoritative()
|
protected override bool OnIsServerAuthoritative()
|
||||||
{
|
{
|
||||||
return ServerAuthority;
|
return ServerAuthority;
|
||||||
@@ -90,16 +117,37 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
{
|
{
|
||||||
base.OnNetworkSpawn();
|
base.OnNetworkSpawn();
|
||||||
if (CanCommitToTransform)
|
if (CanCommitToTransform)
|
||||||
|
{
|
||||||
|
if (!IsSubChild())
|
||||||
{
|
{
|
||||||
AuthorityInstance = this;
|
AuthorityInstance = this;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
AuthoritySubInstance = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!IsSubChild())
|
||||||
{
|
{
|
||||||
Instances.Add(this);
|
Instances.Add(this);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SubInstances.Add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasSubChild && IsSubChild())
|
||||||
|
{
|
||||||
|
ClientSubChildInstances.Add(NetworkManager.LocalClientId, NetworkObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ClientInstances.Add(NetworkManager.LocalClientId, NetworkObject);
|
ClientInstances.Add(NetworkManager.LocalClientId, NetworkObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Integration tests for NetworkTransform that will test both
|
/// Integration tests for NetworkTransform that will test both
|
||||||
@@ -116,6 +164,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
private NetworkObject m_AuthoritativePlayer;
|
private NetworkObject m_AuthoritativePlayer;
|
||||||
private NetworkObject m_NonAuthoritativePlayer;
|
private NetworkObject m_NonAuthoritativePlayer;
|
||||||
private NetworkObject m_ChildObject;
|
private NetworkObject m_ChildObject;
|
||||||
|
private NetworkObject m_SubChildObject;
|
||||||
private NetworkObject m_ParentObject;
|
private NetworkObject m_ParentObject;
|
||||||
|
|
||||||
private NetworkTransformTestComponent m_AuthoritativeTransform;
|
private NetworkTransformTestComponent m_AuthoritativeTransform;
|
||||||
@@ -231,6 +280,11 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
protected override void OnServerAndClientsCreated()
|
protected override void OnServerAndClientsCreated()
|
||||||
{
|
{
|
||||||
|
var subChildObject = CreateNetworkObjectPrefab("SubChildObject");
|
||||||
|
var subChildNetworkTransform = subChildObject.AddComponent<SubChildObjectComponent>();
|
||||||
|
subChildNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
|
||||||
|
m_SubChildObject = subChildObject.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
var childObject = CreateNetworkObjectPrefab("ChildObject");
|
var childObject = CreateNetworkObjectPrefab("ChildObject");
|
||||||
var childNetworkTransform = childObject.AddComponent<ChildObjectComponent>();
|
var childNetworkTransform = childObject.AddComponent<ChildObjectComponent>();
|
||||||
childNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
|
childNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
|
||||||
@@ -241,13 +295,19 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
parentNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
|
parentNetworkTransform.ServerAuthority = m_Authority == Authority.ServerAuthority;
|
||||||
m_ParentObject = parentObject.GetComponent<NetworkObject>();
|
m_ParentObject = parentObject.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
|
|
||||||
// Now apply local transform values
|
// Now apply local transform values
|
||||||
m_ChildObject.transform.position = m_ChildObjectLocalPosition;
|
m_ChildObject.transform.position = m_ChildObjectLocalPosition;
|
||||||
var childRotation = m_ChildObject.transform.rotation;
|
var childRotation = m_ChildObject.transform.rotation;
|
||||||
childRotation.eulerAngles = m_ChildObjectLocalRotation;
|
childRotation.eulerAngles = m_ChildObjectLocalRotation;
|
||||||
m_ChildObject.transform.rotation = childRotation;
|
m_ChildObject.transform.rotation = childRotation;
|
||||||
m_ChildObject.transform.localScale = m_ChildObjectLocalScale;
|
m_ChildObject.transform.localScale = m_ChildObjectLocalScale;
|
||||||
|
|
||||||
|
m_SubChildObject.transform.position = m_SubChildObjectLocalPosition;
|
||||||
|
var subChildRotation = m_SubChildObject.transform.rotation;
|
||||||
|
subChildRotation.eulerAngles = m_SubChildObjectLocalRotation;
|
||||||
|
m_SubChildObject.transform.rotation = childRotation;
|
||||||
|
m_SubChildObject.transform.localScale = m_SubChildObjectLocalScale;
|
||||||
|
|
||||||
if (m_EnableVerboseDebug)
|
if (m_EnableVerboseDebug)
|
||||||
{
|
{
|
||||||
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
|
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
|
||||||
@@ -301,6 +361,11 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ChildObjectComponent.HasSubChild && ChildObjectComponent.AuthoritySubInstance == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||||
{
|
{
|
||||||
if (!ChildObjectComponent.ClientInstances.ContainsKey(clientNetworkManager.LocalClientId))
|
if (!ChildObjectComponent.ClientInstances.ContainsKey(clientNetworkManager.LocalClientId))
|
||||||
@@ -320,6 +385,16 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ChildObjectComponent.HasSubChild)
|
||||||
|
{
|
||||||
|
foreach (var instance in ChildObjectComponent.ClientSubChildInstances.Values)
|
||||||
|
{
|
||||||
|
if (instance.transform.parent == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,6 +402,10 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
private Vector3 m_ChildObjectLocalPosition = new Vector3(5.0f, 0.0f, -5.0f);
|
private Vector3 m_ChildObjectLocalPosition = new Vector3(5.0f, 0.0f, -5.0f);
|
||||||
private Vector3 m_ChildObjectLocalRotation = new Vector3(-35.0f, 90.0f, 270.0f);
|
private Vector3 m_ChildObjectLocalRotation = new Vector3(-35.0f, 90.0f, 270.0f);
|
||||||
private Vector3 m_ChildObjectLocalScale = new Vector3(0.1f, 0.5f, 0.4f);
|
private Vector3 m_ChildObjectLocalScale = new Vector3(0.1f, 0.5f, 0.4f);
|
||||||
|
private Vector3 m_SubChildObjectLocalPosition = new Vector3(2.0f, 1.0f, -1.0f);
|
||||||
|
private Vector3 m_SubChildObjectLocalRotation = new Vector3(5.0f, 15.0f, 124.0f);
|
||||||
|
private Vector3 m_SubChildObjectLocalScale = new Vector3(1.0f, 0.15f, 0.75f);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A wait condition specific method that assures the local space coordinates
|
/// A wait condition specific method that assures the local space coordinates
|
||||||
@@ -374,17 +453,17 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
/// If not, it generates a message containing the axial values that did not match
|
/// If not, it generates a message containing the axial values that did not match
|
||||||
/// the target/start local space values.
|
/// the target/start local space values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void AllChildrenLocalTransformValuesMatch()
|
private void AllChildrenLocalTransformValuesMatch(bool useSubChild)
|
||||||
{
|
{
|
||||||
var success = WaitForConditionOrTimeOutWithTimeTravel(AllInstancesKeptLocalTransformValues);
|
var success = WaitForConditionOrTimeOutWithTimeTravel(AllInstancesKeptLocalTransformValues);
|
||||||
//TimeTravelToNextTick();
|
var infoMessage = new StringBuilder($"Timed out waiting for all children to have the correct local space values:\n");
|
||||||
var infoMessage = new System.Text.StringBuilder($"Timed out waiting for all children to have the correct local space values:\n");
|
var authorityObjectLocalPosition = useSubChild ? m_AuthoritySubChildObject.transform.localPosition : m_AuthorityChildObject.transform.localPosition;
|
||||||
var authorityObjectLocalPosition = m_AuthorityChildObject.transform.localPosition;
|
var authorityObjectLocalRotation = useSubChild ? m_AuthoritySubChildObject.transform.localRotation.eulerAngles : m_AuthorityChildObject.transform.localRotation.eulerAngles;
|
||||||
var authorityObjectLocalRotation = m_AuthorityChildObject.transform.localRotation.eulerAngles;
|
var authorityObjectLocalScale = useSubChild ? m_AuthoritySubChildObject.transform.localScale : m_AuthorityChildObject.transform.localScale;
|
||||||
var authorityObjectLocalScale = m_AuthorityChildObject.transform.localScale;
|
|
||||||
|
|
||||||
if (s_GlobalTimeoutHelper.TimedOut || !success)
|
if (s_GlobalTimeoutHelper.TimedOut || !success)
|
||||||
{
|
{
|
||||||
|
var instances = useSubChild ? ChildObjectComponent.SubInstances : ChildObjectComponent.Instances;
|
||||||
foreach (var childInstance in ChildObjectComponent.Instances)
|
foreach (var childInstance in ChildObjectComponent.Instances)
|
||||||
{
|
{
|
||||||
var childLocalPosition = childInstance.transform.localPosition;
|
var childLocalPosition = childInstance.transform.localPosition;
|
||||||
@@ -427,8 +506,11 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
private NetworkObject m_AuthorityParentObject;
|
private NetworkObject m_AuthorityParentObject;
|
||||||
private NetworkTransformTestComponent m_AuthorityParentNetworkTransform;
|
private NetworkTransformTestComponent m_AuthorityParentNetworkTransform;
|
||||||
private NetworkObject m_AuthorityChildObject;
|
private NetworkObject m_AuthorityChildObject;
|
||||||
|
private NetworkObject m_AuthoritySubChildObject;
|
||||||
private ChildObjectComponent m_AuthorityChildNetworkTransform;
|
private ChildObjectComponent m_AuthorityChildNetworkTransform;
|
||||||
|
|
||||||
|
private ChildObjectComponent m_AuthoritySubChildNetworkTransform;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates that transform values remain the same when a NetworkTransform is
|
/// Validates that transform values remain the same when a NetworkTransform is
|
||||||
/// parented under another NetworkTransform under all of the possible axial conditions
|
/// parented under another NetworkTransform under all of the possible axial conditions
|
||||||
@@ -450,9 +532,11 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
authorityNetworkManager = m_ClientNetworkManagers[0];
|
authorityNetworkManager = m_ClientNetworkManagers[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn a parent and child object
|
// Spawn a parent and children
|
||||||
|
ChildObjectComponent.HasSubChild = true;
|
||||||
var serverSideParent = SpawnObject(m_ParentObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
|
var serverSideParent = SpawnObject(m_ParentObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
|
||||||
var serverSideChild = SpawnObject(m_ChildObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
|
var serverSideChild = SpawnObject(m_ChildObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
|
||||||
|
var serverSideSubChild = SpawnObject(m_SubChildObject.gameObject, authorityNetworkManager).GetComponent<NetworkObject>();
|
||||||
|
|
||||||
// Assure all of the child object instances are spawned before proceeding to parenting
|
// Assure all of the child object instances are spawned before proceeding to parenting
|
||||||
var success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesAreSpawned);
|
var success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesAreSpawned);
|
||||||
@@ -461,6 +545,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
// Get the authority parent and child instances
|
// Get the authority parent and child instances
|
||||||
m_AuthorityParentObject = NetworkTransformTestComponent.AuthorityInstance.NetworkObject;
|
m_AuthorityParentObject = NetworkTransformTestComponent.AuthorityInstance.NetworkObject;
|
||||||
m_AuthorityChildObject = ChildObjectComponent.AuthorityInstance.NetworkObject;
|
m_AuthorityChildObject = ChildObjectComponent.AuthorityInstance.NetworkObject;
|
||||||
|
m_AuthoritySubChildObject = ChildObjectComponent.AuthoritySubInstance.NetworkObject;
|
||||||
|
|
||||||
// The child NetworkTransform will use world space when world position stays and
|
// The child NetworkTransform will use world space when world position stays and
|
||||||
// local space when world position does not stay when parenting.
|
// local space when world position does not stay when parenting.
|
||||||
@@ -469,15 +554,26 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
ChildObjectComponent.AuthorityInstance.UseQuaternionSynchronization = rotation == Rotation.Quaternion;
|
ChildObjectComponent.AuthorityInstance.UseQuaternionSynchronization = rotation == Rotation.Quaternion;
|
||||||
ChildObjectComponent.AuthorityInstance.UseQuaternionCompression = rotationCompression == RotationCompression.QuaternionCompress;
|
ChildObjectComponent.AuthorityInstance.UseQuaternionCompression = rotationCompression == RotationCompression.QuaternionCompress;
|
||||||
|
|
||||||
|
ChildObjectComponent.AuthoritySubInstance.InLocalSpace = !worldPositionStays;
|
||||||
|
ChildObjectComponent.AuthoritySubInstance.UseHalfFloatPrecision = precision == Precision.Half;
|
||||||
|
ChildObjectComponent.AuthoritySubInstance.UseQuaternionSynchronization = rotation == Rotation.Quaternion;
|
||||||
|
ChildObjectComponent.AuthoritySubInstance.UseQuaternionCompression = rotationCompression == RotationCompression.QuaternionCompress;
|
||||||
|
|
||||||
// Set whether we are interpolating or not
|
// Set whether we are interpolating or not
|
||||||
m_AuthorityParentNetworkTransform = m_AuthorityParentObject.GetComponent<NetworkTransformTestComponent>();
|
m_AuthorityParentNetworkTransform = m_AuthorityParentObject.GetComponent<NetworkTransformTestComponent>();
|
||||||
m_AuthorityParentNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
m_AuthorityParentNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||||
m_AuthorityChildNetworkTransform = m_AuthorityChildObject.GetComponent<ChildObjectComponent>();
|
m_AuthorityChildNetworkTransform = m_AuthorityChildObject.GetComponent<ChildObjectComponent>();
|
||||||
m_AuthorityChildNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
m_AuthorityChildNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||||
|
m_AuthoritySubChildNetworkTransform = m_AuthoritySubChildObject.GetComponent<ChildObjectComponent>();
|
||||||
|
m_AuthoritySubChildNetworkTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||||
|
|
||||||
|
|
||||||
// Apply a scale to the parent object to make sure the scale on the child is properly updated on
|
// Apply a scale to the parent object to make sure the scale on the child is properly updated on
|
||||||
// non-authority instances.
|
// non-authority instances.
|
||||||
m_AuthorityParentObject.transform.localScale = new Vector3(scale, scale, scale);
|
var halfScale = scale * 0.5f;
|
||||||
|
m_AuthorityParentObject.transform.localScale = GetRandomVector3(scale - halfScale, scale + halfScale);
|
||||||
|
m_AuthorityChildObject.transform.localScale = GetRandomVector3(scale - halfScale, scale + halfScale);
|
||||||
|
m_AuthoritySubChildObject.transform.localScale = GetRandomVector3(scale - halfScale, scale + halfScale);
|
||||||
|
|
||||||
// Allow one tick for authority to update these changes
|
// Allow one tick for authority to update these changes
|
||||||
TimeTravelToNextTick();
|
TimeTravelToNextTick();
|
||||||
@@ -485,12 +581,18 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
// Parent the child under the parent with the current world position stays setting
|
// 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!");
|
Assert.True(serverSideChild.TrySetParent(serverSideParent.transform, worldPositionStays), "[Server-Side Child] Failed to set child's parent!");
|
||||||
|
|
||||||
|
// Parent the sub-child under the child with the current world position stays setting
|
||||||
|
Assert.True(serverSideSubChild.TrySetParent(serverSideChild.transform, worldPositionStays), "[Server-Side SubChild] Failed to set sub-child's parent!");
|
||||||
|
|
||||||
// This waits for all child instances to be parented
|
// This waits for all child instances to be parented
|
||||||
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesHaveChild);
|
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesHaveChild);
|
||||||
Assert.True(success, "Timed out waiting for all instances to have parented a child!");
|
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
|
// This validates each child instance has preserved their local space values
|
||||||
AllChildrenLocalTransformValuesMatch();
|
AllChildrenLocalTransformValuesMatch(false);
|
||||||
|
|
||||||
|
// This validates each sub-child instance has preserved their local space values
|
||||||
|
AllChildrenLocalTransformValuesMatch(true);
|
||||||
|
|
||||||
// Verify that a late joining client will synchronize to the parented NetworkObjects properly
|
// Verify that a late joining client will synchronize to the parented NetworkObjects properly
|
||||||
CreateAndStartNewClientWithTimeTravel();
|
CreateAndStartNewClientWithTimeTravel();
|
||||||
@@ -499,8 +601,15 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesAreSpawned);
|
success = WaitForConditionOrTimeOutWithTimeTravel(AllChildObjectInstancesAreSpawned);
|
||||||
Assert.True(success, "Timed out waiting for all child instances to be spawned!");
|
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
|
// This waits for all child instances to be parented
|
||||||
AllChildrenLocalTransformValuesMatch();
|
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
|
||||||
|
AllChildrenLocalTransformValuesMatch(false);
|
||||||
|
|
||||||
|
// This validates each sub-child instance has preserved their local space values
|
||||||
|
AllChildrenLocalTransformValuesMatch(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -568,8 +677,8 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The number of iterations to change position, rotation, and scale for NetworkTransformMultipleChangesOverTime
|
// The number of iterations to change position, rotation, and scale for NetworkTransformMultipleChangesOverTime
|
||||||
// Note: this was reduced from 8 iterations to 3 due to the number of tests based on all of the various parameter combinations
|
|
||||||
private const int k_PositionRotationScaleIterations = 3;
|
private const int k_PositionRotationScaleIterations = 3;
|
||||||
|
private const int k_PositionRotationScaleIterations3Axis = 8;
|
||||||
|
|
||||||
protected override void OnNewClientCreated(NetworkManager networkManager)
|
protected override void OnNewClientCreated(NetworkManager networkManager)
|
||||||
{
|
{
|
||||||
@@ -594,22 +703,69 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
|
|
||||||
private Axis m_CurrentAxis;
|
private Axis m_CurrentAxis;
|
||||||
|
|
||||||
|
private bool m_AxisExcluded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Randomly determine if an axis should be excluded.
|
||||||
|
/// If so, then randomly pick one of the axis to be excluded.
|
||||||
|
/// </summary>
|
||||||
|
private Vector3 RandomlyExcludeAxis(Vector3 delta)
|
||||||
|
{
|
||||||
|
if (Random.Range(0.0f, 1.0f) >= 0.5f)
|
||||||
|
{
|
||||||
|
m_AxisExcluded = true;
|
||||||
|
var axisToIgnore = Random.Range(0, 2);
|
||||||
|
switch (axisToIgnore)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
delta.x = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
delta.y = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
delta.z = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This validates that multiple changes can occur within the same tick or over
|
/// This validates that multiple changes can occur within the same tick or over
|
||||||
/// several ticks while still keeping non-authoritative instances synchronized.
|
/// several ticks while still keeping non-authoritative instances synchronized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When testing < 3 axis: Interpolation is disabled and only 3 delta updates are applied per unique test
|
||||||
|
/// When testing 3 axis: Interpolation is enabled, sometimes an axis is intentionally excluded during a
|
||||||
|
/// delta update, and it runs through 8 delta updates per unique test.
|
||||||
|
/// </remarks>
|
||||||
[Test]
|
[Test]
|
||||||
public void NetworkTransformMultipleChangesOverTime([Values] TransformSpace testLocalTransform, [Values] OverrideState overideState,
|
public void NetworkTransformMultipleChangesOverTime([Values] TransformSpace testLocalTransform, [Values] OverrideState overideState,
|
||||||
[Values] Precision precision, [Values] Rotation rotationSynch, [Values] Axis axis)
|
[Values] Precision precision, [Values] Rotation rotationSynch, [Values] Axis axis)
|
||||||
{
|
{
|
||||||
// In the name of reducing the very long time it takes to interpolate and run all of the possible combinations,
|
|
||||||
// we only interpolate when the second client joins
|
|
||||||
m_AuthoritativeTransform.Interpolate = false;
|
|
||||||
m_AuthoritativeTransform.InLocalSpace = testLocalTransform == TransformSpace.Local;
|
m_AuthoritativeTransform.InLocalSpace = testLocalTransform == TransformSpace.Local;
|
||||||
bool axisX = axis == Axis.X || axis == Axis.XY || axis == Axis.XZ || axis == Axis.XYZ;
|
bool axisX = axis == Axis.X || axis == Axis.XY || axis == Axis.XZ || axis == Axis.XYZ;
|
||||||
bool axisY = axis == Axis.Y || axis == Axis.XY || axis == Axis.YZ || axis == Axis.XYZ;
|
bool axisY = axis == Axis.Y || axis == Axis.XY || axis == Axis.YZ || axis == Axis.XYZ;
|
||||||
bool axisZ = axis == Axis.Z || axis == Axis.XZ || axis == Axis.YZ || axis == Axis.XYZ;
|
bool axisZ = axis == Axis.Z || axis == Axis.XZ || axis == Axis.YZ || axis == Axis.XYZ;
|
||||||
|
|
||||||
|
var axisCount = axisX ? 1 : 0;
|
||||||
|
axisCount += axisY ? 1 : 0;
|
||||||
|
axisCount += axisZ ? 1 : 0;
|
||||||
|
|
||||||
|
// Enable interpolation when all 3 axis are selected to make sure we are synchronizing properly
|
||||||
|
// when interpolation is enabled.
|
||||||
|
m_AuthoritativeTransform.Interpolate = axisCount == 3 ? true : false;
|
||||||
|
|
||||||
m_CurrentAxis = axis;
|
m_CurrentAxis = axis;
|
||||||
|
|
||||||
// Authority dictates what is synchronized and what the precision is going to be
|
// Authority dictates what is synchronized and what the precision is going to be
|
||||||
// so we only need to set this on the authoritative side.
|
// so we only need to set this on the authoritative side.
|
||||||
m_AuthoritativeTransform.UseHalfFloatPrecision = precision == Precision.Half;
|
m_AuthoritativeTransform.UseHalfFloatPrecision = precision == Precision.Half;
|
||||||
@@ -640,29 +796,49 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
m_AuthoritativeTransform.SyncScaleY = axisY;
|
m_AuthoritativeTransform.SyncScaleY = axisY;
|
||||||
m_AuthoritativeTransform.SyncScaleZ = axisZ;
|
m_AuthoritativeTransform.SyncScaleZ = axisZ;
|
||||||
|
|
||||||
|
|
||||||
var positionStart = GetRandomVector3(0.25f, 1.75f);
|
var positionStart = GetRandomVector3(0.25f, 1.75f);
|
||||||
var rotationStart = GetRandomVector3(1f, 15f);
|
var rotationStart = GetRandomVector3(1f, 15f);
|
||||||
var scaleStart = GetRandomVector3(0.25f, 2.0f);
|
var scaleStart = GetRandomVector3(0.25f, 2.0f);
|
||||||
var position = positionStart;
|
var position = positionStart;
|
||||||
var rotation = rotationStart;
|
var rotation = rotationStart;
|
||||||
var scale = scaleStart;
|
var scale = scaleStart;
|
||||||
|
var success = false;
|
||||||
|
|
||||||
m_AuthoritativeTransform.StatePushed = false;
|
m_AuthoritativeTransform.StatePushed = false;
|
||||||
// Wait for the deltas to be pushed
|
// Wait for the deltas to be pushed
|
||||||
WaitForConditionOrTimeOutWithTimeTravel(() => m_AuthoritativeTransform.StatePushed);
|
WaitForConditionOrTimeOutWithTimeTravel(() => m_AuthoritativeTransform.StatePushed);
|
||||||
// Allow the precision settings to propagate first as changing precision
|
// Allow the precision settings to propagate first as changing precision
|
||||||
// causes a teleport event to occur
|
// causes a teleport event to occur
|
||||||
WaitForNextTick();
|
WaitForNextTick();
|
||||||
|
var iterations = axisCount == 3 ? k_PositionRotationScaleIterations3Axis : k_PositionRotationScaleIterations;
|
||||||
|
|
||||||
// Move and rotate within the same tick, validate the non-authoritative instance updates
|
// Move and rotate within the same tick, validate the non-authoritative instance updates
|
||||||
// to each set of changes. Repeat several times.
|
// to each set of changes. Repeat several times.
|
||||||
for (int i = 0; i < k_PositionRotationScaleIterations; i++)
|
for (int i = 0; i < iterations; i++)
|
||||||
{
|
{
|
||||||
|
// Always reset this per delta update pass
|
||||||
|
m_AxisExcluded = false;
|
||||||
|
var deltaPositionDelta = GetRandomVector3(-1.5f, 1.5f);
|
||||||
|
var deltaRotationDelta = GetRandomVector3(-3.5f, 3.5f);
|
||||||
|
var deltaScaleDelta = GetRandomVector3(-0.5f, 0.5f);
|
||||||
|
|
||||||
m_NonAuthoritativeTransform.StateUpdated = false;
|
m_NonAuthoritativeTransform.StateUpdated = false;
|
||||||
m_AuthoritativeTransform.StatePushed = false;
|
m_AuthoritativeTransform.StatePushed = false;
|
||||||
position = positionStart * i;
|
|
||||||
rotation = rotationStart * i;
|
// With two or more axis, excluding one of them while chaging another will validate that
|
||||||
scale = scaleStart * i;
|
// full precision updates are maintaining their target state value(s) to interpolate towards
|
||||||
|
if (axisCount == 3)
|
||||||
|
{
|
||||||
|
position += RandomlyExcludeAxis(deltaPositionDelta);
|
||||||
|
rotation += RandomlyExcludeAxis(deltaRotationDelta);
|
||||||
|
scale += RandomlyExcludeAxis(deltaScaleDelta);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
position += deltaPositionDelta;
|
||||||
|
rotation += deltaRotationDelta;
|
||||||
|
scale += deltaScaleDelta;
|
||||||
|
}
|
||||||
|
|
||||||
// Apply delta between ticks
|
// Apply delta between ticks
|
||||||
MoveRotateAndScaleAuthority(position, rotation, scale, overideState);
|
MoveRotateAndScaleAuthority(position, rotation, scale, overideState);
|
||||||
@@ -670,54 +846,37 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
// Wait for the deltas to be pushed
|
// Wait for the deltas to be pushed
|
||||||
Assert.True(WaitForConditionOrTimeOutWithTimeTravel(() => m_AuthoritativeTransform.StatePushed && m_NonAuthoritativeTransform.StateUpdated), $"[Non-Interpolate {i}] Timed out waiting for state to be pushed ({m_AuthoritativeTransform.StatePushed}) or state to be updated ({m_NonAuthoritativeTransform.StateUpdated})!");
|
Assert.True(WaitForConditionOrTimeOutWithTimeTravel(() => m_AuthoritativeTransform.StatePushed && m_NonAuthoritativeTransform.StateUpdated), $"[Non-Interpolate {i}] Timed out waiting for state to be pushed ({m_AuthoritativeTransform.StatePushed}) or state to be updated ({m_NonAuthoritativeTransform.StateUpdated})!");
|
||||||
|
|
||||||
|
// For 3 axis, we will skip validating that the non-authority interpolates to its target point at least once.
|
||||||
|
// This will validate that non-authoritative updates are maintaining their target state axis values if only 2
|
||||||
|
// of the axis are being updated to assure interpolation maintains the targeted axial value per axis.
|
||||||
|
// For 2 and 1 axis tests we always validate per delta update
|
||||||
|
if (m_AxisExcluded || axisCount < 3)
|
||||||
|
{
|
||||||
// Wait for deltas to synchronize on non-authoritative side
|
// Wait for deltas to synchronize on non-authoritative side
|
||||||
var success = WaitForConditionOrTimeOutWithTimeTravel(PositionRotationScaleMatches);
|
success = WaitForConditionOrTimeOutWithTimeTravel(PositionRotationScaleMatches);
|
||||||
// Provide additional debug info about what failed (if it fails)
|
// Provide additional debug info about what failed (if it fails)
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
m_EnableVerboseDebug = true;
|
m_EnableVerboseDebug = true;
|
||||||
PositionRotationScaleMatches();
|
success = PositionRotationScaleMatches();
|
||||||
m_EnableVerboseDebug = false;
|
m_EnableVerboseDebug = false;
|
||||||
}
|
}
|
||||||
Assert.True(success, $"[Non-Interpolate {i}] Timed out waiting for non-authority to match authority's position or rotation");
|
Assert.True(success, $"[Non-Interpolate {i}] Timed out waiting for non-authority to match authority's position or rotation");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only enable interpolation when all axis are set (to reduce the test times)
|
if (axisCount == 3)
|
||||||
if (axis == Axis.XYZ)
|
|
||||||
{
|
{
|
||||||
// Now, enable interpolation
|
// As a final test, wait for deltas to synchronize on non-authoritative side to assure it interpolates to th
|
||||||
m_AuthoritativeTransform.Interpolate = true;
|
success = WaitForConditionOrTimeOutWithTimeTravel(PositionRotationScaleMatches);
|
||||||
m_NonAuthoritativeTransform.StateUpdated = false;
|
|
||||||
m_AuthoritativeTransform.StatePushed = false;
|
|
||||||
// Wait for the delta (change in interpolation) to be pushed
|
|
||||||
var success = WaitForConditionOrTimeOutWithTimeTravel(() => m_AuthoritativeTransform.StatePushed && m_NonAuthoritativeTransform.StateUpdated);
|
|
||||||
Assert.True(success, $"[Interpolation Enable] Timed out waiting for state to be pushed ({m_AuthoritativeTransform.StatePushed}) or state to be updated ({m_NonAuthoritativeTransform.StateUpdated})!");
|
|
||||||
|
|
||||||
// Continue for one more update with interpolation enabled
|
|
||||||
// Note: We are just verifying one update with interpolation enabled due to the number of tests this integration test has to run
|
|
||||||
// and since the NestedNetworkTransformTests already tests interpolation under the same number of conditions (excluding Axis).
|
|
||||||
// This is just to verify selecting specific axis doesn't cause issues when interpolating as well.
|
|
||||||
m_NonAuthoritativeTransform.StateUpdated = false;
|
|
||||||
m_AuthoritativeTransform.StatePushed = false;
|
|
||||||
position = positionStart * k_PositionRotationScaleIterations;
|
|
||||||
rotation = rotationStart * k_PositionRotationScaleIterations;
|
|
||||||
scale = scaleStart * k_PositionRotationScaleIterations;
|
|
||||||
MoveRotateAndScaleAuthority(position, rotation, scale, overideState);
|
|
||||||
|
|
||||||
// Wait for the deltas to be pushed and updated
|
|
||||||
success = WaitForConditionOrTimeOutWithTimeTravel(() => m_AuthoritativeTransform.StatePushed && m_NonAuthoritativeTransform.StateUpdated);
|
|
||||||
Assert.True(success, $"[Interpolation {k_PositionRotationScaleIterations}] Timed out waiting for state to be pushed ({m_AuthoritativeTransform.StatePushed}) or state to be updated ({m_NonAuthoritativeTransform.StateUpdated})!");
|
|
||||||
|
|
||||||
success = WaitForConditionOrTimeOutWithTimeTravel(PositionRotationScaleMatches, 120);
|
|
||||||
|
|
||||||
// Provide additional debug info about what failed (if it fails)
|
// Provide additional debug info about what failed (if it fails)
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
m_EnableVerboseDebug = true;
|
m_EnableVerboseDebug = true;
|
||||||
PositionRotationScaleMatches();
|
success = PositionRotationScaleMatches();
|
||||||
m_EnableVerboseDebug = false;
|
m_EnableVerboseDebug = false;
|
||||||
}
|
}
|
||||||
Assert.True(success, $"[Interpolation {k_PositionRotationScaleIterations}] Timed out waiting for non-authority to match authority's position or rotation");
|
Assert.True(success, $"Timed out waiting for non-authority to match authority's position or rotation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,7 +1162,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
m_AuthoritativeTransform.transform.rotation = Quaternion.Euler(1, 2, 3);
|
m_AuthoritativeTransform.transform.rotation = Quaternion.Euler(1, 2, 3);
|
||||||
var serverLastSentState = m_AuthoritativeTransform.AuthorityLastSentState;
|
var serverLastSentState = m_AuthoritativeTransform.AuthorityLastSentState;
|
||||||
var clientReplicatedState = m_NonAuthoritativeTransform.ReplicatedNetworkState.Value;
|
var clientReplicatedState = m_NonAuthoritativeTransform.LocalAuthoritativeNetworkState;
|
||||||
var success = WaitForConditionOrTimeOutWithTimeTravel(() => ValidateBitSetValues(serverLastSentState, clientReplicatedState));
|
var success = WaitForConditionOrTimeOutWithTimeTravel(() => ValidateBitSetValues(serverLastSentState, clientReplicatedState));
|
||||||
Assert.True(success, $"Timed out waiting for Authoritative Bitset state to equal NonAuthoritative replicated Bitset state!");
|
Assert.True(success, $"Timed out waiting for Authoritative Bitset state to equal NonAuthoritative replicated Bitset state!");
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
return TestComplete;
|
return TestComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnInitialize(ref NetworkVariable<NetworkTransformState> replicatedState)
|
protected override void OnInitialize(ref NetworkTransformState replicatedState)
|
||||||
{
|
{
|
||||||
m_LocalSpaceToggles = 0;
|
m_LocalSpaceToggles = 0;
|
||||||
m_FrameRateFractional = 1.0f / Application.targetFrameRate;
|
m_FrameRateFractional = 1.0f / Application.targetFrameRate;
|
||||||
|
|||||||
10
ValidationExceptions.json
Normal file
10
ValidationExceptions.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ErrorExceptions": [
|
||||||
|
{
|
||||||
|
"ValidationTest": "API Validation",
|
||||||
|
"ExceptionMessage": "Additions require a new minor or major version.",
|
||||||
|
"PackageVersion": "1.5.2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"WarningExceptions": []
|
||||||
|
}
|
||||||
7
ValidationExceptions.json.meta
Normal file
7
ValidationExceptions.json.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2a43005be301c9043aab7034757d4868
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
10
package.json
10
package.json
@@ -2,23 +2,23 @@
|
|||||||
"name": "com.unity.netcode.gameobjects",
|
"name": "com.unity.netcode.gameobjects",
|
||||||
"displayName": "Netcode for GameObjects",
|
"displayName": "Netcode for GameObjects",
|
||||||
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
|
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
|
||||||
"version": "1.5.1",
|
"version": "1.6.0",
|
||||||
"unity": "2020.3",
|
"unity": "2020.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"com.unity.nuget.mono-cecil": "1.10.1",
|
"com.unity.nuget.mono-cecil": "1.10.1",
|
||||||
"com.unity.transport": "1.3.4"
|
"com.unity.transport": "1.3.4"
|
||||||
},
|
},
|
||||||
"_upm": {
|
"_upm": {
|
||||||
"changelog": "### Added\n\n- 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)\n- The location of the automatically-created default network prefab list can now be configured (#2544)\n- 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)\n- 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)\n\n### Fixed\n\n- Fixed: Fixed a null reference in codegen in some projects (#2581)\n- Fixed issue where the `OnClientDisconnected` client identifier was incorrect after a pending client connection was denied. (#2569)\n- Fixed warning \"Runtime Network Prefabs was not empty at initialization time.\" being erroneously logged when no runtime network prefabs had been added (#2565)\n- Fixed issue where some temporary debug console logging was left in a merged PR. (#2562)\n- Fixed the \"Generate Default Network Prefabs List\" setting not loading correctly and always reverting to being checked. (#2545)\n- Fixed issue where users could not use NetworkSceneManager.VerifySceneBeforeLoading to exclude runtime generated scenes from client synchronization. (#2550)\n- Fixed missing value on `NetworkListEvent` for `EventType.RemoveAt` events. (#2542,#2543)\n- 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)\n- Fixed issue where a server would include scene migrated and then despawned NetworkObjects to a client that was being synchronized. (#2532)\n- Fixed the inspector throwing exceptions when attempting to render `NetworkVariable`s of enum types. (#2529)\n- 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)\n- Fixed Multiplayer Tools package installation docs page link on the NetworkManager popup. (#2526)\n- Fixed an exception and error logging when two different objects are shown and hidden on the same frame (#2524)\n- Fixed a memory leak in `UnityTransport` that occurred if `StartClient` failed. (#2518)\n- Fixed issue where a client could throw an exception if abruptly disconnected from a network session with one or more spawned `NetworkObject`(s). (#2510)\n- Fixed issue where invalid endpoint addresses were not being detected and returning false from NGO UnityTransport. (#2496)\n- Fixed some errors that could occur if a connection is lost and the loss is detected when attempting to write to the socket. (#2495)\n\n## Changed\n\n- Adding network prefabs before NetworkManager initialization is now supported. (#2565)\n- Connecting clients being synchronized now switch to the server's active scene before spawning and synchronizing NetworkObjects. (#2532)\n- Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.4. (#2533)\n- 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)"
|
"changelog": "### Added\n\n- Added a protected virtual method `NetworkTransform.OnInitialize(ref NetworkTransformState replicatedState)` that just returns the replicated state reference.\n \n### Fixed\n\n- Fixed issue where invoking `NetworkManager.Shutdown` within `NetworkManager.OnClientStopped` or `NetworkManager.OnServerStopped` would force `NetworkManager.ShutdownInProgress` to remain true after completing the shutdown process. (#2661)\n- Fixed issue with client synchronization of position when using half precision and the delta position reaches the maximum value and is collapsed on the host prior to being forwarded to the non-owner clients. (#2636)\n- Fixed issue with scale not synchronizing properly depending upon the spawn order of NetworkObjects. (#2636)\n- Fixed issue position was not properly transitioning between ownership changes with an owner authoritative NetworkTransform. (#2636)\n- Fixed issue where a late joining non-owner client could update an owner authoritative NetworkTransform if ownership changed without any updates to position prior to the non-owner client joining. (#2636)\n\n### Changed"
|
||||||
},
|
},
|
||||||
"upmCi": {
|
"upmCi": {
|
||||||
"footprint": "35c5325acc3edf18c37ef8c9d19e0944fae0d42a"
|
"footprint": "58b37aee2ff0caec7a160473c56e62119d3b760a"
|
||||||
},
|
},
|
||||||
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@1.5/manual/index.html",
|
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@1.6/manual/index.html",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git",
|
"url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"revision": "7a969f89d6dda65ac373ce552c0c997c9116f21a"
|
"revision": "4ee8da05b439a5b8f76ee6d008221c6d58b7965a"
|
||||||
},
|
},
|
||||||
"samples": [
|
"samples": [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user