Skip to content

docs: [1.X] fixes of PVP exceptions #3300

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .yamato/wrench/package-pack-jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ package_pack_-_netcode_gameobjects:
Job Maintainers: '#rm-packageworks'
Wrench: 0.10.44.0


Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,24 @@ namespace Unity.Netcode.Components
[DefaultExecutionOrder(100000)] // this is needed to catch the update time after the transform was updated by user scripts
public class AnticipatedNetworkTransform : NetworkTransform
{
/// <summary>
/// Represents the state of a transform, including position, rotation, and scale.
/// </summary>
public struct TransformState
{
/// <summary>
/// The position of the transform.
/// </summary>
public Vector3 Position;

/// <summary>
/// The rotation of the transform.
/// </summary>
public Quaternion Rotation;

/// <summary>
/// The scale of the transform.
/// </summary>
public Vector3 Scale;
}

Expand Down Expand Up @@ -242,6 +256,9 @@ public void AnticipateState(TransformState newState)
m_CurrentSmoothTime = 0;
}

/// <summary>
/// Updates the AnticipatedNetworkTransform each frame.
/// </summary>
protected override void Update()
{
// If not spawned or this instance has authority, exit early
Expand Down Expand Up @@ -350,6 +367,13 @@ private void ResetAnticipatedState()
m_CurrentSmoothTime = 0;
}

/// <summary>
/// Invoked when a new client joins (server and client sides).
/// Server Side: Serializes as if we were teleporting (everything is sent via NetworkTransformState).
/// Client Side: Adds the interpolated state which applies the NetworkTransformState as well.
/// </summary>
/// <typeparam name="T">The type of the serializer.</typeparam>
/// <param name="serializer">The serializer used to serialize the state.</param>
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
{
base.OnSynchronize(ref serializer);
Expand All @@ -361,6 +385,9 @@ protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
}
}

/// <summary>
/// Invoked when the NetworkObject is spawned.
/// </summary>
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
Expand All @@ -373,6 +400,9 @@ public override void OnNetworkSpawn()
NetworkManager.AnticipationSystem.AllAnticipatedObjects.Add(m_AnticipatedObject);
}

/// <summary>
/// Invoked when the NetworkObject is despawned.
/// </summary>
public override void OnNetworkDespawn()
{
if (m_AnticipatedObject != null)
Expand All @@ -387,6 +417,9 @@ public override void OnNetworkDespawn()
base.OnNetworkDespawn();
}

