A game about forced loneliness, made by TACStudios
at master 529 lines 22 kB view raw
1using System; 2using System.IO; 3using System.Linq; 4using System.Collections.Generic; 5using System.Reflection; 6using UnityEditor.SceneManagement; 7using UnityEditor.U2D.Sprites; 8using UnityEngine; 9using UnityEngine.SceneManagement; 10using UnityEngine.U2D.Animation; 11 12using Object = UnityEngine.Object; 13 14namespace UnityEditor.U2D.Animation.Upgrading 15{ 16 internal class SpriteLibUpgrader : BaseUpgrader 17 { 18 static class Contents 19 { 20 public static readonly string ProgressBarTitle = L10n.Tr("Upgrading Sprite Library Assets"); 21 public static readonly string VerifyingSelection = L10n.Tr("Verifying the selection"); 22 public static readonly string CreatingNewLibraries = L10n.Tr("Creating new Sprite Library Assets"); 23 public static readonly string ReassignAssetsInComponents = L10n.Tr("Re-assigning assets in components"); 24 public static readonly string RemoveOldSpriteLibraries = L10n.Tr("Removing old Sprite Library Assets"); 25 } 26 27 const string k_SpriteLibTypeId = "t:SpriteLibraryAsset"; 28 const string k_PsbImporterCategoriesId = "m_SpriteCategoryList.m_Categories"; 29 static readonly Dictionary<Type, List<FieldInfo>> k_SpriteLibraryReferenceLookup; 30 31 bool m_OnlyFindOldAssets; 32 bool m_OnlySearchInAssets; 33 34 HashSet<int> m_IndicesWithAssetBundleConnection = new HashSet<int>(); 35 HashSet<string> m_AssetBundlesNeedingUpgrade = new HashSet<string>(); 36 37 static SpriteLibUpgrader() 38 { 39 k_SpriteLibraryReferenceLookup = GetSpriteLibReferenceLookup(); 40 } 41 42 /// <param name="onlyFindOldAssets">Set this to true if you only want to find .asset SpriteLibraries</param> 43 /// <param name="onlySearchInAssets">Set this to true if you only want to find SpriteLibraries in the Assets/ folder or its children</param> 44 public SpriteLibUpgrader(bool onlyFindOldAssets = true, bool onlySearchInAssets = true) 45 { 46 m_OnlyFindOldAssets = onlyFindOldAssets; 47 m_OnlySearchInAssets = onlySearchInAssets; 48 } 49 50 static Dictionary<Type, List<FieldInfo>> GetSpriteLibReferenceLookup() 51 { 52 var result = new Dictionary<Type, List<FieldInfo>>(); 53 54 var allObjectsWithSpriteLibProperties = TypeCache 55 .GetTypesDerivedFrom<Component>() 56 .Where(type => type 57 .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) 58 .Any(HasSpriteLibField)); 59 60 foreach (var property in allObjectsWithSpriteLibProperties) 61 { 62 if (!result.ContainsKey(property)) 63 result.Add(property, new List<FieldInfo>()); 64 65 var libraryFields = property 66 .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) 67 .Where(HasSpriteLibField) 68 .ToList(); 69 70 foreach (var field in libraryFields) 71 { 72 result[property].Add(field); 73 } 74 } 75 76 return result; 77 } 78 79 static bool HasSpriteLibField(FieldInfo field) 80 { 81 return (field.FieldType == typeof(SpriteLibraryAsset) || 82 field.FieldType == typeof(SpriteLibraryAsset[]) || 83 field.FieldType == typeof(List<SpriteLibraryAsset>)) && 84 (field.IsPublic || field.IsDefined(typeof(SerializeField))); 85 } 86 87 internal override List<Object> GetUpgradableAssets() 88 { 89 var rootFolder = m_OnlySearchInAssets ? new[] { "Assets" } : null; 90 var assetPaths = AssetDatabase.FindAssets(k_SpriteLibTypeId, rootFolder) 91 .Select(AssetDatabase.GUIDToAssetPath).ToArray(); 92 93 if (m_OnlyFindOldAssets) 94 { 95 assetPaths = assetPaths 96 .Where(x => x.EndsWith(".asset") || UpgradeUtilities.IsPsbImportedFile(x)) 97 .ToArray(); 98 } 99 100 var assets = assetPaths 101 .Select(AssetDatabase.LoadAssetAtPath<SpriteLibraryAsset>) 102 .Cast<Object>() 103 .ToList(); 104 return assets; 105 } 106 107 internal override UpgradeReport UpgradeSelection(List<ObjectIndexPair> objects) 108 { 109 EditorUtility.DisplayProgressBar(Contents.ProgressBarTitle, Contents.VerifyingSelection, 0f); 110 111 var entries = new List<UpgradeEntry>(); 112 113 var libraryIndexPairs = new Dictionary<int, SpriteLibraryAsset>(); 114 string msg; 115 foreach (var obj in objects) 116 { 117 if (obj.Target == null) 118 { 119 msg = "The upgrade failed. Invalid selection."; 120 m_Logger.Add(msg); 121 m_Logger.AddLineBreak(); 122 entries.Add(new UpgradeEntry() 123 { 124 Result = UpgradeResult.Error, 125 Target = obj.Target, 126 Index = obj.Index, 127 Message = msg 128 }); 129 } 130 else if (obj.Target is SpriteLibraryAsset lib) 131 { 132 libraryIndexPairs.Add(obj.Index, lib); 133 } 134 else 135 { 136 msg = $"The upgrade failed. {obj.Target.name} is not a SpriteLibraryAsset."; 137 m_Logger.Add(msg); 138 m_Logger.AddLineBreak(); 139 entries.Add(new UpgradeEntry() 140 { 141 Result = UpgradeResult.Error, 142 Target = obj.Target, 143 Index = obj.Index, 144 Message = msg 145 }); 146 } 147 } 148 var oldLibraries = libraryIndexPairs.Values.ToList(); 149 150 EditorUtility.DisplayProgressBar(Contents.ProgressBarTitle, Contents.CreatingNewLibraries, 0.2f); 151 m_Logger.AddLineBreak(); 152 m_Logger.Add(Contents.CreatingNewLibraries); 153 var newSourceAssetPaths = CreateNewAssetLibraries(libraryIndexPairs); 154 AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); 155 var newLibraries = LoadNewLibraries(newSourceAssetPaths); 156 157 EditorUtility.DisplayProgressBar(Contents.ProgressBarTitle, Contents.ReassignAssetsInComponents, 0.4f); 158 m_Logger.AddLineBreak(); 159 m_Logger.Add(Contents.ReassignAssetsInComponents); 160 var assetPaths = GetAllAssetPaths(libraryIndexPairs); 161 if (assetPaths.Length > 0) 162 ReassignAssets(assetPaths, oldLibraries, newLibraries); 163 164 EditorUtility.DisplayProgressBar(Contents.ProgressBarTitle, Contents.RemoveOldSpriteLibraries, 0.8f); 165 m_Logger.AddLineBreak(); 166 m_Logger.Add(Contents.RemoveOldSpriteLibraries); 167 RemoveOldLibraries(oldLibraries); 168 AssetDatabase.Refresh(); 169 170 for (var i = 0; i < newLibraries.Count; ++i) 171 { 172 UpgradeResult result; 173 if (m_IndicesWithAssetBundleConnection.Contains(newSourceAssetPaths[i].Item1)) 174 { 175 msg = $"Successfully replaced {newLibraries[i].name}. Note that the asset is connected to an AssetBundle. Make sure to rebuild this AssetBundle to complete the upgrade. See more info in the upgrade log."; 176 result = UpgradeResult.Warning; 177 } 178 else 179 { 180 msg = $"Successfully replaced {newLibraries[i].name}"; 181 result = UpgradeResult.Successful; 182 } 183 184 m_Logger.Add(msg); 185 entries.Add(new UpgradeEntry() 186 { 187 Result = result, 188 Target = newLibraries[i], 189 Index = newSourceAssetPaths[i].Item1, 190 Message = msg 191 }); 192 } 193 194 EditorUtility.ClearProgressBar(); 195 196 if (m_AssetBundlesNeedingUpgrade.Count > 0) 197 AddAssetBundlesToLog(); 198 199 var report = new UpgradeReport() 200 { 201 UpgradeEntries = entries, 202 Log = m_Logger.GetLog() 203 }; 204 205 m_Logger.Clear(); 206 m_AssetBundlesNeedingUpgrade.Clear(); 207 return report; 208 } 209 210 static string[] GetAllAssetPaths(Dictionary<int, SpriteLibraryAsset> spriteLibraries) 211 { 212 var ids = new List<string>(); 213 foreach(var lib in spriteLibraries.Values) 214 ids.Add(GetObjectIDString(lib)); 215 var spriteLibIds = ids.ToArray(); 216 217 var assetPaths = new List<string>(); 218 var allAssetPaths = AssetDatabase.GetAllAssetPaths(); 219 foreach (var path in allAssetPaths) 220 { 221 if (!IsPrefabOrScenePath(path, spriteLibIds)) 222 continue; 223 224 assetPaths.Add(path); 225 } 226 227 return assetPaths.ToArray(); 228 } 229 230 static string GetObjectIDString(Object obj) 231 { 232 if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj.GetInstanceID(), out string guid, out long localId)) 233 return "fileID: " + localId + ", guid: " + guid; 234 235 return null; 236 } 237 238 static bool IsPrefabOrScenePath(string path, string[] ids) 239 { 240 if (string.IsNullOrEmpty(path)) 241 throw new ArgumentNullException(nameof(path)); 242 243 if (path.StartsWith("Packages")) 244 return false; 245 246 if (path.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase) || path.EndsWith(".unity", StringComparison.OrdinalIgnoreCase)) 247 return DoesFileContainString(path, ids); 248 249 return false; 250 } 251 252 static bool DoesFileContainString(string path, string[] strings) 253 { 254 if (strings != null && strings.Length > 0) 255 { 256 using (var file = File.OpenText(path)) 257 { 258 string line; 259 while ((line = file.ReadLine()) != null) 260 { 261 for (var i = 0; i < strings.Length; i++) 262 { 263 if (line.Contains(strings[i])) 264 return true; 265 } 266 } 267 } 268 } 269 270 return false; 271 } 272 273 List<(int, string)> CreateNewAssetLibraries(Dictionary<int, SpriteLibraryAsset> spriteLibraries) 274 { 275 var newLibraryPaths = new List<(int, string)>(); 276 foreach (var pair in spriteLibraries) 277 { 278 var sourceAsset = pair.Value; 279 280 var path = AssetDatabase.GetAssetPath(sourceAsset); 281 var currentAssetPath = Path.GetDirectoryName(path); 282 var fileName = Path.GetFileNameWithoutExtension(path); 283 var convertFileName = fileName + SpriteLibrarySourceAsset.extension; 284 convertFileName = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(currentAssetPath, convertFileName)); 285 286 var destAsset = ScriptableObject.CreateInstance<SpriteLibrarySourceAsset>(); 287 destAsset.SetLibrary(new List<SpriteLibCategoryOverride>(sourceAsset.categories.Count)); 288 foreach (var sourceCat in sourceAsset.categories) 289 { 290 var destCat = new SpriteLibCategoryOverride() 291 { 292 overrideEntries = new List<SpriteCategoryEntryOverride>(sourceCat.categoryList.Count), 293 name = sourceCat.name, 294 entryOverrideCount = sourceCat.categoryList.Count, 295 fromMain = false 296 }; 297 destAsset.AddCategory(destCat); 298 foreach (var entry in sourceCat.categoryList) 299 { 300 destCat.overrideEntries.Add(new SpriteCategoryEntryOverride() 301 { 302 name = entry.name, 303 sprite = entry.sprite, 304 fromMain = false, 305 spriteOverride = entry.sprite 306 }); 307 } 308 } 309 310 var assetBundle = AssetDatabase.GetImplicitAssetBundleName(path); 311 if (!string.IsNullOrEmpty(assetBundle)) 312 { 313 m_AssetBundlesNeedingUpgrade.Add(assetBundle); 314 m_IndicesWithAssetBundleConnection.Add(pair.Key); 315 m_Logger.Add($"{sourceAsset.name} is connected with the following AssetBundle: {assetBundle}."); 316 } 317 318 SpriteLibrarySourceAssetImporter.SaveSpriteLibrarySourceAsset(destAsset, convertFileName); 319 newLibraryPaths.Add(new ValueTuple<int, string>(pair.Key, convertFileName)); 320 m_Logger.Add($"Created a new SpriteLibrary with the data of {sourceAsset.name}. The new SpriteLibrary is located at: {convertFileName}"); 321 } 322 323 return newLibraryPaths; 324 } 325 326 static List<SpriteLibraryAsset> LoadNewLibraries(List<(int, string)> sourceAssetPaths) 327 { 328 var newLibraries = new List<SpriteLibraryAsset>(); 329 foreach (var path in sourceAssetPaths) 330 { 331 var newLibraryAsset = AssetDatabase.LoadAssetAtPath<SpriteLibraryAsset>(path.Item2); 332 newLibraries.Add(newLibraryAsset); 333 } 334 335 return newLibraries; 336 } 337 338 void ReassignAssets(string[] assetPaths, List<SpriteLibraryAsset> oldLibraries, List<SpriteLibraryAsset> newLibraries) 339 { 340 var index = 0; 341 foreach (var assetPath in assetPaths) 342 { 343 m_Logger.Add($"Scanning {assetPath} for components with SpriteLibraryAsset references in need of reassignment."); 344 var ext = Path.GetExtension(assetPath); 345 if (ext == ".prefab") 346 UpgradePrefab(assetPath, oldLibraries, newLibraries, UpgradeGameObject); 347 else if (ext == ".unity") 348 UpgradeScene(assetPath, oldLibraries, newLibraries, UpgradeGameObject); 349 350 var assetBundle = AssetDatabase.GetImplicitAssetBundleName(assetPath); 351 if (!string.IsNullOrEmpty(assetBundle)) 352 { 353 m_AssetBundlesNeedingUpgrade.Add(assetBundle); 354 m_IndicesWithAssetBundleConnection.Add(index); 355 } 356 357 index++; 358 } 359 } 360 361 static void UpgradePrefab(string path, List<SpriteLibraryAsset> oldLibraries, List<SpriteLibraryAsset> newLibraries, 362 Action<GameObject, List<SpriteLibraryAsset>, List<SpriteLibraryAsset>> objectUpgrader) 363 { 364 var objects = AssetDatabase.LoadAllAssetsAtPath(path); 365 366 var firstIndex = 0; 367 for (var i = 0; i < objects.Length; i++) 368 { 369 if (objects[i] as GameObject) 370 { 371 firstIndex = i; 372 break; 373 } 374 } 375 376 if (!PrefabUtility.IsPartOfImmutablePrefab(objects[firstIndex])) 377 { 378 foreach (var obj in objects) 379 { 380 var go = obj as GameObject; 381 if (go != null) 382 { 383 objectUpgrader(go, oldLibraries, newLibraries); 384 } 385 } 386 387 var asset = objects[firstIndex] as GameObject; 388 PrefabUtility.SavePrefabAsset(asset.transform.root.gameObject); 389 } 390 } 391 392 static void UpgradeScene(string path, List<SpriteLibraryAsset> oldLibraries, List<SpriteLibraryAsset> newLibraries, 393 Action<GameObject, List<SpriteLibraryAsset>, List<SpriteLibraryAsset>> objectUpgrader) 394 { 395 var scene = default(Scene); 396 var openedByUser = false; 397 for (var i = 0; i < SceneManager.sceneCount && !openedByUser; i++) 398 { 399 scene = SceneManager.GetSceneAt(i); 400 if (path == scene.path) 401 openedByUser = true; 402 } 403 404 if (!openedByUser) 405 scene = EditorSceneManager.OpenScene(path, OpenSceneMode.Additive); 406 407 var gameObjects = scene.GetRootGameObjects(); 408 foreach (var go in gameObjects) 409 objectUpgrader(go, oldLibraries, newLibraries); 410 411 EditorSceneManager.SaveScene(scene); 412 if (!openedByUser) 413 EditorSceneManager.CloseScene(scene, true); 414 } 415 416 void UpgradeGameObject(GameObject go, List<SpriteLibraryAsset> oldLibraries, List<SpriteLibraryAsset> newLibraries) 417 { 418 var types = k_SpriteLibraryReferenceLookup.Keys; 419 foreach (var referenceType in types) 420 { 421 var components = go.GetComponentsInChildren(referenceType); 422 foreach (var component in components) 423 { 424 if (PrefabUtility.IsPartOfPrefabInstance(component)) 425 continue; 426 427 var fieldInfos = k_SpriteLibraryReferenceLookup[referenceType]; 428 foreach (var field in fieldInfos) 429 { 430 var asset = field.GetValue(component); 431 if (asset is SpriteLibraryAsset spriteLibAsset) 432 { 433 var index = oldLibraries.FindIndex(x => x.GetHashCode() == spriteLibAsset.GetHashCode()); 434 if (index == -1) 435 continue; 436 437 field.SetValue(component, newLibraries[index]); 438 m_Logger.Add($"Updated the SpriteLibraryAsset reference in {component.GetType()} on the GameObject {component.name}"); 439 } 440 else if (asset is SpriteLibraryAsset[] spriteLibArray) 441 { 442 for (var i = 0; i < spriteLibArray.Length; ++i) 443 { 444 var index = oldLibraries.FindIndex(x => x.GetHashCode() == spriteLibArray[i].GetHashCode()); 445 if (index == -1) 446 continue; 447 448 spriteLibArray[i] = newLibraries[index]; 449 } 450 451 field.SetValue(component, spriteLibArray); 452 m_Logger.Add($"Updated the SpriteLibraryAsset[] reference in {component.GetType()} on the GameObject {component.name}"); 453 } 454 else if (asset is List<SpriteLibraryAsset> spriteLibList) 455 { 456 for (var i = 0; i < spriteLibList.Count; ++i) 457 { 458 var index = oldLibraries.FindIndex(x => x.GetHashCode() == spriteLibList[i].GetHashCode()); 459 if (index == -1) 460 continue; 461 462 spriteLibList[i] = newLibraries[index]; 463 } 464 465 field.SetValue(component, spriteLibList); 466 m_Logger.Add($"Updated the List<SpriteLibraryAsset> reference in {component.GetType()} on the GameObject {component.name}"); 467 } 468 } 469 } 470 } 471 } 472 473 void RemoveOldLibraries(List<SpriteLibraryAsset> oldLibraries) 474 { 475 foreach (var library in oldLibraries) 476 { 477 var path = AssetDatabase.GetAssetPath(library); 478 var isPsbFile = UpgradeUtilities.IsPsbImportedFile(path); 479 if (!string.IsNullOrEmpty(path) && !isPsbFile) 480 { 481 m_Logger.Add($"Deleting {path} from project"); 482 AssetDatabase.DeleteAsset(path); 483 } 484 } 485 } 486 487 // Leaving this in if we want to cleanup .psbs in the future 488 void RemoveSpriteLibFromPsb(string path) 489 { 490 var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path); 491 492 var factory = new SpriteDataProviderFactories(); 493 factory.Init(); 494 var dataProvider = factory.GetSpriteEditorDataProviderFromObject(texture); 495 dataProvider.InitSpriteEditorDataProvider(); 496 if (dataProvider.targetObject == null) 497 { 498 m_Logger.Add($"Could not load the PSDImporter from the path: {path}. Aborting the Sprite Library cleanup inside the .psb."); 499 return; 500 } 501 502 var so = new SerializedObject(dataProvider.targetObject); 503 var property = so.FindProperty(k_PsbImporterCategoriesId); 504 if (property != null && property.isArray) 505 { 506 property.arraySize = 0; 507 so.ApplyModifiedPropertiesWithoutUndo(); 508 dataProvider.Apply(); 509 m_Logger.Add($"Removed the Sprite Library asset inside {path}"); 510 511 var assetImporter = dataProvider.targetObject as AssetImporter; 512 assetImporter.SaveAndReimport(); 513 m_Logger.Add($"Saved and re-imported the file."); 514 } 515 else 516 { 517 m_Logger.Add($"Could not find any Sprite Library asset inside the .psb to cleanup."); 518 } 519 } 520 521 void AddAssetBundlesToLog() 522 { 523 m_Logger.AddLineBreak(); 524 m_Logger.Add("[NOTE] The following AssetBundles need to be rebuilt:"); 525 foreach(var assetBundle in m_AssetBundlesNeedingUpgrade) 526 m_Logger.Add(assetBundle); 527 } 528 } 529}