using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; namespace ES3Internal { [Serializable] [DisallowMultipleComponent] public abstract class ES3ReferenceMgrBase : MonoBehaviour { internal object _lock = new object(); public const string referencePropertyName = "_ES3Ref"; private static ES3ReferenceMgrBase _current = null; private static HashSet mgrs = new HashSet(); [NonSerialized] public List excludeObjects = new List(); private static System.Random rng; [HideInInspector] public bool openPrefabs; public List prefabs = new List(); [SerializeField] public ES3IdRefDictionary idRef = new ES3IdRefDictionary(); private ES3RefIdDictionary _refId; public static ES3ReferenceMgrBase Current { get { if (_current == null) { ES3ReferenceMgrBase managerFromScene = GetManagerFromScene(SceneManager.GetActiveScene()); if (managerFromScene != null) { mgrs.Add(_current = managerFromScene); } } return _current; } } public bool IsInitialised => idRef.Count > 0; public ES3RefIdDictionary refId { get { if (_refId == null) { _refId = new ES3RefIdDictionary(); foreach (KeyValuePair item in idRef) { if (item.Value != null) { _refId[item.Value] = item.Key; } } } return _refId; } set { _refId = value; } } public ES3GlobalReferences GlobalReferences => ES3GlobalReferences.Instance; public static ES3ReferenceMgrBase GetManagerFromScene(Scene scene, bool getAnyManagerIfNotInScene = true) { if (scene.IsValid()) { foreach (ES3ReferenceMgrBase mgr in mgrs) { if (mgr != null && mgr.gameObject.scene == scene) { return mgr; } } GameObject[] rootGameObjects; try { rootGameObjects = scene.GetRootGameObjects(); } catch { return null; } GameObject[] array = rootGameObjects; foreach (GameObject gameObject in array) { if (gameObject.name == "Easy Save 3 Manager") { ES3ReferenceMgr component = gameObject.GetComponent(); if (component != null) { return component; } } } array = rootGameObjects; for (int i = 0; i < array.Length; i++) { ES3ReferenceMgr componentInChildren = array[i].GetComponentInChildren(); if (componentInChildren != null) { return componentInChildren; } } } if (getAnyManagerIfNotInScene) { for (int j = 0; j < SceneManager.sceneCount; j++) { Scene sceneAt = SceneManager.GetSceneAt(j); if (sceneAt != scene && sceneAt.IsValid()) { ES3ReferenceMgrBase managerFromScene = GetManagerFromScene(sceneAt, getAnyManagerIfNotInScene: false); if (managerFromScene != null) { ES3Debug.LogWarning("The reference you're trying to save does not exist in any scene, or the scene it belongs to does not contain an Easy Save 3 Manager. Using the reference manager from scene " + sceneAt.name + " instead. This may cause unexpected behaviour or leak memory in some situations. See the Saving and Loading References guide for more information."); return managerFromScene; } } } } return null; } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void Init() { _current = null; mgrs = new HashSet(); rng = null; } internal void Awake() { if (_current != null && _current != this) { ES3ReferenceMgrBase current = _current; if (Current != null) { RemoveNullValues(); _current = current; } } else { _current = this; } mgrs.Add(this); } private void OnDestroy() { if (_current == this) { _current = null; } mgrs.Remove(this); } public void Merge(ES3ReferenceMgrBase otherMgr) { foreach (KeyValuePair item in otherMgr.idRef) { Add(item.Value, item.Key); } } public long Get(UnityEngine.Object obj) { if (!mgrs.Contains(this)) { mgrs.Add(this); } foreach (ES3ReferenceMgrBase mgr in mgrs) { if (!(mgr == null)) { if (obj == null) { return -1L; } if (mgr.refId.TryGetValue(obj, out var value)) { return value; } } } return -1L; } internal UnityEngine.Object Get(long id, Type type, bool suppressWarnings = false) { if (!mgrs.Contains(this)) { mgrs.Add(this); } foreach (ES3ReferenceMgrBase mgr in mgrs) { if (mgr == null) { continue; } if (id == -1) { return null; } if (mgr.idRef.TryGetValue(id, out var value)) { if (value == null) { return null; } return value; } } if (GlobalReferences != null) { UnityEngine.Object obj = GlobalReferences.Get(id); if (obj != null) { return obj; } } if (!suppressWarnings) { if (type != null) { ES3Debug.LogWarning("Reference for " + type?.ToString() + " with ID " + id + " could not be found in Easy Save's reference manager. See the Saving and Loading References guide for more information.", this); } else { ES3Debug.LogWarning("Reference with ID " + id + " could not be found in Easy Save's reference manager. See the Saving and Loading References guide for more information.", this); } } return null; } public UnityEngine.Object Get(long id, bool suppressWarnings = false) { return Get(id, null, suppressWarnings); } public ES3Prefab GetPrefab(long id, bool suppressWarnings = false) { if (!mgrs.Contains(this)) { mgrs.Add(this); } foreach (ES3ReferenceMgrBase mgr in mgrs) { if (mgr == null) { continue; } foreach (ES3Prefab prefab in mgr.prefabs) { if (prefab != null && prefab.prefabId == id) { return prefab; } } } if (!suppressWarnings) { ES3Debug.LogWarning("Prefab with ID " + id + " could not be found in Easy Save's reference manager. Try pressing the Refresh References button on the ES3ReferenceMgr Component of the Easy Save 3 Manager in your scene, or exit play mode and right-click the prefab and select Easy Save 3 > Add Reference(s) to Manager.", this); } return null; } public long GetPrefab(ES3Prefab prefabToFind, bool suppressWarnings = false) { if (!mgrs.Contains(this)) { mgrs.Add(this); } foreach (ES3ReferenceMgrBase mgr in mgrs) { if (mgr == null) { continue; } foreach (ES3Prefab prefab in prefabs) { if (prefab == prefabToFind) { return prefab.prefabId; } } } if (!suppressWarnings) { ES3Debug.LogWarning("Prefab with name " + prefabToFind.name + " could not be found in Easy Save's reference manager. Try pressing the Refresh References button on the ES3ReferenceMgr Component of the Easy Save 3 Manager in your scene, or exit play mode and right-click the prefab and select Easy Save 3 > Add Reference(s) to Manager.", prefabToFind); } return -1L; } public long Add(UnityEngine.Object obj) { if (obj == null) { return -1L; } if (!CanBeSaved(obj)) { return -1L; } if (refId.TryGetValue(obj, out var value)) { return value; } if (GlobalReferences != null) { value = GlobalReferences.GetOrAdd(obj); if (value != -1) { Add(obj, value); return value; } } lock (_lock) { value = GetNewRefID(); return Add(obj, value); } } public long Add(UnityEngine.Object obj, long id) { if (obj == null) { return -1L; } if (!CanBeSaved(obj)) { return -1L; } if (id == -1) { id = GetNewRefID(); } lock (_lock) { idRef[id] = obj; if (obj != null) { refId[obj] = id; } } return id; } public bool AddPrefab(ES3Prefab prefab) { if (!prefabs.Contains(prefab)) { prefabs.Add(prefab); return true; } return false; } public void Remove(UnityEngine.Object obj) { if (!mgrs.Contains(this)) { mgrs.Add(this); } foreach (ES3ReferenceMgrBase mgr in mgrs) { if (mgr == null || (!Application.isPlaying && mgr != this)) { continue; } lock (mgr._lock) { mgr.refId.Remove(obj); foreach (KeyValuePair item in mgr.idRef.Where((KeyValuePair kvp) => kvp.Value == obj).ToList()) { mgr.idRef.Remove(item.Key); } } } } public void Remove(long referenceID) { foreach (ES3ReferenceMgrBase mgr in mgrs) { if (mgr == null) { continue; } lock (mgr._lock) { mgr.idRef.Remove(referenceID); foreach (KeyValuePair item in mgr.refId.Where((KeyValuePair kvp) => kvp.Value == referenceID).ToList()) { mgr.refId.Remove(item.Key); } } } } public void RemoveNullValues() { foreach (long item in (from pair in idRef where pair.Value == null select pair.Key).ToList()) { idRef.Remove(item); } } public void RemoveNullOrInvalidValues() { foreach (long item in (from pair in idRef where pair.Value == null || !CanBeSaved(pair.Value) || excludeObjects.Contains(pair.Value) select pair.Key).ToList()) { idRef.Remove(item); } if (GlobalReferences != null) { GlobalReferences.RemoveInvalidKeys(); } } public void Clear() { lock (_lock) { refId.Clear(); idRef.Clear(); } } public bool Contains(UnityEngine.Object obj) { return refId.ContainsKey(obj); } public bool Contains(long referenceID) { return idRef.ContainsKey(referenceID); } public void ChangeId(long oldId, long newId) { idRef.ChangeKey(oldId, newId); refId = null; } internal static long GetNewRefID() { if (rng == null) { rng = new System.Random(); } byte[] array = new byte[8]; rng.NextBytes(array); return Math.Abs(BitConverter.ToInt64(array, 0) % long.MaxValue); } internal static bool CanBeSaved(UnityEngine.Object obj) { return true; } } }