using FishNet.Managing;
using FishNet.Object;
using GameKit.Dependencies.Utilities;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
#if UNITY_6000_5_OR_NEWER
using SceneHandle = System.UInt64;
#else
using SceneHandle = System.Int32;
#endif
namespace FishNet.Utility.Extension
{
public static class Scenes
{
///
/// Gets all NetworkObjects in a scene.
///
/// Scene to get objects in.
/// True to only return the first NetworkObject within an object chain. False will return nested NetworkObjects.
///
public static void GetSceneNetworkObjects(Scene s, bool firstOnly, bool errorOnDuplicates, bool ignoreUnsetSceneIds, ref List result)
{
List nobCacheA = CollectionCaches.RetrieveList();
List nobCacheB = CollectionCaches.RetrieveList();
List gameObjectCache = CollectionCaches.RetrieveList();
Dictionary sceneIds = CollectionCaches.RetrieveDictionary();
// Iterate all root objects for the scene.
s.GetRootGameObjects(gameObjectCache);
foreach (GameObject go in gameObjectCache)
{
// Get NetworkObjects within children of each root.
go.GetComponentsInChildren(true, nobCacheA);
// If network objects are found.
if (nobCacheA.Count > 0)
{
// Add only the first networkobject
if (firstOnly)
{
/* The easiest way to see if a nob is nested is to
* get nobs in parent and if the count is greater than 1, then
* it is nested. The technique used here isn't exactly fast but
* it will only occur during scene loads, so I'm trading off speed
* for effort and readability. */
foreach (NetworkObject nob in nobCacheA)
{
if (ignoreUnsetSceneIds && !nob.IsSceneObject)
continue;
nob.GetComponentsInParent(true, nobCacheB);
// No extra nobs, only this one.
if (nobCacheB.Count == 1 && !TryDisplayDuplicateError(nob))
result.Add(nob);
}
}
// Not first only, add them all.
else
{
foreach (NetworkObject item in nobCacheA)
{
if (ignoreUnsetSceneIds && !item.IsSceneObject)
continue;
if (!TryDisplayDuplicateError(item))
result.Add(item);
}
}
}
}
CollectionCaches.Store(sceneIds);
bool TryDisplayDuplicateError(NetworkObject nob)
{
if (!errorOnDuplicates)
return false;
ulong id = nob.SceneId;
// There is a duplicate.
if (sceneIds.TryGetValue(id, out NetworkObject originalNob))
{
string err = $"Object {nob.name} and {originalNob.name} in scene {nob.gameObject.scene.name} have the same sceneId of {id}. This will result in spawning errors. Exit play mode and use the Fish-Networking menu to reserialize sceneIds for scene {nob.gameObject.scene.name}.";
NetworkManagerExtensions.LogError(err);
return true;
}
else
{
sceneIds[id] = nob;
return false;
}
}
}
///
/// Returns the raw handle of a scene as a version-appropriate value.
/// Unity 6000.5 changed Scene.handle from an int to an EntityId struct exposing GetRawData(); earlier versions expose it directly as an int.
///
/// Scene to return the handle of.
public static SceneHandle GetRawHandle(this Scene scene)
{
#if UNITY_6000_5_OR_NEWER
return scene.handle.GetRawData();
#else
return scene.handle;
#endif
}
///
/// Converts a legacy 32-bit scene handle into a version-appropriate raw handle.
///
/// Legacy 32-bit scene handle.
public static SceneHandle ToRawHandle(int legacyHandle)
{
#if UNITY_6000_5_OR_NEWER
return unchecked((uint)legacyHandle);
#else
return legacyHandle;
#endif
}
}
}