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 ![image](https://github.com/user-attachments/assets/096953ca-5d7d-40d5-916b-72212575d258) -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` ![image](https://github.com/user-attachments/assets/13c627bd-920d-40c8-8947-69aa37b44ebf) -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) ![image](Images/NetworkManagerBootstrapperProperties.png) -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) ![image](Images/SceneBootstrapLoader.png) -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 0e1bbc0de0..241a6d88b9 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -27,6 +27,10 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added - Added serializer for `Pose` (#3546) +- 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`, a variant of `INetworkPrefabInstanceHandler` that provides access to custom instantiation data directly within the `Instantiate()` method. (#3430) @@ -60,7 +64,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) @@ -89,12 +93,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) @@ -131,23 +135,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) @@ -172,24 +176,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 @@ -200,13 +204,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) @@ -219,7 +223,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 @@ -228,28 +232,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) @@ -265,10 +269,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` was using `NetworkVariableSerialization.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) @@ -276,7 +280,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) @@ -320,17 +324,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 @@ -352,15 +356,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 @@ -374,13 +378,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). @@ -396,13 +400,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) @@ -422,8 +426,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 @@ -436,18 +440,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`, 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 with the callback NetworkVariable.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) @@ -461,7 +465,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) @@ -469,8 +473,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 @@ -503,10 +507,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 @@ -516,7 +520,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) @@ -531,7 +535,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 @@ -543,7 +547,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.Read`, `NetworkVariableSerialization.Write`, `NetworkVariableSerialization.AreEqual`, and `NetworkVariableSerialization.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 @@ -555,9 +559,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) @@ -593,7 +597,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) @@ -649,14 +653,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) @@ -665,25 +669,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) @@ -713,20 +717,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 @@ -736,14 +740,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 @@ -757,7 +761,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) @@ -804,9 +808,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 @@ -827,12 +831,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) @@ -847,8 +851,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) @@ -862,7 +866,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) @@ -870,13 +874,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 @@ -892,7 +896,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`) 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`. (#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 @@ -902,13 +906,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 @@ -1064,8 +1068,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` 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` 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) @@ -1075,12 +1079,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 @@ -1103,13 +1107,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` 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) @@ -1132,8 +1136,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 @@ -1141,22 +1145,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 @@ -1199,21 +1203,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` | @@ -1248,8 +1252,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. @@ -1259,16 +1263,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 f784cc5148..767fe3bad4 100644 --- a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md +++ b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md @@ -19,16 +19,20 @@ * [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) + * [Core components](components/core/corecomponents.md) + * [NetworkObject](components/core/networkobject.md) + * [NetworkObject parenting](advanced-topics/networkobject-parenting.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) * [Ownership race conditions](basics/race-conditions.md) @@ -39,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~/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. 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/core/networkbehaviour-synchronize.md b/com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour-synchronize.md new file mode 100644 index 0000000000..bef5662a53 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/networkbehaviour-synchronize.md @@ -0,0 +1,240 @@ +# 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. + +You can use NetworkBehaviours to synchronize settings before, during, and after spawning NetworkObjects. + +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. + +- 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. +- 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: + +Method | Scope | Use case | Context +---------------------------- | ------------------------ | ------------------------------------------------------ | ------------- +OnNetworkPreSpawn | NetworkObject | Pre-spawn initialization | Client and server +OnNetworkSpawn | NetworkObject | During spawn initialization | Client and server +OnNetworkPostSpawn | NetworkObject | Post-spawn actions | Client and server +OnNetworkSessionSynchronized | All NetworkObjects | New client finished synchronizing | Client-side only +OnInSceneObjectsSpawned | In-scene NetworkObjects | New client finished synchronizing or a scene is loaded | Client and server + +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 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. + - On the server and client side after a scene has been loaded and all newly instantiated in-scene placed NetworkObjects have been spawned. + +### 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. + +`NetworkBehaviour.OnSynchronize` allows you to write and read custom serialized data during the NetworkObject serialization process. + +There are two cases where NetworkObject synchronization occurs: + +- When dynamically spawning a NetworkObject. +- When a client is being synchronized after connection approval + - 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. + +#### Order of operations when dynamically spawning + +The following provides you with an outline of the order of operations that occur during NetworkObject serialization when dynamically spawned. + +Server-side: + +- 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. + - `NetworkVariable` state is serialized. + - `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. + + +Client-side: +- The `CreateObjectMessage` is received. + - GameObject with NetworkObject component is instantiated. + - `NetworkVariable` state is deserialized and applied. + - `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. + +#### 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. + - `NetworkVariable` state is serialized. + - `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 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. + +### 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: + +```csharp +using UnityEngine; +using Unity.Netcode; + +/// +/// Simple RPC driven state that shows one +/// form of NetworkBehaviour.OnSynchronize usage +/// +public class SimpleRpcState : NetworkBehaviour +{ + private bool m_ToggleState; + + /// + /// Late joining clients will be synchronized + /// to the most current m_ToggleState. + /// + protected override void OnSynchronize(ref BufferSerializer serializer) + { + serializer.SerializeValue(ref m_ToggleState); + base.OnSynchronize(ref serializer); + } + + public void ToggleState(bool stateIsSet) + { + m_ToggleState = stateIsSet; + } + + /// + /// Synchronizes connected clients with the + /// server-side m_ToggleState. + /// + /// + [Rpc(SendTo.ClientsAndHost)] + private void ToggleStateClientRpc(bool stateIsSet) + { + m_ToggleState = stateIsSet; + } +} +``` +> [!NOTE] +> `NetworkBehaviour.OnSynchronize` is only invoked on the server side during the write part of serialization and only invoked on the client side during the read part of serialization. When running a host, `NetworkBehaviour.OnSynchronize` is still only invoked once (server-side) during the write part of serialization. + +### Debugging `OnSynchronize` serialization + +If your serialization code has a bug and throws an exception, then `NetworkBehaviour.OnSynchronize` has additional safety checking to handle a graceful recovery without completely breaking the rest of the synchronization serialization pipeline. + +#### 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 = 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. + +#### 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. + +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 NetworkBehaviours + +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(); + } + } + + 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/networkmanager.md b/com.unity.netcode.gameobjects/Documentation~/components/core/networkmanager.md similarity index 79% rename from com.unity.netcode.gameobjects/Documentation~/components/networkmanager.md rename to com.unity.netcode.gameobjects/Documentation~/components/core/networkmanager.md index 74e3720b7a..5b03fccb2d 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/networkmanager.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/networkmanager.md @@ -1,17 +1,17 @@ # 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](../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. @@ -19,22 +19,22 @@ 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:
+## NetworkManager sub-systems +NetworkManager is also where you can find references to other Netcode related management systems:
> [!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. -- [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 -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](../basics/networkobject.md) - - [Connection Approval](../basics/connection-approval) + - [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: @@ -81,16 +81,16 @@ 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 ### 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~/basics/networkobject.md b/com.unity.netcode.gameobjects/Documentation~/components/core/networkobject.md similarity index 84% rename from com.unity.netcode.gameobjects/Documentation~/basics/networkobject.md rename to com.unity.netcode.gameobjects/Documentation~/components/core/networkobject.md index 4cbfb18423..a1f96917ef 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/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](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) + 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 @@ -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. @@ -70,37 +70,37 @@ 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`. ## Spawning with (or without) observers -![image](../images/SpawnWithObservers.png) +![image](../../images/SpawnWithObservers.png) 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 -![image](../images/NetworkObject-TransformSynchronization.png) +![image](../../images/NetworkObject-TransformSynchronization.png) -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 -![image](../images/ActiveSceneMigration.png) +![image](../../images/ActiveSceneMigration.png) 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 -![image](../images/SceneMigrationSynchronization.png) +![image](../../images/SceneMigrationSynchronization.png) 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/core/playerobjects.md similarity index 74% rename from com.unity.netcode.gameobjects/Documentation~/basics/playerobjects.md rename to com.unity.netcode.gameobjects/Documentation~/components/core/playerobjects.md index 9ad3260a70..e7ae88353f 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/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; @@ -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().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~/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. + +![Inspector UI view with AttachableBehaviour component added](../../images/attachable/AttachableBehaviour_InspectorView-1.png) + +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: + +![Tree view of two spawned NetworkPrefabs](../../images/attachable/SpawnObjectA-B.png) + +Then parent one instance under the other: + +![Tree view of two spawned NetworkPrefabs with one parented under the other](../../images/attachable/SpawnObjectA-B-2.png) + +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. + +![Tree view of nested GameObject children](../../images/attachable/PlayerAndWorldItem-1.png) + +- 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. + +![Tree view of attached child GameObject](../../images/attachable/PlayerAndWorldItem-2.png) + +## 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: + +![Hierarchy diagram of Player and WorldItem NetworkPrefabs](../../images/attachable/AttachableDiagram-1.png) + +#### 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 + +![Hierachy diagram of WorldItem NetworkPrefab with arrows indicating relationships between components](../../images/attachable/AttachableDiagram-2.png) + +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: + +![Inspector view UI with details of ComponentController](../../images/attachable/WorldItem-Inspector-View-1.png) + +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: + +![Inspector view UI with details of CarryObject's AttachableBehaviour component](../../images/attachable/WorldItem-Inspector-View-2.png) + +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 + +![Hierarchy diagram of Player and WorldItem NetworkPrefabs when attached](../../images/attachable/AttachableDiagram-3.png) + +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. + +![Inspector view UI with details of AttachableNode component](../../images/attachable/AttachableNode_InspectorView-1.png) + +## 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 + +![Inspector UI view with ComponentController component added](../../images/attachable/WorldItem-Inspector-View-1.png) + +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 +/// +/// Use as a component in the ComponentController that will +/// trigger the Controller (ComponentController). +/// This pattern can repeat/be daisy chained. +/// +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/networkanimator.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md similarity index 52% rename from com.unity.netcode.gameobjects/Documentation~/components/networkanimator.md rename to com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md index 21e0a28c57..62b0162374 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/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: -![ServerAuthMode](../images/NetworkAnimatorServerAuthTiming.png) +![ServerAuthMode](../../images/NetworkAnimatorServerAuthTiming.png) -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. -![ServerAuthMode](../images/NetworkAnimatorOwnerAuthTiming.png) +![ServerAuthMode](../../images/NetworkAnimatorOwnerAuthTiming.png) -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. -![Usage-1](../images/NetworkAnimatorUsage-1.png) +![Usage-1](../../images/NetworkAnimatorUsage-1.png) ### 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: -![Usage-1](../images/NetworkAnimatorUsage-2.png) +![Usage-1](../../images/NetworkAnimatorUsage-2.png) > [!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/networktransform.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/networktransform.md similarity index 92% rename from com.unity.netcode.gameobjects/Documentation~/components/networktransform.md rename to com.unity.netcode.gameobjects/Documentation~/components/helper/networktransform.md index 2bbc224f0e..0ad8366692 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/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](../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](../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,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](../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: -![image](../images/networktransform/SingleGeneration.png) +![image](../../images/networktransform/SingleGeneration.png) Alternatively, the parent GameObject can have multiple children where any child can have a NetworkTransform: -![image](../images/networktransform/MultiGeneration.png) +![image](../../images/networktransform/MultiGeneration.png) 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](../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 @@ -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: -![image](../images/networktransform/NetworkTransformProperties.png) +![image](../../images/networktransform/NetworkTransformProperties.png) ### Property synchronization @@ -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) @@ -77,7 +77,7 @@ The following NetworkTransform properties can cause a full state update when cha ### Axis to Synchronize -![image](../images/networktransform/AxisToSynchronize.png) +![image](../../images/networktransform/AxisToSynchronize.png) 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. @@ -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. @@ -99,9 +99,9 @@ The __Axis to Synchronize__ properties that determine which axes are synchronize ### Authority -![image](../images/networktransform/AuthorityMode.png) +![image](../../images/networktransform/AuthorityMode.png) -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 -![image](../images/networktransform/Thresholds.png) +![image](../../images/networktransform/Thresholds.png) 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 -![image](../images/networktransform/Delivery.png) +![image](../../images/networktransform/Delivery.png) #### Tick Synchronize Children @@ -160,13 +160,13 @@ When unreliable state updates are enabled, NetworkTransform instances are assign ### Configurations -![image](../images/networktransform/Configurations.png) +![image](../../images/networktransform/Configurations.png) #### In Local Space 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. @@ -182,7 +182,7 @@ To resolve this issue, you can enable the __Switch Transform Space When Parented ### Interpolation -![image](../images/networktransform/Interpolation.png) +![image](../../images/networktransform/Interpolation.png) 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 -![image](../images/networktransform/PositionSlerp.png) +![image](../../images/networktransform/PositionSlerp.png) 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: -![image](../images/networktransform/NetworkTransformQuaternionSynch.png) +![image](../../images/networktransform/NetworkTransformQuaternionSynch.png) :::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~/images/attachable/AttachableBehaviour_InspectorView-1.png b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableBehaviour_InspectorView-1.png new file mode 100644 index 0000000000..9868301c12 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableBehaviour_InspectorView-1.png differ 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 0000000000..569fecc721 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-1.png differ 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 0000000000..05a428e82d Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-2.png differ 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 0000000000..6c33acdd63 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableDiagram-3.png differ 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 0000000000..afb060db66 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/AttachableNode_InspectorView-1.png differ 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 0000000000..f425e98ccd Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/ComponentController_InspectorView-1.png differ 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 0000000000..841dde1df9 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/PlayerAndWorldItem-1.png differ 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 0000000000..97208a05d7 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/PlayerAndWorldItem-2.png differ 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 0000000000..e7a2ca6762 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/SpawnObjectA-B-2.png differ 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 0000000000..474da9fd4a Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/SpawnObjectA-B.png differ 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 0000000000..487bc16479 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/WorldItem-Inspector-View-1.png differ 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 0000000000..b16d7c9091 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/attachable/WorldItem-Inspector-View-2.png differ 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 7781c0657e..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/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 6d6c95c9fa..a14cc6d0da 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. -![The server gets to make the final gameplay decisions](../../images/sequence_diagrams/dealing_with_latency/Example_CharPos_ServerAuthority.png) +![The server gets to make the final gameplay decisions](../images/sequence_diagrams/dealing_with_latency/Example_CharPos_ServerAuthority.png) #### 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. -![The client gets to make the final gameplay decisions](../../images/sequence_diagrams/dealing_with_latency/Example_CharPos_ClientAuthority.png) +![The client gets to make the final gameplay decisions](../images/sequence_diagrams/dealing_with_latency/Example_CharPos_ClientAuthority.png) #### Good for reactivity @@ -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. -![Multiple clients trying to impose their reality on a shared object.](../../images/sequence_diagrams/dealing_with_latency/Example_CaptureFlagPart1_ClientAuthorityIssue.png) +![Multiple clients trying to impose their reality on a shared object.](../images/sequence_diagrams/dealing_with_latency/Example_CaptureFlagPart1_ClientAuthorityIssue.png) 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. -![Multiple clients ASKING to interact with a shared object.](../../images/sequence_diagrams/dealing_with_latency/Example_CaptureFlagPart2_ServerAuthorityFix.png) +![Multiple clients ASKING to interact with a shared object.](../images/sequence_diagrams/dealing_with_latency/Example_CaptureFlagPart2_ServerAuthorityFix.png) #### Issue: Security @@ -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/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/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 272f6ea8b3..b4df3986e3 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 +| **[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 bf8981fa11..c690759906 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/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(); diff --git a/com.unity.netcode.gameobjects/Documentation~/samples.md b/com.unity.netcode.gameobjects/Documentation~/samples.md index b6a91e5e7d..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](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/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 f31a3dc137..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/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 c000aeac23..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/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 b6c40567b1..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/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 d0dc30537c..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/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/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/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/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 3510332f7f..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](../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/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 69289e04bf..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/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 019f6e7b84..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 @@ -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: -![nlc-in-system-preferences](../images/nlc-in-system-preferences.png) +![nlc-in-system-preferences](../../images/nlc-in-system-preferences.png) To test the builds with Network Link Conditioner: - Run Network Link Conditioner 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/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: 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..0d1a85d516 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -0,0 +1,564 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +#if UNITY_EDITOR +using UnityEditor; +#endif +using UnityEngine; + +namespace Unity.Netcode.Components +{ + /// + /// Attachable NetworkBehaviours
+ /// This component handles the parenting synchronization of the that this component is attached to.
+ /// under another 's .
+ /// The to be parented must have this component attached to it and must be nested on any child under the 's .
+ /// The target parent must have an component attached to it and must belong to a + /// different than that of the 's. + ///
+ /// + /// The term "attach" is used in place of parenting in order to distinguish between parenting and + /// parenting ("attaching" and "detaching").
+ /// This component can be used along with one or more in order to enable or disable different components depending + /// upon the instance's current state.
+ /// invocation order: + /// - When attaching, the 's is invoked just before the is invoked with the state.
+ /// - When detaching, the 's is invoked right after the is invoked with the notification.
+ ///
+ public class AttachableBehaviour : NetworkBehaviour + { + [Serializable] + internal class ComponentControllerEntry + { + // Ignoring the naming convention in order to auto-assign element names +#pragma warning disable IDE1006 + /// + /// Used for naming each element entry. + /// + [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 + /// + /// + /// In the event an is placed on the same + /// as the , this will automatically create a child and add an + /// to that. + /// + protected virtual void OnValidate() + { + var networkObject = gameObject.GetComponentInParent(); + if (!networkObject) + { + networkObject = gameObject.GetComponent(); + } + 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; + } + if (ComponentControllers == null) + { + return; + } + foreach (var componentController in ComponentControllers) + { + componentController?.OnValidate(); + } + } + + private void CreatedNestedChild() + { + EditorApplication.update -= CreatedNestedChild; + var childGameObject = new GameObject($"{name}-Child"); + childGameObject.transform.parent = transform; + childGameObject.AddComponent(); + Debug.Log($"[{name}][Created Child] Adding {nameof(AttachableBehaviour)} to newly created child {childGameObject.name}."); + DestroyImmediate(this); + } +#endif + /// + /// Flags to determine if the will automatically detach. + /// + [Flags] + public enum AutoDetachTypes + { + /// + /// Disables auto detach. + /// + None, + /// + /// Detach on ownership change. + /// + OnOwnershipChange, + /// + /// Detach on despawn. + /// + OnDespawn, + /// + /// Detach on destroy. + /// + OnAttachNodeDestroy, + } + + /// + /// Determines if this will automatically detach on all instances if it has one of the flags. + /// + public AutoDetachTypes AutoDetach = AutoDetachTypes.OnDespawn | AutoDetachTypes.OnOwnershipChange | AutoDetachTypes.OnAttachNodeDestroy; + + [SerializeField] + internal List ComponentControllers; + + /// + /// Invoked when the of this instance has changed. + /// + public event Action AttachStateChange; + + /// + /// The various states of . + /// + public enum AttachState + { + /// + /// The instance is not attached to anything. + /// When not attached to anything, the instance will be parented under the original + /// . + /// + Detached, + /// + /// The instance is attaching to an . + /// + /// + /// One example usage:
+ /// When using an with one or more component(s), + /// this would be a good time to enable or disable components. + ///
+ Attaching, + /// + /// The instance is attached to an . + /// + /// + /// This would be a good time to apply any additional local position or rotation values to this instance. + /// + Attached, + /// + /// The instance is detaching from an . + /// + /// + /// One example usage:
+ /// When using an with one or more component(s), + /// this would be a good time to enable or disable components. + ///
+ Detaching + } + + /// + /// The current instance's . + /// + protected AttachState m_AttachState { get; private set; } + + /// + /// The original parent of this instance. + /// + protected GameObject m_DefaultParent { get; private set; } + + /// + /// If attached, attaching, or detaching this will be the this instance is attached to. + /// + protected AttachableNode m_AttachableNode { get; private set; } + internal AttachableNode InternalAttachableNode => m_AttachableNode; + + private NetworkBehaviourReference m_AttachedNodeReference = new NetworkBehaviourReference(null); + private Vector3 m_OriginalLocalPosition; + private Quaternion m_OriginalLocalRotation; + + /// + protected override void OnSynchronize(ref BufferSerializer serializer) + { + // Example of how to synchronize late joining clients when using an RPC to update + // a local property's state. + serializer.SerializeValue(ref m_AttachedNodeReference); + base.OnSynchronize(ref serializer); + } + + /// + /// Override this method in place of Awake. This method is invoked during Awake. + /// + /// + /// The 's Awake method is protected to assure it initializes itself at this point in time. + /// + protected virtual void OnAwake() + { + } + + /// + /// If you create a custom and override this method, you must invoke + /// this base instance of . + /// + 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; + OnAwake(); + } + + /// + /// + /// If you create a custom and override this method, you will want to + /// invoke this base instance of if you want the current + /// state to have been applied before executing the derived class's + /// script. + /// + protected override void OnNetworkSessionSynchronized() + { + UpdateAttachedState(); + base.OnNetworkSessionSynchronized(); + } + + internal void ForceDetach() + { + if (m_AttachState == AttachState.Detached || m_AttachState == AttachState.Detaching) + { + return; + } + + ForceComponentChange(false, true); + + InternalDetach(); + // Notify of the changed attached state + NotifyAttachedStateChanged(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) + { + m_AttachableNode.Detach(this); + m_AttachableNode = null; + } + } + + /// + public override void OnNetworkPreDespawn() + { + if (AutoDetach.HasFlag(AutoDetachTypes.OnDespawn)) + { + ForceDetach(); + } + base.OnNetworkDespawn(); + } + + /// + /// This will apply the final attach or detatch state based on the current value of . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void UpdateAttachedState() + { + // 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 && + ((isAttaching && m_AttachState == AttachState.Attached) || + (!isAttaching && m_AttachState == AttachState.Detached))) + { + return; + } + + // 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. + NotifyAttachedStateChanged(AttachState.Detaching, m_AttachableNode); + InternalDetach(); + NotifyAttachedStateChanged(AttachState.Detached, m_AttachableNode); + + m_AttachableNode.Detach(this); + m_AttachableNode = null; + } + + // Change the state to attaching or detaching + NotifyAttachedStateChanged(isAttaching ? AttachState.Attaching : AttachState.Detaching, isAttaching ? attachableNode : m_AttachableNode); + + ForceComponentChange(isAttaching, false); + if (isAttaching) + { + InternalAttach(attachableNode); + } + else + { + InternalDetach(); + } + + // Notify of the changed attached state + NotifyAttachedStateChanged(m_AttachState, m_AttachableNode); + + // When detaching, we want to make our final action + // the invocation of the AttachableNode's Detach method. + if (!isAttaching && m_AttachableNode) + { + m_AttachableNode.Detach(this); + m_AttachableNode = null; + } + } + + /// + /// For customized/derived s, override this method to receive notifications + /// when the has changed. + /// + /// The new . + /// The being attached to or from. Will be null when completely detached. + protected virtual void OnAttachStateChanged(AttachState attachState, AttachableNode attachableNode) + { + + } + + /// + /// Update the attached state. + /// + private void NotifyAttachedStateChanged(AttachState attachState, AttachableNode attachableNode) + { + try + { + AttachStateChange?.Invoke(attachState, attachableNode); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + + try + { + OnAttachStateChanged(attachState, attachableNode); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } + + /// + protected override void OnOwnershipChanged(ulong previous, ulong current) + { + if (AutoDetach.HasFlag(AutoDetachTypes.OnOwnershipChange)) + { + ForceDetach(); + } + 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); + } + } + } + + /// + /// Internal attach method that just handles changing state, parenting, and sending the a + /// notification that an has attached. + /// + internal void InternalAttach(AttachableNode attachableNode) + { + m_AttachState = AttachState.Attached; + m_AttachableNode = attachableNode; + // Attachables are always local space relative + transform.SetParent(m_AttachableNode.transform, false); + m_AttachableNode.Attach(this); + } + + /// + /// Attaches the of this instance to the of the . + /// + /// + /// This effectively applies a new parent to a nested and all children + /// of the nested .
+ /// Both the and this instances should be in the spawned state before this + /// is invoked. + ///
+ /// The to attach this instance to. + public void Attach(AttachableNode attachableNode) + { + if (!IsSpawned) + { + NetworkLog.LogError($"[{name}][Attach][Not Spawned] Cannot attach before being spawned!"); + return; + } + + if (!OnHasAuthority()) + { + 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; + } + + ChangeReference(new NetworkBehaviourReference(attachableNode)); + } + + /// + /// Internal detach method that just handles changing state, parenting, and sending the a + /// notification that an has detached. + /// + internal void InternalDetach() + { + if (m_AttachableNode) + { + 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; + } + } + + /// + /// Invoke to detach from a . + /// + public void Detach() + { + if (!gameObject) + { + return; + } + if (!IsSpawned) + { + NetworkLog.LogError($"[{name}][Detach][Not Spawned] Cannot detach if not spawned!"); + return; + } + + if (!OnHasAuthority()) + { + NetworkLog.LogError($"[{name}][Detach][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!"); + return; + } + + 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) + { + 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!"); + } + } + 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; + } + + ChangeReference(new NetworkBehaviourReference(null)); + } + + /// + /// Override this method to change how the instance determines the authority.
+ /// The default is to use the method. + ///
+ /// + /// Useful when using a network topology and you would like + /// to have the owner be the authority of this instance. + /// + /// true = has authoriy | false = does not have authority + 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, RpcParams rpcParams = default) + { + ChangeReference(attachedNodeReference); + } + + /// + /// Notification that the is being destroyed + /// + internal void OnAttachNodeDestroy() + { + // If this instance should force a detach on destroy + if (AutoDetach.HasFlag(AutoDetachTypes.OnAttachNodeDestroy)) + { + // Force a detach + ForceDetach(); + } + } + } +} 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/AttachableNode.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs new file mode 100644 index 0000000000..d1a2ca9c16 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs @@ -0,0 +1,144 @@ +using System.Collections.Generic; +using Unity.Netcode; +using Unity.Netcode.Components; + + +/// +/// This component is used in conjunction with and is used to +/// denote a specific child that an +/// can attach itself to. +/// +/// +/// Primarily, the can be used as it is or can be extended to perform additional +/// logical operations when something attaches to or detaches from the instance. +/// +public class AttachableNode : NetworkBehaviour +{ + /// + /// Returns true if the instance has one or more attached components. + /// + public bool HasAttachments => m_AttachedBehaviours.Count > 0; + + /// + /// When enabled, any attached s will be automatically detached and re-parented under its original parent. + /// + public bool DetachOnDespawn = true; + + /// + /// A of the currently attached s. + /// + protected readonly List m_AttachedBehaviours = new List(); + + /// + protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) + { + m_AttachedBehaviours.Clear(); + base.OnNetworkPreSpawn(ref networkManager); + } + + /// + /// + /// When the ownership of an changes, it will find all currently attached components + /// that are registered as being attached to this instance. + /// + 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) + { + // Rebuild the list of AttachableBehaviours for the new owner + var attachables = NetworkObject.transform.GetComponentsInChildren(); + foreach (var attachable in attachables) + { + if (attachable.InternalAttachableNode == this) + { + m_AttachedBehaviours.Add(attachable); + } + } + } + base.OnOwnershipChanged(previous, current); + } + + /// + /// + /// If the this belongs to is despawned, + /// then any attached will be detached during . + /// + public override void OnNetworkPreDespawn() + { + if (IsSpawned && DetachOnDespawn) + { + for (int i = m_AttachedBehaviours.Count - 1; i >= 0; i--) + { + 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(); + } + + 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.InternalOnDestroy(); + } + + /// + /// Override this method to be notified when an has attached to this node. + /// + /// The that has been attached. + 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); + } + + /// + /// Override this method to be notified when an has detached from this node. + /// + /// The that has been detached. + 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 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..d3d6e678b9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -0,0 +1,543 @@ +using System; +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 +{ + /// + /// Handles enabling or disabling commonly used components like , , , etc.
+ /// Anything that derives from and has an enabled property can be added to the list of objects.
+ /// NOTE: derived components are not allowed and will be automatically removed. + ///
+ /// + /// This will synchronize the enabled or disabled state of the s with connected and late joining clients.
+ /// - Use to determine the current synchronized enabled state.
+ /// - Use to change the enabled state and have the change applied to all components this is synchronizing.
+ /// It is encouraged to create custom derived versions of this class to provide any additional functionality required for your project specific needs. + ///
+ public class ComponentController : NetworkBehaviour + { + /// + /// This is a serializable contianer class for entries. + /// + [Serializable] + internal class ComponentEntry + { + + // Ignoring the naming convention in order to auto-assign element names +#pragma warning disable IDE1006 + /// + /// Used for naming each element entry. + /// + [HideInInspector] + public string name; +#pragma warning restore IDE1006 + + /// + /// When true, this component's enabled state will be the inverse of the value passed into . + /// + [Tooltip("When enabled, this component will inversely mirror the currently applied ComponentController's enabled state.")] + public bool InvertEnabled; + + /// + /// The amount of time to delay enabling this component when the has just transitioned from a disabled to enabled state. + /// + /// + /// This can be useful under scenarios where you might want to prevent a component from being enabled too early prior to making any adjustments.
+ /// As an example, you might find that delaying the enabling of a until at least the next frame will avoid any single frame + /// rendering anomalies until the has updated the . + ///
+ [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; + + /// + /// The amount of time to delay disabling this component when the has just transitioned from an enabled to disabled state. + /// + /// + /// This can be useful under scenarios where you might want to prevent a component from being disabled too early prior to making any adjustments.
+ ///
+ [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; + + /// + /// The component that will have its enabled property synchronized. + /// + /// + /// You can assign an entire to this property which will add all components attached to the and its children. + /// + [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) + { + return InvertEnabled ? !enabled : enabled; + } + + private List m_PendingStateUpdates = new List(); + + /// + /// Invoke prior to setting the state. + /// + internal bool QueueForDelay(bool enabled) + { + var relativeEnabled = GetRelativeEnabled(enabled); + + if (relativeEnabled ? EnableDelay > 0.0f : DisableDelay > 0.0f) + { + // 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; + } + + 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 HasPendingStateUpdates() + { + for (int i = m_PendingStateUpdates.Count - 1; i >= 0; i--) + { + 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; + + internal ComponentEntry ComponentEntry; + + internal bool CheckTimeDeltaDelay() + { + if (!TimeDeltaDelayInProgress) + { + return false; + } + + var isDeltaDelayInProgress = DelayTimeDelta > Time.realtimeSinceStartup; + + if (!isDeltaDelayInProgress) + { + ComponentEntry.SetValue(PendingState); + } + TimeDeltaDelayInProgress = isDeltaDelayInProgress; + return TimeDeltaDelayInProgress; + } + + internal PendingStateUpdate(ComponentEntry componentControllerEntry, bool isEnabled, float relativeTimeOffset) + { + 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; + } + } + } + /// + /// Determines whether the selected s will start enabled or disabled when spawned. + /// + [Tooltip("The initial state of the component controllers enabled status when instantiated.")] + public bool StartEnabled = true; + + /// + /// The list of s to be enabled and disabled. + /// + [Tooltip("The list of components to control. You can drag and drop an entire GameObject on this to include all components.")] + [SerializeField] + internal List Components; + + /// + /// Returns the current enabled state of the . + /// + public bool EnabledState => m_IsEnabled; + + internal List ValidComponents = new List(); + 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)] + 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(); + return $"{component.name} ({classNameDisplay})"; + } + + /// + /// + /// Checks for invalid entries. + /// + protected virtual void OnValidate() + { + if (Components == null || Components.Count == 0) + { + return; + } + + var gameObjectsToScan = new List(); + // 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) + { + continue; + } + + if (Components[i].Component == null) + { + continue; + } + var objectType = Components[i].Component.GetType(); + if (objectType == typeof(GameObject)) + { + if (!gameObjectsToScan.Contains(Components[i])) + { + gameObjectsToScan.Add(Components[i]); + } + Components.RemoveAt(i); + continue; + } + + if (!IsValidComponentType(Components[i].Component)) + { + Debug.LogWarning($"Removing {GetComponentNameFormatted(Components[i].Component)} since {Components[i].Component.GetType().Name} is not an allowed component type."); + Components.RemoveAt(i); + continue; + } + + var propertyInfo = Components[i].Component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + if (propertyInfo == null || propertyInfo.PropertyType != typeof(bool)) + { + Debug.LogWarning($"{Components[i].Component.name} does not contain a public enabled property! (Removing)"); + Components.RemoveAt(i); + } + } + + // 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.GetComponentsInChildren(); + foreach (var component in components) + { + // Ignore any NetworkBehaviour derived, NetworkObject, or NetworkManager components + if (!IsValidComponentType(component)) + { + continue; + } + + var propertyInfo = component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) + { + var componentEntry = new ComponentEntry() + { + Component = component, + PropertyInfo = propertyInfo, + }; + Components.Add(componentEntry); + } + } + } + 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) + { + if (!componentEntry.Component) + { + continue; + } + componentEntry.name = GetComponentNameFormatted(componentEntry.Component); + } + } +#endif + + /// + protected override void OnSynchronize(ref BufferSerializer serializer) + { + // Example of how to synchronize late joining clients when using an RPC to update + // a local property's state. + serializer.SerializeValue(ref m_IsEnabled); + base.OnSynchronize(ref serializer); + } + + /// + /// Override this method in place of Awake. This method is invoked during Awake. + /// + /// + /// The 's Awake method is protected to assure it is invoked in the correct order. + /// + protected virtual void OnAwake() + { + } + + private 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) + { + emptyEntries++; + continue; + } + var propertyInfo = entry.Component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public); + if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) + { + entry.PropertyInfo = propertyInfo; + ValidComponents.Add(entry); + } + else + { + NetworkLog.LogWarning($"{name} does not contain a public enable property! (Ignoring)"); + } + } + if (emptyEntries > 0) + { + NetworkLog.LogWarning($"{name} has {emptyEntries} emtpy(null) entries in the {nameof(Components)} list!"); + } + + // Apply the initial state of all components this instance is controlling. + InitializeComponents(); + + try + { + OnAwake(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } + + /// + /// + /// If overriding this method, it is required that you invoke this base method. + /// + public override void OnNetworkSpawn() + { + if (OnHasAuthority()) + { + m_IsEnabled = StartEnabled; + } + base.OnNetworkSpawn(); + } + + /// + /// + /// If overriding this method, it is required that you invoke this base method.
+ /// Assures all instances subscribe to the internal of type + /// that synchronizes all instances when s are enabled + /// or disabled. + ///
+ protected override void OnNetworkPostSpawn() + { + ApplyEnabled(); + base.OnNetworkPostSpawn(); + } + + /// + /// + /// If overriding this method, it is required that you invoke this base method. + /// + public override void OnDestroy() + { + if (m_CoroutineObject.IsRunning) + { + StopCoroutine(m_CoroutineObject.Coroutine); + m_CoroutineObject.IsRunning = false; + } + base.OnDestroy(); + } + + /// + /// Initializes each component entry to its initial state. + /// + private void InitializeComponents() + { + 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 ? !StartEnabled : StartEnabled; + entry.PropertyInfo.SetValue(entry.Component, isEnabled); + } + } + + /// + /// Applies states changes to all components being controlled by this instance. + /// + /// the state update to apply + private void ApplyEnabled(bool ignoreDelays = false) + { + foreach (var entry in ValidComponents) + { + if (!ignoreDelays && entry.QueueForDelay(m_IsEnabled)) + { + if (!m_CoroutineObject.IsRunning) + { + m_CoroutineObject.Coroutine = StartCoroutine(PendingAppliedState()); + m_CoroutineObject.IsRunning = true; + } + } + else + { + entry.SetValue(m_IsEnabled); + } + } + } + + private class CoroutineObject + { + public Coroutine Coroutine; + public bool IsRunning; + } + + private CoroutineObject m_CoroutineObject = new CoroutineObject(); + + + private IEnumerator PendingAppliedState() + { + var continueProcessing = true; + + while (continueProcessing) + { + continueProcessing = false; + foreach (var entry in ValidComponents) + { + if (entry.HasPendingStateUpdates()) + { + continueProcessing = true; + } + } + if (continueProcessing) + { + yield return null; + } + } + m_CoroutineObject.IsRunning = false; + } + + /// + /// Invoke on the authority side to enable or disable components assigned to this instance. + /// + /// + /// If any component entry has the set to true, + /// then the inverse of the isEnabled property passed in will be used. If the component entry has the + /// set to false (default), then the value of the + /// isEnabled property will be applied. + /// + /// true = enabled | false = disabled + public void SetEnabled(bool isEnabled) + { + if (!IsSpawned) + { + Debug.Log($"[{name}] Must be spawned to use {nameof(SetEnabled)}!"); + return; + } + + if (!OnHasAuthority()) + { + Debug.Log($"[Client-{NetworkManager.LocalClientId}] Attempting to invoke {nameof(SetEnabled)} without authority!"); + return; + } + ChangeEnabled(isEnabled); + } + + private void ChangeEnabled(bool isEnabled) + { + m_IsEnabled = isEnabled; + ApplyEnabled(); + + if (OnHasAuthority()) + { + ToggleEnabledRpc(m_IsEnabled); + } + } + + internal void ForceChangeEnabled(bool isEnabled, bool ignoreDelays = false) + { + m_IsEnabled = isEnabled; + ApplyEnabled(ignoreDelays); + } + + /// + /// Override this method to change how the instance determines the authority.
+ /// The default is to use the method. + ///
+ /// + /// Useful when using a network topology and you would like + /// to have the owner be the authority of this instance. + /// + /// true = has authoriy | false = does not have authority + protected virtual bool OnHasAuthority() + { + return HasAuthority; + } + + [Rpc(SendTo.NotMe)] + private void ToggleEnabledRpc(bool enabled, RpcParams rpcParams = default) + { + ChangeEnabled(enabled); + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs.meta new file mode 100644 index 0000000000..e7482640fa --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4d0b25b3f95e5324abc80c09cb29f271 \ No newline at end of file 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 can be viewed as the parent. ///
- /// This is the recommended way, as opposed to parenting, to attached/detatch two rigid bodies to one another when is enabled. + /// This is the recommended way, as opposed to parenting, to attached/detach two rigid bodies to one another when is enabled. /// For more details on using and . ///
/// 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. /// /// - /// This is the recommended way, as opposed to parenting, to attached/detatch two rigid bodies to one another when is enabled. + /// This is the recommended way, as opposed to parenting, to attached/detach two rigid bodies to one another when is enabled. /// 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 } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index f871b79331..d86f72f2ac 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -747,6 +747,11 @@ protected virtual void OnInSceneObjectsSpawned() { } /// public virtual void OnNetworkDespawn() { } + /// + /// Gets called before has been invoked for all s associated with the currently spawned instance. + /// + public virtual void OnNetworkPreDespawn() { } + internal void NetworkPreSpawn(ref NetworkManager networkManager) { try @@ -826,6 +831,18 @@ internal void InSceneNetworkObjectsSpawned() } } + internal void InternalOnNetworkPreDespawn() + { + try + { + OnNetworkPreDespawn(); + } + catch (Exception e) + { + Debug.LogException(e); + } + } + internal void InternalOnNetworkDespawn() { IsSpawned = false; @@ -1518,6 +1535,14 @@ internal bool Synchronize(ref BufferSerializer serializer, ulong targetCli } } + /// + /// Use to assure a helper component invokes script during destroy in the + /// event that a derived class does not invoke base.OnDestroy. + /// + internal virtual void InternalOnDestroy() + { + + } /// /// Invoked when the the is attached to is destroyed. @@ -1525,6 +1550,7 @@ internal bool Synchronize(ref BufferSerializer serializer, ulong targetCli /// 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/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 94308a2839..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(s_PrefabStage.assetPath); } - if (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). @@ -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); } } } 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 { /// - /// Component attached to the "Player Prefab" on the `NetworkManager`. + /// Component attached to the "Player Prefab" on the NetworkManager. /// 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. /// /// - /// 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. /// [ServerRpc] public void RandomTeleportServerRpc() 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..4616525896 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/AttachableBehaviourTests.cs @@ -0,0 +1,815 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +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_AttachablePrefab; + private GameObject m_TargetNodePrefabA; + private GameObject m_TargetNodePrefabB; + + /// + /// All of the below instances belong to the authority + /// + private ulong m_TargetInstanceId; + 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; + + 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_AttachablePrefab = CreateNetworkObjectPrefab("Source"); + var attachableNetworkObject = m_AttachablePrefab.GetComponent(); + 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_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_AttachablePrefab.transform; + targetChildA.transform.parent = m_TargetNodePrefabA.transform; + targetChildB.transform.parent = m_TargetNodePrefabB.transform; + + m_PrefabTestAttachable = sourceChild.AddComponent(); + var targetChildATestNode = targetChildA.AddComponent(); + targetChildATestNode.DetachOnDespawn = true; + targetChildB.AddComponent(); + 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(); + + 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; + + 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(); + attachable.ResetStates(); + } + + // Target + if (m_TargetInstance && !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(); + node.ResetStates(); + } + + // Target B + if (m_TargetInstanceB && !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(); + node.ResetStates(); + } + } + return m_ErrorLog.Length == 0; + } + + 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; + var node = (TestNode)null; + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(currentAttachableRoot.NetworkObjectId)) + { + if (!ignoreIfDespawned) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {currentAttachableRoot.name}!"); + } + continue; + } + else + { + attachable = networkManager.SpawnManager.SpawnedObjects[currentAttachableRoot.NetworkObjectId].GetComponentInChildren(); + } + + if (!attachable) + { + attachable = networkManager.SpawnManager.SpawnedObjects[m_TargetInstanceId].GetComponentInChildren(); + if (!attachable) + { + attachable = networkManager.SpawnManager.SpawnedObjects[m_TargetInstanceB.NetworkObjectId].GetComponentInChildren(); + if (!attachable) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][Attachable] Attachable was not found!"); + } + } + continue; + } + + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(targetId)) + { + if (!ignoreIfDespawned) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}] Has no spawned instance of {target.name}!"); + } + continue; + } + else + { + node = networkManager.SpawnManager.SpawnedObjects[targetId].GetComponentInChildren(); + } + + if (!node && ignoreIfDespawned) + { + VerboseDebug("Skipping check during despawn."); + continue; + } + + if (!attachable.CheckForState(checkAttached, false)) + { + m_ErrorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{attachable.name}] Did not have its override invoked!"); + } + if (!attachable.CheckForState(checkAttached, true)) + { + 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; + } + + 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() + { + var authority = GetAuthorityNetworkManager(); + + m_SourceInstance = SpawnObject(m_AttachablePrefab, authority).GetComponent(); + m_TargetInstance = SpawnObject(m_TargetNodePrefabA, authority).GetComponent(); + m_TargetInstanceB = SpawnObject(m_TargetNodePrefabB, authority).GetComponent(); + 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(); + Assert.NotNull(m_AttachableBehaviourInstance, $"{m_SourceInstance.name} does not have a nested child {nameof(AttachableBehaviour)}!"); + + m_AttachableNodeInstance = m_TargetInstance.GetComponentInChildren(); + Assert.NotNull(m_AttachableNodeInstance, $"{m_TargetInstance.name} does not have a nested child {nameof(AttachableNode)}!"); + + m_AttachableNodeInstanceB = m_TargetInstanceB.GetComponentInChildren(); + 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}"); + 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); + + // 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}"); + + // 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}!"); + } + + + 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(); + } + + 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.InternalAttachableNode != null) + { + 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; + } + + 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(); + 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(); + m_TargetInstance = SpawnObject(m_TargetNodePrefabA, m_Authority).GetComponent(); + m_TargetInstanceB = SpawnObject(m_TargetNodePrefabB, m_Authority).GetComponent(); + 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(); + Assert.NotNull(m_AttachableBehaviourInstance, $"{m_SourceInstance.name} does not have a nested child {nameof(AttachableBehaviour)}!"); + + m_AttachableNodeInstance = m_TargetInstance.GetComponentInChildren(); + Assert.NotNull(m_AttachableNodeInstance, $"{m_TargetInstance.name} does not have a nested child {nameof(AttachableNode)}!"); + + m_AttachableNodeInstanceB = m_TargetInstanceB.GetComponentInChildren(); + 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(); + m_AttachableNodeInstance = m_Authority.SpawnManager.SpawnedObjects[m_AttachableNodeInstance.NetworkObjectId].GetComponentInChildren(); + 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(); + 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(); + 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(); + // 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}"); + } + } + + /// + /// 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 instance is passed in as a parameter. + /// + internal class TestAttachable : AttachableBehaviour + { + public static bool VerboseMode; + + public static readonly Dictionary> LastKnownOverrideStates = new Dictionary>(); + public static readonly Dictionary> LastKnownEventStates = new Dictionary>(); + + private Dictionary m_StateUpdates = new Dictionary(); + private Dictionary m_StateUpdateEvents = new Dictionary(); + + 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() + { + if (!LastKnownOverrideStates.ContainsKey(NetworkManager.LocalClientId)) + { + var localClientId = NetworkManager.LocalClientId; + LastKnownOverrideStates.Add(localClientId, new Dictionary()); + LastKnownEventStates.Add(localClientId, new Dictionary()); + + 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); + } + + public void ResetStates() + { + m_StateUpdates.Clear(); + m_StateUpdateEvents.Clear(); + } + + private void Log(string message) + { + if (!VerboseMode) + { + return; + } + Debug.Log($"[{name}] {message}"); + } + + public bool CheckForState(bool checkAttached, bool checkEvent) + { + var tableToCheck = checkEvent ? m_StateUpdateEvents : m_StateUpdates; + var expectedState = checkAttached ? AttachState.Attached : AttachState.Detached; + var checkStatus = tableToCheck.ContainsKey(expectedState); + + if (checkStatus) + { + if ((checkAttached && transform.parent == DefaultParent.transform) || (!checkAttached && transform.parent != DefaultParent.transform)) + { + if (!checkAttached) + { + Log($"[CheckState][Fail][Wrong Parent] checkAttached = {checkAttached} | parent = {transform.parent?.name} | Expected {DefaultParent.name}"); + } + else + { + 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; + } + } + + /// + /// Helps to validate that the overrides are invoked when an attachable attaches or detaches from the instance. + /// + 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; + OnDetachedInvoked = false; + } + + protected override void OnAttached(AttachableBehaviour attachableBehaviour) + { + OnAttachedInvoked = true; + base.OnAttached(attachableBehaviour); + } + + protected override void OnDetached(AttachableBehaviour attachableBehaviour) + { + OnDetachedInvoked = true; + base.OnDetached(attachableBehaviour); + } + } + + internal class TestController : ComponentController + { + + } + } +} 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 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..8262614cde --- /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(); + var light = sourceChild.AddComponent(); + var controller = m_TestPrefab.AddComponent(); + controller.Components = new List + { + new ComponentController.ComponentEntry() + { + Component = meshRenderer, + }, + new ComponentController.ComponentEntry() + { + InvertEnabled = true, + Component = light, + } + }; + 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(); + 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(); + 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(); + + 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