using FishNet.Utility.Extension; using GameKit.Dependencies.Utilities; using System; using System.Collections.Generic; using System.IO; using UnityEngine.SceneManagement; #if UNITY_6000_5_OR_NEWER using SceneHandle = System.UInt64; #else using SceneHandle = System.Int32; #endif namespace FishNet.Managing.Scened { /// /// Extensions for SceneLookupData. /// internal static class SceneLookupDataExtensions { [Obsolete("Use GetFullNames instead.")] public static string[] GetNames(this SceneLookupData[] datas) => GetFullNames(datas); /// /// Returns Names from SceneLookupData. /// /// /// public static string[] GetFullNames(this SceneLookupData[] datas) { string[] names = new string[datas.Length]; for (int i = 0; i < datas.Length; i++) names[i] = datas[i].FullName; return names; } [Obsolete("Use GetFullNamesOnly instead.")] public static string[] GetNamesOnly(this SceneLookupData[] datas) => GetFulLNamesOnly(datas); /// /// Returns Names from SceneLookupData. /// /// /// public static string[] GetFulLNamesOnly(this SceneLookupData[] datas) { string[] names = new string[datas.Length]; for (int i = 0; i < datas.Length; i++) names[i] = datas[i].FullNameOnly; return names; } } /// /// Data container for looking up, loading, or unloading a scene. /// public class SceneLookupData : IEquatable { /// /// Handle of the scene. If value is 0, then handle is not used. /// public SceneHandle Handle; [Obsolete("Use FullName instead.")] public string Name { get => FullName; set => FullName = value; } /// /// Full name of the scene. /// public string FullName = string.Empty; [Obsolete("Use FullNameOnly instead.")] public string NameOnly => FullNameOnly; /// /// Full name of the scene without extensions. /// /// /// Returns the scene name without a directory path should one exist. /// public string FullNameOnly { get { if (string.IsNullOrEmpty(FullName)) return string.Empty; string name = System.IO.Path.GetFileName(FullName); return RemoveUnityExtension(name); } } /// /// Returns if this data is valid for use. /// Being valid does not mean that the scene exist, rather that there is enough data to try and lookup a scene. /// public bool IsValid => FullName != string.Empty || Handle != 0; #region Const /// /// String to display when scene data is invalid. /// private const string INVALID_SCENE = "One or more scene information entries contain invalid data and have been skipped."; #endregion /// /// public SceneLookupData() { } /// /// /// Scene to generate from. public SceneLookupData(Scene scene) { Handle = scene.GetRawHandle(); string fullPath = SceneUtility.GetScenePathByBuildIndex(scene.buildIndex); FullName = Path.GetFileNameWithoutExtension(fullPath); } /// /// /// Scene name to generate from. public SceneLookupData(string fullName) { FullName = fullName; } /// /// /// Scene handle to generate from. public SceneLookupData(int handle) { Handle = Scenes.ToRawHandle(handle); } /// /// /// Scene handle to generate from. /// Name to generate from if handle is 0. public SceneLookupData(int handle, string fullName) { Handle = Scenes.ToRawHandle(handle); FullName = fullName; } #region Comparers. public static bool operator ==(SceneLookupData sldA, SceneLookupData sldB) { // One is null while the other is not. if (sldA is null != sldB is null) return false; /*If here both are either null or have value. */ if (!(sldA is null)) return sldA.Equals(sldB); // Fall through indicates both are null. return true; } public static bool operator !=(SceneLookupData sldA, SceneLookupData sldB) { // One is null while the other is not. if (sldA is null != sldB is null) return true; /*If here both are either null or have value. */ if (!(sldA is null)) return !sldA.Equals(sldB); // Fall through indicates both are null. return true; } public bool Equals(SceneLookupData sld) { // Comparing instanced against null. if (sld is null) return false; // True if both handles are empty. bool bothHandlesEmpty = Handle == 0 && sld.Handle == 0; // If both have handles and they match. if (!bothHandlesEmpty && sld.Handle == Handle) return true; // If neither have handles and name matches. if (bothHandlesEmpty && sld.FullName == FullName) return true; // Fall through. return false; } public override int GetHashCode() { int hashCode = 2053068273; hashCode = hashCode * -1521134295 + Handle.GetHashCode(); hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(FullName); return hashCode; } public override bool Equals(object obj) { return base.Equals(obj); } public override string ToString() { return $"Name {FullName}, Handle {Handle}"; // return base.ToString(); } #endregion #region CreateData. /// /// Returns a new SceneLookupData. /// /// Scene to create from. /// public static SceneLookupData CreateData(Scene scene) => new(scene); /// /// Returns a new SceneLookupData. /// /// Scene name to create from. /// public static SceneLookupData CreateData(string name) => new(name); /// /// Returns a new SceneLookupData. /// /// Scene handle to create from. /// public static SceneLookupData CreateData(int handle) => new(handle); /// /// Returns a SceneLookupData collection. /// /// Scenes to create from. /// public static SceneLookupData[] CreateData(List scenes) => CreateData(scenes.ToArray()); /// /// Returns a SceneLookupData collection. /// /// Scene names to create from. /// public static SceneLookupData[] CreateData(List names) => CreateData(names.ToArray()); /// /// Returns a SceneLookupData collection. /// /// Scene handles to create from. /// public static SceneLookupData[] CreateData(List handles) => CreateData(handles.ToArray()); /// /// Returns a SceneLookupData collection. /// /// Scenes to create from. /// public static SceneLookupData[] CreateData(Scene[] scenes) { bool invalidFound = false; List result = new(); foreach (Scene item in scenes) { if (!item.IsValid()) { invalidFound = true; continue; } result.Add(CreateData(item)); } if (invalidFound) NetworkManagerExtensions.LogWarning(INVALID_SCENE); return result.ToArray(); } /// /// Returns a SceneLookupData collection. /// /// Scene names to create from. /// public static SceneLookupData[] CreateData(string[] names) { SceneLookupData[] result = new SceneLookupData[names.Length]; for (int i = 0; i < result.Length; i++) result[i] = new(names[i]); return ValidateData(result); } /// /// Validates SceneLookupdatas and returns only valid entries. /// public static SceneLookupData[] ValidateData(SceneLookupData data) => ValidateData(new SceneLookupData[] { data }); /// /// Validates SceneLookupdatas and returns only valid entries. /// /// Datas to validate. public static SceneLookupData[] ValidateData(SceneLookupData[] datas) { bool invalidFound = false; List result = CollectionCaches.RetrieveList(); foreach (SceneLookupData item in datas) { if (item.IsValid) { int failingIndex = -1; // Scene name or handle is set, make sure it's not duplicated in datas. for (int i = 0; i < result.Count; i++) { bool nameMatches = result[i].FullName == item.FullName; bool handleMatches = result[i].Handle == item.Handle; // Handle is the same (could be 0 handle). if (handleMatches) { // If handle matches and not default then the same scene was added multiple times. if (item.Handle != 0) failingIndex = i; } // Name is the same. else if (nameMatches) { // If handle and name matches then also fail. if (handleMatches) failingIndex = i; } } if (failingIndex != -1) NetworkManagerExtensions.LogWarning($"Data {item.ToString()} matches {result[failingIndex].ToString()} and has been removed from datas."); else result.Add(item); } else { invalidFound = true; } } SceneLookupData[] returnedValue; if (invalidFound) { NetworkManagerExtensions.LogWarning(INVALID_SCENE); returnedValue = result.ToArray(); } else { returnedValue = datas; } CollectionCaches.Store(result); return returnedValue; } /// /// Returns a SceneLookupData collection. /// /// Scene handles to create from. /// public static SceneLookupData[] CreateData(int[] handles) { bool invalidFound = false; List result = new(); foreach (int item in handles) { if (item == 0) { invalidFound = true; continue; } result.Add(CreateData(item)); } if (invalidFound) NetworkManagerExtensions.LogWarning(INVALID_SCENE); return result.ToArray(); } #endregion /// /// Removes .Unity from text. /// private static string RemoveUnityExtension(string text) { string extension = ".unity"; int extIndex = text.ToLower().IndexOf(extension); if (extIndex != -1 && text.Length - extIndex == extension.Length) text = text.Substring(0, extIndex); return text; } /// /// Returns the first scene found using Handle or Name, preferring Handle. /// /// /// True if scene was found by handle. Handle is always checked first. /// True to warn if duplicates are found. public Scene GetScene(out bool foundByHandle, bool warnIfDuplicates = true) { foundByHandle = false; if (Handle == 0 && string.IsNullOrEmpty(FullNameOnly)) { NetworkManagerExtensions.LogWarning("Scene handle and name is unset; scene cannot be returned."); return default; } Scene result = default; // Lookup my handle. if (Handle != 0) { result = SceneManager.GetScene(Handle); if (result.GetRawHandle() != 0) foundByHandle = true; } // If couldnt find handle try by string. if (!foundByHandle) result = SceneManager.GetScene(FullNameOnly, null, warnIfDuplicates); return result; } } }