using System; namespace UnityEngine.Rendering.HighDefinition { /// Helpers to manipulate static class MigrationDescription { public static T LastVersion() where T : struct, IConvertible { return TypeInfo.GetEnumLastValue(); } /// Create a new migration description. /// An enum identifying the version. /// The type to migrate. /// The steps of the migration. /// The migration description. public static MigrationDescription New( params MigrationStep[] steps ) where TVersion : struct, IConvertible where TTarget : class, IVersionable { return new MigrationDescription(steps); } } // Moving the example here as it seems to not be parsed correctly by the doc validation tool... // // // // // class MyComponent : MonoBehaviour, IVersionable // { // enum Version // { // NeverMigrated, // First, // Second // } // // static readonly MigrationDescription k_MigrationDescription // = MigrationDescription.New( // MigrationStep.New(Version.First, (MyComponent target) => // { // // Migration code for first version // }), // MigrationStep.New(Version.Second, (MyComponent target) => // { // // Migration code for second version // }) // ); // // [SerializeField] // Version m_Version; // Version IVersionable.Version { get { return m_Version; } set { m_Version = value; } } // // void Awake() // { // k_MigrationDescription.Migrate(this); // } // } // // // // About the NeverMigrated entry: // When using this generic versionable framework, it is better to use 0 as a place holder to detect a never migrated version // (and thus a step never to be executed for that enum entry) instead of eg -1 because underlying enum values are ordered as unsigned and // MigrationDescription.LastVersion() will not work properly - ie it can return -1 if this "-1" value exist in the enum, instead // of other positive values that are intended to be more recent migration steps. // (The enum symbol with -1 will be listed as the last enum values in UnityEngine.Rendering.HighDefinition.TypeInfo.GetEnumLastValue()) /// Describe migration steps to perform when upgrading from one version of an object to another. /// An enum identifying the version. /// The type to migrate. public struct MigrationDescription where TVersion : struct, IConvertible where TTarget : class, IVersionable { /// Steps of the migration. They will be in ascending order of . readonly MigrationStep[] Steps; /// Build a migration description. /// The step to follow between each version migration. public MigrationDescription(params MigrationStep[] steps) { // Sort by version Array.Sort(steps, (l, r) => Compare(l.Version, r.Version)); Steps = steps; } /// /// Execute the migration on the provided instance. /// /// All steps with a version greater than the instance version will be executed in ascending order. /// Eg: for instance with version 2 and step version 1, 3, 5, and 6. /// It will execute steps 3 then 5 then 6. /// /// The instance to migrate. /// True if it has executed migration steps, false otherwise. public bool Migrate(TTarget target) { if (IsLastVersionOrAbove(target.version)) return false; for (int i = 0; i < Steps.Length; ++i) { if (Compare(target.version, Steps[i].Version) < 0) { Steps[i].Migrate(target); target.version = Steps[i].Version; } } #if UNITY_EDITOR UnityEngine.Object targetObject = target as UnityEngine.Object; if (targetObject != null) { // Special in prefab case if (UnityEditor.PrefabUtility.IsPartOfNonAssetPrefabInstance(targetObject)) { UnityEditor.PrefabUtility.RecordPrefabInstancePropertyModifications(targetObject); } UnityEditor.EditorApplication.delayCall += () => { if (targetObject != null && !targetObject.Equals(null)) { // Only dirty the object's scene if it can be saved, preview scenes are not saved (case 1367204). if (!UnityEditor.SceneManagement.EditorSceneManager.IsPreviewSceneObject(targetObject)) UnityEditor.EditorUtility.SetDirty(targetObject); } }; } #endif return true; } /// /// Execute a migration step. /// /// Target. /// Step version. public void ExecuteStep(TTarget target, TVersion stepVersion) { for (int i = 0; i < Steps.Length; ++i) { if (Equals(Steps[i].Version, stepVersion)) { Steps[i].Migrate(target); return; } } } static bool Equals(TVersion l, TVersion r) => Compare(l, r) == 0; static int Compare(TVersion l, TVersion r) => (int)(object)l - (int)(object)r; bool IsLastVersionOrAbove(TVersion target) => Compare(target, Steps[Steps.Length - 1].Version) >= 0; } }