using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor.AddressableAssets.Build.AnalyzeRules; using UnityEditor.AddressableAssets.GUI; using UnityEditor.AddressableAssets.Settings; using UnityEngine; using UnityEngine.AddressableAssets; namespace UnityEditor.AddressableAssets.Build { /// /// Static system to manage Analyze functionality. /// [Serializable] public static class AnalyzeSystem { /// /// Method used to register any custom AnalyzeRules with the AnalyzeSystem. This replaces calling into the AnalyzeWindow /// directly to remove logic from the GUI. The recommended pattern is to create /// your rules like so: /// /// class MyRule : AnalyzeRule {} /// [InitializeOnLoad] /// class RegisterMyRule /// { /// static RegisterMyRule() /// { /// AnalyzeSystem.RegisterNewRule<MyRule>(); /// } /// } /// /// /// The rule type. public static void RegisterNewRule() where TRule : AnalyzeRule, new() { foreach (var rule in Rules) { if (rule.GetType().Equals(typeof(TRule))) return; } Rules.Add(new TRule()); } internal static string AnalyzeRuleDataFolder { get { return $"{Addressables.LibraryPath}/AnalyzeData"; } } internal static string AnalyzeRuleDataName => "AnalyzeRuleData.json"; internal static string AnalyzeRuleDataPath => AnalyzeRuleDataFolder + "/" + AnalyzeRuleDataName; internal static string AnalyzeRuleDataAssetsFolderPath { get { var settings = AddressableAssetSettingsDefaultObject.Settings; var path = AddressableAssetSettingsDefaultObject.kDefaultConfigFolder; if (settings != null && settings.IsPersisted) path = settings.ConfigFolder; return path + "/AnalyzeData/"; } } internal static AddressableAssetSettings Settings => AddressableAssetSettingsDefaultObject.Settings; internal static List Rules { get; } = new List(); [SerializeField] private static AddressablesAnalyzeResultData m_AnalyzeData; internal static AssetSettingsAnalyzeTreeView TreeView { get; set; } internal static AddressablesAnalyzeResultData AnalyzeData { get { if (m_AnalyzeData == null) { if (!Directory.Exists(AnalyzeRuleDataFolder)) Directory.CreateDirectory(AnalyzeRuleDataFolder); DeserializeData(); } return m_AnalyzeData; } } internal static void ReloadUI() { TreeView?.Reload(); } internal static void SerializeData() { SerializeData(AnalyzeRuleDataPath); } internal static void DeserializeData() { DeserializeData(AnalyzeRuleDataPath); } /// /// Serialize the analysis data to json and save to disk /// /// File path to save to public static void SerializeData(string path) { File.WriteAllText(path, JsonUtility.ToJson(m_AnalyzeData)); } /// /// Load and deserialize analysis data from json file and reload /// /// File path to load from public static void DeserializeData(string path) { if (!File.Exists(path)) File.WriteAllText(path, JsonUtility.ToJson(new AddressablesAnalyzeResultData())); //Cleans up the previous result data if (Directory.Exists(AnalyzeRuleDataAssetsFolderPath)) Directory.Delete(AnalyzeRuleDataAssetsFolderPath, true); m_AnalyzeData = JsonUtility.FromJson(File.ReadAllText(path)); if (m_AnalyzeData == null) Addressables.LogWarning($"Unable to load Analyze Result Data at {path}."); else { if (m_AnalyzeData.Data == null) m_AnalyzeData.Data = new Dictionary>(); foreach (var rule in Rules) { if (rule == null) { Addressables.LogWarning("An unknown Analyze rule is being skipped because it is null."); continue; } if (!m_AnalyzeData.Data.ContainsKey(rule.ruleName)) m_AnalyzeData.Data.Add(rule.ruleName, new List()); } } ReloadUI(); } internal static void SaveDataForRule(AnalyzeRule rule, object data) { string jsonData = JsonUtility.ToJson(data); string path = $"{AnalyzeRuleDataFolder}/{rule.ruleName}Data.json"; File.WriteAllText(path, jsonData); } internal static T GetDataForRule(AnalyzeRule rule) { string path = $"{AnalyzeRuleDataFolder}/{rule.ruleName}Data.json"; if (!File.Exists(path)) return default; string fileRead = File.ReadAllText(path); return JsonUtility.FromJson(fileRead); } internal static void ReplaceAnalyzeData(AnalyzeRule rule, List results) { m_AnalyzeData.Data[rule.ruleName] = results; } internal static List RefreshAnalysis() where TRule : AnalyzeRule { return RefreshAnalysis(FindRule()); } internal static List RefreshAnalysis(AnalyzeRule rule) { if (rule == null) return null; if (!AnalyzeData.Data.ContainsKey(rule.ruleName)) AnalyzeData.Data.Add(rule.ruleName, new List()); AnalyzeData.Data[rule.ruleName] = rule.RefreshAnalysis(Settings); return AnalyzeData.Data[rule.ruleName]; } internal static void ClearAnalysis() where TRule : AnalyzeRule { ClearAnalysis(FindRule()); } internal static void ClearAnalysis(AnalyzeRule rule) { if (rule == null) return; if (!AnalyzeData.Data.ContainsKey(rule.ruleName)) AnalyzeData.Data.Add(rule.ruleName, new List()); rule.ClearAnalysis(); ; AnalyzeData.Data[rule.ruleName].Clear(); } internal static void FixIssues() where TRule : AnalyzeRule { FixIssues(FindRule()); } internal static void FixIssues(AnalyzeRule rule) { rule?.FixIssues(Settings); } private static AnalyzeRule FindRule() where TRule : AnalyzeRule { var rule = Rules.FirstOrDefault(r => r.GetType().IsAssignableFrom(typeof(TRule))); if (rule == null) throw new ArgumentException($"No rule found corresponding to type {typeof(TRule)}"); return rule; } } }