diff --git a/Assets/FullInspector2/Core/Editor/IBehaviorEditor.cs b/Assets/FullInspector2/Core/Editor/IBehaviorEditor.cs index 8822c62..c3724ec 100644 --- a/Assets/FullInspector2/Core/Editor/IBehaviorEditor.cs +++ b/Assets/FullInspector2/Core/Editor/IBehaviorEditor.cs @@ -77,7 +77,7 @@ public void Edit(Rect rect, UnityObject behavior) { EditorGUIUtility.labelWidth = fiGUI.PushLabelWidth(GUIContent.none, rect.width); // Run the editor - OnEdit(rect, (TBehavior)behavior, fiPersistentMetadata.GetMetadataFor(behavior)); + OnEdit(rect, (TBehavior)behavior, fiPersistentMetadata.GetMetadataFor(new fiUnityObjectReference(behavior, tryRestore: false))); EditorGUIUtility.labelWidth = savedLabelWidth; @@ -99,7 +99,7 @@ public void Edit(Rect rect, UnityObject behavior) { } public float GetHeight(UnityObject behavior) { - return OnGetHeight((TBehavior)behavior, fiPersistentMetadata.GetMetadataFor(behavior)); + return OnGetHeight((TBehavior)behavior, fiPersistentMetadata.GetMetadataFor(new fiUnityObjectReference(behavior, tryRestore: false))); } } } \ No newline at end of file diff --git a/Assets/FullInspector2/Core/Editor/PropertyEditors/fiGenericPropertyDrawerPropertyEditorManager.cs b/Assets/FullInspector2/Core/Editor/PropertyEditors/fiGenericPropertyDrawerPropertyEditorManager.cs index 4a75bd3..e422f14 100644 --- a/Assets/FullInspector2/Core/Editor/PropertyEditors/fiGenericPropertyDrawerPropertyEditorManager.cs +++ b/Assets/FullInspector2/Core/Editor/PropertyEditors/fiGenericPropertyDrawerPropertyEditorManager.cs @@ -5,6 +5,7 @@ using FullSerializer; using UnityEditor; using UnityEngine; +using System.Reflection; namespace FullInspector.Internal { public class fiGenericPropertyDrawerPropertyEditor : PropertyEditor @@ -147,28 +148,45 @@ public override string ToString() { } } - private static readonly PropertyDrawerContainer[] _propertyDrawers; + private static readonly List _propertyDrawers = new List(); static fiGenericPropertyDrawerPropertyEditorManager() { - _propertyDrawers = - (from assembly in fiRuntimeReflectionUtility.GetAllEditorAssemblies() - from type in assembly.GetTypesWithoutException() - - let attrs = type.GetCustomAttributes(typeof(CustomPropertyDrawer), true) - where attrs != null - - // Do not generate bindings for various the PropertyDrawer - // binders - where type != typeof(fiInspectorOnly_PropertyDrawer) - where type != typeof(fiValuePropertyDrawer) - - from CustomPropertyDrawer attr in attrs - - select new PropertyDrawerContainer { - // Unity renamed these fields after 4.3 - IsInherited = fiRuntimeReflectionUtility.ReadFields(attr, "m_UseForChildren", "useForChildren"), - PropertyType = fiRuntimeReflectionUtility.ReadFields(attr, "m_Type", "Type", "type") // Unity sure likes to rename things... - }).ToArray(); + Assembly[] assemblies = fiRuntimeReflectionUtility.GetAllEditorAssembliesAsArray(); + for (int i = 0; i < assemblies.Length; i++) { + Type[] types = assemblies[i].GetTypesWithoutException(); + for (var j = 0; j < types.Length; j++) { + var type = types[j]; + if (type == typeof(fiInspectorOnly_PropertyDrawer) || type == typeof(fiValuePropertyDrawer)) { + continue; + } + + var attrs = type.GetCustomAttributes(typeof(CustomPropertyDrawer), true) as CustomPropertyDrawer[]; + if (attrs == null) { + continue; + } + + for (int k = 0; k < attrs.Length; k++) { + var attr = attrs[k]; + if (attr == null) { + continue; + } + + var container = new PropertyDrawerContainer() { + // Unity renamed these fields after 4.3 + IsInherited = + fiRuntimeReflectionUtility.ReadFields( + attr, + "m_UseForChildren", + "useForChildren"), + PropertyType = fiRuntimeReflectionUtility.ReadFields(attr, "m_Type", "Type", "type") + + // Unity sure likes to rename things... + }; + + _propertyDrawers.Add(container); + } + } + } } private static bool HasPropertyDrawer(Type type) { diff --git a/Assets/FullInspector2/Core/Editor/fiAutoCleanMissingScripts.cs b/Assets/FullInspector2/Core/Editor/fiAutoCleanMissingScripts.cs index c985f80..7ed2359 100644 --- a/Assets/FullInspector2/Core/Editor/fiAutoCleanMissingScripts.cs +++ b/Assets/FullInspector2/Core/Editor/fiAutoCleanMissingScripts.cs @@ -15,20 +15,19 @@ public static void AutoCleanupMissingScripts() { // NOTE: If this approach doesn't work, then we can use // RemoveComponent with the specific component type to // remove. This is more similar to how RemoveMetadata works. - fiEditorUtility.RemoveMissingScripts(fiPersistentEditorStorage.SceneStorage); - EditorUtility.SetDirty(fiPersistentEditorStorage.SceneStorage); - fiEditorUtility.RemoveMissingScripts(fiPersistentEditorStorage.PrefabStorage); + foreach (var storage in fiPersistentEditorStorage.GetAllCachedSceneStorages()) { + fiEditorUtility.RemoveMissingScripts(storage.gameObject); + EditorUtility.SetDirty(storage); + } + + fiEditorUtility.RemoveMissingScripts(fiPersistentEditorStorage.PrefabStorage.gameObject); EditorUtility.SetDirty(fiPersistentEditorStorage.PrefabStorage); - if (fiPrefabManager.Storage != null) { - fiEditorUtility.RemoveMissingScripts(fiPrefabManager.Storage.gameObject); - EditorUtility.SetDirty(fiPrefabManager.Storage); - } - if (fiSceneManager.Storage != null) { - fiEditorUtility.RemoveMissingScripts(fiSceneManager.Storage.gameObject); - EditorUtility.SetDirty(fiSceneManager.Storage); + if (fiStorageManager.PrefabStorage != null) { + fiEditorUtility.RemoveMissingScripts(fiStorageManager.PrefabStorage .gameObject); + EditorUtility.SetDirty(fiStorageManager.PrefabStorage ); } }); } } -} \ No newline at end of file +} diff --git a/Assets/FullInspector2/Core/Editor/fiCommonSerializedObjectEditor.cs b/Assets/FullInspector2/Core/Editor/fiCommonSerializedObjectEditor.cs index 396d7e0..197cda8 100644 --- a/Assets/FullInspector2/Core/Editor/fiCommonSerializedObjectEditor.cs +++ b/Assets/FullInspector2/Core/Editor/fiCommonSerializedObjectEditor.cs @@ -105,14 +105,12 @@ public void OnDisable() { BehaviorEditor.Get(target.GetType()).OnEditorDeactivate(target); } - private static void ShowBackupButton(UnityObject target) { - if (target is CommonBaseBehavior == false) { + private static void ShowBackupButton(fiUnityObjectReference target) { + if (target.Target is CommonBaseBehavior == false) { return; } - var behavior = (CommonBaseBehavior)target; - - if (fiStorageManager.HasBackups(behavior)) { + if (fiStorageManager.HasBackups(target)) { // TODO: find a better location for these calls fiStorageManager.MigrateStorage(); fiStorageManager.RemoveInvalidBackups(); @@ -129,16 +127,18 @@ private static void ShowBackupButton(UnityObject target) { GUILayout.Space(marginVertical); GUI.Box(boxed, GUIContent.none); + { List toRemove = new List(); GUILayout.BeginVertical(GUILayout.ExpandWidth(true)); - fiBackupEditorGUILayout.DrawBackupsFor(behavior, toRemove); + fiBackupEditorGUILayout.DrawBackupsFor(target, toRemove); GUILayout.EndVertical(); foreach (fiSerializedObject rem in toRemove) { fiStorageManager.RemoveBackup(rem); } + } GUILayout.Space(marginVertical); @@ -169,6 +169,7 @@ private static void DrawOpenScriptButton(UnityObject element) { /* TODO: Support replacing the script with another one. if (newScript != monoScript && element is MonoBehaviour && element is ISerializedObject) { + var root = ((MonoBehaviour)element).gameObject; var newInstance = root.AddComponent(newScript.GetClass()); var newSerialized = new SerializedObject(newInstance); @@ -209,23 +210,19 @@ private static void CheckForNewBaseBehaviorType(Type type) { // 5. When these objects are next serialized, they will use the // incorrect prefab data. Null check, sometimes there is no prefab // state. - if (fiPrefabManager.Storage == null || fiPrefabManager.Storage.SeenBaseBehaviors == null) + if (fiStorageManager.PrefabStorage == null || fiStorageManager.PrefabStorage .SeenBaseBehaviors == null) return; - if (fiPrefabManager.Storage.SeenBaseBehaviors.Contains(type.CSharpName()) == false) { + if (fiStorageManager.PrefabStorage .SeenBaseBehaviors.Contains(type.CSharpName()) == false) { fiLog.Log(typeof(fiCommonSerializedObjectEditor), "Saving all BaseBehaviors of type " + type.CSharpName()); - fiPrefabManager.Storage.SeenBaseBehaviors.Add(type.CSharpName()); - EditorUtility.SetDirty(fiPrefabManager.Storage); + fiStorageManager.PrefabStorage .SeenBaseBehaviors.Add(type.CSharpName()); + EditorUtility.SetDirty(fiStorageManager.PrefabStorage ); fiSaveManager.SaveAll(type); } } - public static void ShowInspectorForSerializedObject(UnityObject target) { - ShowInspectorForSerializedObject(new[] { target }); - } - public static void ShowInspectorForSerializedObject(UnityObject[] targets) { CheckForNewBaseBehaviorType(targets[0].GetType()); DrawOpenScriptButton(targets[0]); @@ -266,8 +263,13 @@ public static void ShowInspectorForSerializedObject(UnityObject[] targets) { } } + private fiUnityObjectReference _fiUnityObjectReference; public override void OnInspectorGUI() { - ShowBackupButton(target); + if (_fiUnityObjectReference == null) { + _fiUnityObjectReference = new fiUnityObjectReference(target, tryRestore: false); + } + + ShowBackupButton(_fiUnityObjectReference); ShowInspectorForSerializedObject(targets); } @@ -290,4 +292,4 @@ public override void OnPreviewSettings() { } } } -} \ No newline at end of file +} diff --git a/Assets/FullInspector2/Core/Editor/fiInspectorOnly_MonoBehaviourEditor.cs b/Assets/FullInspector2/Core/Editor/fiInspectorOnly_MonoBehaviourEditor.cs index 476d349..b1a47a8 100644 --- a/Assets/FullInspector2/Core/Editor/fiInspectorOnly_MonoBehaviourEditor.cs +++ b/Assets/FullInspector2/Core/Editor/fiInspectorOnly_MonoBehaviourEditor.cs @@ -9,9 +9,11 @@ namespace FullInspector.Internal { [CanEditMultipleObjects] [CustomEditor(typeof(MonoBehaviour), true)] public class fiInspectorOnly_MonoBehaviourEditor : Editor { + private readonly UnityEngine.Object[] _targetArr = new UnityEngine.Object[1]; public override void OnInspectorGUI() { if (fsPortableReflection.HasAttribute(target.GetType()) || target is tkCustomEditor) { - fiCommonSerializedObjectEditor.ShowInspectorForSerializedObject(target); + _targetArr[0] = target; + fiCommonSerializedObjectEditor.ShowInspectorForSerializedObject(_targetArr); } else { base.OnInspectorGUI(); diff --git a/Assets/FullInspector2/Core/Editor/fiLateBindingsBinder.cs b/Assets/FullInspector2/Core/Editor/fiLateBindingsBinder.cs index ee97dcf..9a64630 100644 --- a/Assets/FullInspector2/Core/Editor/fiLateBindingsBinder.cs +++ b/Assets/FullInspector2/Core/Editor/fiLateBindingsBinder.cs @@ -1,20 +1,21 @@ -using System; -using System.Collections.Generic; +using UnityEngine; using UnityEditor; -using UnityEngine; +using UnityEditor.SceneManagement; namespace FullInspector.Internal { - // note: See the docs on fiLateBindings This is just the actual injection - // code which only gets run if we're in an editor + // note: See the docs on fiLateBindings + // This is just the actual injection code which only gets run if we're in an editor // - // note: If there is ever a binding that doesn't occur quickly enough, then - // we can use reflection to discover it immediately + // note: If there is ever a binding that doesn't occur quickly enough, then we can use + // reflection to discover it immediately [InitializeOnLoad] public class fiLateBindingsBinder { static fiLateBindingsBinder() { fiLateBindings._Bindings._AssetDatabase_LoadAssetAtPath = AssetDatabase.LoadAssetAtPath; + fiLateBindings._Bindings._AssetDatabase_SaveAssets = AssetDatabase.SaveAssets; + fiLateBindings._Bindings._EditorApplication_isPlaying = () => EditorApplication.isPlaying; fiLateBindings._Bindings._EditorApplication_isCompilingOrChangingToPlayMode = () => EditorApplication.isCompiling || EditorApplication.isPlayingOrWillChangePlaymode; @@ -26,24 +27,22 @@ static fiLateBindingsBinder() { }; EditorApplication.update += updateFn; }; - fiLateBindings._Bindings._EditorApplication_Callbacks = new List(); - fiLateBindings._Bindings._EditorApplication_CallbacksToBeAdded = new List(); - fiLateBindings._Bindings._EditorApplication_CallbacksToBeRemoved = new List(); - fiLateBindings._Bindings._EditorApplication_AddUpdateAction = a => fiLateBindings._Bindings._EditorApplication_CallbacksToBeAdded.Add(a); - fiLateBindings._Bindings._EditorApplication_RemUpdateAction = a => fiLateBindings._Bindings._EditorApplication_CallbacksToBeRemoved.Add(a); - EditorApplication.update -= OnEditorUpdate; - EditorApplication.update += OnEditorUpdate; - + fiLateBindings._Bindings._EditorApplication_AddUpdateAction = a => EditorApplication.update += new EditorApplication.CallbackFunction(a); + fiLateBindings._Bindings._EditorApplication_RemUpdateAction = a => EditorApplication.update -= new EditorApplication.CallbackFunction(a); fiLateBindings._Bindings._EditorApplication_timeSinceStartup = () => EditorApplication.timeSinceStartup; + fiLateBindings._Bindings._EditorPrefs_GetString = EditorPrefs.GetString; fiLateBindings._Bindings._EditorPrefs_SetString = EditorPrefs.SetString; + fiLateBindings._Bindings._EditorUtility_SetDirty = EditorUtility.SetDirty; + fiLateBindings._Bindings._SceneManagement_EditorSceneManager_MarkSceneDirty = EditorSceneManager.MarkSceneDirty; fiLateBindings._Bindings._EditorUtility_InstanceIdToObject = EditorUtility.InstanceIDToObject; fiLateBindings._Bindings._EditorUtility_IsPersistent = EditorUtility.IsPersistent; fiLateBindings._Bindings._EditorUtility_CreateGameObjectWithHideFlags = (name, flags) => EditorUtility.CreateGameObjectWithHideFlags(name, flags); + fiLateBindings._Bindings._EditorGUI_BeginChangeCheck = EditorGUI.BeginChangeCheck; fiLateBindings._Bindings._EditorGUI_EndChangeCheck = EditorGUI.EndChangeCheck; fiLateBindings._Bindings._EditorGUI_BeginDisabledGroup = EditorGUI.BeginDisabledGroup; @@ -54,19 +53,24 @@ static fiLateBindingsBinder() { fiLateBindings._Bindings._EditorGUI_Popup = EditorGUI.Popup; fiLateBindings._Bindings._EditorGUI_Slider = EditorGUI.Slider; + fiLateBindings.EditorGUIUtility.standardVerticalSpacing = EditorGUIUtility.standardVerticalSpacing; fiLateBindings.EditorGUIUtility.singleLineHeight = EditorGUIUtility.singleLineHeight; + fiLateBindings._Bindings._EditorStyles_label = () => EditorStyles.label; fiLateBindings._Bindings._EditorStyles_foldout = () => EditorStyles.foldout; + fiLateBindings._Bindings._fiEditorGUI_PushHierarchyMode = state => fiEditorGUI.PushHierarchyMode(state); fiLateBindings._Bindings._fiEditorGUI_PopHierarchyMode = () => fiEditorGUI.PopHierarchyMode(); + fiLateBindings._Bindings._PrefabUtility_CreatePrefab = (string path, GameObject template) => PrefabUtility.CreatePrefab(path, template); fiLateBindings._Bindings._PrefabUtility_IsPrefab = unityObj => PrefabUtility.GetPrefabType(unityObj) == PrefabType.Prefab; fiLateBindings._Bindings._PrefabUtility_IsPrefabInstance = unityObj => PrefabUtility.GetPrefabType(unityObj) == PrefabType.PrefabInstance; + fiLateBindings._Bindings._PropertyEditor_Edit = (objType, attrs, rect, label, obj, metadata, skippedEditors) => PropertyEditor.Get(objType, attrs).SkipUntilNot(skippedEditors).Edit(rect, label, obj, metadata); @@ -74,6 +78,7 @@ static fiLateBindingsBinder() { (objType, attrs, label, obj, metadata, skippedEditors) => PropertyEditor.Get(objType, attrs).SkipUntilNot(skippedEditors).GetElementHeight(label, obj, metadata); + fiLateBindings._Bindings._PropertyEditor_EditSkipUntilNot = (skipUntilNot, objType, attrs, rect, label, obj, metadata) => PropertyEditor.Get(objType, attrs).SkipUntilNot(skipUntilNot).Edit(rect, label, obj, metadata); @@ -81,6 +86,7 @@ static fiLateBindingsBinder() { (skipUntilNot, objType, attrs, label, obj, metadata) => PropertyEditor.Get(objType, attrs).SkipUntilNot(skipUntilNot).GetElementHeight(label, obj, metadata); + fiLateBindings._Bindings._Selection_activeObject = () => Selection.activeObject; fiLateBindings._Bindings._Selection_activeSelection = () => { if (Selection.activeObject is GameObject) @@ -116,4 +122,4 @@ public static void EnsureLoaded() { // no-op, but it ensures that the static constructor has executed } } -} \ No newline at end of file +} diff --git a/Assets/FullInspector2/Core/Editor/fiSaveManager.cs b/Assets/FullInspector2/Core/Editor/fiSaveManager.cs index fc54da5..c56627b 100644 --- a/Assets/FullInspector2/Core/Editor/fiSaveManager.cs +++ b/Assets/FullInspector2/Core/Editor/fiSaveManager.cs @@ -54,7 +54,10 @@ public static void RestoreAll() { [MenuItem("Window/Full Inspector/Developer/Remove Metadata", priority = 2)] public static void RemoveMetadata() { - fiUtility.DestroyObject(fiPersistentEditorStorage.SceneStorage); + foreach (var storage in fiPersistentEditorStorage.GetAllCachedSceneStorages()) { + fiUtility.DestroyObject(storage); + } + fiUtility.DestroyObject(fiPersistentEditorStorage.PrefabStorage); } diff --git a/Assets/FullInspector2/Core/Utility/fiAssemblyExtensions.cs b/Assets/FullInspector2/Core/Utility/fiAssemblyExtensions.cs index 6b94d50..a36e0f0 100644 --- a/Assets/FullInspector2/Core/Utility/fiAssemblyExtensions.cs +++ b/Assets/FullInspector2/Core/Utility/fiAssemblyExtensions.cs @@ -1,13 +1,20 @@ using System; using System.Reflection; +using System.Collections.Generic; namespace FullInspector.Internal { public static class fiAssemblyExtensions { - private static Type[] s_EmptyArray = { }; + private static readonly Type[] s_EmptyArray = { }; + private static readonly Dictionary s_assemblyTotypeCache = new Dictionary(); public static Type[] GetTypesWithoutException(this Assembly assembly) { try { - return assembly.GetTypes(); + Type[] types; + if (!s_assemblyTotypeCache.TryGetValue(assembly, out types)) { + s_assemblyTotypeCache[assembly] = types = assembly.GetTypes(); + } + + return types; } catch { return s_EmptyArray; diff --git a/Assets/FullInspector2/Core/fiLateBindings.cs b/Assets/FullInspector2/Core/fiLateBindings.cs index aa00e11..b737765 100644 --- a/Assets/FullInspector2/Core/fiLateBindings.cs +++ b/Assets/FullInspector2/Core/fiLateBindings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reflection; using UnityEngine; +using UnityEngine.SceneManagement; using UnityObject = UnityEngine.Object; namespace FullInspector.Internal { @@ -16,6 +17,7 @@ namespace FullInspector.Internal { public static class fiLateBindings { public static class _Bindings { public static Func _AssetDatabase_LoadAssetAtPath; + public static Action _AssetDatabase_SaveAssets; public static Func _EditorApplication_isPlaying; public static Func _EditorApplication_isCompilingOrChangingToPlayMode; @@ -31,6 +33,7 @@ public static class _Bindings { public static Action _EditorPrefs_SetString; public static Action _EditorUtility_SetDirty; + public static Func _SceneManagement_EditorSceneManager_MarkSceneDirty; public static Func _EditorUtility_InstanceIdToObject; public static Func _EditorUtility_IsPersistent; public static Func _EditorUtility_CreateGameObjectWithHideFlags; @@ -81,6 +84,12 @@ public static UnityObject LoadAssetAtPath(string path, Type type) { } return null; } + + public static void SaveAssets() { + if (VerifyBinding("AssetDatabase.SaveAssets", _Bindings._AssetDatabase_SaveAssets)) { + _Bindings._AssetDatabase_SaveAssets(); + } + } } public static class EditorApplication { @@ -179,6 +188,19 @@ public static GameObject CreateGameObjectWithHideFlags(string name, HideFlags hi } } + + public static class SceneManagement { + public static class EditorSceneManager { + public static bool MarkSceneDirty(Scene scene) { + if (VerifyBinding("EditorSceneManager.MarkSceneDirty", _Bindings._SceneManagement_EditorSceneManager_MarkSceneDirty)) { + return _Bindings._SceneManagement_EditorSceneManager_MarkSceneDirty(scene); + } + + return false; + } + } + } + public static class EditorGUI { public static void BeginChangeCheck() { if (VerifyBinding("EditorGUI.BeginChangeCheck", _Bindings._EditorGUI_BeginDisabledGroup)) { diff --git a/Assets/FullInspector2/Core/fiRuntimeReflectionUtility.cs b/Assets/FullInspector2/Core/fiRuntimeReflectionUtility.cs index de02e4d..59bc240 100644 --- a/Assets/FullInspector2/Core/fiRuntimeReflectionUtility.cs +++ b/Assets/FullInspector2/Core/fiRuntimeReflectionUtility.cs @@ -260,6 +260,21 @@ where IsUnityEditorAssembly(assembly) == false #endif } private static List _cachedUserDefinedEditorAssemblies; + private static Assembly[] _cachedUserDefinedEditorAssembliesArray; + + + /// + /// Samve as but returns an array. + /// Useful for tighter loops. + /// + public static Assembly[] GetAllEditorAssembliesAsArray() { + if (_cachedUserDefinedEditorAssembliesArray == null) { + _cachedUserDefinedEditorAssembliesArray = GetAllEditorAssemblies().ToArray(); + } + + return _cachedUserDefinedEditorAssembliesArray; + } + /// /// Gets all possible editor assemblies, including those defined by diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupEditorGUILayout.cs b/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupEditorGUILayout.cs index b21b46b..f4960ad 100644 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupEditorGUILayout.cs +++ b/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupEditorGUILayout.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using FullInspector.Internal; +using FullInspector.Internal; +using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityObject = UnityEngine.Object; @@ -10,15 +10,22 @@ namespace FullInspector.BackupService { /// viewer. /// public class fiBackupEditorGUILayout { - public static void DrawBackupsFor(UnityObject target, List toRemove) { + + public static void DrawBackupsFor(fiUnityObjectReference target, List toRemove) { bool showSpace = false; fiEditorGUI.PushHierarchyMode(false); fiGraphMetadata metadata = fiPersistentMetadata.GetMetadataFor(target); - foreach (fiSerializedObject backup in fiStorageManager.SerializedObjects) { - if (backup.Target.Target != target) { + var t = target.Target as CommonBaseBehavior; + if (t == null) { + return; + } + + var backups = fiBackupManager.GetBackupsFor(t); + foreach (fiSerializedObject backup in backups) { + if (!Equals(backup.Target, target)) { continue; } @@ -88,7 +95,7 @@ public static void DisplayDeserializedObject(fiDeserializedObject obj, fiGraphMe string label = member.InspectedProperty.DisplayName; if (member.ShouldRestore.Enabled) { - editor.FirstEditor.EditWithGUILayout(new GUIContent(label), member.Value, metadata.Enter(label, null)); + editor.FirstEditor.EditWithGUILayout(new GUIContent(label), member.Value, metadata.Enter(label, metadata.Context)); } else { GUILayout.Label(new GUIContent(label + " (will not restore)")); @@ -107,4 +114,4 @@ public static void DisplayDeserializedObject(fiDeserializedObject obj, fiGraphMe EditorGUILayout.EndHorizontal(); } } -} \ No newline at end of file +} diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupEditorWindow.cs b/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupEditorWindow.cs index e28e2fc..473db5a 100644 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupEditorWindow.cs +++ b/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupEditorWindow.cs @@ -1,7 +1,7 @@ -using System; +using FullInspector.Internal; +using System; using System.Collections.Generic; using System.Linq; -using FullInspector.Internal; using UnityEditor; using UnityEngine; using UnityObject = UnityEngine.Object; @@ -40,23 +40,21 @@ private bool PassesFilter(UnityObject target) { return target.name.IndexOf(NameFilter, StringComparison.CurrentCultureIgnoreCase) >= 0; } - private IEnumerable Targets(IEnumerable backups) { - var targets = new HashSet(); + private IEnumerable Targets(IEnumerable backups) { + var targets = new HashSet(); foreach (var backup in backups) { - targets.Add(backup.Target.Target); + targets.Add(backup.Target); } - return targets.OrderBy(obj => obj.name); + return targets.OrderBy(obj => obj.Target.name); } private void OnGUI() { var savedHierarchyMode = EditorGUIUtility.hierarchyMode; EditorGUIUtility.hierarchyMode = false; - fiStorageManager.MigrateStorage(); fiStorageManager.RemoveInvalidBackups(); - Repaint(); //GUILayout.Label("Full Inspector Dynamic Object Saved State Solution", EditorStyles.boldLabel); @@ -80,7 +78,8 @@ private void OnGUI() { EditorGUILayout.Space(); - if (fiStorageManager.SerializedObjects.Count() == 0) { + var allObjects = fiStorageManager.GetAllSerializedObjects().ToList(); + if (!allObjects.Any()) { EditorGUILayout.HelpBox( "It looks like you don't have any backups! To create one, simply right click a " + "supported behavior and hit the Backup context menu item. This will work in both " + @@ -94,14 +93,14 @@ private void OnGUI() { _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, GUIStyle.none); var toRemove = new List(); - foreach (UnityObject target in Targets(fiStorageManager.SerializedObjects)) { - if (PassesFilter(target) == false) { + foreach (fiUnityObjectReference target in Targets(allObjects)) { + if (PassesFilter(target.Target) == false) { continue; } EditorGUILayout.BeginHorizontal(); - EditorGUILayout.LabelField("Target (" + target.GetType().Name + ")", EditorStyles.boldLabel); - EditorGUILayout.ObjectField(target, target.GetType(), false, GUILayout.Width(200)); + EditorGUILayout.LabelField("Target (" + target.Target.GetType().Name + ")", EditorStyles.boldLabel); + EditorGUILayout.ObjectField(target.Target, target.GetType(), false, GUILayout.Width(200)); EditorGUILayout.EndHorizontal(); GUILayout.Space(4); @@ -116,8 +115,7 @@ private void OnGUI() { fiEditorGUILayout.Splitter(3); } - foreach (fiSerializedObject backup in toRemove) { - Undo.RecordObject(fiStorageManager.PersistentStorage, "Undo Backup Removal"); + foreach (var backup in toRemove) { fiStorageManager.RemoveBackup(backup); } diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupManager.cs b/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupManager.cs index d944209..861899b 100644 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupManager.cs +++ b/Assets/FullInspector2/Modules/BackupService/Editor/fiBackupManager.cs @@ -1,6 +1,6 @@ -using System; +using FullInspector.Internal; +using System; using System.Collections.Generic; -using FullInspector.Internal; using UnityEditor; using UnityEngine; @@ -14,7 +14,8 @@ public static class fiBackupManager { /// Returns all backups for the given object. /// public static IEnumerable GetBackupsFor(CommonBaseBehavior behavior) { - foreach (var backup in fiStorageManager.SerializedObjects) { + var allObjects = fiStorageManager.GetAllSerializedObjects(); + foreach (var backup in allObjects) { if (backup.Target.Target == behavior) { yield return backup; } @@ -31,19 +32,21 @@ public static void CreateBackup(Component behavior) { // serialization failed if (serialized == null) { + Debug.Log("Failed to serialize object. Backup failed."); return; } + fiStorageComponent storage; if (PrefabUtility.GetPrefabType(behavior) == PrefabType.Prefab) { - fiPrefabManager.Storage.Objects.Add(serialized); - EditorGUIUtility.PingObject(fiPrefabManager.Storage); - } - else { - fiStorageManager.PersistentStorage.Objects.Add(serialized); - EditorGUIUtility.PingObject(fiStorageManager.PersistentStorage); + storage = fiStorageManager.PrefabStorage ; + } else { + var target = serialized.Target.Target; + storage = fiStorageManager.GetPersistentStorage(target.GetInstanceID()); } - fiPrefabManager.SetDirty(); + storage.Objects.Add(serialized); + storage.SetDirty(); + EditorGUIUtility.PingObject(storage); } /// @@ -77,6 +80,7 @@ public static void RestoreBackup(fiSerializedObject serializedState) { Type serializerType = BehaviorTypeToSerializerTypeMap.GetSerializerType(targetType); var serializer = (BaseSerializer)fiSingletons.Get(serializerType); + Undo.RegisterCompleteObjectUndo(serializedState.Target.Target, "Restored backup"); foreach (fiSerializedMember member in serializedState.Members) { // user requested a skip for restoring this property if (member.ShouldRestore.Enabled == false) { @@ -141,4 +145,4 @@ private static fiSerializedObject Serialize(Component target) { return state; } } -} \ No newline at end of file +} diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiContextMenus.cs b/Assets/FullInspector2/Modules/BackupService/Editor/fiContextMenus.cs index 593aabc..9cb094a 100644 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiContextMenus.cs +++ b/Assets/FullInspector2/Modules/BackupService/Editor/fiContextMenus.cs @@ -4,12 +4,9 @@ namespace FullInspector.BackupService { public class fiContextMenus { // We support backup for *all* components derived from BaseBehavior - [MenuItem("CONTEXT/CommonBaseBehavior/Backup")] + [MenuItem("CONTEXT/CommonBaseBehavior/\ud83d\udcbe Backup")] public static void BackupBaseBehavior(MenuCommand command) { - if (command.context is Component) { - var component = (Component)command.context; - fiBackupManager.CreateBackup(component); - } + TryBackup(command.context); } // We have to white-list other components, to verify that they work with @@ -17,10 +14,14 @@ public static void BackupBaseBehavior(MenuCommand command) { // better support in the future for more components, we might need to // introduction an abstraction into CreateBackup. - [MenuItem("CONTEXT/Transform/Backup")] + [MenuItem("CONTEXT/Transform/\ud83d\udcbe Backup")] public static void BackupTransform(MenuCommand command) { - if (command.context is Component) { - var component = (Component)command.context; + TryBackup(command.context); + } + + private static void TryBackup(Object o) { + var component = o as Component; + if (component != null) { fiBackupManager.CreateBackup(component); } } diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiPrefabManager.cs b/Assets/FullInspector2/Modules/BackupService/Editor/fiPrefabManager.cs index 7ced4be..e63f26c 100644 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiPrefabManager.cs +++ b/Assets/FullInspector2/Modules/BackupService/Editor/fiPrefabManager.cs @@ -4,10 +4,9 @@ namespace FullInspector.BackupService { /// - /// Manages backups stored in prefab storage container. The prefab container - /// stores backups when Unity is in play-mode and when the backup target does - /// not live in the scene (an example would be a backup targeting another - /// prefab). + /// Manages backups stored in prefab storage container. The prefab container stores backups + /// when Unity is in play-mode and when the backup target does not live in the scene (an + /// example would be a backup targeting another prefab). /// public static class fiPrefabManager { private static string PrefabPath = fiUtility.CombinePaths(fiSettings.RootGeneratedDirectory, "fiBackupStorage.prefab"); diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiPrefabManager.cs.meta b/Assets/FullInspector2/Modules/BackupService/Editor/fiPrefabManager.cs.meta deleted file mode 100644 index cbdb47e..0000000 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiPrefabManager.cs.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 9d8d8e9f83b8edd468c39006cf7ead2d -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiSceneManager.cs b/Assets/FullInspector2/Modules/BackupService/Editor/fiSceneManager.cs deleted file mode 100644 index d0f8fa3..0000000 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiSceneManager.cs +++ /dev/null @@ -1,39 +0,0 @@ -using FullInspector.Internal; -using UnityEditor; -using UnityEngine; - -namespace FullInspector.BackupService { - /// - /// Manages the backup storage that lives in the scene. - /// - public class fiSceneManager { - private const string SceneStorageName = "fiBackupSceneStorage"; - private static fiStorageComponent _storage; - - private static void FindAndRemoveStaleObjects() { - GameObject oldObject = GameObject.Find(SceneStorageName); - if (oldObject != null && oldObject.GetComponent() == null) - fiUtility.DestroyObject(oldObject); - } - - public static fiStorageComponent Storage { - get { - if (_storage == null) { - FindAndRemoveStaleObjects(); - _storage = GameObject.FindObjectOfType(); - - if (_storage == null) { - // If we use new GameObject(), then for a split second - // Unity will show the game object in the hierarchy, - // which is bad UX. - var obj = EditorUtility.CreateGameObjectWithHideFlags(SceneStorageName, - HideFlags.HideInHierarchy); - _storage = obj.AddComponent(); - } - } - - return _storage; - } - } - } -} \ No newline at end of file diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiSceneManager.cs.meta b/Assets/FullInspector2/Modules/BackupService/Editor/fiSceneManager.cs.meta deleted file mode 100644 index 96f3c16..0000000 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiSceneManager.cs.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 88eadc0aa228d33439f39338fbce4c30 -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: diff --git a/Assets/FullInspector2/Modules/BackupService/Editor/fiStorageManager.cs b/Assets/FullInspector2/Modules/BackupService/Editor/fiStorageManager.cs index 088d614..1c85aad 100644 --- a/Assets/FullInspector2/Modules/BackupService/Editor/fiStorageManager.cs +++ b/Assets/FullInspector2/Modules/BackupService/Editor/fiStorageManager.cs @@ -1,36 +1,53 @@ -using System.Collections.Generic; +using System.Linq; +using System.Collections.Generic; using FullInspector.Internal; using UnityEditor; using UnityEngine; +using FullInspector.StoragesManager; namespace FullInspector.BackupService { + /// /// This class provides a unified API for accessing backups across scene and /// prefab storage. /// public static class fiStorageManager { + private const string SceneStorageName = "fiBackupSceneStorage"; + + private static readonly string _PrefabStoragePath = + fiUtility.CombinePaths(fiSettings.RootGeneratedDirectory, "fiBackupStorage.prefab"); + + private static readonly fiStoragesManager _StoragesManager = + new fiStoragesManager(SceneStorageName, _PrefabStoragePath); + + + public static fiStorageComponent PrefabStorage { + get { return _StoragesManager.PrefabStorage; } + } + /// /// Returns the storage component that is currently best suited for use. /// - public static fiStorageComponent PersistentStorage { - get { - // If we're playing, scene storage will not persist, so we *must* - // use prefab storage - if (Application.isPlaying) { - return fiPrefabManager.Storage; - } + public static fiStorageComponent GetPersistentStorage(int instanceID) { + var o = fiLateBindings.EditorUtility.InstanceIDToObject(instanceID); + var isPersistent = AssetDatabase.Contains(o); - return fiSceneManager.Storage; - } + // If we're playing, scene storage will not persist, so we *must* use prefab storage + return isPersistent || Application.isPlaying + ? _StoragesManager.PrefabStorage + : _StoragesManager.GetStorage(o); } /// /// Removes the given backup instance. /// public static void RemoveBackup(fiSerializedObject serializedObj) { - fiSceneManager.Storage.Objects.Remove(serializedObj); - if (fiPrefabManager.Storage.Objects.Remove(serializedObj)) { - fiPrefabManager.SetDirty(); + var storage = GetPersistentStorage(serializedObj.Target.Target.GetInstanceID()); + var hasObject = storage.Objects.Contains(serializedObj); + if (hasObject) { + Undo.RegisterCompleteObjectUndo(storage, "Undo Backup Removal"); + storage.Objects.Remove(serializedObj); + storage.SetDirty(); } } @@ -39,12 +56,15 @@ public static void RemoveBackup(fiSerializedObject serializedObj) { /// etc). /// public static void RemoveInvalidBackups() { - fiSceneManager.Storage.RemoveInvalidBackups(); - fiPrefabManager.Storage.RemoveInvalidBackups(); + _StoragesManager.PrefabStorage.RemoveInvalidBackups(); + foreach (var sceneStorage in _StoragesManager.GetAllSceneStorages()) { + sceneStorage.RemoveInvalidBackups(); + } } /// - /// Attempts to migrate prefab storage into scene storage. + /// Attempts to migrate prefab storage and cross-scene references + /// into correct scene storage. /// public static void MigrateStorage() { // We cannot migrate data while playing -- scene storage will not be @@ -52,74 +72,92 @@ public static void MigrateStorage() { if (Application.isPlaying) { return; } + var i = 0; + //migrate cross-scene references + //this will still show warnings, as Unity will show then the moment it detects cross-scene refs + //we at least won't lose the backups. + //currently, we don't have a similar solution for fiPersistendEditorStorage, + //so it will be ugly and show warnings when moving objects around + //and when saving. Lots of warnings. + foreach (var storage in _StoragesManager.GetAllSceneStorages()) { + while (i < storage.Objects.Count) { + fiSerializedObject fiSerializedObject = storage.Objects[i]; + var unityObject = fiSerializedObject.Target.Target; + var c = unityObject as Component; + if (c == null || + !c.gameObject.scene.IsValid() || + !c.gameObject.scene.isLoaded || + c.gameObject.scene == storage.gameObject.scene) { + ++i; + continue; + } + + var correctStorage = _StoragesManager.GetStorage(c.gameObject.scene); + correctStorage.Objects.Add(fiSerializedObject); + storage.Objects.RemoveAt(i); + UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(storage.gameObject.scene); + UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(correctStorage.gameObject.scene); + } + } // Nothing to migrate. - if (fiPrefabManager.Storage.Objects.Count == 0) { + if (_StoragesManager.PrefabStorage.Objects.Count == 0) { return; } // Migrate everything except prefabs into storage. - int i = 0; - while (i < fiPrefabManager.Storage.Objects.Count) { - fiSerializedObject obj = fiPrefabManager.Storage.Objects[i]; - - // If the target object is persistent (ie, not from the scene or - // a temporary object), then we want to keep it in the prefab - // storage. - if (AssetDatabase.Contains(obj.Target.Target)) { + //Debug.Log("_StoragesManager.PrefabStorage has " + _StoragesManager.PrefabStorage.Objects.Count + " objects"); + while (i < _StoragesManager.PrefabStorage.Objects.Count) { + var obj = _StoragesManager.PrefabStorage.Objects[i]; + var target = _StoragesManager.PrefabStorage.Objects[i].Target.Target; + + var sceneStorage = GetPersistentStorage(target.GetInstanceID()); + + //this means that the object is persistent or we failed to retrieve a storage + if (sceneStorage == _StoragesManager.PrefabStorage) { ++i; + continue; } - // This appears to be a scene object, so migrate it to scene - // storage. - else { - fiSceneManager.Storage.Objects.Add(obj); - fiPrefabManager.Storage.Objects.RemoveAt(i); - fiPrefabManager.SetDirty(); - } + var o = fiLateBindings.EditorUtility.InstanceIDToObject(target.GetInstanceID()); + var state = new fiSerializedObject() + { + Target = new fiUnityObjectReference(o, false), + SavedAt = obj.SavedAt + }; + + //Debug.Log("Migrating " + state.Target.Target.name + " to new storage", o); + sceneStorage.Objects.Add(state); + sceneStorage.SetDirty(); + EditorGUIUtility.PingObject(sceneStorage); + _StoragesManager.PrefabStorage.Objects.Remove(obj); + _StoragesManager.PrefabStorage.SetDirty(); + EditorGUIUtility.PingObject(_StoragesManager.PrefabStorage); } + + } /// - /// Returns true if there is a backup for the given behavior. + /// Returns true if there is a backup for the given target. /// - public static bool HasBackups(CommonBaseBehavior behavior) { - // TODO: Maybe this should be triggering? If a user reports a bug - // about having backups not work then this is probably the - // cause... - if (fiSceneManager.Storage == null || fiPrefabManager.Storage == null) - return false; - - for (int i = 0; i < fiSceneManager.Storage.Objects.Count; ++i) { - var backup = fiSceneManager.Storage.Objects[i]; - if (backup.Target.Target == behavior) { - return true; - } - } - - for (int i = 0; i < fiPrefabManager.Storage.Objects.Count; ++i) { - var backup = fiPrefabManager.Storage.Objects[i]; - if (backup.Target.Target == behavior) { - return true; - } - } - - return false; + public static bool HasBackups(fiUnityObjectReference target) { + return GetAllSerializedObjects().Any(s => Equals(s.Target, target)); } /// /// Returns every serialized object. /// - public static IEnumerable SerializedObjects { - get { - foreach (var obj in fiSceneManager.Storage.Objects) { - yield return obj; - } + public static IEnumerable GetAllSerializedObjects() { - foreach (var obj in fiPrefabManager.Storage.Objects) { - yield return obj; - } + foreach (var obj in _StoragesManager.GetAllSceneStorages().SelectMany(list => list.Objects)) { + yield return obj; + } + + foreach (var obj in _StoragesManager.PrefabStorage.Objects) { + yield return obj; } } + } -} \ No newline at end of file +} diff --git a/Assets/FullInspector2/Modules/BackupService/fiStorageComponent.cs b/Assets/FullInspector2/Modules/BackupService/fiStorageComponent.cs index 2e48a30..3891afe 100644 --- a/Assets/FullInspector2/Modules/BackupService/fiStorageComponent.cs +++ b/Assets/FullInspector2/Modules/BackupService/fiStorageComponent.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using FullInspector.Internal; +using FullInspector.Internal; +using System.Collections.Generic; using UnityEngine; namespace FullInspector.BackupService { @@ -24,15 +24,42 @@ public class fiStorageComponent : MonoBehaviour, fiIEditorOnlyTag { /// Removes all backups that no longer have a target. /// public void RemoveInvalidBackups() { + bool removedAny = false; int i = 0; while (i < Objects.Count) { if (Objects[i].Target.Target == null) { Objects.RemoveAt(i); + removedAny = true; } else { ++i; } } + + if (removedAny) { + SetDirty(); + } } + + + /// + /// Unified method to set the storage dirty. Works with both prefab and scene storages. + /// + public void SetDirty() { + if (this == null) { + return; + } + + if (fiLateBindings.PrefabUtility.IsPrefab(this)) { + fiLateBindings.EditorUtility.SetDirty(gameObject); + fiLateBindings.AssetDatabase.SaveAssets(); + return; + } + + if (!Application.isPlaying && gameObject.scene.IsValid()) { + fiLateBindings.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene); + } + } + } -} \ No newline at end of file +} diff --git a/Assets/FullInspector2/Modules/PersistentMetadata/StorageComponents/fiPersistentEditorStorageComponent.cs b/Assets/FullInspector2/Modules/PersistentMetadata/StorageComponents/fiPersistentEditorStorageComponent.cs new file mode 100644 index 0000000..6703b72 --- /dev/null +++ b/Assets/FullInspector2/Modules/PersistentMetadata/StorageComponents/fiPersistentEditorStorageComponent.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +namespace FullInspector.Internal { + /// + /// Shell component just so we can easily search for our storages. + /// + [AddComponentMenu("")] + public class fiPersistentEditorStorageComponent: MonoBehaviour, fiIEditorOnlyTag { } +} diff --git a/Assets/FullInspector2/Modules/PersistentMetadata/StorageComponents/fiPersistentEditorStorageComponent.cs.meta b/Assets/FullInspector2/Modules/PersistentMetadata/StorageComponents/fiPersistentEditorStorageComponent.cs.meta new file mode 100644 index 0000000..14a0e92 --- /dev/null +++ b/Assets/FullInspector2/Modules/PersistentMetadata/StorageComponents/fiPersistentEditorStorageComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 236923e06fa149ad8540eb31b87a9788 +timeCreated: 1490357681 \ No newline at end of file diff --git a/Assets/FullInspector2/Modules/PersistentMetadata/fiPersistentEditorStorage.cs b/Assets/FullInspector2/Modules/PersistentMetadata/fiPersistentEditorStorage.cs index 1fac5e8..7f89182 100644 --- a/Assets/FullInspector2/Modules/PersistentMetadata/fiPersistentEditorStorage.cs +++ b/Assets/FullInspector2/Modules/PersistentMetadata/fiPersistentEditorStorage.cs @@ -2,10 +2,21 @@ using System.Collections.Generic; using System.Linq; using FullSerializer; -using UnityEngine; +using FullInspector.StoragesManager; +using UnityObject = UnityEngine.Object; namespace FullInspector.Internal { + public class fiPersistentEditorStorage { + + private const string SceneStorageName = "fiPersistentEditorStorage"; + + private static readonly string _PrefabStoragePath = + fiUtility.CombinePaths(fiSettings.RootGeneratedDirectory, "fiPersistentEditorStorage.prefab"); + + private static readonly fiStoragesManager _StoragesManager = + new fiStoragesManager(SceneStorageName, _PrefabStoragePath); + #region Reading/Writing public static void Reset(fiUnityObjectReference key) { fiBaseStorageComponent storage; @@ -13,90 +24,42 @@ public static void Reset(fiUnityObjectReference key) { storage = GetStorageDictionary(PrefabStorage); } else { - storage = GetStorageDictionary(SceneStorage); + storage = GetStorageDictionary(GetStorage(key.Target)); } - storage.Data.Remove(key.Target); - fiLateBindings.EditorUtility.SetDirty(storage); + if (storage != null) { + storage.Data.Remove(key.Target); + fiLateBindings.EditorUtility.SetDirty(storage); + } } + public static T Read(fiUnityObjectReference key) where T : new() { - fiBaseStorageComponent storage; + + fiBaseStorageComponent storage = null; if (fiLateBindings.EditorUtility.IsPersistent(key.Target)) { storage = GetStorageDictionary(PrefabStorage); - } - else { - storage = GetStorageDictionary(SceneStorage); - } - - if (storage.Data.ContainsKey(key.Target)) { - return storage.Data[key.Target]; - } - - var value = storage.Data[key.Target] = new T(); - fiLateBindings.EditorUtility.SetDirty(storage); - return value; - } - -#if false - // This code is commented out because it is no longer used. It used to - // migrate data inside of the prefab into the scene object. Now, we just - // don't store scene objects inside of the prefab (we lose the ability to - // save the state from play-mode, but that's okay) - - /// - /// Attempts to migrate prefab storage into scene storage. - /// - private static void MigratePrefabIntoSceneStorage() - where T : fiPersistentEditorStorageItem { - // Only try to do the migration once (per type) - if (_didMigrate.Add(typeof(T)) == false) return; - - // We cannot migrate data while playing -- scene storage will not be - // persisted - if (Application.isPlaying) { - return; - } - - var prefabStorage = PrefabStorage.GetComponent>(); - var sceneStorage = SceneStorage.GetComponent>(); - - // Nothing to migrate. - if (prefabStorage == null || prefabStorage.Data == null) { - return; + } else { + storage = GetStorageDictionary(GetStorage(key.Target)); } - // Migrate everything into scene storage - var toRemove = new List(); - foreach (var entry in prefabStorage.Data) { - if (AssetDatabase.Contains(entry.Key.Target)) { - // data should stay in prefab storage, as the UnityObject - // target does not live in the scene - continue; + if (storage != null) { + if (storage.Data.ContainsKey(key.Target)) { + return storage.Data[key.Target]; } - if (sceneStorage.Data.ContainsKey(entry.Key) == false) { - // move directly into scene storage - sceneStorage.Data[entry.Key] = entry.Value; - } - else { - // copy into scene storage - sceneStorage.Data[entry.Key].CopyFromAndClear(prefabStorage.Data[entry.Key]); - } + var value = storage.Data[key.Target] = new T(); + fiLateBindings.EditorUtility.SetDirty(storage); - toRemove.Add(entry.Key); + return value; } - foreach (var item in toRemove) prefabStorage.Data.Remove(item); - fiLateBindings.EditorUtility.SetDirty(sceneStorage); - fiLateBindings.EditorUtility.SetDirty(prefabStorage); + return default(T); } - private static HashSet _didMigrate = new HashSet(); -#endif private static Dictionary _cachedRealComponentTypes = new Dictionary(); - private static fiBaseStorageComponent GetStorageDictionary(GameObject container) { + private static fiBaseStorageComponent GetStorageDictionary(fiPersistentEditorStorageComponent container) { Type realComponentType; if (_cachedRealComponentTypes.TryGetValue(typeof(fiBaseStorageComponent), out realComponentType) == false) { realComponentType = fiRuntimeReflectionUtility.AllSimpleTypesDerivingFrom(typeof(fiBaseStorageComponent)).FirstOrDefault(); @@ -110,58 +73,24 @@ private static fiBaseStorageComponent GetStorageDictionary(GameObject cont var component = container.GetComponent(realComponentType); if (component == null) { - component = container.AddComponent(realComponentType); + component = container.gameObject.AddComponent(realComponentType); } return (fiBaseStorageComponent)component; } - #endregion Reading/Writing + #endregion - #region Scene Storage - private const string SceneStorageName = "fiPersistentEditorStorage"; - private static GameObject _cachedSceneStorage; - public static GameObject SceneStorage { - get { - if (_cachedSceneStorage == null) { - _cachedSceneStorage = GameObject.Find(SceneStorageName); - - if (_cachedSceneStorage == null) { - // If we use new GameObject(), then for a split second - // Unity will show the game object in the hierarchy, - // which is bad UX. - _cachedSceneStorage = fiLateBindings.EditorUtility.CreateGameObjectWithHideFlags(SceneStorageName, HideFlags.HideInHierarchy); - } - } + public static IEnumerable GetAllCachedSceneStorages() { + return _StoragesManager.GetAllSceneStorages(); + } - return _cachedSceneStorage; - } + public static fiPersistentEditorStorageComponent GetStorage(UnityObject o) { + return _StoragesManager.GetStorage(o); } - #endregion Scene Storage - - #region Prefab Storage - private static string PrefabPath = fiUtility.CombinePaths(fiSettings.RootGeneratedDirectory, "fiPersistentEditorStorage.prefab"); - private static GameObject _cachedPrefabStorage; - public static GameObject PrefabStorage { - get { - if (_cachedPrefabStorage == null) { - // Try finding the current prefab - _cachedPrefabStorage = (GameObject)fiLateBindings.AssetDatabase.LoadAssetAtPath(PrefabPath, typeof(GameObject)); - - // Failed to find it; create a new one - if (_cachedPrefabStorage == null) { - var tmp = new GameObject(); - _cachedPrefabStorage = fiLateBindings.PrefabUtility.CreatePrefab(PrefabPath, tmp); - fiUtility.DestroyObject(tmp); - - Debug.Log("Created new editor storage object at " + PrefabPath + - "; this should only happen once. Please report a bug if it keeps on " + - "occurring.", _cachedPrefabStorage); - } - } - return _cachedPrefabStorage; - } + public static fiPersistentEditorStorageComponent PrefabStorage { + get { return _StoragesManager.PrefabStorage; } } - #endregion Prefab Storage + } -} \ No newline at end of file +} diff --git a/Assets/FullInspector2/Modules/PersistentMetadata/fiPersistentEditorStorageMetadataProvider.cs b/Assets/FullInspector2/Modules/PersistentMetadata/fiPersistentEditorStorageMetadataProvider.cs index f593e55..29bbbb9 100644 --- a/Assets/FullInspector2/Modules/PersistentMetadata/fiPersistentEditorStorageMetadataProvider.cs +++ b/Assets/FullInspector2/Modules/PersistentMetadata/fiPersistentEditorStorageMetadataProvider.cs @@ -8,8 +8,12 @@ public interface fiIGraphMetadataStorage { public abstract class fiPersistentEditorStorageMetadataProvider : fiIPersistentMetadataProvider where TItem : new() where TStorage : fiIGraphMetadataStorage, new() { + public void RestoreData(fiUnityObjectReference target) { - fiPersistentEditorStorage.Read(target).RestoreData(target); + var o = fiPersistentEditorStorage.Read(target); + if (o != null) { + o.RestoreData(target); + } } public void Reset(fiUnityObjectReference target) { diff --git a/Assets/FullInspector2/Modules/StoragesManager/Editor.meta b/Assets/FullInspector2/Modules/StoragesManager/Editor.meta new file mode 100644 index 0000000..7850f24 --- /dev/null +++ b/Assets/FullInspector2/Modules/StoragesManager/Editor.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6a154f81e0964227b592e845046e33bd +timeCreated: 1490355339 \ No newline at end of file diff --git a/Assets/FullInspector2/Modules/StoragesManager/fiStoragesManager.cs b/Assets/FullInspector2/Modules/StoragesManager/fiStoragesManager.cs new file mode 100644 index 0000000..7e312e1 --- /dev/null +++ b/Assets/FullInspector2/Modules/StoragesManager/fiStoragesManager.cs @@ -0,0 +1,198 @@ +using UnityEngine; +using System.Collections.Generic; +using System.Linq; +using FullInspector.Internal; +using UnityEngine.SceneManagement; + +namespace FullInspector.StoragesManager { + + public class fiStoragesManager + where T : MonoBehaviour { + private readonly Dictionary _Storages = new Dictionary(); + private readonly string SceneStorageName; + private readonly string PrefabStoragePath; + + private bool _didInitialRefresh; + + private List _lastSeenScenes = new List(); + + + public fiStoragesManager(string sceneStorageName, string prefabStoragePath) { + SceneStorageName = sceneStorageName; + PrefabStoragePath = prefabStoragePath; + } + + private T _prefabStorage; + public T PrefabStorage { + get { + if (_prefabStorage == null) { + GameObject prefabGameObject = null; + + // Try finding the current prefab + prefabGameObject = fiLateBindings.AssetDatabase.LoadAssetAtPath(PrefabStoragePath, typeof(GameObject)) as GameObject; + + // Failed to find it; create a new one + if (prefabGameObject == null) { + var cloned = new GameObject(); + prefabGameObject = fiLateBindings.PrefabUtility.CreatePrefab(PrefabStoragePath, cloned); + fiUtility.DestroyObject(cloned); + + prefabGameObject.AddComponent(); + + Debug.Log("Created new backup persistent storage object at " + PrefabStorage + + "; this should only happen once. Please report a bug if it keeps on " + + "occurring.", prefabGameObject); + } + + _prefabStorage = prefabGameObject.GetComponent(); + + if (_prefabStorage == null) { + _prefabStorage = prefabGameObject.AddComponent(); + } + } + + return _prefabStorage; + } + } + + + public IEnumerable GetAllSceneStorages() { + RefreshStorages(); + return _Storages.Select(t => t.Value); + } + + + public T GetStorage(Scene scene) { + RefreshStorages(); + T storage; + if (_Storages.TryGetValue(scene, out storage)) { + return storage; + } + + return _prefabStorage; + } + + public T GetStorage(Object o) { + RefreshStorages(); + + Scene scene; + var c = o as Component; + if (c != null) { + scene = c.gameObject.scene; + } else { + var g = o as GameObject; + if (g != null) { + scene = g.scene; + } else { + return PrefabStorage; + } + } + + if (!scene.IsValid() || !scene.isLoaded || scene.name == "DontDestroyOnLoad") { + return PrefabStorage; + } + + return _Storages[scene]; + } + + + /// + /// This will ensure that always has fresh + /// references for each storage in the loaded scenes. + /// + /// + private void RefreshStorages(bool forced = false) { + //always refresh at least once + if (_didInitialRefresh && !UpdateLastSeenScenes()) { + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + var hasNullStorages = _Storages.Any(s => s.Value == null || s.Value.gameObject == null); + var mustRefresh = false; + if (!hasNullStorages) { + if (_Storages.Count == 0) { + mustRefresh = true; + } else { + for (var i = 0; i < SceneManager.sceneCount; i++) { + Scene scene = SceneManager.GetSceneAt(i); + if (scene.isLoaded) { + if (_Storages.ContainsKey(scene)) { + continue; + } + + //we'll have to refresh if we stored keys for scenes that were unloaded + } else if (!_Storages.ContainsKey(scene)) { + continue; + } + + mustRefresh = true; + break; + } + } + } + + if (!forced && !mustRefresh && !hasNullStorages) { + return; + } + } + + _didInitialRefresh = true; + + //get fresh references for all storages that already exist in the currently loaded scenes + _Storages.Clear(); + foreach (var found in Object.FindObjectsOfType()) { + var scene = found.gameObject.scene; + if (!_Storages.Keys.Contains(scene)) { + _Storages[scene] = found; + } else { + //Debug.LogWarning("scene " + scene.name + " already has a storage, but found another. Destroying..."); + Object.DestroyImmediate(found.gameObject); + if (!fiLateBindings.EditorApplication.isPlaying) { + fiLateBindings.SceneManagement.EditorSceneManager.MarkSceneDirty(scene); + } + } + } + + for (var i = 0; i < SceneManager.sceneCount; i++) { + Scene scene = SceneManager.GetSceneAt(i); + if (_Storages.ContainsKey(scene) || !scene.isLoaded) { + continue; + } + + //if we are here, it means that this scene doesn't have a storage, so we created one + var obj = fiLateBindings.EditorUtility.CreateGameObjectWithHideFlags(SceneStorageName, HideFlags.HideInHierarchy); + SceneManager.MoveGameObjectToScene(obj.gameObject, scene); + var storage = obj.AddComponent(); + _Storages[scene] = storage; + //Debug.Log("No storage for scene " + scene.name + ". New one was created", storage.gameObject); + } + + } + + + private bool UpdateLastSeenScenes() { + List currentlyLoaded = new List(); + for (int i = 0; i < SceneManager.sceneCount; i++) { + Scene s = SceneManager.GetSceneAt(i); + if (s.isLoaded) { + currentlyLoaded.Add(s); + } + } + + List lastSeenLoadedScenes = _lastSeenScenes.Where(s => s.isLoaded).ToList(); + if (lastSeenLoadedScenes.Count != _lastSeenScenes.Count) { + _lastSeenScenes = lastSeenLoadedScenes; + } + + if (currentlyLoaded.All(s => _lastSeenScenes.Contains(s))) { + return false; + } + + _lastSeenScenes.Clear(); + foreach (var scene in currentlyLoaded) { + _lastSeenScenes.Add(scene); + } + + return true; + } + + } +} diff --git a/Assets/FullInspector2/Modules/StoragesManager/fiStoragesManager.cs.meta b/Assets/FullInspector2/Modules/StoragesManager/fiStoragesManager.cs.meta new file mode 100644 index 0000000..c846a5f --- /dev/null +++ b/Assets/FullInspector2/Modules/StoragesManager/fiStoragesManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4c399951c8d24eae85ffcf576a08d5ea +timeCreated: 1490355160 \ No newline at end of file