using System.Collections.Generic; using UnityEngine; using ES3Internal; #if UNITY_EDITOR using UnityEditor; #endif namespace ES3Internal { public class ES3Prefab : MonoBehaviour { public long prefabId = GetNewRefID(); /* * We need to store references to all dependencies of the prefab because only supported scripts will be serialised. * This means that although supported scripts have their dependencies added to the reference manager when we load the prefab, * there will not be any dependencies in the reference manager for scripts which are not supported. So it will not be possible to save any reference to these. */ public ES3RefIdDictionary localRefs = new ES3RefIdDictionary(); public void Awake() { // Add the references to the reference list when this prefab is instantiated. var mgr = ES3ReferenceMgrBase.Current; if (mgr == null) return; foreach (var kvp in localRefs) if (kvp.Key != null) mgr.Add(kvp.Key); } public long Get(UnityEngine.Object obj) { long id; if (localRefs.TryGetValue(obj, out id)) return id; return -1; } public long Add(UnityEngine.Object obj) { long id; if (localRefs.TryGetValue(obj, out id)) return id; if (!ES3ReferenceMgr.CanBeSaved(obj)) return -1; id = GetNewRefID(); localRefs.Add(obj, id); return id; } public Dictionary GetReferences() { var localToGlobal = new Dictionary(); var refMgr = ES3ReferenceMgr.Current; if (refMgr == null) return localToGlobal; foreach (var kvp in localRefs) { long id = refMgr.Add(kvp.Key); localToGlobal[kvp.Value.ToString()] = id.ToString(); } return localToGlobal; } public void ApplyReferences(Dictionary localToGlobal) { if (ES3ReferenceMgrBase.Current == null) return; foreach (var localRef in localRefs) { long globalId; if (localToGlobal.TryGetValue(localRef.Value, out globalId)) ES3ReferenceMgrBase.Current.Add(localRef.Key, globalId); } } public static long GetNewRefID() { return ES3ReferenceMgrBase.GetNewRefID(); } #if UNITY_EDITOR public void GeneratePrefabReferences() { #if UNITY_2021_3_OR_NEWER if (this.gameObject.scene.name != null || UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null) #elif UNITY_2018_3_OR_NEWER if (this.gameObject.scene.name != null || UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null) #else if (this.gameObject.scene.name != null) #endif return; // Create a new reference list so that any objects which are no longer dependencies are removed. var tempLocalRefs = new ES3RefIdDictionary(); // Get dependencies of children also. var transforms = GetComponentsInChildren(); var gos = new GameObject[transforms.Length]; for (int i = 0; i < transforms.Length; i++) gos[i] = transforms[i].gameObject; bool addedNewReference = false; // Add the GameObject's dependencies to the reference list. foreach (var obj in EditorUtility.CollectDependencies(gos)) { var dependency = (UnityEngine.Object)obj; if (obj == null || !ES3ReferenceMgr.CanBeSaved(dependency)) continue; var id = Get(dependency); // If we're adding a new reference, do an Undo.RecordObject to ensure it persists. if (id == -1) { addedNewReference = true; Undo.RecordObject(this, "Update Easy Save 3 Prefab"); EditorUtility.SetDirty(this); } tempLocalRefs.Add(dependency, id == -1 ? GetNewRefID() : id); } if (addedNewReference || tempLocalRefs.Count != localRefs.Count) localRefs = tempLocalRefs; } #endif } } /* * Create a blank ES3Type for ES3Prefab as it does not require serialising/deserialising when stored as a Component. */ namespace ES3Types { [UnityEngine.Scripting.Preserve] public class ES3Type_ES3Prefab : ES3Type { public static ES3Type Instance = null; public ES3Type_ES3Prefab() : base(typeof(ES3Prefab)) { Instance = this; } public override void Write(object obj, ES3Writer writer) { } public override object Read(ES3Reader reader) { return null; } } /* * Use this ES3Type to serialise the . */ public class ES3Type_ES3PrefabInternal : ES3Type { public static ES3Type Instance = new ES3Type_ES3PrefabInternal(); public ES3Type_ES3PrefabInternal() : base(typeof(ES3Type_ES3PrefabInternal)) { Instance = this; } public override void Write(object obj, ES3Writer writer) { ES3Prefab es3Prefab = (ES3Prefab)obj; writer.WriteProperty("prefabId", es3Prefab.prefabId.ToString(), ES3Type_string.Instance); writer.WriteProperty("refs", es3Prefab.GetReferences()); } public override object Read(ES3Reader reader) { var prefabId = reader.ReadRefProperty(); if (ES3ReferenceMgrBase.Current == null) return null; var es3Prefab = ES3ReferenceMgrBase.Current.GetPrefab(prefabId); if (es3Prefab == null) throw new MissingReferenceException("Prefab with ID " + prefabId + " could not be found.\nPress the 'Refresh References' button on the ES3ReferenceMgr Component of the Easy Save 3 Manager in the scene to refresh prefabs."); #if UNITY_EDITOR // Use PrefabUtility.InstantiatePrefab if we're saving in the Editor and the application isn't playing. // This keeps the connection to the prefab, which is useful for Editor scripts saving data outside of runtime. var instance = Application.isPlaying ? GameObject.Instantiate(es3Prefab.gameObject) : PrefabUtility.InstantiatePrefab(es3Prefab.gameObject); #else var instance = GameObject.Instantiate(es3Prefab.gameObject); #endif var instanceES3Prefab = ((GameObject)instance).GetComponent(); if (instanceES3Prefab == null) throw new MissingReferenceException("Prefab with ID " + prefabId + " was found, but it does not have an ES3Prefab component attached."); ReadInto(reader, instance); return instanceES3Prefab.gameObject; } public override void ReadInto(ES3Reader reader, object obj) { // Load as ES3Refs and convert to longs. var localToGlobal_refs = reader.ReadProperty>(ES3Type_ES3RefDictionary.Instance); var localToGlobal = new Dictionary(); foreach (var kvp in localToGlobal_refs) localToGlobal.Add(kvp.Key.id, kvp.Value.id); if (ES3ReferenceMgrBase.Current == null) return; ((GameObject)obj).GetComponent().ApplyReferences(localToGlobal); } } }