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 { /// <summary> /// Static system to manage Analyze functionality. /// </summary> [Serializable] public static class AnalyzeSystem { /// <summary> /// 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: /// <code> /// class MyRule : AnalyzeRule {} /// [InitializeOnLoad] /// class RegisterMyRule /// { /// static RegisterMyRule() /// { /// AnalyzeSystem.RegisterNewRule<MyRule>(); /// } /// } /// </code> /// </summary> /// <typeparam name="TRule">The rule type.</typeparam> public static void RegisterNewRule<TRule>() 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<AnalyzeRule> Rules { get; } = new List<AnalyzeRule>(); [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); } /// <summary> /// Serialize the analysis data to json and save to disk /// </summary> /// <param name="path">File path to save to</param> public static void SerializeData(string path) { File.WriteAllText(path, JsonUtility.ToJson(m_AnalyzeData)); } /// <summary> /// Load and deserialize analysis data from json file and reload /// </summary> /// <param name="path">File path to load from</param> 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<AddressablesAnalyzeResultData>(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<string, List<AnalyzeRule.AnalyzeResult>>(); 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<AnalyzeRule.AnalyzeResult>()); } } 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<T>(AnalyzeRule rule) { string path = $"{AnalyzeRuleDataFolder}/{rule.ruleName}Data.json"; if (!File.Exists(path)) return default; string fileRead = File.ReadAllText(path); return JsonUtility.FromJson<T>(fileRead); } internal static void ReplaceAnalyzeData(AnalyzeRule rule, List<AnalyzeRule.AnalyzeResult> results) { m_AnalyzeData.Data[rule.ruleName] = results; } internal static List<AnalyzeRule.AnalyzeResult> RefreshAnalysis<TRule>() where TRule : AnalyzeRule { return RefreshAnalysis(FindRule<TRule>()); } internal static List<AnalyzeRule.AnalyzeResult> RefreshAnalysis(AnalyzeRule rule) { if (rule == null) return null; if (!AnalyzeData.Data.ContainsKey(rule.ruleName)) AnalyzeData.Data.Add(rule.ruleName, new List<AnalyzeRule.AnalyzeResult>()); AnalyzeData.Data[rule.ruleName] = rule.RefreshAnalysis(Settings); return AnalyzeData.Data[rule.ruleName]; } internal static void ClearAnalysis<TRule>() where TRule : AnalyzeRule { ClearAnalysis(FindRule<TRule>()); } internal static void ClearAnalysis(AnalyzeRule rule) { if (rule == null) return; if (!AnalyzeData.Data.ContainsKey(rule.ruleName)) AnalyzeData.Data.Add(rule.ruleName, new List<AnalyzeRule.AnalyzeResult>()); rule.ClearAnalysis(); ; AnalyzeData.Data[rule.ruleName].Clear(); } internal static void FixIssues<TRule>() where TRule : AnalyzeRule { FixIssues(FindRule<TRule>()); } internal static void FixIssues(AnalyzeRule rule) { rule?.FixIssues(Settings); } private static AnalyzeRule FindRule<TRule>() 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; } } }