/// <summary>
/// Invoked when the NetworkObject is destroyed.
/// </summary>
public override void OnDestroy()
{
if (m_AnticipatedObject != null)
Expand Down Expand Up @@ -438,19 +471,30 @@ public void Smooth(TransformState from, TransformState to, float durationSeconds
m_CurrentSmoothTime = 0;
}

/// <summary>
/// Invoked just before the authoritative state is updated and pushed to non-authoritative instances.
/// </summary>
protected override void OnBeforeUpdateTransformState()
{
// this is called when new data comes from the server
m_LastAuthorityUpdateCounter = NetworkManager.AnticipationSystem.LastAnticipationAck;
m_OutstandingAuthorityChange = true;
}

/// <summary>
/// Invoked when the NetworkTransform state is updated.
/// </summary>
/// <param name="oldState">The previous state of the NetworkTransform.</param>
/// <param name="newState">The new state of the NetworkTransform.</param>
protected override void OnNetworkTransformStateUpdated(ref NetworkTransformState oldState, ref NetworkTransformState newState)
{
base.OnNetworkTransformStateUpdated(ref oldState, ref newState);
ApplyAuthoritativeState();
}

/// <summary>
/// Invoked whenever the transform has been updated.
/// </summary>
protected override void OnTransformUpdated()
{
if (CanCommitToTransform || m_AnticipatedObject == null)
Expand Down
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/Components/HalfVector3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ private void SerializeRead(FastBufferReader reader)
/// <summary>
/// The serialization implementation of <see cref="INetworkSerializable"/>.
/// </summary>
/// <typeparam name="T">The type of the serializer.</typeparam>
/// <param name="serializer">The serializer used to serialize or deserialize the state.</param>
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
if (serializer.IsReader)
Expand Down
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/Components/HalfVector4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ private void SerializeRead(FastBufferReader reader)
/// <summary>
/// The serialization implementation of <see cref="INetworkSerializable"/>.
/// </summary>
/// <typeparam name="T">The type of the serializer.</typeparam>
/// <param name="serializer">The serializer used to serialize or deserialize the state.</param>
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
if (serializer.IsReader)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public struct NetworkDeltaPosition : INetworkSerializable
/// <summary>
/// The serialization implementation of <see cref="INetworkSerializable"/>
/// </summary>
/// <typeparam name="T">The type of the serializer.</typeparam>
/// <param name="serializer">The serializer used to serialize or deserialize the state.</param>
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
if (!SynchronizeBase)
Expand Down Expand Up @@ -105,6 +107,7 @@ public Vector3 GetFullPosition()
/// <remarks>
/// Only applies to the authoritative side for <see cref="NetworkTransform"/> instances.
/// </remarks>
/// <returns>The current delta position as a <see cref="Vector3"/> with half float precision.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 GetConvertedDelta()
{
Expand All @@ -120,6 +123,7 @@ public Vector3 GetConvertedDelta()
/// Precision loss adjustments are one network tick behind on the
/// non-authoritative side.
/// </remarks>
/// <returns>The current delta position as a <see cref="Vector3"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 GetDeltaPosition()
{
Expand Down
65 changes: 42 additions & 23 deletions com.unity.netcode.gameobjects/Components/NetworkTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public class NetworkTransform : NetworkBehaviour
/// - The teleporting state update.
/// - When using half float precision and the `NetworkDeltaPosition` delta exceeds the maximum delta forcing the axis in
/// question to be collapsed into the core base position, this state update will be sent as reliable fragmented sequenced.
///
///
/// In order to preserve a continual consistency of axial values when unreliable delta messaging is enabled (due to the
/// possibility of dropping packets), NetworkTransform instances will send 1 axial frame synchronization update per
/// second (only for the axis marked to synchronize are sent as reliable fragmented sequenced) as long as a delta state
Expand Down Expand Up @@ -104,7 +104,7 @@ public struct NetworkTransformState : INetworkSerializable
private const int k_ReliableSequenced = 0x00080000;
private const int k_UseUnreliableDeltas = 0x00100000;
private const int k_UnreliableFrameSync = 0x00200000;
private const int k_TrackStateId = 0x10000000; // (Internal Debugging) When set each state update will contain a state identifier
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;
Expand Down Expand Up @@ -459,10 +459,11 @@ internal set
}

/// <summary>
/// Returns whether this state update was a frame synchronization when
/// UseUnreliableDeltas is enabled. When set, the entire transform will
/// Returns whether this state update was a frame synchronization when
/// UseUnreliableDeltas is enabled. When set, the entire transform will
/// be or has been synchronized.
/// </summary>
/// <returns><see cref="true"/> if this state update was a frame synchronization; otherwise, <see cref="false"/>.</returns>
public bool IsUnreliableFrameSync()
{
return UnreliableFrameSync;
Expand All @@ -475,6 +476,7 @@ public bool IsUnreliableFrameSync()
/// <remarks>
/// Unreliable delivery will only be used if <see cref="UseUnreliableDeltas"/> is set.
/// </remarks>
/// <returns><see cref="true"/> if this state was sent with reliable delivery; otherwise, <see cref="false"/>.</returns>
public bool IsReliableStateUpdate()
{
return ReliableSequenced;
Expand Down Expand Up @@ -586,7 +588,7 @@ public Quaternion GetRotation()
/// <remarks>
/// When there is no change in an updated state's position then there are no values to return.
/// Checking for <see cref="HasPositionChange"/> is one way to detect this.
/// When used with half precision it returns the half precision delta position state update
/// When used with half precision it returns the half precision delta position state update
/// which will not be the full position.
/// To get a NettworkTransform's full position, use <see cref="GetSpaceRelativePosition(bool)"/> and
/// pass true as the parameter.
Expand Down Expand Up @@ -661,6 +663,8 @@ public int GetNetworkTick()
/// <summary>
/// Serializes this <see cref="NetworkTransformState"/>
/// </summary>
/// <typeparam name="T">The type of the serializer.</typeparam>
/// <param name="serializer">The serializer used for reading or writing the state.</param>
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
// Used to calculate the LastSerializedSize value
Expand Down Expand Up @@ -897,7 +901,7 @@ public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReade
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
// then we want to serialize the LossyScale since NetworkObject spawn order is not guaranteed
if (IsTeleportingNextFrame && IsParented)
{
serializer.SerializeValue(ref LossyScale);
Expand Down Expand Up @@ -1419,9 +1423,8 @@ private bool ShouldSynchronizeHalfFloat(ulong targetClientId)
/// <remarks>
/// If a derived class overrides this, then make sure to invoke this base method!
/// </remarks>
/// <typeparam name="T"></typeparam>
/// <param name="serializer"></param>
/// <param name="targetClientId">the clientId being synchronized (both reading and writing)</param>
/// <typeparam name="T">The type of the serializer.</typeparam>
/// <param name="serializer">The serializer used for reading or writing the state.</param>
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
{
m_CachedNetworkManager = NetworkManager;
Expand Down Expand Up @@ -1575,11 +1578,11 @@ private void TryCommitTransform(ref Transform transformToCommit, bool synchroniz
Debug.LogException(ex);
}

// The below is part of assuring we only send a frame synch, when sending unreliable deltas, if
// The below is part of assuring we only send a frame synch, when sending unreliable deltas, if
// we have already sent at least one unreliable delta state update. At this point in the callstack,
// a delta state update has just been sent in the above UpdateTransformState() call and as long as
// we didn't send a frame synch and we are not synchronizing then we know at least one unreliable
// delta has been sent. Under this scenario, we should start checking for this instance's alloted
// delta has been sent. Under this scenario, we should start checking for this instance's alloted
// frame synch "tick slot". Once we send a frame synch, if no other deltas occur after that
// (i.e. the object is at rest) then we will stop sending frame synch's until the object begins
// moving, rotating, or scaling again.
Expand Down Expand Up @@ -1862,7 +1865,7 @@ private bool ApplyTransformToNetworkStateWithInfo(ref NetworkTransformState netw

networkState.NetworkDeltaPosition = m_HalfPositionState;

// If ownership offset is greater or we are doing an axial synchronization then synchronize the base position
// If ownership offset is greater or we are doing an axial synchronization then synchronize the base position
if ((m_HalfFloatTargetTickOwnership > m_CachedNetworkManager.ServerTime.Tick || isAxisSync) && !networkState.IsTeleportingNextFrame)
{
networkState.SynchronizeBaseHalfFloat = true;
Expand Down Expand Up @@ -2056,6 +2059,10 @@ private bool ApplyTransformToNetworkStateWithInfo(ref NetworkTransformState netw
return isDirty;
}

/// <summary>
/// Invoked whenever the transform has been updated.
/// This method can be overridden to handle any custom logic that needs to occur after the transform has been updated.
/// </summary>
protected virtual void OnTransformUpdated()
{

Expand Down Expand Up @@ -2602,6 +2609,10 @@ protected virtual void OnNetworkTransformStateUpdated(ref NetworkTransformState

}

/// <summary>
/// Invoked just before the transform state is updated.
/// This method can be overridden to handle any custom logic that needs to occur before the transform state is updated.
/// </summary>
protected virtual void OnBeforeUpdateTransformState()
{

Expand Down Expand Up @@ -2816,6 +2827,12 @@ public override void OnGainedOwnership()
base.OnGainedOwnership();
}

/// <summary>
/// Invoked when the ownership of the <see cref="NetworkObject"/> changes.
/// This method handles reinitialization when the local client gains or loses ownership of the <see cref="NetworkObject"/>.
/// </summary>
/// <param name="previous">The client ID of the previous owner.</param>
/// <param name="current">The client ID of the new owner.</param>
protected override void OnOwnershipChanged(ulong previous, ulong current)
{
// If we were the previous owner or the newly assigned owner then reinitialize
Expand All @@ -2842,7 +2859,7 @@ protected virtual void OnInitialize(ref NetworkTransformState replicatedState)
/// This method is only invoked by the owner
/// Use: OnInitialize(ref NetworkTransformState replicatedState) to be notified on all instances
/// </summary>
/// <param name="replicatedState"></param>
/// <param name="replicatedState">The current <see cref="NetworkVariable{NetworkTransformState}"/> after initializing.</param>
protected virtual void OnInitialize(ref NetworkVariable<NetworkTransformState> replicatedState)
{

Expand All @@ -2851,9 +2868,9 @@ protected virtual void OnInitialize(ref NetworkVariable<NetworkTransformState> r
private int m_HalfFloatTargetTickOwnership;

/// <summary>
/// The internal initialzation method to allow for internal API adjustments
/// The internal initialization method to allow for internal API adjustments
/// </summary>
/// <param name="isOwnershipChange"></param>
/// <param name="isOwnershipChange">Indicates whether the initialization is due to an ownership change.</param>
private void InternalInitialization(bool isOwnershipChange = false)
{
if (!IsSpawned)
Expand Down Expand Up @@ -2956,11 +2973,11 @@ public override void OnNetworkObjectParentChanged(NetworkObject parentNetworkObj
/// The parameters are broken up into pos / rot / scale on purpose so that the caller can perturb
/// just the desired one(s)
/// </summary>
/// <param name="posIn"></param> new position to move to. Can be null
/// <param name="rotIn"></param> new rotation to rotate to. Can be null
/// <param name="posIn">new position to move to. Can be null.</param>
/// <param name="rotIn">new rotation to rotate to. Can be null.</param>
/// <param name="scaleIn">new scale to scale to. Can be null</param>
/// <param name="teleportDisabled">When true (the default) the <see cref="NetworkObject"/> will not be teleported and, if enabled, will interpolate. When false the <see cref="NetworkObject"/> will teleport/apply the parameters provided immediately.</param>
/// <exception cref="Exception"></exception>
/// <exception cref="Exception">Thrown when the function is called on non-spawned object or, when it's called without proper authority</exception>
public void SetState(Vector3? posIn = null, Quaternion? rotIn = null, Vector3? scaleIn = null, bool teleportDisabled = true)
{
if (!IsSpawned)
Expand Down Expand Up @@ -3023,7 +3040,7 @@ private void SetStateInternal(Vector3 pos, Quaternion rot, Vector3 scale, bool s

var transformToCommit = transform;

// Explicit set states are cumulative during a fractional tick period of time (i.e. each SetState invocation will
// Explicit set states are cumulative during a fractional tick period of time (i.e. each SetState invocation will
// update the axial deltas to whatever changes are applied). As such, we need to preserve the dirty and explicit
// state flags.
var stateWasDirty = m_LocalAuthoritativeNetworkState.IsDirty;
Expand Down Expand Up @@ -3111,7 +3128,9 @@ private void UpdateInterpolation()
}
}

/// <inheritdoc/>
/// <summary>
/// This method is called once per frame.
/// </summary>
/// <remarks>
/// If you override this method, be sure that:
/// - Non-authority always invokes this base class method.
Expand All @@ -3134,10 +3153,10 @@ protected virtual void Update()
/// <summary>
/// Teleport the transform to the given values without interpolating
/// </summary>
/// <param name="newPosition"></param> new position to move to.
/// <param name="newRotation"></param> new rotation to rotate to.
/// <param name="newPosition">new position to move to.</param>
/// <param name="newRotation">new rotation to rotate to.</param>
/// <param name="newScale">new scale to scale to.</param>
/// <exception cref="Exception"></exception>
/// <exception cref="Exception">Thrown if teleporting is attempted on a non-authoritative side.</exception>
public void Teleport(Vector3 newPosition, Quaternion newRotation, Vector3 newScale)
{
if (!CanCommitToTransform)
Expand Down
Loading