Skip to content
Merged
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

- Fixed issue where a prefab added to a `NetworkPrefabsList` while a session was running was registered more than once and logged a duplicate `GlobalObjectIdHash` error. (#4184)

### Security

### Obsolete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ public abstract class NetworkRigidbodyBase : NetworkBehaviour
internal bool NetworkRigidbodyBaseExpanded;
#endif

// TODO-UNIFIED:
// Provide an option to automatically remove the NetworkRigidbodyBase component at runtime if it is a hybrid prefab that is spawned since this
// component is primarily used for the Rigidbody interpolation and extrapolation features of NetworkTransform which are not relevant for a hybrid
// prefab that is spawned since it will be using N4E's built in interpolation and extrapolation features. This greatly improves performance on
// the client side. If using N4E prediction or distributed authority mode, then Rigibody and any component derived from this should always be used.


/// <summary>
/// When enabled, the associated <see cref="NetworkTransform"/> will use the Rigidbody/Rigidbody2D to apply and synchronize changes in position, rotation, and
/// allows for the use of Rigidbody interpolation/extrapolation.
Expand Down Expand Up @@ -133,6 +126,13 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network
NetworkTransform = GetComponent<NetworkTransform>();
}

#if UNIFIED_NETCODE
if (InitializeHybrid())
{
return;
}
#endif

if (NetworkTransform != null)
{
NetworkTransform.RegisterRigidbody(this);
Expand Down Expand Up @@ -184,6 +184,13 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network
NetworkTransform = GetComponent<NetworkTransform>();
}

#if UNIFIED_NETCODE
if (InitializeHybrid())
{
return;
}
#endif

if (NetworkTransform != null)
{
NetworkTransform.RegisterRigidbody(this);
Expand All @@ -201,11 +208,6 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network
#endif

#if COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D
#if UNIFIED_NETCODE
// Used to keep track of the original kinematic state upon awake.
// (see OnDestroy below)
private bool m_OriginalKinematicState;
#endif
/// <summary>
/// Initializes the networked Rigidbody based on the <see cref="RigidbodyTypes"/>
/// passed in as a parameter.
Expand Down Expand Up @@ -248,6 +250,13 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network
NetworkTransform = GetComponent<NetworkTransform>();
}

#if UNIFIED_NETCODE
if (InitializeHybrid())
{
return;
}
#endif

if (NetworkTransform != null)
{
NetworkTransform.RegisterRigidbody(this);
Expand All @@ -259,31 +268,68 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network

if (AutoUpdateKinematicState)
{
#if UNIFIED_NETCODE
// Keep track of the original kinematic state. (see OnDestroy)
m_OriginalKinematicState = IsKinematic();
#endif
SetIsKinematic(true);
}
}
#endif

#if UNIFIED_NETCODE
// The authored kinematic state, restored in OnDestroy.
private bool m_OriginalKinematicState;

/// <summary>
/// Skips initialization on a hybrid prefab, whose GhostObject drives its motion. The component stays so <see cref="NetworkBehaviour.NetworkBehaviourId"/> values match on every peer.
/// </summary>
/// <returns>true for a hybrid prefab</returns>
private bool InitializeHybrid()
{
if (NetworkObject == null || !NetworkObject.HasGhost)
{
return false;
}

// Clears any registration left behind by a prior Initialize call.
if (NetworkTransform != null)
{
NetworkTransform.UnregisterRigidbody();
}

m_OriginalKinematicState = IsKinematic();
return true;
}

/// <summary>
/// Kinematic on every peer except the server. Called at spawn because an in-scene placed instance has no session during Awake.
/// </summary>
private void SetHybridKinematicState()
{
if (!m_LocalNetworkManager.IsServer)
{
SetIsKinematic(true);
}
}

