Skip to content

Commit

Permalink
fix: NetworkObject.NetworkHide destroys client-side in-scene placed N…
Browse files Browse the repository at this point in the history
…etworkObjects [MTT-4211] (Unity-Technologies#2086)

* fix

MTT-4211
This fixes the issue where in-scene placed NetworkObjects were being destroyed when NetworkHide is invoked.

* test

MTT-4211
This adds an additional check for NetworkHide and NetworkShow towards the end of the in-scene placed NetworkObject synchronization test.

* style

whitespace fix

* style

Adjusting comments and adding additional API XML Documentation for readability and clarification purposes.

* update

MTT-4211
Updating the changelog.

* update

improving the logic used for readability purposes since we know IsSceneObject will be valid.

* style

migrating the majority of the comments into remarks.

* style

Adding space within all added <br/> tags so they look like this: <br />
This is to see if the version of docusaurus we are using will not crash when it runs across this markdown syntax.
  • Loading branch information
NoelStephensUnity authored Jul 29, 2022
1 parent 11922a0 commit 6d37247
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 14 deletions.
1 change: 1 addition & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
- When using `UnityTransport`, _reliable_ payloads are now allowed to exceed the configured 'Max Payload Size'. Unreliable payloads remain bounded by this setting. (#2081)

### Fixed
- Fixed issue where `NetworkObject.NetworkHide` was despawning and destroying, as opposed to only despawning, in-scene placed `NetworkObject`s. (#2086)
- 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)
Expand Down
69 changes: 55 additions & 14 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,19 @@ private void Awake()
}

/// <summary>
/// Shows a previously hidden <see cref="NetworkObject"/> to a client
/// Makes the previously hidden <see cref="NetworkObject"/> "netcode visible" to the targeted client.
/// </summary>
/// <param name="clientId">The client to show the <see cref="NetworkObject"/> to</param>
/// <remarks>
/// Usage: Use to start sending updates for a previously hidden <see cref="NetworkObject"/> to the targeted client.<br />
/// <br />
/// Dynamically Spawned: <see cref="NetworkObject"/>s will be instantiated and spawned on the targeted client side.<br />
/// In-Scene Placed: The instantiated but despawned <see cref="NetworkObject"/>s will be spawned on the targeted client side.<br />
/// <br />
/// See Also:<br />
/// <see cref="NetworkShow(ulong)"/><br />
/// <see cref="NetworkHide(ulong)"/> or <see cref="NetworkHide(List{NetworkObject}, ulong)"/><br />
/// </remarks>
/// <param name="clientId">The targeted client</param>
public void NetworkShow(ulong clientId)
{
if (!IsSpawned)
Expand All @@ -260,11 +270,22 @@ public void NetworkShow(ulong clientId)
NetworkManager.SpawnManager.SendSpawnCallForObject(clientId, this);
}


/// <summary>
/// Shows a list of previously hidden <see cref="NetworkObject"/>s to a client
/// Makes a list of previously hidden <see cref="NetworkObject"/>s "netcode visible" for the client specified.
/// </summary>
/// <param name="networkObjects">The <see cref="NetworkObject"/>s to show</param>
/// <param name="clientId">The client to show the objects to</param>
/// <remarks>
/// Usage: Use to start sending updates for previously hidden <see cref="NetworkObject"/>s to the targeted client.<br />
/// <br />
/// Dynamically Spawned: <see cref="NetworkObject"/>s will be instantiated and spawned on the targeted client's side.<br />
/// In-Scene Placed: Already instantiated but despawned <see cref="NetworkObject"/>s will be spawned on the targeted client's side.<br />
/// <br />
/// See Also:<br />
/// <see cref="NetworkShow(ulong)"/><br />
/// <see cref="NetworkHide(ulong)"/> or <see cref="NetworkHide(List{NetworkObject}, ulong)"/><br />
/// </remarks>
/// <param name="networkObjects">The objects to become "netcode visible" to the targeted client</param>
/// <param name="clientId">The targeted client</param>
public static void NetworkShow(List<NetworkObject> networkObjects, ulong clientId)
{
if (networkObjects == null || networkObjects.Count == 0)
Expand Down Expand Up @@ -305,9 +326,19 @@ public static void NetworkShow(List<NetworkObject> networkObjects, ulong clientI
}

/// <summary>
/// Hides a object from a specific client
/// Hides the <see cref="NetworkObject"/> from the targeted client.
/// </summary>
/// <param name="clientId">The client to hide the object for</param>
/// <remarks>
/// Usage: Use to stop sending updates to the targeted client, "netcode invisible", for a currently visible <see cref="NetworkObject"/>.<br />
/// <br />
/// Dynamically Spawned: <see cref="NetworkObject"/>s will be despawned and destroyed on the targeted client's side.<br />
/// In-Scene Placed: <see cref="NetworkObject"/>s will only be despawned on the targeted client's side.<br />
/// <br />
/// See Also:<br />
/// <see cref="NetworkHide(List{NetworkObject}, ulong)"/><br />
/// <see cref="NetworkShow(ulong)"/> or <see cref="NetworkShow(List{NetworkObject}, ulong)"/><br />
/// </remarks>
/// <param name="clientId">The targeted client</param>
public void NetworkHide(ulong clientId)
{
if (!IsSpawned)
Expand Down Expand Up @@ -335,18 +366,28 @@ public void NetworkHide(ulong clientId)
var message = new DestroyObjectMessage
{
NetworkObjectId = NetworkObjectId,
DestroyGameObject = true
DestroyGameObject = !IsSceneObject.Value
};
// Send destroy call
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
}

/// <summary>
/// Hides a list of objects from a client
/// Hides a list of <see cref="NetworkObject"/>s from the targeted client.
/// </summary>
/// <param name="networkObjects">The objects to hide</param>
/// <param name="clientId">The client to hide the objects from</param>
/// <remarks>
/// Usage: Use to stop sending updates to the targeted client, "netcode invisible", for the currently visible <see cref="NetworkObject"/>s.<br />
/// <br />
/// Dynamically Spawned: <see cref="NetworkObject"/>s will be despawned and destroyed on the targeted client's side.<br />
/// In-Scene Placed: <see cref="NetworkObject"/>s will only be despawned on the targeted client's side.<br />
/// <br />
/// See Also:<br />
/// <see cref="NetworkHide(ulong)"/><br />
/// <see cref="NetworkShow(ulong)"/> or <see cref="NetworkShow(List{NetworkObject}, ulong)"/><br />
/// </remarks>
/// <param name="networkObjects">The <see cref="NetworkObject"/>s that will become "netcode invisible" to the targeted client</param>
/// <param name="clientId">The targeted client</param>
public static void NetworkHide(List<NetworkObject> networkObjects, ulong clientId)
{
if (networkObjects == null || networkObjects.Count == 0)
Expand Down Expand Up @@ -455,8 +496,8 @@ public void SpawnWithOwnership(ulong clientId, bool destroyWithScene = false)
/// <summary>
/// Spawns a <see cref="NetworkObject"/> across the network and makes it the player object for the given client
/// </summary>
/// <param name="clientId">The clientId whos player object this is</param>
/// <param name="destroyWithScene">Should the object be destroyd when the scene is changed</param>
/// <param name="clientId">The clientId who's player object this is</param>
/// <param name="destroyWithScene">Should the object be destroyed when the scene is changed</param>
public void SpawnAsPlayerObject(ulong clientId, bool destroyWithScene = false)
{
SpawnInternal(destroyWithScene, clientId, true);
Expand Down Expand Up @@ -697,7 +738,7 @@ private void OnTransformParentChanged()
// For instance, if we're spawning NetworkObject 5 and its parent is 10, what should happen if we do not have 10 yet?
// let's say 10 is on the way to be replicated in a few frames and we could fix that parent-child relationship later.
//
// If you couldn't find your parent, we put you into OrphanChildren set and everytime we spawn another NetworkObject locally due to replication,
// If you couldn't find your parent, we put you into OrphanChildren set and every time we spawn another NetworkObject locally due to replication,
// we call CheckOrphanChildren() method and quickly iterate over OrphanChildren set and see if we can reparent/adopt one.
internal static HashSet<NetworkObject> OrphanChildren = new HashSet<NetworkObject>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ public IEnumerator InSceneNetworkObjectSynchAndSpawn()

yield return WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == TotalClients);
AssertOnTimeout($"Timed out waiting for all in-scene instances to be spawned! Current spawned count: {NetworkObjectTestComponent.SpawnedInstances.Count()} | Expected spawn count: {TotalClients}");

// Test NetworkHide on the first client
var firstClientId = m_ClientNetworkManagers[0].LocalClientId;

serverObject.NetworkHide(firstClientId);

yield return WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == TotalClients - 1);
AssertOnTimeout($"[NetworkHide] Timed out waiting for Client-{firstClientId} to despawn the in-scene placed NetworkObject! Current spawned count: {NetworkObjectTestComponent.SpawnedInstances.Count()} | Expected spawn count: {TotalClients - 1}");

// Validate that the first client can spawn the "netcode hidden" in-scene placed NetworkObject
serverObject.NetworkShow(firstClientId);
yield return WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == TotalClients);
AssertOnTimeout($"[NetworkShow] Timed out waiting for Client-{firstClientId} to spawn the in-scene placed NetworkObject! Current spawned count: {NetworkObjectTestComponent.SpawnedInstances.Count()} | Expected spawn count: {TotalClients}");

CleanUpLoadedScene();
}

Expand Down

0 comments on commit 6d37247

Please sign in to comment.