Skip to content
Open
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
1 change: 1 addition & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Deprecated

- Deprecated the nullable boolean `NetworkObject.IsSceneObject` and introduced `NetworkObject.InScenePlaced`. (#4000)
- Deprecated a number of methods that were no longer valid or being used. (#3987)

### Removed
Expand Down
45 changes: 45 additions & 0 deletions com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Unity.Netcode.Editor
{
/// <summary>
/// A <see cref="IProcessSceneWithReport"/> that sets the <see cref
/// "NetworkObject.InScenePlaced"/> property to true for all <see cref="NetworkObject"/>s in the scene.
/// Ensures that InScenePlaced is always true for all objects in the scene.
/// </summary>
/// <remarks>
/// This will always run as the game enters the scene,
/// </remarks>
internal class SetInScenePlaced : IProcessSceneWithReport
{
public int callbackOrder => 0;
public void OnProcessScene(Scene scene, BuildReport report)
{
foreach (var networkObject in FindObjects.FromSceneByType<NetworkObject>(scene, true))
{
networkObject.InScenePlaced = true;
}
}
}

/// <summary>
/// An <see cref="AssetPostprocessor"/> that sets the <see cref="NetworkObject.InScenePlaced"/> property to false for all <see cref="NetworkObject"/>s in prefabs.
/// Ensures that InScenePlaced is always false for all prefab objects.
/// This is important because when a prefab is instantiated in the scene, it should be treated as a dynamically spawned object.
/// </summary>
internal class InScenePlacedPrefabBuilder : AssetPostprocessor
{
public void OnPostprocessPrefab(GameObject root)
{
var networkObjects = root.GetComponentsInChildren<NetworkObject>(true);
foreach (var networkObject in networkObjects)
{
networkObject.InScenePlaced = false;
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 4 additions & 8 deletions com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,10 @@ public override void OnInspectorGUI()
EditorGUILayout.Toggle(nameof(NetworkObject.IsOwner), m_NetworkObject.IsOwner);
EditorGUILayout.Toggle(nameof(NetworkObject.IsOwnedByServer), m_NetworkObject.IsOwnedByServer);
EditorGUILayout.Toggle(nameof(NetworkObject.IsPlayerObject), m_NetworkObject.IsPlayerObject);
if (m_NetworkObject.IsSceneObject.HasValue)
{
EditorGUILayout.Toggle(nameof(NetworkObject.IsSceneObject), m_NetworkObject.IsSceneObject.Value);
}
else
{
EditorGUILayout.TextField(nameof(NetworkObject.IsSceneObject), "null");
}
#pragma warning disable CS0618 // Type or member is obsolete
// TODO-3.x: Update name in 3.x branch
EditorGUILayout.Toggle(nameof(NetworkObject.IsSceneObject), m_NetworkObject.InScenePlaced);
#pragma warning restore CS0618 // Type or member is obsolete
EditorGUILayout.Toggle(nameof(NetworkObject.DestroyWithScene), m_NetworkObject.DestroyWithScene);
EditorGUILayout.TextField(nameof(NetworkObject.NetworkManager), m_NetworkObject.NetworkManager == null ? "null" : m_NetworkObject.NetworkManager.gameObject.name);
GUI.enabled = guiEnabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2231,7 +2231,7 @@ private bool CheckForStateChange(ref NetworkTransformState networkState, bool is

// In-scene placed NetworkObjects parented under a GameObject with no
// NetworkObject preserve their lossyScale when synchronizing.
if (parentNetworkObject == null && NetworkObject.IsSceneObject != false)
if (parentNetworkObject == null && NetworkObject.InScenePlaced)
{
hasParentNetworkObject = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1184,7 +1184,11 @@ internal void CreateAndSpawnPlayer(ulong ownerId)
return;
}

networkObject.IsSceneObject = false;
#pragma warning disable CS0618 // Type or member is obsolete
// Obsolete with warning means we need the underlying behaviour to keep existing
// TODO: remove in the 3.x branch
networkObject.SetSceneObjectStatus(false);
#pragma warning restore CS0618 // Type or member is obsolete
networkObject.NetworkManagerOwner = NetworkManager;
networkObject.SpawnAsPlayerObject(ownerId, networkObject.DestroyWithScene);
}
Expand Down
94 changes: 90 additions & 4 deletions com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#if NGO_FINDOBJECTS_NOSORTING
using System;
#endif
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;

namespace Unity.Netcode
Expand All @@ -11,14 +12,14 @@ namespace Unity.Netcode
/// </summary>
/// <remarks>
/// It is intentional that we do not include the UnityEngine namespace in order to avoid
/// over-complicatd define wrapping between versions that do or don't support FindObjectsSortMode.
/// over-complicated define wrapping between versions that do or don't support FindObjectsSortMode.
/// </remarks>
internal static class FindObjects
{
/// <summary>
/// Replaces <see cref="Object.FindObjectsByType"/> to have one place where these changes are applied.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T">The type of object to find. Must be a reference type derived from <see cref="Object"/></typeparam>
/// <param name="includeInactive">When true, inactive objects will be included.</param>
/// <param name="orderByIdentifier">When true, the array returned will be sorted by identifier.</param>
/// <returns>Results as an <see cref="Array"/> of type T</returns>
Expand All @@ -39,5 +40,90 @@ public static T[] ByType<T>(bool includeInactive = false, bool orderByIdentifier
#endif
return results;
}

/// <summary>
/// Returns an enumerator that enumerates over all the components of a given type in a scene.
/// </summary>
/// <param name="scene">The scene to use for searching</param>
/// <param name="includeInactive">When true, inactive objects will be included.</param>
/// <typeparam name="T">Type of <see cref="Component"/> to get from the scene</typeparam>
/// <returns>a generator that yields successive NetworkObjects in the current scene</returns>
public static IEnumerable<T> FromSceneByType<T>(Scene scene, bool includeInactive) where T : UnityEngine.Component
{
return new ObjectsInSceneEnumerator<T>(scene, includeInactive);
}

/// <summary>
/// An Enumerator that enumerates over each component of type <see cref="T"/> in the given scene.
/// </summary>
/// <typeparam name="T">Type of <see cref="Component"/> to get from the scene</typeparam>
private struct ObjectsInSceneEnumerator<T> : IEnumerable<T>, IEnumerator<T> where T : UnityEngine.Component
{
private readonly UnityEngine.GameObject[] m_RootObjects;
private int m_RootIndex;
private T[] m_CurrentChildObjects;
private int m_CurrentChildIndex;

private readonly bool m_IncludeInactive;

internal ObjectsInSceneEnumerator(Scene scene, bool includeInactive)
{
m_IncludeInactive = includeInactive;

m_RootObjects = scene.GetRootGameObjects();
m_RootIndex = 0;
m_CurrentChildObjects = null;
m_CurrentChildIndex = 0;
Current = null;
}

public void Dispose() { }

public bool MoveNext()
{
while (m_CurrentChildObjects == null && m_RootIndex < m_RootObjects.Length)
{
m_CurrentChildObjects = m_RootObjects[m_RootIndex].GetComponentsInChildren<T>(m_IncludeInactive);
m_RootIndex++;

if (m_CurrentChildObjects.Length == 0)
{
m_CurrentChildObjects = null;
}
}

if (m_CurrentChildObjects != null && m_CurrentChildIndex < m_CurrentChildObjects.Length)
{
Current = m_CurrentChildObjects[m_CurrentChildIndex];
m_CurrentChildIndex++;

if (m_CurrentChildIndex >= m_CurrentChildObjects.Length)
{
m_CurrentChildIndex = 0;
m_CurrentChildObjects = null;
}
return true;
}

Current = null;
return false;
}

public void Reset()
{
m_RootIndex = 0;
m_CurrentChildObjects = null;
m_CurrentChildIndex = 0;
Current = null;
}

object IEnumerator.Current => Current;

public T Current { get; private set; }

public IEnumerator<T> GetEnumerator() => this;

IEnumerator IEnumerable.GetEnumerator() => this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1644,7 +1644,7 @@ internal void ShutdownInternal()
// place (i.e. sending any last state updates or the like).

SpawnManager?.DespawnAndDestroyNetworkObjects();
SpawnManager?.ServerResetShudownStateForSceneObjects();
SpawnManager?.ServerResetShutdownStateForSceneObjects();
////

RpcTarget?.Dispose();
Expand Down
47 changes: 30 additions & 17 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ internal void OnValidate()
if (GlobalObjectIdHash != oldValue)
{
// Check if this is an in-scnee placed NetworkObject (Special Case for In-Scene Placed).
if (IsSceneObject.HasValue && IsSceneObject.Value)
if (InScenePlaced)
{
// Sanity check to make sure this is a scene placed object.
if (globalId.identifierType != k_SceneObjectType)
Expand Down Expand Up @@ -340,7 +340,12 @@ private void CheckForInScenePlaced()
EditorUtility.SetDirty(this);
}
}
IsSceneObject = true;

#pragma warning disable CS0618 // Type or member is obsolete
// Obsolete with warning means we need the underlying behaviour to keep existing
// TODO-3.x: remove in the 3.x branch
SetSceneObjectStatus(true);
#pragma warning restore CS0618 // Type or member is obsolete

// Default scene migration synchronization to false for in-scene placed NetworkObjects
SceneMigrationSynchronization = false;
Expand Down Expand Up @@ -1225,15 +1230,24 @@ private bool InternalHasAuthority()
public bool IsSpawned { get; internal set; }

/// <summary>
/// Gets if the object is a SceneObject, null if it's not yet spawned but is a scene object.
/// Gets if the object is a SceneObject.
/// </summary>
[Obsolete("Use InScenePlaced instead")]
public bool? IsSceneObject { get; internal set; }

//DANGOEXP TODO: Determine if we want to keep this
/// <summary>
/// True if this object is placed in a scene; false otherwise.
/// </summary>
[field: HideInInspector]
[field: SerializeField]
public bool InScenePlaced { get; internal set; }

/// <summary>
/// Sets whether this NetworkObject was instantiated as part of a scene
/// </summary>
/// <remarks>Only use this when using custom scene loading</remarks>
/// <param name="isSceneObject">When true, marks this as a scene-instantiated object; when false, marks it as runtime-instantiated</param>
[Obsolete("SetSceneObjectStatus is now calculated during the build.")]
public void SetSceneObjectStatus(bool isSceneObject = false)
{
IsSceneObject = isSceneObject;
Expand Down Expand Up @@ -1457,7 +1471,7 @@ internal Scene SceneOrigin
/// </summary>
internal NetworkSceneHandle GetSceneOriginHandle()
{
if (SceneOriginHandle.IsEmpty() && IsSpawned && IsSceneObject != false)
if (SceneOriginHandle.IsEmpty() && IsSpawned && InScenePlaced)
{
if (NetworkManager.LogLevel <= LogLevel.Error)
{
Expand Down Expand Up @@ -1622,7 +1636,7 @@ public void NetworkHide(ulong clientId)
var message = new DestroyObjectMessage
{
NetworkObjectId = NetworkObjectId,
DestroyGameObject = !IsSceneObject.Value,
DestroyGameObject = !InScenePlaced,
IsDistributedAuthority = NetworkManagerOwner.DistributedAuthorityMode,
IsTargetedDestroy = NetworkManagerOwner.DistributedAuthorityMode,
TargetClientId = clientId, // Just always populate this value whether we write it or not
Expand Down Expand Up @@ -1750,7 +1764,7 @@ private void OnDestroy()
var isStillValid = gameObject != null && gameObject.scene.IsValid() && gameObject.scene.isLoaded;

// If we're not the authority and everything is valid and dynamically spawned, then the destroy is not valid.
if (!isAuthorityDestroy && IsSceneObject == false && isStillValid)
if (!isAuthorityDestroy && !InScenePlaced && isStillValid)
{
if (networkManager.LogLevel <= LogLevel.Error)
{
Expand Down Expand Up @@ -1849,7 +1863,7 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla
}
}

if (!NetworkManagerOwner.SpawnManager.AuthorityLocalSpawn(this, NetworkManagerOwner.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene))
if (!NetworkManagerOwner.SpawnManager.AuthorityLocalSpawn(this, NetworkManagerOwner.SpawnManager.GetNetworkObjectId(), InScenePlaced, playerObject, ownerClientId, destroyWithScene))
{
if (NetworkManagerOwner.LogLevel <= LogLevel.Normal)
{
Expand Down Expand Up @@ -2528,8 +2542,7 @@ internal bool ApplyNetworkParenting(bool removeParent = false, bool ignoreNotSpa
// Handle the first in-scene placed NetworkObject parenting scenarios. Once the m_LatestParent
// has been set, this will not be entered into again (i.e. the later code will be invoked and
// users will get notifications when the parent changes).
var isInScenePlaced = IsSceneObject.HasValue && IsSceneObject.Value;
if (transform.parent != null && !removeParent && !m_LatestParent.HasValue && isInScenePlaced)
if (transform.parent != null && !removeParent && !m_LatestParent.HasValue && InScenePlaced)
{
var parentNetworkObject = transform.parent.GetComponent<NetworkObject>();

Expand Down Expand Up @@ -3271,7 +3284,7 @@ internal SerializedObject Serialize(ulong targetClientId = NetworkManager.Server
NetworkObjectId = NetworkObjectId,
OwnerClientId = OwnerClientId,
IsPlayerObject = IsPlayerObject,
IsSceneObject = IsSceneObject ?? true,
IsSceneObject = InScenePlaced,
DestroyWithScene = DestroyWithScene,
DontDestroyWithOwner = DontDestroyWithOwner,
HasOwnershipFlags = NetworkManagerOwner.DistributedAuthorityMode,
Expand Down Expand Up @@ -3456,7 +3469,7 @@ internal void SubscribeToActiveSceneForSynch()
{
if (ActiveSceneSynchronization)
{
if (IsSceneObject.HasValue && !IsSceneObject.Value)
if (!InScenePlaced)
{
// Just in case it is a recycled NetworkObject, unsubscribe first
SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged;
Expand All @@ -3473,7 +3486,7 @@ private void CurrentlyActiveSceneChanged(Scene current, Scene next)
{
// Early exit if the NetworkObject is not spawned, is an in-scene placed NetworkObject,
// or the NetworkManager is shutting down.
if (!IsSpawned || IsSceneObject != false || NetworkManagerOwner.ShutdownInProgress)
if (!IsSpawned || NetworkManagerOwner.ShutdownInProgress || InScenePlaced)
{
return;
}
Expand All @@ -3483,7 +3496,7 @@ private void CurrentlyActiveSceneChanged(Scene current, Scene next)
{
// Only dynamically spawned NetworkObjects that are not already in the newly assigned active scene will migrate
// and update their scene handles
if (IsSceneObject.HasValue && !IsSceneObject.Value && gameObject.scene != next && gameObject.transform.parent == null)
if (gameObject.scene != next && gameObject.transform.parent == null)
{
SceneManager.MoveGameObjectToScene(gameObject, next);
SceneChangedUpdate(next);
Expand Down Expand Up @@ -3571,7 +3584,7 @@ internal bool UpdateForSceneChanges()
// the NetworkManager is shutting down, the NetworkObject is not spawned, it is an in-scene placed
// NetworkObject, or the GameObject's current scene handle is the same as the SceneOriginHandle
if (!SceneMigrationSynchronization || !IsSpawned || NetworkManagerOwner.ShutdownInProgress ||
!NetworkManagerOwner.NetworkConfig.EnableSceneManagement || IsSceneObject != false || !gameObject)
!NetworkManagerOwner.NetworkConfig.EnableSceneManagement || InScenePlaced || !gameObject)
{
// Stop checking for a scene migration
return false;
Expand Down Expand Up @@ -3606,15 +3619,15 @@ internal uint CheckForGlobalObjectIdHashOverride()

// If scene management is disabled and this is an in-scene placed NetworkObject then go ahead
// and send the InScenePlacedSourcePrefab's GlobalObjectIdHash value (i.e. what to dynamically spawn)
if (!networkManager.NetworkConfig.EnableSceneManagement && IsSceneObject.Value && InScenePlacedSourceGlobalObjectIdHash != 0)
if (!networkManager.NetworkConfig.EnableSceneManagement && InScenePlaced && InScenePlacedSourceGlobalObjectIdHash != 0)
{
return InScenePlacedSourceGlobalObjectIdHash;
}

// If the PrefabGlobalObjectIdHash is a non-zero value and the GlobalObjectIdHash value is
// different from the PrefabGlobalObjectIdHash value, then the NetworkObject instance is
// an override for the original network prefab (i.e. PrefabGlobalObjectIdHash)
if (!IsSceneObject.Value && GlobalObjectIdHash != PrefabGlobalObjectIdHash)
if (!InScenePlaced && GlobalObjectIdHash != PrefabGlobalObjectIdHash)
{
// If the PrefabGlobalObjectIdHash is already populated (i.e. InstantiateAndSpawn used), then return this
if (PrefabGlobalObjectIdHash != 0)
Expand Down
Loading
Loading