/// <inheritdoc/>
public override void OnDestroy()
{
base.OnDestroy();
// If the user has left this component on their prefab and this is a hybrid prefab,
// then we want to set the rigid body back to its original kinematic settings since
// we are automatically destroying these components at runtime when it is a hybrid
// prefab that is spawned.
if (NetworkObject && NetworkObject.HasGhost)
if (!NetworkObject || !NetworkObject.HasGhost)
{
if (m_InternalRigidbody || m_InternalRigidbody2D)
{
SetIsKinematic(m_OriginalKinematicState);
}
return;
}
}
#if COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D
if (m_InternalRigidbody || m_InternalRigidbody2D)
#endif
#if COM_UNITY_MODULES_PHYSICS && !COM_UNITY_MODULES_PHYSICS2D
if (m_InternalRigidbody)
#endif
#if !COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D
if (m_InternalRigidbody2D)
#endif
{
SetIsKinematic(m_OriginalKinematicState);
}
}
#endif
internal Vector3 GetAdjustedPositionThreshold()
{
Expand Down Expand Up @@ -1019,7 +1065,12 @@ public void ResetInterpolation()

protected override void OnOwnershipChanged(ulong previous, ulong current)
{
UpdateOwnershipAuthority();
#if UNIFIED_NETCODE
if (!NetworkObject.HasGhost)
#endif
{
UpdateOwnershipAuthority();
}
base.OnOwnershipChanged(previous, current);
}

Expand Down Expand Up @@ -1062,6 +1113,13 @@ internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManag
/// <inheritdoc />
public override void OnNetworkSpawn()
{
#if UNIFIED_NETCODE
if (NetworkObject.HasGhost)
Comment thread
NoelStephensUnity marked this conversation as resolved.
{
SetHybridKinematicState();
return;
}
#endif
m_TickFrequency = 1.0f / m_LocalNetworkManager.NetworkConfig.TickRate;
m_TickRate = m_LocalNetworkManager.NetworkConfig.TickRate;
UpdateOwnershipAuthority();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ namespace Unity.Netcode.Components
/// mode of the <see cref="Rigidbody"/> and disabling it on all peers but the authoritative one.
/// </summary>
[RequireComponent(typeof(NetworkTransform))]
// TODO-UNIFIED: We should not require this for unified and come up with a different way of handling the dependency
#if !UNIFIED_NETCODE
[RequireComponent(typeof(Rigidbody))]
#endif
[AddComponentMenu("Netcode/Network Rigidbody")]
[HelpURL(HelpUrls.NetworkRigidbody)]
public class NetworkRigidbody : NetworkRigidbodyBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ namespace Unity.Netcode.Components
/// mode of the rigidbody and disabling it on all peers but the authoritative one.
/// </summary>
[RequireComponent(typeof(NetworkTransform))]
// TODO-UNIFIED: We should not require this for unified and come up with a different way of handling the dependency
#if !UNIFIED_NETCODE
[RequireComponent(typeof(Rigidbody2D))]
#endif
[AddComponentMenu("Netcode/Network Rigidbody 2D")]
[HelpURL(HelpUrls.NetworkRigidbody2D)]
public class NetworkRigidbody2D : NetworkRigidbodyBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1908,6 +1908,13 @@ private bool ShouldSynchronizeHalfFloat(ulong targetClientId)
/// </remarks>
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
{
#if UNIFIED_NETCODE
// No transform state is synchronized for a hybrid prefab.
if (NetworkObject.HasGhost)
{
return;
}
#endif
var targetClientId = m_TargetIdBeingSynchronized;
SynchronizeState = new NetworkTransformState()
{
Expand Down Expand Up @@ -3527,6 +3534,13 @@ private void NonAuthorityFinalizeSynchronization()
/// </summary>
protected internal override void InternalOnNetworkSessionSynchronized()
{
#if UNIFIED_NETCODE
// Nothing was synchronized for a hybrid prefab.
if (NetworkObject.HasGhost)
{
return;
}
#endif
NonAuthorityFinalizeSynchronization();

base.InternalOnNetworkSessionSynchronized();
Expand All @@ -3548,6 +3562,12 @@ private void ApplyPlayerTransformState()
/// </summary>
protected internal override void InternalOnNetworkPostSpawn()
{
#if UNIFIED_NETCODE
if (NetworkObject.HasGhost)
{
return;
}
#endif
// This is a special case for client-server where a server is spawning an owner authoritative NetworkObject but has yet to serialize anything.
// When the server detects that:
// - We are not in a distributed authority session (DAHost check).
Expand Down Expand Up @@ -3643,9 +3663,6 @@ internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManag
public override void OnNetworkSpawn()
{
#if UNIFIED_NETCODE
// TODO-UNIFIED:
// Provide a notification to users that NetworkTransform component will be removed at runtime if it is a hybrid prefab that is spawned since
// it will be using N4E's built in interpolation and extrapolation features.
if (NetworkObject.HasGhost)
{
return;
Expand Down Expand Up @@ -3743,9 +3760,7 @@ private void ResetInterpolatedStateToCurrentAuthoritativeState()
internal virtual void InternalInitialization(bool isOwnershipChange = false)
{
#if UNIFIED_NETCODE
// TODO-UNIFIED:
// Provide a notification to users that NetworkTransform component will be removed at runtime if it is a hybrid prefab that is spawned since
// it will be using N4E's built in interpolation and extrapolation features.
// Inert on a hybrid prefab, but kept so NetworkBehaviourId values match on every peer.
if (NetworkObject.HasGhost)
{
return;
Expand Down Expand Up @@ -3932,6 +3947,13 @@ private void DefaultParentChanged()

internal override void InternalOnNetworkObjectParentChanged(NetworkObject parentNetworkObject)
{
#if UNIFIED_NETCODE
// A hybrid prefab's transform space is driven by its GhostObject.
if (NetworkObject.HasGhost)
{
return;
}
#endif
if (!SwitchTransformSpaceWhenParented)
{
// Motion authority doesn't need to adjust anything
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,17 @@ internal void InitializePrefabs()
Prefabs.Initialize();
}

#if UNIFIED_NETCODE
/// <summary>
/// Registers any prefab list assigned after Awake, and returns false for a distributed authority session with a hybrid prefab registered.
/// </summary>
internal bool InitializePrefabsForStart()
{
InitializePrefabs();
return NetworkTopology != NetworkTopologyTypes.DistributedAuthority || Prefabs.ValidateForDistributedAuthority();
}
#endif

[NonSerialized]
private bool m_DidWarnOldPrefabList = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
/// Class that represents a NetworkPrefab
/// </summary>
[Serializable]
[System.Diagnostics.DebuggerDisplay("{GetDebugName()}")]
public class NetworkPrefab
{
/// <summary>
Expand Down Expand Up @@ -154,7 +155,8 @@
/// <returns>True if the NetworkPrefab is valid and ready for use, false otherwise</returns>
public bool Validate(int index = -1)
{
NetworkObject networkObject;
// Null for a hash override.
NetworkObject networkObject = null;
if (Override == NetworkPrefabOverride.None)
{
if (Prefab == null)
Expand Down Expand Up @@ -270,9 +272,43 @@
return false;
}

#if UNIFIED_NETCODE
// N4E spawns the ghost's own prefab on every client, so the override would never be applied.
if ((networkObject != null && networkObject.HasGhost)
|| (OverridingTargetPrefab.TryGetComponent(out NetworkObject targetNetworkObject) && targetNetworkObject.HasGhost))
{
NetworkLog.LogError($"{HybridPrefabOverrideError} {GetDebugName()} (entry will be ignored).");
return false;
}
#endif
return true;
}

#if UNIFIED_NETCODE
internal const string HybridPrefabOverrideError = "NetworkPrefab overrides are not supported for hybrid prefabs yet.";
#endif

/// <summary>
/// Names the prefab, including its override target, for logs and the debugger.
/// </summary>
internal string GetDebugName()
{
switch (Override)

Check warning on line 296 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs#L295-L296

Added lines #L295 - L296 were not covered by tests
{
case NetworkPrefabOverride.Prefab:
return $"{GetName(SourcePrefabToOverride)} (overridden by {GetName(OverridingTargetPrefab)})";

Check warning on line 299 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs#L299

Added line #L299 was not covered by tests
case NetworkPrefabOverride.Hash:
return $"{SourceHashToOverride} (overridden by {GetName(OverridingTargetPrefab)})";

Check warning on line 301 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs#L301

Added line #L301 was not covered by tests
default:
return GetName(Prefab);

Check warning on line 303 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs#L303

Added line #L303 was not covered by tests
}
}

Check warning on line 305 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs#L305

Added line #L305 was not covered by tests

private static string GetName(GameObject prefab)
{
return prefab != null ? prefab.name : "null";
}

Check warning on line 310 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs#L308-L310

Added lines #L308 - L310 were not covered by tests

/// <summary>
/// Returns a string representation of this NetworkPrefab's source and target hash values
/// </summary>
Expand Down
Loading
Loading