1+ // SPDX-License-Identifier: Apache-2.0
2+ // © 2023-2026 Depra <n.melnikov@depra.org>
3+
4+ using System ;
5+ using System . Collections . Generic ;
6+ using System . IO ;
7+ using System . Linq ;
8+ using UnityEditor ;
9+ using UnityEngine ;
10+ using Object = UnityEngine . Object ;
11+
12+ namespace Depra . Ecs . Hybrid . Editor . Migration
13+ {
14+ /// <summary>
15+ /// TEMPORARY: Migration utility for AuthoringComponentDatabase -> AuthoringRecipe
16+ /// TODO: Remove this file after migration is complete.
17+ /// </summary>
18+ internal static class AuthoringComponentDatabaseMigration
19+ {
20+ private const string FILE_POSFIX = " Entity.asset" ;
21+ private const string MIGRATION_KEY = "AuthoringComponentDatabase_Migration_v1_Completed" ;
22+
23+ [ MenuItem ( "Ecs/Migration/Authoring Component Database/1. Preview Changes" ) ]
24+ public static void PreviewMigration ( )
25+ {
26+ var targets = FindMigrationTargets ( ) . ToArray ( ) ;
27+ if ( targets . Length == 0 )
28+ {
29+ EditorUtility . DisplayDialog ( "Migration Preview" ,
30+ "No prefabs found with AuthoringComponentDatabase." , "OK" ) ;
31+ return ;
32+ }
33+
34+ var message = $ "Found { targets . Length } prefab(s) to migrate:\n \n ";
35+ foreach ( var path in targets )
36+ {
37+ message += $ "• { path } \n ";
38+ }
39+
40+ Debug . Log ( "[MIGRATION PREVIEW]\n " + message ) ;
41+ EditorUtility . DisplayDialog ( "Migration Preview" , message , "OK" ) ;
42+ }
43+
44+ [ MenuItem ( "Ecs/Migration/Authoring Component Database/2. Run Migration" ) ]
45+ public static void RunMigration ( )
46+ {
47+ if ( EditorPrefs . GetBool ( MIGRATION_KEY , false ) )
48+ {
49+ if ( ! EditorUtility . DisplayDialog ( "Migration Warning" ,
50+ "Migration was already completed before. Run again?" ,
51+ "Yes, Run Again" , "Cancel" ) )
52+ {
53+ return ;
54+ }
55+ }
56+
57+ var targets = FindMigrationTargets ( ) . ToArray ( ) ;
58+ if ( targets . Length == 0 )
59+ {
60+ EditorUtility . DisplayDialog ( "Migration" , "No prefabs to migrate." , "OK" ) ;
61+ return ;
62+ }
63+
64+ if ( ! EditorUtility . DisplayDialog ( "Confirm Migration" ,
65+ $ "This will modify { targets . Length } prefab(s).\n \n " +
66+ "Make sure you have a backup or are using version control!\n \n " +
67+ "Continue?" ,
68+ "Yes, Migrate" , "Cancel" ) )
69+ {
70+ return ;
71+ }
72+
73+ var results = PerformMigration ( targets ) ;
74+
75+ EditorPrefs . SetBool ( MIGRATION_KEY , true ) ;
76+ EditorUtility . DisplayDialog ( "Migration Complete" ,
77+ $ "Successfully migrated: { results . successCount } \n " +
78+ $ "Failed: { results . failCount } \n \n " +
79+ "Check Console for details." ,
80+ "OK" ) ;
81+ }
82+
83+ [ MenuItem ( "Ecs/Migration/Authoring Component Database/3. Reset Migration Flag" ) ]
84+ public static void ResetMigrationFlag ( )
85+ {
86+ EditorPrefs . DeleteKey ( MIGRATION_KEY ) ;
87+ Debug . Log ( "Migration flag reset." ) ;
88+ }
89+
90+ public static bool MigratePrefab ( string path )
91+ {
92+ var prefab = AssetDatabase . LoadAssetAtPath < GameObject > ( path ) ;
93+ if ( ! prefab )
94+ {
95+ return false ;
96+ }
97+
98+ var entity = prefab . GetComponent < AuthoringEntity > ( ) ;
99+ if ( ! entity )
100+ {
101+ return false ;
102+ }
103+
104+ var databases = prefab . GetComponentsInChildren < AuthoringComponentDatabase > ( ) ;
105+ if ( databases . Length == 0 )
106+ {
107+ return false ;
108+ }
109+
110+ var recipe = prefab . GetComponent < AuthoringEntityRecipe > ( ) ?? prefab . AddComponent < AuthoringEntityRecipe > ( ) ;
111+ if ( ! recipe . Recipe )
112+ {
113+ var recipePath = Path . Combine ( Path . GetDirectoryName ( path ) ! ,
114+ Path . GetFileNameWithoutExtension ( path ) + FILE_POSFIX ) ;
115+ var newRecipe = ScriptableObject . CreateInstance < EntityRecipe > ( ) ;
116+ AssetDatabase . CreateAsset ( newRecipe , recipePath ) ;
117+ recipe . Recipe = newRecipe ;
118+ }
119+
120+ foreach ( var database in databases )
121+ {
122+ foreach ( var set in database . Enumerate ( ) )
123+ {
124+ recipe . Recipe . Add ( set ) ;
125+ }
126+
127+ Object . DestroyImmediate ( database , true ) ;
128+ }
129+
130+ EditorUtility . SetDirty ( prefab ) ;
131+ EditorUtility . SetDirty ( recipe . Recipe ) ;
132+ return true ;
133+ }
134+
135+ private static IEnumerable < string > FindMigrationTargets ( ) =>
136+ from guid in AssetDatabase . FindAssets ( "t:Prefab" )
137+ select AssetDatabase . GUIDToAssetPath ( guid )
138+ into assetPath
139+ let prefab = AssetDatabase . LoadAssetAtPath < GameObject > ( assetPath )
140+ where prefab != null && prefab . GetComponent < AuthoringEntity > ( ) != null &&
141+ prefab . GetComponentInChildren < AuthoringComponentDatabase > ( ) != null
142+ select assetPath ;
143+
144+ private static ( int successCount , int failCount ) PerformMigration ( string [ ] paths )
145+ {
146+ var successCount = 0 ;
147+ var failCount = 0 ;
148+
149+ for ( var index = 0 ; index < paths . Length ; index ++ )
150+ {
151+ var path = paths [ index ] ;
152+ EditorUtility . DisplayProgressBar ( "Migrating Authoring Component Databases" ,
153+ $ "Processing { index + 1 } /{ paths . Length } : { path } ",
154+ ( float ) index / paths . Length ) ;
155+
156+ try
157+ {
158+ if ( MigratePrefab ( path ) )
159+ {
160+ successCount ++ ;
161+ Debug . Log ( $ "✓ Migrated: { path } ") ;
162+ }
163+ else
164+ {
165+ failCount ++ ;
166+ Debug . LogWarning ( $ "✗ Failed: { path } ") ;
167+ }
168+ }
169+ catch ( Exception e )
170+ {
171+ failCount ++ ;
172+ Debug . LogError ( $ "✗ Error migrating { path } : { e . Message } ") ;
173+ }
174+ }
175+
176+ EditorUtility . ClearProgressBar ( ) ;
177+ AssetDatabase . SaveAssets ( ) ;
178+ AssetDatabase . Refresh ( ) ;
179+
180+ return ( successCount , failCount ) ;
181+ }
182+ }
183+ }
0 commit comments