com.unity.netcode.gameobjects@1.6.0

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [1.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
This commit is contained in:
Unity Technologies
2023-08-09 00:00:00 +00:00
parent 0581a42b70
commit b3bd4727ab
16 changed files with 913 additions and 386 deletions

View File

@@ -27,7 +27,7 @@ namespace Unity.Netcode.Components
/// <summary>
/// The half float precision value of the z-axis as a <see cref="half"/>.
/// </summary>
public half Z => Axis.x;
public half Z => Axis.z;
/// <summary>
/// Used to store the half float precision values as a <see cref="half3"/>
@@ -39,6 +39,17 @@ namespace Unity.Netcode.Components
/// </summary>
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)
{
for (int i = 0; i < Length; i++)

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
@@ -49,6 +50,11 @@ namespace Unity.Netcode.Components
/// </summary>
public OnClientRequestChangeDelegate OnClientRequestChange;
/// <summary>
/// When set each state update will contain a state identifier
/// </summary>
internal static bool TrackByStateId;
/// <summary>
/// Data structure used to synchronize the <see cref="NetworkTransform"/>
/// </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_Synchronization = 0x00008000;
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
private uint m_Bitset;
internal uint BitSet
{
get { return m_Bitset; }
set { m_Bitset = value; }
}
// Used to store the tick calculated sent time
internal double SentTime;
@@ -98,6 +111,7 @@ namespace Unity.Netcode.Components
// Used for half precision scale
internal HalfVector3 HalfVectorScale;
internal Vector3 Scale;
internal Vector3 LossyScale;
// Used for half precision quaternion
internal HalfVector4 HalfVectorRotation;
@@ -118,7 +132,6 @@ namespace Unity.Netcode.Components
internal int NetworkTick;
// Used when tracking by state ID is enabled
internal bool TrackByStateId;
internal int StateId;
// 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)]
private bool GetFlag(int flag)
{
@@ -534,6 +565,8 @@ namespace Unity.Netcode.Components
return NetworkTick;
}
internal HalfVector3 HalfEulerRotation;
/// <summary>
/// Serializes this <see cref="NetworkTransformState"/>
/// </summary>
@@ -553,23 +586,6 @@ namespace Unity.Netcode.Components
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
{
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
if (HasPositionChange)
{
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)
{
// **Always use full precision when teleporting and UseHalfFloatPrecision is enabled**
@@ -604,7 +631,7 @@ namespace Unity.Netcode.Components
serializer.SerializeValue(ref DeltaPosition);
if (!isWriting)
{
NetworkDeltaPosition = new NetworkDeltaPosition(Vector3.zero, 0, math.bool3(HasPositionX, HasPositionY, HasPositionZ));
NetworkDeltaPosition.NetworkTick = NetworkTick;
NetworkDeltaPosition.NetworkSerialize(serializer);
}
else
@@ -617,7 +644,7 @@ namespace Unity.Netcode.Components
{
if (!isWriting)
{
NetworkDeltaPosition = new NetworkDeltaPosition(Vector3.zero, 0, math.bool3(HasPositionX, HasPositionY, HasPositionZ));
NetworkDeltaPosition.NetworkTick = NetworkTick;
NetworkDeltaPosition.NetworkSerialize(serializer);
}
else
@@ -626,9 +653,8 @@ namespace Unity.Netcode.Components
}
}
}
else // Legacy Position Synchronization
else // Full precision axis specific position synchronization
{
// Position Values
if (HasPositionX)
{
serializer.SerializeValue(ref PositionX);
@@ -703,11 +729,21 @@ namespace Unity.Netcode.Components
{
if (HasRotAngleChange)
{
var halfPrecisionRotation = new HalfVector3(RotAngleX, RotAngleY, RotAngleZ, math.bool3(HasRotAngleX, HasRotAngleY, HasRotAngleZ));
serializer.SerializeValue(ref halfPrecisionRotation);
// Apply which axis should be updated for both write/read
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)
{
var eulerRotation = halfPrecisionRotation.ToVector3();
var eulerRotation = HalfEulerRotation.ToVector3();
if (HasRotAngleX)
{
RotAngleX = eulerRotation.x;
@@ -749,6 +785,12 @@ namespace Unity.Netcode.Components
// Synchronize Scale
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
if (UseHalfFloatPrecision)
{
@@ -758,9 +800,19 @@ namespace Unity.Netcode.Components
}
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
HalfVectorScale = new HalfVector3(Scale, math.bool3(HasScaleX, HasScaleY, HasScaleZ));
if (isWriting)
{
HalfVectorScale.Set(Scale[0], Scale[1], Scale[2]);
}
serializer.SerializeValue(ref HalfVectorScale);
if (!isWriting)
{
Scale = HalfVectorScale.ToVector3();
@@ -1029,26 +1081,6 @@ namespace Unity.Netcode.Components
/// </summary>
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>
/// Helper method that returns the space relative position of the transform.
/// </summary>
@@ -1149,6 +1181,8 @@ namespace Unity.Netcode.Components
// This represents the most recent local authoritative state.
private NetworkTransformState m_LocalAuthoritativeNetworkState;
internal NetworkTransformState LocalAuthoritativeNetworkState => m_LocalAuthoritativeNetworkState;
private ClientRpcParams m_ClientRpcParams = new ClientRpcParams() { Send = new ClientRpcSendParams() };
private List<ulong> m_ClientIds = new List<ulong>() { 0 };
@@ -1165,6 +1199,9 @@ namespace Unity.Netcode.Components
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)]
internal void UpdatePositionInterpolator(Vector3 position, double time, bool resetInterpolator = false)
@@ -1258,7 +1295,13 @@ namespace Unity.Netcode.Components
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
{
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)
{
@@ -1286,6 +1329,7 @@ namespace Unity.Netcode.Components
m_LocalAuthoritativeNetworkState = synchronizationState;
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
m_LocalAuthoritativeNetworkState.IsSynchronizing = false;
}
}
@@ -1366,11 +1410,11 @@ namespace Unity.Netcode.Components
// If the transform has deltas (returns dirty) then...
if (ApplyTransformToNetworkStateWithInfo(ref m_LocalAuthoritativeNetworkState, ref transformToCommit, synchronize))
{
m_LocalAuthoritativeNetworkState.LastSerializedSize = ReplicatedNetworkState.Value.LastSerializedSize;
m_LocalAuthoritativeNetworkState.LastSerializedSize = m_OldState.LastSerializedSize;
OnAuthorityPushTransformState(ref m_LocalAuthoritativeNetworkState);
// "push"/commit the state
ReplicatedNetworkState.Value = m_LocalAuthoritativeNetworkState;
// Update the state
UpdateTransformState();
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
}
@@ -1651,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 (!UseHalfFloatPrecision)
@@ -1682,7 +1762,7 @@ namespace Unity.Netcode.Components
var previousScale = networkState.Scale;
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;
networkState.Scale[i] = scale[i];
@@ -1691,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)
{
// 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)
{
networkState.ScaleX = scaleToUse.x;
networkState.ScaleY = scaleToUse.y;
networkState.ScaleZ = scaleToUse.z;
networkState.ScaleX = transform.localScale.x;
networkState.ScaleY = transform.localScale.y;
networkState.ScaleZ = transform.localScale.z;
}
else
{
networkState.Scale = scaleToUse;
networkState.Scale = transform.localScale;
}
networkState.HasScaleX = true;
networkState.HasScaleY = true;
@@ -2027,28 +2079,41 @@ namespace Unity.Netcode.Components
if (newState.HasScaleChange)
{
bool shouldUseLossy = false;
if (newState.IsParented)
{
if (transform.parent == null)
{
shouldUseLossy = NetworkObject.WorldPositionStays();
}
else
{
shouldUseLossy = !NetworkObject.WorldPositionStays();
}
}
if (UseHalfFloatPrecision)
{
currentScale = newState.Scale;
currentScale = shouldUseLossy ? newState.LossyScale : newState.Scale;
}
else
{
// Adjust based on which axis changed
if (newState.HasScaleX)
{
currentScale.x = newState.ScaleX;
currentScale.x = shouldUseLossy ? newState.LossyScale.x : newState.ScaleX;
}
if (newState.HasScaleY)
{
currentScale.y = newState.ScaleY;
currentScale.y = shouldUseLossy ? newState.LossyScale.y : newState.ScaleY;
}
if (newState.HasScaleZ)
{
currentScale.z = newState.ScaleZ;
currentScale.z = shouldUseLossy ? newState.LossyScale.z : newState.ScaleZ;
}
}
m_CurrentScale = currentScale;
@@ -2112,7 +2177,7 @@ namespace Unity.Netcode.Components
/// <remarks>
/// Only non-authoritative instances should invoke this
/// </remarks>
private void UpdateState(NetworkTransformState oldState, NetworkTransformState newState)
private void ApplyUpdatedState(NetworkTransformState newState)
{
// Set the transforms's synchronization modes
InLocalSpace = newState.InLocalSpace;
@@ -2142,8 +2207,9 @@ namespace Unity.Netcode.Components
{
// assure our local NetworkDeltaPosition state is updated
m_HalfPositionState.HalfVector3.Axis = m_LocalAuthoritativeNetworkState.NetworkDeltaPosition.HalfVector3.Axis;
// and update our current position
m_LocalAuthoritativeNetworkState.CurrentPosition = m_HalfPositionState.ToVector3(newState.NetworkTick);
// and update our target position
m_TargetPosition = m_HalfPositionState.ToVector3(newState.NetworkTick);
m_LocalAuthoritativeNetworkState.CurrentPosition = m_TargetPosition;
}
if (!Interpolate)
@@ -2154,14 +2220,9 @@ namespace Unity.Netcode.Components
// Apply axial changes from the new state
// Either apply the delta position target position or the current state's delta position
// depending upon whether UsePositionDeltaCompression is enabled
if (m_LocalAuthoritativeNetworkState.HasPositionChange)
{
if (m_LocalAuthoritativeNetworkState.UseHalfFloatPrecision)
{
UpdatePositionInterpolator(m_LocalAuthoritativeNetworkState.CurrentPosition, sentTime);
}
else
if (!m_LocalAuthoritativeNetworkState.UseHalfFloatPrecision)
{
var newTargetPosition = m_TargetPosition;
if (m_LocalAuthoritativeNetworkState.HasPositionX)
@@ -2178,9 +2239,9 @@ namespace Unity.Netcode.Components
{
newTargetPosition.z = m_LocalAuthoritativeNetworkState.PositionZ;
}
UpdatePositionInterpolator(newTargetPosition, sentTime);
m_TargetPosition = newTargetPosition;
}
UpdatePositionInterpolator(m_TargetPosition, sentTime);
}
if (m_LocalAuthoritativeNetworkState.HasScaleChange)
@@ -2263,6 +2324,8 @@ namespace Unity.Netcode.Components
}
private NetworkTransformState m_OldState = new NetworkTransformState();
/// <summary>
/// Only non-authoritative instances should invoke this method
/// </summary>
@@ -2276,8 +2339,8 @@ namespace Unity.Netcode.Components
// Get the time when this new state was sent
newState.SentTime = new NetworkTime(NetworkManager.NetworkConfig.TickRate, newState.NetworkTick).Time;
// Update the state
UpdateState(oldState, newState);
// Apply the new state
ApplyUpdatedState(newState);
// Provide notifications when the state has been updated
OnNetworkTransformStateUpdated(ref oldState, ref newState);
@@ -2360,10 +2423,19 @@ namespace Unity.Netcode.Components
internal void OnUpdateAuthoritativeState(ref Transform transformSource)
{
// 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
m_LocalAuthoritativeNetworkState.ClearBitSetForNextTick();
if (TrackByStateId)
{
m_LocalAuthoritativeNetworkState.TrackByStateId = true;
m_LocalAuthoritativeNetworkState.StateId++;
}
else
{
m_LocalAuthoritativeNetworkState.TrackByStateId = false;
}
}
AxisChangedDeltaPositionCheck();
@@ -2394,20 +2466,31 @@ namespace Unity.Netcode.Components
}
}
/// <inheritdoc/>
public override void OnNetworkSpawn()
{
///////////////////////////////////////////////////////////////
// NOTE: Legacy and no longer used (candidates for deprecation)
m_CachedIsServer = IsServer;
m_CachedNetworkManager = NetworkManager;
///////////////////////////////////////////////////////////////
// Register a custom named message specifically for this instance
m_MessageName = $"NTU_{NetworkObjectId}_{NetworkBehaviourId}";
NetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(m_MessageName, TransformStateUpdate);
Initialize();
}
/// <inheritdoc/>
public override void OnNetworkDespawn()
{
ReplicatedNetworkState.OnValueChanged -= OnNetworkStateChanged;
if (!NetworkManager.ShutdownInProgress && NetworkManager.CustomMessagingManager != null)
{
NetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler(m_MessageName);
}
CanCommitToTransform = false;
if (NetworkManager != null && NetworkManager.NetworkTickSystem != null)
{
@@ -2424,9 +2507,6 @@ namespace Unity.Netcode.Components
}
CanCommitToTransform = false;
base.OnDestroy();
m_ReplicatedNetworkStateServer.Dispose();
m_ReplicatedNetworkStateOwner.Dispose();
}
/// <inheritdoc/>
@@ -2453,7 +2533,20 @@ namespace Unity.Netcode.Components
/// <summary>
/// Invoked when first spawned and when ownership changes.
/// </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)
{
@@ -2470,7 +2563,6 @@ namespace Unity.Netcode.Components
}
CanCommitToTransform = IsServerAuthoritative() ? IsServer : IsOwner;
var replicatedState = ReplicatedNetworkState;
var currentPosition = GetSpaceRelativePosition();
var currentRotation = GetSpaceRelativeRotation();
@@ -2490,14 +2582,12 @@ namespace Unity.Netcode.Components
}
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
NetworkManager.NetworkTickSystem.Tick -= NetworkTickSystem_Tick;
ResetInterpolatedStateToCurrentAuthoritativeState();
m_CurrentPosition = currentPosition;
m_TargetPosition = currentPosition;
m_CurrentScale = transform.localScale;
@@ -2506,8 +2596,13 @@ namespace Unity.Netcode.Components
m_TargetRotation = currentRotation.eulerAngles;
}
OnInitialize(ref m_LocalAuthoritativeNetworkState);
OnInitialize(ref replicatedState);
if (IsOwner)
{
m_InternalStatNetVar.Value = m_LocalAuthoritativeNetworkState;
OnInitialize(ref m_InternalStatNetVar);
}
}
/// <inheritdoc/>
@@ -2690,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
ApplyAuthoritativeState();
}
@@ -2738,6 +2827,103 @@ namespace Unity.Netcode.Components
{
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