IndieGame/client/Assets/Plugins/Easy Save 3/Scripts/ES3Prefab.cs

218 lines
7.7 KiB
C#
Raw Normal View History

2024-10-11 10:12:15 +08:00
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<string, string> GetReferences()
{
var localToGlobal = new Dictionary<string, string>();
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<long, long> 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<Transform>();
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<T>(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<T>(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<ES3Prefab>();
if (instanceES3Prefab == null)
throw new MissingReferenceException("Prefab with ID " + prefabId + " was found, but it does not have an ES3Prefab component attached.");
ReadInto<T>(reader, instance);
return instanceES3Prefab.gameObject;
}
public override void ReadInto<T>(ES3Reader reader, object obj)
{
// Load as ES3Refs and convert to longs.
var localToGlobal_refs = reader.ReadProperty<Dictionary<ES3Ref, ES3Ref>>(ES3Type_ES3RefDictionary.Instance);
var localToGlobal = new Dictionary<long, long>();
foreach (var kvp in localToGlobal_refs)
localToGlobal.Add(kvp.Key.id, kvp.Value.id);
if (ES3ReferenceMgrBase.Current == null)
return;
((GameObject)obj).GetComponent<ES3Prefab>().ApplyReferences(localToGlobal);
}
}
}