From 43906be1af85d046cdf2453ad9e8707fc22b5a46 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Thu, 22 May 2025 18:14:47 -0500 Subject: [PATCH 01/43] update Adding AttachableBehaviour and ObjectController. --- .../Components/Helpers/AttachableBehaviour.cs | 146 ++++++++++++++++++ .../Helpers/AttachableBehaviour.cs.meta | 2 + .../Components/Helpers/ObjectController.cs | 119 ++++++++++++++ .../Helpers/ObjectController.cs.meta | 2 + 4 files changed, 269 insertions(+) create mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs new file mode 100644 index 0000000000..db21ea110c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -0,0 +1,146 @@ +using System; +using UnityEngine; + +namespace Unity.Netcode.Components +{ + /// <summary> + /// Handles parenting the <see cref="GameObject"/> that is a child under a root network prefab + /// without having to use the same <see cref="NetworkObject"/> parenting rules. + /// </summary> + public class AttachableBehaviour : NetworkBehaviour + { + /// <summary> + /// Invoked just prior to the parent being applied. + /// </summary> + /// <remarks> + /// The <see cref="NetworkBehaviour"/> parameter passed into a susbcriber callback will be either null or a valid. <br /> + /// When null, the parent is being unapplied/removed from the <see cref="GameObject"/> this <see cref="AttachableBehaviour"/> instance is attached to. + /// </remarks> + public event Action<NetworkBehaviour> ParentIsBeingApplied; + + private NetworkVariable<NetworkBehaviourReference> m_AppliedParent = new NetworkVariable<NetworkBehaviourReference>(new NetworkBehaviourReference(null)); + private GameObject m_DefaultParent; + private Vector3 m_OriginalLocalPosition; + private Quaternion m_OriginalLocalRotation; + + /// <summary> + /// Will be true when this <see href="AttachableBehaviour"/> instance has a parent applied to it.<br /> + /// Will be false when this <see href="AttachableBehaviour"/> instance does not have a parent applied to it.<br /> + /// </summary> + public bool ParentIsApplied { get; private set; } + + /// <inheritdoc/> + protected virtual void Awake() + { + m_DefaultParent = transform.parent == null ? gameObject : transform.parent.gameObject; + m_OriginalLocalPosition = transform.localPosition; + m_OriginalLocalRotation = transform.localRotation; + } + + /// <inheritdoc/> + protected override void OnNetworkPostSpawn() + { + base.OnNetworkPostSpawn(); + if (HasAuthority) + { + m_AppliedParent.Value = new NetworkBehaviourReference(null); + } + UpdateParent(); + m_AppliedParent.OnValueChanged += OnAppliedParentChanged; + } + + /// <inheritdoc/> + public override void OnNetworkDespawn() + { + ResetToDefault(); + base.OnNetworkDespawn(); + } + + private void OnAppliedParentChanged(NetworkBehaviourReference previous, NetworkBehaviourReference current) + { + UpdateParent(); + } + + private void UpdateParent() + { + var parent = (NetworkBehaviour)null; + if (m_AppliedParent.Value.TryGet(out parent)) + { + ParentIsApplied = true; + transform.SetParent(parent.gameObject.transform, false); + } + else + { + ParentIsApplied = false; + ResetToDefault(); + } + + OnParentUpdated(parent); + } + + private void ResetToDefault() + { + if (m_DefaultParent != null) + { + transform.SetParent(m_DefaultParent.transform, false); + } + transform.localPosition = m_OriginalLocalPosition; + transform.localRotation = m_OriginalLocalRotation; + } + + /// <summary> + /// Invoked after the parent has been applied.<br /> + /// </summary> + /// <remarks> + /// The <param name="parent"/> can be either null or a valid <see cref="NetworkBehaviour"/>. <br /> + /// When null, the parent is being unapplied/removed from the <see cref="GameObject"/> this <see cref="AttachableBehaviour"/> instance is attached to. + /// </remarks> + /// <param name="parent">The <see cref="NetworkBehaviour"/> that is applied or null if it is no longer applied.</param> + protected virtual void OnParentUpdated(NetworkBehaviour parent) + { + + } + + /// <summary> + /// Invoked just prior to the parent being applied. <br /> + /// This is a good time to handle disabling or enabling <see cref="Object"/>s using an <see cref="ObjectController"/>. + /// </summary> + /// <remarks> + /// The <param name="parent"/> can be either null or a valid <see cref="NetworkBehaviour"/>. <br /> + /// When null, the parent is being unapplied/removed from the <see cref="GameObject"/> this <see cref="AttachableBehaviour"/> instance is attached to. + /// </remarks> + /// <param name="parent">The <see cref="NetworkBehaviour"/> that is applied or null if it is no longer applied.</param> + protected virtual void OnParentBeingApplied(NetworkBehaviour parent) + { + + } + + /// <summary> + /// Applies a parent to a nested <see cref="NetworkBehaviour"/> and all <see cref="GameObject"/> children + /// of the nested <see cref="NetworkBehaviour"/>. + /// </summary> + /// <param name="parent">The <see cref="NetworkBehaviour"/> to be applied or null to reparent under its original <see cref="GameObject"/> when spawned.</param> + public void ApplyParent(NetworkBehaviour parent) + { + if (!IsSpawned) + { + Debug.LogError($"[{name}][Not Spawned] Can only have a parent applied when it is spawned!"); + return; + } + + if (!HasAuthority) + { + Debug.LogError($"[{name}][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!"); + return; + } + // Notify any subscriptions + ParentIsBeingApplied?.Invoke(parent); + + // Invoke for any overrides + OnParentBeingApplied(parent); + + // Once everything has been notified that we are applying a parent...apply the parent. + m_AppliedParent.Value = new NetworkBehaviourReference(parent); + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs.meta new file mode 100644 index 0000000000..ade010ae67 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7aa87489bfc51d448940c66c2a2cf840 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs new file mode 100644 index 0000000000..10871b4b71 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs @@ -0,0 +1,119 @@ +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Unity.Netcode.Components +{ + /// <summary> + /// Handles enabling or disabling commonly used components, behaviours, RenderMeshes, etc.<br /> + /// Anything that derives from <see cref="Object"/> and has an enabled property can be added + /// to the list of objects.<br /> + /// This also synchronizes the enabling or disabling of the objects with connected and late + /// joining clients. + /// </summary> + public class ObjectController : NetworkBehaviour + { + /// <summary> + /// Determines whether the selected <see cref="Object"/>s will start out enabled or disabled. + /// </summary> + public bool InitialState; + + /// <summary> + /// The list of <see cref="Object"/>s to be enabled and disabled. + /// </summary> + public List<Object> Objects; + + private Dictionary<Object, PropertyInfo> m_ValidObjects = new Dictionary<Object, PropertyInfo>(); + private NetworkVariable<bool> m_IsEnabled = new NetworkVariable<bool>(); + + /// <inheritdoc/> + protected virtual void Awake() + { + var emptyEntries = 0; + foreach (var someObject in Objects) + { + if (someObject == null) + { + emptyEntries++; + continue; + } + var propertyInfo = someObject.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) + { + m_ValidObjects.Add(someObject, propertyInfo); + } + else + { + Debug.LogWarning($"{name} does not contain a public enable property! (Ignoring)"); + } + } + if (emptyEntries > 0) + { + Debug.LogWarning($"{name} has {emptyEntries} emtpy(null) entries in the Objects list!"); + } + else + { + Debug.Log($"{name} has {m_ValidObjects.Count} valid object entries."); + } + } + + /// <inheritdoc/> + public override void OnNetworkSpawn() + { + if (HasAuthority) + { + m_IsEnabled.Value = InitialState; + } + base.OnNetworkSpawn(); + } + + /// <inheritdoc/> + protected override void OnNetworkPostSpawn() + { + m_IsEnabled.OnValueChanged += OnEnabledChanged; + ApplyEnabled(m_IsEnabled.Value); + base.OnNetworkPostSpawn(); + } + + /// <inheritdoc/> + public override void OnNetworkDespawn() + { + m_IsEnabled.OnValueChanged -= OnEnabledChanged; + base.OnNetworkDespawn(); + } + + private void OnEnabledChanged(bool previous, bool current) + { + ApplyEnabled(current); + } + + private void ApplyEnabled(bool enabled) + { + foreach (var entry in m_ValidObjects) + { + entry.Value.SetValue(entry.Key, enabled); + } + } + + /// <summary> + /// Invoke on the authority side to enable or disable the <see cref="Objects"/>. + /// </summary> + /// <param name="isEnabled">true = enabled | false = disabled</param> + public void SetEnabled(bool isEnabled) + { + if (!IsSpawned) + { + Debug.Log($"[{name}] Must be spawned to use {nameof(SetEnabled)}!"); + return; + } + + if (!HasAuthority) + { + Debug.Log($"[Client-{NetworkManager.LocalClientId}] Attempting to invoke {nameof(SetEnabled)} without authority!"); + return; + } + m_IsEnabled.Value = isEnabled; + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs.meta new file mode 100644 index 0000000000..e7482640fa --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4d0b25b3f95e5324abc80c09cb29f271 \ No newline at end of file From 6267a3e5700101b65a66a62736410aa774f6c811 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 28 May 2025 11:20:38 -0500 Subject: [PATCH 02/43] update Renaming ObjectController to ComponentController. Added some additional validation checking and handling. Updated XML API. --- .../Components/Helpers/AttachableBehaviour.cs | 41 +++- .../Components/Helpers/ComponentController.cs | 197 ++++++++++++++++++ ...er.cs.meta => ComponentController.cs.meta} | 0 .../Components/Helpers/ObjectController.cs | 119 ----------- 4 files changed, 236 insertions(+), 121 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs rename com.unity.netcode.gameobjects/Runtime/Components/Helpers/{ObjectController.cs.meta => ComponentController.cs.meta} (100%) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index db21ea110c..ae423c5c57 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -1,14 +1,50 @@ using System; +#if UNITY_EDITOR +using UnityEditor; +#endif using UnityEngine; namespace Unity.Netcode.Components { /// <summary> - /// Handles parenting the <see cref="GameObject"/> that is a child under a root network prefab - /// without having to use the same <see cref="NetworkObject"/> parenting rules. + /// Handles parenting of the <see cref="GameObject"/> this component is attached to and is a nested <see cref="NetworkBehaviour"/>.<br /> + /// The <see cref="GameObject"/> can reside under a parent <see cref="NetworkObject"/> or some higher generational parent. /// </summary> public class AttachableBehaviour : NetworkBehaviour { +#if UNITY_EDITOR + /// <inheritdoc/> + /// <remarks> + /// In the event an <see cref="AttachableBehaviour"/> is placed on the same <see cref="GameObject"/> + /// as the <see cref="NetworkObject"/>, this will automatically create a child and add an + /// <see cref="AttachableBehaviour"/> to that. + /// </remarks> + protected virtual void OnValidate() + { + var networkObject = gameObject.GetComponentInParent<NetworkObject>(); + if (!networkObject) + { + networkObject = gameObject.GetComponent<NetworkObject>(); + } + if (networkObject && networkObject.gameObject == gameObject) + { + Debug.LogWarning($"[{name}][{nameof(AttachableBehaviour)}] Cannot be placed on the same {nameof(GameObject)} as the {nameof(NetworkObject)}!"); + // Wait for the next editor update to create a nested child and add the AttachableBehaviour + EditorApplication.update += CreatedNestedChild; + } + } + + private void CreatedNestedChild() + { + EditorApplication.update -= CreatedNestedChild; + var childGameObject = new GameObject($"{name}-Child"); + childGameObject.transform.parent = transform; + childGameObject.AddComponent<AttachableBehaviour>(); + Debug.Log($"[{name}][Created Child] Adding {nameof(AttachableBehaviour)} to newly created child {childGameObject.name}."); + DestroyImmediate(this); + } +#endif + /// <summary> /// Invoked just prior to the parent being applied. /// </summary> @@ -133,6 +169,7 @@ public void ApplyParent(NetworkBehaviour parent) Debug.LogError($"[{name}][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!"); return; } + // Notify any subscriptions ParentIsBeingApplied?.Invoke(parent); diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs new file mode 100644 index 0000000000..2952f13139 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -0,0 +1,197 @@ +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Unity.Netcode.Components +{ + /// <summary> + /// Handles enabling or disabling commonly used components, behaviours, RenderMeshes, etc.<br /> + /// Anything that derives from <see cref="Component"/> and has an enabled property can be added + /// to the list of objects.<br /> + /// <see cref="NetworkBehaviour"/> derived components are not allowed and will be automatically removed. + /// </summary> + /// <remarks> + /// This will synchronize the enabled or disabled state of the <see cref="Component"/>s with + /// connected and late joining clients. + /// </remarks> + public class ComponentController : NetworkBehaviour + { + /// <summary> + /// Determines whether the selected <see cref="Components"/>s will start enabled or disabled when spawned. + /// </summary> + [Tooltip("The initial state of the components when spawned.")] + public bool InitialState = true; + + /// <summary> + /// The list of <see cref="Components"/>s to be enabled and disabled. + /// </summary> + [Tooltip("The list of components to control. You can drag and drop an entire GameObject on this to include all components.")] + public List<Object> Components; + + private Dictionary<Component, PropertyInfo> m_ValidComponents = new Dictionary<Component, PropertyInfo>(); + private NetworkVariable<bool> m_IsEnabled = new NetworkVariable<bool>(); + +#if UNITY_EDITOR + /// <inheritdoc/> + /// <remarks> + /// Checks for invalid <see cref="Object"/> entries. + /// </remarks> + protected virtual void OnValidate() + { + if (Components == null || Components.Count == 0) + { + return; + } + + var gameObjectsToScan = new List<GameObject>(); + for (int i = Components.Count - 1; i >= 0; i--) + { + if (Components[i] == null) + { + continue; + } + var componentType = Components[i].GetType(); + if (componentType == typeof(GameObject)) + { + gameObjectsToScan.Add(Components[i] as GameObject); + Components.RemoveAt(i); + continue; + } + + if (componentType.IsSubclassOf(typeof(NetworkBehaviour))) + { + Debug.LogWarning($"Removing {Components[i].name} since {nameof(NetworkBehaviour)}s are not allowed to be controlled by this component."); + Components.RemoveAt(i); + continue; + } + + var propertyInfo = Components[i].GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + if (propertyInfo == null && propertyInfo.PropertyType != typeof(bool)) + { + Debug.LogWarning($"{Components[i].name} does not contain a public enabled property! (Removing)"); + Components.RemoveAt(i); + } + } + + foreach (var entry in gameObjectsToScan) + { + var components = entry.GetComponents<Component>(); + foreach (var component in components) + { + // Ignore any NetworkBehaviour derived components + if (component.GetType().IsSubclassOf(typeof(NetworkBehaviour))) + { + continue; + } + + var propertyInfo = component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) + { + Components.Add(component); + } + } + } + gameObjectsToScan.Clear(); + } +#endif + + /// <inheritdoc/> + /// <remarks> + /// Also checks to assure all <see cref="Component"/> entries are valid and creates a final table of + /// <see cref="Component"/>s paired to their <see cref="PropertyInfo"/>. + /// </remarks> + protected virtual void Awake() + { + var emptyEntries = 0; + foreach (var someObject in Components) + { + if (someObject == null) + { + emptyEntries++; + continue; + } + var propertyInfo = someObject.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) + { + m_ValidComponents.Add(someObject as Component, propertyInfo); + } + else + { + Debug.LogWarning($"{name} does not contain a public enable property! (Ignoring)"); + } + } + if (emptyEntries > 0) + { + Debug.LogWarning($"{name} has {emptyEntries} emtpy(null) entries in the {nameof(Components)} list!"); + } + else + { + Debug.Log($"{name} has {m_ValidComponents.Count} valid {nameof(Component)} entries."); + } + } + + /// <inheritdoc/> + public override void OnNetworkSpawn() + { + if (HasAuthority) + { + m_IsEnabled.Value = InitialState; + } + base.OnNetworkSpawn(); + } + + /// <inheritdoc/> + /// <remarks> + /// Assures all instances subscribe to the internal <see cref="NetworkVariable{T}"/> of type + /// <see cref="bool"/> that synchronizes all instances when <see cref="Object"/>s are enabled + /// or disabled. + /// </remarks> + protected override void OnNetworkPostSpawn() + { + m_IsEnabled.OnValueChanged += OnEnabledChanged; + ApplyEnabled(m_IsEnabled.Value); + base.OnNetworkPostSpawn(); + } + + /// <inheritdoc/> + public override void OnNetworkDespawn() + { + m_IsEnabled.OnValueChanged -= OnEnabledChanged; + base.OnNetworkDespawn(); + } + + private void OnEnabledChanged(bool previous, bool current) + { + ApplyEnabled(current); + } + + private void ApplyEnabled(bool enabled) + { + foreach (var entry in m_ValidComponents) + { + entry.Value.SetValue(entry.Key, enabled); + } + } + + /// <summary> + /// Invoke on the authority side to enable or disable the <see cref="Object"/>s. + /// </summary> + /// <param name="isEnabled">true = enabled | false = disabled</param> + public void SetEnabled(bool isEnabled) + { + if (!IsSpawned) + { + Debug.Log($"[{name}] Must be spawned to use {nameof(SetEnabled)}!"); + return; + } + + if (!HasAuthority) + { + Debug.Log($"[Client-{NetworkManager.LocalClientId}] Attempting to invoke {nameof(SetEnabled)} without authority!"); + return; + } + m_IsEnabled.Value = isEnabled; + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs.meta similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs deleted file mode 100644 index 10871b4b71..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace Unity.Netcode.Components -{ - /// <summary> - /// Handles enabling or disabling commonly used components, behaviours, RenderMeshes, etc.<br /> - /// Anything that derives from <see cref="Object"/> and has an enabled property can be added - /// to the list of objects.<br /> - /// This also synchronizes the enabling or disabling of the objects with connected and late - /// joining clients. - /// </summary> - public class ObjectController : NetworkBehaviour - { - /// <summary> - /// Determines whether the selected <see cref="Object"/>s will start out enabled or disabled. - /// </summary> - public bool InitialState; - - /// <summary> - /// The list of <see cref="Object"/>s to be enabled and disabled. - /// </summary> - public List<Object> Objects; - - private Dictionary<Object, PropertyInfo> m_ValidObjects = new Dictionary<Object, PropertyInfo>(); - private NetworkVariable<bool> m_IsEnabled = new NetworkVariable<bool>(); - - /// <inheritdoc/> - protected virtual void Awake() - { - var emptyEntries = 0; - foreach (var someObject in Objects) - { - if (someObject == null) - { - emptyEntries++; - continue; - } - var propertyInfo = someObject.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); - if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) - { - m_ValidObjects.Add(someObject, propertyInfo); - } - else - { - Debug.LogWarning($"{name} does not contain a public enable property! (Ignoring)"); - } - } - if (emptyEntries > 0) - { - Debug.LogWarning($"{name} has {emptyEntries} emtpy(null) entries in the Objects list!"); - } - else - { - Debug.Log($"{name} has {m_ValidObjects.Count} valid object entries."); - } - } - - /// <inheritdoc/> - public override void OnNetworkSpawn() - { - if (HasAuthority) - { - m_IsEnabled.Value = InitialState; - } - base.OnNetworkSpawn(); - } - - /// <inheritdoc/> - protected override void OnNetworkPostSpawn() - { - m_IsEnabled.OnValueChanged += OnEnabledChanged; - ApplyEnabled(m_IsEnabled.Value); - base.OnNetworkPostSpawn(); - } - - /// <inheritdoc/> - public override void OnNetworkDespawn() - { - m_IsEnabled.OnValueChanged -= OnEnabledChanged; - base.OnNetworkDespawn(); - } - - private void OnEnabledChanged(bool previous, bool current) - { - ApplyEnabled(current); - } - - private void ApplyEnabled(bool enabled) - { - foreach (var entry in m_ValidObjects) - { - entry.Value.SetValue(entry.Key, enabled); - } - } - - /// <summary> - /// Invoke on the authority side to enable or disable the <see cref="Objects"/>. - /// </summary> - /// <param name="isEnabled">true = enabled | false = disabled</param> - public void SetEnabled(bool isEnabled) - { - if (!IsSpawned) - { - Debug.Log($"[{name}] Must be spawned to use {nameof(SetEnabled)}!"); - return; - } - - if (!HasAuthority) - { - Debug.Log($"[Client-{NetworkManager.LocalClientId}] Attempting to invoke {nameof(SetEnabled)} without authority!"); - return; - } - m_IsEnabled.Value = isEnabled; - } - } -} From 83ee6509b9ff175d735f73e5572aa63412366e29 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 28 May 2025 11:21:30 -0500 Subject: [PATCH 03/43] update Adding helpers meta. --- .../Runtime/Components/Helpers.meta | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers.meta new file mode 100644 index 0000000000..28b2944c3d --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fbc8738cd4ff119499520131b7aa7232 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 1832d21e2dd83a5a73baedaae1e2bc3c50e34cc2 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Mon, 23 Jun 2025 19:36:07 -0500 Subject: [PATCH 04/43] refactor Adding an AttachableNode as the target for AttachableBehaviour. Refactoring AttachableBehaviour. --- .../Components/Helpers/AttachableBehaviour.cs | 313 ++++++++++++++---- .../Components/Helpers/AttachableNode.cs | 77 +++++ .../Components/Helpers/AttachableNode.cs.meta | 2 + 3 files changed, 331 insertions(+), 61 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index ae423c5c57..57afe67707 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -7,9 +7,19 @@ namespace Unity.Netcode.Components { /// <summary> - /// Handles parenting of the <see cref="GameObject"/> this component is attached to and is a nested <see cref="NetworkBehaviour"/>.<br /> - /// The <see cref="GameObject"/> can reside under a parent <see cref="NetworkObject"/> or some higher generational parent. + /// Attachable NetworkBehaviours<br/> + /// This component handles the parenting synchronization of the <see cref="GameObject"/> that this component is attached to.<br /> + /// under another <see cref="NetworkBehaviour"/>'s <see cref="GameObject"/>.<br /> + /// The <see cref="GameObject"/> to be parented must have this component attached to it and must be nested on any child <see cref="GameObject"/> under the <see cref="NetworkObject"/>'s <see cref="GameObject"/>.<br /> + /// The <see cref="GameObject"/> target parent must have an <see cref="AttachableNode"/> component attached to it and must belong to a + /// different <see cref="NetworkObject"/> than that of the <see cref="AttachableBehaviour"/>'s. /// </summary> + /// <remarks> + /// The term "attach" is used in place of parenting in order to distinguish between <see cref="NetworkObject"/> parenting and + /// <see cref="AttachableBehaviour"/> parenting ("attaching" and "detaching").<br /> + /// This component can be used along with one or more <see cref="ComponentController"/> in order to enable or disable different components depending + /// upon the <see cref="AttachableBehaviour"/> instance's current state. + /// </remarks> public class AttachableBehaviour : NetworkBehaviour { #if UNITY_EDITOR @@ -46,138 +56,319 @@ private void CreatedNestedChild() #endif /// <summary> - /// Invoked just prior to the parent being applied. + /// Invoked when the <see cref="AttachState"/> of this instance has changed. /// </summary> - /// <remarks> - /// The <see cref="NetworkBehaviour"/> parameter passed into a susbcriber callback will be either null or a valid. <br /> - /// When null, the parent is being unapplied/removed from the <see cref="GameObject"/> this <see cref="AttachableBehaviour"/> instance is attached to. - /// </remarks> - public event Action<NetworkBehaviour> ParentIsBeingApplied; + public event Action<AttachState, AttachableNode> AttachStateChange; - private NetworkVariable<NetworkBehaviourReference> m_AppliedParent = new NetworkVariable<NetworkBehaviourReference>(new NetworkBehaviourReference(null)); - private GameObject m_DefaultParent; - private Vector3 m_OriginalLocalPosition; - private Quaternion m_OriginalLocalRotation; + /// <summary> + /// The various states of <see cref="AttachableBehaviour"/>. + /// </summary> + public enum AttachState + { + /// <summary> + /// The <see cref="AttachableBehaviour"/> instance is not attached to anything. + /// When not attached to anything, the instance will be parented under the original + /// <see cref="GameObject"/>. + /// </summary> + Detached, + /// <summary> + /// The <see cref="AttachableBehaviour"/> instance is attaching to an <see cref="AttachableNode"/>. + /// </summary> + /// <remarks> + /// One example usage:<br /> + /// When using an <see cref="AttachableBehaviour"/> with one or more <see cref="ComponentController"/> component(s), + /// this would be a good time to enable or disable components. + /// </remarks> + Attaching, + /// <summary> + /// The <see cref="AttachableBehaviour"/> instance is attached to an <see cref="AttachableNode"/>. + /// </summary> + /// <remarks> + /// This would be a good time to apply any additional local position or rotation values to this <see cref="AttachableBehaviour"/> instance. + /// </remarks> + Attached, + /// <summary> + /// The <see cref="AttachableBehaviour"/> instance is detaching from an <see cref="AttachableNode"/>. + /// </summary> + /// <remarks> + /// One example usage:<br /> + /// When using an <see cref="AttachableBehaviour"/> with one or more <see cref="ComponentController"/> component(s), + /// this would be a good time to enable or disable components. + /// </remarks> + Detaching + } + + /// <summary> + /// The current <see cref="AttachableBehaviour"/> instance's <see cref="AttachState"/>. + /// </summary> + protected AttachState m_AttachState { get; private set; } /// <summary> - /// Will be true when this <see href="AttachableBehaviour"/> instance has a parent applied to it.<br /> - /// Will be false when this <see href="AttachableBehaviour"/> instance does not have a parent applied to it.<br /> + /// The original parent of this <see cref="AttachableBehaviour"/> instance. /// </summary> - public bool ParentIsApplied { get; private set; } + protected GameObject m_DefaultParent { get; private set; } + + /// <summary> + /// If attached, attaching, or detaching this will be the <see cref="AttachableNode"/> this <see cref="AttachableBehaviour"/> instance is attached to. + /// </summary> + protected AttachableNode m_AttachableNode { get; private set; } + + private NetworkVariable<NetworkBehaviourReference> m_AttachedNodeReference = new NetworkVariable<NetworkBehaviourReference>(new NetworkBehaviourReference(null)); + private Vector3 m_OriginalLocalPosition; + private Quaternion m_OriginalLocalRotation; /// <inheritdoc/> + /// <remarks> + /// If you create a custom <see cref="AttachableBehaviour"/> and override this method, you must invoke + /// this base instance of <see cref="Awake"/>. + /// </remarks> protected virtual void Awake() { m_DefaultParent = transform.parent == null ? gameObject : transform.parent.gameObject; m_OriginalLocalPosition = transform.localPosition; m_OriginalLocalRotation = transform.localRotation; + m_AttachState = AttachState.Detached; + m_AttachableNode = null; } /// <inheritdoc/> + /// <remarks> + /// If you create a custom <see cref="AttachableBehaviour"/> and override this method, you must invoke + /// this base instance of <see cref="OnNetworkPostSpawn"/>. + /// </remarks> protected override void OnNetworkPostSpawn() { - base.OnNetworkPostSpawn(); if (HasAuthority) { - m_AppliedParent.Value = new NetworkBehaviourReference(null); + m_AttachedNodeReference.Value = new NetworkBehaviourReference(null); } - UpdateParent(); - m_AppliedParent.OnValueChanged += OnAppliedParentChanged; + m_AttachedNodeReference.OnValueChanged += OnAttachedNodeReferenceChanged; + base.OnNetworkPostSpawn(); + } + + /// <inheritdoc/> + /// <remarks> + /// If you create a custom <see cref="AttachableBehaviour"/> and override this method, you will want to + /// invoke this base instance of <see cref="OnNetworkSessionSynchronized"/> if you want the current + /// state to have been applied before executing the derived class's <see cref="OnNetworkSessionSynchronized"/> + /// script. + /// </remarks> + protected override void OnNetworkSessionSynchronized() + { + UpdateAttachedState(); + base.OnNetworkSessionSynchronized(); } /// <inheritdoc/> public override void OnNetworkDespawn() { - ResetToDefault(); + m_AttachedNodeReference.OnValueChanged -= OnAttachedNodeReferenceChanged; + InternalDetach(); + if (NetworkManager && !NetworkManager.ShutdownInProgress) + { + // Notify of the changed attached state + UpdateAttachState(m_AttachState, m_AttachableNode); + } base.OnNetworkDespawn(); } - private void OnAppliedParentChanged(NetworkBehaviourReference previous, NetworkBehaviourReference current) + private void OnAttachedNodeReferenceChanged(NetworkBehaviourReference previous, NetworkBehaviourReference current) { - UpdateParent(); + UpdateAttachedState(); } - private void UpdateParent() + private void UpdateAttachedState() { - var parent = (NetworkBehaviour)null; - if (m_AppliedParent.Value.TryGet(out parent)) + var attachableNode = (AttachableNode)null; + var shouldParent = m_AttachedNodeReference.Value.TryGet(out attachableNode, NetworkManager); + var preState = shouldParent ? AttachState.Attaching : AttachState.Detaching; + var preNode = shouldParent ? attachableNode : m_AttachableNode; + shouldParent = shouldParent && attachableNode != null; + + if (shouldParent && m_AttachableNode != null && m_AttachState == AttachState.Attached) { - ParentIsApplied = true; - transform.SetParent(parent.gameObject.transform, false); + // If we are attached to some other AttachableNode, then detach from that before attaching to a new one. + if (m_AttachableNode != attachableNode) + { + // Run through the same process without being triggerd by a NetVar update. + UpdateAttachState(AttachState.Detaching, m_AttachableNode); + m_AttachableNode.Detach(this); + transform.parent = null; + UpdateAttachState(AttachState.Detached, null); + } + } + + // Change the state to attaching or detaching + UpdateAttachState(preState, preNode); + + if (shouldParent) + { + InternalAttach(attachableNode); } else { - ParentIsApplied = false; - ResetToDefault(); + InternalDetach(); + } + + // Notify of the changed attached state + UpdateAttachState(m_AttachState, m_AttachableNode); + } + + /// <summary> + /// For customized/derived <see cref="AttachableBehaviour"/>s, override this method to receive notifications + /// when the <see cref="AttachState"/> has changed. + /// </summary> + /// <param name="attachState">the new <see cref="AttachState"/>.</param> + /// <param name="attachableNode"></param> + protected virtual void OnAttachStateChanged(AttachState attachState, AttachableNode attachableNode) + { + + } + + /// <summary> + /// Update the attached state. + /// </summary> + private void UpdateAttachState(AttachState attachState, AttachableNode attachableNode) + { + try + { + AttachStateChange?.Invoke(attachState, attachableNode); + } + catch (Exception ex) + { + Debug.LogException(ex); } - OnParentUpdated(parent); + try + { + OnAttachStateChanged(attachState, attachableNode); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } - private void ResetToDefault() + /// <summary> + /// Internal attach method that just handles changing state, parenting, and sending the <see cref="AttachableNode"/> a + /// notification that an <see cref="AttachableBehaviour"/> has attached. + /// </summary> + internal void InternalAttach(AttachableNode attachableNode) { - if (m_DefaultParent != null) + if (attachableNode.NetworkManager != NetworkManager) { - transform.SetParent(m_DefaultParent.transform, false); + Debug.Log("Blam!"); } - transform.localPosition = m_OriginalLocalPosition; - transform.localRotation = m_OriginalLocalRotation; + m_AttachState = AttachState.Attached; + m_AttachableNode = attachableNode; + // Attachables are always local space relative + transform.SetParent(m_AttachableNode.transform, false); + m_AttachableNode.Attach(this); } /// <summary> - /// Invoked after the parent has been applied.<br /> + /// Attaches the <see cref="GameObject"/> of this <see cref="AttachableBehaviour"/> instance to the <see cref="GameObject"/> of the <see cref="AttachableNode"/>. /// </summary> /// <remarks> - /// The <param name="parent"/> can be either null or a valid <see cref="NetworkBehaviour"/>. <br /> - /// When null, the parent is being unapplied/removed from the <see cref="GameObject"/> this <see cref="AttachableBehaviour"/> instance is attached to. + /// This effectively applies a new parent to a nested <see cref="NetworkBehaviour"/> and all <see cref="GameObject"/> children + /// of the nested <see cref="NetworkBehaviour"/>.<br /> + /// Both the <see cref="AttachableNode"/> and this <see cref="AttachableBehaviour"/> instances should be in the spawned state before this + /// is invoked. /// </remarks> - /// <param name="parent">The <see cref="NetworkBehaviour"/> that is applied or null if it is no longer applied.</param> - protected virtual void OnParentUpdated(NetworkBehaviour parent) + /// <param name="parent">The <see cref="NetworkBehaviour"/> to be applied or null to reparent under its original <see cref="GameObject"/> when spawned.</param> + public void Attach(AttachableNode attachableNode) { + if (!IsSpawned) + { + NetworkLog.LogError($"[{name}][Attach][Not Spawned] Cannot attach before being spawned!"); + return; + } + if (!HasAuthority) + { + NetworkLog.LogError($"[{name}][Attach][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!"); + return; + } + + if (attachableNode.NetworkObject == NetworkObject) + { + NetworkLog.LogError($"[{name}][Attach] Cannot attach to the original {NetworkObject} instance!"); + return; + } + + if (m_AttachableNode != null && m_AttachState == AttachState.Attached && m_AttachableNode == attachableNode) + { + NetworkLog.LogError($"[{name}][Attach] Cannot attach! {name} is already attached to {attachableNode.name}!"); + return; + } + + // Update the attached node reference to the new attachable node. + m_AttachedNodeReference.Value = new NetworkBehaviourReference(attachableNode); } /// <summary> - /// Invoked just prior to the parent being applied. <br /> - /// This is a good time to handle disabling or enabling <see cref="Object"/>s using an <see cref="ObjectController"/>. + /// Internal detach method that just handles changing state, parenting, and sending the <see cref="AttachableNode"/> a + /// notification that an <see cref="AttachableBehaviour"/> has detached. /// </summary> - /// <remarks> - /// The <param name="parent"/> can be either null or a valid <see cref="NetworkBehaviour"/>. <br /> - /// When null, the parent is being unapplied/removed from the <see cref="GameObject"/> this <see cref="AttachableBehaviour"/> instance is attached to. - /// </remarks> - /// <param name="parent">The <see cref="NetworkBehaviour"/> that is applied or null if it is no longer applied.</param> - protected virtual void OnParentBeingApplied(NetworkBehaviour parent) + internal void InternalDetach() { - + if (m_AttachableNode) + { + m_AttachableNode.Detach(this); + m_AttachableNode = null; + if (m_DefaultParent) + { + // Set the original parent and origianl local position and rotation + transform.SetParent(m_DefaultParent.transform, false); + transform.localPosition = m_OriginalLocalPosition; + transform.localRotation = m_OriginalLocalRotation; + } + m_AttachState = AttachState.Detached; + } } /// <summary> - /// Applies a parent to a nested <see cref="NetworkBehaviour"/> and all <see cref="GameObject"/> children - /// of the nested <see cref="NetworkBehaviour"/>. + /// Invoke to detach from a <see cref="AttachableNode"/>. /// </summary> - /// <param name="parent">The <see cref="NetworkBehaviour"/> to be applied or null to reparent under its original <see cref="GameObject"/> when spawned.</param> - public void ApplyParent(NetworkBehaviour parent) + public void Detach() { if (!IsSpawned) { - Debug.LogError($"[{name}][Not Spawned] Can only have a parent applied when it is spawned!"); + NetworkLog.LogError($"[{name}][Detach][Not Spawned] Cannot detach if not spawned!"); return; } if (!HasAuthority) { - Debug.LogError($"[{name}][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!"); + NetworkLog.LogError($"[{name}][Detach][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!"); return; } - // Notify any subscriptions - ParentIsBeingApplied?.Invoke(parent); + if (m_AttachState != AttachState.Attached || m_AttachableNode == null) + { + // Check for the unlikely scenario that an instance has mismatch between the state and assigned attachable node. + if (!m_AttachableNode && m_AttachState == AttachState.Attached) + { + NetworkLog.LogError($"[{name}][Detach] Invalid state detected! {name}'s state is still {m_AttachState} but has no {nameof(AttachableNode)} assigned!"); + } + + // Developer only notification for the most likely scenario where this method is invoked but the instance is not attached to anything. + if (NetworkManager && NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"[{name}][Detach] Cannot detach! {name} is not attached to anything!"); + } - // Invoke for any overrides - OnParentBeingApplied(parent); + // If we have the attachable node set and we are not in the middle of detaching, then log an error and note + // this could potentially occur if inoked more than once for the same instance in the same frame. + if (m_AttachableNode && m_AttachState != AttachState.Detaching) + { + NetworkLog.LogError($"[{name}][Detach] Invalid state detected! {name} is still referencing {nameof(AttachableNode)} {m_AttachableNode.name}! Could {nameof(AttachableBehaviour.Detach)} be getting invoked more than once for the same instance?"); + } + return; + } - // Once everything has been notified that we are applying a parent...apply the parent. - m_AppliedParent.Value = new NetworkBehaviourReference(parent); + // Update the attached node reference to nothing-null. + m_AttachedNodeReference.Value = new NetworkBehaviourReference(null); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs new file mode 100644 index 0000000000..8c5ad47cf5 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using Unity.Netcode; +using Unity.Netcode.Components; + + +/// <summary> +/// This component is used in conjunction with <see cref="AttachableBehaviour"/> and is used to +/// denote a specific child <see cref="UnityEngine.GameObject"/> that an <see cref="AttachableBehaviour"/> +/// can attach itself to. +/// </summary> +/// <remarks> +/// Primarily, the <see cref="AttachableNode"/> can be used as it is or can be extended to perform additional +/// logical operations when something attaches to or detaches from the <see cref="AttachableNode"/> instance. +/// </remarks> +public class AttachableNode : NetworkBehaviour +{ + /// <summary> + /// A <see cref="List{T}"/> of the currently attached <see cref="AttachableBehaviour"/>s. + /// </summary> + protected readonly List<AttachableBehaviour> m_AttachedBehaviours = new List<AttachableBehaviour>(); + + /// <inheritdoc/> + /// <remarks> + /// If the <see cref="NetworkObject"/> this <see cref="AttachableNode"/> belongs to is despawned, + /// then any attached <see cref="AttachableBehaviour"/> will be detached during <see cref="OnNetworkDespawn"/>. + /// </remarks> + public override void OnNetworkDespawn() + { + for (int i = m_AttachedBehaviours.Count - 1; i > 0; i--) + { + m_AttachedBehaviours[i].InternalDetach(); + } + base.OnNetworkDespawn(); + } + + /// <summary> + /// Override this method to be notified when an <see cref="AttachableBehaviour"/> has attached to this node. + /// </summary> + /// <param name="attachableBehaviour">The <see cref="AttachableBehaviour"/> that has been attached.</param> + protected virtual void OnAttached(AttachableBehaviour attachableBehaviour) + { + + } + + internal void Attach(AttachableBehaviour attachableBehaviour) + { + if (m_AttachedBehaviours.Contains(attachableBehaviour)) + { + NetworkLog.LogError($"[{nameof(AttachableNode)}][{name}][Attach] {nameof(AttachableBehaviour)} {attachableBehaviour.name} is already attached!"); + return; + } + + m_AttachedBehaviours.Add(attachableBehaviour); + OnAttached(attachableBehaviour); + } + + /// <summary> + /// Override this method to be notified when an <see cref="AttachableBehaviour"/> has detached from this node. + /// </summary> + /// <param name="attachableBehaviour">The <see cref="AttachableBehaviour"/> that has been detached.</param> + protected virtual void OnDetached(AttachableBehaviour attachableBehaviour) + { + + } + + internal void Detach(AttachableBehaviour attachableBehaviour) + { + if (!m_AttachedBehaviours.Contains(attachableBehaviour)) + { + NetworkLog.LogError($"[{nameof(AttachableNode)}][{name}][Detach] {nameof(AttachableBehaviour)} {attachableBehaviour.name} is not attached!"); + return; + } + + m_AttachedBehaviours.Remove(attachableBehaviour); + OnDetached(attachableBehaviour); + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs.meta new file mode 100644 index 0000000000..dda530aca5 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b870dfbc4cccf6244b4ed1a9b379c701 \ No newline at end of file From d08d4966fcc2527891558784befcf396fe72684f Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Mon, 23 Jun 2025 19:36:46 -0500 Subject: [PATCH 05/43] test Adding new test for attachables. --- .../Tests/Runtime/AttachableBehaviourTests.cs | 381 ++++++++++++++++++ .../Runtime/AttachableBehaviourTests.cs.meta | 2 + 2 files changed, 383 insertions(+) create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs new file mode 100644 index 0000000000..0efc6b6654 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs @@ -0,0 +1,381 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using Unity.Netcode.Components; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Unity.Netcode.RuntimeTests +{ + [TestFixture(HostOrServer.Host)] + [TestFixture(HostOrServer.Server)] + [TestFixture(HostOrServer.DAHost)] + internal class AttachableBehaviourTests : NetcodeIntegrationTest + { + protected override int NumberOfClients => 2; + + public AttachableBehaviourTests(HostOrServer hostOrServer) : base(hostOrServer) { } + + private GameObject m_SourcePrefab; + private GameObject m_TargetPrefabA; + private GameObject m_TargetPrefabB; + + /// <summary> + /// All of the below instances belong to the authority + /// </summary> + private NetworkObject m_SourceInstance; + private NetworkObject m_TargetInstance; + private NetworkObject m_TargetInstanceB; + private TestAttachable m_AttachableBehaviourInstance; + private TestNode m_AttachableNodeInstance; + private TestNode m_AttachableNodeInstanceB; + + private bool m_UseTargetB; + + private StringBuilder m_ErrorLog = new StringBuilder(); + + protected override IEnumerator OnSetup() + { + m_ErrorLog.Clear(); + return base.OnSetup(); + } + + protected override void OnServerAndClientsCreated() + { + // The source prefab contains the nested NetworkBehaviour that + // will be parented under the target prefab. + m_SourcePrefab = CreateNetworkObjectPrefab("Source"); + // The target prefab that the source prefab will attach + // will be parented under the target prefab. + m_TargetPrefabA = CreateNetworkObjectPrefab("TargetA"); + m_TargetPrefabB = CreateNetworkObjectPrefab("TargetB"); + var sourceChild = new GameObject("SourceChild"); + var targetChildA = new GameObject("TargetChildA"); + var targetChildB = new GameObject("TargetChildB"); + sourceChild.transform.parent = m_SourcePrefab.transform; + targetChildA.transform.parent = m_TargetPrefabA.transform; + targetChildB.transform.parent = m_TargetPrefabB.transform; + + sourceChild.AddComponent<TestAttachable>(); + targetChildA.AddComponent<TestNode>(); + targetChildB.AddComponent<TestNode>(); + base.OnServerAndClientsCreated(); + } + + private NetworkObject GetTargetInstance() + { + return m_UseTargetB ? m_TargetInstanceB : m_TargetInstance; + } + + private bool AllClientsSpawnedInstances() + { + m_ErrorLog.Clear(); + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_SourceInstance.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has not spawned {m_SourceInstance.name} yet!"); + } + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstance.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has not spawned {m_TargetInstance.name} yet!"); + } + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstanceB.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has not spawned {m_TargetInstanceB.name} yet!"); + } + } + return m_ErrorLog.Length == 0; + } + + private bool ResetAllStates() + { + m_ErrorLog.Clear(); + var target = GetTargetInstance(); + // The attachable can move between the two spawned instances. + var currentAttachableRoot = m_AttachableBehaviourInstance.State == AttachableBehaviour.AttachState.Attached ? target : m_SourceInstance; + foreach (var networkManager in m_NetworkManagers) + { + // Source + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_SourceInstance.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {currentAttachableRoot.name}!"); + } + else + { + var attachable = networkManager.SpawnManager.SpawnedObjects[currentAttachableRoot.NetworkObjectId].GetComponentInChildren<TestAttachable>(); + attachable.ResetStates(); + } + + // Target + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstance.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {m_TargetInstance.name}!"); + } + else + { + var node = networkManager.SpawnManager.SpawnedObjects[m_TargetInstance.NetworkObjectId].GetComponentInChildren<TestNode>(); + node.ResetStates(); + } + + // Target B + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstanceB.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {m_TargetInstanceB.name}!"); + } + else + { + var node = networkManager.SpawnManager.SpawnedObjects[m_TargetInstanceB.NetworkObjectId].GetComponentInChildren<TestNode>(); + node.ResetStates(); + } + } + return m_ErrorLog.Length == 0; + } + + private bool AllInstancesAttachedStateChanged(bool checkAttached) + { + m_ErrorLog.Clear(); + var target = GetTargetInstance(); + // The attachable can move between the two spawned instances so we have to use the appropriate one depending upon the authority's current state. + var currentAttachableRoot = m_AttachableBehaviourInstance.State == AttachableBehaviour.AttachState.Attached ? target : m_SourceInstance; + var attachable = (TestAttachable)null; + var node = (TestNode)null; + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(currentAttachableRoot.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {currentAttachableRoot.name}!"); + continue; + } + else + { + attachable = networkManager.SpawnManager.SpawnedObjects[currentAttachableRoot.NetworkObjectId].GetComponentInChildren<TestAttachable>(); + } + + if (!attachable) + { + attachable = networkManager.SpawnManager.SpawnedObjects[m_TargetInstance.NetworkObjectId].GetComponentInChildren<TestAttachable>(); + if (!attachable) + { + attachable = networkManager.SpawnManager.SpawnedObjects[m_TargetInstanceB.NetworkObjectId].GetComponentInChildren<TestAttachable>(); + if (!attachable) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][Attachable] Attachable was not found!"); + } + } + continue; + } + + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(target.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {target.name}!"); + continue; + } + else + { + node = networkManager.SpawnManager.SpawnedObjects[target.NetworkObjectId].GetComponentInChildren<TestNode>(); + } + + if (!attachable.CheckStateChangedOverride(checkAttached, false, node)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Did not have its override invoked!"); + } + if (!attachable.CheckStateChangedOverride(checkAttached, true, node)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Did not have its event invoked!"); + } + if ((checkAttached && !node.OnAttachedInvoked) || (!checkAttached && !node.OnDetachedInvoked)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{node.name}] Did not have its override invoked!"); + } + if (checkAttached && attachable.transform.parent != node.transform) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] {node.name} is not the parent of {attachable.name}!"); + } + else if (!checkAttached && attachable.transform.parent != attachable.DefaultParent.transform) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] {attachable.DefaultParent.name} is not the parent of {attachable.name}!"); + } + } + return m_ErrorLog.Length == 0; + } + + [UnityTest] + public IEnumerator AttachAndDetachTests() + { + var authority = GetAuthorityNetworkManager(); + m_SourceInstance = SpawnObject(m_SourcePrefab, authority).GetComponent<NetworkObject>(); + m_TargetInstance = SpawnObject(m_TargetPrefabA, authority).GetComponent<NetworkObject>(); + m_TargetInstanceB = SpawnObject(m_TargetPrefabB, authority).GetComponent<NetworkObject>(); + yield return WaitForConditionOrTimeOut(AllClientsSpawnedInstances); + AssertOnTimeout($"Timed out waiting for all clients to spawn {m_SourceInstance.name} and {m_TargetInstance.name}!\n {m_ErrorLog}"); + + m_AttachableBehaviourInstance = m_SourceInstance.GetComponentInChildren<TestAttachable>(); + Assert.NotNull(m_AttachableBehaviourInstance, $"{m_SourceInstance.name} does not have a nested child {nameof(AttachableBehaviour)}!"); + + m_AttachableNodeInstance = m_TargetInstance.GetComponentInChildren<TestNode>(); + Assert.NotNull(m_AttachableNodeInstance, $"{m_TargetInstance.name} does not have a nested child {nameof(AttachableNode)}!"); + + m_AttachableNodeInstanceB = m_TargetInstanceB.GetComponentInChildren<TestNode>(); + Assert.NotNull(m_AttachableNodeInstanceB, $"{m_TargetInstanceB.name} does not have a nested child {nameof(AttachableNode)}!"); + + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + m_AttachableBehaviourInstance.Attach(m_AttachableNodeInstance); + + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(true)); + AssertOnTimeout($"Timed out waiting for all clients to attach {m_AttachableBehaviourInstance.name} to {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + // Wait a brief period of time + yield return s_DefaultWaitForTick; + + // Now late join a client to make sure it synchronizes properly + yield return CreateAndStartNewClient(); + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(true)); + AssertOnTimeout($"Timed out waiting for all clients to attach {m_AttachableBehaviourInstance.name} to {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + // Wait a brief period of time + yield return s_DefaultWaitForTick; + + // Reset all states and prepare for 2nd attach test + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + + // Now, while attached, attach to another attachable node which should detach from the current and attach to the new. + m_AttachableBehaviourInstance.Attach(m_AttachableNodeInstanceB); + + // The attachable should detach from the current AttachableNode first + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(false)); + AssertOnTimeout($"Timed out waiting for all clients to detach {m_AttachableBehaviourInstance.name} from {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + // Switch the conditional to check the target B attachable node + m_UseTargetB = true; + + // Then the attachable should attach to the target B attachable node + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(true)); + AssertOnTimeout($"Timed out waiting for all clients to attach {m_AttachableBehaviourInstance.name} to {m_AttachableNodeInstanceB.name}!\n {m_ErrorLog}"); + + // Reset all states and prepare for final detach test + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + + // Now verify complete detaching works + m_AttachableBehaviourInstance.Detach(); + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(false)); + AssertOnTimeout($"Timed out waiting for all clients to detach {m_AttachableBehaviourInstance.name} from {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + } + + /// <summary> + /// Helps to validate that the overrides and events are invoked when an attachable attaches or detaches from the instance. + /// This also helps to validate that the appropriate <see cref="AttachableNode"/> instance is passed in as a parameter. + /// </summary> + public class TestAttachable : AttachableBehaviour + { + private Dictionary<AttachState, AttachableNode> m_StateUpdates = new Dictionary<AttachState, AttachableNode>(); + + private Dictionary<AttachState, AttachableNode> m_StateUpdateEvents = new Dictionary<AttachState, AttachableNode>(); + + public GameObject DefaultParent => m_DefaultParent; + public AttachState State => m_AttachState; + + public override void OnNetworkSpawn() + { + AttachStateChange += OnAttachStateChangeEvent; + name = $"{name}-{NetworkManager.LocalClientId}"; + base.OnNetworkSpawn(); + } + + public override void OnNetworkDespawn() + { + AttachStateChange -= OnAttachStateChangeEvent; + base.OnNetworkDespawn(); + } + + private void OnAttachStateChangeEvent(AttachState attachState, AttachableNode attachableNode) + { + m_StateUpdateEvents.Add(attachState, attachableNode); + } + + protected override void OnAttachStateChanged(AttachState attachState, AttachableNode attachableNode) + { + m_StateUpdates.Add(attachState, attachableNode); + base.OnAttachStateChanged(attachState, attachableNode); + } + + public void ResetStates() + { + m_StateUpdates.Clear(); + m_StateUpdateEvents.Clear(); + } + + private void Log(string message) + { + Debug.Log($"[{name}] {message}"); + } + + public bool CheckStateChangedOverride(bool checkAttached, bool checkEvent, AttachableNode attachableNode) + { + var tableToCheck = checkEvent ? m_StateUpdateEvents : m_StateUpdates; + var checkStatus = checkAttached ? (tableToCheck.ContainsKey(AttachState.Attaching) && tableToCheck.ContainsKey(AttachState.Attached)) : + (tableToCheck.ContainsKey(AttachState.Detaching) && tableToCheck.ContainsKey(AttachState.Detached)); + + if (checkStatus) + { + foreach (var entry in tableToCheck) + { + // Ignore any states that don't match what is being checked + if ((checkStatus && (entry.Key == AttachState.Detaching || entry.Key == AttachState.Detached)) || + (!checkStatus && (entry.Key == AttachState.Attaching || entry.Key == AttachState.Attached))) + { + continue; + } + + // Special case for completely detached + if (entry.Key == AttachState.Detached) + { + if (entry.Value != null) + { + Log($"[Value] The value {entry.Value.name} is not null!"); + checkStatus = false; + break; + } + } + else if (entry.Value != attachableNode) + { + Log($"[{entry.Key}][Value] The value {entry.Value.name} is not the same as {attachableNode.name}!"); + checkStatus = false; + break; + } + } + } + return checkStatus; + } + } + + /// <summary> + /// Helps to validate that the overrides are invoked when an attachable attaches or detaches from the instance. + /// </summary> + public class TestNode : AttachableNode + { + public bool OnAttachedInvoked { get; private set; } + public bool OnDetachedInvoked { get; private set; } + + public void ResetStates() + { + OnAttachedInvoked = false; + OnDetachedInvoked = false; + } + + protected override void OnAttached(AttachableBehaviour attachableBehaviour) + { + OnAttachedInvoked = true; + base.OnAttached(attachableBehaviour); + } + + protected override void OnDetached(AttachableBehaviour attachableBehaviour) + { + OnDetachedInvoked = true; + base.OnDetached(attachableBehaviour); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs.meta new file mode 100644 index 0000000000..5e7eb6db85 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 016a03eb97e603345a44bca4defacf24 \ No newline at end of file From 4a9775aeb46b25f25f2836a018feef06b94d0e97 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Mon, 23 Jun 2025 19:38:02 -0500 Subject: [PATCH 06/43] style Replacing any improperly spelled "detatch" with "detach". XML API and private methods. --- .../Runtime/Components/NetworkRigidBodyBase.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index 96cd6b79e0..d368465cf3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -1121,7 +1121,7 @@ private void ApplyFixedJoint(NetworkRigidbodyBase bodyToConnectTo, Vector3 posit /// - This instance can be viewed as the child. /// - The <param name="objectToConnectTo"/> can be viewed as the parent. /// <br/> - /// This is the recommended way, as opposed to parenting, to attached/detatch two rigid bodies to one another when <see cref="UseRigidBodyForMotion"/> is enabled. + /// This is the recommended way, as opposed to parenting, to attached/detach two rigid bodies to one another when <see cref="UseRigidBodyForMotion"/> is enabled. /// For more details on using <see cref="UnityEngine.FixedJoint"/> and <see cref="UnityEngine.FixedJoint2D"/>. /// <br/> /// This provides a simple joint solution between two rigid bodies and serves as an example. You can add different joint types by creating a customized/derived @@ -1187,7 +1187,7 @@ private void RemoveFromParentBody() #if COM_UNITY_MODULES_PHYSICS2D [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DetatchFromFixedJoint2D() + private void DetachFromFixedJoint2D() { if (FixedJoint2D == null) { @@ -1206,7 +1206,7 @@ private void DetatchFromFixedJoint2D() #endif #if COM_UNITY_MODULES_PHYSICS [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DetatchFromFixedJoint3D() + private void DetachFromFixedJoint3D() { if (FixedJoint == null) { @@ -1227,7 +1227,7 @@ private void DetatchFromFixedJoint3D() /// this will detach from the fixed joint and destroy the fixed joint component. /// </summary> /// <remarks> - /// This is the recommended way, as opposed to parenting, to attached/detatch two rigid bodies to one another when <see cref="UseRigidBodyForMotion"/> is enabled. + /// This is the recommended way, as opposed to parenting, to attached/detach two rigid bodies to one another when <see cref="UseRigidBodyForMotion"/> is enabled. /// </remarks> public void DetachFromFixedJoint() { @@ -1240,18 +1240,18 @@ public void DetachFromFixedJoint() #if COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D if (m_IsRigidbody2D) { - DetatchFromFixedJoint2D(); + DetachFromFixedJoint2D(); } else { - DetatchFromFixedJoint3D(); + DetachFromFixedJoint3D(); } #endif #if COM_UNITY_MODULES_PHYSICS && !COM_UNITY_MODULES_PHYSICS2D - DetatchFromFixedJoint3D(); + DetachFromFixedJoint3D(); #endif #if !COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D - DetatchFromFixedJoint2D(); + DetachFromFixedJoint2D(); #endif } } From 1f81ebd625e6e0c4badf30323ac3431bedd37890 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Mon, 23 Jun 2025 19:51:44 -0500 Subject: [PATCH 07/43] style - PVP Minor XML API fixes. --- .../Runtime/Components/Helpers/AttachableBehaviour.cs | 11 +++++------ .../Runtime/Components/Helpers/ComponentController.cs | 5 ++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 57afe67707..fbd9acc1cd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -117,11 +117,10 @@ public enum AttachState private Vector3 m_OriginalLocalPosition; private Quaternion m_OriginalLocalRotation; - /// <inheritdoc/> - /// <remarks> + /// <summary> /// If you create a custom <see cref="AttachableBehaviour"/> and override this method, you must invoke /// this base instance of <see cref="Awake"/>. - /// </remarks> + /// </summary> protected virtual void Awake() { m_DefaultParent = transform.parent == null ? gameObject : transform.parent.gameObject; @@ -218,8 +217,8 @@ private void UpdateAttachedState() /// For customized/derived <see cref="AttachableBehaviour"/>s, override this method to receive notifications /// when the <see cref="AttachState"/> has changed. /// </summary> - /// <param name="attachState">the new <see cref="AttachState"/>.</param> - /// <param name="attachableNode"></param> + /// <param name="attachState">The new <see cref="AttachState"/>.</param> + /// <param name="attachableNode">The <see cref="AttachableNode"/> being attached to or from. Will be null when completely detatched.</param> protected virtual void OnAttachStateChanged(AttachState attachState, AttachableNode attachableNode) { @@ -275,7 +274,7 @@ internal void InternalAttach(AttachableNode attachableNode) /// Both the <see cref="AttachableNode"/> and this <see cref="AttachableBehaviour"/> instances should be in the spawned state before this /// is invoked. /// </remarks> - /// <param name="parent">The <see cref="NetworkBehaviour"/> to be applied or null to reparent under its original <see cref="GameObject"/> when spawned.</param> + /// <param name="attachableNode">The <see cref="AttachableNode"/> to attach this instance to.</param> public void Attach(AttachableNode attachableNode) { if (!IsSpawned) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 2952f13139..5de25bd9b7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -96,11 +96,10 @@ protected virtual void OnValidate() } #endif - /// <inheritdoc/> - /// <remarks> + /// <summary> /// Also checks to assure all <see cref="Component"/> entries are valid and creates a final table of /// <see cref="Component"/>s paired to their <see cref="PropertyInfo"/>. - /// </remarks> + /// </summary> protected virtual void Awake() { var emptyEntries = 0; From 4148bfe590f1178cce433366f3c34cc1fa508a2c Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Mon, 23 Jun 2025 21:42:37 -0500 Subject: [PATCH 08/43] style - standards Simplified the nameof AttachableBehaviour.Detach to just Detach. --- .../Runtime/Components/Helpers/AttachableBehaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index fbd9acc1cd..260257bfa4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -361,7 +361,7 @@ public void Detach() // this could potentially occur if inoked more than once for the same instance in the same frame. if (m_AttachableNode && m_AttachState != AttachState.Detaching) { - NetworkLog.LogError($"[{name}][Detach] Invalid state detected! {name} is still referencing {nameof(AttachableNode)} {m_AttachableNode.name}! Could {nameof(AttachableBehaviour.Detach)} be getting invoked more than once for the same instance?"); + NetworkLog.LogError($"[{name}][Detach] Invalid state detected! {name} is still referencing {nameof(AttachableNode)} {m_AttachableNode.name}! Could {nameof(Detach)} be getting invoked more than once for the same instance?"); } return; } From 1fce513b6615d515d62987e0416b5bd6c0d2aa23 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Tue, 24 Jun 2025 11:12:36 -0500 Subject: [PATCH 09/43] refactor Refactoring the ComponentController to provide more flexibility as well as being able to have component entries that will apply the inverse of the current ComponentController's current state....which allows for switching between different sets of components depending upon the controller's state. --- .../Components/Helpers/ComponentController.cs | 115 ++++++++++++++---- 1 file changed, 94 insertions(+), 21 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 5de25bd9b7..a86c6a9aac 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; @@ -5,6 +6,31 @@ namespace Unity.Netcode.Components { + /// <summary> + /// This is a serializable contianer class for <see cref="ComponentController"/> entries. + /// </summary> + [Serializable] + public class ComponentControllerEntry + { + /// <summary> + /// When true, this component's enabled state will be the inverse of + /// the value passed into <see cref="ComponentController.SetEnabled(bool)"/>. + /// </summary> + public bool InvertEnabled; + + /// <summary> + /// The component to control. + /// </summary> + /// <remarks> + /// You can assign an entire <see cref="GameObject"/> to this property which will + /// add all components attached to the <see cref="GameObject"/>. The <see cref="StartEnabled"/> + /// and <see cref="InvertEnabled"/> properties will be applied to all components found on the <see cref="GameObject"/>. + /// </remarks> + public Object Component; + + internal PropertyInfo PropertyInfo; + } + /// <summary> /// Handles enabling or disabling commonly used components, behaviours, RenderMeshes, etc.<br /> /// Anything that derives from <see cref="Component"/> and has an enabled property can be added @@ -20,16 +46,21 @@ public class ComponentController : NetworkBehaviour /// <summary> /// Determines whether the selected <see cref="Components"/>s will start enabled or disabled when spawned. /// </summary> - [Tooltip("The initial state of the components when spawned.")] - public bool InitialState = true; + [Tooltip("The initial state of the component controllers enabled status when instnatiated.")] + public bool StartEnabled = true; /// <summary> /// The list of <see cref="Components"/>s to be enabled and disabled. /// </summary> [Tooltip("The list of components to control. You can drag and drop an entire GameObject on this to include all components.")] - public List<Object> Components; + public List<ComponentControllerEntry> Components; - private Dictionary<Component, PropertyInfo> m_ValidComponents = new Dictionary<Component, PropertyInfo>(); + /// <summary> + /// Returns the current enabled state of the <see cref="ComponentController"/>. + /// </summary> + public bool EnabledState => m_IsEnabled.Value; + + private List<ComponentControllerEntry> m_ValidComponents = new List<ComponentControllerEntry>(); private NetworkVariable<bool> m_IsEnabled = new NetworkVariable<bool>(); #if UNITY_EDITOR @@ -44,39 +75,45 @@ protected virtual void OnValidate() return; } - var gameObjectsToScan = new List<GameObject>(); + var gameObjectsToScan = new List<ComponentControllerEntry>(); for (int i = Components.Count - 1; i >= 0; i--) { if (Components[i] == null) { continue; } - var componentType = Components[i].GetType(); + + if (Components[i].Component == null) + { + continue; + } + var componentType = Components[i].Component.GetType(); if (componentType == typeof(GameObject)) { - gameObjectsToScan.Add(Components[i] as GameObject); + gameObjectsToScan.Add(Components[i]); Components.RemoveAt(i); continue; } if (componentType.IsSubclassOf(typeof(NetworkBehaviour))) { - Debug.LogWarning($"Removing {Components[i].name} since {nameof(NetworkBehaviour)}s are not allowed to be controlled by this component."); + Debug.LogWarning($"Removing {Components[i].Component.name} since {nameof(NetworkBehaviour)}s are not allowed to be controlled by this component."); Components.RemoveAt(i); continue; } - var propertyInfo = Components[i].GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + var propertyInfo = Components[i].Component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); if (propertyInfo == null && propertyInfo.PropertyType != typeof(bool)) { - Debug.LogWarning($"{Components[i].name} does not contain a public enabled property! (Removing)"); + Debug.LogWarning($"{Components[i].Component.name} does not contain a public enabled property! (Removing)"); Components.RemoveAt(i); } } foreach (var entry in gameObjectsToScan) { - var components = entry.GetComponents<Component>(); + var asGameObject = entry.Component as GameObject; + var components = asGameObject.GetComponents<Component>(); foreach (var component in components) { // Ignore any NetworkBehaviour derived components @@ -88,7 +125,12 @@ protected virtual void OnValidate() var propertyInfo = component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) { - Components.Add(component); + var componentEntry = new ComponentControllerEntry() + { + Component = component, + PropertyInfo = propertyInfo, + }; + Components.Add(componentEntry); } } } @@ -97,23 +139,24 @@ protected virtual void OnValidate() #endif /// <summary> - /// Also checks to assure all <see cref="Component"/> entries are valid and creates a final table of - /// <see cref="Component"/>s paired to their <see cref="PropertyInfo"/>. + /// This checks to make sure that all <see cref="Component"/> entries are valid and will create a final + /// <see cref="ComponentControllerEntry"/> list of valid entries. /// </summary> protected virtual void Awake() { var emptyEntries = 0; - foreach (var someObject in Components) + foreach (var entry in Components) { - if (someObject == null) + if (entry == null) { emptyEntries++; continue; } - var propertyInfo = someObject.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + var propertyInfo = entry.Component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) { - m_ValidComponents.Add(someObject as Component, propertyInfo); + entry.PropertyInfo = propertyInfo; + m_ValidComponents.Add(entry); } else { @@ -128,6 +171,9 @@ protected virtual void Awake() { Debug.Log($"{name} has {m_ValidComponents.Count} valid {nameof(Component)} entries."); } + + // Apply the initial state of all components this instance is controlling. + InitializeComponents(); } /// <inheritdoc/> @@ -135,7 +181,7 @@ public override void OnNetworkSpawn() { if (HasAuthority) { - m_IsEnabled.Value = InitialState; + m_IsEnabled.Value = StartEnabled; } base.OnNetworkSpawn(); } @@ -165,17 +211,44 @@ private void OnEnabledChanged(bool previous, bool current) ApplyEnabled(current); } + /// <summary> + /// Initializes each component entry to its initial state. + /// </summary> + private void InitializeComponents() + { + foreach (var entry in m_ValidComponents) + { + // If invert enabled is true, then use the inverted value passed in. + // Otherwise, directly apply the value passed in. + var isEnabled = entry.InvertEnabled ? !StartEnabled : StartEnabled; + entry.PropertyInfo.SetValue(entry.Component, isEnabled); + } + } + + /// <summary> + /// Applies states changes to all components being controlled by this instance. + /// </summary> + /// <param name="enabled">the state update to apply</param> private void ApplyEnabled(bool enabled) { foreach (var entry in m_ValidComponents) { - entry.Value.SetValue(entry.Key, enabled); + // If invert enabled is true, then use the inverted value passed in. + // Otherwise, directly apply the value passed in. + var isEnabled = entry.InvertEnabled ? !enabled : enabled; + entry.PropertyInfo.SetValue(entry.Component, isEnabled); } } /// <summary> - /// Invoke on the authority side to enable or disable the <see cref="Object"/>s. + /// Invoke on the authority side to enable or disable components assigned to this instance. /// </summary> + /// <remarks> + /// If any component entry has the <see cref="ComponentControllerEntry.InvertEnabled"/> set to true, + /// then the inverse of the isEnabled property passed in will be used. If the component entry has the + /// <see cref="ComponentControllerEntry.InvertEnabled"/> set to false (default), then the value of the + /// isEnabled property will be applied. + /// </remarks> /// <param name="isEnabled">true = enabled | false = disabled</param> public void SetEnabled(bool isEnabled) { From 1ceb76c8fecc1a4f3ab53e72a3d7e2d195de97b4 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Tue, 24 Jun 2025 12:33:43 -0500 Subject: [PATCH 10/43] update Made some minor adjustments while writing the base test for ComponentController. --- .../Components/Helpers/ComponentController.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index a86c6a9aac..cc87d6f732 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -39,7 +39,10 @@ public class ComponentControllerEntry /// </summary> /// <remarks> /// This will synchronize the enabled or disabled state of the <see cref="Component"/>s with - /// connected and late joining clients. + /// connected and late joining clients.<br /> + /// This class provides the basic functionality to synchronizing components' enabled state.<br /> + /// It is encouraged to create custom derived versions of this class to provide any additional + /// functionality required for your project specific needs. /// </remarks> public class ComponentController : NetworkBehaviour { @@ -60,7 +63,7 @@ public class ComponentController : NetworkBehaviour /// </summary> public bool EnabledState => m_IsEnabled.Value; - private List<ComponentControllerEntry> m_ValidComponents = new List<ComponentControllerEntry>(); + internal List<ComponentControllerEntry> ValidComponents = new List<ComponentControllerEntry>(); private NetworkVariable<bool> m_IsEnabled = new NetworkVariable<bool>(); #if UNITY_EDITOR @@ -144,7 +147,16 @@ protected virtual void OnValidate() /// </summary> protected virtual void Awake() { + ValidComponents.Clear(); + + // If no components then don't try to initialize. + if (Components == null) + { + return; + } + var emptyEntries = 0; + foreach (var entry in Components) { if (entry == null) @@ -156,20 +168,16 @@ protected virtual void Awake() if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) { entry.PropertyInfo = propertyInfo; - m_ValidComponents.Add(entry); + ValidComponents.Add(entry); } else { - Debug.LogWarning($"{name} does not contain a public enable property! (Ignoring)"); + NetworkLog.LogWarning($"{name} does not contain a public enable property! (Ignoring)"); } } if (emptyEntries > 0) { - Debug.LogWarning($"{name} has {emptyEntries} emtpy(null) entries in the {nameof(Components)} list!"); - } - else - { - Debug.Log($"{name} has {m_ValidComponents.Count} valid {nameof(Component)} entries."); + NetworkLog.LogWarning($"{name} has {emptyEntries} emtpy(null) entries in the {nameof(Components)} list!"); } // Apply the initial state of all components this instance is controlling. @@ -216,7 +224,7 @@ private void OnEnabledChanged(bool previous, bool current) /// </summary> private void InitializeComponents() { - foreach (var entry in m_ValidComponents) + foreach (var entry in ValidComponents) { // If invert enabled is true, then use the inverted value passed in. // Otherwise, directly apply the value passed in. @@ -231,7 +239,7 @@ private void InitializeComponents() /// <param name="enabled">the state update to apply</param> private void ApplyEnabled(bool enabled) { - foreach (var entry in m_ValidComponents) + foreach (var entry in ValidComponents) { // If invert enabled is true, then use the inverted value passed in. // Otherwise, directly apply the value passed in. From db05292d4be127d0821ef765738911b9823f1681 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Tue, 24 Jun 2025 12:34:15 -0500 Subject: [PATCH 11/43] test Adding the base ComponentController test. --- .../Tests/Runtime/ComponentControllerTests.cs | 191 ++++++++++++++++++ .../Runtime/ComponentControllerTests.cs.meta | 2 + 2 files changed, 193 insertions(+) create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs new file mode 100644 index 0000000000..f5f0be06cc --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs @@ -0,0 +1,191 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using Unity.Netcode.Components; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Unity.Netcode.RuntimeTests +{ + [TestFixture(HostOrServer.Host)] + [TestFixture(HostOrServer.Server)] + [TestFixture(HostOrServer.DAHost)] + internal class ComponentControllerTests : NetcodeIntegrationTest + { + protected override int NumberOfClients => 2; + + private StringBuilder m_ErrorLog = new StringBuilder(); + private GameObject m_TestPrefab; + + private NetworkManager m_Authority; + private ComponentController m_AuthorityController; + + public ComponentControllerTests(HostOrServer hostOrServer) : base(hostOrServer) { } + + protected override IEnumerator OnSetup() + { + m_ErrorLog.Clear(); + yield return base.OnSetup(); + } + + protected override void OnServerAndClientsCreated() + { + // The source prefab contains the nested NetworkBehaviour that + // will be parented under the target prefab. + m_TestPrefab = CreateNetworkObjectPrefab("TestObject"); + var sourceChild = new GameObject("Child"); + sourceChild.transform.parent = m_TestPrefab.transform; + var meshRenderer = sourceChild.AddComponent<MeshRenderer>(); + var boxCollider = sourceChild.AddComponent<BoxCollider>(); + var controller = m_TestPrefab.AddComponent<ComponentController>(); + controller.Components = new List<ComponentControllerEntry> + { + new ComponentControllerEntry() + { + Component = meshRenderer, + }, + new ComponentControllerEntry() + { + InvertEnabled = true, + Component = boxCollider, + } + }; + base.OnServerAndClientsCreated(); + } + + private bool AllClientsSpawnedInstances() + { + m_ErrorLog.Clear(); + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_AuthorityController.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has not spawned {m_AuthorityController.name} yet!"); + } + } + return m_ErrorLog.Length == 0; + } + + private void ControllerStateMatches(ComponentController controller) + { + if (m_AuthorityController.EnabledState != controller.EnabledState) + { + m_ErrorLog.AppendLine($"[Client-{controller.NetworkManager.LocalClientId}] The authority controller state ({m_AuthorityController.EnabledState})" + + $" does not match the local controller state ({controller.EnabledState})!"); + return; + } + + if (m_AuthorityController.ValidComponents.Count != controller.ValidComponents.Count) + { + m_ErrorLog.AppendLine($"[Client-{controller.NetworkManager.LocalClientId}] The authority controller has {m_AuthorityController.ValidComponents.Count} valid components but " + + $"the local instance has {controller.ValidComponents.Count}!"); + return; + } + + for (int i = 0; i < m_AuthorityController.ValidComponents.Count; i++) + { + var authorityEntry = m_AuthorityController.ValidComponents[i]; + var nonAuthorityEntry = controller.ValidComponents[i]; + if (authorityEntry.InvertEnabled != nonAuthorityEntry.InvertEnabled) + { + m_ErrorLog.AppendLine($"[Client-{controller.NetworkManager.LocalClientId}] The authority controller's component entry ({i}) " + + $"has an inverted state of {authorityEntry.InvertEnabled} but the local instance has a value of " + + $"{nonAuthorityEntry.InvertEnabled}!"); + } + + var authorityIsEnabled = (bool)authorityEntry.PropertyInfo.GetValue(authorityEntry.Component); + var nonAuthorityIsEnabled = (bool)nonAuthorityEntry.PropertyInfo.GetValue(authorityEntry.Component); + if (authorityIsEnabled != nonAuthorityIsEnabled) + { + m_ErrorLog.AppendLine($"[Client-{controller.NetworkManager.LocalClientId}] The authority controller's component ({i}) " + + $"entry's enabled state is {authorityIsEnabled} but the local instance's value is {nonAuthorityIsEnabled}!"); + } + } + } + + private bool AllComponentStatesMatch() + { + m_ErrorLog.Clear(); + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_AuthorityController.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Does not have a spawned instance of {m_AuthorityController.name}!"); + } + var controller = networkManager.SpawnManager.SpawnedObjects[m_AuthorityController.NetworkObjectId].GetComponent<ComponentController>(); + ControllerStateMatches(controller); + } + return m_ErrorLog.Length == 0; + } + + private bool AllComponentStatesAreCorrect(bool isEnabled) + { + m_ErrorLog.Clear(); + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_AuthorityController.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Does not have a spawned instance of {m_AuthorityController.name}!"); + } + var controller = networkManager.SpawnManager.SpawnedObjects[m_AuthorityController.NetworkObjectId].GetComponent<ComponentController>(); + for (int i = 0; i < controller.ValidComponents.Count; i++) + { + var componentEntry = controller.ValidComponents[i]; + + var componentEntryIsEnabled = (bool)componentEntry.PropertyInfo.GetValue(componentEntry.Component); + var valueToCheck = componentEntry.InvertEnabled ? !isEnabled : isEnabled; + + if (valueToCheck != componentEntryIsEnabled) + { + m_ErrorLog.AppendLine($"[Client-{controller.NetworkManager.LocalClientId}] The enabled state for entry ({i}) " + + $"should be {valueToCheck} but is {componentEntryIsEnabled}!"); + } + } + } + return m_ErrorLog.Length == 0; + } + + [UnityTest] + public IEnumerator EnabledDisabledSynchronizationTests() + { + m_Authority = GetAuthorityNetworkManager(); + + m_AuthorityController = SpawnObject(m_TestPrefab, m_Authority).GetComponent<ComponentController>(); + + yield return WaitForConditionOrTimeOut(AllClientsSpawnedInstances); + AssertOnTimeout($"All clients did not spawn an instance of {m_AuthorityController.name}!\n {m_ErrorLog}"); + + // Validate that clients start off with matching states. + yield return WaitForConditionOrTimeOut(AllComponentStatesMatch); + AssertOnTimeout($"Not all client instances matched the authority instance {m_AuthorityController.name}! \n {m_ErrorLog}"); + + // Validate that all controllers have the correct enabled value for the current authority controller instance's value. + yield return WaitForConditionOrTimeOut(() => AllComponentStatesAreCorrect(m_AuthorityController.EnabledState)); + AssertOnTimeout($"Not all client instances have the correct enabled state!\n {m_ErrorLog}"); + + // Toggle the enabled state of the authority controller + m_AuthorityController.SetEnabled(!m_AuthorityController.EnabledState); + + // Validate that all controllers' states match + yield return WaitForConditionOrTimeOut(AllComponentStatesMatch); + AssertOnTimeout($"Not all client instances matched the authority instance {m_AuthorityController.name}! \n {m_ErrorLog}"); + + // Validate that all controllers have the correct enabled value for the current authority controller instance's value. + yield return WaitForConditionOrTimeOut(() => AllComponentStatesAreCorrect(m_AuthorityController.EnabledState)); + AssertOnTimeout($"Not all client instances have the correct enabled state!\n {m_ErrorLog}"); + + // Late join a client to assure the late joining client's values are synchronized properly + yield return CreateAndStartNewClient(); + + // Validate that all controllers' states match + yield return WaitForConditionOrTimeOut(AllComponentStatesMatch); + AssertOnTimeout($"Not all client instances matched the authority instance {m_AuthorityController.name}! \n {m_ErrorLog}"); + + // Validate that all controllers have the correct enabled value for the current authority controller instance's value. + yield return WaitForConditionOrTimeOut(() => AllComponentStatesAreCorrect(m_AuthorityController.EnabledState)); + AssertOnTimeout($"Not all client instances have the correct enabled state!\n {m_ErrorLog}"); + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs.meta new file mode 100644 index 0000000000..c5cb8ed883 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 22e1625a8e8c9a24ab0408e95a5250a9 \ No newline at end of file From 2d391ed7e93578cb5ed1ee3d24793967d6b4cd87 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Tue, 24 Jun 2025 13:13:13 -0500 Subject: [PATCH 12/43] test - update switching to a Light component as opposed to BoxCollider as BoxCollider requires the physics package and we are just testing the functionality of ComponentController and not specifically any one other type of component. --- .../Tests/Runtime/ComponentControllerTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs index f5f0be06cc..f1a537901e 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs @@ -5,6 +5,7 @@ using Unity.Netcode.Components; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; +using UnityEngine.Experimental.GlobalIllumination; using UnityEngine.TestTools; namespace Unity.Netcode.RuntimeTests @@ -38,7 +39,7 @@ protected override void OnServerAndClientsCreated() var sourceChild = new GameObject("Child"); sourceChild.transform.parent = m_TestPrefab.transform; var meshRenderer = sourceChild.AddComponent<MeshRenderer>(); - var boxCollider = sourceChild.AddComponent<BoxCollider>(); + var light = sourceChild.AddComponent<Light>(); var controller = m_TestPrefab.AddComponent<ComponentController>(); controller.Components = new List<ComponentControllerEntry> { @@ -49,7 +50,7 @@ protected override void OnServerAndClientsCreated() new ComponentControllerEntry() { InvertEnabled = true, - Component = boxCollider, + Component = light, } }; base.OnServerAndClientsCreated(); From ae2987301845d5653592201ff9c25ca49793189a Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Tue, 24 Jun 2025 13:13:42 -0500 Subject: [PATCH 13/43] style Removing using directive. --- .../Tests/Runtime/ComponentControllerTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs index f1a537901e..7d2d6e86cb 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs @@ -5,7 +5,6 @@ using Unity.Netcode.Components; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; -using UnityEngine.Experimental.GlobalIllumination; using UnityEngine.TestTools; namespace Unity.Netcode.RuntimeTests From 043b0e72dbc5fb2439533f57541ef652ca1306f2 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Thu, 10 Jul 2025 14:32:06 -0500 Subject: [PATCH 14/43] update Updating the change log entries. --- com.unity.netcode.gameobjects/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 9788b12a69..1f3ea5a441 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -10,6 +10,9 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added +- Added `AttachableBehaviour` helper component to provide an alternate approach to parenting items without using the `NetworkObject` parenting. (#3518) +- Added `AttachableNode` helper component that is used by `AttachableBehaviour` as the target node for parenting. (#3518) +- Added `ComponentController` helper component that can be used to synchronize the enabling and disabling of components and can be used in conjunction with `AttachableBehaviour`. (#3518) - Added methods `GetDefaultNetworkSettings` and `GetDefaultPipelineConfigurations` to `UnityTransport`. These can be used to retrieve the default settings and pipeline stages that are used by `UnityTransport`. This is useful when providing a custom driver constructor through `UnityTransport.s_DriverConstructor`, since it allows reusing or tuning the existing configuration instead of trying to recreate it. This means a transport with a custom driver can now easily benefit from most of the features of `UnityTransport`, like integration with the Network Simulator and Network Profiler from the multiplayer tools package. (#3501) - Added mappings between `ClientId` and `TransportId`. (#3516) - Added `NetworkPrefabInstanceHandlerWithData<T>`, a variant of `INetworkPrefabInstanceHandler` that provides access to custom instantiation data directly within the `Instantiate()` method. (#3430) From abd75d0bda1d843db73ae1b3020abc5f3197b894 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Thu, 10 Jul 2025 15:33:04 -0500 Subject: [PATCH 15/43] update Removing debug log info. --- .../Runtime/Components/Helpers/AttachableBehaviour.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 260257bfa4..91739f5d1b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -254,10 +254,6 @@ private void UpdateAttachState(AttachState attachState, AttachableNode attachabl /// </summary> internal void InternalAttach(AttachableNode attachableNode) { - if (attachableNode.NetworkManager != NetworkManager) - { - Debug.Log("Blam!"); - } m_AttachState = AttachState.Attached; m_AttachableNode = attachableNode; // Attachables are always local space relative From e098e31405687e3b986dc46d9037a4097b5c41ff Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Sat, 12 Jul 2025 19:37:20 -0500 Subject: [PATCH 16/43] update Work in progress adjustments for usability under various scenarios. --- .../Components/Helpers/AttachableBehaviour.cs | 21 ++- .../Components/Helpers/AttachableNode.cs | 5 + .../Components/Helpers/ComponentController.cs | 140 +++++++++++++++++- 3 files changed, 156 insertions(+), 10 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 91739f5d1b..378146f7ca 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -18,7 +18,10 @@ namespace Unity.Netcode.Components /// The term "attach" is used in place of parenting in order to distinguish between <see cref="NetworkObject"/> parenting and /// <see cref="AttachableBehaviour"/> parenting ("attaching" and "detaching").<br /> /// This component can be used along with one or more <see cref="ComponentController"/> in order to enable or disable different components depending - /// upon the <see cref="AttachableBehaviour"/> instance's current state. + /// upon the <see cref="AttachableBehaviour"/> instance's current state.<br /> + /// <see cref="AttachableNode"/> invocation order: + /// - When attaching, the <see cref="AttachableNode"/>'s <see cref="AttachableNode.OnAttached(AttachableBehaviour)"/> is invoked just before the <see cref="OnAttachStateChanged"/> is invoked with the <see cref="AttachState.Attached"/> state.<br /> + /// - When detaching, the <see cref="AttachableNode"/>'s <see cref="AttachableNode.OnDetached(AttachableBehaviour)"/> is invoked right after the <see cref="OnAttachStateChanged"/> is invoked with the <see cref="AttachState.Detached"/> notification.<br /> /// </remarks> public class AttachableBehaviour : NetworkBehaviour { @@ -191,9 +194,11 @@ private void UpdateAttachedState() { // Run through the same process without being triggerd by a NetVar update. UpdateAttachState(AttachState.Detaching, m_AttachableNode); + InternalDetach(); + UpdateAttachState(AttachState.Detached, m_AttachableNode); + m_AttachableNode.Detach(this); - transform.parent = null; - UpdateAttachState(AttachState.Detached, null); + m_AttachableNode = null; } } @@ -211,6 +216,14 @@ private void UpdateAttachedState() // Notify of the changed attached state UpdateAttachState(m_AttachState, m_AttachableNode); + + // When detatching, we want to make our final action + // the invocation of the AttachableNode's Detatch method. + if (!shouldParent && m_AttachableNode) + { + m_AttachableNode.Detach(this); + m_AttachableNode = null; + } } /// <summary> @@ -309,8 +322,6 @@ internal void InternalDetach() { if (m_AttachableNode) { - m_AttachableNode.Detach(this); - m_AttachableNode = null; if (m_DefaultParent) { // Set the original parent and origianl local position and rotation diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs index 8c5ad47cf5..e732b413dd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs @@ -14,6 +14,11 @@ /// </remarks> public class AttachableNode : NetworkBehaviour { + /// <summary> + /// Returns true if the <see cref="AttachableNode"/> instance has one or more attached <see cref="AttachableBehaviour"/> components. + /// </summary> + public bool HasAttachments => m_AttachedBehaviours.Count > 0; + /// <summary> /// A <see cref="List{T}"/> of the currently attached <see cref="AttachableBehaviour"/>s. /// </summary> diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index cc87d6f732..6aa8d60f84 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Reflection; using UnityEngine; @@ -16,8 +17,19 @@ public class ComponentControllerEntry /// When true, this component's enabled state will be the inverse of /// the value passed into <see cref="ComponentController.SetEnabled(bool)"/>. /// </summary> + [Tooltip("When enabled, this component will inversely mirror the currently applied enable or disable state.")] public bool InvertEnabled; + /// <summary> + /// The amount of time to delay enabling this component when the <see cref="ComponentController"/> has just transitioned from a disabled to enabled state. + /// </summary> + public float EnableDelay; + + /// <summary> + /// The amount of time to delay disabling this component when the <see cref="ComponentController"/> has just transitioned from an enabled to disabled state. + /// </summary> + public float DisableDelay; + /// <summary> /// The component to control. /// </summary> @@ -27,8 +39,63 @@ public class ComponentControllerEntry /// and <see cref="InvertEnabled"/> properties will be applied to all components found on the <see cref="GameObject"/>. /// </remarks> public Object Component; - internal PropertyInfo PropertyInfo; + + + internal bool TimeDeltaDelayInProgress; + internal bool PendingState; + internal float EnableDelayTimeDelta; + internal float DisableDelayTimeDelta; + + private bool GetRelativeEnabled(bool enabled) + { + return InvertEnabled ? !enabled : enabled; + } + + internal bool HasDelay(bool enabled) + { + return GetRelativeEnabled(enabled) ? EnableDelay > 0.0f : DisableDelay > 0.0f; + } + + internal void StartTimeDeltaTracking(bool isEnabled) + { + if (GetRelativeEnabled(isEnabled)) + { + EnableDelayTimeDelta = Time.realtimeSinceStartup + EnableDelay; + } + else + { + DisableDelayTimeDelta = Time.realtimeSinceStartup + DisableDelay; + } + TimeDeltaDelayInProgress = true; + PendingState = isEnabled; + } + + + internal void SetValue(bool isEnabled) + { + // If invert enabled is true, then use the inverted value passed in. + // Otherwise, directly apply the value passed in. + PropertyInfo.SetValue(Component, GetRelativeEnabled(isEnabled)); + } + + internal bool CheckTimeDeltaDelay() + { + if (!TimeDeltaDelayInProgress) + { + return false; + } + + var isDeltaDelayInProgress = ((EnableDelayTimeDelta > Time.realtimeSinceStartup) + || (DisableDelayTimeDelta > Time.realtimeSinceStartup)); + + if (!isDeltaDelayInProgress) + { + SetValue(PendingState); + } + TimeDeltaDelayInProgress = isDeltaDelayInProgress; + return TimeDeltaDelayInProgress; + } } /// <summary> @@ -52,6 +119,17 @@ public class ComponentController : NetworkBehaviour [Tooltip("The initial state of the component controllers enabled status when instnatiated.")] public bool StartEnabled = true; + /// <summary> + /// The coroutine yield time used to check on any pending delayed <see cref="ComponentControllerEntry"/> state transitions. + /// </summary> + /// <remarks> + /// If there are any <see cref="ComponentControllerEntry"/> delays (enable, disable, or both), then upon changing the state of a <see cref="ComponentController"/> a coroutine will be started + /// (if not already started) that will monitor any pending delayed transitions (<see cref="EnableDelay"/> or <see cref="DisableDelay"/>) and will + /// handle changing each delayed <see cref="ComponentControllerEntry"/> until all instances have transitioned their value to the pending state change. + /// When there are no more pending delayed <see cref="ComponentControllerEntry"/>s, the coroutine will stop. + /// </remarks> + public float PendingDelayYieldTime = 0.032f; + /// <summary> /// The list of <see cref="Components"/>s to be enabled and disabled. /// </summary> @@ -214,6 +292,17 @@ public override void OnNetworkDespawn() base.OnNetworkDespawn(); } + /// <inheritdoc/> + public override void OnDestroy() + { + if (m_CoroutineObject.IsRunning) + { + StopCoroutine(m_CoroutineObject.Coroutine); + m_CoroutineObject.IsRunning = false; + } + base.OnDestroy(); + } + private void OnEnabledChanged(bool previous, bool current) { ApplyEnabled(current); @@ -239,13 +328,54 @@ private void InitializeComponents() /// <param name="enabled">the state update to apply</param> private void ApplyEnabled(bool enabled) { + foreach (var entry in ValidComponents) { - // If invert enabled is true, then use the inverted value passed in. - // Otherwise, directly apply the value passed in. - var isEnabled = entry.InvertEnabled ? !enabled : enabled; - entry.PropertyInfo.SetValue(entry.Component, isEnabled); + if (entry.HasDelay(enabled)) + { + if (!m_CoroutineObject.IsRunning) + { + m_CoroutineObject.Coroutine = StartCoroutine(PendingAppliedState()); + m_CoroutineObject.IsRunning = true; + } + entry.StartTimeDeltaTracking(enabled); + } + else + { + entry.SetValue(enabled); + } + } + } + + private class CoroutineObject + { + public Coroutine Coroutine; + public bool IsRunning; + } + + private CoroutineObject m_CoroutineObject = new CoroutineObject(); + + + private IEnumerator PendingAppliedState() + { + var checkPendingDeltaDelays = true; + var waitTime = new WaitForSeconds(PendingDelayYieldTime); + + while (checkPendingDeltaDelays) + { + yield return waitTime; + checkPendingDeltaDelays = false; + foreach (var entry in ValidComponents) + { + if (!entry.CheckTimeDeltaDelay()) + { + continue; + } + checkPendingDeltaDelays = true; + } } + m_CoroutineObject.IsRunning = false; + yield break; } /// <summary> From cab19832cc21d566262a5318258de0204340c7f1 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Sat, 12 Jul 2025 22:31:27 -0500 Subject: [PATCH 17/43] refactor Using RPCs and synchronizing the property being set using NetworkBehaviour.OnSynchronize in order to assure order of operations when it comes to messages. Updated the ComponentController to be able to stagger state updates in the event this occurs (wip and I might remove this part and not allow changes to state until any pending state is finished). --- .../Components/Helpers/AttachableBehaviour.cs | 83 ++++--- .../Components/Helpers/ComponentController.cs | 215 ++++++++++++------ 2 files changed, 195 insertions(+), 103 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 378146f7ca..131b7e7bea 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -116,10 +116,26 @@ public enum AttachState /// </summary> protected AttachableNode m_AttachableNode { get; private set; } - private NetworkVariable<NetworkBehaviourReference> m_AttachedNodeReference = new NetworkVariable<NetworkBehaviourReference>(new NetworkBehaviourReference(null)); + private NetworkBehaviourReference m_AttachedNodeReference = new NetworkBehaviourReference(null); private Vector3 m_OriginalLocalPosition; private Quaternion m_OriginalLocalRotation; + /// <inheritdoc/> + protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer) + { + // Example of how to synchronize late joining clients when using an RPC to update + // a local property's state. + if (serializer.IsWriter) + { + serializer.SerializeValue(ref m_AttachedNodeReference); + } + else + { + serializer.SerializeValue(ref m_AttachedNodeReference); + } + base.OnSynchronize(ref serializer); + } + /// <summary> /// If you create a custom <see cref="AttachableBehaviour"/> and override this method, you must invoke /// this base instance of <see cref="Awake"/>. @@ -133,21 +149,6 @@ protected virtual void Awake() m_AttachableNode = null; } - /// <inheritdoc/> - /// <remarks> - /// If you create a custom <see cref="AttachableBehaviour"/> and override this method, you must invoke - /// this base instance of <see cref="OnNetworkPostSpawn"/>. - /// </remarks> - protected override void OnNetworkPostSpawn() - { - if (HasAuthority) - { - m_AttachedNodeReference.Value = new NetworkBehaviourReference(null); - } - m_AttachedNodeReference.OnValueChanged += OnAttachedNodeReferenceChanged; - base.OnNetworkPostSpawn(); - } - /// <inheritdoc/> /// <remarks> /// If you create a custom <see cref="AttachableBehaviour"/> and override this method, you will want to @@ -164,25 +165,20 @@ protected override void OnNetworkSessionSynchronized() /// <inheritdoc/> public override void OnNetworkDespawn() { - m_AttachedNodeReference.OnValueChanged -= OnAttachedNodeReferenceChanged; InternalDetach(); if (NetworkManager && !NetworkManager.ShutdownInProgress) { // Notify of the changed attached state UpdateAttachState(m_AttachState, m_AttachableNode); } + m_AttachedNodeReference = new NetworkBehaviourReference(null); base.OnNetworkDespawn(); } - private void OnAttachedNodeReferenceChanged(NetworkBehaviourReference previous, NetworkBehaviourReference current) - { - UpdateAttachedState(); - } - private void UpdateAttachedState() { var attachableNode = (AttachableNode)null; - var shouldParent = m_AttachedNodeReference.Value.TryGet(out attachableNode, NetworkManager); + var shouldParent = m_AttachedNodeReference.TryGet(out attachableNode, NetworkManager); var preState = shouldParent ? AttachState.Attaching : AttachState.Detaching; var preNode = shouldParent ? attachableNode : m_AttachableNode; shouldParent = shouldParent && attachableNode != null; @@ -310,8 +306,7 @@ public void Attach(AttachableNode attachableNode) return; } - // Update the attached node reference to the new attachable node. - m_AttachedNodeReference.Value = new NetworkBehaviourReference(attachableNode); + ChangeReference(new NetworkBehaviourReference(attachableNode)); } /// <summary> @@ -344,7 +339,7 @@ public void Detach() return; } - if (!HasAuthority) + if (!OnHasAuthority()) { NetworkLog.LogError($"[{name}][Detach][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!"); return; @@ -373,8 +368,40 @@ public void Detach() return; } - // Update the attached node reference to nothing-null. - m_AttachedNodeReference.Value = new NetworkBehaviourReference(null); + ChangeReference(new NetworkBehaviourReference(null)); + } + + /// <summary> + /// Override this method to change how the instance determines the authority.<br /> + /// The default is to use the <see cref="NetworkObject.HasAuthority"/> method. + /// </summary> + /// <remarks> + /// Useful when using a <see cref="NetworkTopologyTypes.ClientServer"/> network topology and you would like + /// to have the owner be the authority of this <see cref="ComponentController"/> instance. + /// </remarks> + /// <returns>true = has authoriy | false = does not have authority</returns> + protected virtual bool OnHasAuthority() + { + return HasAuthority; + } + + private void ChangeReference(NetworkBehaviourReference networkBehaviourReference) + { + // Update the attached node reference to the new attachable node. + m_AttachedNodeReference = networkBehaviourReference; + UpdateAttachedState(); + + if (OnHasAuthority()) + { + // Send notification of the change in this property's state. + UpdateAttachStateRpc(m_AttachedNodeReference); + } + } + + [Rpc(SendTo.NotMe)] + private void UpdateAttachStateRpc(NetworkBehaviourReference attachedNodeReference) + { + ChangeReference(attachedNodeReference); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 6aa8d60f84..03a3dfb16d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -23,11 +23,13 @@ public class ComponentControllerEntry /// <summary> /// The amount of time to delay enabling this component when the <see cref="ComponentController"/> has just transitioned from a disabled to enabled state. /// </summary> + [Range(0.001f, 2.0f)] public float EnableDelay; /// <summary> /// The amount of time to delay disabling this component when the <see cref="ComponentController"/> has just transitioned from an enabled to disabled state. /// </summary> + [Range(0.001f, 2.0f)] public float DisableDelay; /// <summary> @@ -41,37 +43,38 @@ public class ComponentControllerEntry public Object Component; internal PropertyInfo PropertyInfo; - - internal bool TimeDeltaDelayInProgress; - internal bool PendingState; - internal float EnableDelayTimeDelta; - internal float DisableDelayTimeDelta; - - private bool GetRelativeEnabled(bool enabled) + internal bool GetRelativeEnabled(bool enabled) { return InvertEnabled ? !enabled : enabled; } - internal bool HasDelay(bool enabled) - { - return GetRelativeEnabled(enabled) ? EnableDelay > 0.0f : DisableDelay > 0.0f; - } + private List<PendingStateUpdate> m_PendingStateUpdates = new List<PendingStateUpdate>(); - internal void StartTimeDeltaTracking(bool isEnabled) + /// <summary> + /// Invoke prior to setting the state + /// </summary> + internal bool QueueForDelay(bool enabled) { - if (GetRelativeEnabled(isEnabled)) - { - EnableDelayTimeDelta = Time.realtimeSinceStartup + EnableDelay; - } - else + var relativeEnabled = GetRelativeEnabled(enabled); + + if (relativeEnabled ? EnableDelay > 0.0f : DisableDelay > 0.0f) { - DisableDelayTimeDelta = Time.realtimeSinceStartup + DisableDelay; + // Start with no relative time offset + var relativeTimeOffset = 0.0f; + // If we have pending state updates, then get that time of the last state update + // and use that as the time to add this next state update. + if (m_PendingStateUpdates.Count > 0) + { + relativeTimeOffset = m_PendingStateUpdates[m_PendingStateUpdates.Count - 1].DelayTimeDelta; + } + + // We process backwards, so insert new entries at the front + m_PendingStateUpdates.Insert(0, new PendingStateUpdate(this, enabled, relativeTimeOffset)); + return true; } - TimeDeltaDelayInProgress = true; - PendingState = isEnabled; + return false; } - internal void SetValue(bool isEnabled) { // If invert enabled is true, then use the inverted value passed in. @@ -79,25 +82,66 @@ internal void SetValue(bool isEnabled) PropertyInfo.SetValue(Component, GetRelativeEnabled(isEnabled)); } - internal bool CheckTimeDeltaDelay() + internal bool HasPendingStateUpdates() { - if (!TimeDeltaDelayInProgress) + for (int i = m_PendingStateUpdates.Count - 1; i >= 0; i--) { - return false; + if (!m_PendingStateUpdates[i].CheckTimeDeltaDelay()) + { + m_PendingStateUpdates.RemoveAt(i); + continue; + } } + return m_PendingStateUpdates.Count > 0; + } + + private class PendingStateUpdate + { + internal bool TimeDeltaDelayInProgress; + internal bool PendingState; + internal float DelayTimeDelta; - var isDeltaDelayInProgress = ((EnableDelayTimeDelta > Time.realtimeSinceStartup) - || (DisableDelayTimeDelta > Time.realtimeSinceStartup)); + internal ComponentControllerEntry ComponentControllerEntry; - if (!isDeltaDelayInProgress) + internal bool CheckTimeDeltaDelay() { - SetValue(PendingState); + if (!TimeDeltaDelayInProgress) + { + return false; + } + + var isDeltaDelayInProgress = DelayTimeDelta > Time.realtimeSinceStartup; + + if (!isDeltaDelayInProgress) + { + ComponentControllerEntry.SetValue(PendingState); + } + TimeDeltaDelayInProgress = isDeltaDelayInProgress; + return TimeDeltaDelayInProgress; + } + + internal PendingStateUpdate(ComponentControllerEntry componentControllerEntry, bool isEnabled, float relativeTimeOffset) + { + ComponentControllerEntry = componentControllerEntry; + // If there is a pending state, then add the delay to the end of the last pending state's. + var referenceTime = relativeTimeOffset > 0.0f ? relativeTimeOffset : Time.realtimeSinceStartup; + + if (ComponentControllerEntry.GetRelativeEnabled(isEnabled)) + { + DelayTimeDelta = referenceTime + ComponentControllerEntry.EnableDelay; + } + else + { + DelayTimeDelta = referenceTime + ComponentControllerEntry.DisableDelay; + } + TimeDeltaDelayInProgress = true; + PendingState = isEnabled; } - TimeDeltaDelayInProgress = isDeltaDelayInProgress; - return TimeDeltaDelayInProgress; } } + + /// <summary> /// Handles enabling or disabling commonly used components, behaviours, RenderMeshes, etc.<br /> /// Anything that derives from <see cref="Component"/> and has an enabled property can be added @@ -119,17 +163,6 @@ public class ComponentController : NetworkBehaviour [Tooltip("The initial state of the component controllers enabled status when instnatiated.")] public bool StartEnabled = true; - /// <summary> - /// The coroutine yield time used to check on any pending delayed <see cref="ComponentControllerEntry"/> state transitions. - /// </summary> - /// <remarks> - /// If there are any <see cref="ComponentControllerEntry"/> delays (enable, disable, or both), then upon changing the state of a <see cref="ComponentController"/> a coroutine will be started - /// (if not already started) that will monitor any pending delayed transitions (<see cref="EnableDelay"/> or <see cref="DisableDelay"/>) and will - /// handle changing each delayed <see cref="ComponentControllerEntry"/> until all instances have transitioned their value to the pending state change. - /// When there are no more pending delayed <see cref="ComponentControllerEntry"/>s, the coroutine will stop. - /// </remarks> - public float PendingDelayYieldTime = 0.032f; - /// <summary> /// The list of <see cref="Components"/>s to be enabled and disabled. /// </summary> @@ -139,10 +172,10 @@ public class ComponentController : NetworkBehaviour /// <summary> /// Returns the current enabled state of the <see cref="ComponentController"/>. /// </summary> - public bool EnabledState => m_IsEnabled.Value; + public bool EnabledState => m_IsEnabled; internal List<ComponentControllerEntry> ValidComponents = new List<ComponentControllerEntry>(); - private NetworkVariable<bool> m_IsEnabled = new NetworkVariable<bool>(); + private bool m_IsEnabled; #if UNITY_EDITOR /// <inheritdoc/> @@ -219,6 +252,22 @@ protected virtual void OnValidate() } #endif + /// <inheritdoc/> + protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer) + { + // Example of how to synchronize late joining clients when using an RPC to update + // a local property's state. + if (serializer.IsWriter) + { + serializer.SerializeValue(ref m_IsEnabled); + } + else + { + serializer.SerializeValue(ref m_IsEnabled); + } + base.OnSynchronize(ref serializer); + } + /// <summary> /// This checks to make sure that all <see cref="Component"/> entries are valid and will create a final /// <see cref="ComponentControllerEntry"/> list of valid entries. @@ -265,9 +314,9 @@ protected virtual void Awake() /// <inheritdoc/> public override void OnNetworkSpawn() { - if (HasAuthority) + if (OnHasAuthority()) { - m_IsEnabled.Value = StartEnabled; + m_IsEnabled = StartEnabled; } base.OnNetworkSpawn(); } @@ -280,18 +329,10 @@ public override void OnNetworkSpawn() /// </remarks> protected override void OnNetworkPostSpawn() { - m_IsEnabled.OnValueChanged += OnEnabledChanged; - ApplyEnabled(m_IsEnabled.Value); + ApplyEnabled(); base.OnNetworkPostSpawn(); } - /// <inheritdoc/> - public override void OnNetworkDespawn() - { - m_IsEnabled.OnValueChanged -= OnEnabledChanged; - base.OnNetworkDespawn(); - } - /// <inheritdoc/> public override void OnDestroy() { @@ -303,11 +344,6 @@ public override void OnDestroy() base.OnDestroy(); } - private void OnEnabledChanged(bool previous, bool current) - { - ApplyEnabled(current); - } - /// <summary> /// Initializes each component entry to its initial state. /// </summary> @@ -326,23 +362,21 @@ private void InitializeComponents() /// Applies states changes to all components being controlled by this instance. /// </summary> /// <param name="enabled">the state update to apply</param> - private void ApplyEnabled(bool enabled) + private void ApplyEnabled() { - foreach (var entry in ValidComponents) { - if (entry.HasDelay(enabled)) + if (entry.QueueForDelay(m_IsEnabled)) { if (!m_CoroutineObject.IsRunning) { m_CoroutineObject.Coroutine = StartCoroutine(PendingAppliedState()); m_CoroutineObject.IsRunning = true; } - entry.StartTimeDeltaTracking(enabled); } else { - entry.SetValue(enabled); + entry.SetValue(m_IsEnabled); } } } @@ -358,24 +392,24 @@ private class CoroutineObject private IEnumerator PendingAppliedState() { - var checkPendingDeltaDelays = true; - var waitTime = new WaitForSeconds(PendingDelayYieldTime); + var continueProcessing = true; - while (checkPendingDeltaDelays) + while (continueProcessing) { - yield return waitTime; - checkPendingDeltaDelays = false; + continueProcessing = false; foreach (var entry in ValidComponents) { - if (!entry.CheckTimeDeltaDelay()) + if (entry.HasPendingStateUpdates()) { - continue; + continueProcessing = true; } - checkPendingDeltaDelays = true; + } + if (continueProcessing) + { + yield return null; } } m_CoroutineObject.IsRunning = false; - yield break; } /// <summary> @@ -396,12 +430,43 @@ public void SetEnabled(bool isEnabled) return; } - if (!HasAuthority) + if (!OnHasAuthority()) { Debug.Log($"[Client-{NetworkManager.LocalClientId}] Attempting to invoke {nameof(SetEnabled)} without authority!"); return; } - m_IsEnabled.Value = isEnabled; + ChangeEnabled(isEnabled); + } + + private void ChangeEnabled(bool isEnabled) + { + m_IsEnabled = isEnabled; + ApplyEnabled(); + + if (OnHasAuthority()) + { + ToggleEnabledRpc(m_IsEnabled); + } + } + + /// <summary> + /// Override this method to change how the instance determines the authority.<br /> + /// The default is to use the <see cref="NetworkObject.HasAuthority"/> method. + /// </summary> + /// <remarks> + /// Useful when using a <see cref="NetworkTopologyTypes.ClientServer"/> network topology and you would like + /// to have the owner be the authority of this <see cref="ComponentController"/> instance. + /// </remarks> + /// <returns>true = has authoriy | false = does not have authority</returns> + protected virtual bool OnHasAuthority() + { + return HasAuthority; + } + + [Rpc(SendTo.NotMe)] + private void ToggleEnabledRpc(bool enabled) + { + ChangeEnabled(enabled); } } } From 1341aa085ddad47d47a90e569daa5c255cc76b63 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Sat, 12 Jul 2025 22:34:17 -0500 Subject: [PATCH 18/43] style --- .../Runtime/Components/Helpers/ComponentController.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 03a3dfb16d..110644a206 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -51,7 +51,7 @@ internal bool GetRelativeEnabled(bool enabled) private List<PendingStateUpdate> m_PendingStateUpdates = new List<PendingStateUpdate>(); /// <summary> - /// Invoke prior to setting the state + /// Invoke prior to setting the state. /// </summary> internal bool QueueForDelay(bool enabled) { @@ -140,8 +140,6 @@ internal PendingStateUpdate(ComponentControllerEntry componentControllerEntry, b } } - - /// <summary> /// Handles enabling or disabling commonly used components, behaviours, RenderMeshes, etc.<br /> /// Anything that derives from <see cref="Component"/> and has an enabled property can be added From 13d9a03ed6eeb7b298173928a9024f84dae11250 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Sun, 13 Jul 2025 17:01:41 -0500 Subject: [PATCH 19/43] update & style Minor adjustment to the range for delays allow it to be zero. Fixing spelling issues. --- .../Runtime/Components/Helpers/AttachableBehaviour.cs | 6 +++--- .../Runtime/Components/Helpers/ComponentController.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 131b7e7bea..cd1cedc09f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -213,8 +213,8 @@ private void UpdateAttachedState() // Notify of the changed attached state UpdateAttachState(m_AttachState, m_AttachableNode); - // When detatching, we want to make our final action - // the invocation of the AttachableNode's Detatch method. + // When detaching, we want to make our final action + // the invocation of the AttachableNode's Detach method. if (!shouldParent && m_AttachableNode) { m_AttachableNode.Detach(this); @@ -227,7 +227,7 @@ private void UpdateAttachedState() /// when the <see cref="AttachState"/> has changed. /// </summary> /// <param name="attachState">The new <see cref="AttachState"/>.</param> - /// <param name="attachableNode">The <see cref="AttachableNode"/> being attached to or from. Will be null when completely detatched.</param> + /// <param name="attachableNode">The <see cref="AttachableNode"/> being attached to or from. Will be null when completely detached.</param> protected virtual void OnAttachStateChanged(AttachState attachState, AttachableNode attachableNode) { diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 110644a206..8093e6ef44 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -23,13 +23,13 @@ public class ComponentControllerEntry /// <summary> /// The amount of time to delay enabling this component when the <see cref="ComponentController"/> has just transitioned from a disabled to enabled state. /// </summary> - [Range(0.001f, 2.0f)] + [Range(0.0f, 2.0f)] public float EnableDelay; /// <summary> /// The amount of time to delay disabling this component when the <see cref="ComponentController"/> has just transitioned from an enabled to disabled state. /// </summary> - [Range(0.001f, 2.0f)] + [Range(0f, 2.0f)] public float DisableDelay; /// <summary> From 8bd712cffe1d72666cf3ff8daefc2a541c4fdfa7 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Mon, 14 Jul 2025 20:28:28 -0500 Subject: [PATCH 20/43] refactor Had to make some minor adjustments in order to assure that users could handle sending any last micro-second tasks on any spawned instances prior to them despawning. Added NetworkBehaviour.OnNetworkPreDespawn. Did a slight order of operations on NetworkManager.Shutdown internal in order to assure sending RPCs during despawn would still work. Minor adjustments to the new helper components associated with this PR. --- .../Components/Helpers/AttachableBehaviour.cs | 6 +++- .../Components/Helpers/AttachableNode.cs | 21 ++++++++++-- .../Components/Helpers/ComponentController.cs | 2 +- .../Runtime/Core/NetworkBehaviour.cs | 17 ++++++++++ .../Runtime/Core/NetworkManager.cs | 32 ++++++++++++------- .../Runtime/Core/NetworkObject.cs | 6 ++++ .../Runtime/Spawning/NetworkSpawnManager.cs | 12 ++----- 7 files changed, 70 insertions(+), 26 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index cd1cedc09f..1962ca6222 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -333,6 +333,10 @@ internal void InternalDetach() /// </summary> public void Detach() { + if (!gameObject) + { + return; + } if (!IsSpawned) { NetworkLog.LogError($"[{name}][Detach][Not Spawned] Cannot detach if not spawned!"); @@ -399,7 +403,7 @@ private void ChangeReference(NetworkBehaviourReference networkBehaviourReference } [Rpc(SendTo.NotMe)] - private void UpdateAttachStateRpc(NetworkBehaviourReference attachedNodeReference) + private void UpdateAttachStateRpc(NetworkBehaviourReference attachedNodeReference, RpcParams rpcParams = default) { ChangeReference(attachedNodeReference); } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs index e732b413dd..c62e16085b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs @@ -19,6 +19,11 @@ public class AttachableNode : NetworkBehaviour /// </summary> public bool HasAttachments => m_AttachedBehaviours.Count > 0; + /// <summary> + /// When enabled, any attached <see cref="AttachableBehaviour"/>s will be automatically detached and re-parented under its original parent. + /// </summary> + public bool DetachOnDespawn = true; + /// <summary> /// A <see cref="List{T}"/> of the currently attached <see cref="AttachableBehaviour"/>s. /// </summary> @@ -29,12 +34,22 @@ public class AttachableNode : NetworkBehaviour /// If the <see cref="NetworkObject"/> this <see cref="AttachableNode"/> belongs to is despawned, /// then any attached <see cref="AttachableBehaviour"/> will be detached during <see cref="OnNetworkDespawn"/>. /// </remarks> - public override void OnNetworkDespawn() + public override void OnNetworkPreDespawn() { - for (int i = m_AttachedBehaviours.Count - 1; i > 0; i--) + if (IsSpawned && HasAuthority && DetachOnDespawn) { - m_AttachedBehaviours[i].InternalDetach(); + for (int i = m_AttachedBehaviours.Count - 1; i >= 0; i--) + { + m_AttachedBehaviours[i]?.Detach(); + } } + base.OnNetworkPreDespawn(); + } + + /// <inheritdoc/> + public override void OnNetworkDespawn() + { + m_AttachedBehaviours.Clear(); base.OnNetworkDespawn(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 8093e6ef44..8a7e1710a3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -462,7 +462,7 @@ protected virtual bool OnHasAuthority() } [Rpc(SendTo.NotMe)] - private void ToggleEnabledRpc(bool enabled) + private void ToggleEnabledRpc(bool enabled, RpcParams rpcParams = default) { ChangeEnabled(enabled); } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 62e60fb436..73696fa0a4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -737,6 +737,11 @@ protected virtual void OnInSceneObjectsSpawned() { } /// </summary> public virtual void OnNetworkDespawn() { } + /// <summary> + /// Gets called before <see cref="OnNetworkDespawn"/> has been invoked for all <see cref="NetworkBehaviour"/>s associated with the currently spawned <see cref="NetworkObject"/> instance. + /// </summary> + public virtual void OnNetworkPreDespawn() { } + internal void NetworkPreSpawn(ref NetworkManager networkManager) { try @@ -816,6 +821,18 @@ internal void InSceneNetworkObjectsSpawned() } } + internal void InternalOnNetworkPreDespawn() + { + try + { + OnNetworkPreDespawn(); + } + catch (Exception e) + { + Debug.LogException(e); + } + } + internal void InternalOnNetworkDespawn() { IsSpawned = false; diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index cff2768405..3c6662b7be 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1552,12 +1552,27 @@ internal void ShutdownInternal() DeferredMessageManager?.CleanupAllTriggers(); CustomMessagingManager = null; - RpcTarget?.Dispose(); - RpcTarget = null; - BehaviourUpdater?.Shutdown(); BehaviourUpdater = null; + /// Despawning upon shutdown + + // We need to clean up NetworkObjects before we reset the IsServer + // and IsClient properties. This provides consistency of these two + // property values for NetworkObjects that are still spawned when + // the shutdown cycle begins. + + // We need to handle despawning prior to shutting down the connection + // manager or disposing of the RpcTarget so any final updates can take + // place (i.e. sending any last state updates or the like). + + SpawnManager?.DespawnAndDestroyNetworkObjects(); + SpawnManager?.ServerResetShudownStateForSceneObjects(); + //// + + RpcTarget?.Dispose(); + RpcTarget = null; + // Shutdown connection manager last which shuts down transport ConnectionManager.Shutdown(); @@ -1567,17 +1582,12 @@ internal void ShutdownInternal() MessageManager = null; } - // We need to clean up NetworkObjects before we reset the IsServer - // and IsClient properties. This provides consistency of these two - // property values for NetworkObjects that are still spawned when - // the shutdown cycle begins. - SpawnManager?.DespawnAndDestroyNetworkObjects(); - SpawnManager?.ServerResetShudownStateForSceneObjects(); - SpawnManager = null; - // Let the NetworkSceneManager clean up its two SceneEvenData instances SceneManager?.Dispose(); SceneManager = null; + + SpawnManager = null; + IsListening = false; m_ShuttingDown = false; diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index b8c5c5b551..c6e2228819 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -2604,6 +2604,12 @@ internal void InternalInSceneNetworkObjectsSpawned() internal void InvokeBehaviourNetworkDespawn() { + // Invoke OnNetworkPreDespawn on all child behaviours + for (int i = 0; i < ChildNetworkBehaviours.Count; i++) + { + ChildNetworkBehaviours[i].InternalOnNetworkPreDespawn(); + } + NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId, true); NetworkManager.SpawnManager.RemoveNetworkObjectFromSceneChangedUpdates(this); diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 92b90972a7..d9a571b97b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1474,16 +1474,8 @@ internal void DespawnAndDestroyNetworkObjects() } } - // If spawned, then despawn and potentially destroy. - if (networkObjects[i].IsSpawned) - { - OnDespawnObject(networkObjects[i], shouldDestroy); - } - else // Otherwise, if we are not spawned and we should destroy...then destroy. - if (shouldDestroy) - { - UnityEngine.Object.Destroy(networkObjects[i].gameObject); - } + //Despawn and potentially destroy. + OnDespawnObject(networkObjects[i], shouldDestroy); } } } From a63de0b462fcb33d1c30259615bf5bed7371fdba Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Mon, 14 Jul 2025 20:30:26 -0500 Subject: [PATCH 21/43] test Updating the AttachableBehaviourTests to include testing that an attachable will be automatically detatched upon despawning the AttachableNode it is attached to. --- .../Tests/Runtime/AttachableBehaviourTests.cs | 72 ++++++++++++++++--- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs index 0efc6b6654..af0568ab7f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs @@ -25,6 +25,7 @@ public AttachableBehaviourTests(HostOrServer hostOrServer) : base(hostOrServer) /// <summary> /// All of the below instances belong to the authority /// </summary> + private ulong m_TargetInstanceId; private NetworkObject m_SourceInstance; private NetworkObject m_TargetInstance; private NetworkObject m_TargetInstanceB; @@ -47,6 +48,7 @@ protected override void OnServerAndClientsCreated() // The source prefab contains the nested NetworkBehaviour that // will be parented under the target prefab. m_SourcePrefab = CreateNetworkObjectPrefab("Source"); + m_SourcePrefab.GetComponent<NetworkObject>().DontDestroyWithOwner = true; // The target prefab that the source prefab will attach // will be parented under the target prefab. m_TargetPrefabA = CreateNetworkObjectPrefab("TargetA"); @@ -94,8 +96,11 @@ private bool ResetAllStates() { m_ErrorLog.Clear(); var target = GetTargetInstance(); + + // The attachable can move between the two spawned instances. var currentAttachableRoot = m_AttachableBehaviourInstance.State == AttachableBehaviour.AttachState.Attached ? target : m_SourceInstance; + foreach (var networkManager in m_NetworkManagers) { // Source @@ -110,7 +115,7 @@ private bool ResetAllStates() } // Target - if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstance.NetworkObjectId)) + if (m_TargetInstance && !networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstance.NetworkObjectId)) { m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {m_TargetInstance.name}!"); } @@ -121,7 +126,7 @@ private bool ResetAllStates() } // Target B - if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstanceB.NetworkObjectId)) + if (m_TargetInstanceB && !networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstanceB.NetworkObjectId)) { m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {m_TargetInstanceB.name}!"); } @@ -134,10 +139,11 @@ private bool ResetAllStates() return m_ErrorLog.Length == 0; } - private bool AllInstancesAttachedStateChanged(bool checkAttached) + private bool AllInstancesAttachedStateChanged(bool checkAttached, bool ignoreIfDespawned = false) { m_ErrorLog.Clear(); var target = GetTargetInstance(); + var targetId = target == null ? m_TargetInstanceId : target.NetworkObjectId; // The attachable can move between the two spawned instances so we have to use the appropriate one depending upon the authority's current state. var currentAttachableRoot = m_AttachableBehaviourInstance.State == AttachableBehaviour.AttachState.Attached ? target : m_SourceInstance; var attachable = (TestAttachable)null; @@ -146,7 +152,10 @@ private bool AllInstancesAttachedStateChanged(bool checkAttached) { if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(currentAttachableRoot.NetworkObjectId)) { - m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {currentAttachableRoot.name}!"); + if (!ignoreIfDespawned) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {currentAttachableRoot.name}!"); + } continue; } else @@ -156,7 +165,7 @@ private bool AllInstancesAttachedStateChanged(bool checkAttached) if (!attachable) { - attachable = networkManager.SpawnManager.SpawnedObjects[m_TargetInstance.NetworkObjectId].GetComponentInChildren<TestAttachable>(); + attachable = networkManager.SpawnManager.SpawnedObjects[m_TargetInstanceId].GetComponentInChildren<TestAttachable>(); if (!attachable) { attachable = networkManager.SpawnManager.SpawnedObjects[m_TargetInstanceB.NetworkObjectId].GetComponentInChildren<TestAttachable>(); @@ -168,14 +177,23 @@ private bool AllInstancesAttachedStateChanged(bool checkAttached) continue; } - if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(target.NetworkObjectId)) + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(targetId)) { - m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {target.name}!"); + if (!ignoreIfDespawned) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {target.name}!"); + } continue; } else { - node = networkManager.SpawnManager.SpawnedObjects[target.NetworkObjectId].GetComponentInChildren<TestNode>(); + node = networkManager.SpawnManager.SpawnedObjects[targetId].GetComponentInChildren<TestNode>(); + } + + if (!node && ignoreIfDespawned) + { + VerboseDebug("Skipping check during despawn."); + continue; } if (!attachable.CheckStateChangedOverride(checkAttached, false, node)) @@ -202,6 +220,18 @@ private bool AllInstancesAttachedStateChanged(bool checkAttached) return m_ErrorLog.Length == 0; } + private bool AllInstancesDespawned() + { + foreach (var networkManager in m_NetworkManagers) + { + if (networkManager.SpawnManager != null && networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstanceId)) + { + return false; + } + } + return true; + } + [UnityTest] public IEnumerator AttachAndDetachTests() { @@ -209,6 +239,7 @@ public IEnumerator AttachAndDetachTests() m_SourceInstance = SpawnObject(m_SourcePrefab, authority).GetComponent<NetworkObject>(); m_TargetInstance = SpawnObject(m_TargetPrefabA, authority).GetComponent<NetworkObject>(); m_TargetInstanceB = SpawnObject(m_TargetPrefabB, authority).GetComponent<NetworkObject>(); + m_TargetInstanceId = m_TargetInstance.NetworkObjectId; yield return WaitForConditionOrTimeOut(AllClientsSpawnedInstances); AssertOnTimeout($"Timed out waiting for all clients to spawn {m_SourceInstance.name} and {m_TargetInstance.name}!\n {m_ErrorLog}"); @@ -262,6 +293,26 @@ public IEnumerator AttachAndDetachTests() m_AttachableBehaviourInstance.Detach(); yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(false)); AssertOnTimeout($"Timed out waiting for all clients to detach {m_AttachableBehaviourInstance.name} from {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + // Finally, re-attach to the original spawned instance + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + m_AttachableBehaviourInstance.Attach(m_AttachableNodeInstance); + + // Switch back to using the first target attachable node + m_UseTargetB = false; + + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(true)); + AssertOnTimeout($"[Despawn Detach Phase] Timed out waiting for all clients to attach {m_AttachableBehaviourInstance.name} to {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + var targetInstanceName = m_TargetInstance.name; + VerboseDebug("======== DESPAWN & DETACH ========"); + m_TargetInstance.Despawn(); + m_TargetInstance = null; + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(false, true)); + AssertOnTimeout($"[Despawn Detach Phase] Timed out waiting for all clients to detach {m_AttachableBehaviourInstance.name} from {targetInstanceName}!\n {m_ErrorLog}"); + + yield return WaitForConditionOrTimeOut(AllInstancesDespawned); + AssertOnTimeout($"[Despawn Detach Phase] Timed out waiting for all clients to despawn {targetInstanceName}!"); } /// <summary> @@ -317,7 +368,6 @@ public bool CheckStateChangedOverride(bool checkAttached, bool checkEvent, Attac var tableToCheck = checkEvent ? m_StateUpdateEvents : m_StateUpdates; var checkStatus = checkAttached ? (tableToCheck.ContainsKey(AttachState.Attaching) && tableToCheck.ContainsKey(AttachState.Attached)) : (tableToCheck.ContainsKey(AttachState.Detaching) && tableToCheck.ContainsKey(AttachState.Detached)); - if (checkStatus) { foreach (var entry in tableToCheck) @@ -341,7 +391,9 @@ public bool CheckStateChangedOverride(bool checkAttached, bool checkEvent, Attac } else if (entry.Value != attachableNode) { - Log($"[{entry.Key}][Value] The value {entry.Value.name} is not the same as {attachableNode.name}!"); + var attachableName = attachableNode == null ? "null" : attachableNode.name; + var entryName = entry.Value == null ? "null" : entry.Value.name; + Log($"[{entry.Key}][Value] The value {entryName} is not the same as {attachableName}!"); checkStatus = false; break; } From 912b26ff97d58b2730f85943f1468a484555943b Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Mon, 14 Jul 2025 20:52:36 -0500 Subject: [PATCH 22/43] update Updating changelog entry. --- com.unity.netcode.gameobjects/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 36f925353f..6d7e8ae4e5 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -13,6 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `AttachableBehaviour` helper component to provide an alternate approach to parenting items without using the `NetworkObject` parenting. (#3518) - Added `AttachableNode` helper component that is used by `AttachableBehaviour` as the target node for parenting. (#3518) - Added `ComponentController` helper component that can be used to synchronize the enabling and disabling of components and can be used in conjunction with `AttachableBehaviour`. (#3518) +- Added `NetworkBehaviour.OnNetworkPreDespawn` that is invoked before running through the despawn sequence for the `NetworkObject` and all `NetworkBehaviour` children of the `NetworkObject` being despawned. (#3518) - Added methods `GetDefaultNetworkSettings` and `GetDefaultPipelineConfigurations` to `UnityTransport`. These can be used to retrieve the default settings and pipeline stages that are used by `UnityTransport`. This is useful when providing a custom driver constructor through `UnityTransport.s_DriverConstructor`, since it allows reusing or tuning the existing configuration instead of trying to recreate it. This means a transport with a custom driver can now easily benefit from most of the features of `UnityTransport`, like integration with the Network Simulator and Network Profiler from the multiplayer tools package. (#3501) - Added mappings between `ClientId` and `TransportId`. (#3516) - Added `NetworkPrefabInstanceHandlerWithData<T>`, a variant of `INetworkPrefabInstanceHandler` that provides access to custom instantiation data directly within the `Instantiate()` method. (#3430) From 9dd32d5084a3730e5882e532a09e17f73504aea6 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Tue, 15 Jul 2025 12:18:28 -0500 Subject: [PATCH 23/43] update Doing a second inspector UI pass to include tool tips and rename each element item to the component's standard inspector view naming where it is the GameObject's name followed by the class name that is separated by capitalization and contained within parenthesis. --- .../Components/Helpers/ComponentController.cs | 95 +++++++++++++++---- 1 file changed, 76 insertions(+), 19 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 8a7e1710a3..b3bda2261a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -2,9 +2,14 @@ using System.Collections; using System.Collections.Generic; using System.Reflection; +using System.Runtime.CompilerServices; +#if UNITY_EDITOR +using System.Text.RegularExpressions; +#endif using UnityEngine; using Object = UnityEngine.Object; + namespace Unity.Netcode.Components { /// <summary> @@ -13,33 +18,48 @@ namespace Unity.Netcode.Components [Serializable] public class ComponentControllerEntry { + [HideInInspector] + // Ignoring the naming convention in order to auto-assign element names +#pragma warning disable IDE1006 + public string name = "Component"; +#pragma warning restore IDE1006 + /// <summary> /// When true, this component's enabled state will be the inverse of /// the value passed into <see cref="ComponentController.SetEnabled(bool)"/>. /// </summary> - [Tooltip("When enabled, this component will inversely mirror the currently applied enable or disable state.")] + [Tooltip("When enabled, this component will inversely mirror the currently applied ComponentController's enabled state.")] public bool InvertEnabled; /// <summary> /// The amount of time to delay enabling this component when the <see cref="ComponentController"/> has just transitioned from a disabled to enabled state. /// </summary> + /// <remarks> + /// This can be useful under scenarios where you might want to prevent a component from being enabled too early prior to making any adjustments.<br /> + /// As an example, you might find that delaying the enabling of a <see cref="MeshRenderer"/> until at least the next frame will avoid any single frame + /// rendering anomalies until the <see cref="Rigidbody"/> has updated the <see cref="Transform"/>. + /// </remarks> [Range(0.0f, 2.0f)] + [Tooltip("The amount of time to delay when transitioning this component from disabled to enabled. When 0, the change is immediate.")] public float EnableDelay; /// <summary> /// The amount of time to delay disabling this component when the <see cref="ComponentController"/> has just transitioned from an enabled to disabled state. /// </summary> + /// <remarks> + /// This can be useful under scenarios where you might want to prevent a component from being disabled too early prior to making any adjustments.<br /> + /// </remarks> + [Tooltip("The amount of time to delay when transitioning this component from enabled to disabled. When 0, the change is immediate.")] [Range(0f, 2.0f)] public float DisableDelay; /// <summary> - /// The component to control. + /// The component that will have its enabled property synchronized. /// </summary> /// <remarks> - /// You can assign an entire <see cref="GameObject"/> to this property which will - /// add all components attached to the <see cref="GameObject"/>. The <see cref="StartEnabled"/> - /// and <see cref="InvertEnabled"/> properties will be applied to all components found on the <see cref="GameObject"/>. + /// You can assign an entire <see cref="GameObject"/> to this property which will add all components attached to the <see cref="GameObject"/> and its children. /// </remarks> + [Tooltip("The component that will have its enabled status synchonized. You can drop a GameObject onto this field and all valid components will be added to the list.")] public Object Component; internal PropertyInfo PropertyInfo; @@ -141,17 +161,16 @@ internal PendingStateUpdate(ComponentControllerEntry componentControllerEntry, b } /// <summary> - /// Handles enabling or disabling commonly used components, behaviours, RenderMeshes, etc.<br /> - /// Anything that derives from <see cref="Component"/> and has an enabled property can be added - /// to the list of objects.<br /> - /// <see cref="NetworkBehaviour"/> derived components are not allowed and will be automatically removed. + /// Handles enabling or disabling commonly used components like <see cref="MonoBehaviour"/>, <see cref="MeshRenderer"/>, <see cref="Collider"/>, etc.<br /> + /// Anything that derives from <see cref="Component"/> and has an enabled property can be added to the list of objects.<br /> + /// NOTE: <see cref="NetworkBehaviour"/> derived components are not allowed and will be automatically removed. /// </summary> /// <remarks> - /// This will synchronize the enabled or disabled state of the <see cref="Component"/>s with - /// connected and late joining clients.<br /> - /// This class provides the basic functionality to synchronizing components' enabled state.<br /> - /// It is encouraged to create custom derived versions of this class to provide any additional - /// functionality required for your project specific needs. + /// This will synchronize the enabled or disabled state of the <see cref="Component"/>s with connected and late joining clients.<br /> + /// - Use <see cref="EnabledState"/> to determine the current synchronized enabled state. <br /> + /// - Use <see cref="SetEnabled(bool)"/> to change the enabled state and have the change applied to all components this <see cref="ComponentController"/> is synchronizing.<br /> + /// + /// It is encouraged to create custom derived versions of this class to provide any additional functionality required for your project specific needs. /// </remarks> public class ComponentController : NetworkBehaviour { @@ -176,6 +195,21 @@ public class ComponentController : NetworkBehaviour private bool m_IsEnabled; #if UNITY_EDITOR + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsValidComponentType(Object component) + { + return !(component.GetType().IsSubclassOf(typeof(NetworkBehaviour)) || component.GetType() == typeof(NetworkObject) || component.GetType() == typeof(NetworkManager)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private string GetComponentNameFormatted(Object component) + { + // Split the class name up based on capitalization + var classNameDisplay = Regex.Replace(component.GetType().Name, "([A-Z])", " $1", RegexOptions.Compiled).Trim(); + return $"{component.name} ({classNameDisplay})"; + } + /// <inheritdoc/> /// <remarks> /// Checks for invalid <see cref="Object"/> entries. @@ -188,6 +222,7 @@ protected virtual void OnValidate() } var gameObjectsToScan = new List<ComponentControllerEntry>(); + // First pass is to verify all entries are valid and look for any GameObjects added as an entry to process next for (int i = Components.Count - 1; i >= 0; i--) { if (Components[i] == null) @@ -207,9 +242,9 @@ protected virtual void OnValidate() continue; } - if (componentType.IsSubclassOf(typeof(NetworkBehaviour))) + if (!IsValidComponentType(Components[i].Component)) { - Debug.LogWarning($"Removing {Components[i].Component.name} since {nameof(NetworkBehaviour)}s are not allowed to be controlled by this component."); + Debug.LogWarning($"Removing {GetComponentNameFormatted(Components[i].Component)} since {Components[i].Component.GetType().Name} is not an allowed component type."); Components.RemoveAt(i); continue; } @@ -222,14 +257,16 @@ protected virtual void OnValidate() } } + // Second pass is to process any GameObjects added. + // Scan the GameObject and all of its children and add all valid components to the list. foreach (var entry in gameObjectsToScan) { var asGameObject = entry.Component as GameObject; - var components = asGameObject.GetComponents<Component>(); + var components = asGameObject.GetComponentsInChildren<Component>(); foreach (var component in components) { - // Ignore any NetworkBehaviour derived components - if (component.GetType().IsSubclassOf(typeof(NetworkBehaviour))) + // Ignore any NetworkBehaviour derived, NetworkObject, or NetworkManager components + if (!IsValidComponentType(component)) { continue; } @@ -247,6 +284,16 @@ protected virtual void OnValidate() } } gameObjectsToScan.Clear(); + + // Final (third) pass is to name each list element item as the component is normally viewed in the inspector view. + for (int i = 0; i < Components.Count; i++) + { + if (!Components[i].Component) + { + continue; + } + Components[i].name = GetComponentNameFormatted(Components[i].Component); + } } #endif @@ -270,6 +317,9 @@ protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer) /// This checks to make sure that all <see cref="Component"/> entries are valid and will create a final /// <see cref="ComponentControllerEntry"/> list of valid entries. /// </summary> + /// <remarks> + /// If overriding this method, it is required that you invoke this base method. + /// </remarks> protected virtual void Awake() { ValidComponents.Clear(); @@ -310,6 +360,9 @@ protected virtual void Awake() } /// <inheritdoc/> + /// <remarks> + /// If overriding this method, it is required that you invoke this base method. + /// </remarks> public override void OnNetworkSpawn() { if (OnHasAuthority()) @@ -321,6 +374,7 @@ public override void OnNetworkSpawn() /// <inheritdoc/> /// <remarks> + /// If overriding this method, it is required that you invoke this base method. <br /> /// Assures all instances subscribe to the internal <see cref="NetworkVariable{T}"/> of type /// <see cref="bool"/> that synchronizes all instances when <see cref="Object"/>s are enabled /// or disabled. @@ -332,6 +386,9 @@ protected override void OnNetworkPostSpawn() } /// <inheritdoc/> + /// <remarks> + /// If overriding this method, it is required that you invoke this base method. + /// </remarks> public override void OnDestroy() { if (m_CoroutineObject.IsRunning) From 793cdb67aa82e2697e7de6c1603b2b14bc2c5447 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Tue, 15 Jul 2025 12:19:27 -0500 Subject: [PATCH 24/43] fix Fixing an exception that can occur when you have a network prefab opened for editing and then you delete the prefab asset before exiting the prefab edit mode. --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 560b280a02..754b83653d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -218,7 +218,7 @@ private static void CheckPrefabStage(PrefabStage prefabStage) s_PrefabAsset = AssetDatabase.LoadAssetAtPath<NetworkObject>(s_PrefabStage.assetPath); } - if (s_PrefabInstance.GlobalObjectIdHash != s_PrefabAsset.GlobalObjectIdHash) + if (s_PrefabAsset && s_PrefabInstance && s_PrefabInstance.GlobalObjectIdHash != s_PrefabAsset.GlobalObjectIdHash) { s_PrefabInstance.GlobalObjectIdHash = s_PrefabAsset.GlobalObjectIdHash; // For InContext mode, we don't want to record these modifications (the in-scene GlobalObjectIdHash is serialized with the scene). From fb73b8c4bbfd75e8e6101211bad6e58583e1469a Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Tue, 15 Jul 2025 12:29:49 -0500 Subject: [PATCH 25/43] style - PVP Fixing some PVP related issues. --- .../Components/Helpers/ComponentController.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index b3bda2261a..9c7cc43d5d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -18,15 +18,18 @@ namespace Unity.Netcode.Components [Serializable] public class ComponentControllerEntry { - [HideInInspector] + // Ignoring the naming convention in order to auto-assign element names #pragma warning disable IDE1006 - public string name = "Component"; + /// <summary> + /// Used for naming each element entry. + /// </summary> + [HideInInspector] + public string name; #pragma warning restore IDE1006 /// <summary> - /// When true, this component's enabled state will be the inverse of - /// the value passed into <see cref="ComponentController.SetEnabled(bool)"/>. + /// When true, this component's enabled state will be the inverse of the value passed into <see cref="ComponentController.SetEnabled(bool)"/>. /// </summary> [Tooltip("When enabled, this component will inversely mirror the currently applied ComponentController's enabled state.")] public bool InvertEnabled; @@ -167,9 +170,8 @@ internal PendingStateUpdate(ComponentControllerEntry componentControllerEntry, b /// </summary> /// <remarks> /// This will synchronize the enabled or disabled state of the <see cref="Component"/>s with connected and late joining clients.<br /> - /// - Use <see cref="EnabledState"/> to determine the current synchronized enabled state. <br /> + /// - Use <see cref="EnabledState"/> to determine the current synchronized enabled state.<br /> /// - Use <see cref="SetEnabled(bool)"/> to change the enabled state and have the change applied to all components this <see cref="ComponentController"/> is synchronizing.<br /> - /// /// It is encouraged to create custom derived versions of this class to provide any additional functionality required for your project specific needs. /// </remarks> public class ComponentController : NetworkBehaviour @@ -374,7 +376,7 @@ public override void OnNetworkSpawn() /// <inheritdoc/> /// <remarks> - /// If overriding this method, it is required that you invoke this base method. <br /> + /// If overriding this method, it is required that you invoke this base method.<br /> /// Assures all instances subscribe to the internal <see cref="NetworkVariable{T}"/> of type /// <see cref="bool"/> that synchronizes all instances when <see cref="Object"/>s are enabled /// or disabled. From 4b4c8ca618b0386947078a01ec9ed9d3f7d3ec13 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Fri, 18 Jul 2025 18:18:56 -0500 Subject: [PATCH 26/43] update Several improvements on the attach and detach processing. Added the ability to tie ComponentControllers to AttachableBehaviours in order to auto-notify when something is attaching and detaching. Several adjustments to fix the issue with ungraceful disconnects and re-synchronizing attachables. Added forced change based on flags applied, things like when an AttachableNode is despawning, changing ownership, or being destroyed then local instances, whether authority or not, will all force the attach or detach state. Added an internal virtual destroy method on NetworkBehaviour to allow for helper components to assure on destroy script is invoked. --- .../Components/Helpers/AttachableBehaviour.cs | 190 ++++++++-- .../Components/Helpers/AttachableNode.cs | 41 ++- .../Components/Helpers/ComponentController.cs | 324 +++++++++--------- .../Runtime/Core/NetworkBehaviour.cs | 9 + .../Tests/Runtime/ComponentControllerTests.cs | 6 +- 5 files changed, 386 insertions(+), 184 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 1962ca6222..9f509fb37b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; + #if UNITY_EDITOR using UnityEditor; #endif @@ -25,6 +27,49 @@ namespace Unity.Netcode.Components /// </remarks> public class AttachableBehaviour : NetworkBehaviour { + [Serializable] + internal class ComponentControllerEntry + { + // Ignoring the naming convention in order to auto-assign element names +#pragma warning disable IDE1006 + /// <summary> + /// Used for naming each element entry. + /// </summary> + [HideInInspector] + public string name; +#pragma warning restore IDE1006 + + +#if UNITY_EDITOR + + internal void OnValidate() + { + if (!HasInitialized) + { + AutoTrigger = TriggerTypes.OnAttach | TriggerTypes.OnDetach; + HasInitialized = true; + } + name = ComponentController != null ? ComponentController.GetComponentNameFormatted(ComponentController) : "Component Controller"; + } +#endif + + [Flags] + public enum TriggerTypes : byte + { + Nothing, + OnAttach, + OnDetach, + } + + public TriggerTypes AutoTrigger; + public bool EnableOnAttach = true; + public ComponentController ComponentController; + + [HideInInspector] + [SerializeField] + internal bool HasInitialized; + } + #if UNITY_EDITOR /// <inheritdoc/> /// <remarks> @@ -45,6 +90,15 @@ protected virtual void OnValidate() // Wait for the next editor update to create a nested child and add the AttachableBehaviour EditorApplication.update += CreatedNestedChild; } + + foreach (var componentController in ComponentControllers) + { + if (componentController == null) + { + continue; + } + componentController.OnValidate(); + } } private void CreatedNestedChild() @@ -57,6 +111,34 @@ private void CreatedNestedChild() DestroyImmediate(this); } #endif + /// <summary> + /// Flags to determine if the <see cref="AttachableBehaviour"/> will automatically detatch. + /// </summary> + [Flags] + public enum AutoDetatchTypes + { + None, + /// <summary> + /// Detatch on ownership change. + /// </summary> + OnOwnershipChange, + /// <summary> + /// Detatch on despawn. + /// </summary> + OnDespawn, + /// <summary> + /// Detatch on destroy. + /// </summary> + OnAttachNodeDestroy, + } + + /// <summary> + /// Determines if this <see cref="AttachableBehaviour"/> will automatically detatch on all instances if it has one of the <see cref="AutoDetatchTypes"/> flags. + /// </summary> + public AutoDetatchTypes AutoDetach = AutoDetatchTypes.OnDespawn | AutoDetatchTypes.OnOwnershipChange | AutoDetatchTypes.OnAttachNodeDestroy; + + [SerializeField] + internal List<ComponentControllerEntry> ComponentControllers; /// <summary> /// Invoked when the <see cref="AttachState"/> of this instance has changed. @@ -115,6 +197,7 @@ public enum AttachState /// If attached, attaching, or detaching this will be the <see cref="AttachableNode"/> this <see cref="AttachableBehaviour"/> instance is attached to. /// </summary> protected AttachableNode m_AttachableNode { get; private set; } + internal AttachableNode AttachableNode => m_AttachableNode; private NetworkBehaviourReference m_AttachedNodeReference = new NetworkBehaviourReference(null); private Vector3 m_OriginalLocalPosition; @@ -125,17 +208,20 @@ protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer) { // Example of how to synchronize late joining clients when using an RPC to update // a local property's state. - if (serializer.IsWriter) - { - serializer.SerializeValue(ref m_AttachedNodeReference); - } - else - { - serializer.SerializeValue(ref m_AttachedNodeReference); - } + serializer.SerializeValue(ref m_AttachedNodeReference); base.OnSynchronize(ref serializer); } + /// <summary> + /// Override this method in place of Awake. This method is invoked during Awake. + /// </summary> + /// <remarks> + /// The <see cref="AttachableBehaviour"/>'s Awake method is protected to assure it initializes itself at this point in time. + /// </remarks> + protected virtual void OnAwake() + { + } + /// <summary> /// If you create a custom <see cref="AttachableBehaviour"/> and override this method, you must invoke /// this base instance of <see cref="Awake"/>. @@ -147,6 +233,7 @@ protected virtual void Awake() m_OriginalLocalRotation = transform.localRotation; m_AttachState = AttachState.Detached; m_AttachableNode = null; + OnAwake(); } /// <inheritdoc/> @@ -162,9 +249,14 @@ protected override void OnNetworkSessionSynchronized() base.OnNetworkSessionSynchronized(); } - /// <inheritdoc/> - public override void OnNetworkDespawn() + internal void ForceDetatch() { + if (m_AttachState == AttachState.Detached || m_AttachState == AttachState.Detaching) + { + return; + } + + ForceComponentChange(false, true); InternalDetach(); if (NetworkManager && !NetworkManager.ShutdownInProgress) { @@ -172,18 +264,37 @@ public override void OnNetworkDespawn() UpdateAttachState(m_AttachState, m_AttachableNode); } m_AttachedNodeReference = new NetworkBehaviourReference(null); + } + + /// <inheritdoc/> + public override void OnNetworkPreDespawn() + { + if (AutoDetach.HasFlag(AutoDetatchTypes.OnDespawn)) + { + ForceDetatch(); + } base.OnNetworkDespawn(); } private void UpdateAttachedState() { var attachableNode = (AttachableNode)null; - var shouldParent = m_AttachedNodeReference.TryGet(out attachableNode, NetworkManager); - var preState = shouldParent ? AttachState.Attaching : AttachState.Detaching; - var preNode = shouldParent ? attachableNode : m_AttachableNode; - shouldParent = shouldParent && attachableNode != null; + var isAttaching = m_AttachedNodeReference.TryGet(out attachableNode, NetworkManager); + var preState = isAttaching ? AttachState.Attaching : AttachState.Detaching; + + // Exit early if we are already in the correct attached state and the incoming + // AttachableNode reference is the same as the local AttachableNode property. + if (attachableNode == m_AttachableNode && + ((isAttaching && m_AttachState == AttachState.Attached) || + (!isAttaching && m_AttachState == AttachState.Detached))) + { + return; + } + + var preNode = isAttaching ? attachableNode : m_AttachableNode; + isAttaching = isAttaching && attachableNode != null; - if (shouldParent && m_AttachableNode != null && m_AttachState == AttachState.Attached) + if (isAttaching && m_AttachableNode != null && m_AttachState == AttachState.Attached) { // If we are attached to some other AttachableNode, then detach from that before attaching to a new one. if (m_AttachableNode != attachableNode) @@ -201,7 +312,8 @@ private void UpdateAttachedState() // Change the state to attaching or detaching UpdateAttachState(preState, preNode); - if (shouldParent) + ForceComponentChange(isAttaching, false); + if (isAttaching) { InternalAttach(attachableNode); } @@ -215,7 +327,7 @@ private void UpdateAttachedState() // When detaching, we want to make our final action // the invocation of the AttachableNode's Detach method. - if (!shouldParent && m_AttachableNode) + if (!isAttaching && m_AttachableNode) { m_AttachableNode.Detach(this); m_AttachableNode = null; @@ -257,6 +369,29 @@ private void UpdateAttachState(AttachState attachState, AttachableNode attachabl } } + /// <inheritdoc/> + protected override void OnOwnershipChanged(ulong previous, ulong current) + { + if (AutoDetach.HasFlag(AutoDetatchTypes.OnOwnershipChange)) + { + ForceDetatch(); + } + base.OnOwnershipChanged(previous, current); + } + + internal void ForceComponentChange(bool isAttaching, bool forcedChange) + { + var triggerType = isAttaching ? ComponentControllerEntry.TriggerTypes.OnAttach : ComponentControllerEntry.TriggerTypes.OnDetach; + + foreach (var componentControllerEntry in ComponentControllers) + { + if (componentControllerEntry.AutoTrigger.HasFlag(triggerType)) + { + componentControllerEntry.ComponentController.ForceChangeEnabled(componentControllerEntry.EnableOnAttach ? isAttaching : !isAttaching, forcedChange); + } + } + } + /// <summary> /// Internal attach method that just handles changing state, parenting, and sending the <see cref="AttachableNode"/> a /// notification that an <see cref="AttachableBehaviour"/> has attached. @@ -349,23 +484,23 @@ public void Detach() return; } - if (m_AttachState != AttachState.Attached || m_AttachableNode == null) + if (m_AttachState == AttachState.Detached || m_AttachState == AttachState.Detaching || m_AttachableNode == null) { // Check for the unlikely scenario that an instance has mismatch between the state and assigned attachable node. - if (!m_AttachableNode && m_AttachState == AttachState.Attached) + if (!m_AttachableNode) { NetworkLog.LogError($"[{name}][Detach] Invalid state detected! {name}'s state is still {m_AttachState} but has no {nameof(AttachableNode)} assigned!"); } // Developer only notification for the most likely scenario where this method is invoked but the instance is not attached to anything. - if (NetworkManager && NetworkManager.LogLevel <= LogLevel.Developer) + if (!m_AttachableNode && NetworkManager && NetworkManager.LogLevel <= LogLevel.Developer) { NetworkLog.LogWarning($"[{name}][Detach] Cannot detach! {name} is not attached to anything!"); } // If we have the attachable node set and we are not in the middle of detaching, then log an error and note // this could potentially occur if inoked more than once for the same instance in the same frame. - if (m_AttachableNode && m_AttachState != AttachState.Detaching) + if (m_AttachableNode) { NetworkLog.LogError($"[{name}][Detach] Invalid state detected! {name} is still referencing {nameof(AttachableNode)} {m_AttachableNode.name}! Could {nameof(Detach)} be getting invoked more than once for the same instance?"); } @@ -407,5 +542,18 @@ private void UpdateAttachStateRpc(NetworkBehaviourReference attachedNodeReferenc { ChangeReference(attachedNodeReference); } + + /// <summary> + /// Notification that the <see cref="AttachableNode"/> is being destroyed + /// </summary> + internal void OnAttachNodeDestroy() + { + // If this instance should force a detatch on destroy + if (AutoDetach.HasFlag(AutoDetatchTypes.OnAttachNodeDestroy)) + { + // Force a detatch + ForceDetatch(); + } + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs index c62e16085b..bdd2fc47b6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs @@ -29,6 +29,35 @@ public class AttachableNode : NetworkBehaviour /// </summary> protected readonly List<AttachableBehaviour> m_AttachedBehaviours = new List<AttachableBehaviour>(); + /// <inheritdoc/> + protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) + { + m_AttachedBehaviours.Clear(); + base.OnNetworkPreSpawn(ref networkManager); + } + + /// <inheritdoc/> + /// <remarks> + /// When the ownership of an <see cref="AttachableNode"/> changes, it will find all currently attached <see cref="AttachableBehaviour"/> components + /// that are registered as being attached to this instance. + /// </remarks> + protected override void OnOwnershipChanged(ulong previous, ulong current) + { + if (current == NetworkManager.LocalClientId) + { + m_AttachedBehaviours.Clear(); + var attachables = NetworkObject.transform.GetComponentsInChildren<AttachableBehaviour>(); + foreach (var attachable in attachables) + { + if (attachable.AttachableNode == this) + { + m_AttachedBehaviours.Add(attachable); + } + } + } + base.OnOwnershipChanged(previous, current); + } + /// <inheritdoc/> /// <remarks> /// If the <see cref="NetworkObject"/> this <see cref="AttachableNode"/> belongs to is despawned, @@ -46,11 +75,15 @@ public override void OnNetworkPreDespawn() base.OnNetworkPreDespawn(); } - /// <inheritdoc/> - public override void OnNetworkDespawn() + internal override void InternalOnDestroy() { + // Notify any attached behaviours that this node is being destroyed. + for (int i = m_AttachedBehaviours.Count - 1; i >= 0; i--) + { + m_AttachedBehaviours[i]?.OnAttachNodeDestroy(); + } m_AttachedBehaviours.Clear(); - base.OnNetworkDespawn(); + base.InternalOnDestroy(); } /// <summary> @@ -69,7 +102,6 @@ internal void Attach(AttachableBehaviour attachableBehaviour) NetworkLog.LogError($"[{nameof(AttachableNode)}][{name}][Attach] {nameof(AttachableBehaviour)} {attachableBehaviour.name} is already attached!"); return; } - m_AttachedBehaviours.Add(attachableBehaviour); OnAttached(attachableBehaviour); } @@ -90,7 +122,6 @@ internal void Detach(AttachableBehaviour attachableBehaviour) NetworkLog.LogError($"[{nameof(AttachableNode)}][{name}][Detach] {nameof(AttachableBehaviour)} {attachableBehaviour.name} is not attached!"); return; } - m_AttachedBehaviours.Remove(attachableBehaviour); OnDetached(attachableBehaviour); } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 9c7cc43d5d..f135446184 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -13,169 +13,168 @@ namespace Unity.Netcode.Components { /// <summary> - /// This is a serializable contianer class for <see cref="ComponentController"/> entries. + /// Handles enabling or disabling commonly used components like <see cref="MonoBehaviour"/>, <see cref="MeshRenderer"/>, <see cref="Collider"/>, etc.<br /> + /// Anything that derives from <see cref="Component"/> and has an enabled property can be added to the list of objects.<br /> + /// NOTE: <see cref="NetworkBehaviour"/> derived components are not allowed and will be automatically removed. /// </summary> - [Serializable] - public class ComponentControllerEntry + /// <remarks> + /// This will synchronize the enabled or disabled state of the <see cref="Component"/>s with connected and late joining clients.<br /> + /// - Use <see cref="EnabledState"/> to determine the current synchronized enabled state.<br /> + /// - Use <see cref="SetEnabled(bool)"/> to change the enabled state and have the change applied to all components this <see cref="ComponentController"/> is synchronizing.<br /> + /// It is encouraged to create custom derived versions of this class to provide any additional functionality required for your project specific needs. + /// </remarks> + public class ComponentController : NetworkBehaviour { - - // Ignoring the naming convention in order to auto-assign element names -#pragma warning disable IDE1006 - /// <summary> - /// Used for naming each element entry. - /// </summary> - [HideInInspector] - public string name; -#pragma warning restore IDE1006 - - /// <summary> - /// When true, this component's enabled state will be the inverse of the value passed into <see cref="ComponentController.SetEnabled(bool)"/>. - /// </summary> - [Tooltip("When enabled, this component will inversely mirror the currently applied ComponentController's enabled state.")] - public bool InvertEnabled; - - /// <summary> - /// The amount of time to delay enabling this component when the <see cref="ComponentController"/> has just transitioned from a disabled to enabled state. - /// </summary> - /// <remarks> - /// This can be useful under scenarios where you might want to prevent a component from being enabled too early prior to making any adjustments.<br /> - /// As an example, you might find that delaying the enabling of a <see cref="MeshRenderer"/> until at least the next frame will avoid any single frame - /// rendering anomalies until the <see cref="Rigidbody"/> has updated the <see cref="Transform"/>. - /// </remarks> - [Range(0.0f, 2.0f)] - [Tooltip("The amount of time to delay when transitioning this component from disabled to enabled. When 0, the change is immediate.")] - public float EnableDelay; - - /// <summary> - /// The amount of time to delay disabling this component when the <see cref="ComponentController"/> has just transitioned from an enabled to disabled state. - /// </summary> - /// <remarks> - /// This can be useful under scenarios where you might want to prevent a component from being disabled too early prior to making any adjustments.<br /> - /// </remarks> - [Tooltip("The amount of time to delay when transitioning this component from enabled to disabled. When 0, the change is immediate.")] - [Range(0f, 2.0f)] - public float DisableDelay; - /// <summary> - /// The component that will have its enabled property synchronized. + /// This is a serializable contianer class for <see cref="ComponentController"/> entries. /// </summary> - /// <remarks> - /// You can assign an entire <see cref="GameObject"/> to this property which will add all components attached to the <see cref="GameObject"/> and its children. - /// </remarks> - [Tooltip("The component that will have its enabled status synchonized. You can drop a GameObject onto this field and all valid components will be added to the list.")] - public Object Component; - internal PropertyInfo PropertyInfo; - - internal bool GetRelativeEnabled(bool enabled) + [Serializable] + internal class ComponentEntry { - return InvertEnabled ? !enabled : enabled; - } - private List<PendingStateUpdate> m_PendingStateUpdates = new List<PendingStateUpdate>(); - - /// <summary> - /// Invoke prior to setting the state. - /// </summary> - internal bool QueueForDelay(bool enabled) - { - var relativeEnabled = GetRelativeEnabled(enabled); + // Ignoring the naming convention in order to auto-assign element names +#pragma warning disable IDE1006 + /// <summary> + /// Used for naming each element entry. + /// </summary> + [HideInInspector] + public string name; +#pragma warning restore IDE1006 - if (relativeEnabled ? EnableDelay > 0.0f : DisableDelay > 0.0f) + /// <summary> + /// When true, this component's enabled state will be the inverse of the value passed into <see cref="SetEnabled(bool)"/>. + /// </summary> + [Tooltip("When enabled, this component will inversely mirror the currently applied ComponentController's enabled state.")] + public bool InvertEnabled; + + /// <summary> + /// The amount of time to delay enabling this component when the <see cref="ComponentController"/> has just transitioned from a disabled to enabled state. + /// </summary> + /// <remarks> + /// This can be useful under scenarios where you might want to prevent a component from being enabled too early prior to making any adjustments.<br /> + /// As an example, you might find that delaying the enabling of a <see cref="MeshRenderer"/> until at least the next frame will avoid any single frame + /// rendering anomalies until the <see cref="Rigidbody"/> has updated the <see cref="Transform"/>. + /// </remarks> + [Range(0.0f, 2.0f)] + [Tooltip("The amount of time to delay when transitioning this component from disabled to enabled. When 0, the change is immediate.")] + public float EnableDelay; + + /// <summary> + /// The amount of time to delay disabling this component when the <see cref="ComponentController"/> has just transitioned from an enabled to disabled state. + /// </summary> + /// <remarks> + /// This can be useful under scenarios where you might want to prevent a component from being disabled too early prior to making any adjustments.<br /> + /// </remarks> + [Tooltip("The amount of time to delay when transitioning this component from enabled to disabled. When 0, the change is immediate.")] + [Range(0f, 2.0f)] + public float DisableDelay; + + /// <summary> + /// The component that will have its enabled property synchronized. + /// </summary> + /// <remarks> + /// You can assign an entire <see cref="GameObject"/> to this property which will add all components attached to the <see cref="GameObject"/> and its children. + /// </remarks> + [Tooltip("The component that will have its enabled status synchonized. You can drop a GameObject onto this field and all valid components will be added to the list.")] + public Object Component; + internal PropertyInfo PropertyInfo; + + internal bool GetRelativeEnabled(bool enabled) { - // Start with no relative time offset - var relativeTimeOffset = 0.0f; - // If we have pending state updates, then get that time of the last state update - // and use that as the time to add this next state update. - if (m_PendingStateUpdates.Count > 0) - { - relativeTimeOffset = m_PendingStateUpdates[m_PendingStateUpdates.Count - 1].DelayTimeDelta; - } - - // We process backwards, so insert new entries at the front - m_PendingStateUpdates.Insert(0, new PendingStateUpdate(this, enabled, relativeTimeOffset)); - return true; + return InvertEnabled ? !enabled : enabled; } - return false; - } - internal void SetValue(bool isEnabled) - { - // If invert enabled is true, then use the inverted value passed in. - // Otherwise, directly apply the value passed in. - PropertyInfo.SetValue(Component, GetRelativeEnabled(isEnabled)); - } + private List<PendingStateUpdate> m_PendingStateUpdates = new List<PendingStateUpdate>(); - internal bool HasPendingStateUpdates() - { - for (int i = m_PendingStateUpdates.Count - 1; i >= 0; i--) + /// <summary> + /// Invoke prior to setting the state. + /// </summary> + internal bool QueueForDelay(bool enabled) { - if (!m_PendingStateUpdates[i].CheckTimeDeltaDelay()) + var relativeEnabled = GetRelativeEnabled(enabled); + + if (relativeEnabled ? EnableDelay > 0.0f : DisableDelay > 0.0f) { - m_PendingStateUpdates.RemoveAt(i); - continue; + // Start with no relative time offset + var relativeTimeOffset = 0.0f; + // If we have pending state updates, then get that time of the last state update + // and use that as the time to add this next state update. + if (m_PendingStateUpdates.Count > 0) + { + relativeTimeOffset = m_PendingStateUpdates[m_PendingStateUpdates.Count - 1].DelayTimeDelta; + } + + // We process backwards, so insert new entries at the front + m_PendingStateUpdates.Insert(0, new PendingStateUpdate(this, enabled, relativeTimeOffset)); + return true; } + return false; } - return m_PendingStateUpdates.Count > 0; - } - - private class PendingStateUpdate - { - internal bool TimeDeltaDelayInProgress; - internal bool PendingState; - internal float DelayTimeDelta; - internal ComponentControllerEntry ComponentControllerEntry; - - internal bool CheckTimeDeltaDelay() + internal void SetValue(bool isEnabled) { - if (!TimeDeltaDelayInProgress) - { - return false; - } - - var isDeltaDelayInProgress = DelayTimeDelta > Time.realtimeSinceStartup; + // If invert enabled is true, then use the inverted value passed in. + // Otherwise, directly apply the value passed in. + PropertyInfo.SetValue(Component, GetRelativeEnabled(isEnabled)); + } - if (!isDeltaDelayInProgress) + internal bool HasPendingStateUpdates() + { + for (int i = m_PendingStateUpdates.Count - 1; i >= 0; i--) { - ComponentControllerEntry.SetValue(PendingState); + if (!m_PendingStateUpdates[i].CheckTimeDeltaDelay()) + { + m_PendingStateUpdates.RemoveAt(i); + continue; + } } - TimeDeltaDelayInProgress = isDeltaDelayInProgress; - return TimeDeltaDelayInProgress; + return m_PendingStateUpdates.Count > 0; } - internal PendingStateUpdate(ComponentControllerEntry componentControllerEntry, bool isEnabled, float relativeTimeOffset) + private class PendingStateUpdate { - ComponentControllerEntry = componentControllerEntry; - // If there is a pending state, then add the delay to the end of the last pending state's. - var referenceTime = relativeTimeOffset > 0.0f ? relativeTimeOffset : Time.realtimeSinceStartup; + internal bool TimeDeltaDelayInProgress; + internal bool PendingState; + internal float DelayTimeDelta; + + internal ComponentEntry ComponentEntry; - if (ComponentControllerEntry.GetRelativeEnabled(isEnabled)) + internal bool CheckTimeDeltaDelay() { - DelayTimeDelta = referenceTime + ComponentControllerEntry.EnableDelay; + if (!TimeDeltaDelayInProgress) + { + return false; + } + + var isDeltaDelayInProgress = DelayTimeDelta > Time.realtimeSinceStartup; + + if (!isDeltaDelayInProgress) + { + ComponentEntry.SetValue(PendingState); + } + TimeDeltaDelayInProgress = isDeltaDelayInProgress; + return TimeDeltaDelayInProgress; } - else + + internal PendingStateUpdate(ComponentEntry componentControllerEntry, bool isEnabled, float relativeTimeOffset) { - DelayTimeDelta = referenceTime + ComponentControllerEntry.DisableDelay; + ComponentEntry = componentControllerEntry; + // If there is a pending state, then add the delay to the end of the last pending state's. + var referenceTime = relativeTimeOffset > 0.0f ? relativeTimeOffset : Time.realtimeSinceStartup; + + if (ComponentEntry.GetRelativeEnabled(isEnabled)) + { + DelayTimeDelta = referenceTime + ComponentEntry.EnableDelay; + } + else + { + DelayTimeDelta = referenceTime + ComponentEntry.DisableDelay; + } + TimeDeltaDelayInProgress = true; + PendingState = isEnabled; } - TimeDeltaDelayInProgress = true; - PendingState = isEnabled; } } - } - - /// <summary> - /// Handles enabling or disabling commonly used components like <see cref="MonoBehaviour"/>, <see cref="MeshRenderer"/>, <see cref="Collider"/>, etc.<br /> - /// Anything that derives from <see cref="Component"/> and has an enabled property can be added to the list of objects.<br /> - /// NOTE: <see cref="NetworkBehaviour"/> derived components are not allowed and will be automatically removed. - /// </summary> - /// <remarks> - /// This will synchronize the enabled or disabled state of the <see cref="Component"/>s with connected and late joining clients.<br /> - /// - Use <see cref="EnabledState"/> to determine the current synchronized enabled state.<br /> - /// - Use <see cref="SetEnabled(bool)"/> to change the enabled state and have the change applied to all components this <see cref="ComponentController"/> is synchronizing.<br /> - /// It is encouraged to create custom derived versions of this class to provide any additional functionality required for your project specific needs. - /// </remarks> - public class ComponentController : NetworkBehaviour - { /// <summary> /// Determines whether the selected <see cref="Components"/>s will start enabled or disabled when spawned. /// </summary> @@ -186,14 +185,15 @@ public class ComponentController : NetworkBehaviour /// The list of <see cref="Components"/>s to be enabled and disabled. /// </summary> [Tooltip("The list of components to control. You can drag and drop an entire GameObject on this to include all components.")] - public List<ComponentControllerEntry> Components; + [SerializeField] + internal List<ComponentEntry> Components; /// <summary> /// Returns the current enabled state of the <see cref="ComponentController"/>. /// </summary> public bool EnabledState => m_IsEnabled; - internal List<ComponentControllerEntry> ValidComponents = new List<ComponentControllerEntry>(); + internal List<ComponentEntry> ValidComponents = new List<ComponentEntry>(); private bool m_IsEnabled; #if UNITY_EDITOR @@ -205,7 +205,7 @@ private bool IsValidComponentType(Object component) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private string GetComponentNameFormatted(Object component) + internal static string GetComponentNameFormatted(Object component) { // Split the class name up based on capitalization var classNameDisplay = Regex.Replace(component.GetType().Name, "([A-Z])", " $1", RegexOptions.Compiled).Trim(); @@ -223,7 +223,7 @@ protected virtual void OnValidate() return; } - var gameObjectsToScan = new List<ComponentControllerEntry>(); + var gameObjectsToScan = new List<ComponentEntry>(); // First pass is to verify all entries are valid and look for any GameObjects added as an entry to process next for (int i = Components.Count - 1; i >= 0; i--) { @@ -236,10 +236,13 @@ protected virtual void OnValidate() { continue; } - var componentType = Components[i].Component.GetType(); - if (componentType == typeof(GameObject)) + var objectType = Components[i].Component.GetType(); + if (objectType == typeof(GameObject)) { - gameObjectsToScan.Add(Components[i]); + if (!gameObjectsToScan.Contains(Components[i])) + { + gameObjectsToScan.Add(Components[i]); + } Components.RemoveAt(i); continue; } @@ -276,7 +279,7 @@ protected virtual void OnValidate() var propertyInfo = component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) { - var componentEntry = new ComponentControllerEntry() + var componentEntry = new ComponentEntry() { Component = component, PropertyInfo = propertyInfo, @@ -304,25 +307,21 @@ protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer) { // Example of how to synchronize late joining clients when using an RPC to update // a local property's state. - if (serializer.IsWriter) - { - serializer.SerializeValue(ref m_IsEnabled); - } - else - { - serializer.SerializeValue(ref m_IsEnabled); - } + serializer.SerializeValue(ref m_IsEnabled); base.OnSynchronize(ref serializer); } /// <summary> - /// This checks to make sure that all <see cref="Component"/> entries are valid and will create a final - /// <see cref="ComponentControllerEntry"/> list of valid entries. + /// Override this method in place of Awake. This method is invoked during Awake. /// </summary> /// <remarks> - /// If overriding this method, it is required that you invoke this base method. + /// The <see cref="ComponentController"/>'s Awake method is protected to assure it is invoked in the correct order. /// </remarks> - protected virtual void Awake() + protected virtual void OnAwake() + { + } + + private void Awake() { ValidComponents.Clear(); @@ -359,6 +358,15 @@ protected virtual void Awake() // Apply the initial state of all components this instance is controlling. InitializeComponents(); + + try + { + OnAwake(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } /// <inheritdoc/> @@ -419,11 +427,11 @@ private void InitializeComponents() /// Applies states changes to all components being controlled by this instance. /// </summary> /// <param name="enabled">the state update to apply</param> - private void ApplyEnabled() + private void ApplyEnabled(bool ignoreDelays = false) { foreach (var entry in ValidComponents) { - if (entry.QueueForDelay(m_IsEnabled)) + if (!ignoreDelays && entry.QueueForDelay(m_IsEnabled)) { if (!m_CoroutineObject.IsRunning) { @@ -506,6 +514,12 @@ private void ChangeEnabled(bool isEnabled) } } + internal void ForceChangeEnabled(bool isEnabled, bool ignoreDelays = false) + { + m_IsEnabled = isEnabled; + ApplyEnabled(ignoreDelays); + } + /// <summary> /// Override this method to change how the instance determines the authority.<br /> /// The default is to use the <see cref="NetworkObject.HasAuthority"/> method. diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 73696fa0a4..307068214c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -1525,6 +1525,14 @@ internal bool Synchronize<T>(ref BufferSerializer<T> serializer, ulong targetCli } } + /// <summary> + /// Use to assure a helper component invokes script during destroy in the + /// event that a derived class does not invoke base.OnDestroy. + /// </summary> + internal virtual void InternalOnDestroy() + { + + } /// <summary> /// Invoked when the <see cref="GameObject"/> the <see cref="NetworkBehaviour"/> is attached to is destroyed. @@ -1532,6 +1540,7 @@ internal bool Synchronize<T>(ref BufferSerializer<T> serializer, ulong targetCli /// </summary> public virtual void OnDestroy() { + InternalOnDestroy(); if (NetworkObject != null && NetworkObject.IsSpawned && IsSpawned) { // If the associated NetworkObject is still spawned then this diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs index 7d2d6e86cb..8262614cde 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/ComponentControllerTests.cs @@ -40,13 +40,13 @@ protected override void OnServerAndClientsCreated() var meshRenderer = sourceChild.AddComponent<MeshRenderer>(); var light = sourceChild.AddComponent<Light>(); var controller = m_TestPrefab.AddComponent<ComponentController>(); - controller.Components = new List<ComponentControllerEntry> + controller.Components = new List<ComponentController.ComponentEntry> { - new ComponentControllerEntry() + new ComponentController.ComponentEntry() { Component = meshRenderer, }, - new ComponentControllerEntry() + new ComponentController.ComponentEntry() { InvertEnabled = true, Component = light, From 42ac2a8362b495f63937703944dabf90dd015ea7 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Fri, 18 Jul 2025 21:56:41 -0500 Subject: [PATCH 27/43] sty;e - PvP Adding documentation to an undocumented enum value. --- .../Runtime/Components/Helpers/AttachableBehaviour.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 9f509fb37b..b8abd7980f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -117,6 +117,9 @@ private void CreatedNestedChild() [Flags] public enum AutoDetatchTypes { + /// <summary> + /// Disables auto detach. + /// </summary> None, /// <summary> /// Detatch on ownership change. From efcc40449669d188987eccc74061e59fadff7e8d Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Sun, 20 Jul 2025 21:18:42 -0500 Subject: [PATCH 28/43] update and test additions Updating the core components based on some bugs discovered during testing. Updated the AttachableBehaviourTests to validate the different types of auto-detach flag combinations. Fixed some spelling issues with "detatch" (not sure why I got into that habit)...corrected to "detach". --- .../Components/Helpers/AttachableBehaviour.cs | 52 +- .../Components/Helpers/AttachableNode.cs | 20 +- .../Tests/Runtime/AttachableBehaviourTests.cs | 474 ++++++++++++++++-- 3 files changed, 477 insertions(+), 69 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index b8abd7980f..8922b4ad91 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -90,7 +90,10 @@ protected virtual void OnValidate() // Wait for the next editor update to create a nested child and add the AttachableBehaviour EditorApplication.update += CreatedNestedChild; } - + if (ComponentControllers == null) + { + return; + } foreach (var componentController in ComponentControllers) { if (componentController == null) @@ -112,33 +115,33 @@ private void CreatedNestedChild() } #endif /// <summary> - /// Flags to determine if the <see cref="AttachableBehaviour"/> will automatically detatch. + /// Flags to determine if the <see cref="AttachableBehaviour"/> will automatically detach. /// </summary> [Flags] - public enum AutoDetatchTypes + public enum AutoDetachTypes { /// <summary> /// Disables auto detach. /// </summary> None, /// <summary> - /// Detatch on ownership change. + /// Detach on ownership change. /// </summary> OnOwnershipChange, /// <summary> - /// Detatch on despawn. + /// Detach on despawn. /// </summary> OnDespawn, /// <summary> - /// Detatch on destroy. + /// Detach on destroy. /// </summary> OnAttachNodeDestroy, } /// <summary> - /// Determines if this <see cref="AttachableBehaviour"/> will automatically detatch on all instances if it has one of the <see cref="AutoDetatchTypes"/> flags. + /// Determines if this <see cref="AttachableBehaviour"/> will automatically detach on all instances if it has one of the <see cref="AutoDetachTypes"/> flags. /// </summary> - public AutoDetatchTypes AutoDetach = AutoDetatchTypes.OnDespawn | AutoDetatchTypes.OnOwnershipChange | AutoDetatchTypes.OnAttachNodeDestroy; + public AutoDetachTypes AutoDetach = AutoDetachTypes.OnDespawn | AutoDetachTypes.OnOwnershipChange | AutoDetachTypes.OnAttachNodeDestroy; [SerializeField] internal List<ComponentControllerEntry> ComponentControllers; @@ -252,7 +255,7 @@ protected override void OnNetworkSessionSynchronized() base.OnNetworkSessionSynchronized(); } - internal void ForceDetatch() + internal void ForceDetach() { if (m_AttachState == AttachState.Detached || m_AttachState == AttachState.Detaching) { @@ -260,21 +263,28 @@ internal void ForceDetatch() } ForceComponentChange(false, true); + InternalDetach(); - if (NetworkManager && !NetworkManager.ShutdownInProgress) + // Notify of the changed attached state + UpdateAttachState(m_AttachState, m_AttachableNode); + + m_AttachedNodeReference = new NetworkBehaviourReference(null); + + // When detaching, we want to make our final action + // the invocation of the AttachableNode's Detach method. + if (m_AttachableNode) { - // Notify of the changed attached state - UpdateAttachState(m_AttachState, m_AttachableNode); + m_AttachableNode.Detach(this); + m_AttachableNode = null; } - m_AttachedNodeReference = new NetworkBehaviourReference(null); } /// <inheritdoc/> public override void OnNetworkPreDespawn() { - if (AutoDetach.HasFlag(AutoDetatchTypes.OnDespawn)) + if (AutoDetach.HasFlag(AutoDetachTypes.OnDespawn)) { - ForceDetatch(); + ForceDetach(); } base.OnNetworkDespawn(); } @@ -375,9 +385,9 @@ private void UpdateAttachState(AttachState attachState, AttachableNode attachabl /// <inheritdoc/> protected override void OnOwnershipChanged(ulong previous, ulong current) { - if (AutoDetach.HasFlag(AutoDetatchTypes.OnOwnershipChange)) + if (AutoDetach.HasFlag(AutoDetachTypes.OnOwnershipChange)) { - ForceDetatch(); + ForceDetach(); } base.OnOwnershipChanged(previous, current); } @@ -551,11 +561,11 @@ private void UpdateAttachStateRpc(NetworkBehaviourReference attachedNodeReferenc /// </summary> internal void OnAttachNodeDestroy() { - // If this instance should force a detatch on destroy - if (AutoDetach.HasFlag(AutoDetatchTypes.OnAttachNodeDestroy)) + // If this instance should force a detach on destroy + if (AutoDetach.HasFlag(AutoDetachTypes.OnAttachNodeDestroy)) { - // Force a detatch - ForceDetatch(); + // Force a detach + ForceDetach(); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs index bdd2fc47b6..d768b17fcf 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs @@ -43,9 +43,11 @@ protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) /// </remarks> protected override void OnOwnershipChanged(ulong previous, ulong current) { + // Clear any known behaviours on all instances (really only the previous owner should know about AttachedBehaviours + m_AttachedBehaviours.Clear(); if (current == NetworkManager.LocalClientId) { - m_AttachedBehaviours.Clear(); + // Rebuild the list of AttachableBehaviours for the new owner var attachables = NetworkObject.transform.GetComponentsInChildren<AttachableBehaviour>(); foreach (var attachable in attachables) { @@ -69,7 +71,21 @@ public override void OnNetworkPreDespawn() { for (int i = m_AttachedBehaviours.Count - 1; i >= 0; i--) { - m_AttachedBehaviours[i]?.Detach(); + if (!m_AttachedBehaviours[i]) + { + continue; + } + // If we don't have authority but should detach on despawn, + // then proceed to detach. + if (!m_AttachedBehaviours[i].HasAuthority) + { + m_AttachedBehaviours[i].ForceDetach(); + } + else + { + // Detach the normal way with authority + m_AttachedBehaviours[i].Detach(); + } } } base.OnNetworkPreDespawn(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs index af0568ab7f..1d3d3778cf 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text; using NUnit.Framework; using Unity.Netcode.Components; @@ -18,9 +19,9 @@ internal class AttachableBehaviourTests : NetcodeIntegrationTest public AttachableBehaviourTests(HostOrServer hostOrServer) : base(hostOrServer) { } - private GameObject m_SourcePrefab; - private GameObject m_TargetPrefabA; - private GameObject m_TargetPrefabB; + private GameObject m_AttachablePrefab; + private GameObject m_TargetNodePrefabA; + private GameObject m_TargetNodePrefabB; /// <summary> /// All of the below instances belong to the authority @@ -29,6 +30,7 @@ public AttachableBehaviourTests(HostOrServer hostOrServer) : base(hostOrServer) private NetworkObject m_SourceInstance; private NetworkObject m_TargetInstance; private NetworkObject m_TargetInstanceB; + private TestAttachable m_PrefabTestAttachable; private TestAttachable m_AttachableBehaviourInstance; private TestNode m_AttachableNodeInstance; private TestNode m_AttachableNodeInstanceB; @@ -47,21 +49,25 @@ protected override void OnServerAndClientsCreated() { // The source prefab contains the nested NetworkBehaviour that // will be parented under the target prefab. - m_SourcePrefab = CreateNetworkObjectPrefab("Source"); - m_SourcePrefab.GetComponent<NetworkObject>().DontDestroyWithOwner = true; + m_AttachablePrefab = CreateNetworkObjectPrefab("Source"); + var attachableNetworkObject = m_AttachablePrefab.GetComponent<NetworkObject>(); + attachableNetworkObject.DontDestroyWithOwner = true; + attachableNetworkObject.SetOwnershipStatus(NetworkObject.OwnershipStatus.Transferable); + // The target prefab that the source prefab will attach // will be parented under the target prefab. - m_TargetPrefabA = CreateNetworkObjectPrefab("TargetA"); - m_TargetPrefabB = CreateNetworkObjectPrefab("TargetB"); + m_TargetNodePrefabA = CreateNetworkObjectPrefab("TargetA"); + m_TargetNodePrefabB = CreateNetworkObjectPrefab("TargetB"); var sourceChild = new GameObject("SourceChild"); var targetChildA = new GameObject("TargetChildA"); var targetChildB = new GameObject("TargetChildB"); - sourceChild.transform.parent = m_SourcePrefab.transform; - targetChildA.transform.parent = m_TargetPrefabA.transform; - targetChildB.transform.parent = m_TargetPrefabB.transform; + sourceChild.transform.parent = m_AttachablePrefab.transform; + targetChildA.transform.parent = m_TargetNodePrefabA.transform; + targetChildB.transform.parent = m_TargetNodePrefabB.transform; - sourceChild.AddComponent<TestAttachable>(); - targetChildA.AddComponent<TestNode>(); + m_PrefabTestAttachable = sourceChild.AddComponent<TestAttachable>(); + var targetChildATestNode = targetChildA.AddComponent<TestNode>(); + targetChildATestNode.DetachOnDespawn = true; targetChildB.AddComponent<TestNode>(); base.OnServerAndClientsCreated(); } @@ -97,7 +103,8 @@ private bool ResetAllStates() m_ErrorLog.Clear(); var target = GetTargetInstance(); - + TestAttachable.LastKnownEventStates.Clear(); + TestAttachable.LastKnownOverrideStates.Clear(); // The attachable can move between the two spawned instances. var currentAttachableRoot = m_AttachableBehaviourInstance.State == AttachableBehaviour.AttachState.Attached ? target : m_SourceInstance; @@ -196,11 +203,11 @@ private bool AllInstancesAttachedStateChanged(bool checkAttached, bool ignoreIfD continue; } - if (!attachable.CheckStateChangedOverride(checkAttached, false, node)) + if (!attachable.CheckForState(checkAttached, false)) { m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Did not have its override invoked!"); } - if (!attachable.CheckStateChangedOverride(checkAttached, true, node)) + if (!attachable.CheckForState(checkAttached, true)) { m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Did not have its event invoked!"); } @@ -232,13 +239,15 @@ private bool AllInstancesDespawned() return true; } + [UnityTest] public IEnumerator AttachAndDetachTests() { var authority = GetAuthorityNetworkManager(); - m_SourceInstance = SpawnObject(m_SourcePrefab, authority).GetComponent<NetworkObject>(); - m_TargetInstance = SpawnObject(m_TargetPrefabA, authority).GetComponent<NetworkObject>(); - m_TargetInstanceB = SpawnObject(m_TargetPrefabB, authority).GetComponent<NetworkObject>(); + + m_SourceInstance = SpawnObject(m_AttachablePrefab, authority).GetComponent<NetworkObject>(); + m_TargetInstance = SpawnObject(m_TargetNodePrefabA, authority).GetComponent<NetworkObject>(); + m_TargetInstanceB = SpawnObject(m_TargetNodePrefabB, authority).GetComponent<NetworkObject>(); m_TargetInstanceId = m_TargetInstance.NetworkObjectId; yield return WaitForConditionOrTimeOut(AllClientsSpawnedInstances); AssertOnTimeout($"Timed out waiting for all clients to spawn {m_SourceInstance.name} and {m_TargetInstance.name}!\n {m_ErrorLog}"); @@ -271,6 +280,7 @@ public IEnumerator AttachAndDetachTests() // Reset all states and prepare for 2nd attach test Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + Debug.Log("Attaching Node-B"); // Now, while attached, attach to another attachable node which should detach from the current and attach to the new. m_AttachableBehaviourInstance.Attach(m_AttachableNodeInstanceB); @@ -315,14 +325,352 @@ public IEnumerator AttachAndDetachTests() AssertOnTimeout($"[Despawn Detach Phase] Timed out waiting for all clients to despawn {targetInstanceName}!"); } + + private bool OwnershipChangedOnAllInstances() + { + foreach (var networkManager in m_NetworkManagers) + { + if (networkManager.SpawnManager != null && !networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_AttachableBehaviourInstance.NetworkObjectId)) + { + return false; + } + if (m_NonAuthority.LocalClientId != networkManager.SpawnManager.SpawnedObjects[m_AttachableBehaviourInstance.NetworkObjectId].OwnerClientId) + { + return false; + } + } + return true; + } + + + private bool ObjectDespawnedOnAllInstances(ulong networkObjectId) + { + foreach (var networkManager in m_NetworkManagers) + { + if (networkManager.SpawnManager != null && networkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) + { + return false; + } + } + return true; + } + + private bool AllInstancesDetached() + { + m_ErrorLog.Clear(); + // The attachable can move between the two spawned instances so we have to use the appropriate one depending upon the authority's current state. + var attachable = (TestAttachable)null; + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_AttachableBehaviourInstance.NetworkObjectId)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {m_AttachableBehaviourInstance.name}!"); + continue; + } + else + { + attachable = networkManager.SpawnManager.SpawnedObjects[m_AttachableBehaviourInstance.NetworkObjectId].GetComponentInChildren<TestAttachable>(); + } + + if (!attachable) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][Attachable] Attachable was not found!"); + continue; + } + + if (!attachable.CheckForState(false, false)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Did not have its override invoked!"); + } + if (!attachable.CheckForState(false, true)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Did not have its event invoked!"); + } + + if (attachable.AttachableNode != null) + { + var nodeHasAttachments = attachable.AttachableNode.HasAttachments ? $" {attachable.AttachableNode.name} still has attachments!" : ""; + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Still refers to {attachable.AttachableNode.name}!{nodeHasAttachments}"); + } + } + return m_ErrorLog.Length == 0; + } + + private bool AllInstancesDetachedWhenAttachableDespawned() + { + m_ErrorLog.Clear(); + foreach (var networkManager in m_NetworkManagers) + { + var localClientId = networkManager.LocalClientId; + if (!TestAttachable.LastKnownOverrideStates.ContainsKey(localClientId)) + { + m_ErrorLog.AppendLine($"[Client-{localClientId}] Has no override states!"); + continue; + } + if (!TestAttachable.LastKnownEventStates.ContainsKey(localClientId)) + { + m_ErrorLog.AppendLine($"[Client-{localClientId}] Has no event states!"); + continue; + } + if (!TestAttachable.LastKnownOverrideStates[localClientId].ContainsKey(AttachableBehaviour.AttachState.Detached)) + { + m_ErrorLog.AppendLine($"[Client-{localClientId}] Does not contain the {AttachableBehaviour.AttachState.Detached} override state!"); + continue; + } + if (!TestAttachable.LastKnownEventStates[localClientId].ContainsKey(AttachableBehaviour.AttachState.Detached)) + { + m_ErrorLog.AppendLine($"[Client-{localClientId}] Does not contain the {AttachableBehaviour.AttachState.Detached} event state!"); + continue; + } + + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_TargetInstanceId)) + { + m_ErrorLog.AppendLine($"[Client-{localClientId}] Does not have a spawned target node ({m_TargetInstanceId})!"); + continue; + } + var targetNode = networkManager.SpawnManager.SpawnedObjects[m_TargetInstanceId].GetComponentInChildren<TestNode>(); + if (targetNode == null) + { + m_ErrorLog.AppendLine($"[Client-{localClientId}] Does not have a target node component (null)!"); + continue; + } + + if (targetNode.HasAttachments) + { + m_ErrorLog.AppendLine($"[Client-{localClientId}] {targetNode.name} still has attachments!"); + } + } + return m_ErrorLog.Length == 0; + } + + private NetworkManager m_NonAuthority; + private NetworkManager m_Authority; + + public enum DetachCombinations + { + AllFlags, + OwnerDespawn, + OwnerDestroy, + DespawnDestroy, + Owner, + Despawn, + Destroy + } + + private AttachableBehaviour.AutoDetachTypes GetDetachType(DetachCombinations detachCombination) + { + var autoDetachTypeFlags = AttachableBehaviour.AutoDetachTypes.OnDespawn | AttachableBehaviour.AutoDetachTypes.OnOwnershipChange | AttachableBehaviour.AutoDetachTypes.OnAttachNodeDestroy; + switch (detachCombination) + { + case DetachCombinations.AllFlags: + { + break; + } + case DetachCombinations.OwnerDespawn: + { + autoDetachTypeFlags = AttachableBehaviour.AutoDetachTypes.OnDespawn | AttachableBehaviour.AutoDetachTypes.OnOwnershipChange; + break; + } + case DetachCombinations.OwnerDestroy: + { + autoDetachTypeFlags = AttachableBehaviour.AutoDetachTypes.OnOwnershipChange | AttachableBehaviour.AutoDetachTypes.OnAttachNodeDestroy; + break; + } + case DetachCombinations.DespawnDestroy: + { + autoDetachTypeFlags = AttachableBehaviour.AutoDetachTypes.OnDespawn | AttachableBehaviour.AutoDetachTypes.OnAttachNodeDestroy; + break; + } + case DetachCombinations.Owner: + { + autoDetachTypeFlags = AttachableBehaviour.AutoDetachTypes.OnOwnershipChange; + break; + } + case DetachCombinations.Despawn: + { + autoDetachTypeFlags = AttachableBehaviour.AutoDetachTypes.OnDespawn; + break; + } + case DetachCombinations.Destroy: + { + autoDetachTypeFlags = AttachableBehaviour.AutoDetachTypes.OnAttachNodeDestroy; + break; + } + } + + return autoDetachTypeFlags; + } + + [UnityTest] + public IEnumerator AutoDetachTests([Values] DetachCombinations detachCombination) + { + var autoDetachTypeFlags = GetDetachType(detachCombination); + m_UseTargetB = false; + m_Authority = GetAuthorityNetworkManager(); + m_NonAuthority = GetNonAuthorityNetworkManager(); + + m_PrefabTestAttachable.AutoDetach = autoDetachTypeFlags; + + m_SourceInstance = SpawnObject(m_AttachablePrefab, m_Authority).GetComponent<NetworkObject>(); + m_TargetInstance = SpawnObject(m_TargetNodePrefabA, m_Authority).GetComponent<NetworkObject>(); + m_TargetInstanceB = SpawnObject(m_TargetNodePrefabB, m_Authority).GetComponent<NetworkObject>(); + m_TargetInstanceId = m_TargetInstance.NetworkObjectId; + yield return WaitForConditionOrTimeOut(AllClientsSpawnedInstances); + AssertOnTimeout($"Timed out waiting for all clients to spawn {m_SourceInstance.name} and {m_TargetInstance.name}!\n {m_ErrorLog}"); + + m_AttachableBehaviourInstance = m_SourceInstance.GetComponentInChildren<TestAttachable>(); + Assert.NotNull(m_AttachableBehaviourInstance, $"{m_SourceInstance.name} does not have a nested child {nameof(AttachableBehaviour)}!"); + + m_AttachableNodeInstance = m_TargetInstance.GetComponentInChildren<TestNode>(); + Assert.NotNull(m_AttachableNodeInstance, $"{m_TargetInstance.name} does not have a nested child {nameof(AttachableNode)}!"); + + m_AttachableNodeInstanceB = m_TargetInstanceB.GetComponentInChildren<TestNode>(); + Assert.NotNull(m_AttachableNodeInstanceB, $"{m_TargetInstanceB.name} does not have a nested child {nameof(AttachableNode)}!"); + + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + m_AttachableBehaviourInstance.Attach(m_AttachableNodeInstance); + + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(true)); + AssertOnTimeout($"Timed out waiting for all clients to attach {m_AttachableBehaviourInstance.name} to {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + if (autoDetachTypeFlags.HasFlag(AttachableBehaviour.AutoDetachTypes.OnOwnershipChange)) + { + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + if (m_DistributedAuthority) + { + m_Authority.SpawnManager.SpawnedObjects[m_SourceInstance.NetworkObjectId].ChangeOwnership(m_NonAuthority.LocalClientId); + } + else + { + m_SourceInstance.ChangeOwnership(m_NonAuthority.LocalClientId); + } + + yield return WaitForConditionOrTimeOut(OwnershipChangedOnAllInstances); + AssertOnTimeout($"[OnOwnershipChange] Timed out waiting for all clients to change the ownership from {m_Authority.name} to {m_NonAuthority.name}!"); + + yield return WaitForConditionOrTimeOut(AllInstancesDetached); + AssertOnTimeout($"[OnOwnershipChange] Timed out waiting for all clients to detach {m_AttachableBehaviourInstance.name} from {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + if (m_DistributedAuthority) + { + var nonAuthority = m_Authority; + m_Authority = m_NonAuthority; + m_NonAuthority = nonAuthority; + + m_SourceInstance = m_Authority.SpawnManager.SpawnedObjects[m_SourceInstance.NetworkObjectId]; + m_AttachableBehaviourInstance = m_SourceInstance.GetComponentInChildren<TestAttachable>(); + m_AttachableNodeInstance = m_Authority.SpawnManager.SpawnedObjects[m_AttachableNodeInstance.NetworkObjectId].GetComponentInChildren<TestNode>(); + Assert.NotNull(m_AttachableBehaviourInstance, $"{m_SourceInstance.name} does not have a nested child {nameof(AttachableBehaviour)}!"); + } + Assert.False(m_AttachableNodeInstance.IsAttached(m_AttachableBehaviourInstance), $"{m_AttachableNodeInstance.name} still thinks it is attached to {m_AttachableBehaviourInstance.name}!"); + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + + m_AttachableBehaviourInstance.Attach(m_AttachableNodeInstance); + + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(true)); + AssertOnTimeout($"[OnOwnershipChange][End] Timed out waiting for all clients to attach {m_AttachableBehaviourInstance.name} to {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + // Reset context of the AttachableNode instance to the owner of the m_TargetInstance for other below tests + if (m_DistributedAuthority) + { + var attachableNodeInstanceName = m_AttachableNodeInstance.name; + var ownerNetworkManager = m_NetworkManagers.Where((c) => c.LocalClientId == m_AttachableNodeInstance.OwnerClientId).First(); + Assert.True(ownerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(m_AttachableNodeInstance.NetworkObjectId), $"{ownerNetworkManager.name} does not have a spawned instance of {m_AttachableNodeInstance.name}!"); + m_AttachableNodeInstance = ownerNetworkManager.SpawnManager.SpawnedObjects[m_AttachableNodeInstance.NetworkObjectId].GetComponentInChildren<TestNode>(); + Assert.NotNull(m_AttachableNodeInstance, $"{attachableNodeInstanceName} does not exist on {ownerNetworkManager.name}!"); + } + } + + // Detach on despawn validation + if (autoDetachTypeFlags.HasFlag(AttachableBehaviour.AutoDetachTypes.OnDespawn)) + { + // Validates AttachableNode detaches AttachableBehaviours when despawned + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + m_TargetInstance.Despawn(false); + + yield return WaitForConditionOrTimeOut(() => ObjectDespawnedOnAllInstances(m_TargetInstanceId)); + AssertOnTimeout($"[OnDespawn] Timed out waiting for all clients to despawn {m_TargetInstance.name}!"); + + yield return WaitForConditionOrTimeOut(AllInstancesDetached); + AssertOnTimeout($"[OnDespawn] Timed out waiting for all clients to detach {m_AttachableBehaviourInstance.name} from {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + m_TargetInstance.Spawn(); + + yield return WaitForConditionOrTimeOut(AllClientsSpawnedInstances); + AssertOnTimeout($"Timed out waiting for all clients to spawn {m_TargetInstance.name}!\n {m_ErrorLog}"); + + m_TargetInstanceId = m_TargetInstance.NetworkObjectId; + + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + m_AttachableBehaviourInstance.Attach(m_AttachableNodeInstance); + + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(true)); + AssertOnTimeout($"[OnDespawn][End] Timed out waiting for all clients to attach {m_AttachableBehaviourInstance.name} to {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + // Validates when the AttachableBehaviour is despawned it will detach from the AttachableNode + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + var sourceInstanceId = m_SourceInstance.NetworkObjectId; + var sourceName = m_SourceInstance.name; + var attachableName = m_AttachableBehaviourInstance.name; + m_SourceInstance.Despawn(false); + + yield return WaitForConditionOrTimeOut(() => ObjectDespawnedOnAllInstances(sourceInstanceId)); + AssertOnTimeout($"[OnDespawn] Timed out waiting for all clients to despawn {sourceName}!"); + + yield return WaitForConditionOrTimeOut(AllInstancesDetachedWhenAttachableDespawned); + AssertOnTimeout($"[OnDespawn] Timed out waiting for all clients to detach {attachableName} from {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + + m_SourceInstance.Spawn(); + + yield return WaitForConditionOrTimeOut(AllClientsSpawnedInstances); + AssertOnTimeout($"Timed out waiting for all clients to spawn {m_TargetInstance.name}!\n {m_ErrorLog}"); + + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + if (m_DistributedAuthority) + { + m_AttachableBehaviourInstance = m_SourceInstance.GetComponentInChildren<TestAttachable>(); + Assert.NotNull(m_AttachableBehaviourInstance, $"{m_SourceInstance.name} does not have a nested child {nameof(AttachableBehaviour)}!"); + } + + m_AttachableBehaviourInstance.Attach(m_AttachableNodeInstance); + + yield return WaitForConditionOrTimeOut(() => AllInstancesAttachedStateChanged(true)); + AssertOnTimeout($"[OnDespawn][End] Timed out waiting for all clients to attach {m_AttachableBehaviourInstance.name} to {m_AttachableNodeInstance.name}!\n {m_ErrorLog}"); + } + + // Detach on destroy validation + if (autoDetachTypeFlags.HasFlag(AttachableBehaviour.AutoDetachTypes.OnAttachNodeDestroy)) + { + Assert.True(ResetAllStates(), $"Failed to reset all states!\n {m_ErrorLog}"); + // Mock the edge case scenario where the AttachableNode could be destroyed when an AttachableBehaviour is attached. + // Remove all other flags but the OnAttachNodeDestroy to assure this is what is invoked when the spawned AttachableNode (TargetInstance) + // is destroyed. + foreach (var networkManager in m_NetworkManagers) + { + var targetInstance = networkManager.SpawnManager.SpawnedObjects[m_TargetInstance.NetworkObjectId]; + var attachable = targetInstance.GetComponentInChildren<TestAttachable>(); + // Directly assign the value to assure this is the only thing that will trigger a detach + attachable.AutoDetach = AttachableBehaviour.AutoDetachTypes.OnAttachNodeDestroy; + } + var attachableNodeName = m_AttachableNodeInstance.name; + Object.Destroy(m_TargetInstance.gameObject); + yield return WaitForConditionOrTimeOut(AllInstancesDetached); + AssertOnTimeout($"[OnAttachNodeDestroy] Timed out waiting for all clients to detach {m_AttachableBehaviourInstance.name} from {attachableNodeName}!\n {m_ErrorLog}"); + } + } + /// <summary> /// Helps to validate that the overrides and events are invoked when an attachable attaches or detaches from the instance. /// This also helps to validate that the appropriate <see cref="AttachableNode"/> instance is passed in as a parameter. /// </summary> - public class TestAttachable : AttachableBehaviour + internal class TestAttachable : AttachableBehaviour { - private Dictionary<AttachState, AttachableNode> m_StateUpdates = new Dictionary<AttachState, AttachableNode>(); + public static bool VerboseMode; + + public static readonly Dictionary<ulong, Dictionary<AttachState, AttachableNode>> LastKnownOverrideStates = new Dictionary<ulong, Dictionary<AttachState, AttachableNode>>(); + public static readonly Dictionary<ulong, Dictionary<AttachState, AttachableNode>> LastKnownEventStates = new Dictionary<ulong, Dictionary<AttachState, AttachableNode>>(); + private Dictionary<AttachState, AttachableNode> m_StateUpdates = new Dictionary<AttachState, AttachableNode>(); private Dictionary<AttachState, AttachableNode> m_StateUpdateEvents = new Dictionary<AttachState, AttachableNode>(); public GameObject DefaultParent => m_DefaultParent; @@ -337,17 +685,47 @@ public override void OnNetworkSpawn() public override void OnNetworkDespawn() { + if (!LastKnownOverrideStates.ContainsKey(NetworkManager.LocalClientId)) + { + var localClientId = NetworkManager.LocalClientId; + LastKnownOverrideStates.Add(localClientId, new Dictionary<AttachState, AttachableNode>()); + LastKnownEventStates.Add(localClientId, new Dictionary<AttachState, AttachableNode>()); + + foreach (var overrideEntry in m_StateUpdates) + { + LastKnownOverrideStates[localClientId].Add(overrideEntry.Key, overrideEntry.Value); + } + + foreach (var eventEntry in m_StateUpdateEvents) + { + LastKnownEventStates[localClientId].Add(eventEntry.Key, eventEntry.Value); + } + } AttachStateChange -= OnAttachStateChangeEvent; base.OnNetworkDespawn(); } private void OnAttachStateChangeEvent(AttachState attachState, AttachableNode attachableNode) { + Log($"[Event][{name}][AttachState Changed] State: {attachState}"); + // When attaching to a new target node while attached to an existing one, just overwrite + // to get the most current attach state. + if (m_StateUpdateEvents.ContainsKey(attachState)) + { + m_StateUpdateEvents.Remove(attachState); + } m_StateUpdateEvents.Add(attachState, attachableNode); } protected override void OnAttachStateChanged(AttachState attachState, AttachableNode attachableNode) { + Log($"[Override][{name}][AttachState Changed] State: {attachState}"); + // When attaching to a new target node while attached to an existing one, just overwrite + // to get the most current attach state. + if (m_StateUpdates.ContainsKey(attachState)) + { + m_StateUpdates.Remove(attachState); + } m_StateUpdates.Add(attachState, attachableNode); base.OnAttachStateChanged(attachState, attachableNode); } @@ -360,45 +738,39 @@ public void ResetStates() private void Log(string message) { + if (!VerboseMode) + { + return; + } Debug.Log($"[{name}] {message}"); } - public bool CheckStateChangedOverride(bool checkAttached, bool checkEvent, AttachableNode attachableNode) + public bool CheckForState(bool checkAttached, bool checkEvent) { var tableToCheck = checkEvent ? m_StateUpdateEvents : m_StateUpdates; - var checkStatus = checkAttached ? (tableToCheck.ContainsKey(AttachState.Attaching) && tableToCheck.ContainsKey(AttachState.Attached)) : - (tableToCheck.ContainsKey(AttachState.Detaching) && tableToCheck.ContainsKey(AttachState.Detached)); + var expectedState = checkAttached ? AttachState.Attached : AttachState.Detached; + var checkStatus = tableToCheck.ContainsKey(expectedState); + if (checkStatus) { - foreach (var entry in tableToCheck) + if ((checkAttached && transform.parent == DefaultParent.transform) || (!checkAttached && transform.parent != DefaultParent.transform)) { - // Ignore any states that don't match what is being checked - if ((checkStatus && (entry.Key == AttachState.Detaching || entry.Key == AttachState.Detached)) || - (!checkStatus && (entry.Key == AttachState.Attaching || entry.Key == AttachState.Attached))) + if (!checkAttached) { - continue; + Log($"[CheckState][Fail][Wrong Parent] checkAttached = {checkAttached} | parent = {transform.parent?.name} | Expected {DefaultParent.name}"); } - - // Special case for completely detached - if (entry.Key == AttachState.Detached) - { - if (entry.Value != null) - { - Log($"[Value] The value {entry.Value.name} is not null!"); - checkStatus = false; - break; - } - } - else if (entry.Value != attachableNode) + else { - var attachableName = attachableNode == null ? "null" : attachableNode.name; - var entryName = entry.Value == null ? "null" : entry.Value.name; - Log($"[{entry.Key}][Value] The value {entryName} is not the same as {attachableName}!"); - checkStatus = false; - break; + Log($"[CheckState][Fail][Wrong Parent] checkAttached = {checkAttached} | parent = {transform.parent?.name}"); } + return false; } } + else + { + var checkType = checkEvent ? "m_StateUpdateEvents" : "m_StateUpdates"; + Log($"[CheckState][Fail][No Event Logged] checkAttached = {checkAttached} | table {checkType} does not contain the expected state {expectedState} log."); + } return checkStatus; } } @@ -406,11 +778,16 @@ public bool CheckStateChangedOverride(bool checkAttached, bool checkEvent, Attac /// <summary> /// Helps to validate that the overrides are invoked when an attachable attaches or detaches from the instance. /// </summary> - public class TestNode : AttachableNode + internal class TestNode : AttachableNode { public bool OnAttachedInvoked { get; private set; } public bool OnDetachedInvoked { get; private set; } + public bool IsAttached(AttachableBehaviour attachableBehaviour) + { + return m_AttachedBehaviours.Contains(attachableBehaviour); + } + public void ResetStates() { OnAttachedInvoked = false; @@ -429,5 +806,10 @@ protected override void OnDetached(AttachableBehaviour attachableBehaviour) base.OnDetached(attachableBehaviour); } } + + internal class TestController : ComponentController + { + + } } } From 741b42bb5800404adad5dd151b10a5100dd3fc04 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 00:42:08 -0500 Subject: [PATCH 29/43] docs (update,, refactor. and fix) Adjusting the component section from a file and folder context. Adjusting the component section for the table of contents. Adding foundational components and helpers sub-sections to network components section. Moved foundational components into the foundational (components) folder and updated links and image paths. Adding AttachableBehaviour document along with images. Added some place holders for AttachableNode and ComponentController. Fixing some invalid image paths. --- .../Documentation~/TableOfContents.md | 26 +-- .../Documentation~/advanced-topics/physics.md | 2 +- .../advanced-topics/session-management.md | 2 +- .../Documentation~/basics/object-spawning.md | 2 +- .../basics/object-visibility.md | 2 +- .../Documentation~/basics/ownership.md | 2 +- .../scenemanagement/custom-management.md | 2 +- .../inscene-placed-networkobjects.md | 2 +- .../basics/scenemanagement/scene-events.md | 6 +- .../components/Helpers/attachablebehaviour.md | 166 ++++++++++++++++++ .../components/Helpers/helpercomponents.md | 12 ++ .../{ => Helpers}/networkanimator.md | 8 +- .../{ => Helpers}/networktransform.md | 30 ++-- .../foundational/foundationalcomponents.md | 34 ++++ .../networkbehaviour-synchronize.md | 2 +- .../foundational}/networkbehaviour.md | 0 .../{ => foundational}/networkmanager.md | 22 +-- .../foundational}/networkobject.md | 20 +-- .../foundational}/playerobjects.md | 22 +-- .../AttachableBehaviour_InspectorView-1.png | Bin 0 -> 23713 bytes .../images/attachable/AttachableDiagram-1.png | Bin 0 -> 70586 bytes .../images/attachable/AttachableDiagram-2.png | Bin 0 -> 69939 bytes .../images/attachable/AttachableDiagram-3.png | Bin 0 -> 85502 bytes .../AttachableNode_InspectorView-1.png | Bin 0 -> 8167 bytes .../ComponentController_InspectorView-1.png | Bin 0 -> 43859 bytes .../attachable/PlayerAndWorldItem-1.png | Bin 0 -> 7087 bytes .../attachable/PlayerAndWorldItem-2.png | Bin 0 -> 8959 bytes .../images/attachable/SpawnObjectA-B-2.png | Bin 0 -> 4244 bytes .../images/attachable/SpawnObjectA-B.png | Bin 0 -> 2763 bytes .../attachable/WorldItem-Inspector-View-1.png | Bin 0 -> 72855 bytes .../attachable/WorldItem-Inspector-View-2.png | Bin 0 -> 41631 bytes .../learn/clientside-interpolation.md | 2 +- .../learn/dealing-with-latency.md | 8 +- .../Documentation~/network-components.md | 9 +- .../networkbehaviour-landing.md | 7 +- .../Documentation~/samples.md | 4 +- .../samples/bitesize/bitesize-clientdriven.md | 2 +- .../samples/bitesize/bitesize-invaders.md | 2 +- .../bossroom/networkobject-parenting.md | 2 +- .../samples/bossroom/optimizing-bossroom.md | 6 +- .../tutorials/get-started-with-ngo.md | 2 +- .../Documentation~/tutorials/helloworld.md | 2 +- .../testing_with_artificial_conditions.md | 2 +- 43 files changed, 311 insertions(+), 99 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md rename com.unity.netcode.gameobjects/Documentation~/components/{ => Helpers}/networkanimator.md (97%) rename com.unity.netcode.gameobjects/Documentation~/components/{ => Helpers}/networktransform.md (94%) create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/foundational/foundationalcomponents.md rename com.unity.netcode.gameobjects/Documentation~/{basics => components/foundational}/networkbehaviour-synchronize.md (98%) rename com.unity.netcode.gameobjects/Documentation~/{basics => components/foundational}/networkbehaviour.md (100%) rename com.unity.netcode.gameobjects/Documentation~/components/{ => foundational}/networkmanager.md (90%) rename com.unity.netcode.gameobjects/Documentation~/{basics => components/foundational}/networkobject.md (86%) rename com.unity.netcode.gameobjects/Documentation~/{basics => components/foundational}/playerobjects.md (77%) create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableBehaviour_InspectorView-1.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-1.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-2.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-3.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableNode_InspectorView-1.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/ComponentController_InspectorView-1.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/PlayerAndWorldItem-1.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/PlayerAndWorldItem-2.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/SpawnObjectA-B-2.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/SpawnObjectA-B.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/WorldItem-Inspector-View-1.png create mode 100644 com.unity.netcode.gameobjects/Documentation~/images/attachable/WorldItem-Inspector-View-2.png diff --git a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md index f784cc5148..9759fa90a7 100644 --- a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md +++ b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md @@ -18,17 +18,21 @@ * [Max players](basics/maxnumberplayers.md) * [Transports](advanced-topics/transports.md) * [Relay](relay/relay.md) -* [Network components](network-components.md) - * [NetworkObject](basics/networkobject.md) - * [PlayerObjects and player prefabs](basics/playerobjects.md) - * [NetworkObject parenting](advanced-topics/networkobject-parenting.md) - * [NetworkBehaviour](networkbehaviour-landing.md) - * [NetworkBehaviour](basics/networkbehaviour.md) - * [Synchronize](basics/networkbehaviour-synchronize.md) - * [Physics](advanced-topics/physics.md) - * [NetworkManager](components/networkmanager.md) - * [NetworkTransform](components/networktransform.md) - * [NetworkAnimator](components/networkanimator.md) +* [Network components](network-components.md) + * [Foundational Components](components/foundational/foundationalcomponents.md) + * [NetworkObject](components/foundational/networkobject.md) + * [NetworkObject parenting](advanced-topics/networkobject-parenting.md) + * [NetworkBehaviour](components/foundational/networkbehaviour.md) + * [Synchronizing & Order of Operations](components/foundational/networkbehaviour-synchronize.md) + * [NetworkManager](components/foundational/networkmanager.md) + * [PlayerObjects and player prefabs](components/foundational/playerobjects.md) + * [Helper Components](components/Helpers/helpercomponents.md) + * [AttachableBehaviour](components/Helpers/attachablebehaviour.md) + * AttachableNode + * ComponentController + * [NetworkAnimator](components/helpers/networkanimator.md) + * [NetworkTransform](components/helpers/networktransform.md) + * [Physics](advanced-topics/physics.md) * [Ownership and authority](ownership-authority.md) * [Understanding ownership and authority](basics/ownership.md) * [Ownership race conditions](basics/race-conditions.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/physics.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/physics.md index 72dd8c5d9f..6b009f3d51 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/physics.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/physics.md @@ -21,7 +21,7 @@ Some collision events aren't fired when using `NetworkRigidbody`. ## NetworkRigidbody and ClientNetworkTransform -You can use NetworkRigidbody with the [`ClientNetworkTransform`](../components/networktransform.md) package sample to allow the owner client of a NetworkObject to move it authoritatively. In this mode, collisions only result in realistic dynamic collisions if the object is colliding with other NetworkObjects (owned by the same client). +You can use NetworkRigidbody with the [`ClientNetworkTransform`](../components/helpers/networktransform.md) package sample to allow the owner client of a NetworkObject to move it authoritatively. In this mode, collisions only result in realistic dynamic collisions if the object is colliding with other NetworkObjects (owned by the same client). > [!NOTE] > Add the ClientNetworkTransform component to your GameObject first. Otherwise the NetworkRigidbody automatically adds a regular NetworkTransform. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/session-management.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/session-management.md index 6f3e0f0d19..ba3d699ed2 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/session-management.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/session-management.md @@ -19,7 +19,7 @@ You can also decide to clear all data when a session completes or add a timeout # Reconnection -The best way to reconnect players depends on your game. For example, if you use a [Player Object](../basics/networkobject.md#player-objects), a new `Default Player Prefab` automatically spawns when a player connects to the game (including when they reconnect). You can use the player's earlier saved session data to update that object so that it returns to the same state before disconnecting. In those cases, you would need to keep all the important data that you want to restore and map it to the player using your identification system. You can save this data when a player disconnects or update it periodically. You can then use the `OnNetworkSpawn` event on the Player Object's `NetworkBehavior`(s) to get this data and apply it where needed. +The best way to reconnect players depends on your game. For example, if you use a [Player Object](../components/foundational/networkobject.md#player-objects), a new `Default Player Prefab` automatically spawns when a player connects to the game (including when they reconnect). You can use the player's earlier saved session data to update that object so that it returns to the same state before disconnecting. In those cases, you would need to keep all the important data that you want to restore and map it to the player using your identification system. You can save this data when a player disconnects or update it periodically. You can then use the `OnNetworkSpawn` event on the Player Object's `NetworkBehavior`(s) to get this data and apply it where needed. In cases where we don't use the Player Object approach and instead manually attribute client ownership to `NetworkObject`(s), we can keep the objects that a player owns when they disconnect, and set the reconnected player as their new owner. To accomplish this, the only data we would need to keep would be the mapping between those objects and their owning player's identifier, then when a player reconnects we can use this mapping to set them as the new owner. This mapping can be as simple as a dictionary mapping the player identifier with the `NetworkObjectId`(s) of the `NetworkObject`(s) they own. Then, in the `OnClientConnectedCallback` from the `NetworkManager`, the server can set the ownership of these objects. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md b/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md index ffab9107fa..0fa234bba8 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md @@ -31,7 +31,7 @@ _In most cases, you will want to keep the `NetworkObject` component attached to By default a newly spawned network Prefab instance is owned by the server unless otherwise specified. -See [Ownership](networkobject.md#ownership) for more information. +See [Ownership](../components/foundational/networkobject.md#ownership) for more information. The following is a basic example of how to spawn a network Prefab instance (with the default server ownership): diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/object-visibility.md b/com.unity.netcode.gameobjects/Documentation~/basics/object-visibility.md index 1c730811ed..6d98cb5990 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/object-visibility.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/object-visibility.md @@ -111,4 +111,4 @@ NetworkObject.SpawnWithObservers = false; NetworkObject.Spawn(); ``` -See [Spawning With (or Without) Observers](networkobject.md#spawning-with-or-without-observers) for more information. +See [Spawning With (or Without) Observers](../components/foundational/networkobject.md#spawning-with-or-without-observers) for more information. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/ownership.md b/com.unity.netcode.gameobjects/Documentation~/basics/ownership.md index 93035f2653..cd63291f66 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/ownership.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/ownership.md @@ -1,6 +1,6 @@ # Understanding ownership and authority -By default, Netcode for GameObjects assumes a [client-server topology](../terms-concepts/client-server.md), in which the server owns all NetworkObjects (with [some exceptions](networkobject.md#ownership)) and has ultimate authority over [spawning and despawning](object-spawning.md). +By default, Netcode for GameObjects assumes a [client-server topology](../terms-concepts/client-server.md), in which the server owns all NetworkObjects (with [some exceptions](../components/foundational/networkobject.md#ownership)) and has ultimate authority over [spawning and despawning](object-spawning.md). Netcode for GameObjects also supports building games with a [distributed authority topology](../terms-concepts/distributed-authority.md), which provides more options for ownership and authority over NetworkObjects. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/custom-management.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/custom-management.md index fc64c2d204..883ad9914a 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/custom-management.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/custom-management.md @@ -39,7 +39,7 @@ Once you've registered your in-scene placed Network Prefabs with your `NetworkPr > When a client first connects, it deletes any in-scene placed `NetworkObjects` in any of the scenes it has currently loaded. When using a custom scene management solution, in-scene placed `NetworkObject`s are actually dynamically spawned. This means any changes you make to your in-scene placed Network Prefabs will *not* be synchronized with clients automatically. ### Synchronizing In-Scene Placed Network Prefab Instances -If you want to change an in-scene placed network prefab instance, you need to handle the serialization of these settings yourself. You can do this by overriding `NetworkBehaviour.OnSynchronize` and serializing any property updates you want to have synchronized with clients when they join. [Read More About OnSynchronize Here](../../basics/networkbehaviour.md#pre-spawn-synchronization). +If you want to change an in-scene placed network prefab instance, you need to handle the serialization of these settings yourself. You can do this by overriding `NetworkBehaviour.OnSynchronize` and serializing any property updates you want to have synchronized with clients when they join. [Read More About OnSynchronize Here](../../components/foundational/networkbehaviour.md#pre-spawn-synchronization). ## Starting a Netcode Enabled Game Session The recommended way of starting session using your own scene management solution is to assure that when a client attempts to join a netcode game session it should already have (as best as possible) any scenes that the server might have loaded. While this does not assure that your newly connecting client will load any additional scenes that might have been loaded, using this approach initially will get you started so you can then come up with a strategy to handling: diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md index 28fcbe3714..4bc8228a36 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md @@ -13,7 +13,7 @@ In-scene placed `NetworkObject`s are GameObjects with a `NetworkObject` componen - For example, a heads up display (HUD) that includes information about other items or players. - Or some form of platform or teleporter that moves a player from one location to the next when a player enters a trigger or uses an object. -Another benefit of in-scene placed `NetworkObject`s is that they don't require you to register them with the [`NetworkManager`](../../components/networkmanager.md). In-scene placed `NetworkObjects` are registered internally, when scene management is enabled, for tracking and identification purposes. +Another benefit of in-scene placed `NetworkObject`s is that they don't require you to register them with the [`NetworkManager`](../../components/foundational/networkmanager.md). In-scene placed `NetworkObjects` are registered internally, when scene management is enabled, for tracking and identification purposes. > [!NOTE] > Items that can be picked up are typically better implemented using a [hybrid approach](#hybrid-approach) with both an in-scene placed and a dynamically spawned `NetworkObject`. The in-scene placed `NetworkObject` can be used to configure additional information about the item (what kind, does another one respawn after one is picked up, and if so how much time should it wait before spawning a new item), while the dynamically spawned object is the item itself. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-events.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-events.md index e178aa2058..89933f4dcc 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-events.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-events.md @@ -57,7 +57,7 @@ While client synchronization does fall partially outside of the scene management <br/> Below is a diagram of the client connection approval and synchronization process: - + Starting with the "Player" in the top right part of the above diagram, the client (Player) runs through the connection and approval process first which occurs within the `NetworkManager`. Once approved, the server-side `NetworkSceneManager` begins the client synchronization process by sending the `SceneEventType.Synchronize` Scene Event message to the approved client. The client then processes through the synchronization message. Once the client is finished processing the synchronize message, it responds to the server with a `SceneEventType.SynchronizeComplete` message. At this point the client is considered "synchronized". If the server determines any `NetworkObject` was despawned during the client-side synchronization message processing period, it will send a list of `NetworkObject` identifiers to the client via the `SceneEventType.ReSynchronize` message and the client will locally despawn the `NetworkObject`s. @@ -228,10 +228,10 @@ You can stop the coroutine checking the progress upon receiving any of the follo The SceneEvent class has values that may or may not be set depending upon the `SceneEventType`. Below are two quick lookup tables to determine which property is set for each `SceneEventType`. **Part-1**<br/> -<br/> +<br/> **Part-2** <br/> -<br/> +<br/> So, the big "take-away" from the above table is that you need to understand the `SceneEventType` context of the `SceneEvent` you are processing to know which properties are valid and you can use. As an example, it wouldn't make sense to provide the AsyncOperation for the following `SceneEventType`s: - LoadComplete or LoadEventCompleted diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md new file mode 100644 index 0000000000..2bbc29c4a5 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md @@ -0,0 +1,166 @@ +# AttachableBehaviour +The `AttachableBehaviour` Provides "out of the box" support for attaching (i.e. parenting) a nested child `GameObject` that includes an `AttachableBehaviour` component to another nested child `GameObject` with an `AttachableNode` component that is associated with a different `NetworkObject`. + +## Attaching vs NetworkObject parenting + +Fundamentally, attaching is another way to synchronize parenting while not requiring one to use the traditional `NetworkObject` parenting. Attaching a child `GameObject` nested under a `NeworkObject` (_really the `GameObject` the `NetworkObject` component belongs to_) will only take the child `GameObject` and parent it under the `GameObject` of an `AttachableNode`. The target to parent under must be of a different spawned `NetworkObject` and the `AttachableNode` needs to be on the same or child `GameObject` of the target `NetworkObject`. + +### NetworkObject parenting + +The traditional approach has been to spawn two network prefab instances:<br /> + + +Then parent one instance under the other:<br /> + + +This is simple enough for many scenarios, but can become cumbersome under more specific scenarios where a user might want to have a "world" version of the item and a "picked up" version of the item. + +### Attaching + +With attaching, a user would create nested `GameObject` children that represent the item when it is picked up and when it is dropped/placed somewhere in the scene (i.e. world).<br /> + + + - The WorldItemRoot is where the `NetworkObject` component is placed. + - The NestedChild-World contains the components needed for the item when it is placed in the world. + - The NestedChild-PickedUp contains the components needed for the item when it is picked up by a player. + +By placing an `AttachableBehaviour` component on the NestedChild-PickedUp `GameObject` and an `AttachableNode` component on the TargetNode, a user can then invoke the `AttachableBehaviour.Attach` method while passing in the `AttachableNode` component and the NestedChild-PickedUp `GameObject` will get parented under the TargetNode while also synchronizing this action with all other clients.<br /> + + +### AttachableBehaviour + + + +The basic functionality of the `AttachableBehaviour` component provides: +- The ability to assign (make aware) `ComponetController` components from any part of the parent-child hierarchy. + - Each `ComponentControllerEntry` provides the ability to select when the `ComponentController` should be triggered (via the **Auto Trigger** property) and whether its enabled state should be enabled or disabled upon attaching (via the **Enable On Attach** property). The default setting is to be disabled upon the `AttachableBehaviour` attaching to an `AttachableNode` and enabled upon detaching. When the **Enable On Attach** property is enabled, the `ComponentController` will be set to enabled upon the `AttachableBehaviour` attaching to an `AttachableNode` and disabled upon detaching. +- The ability to control when an `AttachableBehaviour` component will automatically detach from an `AttachableNode` via the **Auto Detach** property. + - The **Auto Detach** property can have any combination of the below flags or none (no flags): + - **On Ownership Changed:** Upon ownership changing, the `AttachableBehaviour` will detach from any `AttachableNode` it is attached to. + - **On Despawn:** Upon the `AttachableBehaviour` being despawned, it will detach from any `AttachableNode` it is attached to. + - **On Attach Node Destroy**: Just prior to the `AttachableNode` being destroyed, any attached `AttachableBehaviour` with this flag will automatically detach from the `AttachableNode`. + +_Any of the `AttachableBehaviour.AutoDetach` settings will be invoked on all instances without the need for the owner to synchronize the end result(i.e. detaching) which provides a level of redundancy for edge case scenarios like a player being disconnected abruptly by the host or by timing out or any scenario where a spawned object is being destroyed with the owner or perhaps being redistributed to another client authority in a distributed authority session. Having the ability to select or deselect any of the auto-detach flags coupled with the ability to derive from `AttachableBehaviour` provides additional levels of modularity/customization._ + +### AttachableNode + + + +The simplest component in the bunch, this provides a valid connection point (_i.e. what an `AttachableBehaviour` can attach to_) with the ability to have it automatically detach from any attached `AttachableBehaviour` instances when it is despawned. + +### ComponentController + + + +Taking the above example into consideration, it would make sense that a user would want to be able to easily control whether a specific component is enabled or disabled when something is attached or detached. + +As an example: + +- When the WorldItemRoot is in the "placed in the world" state, it would make sense to disable any `MeshRenderer`, `Collider`, and other components on the NestedChild-PickedUp `GameObject` while enabling similar types of components on the NestedChild-World. +- When the WorldItemRoot is in the "picked up" state, it would make sense to enable any `MeshRenderer`, `Collider`, and other components on the NestedChild-PickedUp `GameObject` while disabling similar types of components on the NestedChild-World. +- It would also make sense to synchronize the enabling or disabling of components with all instances. + +The `ComponentController` provides this type of functionality: +- Can be used with `AttachableBehaviour` or independently for another purpose. +- Each assigned component entry can be configured to directly or inversely follow the `ComponentController`'s current state. +- Each assigned component entry can have an enable and/or disable delay. + - _When invoked internally by `AttachableBehaviour`, delays are ignored when an `AttachableNode` is being destroyed and the changes are immediate._ + +The `ComponentController` could be daisy chained with minimal user script: +```csharp +/// <summary> +/// Use as a component in the ComponentController that will +/// trigger the Controller (ComponentController). +/// This pattern can repeat. +/// </summary> +public class DaisyChainedController : MonoBehaviour +{ + public ComponentController Controller; + + private void OnEnable() + { + if (!Controller || !Controller.HasAuthority) + { + return; + } + Controller.SetEnabled(true); + } + + private void OnDisable() + { + if (!Controller || !Controller.HasAuthority) + { + return; + } + Controller.SetEnabled(false); + } +} +``` + +### Example of synchronized RPC driven properties + +Both the `AttachableBehaviour` and the `ComponentController` provide an example of using synchronized RPC driven properties in place of `NetworkVariable`. Under certain conditions it is better to use RPCs when a specific order of operations is needed as opposed to `NetworkVariable`s which can update out of order (regarding the order in which certain states were updated) depending upon several edge case scenarios. + +Under this condition using reliable RPCs will assure the messages are received in the order they were generated while also reducing the latency time between the change and the non-authority instances being notified of the change. Synchronized RPC driven properties only require overriding the `NetworkBehaviour.OnSynchronize` method and serializing any properties that need to be synchronized with late joining players or handling network object visibility related scenarios. + +## Usage Walk Through + +### Introduction + +For example purposes, we will walk through a common scenario where you might want to have a world item that had unique visual and scripted components active while while placed in the world but then can switch to a different set of visual and scripted components when picked up by a player's avatar. Additionally, you might want to be able to easily "attach" only the portion of the item, that is active when picked up, to one of the player's avatar's child nodes. Below is a high-level diagram overview of what both the player and world item network prefabs could look like:<br /> + + + +#### Player + +The player prefab in the above diagram is not complete, includes the components of interest, and some additional children and components for example purposes. A complete diagram would most definitely have additional components and children. The `AttachableNode` components provide a "target attach point" that any other spawned network prefab with an `AttachableBehaviour` could attach itself to. + +#### World Item + +This diagram has a bit more detail to it and introduces one possible usage of a `ComponentController` and `AttachableBehaviour`. The `ComponentController` will be used to control the enabling and disabling of components and synchronizing this with non-authority instances. The `AttachableBehaviour` resides on the child `AttachedView`'s `GameObject` and will be the catalyst for attaching to a player. + +### World vs Attached View Modes + + + + +In the diagram above, we see arrows pointing from the `ComponentController` to the non-netcode standard Unity components such as a `MeshRenderer`, `Collider`, or any other component that should only be enabled when either in "World View" or "Attached View" modes. We can also see that the `AttachableBehaviour` points to the `ComponentController` with a diagram to the right that shows the `AttachableBehaviour` notifies the `ComponentController` that, in turn, enables or disables certain components. + +#### World Item Component Controller +Below is a screenshot of what the `ComponentController` would look like in the inspector view:<br /> + + + +Looking at the `ComponentController`'s **Components** property, we can see two of the component entries have references to the `WorldItemView`'s `BoxCollider` and `MeshRenderer` that are both configured to be enabled when the `ComponentController`'s state is `true`. We can also see that the `CarryView`'s `MeshRenderer` is added and configured to be the inverse of the current `ComponentController`'s state. Since the `ComponentController`'s **Start Enabled** property is enabled we can logically deduce the **WorldItem** network prefab will start with the `WorldItemView` being active when spawned. Taking a look at the **CarryObject** child's properties: + + + +We can see the `AttachableBehaviour`'s **Component Controllers** list contains `ComponentControllerEntry` (WorldItem Component Controller) that references to the `WorldItem`'s `ComponentController`. We can also see that the `ComponentControllerEntry` is configured to trigger on everything (_OnAttach and OnDetach_) and will set the `ComponentController`'s state to disabled _(false)_. This means when the `AttachableBehaviour` is attached the `ComponentController` will be in the disabled state along with the `WorldItemView` components while the `CarryView`'s `MeshRenderer` will be enabled. + +**Summarized Overview:** +- `AttachableBehaviour` sets the `ComponentController` state (true/enabled or false/disabled). +- `ComponentController` states: + - Enabled (true) + - World Item View (enabled/true) + - Carry View (disabled/false) + - Disabled (false) + - World Item View (disabled/false) + - Carry View (enabled/true) + +### Attaching + + + +The above diagram represents what the **Player** and **World Item** spawned objects (_including cloned/non-authority instances_) would look like once the **Attached View** object has been parented under the avatar's **Right Attach** object. The green area and arrow represent the still existing relationship that the **Attached View** has with the **World Item**'s `NetworkObject`. + +:::info +**AttachableBehaviour & NetworkObject Relationship** + +Upon a `NetworkObject` component being spawned, all associated `NetworkBehaviour` based component instances, that are directly attached to the `NetworkObject`'s `GameObject` or are on any child `GameObject`, will be registered with the `NetworkObject` instance. This remains true even when a child `GameObject` containing one or more `NetworkBehaviour` based component instances of a spawned `NetworkObject` is parented, during runtime, under another `GameObject` that is associated with a different spawned `NetworkObject`. Of course, there are additional considerations like: + - What happens when one or both of the NetworkObjects is de-spawned? + - How do you assure the child attachable will return back to its default parent? + - and several other edge case scenarios... + +`AttachableBehaviour` leverages from this "spawn lifetime" relationship to provide another type of "parenting" (attaching) while also taking into consideration these types of edge case scenarios. +::: + diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md new file mode 100644 index 0000000000..d146568479 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md @@ -0,0 +1,12 @@ +# Helper Components + +Understand the helper components available to use in your Netcode for GameObjects project. + + **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[AttachableBehaviour](attachablebehaviour.md)**| Provides an alternative to `NetworkObject` parenting. (wip) | +| **AttachableNode**| Target parent for an `AttachableBehaviour`. (wip) | +| **ComponentController**| Provides the synchronization of and control over enabling or disabling objects. (wip) | +| **[NetworkAnimator](networkanimator.md)**| The `NetworkAnimator` component provides you with a fundamental example of how to synchronize animations during a network session. Animation states are synchronized with players joining an existing network session and any client already connected before the animation state changing. | +| **[NetworkTransform](networktransform.md)**| [NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](../foundational/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. | +| **[Physics](../../advanced-topics/physics.md)**| Netcode for GameObjects has a built in approach which allows for server-authoritative physics where the physics simulation only runs on the server. | diff --git a/com.unity.netcode.gameobjects/Documentation~/components/networkanimator.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/networkanimator.md similarity index 97% rename from com.unity.netcode.gameobjects/Documentation~/components/networkanimator.md rename to com.unity.netcode.gameobjects/Documentation~/components/Helpers/networkanimator.md index 21e0a28c57..51b8d52eed 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/networkanimator.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/networkanimator.md @@ -34,7 +34,7 @@ The `Animator` trigger property type ("trigger") is basically nothing more than The default setting for `NetworkAnimator` is server authoritative mode. When operating in server authoritative mode, any animation state changes that are set (triggers) or detected (change in layer, state, or any `Animator` properties excluding triggers) on the server side will be synchronized with all clients. Because the server initiates any synchronization of changes to an `Animator` 's state, a client that's the owner of the `NetworkObject` associated with the `NetworkAnimator` can lag by roughly the full round trip time (RTT). Below is a timing diagram to show this: - + In the above diagram, a client might be sending the server an RPC to tell the server that the player is performing some kind of action that can change the player's animations (including setting a trigger). Under this scenario, the client sends an RPC to the server (half RTT), the server processes the RPC, the associated `Animator` state changes are detected by the `NetworkAnimator` (server-side), and then all clients (including the owner client) are synchronized with the changed. @@ -63,7 +63,7 @@ Usually, your project's design (or personal preference) might require that owner Looking at the timing for an owner authoritative `NetworkAnimator`, in the diagram below, you can see that while the owner client gets "immediate visual animation response" the non-owner clients end up being roughly one full RTT behind the owner client and a host would be half RTT behind the owner client. - + In the above diagram, it shows that the owner client has an `Animator` state change that's detected by the `NetworkAnimator` ( `OwnerNetworkAnimator`) which automatically synchronizes the server with the changed state. The server applies the change(s) locally and then broadcasts this state change to all non-owner clients. @@ -90,13 +90,13 @@ Using `NetworkAnimator` is a pretty straight forward approach with the only subt If you decide you want to use the server authoritative model, then you can add a `NetworkAnimator` component to either the same `GameObject` that has the `NetworkObject` component attached to it or any child `GameObject`. In the below screenshot, you can see a network Prefab that houses two authoritative models. The `NetworkAnimatorCube-Server` `GameObject` has an `Animator` component, an `AnimatedCubeController` component (used for manual testing), and the `NetworkAnimator` component that has a reference to the `Animator` component. - + ### Owner Authoritative If you decide you want to use the owner authoritative model, then (for example purposes) you would use your derived `OwnerNetworkAnimator` component as opposed to the default `NetworkAnimator` component like in the screenshot below: - + > [!NOTE] > While it isn't advised to have different `NetworkAnimator` authoritative models "under the same root network Prefab `GameObject`, " you can have multiple children that each have their own `Animator` and `NetworkAnimator` all housed under a single `NetworkObject` and all use the same authoritative model. However, you should always consider the balance between performance (CPU or bandwidth consumption) and convenience/modularity. diff --git a/com.unity.netcode.gameobjects/Documentation~/components/networktransform.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/networktransform.md similarity index 94% rename from com.unity.netcode.gameobjects/Documentation~/components/networktransform.md rename to com.unity.netcode.gameobjects/Documentation~/components/Helpers/networktransform.md index 2bbc224f0e..c3b3456148 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/networktransform.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/networktransform.md @@ -1,6 +1,6 @@ # NetworkTransform -[NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](../basics/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. +[NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](../foundational/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. The synchronization of a GameObject's Transform is a key netcode task, and usually proceeds in the following order: @@ -20,20 +20,20 @@ There are other considerations when synchronizing Transform values, however, suc ## Add a NetworkTransform component to a GameObject -Because a NetworkTransform component is derived from the NetworkBehaviour class, it has many of the [same requirements](../basics/networkbehaviour.md). For example, when adding a NetworkTransform component to a GameObject, it should be added to the same or any hierarchy generation relative to the `NetworkObject` component. +Because a NetworkTransform component is derived from the NetworkBehaviour class, it has many of the [same requirements](../foundational/networkbehaviour.md). For example, when adding a NetworkTransform component to a GameObject, it should be added to the same or any hierarchy generation relative to the `NetworkObject` component. In the following image both NetworkTransform and NetworkObject components are on the same GameObject: - + Alternatively, the parent GameObject can have multiple children where any child can have a NetworkTransform: - + Theoretically, you can have a NetworkTransform on every child object of a 100 leaf deep hierarchy. However, it's recommended to exercise caution with the amount of nested NetworkTransforms in a network prefab, particularly if there will be many instances of the network prefab. > [!NOTE] -> Generally, as long as there's at least one [NetworkObject](../basics/networkobject.md) at the same GameObject hierarchy level or above, you can attach a NetworkTransform component to a GameObject. You could have a single root-parent GameObject that has a NetworkObject component and under the root-parent several levels of nested child GameObjects that all have NetworkTransform components attached to them. Each child GameObject doesn't require a NetworkObject component in order for the respective NetworkTransform component to synchronize properly. +> Generally, as long as there's at least one [NetworkObject](../foundational/networkobject.md) at the same GameObject hierarchy level or above, you can attach a NetworkTransform component to a GameObject. You could have a single root-parent GameObject that has a NetworkObject component and under the root-parent several levels of nested child GameObjects that all have NetworkTransform components attached to them. Each child GameObject doesn't require a NetworkObject component in order for the respective NetworkTransform component to synchronize properly. ### Nesting NetworkTransforms @@ -48,7 +48,7 @@ For example, if you use a [NetworkAnimator](networkanimator.md) component to syn When you select a NetworkTransform component, there are the following properties in the inspector view: - + ### Property synchronization @@ -77,7 +77,7 @@ The following NetworkTransform properties can cause a full state update when cha ### Axis to Synchronize - + You often don't need to synchronize all Transform values of a GameObject over the network. For example, if the scale of the GameObject never changes, you can deactivate it in the __Scale__ row of the __Axis to Synchronize__ area within the Inspector. Deactivating synchronization saves some processing costs and reduces network bandwidth consumption. @@ -99,9 +99,9 @@ The __Axis to Synchronize__ properties that determine which axes are synchronize ### Authority - + -The authority mode of a NetworkTransform determines who is the authority over changes to the Transform state. This setting only applies when using a [client-server network topology](../terms-concepts/client-server.md) because in a [distributed authority network topology](../terms-concepts/distributed-authority.md) Netcode for GameObjects automatically sets the owner authority for every NetworkTransform. If you plan on developing for both network topologies then you can use this setting to preserve authority (whether server or owner) for the client-server network topology. +The authority mode of a NetworkTransform determines who is the authority over changes to the Transform state. This setting only applies when using a [client-server network topology](../../terms-concepts/client-server.md) because in a [distributed authority network topology](../../terms-concepts/distributed-authority.md) Netcode for GameObjects automatically sets the owner authority for every NetworkTransform. If you plan on developing for both network topologies then you can use this setting to preserve authority (whether server or owner) for the client-server network topology. By default, NetworkTransform operates in server-authoritative mode. This means that changes to the Transform axis (marked to be synchronized) are detected on the server-side and state updates are pushed to connected clients. This also means any changes to the Transform axis values are overridden by the authoritative state (in this case the server-side Transform state). @@ -120,7 +120,7 @@ When mixing authority motion models and using physics, latency will impact how ( ### Thresholds - + You can use the threshold values to set a minimum threshold value for synchronizing changes. This can be help reduce the frequency of synchronization updates by only synchronizing changes above or equal to the threshold values (changes below won't be synchronized). @@ -131,7 +131,7 @@ For example, if your NetworkTransform has [__Interpolate__](#interpolation) enab ### Delivery - + #### Tick Synchronize Children @@ -160,7 +160,7 @@ When unreliable state updates are enabled, NetworkTransform instances are assign ### Configurations - + #### In Local Space @@ -182,7 +182,7 @@ To resolve this issue, you can enable the __Switch Transform Space When Parented ### Interpolation - + Interpolation is enabled by default and is recommended if you desire smooth transitions between Transform updates on non-authoritative instances. Interpolation buffers incoming state updates that can introduce a slight delay between the authority and non-authority instances. When the __Interpolate__ property is disabled, changes to the transform are immediately applied on non-authoritative instances, which can result in visual jitter and/or objects jumping to newly applied state updates when latency is high. Changes to the __Interpolation__ property during runtime on the authoritative instance will be synchronized with all non-authoritative instances. Of course, you can increase the network tick to a higher value in order to get more samples per second, but that still will not yield an over-all smooth motion on the non-authoritative instances and will only consume more bandwidth. @@ -198,7 +198,7 @@ All interpolation types provide you with the ability to enable or disable lerp s ##### Slerp position - + When this property and __Interpolation__ are both set, non-authoritative instances will [slerp](https://docs.unity3d.com/ScriptReference/Vector3.Slerp.html) towards their destination position rather than [lerping](https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html). Slerping is typically used when your object is following a circular and/or spline-based motion path and you want to preserve the curvature of that path. Since lerping between two points yields a linear progression over a line between two points, there can be scenarios where the frequency of delta position state updates could yield a loss in the curvature of an object's motion. @@ -277,7 +277,7 @@ With quaternion synchronization enabled, the authoritative instance still compar Quaternion synchronization comes with a price, however. It increases the bandwidth cost, 16 bytes per instance, in exchange for handling the more complex rotation issues that often occur when using nested NetworkTransform (one or more parent transforms with one or more child transforms). However, when you enable the __Use Quaternion Synchronization__ property you will notice a change in both the __Syncing__ axis selection check boxes and a new __Use Quaternion Compression__ property will appear: - + :::note The rotation synchronization axis checkboxes are no longer available when __Use Quaternion Synchronization__ is enabled (since synchronizing the quaternion of a transform always updates all rotation axes) and __Use Quaternion Compression__ becomes a visible option. diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/foundationalcomponents.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/foundationalcomponents.md new file mode 100644 index 0000000000..09b8f52274 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/foundational/foundationalcomponents.md @@ -0,0 +1,34 @@ +# Foundational Components + +While there are many classes within the Netcode for GameObjects SDK, there are really only three foundational components: + +* **NetworkObject:** This component declares a prefab or in-scene placed object as "networked object" that can have states synchronized between clients and/or a server. A `NetworkObject` component provides the means to identify each uniquely spawned instance (dynamically or in-scene placed). _You cannot derive from the `NetworkObject` component class_. + +* **NetworkBehaviour:** This is the fundamental "netcode" scripting component that provides the ability to synchronize state and write netcode script(s). _You derive from this class to create your own netcode component scripts_. + +* **NetworkManager:** This component is the over-all network session configuration and session management component. The `NetworkManager` component is required in order to start or join a network session. + + +## NetworkObject + +| **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[NetworkObject](networkobject.md)** | A NetworkObject is a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) with a NetworkObject component and at least one [NetworkBehaviour](networkbehaviour.md) component, which enables the GameObject to respond to and interact with netcode. | +| **[NetworkObject parenting](../../advanced-topics/networkobject-parenting.md)** | Understand how NetworkObjects are parented in Netcode for GameObjects. | + + +## NetworkBehaviour + + +| **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[NetworkBehaviour](networkbehaviour.md)** | [`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](networkobject.md) component and at least one `NetworkBehaviour` component. | +| **[Synchronizing](networkbehaviour-synchronize.md)** | Understanding a `NetworkBehaviour` component's order of operations when it comes to spawning, de-spawning, and adding custom synchronization data. | + + +## NetworkManager + +| **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[NetworkManager](networkmanager.md)**| The `NetworkManager` is a required Netcode for GameObjects component that has all of your project's netcode-related settings. Think of it as the central netcode hub for your netcode-enabled project. | +| **[Player prefabs and spawning players](playerobjects.md)**| Learn about spawning player objects and creating player prefabs.| \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/networkbehaviour-synchronize.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md similarity index 98% rename from com.unity.netcode.gameobjects/Documentation~/basics/networkbehaviour-synchronize.md rename to com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md index 8a34cc0286..92fc4f68e2 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/networkbehaviour-synchronize.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md @@ -45,7 +45,7 @@ There are two cases where NetworkObject synchronization occurs: - that is, Full synchronization of the NetworkObjects and scenes. > [!NOTE] -> If you aren't familiar with the [`INetworkSerializable` interface](../advanced-topics/serialization/inetworkserializable.md), then you might read up on that before proceeding, because `NetworkBehaviour.OnSynchronize` follows a similar usage pattern. +> If you aren't familiar with the [`INetworkSerializable` interface](../../advanced-topics/serialization/inetworkserializable.md), then you might read up on that before proceeding, because `NetworkBehaviour.OnSynchronize` follows a similar usage pattern. #### Order of operations when dynamically spawning diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/networkbehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour.md similarity index 100% rename from com.unity.netcode.gameobjects/Documentation~/basics/networkbehaviour.md rename to com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour.md diff --git a/com.unity.netcode.gameobjects/Documentation~/components/networkmanager.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkmanager.md similarity index 90% rename from com.unity.netcode.gameobjects/Documentation~/components/networkmanager.md rename to com.unity.netcode.gameobjects/Documentation~/components/foundational/networkmanager.md index 74e3720b7a..a67560fc21 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/networkmanager.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkmanager.md @@ -5,13 +5,13 @@ The `NetworkManager` is a required Netcode for GameObjects component that has al ## `NetworkManager` Inspector properties - **LogLevel**: Sets the network logging level -- **PlayerPrefab**: When a Prefab is assigned, the Prefab will be instantiated as the player object and assigned to the newly connected and authorized client. For more information about player prefabs, refer to [Player NetworkObjects](../basics/networkobject.md#player-networkobjects). +- **PlayerPrefab**: When a Prefab is assigned, the Prefab will be instantiated as the player object and assigned to the newly connected and authorized client. For more information about player prefabs, refer to [Player NetworkObjects](networkobject.md#player-networkobjects). - **NetworkPrefabs**: Where you register your network prefabs. You can also create a single network Prefab override per registered network Prefab here. - **Protocol Version**: Set this value to help distinguish between builds when the most current build has new assets that can cause issues with older builds connecting. - **Network Transport**: Where your network specific settings and transport type is set. This field accepts any INetworkTransport implementation. However, unless you have unique transport specific needs UnityTransport is the recommended transport to use with Netcode for GameObjects. - **Tick Rate**: This value controls the network tick update rate. - **Ensure Network Variable Length Safety**: (Increases cpu processing and bandwidth) When this property is checked, Netcode for GameObjects will prevent user code from writing past the boundaries of a NetworkVariable. -- **Connection Approval**: This enables [connection approval](../basics/connection-approval.md) when this is checked and the `NetworkManager.ConnectionApprovalCallback` is assigned. +- **Connection Approval**: This enables [connection approval](../../basics/connection-approval.md) when this is checked and the `NetworkManager.ConnectionApprovalCallback` is assigned. - **Client Connection Buffer Timeout**: This value sets the amount of time that has to pass for a connecting client to complete the connection approval process. If the time specified is exceeded the connecting client will be disconnected. - **Force Same Prefabs**: When checked it will always verify that connecting clients have the same registered network prefabs as the server. When not checked, Netcode for GameObjects will ignore any differences. - **Recycle Network Ids**: When checked this will re-use previously assigned `NetworkObject.NetworkObjectIds` after the specified period of time. @@ -25,12 +25,12 @@ The `NetworkManager` is a required Netcode for GameObjects component that has al > [!NOTE] > All `NetworkManager` sub-systems are instantiated once the `NetworkManager` is started (that is, `NetworkManager.IsListening == true`). A good general "rule of thumb" is to not attempt to access the below sub-systems before starting the `NetworkManager`, otherwise they won't yet be initialized. -- [NetworkManager.PrefabHandler](../advanced-topics/object-pooling.md): This provides access to the NetworkPrefabHandler that is used for NetworkObject pools and to have more control overriding network prefabs. -- [NetworkManager.SceneManager](../basics/scenemanagement/using-networkscenemanager.md): When scene management is enabled, this is used to load and unload scenes, register for scene events, and other scene management related actions. -- [NetworkManager.SpawnManager](../basics/object-spawning.md): This handles NetworkObject spawn related functionality. -- [NetworkManager.NetworkTimeSystem](../advanced-topics/networktime-ticks.md): a synchronized time that can be used to handle issues with latency between a client and the server. -- [NetworkManager.NetworkTickSystem](../advanced-topics/networktime-ticks.md#network-ticks): Use this to adjust the frequency of when NetworkVariables are updated. -- [NetworkManager.CustomMessagingManager](../advanced-topics/message-system/custom-messages.md): Use this system to create and send custom messages. +- [NetworkManager.PrefabHandler](../../advanced-topics/object-pooling.md): This provides access to the NetworkPrefabHandler that is used for NetworkObject pools and to have more control overriding network prefabs. +- [NetworkManager.SceneManager](../../basics/scenemanagement/using-networkscenemanager.md): When scene management is enabled, this is used to load and unload scenes, register for scene events, and other scene management related actions. +- [NetworkManager.SpawnManager](../../basics/object-spawning.md): This handles NetworkObject spawn related functionality. +- [NetworkManager.NetworkTimeSystem](../../advanced-topics/networktime-ticks.md): a synchronized time that can be used to handle issues with latency between a client and the server. +- [NetworkManager.NetworkTickSystem](../../advanced-topics/networktime-ticks.md#network-ticks): Use this to adjust the frequency of when NetworkVariables are updated. +- [NetworkManager.CustomMessagingManager](../../advanced-topics/message-system/custom-messages.md): Use this system to create and send custom messages. ## Starting a server, host, or client @@ -47,8 +47,8 @@ NetworkManager.Singleton.StartClient(); // Starts the NetworkManager as jus When starting a Server or joining an already started session as client, the `NetworkManager` can spawn a "Player Object" belonging to the client. For more information about player prefabs, refer to: - - [NetworkObject Player Prefab Documentation](../basics/networkobject.md) - - [Connection Approval](../basics/connection-approval) + - [NetworkObject Player Prefab Documentation](networkobject.md) + - [Connection Approval](../../basics/connection-approval) ## Connecting @@ -81,7 +81,7 @@ It is possible to access the current connection data at runtime, via `NetworkMan If you are using Unity Relay to handle connections, however, **don't use `SetConnectionData`**. The host should call [`SetHostRelayData`](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/11922a0bc100a1615c541aa7298c47d253b74937/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs#L575), and clients should call [`SetClientRelayData`](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/11922a0bc100a1615c541aa7298c47d253b74937/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs#L588). Attempting to join a **Relay**-hosted game via entering IP/port number (via `SetConnectionData`) **won't work**. -[More information about Netcode for GameObjects Transports](../advanced-topics/transports.md) +[More information about Netcode for GameObjects Transports](../../advanced-topics/transports.md) ## Disconnecting and shutting down diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/networkobject.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkobject.md similarity index 86% rename from com.unity.netcode.gameobjects/Documentation~/basics/networkobject.md rename to com.unity.netcode.gameobjects/Documentation~/components/foundational/networkobject.md index 4cbfb18423..5ea762d5af 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/networkobject.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkobject.md @@ -1,8 +1,8 @@ # NetworkObject -A NetworkObject is a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) with a NetworkObject component and at least one [NetworkBehaviour](networkbehaviour.md) component, which enables the GameObject to respond to and interact with netcode. NetworkObjects are session-mode agnostic and used in both [client-server](../terms-concepts/client-server.md) and [distributed authority](../terms-concepts/distributed-authority.md) contexts. +A NetworkObject is a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) with a NetworkObject component and at least one [NetworkBehaviour](networkbehaviour.md) component, which enables the GameObject to respond to and interact with netcode. NetworkObjects are session-mode agnostic and used in both [client-server](../terms-concepts/client-server.md) and [distributed authority](../../terms-concepts/distributed-authority.md) contexts. -Netcode for GameObjects' high level components, [the RPC system](../advanced-topics/messaging-system.md), [object spawning](object-spawning), and [`NetworkVariable`](networkvariable.md)s all rely on there being at least two Netcode components added to a GameObject: +Netcode for GameObjects' high level components, [the RPC system](../../advanced-topics/messaging-system.md), [object spawning](../../basics/object-spawning.md), and [`NetworkVariable`](../../basics/networkvariable.md)s all rely on there being at least two Netcode components added to a GameObject: 1. NetworkObject 2. [`NetworkBehaviour`](networkbehaviour.md) @@ -31,7 +31,7 @@ You can avoid execution order issues in any NetworkBehaviour component scripts t ## Ownership -Either the server (default) or any connected and approved client owns each NetworkObject. By default, Netcode for GameObjects is [server-authoritative](../terms-concepts/client-server.md), which means only the server can spawn and despawn NetworkObjects, but you can also build [distributed authority](../terms-concepts/distributed-authority.md) games where clients have the authority to spawn and despawn NetworkObjects as well. +Either the server (default) or any connected and approved client owns each NetworkObject. By default, Netcode for GameObjects is [server-authoritative](../../terms-concepts/client-server.md), which means only the server can spawn and despawn NetworkObjects, but you can also build [distributed authority](../../terms-concepts/distributed-authority.md) games where clients have the authority to spawn and despawn NetworkObjects as well. If you're creating a client-server game and you want a client to control more than one NetworkObject, use the following ownership methods. @@ -77,30 +77,30 @@ However, when you want to limit which prefabs are available (for example, to red ## Spawning with (or without) observers - + The `NetworkObject.SpawnWithObservers` property (default is true) enables you to spawn a NetworkObject with no initial observers. This is the recommended alternative to using `NetworkObject.CheckObjectVisibility` when you just want it to be applied globally to all clients (only when spawning an instance of the NetworkObject in question). If you want more precise per-client control then `NetworkObject.CheckObjectVisibility` is recommended. `NetworkObject.SpawnWithObservers` is only applied upon the initial server-side spawning and once spawned it has no impact on object visibility. ## Transform synchronization - + -There are times when you want to use a NetworkObject for something that doesn't require the synchronization of its transform. You might have an [in-scene placed NetworkObject](./scenemanagement/inscene-placed-networkobjects.md) that's only used to manage game state and it doesn't make sense to incur the initial client synchronization cost of synchronizing its transform. To prevent a NetworkObject from initially synchronizing its transform when spawned, deselect the **Synchronize Transform** property. This property is enabled by default. +There are times when you want to use a NetworkObject for something that doesn't require the synchronization of its transform. You might have an [in-scene placed NetworkObject](../../basics/scenemanagement/inscene-placed-networkobjects.md) that's only used to manage game state and it doesn't make sense to incur the initial client synchronization cost of synchronizing its transform. To prevent a NetworkObject from initially synchronizing its transform when spawned, deselect the **Synchronize Transform** property. This property is enabled by default. > [!NOTE] > If you're planning to use a NetworkTransform, then you always want to make sure the **Synchronize Transform** property is enabled. ## Active scene synchronization - + When a GameObject is instantiated, it gets instantiated in the current active scene. However, sometimes you might find that you want to change the currently active scene and would like specific NetworkObject instances to automatically migrate to the newly assigned active scene. While you could keep a list or table of the NetworkObject instances and write the code/logic to migrate them into a newly assigned active scene, this can be time consuming and become complicated depending on project size and complexity. The alternate and recommended way to handle this is by enabling the **Active Scene Synchronization** property of each NetworkObject you want to automatically migrate into any newly assigned scene. This property defaults to disabled. -Refer to the [NetworkSceneManager active scene synchronization](scenemanagement/using-networkscenemanager.md#active-scene-synchronization) page for more details. +Refer to the [NetworkSceneManager active scene synchronization](../../basics/scenemanagement/using-networkscenemanager.md#active-scene-synchronization) page for more details. ## Scene migration synchronization - + Similar to [`NetworkObject.ActiveSceneSynchronization`](#active-scene-synchronization), [`NetworkObject.SceneMigrationSynchronization`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObject.html#Unity_Netcode_NetworkObject_SceneMigrationSynchronization) automatically synchronizes client-side NetworkObject instances that are migrated to a scene via [`SceneManager.MoveGameObjectToScene`](https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.MoveGameObjectToScene.html) on the host or server side. This can be useful if you have a specific scene you wish to migrate NetworkObject instances to that is not the currently active scene. @@ -113,4 +113,4 @@ Scene migration synchronization is enabled by default. For NetworkObjects that d ## Additional resources - [NetworkBehaviour](networkbehaviour.md) -- [NetworkVariable](networkvariable.md) +- [NetworkVariable](../../basics/networkvariable.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/playerobjects.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/playerobjects.md similarity index 77% rename from com.unity.netcode.gameobjects/Documentation~/basics/playerobjects.md rename to com.unity.netcode.gameobjects/Documentation~/components/foundational/playerobjects.md index 9ad3260a70..6650aee374 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/playerobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/foundational/playerobjects.md @@ -35,7 +35,7 @@ public override void OnNetworkDespawn() ## Session-mode agnostic methods -Netcode for GameObjects can spawn a default PlayerObject for you. If you enable **Create Player Prefab** in the [NetworkManager](../components/networkmanager.md) and assign a valid prefab, then Netcode for GameObjects spawns a unique instance of the designated player prefab for each connected and approved client, referred to as the PlayerObject. +Netcode for GameObjects can spawn a default PlayerObject for you. If you enable **Create Player Prefab** in the [NetworkManager](networkmanager.md) and assign a valid prefab, then Netcode for GameObjects spawns a unique instance of the designated player prefab for each connected and approved client, referred to as the PlayerObject. To manually spawn an object as PlayerObject, use the following method: @@ -45,19 +45,19 @@ GetComponent<NetworkObject>().SpawnAsPlayerObject(clientId); If the player already had a prefab instance assigned, then the client owns the NetworkObject of that prefab instance unless there's additional server-side specific user code that removes or changes the ownership. -Alternatively, you can choose not to spawn anything immediately after a client connects and instead use a [NetworkBehaviour component](networkbehaviour.md) to handle avatar/initial player prefab selection. This NetworkBehaviour component could be configured by the server or initiating session owner, or be associated with an [in-scene](scenemanagement/inscene-placed-networkobjects.md) or [dynamically spawned](object-spawning.md#dynamically-spawned-network-prefabs) NetworkObject, as suits the needs of your project. +Alternatively, you can choose not to spawn anything immediately after a client connects and instead use a [NetworkBehaviour component](networkbehaviour.md) to handle avatar/initial player prefab selection. This NetworkBehaviour component could be configured by the server or initiating session owner, or be associated with an [in-scene](../../basics/scenemanagement/inscene-placed-networkobjects.md) or [dynamically spawned](../../basics/object-spawning.md#dynamically-spawned-network-prefabs) NetworkObject, as suits the needs of your project. ### Client-server contexts only -In addition to the [session-mode agnostic spawning methods](#session-mode-agnostic-methods) above, you can assign a unique player prefab on a per-client connection basis using a client [connection approval process](connection-approval.md) when in [client-server contexts](../terms-concepts/client-server.md). +In addition to the [session-mode agnostic spawning methods](#session-mode-agnostic-methods) above, you can assign a unique player prefab on a per-client connection basis using a client [connection approval process](../../basics/connection-approval.md) when in [client-server contexts](../../terms-concepts/client-server.md). ### Distributed authority contexts only -In addition to the [session-mode agnostic spawning methods](#session-mode-agnostic-methods) above, you can use the [`OnFetchLocalPlayerPrefabToSpawn`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkManager.html#Unity_Netcode_NetworkManager_OnFetchLocalPlayerPrefabToSpawn) method to assign a unique player prefab on a per-client basis when in [distributed authority contexts](../terms-concepts/distributed-authority.md). +In addition to the [session-mode agnostic spawning methods](#session-mode-agnostic-methods) above, you can use the [`OnFetchLocalPlayerPrefabToSpawn`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkManager.html#Unity_Netcode_NetworkManager_OnFetchLocalPlayerPrefabToSpawn) method to assign a unique player prefab on a per-client basis when in [distributed authority contexts](../../terms-concepts/distributed-authority.md). -To use `OnFetchLocalPlayerPrefabToSpawn` in your project, assign a callback handler to `OnFetchLocalPlayerPrefabToSpawn` and whatever the client script returns is what will be spawned for that client. Ensure that the prefab being spawned is in a NetworkPrefabList [registered with the NetworkManager](object-spawning.md#registering-a-network-prefab). +To use `OnFetchLocalPlayerPrefabToSpawn` in your project, assign a callback handler to `OnFetchLocalPlayerPrefabToSpawn` and whatever the client script returns is what will be spawned for that client. Ensure that the prefab being spawned is in a NetworkPrefabList [registered with the NetworkManager](../../basics/object-spawning.md#registering-a-network-prefab). -If you don't assign a callback handler to `OnFetchLocalPlayerPrefabToSpawn`, then the default behaviour is to return the `NetworkConfig.PlayerPrefab` (or null if neither are set). +If you don't assign a callback handler to `OnFetchLocalPlayerPrefabToSpawn`, then the default behavior is to return the `NetworkConfig.PlayerPrefab` (or null if neither are set). :::note `AutoSpawnPlayerPrefabClientSide` required For `OnFetchLocalPlayerPrefabToSpawn` to work, [`AutoSpawnPlayerPrefabClientSide`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkManager.html#Unity_Netcode_NetworkManager_AutoSpawnPlayerPrefabClientSide) must be enabled. @@ -65,7 +65,7 @@ For `OnFetchLocalPlayerPrefabToSpawn` to work, [`AutoSpawnPlayerPrefabClientSide ## PlayerObject spawning timeline -When using automatic methods of PlayerObject spawning (such as enabling **Create Player Prefab** in the [NetworkManager](../components/networkmanager.md)), PlayerObjects are spawned at different times depending on whether you have [scene management](scenemanagement/scene-management-overview.md) enabled. +When using automatic methods of PlayerObject spawning (such as enabling **Create Player Prefab** in the [NetworkManager](networkmanager.md)), PlayerObjects are spawned at different times depending on whether you have [scene management](../../basics/scenemanagement/scene-management-overview.md) enabled. - When scene management is disabled, PlayerObjects are spawned when a joining client's connection is approved. - When scene management is enabled, PlayerObjects are spawned when a joining client finishes initial synchronization. @@ -93,7 +93,7 @@ To find your own player object just pass `NetworkManager.Singleton.LocalClientId ## Additional resources - [NetworkObject](networkobject.md) -- [NetworkManager](../components/networkmanager.md) -- [Distributed authority topologies](../terms-concepts/distributed-authority.md) -- [Client-server topologies](../terms-concepts/client-server.md) -- [Object spawning](object-spawning.md) +- [NetworkManager](networkmanager.md) +- [Distributed authority topologies](../../terms-concepts/distributed-authority.md) +- [Client-server topologies](../../terms-concepts/client-server.md) +- [Object spawning](../../basics/object-spawning.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableBehaviour_InspectorView-1.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableBehaviour_InspectorView-1.png new file mode 100644 index 0000000000000000000000000000000000000000..9868301c1257b2713fbd7303c2c4b2beec5472b6 GIT binary patch literal 23713 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 literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-1.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-1.png new file mode 100644 index 0000000000000000000000000000000000000000..569fecc721f568644783375d06781630104cf953 GIT binary patch literal 70586 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.p@biM`h?O~31~)d9WTd@%QG;ePeh68k&% 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 literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-2.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-2.png new file mode 100644 index 0000000000000000000000000000000000000000..05a428e82d47dca1de7a1f9310a9599743732cfa GIT binary patch literal 69939 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%0A@^m;+`oV!y5^yNLIGXGw zx^D~&*G))qh+(E>5^6+>>Xx9n(07@a<9GK2Z?O{MzknFw^I)H5MK1~S2`3*&555$J zh*Ibkp?gsm`{v_T6)+7p2DsD`!3pf3&-lo18zXV~%X;AS?Cbmc5#U}DL-@3_Cvw#; z=C>&@+6U^k<zrH5eD(7#;1i-QN!#)KX1r4!^XH$#QSG!ku@>*D1M`!Voq0!HGIp({ z&suRb(#s$w=zu^5G`efR{A5i|K`k6$+`}MKa!vi!I>Bos?fo6V*GdI?iLoq7a!z?* zk`e~PCHed=jQDdQH};k@A>Nzx`;iBZPnypXV=U&|ed{(yvvj{_YGRmxw*;eKj8Do2 zVzCP|?MlCnczyzy!5^6Xf<Khc5QZ}B1CL&+o$Q?$#Ar0(@)+tbLDHu~4CA>&g|B@< zR>G@uV4}ke9=>RCo^R(VaB~8QiC4|F#L(rRXPqg&Redy{UOqjKS{3sMxV4J8QUN9v zHt>e4PzJ$1x?cW0_m?<>Y7;|1<#4FQ1TaevUE%RO%sLVZxD(Cdpz}{6)U<42P}a$I zW!~g+Y;D>ZH8&zWB~A@Dv1cddn2-H2{K8}J+HAr)k@3)&=SAdr>C_64#cIEhHU}XF za6N!cIDy{R9sr!WVURB_);sF{NY23mdB|&Xq}s$-#XG?NSQuv2XT^GgG4=*du5AI( z5+H~^HYCv79xZ<>f1e9#%x2{J>&e|V(2i|OBdTamBi;AG^&ZHR04La5?G}*yoOYm? zFX5|BmFlteJ<9_<nUob9SuUGgVLcqwNz<wjJ}ETZr%CjRJ#X|sgH<C~4LSnx2EH$0 zWAOHN2Y!PIYdr*Hzi=5h3VH)bpVjaMpnt>Nd&>QMK;lCl9B5u6(z%8Ldm(tX>+RL2 zv`6?x=M2njTtYvtb=)4N0?QjGnYv<D0)mW6r`_o?c#?7It`JBF^#!)@d1B!IpwI*} zRgHjOzhKr|8OE;e<?J!VrFl>DpMP2#KMVeaXo)P|2?P`6P9ldq@Z&L*+KiZuk%$v? zM*|IT^BDVTup&0$wkNRByytWEJU$?CtyHf<XS&W_lTEugJnpHO4j>_KbC!V@fCA$6 zdEgG%EUN&|mFb^-dI0&Mp|cKX2NxGhARUzt@b3{l^*k}paz+-GoEv276x)yt@u0hA z6Sr=Z3PZAX0KA{wcZ`4D`Ww&|P<Syvm(2af9L;F??;zRhn>cZ)l`W*Prmssu0Yw=T zpEv5h)}3B|el~Ci!k)6W*wi6Kawv{~N1OE(TnyJVV?g^FeFQqNQO$+@d*XNm*26Kv zgQ9r`jM<&cbe}te2H_M4*>dK<XQt>issYjjZXmG;5v*VNzu)z{d<I%a@F6BwEnrs# g{+eno?dc;z?ZW|x^xkxQ0)D(ue5p_^XA%1U0b{KY82|tP literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-3.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-3.png new file mode 100644 index 0000000000000000000000000000000000000000..6c33acdd637dd1b8aa5eaa3424148d995f63caf1 GIT binary patch literal 85502 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 literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableNode_InspectorView-1.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableNode_InspectorView-1.png new file mode 100644 index 0000000000000000000000000000000000000000..afb060db66b0aed93a9e7387e51f1c66adcddba7 GIT binary patch literal 8167 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 E0ZO6TTL1t6 literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/ComponentController_InspectorView-1.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/ComponentController_InspectorView-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f425e98ccd75cff966d7a405e5de8dc3b5586401 GIT binary patch literal 43859 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 A{r~^~ literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/PlayerAndWorldItem-1.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/PlayerAndWorldItem-1.png new file mode 100644 index 0000000000000000000000000000000000000000..841dde1df932299e9044fe8eb9cf48afb651a3fa GIT binary patch literal 7087 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 H3)uev37wAO literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/PlayerAndWorldItem-2.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/PlayerAndWorldItem-2.png new file mode 100644 index 0000000000000000000000000000000000000000..97208a05d75c4878959f8e82ada479b2e8fc6ac6 GIT binary patch literal 8959 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 Z`F~@U+!4^bp1A-3002ovPDHLkV1g@x40Heh literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/SpawnObjectA-B-2.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/SpawnObjectA-B-2.png new file mode 100644 index 0000000000000000000000000000000000000000..e7a2ca676275accbc4536e89d9dfcf2b565e29e1 GIT binary patch literal 4244 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 literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/SpawnObjectA-B.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/SpawnObjectA-B.png new file mode 100644 index 0000000000000000000000000000000000000000..474da9fd4ad413aaf47b4da996904d2045207a50 GIT binary patch literal 2763 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 RC~E)!002ovPDHLkV1gUoDwqHO literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/WorldItem-Inspector-View-1.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/WorldItem-Inspector-View-1.png new file mode 100644 index 0000000000000000000000000000000000000000..487bc16479a130472c327ad76b3a33c4472af629 GIT binary patch literal 72855 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�CHtC)F6XSPH*3nFM1o|=h5&Z|*o0lJr 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 GqW>?j!f{>z literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/images/attachable/WorldItem-Inspector-View-2.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/WorldItem-Inspector-View-2.png new file mode 100644 index 0000000000000000000000000000000000000000..b16d7c9091a6e0564655fc4dbcedc532ce88a05b GIT binary patch literal 41631 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 b%J$XF=HQ)rw^j6xfScYGW9=$U$C&>Bh&egE literal 0 HcmV?d00001 diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/clientside-interpolation.md b/com.unity.netcode.gameobjects/Documentation~/learn/clientside-interpolation.md index 7781c0657e..d65c358e4d 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/clientside-interpolation.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/clientside-interpolation.md @@ -15,7 +15,7 @@ In addition to reduced responsiveness, this approach can also directly interfere ## With client-side interpolation -When using client-side interpolation, all clients intentionally run slightly behind the server, giving them time to transition smoothly between state updates and conceal the effects of latency from users. To implement client-side interpolation, use the [NetworkTransform](../components/networktransform.md) component. +When using client-side interpolation, all clients intentionally run slightly behind the server, giving them time to transition smoothly between state updates and conceal the effects of latency from users. To implement client-side interpolation, use the [NetworkTransform](../components/helpers/networktransform.md) component. In a standard client-server [topology](../terms-concepts/network-topologies.md), clients are able to render a state that's approximately half the [round-trip time (RTT)](lagandpacketloss.md#round-trip-time-and-pings) behind the server. When using client-side interpolation, a further intentional delay is added so that by the time the client is rendering state _n_, it's already received state _n+1_, which allows the client to always smoothly transition from one state to another. This is effectively increasing latency, but in a consistent, client-universal way that can be used to reduce the negative impacts of unpredictable network jitter on gameplay. diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md b/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md index 6d6c95c9fa..9089eaf2a6 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md @@ -30,7 +30,7 @@ In summary, you want accuracy, security, and responsiveness for your game. The authority is who has the right to make final gameplay decisions over objects. A server authoritative game has all its final gameplay decisions executed by the server. - + #### Good for world consistency @@ -58,7 +58,7 @@ An issue with server authority is you're waiting for your server to tell you to In a client authoritative game using Netcode for GameObjects, you still have a server that's used to share world state, but clients will own and impose their reality. - + #### Good for reactivity @@ -157,7 +157,7 @@ To do continuous client driven actions, there's a few more considerations to tak - You then need to make sure you don't send RPCs to the server (containing your authoritative state) when no data has changed and do dirty checks. - You'd need to send it on tick or at worst on FixedUpdate. Sending on Update() would spam your connection. -A sample for a [ClientNetworkTransform](../components/networktransform.md#clientnetworktransform) has been created, so you don't have to reimplement this yourself for transform updates. A [sample](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/tree/main/Basic/ClientDriven) has been created on how to use it. See [movement script](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/ClientDriven/Assets/Scripts/ClientPlayerMove.cs). +A sample for a [ClientNetworkTransform](../components/helpers/networktransform.md#clientnetworktransform) has been created, so you don't have to reimplement this yourself for transform updates. A [sample](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/tree/main/Basic/ClientDriven) has been created on how to use it. See [movement script](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/ClientDriven/Assets/Scripts/ClientPlayerMove.cs). > [!NOTE] > A rule of thumb here is to ask yourself: "Can the server correct me on this?". If it can, use server authority. @@ -184,7 +184,7 @@ Extrapolation is an attempt to estimate a future game state, without taking into The client will normally assume that a moving object will continue in the same direction. When a new packet is received, the position may be updated. -For Netcode for gameobjects (Netcode), a basic extrapolation implementation has been provided in [`NetworkTransform`](../components/networktransform.md) and is estimated between the time a tick advances in server-side animation and the update of the frame on the client-side. The game object extrapolates the next frame's values based on the ratio. +For Netcode for gameobjects (Netcode), a basic extrapolation implementation has been provided in [`NetworkTransform`](../components/helpers/networktransform.md) and is estimated between the time a tick advances in server-side animation and the update of the frame on the client-side. The game object extrapolates the next frame's values based on the ratio. > [!NOTE] > While Netcode for GameObjects doesn't have a full implementation of client-side prediction and reconciliation, you can build such a system on top of the existing client-side anticipation building-blocks, `AnticipatedNetworkVariable` and `AnticipatedNetworkTransform`. These components allow differentiating between the "authoritative" value and the value that is shown to the players. These components provide most of the information needed to implement prediction, but do require you to implement certain aspects yourself. Because of the complexity inherent in building a full client prediction system, the details of that are left for users, and we recommend only advanced users pursue this option. diff --git a/com.unity.netcode.gameobjects/Documentation~/network-components.md b/com.unity.netcode.gameobjects/Documentation~/network-components.md index 272f6ea8b3..dc93f61356 100644 --- a/com.unity.netcode.gameobjects/Documentation~/network-components.md +++ b/com.unity.netcode.gameobjects/Documentation~/network-components.md @@ -4,10 +4,5 @@ Understand the network components involved in a Netcode for GameObjects project. | **Topic** | **Description** | | :------------------------------ | :------------------------------- | -| **[NetworkObject](basics/networkobject.md)** | A NetworkObject is a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) with a NetworkObject component and at least one [NetworkBehaviour](basics/networkbehaviour.md) component, which enables the GameObject to respond to and interact with netcode. | -| **[NetworkObject parenting](advanced-topics/networkobject-parenting.md)** | Understand how NetworkObjects are parented in Netcode for GameObjects. | -| **[NetworkBehaviour](networkbehaviour-landing.md)**| Understand how to use NetworkBehaviour components in your Netcode for GameObjects project. | -| **[Physics](advanced-topics/physics.md)**| Netcode for GameObjects has a built in approach which allows for server-authoritative physics where the physics simulation only runs on the server. | -| **[NetworkManager](components/networkmanager.md)**| The `NetworkManager` is a required Netcode for GameObjects component that has all of your project's netcode-related settings. Think of it as the central netcode hub for your netcode-enabled project. | -| **[NetworkTransform](components/networktransform.md)**| [NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](basics/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. | -| **[NetworkAnimator](components/networkanimator.md)**| The `NetworkAnimator` component provides you with a fundamental example of how to synchronize animations during a network session. Animation states are synchronized with players joining an existing network session and any client already connected before the animation state changing. | \ No newline at end of file +| **[Foundational Components](components/foundational/foundationalcomponents.md)** | Learn about the three foundational components: `NetworkObject`, `NetworkBehaviour`, and `NetworkManager`. | +| **[Helper Components](components/Helpers/helpercomponents.md)** | Helper components available to use in your Netcode for GameObjects project. | diff --git a/com.unity.netcode.gameobjects/Documentation~/networkbehaviour-landing.md b/com.unity.netcode.gameobjects/Documentation~/networkbehaviour-landing.md index bf8981fa11..b6432462cf 100644 --- a/com.unity.netcode.gameobjects/Documentation~/networkbehaviour-landing.md +++ b/com.unity.netcode.gameobjects/Documentation~/networkbehaviour-landing.md @@ -1,8 +1,9 @@ -# NetworkBehaviour +# NetworkBehaviours +`NetworkBehaviour` components are the 2nd Understand how to use NetworkBehaviour components in your project. | **Topic** | **Description** | | :------------------------------ | :------------------------------- | -| **[NetworkBehaviour](basics/networkbehaviour.md)** | [`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](basics/networkobject.md) component and at least one `NetworkBehaviour` component. | -| **[Synchronize](basics/networkbehaviour-synchronize.md)** | You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. | \ No newline at end of file +| **[NetworkBehaviour](components/foundational/networkbehaviour.md)** | [`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](components/foundational/networkobject.md) component and at least one `NetworkBehaviour` component. | +| **[Synchronize](components/foundational/networkbehaviour-synchronize.md)** | You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/samples.md b/com.unity.netcode.gameobjects/Documentation~/samples.md index b6a91e5e7d..d61850d480 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples.md @@ -4,5 +4,5 @@ Use the samples in this section to learn how to use Netcode for GameObjects in y | **Topic** | **Description** | | :------------------------------ | :------------------------------- | -| **[NetworkBehaviour](basics/networkbehaviour.md)** | [`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](basics/networkobject.md) component and at least one `NetworkBehaviour` component. | -| **[Synchronize](basics/networkbehaviour-synchronize.md)** | You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. | \ No newline at end of file +| **[NetworkBehaviour](components/foundational/networkbehaviour.md)** | [`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](components/foundational/networkobject.md) component and at least one `NetworkBehaviour` component. | +| **[Synchronize](components/foundational/networkbehaviour-synchronize.md)** | You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-clientdriven.md b/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-clientdriven.md index f31a3dc137..aa9445c02d 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-clientdriven.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-clientdriven.md @@ -6,7 +6,7 @@ Making movements feel responsive while staying consistent over multiple game exe ## TLDR: -- Use [ClientNetworkTransform](../../components/networktransform.md) to sync client authoritative transform updates to the server and other clients. +- Use [ClientNetworkTransform](../../components/helpers/networktransform.md) to sync client authoritative transform updates to the server and other clients. - Make sure ownership is set properly on that NetworkObject to be able to move it. - Since your clients live on different timelines (one per player), you have to be careful about who takes decisions and keep some of those decisions centralized on the server. - DON'T FORGET to test with latency, as your game will behave differently depending on whether client or server make decisions. diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-invaders.md b/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-invaders.md index c000aeac23..d4f6cb8d40 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-invaders.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-invaders.md @@ -86,7 +86,7 @@ https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blo ## Unconventional Networked Movement -Invaders has an easy movement type - moving only on one (horizontal) axis - which allows you to only modify the transform client-side without waiting for server-side validation. You can find where we perform the move logic in *[PlayerControl.cs](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/Invaders/Assets/Scripts/PlayerControl.cs)* in the `InGameUpdate` function . With the help of a [`NetworkTransform`](../../components/networktransform.md) that is attached directly to the Player Game Object, it will automatically sync up the Transform with the other clients. At the same time, it will smooth out the movement by interpolating or extrapolating for all of them. +Invaders has an easy movement type - moving only on one (horizontal) axis - which allows you to only modify the transform client-side without waiting for server-side validation. You can find where we perform the move logic in *[PlayerControl.cs](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/Invaders/Assets/Scripts/PlayerControl.cs)* in the `InGameUpdate` function . With the help of a [`NetworkTransform`](../../components/helpers/networktransform.md) that is attached directly to the Player Game Object, it will automatically sync up the Transform with the other clients. At the same time, it will smooth out the movement by interpolating or extrapolating for all of them. ```csharp reference https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/Invaders/Assets/Scripts/PlayerControl.cs#L176-L193 diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkobject-parenting.md b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkobject-parenting.md index b6c40567b1..5ab881e6c7 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkobject-parenting.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkobject-parenting.md @@ -17,4 +17,4 @@ To accommodate the limitation highlighted at the beginning of this document, Bos A special hand bone has been added to our Character's avatar. Upon a character's successful parenting attempt, this special bone is set as the `PickUpPot`'s `PositonConstraint` target. So while the `PickUpPot` is technically parented to a player, the `PositionConstraint` component allows the `PickUpPot` to follow a bone's position, presenting the **illusion** that the `PickUpPot` is parented to the player's hand bone itself. -Once the `PickUpPot` is parented, local space simulation is enabled on its [`NetworkTransform` component](../../components/networktransform.md). +Once the `PickUpPot` is parented, local space simulation is enabled on its [`NetworkTransform` component](../../components/helpers/networktransform.md). diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/optimizing-bossroom.md b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/optimizing-bossroom.md index d0dc30537c..2753803ce4 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/optimizing-bossroom.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/optimizing-bossroom.md @@ -37,7 +37,7 @@ For more examples, see [RPCs vs. NetworkVariables Examples](../../learn/rpcnetva ## NetworkTransform configuration {#networktransform-configuration} -The NetworkTransform component handles the synchronization of a NetworkObject's Transform. By default, the NetworkTransform component synchronizes every part of the transform at every tick if a change bigger than the specified [threshold](../../components/networktransform.md) occurs. However, you can configure it to only synchronize the necessary data by omitting particular axeis of the position, rotation, or scale vectors. See [Restricting synchronization](../../components/networktransform.md). +The NetworkTransform component handles the synchronization of a NetworkObject's Transform. By default, the NetworkTransform component synchronizes every part of the transform at every tick if a change bigger than the specified [threshold](../../components/helpers/networktransform.md) occurs. However, you can configure it to only synchronize the necessary data by omitting particular axeis of the position, rotation, or scale vectors. See [Restricting synchronization](../../components/helpers/networktransform.md). You can also increase the thresholds to reduce the frequency of updates if you don't mind reducing the accuracy and responsiveness of the replicated Transform. @@ -47,7 +47,7 @@ Since the characters evolve on a plane, we only synchronize their position's x a Additionally, with the changes introduced in [Netcode for GameObjects v1.4.0](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/releases/tag/ngo%2F1.4.0), we were able to further reduce the bandwidth cost associated for some prefabs that utilized NetworkTransform. The synchronization payload was reduced by 5 bytes for the Character and the Arrow prefab inside Boss Room, for example, by enabling "Use Half Float Precision" on their respective NetworkTransforms. -See [NetworkTransform](../../components/networktransform.md) for more information on the NetworkTransform component. +See [NetworkTransform](../../components/helpers/networktransform.md) for more information on the NetworkTransform component. ## Pooling {#pooling} @@ -103,7 +103,7 @@ As a result, the maximum number of reliable packets sent or received in a single ## NetworkManager tick rate configuration {#networkmanager-tick-rate-configuration} -Netcode's [NetworkManager](../../components/networkmanager.md) provides some configuration options, one of which is the [tick rate](../../advanced-topics/networktime-ticks.md). The tick rate configuration option determines the frequency at which network ticks occur. The ideal tick rate value relies on balancing smoothness, accuracy, and bandwidth usage. +Netcode's [NetworkManager](../../components/foundational/networkmanager.md) provides some configuration options, one of which is the [tick rate](../../advanced-topics/networktime-ticks.md). The tick rate configuration option determines the frequency at which network ticks occur. The ideal tick rate value relies on balancing smoothness, accuracy, and bandwidth usage. Lowering the tick rate reduces the frequency of NetworkVariable update messages (because they're sent at each tick). However, since it reduces the frequency of updates, it also reduces the smoothness of gameplay for the clients. You can reduce the impact of lower tick rates by using interpolation to provide smoothness, such as in the NetworkTransform. However, because there are fewer updates, the interpolation will be less accurate because it has less information. diff --git a/com.unity.netcode.gameobjects/Documentation~/tutorials/get-started-with-ngo.md b/com.unity.netcode.gameobjects/Documentation~/tutorials/get-started-with-ngo.md index 3510332f7f..d4b8e87da4 100644 --- a/com.unity.netcode.gameobjects/Documentation~/tutorials/get-started-with-ngo.md +++ b/com.unity.netcode.gameobjects/Documentation~/tutorials/get-started-with-ngo.md @@ -68,7 +68,7 @@ First, create the NetworkManager component: ### Create an object to spawn for each connected player > [!NOTE] -> When you drop the prefab into the **PlayerPrefab** slot, you're telling the library that when a client connects to the game, it automatically spawns this prefab as the character for the connecting client. Netcode for GameObjects won't spawn a player object if you don't have any prefab set as the **PlayerPrefab**. Refer to [Player Objects](../basics/networkobject.md#finding-playerobjects). +> When you drop the prefab into the **PlayerPrefab** slot, you're telling the library that when a client connects to the game, it automatically spawns this prefab as the character for the connecting client. Netcode for GameObjects won't spawn a player object if you don't have any prefab set as the **PlayerPrefab**. Refer to [Player Objects](../components/foundational/networkobject.md#finding-playerobjects). This section guides you through creating an object that spawns for each connected player. diff --git a/com.unity.netcode.gameobjects/Documentation~/tutorials/helloworld.md b/com.unity.netcode.gameobjects/Documentation~/tutorials/helloworld.md index 69289e04bf..1fb4582658 100644 --- a/com.unity.netcode.gameobjects/Documentation~/tutorials/helloworld.md +++ b/com.unity.netcode.gameobjects/Documentation~/tutorials/helloworld.md @@ -22,7 +22,7 @@ In this section we will create the basic building blocks of a multiplayer game. ### Creating Network Manager and selecting the Transport -In this section we add a Network Manager and add Unity Transport to our project. The [NetworkManager](../components/networkmanager.md) is the component that has all your project's netcode-related settings. Unity Transport is the transport layer that Netcode uses for communication between the server and the clients. See [here](../advanced-topics/transports.md) for more. +In this section we add a Network Manager and add Unity Transport to our project. The [NetworkManager](../components/foundational/networkmanager.md) is the component that has all your project's netcode-related settings. Unity Transport is the transport layer that Netcode uses for communication between the server and the clients. See [here](../advanced-topics/transports.md) for more. 1. Right-click in the **Hierarchy** tab of the main Unity Window. 1. Select **Create Empty**. diff --git a/com.unity.netcode.gameobjects/Documentation~/tutorials/testing/testing_with_artificial_conditions.md b/com.unity.netcode.gameobjects/Documentation~/tutorials/testing/testing_with_artificial_conditions.md index 019f6e7b84..d5972ebe87 100644 --- a/com.unity.netcode.gameobjects/Documentation~/tutorials/testing/testing_with_artificial_conditions.md +++ b/com.unity.netcode.gameobjects/Documentation~/tutorials/testing/testing_with_artificial_conditions.md @@ -133,7 +133,7 @@ Apple's Network Link Conditioner can be downloaded from the [Additional Tools fo Download the version that's appropriate for your XCode version and then run the .dmg file. Navigate to the `Hardware` folder and install the Network Link Conditioner panel. After that you will be able to find Network Link Conditioner in the System Preferences panel of your Mac: - + To test the builds with Network Link Conditioner: - Run Network Link Conditioner From cc04caf60b918407d274c3f7fcc5bf9ba53aa799 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 00:53:54 -0500 Subject: [PATCH 30/43] docs (style-PVP) Removing white space at the end of lines. --- .../Documentation~/TableOfContents.md | 4 ++-- .../components/Helpers/attachablebehaviour.md | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md index 9759fa90a7..f9c0d49b26 100644 --- a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md +++ b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md @@ -18,10 +18,10 @@ * [Max players](basics/maxnumberplayers.md) * [Transports](advanced-topics/transports.md) * [Relay](relay/relay.md) -* [Network components](network-components.md) +* [Network components](network-components.md) * [Foundational Components](components/foundational/foundationalcomponents.md) * [NetworkObject](components/foundational/networkobject.md) - * [NetworkObject parenting](advanced-topics/networkobject-parenting.md) + * [NetworkObject parenting](advanced-topics/networkobject-parenting.md) * [NetworkBehaviour](components/foundational/networkbehaviour.md) * [Synchronizing & Order of Operations](components/foundational/networkbehaviour-synchronize.md) * [NetworkManager](components/foundational/networkmanager.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md index 2bbc29c4a5..794aced829 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md @@ -27,7 +27,7 @@ With attaching, a user would create nested `GameObject` children that represent By placing an `AttachableBehaviour` component on the NestedChild-PickedUp `GameObject` and an `AttachableNode` component on the TargetNode, a user can then invoke the `AttachableBehaviour.Attach` method while passing in the `AttachableNode` component and the NestedChild-PickedUp `GameObject` will get parented under the TargetNode while also synchronizing this action with all other clients.<br />  -### AttachableBehaviour +### AttachableBehaviour  @@ -52,7 +52,7 @@ The simplest component in the bunch, this provides a valid connection point (_i.  -Taking the above example into consideration, it would make sense that a user would want to be able to easily control whether a specific component is enabled or disabled when something is attached or detached. +Taking the above example into consideration, it would make sense that a user would want to be able to easily control whether a specific component is enabled or disabled when something is attached or detached. As an example: @@ -65,7 +65,7 @@ The `ComponentController` provides this type of functionality: - Each assigned component entry can be configured to directly or inversely follow the `ComponentController`'s current state. - Each assigned component entry can have an enable and/or disable delay. - _When invoked internally by `AttachableBehaviour`, delays are ignored when an `AttachableNode` is being destroyed and the changes are immediate._ - + The `ComponentController` could be daisy chained with minimal user script: ```csharp /// <summary> @@ -111,7 +111,7 @@ For example purposes, we will walk through a common scenario where you might wan  -#### Player +#### Player The player prefab in the above diagram is not complete, includes the components of interest, and some additional children and components for example purposes. A complete diagram would most definitely have additional components and children. The `AttachableNode` components provide a "target attach point" that any other spawned network prefab with an `AttachableBehaviour` could attach itself to. @@ -151,16 +151,15 @@ We can see the `AttachableBehaviour`'s **Component Controllers** list contains `  -The above diagram represents what the **Player** and **World Item** spawned objects (_including cloned/non-authority instances_) would look like once the **Attached View** object has been parented under the avatar's **Right Attach** object. The green area and arrow represent the still existing relationship that the **Attached View** has with the **World Item**'s `NetworkObject`. +The above diagram represents what the **Player** and **World Item** spawned objects (_including cloned/non-authority instances_) would look like once the **Attached View** object has been parented under the avatar's **Right Attach** object. The green area and arrow represent the still existing relationship that the **Attached View** has with the **World Item**'s `NetworkObject`. :::info **AttachableBehaviour & NetworkObject Relationship** Upon a `NetworkObject` component being spawned, all associated `NetworkBehaviour` based component instances, that are directly attached to the `NetworkObject`'s `GameObject` or are on any child `GameObject`, will be registered with the `NetworkObject` instance. This remains true even when a child `GameObject` containing one or more `NetworkBehaviour` based component instances of a spawned `NetworkObject` is parented, during runtime, under another `GameObject` that is associated with a different spawned `NetworkObject`. Of course, there are additional considerations like: - What happens when one or both of the NetworkObjects is de-spawned? - - How do you assure the child attachable will return back to its default parent? + - How do you assure the child attachable will return back to its default parent? - and several other edge case scenarios... `AttachableBehaviour` leverages from this "spawn lifetime" relationship to provide another type of "parenting" (attaching) while also taking into consideration these types of edge case scenarios. -::: - +::: \ No newline at end of file From 8ad7a824540ec6753d69428c5aa5394a3fce60c1 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 01:06:53 -0500 Subject: [PATCH 31/43] docs(style-pvp) Removing trickier white spaces... --- .../Documentation~/TableOfContents.md | 12 ++++++------ .../components/Helpers/attachablebehaviour.md | 14 ++++++-------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md index f9c0d49b26..13e2170c03 100644 --- a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md +++ b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md @@ -21,7 +21,7 @@ * [Network components](network-components.md) * [Foundational Components](components/foundational/foundationalcomponents.md) * [NetworkObject](components/foundational/networkobject.md) - * [NetworkObject parenting](advanced-topics/networkobject-parenting.md) + * [NetworkObject parenting](advanced-topics/networkobject-parenting.md) * [NetworkBehaviour](components/foundational/networkbehaviour.md) * [Synchronizing & Order of Operations](components/foundational/networkbehaviour-synchronize.md) * [NetworkManager](components/foundational/networkmanager.md) @@ -43,11 +43,11 @@ * [Spawning synchronization](basics/spawning-synchronization.md) * [Deferred despawning](basics/deferred-despawning.md) * [Latency and performance](latency-performance.md) - * [Understanding latency](learn/lagandpacketloss.md) - * [Ticks and update rates](learn/ticks-and-update-rates.md) - * [Improving performance with client-side interpolation](learn/clientside-interpolation.md) - * [Client anticipation](advanced-topics/client-anticipation.md) - * [Dealing with latency](learn/dealing-with-latency.md) + * [Understanding latency](learn/lagandpacketloss.md) + * [Ticks and update rates](learn/ticks-and-update-rates.md) + * [Improving performance with client-side interpolation](learn/clientside-interpolation.md) + * [Client anticipation](advanced-topics/client-anticipation.md) + * [Dealing with latency](learn/dealing-with-latency.md) * [Network synchronization](network-synchronization.md) * [Synchronizing states and events](advanced-topics/ways-to-synchronize.md) * [NetworkVariables](networkvariables-landing.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md index 794aced829..a2b6b18c4a 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md @@ -20,9 +20,9 @@ This is simple enough for many scenarios, but can become cumbersome under more s With attaching, a user would create nested `GameObject` children that represent the item when it is picked up and when it is dropped/placed somewhere in the scene (i.e. world).<br />  - - The WorldItemRoot is where the `NetworkObject` component is placed. - - The NestedChild-World contains the components needed for the item when it is placed in the world. - - The NestedChild-PickedUp contains the components needed for the item when it is picked up by a player. +- The WorldItemRoot is where the `NetworkObject` component is placed. +- The NestedChild-World contains the components needed for the item when it is placed in the world. +- The NestedChild-PickedUp contains the components needed for the item when it is picked up by a player. By placing an `AttachableBehaviour` component on the NestedChild-PickedUp `GameObject` and an `AttachableNode` component on the TargetNode, a user can then invoke the `AttachableBehaviour.Attach` method while passing in the `AttachableNode` component and the NestedChild-PickedUp `GameObject` will get parented under the TargetNode while also synchronizing this action with all other clients.<br />  @@ -65,7 +65,6 @@ The `ComponentController` provides this type of functionality: - Each assigned component entry can be configured to directly or inversely follow the `ComponentController`'s current state. - Each assigned component entry can have an enable and/or disable delay. - _When invoked internally by `AttachableBehaviour`, delays are ignored when an `AttachableNode` is being destroyed and the changes are immediate._ - The `ComponentController` could be daisy chained with minimal user script: ```csharp /// <summary> @@ -123,7 +122,6 @@ This diagram has a bit more detail to it and introduces one possible usage of a  - In the diagram above, we see arrows pointing from the `ComponentController` to the non-netcode standard Unity components such as a `MeshRenderer`, `Collider`, or any other component that should only be enabled when either in "World View" or "Attached View" modes. We can also see that the `AttachableBehaviour` points to the `ComponentController` with a diagram to the right that shows the `AttachableBehaviour` notifies the `ComponentController` that, in turn, enables or disables certain components. #### World Item Component Controller @@ -157,9 +155,9 @@ The above diagram represents what the **Player** and **World Item** spawned obje **AttachableBehaviour & NetworkObject Relationship** Upon a `NetworkObject` component being spawned, all associated `NetworkBehaviour` based component instances, that are directly attached to the `NetworkObject`'s `GameObject` or are on any child `GameObject`, will be registered with the `NetworkObject` instance. This remains true even when a child `GameObject` containing one or more `NetworkBehaviour` based component instances of a spawned `NetworkObject` is parented, during runtime, under another `GameObject` that is associated with a different spawned `NetworkObject`. Of course, there are additional considerations like: - - What happens when one or both of the NetworkObjects is de-spawned? - - How do you assure the child attachable will return back to its default parent? - - and several other edge case scenarios... +- What happens when one or both of the NetworkObjects is de-spawned? +- How do you assure the child attachable will return back to its default parent? +- and several other edge case scenarios... `AttachableBehaviour` leverages from this "spawn lifetime" relationship to provide another type of "parenting" (attaching) while also taking into consideration these types of edge case scenarios. ::: \ No newline at end of file From 959ccef4b049134e9968afe6a2dff3a6639acc67 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 13:13:02 -0500 Subject: [PATCH 32/43] docs (update & add) Adding `AttachableNode` and `ComponentController` sections. Added "Synchronized RPC driven fields" section to networkbehaviour-synchronize.md. Updated various areas based on the additions. --- .../Documentation~/TableOfContents.md | 4 +- .../components/Helpers/attachablebehaviour.md | 98 +++++-------------- .../components/Helpers/attachablenode.md | 14 +++ .../components/Helpers/componentcontroller.md | 88 +++++++++++++++++ .../components/Helpers/helpercomponents.md | 6 +- .../networkbehaviour-synchronize.md | 75 +++++++++++++- .../learn/dealing-with-latency.md | 4 +- 7 files changed, 205 insertions(+), 84 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablenode.md create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md diff --git a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md index 13e2170c03..416c29c8e3 100644 --- a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md +++ b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md @@ -28,8 +28,8 @@ * [PlayerObjects and player prefabs](components/foundational/playerobjects.md) * [Helper Components](components/Helpers/helpercomponents.md) * [AttachableBehaviour](components/Helpers/attachablebehaviour.md) - * AttachableNode - * ComponentController + * [AttachableNode](components/Helpers/attachablenode.md) + * [ComponentController](components/Helpers/componentcontroller.md) * [NetworkAnimator](components/helpers/networkanimator.md) * [NetworkTransform](components/helpers/networktransform.md) * [Physics](advanced-topics/physics.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md index a2b6b18c4a..d4cbab5c57 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md @@ -1,5 +1,17 @@ # AttachableBehaviour -The `AttachableBehaviour` Provides "out of the box" support for attaching (i.e. parenting) a nested child `GameObject` that includes an `AttachableBehaviour` component to another nested child `GameObject` with an `AttachableNode` component that is associated with a different `NetworkObject`. + + + +The basic functionality of the `AttachableBehaviour` component provides: +- The ability to assign (make aware) `ComponetController` components from any part of the parent-child hierarchy. + - Each `ComponentControllerEntry` provides the ability to select when the `ComponentController` should be triggered (via the **Auto Trigger** property) and whether its enabled state should be enabled or disabled upon attaching (via the **Enable On Attach** property). The default setting is to be disabled upon the `AttachableBehaviour` attaching to an `AttachableNode` and enabled upon detaching. When the **Enable On Attach** property is enabled, the `ComponentController` will be set to enabled upon the `AttachableBehaviour` attaching to an `AttachableNode` and disabled upon detaching. +- The ability to control when an `AttachableBehaviour` component will automatically detach from an `AttachableNode` via the **Auto Detach** property. + - The **Auto Detach** property can have any combination of the below flags or none (no flags): + - **On Ownership Changed:** Upon ownership changing, the `AttachableBehaviour` will detach from any `AttachableNode` it is attached to. + - **On Despawn:** Upon the `AttachableBehaviour` being despawned, it will detach from any `AttachableNode` it is attached to. + - **On Attach Node Destroy**: Just prior to the `AttachableNode` being destroyed, any attached `AttachableBehaviour` with this flag will automatically detach from the `AttachableNode`. + +_Any of the `AttachableBehaviour.AutoDetach` settings will be invoked on all instances without the need for the owner to synchronize the end result(i.e. detaching) which provides a level of redundancy for edge case scenarios like a player being disconnected abruptly by the host or by timing out or any scenario where a spawned object is being destroyed with the owner or perhaps being redistributed to another client authority in a distributed authority session. Having the ability to select or deselect any of the auto-detach flags coupled with the ability to derive from `AttachableBehaviour` provides additional levels of modularity/customization._ ## Attaching vs NetworkObject parenting @@ -27,82 +39,15 @@ With attaching, a user would create nested `GameObject` children that represent By placing an `AttachableBehaviour` component on the NestedChild-PickedUp `GameObject` and an `AttachableNode` component on the TargetNode, a user can then invoke the `AttachableBehaviour.Attach` method while passing in the `AttachableNode` component and the NestedChild-PickedUp `GameObject` will get parented under the TargetNode while also synchronizing this action with all other clients.<br />  -### AttachableBehaviour - - - -The basic functionality of the `AttachableBehaviour` component provides: -- The ability to assign (make aware) `ComponetController` components from any part of the parent-child hierarchy. - - Each `ComponentControllerEntry` provides the ability to select when the `ComponentController` should be triggered (via the **Auto Trigger** property) and whether its enabled state should be enabled or disabled upon attaching (via the **Enable On Attach** property). The default setting is to be disabled upon the `AttachableBehaviour` attaching to an `AttachableNode` and enabled upon detaching. When the **Enable On Attach** property is enabled, the `ComponentController` will be set to enabled upon the `AttachableBehaviour` attaching to an `AttachableNode` and disabled upon detaching. -- The ability to control when an `AttachableBehaviour` component will automatically detach from an `AttachableNode` via the **Auto Detach** property. - - The **Auto Detach** property can have any combination of the below flags or none (no flags): - - **On Ownership Changed:** Upon ownership changing, the `AttachableBehaviour` will detach from any `AttachableNode` it is attached to. - - **On Despawn:** Upon the `AttachableBehaviour` being despawned, it will detach from any `AttachableNode` it is attached to. - - **On Attach Node Destroy**: Just prior to the `AttachableNode` being destroyed, any attached `AttachableBehaviour` with this flag will automatically detach from the `AttachableNode`. - -_Any of the `AttachableBehaviour.AutoDetach` settings will be invoked on all instances without the need for the owner to synchronize the end result(i.e. detaching) which provides a level of redundancy for edge case scenarios like a player being disconnected abruptly by the host or by timing out or any scenario where a spawned object is being destroyed with the owner or perhaps being redistributed to another client authority in a distributed authority session. Having the ability to select or deselect any of the auto-detach flags coupled with the ability to derive from `AttachableBehaviour` provides additional levels of modularity/customization._ - -### AttachableNode - - - -The simplest component in the bunch, this provides a valid connection point (_i.e. what an `AttachableBehaviour` can attach to_) with the ability to have it automatically detach from any attached `AttachableBehaviour` instances when it is despawned. - -### ComponentController - - - -Taking the above example into consideration, it would make sense that a user would want to be able to easily control whether a specific component is enabled or disabled when something is attached or detached. - -As an example: - -- When the WorldItemRoot is in the "placed in the world" state, it would make sense to disable any `MeshRenderer`, `Collider`, and other components on the NestedChild-PickedUp `GameObject` while enabling similar types of components on the NestedChild-World. -- When the WorldItemRoot is in the "picked up" state, it would make sense to enable any `MeshRenderer`, `Collider`, and other components on the NestedChild-PickedUp `GameObject` while disabling similar types of components on the NestedChild-World. -- It would also make sense to synchronize the enabling or disabling of components with all instances. - -The `ComponentController` provides this type of functionality: -- Can be used with `AttachableBehaviour` or independently for another purpose. -- Each assigned component entry can be configured to directly or inversely follow the `ComponentController`'s current state. -- Each assigned component entry can have an enable and/or disable delay. - - _When invoked internally by `AttachableBehaviour`, delays are ignored when an `AttachableNode` is being destroyed and the changes are immediate._ -The `ComponentController` could be daisy chained with minimal user script: -```csharp -/// <summary> -/// Use as a component in the ComponentController that will -/// trigger the Controller (ComponentController). -/// This pattern can repeat. -/// </summary> -public class DaisyChainedController : MonoBehaviour -{ - public ComponentController Controller; - - private void OnEnable() - { - if (!Controller || !Controller.HasAuthority) - { - return; - } - Controller.SetEnabled(true); - } - - private void OnDisable() - { - if (!Controller || !Controller.HasAuthority) - { - return; - } - Controller.SetEnabled(false); - } -} -``` - -### Example of synchronized RPC driven properties +:::info +**Example of synchronized RPC driven properties** Both the `AttachableBehaviour` and the `ComponentController` provide an example of using synchronized RPC driven properties in place of `NetworkVariable`. Under certain conditions it is better to use RPCs when a specific order of operations is needed as opposed to `NetworkVariable`s which can update out of order (regarding the order in which certain states were updated) depending upon several edge case scenarios. Under this condition using reliable RPCs will assure the messages are received in the order they were generated while also reducing the latency time between the change and the non-authority instances being notified of the change. Synchronized RPC driven properties only require overriding the `NetworkBehaviour.OnSynchronize` method and serializing any properties that need to be synchronized with late joining players or handling network object visibility related scenarios. +::: -## Usage Walk Through +## Usage walk through ### Introduction @@ -118,7 +63,7 @@ The player prefab in the above diagram is not complete, includes the components This diagram has a bit more detail to it and introduces one possible usage of a `ComponentController` and `AttachableBehaviour`. The `ComponentController` will be used to control the enabling and disabling of components and synchronizing this with non-authority instances. The `AttachableBehaviour` resides on the child `AttachedView`'s `GameObject` and will be the catalyst for attaching to a player. -### World vs Attached View Modes +### World vs attached view modes  @@ -160,4 +105,9 @@ Upon a `NetworkObject` component being spawned, all associated `NetworkBehaviour - and several other edge case scenarios... `AttachableBehaviour` leverages from this "spawn lifetime" relationship to provide another type of "parenting" (attaching) while also taking into consideration these types of edge case scenarios. -::: \ No newline at end of file +::: + +## Additional resources + +- [AttachableNode](attachablenode.md) +- [ComponentController](componentcontroller.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablenode.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablenode.md new file mode 100644 index 0000000000..5e8b68162f --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablenode.md @@ -0,0 +1,14 @@ +# AttachableNode + + + +This component provides a valid attach point for `AttachableBehaviour` components. It includes the `AttachableNode.DetachOnDespawn` field that, when enabled (the default), it will automatically detach any attached `AttachableBehaviour` instances when the associated `NetworkObject` of the `AttachableNode` is de-spawned. + +## AttachableBehaviour Usage + +[A usage example can be found here.](attachablebehaviour.md#usage-walk-through) + +## Additional resources + +- [AttachableBehaviour](attachablebehaviour.md) +- [ComponentController](componentcontroller.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md new file mode 100644 index 0000000000..916205f86b --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md @@ -0,0 +1,88 @@ +# ComponentController + +A `ComponentController` provides you with the ability to enable or disable one or more components by the authority instance and have those changes synchronized with non-authority/remote instances. It uses a [synchronized RPC driven field approach](../foundational/networkbehaviour-synchronize.md#synchronized-rpc-driven-fields) to synchronize its enabled state of the components it is controlling to assure optimal performance and that the order of operations of changes is relative to other `ComponentController` and/or other `AttachableBehaviour` component instances. + +The `ComponentController` can be: +- Used with `AttachableBehaviour` or independently for another purpose. +- Configured to directly or inversely follow the `ComponentController`'s current state. +- Configured to have an enable and/or disable delay. + - _When invoked internally by `AttachableBehaviour`, delays are ignored when an `AttachableNode` is being destroyed and the changes are immediate._ + + +## Configuring + + + +A `ComponentController` can have one or more `ComponentEntry` entries in its **Components** list. Each `ComponentEntry` has some additional fields that you can adjust based on your desired result: +- **Invert Enabled:** When enabled, this will make the associated component inversely follow the `ComponentControllers` global enabled state. This is useful if you want a set of components to be enabled when the `ComponentController` component's global enable state is set to `false` and for that same set of components to be disabled when the `ComponentController` component's global enable state is set to `true`. +- **Enable Delay:** When greater than 0 (the default), the component will delay transitioning from a disabled state to an enabled state by the amount of time (in seconds) specified. +- **Disable Delay:** When greater than 0 (the default), the component will delay transitioning from an enabled state to a disabled state by the amount of time (in seconds) specified. +- **Component:** The component to control and synchronize its enabled state. + +Both delay values (Enable & Disable) has many uses, but an example would be to prevent a `MeshRenderer` from being enabled prior to other specific events like avoiding it from rendering for a few frames while the attachable is positioned. + + +## Examples + +### Independent Usage + +While `ComponentController` can be used with an `AttachableBehaviour` without writing any script, you might find that it can be used for many other purposes. Below is a pseudo example where a `ComponentController` would have its synchronized enabled state updated when the `DaisyChainedController` is either enabled or disabled. + +```csharp +/// <summary> +/// Use as a component in the ComponentController that will +/// trigger the Controller (ComponentController). +/// This pattern can repeat/be daisy chained. +/// </summary> +public class DaisyChainedController : MonoBehaviour +{ + public ComponentController Controller; + + private void OnEnable() + { + if (!Controller || !Controller.HasAuthority) + { + return; + } + Controller.SetEnabled(true); + } + + private void OnDisable() + { + if (!Controller || !Controller.HasAuthority) + { + return; + } + Controller.SetEnabled(false); + } +} +``` +The above component could be arranged to create a chained sequence of components when the root `DaisyChainedController` component is enabled or disabled. Such a sequence could look like: + +- DaisyChainedController-A + - Controller + - Points to DaisyChainedController-B +- DaisyChainedController-B + - Controller + - Points to DaisyChainedController-C +- DaisyChainedController-C + - Controller + +When DaisyChainedController-A is enabled, then a sequence of events would occur where DaisyChainedController-B and DaisyChainedController-C would be enabled. The same sequence of events would occur when DaisyChainedController-A was then disabled. + +### AttachableBehaviour Usage + +The `AttachableBehaviour` can be assigned one or more component controllers that will be invoked, depending upon configuration, when the `AttachableBehaviour` is attached and detached from an `AttachableNode`. You can find the [usage example with an `AttachableBehaviour` here.](attachablebehaviour.md#usage-walk-through) + +:::info +**Example of synchronized RPC driven properties** + +Both the `AttachableBehaviour` and the `ComponentController` provide an example of using synchronized RPC driven properties in place of `NetworkVariable`. Under certain conditions it is better to use RPCs when a specific order of operations is needed as opposed to `NetworkVariable`s which can update out of order (regarding the order in which certain states were updated) depending upon several edge case scenarios. + +Under this condition using reliable RPCs will assure the messages are received in the order they were generated while also reducing the latency time between the change and the non-authority instances being notified of the change. Synchronized RPC driven properties only require overriding the `NetworkBehaviour.OnSynchronize` method and serializing any properties that need to be synchronized with late joining players or handling network object visibility related scenarios. +::: + +## Additional resources + +- [AttachableBehaviour](attachablebehaviour.md) +- [AttachableNode](attachablenode.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md index d146568479..f8a69be0d3 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md @@ -4,9 +4,9 @@ Understand the helper components available to use in your Netcode for GameObject **Topic** | **Description** | | :------------------------------ | :------------------------------- | -| **[AttachableBehaviour](attachablebehaviour.md)**| Provides an alternative to `NetworkObject` parenting. (wip) | -| **AttachableNode**| Target parent for an `AttachableBehaviour`. (wip) | -| **ComponentController**| Provides the synchronization of and control over enabling or disabling objects. (wip) | +| **[AttachableBehaviour](attachablebehaviour.md)**| Provides an alternative to `NetworkObject` parenting. This section includes a usage example with `AttachableBehaviour`, `AttachableNode`, and `ComponentController`. | +| **[AttachableNode](attachablenode.md)**| Target parent for an `AttachableBehaviour`. | +| **[ComponentController](componentcontroller.md)**| Provides the synchronization of and control over enabling or disabling objects. | | **[NetworkAnimator](networkanimator.md)**| The `NetworkAnimator` component provides you with a fundamental example of how to synchronize animations during a network session. Animation states are synchronized with players joining an existing network session and any client already connected before the animation state changing. | | **[NetworkTransform](networktransform.md)**| [NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](../foundational/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. | | **[Physics](../../advanced-topics/physics.md)**| Netcode for GameObjects has a built in approach which allows for server-authoritative physics where the physics simulation only runs on the server. | diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md index 92fc4f68e2..31a5099d14 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md @@ -97,9 +97,78 @@ Client-side: - The `NetworkObject` is spawned. - For each associated `NetworkBehaviour` component, `NetworkBehaviour.OnNetworkSpawn` is invoked. -### `OnSynchronize` example - -Now that you understand the general concept behind `NetworkBehaviour.OnSynchronize`, the question you might have is "when would you use such a thing"? `NetworkVariable`s can be useful to synchronize state, but they also are only updated every network tick and you might have some form of state that needs to be updated when it happens and not several frames later, so you decide to use RPCs. However, this becomes an issue when you want to synchronize late-joining clients as there is no way to synchronize late-joining clients based on RPC activity over the duration of a network session. This is one of many possible reasons one might want to use `NetworkBehaviour.OnSynchronize`. +### Synchronized RPC driven fields + +Now that you understand the general concept behind `NetworkBehaviour.OnSynchronize`, the question you might have is: + +_"When would you want to use `NetworkBehaviour.OnSynchronize`?"_ + + `NetworkVariable`s can be useful to synchronize state, but they also are only updated every network tick. While it is possible to adjust a `NetworkVariable`'s update frequency, `NetworkVariable`s (in general) guarantees state synchronization but does not guarantee state changes will be updated in the same order they were chanted relative to other `NetworkVariables`. + + With this in mind, you might need states to be updated in the relative order in which they were changed. In order to do this, you can combine the use of an RPC to handle updating the change in a properties state/value while using `NetworkBehaviour.OnSynchronize` to assure that any late joining client will be synchronized with the current state of said property. + +**Using a synchronized RPC driven field approach:** + +- When the authority changes a local field's value, it would then need send an RPC to all non-authority instances. + - RPC messages are immediately queued in the outbound send queue which means the order in which an RPC is invoked is the order in which they will be received (_if using the default reliable fragmented sequenced delivery_). + - Any other synchronized RPC driven fields, whether on the same `NetworkBehaviour` or not, would occur in the order they were invoked on the authority instance side. +- As long as you override the `NetworkBehaviour.OnSynchronize` method and serialize the field, then late joining clients will be synchronized with the authority's most current field value. + +:::info +**Synchronized RPC driven fields vs NetworkVariables** +When a NetworkVariable becomes dirty, the associated `NetworkObject` is add to a queue of `NetworkObject` instances to be scanned for changes to any NetworkVariable declared in a `NetworkBehaviour` associated with the `NetworkObject` instance. Towards the end of the frame, during late update, any `NetworkObject` instances marked as "having one or more dirty NetworkVariables" will be processed and the delta states will be serialized. This can lead to a scenario like the following: + +**Authority side** +- During Update: + - NetworkBehaviour-A.NetworkVariable-1 is updated on the authority instance. + - NetworkObject-A is added to the "dirty NetworkObjects" queue. + - NetworkBehaviour-B.NetworkVariable-1 is updated on the authority instance. + - NetworkObject-B is added to the "dirty NetworkObjects" queue. + - NetworkBehaviour-A.NetworkVariable-2 is updated on the authority instance. + - NetworkObject-A is determined to already be in the "dirty NetworkObjects" queue. +- During Late Update: + - NetworkObject-A is scanned for dirty `NetworkVariables` and a `NetworkVariableDeltaMessage` is generated. + - _This includes NetworkBehaviour-A.NetworkVariable-1 and NetworkBehaviour-A.NetworkVariable-2._ + - NetworkObject-B is scanned for dirty `NetworkVariables` and a `NetworkVariableDeltaMessage` is generated. + - _This includes NetworkBehaviour-B.NetworkVariable-1._ + +**Non-Authority side** +- `NetworkVariableDeltaMessage` for NetworkObject-A is processed. + - NetworkBehaviour-A.NetworkVariable-1 is updated and notifications triggered. + - NetworkBehaviour-A.NetworkVariable-2 is updated and notifications triggered. +- `NetworkVariableDeltaMessage` for NetworkObject-B is processed. + - NetworkBehaviour-B.NetworkVariable-1 is updated and notifications triggered. + +Based on the above, we can determine that there will be a different order of operations in regards to when a `NetworkVariable` is updated on non-authority instances: + +**Authority side order of operations** +- NetworkBehaviour-A.NetworkVariable-1 is changed. +- NetworkBehaviour-B.NetworkVariable-1 is changed. +- NetworkBehaviour-A.NetworkVariable-2 is changed. + +**Non-Authority side order of operations** +- NetworkBehaviour-A.NetworkVariable-1 is updated and notification triggered. +- NetworkBehaviour-A.NetworkVariable-2 is changed and notification triggered. +- NetworkBehaviour-B.NetworkVariable-1 is changed and notification triggered. + +If you depend on the authority's order of operations relative to when states should be updated on the non-authority instances, then using `NetworkVariable` has the potential to not honor the order of operations for changes to `NetworkVariables`. + +**Synchronized RPC driven fields** + +**Authority side** +- NetworkBehaviour-A.Field-1 is changed and an RPC is sent to all non-authority instances. +- NetworkBehaviour-B.Field-1 is changed and an RPC is sent to all non-authority instances. +- NetworkBehaviour-A.Field-2 is changed and an RPC is sent to all non-authority instances. + +**Non-Authority side** +- RPC for NetworkBehaviour-A.Field-1 is processed and field is updated. +- RPC for NetworkBehaviour-B.Field-1 is processed and field is updated. +- RPC for NetworkBehaviour-A.Field-2 is processed and field is updated. + +Using the synchronized RPC driven field approach preserves the order of operations in regards to when each field's state changed. The additional benefit of this approach is that it is more performant than that of a `NetworkVariable`. +::: + +### Synchronized RPC driven field example The following example uses `NetworkBehaviour.OnSynchronize` to synchronize connecting (to-be-synchronized) clients and also uses an RPC to synchronize changes in state for already synchronized and connected clients: diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md b/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md index 9089eaf2a6..657d6f12a8 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md @@ -72,11 +72,11 @@ There are possible synchronizations issues with client authoritative games. If y Multiple clients with the ability to affect the same shared object can quickly become a mess. - + To avoid this, it's recommended to use client **owner** authority, which allows only the owner of an object to interact with it. Since the server controls ownership in Netcode, there's no possibility of two clients running into a [race condition](https://en.wikipedia.org/wiki/Race_condition#In_software). To allow two clients to affect the same object, you must ask the server for ownership, wait for it, then execute the client authoritative logic you want. - + #### Issue: Security From 7c95cc75801248a305b3884470983f573e6437ab Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 13:25:59 -0500 Subject: [PATCH 33/43] docs (style - PVP) Resolving more white space issues. --- .../components/Helpers/componentcontroller.md | 12 +++++------- .../foundational/networkbehaviour-synchronize.md | 10 +++++----- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md index 916205f86b..919a1fdc5a 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md @@ -6,8 +6,7 @@ The `ComponentController` can be: - Used with `AttachableBehaviour` or independently for another purpose. - Configured to directly or inversely follow the `ComponentController`'s current state. - Configured to have an enable and/or disable delay. - - _When invoked internally by `AttachableBehaviour`, delays are ignored when an `AttachableNode` is being destroyed and the changes are immediate._ - + - _When invoked internally by `AttachableBehaviour`, delays are ignored when an `AttachableNode` is being destroyed and the changes are immediate._ ## Configuring @@ -21,7 +20,6 @@ A `ComponentController` can have one or more `ComponentEntry` entries in its **C Both delay values (Enable & Disable) has many uses, but an example would be to prevent a `MeshRenderer` from being enabled prior to other specific events like avoiding it from rendering for a few frames while the attachable is positioned. - ## Examples ### Independent Usage @@ -60,15 +58,15 @@ public class DaisyChainedController : MonoBehaviour The above component could be arranged to create a chained sequence of components when the root `DaisyChainedController` component is enabled or disabled. Such a sequence could look like: - DaisyChainedController-A - - Controller + - Controller - Points to DaisyChainedController-B - DaisyChainedController-B - - Controller + - Controller - Points to DaisyChainedController-C - DaisyChainedController-C - - Controller + - Controller -When DaisyChainedController-A is enabled, then a sequence of events would occur where DaisyChainedController-B and DaisyChainedController-C would be enabled. The same sequence of events would occur when DaisyChainedController-A was then disabled. +When DaisyChainedController-A is enabled, then a sequence of events would occur where DaisyChainedController-B and DaisyChainedController-C would be enabled. The same sequence of events would occur when DaisyChainedController-A was then disabled. ### AttachableBehaviour Usage diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md index 31a5099d14..21c0958ea2 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md @@ -103,9 +103,9 @@ Now that you understand the general concept behind `NetworkBehaviour.OnSynchroni _"When would you want to use `NetworkBehaviour.OnSynchronize`?"_ - `NetworkVariable`s can be useful to synchronize state, but they also are only updated every network tick. While it is possible to adjust a `NetworkVariable`'s update frequency, `NetworkVariable`s (in general) guarantees state synchronization but does not guarantee state changes will be updated in the same order they were chanted relative to other `NetworkVariables`. - - With this in mind, you might need states to be updated in the relative order in which they were changed. In order to do this, you can combine the use of an RPC to handle updating the change in a properties state/value while using `NetworkBehaviour.OnSynchronize` to assure that any late joining client will be synchronized with the current state of said property. +`NetworkVariable`s can be useful to synchronize state, but they also are only updated every network tick. While it is possible to adjust a `NetworkVariable`'s update frequency, `NetworkVariable`s (in general) guarantees state synchronization but does not guarantee state changes will be updated in the same order they were chanted relative to other `NetworkVariables`. + +With this in mind, you might need states to be updated in the relative order in which they were changed. In order to do this, you can combine the use of an RPC to handle updating the change in a properties state/value while using `NetworkBehaviour.OnSynchronize` to assure that any late joining client will be synchronized with the current state of said property. **Using a synchronized RPC driven field approach:** @@ -186,7 +186,7 @@ public class SimpleRpcState : NetworkBehaviour /// <summary> /// Late joining clients will be synchronized - /// to the most current m_ToggleState + /// to the most current m_ToggleState. /// </summary> protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer) { @@ -201,7 +201,7 @@ public class SimpleRpcState : NetworkBehaviour /// <summary> /// Synchronizes connected clients with the - /// server-side m_ToggleState + /// server-side m_ToggleState. /// </summary> /// <param name="stateIsSet"></param> [Rpc(SendTo.ClientsAndHost)] From fe889232aa97af99528452eebaf177b9919e5c00 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 13:32:04 -0500 Subject: [PATCH 34/43] docs (style) Removing a single sneaky white space. --- .../Documentation~/components/Helpers/componentcontroller.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md index 919a1fdc5a..823dd15817 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md @@ -55,6 +55,7 @@ public class DaisyChainedController : MonoBehaviour } } ``` + The above component could be arranged to create a chained sequence of components when the root `DaisyChainedController` component is enabled or disabled. Such a sequence could look like: - DaisyChainedController-A @@ -70,7 +71,7 @@ When DaisyChainedController-A is enabled, then a sequence of events would occur ### AttachableBehaviour Usage -The `AttachableBehaviour` can be assigned one or more component controllers that will be invoked, depending upon configuration, when the `AttachableBehaviour` is attached and detached from an `AttachableNode`. You can find the [usage example with an `AttachableBehaviour` here.](attachablebehaviour.md#usage-walk-through) +The `AttachableBehaviour` can be assigned one or more component controllers that will be invoked, depending upon configuration, when the `AttachableBehaviour` is attached and detached from an `AttachableNode`. You can find the [usage example with an `AttachableBehaviour` here.](attachablebehaviour.md#usage-walk-through) :::info **Example of synchronized RPC driven properties** From 5662003a0aa4c3ad4456a66b54e13182fddeb53e Mon Sep 17 00:00:00 2001 From: Noel Stephens <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 15:15:41 -0500 Subject: [PATCH 35/43] Update com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs Co-authored-by: Emma <emma.mcmillan@unity3d.com> --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 754b83653d..f215abf157 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -218,7 +218,7 @@ private static void CheckPrefabStage(PrefabStage prefabStage) s_PrefabAsset = AssetDatabase.LoadAssetAtPath<NetworkObject>(s_PrefabStage.assetPath); } - if (s_PrefabAsset && s_PrefabInstance && s_PrefabInstance.GlobalObjectIdHash != s_PrefabAsset.GlobalObjectIdHash) + if (s_PrefabAsset && s_PrefabInstance.GlobalObjectIdHash != s_PrefabAsset.GlobalObjectIdHash) { s_PrefabInstance.GlobalObjectIdHash = s_PrefabAsset.GlobalObjectIdHash; // For InContext mode, we don't want to record these modifications (the in-scene GlobalObjectIdHash is serialized with the scene). From 93b645fdbd4e5dbab376f95f2e9cac4441252961 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 16:11:47 -0500 Subject: [PATCH 36/43] update Implementing some suggested changes from PR review. --- .../Runtime/Components/Helpers/AttachableBehaviour.cs | 6 +----- .../Runtime/Components/Helpers/ComponentController.cs | 8 ++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 8922b4ad91..2a8d968dfc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -96,11 +96,7 @@ protected virtual void OnValidate() } foreach (var componentController in ComponentControllers) { - if (componentController == null) - { - continue; - } - componentController.OnValidate(); + componentController?.OnValidate(); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index f135446184..181b61ebf5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -255,7 +255,7 @@ protected virtual void OnValidate() } var propertyInfo = Components[i].Component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); - if (propertyInfo == null && propertyInfo.PropertyType != typeof(bool)) + if (propertyInfo == null || propertyInfo.PropertyType != typeof(bool)) { Debug.LogWarning($"{Components[i].Component.name} does not contain a public enabled property! (Removing)"); Components.RemoveAt(i); @@ -291,13 +291,13 @@ protected virtual void OnValidate() gameObjectsToScan.Clear(); // Final (third) pass is to name each list element item as the component is normally viewed in the inspector view. - for (int i = 0; i < Components.Count; i++) + foreach(var componentEntry in Components) { - if (!Components[i].Component) + if (!componentEntry.Component) { continue; } - Components[i].name = GetComponentNameFormatted(Components[i].Component); + componentEntry.name = GetComponentNameFormatted(componentEntry.Component); } } #endif From e9eec54f24aa1cad9d7553f9aca5cc7a3fe33978 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 30 Jul 2025 16:21:40 -0500 Subject: [PATCH 37/43] style Adding whitespace. --- .../Runtime/Components/Helpers/ComponentController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 181b61ebf5..8a99007012 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -291,7 +291,7 @@ protected virtual void OnValidate() gameObjectsToScan.Clear(); // Final (third) pass is to name each list element item as the component is normally viewed in the inspector view. - foreach(var componentEntry in Components) + foreach (var componentEntry in Components) { if (!componentEntry.Component) { From db4adabceae3c68070b78c28364c76db4e5ddc61 Mon Sep 17 00:00:00 2001 From: Amy Reeve <amy.reeve@unity3d.com> Date: Fri, 1 Aug 2025 18:33:29 +0100 Subject: [PATCH 38/43] Lorge doc review --- .../CharacterControllerMovingBodies/Readme.md | 4 +- Examples/OverridingScenesAndPrefabs/Readme.md | 30 +-- com.unity.netcode.gameobjects/CHANGELOG.md | 250 +++++++++--------- .../Documentation~/TableOfContents.md | 24 +- .../advanced-topics/client-anticipation.md | 4 +- .../inscene_parenting_player.md | 6 +- .../message-system/reliability.md | 2 +- .../message-system/rpc-params.md | 2 +- .../advanced-topics/message-system/rpc.md | 4 +- .../advanced-topics/messaging-system.md | 4 +- .../network-update-loop-system/index.md | 4 +- .../networkobject-parenting.md | 2 +- .../advanced-topics/networktime-ticks.md | 8 +- .../advanced-topics/object-pooling.md | 2 +- .../Documentation~/advanced-topics/physics.md | 14 +- .../advanced-topics/reconnecting-mid-game.md | 4 +- .../networkobject-serialization.md | 14 +- .../advanced-topics/session-management.md | 4 +- .../advanced-topics/transports.md | 2 +- .../basics/connection-approval.md | 2 +- .../basics/custom-networkvariables.md | 10 +- .../Documentation~/basics/networkvariable.md | 30 +-- .../Documentation~/basics/object-spawning.md | 76 +++--- .../basics/object-visibility.md | 16 +- .../Documentation~/basics/ownership.md | 4 +- .../scenemanagement/custom-management.md | 16 +- .../inscene-placed-networkobjects.md | 128 ++++----- .../basics/scenemanagement/scene-events.md | 22 +- .../scene-management-overview.md | 2 +- .../scenemanagement/timing-considerations.md | 28 +- .../using-networkscenemanager.md | 28 +- .../components/Helpers/attachablebehaviour.md | 113 -------- .../components/Helpers/attachablenode.md | 14 - .../components/Helpers/componentcontroller.md | 87 ------ .../components/Helpers/helpercomponents.md | 12 - .../components/core/corecomponents.md | 36 +++ .../networkbehaviour-synchronize.md | 68 ++--- .../components/core/networkbehaviour.md | 127 +++++++++ .../{foundational => core}/networkmanager.md | 26 +- .../{foundational => core}/networkobject.md | 6 +- .../{foundational => core}/playerobjects.md | 2 +- .../foundational/foundationalcomponents.md | 34 --- .../foundational/networkbehaviour.md | 127 --------- .../components/helper/attachablebehaviour.md | 122 +++++++++ .../components/helper/attachablenode.md | 16 ++ .../components/helper/componentcontroller.md | 95 +++++++ .../components/helper/helpercomponents.md | 12 + .../{Helpers => helper}/networkanimator.md | 30 +-- .../{Helpers => helper}/networktransform.md | 12 +- .../Documentation~/integrated-management.md | 2 +- .../learn/clientside-interpolation.md | 2 +- .../learn/dealing-with-latency.md | 4 +- .../Documentation~/learn/faq.md | 4 +- .../Documentation~/learn/rpcnetvarexamples.md | 4 +- .../learn/sample-dedicated-server.md | 4 +- .../Documentation~/network-components.md | 4 +- .../Documentation~/network-synchronization.md | 2 +- .../Documentation~/network-update-loop.md | 2 +- .../networkbehaviour-landing.md | 6 +- .../Documentation~/relay/relay.md | 2 +- .../Documentation~/samples.md | 4 +- .../samples/bitesize/bitesize-clientdriven.md | 2 +- .../samples/bitesize/bitesize-invaders.md | 2 +- .../samples/bossroom/architecture.md | 22 +- .../bossroom/networkobject-parenting.md | 4 +- .../samples/bossroom/networkrigidbody.md | 4 +- .../samples/bossroom/optimizing-bossroom.md | 6 +- .../Documentation~/scene-management.md | 2 +- .../Documentation~/serialization.md | 2 +- .../Documentation~/spawn-despawn.md | 2 +- .../troubleshooting/error-messages.md | 4 +- .../troubleshooting/troubleshooting.md | 4 +- .../tutorials/get-started-with-ngo.md | 20 +- .../Documentation~/tutorials/helloworld.md | 22 +- .../testing_with_artificial_conditions.md | 2 +- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 2 +- .../Bootstrap/Scripts/BootstrapPlayer.cs | 6 +- 77 files changed, 926 insertions(+), 905 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md delete mode 100644 com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablenode.md delete mode 100644 com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md delete mode 100644 com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/core/corecomponents.md rename com.unity.netcode.gameobjects/Documentation~/components/{foundational => core}/networkbehaviour-synchronize.md (72%) create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour.md rename com.unity.netcode.gameobjects/Documentation~/components/{foundational => core}/networkmanager.md (88%) rename com.unity.netcode.gameobjects/Documentation~/components/{foundational => core}/networkobject.md (97%) rename com.unity.netcode.gameobjects/Documentation~/components/{foundational => core}/playerobjects.md (96%) delete mode 100644 com.unity.netcode.gameobjects/Documentation~/components/foundational/foundationalcomponents.md delete mode 100644 com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour.md create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/helper/attachablebehaviour.md create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/helper/attachablenode.md create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/helper/componentcontroller.md create mode 100644 com.unity.netcode.gameobjects/Documentation~/components/helper/helpercomponents.md rename com.unity.netcode.gameobjects/Documentation~/components/{Helpers => helper}/networkanimator.md (54%) rename com.unity.netcode.gameobjects/Documentation~/components/{Helpers => helper}/networktransform.md (95%) diff --git a/Examples/CharacterControllerMovingBodies/Readme.md b/Examples/CharacterControllerMovingBodies/Readme.md index 2403b27a35..539c746856 100644 --- a/Examples/CharacterControllerMovingBodies/Readme.md +++ b/Examples/CharacterControllerMovingBodies/Readme.md @@ -1,11 +1,11 @@ # Netcode for GameObjects Smooth Transform Space Transitions ## Non-Rigidbody CharacterController Parenting with Moving Bodies  -This example provides you with the fundamental building blocks for smooth synchronized transitions between two non-rigidbody based objects. This includes transitioning from world to local, local to world, and local to local transform spaces. +This example provides you with the fundamental building blocks for smooth synchronized transitions between two non-rigidbody based objects. This includes transitioning from world to local, local to world, and local to local transform spaces. ### The `CharacterController`  -The `CharacterController` component is assigned to the `PlayerNoRigidbody` player prefab. It includes a `MoverScriptNoRigidbody` that handles all of the player's motion and includes some additional "non-rigidbody to non-rigidbody" collision handling logic that is applied when a player bumps into a rotation and/or moving body. The player prefab includes a child "PlayerBallPrime" that rotates around the player in local space (nested `NetworkTransform`), and the "PlayerBallPrime" has 3 children ("PlayerBallChild1-3") that each rotates around a different axis of the "PlayerBallPrime". While the end resulting effect is kind of cool looking, they provide a point of reference as to whether there is any deviation of each child's given axial path relative to each parent level. Additionally, it shows how tick synchronized nested `NetworkTransform` components keep synchronized with their parent and how that persists when the parent is parented or has its parent removed. +The `CharacterController` component is assigned to the `PlayerNoRigidbody` player prefab. It includes a `MoverScriptNoRigidbody` that handles all of the player's motion and includes some additional "non-rigidbody to non-rigidbody" collision handling logic that is applied when a player bumps into a rotation and/or moving body. The player prefab includes a child "PlayerBallPrime" that rotates around the player in local space (nested NetworkTransform), and the "PlayerBallPrime" has 3 children ("PlayerBallChild1-3") that each rotates around a different axis of the "PlayerBallPrime". While the end resulting effect is kind of cool looking, they provide a point of reference as to whether there is any deviation of each child's given axial path relative to each parent level. Additionally, it shows how tick synchronized nested NetworkTransform components keep synchronized with their parent and how that persists when the parent is parented or has its parent removed. ### Rotating Bodies #### StationaryBodyA&B diff --git a/Examples/OverridingScenesAndPrefabs/Readme.md b/Examples/OverridingScenesAndPrefabs/Readme.md index f411aae755..68fc73cef8 100644 --- a/Examples/OverridingScenesAndPrefabs/Readme.md +++ b/Examples/OverridingScenesAndPrefabs/Readme.md @@ -5,10 +5,10 @@ _Supports using the client-server and distributed authority network topologies._ This example, based on the [Netcode for GameObjects Smooth Transform Space Transitions](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/tree/example/server-client-unique-scenes-and-prefabs/Examples/CharacterControllerMovingBodies), provides and overview of how to use: - [`NetworkPrefabHandler`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.0/api/Unity.Netcode.NetworkPrefabHandler.html) as a way to dynamically control overriding network prefabs and how they are instantiated. - - For this example, the prefab handler is overriding the player prefab. + - For this example, the prefab handler is overriding the player prefab. - *You will only see the end result of this portion of the example by running a server instance (i.e. not host) as that will create instances of the ServerPlayer network prefab instead of the ClientPlayer network prefab.* -- [`NetworkSceneManager.SetClientSynchronizationMode`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.0/api/Unity.Netcode.NetworkSceneManager.html#Unity_Netcode_NetworkSceneManager_SetClientSynchronizationMode_UnityEngine_SceneManagement_LoadSceneMode_) to change the default client synchronization mode (SingleMode) to an additive client synchronization mode. - - Additive client synchronization mode will prevent already existing preloaded scenes from being unloaded and will use them, as opposed to reloading the same scene, during a client's initial synchronization. +- [`NetworkSceneManager.SetClientSynchronizationMode`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.0/api/Unity.Netcode.NetworkSceneManager.html#Unity_Netcode_NetworkSceneManager_SetClientSynchronizationMode_UnityEngine_SceneManagement_LoadSceneMode_) to change the default client synchronization mode (SingleMode) to an additive client synchronization mode. + - Additive client synchronization mode will prevent already existing preloaded scenes from being unloaded and will use them, as opposed to reloading the same scene, during a client's initial synchronization. - *This is a server-side only setting that gets sent to the clients during the initial synchronization process.* - [`NetworkSceneManager.VerifySceneBeforeLoading`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.0/api/Unity.Netcode.NetworkSceneManager.html#Unity_Netcode_NetworkSceneManager_VerifySceneBeforeLoading) in order to control what scenes the server will include when sending the synchronization message to newly connecting clients. @@ -19,14 +19,14 @@ This example uses unity services. Upon loading the project for the first time, y ## Terminology ### Shared Scenes -These are scenes that will be synchronized between a server or session owner and used when a client runs through the initial synchronization process. -- You can populate these scenes with in-scene placed or dynamically spawned NetworkObjects. +These are scenes that will be synchronized between a server or session owner and used when a client runs through the initial synchronization process. +- You can populate these scenes with in-scene placed or dynamically spawned NetworkObjects. - These scenes **must be** within the scenes in build list. ### Local Scenes -These are scenes that are always only local to the application instances (server or client) and will not be synchronized. -- You should not populate these scenes with NetworkObjects. - -However, this example includes one of several ways you can associate a `MonoBehaviour` with a `NetworkBehaviour`. +These are scenes that are always only local to the application instances (server or client) and will not be synchronized. +- You should not populate these scenes with NetworkObjects. + -However, this example includes one of several ways you can associate a `MonoBehaviour` with a NetworkBehaviour. - These scenes can be dynamically created, included in the scenes in build list, or be an addressable loaded at some point prior to connecting or while connected to a session. ## Client Synchronization and Scene Validation @@ -54,16 +54,16 @@ The first scene loaded. Contains a `NetworkManagerBootstrapper` in-scene placed #### NetworkManager Bootstrapper (component)  -Handles the pre-network session menu interface along with connect and disconnect events. Since it is derived from `NetworkManager`, it also defines the network session configuration (i.e. `NetworkConfig`). The `NetworkManagerBootstrapper` in-scene placed `GameObject` gets loaded into the DDOL scene automatically and will persist throughout the application life time. This derived class requires the `SceneBootstrapLoader` component. +Handles the pre-network session menu interface along with connect and disconnect events. Since it is derived from NetworkManager, it also defines the network session configuration (i.e. `NetworkConfig`). The `NetworkManagerBootstrapper` in-scene placed `GameObject` gets loaded into the DDOL scene automatically and will persist throughout the application life time. This derived class requires the `SceneBootstrapLoader` component. #### Scene Bootstrap Loader (component)  -This component handles preloading scenes for both the client(s) and server. Upon being started, the `NetworkManagerBootstrapper` component will invoke `SceneBootstrapLoader.LoadMainMenu` method that kicks off the scene preloading process. +This component handles preloading scenes for both the client(s) and server. Upon being started, the `NetworkManagerBootstrapper` component will invoke `SceneBootstrapLoader.LoadMainMenu` method that kicks off the scene preloading process. - **Default Active Scene Asset:** There is always an active scene. For this example, the default active scene is the same on both the client and server relative properties. *The active scene is always (and should always) be a "shared scene".* - This could represent a lobby or network session main menu (i.e. create or join session). - Both the client and the server preload this scene prior to starting a network session. -- **Local Scene Assets:** There could be times where you want to load scenes specific to the `NetworkManager` instance type (i.e. client, host, or server). +- **Local Scene Assets:** There could be times where you want to load scenes specific to the NetworkManager instance type (i.e. client, host, or server). - These scenes are not synchronized by a server (client-server) or session owner (distributed authority). - Having different locally loaded scenes is typically more common in a client-server network topology. - In a distributed authority network topology, it is more common to keep all scenes synchronized but you might want to load non-synchronized scenes (i.e. menu interface for settings etc). @@ -72,11 +72,11 @@ This component handles preloading scenes for both the client(s) and server. Upon - If the server synchronizes any scenes from the share scene assets with a client that already has those scene loaded, then those locally loaded scenes on the client side will be used during synchronization. - Depending upon how many scenes you want to synchronize and/or how large one or more scenes are, preloading scenes can reduce synchronization time for clients. The `NetworkManagerBootstrapper` uses the `SceneBootstrapLoader` component to start the creation or joining of a network session. The logical flow looks like: -- `NetworkManagerBootstrapper` invokes `SceneBootstrapLoader.StartSession` when you click one of the (very simple) main menu buttons and passes in the mode/type of `NetworkManager` to start. -- Based on the `NetworkManager` type being started, the `SceneBootstrapLoader` will then: +- `NetworkManagerBootstrapper` invokes `SceneBootstrapLoader.StartSession` when you click one of the (very simple) main menu buttons and passes in the mode/type of NetworkManager to start. +- Based on the NetworkManager type being started, the `SceneBootstrapLoader` will then: - Load the default active scene using the `UnityEngine.SceneManagement.SceneManager`. - Load the local scenes using the `UnityEngine.SceneManagement.SceneManager`. - - Then it will create or join a network session by either starting the `NetworkManager` or connecting to the sesssion via multiplayer services. + - Then it will create or join a network session by either starting the NetworkManager or connecting to the sesssion via multiplayer services. - _Server or Session Owner only:_ - If any, load the shared (i.e. synchronized) scene assets using the `NetworkSceneManager` @@ -87,7 +87,7 @@ This `MonoBehaviour` component implements the `INetworkPrefabInstanceHandler` in - Network Prefab: This is the network prefab that you want to override. In this example, it is what is used to spawn a server-side player prefab and is what is defined within the `NetworkManagerBootstrapper` component. - Network Prefab Override: This is what is used to spawn a player prefab on the client-side. -At runtime the local `NetworkManager` instance is a client/host or server and will spawn either the ClientPlayer or ServerPlayer prefab. The `NetworkPrefabOverrideHandler` does not need to be a `NetworkBehaviour` and sometimes (especially for overriding the player prefab) it is better to register prefab handlers prior to starting the `NetworkManager`. +At runtime the local NetworkManager instance is a client/host or server and will spawn either the ClientPlayer or ServerPlayer prefab. The `NetworkPrefabOverrideHandler` does not need to be a NetworkBehaviour and sometimes (especially for overriding the player prefab) it is better to register prefab handlers prior to starting the NetworkManager. ## Input Controls The following is a list of the input controls used in this project: diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 179d9a84e4..a1d14792a2 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -14,7 +14,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `AttachableBehaviour` helper component to provide an alternate approach to parenting items without using the `NetworkObject` parenting. (#3518) - Added `AttachableNode` helper component that is used by `AttachableBehaviour` as the target node for parenting. (#3518) - Added `ComponentController` helper component that can be used to synchronize the enabling and disabling of components and can be used in conjunction with `AttachableBehaviour`. (#3518) -- Added `NetworkBehaviour.OnNetworkPreDespawn` that is invoked before running through the despawn sequence for the `NetworkObject` and all `NetworkBehaviour` children of the `NetworkObject` being despawned. (#3518) +- Added `NetworkBehaviour.OnNetworkPreDespawn` that is invoked before running through the despawn sequence for the `NetworkObject` and all NetworkBehaviour children of the `NetworkObject` being despawned. (#3518) - Added methods `GetDefaultNetworkSettings` and `GetDefaultPipelineConfigurations` to `UnityTransport`. These can be used to retrieve the default settings and pipeline stages that are used by `UnityTransport`. This is useful when providing a custom driver constructor through `UnityTransport.s_DriverConstructor`, since it allows reusing or tuning the existing configuration instead of trying to recreate it. This means a transport with a custom driver can now easily benefit from most of the features of `UnityTransport`, like integration with the Network Simulator and Network Profiler from the multiplayer tools package. (#3501) - Added mappings between `ClientId` and `TransportId`. (#3516) - Added `NetworkPrefabInstanceHandlerWithData<T>`, a variant of `INetworkPrefabInstanceHandler` that provides access to custom instantiation data directly within the `Instantiate()` method. (#3430) @@ -47,7 +47,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed - Fixed `NullReferenceException` on `NetworkList` when used without a NetworkManager in scene. (#3503) -- Fixed issue where `NetworkClient` could persist some settings if re-using the same `NetworkManager` instance. (#3491) +- Fixed issue where `NetworkClient` could persist some settings if re-using the same NetworkManager instance. (#3491) - Fixed issue where a pooled `NetworkObject` was not resetting the internal latest parent property when despawned. (#3491) - Fixed issue where the initial client synchronization pre-serialization process was not excluding spawned `NetworkObject` instances that already had pending visibility for the client being synchronized. (#3488) - Fixed issue where there was a potential for a small memory leak in the `ConnectionApprovedMessage`. (#3486) @@ -76,12 +76,12 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed - Fixed issue where the `NetworkObject.DontDestroyWithOwner` was not being honored. (#3477) -- Fixed issue where non-authority `NetworkTransform` instances would not allow non-synchronized axis values to be updated locally. (#3471) +- Fixed issue where non-authority NetworkTransform instances would not allow non-synchronized axis values to be updated locally. (#3471) - Fixed issue where invoking `NetworkObject.NetworkShow` and `NetworkObject.ChangeOwnership` consecutively within the same call stack location could result in an unnecessary change in ownership error message generated on the target client side. (#3468) -- Fixed issue where `NetworkVariable`s on a `NetworkBehaviour` could fail to synchronize changes if one has `NetworkVariableUpdateTraits` set and is dirty but is not ready to send. (#3466) +- Fixed issue where `NetworkVariable`s on a NetworkBehaviour could fail to synchronize changes if one has `NetworkVariableUpdateTraits` set and is dirty but is not ready to send. (#3466) - Fixed issue with the Distributed Authority connection sequence with scene management enabled where the `ClientConnected` event was fired before the client was synchronized. (#3459) - Fixed inconsistencies in the `OnSceneEvent` callback. (#3458) -- Fixed issues with the `NetworkBehaviour` and `NetworkVariable` length safety checks. (#3405) +- Fixed issues with the NetworkBehaviour and `NetworkVariable` length safety checks. (#3405) - Fixed memory leaks when domain reload is disabled. (#3427) - Fixed issue where disabling the physics or physics2D package modules could result in a compilation error. (#3422) - Fixed an exception being thrown when unregistering a custom message handler from within the registered callback. (#3417) @@ -118,23 +118,23 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed -- Fixed issue where in-scene placed `NetworkObjects` could fail to synchronize its transform properly (especially without a `NetworkTransform`) if their parenting changes from the default when the scene is loaded and if the same scene remains loaded between network sessions while the parenting is completely different from the original hierarchy. (#3387) +- Fixed issue where in-scene placed `NetworkObjects` could fail to synchronize its transform properly (especially without a NetworkTransform) if their parenting changes from the default when the scene is loaded and if the same scene remains loaded between network sessions while the parenting is completely different from the original hierarchy. (#3387) - Fixed an issue in `UnityTransport` where the transport would accept sends on invalid connections, leading to a useless memory allocation and confusing error message. (#3382) - Fixed issue where the time delta that interpolators used would not be properly updated during multiple fixed update invocations within the same player loop frame. (#3355) - Fixed issue when using a distributed authority network topology and many clients attempt to connect simultaneously the session owner could max-out the maximum in-flight reliable messages allowed, start dropping packets, and some of the connecting clients would fail to fully synchronize. (#3350) - Fixed issue when using a distributed authority network topology and scene management was disabled clients would not be able to spawn any new network prefab instances until synchronization was complete. (#3350) -- Fixed issue where an owner that changes ownership, when using a distributed authority network topology, could yield identical previous and current owner identifiers. This could also cause `NetworkTransform` to fail to change ownership which would leave the previous owner still subscribed to network tick events. (#3347) +- Fixed issue where an owner that changes ownership, when using a distributed authority network topology, could yield identical previous and current owner identifiers. This could also cause NetworkTransform to fail to change ownership which would leave the previous owner still subscribed to network tick events. (#3347) - Fixed issue where the `MaximumInterpolationTime` could not be modified from within the inspector view or runtime. (#3337) - Fixed `ChangeOwnership` changing ownership to clients that are not observers. This also happened with automated object distribution. (#3323) - Fixed issue where `AnticipatedNetworkVariable` previous value returned by `AnticipatedNetworkVariable.OnAuthoritativeValueChanged` is updated correctly on the non-authoritative side. (#3306) - Fixed `OnClientConnectedCallback` passing incorrect `clientId` when scene management is disabled. (#3312) - Fixed issue where the `NetworkObject.Ownership` custom editor did not take the default "Everything" flag into consideration. (#3305) - Fixed DestroyObject flow on non-authority game clients. (#3291) -- Fixed exception being thrown when a `GameObject` with an associated `NetworkTransform` is disabled. (#3243) +- Fixed exception being thrown when a `GameObject` with an associated NetworkTransform is disabled. (#3243) - Fixed issue where the scene migration synchronization table was not cleaned up if the `GameObject` of a `NetworkObject` is destroyed before it should have been. (#3230) -- Fixed issue where the scene migration synchronization table was not cleaned up upon `NetworkManager` shutting down. (#3230) +- Fixed issue where the scene migration synchronization table was not cleaned up upon NetworkManager shutting down. (#3230) - Fixed `NetworkObject.DeferDespawn` to respect the `DestroyGameObject` parameter. (#3219) -- Fixed issue where a `NetworkObject` with nested `NetworkTransform` components of varying authority modes was not being taken into consideration and would break both the initial `NetworkTransform` synchronization and fail to properly handle synchronized state updates of the nested `NetworkTransform` components. (#3209) +- Fixed issue where a `NetworkObject` with nested NetworkTransform components of varying authority modes was not being taken into consideration and would break both the initial NetworkTransform synchronization and fail to properly handle synchronized state updates of the nested NetworkTransform components. (#3209) - Fixed issue with distributing parented children that have the distributable and/or transferrable permissions set and have the same owner as the root parent, that has the distributable permission set, were not being distributed to the same client upon the owning client disconnecting when using a distributed authority network topology. (#3203) - Fixed issue where a spawned `NetworkObject` that was registered with a prefab handler and owned by a client would invoke destroy more than once on the host-server side if the client disconnected while the `NetworkObject` was still spawned. (#3200) - Fixed issue where `NetworkVariableBase` derived classes were not being re-initialized if the associated `NetworkObject` instance was not destroyed and re-spawned. (#3181) @@ -159,24 +159,24 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed -- Fixed issue where the server, host, or session owner would not populate the in-scene place `NetworkObject` table if the scene was loaded prior to starting the `NetworkManager`. (#3177) +- Fixed issue where the server, host, or session owner would not populate the in-scene place `NetworkObject` table if the scene was loaded prior to starting the NetworkManager. (#3177) - Fixed issue where the `NetworkObjectIdHash` value could be incorrect when entering play mode while still in prefab edit mode with pending changes and using MPPM. (#3162) -- Fixed issue where a sever only `NetworkManager` instance would spawn the actual `NetworkPrefab`'s `GameObject` as opposed to creating an instance of it. (#3160) +- Fixed issue where a sever only NetworkManager instance would spawn the actual `NetworkPrefab`'s `GameObject` as opposed to creating an instance of it. (#3160) - Fixed issue where only the session owner (as opposed to all clients) would handle spawning prefab overrides properly when using a distributed authority network topology. (#3160) - Fixed issue where an exception was thrown when calling `NetworkManager.Shutdown` after calling `UnityTransport.Shutdown`. (#3118) - Fixed issue where `NetworkList` properties on in-scene placed `NetworkObject`s could cause small memory leaks when entering playmode. (#3147) -- Fixed in-scene `NertworkObject` synchronization issue when loading a scene with currently connected clients connected to a session created by a `NetworkManager` started as a server (i.e. not as a host). (#3133) -- Fixed issue where a `NetworkManager` started as a server would not add itself as an observer to in-scene placed `NetworkObject`s instantiated and spawned by a scene loading event. (#3133) +- Fixed in-scene `NertworkObject` synchronization issue when loading a scene with currently connected clients connected to a session created by a NetworkManager started as a server (i.e. not as a host). (#3133) +- Fixed issue where a NetworkManager started as a server would not add itself as an observer to in-scene placed `NetworkObject`s instantiated and spawned by a scene loading event. (#3133) - Fixed issue where spawning a player using `NetworkObject.InstantiateAndSpawn` or `NetworkSpawnManager.InstantiateAndSpawn` would not update the `NetworkSpawnManager.PlayerObjects` or assign the newly spawned player to the `NetworkClient.PlayerObject`. (#3122) - Fixed issue where queued UnitTransport (NetworkTransport) message batches were being sent on the next frame. They are now sent at the end of the frame during `PostLateUpdate`. (#3113) - Fixed issue where `NotOwnerRpcTarget` or `OwnerRpcTarget` were not using their replacements `NotAuthorityRpcTarget` and `AuthorityRpcTarget` which would invoke a warning. (#3111) - Fixed issue where client is removed as an observer from spawned objects when their player instance is despawned. (#3110) -- Fixed issue where `NetworkAnimator` would statically allocate write buffer space for `Animator` parameters that could cause a write error if the number of parameters exceeded the space allocated. (#3108) +- Fixed issue where NetworkAnimator would statically allocate write buffer space for `Animator` parameters that could cause a write error if the number of parameters exceeded the space allocated. (#3108) ### Changed - In-scene placed `NetworkObject`s have been made distributable when balancing object distribution after a connection event. (#3175) -- Optimised `NetworkVariable` and `NetworkTransform` related packets when in Distributed Authority mode. +- Optimised `NetworkVariable` and NetworkTransform related packets when in Distributed Authority mode. - The Debug Simulator section of the Unity Transport component was removed. This section was not functional anymore and users are now recommended to use the more featureful [Network Simulator](https://docs-multiplayer.unity3d.com/tools/current/tools-network-simulator/) tool from the Multiplayer Tools package instead. (#3121) ## [2.1.1] - 2024-10-18 @@ -187,13 +187,13 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `IContactEventHandlerWithInfo` that derives from `IContactEventHandler` that can be updated per frame to provide `ContactEventHandlerInfo` information to the `RigidbodyContactEventManager` when processing collisions. (#3094) - `ContactEventHandlerInfo.ProvideNonRigidBodyContactEvents`: When set to true, non-`Rigidbody` collisions with the registered `Rigidbody` will generate contact event notifications. (#3094) - `ContactEventHandlerInfo.HasContactEventPriority`: When set to true, the `Rigidbody` will be prioritized as the instance that generates the event if the `Rigidbody` colliding does not have priority. (#3094) -- Added a static `NetworkManager.OnInstantiated` event notification to be able to track when a new `NetworkManager` instance has been instantiated. (#3088) -- Added a static `NetworkManager.OnDestroying` event notification to be able to track when an existing `NetworkManager` instance is being destroyed. (#3088) +- Added a static `NetworkManager.OnInstantiated` event notification to be able to track when a new NetworkManager instance has been instantiated. (#3088) +- Added a static `NetworkManager.OnDestroying` event notification to be able to track when an existing NetworkManager instance is being destroyed. (#3088) ### Fixed - Fixed issue where `NetworkPrefabProcessor` would not mark the prefab list as dirty and prevent saving the `DefaultNetworkPrefabs` asset when only imports or only deletes were detected.(#3103) -- Fixed an issue where nested `NetworkTransform` components in owner authoritative mode cleared their initial settings on the server, causing improper synchronization. (#3099) +- Fixed an issue where nested NetworkTransform components in owner authoritative mode cleared their initial settings on the server, causing improper synchronization. (#3099) - Fixed issue with service not getting synchronized with in-scene placed `NetworkObject` instances when a session owner starts a `SceneEventType.Load` event. (#3096) - Fixed issue with the in-scene network prefab instance update menu tool where it was not properly updating scenes when invoked on the root prefab instance. (#3092) - Fixed an issue where newly synchronizing clients would always receive current `NetworkVariable` values, potentially causing issues with collections if there were pending updates. Now, pending state updates serialize previous values to avoid duplicates on new clients. (#3081) @@ -206,7 +206,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed -- Changed `NetworkConfig.AutoSpawnPlayerPrefabClientSide` is no longer automatically set when starting `NetworkManager`. (#3097) +- Changed `NetworkConfig.AutoSpawnPlayerPrefabClientSide` is no longer automatically set when starting NetworkManager. (#3097) - Updated `NetworkVariableDeltaMessage` so the server now forwards delta state updates from clients immediately, instead of waiting until the end of the frame or the next network tick. (#3081) ## [2.0.0] - 2024-09-12 @@ -215,28 +215,28 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added tooltips for all of the `NetworkObject` component's properties. (#3052) - Added message size validation to named and unnamed message sending functions for better error messages. (#3049) -- Added "Check for NetworkObject Component" property to the Multiplayer->Netcode for GameObjects project settings. When disabled, this will bypass the in-editor `NetworkObject` check on `NetworkBehaviour` components. (#3031) +- Added "Check for NetworkObject Component" property to the Multiplayer->Netcode for GameObjects project settings. When disabled, this will bypass the in-editor `NetworkObject` check on NetworkBehaviour components. (#3031) - Added `NetworkTransform.SwitchTransformSpaceWhenParented` property that, when enabled, will handle the world to local, local to world, and local to local transform space transitions when interpolation is enabled. (#3013) -- Added `NetworkTransform.TickSyncChildren` that, when enabled, will tick synchronize nested and/or child `NetworkTransform` components to eliminate any potential visual jittering that could occur if the `NetworkTransform` instances get into a state where their state updates are landing on different network ticks. (#3013) +- Added `NetworkTransform.TickSyncChildren` that, when enabled, will tick synchronize nested and/or child NetworkTransform components to eliminate any potential visual jittering that could occur if the NetworkTransform instances get into a state where their state updates are landing on different network ticks. (#3013) - Added `NetworkObject.AllowOwnerToParent` property to provide the ability to allow clients to parent owned objects when running in a client-server network topology. (#3013) - Added `NetworkObject.SyncOwnerTransformWhenParented` property to provide a way to disable applying the server's transform information in the parenting message on the client owner instance which can be useful for owner authoritative motion models. (#3013) - Added `NetcodeEditorBase` editor helper class to provide easier modification and extension of the SDK's components. (#3013) ### Fixed -- Fixed issue where `NetworkAnimator` would send updates to non-observer clients. (#3057) +- Fixed issue where NetworkAnimator would send updates to non-observer clients. (#3057) - Fixed issue where an exception could occur when receiving a universal RPC for a `NetworkObject` that has been despawned. (#3052) - Fixed issue where a NetworkObject hidden from a client that is then promoted to be session owner was not being synchronized with newly joining clients.(#3051) - Fixed issue where clients could have a wrong time delta on `NetworkVariableBase` which could prevent from sending delta state updates. (#3045) - Fixed issue where setting a prefab hash value during connection approval but not having a player prefab assigned could cause an exception when spawning a player. (#3042) - Fixed issue where the `NetworkSpawnManager.HandleNetworkObjectShow` could throw an exception if one of the `NetworkObject` components to show was destroyed during the same frame. (#3030) - Fixed issue where the `NetworkManagerHelper` was continuing to check for hierarchy changes when in play mode. (#3026) -- Fixed issue with newly/late joined clients and `NetworkTransform` synchronization of parented `NetworkObject` instances. (#3013) +- Fixed issue with newly/late joined clients and NetworkTransform synchronization of parented `NetworkObject` instances. (#3013) - Fixed issue with smooth transitions between transform spaces when interpolation is enabled (requires `NetworkTransform.SwitchTransformSpaceWhenParented` to be enabled). (#3013) ### Changed -- Changed `NetworkTransformEditor` now uses `NetworkTransform` as the base type class to assure it doesn't display a foldout group when using the base `NetworkTransform` component class. (#3052) +- Changed `NetworkTransformEditor` now uses NetworkTransform as the base type class to assure it doesn't display a foldout group when using the base NetworkTransform component class. (#3052) - Changed `NetworkAnimator.Awake` is now a protected virtual method. (#3052) - Changed when invoking `NetworkManager.ConnectionManager.DisconnectClient` during a distributed authority session a more appropriate message is logged. (#3052) - Changed `NetworkTransformEditor` so it now derives from `NetcodeEditorBase`. (#3013) @@ -252,10 +252,10 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed -- Fixed issue where nested `NetworkTransform` components were not getting updated. (#3016) -- Fixed issue by adding null checks in `NetworkVariableBase.CanClientRead` and `NetworkVariableBase.CanClientWrite` methods to ensure safe access to `NetworkBehaviour`. (#3012) +- Fixed issue where nested NetworkTransform components were not getting updated. (#3016) +- Fixed issue by adding null checks in `NetworkVariableBase.CanClientRead` and `NetworkVariableBase.CanClientWrite` methods to ensure safe access to NetworkBehaviour. (#3012) - Fixed issue where `FixedStringSerializer<T>` was using `NetworkVariableSerialization<byte>.AreEqual` to determine if two bytes were equal causes an exception to be thrown due to no byte serializer having been defined. (#3009) -- Fixed Issue where a state with dual triggers, inbound and outbound, could cause a false layer to layer state transition message to be sent to non-authority `NetworkAnimator` instances and cause a warning message to be logged. (#3008) +- Fixed Issue where a state with dual triggers, inbound and outbound, could cause a false layer to layer state transition message to be sent to non-authority NetworkAnimator instances and cause a warning message to be logged. (#3008) - Fixed issue using collections within `NetworkVariable` where the collection would not detect changes to items or nested items. (#3004) - Fixed issue where `List`, `Dictionary`, and `HashSet` collections would not uniquely duplicate nested collections. (#3004) - Fixed issue where `NotAuthorityTarget` would include the service observer in the list of targets to send the RPC to as opposed to excluding the service observer as it should. (#3000) @@ -263,7 +263,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed -- Changed `NetworkAnimator` to automatically switch to owner authoritative mode when using a distributed authority network topology. (#3021) +- Changed NetworkAnimator to automatically switch to owner authoritative mode when using a distributed authority network topology. (#3021) - Changed permissions exception thrown in `NetworkList` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3004) - Changed permissions exception thrown in `NetworkVariable.Value` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3004) @@ -307,17 +307,17 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed -- Fixed issue when `NetworkTransform` half float precision is enabled and ownership changes the current base position was not being synchronized. (#2948) +- Fixed issue when NetworkTransform half float precision is enabled and ownership changes the current base position was not being synchronized. (#2948) - Fixed issue where `OnClientConnected` not being invoked on the session owner when connecting to a new distributed authority session. (#2948) - Fixed issue where Rigidbody micro-motion (i.e. relatively small velocities) would result in non-authority instances slightly stuttering as the body would come to a rest (i.e. no motion). Now, the threshold value can increase at higher velocities and can decrease slightly below the provided threshold to account for this. (#2948) ### Changed -- Changed `NetworkAnimator` no longer requires the `Animator` component to exist on the same `GameObject`. (#2957) +- Changed NetworkAnimator no longer requires the `Animator` component to exist on the same `GameObject`. (#2957) - Changed `NetworkObjectReference` and `NetworkBehaviourReference` to allow null references when constructing and serializing. (#2957) - Changed the client's owned objects is now returned (`NetworkClient` and `NetworkSpawnManager`) as an array as opposed to a list for performance purposes. (#2948) - Changed `NetworkTransfrom.TryCommitTransformToServer` to be internal as it will be removed by the final 2.0.0 release. (#2948) -- Changed `NetworkTransformEditor.OnEnable` to a virtual method to be able to customize a `NetworkTransform` derived class by creating a derived editor control from `NetworkTransformEditor`. (#2948) +- Changed `NetworkTransformEditor.OnEnable` to a virtual method to be able to customize a NetworkTransform derived class by creating a derived editor control from `NetworkTransformEditor`. (#2948) ## [2.0.0-exp.5] - 2024-06-03 @@ -339,15 +339,15 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `NetworkRigidbodyBase.AttachToFixedJoint` and `NetworkRigidbodyBase.DetachFromFixedJoint` to replace parenting for rigid bodies that have `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled. (#2933) - Added `NetworkBehaviour.OnNetworkPreSpawn` and `NetworkBehaviour.OnNetworkPostSpawn` methods that provide the ability to handle pre and post spawning actions during the `NetworkObject` spawn sequence. (#2912) -- Added a client-side only `NetworkBehaviour.OnNetworkSessionSynchronized` convenience method that is invoked on all `NetworkBehaviour`s after a newly joined client has finished synchronizing with the network session in progress. (#2912) +- Added a client-side only `NetworkBehaviour.OnNetworkSessionSynchronized` convenience method that is invoked on all NetworkBehaviours after a newly joined client has finished synchronizing with the network session in progress. (#2912) - Added `NetworkBehaviour.OnInSceneObjectsSpawned` convenience method that is invoked when all in-scene `NetworkObject`s have been spawned after a scene has been loaded or upon a host or server starting. (#2912) ### Fixed - Fixed issue where non-authoritative rigid bodies with `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled would constantly log errors about the renderTime being before `StartTimeConsumed`. (#2933) - Fixed issue where in-scene placed NetworkObjects could be destroyed if a client disconnects early and/or before approval. (#2924) -- Fixed issue where a `NetworkObject` component's associated `NetworkBehaviour` components would not be detected if scene loading is disabled in the editor and the currently loaded scene has in-scene placed `NetworkObject`s. (#2912) -- Fixed issue where an in-scene placed `NetworkObject` with `NetworkTransform` that is also parented under a `GameObject` would not properly synchronize when the parent `GameObject` had a world space position other than 0,0,0. (#2898) +- Fixed issue where a `NetworkObject` component's associated NetworkBehaviour components would not be detected if scene loading is disabled in the editor and the currently loaded scene has in-scene placed `NetworkObject`s. (#2912) +- Fixed issue where an in-scene placed `NetworkObject` with NetworkTransform that is also parented under a `GameObject` would not properly synchronize when the parent `GameObject` had a world space position other than 0,0,0. (#2898) ### Changed @@ -361,13 +361,13 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added - Added updates to all internal messages to account for a distributed authority network session connection. (#2863) -- Added `NetworkRigidbodyBase` that provides users with a more customizable network rigidbody, handles both `Rigidbody` and `Rigidbody2D`, and provides an option to make `NetworkTransform` use the rigid body for motion. (#2863) +- Added `NetworkRigidbodyBase` that provides users with a more customizable network rigidbody, handles both `Rigidbody` and `Rigidbody2D`, and provides an option to make NetworkTransform use the rigid body for motion. (#2863) - For a customized `NetworkRigidbodyBase` class: - `NetworkRigidbodyBase.AutoUpdateKinematicState` provides control on whether the kinematic setting will be automatically set or not when ownership changes. - `NetworkRigidbodyBase.AutoSetKinematicOnDespawn` provides control on whether isKinematic will automatically be set to true when the associated `NetworkObject` is despawned. - `NetworkRigidbodyBase.Initialize` is a protected method that, when invoked, will initialize the instance. This includes options to: - Set whether using a `RigidbodyTypes.Rigidbody` or `RigidbodyTypes.Rigidbody2D`. - - Includes additional optional parameters to set the `NetworkTransform`, `Rigidbody`, and `Rigidbody2d` to use. + - Includes additional optional parameters to set the NetworkTransform, `Rigidbody`, and `Rigidbody2d` to use. - Provides additional public methods: - `NetworkRigidbodyBase.GetPosition` to return the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.GetRotation` to return the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). @@ -383,13 +383,13 @@ Additional documentation and release notes are available at [Multiplayer Documen - `NetworkRigidbodyBase.IsKinematic` to determine if the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) is currently kinematic. - `NetworkRigidbodyBase.SetIsKinematic` to set the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) current kinematic state. - `NetworkRigidbodyBase.ResetInterpolation` to reset the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) back to its original interpolation value when initialized. - - Now includes a `MonoBehaviour.FixedUpdate` implementation that will update the assigned `NetworkTransform` when `NetworkRigidbodyBase.UseRigidBodyForMotion` is true. (#2863) + - Now includes a `MonoBehaviour.FixedUpdate` implementation that will update the assigned NetworkTransform when `NetworkRigidbodyBase.UseRigidBodyForMotion` is true. (#2863) - Added `RigidbodyContactEventManager` that provides a more optimized way to process collision enter and collision stay events as opposed to the `Monobehaviour` approach. (#2863) - Can be used in client-server and distributed authority modes, but is particularly useful in distributed authority. -- Added rigid body motion updates to `NetworkTransform` which allows users to set interolation on rigid bodies. (#2863) - - Extrapolation is only allowed on authoritative instances, but custom class derived from `NetworkRigidbodyBase` or `NetworkRigidbody` or `NetworkRigidbody2D` automatically switches non-authoritative instances to interpolation if set to extrapolation. -- Added distributed authority mode support to `NetworkAnimator`. (#2863) -- Added session mode selection to `NetworkManager` inspector view. (#2863) +- Added rigid body motion updates to NetworkTransform which allows users to set interolation on rigid bodies. (#2863) + - Extrapolation is only allowed on authoritative instances, but custom class derived from `NetworkRigidbodyBase` or NetworkRigidBody or `NetworkRigidbody2D` automatically switches non-authoritative instances to interpolation if set to extrapolation. +- Added distributed authority mode support to NetworkAnimator. (#2863) +- Added session mode selection to NetworkManager inspector view. (#2863) - Added distributed authority permissions feature. (#2863) - Added distributed authority mode specific `NetworkObject` permissions flags (Distributable, Transferable, and RequestRequired). (#2863) - Added distributed authority mode specific `NetworkObject.SetOwnershipStatus` method that applies one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863) @@ -409,8 +409,8 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added distributed authority mode specific `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property (default is true) to provide control over the automatic spawning of player prefabs on the local client side. (#2863) - Added distributed authority mode specific `NetworkManager.OnFetchLocalPlayerPrefabToSpawn` callback that, when assigned, will allow the local client to provide the player prefab to be spawned for the local client. (#2863) - This is only invoked if the `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property is set to true. -- Added distributed authority mode specific `NetworkBehaviour.HasAuthority` property that determines if the local client has authority over the associated `NetworkObject` instance (typical use case is within a `NetworkBehaviour` script much like that of `IsServer` or `IsClient`). (#2863) -- Added distributed authority mode specific `NetworkBehaviour.IsSessionOwner` property that determines if the local client is the session owner (typical use case would be to determine if the local client can has scene management authority within a `NetworkBehaviour` script). (#2863) +- Added distributed authority mode specific `NetworkBehaviour.HasAuthority` property that determines if the local client has authority over the associated `NetworkObject` instance (typical use case is within a NetworkBehaviour script much like that of `IsServer` or `IsClient`). (#2863) +- Added distributed authority mode specific `NetworkBehaviour.IsSessionOwner` property that determines if the local client is the session owner (typical use case would be to determine if the local client can has scene management authority within a NetworkBehaviour script). (#2863) - Added support for distributed authority mode scene management where the currently assigned session owner can start scene events (i.e. scene loading and scene unloading). (#2863) ### Fixed @@ -423,18 +423,18 @@ Additional documentation and release notes are available at [Multiplayer Documen - Changed client side awareness of other clients is now the same as a server or host. (#2863) - Changed `NetworkManager.ConnectedClients` can now be accessed by both server and clients. (#2863) - Changed `NetworkManager.ConnectedClientsList` can now be accessed by both server and clients. (#2863) -- Changed `NetworkTransform` defaults to owner authoritative when connected to a distributed authority session. (#2863) +- Changed NetworkTransform defaults to owner authoritative when connected to a distributed authority session. (#2863) - Changed `NetworkVariable` defaults to owner write and everyone read permissions when connected to a distributed authority session (even if declared with server read or write permissions). (#2863) -- Changed `NetworkObject` no longer implements the `MonoBehaviour.Update` method in order to determine whether a `NetworkObject` instance has been migrated to a different scene. Instead, only `NetworkObjects` with the `SceneMigrationSynchronization` property set will be updated internally during the `NetworkUpdateStage.PostLateUpdate` by `NetworkManager`. (#2863) -- Changed `NetworkManager` inspector view layout where properties are now organized by category. (#2863) -- Changed `NetworkTransform` to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810) +- Changed `NetworkObject` no longer implements the `MonoBehaviour.Update` method in order to determine whether a `NetworkObject` instance has been migrated to a different scene. Instead, only `NetworkObjects` with the `SceneMigrationSynchronization` property set will be updated internally during the `NetworkUpdateStage.PostLateUpdate` by NetworkManager. (#2863) +- Changed NetworkManager inspector view layout where properties are now organized by category. (#2863) +- Changed NetworkTransform to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810) - Changed `CustomMessageManager` so it no longer attempts to register or "unregister" a null or empty string and will log an error if this condition occurs. (#2807) ## [1.9.1] - 2024-04-18 ### Added - Added `AnticipatedNetworkVariable<T>`, which adds support for client anticipation of NetworkVariable values, allowing for more responsive game play (#2820) -- Added `AnticipatedNetworkTransform`, which adds support for client anticipation of `NetworkTransform`s (#2820) +- Added `AnticipatedNetworkTransform`, which adds support for client anticipation of NetworkTransforms (#2820) - Added `NetworkVariableBase.ExceedsDirtinessThreshold` to allow network variables to throttle updates by only sending updates when the difference between the current and previous values exceeds a threshold. (This is exposed in NetworkVariable<T> with the callback NetworkVariable<T>.CheckExceedsDirtinessThreshold) (#2820) - Added `NetworkVariableUpdateTraits`, which add additional throttling support: `MinSecondsBetweenUpdates` will prevent the `NetworkVariable` from sending updates more often than the specified time period (even if it exceeds the dirtiness threshold), while `MaxSecondsBetweenUpdates` will force a dirty `NetworkVariable` to send an update after the specified time period even if it has not yet exceeded the dirtiness threshold. (#2820) - Added virtual method `NetworkVariableBase.OnInitialize()` which can be used by `NetworkVariable` subclasses to add initialization code (#2820) @@ -448,7 +448,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed - Fixed issue where `NetworkTransformEditor` would throw and exception if you excluded the physics package. (#2871) -- Fixed issue where `NetworkTransform` could not properly synchronize its base position when using half float precision. (#2845) +- Fixed issue where NetworkTransform could not properly synchronize its base position when using half float precision. (#2845) - Fixed issue where the host was not invoking `OnClientDisconnectCallback` for its own local client when internally shutting down. (#2822) - Fixed issue where NetworkTransform could potentially attempt to "unregister" a named message prior to it being registered. (#2807) - Fixed issue where in-scene placed `NetworkObject`s with complex nested children `NetworkObject`s (more than one child in depth) would not synchronize properly if WorldPositionStays was set to true. (#2796) @@ -456,8 +456,8 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed - Changed `NetworkObjectReference` and `NetworkBehaviourReference` to allow null references when constructing and serializing. (#2874) -- Changed `NetworkAnimator` no longer requires the `Animator` component to exist on the same `GameObject`. (#2872) -- Changed `NetworkTransform` to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810) +- Changed NetworkAnimator no longer requires the `Animator` component to exist on the same `GameObject`. (#2872) +- Changed NetworkTransform to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810) - Changed `CustomMessageManager` so it no longer attempts to register or "unregister" a null or empty string and will log an error if this condition occurs. (#2807) ## [1.8.1] - 2024-02-05 @@ -490,10 +490,10 @@ Additional documentation and release notes are available at [Multiplayer Documen - Fixed issue where if a host or server shutdown while a client owned NetworkObjects (other than the player) it would throw an exception. (#2789) - Fixed issue where setting values on a `NetworkVariable` or `NetworkList` within `OnNetworkDespawn` during a shutdown sequence would throw an exception. (#2789) - Fixed issue where a teleport state could potentially be overridden by a previous unreliable delta state. (#2777) -- Fixed issue where `NetworkTransform` was using the `NetworkManager.ServerTime.Tick` as opposed to `NetworkManager.NetworkTickSystem.ServerTime.Tick` during the authoritative side's tick update where it performed a delta state check. (#2777) +- Fixed issue where NetworkTransform was using the `NetworkManager.ServerTime.Tick` as opposed to `NetworkManager.NetworkTickSystem.ServerTime.Tick` during the authoritative side's tick update where it performed a delta state check. (#2777) - Fixed issue where a parented in-scene placed NetworkObject would be destroyed upon a client or server exiting a network session but not unloading the original scene in which the NetworkObject was placed. (#2737) - Fixed issue where during client synchronization and scene loading, when client synchronization or the scene loading mode are set to `LoadSceneMode.Single`, a `CreateObjectMessage` could be received, processed, and the resultant spawned `NetworkObject` could be instantiated in the client's currently active scene that could, towards the end of the client synchronization or loading process, be unloaded and cause the newly created `NetworkObject` to be destroyed (and throw and exception). (#2735) -- Fixed issue where a `NetworkTransform` instance with interpolation enabled would result in wide visual motion gaps (stuttering) under above normal latency conditions and a 1-5% or higher packet are drop rate. (#2713) +- Fixed issue where a NetworkTransform instance with interpolation enabled would result in wide visual motion gaps (stuttering) under above normal latency conditions and a 1-5% or higher packet are drop rate. (#2713) - Fixed issue where you could not have multiple source network prefab overrides targeting the same network prefab as their override. (#2710) ### Changed @@ -503,7 +503,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Changed `NetworkTransform.SetState` (and related methods) now are cumulative during a fractional tick period and sent on the next pending tick. (#2777) - `NetworkManager.ConnectedClientsIds` is now accessible on the client side and will contain the list of all clients in the session, including the host client if the server is operating in host mode (#2762) - Changed `NetworkSceneManager` to return a `SceneEventProgress` status and not throw exceptions for methods invoked when scene management is disabled and when a client attempts to access a `NetworkSceneManager` method by a client. (#2735) -- Changed `NetworkTransform` authoritative instance tick registration so a single `NetworkTransform` specific tick event update will update all authoritative instances to improve perofmance. (#2713) +- Changed NetworkTransform authoritative instance tick registration so a single NetworkTransform specific tick event update will update all authoritative instances to improve perofmance. (#2713) - Changed `NetworkPrefabs.OverrideToNetworkPrefab` dictionary is no longer used/populated due to it ending up being related to a regression bug and not allowing more than one override to be assigned to a network prefab asset. (#2710) - Changed in-scene placed `NetworkObject`s now store their source network prefab asset's `GlobalObjectIdHash` internally that is used, when scene management is disabled, by clients to spawn the correct prefab even if the `NetworkPrefab` entry has an override. This does not impact dynamically spawning the same prefab which will yield the override on both host and client. (#2710) - Changed in-scene placed `NetworkObject`s no longer require a `NetworkPrefab` entry with `GlobalObjectIdHash` override in order for clients to properly synchronize. (#2710) @@ -518,7 +518,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed - Fixed a bug where having a class with Rpcs that inherits from a class without Rpcs that inherits from NetworkVariable would cause a compile error. (#2751) -- Fixed issue where `NetworkBehaviour.Synchronize` was not truncating the write buffer if nothing was serialized during `NetworkBehaviour.OnSynchronize` causing an additional 6 bytes to be written per `NetworkBehaviour` component instance. (#2749) +- Fixed issue where `NetworkBehaviour.Synchronize` was not truncating the write buffer if nothing was serialized during `NetworkBehaviour.OnSynchronize` causing an additional 6 bytes to be written per NetworkBehaviour component instance. (#2749) ## [1.7.0] - 2023-10-11 @@ -530,7 +530,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `GenerateSerializationForGenericParameterAttribute`, which can be applied to user-created Network Variable types to ensure the codegen generates serialization for the generic types they wrap. (#2694) - Added `GenerateSerializationForTypeAttribute`, which can be applied to any class or method to ensure the codegen generates serialization for the specific provided type. (#2694) - Exposed `NetworkVariableSerialization<T>.Read`, `NetworkVariableSerialization<T>.Write`, `NetworkVariableSerialization<T>.AreEqual`, and `NetworkVariableSerialization<T>.Duplicate` to further support the creation of user-created network variables by allowing users to access the generated serialization methods and serialize generic types efficiently without boxing. (#2694) -- Added `NetworkVariableBase.MarkNetworkBehaviourDirty` so that user-created network variable types can mark their containing `NetworkBehaviour` to be processed by the update loop. (#2694) +- Added `NetworkVariableBase.MarkNetworkBehaviourDirty` so that user-created network variable types can mark their containing NetworkBehaviour to be processed by the update loop. (#2694) ### Fixed @@ -542,9 +542,9 @@ Additional documentation and release notes are available at [Multiplayer Documen - NetworkVariables of non-integer types will no longer break the inspector (#2714) - NetworkVariables with NonSerializedAttribute will not appear in the inspector (#2714) - Fixed issue where `UnityTransport` would attempt to establish WebSocket connections even if using UDP/DTLS Relay allocations when the build target was WebGL. This only applied to working in the editor since UDP/DTLS can't work in the browser. (#2695) -- Fixed issue where a `NetworkBehaviour` component's `OnNetworkDespawn` was not being invoked on the host-server side for an in-scene placed `NetworkObject` when a scene was unloaded (during a scene transition) and the `NetworkBehaviour` component was positioned/ordered before the `NetworkObject` component. (#2685) +- Fixed issue where a NetworkBehaviour component's `OnNetworkDespawn` was not being invoked on the host-server side for an in-scene placed `NetworkObject` when a scene was unloaded (during a scene transition) and the NetworkBehaviour component was positioned/ordered before the `NetworkObject` component. (#2685) - Fixed issue where `SpawnWithObservers` was not being honored when `NetworkConfig.EnableSceneManagement` was disabled. (#2682) -- Fixed issue where `NetworkAnimator` was not internally tracking changes to layer weights which prevented proper layer weight synchronization back to the original layer weight value. (#2674) +- Fixed issue where NetworkAnimator was not internally tracking changes to layer weights which prevented proper layer weight synchronization back to the original layer weight value. (#2674) - Fixed "writing past the end of the buffer" error when calling ResetDirty() on managed network variables that are larger than 256 bytes when serialized. (#2670) - Fixed issue where generation of the `DefaultNetworkPrefabs` asset was not enabled by default. (#2662) - Fixed issue where the `GlobalObjectIdHash` value could be updated but the asset not marked as dirty. (#2662) @@ -580,7 +580,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Bumped minimum Unity version supported to 2021.3 LTS - 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 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) @@ -636,14 +636,14 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `NetworkTransform.UseQuaternionSynchronization` property that, when enabled, will synchronize the entire quaternion. (#2388) - Added `NetworkTransform.UseQuaternionCompression` property that, when enabled, will use a smallest three implementation reducing a full quaternion synchronization update to the size of an unsigned integer. (#2388) - Added `NetworkTransform.SlerpPosition` property that, when enabled along with interpolation being enabled, will interpolate using `Vector3.Slerp`. (#2388) -- Added `BufferedLinearInterpolatorVector3` that replaces the float version, is now used by `NetworkTransform`, and provides the ability to enable or disable `Slerp`. (#2388) +- Added `BufferedLinearInterpolatorVector3` that replaces the float version, is now used by NetworkTransform, and provides the ability to enable or disable `Slerp`. (#2388) - Added `HalfVector3` used for scale when half float precision is enabled. (#2388) - Added `HalfVector4` used for rotation when half float precision and quaternion synchronization is enabled. (#2388) - Added `HalfVector3DeltaPosition` used for position when half float precision is enabled. This handles loss in position precision by updating only the delta position as opposed to the full position. (#2388) - Added `NetworkTransform.GetSpaceRelativePosition` and `NetworkTransform.GetSpaceRelativeRotation` helper methods to return the proper values depending upon whether local or world space. (#2388) - Added `NetworkTransform.OnAuthorityPushTransformState` virtual method that is invoked just prior to sending the `NetworkTransformState` to non-authoritative instances. This provides users with the ability to obtain more precise delta values for prediction related calculations. (#2388) - Added `NetworkTransform.OnNetworkTransformStateUpdated` virtual method that is invoked just after the authoritative `NetworkTransformState` is applied. This provides users with the ability to obtain more precise delta values for prediction related calculations. (#2388) -- Added `NetworkTransform.OnInitialize`virtual method that is invoked after the `NetworkTransform` has been initialized or re-initialized when ownership changes. This provides for a way to make adjustments when `NetworkTransform` is initialized (i.e. resetting client prediction etc) (#2388) +- Added `NetworkTransform.OnInitialize`virtual method that is invoked after the NetworkTransform has been initialized or re-initialized when ownership changes. This provides for a way to make adjustments when NetworkTransform is initialized (i.e. resetting client prediction etc) (#2388) - Added `NetworkObject.SynchronizeTransform` property (default is true) that provides users with another way to help with bandwidth optimizations where, when set to false, the `NetworkObject`'s associated transform will not be included when spawning and/or synchronizing late joining players. (#2388) - Added `NetworkSceneManager.ActiveSceneSynchronizationEnabled` property, disabled by default, that enables client synchronization of server-side active scene changes. (#2383) - Added `NetworkObject.ActiveSceneSynchronization`, disabled by default, that will automatically migrate a `NetworkObject` to a newly assigned active scene. (#2383) @@ -652,25 +652,25 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed - Made sure the `CheckObjectVisibility` delegate is checked and applied, upon `NetworkShow` attempt. Found while supporting (#2454), although this is not a fix for this (already fixed) issue. (#2463) -- Changed `NetworkTransform` authority handles delta checks on each new network tick and no longer consumes processing cycles checking for deltas for all frames in-between ticks. (#2388) +- Changed NetworkTransform authority handles delta checks on each new network tick and no longer consumes processing cycles checking for deltas for all frames in-between ticks. (#2388) - Changed the `NetworkTransformState` structure is now public and now has public methods that provide access to key properties of the `NetworkTransformState` structure. (#2388) -- Changed `NetworkTransform` interpolation adjusts its interpolation "ticks ago" to be 2 ticks latent if it is owner authoritative and the instance is not the server or 1 tick latent if the instance is the server and/or is server authoritative. (#2388) +- Changed NetworkTransform interpolation adjusts its interpolation "ticks ago" to be 2 ticks latent if it is owner authoritative and the instance is not the server or 1 tick latent if the instance is the server and/or is server authoritative. (#2388) - Updated `NetworkSceneManager` to migrate dynamically spawned `NetworkObject`s with `DestroyWithScene` set to false into the active scene if their current scene is unloaded. (#2383) - Updated the server to synchronize its local `NetworkSceneManager.ClientSynchronizationMode` during the initial client synchronization. (#2383) ### Fixed - Fixed issue where during client synchronization the synchronizing client could receive a ObjectSceneChanged message before the client-side NetworkObject instance had been instantiated and spawned. (#2502) -- Fixed issue where `NetworkAnimator` was building client RPC parameters to exclude the host from sending itself messages but was not including it in the ClientRpc parameters. (#2492) -- Fixed issue where `NetworkAnimator` was not properly detecting and synchronizing cross fade initiated transitions. (#2481) -- Fixed issue where `NetworkAnimator` was not properly synchronizing animation state updates. (#2481) +- Fixed issue where NetworkAnimator was building client RPC parameters to exclude the host from sending itself messages but was not including it in the ClientRpc parameters. (#2492) +- Fixed issue where NetworkAnimator was not properly detecting and synchronizing cross fade initiated transitions. (#2481) +- Fixed issue where NetworkAnimator was not properly synchronizing animation state updates. (#2481) - Fixed float NetworkVariables not being rendered properly in the inspector of NetworkObjects. (#2441) - Fixed an issue where Named Message Handlers could remove themselves causing an exception when the metrics tried to access the name of the message.(#2426) -- Fixed registry of public `NetworkVariable`s in derived `NetworkBehaviour`s (#2423) -- Fixed issue where runtime association of `Animator` properties to `AnimationCurve`s would cause `NetworkAnimator` to attempt to update those changes. (#2416) -- Fixed issue where `NetworkAnimator` would not check if its associated `Animator` was valid during serialization and would spam exceptions in the editor console. (#2416) -- Fixed issue with a child's rotation rolling over when interpolation is enabled on a `NetworkTransform`. Now using half precision or full quaternion synchronization will always update all axis. (#2388) -- Fixed issue where `NetworkTransform` was not setting the teleport flag when the `NetworkTransform.InLocalSpace` value changed. This issue only impacted `NetworkTransform` when interpolation was enabled. (#2388) +- Fixed registry of public `NetworkVariable`s in derived NetworkBehaviours (#2423) +- Fixed issue where runtime association of `Animator` properties to `AnimationCurve`s would cause NetworkAnimator to attempt to update those changes. (#2416) +- Fixed issue where NetworkAnimator would not check if its associated `Animator` was valid during serialization and would spam exceptions in the editor console. (#2416) +- Fixed issue with a child's rotation rolling over when interpolation is enabled on a NetworkTransform. Now using half precision or full quaternion synchronization will always update all axis. (#2388) +- Fixed issue where NetworkTransform was not setting the teleport flag when the `NetworkTransform.InLocalSpace` value changed. This issue only impacted NetworkTransform when interpolation was enabled. (#2388) - Fixed issue when the `NetworkSceneManager.ClientSynchronizationMode` is `LoadSceneMode.Additive` and the server changes the currently active scene prior to a client connecting then upon a client connecting and being synchronized the NetworkSceneManager would clear its internal ScenePlacedObjects list that could already be populated. (#2383) - Fixed issue where a client would load duplicate scenes of already preloaded scenes during the initial client synchronization and `NetworkSceneManager.ClientSynchronizationMode` was set to `LoadSceneMode.Additive`. (#2383) @@ -700,20 +700,20 @@ Additional documentation and release notes are available at [Multiplayer Documen - Fixed server side issue where, depending upon component ordering, some NetworkBehaviour components might not have their OnNetworkDespawn method invoked if the client side disconnected. (#2323) - Fixed a case where data corruption could occur when using UnityTransport when reaching a certain level of send throughput. (#2332) - Fixed an issue in `UnityTransport` where an exception would be thrown if starting a Relay host/server on WebGL. This exception should only be thrown if using direct connections (where WebGL can't act as a host/server). (#2321) -- Fixed `NetworkAnimator` issue where it was not checking for `AnimatorStateTtansition.destinationStateMachine` and any possible sub-states defined within it. (#2309) -- Fixed `NetworkAnimator` issue where the host client was receiving the ClientRpc animation updates when the host was the owner.(#2309) -- Fixed `NetworkAnimator` issue with using pooled objects and when specific properties are cleaned during despawn and destroy.(#2309) -- Fixed issue where `NetworkAnimator` was checking for animation changes when the associated `NetworkObject` was not spawned.(#2309) +- Fixed NetworkAnimator issue where it was not checking for `AnimatorStateTtansition.destinationStateMachine` and any possible sub-states defined within it. (#2309) +- Fixed NetworkAnimator issue where the host client was receiving the ClientRpc animation updates when the host was the owner.(#2309) +- Fixed NetworkAnimator issue with using pooled objects and when specific properties are cleaned during despawn and destroy.(#2309) +- Fixed issue where NetworkAnimator was checking for animation changes when the associated `NetworkObject` was not spawned.(#2309) - Corrected an issue with the documentation for BufferSerializer (#2401) ## [1.2.0] - 2022-11-21 ### Added -- Added protected method `NetworkBehaviour.OnSynchronize` which is invoked during the initial `NetworkObject` synchronization process. This provides users the ability to include custom serialization information that will be applied to the `NetworkBehaviour` prior to the `NetworkObject` being spawned. (#2298) +- Added protected method `NetworkBehaviour.OnSynchronize` which is invoked during the initial `NetworkObject` synchronization process. This provides users the ability to include custom serialization information that will be applied to the NetworkBehaviour prior to the `NetworkObject` being spawned. (#2298) - Added support for different versions of the SDK to talk to each other in circumstances where changes permit it. Starting with this version and into future versions, patch versions should be compatible as long as the minor version is the same. (#2290) - Added `NetworkObject` auto-add helper and Multiplayer Tools install reminder settings to Project Settings. (#2285) -- Added `public string DisconnectReason` getter to `NetworkManager` and `string Reason` to `ConnectionApprovalResponse`. Allows connection approval to communicate back a reason. Also added `public void DisconnectClient(ulong clientId, string reason)` allowing setting a disconnection reason, when explicitly disconnecting a client. (#2280) +- Added `public string DisconnectReason` getter to NetworkManager and `string Reason` to `ConnectionApprovalResponse`. Allows connection approval to communicate back a reason. Also added `public void DisconnectClient(ulong clientId, string reason)` allowing setting a disconnection reason, when explicitly disconnecting a client. (#2280) ### Changed @@ -723,14 +723,14 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed - Fixed `IsSpawnedObjectsPendingInDontDestroyOnLoad` is only set to true when loading a scene using `LoadSceneMode.Singleonly`. (#2330) -- Fixed issue where `NetworkTransform` components nested under a parent with a `NetworkObject` component (i.e. network prefab) would not have their associated `GameObject`'s transform synchronized. (#2298) +- Fixed issue where NetworkTransform components nested under a parent with a `NetworkObject` component (i.e. network prefab) would not have their associated `GameObject`'s transform synchronized. (#2298) - Fixed issue where `NetworkObject`s that failed to instantiate could cause the entire synchronization pipeline to be disrupted/halted for a connecting client. (#2298) - Fixed issue where in-scene placed `NetworkObject`s nested under a `GameObject` would be added to the orphaned children list causing continual console warning log messages. (#2298) - Custom messages are now properly received by the local client when they're sent while running in host mode. (#2296) - Fixed issue where the host would receive more than one event completed notification when loading or unloading a scene only when no clients were connected. (#2292) - Fixed an issue in `UnityTransport` where an error would be logged if the 'Use Encryption' flag was enabled with a Relay configuration that used a secure protocol. (#2289) - Fixed issue where in-scene placed `NetworkObjects` were not honoring the `AutoObjectParentSync` property. (#2281) -- Fixed the issue where `NetworkManager.OnClientConnectedCallback` was being invoked before in-scene placed `NetworkObject`s had been spawned when starting `NetworkManager` as a host. (#2277) +- Fixed the issue where `NetworkManager.OnClientConnectedCallback` was being invoked before in-scene placed `NetworkObject`s had been spawned when starting NetworkManager as a host. (#2277) - Creating a `FastBufferReader` with `Allocator.None` will not result in extra memory being allocated for the buffer (since it's owned externally in that scenario). (#2265) ### Removed @@ -744,7 +744,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `NetworkManager.IsApproved` flag that is set to `true` a client has been approved.(#2261) - `UnityTransport` now provides a way to set the Relay server data directly from the `RelayServerData` structure (provided by the Unity Transport package) throuh its `SetRelayServerData` method. This allows making use of the new APIs in UTP 1.3 that simplify integration of the Relay SDK. (#2235) - IPv6 is now supported for direct connections when using `UnityTransport`. (#2232) -- Added WebSocket support when using UTP 2.0 with `UseWebSockets` property in the `UnityTransport` component of the `NetworkManager` allowing to pick WebSockets for communication. When building for WebGL, this selection happens automatically. (#2201) +- Added WebSocket support when using UTP 2.0 with `UseWebSockets` property in the `UnityTransport` component of the NetworkManager allowing to pick WebSockets for communication. When building for WebGL, this selection happens automatically. (#2201) - Added position, rotation, and scale to the `ParentSyncMessage` which provides users the ability to specify the final values on the server-side when `OnNetworkObjectParentChanged` is invoked just before the message is created (when the `Transform` values are applied to the message). (#2146) - Added `NetworkObject.TryRemoveParent` method for convenience purposes opposed to having to cast null to either `GameObject` or `NetworkObject`. (#2146) @@ -791,9 +791,9 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed -- Fixed issue where `NetworkTransform` was not honoring the InLocalSpace property on the authority side during OnNetworkSpawn. (#2170) -- Fixed issue where `NetworkTransform` was not ending extrapolation for the previous state causing non-authoritative instances to become out of synch. (#2170) -- Fixed issue where `NetworkTransform` was not continuing to interpolate for the remainder of the associated tick period. (#2170) +- Fixed issue where NetworkTransform was not honoring the InLocalSpace property on the authority side during OnNetworkSpawn. (#2170) +- Fixed issue where NetworkTransform was not ending extrapolation for the previous state causing non-authoritative instances to become out of synch. (#2170) +- Fixed issue where NetworkTransform was not continuing to interpolate for the remainder of the associated tick period. (#2170) - Fixed issue during `NetworkTransform.OnNetworkSpawn` for non-authoritative instances where it was initializing interpolators with the replicated network state which now only contains the transform deltas that occurred during a network tick and not the entire transform state. (#2170) ## [1.0.1] - 2022-08-23 @@ -814,12 +814,12 @@ Additional documentation and release notes are available at [Multiplayer Documen - Fixed Owner-written NetworkVariable infinitely write themselves (#2109) - Fixed NetworkList issue that showed when inserting at the very end of a NetworkList (#2099) - Fixed issue where a client owner of a `NetworkVariable` with both owner read and write permissions would not update the server side when changed. (#2097) -- Fixed issue when attempting to spawn a parent `GameObject`, with `NetworkObject` component attached, that has one or more child `GameObject`s, that are inactive in the hierarchy, with `NetworkBehaviour` components it will no longer attempt to spawn the associated `NetworkBehaviour`(s) or invoke ownership changed notifications but will log a warning message. (#2096) +- Fixed issue when attempting to spawn a parent `GameObject`, with `NetworkObject` component attached, that has one or more child `GameObject`s, that are inactive in the hierarchy, with NetworkBehaviour components it will no longer attempt to spawn the associated NetworkBehaviour(s) or invoke ownership changed notifications but will log a warning message. (#2096) - Fixed an issue where destroying a NetworkBehaviour would not deregister it from the parent NetworkObject, leading to exceptions when the parent was later destroyed. (#2091) - Fixed issue where `NetworkObject.NetworkHide` was despawning and destroying, as opposed to only despawning, in-scene placed `NetworkObject`s. (#2086) -- Fixed `NetworkAnimator` synchronizing transitions twice due to it detecting the change in animation state once a transition is started by a trigger. (#2084) -- Fixed issue where `NetworkAnimator` would not synchronize a looping animation for late joining clients if it was at the very end of its loop. (#2076) -- Fixed issue where `NetworkAnimator` was not removing its subscription from `OnClientConnectedCallback` when despawned during the shutdown sequence. (#2074) +- Fixed NetworkAnimator synchronizing transitions twice due to it detecting the change in animation state once a transition is started by a trigger. (#2084) +- Fixed issue where NetworkAnimator would not synchronize a looping animation for late joining clients if it was at the very end of its loop. (#2076) +- Fixed issue where NetworkAnimator was not removing its subscription from `OnClientConnectedCallback` when despawned during the shutdown sequence. (#2074) - Fixed IsServer and IsClient being set to false before object despawn during the shutdown sequence. (#2074) - Fixed NetworkList Value event on the server. PreviousValue is now set correctly when a new value is set through property setter. (#2067) - Fixed NetworkLists not populating on client. NetworkList now uses the most recent list as opposed to the list at the end of previous frame, when sending full updates to dynamically spawned NetworkObject. The difference in behaviour is required as scene management spawns those objects at a different time in the frame, relative to updates. (#2062) @@ -834,8 +834,8 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added -- Added a new `OnTransportFailure` callback to `NetworkManager`. This callback is invoked when the manager's `NetworkTransport` encounters an unrecoverable error. Transport failures also cause the `NetworkManager` to shut down. Currently, this is only used by `UnityTransport` to signal a timeout of its connection to the Unity Relay servers. (#1994) -- Added `NetworkEvent.TransportFailure`, which can be used by implementations of `NetworkTransport` to signal to `NetworkManager` that an unrecoverable error was encountered. (#1994) +- Added a new `OnTransportFailure` callback to NetworkManager. This callback is invoked when the manager's `NetworkTransport` encounters an unrecoverable error. Transport failures also cause the NetworkManager to shut down. Currently, this is only used by `UnityTransport` to signal a timeout of its connection to the Unity Relay servers. (#1994) +- Added `NetworkEvent.TransportFailure`, which can be used by implementations of `NetworkTransport` to signal to NetworkManager that an unrecoverable error was encountered. (#1994) - Added test to ensure a warning occurs when nesting NetworkObjects in a NetworkPrefab (#1969) - Added `NetworkManager.RemoveNetworkPrefab(...)` to remove a prefab from the prefabs list (#1950) @@ -849,7 +849,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed - Fixed issue where dynamically spawned `NetworkObject`s could throw an exception if the scene of origin handle was zero (0) and the `NetworkObject` was already spawned. (#2017) - Fixed issue where `NetworkObject.Observers` was not being cleared when despawned. (#2009) -- Fixed `NetworkAnimator` could not run in the server authoritative mode. (#2003) +- Fixed NetworkAnimator could not run in the server authoritative mode. (#2003) - Fixed issue where late joining clients would get a soft synchronization error if any in-scene placed NetworkObjects were parented under another `NetworkObject`. (#1985) - Fixed issue where `NetworkBehaviourReference` would throw a type cast exception if using `NetworkBehaviourReference.TryGet` and the component type was not found. (#1984) - Fixed `NetworkSceneManager` was not sending scene event notifications for the currently active scene and any additively loaded scenes when loading a new scene in `LoadSceneMode.Single` mode. (#1975) @@ -857,13 +857,13 @@ Additional documentation and release notes are available at [Multiplayer Documen - Fixed issues when multiple `ConnectionApprovalCallback`s were registered (#1972) - Fixed a regression in serialization support: `FixedString`, `Vector2Int`, and `Vector3Int` types can now be used in NetworkVariables and RPCs again without requiring a `ForceNetworkSerializeByMemcpy<>` wrapper. (#1961) - Fixed generic types that inherit from NetworkBehaviour causing crashes at compile time. (#1976) -- Fixed endless dialog boxes when adding a `NetworkBehaviour` to a `NetworkManager` or vice-versa. (#1947) -- Fixed `NetworkAnimator` issue where it was only synchronizing parameters if the layer or state changed or was transitioning between states. (#1946) -- Fixed `NetworkAnimator` issue where when it did detect a parameter had changed it would send all parameters as opposed to only the parameters that changed. (#1946) -- Fixed `NetworkAnimator` issue where it was not always disposing the `NativeArray` that is allocated when spawned. (#1946) -- Fixed `NetworkAnimator` issue where it was not taking the animation speed or state speed multiplier into consideration. (#1946) -- Fixed `NetworkAnimator` issue where it was not properly synchronizing late joining clients if they joined while `Animator` was transitioning between states. (#1946) -- Fixed `NetworkAnimator` issue where the server was not relaying changes to non-owner clients when a client was the owner. (#1946) +- Fixed endless dialog boxes when adding a NetworkBehaviour to a NetworkManager or vice-versa. (#1947) +- Fixed NetworkAnimator issue where it was only synchronizing parameters if the layer or state changed or was transitioning between states. (#1946) +- Fixed NetworkAnimator issue where when it did detect a parameter had changed it would send all parameters as opposed to only the parameters that changed. (#1946) +- Fixed NetworkAnimator issue where it was not always disposing the `NativeArray` that is allocated when spawned. (#1946) +- Fixed NetworkAnimator issue where it was not taking the animation speed or state speed multiplier into consideration. (#1946) +- Fixed NetworkAnimator issue where it was not properly synchronizing late joining clients if they joined while `Animator` was transitioning between states. (#1946) +- Fixed NetworkAnimator issue where the server was not relaying changes to non-owner clients when a client was the owner. (#1946) - Fixed issue where the `PacketLoss` metric for tools would return the packet loss over a connection lifetime instead of a single frame. (#2004) ## [1.0.0-pre.9] - 2022-05-10 @@ -879,7 +879,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed - `unmanaged` structs are no longer universally accepted as RPC parameters because some structs (i.e., structs with pointers in them, such as `NativeList<T>`) can't be supported by the default memcpy struct serializer. Structs that are intended to be serialized across the network must add `INetworkSerializeByMemcpy` to the interface list (i.e., `struct Foo : INetworkSerializeByMemcpy`). This interface is empty and just serves to mark the struct as compatible with memcpy serialization. For external structs you can't edit, you can pass them to RPCs by wrapping them in `ForceNetworkSerializeByMemcpy<T>`. (#1901) -- Changed requirement to register in-scene placed NetworkObjects with `NetworkManager` in order to respawn them. In-scene placed NetworkObjects are now automatically tracked during runtime and no longer need to be registered as a NetworkPrefab. (#1898) +- Changed requirement to register in-scene placed NetworkObjects with NetworkManager in order to respawn them. In-scene placed NetworkObjects are now automatically tracked during runtime and no longer need to be registered as a NetworkPrefab. (#1898) ### Removed @@ -889,13 +889,13 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed - Fixed issue where `NetworkSceneManager` did not synchronize despawned in-scene placed NetworkObjects. (#1898) -- Fixed `NetworkTransform` generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890) +- Fixed NetworkTransform generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890) - Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884) - Fixed issue during client synchronization if 'ValidateSceneBeforeLoading' returned false it would halt the client synchronization process resulting in a client that was approved but not synchronized or fully connected with the server. (#1883) - Fixed an issue where UNetTransport.StartServer would return success even if the underlying transport failed to start (#854) - Passing generic types to RPCs no longer causes a native crash (#1901) - Fixed a compile failure when compiling against com.unity.nuget.mono-cecil >= 1.11.4 (#1920) -- Fixed an issue where calling `Shutdown` on a `NetworkManager` that was already shut down would cause an immediate shutdown the next time it was started (basically the fix makes `Shutdown` idempotent). (#1877) +- Fixed an issue where calling `Shutdown` on a NetworkManager that was already shut down would cause an immediate shutdown the next time it was started (basically the fix makes `Shutdown` idempotent). (#1877) ## [1.0.0-pre.7] - 2022-04-06 @@ -1051,8 +1051,8 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `Bootstrap` sample to the SDK package (#1140) - Enhanced `NetworkSceneManager` implementation with additive scene loading capabilities (#1080, #955, #913) - `NetworkSceneManager.OnSceneEvent` provides improved scene event notificaitons -- Enhanced `NetworkTransform` implementation with per axis/component based and threshold based state replication (#1042, #1055, #1061, #1084, #1101) -- Added a jitter-resistent `BufferedLinearInterpolator<T>` for `NetworkTransform` (#1060) +- Enhanced NetworkTransform implementation with per axis/component based and threshold based state replication (#1042, #1055, #1061, #1084, #1101) +- Added a jitter-resistent `BufferedLinearInterpolator<T>` for NetworkTransform (#1060) - Implemented `NetworkPrefabHandler` that provides support for object pooling and `NetworkPrefab` overrides (#1073, #1004, #977, #905,#749, #727) - Implemented auto `NetworkObject` transform parent synchronization at runtime over the network (#855) - Adopted Unity C# Coding Standards in the codebase with `.editorconfig` ruleset (#666, #670) @@ -1062,12 +1062,12 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added `SnapshotSystem` that would allow variables and spawn/despawn messages to be sent in blocks (#805, #852, #862, #963, #1012, #1013, #1021, #1040, #1062, #1064, #1083, #1091, #1111, #1129, #1166, #1192) - Disabled by default for now, except spawn/despawn messages - Will leverage unreliable messages with eventual consistency -- `NetworkBehaviour` and `NetworkObject`'s `NetworkManager` instances can now be overriden (#762) +- NetworkBehaviour and `NetworkObject`'s NetworkManager instances can now be overriden (#762) - Added metrics reporting for the new network profiler if the Multiplayer Tools package is present (#1104, #1089, #1096, #1086, #1072, #1058, #960, #897, #891, #878) - `NetworkBehaviour.IsSpawned` a quick (and stable) way to determine if the associated NetworkObject is spawned (#1190) -- Added `NetworkRigidbody` and `NetworkRigidbody2D` components to support networking `Rigidbody` and `Rigidbody2D` components (#1202, #1175) +- Added NetworkRigidBody and `NetworkRigidbody2D` components to support networking `Rigidbody` and `Rigidbody2D` components (#1202, #1175) - Added `NetworkObjectReference` and `NetworkBehaviourReference` structs which allow to sending `NetworkObject/Behaviours` over RPCs/`NetworkVariable`s (#1173) -- Added `NetworkAnimator` component to support networking `Animator` component (#1281, #872) +- Added NetworkAnimator component to support networking `Animator` component (#1281, #872) ### Changed @@ -1090,13 +1090,13 @@ Additional documentation and release notes are available at [Multiplayer Documen - and other `Unity.Multiplayer.MLAPI.x` variants to `Unity.Netcode.x` variants - Renamed `Prototyping` namespace and assembly definition to `Components` (#1145) - Changed `NetworkObject.Despawn(bool destroy)` API to default to `destroy = true` for better usability (#1217) -- Scene registration in `NetworkManager` is now replaced by Build Setttings → Scenes in Build List (#1080) +- Scene registration in NetworkManager is now replaced by Build Setttings → Scenes in Build List (#1080) - `NetworkSceneManager.SwitchScene` has been replaced by `NetworkSceneManager.LoadScene` (#955) - `NetworkManager, NetworkConfig, and NetworkSceneManager` scene registration replaced with scenes in build list (#1080) - `GlobalObjectIdHash` replaced `PrefabHash` and `PrefabHashGenerator` for stability and consistency (#698) - `NetworkStart` has been renamed to `OnNetworkSpawn`. (#865) - Network variable cleanup - eliminated shared mode, variables are server-authoritative (#1059, #1074) -- `NetworkManager` and other systems are no longer singletons/statics (#696, #705, #706, #737, #738, #739, #746, #747, #763, #765, #766, #783, #784, #785, #786, #787, #788) +- NetworkManager and other systems are no longer singletons/statics (#696, #705, #706, #737, #738, #739, #746, #747, #763, #765, #766, #783, #784, #785, #786, #787, #788) - Changed `INetworkSerializable.NetworkSerialize` method signature to use `BufferSerializer<T>` instead of `NetworkSerializer` (#1187) - Changed `CustomMessagingManager`'s methods to use `FastBufferWriter` and `FastBufferReader` instead of `Stream` (#1187) - Reduced internal runtime allocations by removing LINQ calls and replacing managed lists/arrays with native collections (#1196) @@ -1119,8 +1119,8 @@ Additional documentation and release notes are available at [Multiplayer Documen - Removed `UpdateStage` parameter from `ServerRpcSendParams` and `ClientRpcSendParams` (#1187) - Removed `NetworkBuffer`, `NetworkWriter`, `NetworkReader`, `NetworkSerializer`, `PooledNetworkBuffer`, `PooledNetworkWriter`, and `PooledNetworkReader` (#1187) - Removed `EnableNetworkVariable` in `NetworkConfig`, it is always enabled now (#1179) -- Removed `NetworkTransform`'s FixedSendsPerSecond, AssumeSyncedSends, InterpolateServer, ExtrapolatePosition, MaxSendsToExtrapolate, Channel, EnableNonProvokedResendChecks, DistanceSendrate (#1060) (#826) (#1042, #1055, #1061, #1084, #1101) -- Removed `NetworkManager`'s `StopServer()`, `StopClient()` and `StopHost()` methods and replaced with single `NetworkManager.Shutdown()` method for all (#1108) +- Removed NetworkTransform's FixedSendsPerSecond, AssumeSyncedSends, InterpolateServer, ExtrapolatePosition, MaxSendsToExtrapolate, Channel, EnableNonProvokedResendChecks, DistanceSendrate (#1060) (#826) (#1042, #1055, #1061, #1084, #1101) +- Removed NetworkManager's `StopServer()`, `StopClient()` and `StopHost()` methods and replaced with single `NetworkManager.Shutdown()` method for all (#1108) ### Fixed @@ -1128,22 +1128,22 @@ Additional documentation and release notes are available at [Multiplayer Documen - Fixed `NetworkObject.OwnerClientId` property changing before `NetworkBehaviour.OnGainedOwnership()` callback (#1092) - Fixed `NetworkBehaviourILPP` to iterate over all types in an assembly (#803) - Fixed cross-asmdef RPC ILPP by importing types into external assemblies (#678) -- Fixed `NetworkManager` shutdown when quitting the application or switching scenes (#1011) - - Now `NetworkManager` shutdowns correctly and despawns existing `NetworkObject`s -- Fixed Only one `PlayerPrefab` can be selected on `NetworkManager` inspector UI in the editor (#676) +- Fixed NetworkManager shutdown when quitting the application or switching scenes (#1011) + - Now NetworkManager shutdowns correctly and despawns existing `NetworkObject`s +- Fixed Only one `PlayerPrefab` can be selected on NetworkManager inspector UI in the editor (#676) - Fixed connection approval not being triggered for host (#675) - Fixed various situations where messages could be processed in an invalid order, resulting in errors (#948, #1187, #1218) - Fixed `NetworkVariable`s being default-initialized on the client instead of being initialized with the desired value (#1266) - Improved runtime performance and reduced GC pressure (#1187) - Fixed #915 - clients are receiving data from objects not visible to them (#1099) -- Fixed `NetworkTransform`'s "late join" issues, `NetworkTransform` now uses `NetworkVariable`s instead of RPCs (#826) +- Fixed NetworkTransform's "late join" issues, NetworkTransform now uses `NetworkVariable`s instead of RPCs (#826) - Throw an exception for silent failure when a client tries to get another player's `PlayerObject`, it is now only allowed on the server-side (#844) ### Known Issues - `NetworkVariable` does not serialize `INetworkSerializable` types through their `NetworkSerialize` implementation - `NetworkObjects` marked as `DontDestroyOnLoad` are disabled during some network scene transitions -- `NetworkTransform` interpolates from the origin when switching Local Space synchronization +- NetworkTransform interpolates from the origin when switching Local Space synchronization - Exceptions thrown in `OnNetworkSpawn` user code for an object will prevent the callback in other objects - Cannot send an array of `INetworkSerializable` in RPCs - ILPP generation fails with special characters in project path @@ -1186,21 +1186,21 @@ This is the initial experimental Unity MLAPI Package, v0.1.0. - [GitHub 520](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/520): MLAPI now uses the Unity Package Manager for installation management. - Added functionality and usability to `NetworkVariable`, previously called `NetworkVar`. Updates enhance options and fully replace the need for `SyncedVar`s. -- [GitHub 507](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/507): Reimplemented `NetworkAnimator`, which synchronizes animation states for networked objects. +- [GitHub 507](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/507): Reimplemented NetworkAnimator, which synchronizes animation states for networked objects. - GitHub [444](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/444) and [455](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/455): Channels are now represented as bytes instead of strings. For users of previous versions of MLAPI, this release renames APIs due to refactoring. All obsolete marked APIs have been removed as per [GitHub 513](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/513) and [GitHub 514](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/514). | Previous MLAPI Versions | V 0.1.0 Name | | -- | -- | -| `NetworkingManager` | `NetworkManager` | +| `NetworkingManager` | NetworkManager | | `NetworkedObject` | `NetworkObject` | -| `NetworkedBehaviour` | `NetworkBehaviour` | +| `NetworkedBehaviour` | NetworkBehaviour | | `NetworkedClient` | `NetworkClient` | | `NetworkedPrefab` | `NetworkPrefab` | | `NetworkedVar` | `NetworkVariable` | -| `NetworkedTransform` | `NetworkTransform` | -| `NetworkedAnimator` | `NetworkAnimator` | +| `NetworkedTransform` | NetworkTransform | +| `NetworkedAnimator` | NetworkAnimator | | `NetworkedAnimatorEditor` | `NetworkAnimatorEditor` | | `NetworkedNavMeshAgent` | `NetworkNavMeshAgent` | | `SpawnManager` | `NetworkSpawnManager` | @@ -1235,8 +1235,8 @@ With a new release of MLAPI in Unity, some features have been removed: - [GitHub 527](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/527): Lag compensation systems and `TrackedObject` have moved to the new [MLAPI Community Contributions](https://github.com/Unity-Technologies/mlapi-community-contributions/tree/master/com.mlapi.contrib.extensions) repo. - [GitHub 509](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/509): Encryption has been removed from MLAPI. The `Encryption` option in `NetworkConfig` on the `NetworkingManager` is not available in this release. This change will not block game creation or running. A current replacement for this functionality is not available, and may be developed in future releases. See the following changes: - Removed `SecuritySendFlags` from all APIs. - - Removed encryption, cryptography, and certificate configurations from APIs including `NetworkManager` and `NetworkConfig`. - - Removed "hail handshake", including `NetworkManager` implementation and `NetworkConstants` entries. + - Removed encryption, cryptography, and certificate configurations from APIs including NetworkManager and `NetworkConfig`. + - Removed "hail handshake", including NetworkManager implementation and `NetworkConstants` entries. - Modified `RpcQueue` and `RpcBatcher` internals to remove encryption and authentication from reading and writing. - Removed the previous MLAPI Profiler editor window from Unity versions 2020.2 and later. - Removed previous MLAPI Convenience and Performance RPC APIs with the new standard RPC API. See [RFC #1](https://github.com/Unity-Technologies/com.unity.multiplayer.rfcs/blob/master/text/0001-std-rpc-api.md) for details. @@ -1246,16 +1246,16 @@ With a new release of MLAPI in Unity, some features have been removed: - `NetworkNavMeshAgent` does not synchronize mesh data, Agent Size, Steering, Obstacle Avoidance, or Path Finding settings. It only synchronizes the destination and velocity, not the path to the destination. - For `RPC`, methods with a `ClientRpc` or `ServerRpc` suffix which are not marked with [ServerRpc] or [ClientRpc] will cause a compiler error. -- For `NetworkAnimator`, Animator Overrides are not supported. Triggers do not work. +- For NetworkAnimator, Animator Overrides are not supported. Triggers do not work. - For `NetworkVariable`, the `NetworkDictionary` `List` and `Set` must use the `reliableSequenced` channel. - `NetworkObjects`s are supported but when spawning a prefab with nested child network objects you have to manually call spawn on them -- `NetworkTransform` have the following issues: +- NetworkTransform have the following issues: - Replicated objects may have jitter. - The owner is always authoritative about the object's position. - Scale is not synchronized. - Connection Approval is not called on the host client. - For `NamedMessages`, always use `NetworkBuffer` as the underlying stream for sending named and unnamed messages. -- For `NetworkManager`, connection management is limited. Use `IsServer`, `IsClient`, `IsConnectedClient`, or other code to check if MLAPI connected correctly. +- For NetworkManager, connection management is limited. Use `IsServer`, `IsClient`, `IsConnectedClient`, or other code to check if MLAPI connected correctly. ## [0.0.1-preview.1] - 2020-12-20 diff --git a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md index 416c29c8e3..767fe3bad4 100644 --- a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md +++ b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md @@ -19,19 +19,19 @@ * [Transports](advanced-topics/transports.md) * [Relay](relay/relay.md) * [Network components](network-components.md) - * [Foundational Components](components/foundational/foundationalcomponents.md) - * [NetworkObject](components/foundational/networkobject.md) + * [Core components](components/core/corecomponents.md) + * [NetworkObject](components/core/networkobject.md) * [NetworkObject parenting](advanced-topics/networkobject-parenting.md) - * [NetworkBehaviour](components/foundational/networkbehaviour.md) - * [Synchronizing & Order of Operations](components/foundational/networkbehaviour-synchronize.md) - * [NetworkManager](components/foundational/networkmanager.md) - * [PlayerObjects and player prefabs](components/foundational/playerobjects.md) - * [Helper Components](components/Helpers/helpercomponents.md) - * [AttachableBehaviour](components/Helpers/attachablebehaviour.md) - * [AttachableNode](components/Helpers/attachablenode.md) - * [ComponentController](components/Helpers/componentcontroller.md) - * [NetworkAnimator](components/helpers/networkanimator.md) - * [NetworkTransform](components/helpers/networktransform.md) + * [NetworkBehaviour](components/core/networkbehaviour.md) + * [Synchronizing & Order of Operations](components/core/networkbehaviour-synchronize.md) + * [NetworkManager](components/core/networkmanager.md) + * [PlayerObjects and player prefabs](components/core/playerobjects.md) + * [Helper Components](components/helper/helpercomponents.md) + * [AttachableBehaviour](components/helper/attachablebehaviour.md) + * [AttachableNode](components/helper/attachablenode.md) + * [ComponentController](components/helper/componentcontroller.md) + * [NetworkAnimator](components/helper/networkanimator.md) + * [NetworkTransform](components/helper/networktransform.md) * [Physics](advanced-topics/physics.md) * [Ownership and authority](ownership-authority.md) * [Understanding ownership and authority](basics/ownership.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/client-anticipation.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/client-anticipation.md index 828d5ea3ab..80aab2a550 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/client-anticipation.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/client-anticipation.md @@ -57,7 +57,7 @@ The `OnReanticipate` event can also be used for other purposes, such as "forward ## `OnReanticipate` event -`NetworkBehaviour` has a virtual method called `OnReanticipate`. When server data is received for an `AnticipatedNetworkVariable` or `AnticipatedNetworkTransform`, it's rolled back immediately, setting its anticipated state. During each frame in which a server update for any `AnticipatedNetworkVariable` or `AnticipatedNetworkTransform` is received (after **all** such operations have been performed and **all** objects are rolled back to their server state), each `NetworkObject` that had any rollbacks calls the `OnReanticipate` method on **all** of its `NetworkBehaviour`s. +NetworkBehaviour has a virtual method called `OnReanticipate`. When server data is received for an `AnticipatedNetworkVariable` or `AnticipatedNetworkTransform`, it's rolled back immediately, setting its anticipated state. During each frame in which a server update for any `AnticipatedNetworkVariable` or `AnticipatedNetworkTransform` is received (after **all** such operations have been performed and **all** objects are rolled back to their server state), each NetworkObject that had any rollbacks calls the `OnReanticipate` method on **all** of its NetworkBehaviours. If you need to do any reanticipation to update the anticipated state of any of these variables or transforms, this method is where you will do it. `OnReanticipate` takes as its only parameter a `double` providing the amount of time, in seconds, that the object has been rolled back (which corresponds to the round-trip time of the current batch of responses received from the server). This value can be used to calculate the difference between what the server value is, and what the anticipated client value should be, and apply that change. @@ -65,7 +65,7 @@ However, note that not all variables and transforms on that object may have rece ### Global `OnReanticipate` -In addition to the `NetworkBehaviour`'s `OnReanticipate` method, `NetworkManager` also has a callback that can be subscribed to for global reanticipation. This is useful if you need to run your reanticipation in a more global way, such as if you need to run it step-wise (say, anticipating one frame at a time) and need all objects to complete one step before any of them begin the second one. This callback receives the same `lastRoundTripTime` value as the `NetworkBehaviour` method, and is called after all of the `NetworkBehaviour` methods have been called. +In addition to the NetworkBehaviour's `OnReanticipate` method, NetworkManager also has a callback that can be subscribed to for global reanticipation. This is useful if you need to run your reanticipation in a more global way, such as if you need to run it step-wise (say, anticipating one frame at a time) and need all objects to complete one step before any of them begin the second one. This callback receives the same `lastRoundTripTime` value as the NetworkBehaviour method, and is called after all of the NetworkBehaviour methods have been called. > [!NOTE] >If you want to implement a full client-side prediction model in your game, the global OnReanticipate callback is likely the ideal place to incorporate your rollback and replay logic. The details of implementing this, however, are left up to users. Implementing a full, production-ready prediction loop is a complex topic and recommended for advanced users only. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/inscene_parenting_player.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/inscene_parenting_player.md index fc5e2d09dc..5f616d19a2 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/inscene_parenting_player.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/inscene_parenting_player.md @@ -8,7 +8,7 @@ When a player Prefab has a script that dynamically adds a parent to its transfor Steps to reproduce the behavior: -1. Set up basic networking game with at least one `GameObject` in a scene that isn't the player. +1. Set up basic networking game with at least one GameObject in a scene that isn't the player. 1. Add a script to the player Prefab that adds parenting to its transform via `gameObject.transform.SetParent()` in the `Start()` method. 1. Launch one instance of the game as Host. 1. Launch another instance and try to join as Client. @@ -75,8 +75,8 @@ public class ParentPlayerToInSceneNetworkObject : NetworkBehaviour } ``` -You should place this script on your in-scene placed `NetworkObject` (that is, the first `GameObject`) and do the parenting from it to avoid any timing issues of when it's spawned or the like. It only runs the script on the server-host side since parenting is server authoritative. +You should place this script on your in-scene placed NetworkObject (that is, the first GameObject) and do the parenting from it to avoid any timing issues of when it's spawned or the like. It only runs the script on the server-host side since parenting is server authoritative. > [!NOTE] -> Remove any parenting code you might have had from your player Prefab before using the above script. Depending upon your project's goals, you might be parenting all players under the same in-scene placed `NetworkObject` or you might intend to have each player parenting unique. If you want each player to be parented under a unique in-scene placed `NetworkObject` then you will need to have the same number of in-scene placed `NetworkObject`s as your maximum allowed players per game session. The above example will only parent all players under the same in-scene placed `NetworkObject`. You can extend the above example by migrating the scene event code into an in-scene placed `NetworkObject` that manages the parenting of players (i,e. name it something like `PlayerSpawnManager`) as they connect, make the `SetPlayerParent` method public, and add all in-scene placed `NetworkObject`s to a public list of GameObjects that the `PlayerSpawnManager` will reference and assign player's to as they connect while also freeing in-scene placed `NetworkObject`s as players disconnect during a game session. +> Remove any parenting code you might have had from your player Prefab before using the above script. Depending upon your project's goals, you might be parenting all players under the same in-scene placed NetworkObject or you might intend to have each player parenting unique. If you want each player to be parented under a unique in-scene placed NetworkObject then you will need to have the same number of in-scene placed NetworkObjects as your maximum allowed players per game session. The above example will only parent all players under the same in-scene placed NetworkObject. You can extend the above example by migrating the scene event code into an in-scene placed NetworkObject that manages the parenting of players (i,e. name it something like `PlayerSpawnManager`) as they connect, make the `SetPlayerParent` method public, and add all in-scene placed NetworkObjects to a public list of GameObjects that the `PlayerSpawnManager` will reference and assign player's to as they connect while also freeing in-scene placed NetworkObjects as players disconnect during a game session. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/reliability.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/reliability.md index 0363b82e68..a3f53ae955 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/reliability.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/reliability.md @@ -20,7 +20,7 @@ void MyReliableServerRpc() { /* ... */ } void MyUnreliableServerRpc() { /* ... */ } ``` -Reliable RPCs will be received on the remote end in the same order they are sent, but this in-order guarantee only applies to RPCs on the same `NetworkObject`. Different `NetworkObjects` might have reliable RPCs called but executed in different order compared to each other. To put more simply, **in-order reliable RPC execution is guaranteed per `NetworkObject` basis only**. If you determine an RPC is being updated often (that is, several times per second), it _might_ be better suited as an unreliable RPC. +Reliable RPCs will be received on the remote end in the same order they are sent, but this in-order guarantee only applies to RPCs on the same NetworkObject. Different `NetworkObjects` might have reliable RPCs called but executed in different order compared to each other. To put more simply, **in-order reliable RPC execution is guaranteed per NetworkObject basis only**. If you determine an RPC is being updated often (that is, several times per second), it _might_ be better suited as an unreliable RPC. > [!NOTE] > When testing unreliable RPCs on a local network, the chance of an unreliable packet being dropped is reduced greatly (sometimes never). As such, you might want to use [`UnityTransport`'s Simulator Pipeline](https://docs-multiplayer.unity3d.com/transport/current/pipelines#simulator-pipeline) to simulate poor network conditions to better determine how dropped unreliable RPC messages impacts your project. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc-params.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc-params.md index 9995554c8d..28d0607ef3 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc-params.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc-params.md @@ -29,7 +29,7 @@ For those accustomed to the legacy `ServerRpc` and `ClientRpc` parameters, there - `Defer`: Invokes the RPC locally on the next frame. - `SendImmediate` (default): Immediately invokes the RPC (at the current call stack) locally. -- Changing destination of an RPC is done using the properties and methods of `RpcTarget` (each `NetworkBehaviour` contains a reference to the shared `RpcTarget` object, as does `NetworkManager`.) This allows conveniently selecting various common targets (Server, NotServer, Owner, NotOwner, etc), as well as custom client lists using `RpcTarget.Single()` (to send to one client ID), `RpcTarget.Group()` (to send to multiple client IDs), and`RpcTarget.Not()` (to send to everyone except for the specified client ID or list of client IDs) +- Changing destination of an RPC is done using the properties and methods of `RpcTarget` (each NetworkBehaviour contains a reference to the shared `RpcTarget` object, as does NetworkManager.) This allows conveniently selecting various common targets (Server, NotServer, Owner, NotOwner, etc), as well as custom client lists using `RpcTarget.Single()` (to send to one client ID), `RpcTarget.Group()` (to send to multiple client IDs), and`RpcTarget.Not()` (to send to everyone except for the specified client ID or list of client IDs) - `RpcParams` do not allow overriding the destination at runtime unless either the default target is `SendTo.SpecifiedInParams` or `AllowTargetOverride = true` is passed to the attribute. ```csharp diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md index a8569b4697..c004571dbd 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md @@ -97,9 +97,9 @@ To send to a different target, you must define an additional parameter of type ` #### Built-in targets -`NetworkManager` and `NetworkBehaviour` both provide reference to a field called `RpcTarget`, which provides references to the various override targets you can pass in. The list mirrors the list of targets provided in the `SendTo` enum, and has the same behavior as described in the table above. +NetworkManager and NetworkBehaviour both provide reference to a field called `RpcTarget`, which provides references to the various override targets you can pass in. The list mirrors the list of targets provided in the `SendTo` enum, and has the same behavior as described in the table above. -The `RpcTarget` object is shared by all `NetworkBehaviours` attached to a given `NetworkManager`, so you can get access to this via any `NetworkBehaviour` or via the `NetworkManager` object itself. +The `RpcTarget` object is shared by all `NetworkBehaviours` attached to a given NetworkManager, so you can get access to this via any NetworkBehaviour or via the NetworkManager object itself. ```csharp public class SomeNetworkBehaviour : NetworkBehaviour diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/messaging-system.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/messaging-system.md index 750f034d27..36db9c787e 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/messaging-system.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/messaging-system.md @@ -36,7 +36,7 @@ Refer to [RPC migration and compatibility](message-system/rpc-compatibility.md) ## RPC method calls -A typical SDK user (Unity developer) can declare multiple RPCs under a `NetworkBehaviour` and inbound/outbound RPC calls will be replicated as a part of its replication in a network frame. +A typical SDK user (Unity developer) can declare multiple RPCs under a NetworkBehaviour and inbound/outbound RPC calls will be replicated as a part of its replication in a network frame. A method turned into an RPC is no longer a regular method; it will have its own implications on direct calls and in the network pipeline. @@ -46,7 +46,7 @@ To use RPCs, make sure: - `[Rpc]` attributes are on your method - Your method name ends with `Rpc` (for example, `DoSomethingRpc()`) -- Your method is declared in a class that inherits from `NetworkBehaviour` +- Your method is declared in a class that inherits from NetworkBehaviour - Your GameObject has a NetworkObject component attached ## Serialization types and RPCs diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-update-loop-system/index.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-update-loop-system/index.md index 0546834bf4..d7992d0480 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-update-loop-system/index.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-update-loop-system/index.md @@ -1,8 +1,8 @@ # About NetworkUpdateLoop -Often there is a need to update netcode systems like RPC queue, transport IO, and others outside the standard `MonoBehaviour` event cycle. +Often there is a need to update netcode systems like RPC queue, transport IO, and others outside the standard MonoBehaviour event cycle. -The Network Update Loop infrastructure utilizes Unity's low-level Player Loop API allowing for registering `INetworkUpdateSystems` with `NetworkUpdate()` methods to be executed at specific `NetworkUpdateStages` which may be either before or after `MonoBehaviour`-driven game logic execution. +The Network Update Loop infrastructure utilizes Unity's low-level Player Loop API allowing for registering `INetworkUpdateSystems` with `NetworkUpdate()` methods to be executed at specific `NetworkUpdateStages` which may be either before or after MonoBehaviour-driven game logic execution. Typically you will interact with `NetworkUpdateLoop` for registration and `INetworkUpdateSystem` for implementation. Systems such as network tick and future features (such as network variable snapshotting) will rely on this pipeline. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networkobject-parenting.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networkobject-parenting.md index e725f506db..5c630b60ed 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networkobject-parenting.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networkobject-parenting.md @@ -74,7 +74,7 @@ For more information, refer to: When using the `NetworkObject.TrySetParent` or `NetworkObject.TryRemoveParent` methods, the `WorldPositionStays` parameter is synchronized with currently connected and late joining clients. When removing a child from its parent, use the same `WorldPositionStays` value that you used to parent the child. More specifically, when `WorldPositionStays` is false, this applies. However, if you're using the default value of `true`, this isn't required (because it's the default). -When the `WorldPositionStays` parameter in `NetworkObject.TrySetParent` is the default value of `true`, this will preserve the world space values of the child `NetworkObject` relative to the parent. However, sometimes you might want to only preserve the local space values (pick up an object that only has some initial changes to the child's transform when parented). Through a combination of `NetworkObject.TrySetParent` and `NetworkBehaviour.OnNetworkObjectParentChanged` you can accomplish this without the need for a NetworkTransform component. To better understand how this works, it's important to understand the order of operations for both of these two methods: +When the `WorldPositionStays` parameter in `NetworkObject.TrySetParent` is the default value of `true`, this will preserve the world space values of the child NetworkObject relative to the parent. However, sometimes you might want to only preserve the local space values (pick up an object that only has some initial changes to the child's transform when parented). Through a combination of `NetworkObject.TrySetParent` and `NetworkBehaviour.OnNetworkObjectParentChanged` you can accomplish this without the need for a NetworkTransform component. To better understand how this works, it's important to understand the order of operations for both of these two methods: **Server-Side** diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networktime-ticks.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networktime-ticks.md index 4f56a900a5..157de47e79 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networktime-ticks.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networktime-ticks.md @@ -34,14 +34,14 @@ sequenceDiagram `ServerTime`: - For player objects with server authority (For example, by sending inputs to the server via RPCs) -- In sync with position updates of `NetworkTransform` for all `NetworkObjects` where the client isn't authoritative over the transform. +- In sync with position updates of NetworkTransform for all `NetworkObjects` where the client isn't authoritative over the transform. - For everything on non client controlled `NetworkObjects`. ## Examples ### Example 1: Using network time to synchronize environments -Many games have environmental objects which move in a fixed pattern. By using network time these objects can be moved without having to synchronize their positions with a `NetworkTransform`. +Many games have environmental objects which move in a fixed pattern. By using network time these objects can be moved without having to synchronize their positions with a NetworkTransform. For instance the following code can be used to create a moving elevator platform for a client authoritative game: @@ -136,11 +136,11 @@ sequenceDiagram `}/> > [!NOTE] -> Some components such as `NetworkTransform` add additional buffering. When trying to align an RPC event like in this example, an additional delay would need to be added. +> Some components such as NetworkTransform add additional buffering. When trying to align an RPC event like in this example, an additional delay would need to be added. ## Network Ticks -Network ticks are run at a fixed rate. The 'Tick Rate' field on the `NetworkManager` can be used to set the tick rate. +Network ticks are run at a fixed rate. The 'Tick Rate' field on the NetworkManager can be used to set the tick rate. What does changing the network tick affect? Changes to `NetworkVariables` aren't sent immediately. Instead during each network tick changes to `NetworkVariables` are collected and sent out to other peers. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/object-pooling.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/object-pooling.md index 32e7add573..da5c1ac150 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/object-pooling.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/object-pooling.md @@ -14,7 +14,7 @@ You can register your own spawn handlers by including the `INetworkPrefabInstanc void Destroy(NetworkObject networkObject); } ``` -Netcode will use the `Instantiate` and `Destroy` methods in place of default spawn handlers for the `NetworkObject` used during spawning and despawning. Because the message to instantiate a new `NetworkObject` originates from a Host or Server, both won't have the Instantiate method invoked. All clients (excluding a Host) will have the instantiate method invoked if the `INetworkPrefabInstanceHandler` implementation is registered with `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`) and a Host or Server spawns the registered/associated `NetworkObject`. +Netcode will use the `Instantiate` and `Destroy` methods in place of default spawn handlers for the NetworkObject used during spawning and despawning. Because the message to instantiate a new NetworkObject originates from a Host or Server, both won't have the Instantiate method invoked. All clients (excluding a Host) will have the instantiate method invoked if the `INetworkPrefabInstanceHandler` implementation is registered with `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`) and a Host or Server spawns the registered/associated NetworkObject. <!-- Commenting this out until we can get external code references working diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/physics.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/physics.md index 6b009f3d51..b2b47e6926 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/physics.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/physics.md @@ -1,6 +1,6 @@ # Physics -There are many different ways to manage physics simulation in multiplayer games. Netcode for GameObjects (Netcode) has a built in approach which allows for server-authoritative physics where the physics simulation only runs on the server. To enable network physics, add a `NetworkRigidbody` component to your object. +There are many different ways to manage physics simulation in multiplayer games. Netcode for GameObjects (Netcode) has a built in approach which allows for server-authoritative physics where the physics simulation only runs on the server. To enable network physics, add a NetworkRigidBody component to your object. ## NetworkRigidbody @@ -8,7 +8,7 @@ NetworkRigidbody is a component that sets the Rigidbody of the GameObject into k To use NetworkRigidbody, add a Rigidbody, NetworkTransform, and NetworkRigidbody component to your NetworkObject. -Some collision events aren't fired when using `NetworkRigidbody`. +Some collision events aren't fired when using NetworkRigidBody. - On the `server`, all collision and trigger events (such as `OnCollisionEnter`) fire as expected and you can access (and change) values of the `Rigidbody` (such as velocity). - On the `clients`, the `Rigidbody` is kinematic. Trigger events still fire but collision events won't fire when colliding with other networked `Rigidbody` instances. @@ -21,7 +21,7 @@ Some collision events aren't fired when using `NetworkRigidbody`. ## NetworkRigidbody and ClientNetworkTransform -You can use NetworkRigidbody with the [`ClientNetworkTransform`](../components/helpers/networktransform.md) package sample to allow the owner client of a NetworkObject to move it authoritatively. In this mode, collisions only result in realistic dynamic collisions if the object is colliding with other NetworkObjects (owned by the same client). +You can use NetworkRigidbody with the [`ClientNetworkTransform`](../components/helper/networktransform.md) package sample to allow the owner client of a NetworkObject to move it authoritatively. In this mode, collisions only result in realistic dynamic collisions if the object is colliding with other NetworkObjects (owned by the same client). > [!NOTE] > Add the ClientNetworkTransform component to your GameObject first. Otherwise the NetworkRigidbody automatically adds a regular NetworkTransform. @@ -30,7 +30,7 @@ You can use NetworkRigidbody with the [`ClientNetworkTransform`](../components/h A common issue with physics in multiplayer games is lag and how objects update on basically different timelines. For example, a player would be on a timeline that's offset by the network latency relative to your server's objects. One way to prepare for this is to test your game with artificial lag. You might catch some weird delayed collisions that would otherwise make it into production. -The best way to address the issue of physics latency is to create a custom `NetworkTransform` with a custom physics-based interpolator. You can also use the [Network Simulator tool](https://docs.unity3d.com/Packages/com.unity.multiplayer.tools@latest?subfolder=/manual/network-simulator) to spot issues with latency. +The best way to address the issue of physics latency is to create a custom NetworkTransform with a custom physics-based interpolator. You can also use the [Network Simulator tool](https://docs.unity3d.com/Packages/com.unity.multiplayer.tools@latest?subfolder=/manual/network-simulator) to spot issues with latency. ## Message processing vs. applying changes to state (timing considerations) @@ -49,10 +49,10 @@ From this list of update stages, the `EarlyUpdate` and `FixedUpdate` stages have ### Rigidbody interpolation example -While `NetworkTransform` offers interpolation as a way to smooth between delta state updates, it doesn't get applied to the authoritative instance. You can use `Rigidbody.interpolation` for your authoritative instance while maintaining a strict server-authoritative motion model. +While NetworkTransform offers interpolation as a way to smooth between delta state updates, it doesn't get applied to the authoritative instance. You can use `Rigidbody.interpolation` for your authoritative instance while maintaining a strict server-authoritative motion model. To have a client control their owned objects, you can use either [RPCs](message-system/rpc.md) or [NetworkVariables](../basics/networkvariable.md) on the client-side. However, this often results in the host-client's updates working as expected, but with slight jitter when a client sends updates. You might be scanning for key or device input during the `Update` to `LateUpdate` stages. Any input from the host player is applied after the `FixedUpdate` stage (i.e. physics simulation for the frame has already run), but input from client players is sent via a message and processed, with a half RTT delay, on the host side (or processed 1 network tick + half RTT if using NetworkVariables). Because of when messages are processed, client input updates run the risk of being processed during the `EarlyUpdate` stage which occurs just before the current frame's `FixedUpdate` stage. -To avoid this kind of scenario, it's recommended that you apply any changes received via messages to a Rigidbody _after_ the FixedUpdate has run for the current frame. If you [look at how `NetworkTransform` handles its changes to transform state](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/a2c6f7da5be5af077427eef9c1598fa741585b82/com.unity.netcode.gameobjects/Components/NetworkTransform.cs#L3028), you can see that state updates are applied during the `Update` stage, but are received during the `EarlyUpdate` stage. Following this kind of pattern when synchronizing changes to a Rigidbody via messages will help you to avoid unexpected results in your Netcode for GameObjects project. +To avoid this kind of scenario, it's recommended that you apply any changes received via messages to a Rigidbody _after_ the FixedUpdate has run for the current frame. If you [look at how NetworkTransform handles its changes to transform state](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/a2c6f7da5be5af077427eef9c1598fa741585b82/com.unity.netcode.gameobjects/Components/NetworkTransform.cs#L3028), you can see that state updates are applied during the `Update` stage, but are received during the `EarlyUpdate` stage. Following this kind of pattern when synchronizing changes to a Rigidbody via messages will help you to avoid unexpected results in your Netcode for GameObjects project. -The best way to address the issue of physics latency is to create a custom `NetworkTransform` with a custom physics-based interpolator. You can also use the [Network Simulator tool](https://docs.unity3d.com/Packages/com.unity.multiplayer.tools@latest?subfolder=/manual/network-simulator) to spot issues with latency. +The best way to address the issue of physics latency is to create a custom NetworkTransform with a custom physics-based interpolator. You can also use the [Network Simulator tool](https://docs.unity3d.com/Packages/com.unity.multiplayer.tools@latest?subfolder=/manual/network-simulator) to spot issues with latency. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/reconnecting-mid-game.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/reconnecting-mid-game.md index 3707c9e59c..93f33264a1 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/reconnecting-mid-game.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/reconnecting-mid-game.md @@ -40,7 +40,7 @@ To implement automatic reconnection: - For example, you can use the `NetworkManager.OnClientDisconnectCallback` callback, or some other unique event depending on your game. -- Ensure the `NetworkManager` shuts down before attempting any reconnection. +- Ensure the NetworkManager shuts down before attempting any reconnection. - You can use the `NetworkManager.ShutdownInProgress` property to manage this. @@ -58,7 +58,7 @@ To implement automatic reconnection: Depending on your game, you might want to add the following features as well: -- Include multiple reconnection attempts in case of failure. You need to define the number of attempts, ensure that `NetworkManager` properly shuts down between each try, and reset the client's state (if needed). +- Include multiple reconnection attempts in case of failure. You need to define the number of attempts, ensure that NetworkManager properly shuts down between each try, and reset the client's state (if needed). - Offer an option for players to cancel the reconnection process. This might be useful when there are a lot of reconnection attempts or when each try lasts a long duration. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/networkobject-serialization.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/networkobject-serialization.md index 5b13663b26..9ca778028d 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/networkobject-serialization.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/networkobject-serialization.md @@ -1,14 +1,14 @@ # NetworkObject and NetworkBehaviour -`GameObjects`, `NetworkObjects` and `NetworkBehaviour` aren't serializable types so they can't be used in `RPCs` or `NetworkVariables` by default. +`GameObjects`, `NetworkObjects` and NetworkBehaviour aren't serializable types so they can't be used in `RPCs` or `NetworkVariables` by default. -There are two convenience wrappers which can be used to send a reference to a `NetworkObject` or a `NetworkBehaviour` over RPCs or `NetworkVariables`. +There are two convenience wrappers which can be used to send a reference to a NetworkObject or a NetworkBehaviour over RPCs or `NetworkVariables`. ## NetworkObjectReference -`NetworkObjectReference` can be used to serialize a reference to a `NetworkObject`. It can only be used on already spawned `NetworkObjects`. +`NetworkObjectReference` can be used to serialize a reference to a NetworkObject. It can only be used on already spawned `NetworkObjects`. -Here is an example of using `NetworkObject` reference to send a target `NetworkObject` over an RPC: +Here is an example of using NetworkObject reference to send a target NetworkObject over an RPC: ```csharp public class Weapon : NetworkBehaviour { @@ -52,11 +52,11 @@ public class Weapon : NetworkBehaviour } ``` > [!NOTE] -> The implicit conversion to `NetworkObject` / `GameObject` will result in `Null` if the reference can't be found. +> The implicit conversion to NetworkObject / GameObject will result in `Null` if the reference can't be found. ## NetworkBehaviourReference -`NetworkBehaviourReference` works similar to `NetworkObjectReference` but is used to reference a specific `NetworkBehaviour` component on a spawned `NetworkObject`. +`NetworkBehaviourReference` works similar to `NetworkObjectReference` but is used to reference a specific NetworkBehaviour component on a spawned NetworkObject. ```cs public class Health : NetworkBehaviour @@ -85,6 +85,6 @@ public class Weapon : NetworkBehaviour ## How NetworkObjectReference & NetworkBehaviourReference work -`NetworkObjectReference` and `NetworkBehaviourReference` are convenience wrappers which serialize the id of a `NetworkObject` when being sent and on the receiving end retrieve the corresponding ` ` with that id. `NetworkBehaviourReference` sends an additional index which is used to find the right `NetworkBehaviour` on the `NetworkObject`. +`NetworkObjectReference` and `NetworkBehaviourReference` are convenience wrappers which serialize the id of a NetworkObject when being sent and on the receiving end retrieve the corresponding ` ` with that id. `NetworkBehaviourReference` sends an additional index which is used to find the right NetworkBehaviour on the NetworkObject. Both of them are structs implementing the `INetworkSerializable` interface. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/session-management.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/session-management.md index ba3d699ed2..1f80a08d1a 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/session-management.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/session-management.md @@ -19,9 +19,9 @@ You can also decide to clear all data when a session completes or add a timeout # Reconnection -The best way to reconnect players depends on your game. For example, if you use a [Player Object](../components/foundational/networkobject.md#player-objects), a new `Default Player Prefab` automatically spawns when a player connects to the game (including when they reconnect). You can use the player's earlier saved session data to update that object so that it returns to the same state before disconnecting. In those cases, you would need to keep all the important data that you want to restore and map it to the player using your identification system. You can save this data when a player disconnects or update it periodically. You can then use the `OnNetworkSpawn` event on the Player Object's `NetworkBehavior`(s) to get this data and apply it where needed. +The best way to reconnect players depends on your game. For example, if you use a [Player Object](../components/core/networkobject.md#player-objects), a new `Default Player Prefab` automatically spawns when a player connects to the game (including when they reconnect). You can use the player's earlier saved session data to update that object so that it returns to the same state before disconnecting. In those cases, you would need to keep all the important data that you want to restore and map it to the player using your identification system. You can save this data when a player disconnects or update it periodically. You can then use the `OnNetworkSpawn` event on the Player Object's `NetworkBehavior`(s) to get this data and apply it where needed. -In cases where we don't use the Player Object approach and instead manually attribute client ownership to `NetworkObject`(s), we can keep the objects that a player owns when they disconnect, and set the reconnected player as their new owner. To accomplish this, the only data we would need to keep would be the mapping between those objects and their owning player's identifier, then when a player reconnects we can use this mapping to set them as the new owner. This mapping can be as simple as a dictionary mapping the player identifier with the `NetworkObjectId`(s) of the `NetworkObject`(s) they own. Then, in the `OnClientConnectedCallback` from the `NetworkManager`, the server can set the ownership of these objects. +In cases where we don't use the Player Object approach and instead manually attribute client ownership to NetworkObject(s), we can keep the objects that a player owns when they disconnect, and set the reconnected player as their new owner. To accomplish this, the only data we would need to keep would be the mapping between those objects and their owning player's identifier, then when a player reconnects we can use this mapping to set them as the new owner. This mapping can be as simple as a dictionary mapping the player identifier with the `NetworkObjectId`(s) of the NetworkObject(s) they own. Then, in the `OnClientConnectedCallback` from the NetworkManager, the server can set the ownership of these objects. <!-- Commenting this out until we can get external code references working diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/transports.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/transports.md index 0276955863..1939d8a0ea 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/transports.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/transports.md @@ -26,6 +26,6 @@ UNet is a deprecated solution that is no longer supported after Unity 2022.2. Un You can use any of the community contributed custom transport implementations or write your own. -The community transports are interchangeable transport layers for Netcode and can be installed with the Unity Package Manager. After installation, the transport package will appear in the **Select Transport** dropdown of the `NetworkManager`. Check out the [Netcode community contributed transports](https://github.com/Unity-Technologies/multiplayer-community-contributions/tree/main/Transports) for more information. +The community transports are interchangeable transport layers for Netcode and can be installed with the Unity Package Manager. After installation, the transport package will appear in the **Select Transport** dropdown of the NetworkManager. Check out the [Netcode community contributed transports](https://github.com/Unity-Technologies/multiplayer-community-contributions/tree/main/Transports) for more information. To start writing your own and contributing to the community, check out the [Netcode community contribution repository](https://github.com/Unity-Technologies/multiplayer-community-contributions) for starting points and how to add your content. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/connection-approval.md b/com.unity.netcode.gameobjects/Documentation~/basics/connection-approval.md index 7bf664605d..548891d9cf 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/connection-approval.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/connection-approval.md @@ -134,7 +134,7 @@ The `Payload`, defined by the client-side `NetworkConfig.ConnectionData`, is sen Netcode for GameObjects uses a callback system to allow for external validation. For example, you might have an authentication ticket sent as the `ConnectionData` that you want to validate against the owning servers. This can take some time, so you want to set the `NetworkManager.ConnectionApprovalResponse.Pending` to true while the server (or other third-party authentication service) validates the incoming ticket. -If the third-party authentication process takes longer than the time specified by the `NetworkConfig.ClientConnectionBufferTimeout`, then the connection is dropped. The timer for this starts when the server is notified of the new inbound client connection. You can set the **Client Connection Buffer Timeout** value via the `NetworkManager` in the Inspector view or with the `NetworkManager.NetworkConfig.ClientConnectionBufferTimeout` property. +If the third-party authentication process takes longer than the time specified by the `NetworkConfig.ClientConnectionBufferTimeout`, then the connection is dropped. The timer for this starts when the server is notified of the new inbound client connection. You can set the **Client Connection Buffer Timeout** value via the NetworkManager in the Inspector view or with the `NetworkManager.NetworkConfig.ClientConnectionBufferTimeout` property. ## Security diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/custom-networkvariables.md b/com.unity.netcode.gameobjects/Documentation~/basics/custom-networkvariables.md index c7cb9262e9..c2c1f97286 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/custom-networkvariables.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/custom-networkvariables.md @@ -66,7 +66,7 @@ For dynamically-allocated types with a value that isn't `null` (for example, man You can use `AreEqual` to determine if a value is different from the value that `Duplicate` cached. This avoids sending the same value multiple times. You can also use the previous value that `Duplicate` cached to calculate deltas to use in `ReadDelta` and `WriteDelta`. -The type you use must be serializable according to the [support types list above](#supported-types). Each type needs its own serializer instantiated, so this step tells the codegen which types to create serializers for. Unity's code generator assumes that all `NetworkVariable` types exist as fields inside `NetworkBehaviour` types. This means that Unity only inspects fields inside `NetworkBehaviour` types to identify the types to create serializers for. +The type you use must be serializable according to the [support types list above](#supported-types). Each type needs its own serializer instantiated, so this step tells the codegen which types to create serializers for. Unity's code generator assumes that all `NetworkVariable` types exist as fields inside NetworkBehaviour types. This means that Unity only inspects fields inside NetworkBehaviour types to identify the types to create serializers for. ## Custom NetworkVariable example @@ -235,12 +235,12 @@ While the above example isn't the recommended way to synchronize a list where th You can test the code above by: - Using the above code with a project that includes Netcode for GameObjects v1.0 (or higher). -- Adding the `TestMyCustomNetworkVariable` component to an in-scene placed `NetworkObject`. +- Adding the `TestMyCustomNetworkVariable` component to an in-scene placed NetworkObject. - Creating a stand alone build and running that as a host or server. - Running the same scene within the Editor and connecting as a client. - - Once connected, you can then select the `GameObject` with the attached `NetworkObject` and `TestMyCustomNetworkVariable` components so it appears in the inspector view. There you can verify the `TestMyCustomNetworkVariable.CustomNetworkVariable` property was synchronized with the client (like in the screenshot below): + - Once connected, you can then select the GameObject with the attached NetworkObject and `TestMyCustomNetworkVariable` components so it appears in the inspector view. There you can verify the `TestMyCustomNetworkVariable.CustomNetworkVariable` property was synchronized with the client (like in the screenshot below):  > [!NOTE] -> You can't nest `NetworkVariable`s inside other `NetworkVariable` classes. This is because Netcode for GameObjects performs a code generation step to define serialization callbacks for each type it finds in a `NetworkVariable`. The code generation step looks for variables as fields of `NetworkBehaviour` types; it misses any `NetworkVariable`s declared anywhere else. -> Instead of nesting `NetworkVariable`s inside other `NetworkVariable` classes, declare `NetworkVariable` or `NetworkList` properties within the same `NetworkBehaviour` within which you have declared your custom `NetworkVariableBase` implementation. +> You can't nest `NetworkVariable`s inside other `NetworkVariable` classes. This is because Netcode for GameObjects performs a code generation step to define serialization callbacks for each type it finds in a `NetworkVariable`. The code generation step looks for variables as fields of NetworkBehaviour types; it misses any `NetworkVariable`s declared anywhere else. +> Instead of nesting `NetworkVariable`s inside other `NetworkVariable` classes, declare `NetworkVariable` or `NetworkList` properties within the same NetworkBehaviour within which you have declared your custom `NetworkVariableBase` implementation. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md b/com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md index d58333d0c1..758835843d 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md @@ -5,7 +5,7 @@ `NetworkVariable` is a wrapper of the stored value of type `T`, so you need to use the `NetworkVariable.Value` property to access the actual value being synchronized. A `NetworkVariable` is synchronized with: * New clients joining the game (also referred to as late-joining clients) - * When the associated `NetworkObject` of a `NetworkBehaviour` with `NetworkVariable` properties is spawned, any `NetworkVariable`'s current state (`Value`) is automatically synchronized on the client side. + * When the associated NetworkObject of a NetworkBehaviour with `NetworkVariable` properties is spawned, any `NetworkVariable`'s current state (`Value`) is automatically synchronized on the client side. * Connected clients * When a `NetworkVariable` value changes, all connected clients that subscribed to the `NetworkVariable.OnValueChanged` event (prior to the value being changed) are notified of the change. Two parameters are passed to any `NetworkVariable.OnValueChanged` subscribed callback method: - First parameter (Previous): The previous value before the value was changed @@ -17,20 +17,20 @@ You can use `NetworkVariable` [permissions](#permissions) to control read and wr ### Defining a NetworkVariable -- A `NetworkVariable` property must be defined within a `NetworkBehaviour`-derived class attached to a `GameObject`. - - The `GameObject` or a parent `GameObject` must also have a `NetworkObject` component attached to it. +- A `NetworkVariable` property must be defined within a NetworkBehaviour-derived class attached to a GameObject. + - The GameObject or a parent GameObject must also have a NetworkObject component attached to it. - A `NetworkVariable`'s value can only be set: - When initializing the property (either when it's declared or within the Awake method). - - While the associated `NetworkObject` is spawned (upon being spawned or any time while it's still spawned). + - While the associated NetworkObject is spawned (upon being spawned or any time while it's still spawned). ### Initializing a NetworkVariable -When a client first connects, it's synchronized with the current value of the `NetworkVariable`. Typically, clients should register for `NetworkVariable.OnValueChanged` within the `OnNetworkSpawn` method. A `NetworkBehaviour`'s `Start` and `OnNetworkSpawn` methods are invoked based on the type of `NetworkObject` the `NetworkBehaviour` is associated with: +When a client first connects, it's synchronized with the current value of the `NetworkVariable`. Typically, clients should register for `NetworkVariable.OnValueChanged` within the `OnNetworkSpawn` method. A NetworkBehaviour's `Start` and `OnNetworkSpawn` methods are invoked based on the type of NetworkObject the NetworkBehaviour is associated with: - In-scene placed: Since the instantiation occurs via the scene loading mechanism(s), the `Start` method is invoked before `OnNetworkSpawn`. - Dynamically spawned: Since `OnNetworkSpawn` is invoked immediately (that is, within the same relative call-stack) after instantiation, the `Start` method is invoked after `OnNetworkSpawn`. -Typically, these methods are invoked at least one frame after the `NetworkObject` and associated `NetworkBehaviour` components are instantiated. The table below lists the event order for dynamically spawned and in-scene placed objects respectively. +Typically, these methods are invoked at least one frame after the NetworkObject and associated NetworkBehaviour components are instantiated. The table below lists the event order for dynamically spawned and in-scene placed objects respectively. Dynamically spawned | In-scene placed ------------------- | --------------- @@ -38,7 +38,7 @@ Awake | Awake OnNetworkSpawn | Start Start | OnNetworkSpawn -You should only set the value of a `NetworkVariable` when first initializing it or if it's spawned. It isn't recommended to set a `NetworkVariable` when the associated `NetworkObject` isn't spawned. +You should only set the value of a `NetworkVariable` when first initializing it or if it's spawned. It isn't recommended to set a `NetworkVariable` when the associated NetworkObject isn't spawned. > [!NOTE] > If you need to initialize other components or objects based on a `NetworkVariable`'s initial synchronized state, then you can use a common method that's invoked on the client side within the `NetworkVariable.OnValueChanged` callback (if assigned) and `NetworkBehaviour.OnNetworkSpawn` method. @@ -76,7 +76,7 @@ You can use `NetworkVariable`s with both managed and unmanaged collections, but The following client-server example shows how the initial `NetworkVariable` synchronization has already occurred by the time `OnNetworkSpawn` is invoked. It also shows how subscribing to `NetworkVariable.OnValueChanged` within `OnNetworkSpawn` provides notifications for any changes to `m_SomeValue.Value` that occur. -1. The server initializes the `NetworkVariable` when the associated `NetworkObject` is spawned. +1. The server initializes the `NetworkVariable` when the associated NetworkObject is spawned. 1. The client confirms that the `NetworkVariable` is synchronized to the initial value set by the server and assigns a callback method to `NetworkVariable.OnValueChanged`. 1. Once spawned, the client is notified of any changes made to the `NetworkVariable`. @@ -135,11 +135,11 @@ public class TestNetworkVariableSynchronization : NetworkBehaviour } ``` -If you attach the above script to an in-scene placed `NetworkObject`, make a standalone build, run the standalone build as a host, and then connect to that host by entering Play Mode in the Editor, you can see (in the console output): +If you attach the above script to an in-scene placed NetworkObject, make a standalone build, run the standalone build as a host, and then connect to that host by entering Play Mode in the Editor, you can see (in the console output): - The client side `NetworkVariable` value is the same as the server when `NetworkBehaviour.OnNetworkSpawn` is invoked. -- The client detects any changes made to the `NetworkVariable` after the in-scene placed `NetworkObject` is spawned. +- The client detects any changes made to the `NetworkVariable` after the in-scene placed NetworkObject is spawned. -This works the same way with dynamically spawned `NetworkObject`s. +This works the same way with dynamically spawned NetworkObjects. ## OnValueChanged example @@ -244,10 +244,10 @@ The two types of permissions are defined within [NetworkVariablePermissions.cs]( There are two options for reading a `NetworkVariable.Value`: -- **Everyone(default)**: both owners and non-owners of the `NetworkObject` can read the value. +- **Everyone(default)**: both owners and non-owners of the NetworkObject can read the value. - This is useful for global states that all clients should be aware of, such as player scores, health, or any other state that all clients should know about. - We provided an example of maintaining a door's open or closed state using the everyone permission. -- **Owner**: only the owner of the `NetworkObject` and the server can read the value. +- **Owner**: only the owner of the NetworkObject and the server can read the value. - This is useful if your `NetworkVariable` represents something specific to the client's player that only the server and client should know about, such as a player's inventory or ammo count. ### Write permissions @@ -256,7 +256,7 @@ There are two options for writing a `NetworkVariable.Value`: - **Server(default)**: the server is the only one that can write the value. - This is useful for server-side specific states that all clients should be aware of but can't change, such as an NPC's status or some global world environment state (that is, is it night or day time). -- **Owner**: only the owner of the `NetworkObject` can write to the value. +- **Owner**: only the owner of the NetworkObject can write to the value. - This is useful if your `NetworkVariable` represents something specific to the client's player that only the owning client should be able to set, such as a player's skin or other cosmetics. ### Permissions example @@ -542,7 +542,7 @@ You can use `AreEqual` to determine if a value is different from the value that The type you use must be serializable according to the "Supported Types" list above. Each type needs its own serializer instantiated, so this step tells the codegen which types to create serializers for. -> [!NOTE] Unity's code generator assumes that all `NetworkVariable` types exist as fields inside `NetworkBehaviour` types. This means that Unity only inspects fields inside `NetworkBehaviour` types to identify the types to create serializers for. +> [!NOTE] Unity's code generator assumes that all `NetworkVariable` types exist as fields inside NetworkBehaviour types. This means that Unity only inspects fields inside NetworkBehaviour types to identify the types to create serializers for. ### Custom NetworkVariable Example diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md b/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md index 0fa234bba8..25bc2ee86e 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md @@ -4,34 +4,34 @@ In Unity, you typically create a new game object using the `Instantiate` functio ## Network Prefabs -A network Prefab is any unity Prefab asset that has one `NetworkObject` component attached to a `GameObject` within the prefab. More commonly, the `NetworkObject` component is attached to the root `GameObject` of the Prefab asset because this allows any child `GameObject` to have `NetworkBehaviour` components automatically assigned to the `NetworkObject`. The reason for this is that a `NetworkObject` component attached to a `GameObject` will be assigned (associated with) any `NetworkBehaviour` components on: +A network Prefab is any unity Prefab asset that has one NetworkObject component attached to a GameObject within the prefab. More commonly, the NetworkObject component is attached to the root GameObject of the Prefab asset because this allows any child GameObject to have NetworkBehaviour components automatically assigned to the NetworkObject. The reason for this is that a NetworkObject component attached to a GameObject will be assigned (associated with) any NetworkBehaviour components on: -- the same `GameObject` that the `NetworkObject` component is attached to -- any child or children of the `GameObject` that the `NetworkObject` is attached to. +- the same GameObject that the NetworkObject component is attached to +- any child or children of the GameObject that the NetworkObject is attached to. > [!NOTE] -> A caveat of the above two rules is when one of the children `GameObject`s also has a `NetworkObject` component assigned to it (a.k.a. "Nested NetworkObjects"). Because nested `NetworkObject` components aren't permited in network prefabs, Netcode for GameObjects will notify you in the editor if you are trying to add more than one `NetworkObject` to a Prefab and won't allow you to do this. +> A caveat of the above two rules is when one of the children GameObjects also has a NetworkObject component assigned to it (a.k.a. "Nested NetworkObjects"). Because nested NetworkObject components aren't permited in network prefabs, Netcode for GameObjects will notify you in the editor if you are trying to add more than one NetworkObject to a Prefab and won't allow you to do this. -When a `NetworkBehaviour` is assigned to a `NetworkObject`, the `NetworkObject.NetworkObjectId` is used to help determine which `NetworkBehaviour` component instance will receive an update to a `NetworkVariable` or where to invoke an RPC. A `NetworkObject` component can have one or more `NetworkBehaviour` components assigned to it. +When a NetworkBehaviour is assigned to a NetworkObject, the `NetworkObject.NetworkObjectId` is used to help determine which NetworkBehaviour component instance will receive an update to a `NetworkVariable` or where to invoke an RPC. A NetworkObject component can have one or more NetworkBehaviour components assigned to it. ### Registering a Network Prefab -You must register a Network Prefab instance with a `NetworkManager` using a `NetworkedPrefabsList` scriptable object. -There are four steps to registering a network Prefab with a `NetworkManager`: +You must register a Network Prefab instance with a NetworkManager using a `NetworkedPrefabsList` scriptable object. +There are four steps to registering a network Prefab with a NetworkManager: -1. Create a Network Prefab by creating a Prefab with a `NetworkObject` component attached to the root `GameObject`. +1. Create a Network Prefab by creating a Prefab with a NetworkObject component attached to the root GameObject. 2. Create a scriptable object called `NetworkedPrefabsList` by right-clicking the project window, then: `Create/Netcode/NetworkedPrefabsList`. 3. Add your Network Prefab to the `NetworkPrefabsList`. -4. Add the `NetworkPrefabsList` to the Network Prefabs Lists that's associated with a `NetworkManager`. +4. Add the `NetworkPrefabsList` to the Network Prefabs Lists that's associated with a NetworkManager. ### Spawning a Network Prefab (Overview) -Netcode uses a server authoritative networking model so spawning netcode objects can only be done on a server or host. To spawn a network prefab, you must first create an instance of the network Prefab and then invoke the spawn method on the `NetworkObject` component of the instance you created. -_In most cases, you will want to keep the `NetworkObject` component attached to the root `GameObject` of the network prefab._ +Netcode uses a server authoritative networking model so spawning netcode objects can only be done on a server or host. To spawn a network prefab, you must first create an instance of the network Prefab and then invoke the spawn method on the NetworkObject component of the instance you created. +_In most cases, you will want to keep the NetworkObject component attached to the root GameObject of the network prefab._ By default a newly spawned network Prefab instance is owned by the server unless otherwise specified. -See [Ownership](../components/foundational/networkobject.md#ownership) for more information. +See [Ownership](../components/core/networkobject.md#ownership) for more information. The following is a basic example of how to spawn a network Prefab instance (with the default server ownership): @@ -51,7 +51,7 @@ When you set the destroyWithScene property to `false` it will be treated the sam [Learn more about Netcode Scene Management here](scenemanagement/scene-management-overview.md) > [!NOTE] -> You might find it useful to add a `GameObject` property in a `NetworkBehaviour`-derived component to use when assigning a network prefab instance for dynamically spawning. You need to make sure to instantiate a new instance **prior** to spawning. If you attempt to just spawn the actual network prefab instance it can result in unexpected results. +> You might find it useful to add a GameObject property in a NetworkBehaviour-derived component to use when assigning a network prefab instance for dynamically spawning. You need to make sure to instantiate a new instance **prior** to spawning. If you attempt to just spawn the actual network prefab instance it can result in unexpected results. ### Taking Prefab Overrides Into Consideration Sometimes, you might want to make a simpler prefab instance to be spawned on server version the override for clients. You should take this into consideration when dynamically spawning a network prefab. If you're running as a host, you want the override to spawn since a host is both a server and a client. However, if you also want to have the ability to run as a dedicated server, you might want to spawn the source network prefab. @@ -66,15 +66,15 @@ var instance = Instantiate(NetworkManager.GetNetworkPrefabOverride(myPrefab)); var instanceNetworkObject = instance.GetComponent<NetworkObject>(); instanceNetworkObject.Spawn(); ``` -In the above script, we get the prefab override using the `NetworkManager.GetNetworkPrefabOverride` method. Then we create an instance of the network prefab override, and finally we spawn the network prefab override instance's `NetworkObject`. +In the above script, we get the prefab override using the `NetworkManager.GetNetworkPrefabOverride` method. Then we create an instance of the network prefab override, and finally we spawn the network prefab override instance's NetworkObject. #### Using InstantiateAndSpawn -The second option is to leverage the `NetworkSpawnManager.InstantiateAndSpawn` method that handles whether or not to spawn an override for you. The below script is written as if it's being invoked within a `NetworkBehaviour`. +The second option is to leverage the `NetworkSpawnManager.InstantiateAndSpawn` method that handles whether or not to spawn an override for you. The below script is written as if it's being invoked within a NetworkBehaviour. ```csharp var networkObject = NetworkManager.SpawnManager.InstantiateAndSpawn(myPrefab, ownerId); ``` -We pass in the overridden source network prefab we want to have instantiated and spawned, and then it returns the instantiated and spawned `NetworkObject` of the spawned object. The default behavior of `InstantiateAndSpawn` is to spawn the override if running as a host and the original source prefab if running as a server. +We pass in the overridden source network prefab we want to have instantiated and spawned, and then it returns the instantiated and spawned NetworkObject of the spawned object. The default behavior of `InstantiateAndSpawn` is to spawn the override if running as a host and the original source prefab if running as a server. `InstantiateAndSpawn` has several parameters to provide more control over this process: @@ -82,7 +82,7 @@ We pass in the overridden source network prefab we want to have instantiated and InstantiateAndSpawn(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default) ``` -Looking at the parameters, we can see it defaults to the server as the owner, ensures that the instantiated `NetworkObject` won't be destroyed if the scene is unloaded, is not spawned as a player, has a `forceOverride` parameter, and provides a way to set the position and rotation of the newly instantiated `NetworkObject`. +Looking at the parameters, we can see it defaults to the server as the owner, ensures that the instantiated NetworkObject won't be destroyed if the scene is unloaded, is not spawned as a player, has a `forceOverride` parameter, and provides a way to set the position and rotation of the newly instantiated NetworkObject. The `forceOverride` parameter, when set to true, will use the override whether you're running as either server or host. @@ -91,7 +91,7 @@ The `forceOverride` parameter, when set to true, will use the override whether y By default, a spawned network Prefab instance that is destroyed on the server/host will be automatically destroyed on all clients. -When a client disconnects, all network Prefab instances created during the network session will be destroyed on the client-side by default. If you don't want that to happen, set the `DontDestroyWithOwner` field on `NetworkObject` to true before despawning. +When a client disconnects, all network Prefab instances created during the network session will be destroyed on the client-side by default. If you don't want that to happen, set the `DontDestroyWithOwner` field on NetworkObject to true before despawning. To do this at runtime: @@ -104,27 +104,27 @@ To make this the default from the editor Inspector view:  -As an alternative way, you can make the `NetworkObject.DontDestroyWithOwner` property default to `true` by setting it on the `NetworkObject` itself like in the above screenshot. +As an alternative way, you can make the `NetworkObject.DontDestroyWithOwner` property default to `true` by setting it on the NetworkObject itself like in the above screenshot. ### Despawning -Only a server can despawn a `NetworkObject`, and the default despawn behavior is to destroy the associated GameObject. to despawn but not destroy a `NetworkObject`, you should call `NetworkObject.Despawn` and pass false as the parameter. Clients will always be notified and will mirror the despawn behavior. If you despawn and destroy on the server then all clients will despawn and then destroy the `GameObject` that the `NetworkObjet` component is attached to. +Only a server can despawn a NetworkObject, and the default despawn behavior is to destroy the associated GameObject. to despawn but not destroy a NetworkObject, you should call `NetworkObject.Despawn` and pass false as the parameter. Clients will always be notified and will mirror the despawn behavior. If you despawn and destroy on the server then all clients will despawn and then destroy the GameObject that the `NetworkObjet` component is attached to. -On the client side, you should never call `Object.Destroy` on any `GameObject` with a `NetworkObject` component attached to it (this isn't supported and will cause an exception to be thrown). If you want to use a more client authority model, have the client with ownership invoke an RPC to defer the despawning on server side. +On the client side, you should never call `Object.Destroy` on any GameObject with a NetworkObject component attached to it (this isn't supported and will cause an exception to be thrown). If you want to use a more client authority model, have the client with ownership invoke an RPC to defer the despawning on server side. -The only way to despawn `NetworkObject` for a specific client is to use `NetworkObject.NetworkHide`. +The only way to despawn NetworkObject for a specific client is to use `NetworkObject.NetworkHide`. See: [Object Visibility](object-visibility.md) for more information on this. > [!NOTE] -> If you have `GameObject` children, with `NetworkBehaviour` components attached, of a parent `GameObject`, with a `NetworkObject` component attached, you can't disable the `GameObject` children before spawning or despawning. Doing so, in v1.0.0, can cause unexpected results and it's recommended to make sure all children are enabled in the hierarchy before spawning or despawning. +> If you have GameObject children, with NetworkBehaviour components attached, of a parent GameObject, with a NetworkObject component attached, you can't disable the GameObject children before spawning or despawning. Doing so, in v1.0.0, can cause unexpected results and it's recommended to make sure all children are enabled in the hierarchy before spawning or despawning. ## Dynamically Spawned Network Prefabs -Netcode for GameObjects uses the term "dynamically spawned" to convey that the `NetworkObject` is being spawned via user specific code. Whereas a player or in-scene placed `NetworkObject` (with scene management enabled) is typically spawned by Netcode for GameObjects. There are several ways to spawn a network Prefab via code: +Netcode for GameObjects uses the term "dynamically spawned" to convey that the NetworkObject is being spawned via user specific code. Whereas a player or in-scene placed NetworkObject (with scene management enabled) is typically spawned by Netcode for GameObjects. There are several ways to spawn a network Prefab via code: ### Dynamic Spawning (non-pooled): -This type of dynamically spawned `NetworkObject` typically is a simple wrapper class that holds a reference to the Prefab asset. In the example below, the `NonPooledDynamicSpawner.PrefabToSpawn` property holds a reference to the network prefab: +This type of dynamically spawned NetworkObject typically is a simple wrapper class that holds a reference to the Prefab asset. In the example below, the `NonPooledDynamicSpawner.PrefabToSpawn` property holds a reference to the network prefab: ```csharp public class NonPooledDynamicSpawner : NetworkBehaviour @@ -168,18 +168,18 @@ This type of dynamically spawned `NetworkObject` typically is a simple wrapper c Consumable and/or items that can be picked up by a player or NPC(that is, a weapon, health, potion, etc.) would be some examples of when you might want to use non-pooled dynamically spawned `NetworkObjects`. > [!NOTE] -> While the NonPooledDynamicSpawner example is one of the simplest ways to spawn a NetworkObject, there is a memory allocation cost associated with instantiating and destroying the GameObject and all attached components. This design pattern can sometimes be all you need for the netcode game asset you are working with, and other times you might want to respawn/re-use the object instance. When performance is a concern and you want to spawn more than just one `NetworkObject` during the lifetime of the spawner or want to repeatedly respawn a single `NetworkObject`, the less proccessor and memory allocation intensive technique is to use [pooled dynamic spawning](#pooled-dynamic-spawning). +> While the NonPooledDynamicSpawner example is one of the simplest ways to spawn a NetworkObject, there is a memory allocation cost associated with instantiating and destroying the GameObject and all attached components. This design pattern can sometimes be all you need for the netcode game asset you are working with, and other times you might want to respawn/re-use the object instance. When performance is a concern and you want to spawn more than just one NetworkObject during the lifetime of the spawner or want to repeatedly respawn a single NetworkObject, the less proccessor and memory allocation intensive technique is to use [pooled dynamic spawning](#pooled-dynamic-spawning). > [!NOTE] -> Really, when we use the term "non-pooled" more often than not we are referring to the concept that a `GameObject` will be instantiated on both the server and the clients each time an instance is spawned. +> Really, when we use the term "non-pooled" more often than not we are referring to the concept that a GameObject will be instantiated on both the server and the clients each time an instance is spawned. ### Pooled Dynamic Spawning -Pooled dynamic spawning is when netcode objects (`GameObject` with one `NetworkObject` component) aren't destroyed on the server or the client when despawned. Instead, specific components are just disabled (or the `GameObject` itself) when a netcode object is despawned. A pooled dynamically spawned netcode object is typically instantiated during an already memory allocation heavy period of time (like when a scene is loaded or even at the start of your application before even establishing a network connection). Pooled dynamically spawned netcode objects are more commonly thought of as more than one netcode object that can be re-used without incurring the memory allocation and initialization costs. However, you might also run into scenarios where you need just one dynamically spawned netcode object to be treated like a pooled dynmically spawned netcode object. +Pooled dynamic spawning is when netcode objects (GameObject with one NetworkObject component) aren't destroyed on the server or the client when despawned. Instead, specific components are just disabled (or the GameObject itself) when a netcode object is despawned. A pooled dynamically spawned netcode object is typically instantiated during an already memory allocation heavy period of time (like when a scene is loaded or even at the start of your application before even establishing a network connection). Pooled dynamically spawned netcode objects are more commonly thought of as more than one netcode object that can be re-used without incurring the memory allocation and initialization costs. However, you might also run into scenarios where you need just one dynamically spawned netcode object to be treated like a pooled dynmically spawned netcode object. Fortunately, Netcode for GameObjects provides you with a way to be in control over the instatiation and destruction process for one or many netcode objects by via the `INetworkPrefabInstanceHandler` interface. Any `INetworkPrefabInstanceHandler`implementation should be registered with the `NetworkPrefabHandler`(for multiple netcode objects see [Object Pooling](../advanced-topics/object-pooling.md)) to accomplish this. -The easiest way to not destroy a network Prefab instance is to have something, other than the instance itself, keeping a reference to the instance. This way you can simply set the root `GameObject` to be inactive when it's despawned while still being able to set it active when the same network Prefab type needs to be respawned. Below is one example of how you can accomplish this for a single netcode object instance: +The easiest way to not destroy a network Prefab instance is to have something, other than the instance itself, keeping a reference to the instance. This way you can simply set the root GameObject to be inactive when it's despawned while still being able to set it active when the same network Prefab type needs to be respawned. Below is one example of how you can accomplish this for a single netcode object instance: ```csharp public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstanceHandler @@ -295,44 +295,44 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan } ``` -You might run across a situation where you still want other components on the root `GameObject` of your network Prefab instance to remain active. Primarily, you want to be able to easily disable the components that would normally be active when the netcode object is considered spawned. +You might run across a situation where you still want other components on the root GameObject of your network Prefab instance to remain active. Primarily, you want to be able to easily disable the components that would normally be active when the netcode object is considered spawned. Below is an example of what a non-pooled friendly Prefab might look like:  -The issues you might run into with the above Prefab hierarchy is that everything is on a single `GameObject`, and as such if you wanted to disable the `MeshRenderer` and the `NetworkObjectLabel`, [one of our classes in the Netcode for GameObjects test project](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/f0631414e5a5358a5ac7811d43273b1a82a60ca9/testproject/Assets/Scripts/NetworkObjectLabel.cs#L4), you would need to get those component types before disabling them (that is, during `Start` or `OnNetworkSpawn` or get them when `OnNetworkDespawn` is invoked). +The issues you might run into with the above Prefab hierarchy is that everything is on a single GameObject, and as such if you wanted to disable the `MeshRenderer` and the `NetworkObjectLabel`, [one of our classes in the Netcode for GameObjects test project](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/f0631414e5a5358a5ac7811d43273b1a82a60ca9/testproject/Assets/Scripts/NetworkObjectLabel.cs#L4), you would need to get those component types before disabling them (that is, during `Start` or `OnNetworkSpawn` or get them when `OnNetworkDespawn` is invoked). To reduce this level of complexity, a more "pooled dynamic spawning" friendly Prefab heirarchy might look like this:  -The `NetworkObject` sits at the root `GameObject` of the network prefab. The child `GameObject`, SpawnedComponents, then has everything that you might want to have disabled when the network Prefab instance isn't spawned: +The NetworkObject sits at the root GameObject of the network prefab. The child GameObject, SpawnedComponents, then has everything that you might want to have disabled when the network Prefab instance isn't spawned:  -This reduces the complexity down to setting the SpawnedComponents `GameObject` to inactive, which will also disable all of the components attached to it. +This reduces the complexity down to setting the SpawnedComponents GameObject to inactive, which will also disable all of the components attached to it. > [!NOTE] > Using this type of a hierarchical separation is useful in many ways (especially when you have a much more complex prefab). For more complex prefabs, you can further expand this pattern into specific categories (that is, visuals, physics, sound, etc) which will provide you with a more macrocosmic way to control enabling or disabling many different components without having to have references to all of them. -## In-Scene Placed `NetworkObject` +## In-Scene Placed NetworkObject -Any objects in the scene with active and spawned `NetworkObject` components will get automatically replicated by Netcode. There is no need to manually spawn them when scene management is enabled in the `NetworkManager`. In-scene placed `NetworkObjects` should typically be used like a "static" netcode object, where the netcode object is typically spawned upon the scene being loaded on the server-side and synchronized with clients once they finish loading the same scene. +Any objects in the scene with active and spawned NetworkObject components will get automatically replicated by Netcode. There is no need to manually spawn them when scene management is enabled in the NetworkManager. In-scene placed `NetworkObjects` should typically be used like a "static" netcode object, where the netcode object is typically spawned upon the scene being loaded on the server-side and synchronized with clients once they finish loading the same scene. [Learn more about In-Scene Placed `NetworkObjects`](scenemanagement/inscene-placed-networkobjects.md) -Generally, there are **two** modes that define how an in-scene placed `NetworkObject` is synchronized. +Generally, there are **two** modes that define how an in-scene placed NetworkObject is synchronized. - Soft Synchronization (Scene Management enabled) - Prefab Synchronization (Scene Management disabled) ### Soft Synchronization -`SoftSync` or "Soft Synchronization" is a term you might run across if you run into any issue with in-scene placed `NetworkObjects`. Soft synchronization only occurs if scene management is enabled in the `NetworkManager` properties. If you receive a "soft synchronization error", then this typically means that a client can't locate the same in-scene placed `NetworkObject` after loading a scene. +`SoftSync` or "Soft Synchronization" is a term you might run across if you run into any issue with in-scene placed `NetworkObjects`. Soft synchronization only occurs if scene management is enabled in the NetworkManager properties. If you receive a "soft synchronization error", then this typically means that a client can't locate the same in-scene placed NetworkObject after loading a scene. ### Prefab Synchronization -`PrefabSync` or "Prefab Synchronization" is used if scene management is disabled in the `NetworkManager`. With Prefab synchronization, every in-scene placed `NetworkObject` has to be a network Prefab and must be registered with `NetworkPrefabs` list. When a client starts, Netcode will destroy all existing in-scene placed `NetworkObject`s and spawn its corresponding Prefab from the `NetworkPrefabs` list instead. This also means that you will have to implement your own scene manager and handle how you synchronize clients when they join a network session. +`PrefabSync` or "Prefab Synchronization" is used if scene management is disabled in the NetworkManager. With Prefab synchronization, every in-scene placed NetworkObject has to be a network Prefab and must be registered with `NetworkPrefabs` list. When a client starts, Netcode will destroy all existing in-scene placed NetworkObjects and spawn its corresponding Prefab from the `NetworkPrefabs` list instead. This also means that you will have to implement your own scene manager and handle how you synchronize clients when they join a network session. **PrefabSync is ONLY recommended for advanced development and/or multi project setups**. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/object-visibility.md b/com.unity.netcode.gameobjects/Documentation~/basics/object-visibility.md index 6d98cb5990..70cb358b2f 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/object-visibility.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/object-visibility.md @@ -1,10 +1,10 @@ # Object visibility -Object (NetworkObject) visibility is a Netcode for GameObjects term used to describe whether a `NetworkObject` is visible to one or more clients as it pertains to a netcode/network perspective. When a `NetworkObject` is visible to a client, the server will assure the client has a spawned version (a clone) of the `NetworkObject`. This also means that all network traffic generated by the server for the visible `NetworkObject` will be sent to all clients that are aware (that is, it's "visible to the clients") of it. Likewise, when a `NetworkObject` is "hidden" (that is, not visible) from a client, then the client will despawn and destroy the `NetworkObject` if it was previously visible and no network traffic generated by the hidden `NetworkObject` will be received by the client(s) it's hidden from. +Object (NetworkObject) visibility is a Netcode for GameObjects term used to describe whether a NetworkObject is visible to one or more clients as it pertains to a netcode/network perspective. When a NetworkObject is visible to a client, the server will assure the client has a spawned version (a clone) of the NetworkObject. This also means that all network traffic generated by the server for the visible NetworkObject will be sent to all clients that are aware (that is, it's "visible to the clients") of it. Likewise, when a NetworkObject is "hidden" (that is, not visible) from a client, then the client will despawn and destroy the NetworkObject if it was previously visible and no network traffic generated by the hidden NetworkObject will be received by the client(s) it's hidden from. ## Using Visibility -One way to determine visibility is to assign a callback to `NetworkObject.CheckObjectVisibility`. This callback is invoked when new clients connect or just before the associated `NetworkObject` is spawned. Looking at the example below, we can see the callback includes a client identifier (clientId) value as a parameter which is used to determine whether the `NetworkObject` is visible to the client. If `NetworkObject.CheckObjectVisibility` isn't assigned, then Netcode for GameObjects assumes it's visible to all clients. +One way to determine visibility is to assign a callback to `NetworkObject.CheckObjectVisibility`. This callback is invoked when new clients connect or just before the associated NetworkObject is spawned. Looking at the example below, we can see the callback includes a client identifier (clientId) value as a parameter which is used to determine whether the NetworkObject is visible to the client. If `NetworkObject.CheckObjectVisibility` isn't assigned, then Netcode for GameObjects assumes it's visible to all clients. ### CheckObjectVisibility Callback Example ```csharp @@ -79,27 +79,27 @@ public class VisibilityCheckExample : NetworkBehaviour ``` ### Additional Visibility Methods and Properties: -The `CheckObjectVisibility` callback helps you determine if a `NetworkObject` is visible to a specific client when the `NetworkObject` is spawned. However, you might have the need to change a `NetworkObject`'s visibility after it's spawned. To change the visibility of a `NetworkObject` that is already spawned, you can use the following methods: +The `CheckObjectVisibility` callback helps you determine if a NetworkObject is visible to a specific client when the NetworkObject is spawned. However, you might have the need to change a NetworkObject's visibility after it's spawned. To change the visibility of a NetworkObject that is already spawned, you can use the following methods: -Make a `NetworkObject` visible to a single client: +Make a NetworkObject visible to a single client: ```csharp NetworkObject netObject = GetComponent<NetworkObject>(); netObject.NetworkShow(clientIdToShowTo); ``` -Make a `NetworkObject` invisible/hidden from a single client: +Make a NetworkObject invisible/hidden from a single client: ```csharp NetworkObject netObject = GetComponent<NetworkObject>(); netObject.NetworkHide(clientIdToHideFrom); ``` -Make several `NetworkObject`s visible to a single client (static method): +Make several NetworkObjects visible to a single client (static method): ```csharp /// networkObjects is of type List<NetworkObject> NetworkObject.NetworkShow(networkObjects, clientId); ``` -Make several `NetworkObject`s invisible/hidden to a single client (static method): +Make several NetworkObjects invisible/hidden to a single client (static method): ```csharp /// networkObjects is of type List<NetworkObject> NetworkObject.NetworkHide(networkObjects, clientId); @@ -111,4 +111,4 @@ NetworkObject.SpawnWithObservers = false; NetworkObject.Spawn(); ``` -See [Spawning With (or Without) Observers](../components/foundational/networkobject.md#spawning-with-or-without-observers) for more information. +See [Spawning With (or Without) Observers](../components/core/networkobject.md#spawning-with-or-without-observers) for more information. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/ownership.md b/com.unity.netcode.gameobjects/Documentation~/basics/ownership.md index cd63291f66..bcfeb5e280 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/ownership.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/ownership.md @@ -1,6 +1,6 @@ # Understanding ownership and authority -By default, Netcode for GameObjects assumes a [client-server topology](../terms-concepts/client-server.md), in which the server owns all NetworkObjects (with [some exceptions](../components/foundational/networkobject.md#ownership)) and has ultimate authority over [spawning and despawning](object-spawning.md). +By default, Netcode for GameObjects assumes a [client-server topology](../terms-concepts/client-server.md), in which the server owns all NetworkObjects (with [some exceptions](../components/core/networkobject.md#ownership)) and has ultimate authority over [spawning and despawning](object-spawning.md). Netcode for GameObjects also supports building games with a [distributed authority topology](../terms-concepts/distributed-authority.md), which provides more options for ownership and authority over NetworkObjects. @@ -29,7 +29,7 @@ The following ownership permission settings, defined by `NetworkObject.Ownership You can also use `NetworkObject.SetOwnershipLock` to lock and unlock the permission settings of a NetworkObject for a period of time, preventing ownership changes on a temporary basis. > [!NOTE] -> The ownership permissions are only visible when the Multiplayer Services SDK package is installed and you're inspecting a `NetworkObject` within the editor. Ownership permissions have no impact when using a client-server network topology, since the server always has authority. For ownership permissions to be used, you must be using a distributed authority network topology. +> The ownership permissions are only visible when the Multiplayer Services SDK package is installed and you're inspecting a NetworkObject within the editor. Ownership permissions have no impact when using a client-server network topology, since the server always has authority. For ownership permissions to be used, you must be using a distributed authority network topology. ## Checking for authority diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/custom-management.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/custom-management.md index 883ad9914a..50d4d28531 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/custom-management.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/custom-management.md @@ -4,7 +4,7 @@ > If you haven't already read the [Using NetworkSceneManager](using-networkscenemanager.md) section, it's highly recommended to do so before proceeding. Custom Scene Management currently has some drawbacks that we hope to improve upon over time. ## Building Your Own Scene Management Solution -Netcode for GameObjects is designed to provide a scene management solution that should meet most projects' needs. However, some projects might still need to build their own scene management solution. The first step is to disable **Enable Scene Management** in your `NetworkManager`'s properties. All of your scene loading and unloading must be handled via the `UnityEngine.SceneManagement.SceneManager` class. +Netcode for GameObjects is designed to provide a scene management solution that should meet most projects' needs. However, some projects might still need to build their own scene management solution. The first step is to disable **Enable Scene Management** in your NetworkManager's properties. All of your scene loading and unloading must be handled via the `UnityEngine.SceneManagement.SceneManager` class. ### Integrated NetworkSceneManager Comparison (Enabled vs Disabled) Network Scene Management | Enabled | Disabled @@ -23,23 +23,23 @@ Clients are always synchronized with any in-scene placed Network Prefab instance > [!NOTE] > Any in-scene defined NetworkObject (i.e. not a network prefab instance) will not get synchronized with a newly-joined client when scene management is disabled. -When the integrated `NetworkSceneManager` is enabled, both in-scene placed network prefab instances and in-scene defined `NetworkObject`s get synchronized, and when new scenes are loaded they get synchronized also. +When the integrated `NetworkSceneManager` is enabled, both in-scene placed network prefab instances and in-scene defined NetworkObjects get synchronized, and when new scenes are loaded they get synchronized also. #### Auto (NetworkObject) Scene Migration Synchronization -When integrated scene management is enabled, you can have the server migrate an already spawned `NetworkObject` from one scene to another and it will automatically be synchronized with connected and late-joining clients. +When integrated scene management is enabled, you can have the server migrate an already spawned NetworkObject from one scene to another and it will automatically be synchronized with connected and late-joining clients. When integrated scene management is disabled, this is not handled and you need to use either NetworkVariables, Rpcs, or custom messages to handle synchronizing clients with any migration of `NetworkObjects`s from one scene to another. ### Registering In-Scene Placed Network Prefab Instances -While integrated scene management handles the synchronization of in-scene placed `NetworkObject`s, custom scene management treats everything like a dynamically spawned `NetworkObject`. As such, you can only use network prefabs defined in your NetworkPrefabList assigned to your NetworkManager, and any in-scene defined `NetworkObject`s will not synchronize with clients. +While integrated scene management handles the synchronization of in-scene placed NetworkObjects, custom scene management treats everything like a dynamically spawned NetworkObject. As such, you can only use network prefabs defined in your NetworkPrefabList assigned to your NetworkManager, and any in-scene defined NetworkObjects will not synchronize with clients. -Once you've registered your in-scene placed Network Prefabs with your `NetworkPrefabList` and assigned that to your `NetworkManager`, you can then start a server or host and have a client connect and synchronize with the in-scene placed Network Prefab instances (as long as both client and server have pre-loaded the scene or scenes required). +Once you've registered your in-scene placed Network Prefabs with your `NetworkPrefabList` and assigned that to your NetworkManager, you can then start a server or host and have a client connect and synchronize with the in-scene placed Network Prefab instances (as long as both client and server have pre-loaded the scene or scenes required). > [!NOTE] -> When a client first connects, it deletes any in-scene placed `NetworkObjects` in any of the scenes it has currently loaded. When using a custom scene management solution, in-scene placed `NetworkObject`s are actually dynamically spawned. This means any changes you make to your in-scene placed Network Prefabs will *not* be synchronized with clients automatically. +> When a client first connects, it deletes any in-scene placed `NetworkObjects` in any of the scenes it has currently loaded. When using a custom scene management solution, in-scene placed NetworkObjects are actually dynamically spawned. This means any changes you make to your in-scene placed Network Prefabs will *not* be synchronized with clients automatically. ### Synchronizing In-Scene Placed Network Prefab Instances -If you want to change an in-scene placed network prefab instance, you need to handle the serialization of these settings yourself. You can do this by overriding `NetworkBehaviour.OnSynchronize` and serializing any property updates you want to have synchronized with clients when they join. [Read More About OnSynchronize Here](../../components/foundational/networkbehaviour.md#pre-spawn-synchronization). +If you want to change an in-scene placed network prefab instance, you need to handle the serialization of these settings yourself. You can do this by overriding `NetworkBehaviour.OnSynchronize` and serializing any property updates you want to have synchronized with clients when they join. [Read More About OnSynchronize Here](../../components/core/networkbehaviour.md#pre-spawn-synchronization). ## Starting a Netcode Enabled Game Session The recommended way of starting session using your own scene management solution is to assure that when a client attempts to join a netcode game session it should already have (as best as possible) any scenes that the server might have loaded. While this does not assure that your newly connecting client will load any additional scenes that might have been loaded, using this approach initially will get you started so you can then come up with a strategy to handling: @@ -56,4 +56,4 @@ The recommended way of starting session using your own scene management solution You can accomplish this using either RPCs or custom messages. You might even use your own `INetworkSerializable` implementation that has a list of scenes and whether they should be loaded or unloaded. You should always have some form of "complete" response factored into your design so you know when a client has finished loading/unloading a scene. You will also want to devise a strategy for loading a scene in `LoadSceneMode.Additive` and `LoadSceneMode.Single` modes. > [!NOTE] -> Creating a global scene management script and attaching it to the same GameObject as the `NetworkManager` is one way to assure your custom netcode scene management solution persists while a game session is in progress. +> Creating a global scene management script and attaching it to the same GameObject as the NetworkManager is one way to assure your custom netcode scene management solution persists while a game session is in progress. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md index 4bc8228a36..6bc209722b 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md @@ -3,24 +3,24 @@ > [!NOTE] > If you haven't already read the [Using NetworkSceneManager](using-networkscenemanager.md) section, it's highly recommended to do so before proceeding. -In-scene placed `NetworkObject`s are GameObjects with a `NetworkObject` component that was added to a scene from within the Editor. You can use in-scene placed `NetworkObject`s for: +In-scene placed NetworkObjects are GameObjects with a NetworkObject component that was added to a scene from within the Editor. You can use in-scene placed NetworkObjects for: - Management systems - - For example, a `NetworkObject` pool management system that dynamically spawns network prefabs. + - For example, a NetworkObject pool management system that dynamically spawns network prefabs. - Interactive world objects that are typically easier to place in-scene than spawn - - For example, a door that can only be unlocked by a key. If a player unlocks it then you want other players to know about it being unlocked, and making it an in-scene placed `NetworkObject` simplifies the positioning of the door relative to the surrounding world geometry. + - For example, a door that can only be unlocked by a key. If a player unlocks it then you want other players to know about it being unlocked, and making it an in-scene placed NetworkObject simplifies the positioning of the door relative to the surrounding world geometry. - Static visual elements - For example, a heads up display (HUD) that includes information about other items or players. - Or some form of platform or teleporter that moves a player from one location to the next when a player enters a trigger or uses an object. -Another benefit of in-scene placed `NetworkObject`s is that they don't require you to register them with the [`NetworkManager`](../../components/foundational/networkmanager.md). In-scene placed `NetworkObjects` are registered internally, when scene management is enabled, for tracking and identification purposes. +Another benefit of in-scene placed NetworkObjects is that they don't require you to register them with the [NetworkManager](../../components/core/networkmanager.md). In-scene placed `NetworkObjects` are registered internally, when scene management is enabled, for tracking and identification purposes. > [!NOTE] -> Items that can be picked up are typically better implemented using a [hybrid approach](#hybrid-approach) with both an in-scene placed and a dynamically spawned `NetworkObject`. The in-scene placed `NetworkObject` can be used to configure additional information about the item (what kind, does another one respawn after one is picked up, and if so how much time should it wait before spawning a new item), while the dynamically spawned object is the item itself. +> Items that can be picked up are typically better implemented using a [hybrid approach](#hybrid-approach) with both an in-scene placed and a dynamically spawned NetworkObject. The in-scene placed NetworkObject can be used to configure additional information about the item (what kind, does another one respawn after one is picked up, and if so how much time should it wait before spawning a new item), while the dynamically spawned object is the item itself. -## In-scene placed vs. dynamically spawned `NetworkObject`s (order of operations) +## In-scene placed vs. dynamically spawned NetworkObjects (order of operations) -Because in-scene placed `NetworkObject`s are instantiated when a scene loads, they have a different order of operations than dynamically spawned `NetworkObject`s when it comes to spawning. The table below illustrates that spawning occurs after `Awake` but before `Start` for dynamically spawned `NetworkObject`s, but for in-scene placed `NetworkObject`s it occurs after both `Awake` and `Start` are invoked. +Because in-scene placed NetworkObjects are instantiated when a scene loads, they have a different order of operations than dynamically spawned NetworkObjects when it comes to spawning. The table below illustrates that spawning occurs after `Awake` but before `Start` for dynamically spawned NetworkObjects, but for in-scene placed NetworkObjects it occurs after both `Awake` and `Start` are invoked. Dynamically spawned | In-scene placed ------------------- | --------------- @@ -28,7 +28,7 @@ Dynamically spawned | In-scene placed `OnNetworkSpawn` | `Start` `Start` | `OnNetworkSpawn` -In-scene placed `NetworkObject`s are instantiated when the scene is loaded, which means that both the `Awake` and `Start` methods are invoked before an in-scene placed `NetworkObject` is spawned. This distinct difference is important to keep in mind when doing any form of dependency-related initializations that might require an active network session. This is especially important to consider when you're using the same `NetworkBehaviour` component with both dynamically spawned and in-scene placed `NetworkObjects`. +In-scene placed NetworkObjects are instantiated when the scene is loaded, which means that both the `Awake` and `Start` methods are invoked before an in-scene placed NetworkObject is spawned. This distinct difference is important to keep in mind when doing any form of dependency-related initializations that might require an active network session. This is especially important to consider when you're using the same NetworkBehaviour component with both dynamically spawned and in-scene placed `NetworkObjects`. ## In-scene placed network prefab instances @@ -36,36 +36,36 @@ For frequently used in-scene `NetworkObjects`, you can use a [network prefab](.. ### Creating in-scene placed network prefab instances -To create a network prefab that can be used as an in-scene placed `NetworkObject`, do the following: +To create a network prefab that can be used as an in-scene placed NetworkObject, do the following: 1. Create a prefab asset (from a GameObject in a scene or creating it directly in a subfolder within Assets). -2. Add only one `NetworkObject` component to the root GameObject of the prefab. -3. Add any other `NetworkBehaviour`s (on the root or on any level of child GameObject under the root prefab GameObject). +2. Add only one NetworkObject component to the root GameObject of the prefab. +3. Add any other NetworkBehaviours (on the root or on any level of child GameObject under the root prefab GameObject). 4. If you created the prefab asset from an existing scene, then the original in-scene placed object will automatically become a network prefab instance. 5. If you created the prefab asset directly in a subfolder under the Assets directory, then drag and drop the newly created network prefab into the scene of choice. > [!NOTE] -> You may need to deactivate **Enter Play Mode Options** if your `NetworkBehaviour` components do not spawn. +> You may need to deactivate **Enter Play Mode Options** if your NetworkBehaviour components do not spawn. -## Using in-scene placed `NetworkObject`s +## Using in-scene placed NetworkObjects -There are some common use cases where in-scene placed `NetworkObject`s can prove useful. +There are some common use cases where in-scene placed NetworkObjects can prove useful. ### Static objects -You can use an in-scene placed `NetworkObject` to keep track of when a door is opened, a button pushed, a lever pulled, or any other type of state with a simple on/off toggle. These are referred to as static objects, and have the following in common: +You can use an in-scene placed NetworkObject to keep track of when a door is opened, a button pushed, a lever pulled, or any other type of state with a simple on/off toggle. These are referred to as static objects, and have the following in common: - Static objects are spawned while the originating scene is loaded and only despawned when the originating scene is unloaded. - - The originating scene is the scene in which the in-scene `NetworkObject` was placed. + - The originating scene is the scene in which the in-scene NetworkObject was placed. - Static objects are typically associated with some world object that's visible to players (such as a door or switch). - - Static objects aren't migrated into other scenes or parented under another `NetworkObject`. + - Static objects aren't migrated into other scenes or parented under another NetworkObject. - For example, a drawbridge that comes down when a certain game state is reached. The drawbridge is most likely connected to some castle or perhaps a section of the castle and would never need to be migrated to another scene. ### Netcode managers -You can use an in-scene placed `NetworkObject` as a netcode manager, for tracking game states, or as a `NetworkObject` spawn manager. Typically, a manager stays instantiated and spawned as long as the scene it was placed in remains loaded. For scenarios where you want to keep a global game state, the recommended solution is to place the in-scene `NetworkObject` in an additively loaded scene that remains loaded for the duration of your network game session. +You can use an in-scene placed NetworkObject as a netcode manager, for tracking game states, or as a NetworkObject spawn manager. Typically, a manager stays instantiated and spawned as long as the scene it was placed in remains loaded. For scenarios where you want to keep a global game state, the recommended solution is to place the in-scene NetworkObject in an additively loaded scene that remains loaded for the duration of your network game session. -If you're using scene switching (that is, loading a scene in `LoadSceneMode.Single`), then you can migrate the in-scene placed `NetworkObject` (used for management purposes) into the DDoL by sending its `GameObject` to the DDoL: +If you're using scene switching (that is, loading a scene in `LoadSceneMode.Single`), then you can migrate the in-scene placed NetworkObject (used for management purposes) into the DDoL by sending its GameObject to the DDoL: ```csharp private void Start() @@ -76,68 +76,68 @@ private void Start() ``` > [!NOTE] -> Once migrated into the DDoL, migrating the in-scene placed `NetworkObject` back into a different scene after it has already been spawned will cause soft synchronization errors with late-joining clients. Once in the DDoL it should stay in the DDoL. This is only for scene switching. If you aren't using scene switching, then it's recommended to use an additively loaded scene and keep that scene loaded for as long as you wish to persist the in-scene placed `NetworkObject`(s) being used for state management purposes. +> Once migrated into the DDoL, migrating the in-scene placed NetworkObject back into a different scene after it has already been spawned will cause soft synchronization errors with late-joining clients. Once in the DDoL it should stay in the DDoL. This is only for scene switching. If you aren't using scene switching, then it's recommended to use an additively loaded scene and keep that scene loaded for as long as you wish to persist the in-scene placed NetworkObject(s) being used for state management purposes. -## Complex in-scene `NetworkObject`s +## Complex in-scene NetworkObjects -The most common mistake when using an in-scene placed `NetworkObject` is to try and use it like a dynamically spawned `NetworkObject`. When trying to decide if you should use an in-scene placed or dynamically spawned `NetworkObject`, you should ask yourself the following questions: +The most common mistake when using an in-scene placed NetworkObject is to try and use it like a dynamically spawned NetworkObject. When trying to decide if you should use an in-scene placed or dynamically spawned NetworkObject, you should ask yourself the following questions: -- Do you plan on despawning and destroying the `NetworkObject`? - - If yes, then it's highly recommended that you use a dynmically spawned `NetworkObject`. -- Can it be parented, at runtime, under another `NetworkObject`? +- Do you plan on despawning and destroying the NetworkObject? + - If yes, then it's highly recommended that you use a dynmically spawned NetworkObject. +- Can it be parented, at runtime, under another NetworkObject? - Excluding any special case DDoL scenarios, will it be moved into another scene other than the originating scene? - Do you plan on having full scene-migrations (that is, `LoadSceneMode.Single` or scene switching) during a network session? -If you answered yes to any of the above questions, then using only an in-scene placed `NetworkObject` to implement your feature might not be the right choice. However, you can use a hybrid approach to get the best of both methods. +If you answered yes to any of the above questions, then using only an in-scene placed NetworkObject to implement your feature might not be the right choice. However, you can use a hybrid approach to get the best of both methods. ### Hybrid approach -Because there are additional complexities involved with in-scene placed `NetworkObject`s, some use cases are more suited to dynamically spawned `NetworkObject`s, or require a combination of both types. Perhaps your project's design includes making some world items that can either be consumed (such as health) or picked up (such as weapons and items) by players. Initially, using a single in-scene placed `NetworkObject` might seem like the best approach for this world item feature. +Because there are additional complexities involved with in-scene placed NetworkObjects, some use cases are more suited to dynamically spawned NetworkObjects, or require a combination of both types. Perhaps your project's design includes making some world items that can either be consumed (such as health) or picked up (such as weapons and items) by players. Initially, using a single in-scene placed NetworkObject might seem like the best approach for this world item feature. -However, there's another way to accomplish the same thing while maintaining a clear distinction between dynamically spawned and in-scene placed `NetworkObject`s. Rather than combining everything into a single network prefab and handling the additional complexities involved with in-scene placed `NetworkObject`s, you can create two network prefabs: +However, there's another way to accomplish the same thing while maintaining a clear distinction between dynamically spawned and in-scene placed NetworkObjects. Rather than combining everything into a single network prefab and handling the additional complexities involved with in-scene placed NetworkObjects, you can create two network prefabs: 1. A world item network prefab: since this will be dynamically spawned, you can re-use this network prefab with any spawn manager (pooled or single). -2. A single-spawn manager (non-pooled): this will spawn the world item network prefab. The single-spawn manager can spawn the dynamically spawned `NetworkObject` at its exact location with an option to use the same rotation and scale. +2. A single-spawn manager (non-pooled): this will spawn the world item network prefab. The single-spawn manager can spawn the dynamically spawned NetworkObject at its exact location with an option to use the same rotation and scale. > [!NOTE] > Your single-spawn manager can also have a list of GameObjects used as spawn points if you want to spawn world items in various locations randomly and/or based on game state. Using this approach allows you to: 1. Re-use the same single-spawn manager with any other network prefab registered with a `NetworkPrefabsList`. -2. Not worry about the complexities involved with treating an in-scene placed `NetworkObject` like a dynamically spawned one. +2. Not worry about the complexities involved with treating an in-scene placed NetworkObject like a dynamically spawned one. [You can see a hybrid approach example here.](../object-spawning.md#dynamic-spawning-non-pooled) -## Spawning and despawning in-scene placed `NetworkObject`s +## Spawning and despawning in-scene placed NetworkObjects -By default, an in-scene placed `NetworkObject` is spawned when the scene it's placed in is loaded and a network session is in progress. In-scene placed `NetworkObject`s differ from dynamically spawned `NetworkObject`s when it comes to spawning and despawning: when despawning a dynamically spawned `NetworkObject`, you can always spawn a new instance of the `NetworkObject`'s associated network prefab. So, whether you decide to destroy a dynamically spawned `NetworkObject` or not, you can always make another clone of the same network prefab, unless you want to preserve the current state of the instance being despawned. +By default, an in-scene placed NetworkObject is spawned when the scene it's placed in is loaded and a network session is in progress. In-scene placed NetworkObjects differ from dynamically spawned NetworkObjects when it comes to spawning and despawning: when despawning a dynamically spawned NetworkObject, you can always spawn a new instance of the NetworkObject's associated network prefab. So, whether you decide to destroy a dynamically spawned NetworkObject or not, you can always make another clone of the same network prefab, unless you want to preserve the current state of the instance being despawned. -With in-scene placed `NetworkObject`s, the scene it's placed in is similar to the network prefab used to dynamically spawn a `NetworkObject`, in that both are used to uniquely identify the spawned `NetworkObject`. The primary difference is that where you use a network prefab to create a new dynamically spawned instance, for in-scene placed objects you need to additively load the same scene to create another in-scene placed `NetworkObject` instance. +With in-scene placed NetworkObjects, the scene it's placed in is similar to the network prefab used to dynamically spawn a NetworkObject, in that both are used to uniquely identify the spawned NetworkObject. The primary difference is that where you use a network prefab to create a new dynamically spawned instance, for in-scene placed objects you need to additively load the same scene to create another in-scene placed NetworkObject instance. -### Identifying spawned `NetworkObject`s +### Identifying spawned NetworkObjects Dynamically spawned | In-scene placed ------------------- | --------------- `NetworkPrefab` | Scene `GlobalObjectIdHash` | Scene handle (when loaded) and `GlobalObjectIdHash` -Once the `NetworkObject` is spawned, Netcode for GameObjects uses the `NetworkObjectId` to uniquely identify it for both types. An in-scene placed `NetworkObject` will continue to be uniquely identified by the scene handle that it originated from and the `GlobalObjectIdHash`, even if the in-scene placed `NetworkObject` is migrated to another additively loaded scene and the originating scene is unloaded. +Once the NetworkObject is spawned, Netcode for GameObjects uses the `NetworkObjectId` to uniquely identify it for both types. An in-scene placed NetworkObject will continue to be uniquely identified by the scene handle that it originated from and the `GlobalObjectIdHash`, even if the in-scene placed NetworkObject is migrated to another additively loaded scene and the originating scene is unloaded. -### Despawning and respawning the same in-scene placed `NetworkObject` +### Despawning and respawning the same in-scene placed NetworkObject -When invoking the `Despawn` method of a `NetworkObject` with no parameters, it defaults to destroying the `NetworkObject`: +When invoking the `Despawn` method of a NetworkObject with no parameters, it defaults to destroying the NetworkObject: ```csharp NetworkObject.Despawn(); // Will despawn and destroy (!!! not recommended !!!) ``` -If you want an in-scene placed `NetworkObject` to persist after it's been despawned, it's recommended to always set the first parameter of the `Despawn` method to `false`: +If you want an in-scene placed NetworkObject to persist after it's been despawned, it's recommended to always set the first parameter of the `Despawn` method to `false`: ```csharp NetworkObject.Despawn(false); // Will only despawn (recommended usage for in-scene placed NetworkObjects) ``` -Now that you have a despawned `NetworkObject`, you might notice that the associated `GameObject` and all of its components are still active and possibly visible to all players (that is, like a `MeshRenderer` component). Unless you have a specific reason to keep the associated `GameObject` active in the hierarchy, you can override the virtual `OnNetworkDespawn` method in a `NetworkBehaviour`-derived component and set the `GameObject` to inactive: +Now that you have a despawned NetworkObject, you might notice that the associated GameObject and all of its components are still active and possibly visible to all players (that is, like a `MeshRenderer` component). Unless you have a specific reason to keep the associated GameObject active in the hierarchy, you can override the virtual `OnNetworkDespawn` method in a NetworkBehaviour-derived component and set the GameObject to inactive: ```csharp using UnityEngine; @@ -153,7 +153,7 @@ public class MyInSceneNetworkObjectBehaviour : NetworkBehaviour } ``` -This ensures that when your in-scene placed `NetworkObject` is despawned, it won't consume processing or rendering cycles and will become invisible to all players (either currently connected or that join the session later). Once the `NetworkObject` has been despawned and disabled, you might want to respawn it at some later time. To do this, set the server-side instance's `GameObject` back to being active and spawn it: +This ensures that when your in-scene placed NetworkObject is despawned, it won't consume processing or rendering cycles and will become invisible to all players (either currently connected or that join the session later). Once the NetworkObject has been despawned and disabled, you might want to respawn it at some later time. To do this, set the server-side instance's GameObject back to being active and spawn it: ```csharp using UnityEngine; @@ -179,11 +179,11 @@ public class MyInSceneNetworkObjectBehaviour : NetworkBehaviour ``` > [!NOTE] -> You only need to enable the `NetworkObject` on the server-side to be able to respawn it. Netcode for GameObjects only enables a disabled in-scene placed `NetworkObject` on the client-side if the server-side spawns it. This **does not** apply to dynamically spawned `NetworkObjects`. Refer to [the object pooling page](../../advanced-topics/object-pooling.md) for an example of recycling dynamically spawned `NetworkObject`s. +> You only need to enable the NetworkObject on the server-side to be able to respawn it. Netcode for GameObjects only enables a disabled in-scene placed NetworkObject on the client-side if the server-side spawns it. This **does not** apply to dynamically spawned `NetworkObjects`. Refer to [the object pooling page](../../advanced-topics/object-pooling.md) for an example of recycling dynamically spawned NetworkObjects. -### Setting an in-scene placed `NetworkObject` to a despawned state when instantiating +### Setting an in-scene placed NetworkObject to a despawned state when instantiating -Since in-scene placed `NetworkObject`s are automatically spawned when their respective scene has finished loading during a network session, you might run into the scenario where you want it to start in a despawned state until a certain condition has been met. To do this, you need to add some additional code in the `OnNetworkSpawn` part of your `NetworkBehaviour` component: +Since in-scene placed NetworkObjects are automatically spawned when their respective scene has finished loading during a network session, you might run into the scenario where you want it to start in a despawned state until a certain condition has been met. To do this, you need to add some additional code in the `OnNetworkSpawn` part of your NetworkBehaviour component: ```csharp using UnityEngine; @@ -221,34 +221,34 @@ using Unity.Netcode; } ``` -The above example keeps track of whether the in-scene placed `NetworkObject` has started in a despawned state (to avoid despawning after its first time being spawned), and it only allows the server to execute that block of code in the overridden `OnNetworkSpawn` method. The above `MyInSceneNetworkObjectBehaviour` example also declares a public `bool` property `StartDespawned` to provide control over this through the inspector view in the Editor. +The above example keeps track of whether the in-scene placed NetworkObject has started in a despawned state (to avoid despawning after its first time being spawned), and it only allows the server to execute that block of code in the overridden `OnNetworkSpawn` method. The above `MyInSceneNetworkObjectBehaviour` example also declares a public `bool` property `StartDespawned` to provide control over this through the inspector view in the Editor. -### Synchronizing late-joining clients when an in-scene placed `NetworkObject` has been despawned and destroyed +### Synchronizing late-joining clients when an in-scene placed NetworkObject has been despawned and destroyed -Referring back to the [section on complex in-scene `NetworkObject`s](#complex-in-scene-networkobjects), it's recommended to use dynamically spawned `NetworkObject`s if you intend to destroy the object when it's despawned. However, if either despawning but not destroying or using the [hybrid approach](#a-hybrid-approach-example) don't appear to be options for your project's needs, then there are two other possible (but not recommended) alternatives: +Referring back to the [section on complex in-scene NetworkObjects](#complex-in-scene-networkobjects), it's recommended to use dynamically spawned NetworkObjects if you intend to destroy the object when it's despawned. However, if either despawning but not destroying or using the [hybrid approach](#a-hybrid-approach-example) don't appear to be options for your project's needs, then there are two other possible (but not recommended) alternatives: -- Have another in-scene placed `NetworkObject` track which in-scene placed `NetworkObject`s have been destroyed and upon a player late-joining (that is, `OnClientConnected`) you would need to send the newly-joined client the list of in-scene placed NetworkObjects that it should destroy. This adds an additional in-scene placed `NetworkObject` to your scene hierarchy and will consume memory keeping track of what was destroyed. -- Disable the visual and physics-related components (in Editor as a default) of the in-scene placed `NetworkObject`(s) in question and only enable them in `OnNetworkSpawn`. This doesn't delete/remove the in-scene placed `NetworkObject`(s) for the late-joining client and can be tricky to implement without running into edge case scenario bugs. +- Have another in-scene placed NetworkObject track which in-scene placed NetworkObjects have been destroyed and upon a player late-joining (that is, `OnClientConnected`) you would need to send the newly-joined client the list of in-scene placed NetworkObjects that it should destroy. This adds an additional in-scene placed NetworkObject to your scene hierarchy and will consume memory keeping track of what was destroyed. +- Disable the visual and physics-related components (in Editor as a default) of the in-scene placed NetworkObject(s) in question and only enable them in `OnNetworkSpawn`. This doesn't delete/remove the in-scene placed NetworkObject(s) for the late-joining client and can be tricky to implement without running into edge case scenario bugs. -These two alternatives aren't recommended, but are worth briefly exploring to better understand why it's recommend to use a [non-pooled hybrid approach](../object-spawning.md#dynamic-spawning-non-pooled), or just not destroying the in-scene placed `NetworkObject` when despawning it. +These two alternatives aren't recommended, but are worth briefly exploring to better understand why it's recommend to use a [non-pooled hybrid approach](../object-spawning.md#dynamic-spawning-non-pooled), or just not destroying the in-scene placed NetworkObject when despawning it. -## Parenting in-scene placed `NetworkObject`s +## Parenting in-scene placed NetworkObjects -In-scene placed `NetworkObject`s follow the same parenting rules as [dynamically spawned `NetworkObject`s](../../advanced-topics/networkobject-parenting.md) with only a few differences and recommendations: +In-scene placed NetworkObjects follow the same parenting rules as [dynamically spawned NetworkObjects](../../advanced-topics/networkobject-parenting.md) with only a few differences and recommendations: -- You can create complex nested `NetworkObject` hierarchies when they're in-scene placed. -- If you plan on using full scene-migration (that is, `LoadSceneMode.Single` or scene switching) then parenting an in-scene placed `NetworkObject` that stays parented during the scene migration isn't recommended. - - In this scenario, you should use a hybrid approach where the in-scene placed `NetworkObject` dynamically spawns the item to be picked up. -- If you plan on using a bootstrap scene usage pattern with additive scene loading and unloading with no full scene-migration(s), then you can parent in-scene placed `NetworkObject`s. +- You can create complex nested NetworkObject hierarchies when they're in-scene placed. +- If you plan on using full scene-migration (that is, `LoadSceneMode.Single` or scene switching) then parenting an in-scene placed NetworkObject that stays parented during the scene migration isn't recommended. + - In this scenario, you should use a hybrid approach where the in-scene placed NetworkObject dynamically spawns the item to be picked up. +- If you plan on using a bootstrap scene usage pattern with additive scene loading and unloading with no full scene-migration(s), then you can parent in-scene placed NetworkObjects. ### Auto object parent sync option - Already parented in-scene placed `NetworkObject`s auto object parent sync usage: + Already parented in-scene placed NetworkObjects auto object parent sync usage: -- When disabled, the `NetworkObject` ignores its parent and considers all of its transform values as being world space synchronized (that is, no matter where you move or rotate its parent, it will keep its current position and rotation). - - Typically, when disabling this you need to handle synchronizing the client either through your own custom messages or RPCS, or add a `NetworkTransform` component to it. This is only useful if you want to have some global parent that might shift or have transform values that you don't want to impact the `NetworkObject` in question. -- When enabled, the `NetworkObject` is aware of its parent and will treat all of its transform values as being local space synchronized. - - This also applies to being pre-parented under a `GameObject` with no `NetworkObject` component. +- When disabled, the NetworkObject ignores its parent and considers all of its transform values as being world space synchronized (that is, no matter where you move or rotate its parent, it will keep its current position and rotation). + - Typically, when disabling this you need to handle synchronizing the client either through your own custom messages or RPCS, or add a NetworkTransform component to it. This is only useful if you want to have some global parent that might shift or have transform values that you don't want to impact the NetworkObject in question. +- When enabled, the NetworkObject is aware of its parent and will treat all of its transform values as being local space synchronized. + - This also applies to being pre-parented under a GameObject with no NetworkObject component. _**The caveat to the above is scale**:_ Scale is treated always as local space relative for pre-parented in-scene placed `NetworkObjects`. <br /> @@ -260,11 +260,11 @@ WorldPositionStays = false: Everything is local space relative. _(children offse ### Parenting and transform synchronization -Without the use of a `NetworkTransform`, clients are only synchronized with the transform values when: +Without the use of a NetworkTransform, clients are only synchronized with the transform values when: -- A client is being synchronized with the `NetworkObject` in question: +- A client is being synchronized with the NetworkObject in question: - During the client's first synchronization after a client has their connection approved. - - When a server spawns a new `NetworkObject`. -- A `NetworkObject` has been parented (or a parent removed). + - When a server spawns a new NetworkObject. +- A NetworkObject has been parented (or a parent removed). - The server can override the `NetworkBehaviour.OnNetworkObjectParentChanged` method and adjust the transform values when that is invoked. - These transform changes will be synchronized with clients via the `ParentSyncMessage`. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-events.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-events.md index 89933f4dcc..817affd33f 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-events.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-events.md @@ -27,27 +27,27 @@ This is automatically happens after a client is connected and approved.<br/> **Initiating Scene Event**: `SceneEventType.Synchronize`<br/> **Associated Scene Events**: - `SceneEventType.SynchronizeComplete`: <br/> -signifies that the client has finished loading all scenes and locally spawned all Netcode objects. The client sends this scene event message back to the server. This message also includes a list of `NetworkObject.NetworkObjectId`s for all of the `NetworkObject`s the client spawned. +signifies that the client has finished loading all scenes and locally spawned all Netcode objects. The client sends this scene event message back to the server. This message also includes a list of `NetworkObject.NetworkObjectId`s for all of the NetworkObjects the client spawned. - `SceneEventType.ReSynchronize`: <br/> -signifies that the server determines the client needs to be "re-synchronized" because one or more `NetworkObject`s were despawned while the client was synchronizing. This message is sent to the client with a `NetworkObject.NetworkObjectId` list for all `NetworkObject`s the client needs to despawn. +signifies that the server determines the client needs to be "re-synchronized" because one or more NetworkObjects were despawned while the client was synchronizing. This message is sent to the client with a `NetworkObject.NetworkObjectId` list for all NetworkObjects the client needs to despawn. ## Client Synchronization Details While client synchronization does fall partially outside of the scene management realm, it ended up making more sense to handle the initial client synchronization via the `NetworkSceneManager` since a large part of the synchronization process involves loading scenes and synchronizing (in-scene placed and dynamically spawned) `NetworkObjects`. - Scene synchronization is the first thing a client processes. - The synchronization message includes a list of all scenes the server has loaded via the `NetworkSceneManager`. - - The client will load all of these scenes before proceeding to the `NetworkObject` synchronization. - - This approach was used to assure all `GameObject`, `NetworkObject`, and `NetworkBehaviour` dependencies are loaded and instantiated before a client attempts to locally spawn a `NetworkObject`. + - The client will load all of these scenes before proceeding to the NetworkObject synchronization. + - This approach was used to assure all GameObject, NetworkObject, and NetworkBehaviour dependencies are loaded and instantiated before a client attempts to locally spawn a NetworkObject. - Synchronizing with all spawned `NetworkObjects`. - Typically this involves both in-scene placed and dynamically spawned `NetworkObjects`. - Learn more about [Object Spawning here](..\object-spawning.md). - - The `NetworkObject` list sent to the client is pre-ordered, by the server, to account for certain types of dependencies such as when using [Object Pooling](../../advanced-topics/object-pooling.md). + - The NetworkObject list sent to the client is pre-ordered, by the server, to account for certain types of dependencies such as when using [Object Pooling](../../advanced-topics/object-pooling.md). - Typically object pool managers are in-scene placed and need to be instantiated and spawned before spawning any of its pooled `NetworkObjects` on a client that is synchronizing. As such, `NetworkSceneManager` takes this into account to assure that all `NetworkObjects` spawned via the `NetworkPrefabHandler` will be instantiated and spawned after their object pool manager dependency has been instantiated and spawned locally on the client. - You can have parented in-scene placed NetworkObjects (that is, items that are picked up or consumed by players) - - `NetworkSceneManager` uses a combination of the `NetworkObject.GlobalObjectIdHash` and the instantiating scene's handle to uniquely identify in-scene placed `NetworkObject`s. + - `NetworkSceneManager` uses a combination of the `NetworkObject.GlobalObjectIdHash` and the instantiating scene's handle to uniquely identify in-scene placed NetworkObjects. > [!NOTE] -> With additively loaded scenes, you can run into situations where your object pool manager, instantiated when the scene it's defined within is additively loaded by the server, is leaving its spawned `NetworkObject` instances within the [currently active scene](https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.GetActiveScene.html). While assuring that newly connected clients being synchronized have loaded all of the scenes first helps to avoid scene dependency issues, this alone does not resolve issue with the `NetworkObject` spawning order. The integrated scene management, included in Netcode for GameObjects, takes scenarios such as this into consideration. +> With additively loaded scenes, you can run into situations where your object pool manager, instantiated when the scene it's defined within is additively loaded by the server, is leaving its spawned NetworkObject instances within the [currently active scene](https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.GetActiveScene.html). While assuring that newly connected clients being synchronized have loaded all of the scenes first helps to avoid scene dependency issues, this alone does not resolve issue with the NetworkObject spawning order. The integrated scene management, included in Netcode for GameObjects, takes scenarios such as this into consideration. ### The Client Synchronization Process @@ -59,7 +59,7 @@ Below is a diagram of the client connection approval and synchronization process  -Starting with the "Player" in the top right part of the above diagram, the client (Player) runs through the connection and approval process first which occurs within the `NetworkManager`. Once approved, the server-side `NetworkSceneManager` begins the client synchronization process by sending the `SceneEventType.Synchronize` Scene Event message to the approved client. The client then processes through the synchronization message. Once the client is finished processing the synchronize message, it responds to the server with a `SceneEventType.SynchronizeComplete` message. At this point the client is considered "synchronized". If the server determines any `NetworkObject` was despawned during the client-side synchronization message processing period, it will send a list of `NetworkObject` identifiers to the client via the `SceneEventType.ReSynchronize` message and the client will locally despawn the `NetworkObject`s. +Starting with the "Player" in the top right part of the above diagram, the client (Player) runs through the connection and approval process first which occurs within the NetworkManager. Once approved, the server-side `NetworkSceneManager` begins the client synchronization process by sending the `SceneEventType.Synchronize` Scene Event message to the approved client. The client then processes through the synchronization message. Once the client is finished processing the synchronize message, it responds to the server with a `SceneEventType.SynchronizeComplete` message. At this point the client is considered "synchronized". If the server determines any NetworkObject was despawned during the client-side synchronization message processing period, it will send a list of NetworkObject identifiers to the client via the `SceneEventType.ReSynchronize` message and the client will locally despawn the NetworkObjects. > [!NOTE] > When the server receives and processes the `SceneEventType.SynchronizeComplete` message, the client is considered connected (that is, `NetworkManager.IsConnectedClient` is set to `true`) and both the `NetworkManager.OnClientConnected` delegate handler and the scene event notification for `SceneEventType.SynchronizeComplete` are invoked locally. This can be useful to know if your server sends any additional messages to the already connected clients about the newly connected client's status (that is, a player's status needs to transition from joining to joined). @@ -214,8 +214,8 @@ private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) ``` > [!NOTE] -> This code can be applied to a component on your `GameObject` that has a `NetworkManager` component attached to it. Since the `GameObject`, with the `NetworkManager` component attached to it, is migrated into the DDOL (Dont Destroy on Load) scene, it will remain active for the duration of the network game session. -> With that in mind, you can cache your scene events that occurred (for debug or reference purposes) and/or add your own events that other game objects can subscribe to. The general idea is that if you want to receive all notifications from the moment you start `NetworkManager` then you will want to subscribe to `NetworkSceneManager.OnSceneEvent` immediately after starting it. +> This code can be applied to a component on your GameObject that has a NetworkManager component attached to it. Since the GameObject, with the NetworkManager component attached to it, is migrated into the DDOL (Dont Destroy on Load) scene, it will remain active for the duration of the network game session. +> With that in mind, you can cache your scene events that occurred (for debug or reference purposes) and/or add your own events that other game objects can subscribe to. The general idea is that if you want to receive all notifications from the moment you start NetworkManager then you will want to subscribe to `NetworkSceneManager.OnSceneEvent` immediately after starting it. Scene event notifications provide users with all NetworkSceneManager related scene events (and associated data) through a single event handler. The one exception would be scene loading or unloading progress which users can handle with a coroutine (upon receiving a Load or Unload event) and checking the `SceneEvent.AsyncOperation.progress` value over time. @@ -250,7 +250,7 @@ Some examples: > The general idea was to provide several ways to get scene event notifications. You might have a component that needs to know when a client is finished synchronizing on the client side but you don't want that component to receive notifications for loading or unloading related events. Under this scenario you would subscribe to the `NetworkManager.OnSynchronizeComplete` event on the client-side. ### When is it "OK" to Subscribe? -Possibly the more important aspect of scene event notifications is knowing when/where to subscribe. The recommended time to subscribe is immediately upon starting your `NetworkManager` as a client, server, or host. This will avoid problematic scenarios like trying to subscribe to the `SceneEventType.Synchronize` event within an overridden `NetworkBehaviour.OnNetworkSpawn` method of your `NetworkBehaviour` derived child class. The reason that is "problematic" is that the `NetworkObject` has to be spawned before you can subscribe to and receive events of type `SceneEventType.Synchronize` because that will occur before anything is spawned. Additionally, you would only receive notifications of any scenes loaded after the scene that has the `NetworkObject` (or the object that spawns it) is loaded. +Possibly the more important aspect of scene event notifications is knowing when/where to subscribe. The recommended time to subscribe is immediately upon starting your NetworkManager as a client, server, or host. This will avoid problematic scenarios like trying to subscribe to the `SceneEventType.Synchronize` event within an overridden `NetworkBehaviour.OnNetworkSpawn` method of your NetworkBehaviour derived child class. The reason that is "problematic" is that the NetworkObject has to be spawned before you can subscribe to and receive events of type `SceneEventType.Synchronize` because that will occur before anything is spawned. Additionally, you would only receive notifications of any scenes loaded after the scene that has the NetworkObject (or the object that spawns it) is loaded. An example of subscribing to `NetworkSceneManager.OnSynchronize` for a client: ```csharp diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-management-overview.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-management-overview.md index e0c07e59cc..3f48e9818f 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-management-overview.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/scene-management-overview.md @@ -10,7 +10,7 @@ The Netcode for GameObjects scene management solution is enabled by default and > It's recommended that advanced developers new to Netcode for GameObjects become familiar with the integrated scene management solution before creating their own netcode scene management solution. ### [Custom Scene Management](custom-management.md): -If your project has needs that go beyond the scope of the Netcode integrated scene management solution, you can disable scene management in the editor through `NetworkManager`'s properties. +If your project has needs that go beyond the scope of the Netcode integrated scene management solution, you can disable scene management in the editor through NetworkManager's properties. > [!NOTE] > Writing your own scene management can be time consuming and complex. It's recommended that only advanced users already familiar with Netcode for GameObjects take this path. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/timing-considerations.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/timing-considerations.md index 44f1505c90..7ac56937a8 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/timing-considerations.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/timing-considerations.md @@ -42,12 +42,12 @@ Now that we have covered the high-level synchronization process, we can dive a l  -You can see that upon receiving the message, the client appears to be iterating over portions of the data included in the synchronize message. This is primarily the asynchronous scene loading phase of the client synchronization process. This means the more scenes loaded, the more a client will be required to load which means the Client Synchronization Period is directly proportional to the number of scene being loaded and the size of each scene being loaded. Once all scenes are loaded, the client will then locally spawn all `NetworkObject`s. As a final step, the client sends the list of all `NetworkObjectId`s it spawned as part of its `SceneEventType.SynchronizeComplete` scene event message so the server can determine if it needs to resynchronize the client with a list of any `NetworkObjects` that might have despawned during the Client Synchronization Period. +You can see that upon receiving the message, the client appears to be iterating over portions of the data included in the synchronize message. This is primarily the asynchronous scene loading phase of the client synchronization process. This means the more scenes loaded, the more a client will be required to load which means the Client Synchronization Period is directly proportional to the number of scene being loaded and the size of each scene being loaded. Once all scenes are loaded, the client will then locally spawn all NetworkObjects. As a final step, the client sends the list of all `NetworkObjectId`s it spawned as part of its `SceneEventType.SynchronizeComplete` scene event message so the server can determine if it needs to resynchronize the client with a list of any `NetworkObjects` that might have despawned during the Client Synchronization Period. ## Loading Scenes ### LoadSceneMode.Additive -Looking at the timeline diagram below, "Loading an Additive Scene", we can see that it includes a server, two clients, and that the server will always precede all clients when it comes to processing the scene loading event. The big-picture this diagram is conveying is that only when the server has finished loading the scene and spawning any in-scene placed `NetworkObject`s, instantiated by the newly loaded scene, will it send the scene loading event message to the clients. +Looking at the timeline diagram below, "Loading an Additive Scene", we can see that it includes a server, two clients, and that the server will always precede all clients when it comes to processing the scene loading event. The big-picture this diagram is conveying is that only when the server has finished loading the scene and spawning any in-scene placed NetworkObjects, instantiated by the newly loaded scene, will it send the scene loading event message to the clients. Another point of interest in the below diagram is how Client 1 receives the scene loading event, processes it, and then responds with a `SceneEventType.LoadComplete` scene event message before client 2. This delta between client 1 and client 2 represents the varying client-server latencies and further enforces the underlying concept behind the `SceneEventType.LoadEventCompleted` message. Once a server has received all `SceneEventType.LoadComplete` messages from the connected clients, it will then broadcast the `SceneEventType.LoadEventCompleted` message to all connected clients. At this point, we can consider the scene loading event (truly) complete and all connected clients are able to receive and process netcode messages. @@ -57,14 +57,14 @@ Another point of interest in the below diagram is how Client 1 receives the scen > While a client can start sending the server messages (including NetworkVariable changes) upon local `SceneEventType.LoadComplete` event notifications, under more controlled testing environments where the network being used has little to no latency (that is, using loopback with multiple instances running on the same system or using your LAN), this approach won't expose latency related issues. Even though the timing might "work out" under controlled low latency conditions you can still run into edge case scenarios where if a client approaches or exceeds a 500ms RTT latency you can potentially run into issues. > [!NOTE] -> It is recommended that if your project's design requires that one or more `NetworkBehaviour`s immediately send any form of client to server message (that is, changing a `NetworkVariable`, sending an RPC, sending a custom message, etc.) upon a client being locally notified of a `SceneEventType.LoadComplete` then you should test with artificial/simulated network conditions. +> It is recommended that if your project's design requires that one or more NetworkBehaviours immediately send any form of client to server message (that is, changing a `NetworkVariable`, sending an RPC, sending a custom message, etc.) upon a client being locally notified of a `SceneEventType.LoadComplete` then you should test with artificial/simulated network conditions. > [Learn More About Simulating NetworkConditions Here](../../tutorials/testing/testing_with_artificial_conditions.md) ### LoadSceneMode.Single (a.k.a. Scene Switching) Loading a scene in `LoadSceneMode.Single` mode via `NetworkSceneManager` is almost exactly like loading a scene additively. The primary difference between additively loading and single mode loading is that when loading a scene in single mode: - all currently loaded scenes are unloaded -- all `NetworkObject`s that have `DestroyWithScene` set to `true` will be despawned and destroyed. +- all NetworkObjects that have `DestroyWithScene` set to `true` will be despawned and destroyed. How you load scenes is up to your project/design requirements. @@ -73,12 +73,12 @@ How you load scenes is up to your project/design requirements. - Because your single mode loaded scene is automatically loaded, the server and all clients will already have this scene loaded - To prevent clients from loading the bootstrap scene, you should use server-side [scene validation](using-networkscenemanager.md#scene-validation) - All other scenes are loaded additively - - There is no real need to preserve any `NetworkObject` you want to persist when loading a scene additively. + - There is no real need to preserve any NetworkObject you want to persist when loading a scene additively. - You need to keep track of the scenes loaded by `NetworkSceneManager` to be able to unload them. - **Scene Switch Usage Pattern (Single and Additive Loading)** - Typically this usage pattern is desirable when your project's design separates "areas" by a primary scene that may have companion additively loaded scenes. - - Any scenes loaded by `NetworkSceneManager`, before scene switching, will be unloaded and any `NetworkObject` that has the `DestroyWithScene` set to `true` will be destroyed. + - Any scenes loaded by `NetworkSceneManager`, before scene switching, will be unloaded and any NetworkObject that has the `DestroyWithScene` set to `true` will be destroyed. - If `DestroyWithScene` is set to `false` it will be "preserved" (_see the sub-diagram "Load New Scene Timeline" below_)  @@ -87,24 +87,24 @@ How you load scenes is up to your project/design requirements. Both scene loading diagrams (Additive and Single) refer to the below sub-diagram that provides additional details of the scene loading process. > [!NOTE] ->When looking at the below sub-diagram, both single and additive scene loading modes use close to the exact same flow with the exception that additively loaded scenes only flow through the four middle steps that are grouped together by the light red filled region highlighted by red dashed lines. When loading a scene additively, no other scenes are unloaded nor are any NetworkObjects moved into the DDoL temporarily. This setting, by default, is true for dynamically spawned `NetworkObject`s unless otherwise specified when using `NetworkObject.Spawn`, `NetworkObject.SpawnWithOwnership`, or `NetworkObject.SpawnAsPlayerObject`. In-scene placed `NetworkObject`'s, by default, will be destroyed with the scene that instantiated them. At any point, within a `NetworkBehaviour` you can change the `NetworkObject.DestroyWithScene` property. +>When looking at the below sub-diagram, both single and additive scene loading modes use close to the exact same flow with the exception that additively loaded scenes only flow through the four middle steps that are grouped together by the light red filled region highlighted by red dashed lines. When loading a scene additively, no other scenes are unloaded nor are any NetworkObjects moved into the DDoL temporarily. This setting, by default, is true for dynamically spawned NetworkObjects unless otherwise specified when using `NetworkObject.Spawn`, `NetworkObject.SpawnWithOwnership`, or `NetworkObject.SpawnAsPlayerObject`. In-scene placed NetworkObject's, by default, will be destroyed with the scene that instantiated them. At any point, within a NetworkBehaviour you can change the `NetworkObject.DestroyWithScene` property.  **Load New Scene Additively** 1. Starts loading the scene -2. During the scene loading process, in-scene placed `NetworkObject`s are instantiated and their `Awake` and then `Start` methods are invoked (in that order). +2. During the scene loading process, in-scene placed NetworkObjects are instantiated and their `Awake` and then `Start` methods are invoked (in that order). 3. Scene loading completes. -4. All in-scene placed `NetworkObject`s are spawned. +4. All in-scene placed NetworkObjects are spawned. -Step 3 above signifies that the `UnityEngine.SceneManagement.SceneManager` has finished loading the scene. If you subscribe to the `UnityEngine.SceneManagement.SceneManager.sceneLoaded` event, then step #3 would happen on the same frame that your subscribed handler is invoked. **Don't use this event** as a way to determine that the current Load SceneEvent has completed. <br/> <center>_Doing so will result in unexpected results that most commonly are associated with "yet-to-be-spawned" (locally) `NetworkObject`'s and/or their related `NetworkBehaviour` dependencies._</center> When using Netcode Integrated SceneManagement, it's recommended to use the `NetworkSceneManager` scene events to determine when the "netcode scene loading event" has completed locally or for all clients. +Step 3 above signifies that the `UnityEngine.SceneManagement.SceneManager` has finished loading the scene. If you subscribe to the `UnityEngine.SceneManagement.SceneManager.sceneLoaded` event, then step #3 would happen on the same frame that your subscribed handler is invoked. **Don't use this event** as a way to determine that the current Load SceneEvent has completed. <br/> <center>_Doing so will result in unexpected results that most commonly are associated with "yet-to-be-spawned" (locally) NetworkObject's and/or their related NetworkBehaviour dependencies._</center> When using Netcode Integrated SceneManagement, it's recommended to use the `NetworkSceneManager` scene events to determine when the "netcode scene loading event" has completed locally or for all clients. **Load New Scene Single (a.k.a "Scene Switching")** 1. All `NetworkObjects` that have their `DestroyWithScene` property set to `false` are migrated into the DDoL (temporarily). 2. All currently loaded scenes are unloaded. If you loaded any scenes additively, they will be automatically unloaded. 3. _(refer to the 4 steps outlined above)_ -4. After any newly instantiated `NetworkObject`s are spawned, the newly loaded scene is set as the currently active scene and then the `NetworkObject`s that were previously migrated into th DDoL (from step 1) are now migrated into the newly loaded scene. +4. After any newly instantiated NetworkObjects are spawned, the newly loaded scene is set as the currently active scene and then the NetworkObjects that were previously migrated into th DDoL (from step 1) are now migrated into the newly loaded scene. ## Unloading a Scene Primarily, this applies to unloading additively loaded scenes via th `NetworkSceneManager.UnloadScene` method. to unload a scene it must: @@ -121,10 +121,10 @@ If you look at the below diagram, "Unloading an Additive Scene", you will see a Review over the below diagram and take note of the following things: - **Server Side:** - When a server starts the `SceneEventType.Unload` event, Unity will naturally being to destroy all `GameObjects` in the scene being unloaded. - - If a `GameObject` has a `NetworkObject` component attached to it and it's still considered spawned at the time the `GameObject` is destroyed, then the `NetworkObject` will be despawned before the `GameObject` being destroyed. + - If a GameObject has a NetworkObject component attached to it and it's still considered spawned at the time the GameObject is destroyed, then the NetworkObject will be despawned before the GameObject being destroyed. - This will cause a series of server-to-client despawn messages to be sent to all clients. - **Client Side:** - - While a server is unloading a scene, the client can begin to receive a bunch of despawn messages for the `NetworkObject`s being destroyed on the server-side while the scene is being unloaded. - - By the time a client receives the `SceneEventType.Unload` scene event message, it well can have no remaining `NetworkObject`s in the scene being unloaded. This won't impact the client-side scene unloading process, but it's useful to know that this will happen. + - While a server is unloading a scene, the client can begin to receive a bunch of despawn messages for the NetworkObjects being destroyed on the server-side while the scene is being unloaded. + - By the time a client receives the `SceneEventType.Unload` scene event message, it well can have no remaining NetworkObjects in the scene being unloaded. This won't impact the client-side scene unloading process, but it's useful to know that this will happen.  diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/using-networkscenemanager.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/using-networkscenemanager.md index 26737f71ad..5837249132 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/using-networkscenemanager.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/using-networkscenemanager.md @@ -17,19 +17,19 @@ - In-Scene placed NetworkObject synchronization > [!NOTE] -> In-Scene placed `NetworkObject`s can be used in many ways and are treated uniquely from that of dynamically spawned `NetworkObject`s. An in-scene placed `NetworkObject` is a GameObject with a `NetworkObject` and typically at least one `NetworkBehaviour` component attached to a child of or the same `GameObject`. it's recommended to read through all integrated scene management materials (this document, [Scene Events](scene-events.md), and [Timing Considerations](timing-considerations.md)) before learning about more advanced [In-Scene (placed) NetworkObjects](inscene-placed-networkobjects.md) topics. +> In-Scene placed NetworkObjects can be used in many ways and are treated uniquely from that of dynamically spawned NetworkObjects. An in-scene placed NetworkObject is a GameObject with a NetworkObject and typically at least one NetworkBehaviour component attached to a child of or the same GameObject. it's recommended to read through all integrated scene management materials (this document, [Scene Events](scene-events.md), and [Timing Considerations](timing-considerations.md)) before learning about more advanced [In-Scene (placed) NetworkObjects](inscene-placed-networkobjects.md) topics. All of these scene management features (and more) are handled by the [`NetworkSceneManager`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkSceneManager.html). ### Accessing `NetworkSceneManager` -The `NetworkSceneManager` lives within the `NetworkManager` and is instantiated when the `NetworkManager` is started. +The `NetworkSceneManager` lives within the NetworkManager and is instantiated when the NetworkManager is started. `NetworkSceneManager` can be accessed in the following ways: -- From within a `NetworkBehaviour` derived component, you can access it by using `NetworkManager.SceneManager` -- From anything else that does not already have a reference to `NetworkManager`, you can access it using `NetworkManager.Singleton.SceneManager`. +- From within a NetworkBehaviour derived component, you can access it by using `NetworkManager.SceneManager` +- From anything else that does not already have a reference to NetworkManager, you can access it using `NetworkManager.Singleton.SceneManager`. **Here are some genral rules about accessing and using `NetworkSceneManager`:** -- Don't try to access the `NetworkSceneManager` when the `NetworkManager` is shutdown (it won't exist). - - The `NetworkSceneManager` is only instantiated when a `NetworkManager` is started. +- Don't try to access the `NetworkSceneManager` when the NetworkManager is shutdown (it won't exist). + - The `NetworkSceneManager` is only instantiated when a NetworkManager is started. - As a server: - Any scene you want synchronized with currently connected or late joining clients **must** be loaded via the `NetworkSceneManager` - If you use the `UnityEngine.SceneManagement.SceneManager` during a netcode enabled game session, then those scenes won't be synchronized with currently connected clients. @@ -42,11 +42,11 @@ The `NetworkSceneManager` lives within the `NetworkManager` and is instantiated - _Scene validation is explained later in this document._ > [!NOTE] -> Don't try to access the `NetworkSceneManager` when the `NetworkManager` is shutdown. The `NetworkSceneManager` is only instantiated when a `NetworkManager` is started. _This same rule is true for all Netcode systems that reside within the `NetworkManager`_. +> Don't try to access the `NetworkSceneManager` when the NetworkManager is shutdown. The `NetworkSceneManager` is only instantiated when a NetworkManager is started. _This same rule is true for all Netcode systems that reside within the NetworkManager_. ### Loading a Scene In order to load a scene, there are four requirements: -1. The `NetworkManager` instance loading the scene must be a host or server. +1. The NetworkManager instance loading the scene must be a host or server. 2. The `NetworkSceneManager.Load` method is used to load the scene. 3. The scene being loaded must be registered with your project's [build settings scenes in build list](https://docs.unity3d.com/Manual/BuildSettings.html) 4. A Scene Event can't already be in progress. @@ -87,7 +87,7 @@ public class ProjectSceneManager : NetworkBehaviour } } ``` -In the above code snippet, we have a `NetworkBehaviour` derived class, `ProjectSceneManager`, that has a public `SceneAsset` property (editor only property) that specifies the scene to load. In the `OnNetworkSpawn` method, we make sure that only the server loads the scene and we compare the `SceneEventProgressStatus` returned by the `NetworkSceneManager.Load` method to the `SceneEventProgressStatus.Started` status. If we received any other status, then typically the name of the status provides you with a reasonable clue as to what the issue might be. +In the above code snippet, we have a NetworkBehaviour derived class, `ProjectSceneManager`, that has a public `SceneAsset` property (editor only property) that specifies the scene to load. In the `OnNetworkSpawn` method, we make sure that only the server loads the scene and we compare the `SceneEventProgressStatus` returned by the `NetworkSceneManager.Load` method to the `SceneEventProgressStatus.Started` status. If we received any other status, then typically the name of the status provides you with a reasonable clue as to what the issue might be. ### Scene Events and Scene Event Progress The term "Scene Event" refers to all subsequent scene events that transpire over time after a server has started a `SceneEventType.Load` ("load") or `SceneEventType.Unload` ("unload") Scene Event. For example, a server might load a scene additively while clients are connected. The following high-level overview provides you with a glimpse into the events that transpire during a load Scene Event(_it assumes both the server and clients have registered for scene event notifications_): @@ -95,7 +95,7 @@ The term "Scene Event" refers to all subsequent scene events that transpire over - The server begins asynchronously loading the scene additively - The server will receive a local notification that the scene load event has started - Once the scene is loaded, the server spawns any in-scene placed `NetworkObjects` locally - - After spawning, the server will send the `SceneEventType.Load` message to all connected clients along with information about the newly instantiated and spawned `NetworkObject`s. + - After spawning, the server will send the `SceneEventType.Load` message to all connected clients along with information about the newly instantiated and spawned NetworkObjects. - The server will receive a local `SceneEventType.LoadComplete` event - This only means the server is done loading and spawning `NetworkObjects` instantiated by the scene being loaded. - The clients receive the scene event message for the `SceneEventType.Load` event and begin processing it. @@ -134,7 +134,7 @@ Typically, this is used on the server side to receive notifications for every sc Typically, this is used with clients or components that might only need to be notified of a specific scene event type. There are 9 scene event types and each one has a corresponding "single event type" callback handler in `NetworkSceneManager`. **As an example:** -You might want to register for the `SceneEventType.LoadEventCompleted` scene event type to know, from a client perspective, that the server and all other clients have finished loading a scene. This notification lets you know when you can start performing other netcode related actions on the newly loaded and spawned `NetworkObject`s. +You might want to register for the `SceneEventType.LoadEventCompleted` scene event type to know, from a client perspective, that the server and all other clients have finished loading a scene. This notification lets you know when you can start performing other netcode related actions on the newly loaded and spawned NetworkObjects. #### Scene Event Progress Status As we discussed in the earlier code example, it's important to check the status returned by `NetworkSceneManager.Load` to make sure your scene loading event has started. The following is a list of all [SceneEventProgressStatus](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.SceneEventProgressStatus.html) `enum` values with some additional helpful information: @@ -155,7 +155,7 @@ As we discussed in the earlier code example, it's important to check the status ### Unloading a Scene Now that we understand the loading process, scene events, and can load a scene additively, the next step is understanding the integrated scene management scene unloading process. to unload a scene, here are the requirements: -1. The `NetworkManager` instance unloading the scene should have already been started in host or server mode and already loaded least one scene additively.<br/> +1. The NetworkManager instance unloading the scene should have already been started in host or server mode and already loaded least one scene additively.<br/> 1.1 Only additively loaded scenes can be unloaded. 1.2 You can't unload the currently active scene (see info dialog below) 2. The `NetworkSceneManager.Unload` method is used to unload the scene @@ -347,7 +347,7 @@ You might find yourself in a scenario where you just need to dynamically generat - For this example we would return false any time `VerifySceneBeforeLoading` was invoked with the scene name "WorldCollisionGeometry". > [!NOTE] - > This only works under the scenario where the dynamically generated scene is a server-host side only scene unless you write server-side code that sends enough information to a newly connected client to replicate the dynamically generated scene via RPC, a custom message, a `NetworkVariable`, or an in-scene placed NetworkObject that is in a scene, other than the dynamically generated one, and has a `NetworkBehaviour` component with an overridden `OnSynchronize` method that includes additional serialization information about how to construct the dynamically generated scene. The limitation on this approach is that the scene should not contain already spawned `NetworkObject`s as those won't get automatically synchronized when a client first connects. + > This only works under the scenario where the dynamically generated scene is a server-host side only scene unless you write server-side code that sends enough information to a newly connected client to replicate the dynamically generated scene via RPC, a custom message, a `NetworkVariable`, or an in-scene placed NetworkObject that is in a scene, other than the dynamically generated one, and has a NetworkBehaviour component with an overridden `OnSynchronize` method that includes additional serialization information about how to construct the dynamically generated scene. The limitation on this approach is that the scene should not contain already spawned NetworkObjects as those won't get automatically synchronized when a client first connects. ### Active Scene Synchronization Setting `NetworkSceneManager.ActiveSceneSynchronizationEnabled` to true on the host or server instance will automatically synchronize both connected and late joining clients with changes in the active scene by the server or host. When enabled, the server or host instance subscribes to the `SceneManager.activeSceneChanged` event and generates `SceneEventType.ActiveSceneChanged` messages when the active scene is changed. Disabling this property just means the server or host instance unsubscribes from the event and will no longer send `SceneEventType.ActiveSceneChanged` messages to keep client's synchronized with the server or host's currently active scene. @@ -358,7 +358,7 @@ See Also: [Client Synchronization Mode](client-synchronization-mode.md) ### What Next? -We have covered how to access the `NetworkSceneManager`, how to load and unload a scene, provided a basic overview on scene events and notifications, and even briefly discussed in-scene placed `NetworkObject`s. You now have the fundamental building-blocks one needs to learn more advanced integrated scene management topics. +We have covered how to access the `NetworkSceneManager`, how to load and unload a scene, provided a basic overview on scene events and notifications, and even briefly discussed in-scene placed NetworkObjects. You now have the fundamental building-blocks one needs to learn more advanced integrated scene management topics. _We recommend proceeding to the next integrated scene management topic, "Client Synchronization Mode", in the link below._ <!-- Explore the [Netcode Scene Management Golden Path](link) for step-by-step examples of additive scene loading and management. --> diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md deleted file mode 100644 index d4cbab5c57..0000000000 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablebehaviour.md +++ /dev/null @@ -1,113 +0,0 @@ -# AttachableBehaviour - - - -The basic functionality of the `AttachableBehaviour` component provides: -- The ability to assign (make aware) `ComponetController` components from any part of the parent-child hierarchy. - - Each `ComponentControllerEntry` provides the ability to select when the `ComponentController` should be triggered (via the **Auto Trigger** property) and whether its enabled state should be enabled or disabled upon attaching (via the **Enable On Attach** property). The default setting is to be disabled upon the `AttachableBehaviour` attaching to an `AttachableNode` and enabled upon detaching. When the **Enable On Attach** property is enabled, the `ComponentController` will be set to enabled upon the `AttachableBehaviour` attaching to an `AttachableNode` and disabled upon detaching. -- The ability to control when an `AttachableBehaviour` component will automatically detach from an `AttachableNode` via the **Auto Detach** property. - - The **Auto Detach** property can have any combination of the below flags or none (no flags): - - **On Ownership Changed:** Upon ownership changing, the `AttachableBehaviour` will detach from any `AttachableNode` it is attached to. - - **On Despawn:** Upon the `AttachableBehaviour` being despawned, it will detach from any `AttachableNode` it is attached to. - - **On Attach Node Destroy**: Just prior to the `AttachableNode` being destroyed, any attached `AttachableBehaviour` with this flag will automatically detach from the `AttachableNode`. - -_Any of the `AttachableBehaviour.AutoDetach` settings will be invoked on all instances without the need for the owner to synchronize the end result(i.e. detaching) which provides a level of redundancy for edge case scenarios like a player being disconnected abruptly by the host or by timing out or any scenario where a spawned object is being destroyed with the owner or perhaps being redistributed to another client authority in a distributed authority session. Having the ability to select or deselect any of the auto-detach flags coupled with the ability to derive from `AttachableBehaviour` provides additional levels of modularity/customization._ - -## Attaching vs NetworkObject parenting - -Fundamentally, attaching is another way to synchronize parenting while not requiring one to use the traditional `NetworkObject` parenting. Attaching a child `GameObject` nested under a `NeworkObject` (_really the `GameObject` the `NetworkObject` component belongs to_) will only take the child `GameObject` and parent it under the `GameObject` of an `AttachableNode`. The target to parent under must be of a different spawned `NetworkObject` and the `AttachableNode` needs to be on the same or child `GameObject` of the target `NetworkObject`. - -### NetworkObject parenting - -The traditional approach has been to spawn two network prefab instances:<br /> - - -Then parent one instance under the other:<br /> - - -This is simple enough for many scenarios, but can become cumbersome under more specific scenarios where a user might want to have a "world" version of the item and a "picked up" version of the item. - -### Attaching - -With attaching, a user would create nested `GameObject` children that represent the item when it is picked up and when it is dropped/placed somewhere in the scene (i.e. world).<br /> - - -- The WorldItemRoot is where the `NetworkObject` component is placed. -- The NestedChild-World contains the components needed for the item when it is placed in the world. -- The NestedChild-PickedUp contains the components needed for the item when it is picked up by a player. - -By placing an `AttachableBehaviour` component on the NestedChild-PickedUp `GameObject` and an `AttachableNode` component on the TargetNode, a user can then invoke the `AttachableBehaviour.Attach` method while passing in the `AttachableNode` component and the NestedChild-PickedUp `GameObject` will get parented under the TargetNode while also synchronizing this action with all other clients.<br /> - - -:::info -**Example of synchronized RPC driven properties** - -Both the `AttachableBehaviour` and the `ComponentController` provide an example of using synchronized RPC driven properties in place of `NetworkVariable`. Under certain conditions it is better to use RPCs when a specific order of operations is needed as opposed to `NetworkVariable`s which can update out of order (regarding the order in which certain states were updated) depending upon several edge case scenarios. - -Under this condition using reliable RPCs will assure the messages are received in the order they were generated while also reducing the latency time between the change and the non-authority instances being notified of the change. Synchronized RPC driven properties only require overriding the `NetworkBehaviour.OnSynchronize` method and serializing any properties that need to be synchronized with late joining players or handling network object visibility related scenarios. -::: - -## Usage walk through - -### Introduction - -For example purposes, we will walk through a common scenario where you might want to have a world item that had unique visual and scripted components active while while placed in the world but then can switch to a different set of visual and scripted components when picked up by a player's avatar. Additionally, you might want to be able to easily "attach" only the portion of the item, that is active when picked up, to one of the player's avatar's child nodes. Below is a high-level diagram overview of what both the player and world item network prefabs could look like:<br /> - - - -#### Player - -The player prefab in the above diagram is not complete, includes the components of interest, and some additional children and components for example purposes. A complete diagram would most definitely have additional components and children. The `AttachableNode` components provide a "target attach point" that any other spawned network prefab with an `AttachableBehaviour` could attach itself to. - -#### World Item - -This diagram has a bit more detail to it and introduces one possible usage of a `ComponentController` and `AttachableBehaviour`. The `ComponentController` will be used to control the enabling and disabling of components and synchronizing this with non-authority instances. The `AttachableBehaviour` resides on the child `AttachedView`'s `GameObject` and will be the catalyst for attaching to a player. - -### World vs attached view modes - - - -In the diagram above, we see arrows pointing from the `ComponentController` to the non-netcode standard Unity components such as a `MeshRenderer`, `Collider`, or any other component that should only be enabled when either in "World View" or "Attached View" modes. We can also see that the `AttachableBehaviour` points to the `ComponentController` with a diagram to the right that shows the `AttachableBehaviour` notifies the `ComponentController` that, in turn, enables or disables certain components. - -#### World Item Component Controller -Below is a screenshot of what the `ComponentController` would look like in the inspector view:<br /> - - - -Looking at the `ComponentController`'s **Components** property, we can see two of the component entries have references to the `WorldItemView`'s `BoxCollider` and `MeshRenderer` that are both configured to be enabled when the `ComponentController`'s state is `true`. We can also see that the `CarryView`'s `MeshRenderer` is added and configured to be the inverse of the current `ComponentController`'s state. Since the `ComponentController`'s **Start Enabled** property is enabled we can logically deduce the **WorldItem** network prefab will start with the `WorldItemView` being active when spawned. Taking a look at the **CarryObject** child's properties: - - - -We can see the `AttachableBehaviour`'s **Component Controllers** list contains `ComponentControllerEntry` (WorldItem Component Controller) that references to the `WorldItem`'s `ComponentController`. We can also see that the `ComponentControllerEntry` is configured to trigger on everything (_OnAttach and OnDetach_) and will set the `ComponentController`'s state to disabled _(false)_. This means when the `AttachableBehaviour` is attached the `ComponentController` will be in the disabled state along with the `WorldItemView` components while the `CarryView`'s `MeshRenderer` will be enabled. - -**Summarized Overview:** -- `AttachableBehaviour` sets the `ComponentController` state (true/enabled or false/disabled). -- `ComponentController` states: - - Enabled (true) - - World Item View (enabled/true) - - Carry View (disabled/false) - - Disabled (false) - - World Item View (disabled/false) - - Carry View (enabled/true) - -### Attaching - - - -The above diagram represents what the **Player** and **World Item** spawned objects (_including cloned/non-authority instances_) would look like once the **Attached View** object has been parented under the avatar's **Right Attach** object. The green area and arrow represent the still existing relationship that the **Attached View** has with the **World Item**'s `NetworkObject`. - -:::info -**AttachableBehaviour & NetworkObject Relationship** - -Upon a `NetworkObject` component being spawned, all associated `NetworkBehaviour` based component instances, that are directly attached to the `NetworkObject`'s `GameObject` or are on any child `GameObject`, will be registered with the `NetworkObject` instance. This remains true even when a child `GameObject` containing one or more `NetworkBehaviour` based component instances of a spawned `NetworkObject` is parented, during runtime, under another `GameObject` that is associated with a different spawned `NetworkObject`. Of course, there are additional considerations like: -- What happens when one or both of the NetworkObjects is de-spawned? -- How do you assure the child attachable will return back to its default parent? -- and several other edge case scenarios... - -`AttachableBehaviour` leverages from this "spawn lifetime" relationship to provide another type of "parenting" (attaching) while also taking into consideration these types of edge case scenarios. -::: - -## Additional resources - -- [AttachableNode](attachablenode.md) -- [ComponentController](componentcontroller.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablenode.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablenode.md deleted file mode 100644 index 5e8b68162f..0000000000 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/attachablenode.md +++ /dev/null @@ -1,14 +0,0 @@ -# AttachableNode - - - -This component provides a valid attach point for `AttachableBehaviour` components. It includes the `AttachableNode.DetachOnDespawn` field that, when enabled (the default), it will automatically detach any attached `AttachableBehaviour` instances when the associated `NetworkObject` of the `AttachableNode` is de-spawned. - -## AttachableBehaviour Usage - -[A usage example can be found here.](attachablebehaviour.md#usage-walk-through) - -## Additional resources - -- [AttachableBehaviour](attachablebehaviour.md) -- [ComponentController](componentcontroller.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md deleted file mode 100644 index 823dd15817..0000000000 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/componentcontroller.md +++ /dev/null @@ -1,87 +0,0 @@ -# ComponentController - -A `ComponentController` provides you with the ability to enable or disable one or more components by the authority instance and have those changes synchronized with non-authority/remote instances. It uses a [synchronized RPC driven field approach](../foundational/networkbehaviour-synchronize.md#synchronized-rpc-driven-fields) to synchronize its enabled state of the components it is controlling to assure optimal performance and that the order of operations of changes is relative to other `ComponentController` and/or other `AttachableBehaviour` component instances. - -The `ComponentController` can be: -- Used with `AttachableBehaviour` or independently for another purpose. -- Configured to directly or inversely follow the `ComponentController`'s current state. -- Configured to have an enable and/or disable delay. - - _When invoked internally by `AttachableBehaviour`, delays are ignored when an `AttachableNode` is being destroyed and the changes are immediate._ - -## Configuring - - - -A `ComponentController` can have one or more `ComponentEntry` entries in its **Components** list. Each `ComponentEntry` has some additional fields that you can adjust based on your desired result: -- **Invert Enabled:** When enabled, this will make the associated component inversely follow the `ComponentControllers` global enabled state. This is useful if you want a set of components to be enabled when the `ComponentController` component's global enable state is set to `false` and for that same set of components to be disabled when the `ComponentController` component's global enable state is set to `true`. -- **Enable Delay:** When greater than 0 (the default), the component will delay transitioning from a disabled state to an enabled state by the amount of time (in seconds) specified. -- **Disable Delay:** When greater than 0 (the default), the component will delay transitioning from an enabled state to a disabled state by the amount of time (in seconds) specified. -- **Component:** The component to control and synchronize its enabled state. - -Both delay values (Enable & Disable) has many uses, but an example would be to prevent a `MeshRenderer` from being enabled prior to other specific events like avoiding it from rendering for a few frames while the attachable is positioned. - -## Examples - -### Independent Usage - -While `ComponentController` can be used with an `AttachableBehaviour` without writing any script, you might find that it can be used for many other purposes. Below is a pseudo example where a `ComponentController` would have its synchronized enabled state updated when the `DaisyChainedController` is either enabled or disabled. - -```csharp -/// <summary> -/// Use as a component in the ComponentController that will -/// trigger the Controller (ComponentController). -/// This pattern can repeat/be daisy chained. -/// </summary> -public class DaisyChainedController : MonoBehaviour -{ - public ComponentController Controller; - - private void OnEnable() - { - if (!Controller || !Controller.HasAuthority) - { - return; - } - Controller.SetEnabled(true); - } - - private void OnDisable() - { - if (!Controller || !Controller.HasAuthority) - { - return; - } - Controller.SetEnabled(false); - } -} -``` - -The above component could be arranged to create a chained sequence of components when the root `DaisyChainedController` component is enabled or disabled. Such a sequence could look like: - -- DaisyChainedController-A - - Controller - - Points to DaisyChainedController-B -- DaisyChainedController-B - - Controller - - Points to DaisyChainedController-C -- DaisyChainedController-C - - Controller - -When DaisyChainedController-A is enabled, then a sequence of events would occur where DaisyChainedController-B and DaisyChainedController-C would be enabled. The same sequence of events would occur when DaisyChainedController-A was then disabled. - -### AttachableBehaviour Usage - -The `AttachableBehaviour` can be assigned one or more component controllers that will be invoked, depending upon configuration, when the `AttachableBehaviour` is attached and detached from an `AttachableNode`. You can find the [usage example with an `AttachableBehaviour` here.](attachablebehaviour.md#usage-walk-through) - -:::info -**Example of synchronized RPC driven properties** - -Both the `AttachableBehaviour` and the `ComponentController` provide an example of using synchronized RPC driven properties in place of `NetworkVariable`. Under certain conditions it is better to use RPCs when a specific order of operations is needed as opposed to `NetworkVariable`s which can update out of order (regarding the order in which certain states were updated) depending upon several edge case scenarios. - -Under this condition using reliable RPCs will assure the messages are received in the order they were generated while also reducing the latency time between the change and the non-authority instances being notified of the change. Synchronized RPC driven properties only require overriding the `NetworkBehaviour.OnSynchronize` method and serializing any properties that need to be synchronized with late joining players or handling network object visibility related scenarios. -::: - -## Additional resources - -- [AttachableBehaviour](attachablebehaviour.md) -- [AttachableNode](attachablenode.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md b/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md deleted file mode 100644 index f8a69be0d3..0000000000 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/helpercomponents.md +++ /dev/null @@ -1,12 +0,0 @@ -# Helper Components - -Understand the helper components available to use in your Netcode for GameObjects project. - - **Topic** | **Description** | -| :------------------------------ | :------------------------------- | -| **[AttachableBehaviour](attachablebehaviour.md)**| Provides an alternative to `NetworkObject` parenting. This section includes a usage example with `AttachableBehaviour`, `AttachableNode`, and `ComponentController`. | -| **[AttachableNode](attachablenode.md)**| Target parent for an `AttachableBehaviour`. | -| **[ComponentController](componentcontroller.md)**| Provides the synchronization of and control over enabling or disabling objects. | -| **[NetworkAnimator](networkanimator.md)**| The `NetworkAnimator` component provides you with a fundamental example of how to synchronize animations during a network session. Animation states are synchronized with players joining an existing network session and any client already connected before the animation state changing. | -| **[NetworkTransform](networktransform.md)**| [NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](../foundational/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. | -| **[Physics](../../advanced-topics/physics.md)**| Netcode for GameObjects has a built in approach which allows for server-authoritative physics where the physics simulation only runs on the server. | diff --git a/com.unity.netcode.gameobjects/Documentation~/components/core/corecomponents.md b/com.unity.netcode.gameobjects/Documentation~/components/core/corecomponents.md new file mode 100644 index 0000000000..33947cdeeb --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/corecomponents.md @@ -0,0 +1,36 @@ +# Core components + +Learn about the three core components of Netcode for GameObjects: NetworkObject, NetworkBehaviour, and NetworkManager. + +* [NetworkObject](#networkobject): This component, added to a GameObject, declares a prefab or in-scene placed object as a networked object that can have states synchronized between clients and/or a server. A NetworkObject component allows you to identify each uniquely spawned instance (dynamically or in-scene placed). _You cannot derive from the NetworkObject component class_. +* [NetworkBehaviour](#networkbehaviour): This is the fundamental networked scripting component that allows you to synchronize state and write netcode script(s). _You derive from this class to create your own netcode component scripts_. +* [NetworkManager](#networkmanager): This component is the overall network session configuration and session management component. A NetworkManager component is necessary to start or join a network session. + + +## NetworkObject + +| **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[NetworkObject](networkobject.md)** | A NetworkObject is a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) with a NetworkObject component and at least one [NetworkBehaviour](networkbehaviour.md) component, which enables the GameObject to respond to and interact with netcode. | +| **[NetworkObject parenting](../../advanced-topics/networkobject-parenting.md)** | Understand how NetworkObjects are parented in Netcode for GameObjects. | + + +## NetworkBehaviour + + +| **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[NetworkBehaviour](networkbehaviour.md)** | [NetworkBehaviour](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](networkobject.md) component and at least one NetworkBehaviour component. | +| **[Synchronizing](networkbehaviour-synchronize.md)** | Understand a NetworkBehaviour component's order of operations when it comes to spawning, de-spawning, and adding custom synchronization data. | + + +## NetworkManager + +| **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[NetworkManager](networkmanager.md)**| The NetworkManager component is a required Netcode for GameObjects component that has all of your project's netcode-related settings. Think of it as the central netcode hub for your netcode-enabled project. | +| **[Player prefabs and spawning players](playerobjects.md)**| Learn about spawning player objects and creating player prefabs.| + +## Additional resources + +* [GameObjects](https://docs.unity3d.com/6000.1/Documentation/Manual/working-with-gameobjects.html) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md b/com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour-synchronize.md similarity index 72% rename from com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md rename to com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour-synchronize.md index 21c0958ea2..bef5662a53 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour-synchronize.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour-synchronize.md @@ -1,20 +1,20 @@ # NetworkBehaviour synchronization -[`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](networkobject.md) component and at least one `NetworkBehaviour` component. +[NetworkBehaviour](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](networkobject.md) component and at least one NetworkBehaviour component. -You can use `NetworkBehaviour`s to synchronize settings before, during, and after spawning NetworkObjects. +You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. -For more information about spawning and despawning `NetworkBehaviour`s, refer to the [NetworkBehaviour spawning and despawning page](networkbehaviour.md). +For more information about spawning and despawning NetworkBehaviours, refer to the [NetworkBehaviour spawning and despawning page](networkbehaviour.md). ## Pre-spawn, spawn, post-spawn and synchronization -The NetworkObject spawn process can become complicated when there are multiple `NetworkBehaviour` components attached to the same GameObject. Additionally, there can be times where you want to be able to handle pre- and post-spawn oriented tasks. +The NetworkObject spawn process can become complicated when there are multiple NetworkBehaviour components attached to the same GameObject. Additionally, there can be times where you want to be able to handle pre- and post-spawn oriented tasks. - Pre-spawn example: Instantiating a `NetworkVariable` with owner write permissions and assigning a value to that `NetworkVariable` on the server or host side. -- Spawn example: Applying a local value or setting that may be used during post spawn by another local `NetworkBehaviour` component. +- Spawn example: Applying a local value or setting that may be used during post spawn by another local NetworkBehaviour component. - Post-spawn example: Accessing a `NetworkVariable` or other property that is set during the spawn process. -Below are the three virtual methods you can override within a `NetworkBehaviour`-derived class: +Below are the three virtual methods you can override within a NetworkBehaviour-derived class: Method | Scope | Use case | Context ---------------------------- | ------------------------ | ------------------------------------------------------ | ------------- @@ -26,7 +26,7 @@ OnInSceneObjectsSpawned | In-scene NetworkObjects | New client finished sy In addition to the methods above, there are two special case convenience methods: -- `OnNetworkSessionSynchronized`: When scene management is enabled and a new client joins a session, the client starts synchronizing with the network session. During this period of time the client might need to load additional scenes as well as instantiate and spawn NetworkObjects. When a client has finished loading all scenes and all NetworkObjects are spawned, this method gets invoked on all `NetworkBehaviour`s associated with any spawned NetworkObjects. This can be useful if you want to write scripts that might require access to other spawned NetworkObjects and/or their `NetworkBehaviour` components. When this method is invoked, you are assured everything is spawned and ready to be accessed and/or to have messages sent from them. Remember that this is invoked on clients and is not invoked on a server or host. +- `OnNetworkSessionSynchronized`: When scene management is enabled and a new client joins a session, the client starts synchronizing with the network session. During this period of time the client might need to load additional scenes as well as instantiate and spawn NetworkObjects. When a client has finished loading all scenes and all NetworkObjects are spawned, this method gets invoked on all NetworkBehaviours associated with any spawned NetworkObjects. This can be useful if you want to write scripts that might require access to other spawned NetworkObjects and/or their NetworkBehaviour components. When this method is invoked, you are assured everything is spawned and ready to be accessed and/or to have messages sent from them. Remember that this is invoked on clients and is not invoked on a server or host. - `OnInSceneObjectsSpawned`: Sometimes you might want to have the same kind of assurance that any in-scene placed NetworkObjects have been spawned prior to a specific set of scripts being invoked. This method is invoked on in-scene placed NetworkObjects when: - A server or host first starts up after all in-scene placed NetworkObjects in the currently loaded scene(s) have been spawned. - A client finishes synchronizing. @@ -34,7 +34,7 @@ In addition to the methods above, there are two special case convenience methods ### Pre-spawn synchronization with `OnSynchronize` -There can be scenarios where you need to include additional configuration data or use a `NetworkBehaviour` to configure some non-netcode related component (or the like) before a `NetworkObject` is spawned. This can be particularly critical if you want specific settings applied before `NetworkBehaviour.OnNetworkSpawn` is invoked. When a client is synchronizing with an existing network session, this can become problematic as messaging requires a client to be fully synchronized before you know "it is safe" to send the message, and even if you send a message there is the latency involved in the whole process that might not be convenient and can require additional specialized code to account for this. +There can be scenarios where you need to include additional configuration data or use a NetworkBehaviour to configure some non-netcode related component (or the like) before a NetworkObject is spawned. This can be particularly critical if you want specific settings applied before `NetworkBehaviour.OnNetworkSpawn` is invoked. When a client is synchronizing with an existing network session, this can become problematic as messaging requires a client to be fully synchronized before you know "it is safe" to send the message, and even if you send a message there is the latency involved in the whole process that might not be convenient and can require additional specialized code to account for this. `NetworkBehaviour.OnSynchronize` allows you to write and read custom serialized data during the NetworkObject serialization process. @@ -53,49 +53,49 @@ The following provides you with an outline of the order of operations that occur Server-side: -- `GameObject` with `NetworkObject` component is instantiated. -- The `NetworkObject` is spawned. - - For each associated `NetworkBehaviour` component, `NetworkBehaviour.OnNetworkSpawn` is invoked. +- GameObject with NetworkObject component is instantiated. +- The NetworkObject is spawned. + - For each associated NetworkBehaviour component, `NetworkBehaviour.OnNetworkSpawn` is invoked. - The `CreateObjectMessage` is generated. - - `NetworkObject` state is serialized. + - NetworkObject state is serialized. - `NetworkVariable` state is serialized. - - `NetworkBehaviour.OnSynchronize` is invoked for each `NetworkBehaviour` component. + - `NetworkBehaviour.OnSynchronize` is invoked for each NetworkBehaviour component. - If this method isn't overridden then nothing is written to the serialization buffer. -- The `CreateObjectMessage` is sent to all clients that are observers of the `NetworkObject`. +- The `CreateObjectMessage` is sent to all clients that are observers of the NetworkObject. Client-side: - The `CreateObjectMessage` is received. - - `GameObject` with `NetworkObject` component is instantiated. + - GameObject with NetworkObject component is instantiated. - `NetworkVariable` state is deserialized and applied. - - `NetworkBehaviour.OnSynchronize` is invoked for each `NetworkBehaviour` component. + - `NetworkBehaviour.OnSynchronize` is invoked for each NetworkBehaviour component. - If this method isn't overridden then nothing is read from the serialization buffer. -- The `NetworkObject` is spawned. - - For each associated `NetworkBehaviour` component, `NetworkBehaviour.OnNetworkSpawn` is invoked. +- The NetworkObject is spawned. + - For each associated NetworkBehaviour component, `NetworkBehaviour.OnNetworkSpawn` is invoked. #### Order of operations during full (late-join) client synchronization Server-side: - The `SceneEventMessage` of type `SceneEventType.Synchronize` is created. - All spawned `NetworkObjects` that are visible to the client, already instantiated, and spawned are serialized. - - `NetworkObject` state is serialized. + - NetworkObject state is serialized. - `NetworkVariable` state is serialized. - - `NetworkBehaviour.OnSynchronize` is invoked for each `NetworkBehaviour` component. + - `NetworkBehaviour.OnSynchronize` is invoked for each NetworkBehaviour component. - If this method isn't overridden then nothing is written to the serialization buffer. - The `SceneEventMessage` is sent to the client. Client-side: - The `SceneEventMessage` of type `SceneEventType.Synchronize` is received. - Scene information is deserialized and scenes are loaded (if not already). - - In-scene placed `NetworkObject`s are instantiated when a scene is loaded. -- All `NetworkObject` oriented synchronization information is deserialized. - - Dynamically spawned `NetworkObject`s are instantiated and state is synchronized. - - For each `NetworkObject` instance: + - In-scene placed NetworkObjects are instantiated when a scene is loaded. +- All NetworkObject oriented synchronization information is deserialized. + - Dynamically spawned NetworkObjects are instantiated and state is synchronized. + - For each NetworkObject instance: - `NetworkVariable` state is deserialized and applied. - `NetworkBehaviour.OnSynchronize` is invoked. - If this method isn't overridden then nothing is read from the serialization buffer. - - The `NetworkObject` is spawned. - - For each associated `NetworkBehaviour` component, `NetworkBehaviour.OnNetworkSpawn` is invoked. + - The NetworkObject is spawned. + - For each associated NetworkBehaviour component, `NetworkBehaviour.OnNetworkSpawn` is invoked. ### Synchronized RPC driven fields @@ -111,12 +111,12 @@ With this in mind, you might need states to be updated in the relative order in - When the authority changes a local field's value, it would then need send an RPC to all non-authority instances. - RPC messages are immediately queued in the outbound send queue which means the order in which an RPC is invoked is the order in which they will be received (_if using the default reliable fragmented sequenced delivery_). - - Any other synchronized RPC driven fields, whether on the same `NetworkBehaviour` or not, would occur in the order they were invoked on the authority instance side. + - Any other synchronized RPC driven fields, whether on the same NetworkBehaviour or not, would occur in the order they were invoked on the authority instance side. - As long as you override the `NetworkBehaviour.OnSynchronize` method and serialize the field, then late joining clients will be synchronized with the authority's most current field value. :::info **Synchronized RPC driven fields vs NetworkVariables** -When a NetworkVariable becomes dirty, the associated `NetworkObject` is add to a queue of `NetworkObject` instances to be scanned for changes to any NetworkVariable declared in a `NetworkBehaviour` associated with the `NetworkObject` instance. Towards the end of the frame, during late update, any `NetworkObject` instances marked as "having one or more dirty NetworkVariables" will be processed and the delta states will be serialized. This can lead to a scenario like the following: +When a NetworkVariable becomes dirty, the associated NetworkObject is add to a queue of NetworkObject instances to be scanned for changes to any NetworkVariable declared in a NetworkBehaviour associated with the NetworkObject instance. Towards the end of the frame, during late update, any NetworkObject instances marked as "having one or more dirty NetworkVariables" will be processed and the delta states will be serialized. This can lead to a scenario like the following: **Authority side** - During Update: @@ -221,20 +221,20 @@ If your serialization code has a bug and throws an exception, then `NetworkBehav #### When writing If user-code throws an exception during `NetworkBehaviour.OnSynchronize`, it catches the exception and if: -- **LogLevel = Normal**: A warning message that includes the name of the `NetworkBehaviour` that threw an exception while writing will be logged and that part of the serialization for the given `NetworkBehaviour` is skipped. +- **LogLevel = Normal**: A warning message that includes the name of the NetworkBehaviour that threw an exception while writing will be logged and that part of the serialization for the given NetworkBehaviour is skipped. - **LogLevel = Developer**: It provides the same warning message as well as it logs an error with the exception message and stack trace. -After generating the log message(s), it rewinds the serialization stream to the point just before it invoked `NetworkBehaviour.OnSynchronize` and will continue serializing. Any data written before the exception occurred will be overwritten or dropped depending upon whether there are more `NetworkBehaviour` components to be serialized. +After generating the log message(s), it rewinds the serialization stream to the point just before it invoked `NetworkBehaviour.OnSynchronize` and will continue serializing. Any data written before the exception occurred will be overwritten or dropped depending upon whether there are more NetworkBehaviour components to be serialized. #### When reading -For exceptions this follows the exact same message logging pattern described above when writing. The distinct difference is that after it logs one or more messages to the console, it skips over only the serialization data written by the server-side when `NetworkBehaviour.OnSynchronize` was invoked and continues the deserialization process for any remaining `NetworkBehaviour` components. +For exceptions this follows the exact same message logging pattern described above when writing. The distinct difference is that after it logs one or more messages to the console, it skips over only the serialization data written by the server-side when `NetworkBehaviour.OnSynchronize` was invoked and continues the deserialization process for any remaining NetworkBehaviour components. -However, there is an additional check to assure that the total expected bytes to read were actually read from the buffer. If the total number of bytes read does not equal the expected number of bytes to be read it will log a warning that includes the name of the `NetworkBehaviour` in question, the total bytes read, the expected bytes to be read, and lets you know this `NetworkBehaviour` is being skipped. +However, there is an additional check to assure that the total expected bytes to read were actually read from the buffer. If the total number of bytes read does not equal the expected number of bytes to be read it will log a warning that includes the name of the NetworkBehaviour in question, the total bytes read, the expected bytes to be read, and lets you know this NetworkBehaviour is being skipped. > [!NOTE] > When using `NetworkBehaviour.OnSynchronize` you should be aware that you are increasing the synchronization payload size per instance. If you have 30 instances that each write 100 bytes of information you will have increased the total full client synchronization size by 3000 bytes. -## Serializing `NetworkBehaviour`s +## Serializing NetworkBehaviours -`NetworkBehaviour`s require the use of specialized [`NetworkBehaviourReference`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviourReference.html) structures to be serialized and used with RPCs and `NetworkVariable`s. +NetworkBehaviours require the use of specialized [`NetworkBehaviourReference`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviourReference.html) structures to be serialized and used with RPCs and `NetworkVariable`s. diff --git a/com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour.md new file mode 100644 index 0000000000..26863bf673 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour.md @@ -0,0 +1,127 @@ +# NetworkBehaviour spawning and despawning + +[NetworkBehaviour](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](networkobject.md) component and at least one NetworkBehaviour component. + +A NetworkBehaviour requires a NetworkObject component on the same relative GameObject or on a parent of the GameObject with the NetworkBehaviour component assigned to it. If you add a NetworkBehaviour to a GameObject that doesn't have a NetworkObject (or any parent), then Netcode for GameObjects automatically adds a NetworkObject component to the GameObject in which the NetworkBehaviour was added. + +NetworkBehaviours can use `NetworkVariable`s and RPCs to synchronize states and send messages over the network. When you call an RPC function, the function isn't called locally. Instead, a message is sent containing your parameters, the `networkId` of the NetworkObject associated with the same GameObject (or child) that the NetworkBehaviour is assigned to, and the index of the NetworkObject-relative NetworkBehaviour (NetworkObjects can have several `NetworkBehaviours`, the index communicates which one). + +For more information about serializing and synchronizing NetworkBehaviours, refer to the [NetworkBehaviour synchronization page](networkbehaviour-synchronize.md). + +> [!NOTE] +> It's important that the NetworkBehaviours on each NetworkObject remain the same for the server and any client connected. When using multiple projects, this becomes especially important so the server doesn't try to call a client RPC on a NetworkBehaviour that might not exist on a specific client type (or set a `NetworkVariable` that doesn't exist, and so on). + +## Spawning + +`OnNetworkSpawn` is invoked on each NetworkBehaviour associated with a NetworkObject when it's spawned. This is where all netcode-related initialization should occur. + +You can still use `Awake` and `Start` to do things like finding components and assigning them to local properties, but if `NetworkBehaviour.IsSpawned` is false then don't expect netcode-distinguishing properties (like `IsClient`, `IsServer`, `IsHost`, for example) to be accurate within `Awake` and `Start` methods. + +For reference purposes, below is a table of when `NetworkBehaviour.OnNetworkSpawn` is invoked relative to the NetworkObject type: + +Dynamically spawned | In-scene placed +------------------- | --------------- +`Awake` | `Awake` +`OnNetworkSpawn` | `Start` +`Start` | `OnNetworkSpawn` + +For more information about NetworkBehaviour methods and when they are invoked, see the [Pre-Spawn and MonoBehaviour Methods](networkbehaviour.md#pre-spawn-and-monobehaviour-methods) section. + +### Disabling NetworkBehaviours when spawning + +If you want to disable a specific NetworkBehaviour but still want it to be included in the NetworkObject spawn process (so you can still enable it at a later time), you can disable the individual NetworkBehaviour instead of the entire GameObject. + +NetworkBehaviour components that are disabled by default and are attached to in-scene placed NetworkObjects behave like NetworkBehaviour components that are attached to dynamically spawned NetworkObjects when it comes to the order of operations for the `NetworkBehaviour.Start` and `NetworkBehaviour.OnNetworkSpawn` methods. Since in-scene placed NetworkObjects are spawned when the scene is loaded, a NetworkBehaviour component (that is disabled by default) will have its `NetworkBehaviour.OnNetworkSpawn` method invoked before the `NetworkBehaviour.Start` method, since `NetworkBehaviour.Start` is invoked when a disabled NetworkBehaviour component is enabled. + +Dynamically spawned | In-scene placed (disabled NetworkBehaviour components) +------------------- | --------------- +`Awake` | `Awake` +`OnNetworkSpawn` | `OnNetworkSpawn` +`Start` | `Start` (invoked when disabled NetworkBehaviour components are enabled) + +> [!NOTE] Parenting, inactive GameObjects, and NetworkBehaviour components +> If you have child GameObjects that are not active in the hierarchy but are nested under an active GameObject with an attached NetworkObject component, then the inactive child GameObjects will not be included when the NetworkObject is spawned. This applies for the duration of the NetworkObject's spawned lifetime. If you want all child NetworkBehaviour components to be included in the spawn process, then make sure their respective GameObjects are active in the hierarchy before spawning the NetworkObject. Alternatively, you can just disable the NetworkBehaviour component(s) individually while leaving their associated GameObject active. +> It's recommended to disable a NetworkBehaviour component rather than the GameObject itself. + + +### Dynamically spawned NetworkObjects + +For dynamically spawned NetworkObjects (instantiating a network prefab during runtime) the `OnNetworkSpawn` method is invoked before the `Start` method is invoked. This means that finding and assigning components to a local property within the `Start` method exclusively will result in that property not being set in a NetworkBehaviour component's `OnNetworkSpawn` method when the NetworkObject is dynamically spawned. To circumvent this issue, you can have a common method that initializes the components and is invoked both during the `Start` method and the `OnNetworkSpawned` method like the code example below: + +```csharp +public class MyNetworkBehaviour : NetworkBehaviour +{ + private MeshRenderer m_MeshRenderer; + private void Start() + { + Initialize(); + } + + private void Initialize() + { + if (m_MeshRenderer == null) + { + m_MeshRenderer = FindObjectOfType<MeshRenderer>(); + } + } + + public override void OnNetworkSpawn() + { + Initialize(); + // Do things with m_MeshRenderer + + base.OnNetworkSpawn(); + } +} +``` + +### In-scene placed NetworkObjects + +For in-scene placed NetworkObjects, the `OnNetworkSpawn` method is invoked after the `Start` method, because the SceneManager scene loading process controls when NetworkObjects are instantiated. The previous code example shows how you can design a NetworkBehaviour that ensures both in-scene placed and dynamically spawned NetworkObjects will have assigned the required properties before attempting to access them. Of course, you can always make the decision to have in-scene placed `NetworkObjects` contain unique components to that of dynamically spawned `NetworkObjects`. It all depends upon what usage pattern works best for your project. + +## Despawning + +`NetworkBehaviour.OnNetworkDespawn` is invoked on each NetworkBehaviour associated with a NetworkObject when it's despawned. This is where all netcode cleanup code should occur, but isn't to be confused with destroying ([see below](#destroying)). When a NetworkBehaviour component is destroyed, it means the associated GameObject, and all attached components, are in the middle of being destroyed. Regarding the order of operations, `NetworkBehaviour.OnNetworkDespawn` is always invoked before `NetworkBehaviour.OnDestroy`. + +### Destroying + +Each NetworkBehaviour has a virtual `OnDestroy` method that can be overridden to handle clean up that needs to occur when you know the NetworkBehaviour is being destroyed. + +If you override the virtual `OnDestroy` method it's important to always invoke the base like such: + +```csharp + public override void OnDestroy() + { + // Clean up your NetworkBehaviour + + // Always invoked the base + base.OnDestroy(); + } +``` + +NetworkBehaviour handles other destroy clean up tasks and requires that you invoke the base `OnDestroy` method to operate properly. + +## Pre-spawn and MonoBehaviour methods + +Since NetworkBehaviour is derived from MonoBehaviour, the `NetworkBehaviour.OnNetworkSpawn` method is treated similar to the `Awake`, `Start`, `FixedUpdate`, `Update`, and `LateUpdate` MonoBehaviour methods. Different methods are invoked depending on whether the GameObject is active in the hierarchy. + +- When active: `Awake`, `Start`, `FixedUpdate`, `Update`, and `LateUpdate` are invoked. +- When not active: `Awake`, `Start`, `FixedUpdate`, `Update`, and `LateUpdate` are not invoked. + +For more information about execution order, refer to [Order of execution for event functions](https://docs.unity3d.com/Manual/ExecutionOrder.html) in the main Unity Manual. + +The unique behavior of `OnNetworkSpawn`, compared to the previously listed methods, is that it's not invoked until the associated GameObject is active in the hierarchy and its associated NetworkObject is spawned. + +Additionally, the `FixedUpdate`, `Update`, and `LateUpdate` methods, if defined and the GameObject is active in the hierarchy, will still be invoked on NetworkBehaviours even when they're not yet spawned. If you want portions or all of your update methods to only execute when the associated NetworkObject component is spawned, you can use the `NetworkBehaviour.IsSpawned` flag to determine the spawned status like the below example: + +```csharp +private void Update() +{ + // If the NetworkObject is not yet spawned, exit early. + if (!IsSpawned) + { + return; + } + // Netcode specific logic executed when spawned. +} +``` diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkmanager.md b/com.unity.netcode.gameobjects/Documentation~/components/core/networkmanager.md similarity index 88% rename from com.unity.netcode.gameobjects/Documentation~/components/foundational/networkmanager.md rename to com.unity.netcode.gameobjects/Documentation~/components/core/networkmanager.md index a67560fc21..5b03fccb2d 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkmanager.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/networkmanager.md @@ -1,8 +1,8 @@ # NetworkManager -The `NetworkManager` is a required Netcode for GameObjects component that has all of your project's netcode-related settings. Think of it as the central netcode hub for your netcode-enabled project. +The NetworkManager is a required Netcode for GameObjects component that has all of your project's netcode-related settings. Think of it as the central netcode hub for your netcode-enabled project. -## `NetworkManager` Inspector properties +## NetworkManager Inspector properties - **LogLevel**: Sets the network logging level - **PlayerPrefab**: When a Prefab is assigned, the Prefab will be instantiated as the player object and assigned to the newly connected and authorized client. For more information about player prefabs, refer to [Player NetworkObjects](networkobject.md#player-networkobjects). @@ -19,11 +19,11 @@ The `NetworkManager` is a required Netcode for GameObjects component that has al - **Enable Scene Management**: When checked, Netcode for GameObjects will handle scene management and client synchronization for you. When not checked, you will have to create your own scene management scripts and handle client synchronization. - **Load Scene Time Out**: When Enable Scene Management is checked, this specifies the period of time the `NetworkSceneManager` will wait while a scene is being loaded asynchronously before `NetworkSceneManager` considers the load/unload scene event to have failed/timed out. -## `NetworkManager` sub-systems -`NetworkManager` is also where you can find references to other Netcode related management systems:<br/> +## NetworkManager sub-systems +NetworkManager is also where you can find references to other Netcode related management systems:<br/> > [!NOTE] -> All `NetworkManager` sub-systems are instantiated once the `NetworkManager` is started (that is, `NetworkManager.IsListening == true`). A good general "rule of thumb" is to not attempt to access the below sub-systems before starting the `NetworkManager`, otherwise they won't yet be initialized. +> All NetworkManager sub-systems are instantiated once the NetworkManager is started (that is, `NetworkManager.IsListening == true`). A good general "rule of thumb" is to not attempt to access the below sub-systems before starting the NetworkManager, otherwise they won't yet be initialized. - [NetworkManager.PrefabHandler](../../advanced-topics/object-pooling.md): This provides access to the NetworkPrefabHandler that is used for NetworkObject pools and to have more control overriding network prefabs. - [NetworkManager.SceneManager](../../basics/scenemanagement/using-networkscenemanager.md): When scene management is enabled, this is used to load and unload scenes, register for scene events, and other scene management related actions. @@ -34,7 +34,7 @@ The `NetworkManager` is a required Netcode for GameObjects component that has al ## Starting a server, host, or client -To perform any netcode-related action that involves sending messages, you must first have a server started and listening for connections with at least one client (_a server can send RPCs to itself when running as a host_) that is connected. To accomplish this, you must first start your `NetworkManager` as a server, host, or client. There are three `NetworkManager` methods you can invoke to accomplish this: +To perform any netcode-related action that involves sending messages, you must first have a server started and listening for connections with at least one client (_a server can send RPCs to itself when running as a host_) that is connected. To accomplish this, you must first start your NetworkManager as a server, host, or client. There are three NetworkManager methods you can invoke to accomplish this: ```csharp NetworkManager.Singleton.StartServer(); // Starts the NetworkManager as just a server (that is, no local client). @@ -45,14 +45,14 @@ NetworkManager.Singleton.StartClient(); // Starts the NetworkManager as jus > [!NOTE] > Don't start a NetworkManager within a NetworkBehaviour's Awake method as this can lead to undesirable results depending upon your project's settings! - When starting a Server or joining an already started session as client, the `NetworkManager` can spawn a "Player Object" belonging to the client. For more information about player prefabs, refer to: + When starting a Server or joining an already started session as client, the NetworkManager can spawn a "Player Object" belonging to the client. For more information about player prefabs, refer to: - [NetworkObject Player Prefab Documentation](networkobject.md) - [Connection Approval](../../basics/connection-approval) ## Connecting -When starting a client, the `NetworkManager` uses the IP and the Port provided in your `Transport` component for connecting. While you can set the IP address in the editor, many times you might want to be able to set the IP address and port during runtime. +When starting a client, the NetworkManager uses the IP and the Port provided in your `Transport` component for connecting. While you can set the IP address in the editor, many times you might want to be able to set the IP address and port during runtime. The below examples use [Unity Transport](https://docs.unity3d.com/Packages/com.unity.transport@latest?subfolder=/manual/index.html) to show a few ways you can gain access to the `UnityTransport` component via the `NetworkManager.Singleton` to configure your project's network settings programmatically: @@ -87,10 +87,10 @@ If you are using Unity Relay to handle connections, however, **don't use `SetCon ### Disconnect from a network -When you disconnect from a network you can't use or access any subsystems, for example `NetworkSceneManager`, because they become unavailable when `NetworkManager` stops. For client, host, or server modes, call the `NetworkManager.Shutdown` method to disconnect and shut down at the same time. +When you disconnect from a network you can't use or access any subsystems, for example `NetworkSceneManager`, because they become unavailable when NetworkManager stops. For client, host, or server modes, call the `NetworkManager.Shutdown` method to disconnect and shut down at the same time. > [!NOTE] -> When no network session is active and the `NetworkManager` has been shutdown, you will need to use `UnityEngine.SceneManagement` to load any non-network session related scene. +> When no network session is active and the NetworkManager has been shutdown, you will need to use `UnityEngine.SceneManagement` to load any non-network session related scene. ```csharp public void Disconnect() @@ -120,10 +120,10 @@ At times you might need to disconnect a client for various reasons without shutt - As a read only list of `NetworkClients` via the `NetworkManager.ConnectedClientsList`. - A full list of all connected client identifiers can be accessed via `NetworkManager.ConnectedClientsIds`. - The client identifier is passed as a parameter to all subscribers of the `NetworkManager.OnClientConnected` event. -- The player's `NetworkObject` has the `NetworkObject.OwnerClientId` property. +- The player's NetworkObject has the `NetworkObject.OwnerClientId` property. > [!NOTE] -> One way to get a player's primary `NetworkObject` is via `NetworkClient.PlayerObject`. +> One way to get a player's primary NetworkObject is via `NetworkClient.PlayerObject`. ```csharp @@ -154,7 +154,7 @@ You can also use the `NetworkManager.OnServerStopped` and `NetworkManager.OnClie Below is one example of how you can provide client connect and disconnect notifications to any type of NetworkBehaviour or MonoBehaviour derived component. > [!NOTE] -> The `ConnectionNotificationManager` example below should only be attached to the same GameObject as `NetworkManager` to assure it persists as long as the `NetworkManager.Singleton` instance. +> The `ConnectionNotificationManager` example below should only be attached to the same GameObject as NetworkManager to assure it persists as long as the `NetworkManager.Singleton` instance. ```csharp using System; diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkobject.md b/com.unity.netcode.gameobjects/Documentation~/components/core/networkobject.md similarity index 97% rename from com.unity.netcode.gameobjects/Documentation~/components/foundational/networkobject.md rename to com.unity.netcode.gameobjects/Documentation~/components/core/networkobject.md index 5ea762d5af..a1f96917ef 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkobject.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/networkobject.md @@ -1,11 +1,11 @@ # NetworkObject -A NetworkObject is a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) with a NetworkObject component and at least one [NetworkBehaviour](networkbehaviour.md) component, which enables the GameObject to respond to and interact with netcode. NetworkObjects are session-mode agnostic and used in both [client-server](../terms-concepts/client-server.md) and [distributed authority](../../terms-concepts/distributed-authority.md) contexts. +A NetworkObject is a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) with a NetworkObject component and at least one [NetworkBehaviour](networkbehaviour.md) component, which enables the GameObject to respond to and interact with netcode. NetworkObjects are session-mode agnostic and used in both [client-server](../../terms-concepts/client-server.md) and [distributed authority](../../terms-concepts/distributed-authority.md) contexts. Netcode for GameObjects' high level components, [the RPC system](../../advanced-topics/messaging-system.md), [object spawning](../../basics/object-spawning.md), and [`NetworkVariable`](../../basics/networkvariable.md)s all rely on there being at least two Netcode components added to a GameObject: 1. NetworkObject - 2. [`NetworkBehaviour`](networkbehaviour.md) + 2. [NetworkBehaviour](networkbehaviour.md) NetworkObjects require the use of specialized [`NetworkObjectReference`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObjectReference.html) structures before you can serialize and use them with RPCs and `NetworkVariable`s @@ -70,7 +70,7 @@ To see if the server owns the NetworkObject, you can check the [`NetworkBehaviou Network prefabs are registered to a `NetworkPrefabsList` object (a type of `ScriptableObject`). By default, a default prefabs list containing every network prefab in your project. -However, when you want to limit which prefabs are available (for example, to reduce memory usage), you can disable this behavior in **Project Settings** > **Netcode For GameObjects** > **Project Settings**. You can also manually create a `NetworkPrefabsList` by right-clicking in the assets view and selecting **Create** > **Netcode** > **Network Prefabs List** and adding your prefabs to it. That prefab list can then be assigned to a `NetworkManager` to allow that `NetworkManager` to create those prefabs. +However, when you want to limit which prefabs are available (for example, to reduce memory usage), you can disable this behavior in **Project Settings** > **Netcode For GameObjects** > **Project Settings**. You can also manually create a `NetworkPrefabsList` by right-clicking in the assets view and selecting **Create** > **Netcode** > **Network Prefabs List** and adding your prefabs to it. That prefab list can then be assigned to a NetworkManager to allow that NetworkManager to create those prefabs. > [!NOTE] > You can only have one NetworkObject at the root of a prefab. Don't create prefabs with nested `NetworkObjects`. diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/playerobjects.md b/com.unity.netcode.gameobjects/Documentation~/components/core/playerobjects.md similarity index 96% rename from com.unity.netcode.gameobjects/Documentation~/components/foundational/playerobjects.md rename to com.unity.netcode.gameobjects/Documentation~/components/core/playerobjects.md index 6650aee374..e7ae88353f 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/foundational/playerobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/playerobjects.md @@ -8,7 +8,7 @@ PlayerObjects are instantiated by reference to a player prefab, which defines th If you're using `UnityEngine.InputSystem.PlayerInput` or `UnityEngine.PhysicsModule.CharacterController` components on your player prefab(s), you should disable them by default and only enable them for the local client's PlayerObject. Otherwise, you may get events from the most recently instantiated player prefab instance, even if it isn't the local client instance. -You can disable these components in the **Inspector** view on the prefab itself, or disable them during `Awake` in one of your `NetworkBehaviour` components. Then you can enable the components only on the owner's instance using code like the example below: +You can disable these components in the **Inspector** view on the prefab itself, or disable them during `Awake` in one of your NetworkBehaviour components. Then you can enable the components only on the owner's instance using code like the example below: ```csharp PlayerInput m_PlayerInput; diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/foundationalcomponents.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/foundationalcomponents.md deleted file mode 100644 index 09b8f52274..0000000000 --- a/com.unity.netcode.gameobjects/Documentation~/components/foundational/foundationalcomponents.md +++ /dev/null @@ -1,34 +0,0 @@ -# Foundational Components - -While there are many classes within the Netcode for GameObjects SDK, there are really only three foundational components: - -* **NetworkObject:** This component declares a prefab or in-scene placed object as "networked object" that can have states synchronized between clients and/or a server. A `NetworkObject` component provides the means to identify each uniquely spawned instance (dynamically or in-scene placed). _You cannot derive from the `NetworkObject` component class_. - -* **NetworkBehaviour:** This is the fundamental "netcode" scripting component that provides the ability to synchronize state and write netcode script(s). _You derive from this class to create your own netcode component scripts_. - -* **NetworkManager:** This component is the over-all network session configuration and session management component. The `NetworkManager` component is required in order to start or join a network session. - - -## NetworkObject - -| **Topic** | **Description** | -| :------------------------------ | :------------------------------- | -| **[NetworkObject](networkobject.md)** | A NetworkObject is a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) with a NetworkObject component and at least one [NetworkBehaviour](networkbehaviour.md) component, which enables the GameObject to respond to and interact with netcode. | -| **[NetworkObject parenting](../../advanced-topics/networkobject-parenting.md)** | Understand how NetworkObjects are parented in Netcode for GameObjects. | - - -## NetworkBehaviour - - -| **Topic** | **Description** | -| :------------------------------ | :------------------------------- | -| **[NetworkBehaviour](networkbehaviour.md)** | [`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](networkobject.md) component and at least one `NetworkBehaviour` component. | -| **[Synchronizing](networkbehaviour-synchronize.md)** | Understanding a `NetworkBehaviour` component's order of operations when it comes to spawning, de-spawning, and adding custom synchronization data. | - - -## NetworkManager - -| **Topic** | **Description** | -| :------------------------------ | :------------------------------- | -| **[NetworkManager](networkmanager.md)**| The `NetworkManager` is a required Netcode for GameObjects component that has all of your project's netcode-related settings. Think of it as the central netcode hub for your netcode-enabled project. | -| **[Player prefabs and spawning players](playerobjects.md)**| Learn about spawning player objects and creating player prefabs.| \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour.md deleted file mode 100644 index dde4efc5d7..0000000000 --- a/com.unity.netcode.gameobjects/Documentation~/components/foundational/networkbehaviour.md +++ /dev/null @@ -1,127 +0,0 @@ -# NetworkBehaviour spawning and despawning - -[`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](networkobject.md) component and at least one `NetworkBehaviour` component. - -A `NetworkBehaviour` requires a `NetworkObject` component on the same relative GameObject or on a parent of the GameObject with the `NetworkBehaviour` component assigned to it. If you add a `NetworkBehaviour` to a GameObject that doesn't have a `NetworkObject` (or any parent), then Netcode for GameObjects automatically adds a `NetworkObject` component to the `GameObject` in which the `NetworkBehaviour` was added. - -`NetworkBehaviour`s can use `NetworkVariable`s and RPCs to synchronize states and send messages over the network. When you call an RPC function, the function isn't called locally. Instead, a message is sent containing your parameters, the `networkId` of the NetworkObject associated with the same GameObject (or child) that the `NetworkBehaviour` is assigned to, and the index of the NetworkObject-relative `NetworkBehaviour` (NetworkObjects can have several `NetworkBehaviours`, the index communicates which one). - -For more information about serializing and synchronizing `NetworkBehaviour`s, refer to the [NetworkBehaviour synchronization page](networkbehaviour-synchronize.md). - -> [!NOTE] -> It's important that the `NetworkBehaviour`s on each NetworkObject remain the same for the server and any client connected. When using multiple projects, this becomes especially important so the server doesn't try to call a client RPC on a `NetworkBehaviour` that might not exist on a specific client type (or set a `NetworkVariable` that doesn't exist, and so on). - -## Spawning - -`OnNetworkSpawn` is invoked on each `NetworkBehaviour` associated with a NetworkObject when it's spawned. This is where all netcode-related initialization should occur. - -You can still use `Awake` and `Start` to do things like finding components and assigning them to local properties, but if `NetworkBehaviour.IsSpawned` is false then don't expect netcode-distinguishing properties (like `IsClient`, `IsServer`, `IsHost`, for example) to be accurate within `Awake` and `Start` methods. - -For reference purposes, below is a table of when `NetworkBehaviour.OnNetworkSpawn` is invoked relative to the `NetworkObject` type: - -Dynamically spawned | In-scene placed -------------------- | --------------- -`Awake` | `Awake` -`OnNetworkSpawn` | `Start` -`Start` | `OnNetworkSpawn` - -For more information about `NetworkBehaviour` methods and when they are invoked, see the [Pre-Spawn and MonoBehaviour Methods](networkbehaviour.md#pre-spawn-and-monobehaviour-methods) section. - -### Disabling `NetworkBehaviour`s when spawning - -If you want to disable a specific `NetworkBehaviour` but still want it to be included in the `NetworkObject` spawn process (so you can still enable it at a later time), you can disable the individual `NetworkBehaviour` instead of the entire `GameObject`. - -`NetworkBehaviour` components that are disabled by default and are attached to in-scene placed NetworkObjects behave like `NetworkBehaviour` components that are attached to dynamically spawned NetworkObjects when it comes to the order of operations for the `NetworkBehaviour.Start` and `NetworkBehaviour.OnNetworkSpawn` methods. Since in-scene placed NetworkObjects are spawned when the scene is loaded, a `NetworkBehaviour` component (that is disabled by default) will have its `NetworkBehaviour.OnNetworkSpawn` method invoked before the `NetworkBehaviour.Start` method, since `NetworkBehaviour.Start` is invoked when a disabled `NetworkBehaviour` component is enabled. - -Dynamically spawned | In-scene placed (disabled `NetworkBehaviour` components) -------------------- | --------------- -`Awake` | `Awake` -`OnNetworkSpawn` | `OnNetworkSpawn` -`Start` | `Start` (invoked when disabled `NetworkBehaviour` components are enabled) - -> [!NOTE] Parenting, inactive GameObjects, and `NetworkBehaviour` components -> If you have child GameObjects that are not active in the hierarchy but are nested under an active GameObject with an attached NetworkObject component, then the inactive child GameObjects will not be included when the NetworkObject is spawned. This applies for the duration of the NetworkObject's spawned lifetime. If you want all child `NetworkBehaviour` components to be included in the spawn process, then make sure their respective GameObjects are active in the hierarchy before spawning the `NetworkObject`. Alternatively, you can just disable the NetworkBehaviour component(s) individually while leaving their associated GameObject active. -> It's recommended to disable a `NetworkBehaviour` component rather than the GameObject itself. - - -### Dynamically spawned NetworkObjects - -For dynamically spawned NetworkObjects (instantiating a network prefab during runtime) the `OnNetworkSpawn` method is invoked before the `Start` method is invoked. This means that finding and assigning components to a local property within the `Start` method exclusively will result in that property not being set in a `NetworkBehaviour` component's `OnNetworkSpawn` method when the NetworkObject is dynamically spawned. To circumvent this issue, you can have a common method that initializes the components and is invoked both during the `Start` method and the `OnNetworkSpawned` method like the code example below: - -```csharp -public class MyNetworkBehaviour : NetworkBehaviour -{ - private MeshRenderer m_MeshRenderer; - private void Start() - { - Initialize(); - } - - private void Initialize() - { - if (m_MeshRenderer == null) - { - m_MeshRenderer = FindObjectOfType<MeshRenderer>(); - } - } - - public override void OnNetworkSpawn() - { - Initialize(); - // Do things with m_MeshRenderer - - base.OnNetworkSpawn(); - } -} -``` - -### In-scene placed NetworkObjects - -For in-scene placed NetworkObjects, the `OnNetworkSpawn` method is invoked after the `Start` method, because the SceneManager scene loading process controls when NetworkObjects are instantiated. The previous code example shows how you can design a `NetworkBehaviour` that ensures both in-scene placed and dynamically spawned NetworkObjects will have assigned the required properties before attempting to access them. Of course, you can always make the decision to have in-scene placed `NetworkObjects` contain unique components to that of dynamically spawned `NetworkObjects`. It all depends upon what usage pattern works best for your project. - -## Despawning - -`NetworkBehaviour.OnNetworkDespawn` is invoked on each `NetworkBehaviour` associated with a `NetworkObject` when it's despawned. This is where all netcode cleanup code should occur, but isn't to be confused with destroying ([see below](#destroying)). When a `NetworkBehaviour` component is destroyed, it means the associated GameObject, and all attached components, are in the middle of being destroyed. Regarding the order of operations, `NetworkBehaviour.OnNetworkDespawn` is always invoked before `NetworkBehaviour.OnDestroy`. - -### Destroying - -Each `NetworkBehaviour` has a virtual `OnDestroy` method that can be overridden to handle clean up that needs to occur when you know the `NetworkBehaviour` is being destroyed. - -If you override the virtual `OnDestroy` method it's important to always invoke the base like such: - -```csharp - public override void OnDestroy() - { - // Clean up your NetworkBehaviour - - // Always invoked the base - base.OnDestroy(); - } -``` - -`NetworkBehaviour` handles other destroy clean up tasks and requires that you invoke the base `OnDestroy` method to operate properly. - -## Pre-spawn and `MonoBehaviour` methods - -Since `NetworkBehaviour` is derived from `MonoBehaviour`, the `NetworkBehaviour.OnNetworkSpawn` method is treated similar to the `Awake`, `Start`, `FixedUpdate`, `Update`, and `LateUpdate` `MonoBehaviour` methods. Different methods are invoked depending on whether the GameObject is active in the hierarchy. - -- When active: `Awake`, `Start`, `FixedUpdate`, `Update`, and `LateUpdate` are invoked. -- When not active: `Awake`, `Start`, `FixedUpdate`, `Update`, and `LateUpdate` are not invoked. - -For more information about execution order, refer to [Order of execution for event functions](https://docs.unity3d.com/Manual/ExecutionOrder.html) in the main Unity Manual. - -The unique behavior of `OnNetworkSpawn`, compared to the previously listed methods, is that it's not invoked until the associated GameObject is active in the hierarchy and its associated NetworkObject is spawned. - -Additionally, the `FixedUpdate`, `Update`, and `LateUpdate` methods, if defined and the GameObject is active in the hierarchy, will still be invoked on `NetworkBehaviour`s even when they're not yet spawned. If you want portions or all of your update methods to only execute when the associated `NetworkObject` component is spawned, you can use the `NetworkBehaviour.IsSpawned` flag to determine the spawned status like the below example: - -```csharp -private void Update() -{ - // If the NetworkObject is not yet spawned, exit early. - if (!IsSpawned) - { - return; - } - // Netcode specific logic executed when spawned. -} -``` diff --git a/com.unity.netcode.gameobjects/Documentation~/components/helper/attachablebehaviour.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/attachablebehaviour.md new file mode 100644 index 0000000000..29688da041 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/helper/attachablebehaviour.md @@ -0,0 +1,122 @@ +# AttachableBehaviour + +Use the AttachableBehaviour component to manage [ComponentController](componentcontroller.md) components and to attach a child GameObject to an [AttachableNode](attachablenode.md). The AttachableBehaviour component provides an alternative to NetworkObject parenting, allowing you to attach and detach child objects dynamically during runtime. + + + +The basic functionality of the AttachableBehaviour component provides: + +- The ability to assign ComponentController components from any part of the parent-child hierarchy. + - Each **Component Controller** entry provides the ability to select when the ComponentController should be triggered (via the **Auto Trigger** property) and whether its enabled state should be enabled or disabled upon attaching (via the **Enable On Attach** property). The default setting is to be disabled when the AttachableBehaviour attaches to an AttachableNode and enabled when detaching. When the **Enable On Attach** property is enabled, the ComponentController is enabled when the AttachableBehaviour attaches to an AttachableNode and disabled when detaching. +- The ability to control when an AttachableBehaviour component will automatically detach from an AttachableNode via the **Auto Detach** property. + - The **Auto Detach** property can have any combination of the following flags or none (no flags): + - **On Ownership Changed:** When ownership changes, the AttachableBehaviour will detach from any AttachableNode it's attached to. + - **On Despawn:** When the AttachableBehaviour is despawned, it will detach from any AttachableNode it's attached to. + - **On Attach Node Destroy**: Just before the AttachableNode is destroyed, any attached AttachableBehaviour with this flag will automatically detach from the AttachableNode. + +Any of the `AttachableBehaviour.AutoDetach` settings will be invoked on all instances without the need for the owner to synchronize the end result (by detaching), which provides a level of redundancy for edge case scenarios like a player being disconnected abruptly by the host, timing out, or any scenario where a spawned object is being destroyed with the owner (or perhaps being redistributed to another client authority in a [distributed authority](../../terms-concepts/distributed-authority.md) session). Having the ability to select or deselect any of the auto-detach flags coupled with the ability to derive from AttachableBehaviour provides additional levels of modularity and customization. + +## Attaching and NetworkObject parenting + +Fundamentally, attaching is an alternative method of synchronizing parenting that doesn't involve traditional [NetworkObject parenting](../../advanced-topics/networkobject-parenting.md). Attaching a child GameObject nested under a NetworkObject only takes the child GameObject and parents it under the GameObject of an AttachableNode. The target to parent under must be of a different spawned NetworkObject and the AttachableNode needs to be on the same or child GameObject of the target NetworkObject. + +### NetworkObject parenting + +The traditional approach is to spawn two NetworkPrefabs instances: + + + +Then parent one instance under the other: + + + +This is simple enough for many scenarios, but can become cumbersome in situations where you might want to have a world version of the item and a picked up version of the item. + +### Attaching + +When attaching, you create nested GameObject children that represent the item when it's picked up and when it's placed somewhere in the world, effectively creating a GameObject for each item state. + + + +- The WorldItemRoot is where the NetworkObject component is placed. +- The NestedChild-World contains the components needed for the item when it's placed in the world. +- The NestedChild-PickedUp contains the components needed for the item when it's picked up by a player. + +By placing an AttachableBehaviour component on the NestedChild-PickedUp GameObject and an AttachableNode component on the TargetNode, a user can then invoke the `AttachableBehaviour.Attach` method while passing in the AttachableNode component and the NestedChild-PickedUp GameObject will get parented under the TargetNode while also synchronizing this action with all other clients. + + + +## AttachableBehaviour example + +### Introduction + +This example walks through a common scenario where you want to have a world item that has unique visual and scripted components active while placed in the world, but that switches to a different set of visual and scripted components when picked up by a player's avatar. It also covers attaching only the portion of the item that is active when picked up to one of the player's avatar's child nodes. + +Below is a high-level diagram overview of what both the player and world item NetworkPrefabs could look like: + + + +#### Player + +The player prefab in the above diagram is not complete, and only includes the components relevant to this example plus some additional children and components for example purposes. The AttachableNode components provide an attachment point that any other spawned network prefab with an AttachableBehaviour could attach itself to. + +#### WorldItem + +This diagram has more detail and introduces one possible method of using a ComponentController and AttachableBehaviour. The ComponentController is used to control the enabling and disabling of components and synchronizing this with non-authority instances. The AttachableBehaviour resides on the child AttachedView's GameObject and is the catalyst for attaching to a player. + +### WorldView and AttachedView modes + + + +The diagram above has arrows pointing from the ComponentController to the components that are enabled or disabled depending on the ComponentController's state. These are components that are only relevant when the item is in the world (WorldView mode) or when it's attached to a player (AttachedView mode). + +The AttachableBehaviour also has an arrow that points to the ComponentController, indicating a relationship between the two. The right-hand diagram indicates the flow of events: the AttachableBehaviour notifies the ComponentController that an item has been attached or detached and the ComponentController, in turn, enables or disables certain components. + +#### WorldItem ComponentController + +Below is a screenshot of what the ComponentController would look like in the Inspector view: + + + +The ComponentController's **Components** list has three entries, two of which have references to the WorldItemView's BoxCollider and MeshRenderer that are both configured to be enabled when the ComponentController's state is `true`. There's also the CarryView's MeshRenderer that is added and configured to be the inverse of the current ComponentController's state. + + The ComponentController's **Start Enabled** property is enabled, which means that the WorldItem NetworkPrefab starts with the WorldItemView being active when spawned. + +The CarryObject child's properties are as follows: + + + +The AttachableBehaviour's **Component Controllers** list contains the WorldItem ComponentController that's configured to trigger on everything (`OnAttach` and `OnDetach`) and sets the ComponentController's state to disabled (`false`). This means that when the AttachableBehaviour is attached, the ComponentController is in the disabled state along with the WorldItemView components, while the CarryView's MeshRenderer will be enabled. + +#### Summary + +- AttachableBehaviour sets the ComponentController state (true/enabled or false/disabled). +- ComponentController states: + - Enabled (true) + - World Item View (enabled/true) + - Carry View (disabled/false) + - Disabled (false) + - World Item View (disabled/false) + - Carry View (enabled/true) + +### Attaching + + + +The above diagram represents what the Player and WorldItem spawned objects (including non-authority instances) look like once the AttachedView object has been parented under the avatar's RightAttach object. The green area and arrow represent the still existing relationship that the AttachedView has with the WorldItem's NetworkObject. + +#### AttachableBehaviour and NetworkObject relationship + +When a NetworkObject component is spawned, it registers all NetworkBehaviour-based component instances that are directly attached to the NetworkObject's GameObject or that are on any child GameObject. This remains true even when a child GameObject containing one or more NetworkBehaviour-based component instances of a spawned NetworkObject is parented, during runtime, under another GameObject that is associated with a different spawned NetworkObject. + +There are additional considerations like: + +- What happens when one or both of the NetworkObjects is de-spawned? +- How do you ensure the child attachable will return back to its default parent? + +AttachableBehaviour addresses these issues by leveraging the spawn lifetime relationship to provide another type of parenting (attaching) while also taking into consideration edge case scenarios. + +## Additional resources + +- [AttachableNode](attachablenode.md) +- [ComponentController](componentcontroller.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/helper/attachablenode.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/attachablenode.md new file mode 100644 index 0000000000..0c63fb2c56 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/helper/attachablenode.md @@ -0,0 +1,16 @@ +# AttachableNode + +Use an AttachableNode component to provide an attachment point for an [AttachableBehaviour](attachablebehaviour.md) component. + +AttachableNodes include a **Detach On Despawn** field that, when enabled (the default setting), automatically detaches and destroys any attached AttachableBehaviour instances when the associated NetworkObject of the AttachableNode is despawned. + + + +## AttachableBehaviour example + +Refer to the [AttachableBehaviour example](attachablebehaviour.md#attachablebehaviour-example) for an example of how to use the AttachableNode component with an AttachableBehaviour component. + +## Additional resources + +- [AttachableBehaviour](attachablebehaviour.md) +- [ComponentController](componentcontroller.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/helper/componentcontroller.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/componentcontroller.md new file mode 100644 index 0000000000..26465b9d6e --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/helper/componentcontroller.md @@ -0,0 +1,95 @@ +# ComponentController + +Use a [ComponentController](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.ComponentController.html) component to enable or disable one or more components depending on the authority state of the ComponentController and have those changes synchronized with non-authority instances. + +For example, you can use a ComponentController to enable or disable a MeshRenderer component on the owner of the ComponentController instance, while disabling it for all other clients. This is useful for controlling visibility of objects that should only be visible to the owner, such as a player's avatar or a weapon they are holding. + +The ComponentController can be: + +* Used with an [AttachableBehaviour component](attachablebehaviour.md) or independently for another purpose. +* Configured to directly or inversely follow the ComponentController's current state. +* Configured to have an enable and/or disable delay. + * Note that when invoked internally by AttachableBehaviour, delays are ignored when an [AttachableNode](attachablenode.md) is being destroyed and the changes are immediate. + +ComponentControllers use a [synchronized RPC-driven field approach](#synchronized-rpc-driven-properties) to synchronize the states of the components its controlling. This ensures optimal performance and that the order of operations of changes is relative to other ComponentController or AttachableBehaviour component instances. + +## Configuring a ComponentController + + + +A ComponentController can have one or more entries in its **Components** list. Each entry has some additional fields that you can adjust: + +- **Invert Enabled:** When enabled, this makes the associated component inversely follow the ComponentController's global enabled state. Use this if you want a set of components to be enabled when the ComponentController component's global enable state is set to `false` and for that same set of components to be disabled when the ComponentController component's global enable state is set to `true`. +- **Enable Delay:** When greater than 0 (the default), the associated component will delay transitioning from a disabled state to an enabled state by the amount of time (in seconds) specified. +- **Disable Delay:** When greater than 0 (the default), the associated component will delay transitioning from an enabled state to a disabled state by the amount of time (in seconds) specified. +- **Component:** The component to control and synchronize its enabled state. + +You can use the delay values to, for example, prevent a MeshRenderer from being enabled prior to other events (such as while waiting for the attachable to be positioned). The ComponentController automatically handles the synchronization of these delays across the network, ensuring that all clients see the same behavior. + +## Examples + +### Independent usage + +A ComponentController can be [used with an AttachableBehaviour](#attachablebehaviour-usage) without writing any scripts, but you can also write scripts to use it independently. Below is a pseudo example where a ComponentController has its synchronized state updated when the `DaisyChainedController` is either enabled or disabled. + +```csharp +/// <summary> +/// Use as a component in the ComponentController that will +/// trigger the Controller (ComponentController). +/// This pattern can repeat/be daisy chained. +/// </summary> +public class DaisyChainedController : MonoBehaviour +{ + public ComponentController Controller; + + private void OnEnable() + { + if (!Controller || !Controller.HasAuthority) + { + return; + } + Controller.SetEnabled(true); + } + + private void OnDisable() + { + if (!Controller || !Controller.HasAuthority) + { + return; + } + Controller.SetEnabled(false); + } +} +``` + +The above component could be arranged to create a chained sequence of components when the root `DaisyChainedController` component is enabled or disabled. Such a sequence could look like: + +- DaisyChainedController-A + - Controller + - Points to DaisyChainedController-B +- DaisyChainedController-B + - Controller + - Points to DaisyChainedController-C +- DaisyChainedController-C + - Controller + +When DaisyChainedController-A is enabled, then a sequence of events would occur where DaisyChainedController-B and DaisyChainedController-C would be enabled. The same sequence of events would occur when DaisyChainedController-A was then disabled. + +### AttachableBehaviour usage + +An [AttachableBehaviour component](attachablebehaviour.md) can be assigned one or more ComponentControllers that will be invoked, depending on configuration, when the AttachableBehaviour is attached and detached from an [AttachableNode](attachablenode.md). + +For more information, refer to the [AttachableBehaviour example](attachablebehaviour.md#attachablebehaviour-example). + +## Synchronized RPC-driven properties + +Both AttachableBehaviour and ComponentController provide an example of using synchronized RPC-driven properties instead of [NetworkVariables](../../basics/networkvariable.md). Under certain conditions, it can be preferable to use RPCs when a specific order of operations is needed, because NetworkVariables can update out of order (relative to the order in which certain states were updated) in some edge case scenarios. + +Under such conditions, using reliable RPCs ensures that messages are received in the order they're generated, while also reducing the latency time between the change and non-authority instances being notified of the change. Synchronized RPC-driven properties only require overriding the `NetworkBehaviour.OnSynchronize` method and serializing any properties that need to be synchronized with late joining players or handling network object visibility related scenarios. + +For more information, refer to [NetworkBehaviour synchronization page](../core/networkbehaviour-synchronize.md#synchronized-rpc-driven-fields). + +## Additional resources + +- [AttachableBehaviour](attachablebehaviour.md) +- [AttachableNode](attachablenode.md) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/components/helper/helpercomponents.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/helpercomponents.md new file mode 100644 index 0000000000..8f9d13ec9f --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/helper/helpercomponents.md @@ -0,0 +1,12 @@ +# Helper Components + +Understand the helper components available to use in your Netcode for GameObjects project. + + **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[AttachableBehaviour](attachablebehaviour.md)**| Use the AttachableBehaviour component to manage [ComponentController](componentcontroller.md) components and to attach a child GameObject to an [AttachableNode](attachablenode.md). The AttachableBehaviour component provides an alternative to NetworkObject parenting, allowing you to attach and detach child objects dynamically during runtime. | +| **[AttachableNode](attachablenode.md)**| Use an AttachableNode component to provide an attachment point for an [AttachableBehaviour](attachablebehaviour.md) component. | +| **[ComponentController](componentcontroller.md)**| Use a [ComponentController](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.ComponentController.html) component to enable or disable one or more components depending on the authority state of the ComponentController and have those changes synchronized with non-authority instances. | +| **[NetworkAnimator](networkanimator.md)**| The NetworkAnimator component provides you with a fundamental example of how to synchronize animations during a network session. Animation states are synchronized with players joining an existing network session and any client already connected before the animation state changing. | +| **[NetworkTransform](networktransform.md)**| [NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](../core/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. | +| **[Physics](../../advanced-topics/physics.md)**| Netcode for GameObjects has a built in approach which allows for server-authoritative physics where the physics simulation only runs on the server. | diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/networkanimator.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md similarity index 54% rename from com.unity.netcode.gameobjects/Documentation~/components/Helpers/networkanimator.md rename to com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md index 51b8d52eed..62b0162374 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/networkanimator.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md @@ -1,6 +1,6 @@ # NetworkAnimator -The `NetworkAnimator` component provides you with a fundamental example of how to synchronize animations during a network session. Animation states are synchronized with players joining an existing network session and any client already connected before the animation state changing. +The NetworkAnimator component provides you with a fundamental example of how to synchronize animations during a network session. Animation states are synchronized with players joining an existing network session and any client already connected before the animation state changing. * Players joining an existing network session will be synchronized with: * All the `Animator`'s current properties and states. @@ -10,10 +10,10 @@ The `NetworkAnimator` component provides you with a fundamental example of how t * States * Transitions * Properties - * `NetworkAnimator` will only synchronize properties that have changed since the earlier frame property values. + * NetworkAnimator will only synchronize properties that have changed since the earlier frame property values. * Since triggers are similar to an "event," when an `Animator` property is set to `true` it will always be synchronized. -`NetworkAnimator` can operate in two authoritative modes: +NetworkAnimator can operate in two authoritative modes: * Server Authoritative (default): Server initiates animation state changes. * Owner's can still invoke `NetworkAnimator.SetTrigger`. @@ -21,7 +21,7 @@ The `NetworkAnimator` component provides you with a fundamental example of how t > [!NOTE] -> You need to use `Unity.Netcode.Components` to reference components such as `NetworkAnimator`. +> You need to use `Unity.Netcode.Components` to reference components such as NetworkAnimator. ## Animator Trigger Property @@ -32,11 +32,11 @@ The `Animator` trigger property type ("trigger") is basically nothing more than ## Server Authoritative Mode -The default setting for `NetworkAnimator` is server authoritative mode. When operating in server authoritative mode, any animation state changes that are set (triggers) or detected (change in layer, state, or any `Animator` properties excluding triggers) on the server side will be synchronized with all clients. Because the server initiates any synchronization of changes to an `Animator` 's state, a client that's the owner of the `NetworkObject` associated with the `NetworkAnimator` can lag by roughly the full round trip time (RTT). Below is a timing diagram to show this: +The default setting for NetworkAnimator is server authoritative mode. When operating in server authoritative mode, any animation state changes that are set (triggers) or detected (change in layer, state, or any `Animator` properties excluding triggers) on the server side will be synchronized with all clients. Because the server initiates any synchronization of changes to an `Animator` 's state, a client that's the owner of the NetworkObject associated with the NetworkAnimator can lag by roughly the full round trip time (RTT). Below is a timing diagram to show this:  -In the above diagram, a client might be sending the server an RPC to tell the server that the player is performing some kind of action that can change the player's animations (including setting a trigger). Under this scenario, the client sends an RPC to the server (half RTT), the server processes the RPC, the associated `Animator` state changes are detected by the `NetworkAnimator` (server-side), and then all clients (including the owner client) are synchronized with the changed. +In the above diagram, a client might be sending the server an RPC to tell the server that the player is performing some kind of action that can change the player's animations (including setting a trigger). Under this scenario, the client sends an RPC to the server (half RTT), the server processes the RPC, the associated `Animator` state changes are detected by the NetworkAnimator (server-side), and then all clients (including the owner client) are synchronized with the changed. **Server authoritative model benefits:** @@ -49,7 +49,7 @@ In the above diagram, a client might be sending the server an RPC to tell the se ## Owner Authoritative Mode -Usually, your project's design (or personal preference) might require that owners are immediately updated to any `Animator` state changes. The most typical reason would be to give the local player with instantaneous visual (animation) feedback. To create an owner authoritative `NetworkAnimator` you need to create a new class that's derived from `NetworkAnimator`, override the `NetworkAnimator.OnIsServerAuthoritative` method, and within the overridden `OnIsServerAuthoritative` method you should return false like in the example provided below: +Usually, your project's design (or personal preference) might require that owners are immediately updated to any `Animator` state changes. The most typical reason would be to give the local player with instantaneous visual (animation) feedback. To create an owner authoritative NetworkAnimator you need to create a new class that's derived from NetworkAnimator, override the `NetworkAnimator.OnIsServerAuthoritative` method, and within the overridden `OnIsServerAuthoritative` method you should return false like in the example provided below: ```csharp public class OwnerNetworkAnimator : NetworkAnimator @@ -61,11 +61,11 @@ Usually, your project's design (or personal preference) might require that owner } ``` -Looking at the timing for an owner authoritative `NetworkAnimator`, in the diagram below, you can see that while the owner client gets "immediate visual animation response" the non-owner clients end up being roughly one full RTT behind the owner client and a host would be half RTT behind the owner client. +Looking at the timing for an owner authoritative NetworkAnimator, in the diagram below, you can see that while the owner client gets "immediate visual animation response" the non-owner clients end up being roughly one full RTT behind the owner client and a host would be half RTT behind the owner client.  -In the above diagram, it shows that the owner client has an `Animator` state change that's detected by the `NetworkAnimator` ( `OwnerNetworkAnimator`) which automatically synchronizes the server with the changed state. The server applies the change(s) locally and then broadcasts this state change to all non-owner clients. +In the above diagram, it shows that the owner client has an `Animator` state change that's detected by the NetworkAnimator ( `OwnerNetworkAnimator`) which automatically synchronizes the server with the changed state. The server applies the change(s) locally and then broadcasts this state change to all non-owner clients. **Owner authoritative model benefits:** @@ -81,25 +81,25 @@ In the above diagram, it shows that the owner client has an `Animator` state cha ## Using NetworkAnimator -Using `NetworkAnimator` is a pretty straight forward approach with the only subtle difference being whether you are using a server or owner authoritative model. +Using NetworkAnimator is a pretty straight forward approach with the only subtle difference being whether you are using a server or owner authoritative model. > [!NOTE] -> `NetworkAnimator` is one of several possible ways to synchronize animations during a network session. Netcode for GameObjects provides you with the building blocks (RPCs, NetworkVariables, and Custom Messages) needed to create a completely unique animation synchronization system that has a completely different and potentially more optimized approach. `NetworkAnimator` is a straight forward approach provided for users already familiar with the `Animator` component and, depending upon your project's design requirements, might be all that you need. +> NetworkAnimator is one of several possible ways to synchronize animations during a network session. Netcode for GameObjects provides you with the building blocks (RPCs, NetworkVariables, and Custom Messages) needed to create a completely unique animation synchronization system that has a completely different and potentially more optimized approach. NetworkAnimator is a straight forward approach provided for users already familiar with the `Animator` component and, depending upon your project's design requirements, might be all that you need. ### Server Authoritative -If you decide you want to use the server authoritative model, then you can add a `NetworkAnimator` component to either the same `GameObject` that has the `NetworkObject` component attached to it or any child `GameObject`. In the below screenshot, you can see a network Prefab that houses two authoritative models. The `NetworkAnimatorCube-Server` `GameObject` has an `Animator` component, an `AnimatedCubeController` component (used for manual testing), and the `NetworkAnimator` component that has a reference to the `Animator` component. +If you decide you want to use the server authoritative model, then you can add a NetworkAnimator component to either the same GameObject that has the NetworkObject component attached to it or any child GameObject. In the below screenshot, you can see a network Prefab that houses two authoritative models. The `NetworkAnimatorCube-Server` GameObject has an `Animator` component, an `AnimatedCubeController` component (used for manual testing), and the NetworkAnimator component that has a reference to the `Animator` component.  ### Owner Authoritative -If you decide you want to use the owner authoritative model, then (for example purposes) you would use your derived `OwnerNetworkAnimator` component as opposed to the default `NetworkAnimator` component like in the screenshot below: +If you decide you want to use the owner authoritative model, then (for example purposes) you would use your derived `OwnerNetworkAnimator` component as opposed to the default NetworkAnimator component like in the screenshot below:  > [!NOTE] -> While it isn't advised to have different `NetworkAnimator` authoritative models "under the same root network Prefab `GameObject`, " you can have multiple children that each have their own `Animator` and `NetworkAnimator` all housed under a single `NetworkObject` and all use the same authoritative model. However, you should always consider the balance between performance (CPU or bandwidth consumption) and convenience/modularity. +> While it isn't advised to have different NetworkAnimator authoritative models "under the same root network Prefab GameObject, " you can have multiple children that each have their own `Animator` and NetworkAnimator all housed under a single NetworkObject and all use the same authoritative model. However, you should always consider the balance between performance (CPU or bandwidth consumption) and convenience/modularity. ### Changing Animator Properties @@ -112,7 +112,7 @@ public void ApplyMotion(Vector3 playerVelocity) } ``` -For triggers you always want to use `NetworkAnimator`. One example might be that you use a trigger, called it " `IsJumping`, " to start a blended transition between the player's walking/running animation and the jumping animation: +For triggers you always want to use NetworkAnimator. One example might be that you use a trigger, called it " `IsJumping`, " to start a blended transition between the player's walking/running animation and the jumping animation: ```csharp public void SetPlayerJumping(bool isJumping) diff --git a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/networktransform.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/networktransform.md similarity index 95% rename from com.unity.netcode.gameobjects/Documentation~/components/Helpers/networktransform.md rename to com.unity.netcode.gameobjects/Documentation~/components/helper/networktransform.md index c3b3456148..0ad8366692 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/Helpers/networktransform.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/helper/networktransform.md @@ -1,6 +1,6 @@ # NetworkTransform -[NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](../foundational/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. +[NetworkTransform](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.Components.NetworkTransform.html) is a concrete class that inherits from [NetworkBehaviour](../core/networkbehaviour.md) and synchronizes [Transform](https://docs.unity3d.com/Manual/class-Transform.html) properties across the network, ensuring that the position, rotation, and scale of a [GameObject](https://docs.unity3d.com/Manual/working-with-gameobjects.html) are replicated to other clients. The synchronization of a GameObject's Transform is a key netcode task, and usually proceeds in the following order: @@ -20,7 +20,7 @@ There are other considerations when synchronizing Transform values, however, suc ## Add a NetworkTransform component to a GameObject -Because a NetworkTransform component is derived from the NetworkBehaviour class, it has many of the [same requirements](../foundational/networkbehaviour.md). For example, when adding a NetworkTransform component to a GameObject, it should be added to the same or any hierarchy generation relative to the `NetworkObject` component. +Because a NetworkTransform component is derived from the NetworkBehaviour class, it has many of the [same requirements](../core/networkbehaviour.md). For example, when adding a NetworkTransform component to a GameObject, it should be added to the same or any hierarchy generation relative to the NetworkObject component. In the following image both NetworkTransform and NetworkObject components are on the same GameObject: @@ -33,7 +33,7 @@ Alternatively, the parent GameObject can have multiple children where any child Theoretically, you can have a NetworkTransform on every child object of a 100 leaf deep hierarchy. However, it's recommended to exercise caution with the amount of nested NetworkTransforms in a network prefab, particularly if there will be many instances of the network prefab. > [!NOTE] -> Generally, as long as there's at least one [NetworkObject](../foundational/networkobject.md) at the same GameObject hierarchy level or above, you can attach a NetworkTransform component to a GameObject. You could have a single root-parent GameObject that has a NetworkObject component and under the root-parent several levels of nested child GameObjects that all have NetworkTransform components attached to them. Each child GameObject doesn't require a NetworkObject component in order for the respective NetworkTransform component to synchronize properly. +> Generally, as long as there's at least one [NetworkObject](../core/networkobject.md) at the same GameObject hierarchy level or above, you can attach a NetworkTransform component to a GameObject. You could have a single root-parent GameObject that has a NetworkObject component and under the root-parent several levels of nested child GameObjects that all have NetworkTransform components attached to them. Each child GameObject doesn't require a NetworkObject component in order for the respective NetworkTransform component to synchronize properly. ### Nesting NetworkTransforms @@ -56,7 +56,7 @@ Some NetworkTransform properties are automatically synchronized by the authorita #### Properties that cause a full state update -The following are a list of `NetworkTransform` properties that will cause a full state update (effectively a teleport) when changed during runtime by the authority instance: +The following are a list of NetworkTransform properties that will cause a full state update (effectively a teleport) when changed during runtime by the authority instance: - [UseUnreliableDeltas](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.3/api/Unity.Netcode.Components.NetworkTransform.html#Unity_Netcode_Components_NetworkTransform_UseUnreliableDeltas) - [InLocalSpace](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.3/api/Unity.Netcode.Components.NetworkTransform.html#Unity_Netcode_Components_NetworkTransform_InLocalSpace) @@ -89,7 +89,7 @@ You can have [nested NetworkTransforms](#nesting-networktransforms) as long as t If you don't plan on changing the Transform's scale after the initial synchronization (that occurs when joining a network session or when a network prefab instance is spawned for the first time), then disabling the X, Y, and Z properties for __Scale__ synchronization removes some additional processing overhead per instance. However, if you have a nested NetworkTransform and want to apply a unique scale (per instance) that's applied to that nested NetworkTransform's Transform when it's first spawned, then the adjusted scale won't be synchronized. -Fortunately, the authority of the `NetworkTransform` instance can make changes to any of the __Axis to Synchronize__ properties during runtime and non-authoritative instances will receive updates for the axis marked for synchronization. +Fortunately, the authority of the NetworkTransform instance can make changes to any of the __Axis to Synchronize__ properties during runtime and non-authoritative instances will receive updates for the axis marked for synchronization. When disabling an axis to be synchronized for performance purposes, you should always consider that NetworkTransform won't send updates as long as the axis in question doesn't have a change that exceeds its [threshold](#thresholds) value. So, taking the scale example into consideration, it can be simpler to leave those axes enabled if you only ever plan on changing them once or twice because the CPU cost for checking that change isn't as expensive as the serialization and state update sending process. The associated axis threshold values can make the biggest impact on frequency of sending state updates that, in turn, will reduce the number of state updates sent per second at the cost of losing some motion resolution. @@ -166,7 +166,7 @@ When unreliable state updates are enabled, NetworkTransform instances are assign By default, NetworkTransform synchronizes the Transform of an object in world space. The __In Local Space__ configuration option allows you to change to synchronizing the transform in local space instead. A child's local space axis values (position and rotation primarily) are always relative offsets from the parent transform, where a child's world space axis values include the parent's axis values. Where a transform with no parent really is "parented" to the root of the scene hierarchy which results in its world and local space positions to always be the same. -Enabling the __In Local Space__ property on a parented NetworkTransform can improve the synchronization of the transform when the object gets re-parented because the re-parenting won't change the local space Transform of the object (but does change the world space position) and you only need to update motion of the parented `NetworkTransform` relative to its parent (if the parent is moving and the child has no motion then there are no delta states to detect for the child but the child moves with the parent). +Enabling the __In Local Space__ property on a parented NetworkTransform can improve the synchronization of the transform when the object gets re-parented because the re-parenting won't change the local space Transform of the object (but does change the world space position) and you only need to update motion of the parented NetworkTransform relative to its parent (if the parent is moving and the child has no motion then there are no delta states to detect for the child but the child moves with the parent). :::info The authority instance does synchronize changes to the __In Local Space__ property. As such, you can make adjustments to this property on the authoritative side during runtime and the non-authoritative instances will automatically be updated. diff --git a/com.unity.netcode.gameobjects/Documentation~/integrated-management.md b/com.unity.netcode.gameobjects/Documentation~/integrated-management.md index 6e4994ade1..de76dd38d7 100644 --- a/com.unity.netcode.gameobjects/Documentation~/integrated-management.md +++ b/com.unity.netcode.gameobjects/Documentation~/integrated-management.md @@ -8,4 +8,4 @@ Understand Netcode for GameObject's integrated scene management system. | **[Scene events](basics/scenemanagement/scene-events.md)** | Use scene events to manage sychronization in a scene. | | **[Client synchronization mode](basics/scenemanagement/client-synchronization-mode.md)** | Netcode for GameObjects provides you with the ability to select the client synchronization mode that best suits your project's needs. | | **[Timing considerations](basics/scenemanagement/timing-considerations.md)** | Netcode for GameObjects handles many of the more complicated aspects of scene management. This section is tailored towards those who want to better understand the client-server communication sequence for scene events as they occur over time. | -| **[In-scene placed NetworkObjects](basics/scenemanagement/inscene-placed-networkobjects.md)** | In-scene placed `NetworkObject`s are GameObjects with a `NetworkObject` component that was added to a scene from within the Editor. | \ No newline at end of file +| **[In-scene placed NetworkObjects](basics/scenemanagement/inscene-placed-networkobjects.md)** | In-scene placed NetworkObjects are GameObjects with a NetworkObject component that was added to a scene from within the Editor. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/clientside-interpolation.md b/com.unity.netcode.gameobjects/Documentation~/learn/clientside-interpolation.md index d65c358e4d..f6c6e51829 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/clientside-interpolation.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/clientside-interpolation.md @@ -15,7 +15,7 @@ In addition to reduced responsiveness, this approach can also directly interfere ## With client-side interpolation -When using client-side interpolation, all clients intentionally run slightly behind the server, giving them time to transition smoothly between state updates and conceal the effects of latency from users. To implement client-side interpolation, use the [NetworkTransform](../components/helpers/networktransform.md) component. +When using client-side interpolation, all clients intentionally run slightly behind the server, giving them time to transition smoothly between state updates and conceal the effects of latency from users. To implement client-side interpolation, use the [NetworkTransform](../components/helper/networktransform.md) component. In a standard client-server [topology](../terms-concepts/network-topologies.md), clients are able to render a state that's approximately half the [round-trip time (RTT)](lagandpacketloss.md#round-trip-time-and-pings) behind the server. When using client-side interpolation, a further intentional delay is added so that by the time the client is rendering state _n_, it's already received state _n+1_, which allows the client to always smoothly transition from one state to another. This is effectively increasing latency, but in a consistent, client-universal way that can be used to reduce the negative impacts of unpredictable network jitter on gameplay. diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md b/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md index 657d6f12a8..a14cc6d0da 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/dealing-with-latency.md @@ -157,7 +157,7 @@ To do continuous client driven actions, there's a few more considerations to tak - You then need to make sure you don't send RPCs to the server (containing your authoritative state) when no data has changed and do dirty checks. - You'd need to send it on tick or at worst on FixedUpdate. Sending on Update() would spam your connection. -A sample for a [ClientNetworkTransform](../components/helpers/networktransform.md#clientnetworktransform) has been created, so you don't have to reimplement this yourself for transform updates. A [sample](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/tree/main/Basic/ClientDriven) has been created on how to use it. See [movement script](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/ClientDriven/Assets/Scripts/ClientPlayerMove.cs). +A sample for a [ClientNetworkTransform](../components/helper/networktransform.md#clientnetworktransform) has been created, so you don't have to reimplement this yourself for transform updates. A [sample](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/tree/main/Basic/ClientDriven) has been created on how to use it. See [movement script](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/ClientDriven/Assets/Scripts/ClientPlayerMove.cs). > [!NOTE] > A rule of thumb here is to ask yourself: "Can the server correct me on this?". If it can, use server authority. @@ -184,7 +184,7 @@ Extrapolation is an attempt to estimate a future game state, without taking into The client will normally assume that a moving object will continue in the same direction. When a new packet is received, the position may be updated. -For Netcode for gameobjects (Netcode), a basic extrapolation implementation has been provided in [`NetworkTransform`](../components/helpers/networktransform.md) and is estimated between the time a tick advances in server-side animation and the update of the frame on the client-side. The game object extrapolates the next frame's values based on the ratio. +For Netcode for gameobjects (Netcode), a basic extrapolation implementation has been provided in [NetworkTransform](../components/helper/networktransform.md) and is estimated between the time a tick advances in server-side animation and the update of the frame on the client-side. The game object extrapolates the next frame's values based on the ratio. > [!NOTE] > While Netcode for GameObjects doesn't have a full implementation of client-side prediction and reconciliation, you can build such a system on top of the existing client-side anticipation building-blocks, `AnticipatedNetworkVariable` and `AnticipatedNetworkTransform`. These components allow differentiating between the "authoritative" value and the value that is shown to the players. These components provide most of the information needed to implement prediction, but do require you to implement certain aspects yourself. Because of the complexity inherent in building a full client prediction system, the details of that are left for users, and we recommend only advanced users pursue this option. diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/faq.md b/com.unity.netcode.gameobjects/Documentation~/learn/faq.md index 81d5536282..46a5f40879 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/faq.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/faq.md @@ -49,7 +49,7 @@ Spawning can always be done on the host/server. If you want to give a client con ### What are best practices for handing physics with Netcode? -Networked physics is complicated, with many different ways to handle them. Currently, physics can be a little difficult to handle with Netcode and the built-in `NetworkTransform`. +Networked physics is complicated, with many different ways to handle them. Currently, physics can be a little difficult to handle with Netcode and the built-in NetworkTransform. The Multiplayer Technology Team recommends the following: @@ -90,7 +90,7 @@ See [Apple Support](https://support.apple.com/guide/mac-help/open-a-mac-app-from ### Why is there an `InitBootStrap` scene as the startup scene for Boss Room and Bitesize Samples? -The initial reason is that in Netcode the `NetworkManager` is a singleton class. The Bitesize Samples Team initially created it in the main menu, but when the host was leaving the in-game/networked scene, the Network Manager was getting destroyed, which led to not being able to host a game again without restarting the game instance. +The initial reason is that in Netcode the NetworkManager is a singleton class. The Bitesize Samples Team initially created it in the main menu, but when the host was leaving the in-game/networked scene, the Network Manager was getting destroyed, which led to not being able to host a game again without restarting the game instance. The Bootstrap scene ensures that the NetworkManager and other singletons are initialized first and will be there when you get back to main menu. diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/rpcnetvarexamples.md b/com.unity.netcode.gameobjects/Documentation~/learn/rpcnetvarexamples.md index 9e4ee6e21f..fd328d147a 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/rpcnetvarexamples.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/rpcnetvarexamples.md @@ -16,7 +16,7 @@ Boss Room wants the full history of inputs sent, not just the latest value. Ther ## Arrow's GameObject vs Fireball's VFX -The archer's arrows use a standalone `GameObject` that's replicated over time. Since this object's movements are slow, the Boss Room development team decided to use state (via the `NetworkTransform`) to replicate the ability's status (in case a client connected while the arrow was flying). +The archer's arrows use a standalone GameObject that's replicated over time. Since this object's movements are slow, the Boss Room development team decided to use state (via the NetworkTransform) to replicate the ability's status (in case a client connected while the arrow was flying). ```csharp reference https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/v2.2.0/Assets/Scripts/Gameplay/GameplayObjects/Projectiles/PhysicsProjectile.cs @@ -32,7 +32,7 @@ https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/v2 ## Breakable state -Boss Room might have used a "break" `RPC` to set a breakable object as broken and play the appropriate visual effects. Applying the "replicate information when a player joins the game mid-game" rule of thumb, the Boss Room development team used `NetworkVariable`s instead. Boss Room uses the `OnValueChanged` callback on those values to play the visual effects (and an initial check when spawning the `NetworkBehaviour`). +Boss Room might have used a "break" `RPC` to set a breakable object as broken and play the appropriate visual effects. Applying the "replicate information when a player joins the game mid-game" rule of thumb, the Boss Room development team used `NetworkVariable`s instead. Boss Room uses the `OnValueChanged` callback on those values to play the visual effects (and an initial check when spawning the NetworkBehaviour). ```csharp reference diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/sample-dedicated-server.md b/com.unity.netcode.gameobjects/Documentation~/learn/sample-dedicated-server.md index 7e4f5abb75..3fc0335690 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/sample-dedicated-server.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/sample-dedicated-server.md @@ -52,14 +52,14 @@ This sample strips other components and GameObjects from the server manually. To This sample splits the logic of the Player Character and the AI Character into separate scripts so that you can use the Content Selection window to run each character separately on the client, server and network. For example, the `PlayerCharacter` script logic is split into the following scripts: * Client Player Character. This script only exists on the clients. * Server Player Character.This script only exists on the server. -* Networked Player Character: This script inherits from `NetworkBehaviour`.It synchronizes data and sends messages between the server and clients. This script exists on both clients and the server. +* Networked Player Character: This script inherits from NetworkBehaviour.It synchronizes data and sends messages between the server and clients. This script exists on both clients and the server. These scripts separate the logic of each player which means you can strip the components that each script uses. For example, Client Player Character is the only script that uses the Player Character’s `CharacterController` component, so you can safely strip the `CharacterController` component from the server. To learn where the scripts in this sample exist, do the following: 1. Select a Player Character GameObject. 2. Open the GameObject’s Inspector window. 3. Refer to the script component’s [Multiplayer role icon](https://docs.unity3d.com/Packages/com.unity.dedicated-server@1.0/manual/mutliplayer-roles-icons.html). -The logic for scripts that contain a small class, like the doors and switches in this sample scene, exist in a single script that inherits from ``NetworkBehaviour``. +The logic for scripts that contain a small class, like the doors and switches in this sample scene, exist in a single script that inherits from `NetworkBehaviour`. #### Synchronize animations between clients diff --git a/com.unity.netcode.gameobjects/Documentation~/network-components.md b/com.unity.netcode.gameobjects/Documentation~/network-components.md index dc93f61356..b4df3986e3 100644 --- a/com.unity.netcode.gameobjects/Documentation~/network-components.md +++ b/com.unity.netcode.gameobjects/Documentation~/network-components.md @@ -4,5 +4,5 @@ Understand the network components involved in a Netcode for GameObjects project. | **Topic** | **Description** | | :------------------------------ | :------------------------------- | -| **[Foundational Components](components/foundational/foundationalcomponents.md)** | Learn about the three foundational components: `NetworkObject`, `NetworkBehaviour`, and `NetworkManager`. | -| **[Helper Components](components/Helpers/helpercomponents.md)** | Helper components available to use in your Netcode for GameObjects project. | +| **[Core components](components/core/corecomponents.md)** | Learn about the three core components of Netcode for GameObjects: NetworkObject, NetworkBehaviour, and NetworkManager. | +| **[Helper Components](components/helper/helpercomponents.md)** | Helper components available to use in your Netcode for GameObjects project. | diff --git a/com.unity.netcode.gameobjects/Documentation~/network-synchronization.md b/com.unity.netcode.gameobjects/Documentation~/network-synchronization.md index 30d5576881..aff9529a82 100644 --- a/com.unity.netcode.gameobjects/Documentation~/network-synchronization.md +++ b/com.unity.netcode.gameobjects/Documentation~/network-synchronization.md @@ -9,5 +9,5 @@ Manage latency and performance in your Netcode for GameObjects project. | **[Remote procedure calls (RPCs)](rpc-landing.md)** | Any process can communicate with any other process by sending a remote procedure call (RPC). | | **[Custom messages](advanced-topics/message-system/custom-messages.md)** | Create a custom message system for your Netcode for GameObjects project. | | **[Connection events](advanced-topics/connection-events.md)** | When you need to react to connection or disconnection events for yourself or other clients, you can use `NetworkManager.OnConnectionEvent` as a unified source of information about changes in the network. | -| **[Network update loop](network-update-loop.md)** | The Network Update Loop infrastructure utilizes Unity's low-level Player Loop API allowing for registering `INetworkUpdateSystems` with `NetworkUpdate()` methods to be executed at specific `NetworkUpdateStages` which may be either before or after `MonoBehaviour`-driven game logic execution. | +| **[Network update loop](network-update-loop.md)** | The Network Update Loop infrastructure utilizes Unity's low-level Player Loop API allowing for registering `INetworkUpdateSystems` with `NetworkUpdate()` methods to be executed at specific `NetworkUpdateStages` which may be either before or after MonoBehaviour-driven game logic execution. | | **[Network time and ticks](advanced-topics/networktime-ticks.md)** | Understand how network time and ticks work while synchronizing your project. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/network-update-loop.md b/com.unity.netcode.gameobjects/Documentation~/network-update-loop.md index d705e8e073..323b8d1403 100644 --- a/com.unity.netcode.gameobjects/Documentation~/network-update-loop.md +++ b/com.unity.netcode.gameobjects/Documentation~/network-update-loop.md @@ -4,5 +4,5 @@ Understand how network information is updated in Netcode for GameObjects project | **Topic** | **Description** | | :------------------------------ | :------------------------------- | -| **[About network update loop](advanced-topics/network-update-loop-system/index.md)** | The Network Update Loop infrastructure utilizes Unity's low-level Player Loop API allowing for registering `INetworkUpdateSystems` with `NetworkUpdate()` methods to be executed at specific `NetworkUpdateStages` which may be either before or after `MonoBehaviour`-driven game logic execution. | +| **[About network update loop](advanced-topics/network-update-loop-system/index.md)** | The Network Update Loop infrastructure utilizes Unity's low-level Player Loop API allowing for registering `INetworkUpdateSystems` with `NetworkUpdate()` methods to be executed at specific `NetworkUpdateStages` which may be either before or after MonoBehaviour-driven game logic execution. | | **[Network update loop reference](advanced-topics/network-update-loop-system/network-update-loop-reference.md)** | Diagrams that provide insight into the Network Update Loop process and APIs. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/networkbehaviour-landing.md b/com.unity.netcode.gameobjects/Documentation~/networkbehaviour-landing.md index b6432462cf..c690759906 100644 --- a/com.unity.netcode.gameobjects/Documentation~/networkbehaviour-landing.md +++ b/com.unity.netcode.gameobjects/Documentation~/networkbehaviour-landing.md @@ -1,9 +1,9 @@ # NetworkBehaviours -`NetworkBehaviour` components are the 2nd +NetworkBehaviour components are the 2nd Understand how to use NetworkBehaviour components in your project. | **Topic** | **Description** | | :------------------------------ | :------------------------------- | -| **[NetworkBehaviour](components/foundational/networkbehaviour.md)** | [`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](components/foundational/networkobject.md) component and at least one `NetworkBehaviour` component. | -| **[Synchronize](components/foundational/networkbehaviour-synchronize.md)** | You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. | \ No newline at end of file +| **[NetworkBehaviour](components/core/networkbehaviour.md)** | [NetworkBehaviour](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](components/core/networkobject.md) component and at least one NetworkBehaviour component. | +| **[Synchronize](components/core/networkbehaviour-synchronize.md)** | You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/relay/relay.md b/com.unity.netcode.gameobjects/Documentation~/relay/relay.md index 844a9d3b24..15f4fe66a6 100644 --- a/com.unity.netcode.gameobjects/Documentation~/relay/relay.md +++ b/com.unity.netcode.gameobjects/Documentation~/relay/relay.md @@ -105,7 +105,7 @@ For more information about the join code connection process, refer to [Connectio When an allocation exists, you need to make all traffic that comes from Netcode for GameObjects go through the Relay. To do this, perform the following actions to pass the allocation parameters to UnityTransport: -1. Retrieve Unity transport from your `NetworkManager`: +1. Retrieve Unity transport from your NetworkManager: ```csharp //Retrieve the Unity transport used by the NetworkManager UnityTransport transport = NetworkManager.Singleton.gameObject.GetComponent<UnityTransport>(); diff --git a/com.unity.netcode.gameobjects/Documentation~/samples.md b/com.unity.netcode.gameobjects/Documentation~/samples.md index d61850d480..8c799629a9 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples.md @@ -4,5 +4,5 @@ Use the samples in this section to learn how to use Netcode for GameObjects in y | **Topic** | **Description** | | :------------------------------ | :------------------------------- | -| **[NetworkBehaviour](components/foundational/networkbehaviour.md)** | [`NetworkBehaviour`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [`MonoBehaviour`](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](components/foundational/networkobject.md) component and at least one `NetworkBehaviour` component. | -| **[Synchronize](components/foundational/networkbehaviour-synchronize.md)** | You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. | \ No newline at end of file +| **[NetworkBehaviour](components/core/networkbehaviour.md)** | [NetworkBehaviour](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.html) is an abstract class that derives from [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and is primarily used to create unique netcode or game logic. To replicate any netcode-aware properties or send and receive RPCs, a [GameObject](https://docs.unity3d.com/Manual/GameObjects.html) must have a [NetworkObject](components/core/networkobject.md) component and at least one NetworkBehaviour component. | +| **[Synchronize](components/core/networkbehaviour-synchronize.md)** | You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-clientdriven.md b/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-clientdriven.md index aa9445c02d..bf614d50b8 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-clientdriven.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-clientdriven.md @@ -6,7 +6,7 @@ Making movements feel responsive while staying consistent over multiple game exe ## TLDR: -- Use [ClientNetworkTransform](../../components/helpers/networktransform.md) to sync client authoritative transform updates to the server and other clients. +- Use [ClientNetworkTransform](../../components/helper/networktransform.md) to sync client authoritative transform updates to the server and other clients. - Make sure ownership is set properly on that NetworkObject to be able to move it. - Since your clients live on different timelines (one per player), you have to be careful about who takes decisions and keep some of those decisions centralized on the server. - DON'T FORGET to test with latency, as your game will behave differently depending on whether client or server make decisions. diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-invaders.md b/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-invaders.md index d4f6cb8d40..2d7604fd6a 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-invaders.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bitesize/bitesize-invaders.md @@ -86,7 +86,7 @@ https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blo ## Unconventional Networked Movement -Invaders has an easy movement type - moving only on one (horizontal) axis - which allows you to only modify the transform client-side without waiting for server-side validation. You can find where we perform the move logic in *[PlayerControl.cs](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/Invaders/Assets/Scripts/PlayerControl.cs)* in the `InGameUpdate` function . With the help of a [`NetworkTransform`](../../components/helpers/networktransform.md) that is attached directly to the Player Game Object, it will automatically sync up the Transform with the other clients. At the same time, it will smooth out the movement by interpolating or extrapolating for all of them. +Invaders has an easy movement type - moving only on one (horizontal) axis - which allows you to only modify the transform client-side without waiting for server-side validation. You can find where we perform the move logic in *[PlayerControl.cs](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/Invaders/Assets/Scripts/PlayerControl.cs)* in the `InGameUpdate` function . With the help of a [NetworkTransform](../../components/helper/networktransform.md) that is attached directly to the Player Game Object, it will automatically sync up the Transform with the other clients. At the same time, it will smooth out the movement by interpolating or extrapolating for all of them. ```csharp reference https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/blob/v1.2.1/Basic/Invaders/Assets/Scripts/PlayerControl.cs#L176-L193 diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/architecture.md b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/architecture.md index 0f0928e5a7..d3e64ea4d3 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/architecture.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/architecture.md @@ -32,7 +32,7 @@ After Boss Room starts and the initial bootstrap logic completes, the `Applicati The MainMenu scene only has the `MainMenuClientState`, whereas scenes that contain networked logic also have the server counterparts to the client state components. In the latter case, both the server and client components exist on the same GameObject. -The `NetworkManager` starts when the `CharSelect` scene loads, which happens when a player joins or hosts a game. The host drives game state transitions and controls the set of loaded scenes. Having the host manage the state transitions and scenes indirectly forces all clients to load the same scenes as the server they're connected to (via Netcode's networked scene management). +The NetworkManager starts when the `CharSelect` scene loads, which happens when a player joins or hosts a game. The host drives game state transitions and controls the set of loaded scenes. Having the host manage the state transitions and scenes indirectly forces all clients to load the same scenes as the server they're connected to (via Netcode's networked scene management). ### Application Flow Diagram @@ -57,7 +57,7 @@ With the Unity Relay network transport, clients don't need to worry about sharin See the [Multiplayer over the internet](getting-started-boss-room.md) section of the Boss Room README for more information about using the two network transport mechanisms. -Boss Room uses the Unity Transport package. Boss Room's assigns its instance of Unity Transport to the `transport` field of the `NetworkManager`. +Boss Room uses the Unity Transport package. Boss Room's assigns its instance of Unity Transport to the `transport` field of the NetworkManager. The Unity Transport Package is a network transport layer with network simulation tools that help spot networking issues early during development. Boss Room has both buttons to start a game in the two modes and will setup Unity Transport automatically to use either one of them at runtime. @@ -100,7 +100,7 @@ To keep a single source of truth for service access (and avoid scattering of ser An `Avatar` is at the same level as an `Imp` and lives in a scene. A `Persistent Player` lives across scenes. ::: -A `Persistent Player` Prefab goes into the `Player` Prefab slot in the `NetworkManager` of Boss Room. As a result, Boss Room spawns a single `Persistent Player` Prefab per client, and each client owns their respective `Persistent Player` instances. +A `Persistent Player` Prefab goes into the `Player` Prefab slot in the NetworkManager of Boss Room. As a result, Boss Room spawns a single `Persistent Player` Prefab per client, and each client owns their respective `Persistent Player` instances. :::note There is no need to mark `Persistent Player` instances as `DontDestroyOnLoad`. Netcode for GameObjects automatically keeps these prefabs alive between scene loads while the connections are live. @@ -117,19 +117,19 @@ Inside the Boss Room scene, `ServerBossRoomState` spawns a `PlayerAvatar` per `P The following example of a selected “Archer Boy” class shows the `PlayerAvatar` GameObject hierarchy: * `PlayerAvatar` is a NetworkObject that Boss Room destroys when the scene unloads. -* `PlayerGraphics` is a child `GameObject` containing a `NetworkAnimator` component responsible for replicating animations invoked on the server. +* `PlayerGraphics` is a child GameObject containing a NetworkAnimator component responsible for replicating animations invoked on the server. * `PlayerGraphics_Archer_Boy` is a purely graphical representation of the selected avatar class. -`ClientAvatarGuidHandler`, a `NetworkBehaviour` component residing on the `PlayerAvatar` Prefab instance, fetches the validated avatar GUID from `NetworkAvatarGuidState` and spawns a local, non-networked graphics GameObject corresponding to the avatar GUID. +`ClientAvatarGuidHandler`, a NetworkBehaviour component residing on the `PlayerAvatar` Prefab instance, fetches the validated avatar GUID from `NetworkAvatarGuidState` and spawns a local, non-networked graphics GameObject corresponding to the avatar GUID. ### Characters `ServerCharacter` exists on a `PlayerAvatar` (or another NPC character) and has server RPCs and `NetworkVariables` that store the state of a given character. It's responsible for executing the server-side logic for the characters. This server-side logic includes the following: -* Movement and pathfinding via `ServerCharacterMovement` use `NavMeshAgent,` which exists on the server to translate the character's transform (synchronized using the `NetworkTransform` component); +* Movement and pathfinding via `ServerCharacterMovement` use `NavMeshAgent,` which exists on the server to translate the character's transform (synchronized using the NetworkTransform component); * Player action queueing and execution via `ServerActionPlayer`; * AI logic via `AIBrain` (applies to NPCs); -* Character animations via `ServerAnimationHandler`, which uses `NetworkAnimator` to synchronize; +* Character animations via `ServerAnimationHandler`, which uses NetworkAnimator to synchronize; * `ClientCharacter` is primarily a host for the `ClientActionPlayer` class and has the client RPCs for the character gameplay logic. ### Game config setup @@ -176,7 +176,7 @@ The following list describes the movement flow of a player character. 4. There's network latency before the server receives the RPC. 5. The server receives the RPC (containing the target destination). 6. The server performs pathfinding calculations. -7. The server completes the pathfinding, and the server representation of the entity starts updating its `NetworkTransform` at the same cadence as `FixedUpdate`. +7. The server completes the pathfinding, and the server representation of the entity starts updating its NetworkTransform at the same cadence as `FixedUpdate`. 8. There's network latency before clients receive replication data. 9. The client representation of the entity updates its NetworkVariables. @@ -186,7 +186,7 @@ The Visuals GameObject never outpaces the simulation GameObject and is always sl ### Navigation system -Each scene with navigation (or dynamic navigation objects) should have a `NavigationSystem` component on a scene GameObject. The `GameObject` containing the `NavigationSystem` component must have the `NavigationSystem` tag. +Each scene with navigation (or dynamic navigation objects) should have a `NavigationSystem` component on a scene GameObject. The GameObject containing the `NavigationSystem` component must have the `NavigationSystem` tag. #### Building a navigation mesh @@ -202,7 +202,7 @@ You should also ensure each scene has exactly one `navmesh` file. You can find ` Boss Room implements the [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) (DI) pattern using the [`VContainer`](https://vcontainer.hadashikick.jp/) library. DI allows Boss Room to clearly define its dependencies in code instead of using static access, pervasive singletons, or scriptable object references (Scriptable Object Architecture). Code is easy to version-control and comparatively easy to understand for a programmer, unlike Unity YAML-based objects, such as scenes, scriptable object instances, and prefabs. -DI also allows Boss Room to circumvent the problem of cross-scene references to common dependencies, even though it still has to manage the lifecycle of `MonoBehaviour`-based dependencies by marking them with `DontDestroyOnLoad` and destroying them manually when appropriate. +DI also allows Boss Room to circumvent the problem of cross-scene references to common dependencies, even though it still has to manage the lifecycle of MonoBehaviour-based dependencies by marking them with `DontDestroyOnLoad` and destroying them manually when appropriate. :::note `ApplicationController` inherits from the `VContainer`'s `LifetimeScope`, a class that serves as a dependency injection scope and bootstrapper that facilitates binding dependencies. Scene-specific State classes inherit from `LifetimeScope`, too. @@ -229,7 +229,7 @@ After investigation, the Boss Room development team determined that client/serve * It's not completely necessary to ifdef classes because it's only compile-time insurance that certain parts of client-side code never run. You can still disable the component on Awake at runtime if it's not mean to run on the server or client. * The added complexity outweighed the pros that'd help with stripping whole assemblies. * Most `Client`/`Server` class pairs are tightly coupled and call one another; they have split implementations of the same logical object. Separating them into different assemblies forces you to create “bridge classes” to avoid circular dependencies between your client and server assemblies. By putting your client and server classes in the same assemblies, you allow those circular dependencies in those tightly coupled classes and remove unnecessary bridging and abstractions. -* Whole assembly stripping is incompatible with Netcode for GameObjects because Netcode for GameObjects doesn't support NetworkBehaviour stripping. Components related to a NetworkObject must match on the client and server sides. If these components aren't identical, it creates undefined runtime errors (the errors will change from one use to another; they range from no issue, to silent errors, to buffer exceptions) with Netcode for GameObjects' `NetworkBehaviour` indexing. +* Whole assembly stripping is incompatible with Netcode for GameObjects because Netcode for GameObjects doesn't support NetworkBehaviour stripping. Components related to a NetworkObject must match on the client and server sides. If these components aren't identical, it creates undefined runtime errors (the errors will change from one use to another; they range from no issue, to silent errors, to buffer exceptions) with Netcode for GameObjects' NetworkBehaviour indexing. After those experiments, the Boss Room development team established new rules for the codebase: diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkobject-parenting.md b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkobject-parenting.md index 5ab881e6c7..8c20b1a0d5 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkobject-parenting.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkobject-parenting.md @@ -8,7 +8,7 @@ Before detailing Boss Room's approach to NetworkObject parenting, it's important Boss Room leverages NetworkObject parenting through the server-driven `PickUp` action (see [`PickUpAction.cs`](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Scripts/Gameplay/Action/ConcreteActions/PickUpAction.cs)), where a character has the ability to pick up a specially-tagged, in-scene placed NetworkObject (see [`PickUpPot` prefab](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Prefabs/Game/PickUpPot.prefab)]. -At its root, `PickUpPot` has a NetworkObject, a `NetworkTransform`, and a `PositionConstraint` component. `AutoObjectParentSync` is enabled on its `NetworkTransform` (as is by default) so that: +At its root, `PickUpPot` has a NetworkObject, a NetworkTransform, and a `PositionConstraint` component. `AutoObjectParentSync` is enabled on its NetworkTransform (as is by default) so that: 1. The NetworkObject can verify server-side if parenting a Heavy object to another NetworkObject is allowed. 2. The NetworkObject can notify us when the parent has successfully been modified server-side. @@ -17,4 +17,4 @@ To accommodate the limitation highlighted at the beginning of this document, Bos A special hand bone has been added to our Character's avatar. Upon a character's successful parenting attempt, this special bone is set as the `PickUpPot`'s `PositonConstraint` target. So while the `PickUpPot` is technically parented to a player, the `PositionConstraint` component allows the `PickUpPot` to follow a bone's position, presenting the **illusion** that the `PickUpPot` is parented to the player's hand bone itself. -Once the `PickUpPot` is parented, local space simulation is enabled on its [`NetworkTransform` component](../../components/helpers/networktransform.md). +Once the `PickUpPot` is parented, local space simulation is enabled on its [NetworkTransform component](../../components/helper/networktransform.md). diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkrigidbody.md b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkrigidbody.md index dcb0339cb9..35a7530d6e 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkrigidbody.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/networkrigidbody.md @@ -4,6 +4,6 @@ Required reading: [Physics](../..//advanced-topics/physics.md) ::: -Boss Room leverages `NetworkRigidbody` to simulate physics-based projectiles. See the Vandal Imp's tossed projectile, the [`ImpTossedItem` prefab](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Prefabs/Game/ImpTossedItem.prefab). At its root, this Prefab has a NetworkObject, a `NetworkTransform`, a `Rigidbody`, and a `NetworkRigidbody` component. Refer to [`TossAction.cs`](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Scripts/Gameplay/Action/ConcreteActions/TossAction.cs) for more implementation details. +Boss Room leverages NetworkRigidBody to simulate physics-based projectiles. See the Vandal Imp's tossed projectile, the [`ImpTossedItem` prefab](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Prefabs/Game/ImpTossedItem.prefab). At its root, this Prefab has a NetworkObject, a NetworkTransform, a `Rigidbody`, and a NetworkRigidBody component. Refer to [`TossAction.cs`](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Scripts/Gameplay/Action/ConcreteActions/TossAction.cs) for more implementation details. -An important note: You must do any modifications to a `Rigidbody` that involve Physics (modifying velocity, applying forces, applying torque, and the like) **after** the NetworkObject spawns since `NetworkRigidbody` forces the `Rigidbody`'s `isKinematic` flag to be true on `Awake()`. Once spawned, this flag is modified depending on the ownership status of the NetworkObject. +An important note: You must do any modifications to a `Rigidbody` that involve Physics (modifying velocity, applying forces, applying torque, and the like) **after** the NetworkObject spawns since NetworkRigidBody forces the `Rigidbody`'s `isKinematic` flag to be true on `Awake()`. Once spawned, this flag is modified depending on the ownership status of the NetworkObject. diff --git a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/optimizing-bossroom.md b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/optimizing-bossroom.md index 2753803ce4..f5325cb5b8 100644 --- a/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/optimizing-bossroom.md +++ b/com.unity.netcode.gameobjects/Documentation~/samples/bossroom/optimizing-bossroom.md @@ -37,7 +37,7 @@ For more examples, see [RPCs vs. NetworkVariables Examples](../../learn/rpcnetva ## NetworkTransform configuration {#networktransform-configuration} -The NetworkTransform component handles the synchronization of a NetworkObject's Transform. By default, the NetworkTransform component synchronizes every part of the transform at every tick if a change bigger than the specified [threshold](../../components/helpers/networktransform.md) occurs. However, you can configure it to only synchronize the necessary data by omitting particular axeis of the position, rotation, or scale vectors. See [Restricting synchronization](../../components/helpers/networktransform.md). +The NetworkTransform component handles the synchronization of a NetworkObject's Transform. By default, the NetworkTransform component synchronizes every part of the transform at every tick if a change bigger than the specified [threshold](../../components/helper/networktransform.md) occurs. However, you can configure it to only synchronize the necessary data by omitting particular axeis of the position, rotation, or scale vectors. See [Restricting synchronization](../../components/helper/networktransform.md). You can also increase the thresholds to reduce the frequency of updates if you don't mind reducing the accuracy and responsiveness of the replicated Transform. @@ -47,7 +47,7 @@ Since the characters evolve on a plane, we only synchronize their position's x a Additionally, with the changes introduced in [Netcode for GameObjects v1.4.0](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/releases/tag/ngo%2F1.4.0), we were able to further reduce the bandwidth cost associated for some prefabs that utilized NetworkTransform. The synchronization payload was reduced by 5 bytes for the Character and the Arrow prefab inside Boss Room, for example, by enabling "Use Half Float Precision" on their respective NetworkTransforms. -See [NetworkTransform](../../components/helpers/networktransform.md) for more information on the NetworkTransform component. +See [NetworkTransform](../../components/helper/networktransform.md) for more information on the NetworkTransform component. ## Pooling {#pooling} @@ -103,7 +103,7 @@ As a result, the maximum number of reliable packets sent or received in a single ## NetworkManager tick rate configuration {#networkmanager-tick-rate-configuration} -Netcode's [NetworkManager](../../components/foundational/networkmanager.md) provides some configuration options, one of which is the [tick rate](../../advanced-topics/networktime-ticks.md). The tick rate configuration option determines the frequency at which network ticks occur. The ideal tick rate value relies on balancing smoothness, accuracy, and bandwidth usage. +Netcode's [NetworkManager](../../components/core/networkmanager.md) provides some configuration options, one of which is the [tick rate](../../advanced-topics/networktime-ticks.md). The tick rate configuration option determines the frequency at which network ticks occur. The ideal tick rate value relies on balancing smoothness, accuracy, and bandwidth usage. Lowering the tick rate reduces the frequency of NetworkVariable update messages (because they're sent at each tick). However, since it reduces the frequency of updates, it also reduces the smoothness of gameplay for the clients. You can reduce the impact of lower tick rates by using interpolation to provide smoothness, such as in the NetworkTransform. However, because there are fewer updates, the interpolation will be less accurate because it has less information. diff --git a/com.unity.netcode.gameobjects/Documentation~/scene-management.md b/com.unity.netcode.gameobjects/Documentation~/scene-management.md index e2c9772d2d..2c6cae5e08 100644 --- a/com.unity.netcode.gameobjects/Documentation~/scene-management.md +++ b/com.unity.netcode.gameobjects/Documentation~/scene-management.md @@ -6,4 +6,4 @@ Understand scene management in Netcode for GameObjects. | :------------------------------ | :------------------------------- | | **[Scene management overview](basics/scenemanagement/scene-management-overview.md)** | Introduction to scene management in Netcode for GameObjects. | | **[Integrated management](integrated-management.md)** | The Netcode for GameObjects scene management solution is enabled by default and provides you with a fully functional netcode scene management solution. | -| **[Custom management](basics/scenemanagement/custom-management.md)** | If your project has needs that go beyond the scope of the Netcode integrated scene management solution, you can disable scene management in the Editor through `NetworkManager`'s properties. | \ No newline at end of file +| **[Custom management](basics/scenemanagement/custom-management.md)** | If your project has needs that go beyond the scope of the Netcode integrated scene management solution, you can disable scene management in the Editor through NetworkManager's properties. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/serialization.md b/com.unity.netcode.gameobjects/Documentation~/serialization.md index 370183ef56..fb39fad334 100644 --- a/com.unity.netcode.gameobjects/Documentation~/serialization.md +++ b/com.unity.netcode.gameobjects/Documentation~/serialization.md @@ -10,5 +10,5 @@ Netcode for GameObjects has built-in serialization support for C# and Unity prim | **[Arrays](advanced-topics/serialization/serialization-arrays.md)** | Netcode for GameObjects has built-in serialization code for arrays of [C# value-type primitives](advanced-topics/serialization/cprimitives.md), like `int[]`, and [Unity primitive types](advanced-topics/serialization/unity-primitives.md). Any arrays of types that aren't handled by the built-in serialization code, such as `string[]`, need to be handled using a container class or structure that implements the [`INetworkSerializable`](advanced-topics/serialization/inetworkserializable.md) interface. | | **[INetworkSerializable](advanced-topics/serialization/inetworkserializable.md)** | You can use the `INetworkSerializable` interface to define custom serializable types. | | **[Custom serialization](advanced-topics/custom-serialization.md)** | Create custom serialization types. | -| **[NetworkObject serialization](advanced-topics/serialization/networkobject-serialization.md)** | `GameObjects`, `NetworkObjects` and `NetworkBehaviour` aren't serializable types so they can't be used in `RPCs` or `NetworkVariables` by default. There are two convenience wrappers which can be used to send a reference to a `NetworkObject` or a `NetworkBehaviour` over RPCs or `NetworkVariables`. | +| **[NetworkObject serialization](advanced-topics/serialization/networkobject-serialization.md)** | `GameObjects`, `NetworkObjects` and NetworkBehaviour aren't serializable types so they can't be used in `RPCs` or `NetworkVariables` by default. There are two convenience wrappers which can be used to send a reference to a NetworkObject or a NetworkBehaviour over RPCs or `NetworkVariables`. | | **[FastBufferWriter and FastBufferReader](advanced-topics/fastbufferwriter-fastbufferreader.md)** | The serialization and deserialization is done via `FastBufferWriter` and `FastBufferReader`. These have methods for serializing individual types and methods for serializing packed numbers, but in particular provide a high-performance method called `WriteValue()/ReadValue()` (for Writers and Readers, respectively) that can extremely quickly write an entire unmanaged struct to a buffer. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md b/com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md index fda545daff..f06ba35154 100644 --- a/com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md +++ b/com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md @@ -6,5 +6,5 @@ Spawn and despawn objects in your project. | :------------------------------ | :------------------------------- | | **[Object spawning](basics/object-spawning.md)** | Spawning in Netcode for GameObjects means to instantiate and/or spawn the object that is synchronized between all clients by the server. | | **[Object pooling](advanced-topics/object-pooling.md)** | Netcode for GameObjects provides built-in support for Object Pooling, which allows you to override the default Netcode destroy and spawn handlers with your own logic. | -| **[Object visibility](basics/object-visibility.md)** | Object (NetworkObject) visibility is a Netcode for GameObjects term used to describe whether a `NetworkObject` is visible to one or more clients as it pertains to a netcode/network perspective. | +| **[Object visibility](basics/object-visibility.md)** | Object (NetworkObject) visibility is a Netcode for GameObjects term used to describe whether a NetworkObject is visible to one or more clients as it pertains to a netcode/network perspective. | | **[Spawning synchronization](basics/spawning-synchronization.md)** | Ensuring that objects spawn in a synchronized manner across clients can be difficult, although the challenges differ depending on which [network topology](terms-concepts/network-topologies.md) you're using. | \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/troubleshooting/error-messages.md b/com.unity.netcode.gameobjects/Documentation~/troubleshooting/error-messages.md index 461da3a2a5..ca40666460 100644 --- a/com.unity.netcode.gameobjects/Documentation~/troubleshooting/error-messages.md +++ b/com.unity.netcode.gameobjects/Documentation~/troubleshooting/error-messages.md @@ -15,7 +15,7 @@ Error messages are captured and returned through Unity Editor Diagnostics (requi * `Can't find pending soft sync object. Is the projects the same? UnityEngine.Debug:LogError(Object)` * `ArgumentNullException: Can't spawn null object Parameter name: netObject` -This exception should only occur if your scenes aren't the same, for example if the scene of your server has a `NetworkObject` which isn't present in the client scene. Verify the scene objects work correctly by entering playmode in both editors. +This exception should only occur if your scenes aren't the same, for example if the scene of your server has a NetworkObject which isn't present in the client scene. Verify the scene objects work correctly by entering playmode in both editors. ## ServerRPC errors @@ -25,4 +25,4 @@ This exception should only occur if your scenes aren't the same, for example if The ServerRPC should only be used on the server. Make sure to add `isServer` check before calling. -If the call is added correctly but still returns a `nullreferenceexception`, `NetworkManager.Singleton` may be returning `null`. Verify you created the `GameObject` with a `NetworkManager` component, which handles all initialization. `NetworkManager.Singleton` is a reference to a instance of the `NetworkManager` component. +If the call is added correctly but still returns a `nullreferenceexception`, `NetworkManager.Singleton` may be returning `null`. Verify you created the GameObject with a NetworkManager component, which handles all initialization. `NetworkManager.Singleton` is a reference to a instance of the NetworkManager component. diff --git a/com.unity.netcode.gameobjects/Documentation~/troubleshooting/troubleshooting.md b/com.unity.netcode.gameobjects/Documentation~/troubleshooting/troubleshooting.md index 6704a33d63..e78de950fb 100644 --- a/com.unity.netcode.gameobjects/Documentation~/troubleshooting/troubleshooting.md +++ b/com.unity.netcode.gameobjects/Documentation~/troubleshooting/troubleshooting.md @@ -18,7 +18,7 @@ The following exception is thrown: NullReferenceException: Object reference not set to an instance of an object ``` -**Solution:** You most likely forgot to add the `NetworkManager` component to a game object in your scene. +**Solution:** You most likely forgot to add the NetworkManager component to a game object in your scene. ## NullReferenceException when trying to send an RPC to the server @@ -28,7 +28,7 @@ NullReferenceException: Object reference not set to an instance of an object NullReferenceException: Object reference not set to an instance of an object ``` -**Solution:** You most likely forgot to `Spawn()` your object. Run `Spawn()` on your `NetworkObject` component as the server to fix this issue. +**Solution:** You most likely forgot to `Spawn()` your object. Run `Spawn()` on your NetworkObject component as the server to fix this issue. ## Server build is using 100% CPU diff --git a/com.unity.netcode.gameobjects/Documentation~/tutorials/get-started-with-ngo.md b/com.unity.netcode.gameobjects/Documentation~/tutorials/get-started-with-ngo.md index d4b8e87da4..90be9d8f14 100644 --- a/com.unity.netcode.gameobjects/Documentation~/tutorials/get-started-with-ngo.md +++ b/com.unity.netcode.gameobjects/Documentation~/tutorials/get-started-with-ngo.md @@ -68,7 +68,7 @@ First, create the NetworkManager component: ### Create an object to spawn for each connected player > [!NOTE] -> When you drop the prefab into the **PlayerPrefab** slot, you're telling the library that when a client connects to the game, it automatically spawns this prefab as the character for the connecting client. Netcode for GameObjects won't spawn a player object if you don't have any prefab set as the **PlayerPrefab**. Refer to [Player Objects](../components/foundational/networkobject.md#finding-playerobjects). +> When you drop the prefab into the **PlayerPrefab** slot, you're telling the library that when a client connects to the game, it automatically spawns this prefab as the character for the connecting client. Netcode for GameObjects won't spawn a player object if you don't have any prefab set as the **PlayerPrefab**. Refer to [Player Objects](../components/core/networkobject.md#finding-playerobjects). This section guides you through creating an object that spawns for each connected player. @@ -101,7 +101,7 @@ This section guides you through creating an object that spawns for each connecte ### Scene management and the scenes in build list -Netcode for GameObjects comes with an integrated scene management solution that helps you synchronize what scenes should be loaded by all connected clients. The `NetworkManager` **Enable Scene Management** property, enabled by default, determines whether the integrated scene management solution will be used for your project (or not). In order for the integrated scene management solution to work properly, you must add any scene you want to be synchronized to the scenes in build list. This section guides you through adding your current scene to the scenes in build list. +Netcode for GameObjects comes with an integrated scene management solution that helps you synchronize what scenes should be loaded by all connected clients. The NetworkManager **Enable Scene Management** property, enabled by default, determines whether the integrated scene management solution will be used for your project (or not). In order for the integrated scene management solution to work properly, you must add any scene you want to be synchronized to the scenes in build list. This section guides you through adding your current scene to the scenes in build list. 1. Open the Build Settings window by selecting **File** > **Build Settings**. 2. Select **Add Open Scenes**. @@ -109,7 +109,7 @@ Netcode for GameObjects comes with an integrated scene management solution that ## Test starting a host in the Unity Editor -Now that you have a **NetworkManager**, assigned a **PlayerPrefab**, and added your current scene to the scenes in build test, you can quickly verify everything is functioning/configured correctly via entering play mode in the Unity Editor. By starting a host, you are starting `NetworkManager` as both a server and a client at the same time. +Now that you have a **NetworkManager**, assigned a **PlayerPrefab**, and added your current scene to the scenes in build test, you can quickly verify everything is functioning/configured correctly via entering play mode in the Unity Editor. By starting a host, you are starting NetworkManager as both a server and a client at the same time. You can test your Hello World project using the Unity Editor or a command-line helper. If you choose the latter, refer to [Create a command line helper](../tutorials/command-line-helper/). Otherwise, refer to the following instructions to test using the Unity Editor. Only the Plane appears on the server until the first client connects. Then, Netcode for GameObjects spawns a new Player prefab for each connected client; however, they overlap in the Game view. @@ -130,10 +130,10 @@ If it works correctly, the option to **Stop Host** displays in the **Inspector** ### The `HelloWorldManager.cs` script -Now that you have verified everything is configured correctly, you will want to have the ability to start the `NetworkManager` whether in play mode, as a stand alone build, or in another MPPM instance. This section will walk you through creating the `HelloWorldManager.cs` component script. +Now that you have verified everything is configured correctly, you will want to have the ability to start the NetworkManager whether in play mode, as a stand alone build, or in another MPPM instance. This section will walk you through creating the `HelloWorldManager.cs` component script. 1. Create a new script in the `Scripts` folder named `HelloWorldManager.cs`. -2. Add this component to the `NetworkManager` `GameObject` in your scene. +2. Add this component to the NetworkManager GameObject in your scene. 3. Copy the following code into the `HelloWorldManager.cs` script: ```csharp @@ -206,7 +206,7 @@ namespace HelloWorld } ``` -In your Hello World project, you created a NetworkManager by adding the pre-created NetworkManager component to a `GameObject`. This component allows you to start a Host, Client, or Server in Play Mode via the inspector view. The `HelloWorldManager.cs` script simplifies and extends this functionality by creating a runtime/play mode UI menu that allows you to select the three different `NetworkManager` modes you can start: +In your Hello World project, you created a NetworkManager by adding the pre-created NetworkManager component to a GameObject. This component allows you to start a Host, Client, or Server in Play Mode via the inspector view. The `HelloWorldManager.cs` script simplifies and extends this functionality by creating a runtime/play mode UI menu that allows you to select the three different NetworkManager modes you can start: - The **Host** starts the server and joins as a client. - The **Client** joins the server as a client player. @@ -327,7 +327,7 @@ Client Received the RPC #3 on NetworkObject #1 ... ``` -Only the client owning the `NetworkObject` owning the `RpcTest` script will send RPCs on the server, but they will all receive RPCs from the server. This means that if you test with multiple clients the consoles will log RPCs received once per `NetworkObject` per iteration on the server and all clients. If testing with a host and a client, you will see the following on the host's **Console**. This is because as a server it will receive the other client's server RPCs and as a client it will also receive its own client RPCs. +Only the client owning the NetworkObject owning the `RpcTest` script will send RPCs on the server, but they will all receive RPCs from the server. This means that if you test with multiple clients the consoles will log RPCs received once per NetworkObject per iteration on the server and all clients. If testing with a host and a client, you will see the following on the host's **Console**. This is because as a server it will receive the other client's server RPCs and as a client it will also receive its own client RPCs. ```log Server Received the RPC #0 on NetworkObject #2 @@ -399,7 +399,7 @@ namespace HelloWorld The `HelloWorldPlayer.cs` script adds some basic movement to the Hello World project player. Both the server player and the client player can start player movement. However, the movement occurs through the server's position NetworkVariable, which means the server player can move immediately, but the client player must request a movement from the server, wait for the server to update the position NetworkVariable, then replicate the change locally. -The `HelloWorldPlayer` class inherits from `Unity.Netcode`'s `NetworkBehaviour` instead of `MonoBehaviour`. This allows you to customize the networking code as you override what happens when the Player spawns. +The `HelloWorldPlayer` class inherits from `Unity.Netcode`'s NetworkBehaviour instead of MonoBehaviour. This allows you to customize the networking code as you override what happens when the Player spawns. ```csharp public class HelloWorldPlayer : NetworkBehaviour @@ -417,7 +417,7 @@ For multiplayer games, every object runs on at least two machines: player one an } ``` -Any `MonoBehaviour` implementing a NetworkBehaviour component can override the Netcode for GameObjects method `OnNetworkSpawn()`. The `OnNetworkSpawn()` method fires in response to the `NetworkObject` spawning. The `HelloWorldPlayer` class overrides `OnNetworkSpawn` because clients and the server run different logic. You can override this behavior on any NetworkBehaviour component. +Any MonoBehaviour implementing a NetworkBehaviour component can override the Netcode for GameObjects method `OnNetworkSpawn()`. The `OnNetworkSpawn()` method fires in response to the NetworkObject spawning. The `HelloWorldPlayer` class overrides `OnNetworkSpawn` because clients and the server run different logic. You can override this behavior on any NetworkBehaviour component. Because the server and client can be the same machine and the Player's owner (aka Host), you want further to differentiate the two and have different Move behavior for each. @@ -517,7 +517,7 @@ Add the `HelloWorldPlayer.cs` script to the Player prefab as a component: ## Add a NetworkTransform -This section guides you through adding a `NetworkTransform` component that moves the player. `NetworkTransform` is a component used to synchronize the position, rotation, and scale of objects across the network. +This section guides you through adding a NetworkTransform component that moves the player. NetworkTransform is a component used to synchronize the position, rotation, and scale of objects across the network. Add a NetworkTransform component to the Player prefab: diff --git a/com.unity.netcode.gameobjects/Documentation~/tutorials/helloworld.md b/com.unity.netcode.gameobjects/Documentation~/tutorials/helloworld.md index 1fb4582658..e9a162f184 100644 --- a/com.unity.netcode.gameobjects/Documentation~/tutorials/helloworld.md +++ b/com.unity.netcode.gameobjects/Documentation~/tutorials/helloworld.md @@ -22,16 +22,16 @@ In this section we will create the basic building blocks of a multiplayer game. ### Creating Network Manager and selecting the Transport -In this section we add a Network Manager and add Unity Transport to our project. The [NetworkManager](../components/foundational/networkmanager.md) is the component that has all your project's netcode-related settings. Unity Transport is the transport layer that Netcode uses for communication between the server and the clients. See [here](../advanced-topics/transports.md) for more. +In this section we add a Network Manager and add Unity Transport to our project. The [NetworkManager](../components/core/networkmanager.md) is the component that has all your project's netcode-related settings. Unity Transport is the transport layer that Netcode uses for communication between the server and the clients. See [here](../advanced-topics/transports.md) for more. 1. Right-click in the **Hierarchy** tab of the main Unity Window. 1. Select **Create Empty**. -1. Rename the `GameObject` **NetworkManager**. +1. Rename the GameObject **NetworkManager**. 2. Select **NetworkManager**. 3. Click **Add Component** in the Inspector Tab. 4. Select **Netcode** from the list shown. -5. Select `NetworkManager` Component from the list displayed. -6. Inside the `NetworkManager` component tab, locate the `NetworkTransport` field. +5. Select NetworkManager Component from the list displayed. +6. Inside the NetworkManager component tab, locate the `NetworkTransport` field. 7. Click "Select Transport". 8. Select `UnityTransport`. 9. Save your scene. @@ -42,18 +42,18 @@ This section adds in a player object and spawns it for each connected player. 1. Right-click in the **Hierarchy** tab of the Unity Window to create a **3D Object > Capsule** 1. Rename it **Player**. -1. While **Player** is selected, add a **Netcode** > `NetworkObject` component in the Inspector Tab. +1. While **Player** is selected, add a **Netcode** > NetworkObject component in the Inspector Tab. 1. Click the **Assets** folder under the **Project** tab. 2. Right-click inside the **Assets** folder to **Create** > **Folder** and call it **Prefabs**. 3. Make **Player** a Prefab by dragging it to **Prefabs** folder you just created. 4. Delete **Player** from scene. > [!NOTE] -> We remove the **Player** object from the scene because we assign this network Prefab to the `Player Prefab` property in the `NetworkManager` component. The library does not support defining a player object as an in-scene placed `NetworkObject`. +> We remove the **Player** object from the scene because we assign this network Prefab to the `Player Prefab` property in the NetworkManager component. The library does not support defining a player object as an in-scene placed NetworkObject. -5. Select `NetworkManager`. -6. Inside the `NetworkManager` component tab, locate the `Player Prefab` field. +5. Select NetworkManager. +6. Inside the NetworkManager component tab, locate the `Player Prefab` field. 7. Drag this player Prefab from above into this field. > [!NOTE] @@ -77,11 +77,11 @@ This command line helper will allow us to launch builds with a command line argu 1. Right-click the **Assets** folder and create a new folder by hovering over **Create** and selecting **Folder**. Name it **Scripts**. 2. Create a script called `NetworkCommandLine` by right-clicking on your **Scripts** folder, hovering over **Create** and selecting **C# Script**. -3. In the **Hierarchy** menu, right-click on the `NetworkManager` and choose **Create Empty**. +3. In the **Hierarchy** menu, right-click on the NetworkManager and choose **Create Empty**. - This will create an empty `GameObject` with `NetworkManager` as its parent. + This will create an empty GameObject with NetworkManager as its parent. -4. Rename this child `GameObject` `NetworkCommandLine`. +4. Rename this child GameObject `NetworkCommandLine`. 5. With the new `NetworkCommandLine` object selected, click **Add Component** from the **Inspector** tab. 6. Select **Scripts** from the drop-down and click on the `NetworkCommandLine.cs` script you created earlier. 7. Open the `NetworkCommandLine.cs` script by double-clicking from the **Project** tab > **Assets** > **Scripts**. It will open in your text editor diff --git a/com.unity.netcode.gameobjects/Documentation~/tutorials/testing/testing_with_artificial_conditions.md b/com.unity.netcode.gameobjects/Documentation~/tutorials/testing/testing_with_artificial_conditions.md index d5972ebe87..f3c0646638 100644 --- a/com.unity.netcode.gameobjects/Documentation~/tutorials/testing/testing_with_artificial_conditions.md +++ b/com.unity.netcode.gameobjects/Documentation~/tutorials/testing/testing_with_artificial_conditions.md @@ -78,7 +78,7 @@ To combine the benefits of Simulator Tools Window with ParrelSync - create or op Debug builds do allow for the possibility of applying artificial network conditions to the Unity Transport driver, but the Simulator Tools window itself only sets these values in the Editor. -To set the latency, jitter and packet-loss percentage values for develop builds we need the following code to execute before `NetworkManager` attempts to connect (changing the values of the parameters as desired): +To set the latency, jitter and packet-loss percentage values for develop builds we need the following code to execute before NetworkManager attempts to connect (changing the values of the parameters as desired): ``` #if DEVELOPMENT_BUILD && !UNITY_EDITOR diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index a95c9ec1d6..894aaf76d5 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -109,7 +109,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) if (ImportReferences(mainModule, compiledAssembly.Defines)) { - // process `NetworkBehaviour` types + // process NetworkBehaviour types try { mainModule.GetTypes() diff --git a/com.unity.netcode.gameobjects/Samples~/Bootstrap/Scripts/BootstrapPlayer.cs b/com.unity.netcode.gameobjects/Samples~/Bootstrap/Scripts/BootstrapPlayer.cs index af68e5037a..4e4cc97995 100644 --- a/com.unity.netcode.gameobjects/Samples~/Bootstrap/Scripts/BootstrapPlayer.cs +++ b/com.unity.netcode.gameobjects/Samples~/Bootstrap/Scripts/BootstrapPlayer.cs @@ -3,7 +3,7 @@ namespace Unity.Netcode.Samples { /// <summary> - /// Component attached to the "Player Prefab" on the `NetworkManager`. + /// Component attached to the "Player Prefab" on the NetworkManager. /// </summary> public class BootstrapPlayer : NetworkBehaviour { @@ -12,8 +12,8 @@ public class BootstrapPlayer : NetworkBehaviour /// If this method is invoked on the server instance of this player, it will teleport player to a random position. /// </summary> /// <remarks> - /// Since a `NetworkTransform` component is attached to this player, and the authority on that component is set to "Server", - /// this transform's position modification can only be performed on the server, where it will then be replicated down to all clients through `NetworkTransform`. + /// Since a NetworkTransform component is attached to this player, and the authority on that component is set to "Server", + /// this transform's position modification can only be performed on the server, where it will then be replicated down to all clients through NetworkTransform. /// </remarks> [ServerRpc] public void RandomTeleportServerRpc() From dd7d4c91a846d5a1f5de3028f06d22280b0d7fcb Mon Sep 17 00:00:00 2001 From: Noel Stephens <noel.stephens@unity3d.com> Date: Wed, 6 Aug 2025 16:09:45 -0500 Subject: [PATCH 39/43] Update AttachableBehaviour.cs Applying modifications suggested by Emma. --- .../Components/Helpers/AttachableBehaviour.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 2a8d968dfc..6907f2a1bc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -499,18 +499,16 @@ public void Detach() if (!m_AttachableNode) { NetworkLog.LogError($"[{name}][Detach] Invalid state detected! {name}'s state is still {m_AttachState} but has no {nameof(AttachableNode)} assigned!"); + // Developer only notification for the most likely scenario where this method is invoked but the instance is not attached to anything. + if (NetworkManager && NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"[{name}][Detach] Cannot detach! {name} is not attached to anything!"); + } } - - // Developer only notification for the most likely scenario where this method is invoked but the instance is not attached to anything. - if (!m_AttachableNode && NetworkManager && NetworkManager.LogLevel <= LogLevel.Developer) - { - NetworkLog.LogWarning($"[{name}][Detach] Cannot detach! {name} is not attached to anything!"); - } - - // If we have the attachable node set and we are not in the middle of detaching, then log an error and note - // this could potentially occur if inoked more than once for the same instance in the same frame. - if (m_AttachableNode) + else { + // If we have the attachable node set and we are not in the middle of detaching, then log an error and note + // this could potentially occur if inoked more than once for the same instance in the same frame. NetworkLog.LogError($"[{name}][Detach] Invalid state detected! {name} is still referencing {nameof(AttachableNode)} {m_AttachableNode.name}! Could {nameof(Detach)} be getting invoked more than once for the same instance?"); } return; From 44c5ee79c59231e08b48daaccfea80dbe51e4a68 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Wed, 6 Aug 2025 16:50:16 -0500 Subject: [PATCH 40/43] style Remove whitespace --- .../Runtime/Components/Helpers/AttachableBehaviour.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 6907f2a1bc..d2cde01968 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -41,7 +41,6 @@ internal class ComponentControllerEntry #if UNITY_EDITOR - internal void OnValidate() { if (!HasInitialized) @@ -503,7 +502,7 @@ public void Detach() if (NetworkManager && NetworkManager.LogLevel <= LogLevel.Developer) { NetworkLog.LogWarning($"[{name}][Detach] Cannot detach! {name} is not attached to anything!"); - } + } } else { From 9122d3bf500749b0a772950d955e087ec4eee080 Mon Sep 17 00:00:00 2001 From: Noel Stephens <noel.stephens@unity3d.com> Date: Thu, 7 Aug 2025 09:40:43 -0500 Subject: [PATCH 41/43] Update com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs Co-authored-by: Emma <emma.mcmillan@unity3d.com> --- .../Runtime/Components/Helpers/ComponentController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index 8a99007012..d3d6e678b9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -178,7 +178,7 @@ internal PendingStateUpdate(ComponentEntry componentControllerEntry, bool isEnab /// <summary> /// Determines whether the selected <see cref="Components"/>s will start enabled or disabled when spawned. /// </summary> - [Tooltip("The initial state of the component controllers enabled status when instnatiated.")] + [Tooltip("The initial state of the component controllers enabled status when instantiated.")] public bool StartEnabled = true; /// <summary> From aa8b2f4278c0127b8461d1fc936c968fb588acef Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Thu, 7 Aug 2025 14:50:52 -0500 Subject: [PATCH 42/43] update Modifications from 1:1 PR review session with Emma. Removed some redundant logic. Re-organized a few areas for better readability. Renamed the `AttachableBehaviour.AttachableNode` to `AttachableBehaviour.InternalAttachableNode`. Removed the authority check within `AttachableNode.OnNetworkPreDespawn`. --- .../Components/Helpers/AttachableBehaviour.cs | 35 ++++++++++--------- .../Components/Helpers/AttachableNode.cs | 4 +-- .../Tests/Runtime/AttachableBehaviourTests.cs | 6 ++-- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index d2cde01968..ef99a265fa 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -198,7 +198,7 @@ public enum AttachState /// If attached, attaching, or detaching this will be the <see cref="AttachableNode"/> this <see cref="AttachableBehaviour"/> instance is attached to. /// </summary> protected AttachableNode m_AttachableNode { get; private set; } - internal AttachableNode AttachableNode => m_AttachableNode; + internal AttachableNode InternalAttachableNode => m_AttachableNode; private NetworkBehaviourReference m_AttachedNodeReference = new NetworkBehaviourReference(null); private Vector3 m_OriginalLocalPosition; @@ -287,36 +287,37 @@ public override void OnNetworkPreDespawn() private void UpdateAttachedState() { var attachableNode = (AttachableNode)null; - var isAttaching = m_AttachedNodeReference.TryGet(out attachableNode, NetworkManager); - var preState = isAttaching ? AttachState.Attaching : AttachState.Detaching; + var referenceHasNode = m_AttachedNodeReference.TryGet(out attachableNode, NetworkManager); + ///////////////////////////////////////////////////////////// // Exit early if we are already in the correct attached state and the incoming // AttachableNode reference is the same as the local AttachableNode property. if (attachableNode == m_AttachableNode && - ((isAttaching && m_AttachState == AttachState.Attached) || - (!isAttaching && m_AttachState == AttachState.Detached))) + ((referenceHasNode && m_AttachState == AttachState.Attached) || + (!referenceHasNode && m_AttachState == AttachState.Detached))) { return; } - var preNode = isAttaching ? attachableNode : m_AttachableNode; - isAttaching = isAttaching && attachableNode != null; + // If we are in an attaching state but the node is null then we are still not attaching. + var isAttaching = referenceHasNode && attachableNode != null; + // If we are attached to some other AttachableNode, then detach from that before attaching to a new one. if (isAttaching && m_AttachableNode != null && m_AttachState == AttachState.Attached) { - // If we are attached to some other AttachableNode, then detach from that before attaching to a new one. - if (m_AttachableNode != attachableNode) - { - // Run through the same process without being triggerd by a NetVar update. - UpdateAttachState(AttachState.Detaching, m_AttachableNode); - InternalDetach(); - UpdateAttachState(AttachState.Detached, m_AttachableNode); + // Run through the same process without being triggerd by a NetVar update. + UpdateAttachState(AttachState.Detaching, m_AttachableNode); + InternalDetach(); + UpdateAttachState(AttachState.Detached, m_AttachableNode); - m_AttachableNode.Detach(this); - m_AttachableNode = null; - } + m_AttachableNode.Detach(this); + m_AttachableNode = null; } + // Used for attaching or detatching notifications + var preNode = referenceHasNode ? attachableNode : m_AttachableNode; + var preState = referenceHasNode ? AttachState.Attaching : AttachState.Detaching; + // Change the state to attaching or detaching UpdateAttachState(preState, preNode); diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs index d768b17fcf..d1a2ca9c16 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs @@ -51,7 +51,7 @@ protected override void OnOwnershipChanged(ulong previous, ulong current) var attachables = NetworkObject.transform.GetComponentsInChildren<AttachableBehaviour>(); foreach (var attachable in attachables) { - if (attachable.AttachableNode == this) + if (attachable.InternalAttachableNode == this) { m_AttachedBehaviours.Add(attachable); } @@ -67,7 +67,7 @@ protected override void OnOwnershipChanged(ulong previous, ulong current) /// </remarks> public override void OnNetworkPreDespawn() { - if (IsSpawned && HasAuthority && DetachOnDespawn) + if (IsSpawned && DetachOnDespawn) { for (int i = m_AttachedBehaviours.Count - 1; i >= 0; i--) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs index 1d3d3778cf..4616525896 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs @@ -387,10 +387,10 @@ private bool AllInstancesDetached() m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Did not have its event invoked!"); } - if (attachable.AttachableNode != null) + if (attachable.InternalAttachableNode != null) { - var nodeHasAttachments = attachable.AttachableNode.HasAttachments ? $" {attachable.AttachableNode.name} still has attachments!" : ""; - m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Still refers to {attachable.AttachableNode.name}!{nodeHasAttachments}"); + var nodeHasAttachments = attachable.InternalAttachableNode.HasAttachments ? $" {attachable.InternalAttachableNode.name} still has attachments!" : ""; + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Still refers to {attachable.InternalAttachableNode.name}!{nodeHasAttachments}"); } } return m_ErrorLog.Length == 0; From 48bc4c049e300eda29c69897a19e06847974bac6 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <noel.stephens@unity3d.com> Date: Thu, 7 Aug 2025 16:14:27 -0500 Subject: [PATCH 43/43] update Final suggested modifications from 1:1 with Emma during PR review. --- .../Components/Helpers/AttachableBehaviour.cs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index ef99a265fa..0d1a85d516 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; #if UNITY_EDITOR using UnityEditor; @@ -261,7 +262,7 @@ internal void ForceDetach() InternalDetach(); // Notify of the changed attached state - UpdateAttachState(m_AttachState, m_AttachableNode); + NotifyAttachedStateChanged(m_AttachState, m_AttachableNode); m_AttachedNodeReference = new NetworkBehaviourReference(null); @@ -284,42 +285,39 @@ public override void OnNetworkPreDespawn() base.OnNetworkDespawn(); } + /// <summary> + /// This will apply the final attach or detatch state based on the current value of <see cref="m_AttachedNodeReference"/>. + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UpdateAttachedState() { - var attachableNode = (AttachableNode)null; - var referenceHasNode = m_AttachedNodeReference.TryGet(out attachableNode, NetworkManager); + // Process the NetworkBehaviourReference to get the new AttachableNode or null. + // If null, then isAttaching will always be false. + var isAttaching = m_AttachedNodeReference.TryGet(out AttachableNode attachableNode, NetworkManager); - ///////////////////////////////////////////////////////////// // Exit early if we are already in the correct attached state and the incoming // AttachableNode reference is the same as the local AttachableNode property. if (attachableNode == m_AttachableNode && - ((referenceHasNode && m_AttachState == AttachState.Attached) || - (!referenceHasNode && m_AttachState == AttachState.Detached))) + ((isAttaching && m_AttachState == AttachState.Attached) || + (!isAttaching && m_AttachState == AttachState.Detached))) { return; } - // If we are in an attaching state but the node is null then we are still not attaching. - var isAttaching = referenceHasNode && attachableNode != null; - // If we are attached to some other AttachableNode, then detach from that before attaching to a new one. if (isAttaching && m_AttachableNode != null && m_AttachState == AttachState.Attached) { // Run through the same process without being triggerd by a NetVar update. - UpdateAttachState(AttachState.Detaching, m_AttachableNode); + NotifyAttachedStateChanged(AttachState.Detaching, m_AttachableNode); InternalDetach(); - UpdateAttachState(AttachState.Detached, m_AttachableNode); + NotifyAttachedStateChanged(AttachState.Detached, m_AttachableNode); m_AttachableNode.Detach(this); m_AttachableNode = null; } - // Used for attaching or detatching notifications - var preNode = referenceHasNode ? attachableNode : m_AttachableNode; - var preState = referenceHasNode ? AttachState.Attaching : AttachState.Detaching; - // Change the state to attaching or detaching - UpdateAttachState(preState, preNode); + NotifyAttachedStateChanged(isAttaching ? AttachState.Attaching : AttachState.Detaching, isAttaching ? attachableNode : m_AttachableNode); ForceComponentChange(isAttaching, false); if (isAttaching) @@ -332,7 +330,7 @@ private void UpdateAttachedState() } // Notify of the changed attached state - UpdateAttachState(m_AttachState, m_AttachableNode); + NotifyAttachedStateChanged(m_AttachState, m_AttachableNode); // When detaching, we want to make our final action // the invocation of the AttachableNode's Detach method. @@ -357,7 +355,7 @@ protected virtual void OnAttachStateChanged(AttachState attachState, AttachableN /// <summary> /// Update the attached state. /// </summary> - private void UpdateAttachState(AttachState attachState, AttachableNode attachableNode) + private void NotifyAttachedStateChanged(AttachState attachState, AttachableNode attachableNode) { try { @@ -432,7 +430,7 @@ public void Attach(AttachableNode attachableNode) return; } - if (!HasAuthority) + if (!OnHasAuthority()) { NetworkLog.LogError($"[{name}][Attach][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!"); return;