Skip to content

fix: Provide SpawnWithObservers property alternative to CheckObjectVisibility [MTT-6353] #2568

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Added

- Added `NetworkObject.SpawnWithObservers` property (default is true) that when set to false will spawn a `NetworkObject` with no observers and will not be spawned on any client until `NetworkObject.NetworkShow` is invoked. (#2568)

### Fixed

- Fixed warning "Runtime Network Prefabs was not empty at initialization time." being erroneously logged when no runtime network prefabs had been added (#2565)
Expand Down
6 changes: 6 additions & 0 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ internal void GenerateGlobalObjectIdHash()
/// </remarks>
public Action OnMigratedToNewScene;

/// <summary>
/// When set to false, the NetworkObject will be spawned with no observers initially (other than the server)
/// </summary>
[Tooltip("When false, the NetworkObject will spawn with no observers initially. (default is true)")]
public bool SpawnWithObservers = true;

/// <summary>
/// Delegate type for checking visibility
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -617,8 +617,10 @@ private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong
}
}

if (NetworkManager.IsServer)
// If we are the server and should spawn with observers
if (NetworkManager.IsServer && networkObject.SpawnWithObservers)
{
// Add client observers
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
{
if (networkObject.CheckObjectVisibility == null || networkObject.CheckObjectVisibility(NetworkManager.ConnectedClientsList[i].ClientId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,86 @@ public class NetworkObjectOnSpawnTests : NetcodeIntegrationTest

protected override int NumberOfClients => 2;

public enum ObserverTestTypes
{
WithObservers,
WithoutObservers
}
private GameObject m_ObserverPrefab;
private NetworkObject m_ObserverTestNetworkObject;
private ObserverTestTypes m_ObserverTestType;

private const string k_ObserverTestObjName = "ObsObj";
private const string k_WithObserversError = "Not all clients spawned the";
private const string k_WithoutObserversError = "A client spawned the";

protected override void OnServerAndClientsCreated()
{
m_ObserverPrefab = CreateNetworkObjectPrefab(k_ObserverTestObjName);
base.OnServerAndClientsCreated();
}


private bool CheckClientsSideObserverTestObj()
{
foreach (var client in m_ClientNetworkManagers)
{
if (!s_GlobalNetworkObjects.ContainsKey(client.LocalClientId))
{
// When no observers there shouldn't be any client spawned NetworkObjects
// (players are held in a different list)
return !(m_ObserverTestType == ObserverTestTypes.WithObservers);
}
var clientObjects = s_GlobalNetworkObjects[client.LocalClientId];
// Make sure they did spawn the object
if (m_ObserverTestType == ObserverTestTypes.WithObservers)
{
if (!clientObjects.ContainsKey(m_ObserverTestNetworkObject.NetworkObjectId))
{
return false;
}
if (!clientObjects[m_ObserverTestNetworkObject.NetworkObjectId].IsSpawned)
{
return false;
}
}
}
return true;
}



[UnityTest]
public IEnumerator ObserverSpawnTests([Values] ObserverTestTypes observerTestTypes)
{
m_ObserverTestType = observerTestTypes;
var prefabNetworkObject = m_ObserverPrefab.GetComponent<NetworkObject>();
prefabNetworkObject.SpawnWithObservers = observerTestTypes == ObserverTestTypes.WithObservers;
var instance = SpawnObject(m_ObserverPrefab, m_ServerNetworkManager);
m_ObserverTestNetworkObject = instance.GetComponent<NetworkObject>();
var withoutObservers = m_ObserverTestType == ObserverTestTypes.WithoutObservers;
if (withoutObservers)
{
// Just give a little time to make sure nothing spawned
yield return s_DefaultWaitForTick;
}
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
AssertOnTimeout($"{(withoutObservers ? k_WithoutObserversError : k_WithObserversError)} {k_ObserverTestObjName} object!");
// If we spawned without observers
if (withoutObservers)
{
// Make each client an observer
foreach (var client in m_ClientNetworkManagers)
{
m_ObserverTestNetworkObject.NetworkShow(client.LocalClientId);
}

// Validate the clients spawned the NetworkObject
m_ObserverTestType = ObserverTestTypes.WithObservers;
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
AssertOnTimeout($"{k_WithObserversError} {k_ObserverTestObjName} object!");
}
}
/// <summary>
/// Tests that instantiating a <see cref="NetworkObject"/> and destroying without spawning it
/// does not run <see cref="NetworkBehaviour.OnNetworkSpawn"/> or <see cref="NetworkBehaviour.OnNetworkSpawn"/>.
Expand Down Expand Up @@ -52,6 +132,11 @@ protected override void OnCreatePlayerPrefab()

protected override IEnumerator OnTearDown()
{
if (m_ObserverPrefab != null)
{
Object.Destroy(m_ObserverPrefab);
}

if (m_TestNetworkObjectPrefab != null)
{
Object.Destroy(m_TestNetworkObjectPrefab);
Expand Down