using AssetBank.Settings; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using Newtonsoft.Json.Linq; namespace AssetBank.Editor.Tools { /// /// Handles the core logic for scanning the project and managing the component whitelist. /// public class ComponentWhitelistController { private static readonly string ProjectRoot = Path.GetDirectoryName(Application.dataPath); public enum ScanSource { Scenes, Prefabs, Both } /// /// Scans the project for components based on the selected source. /// /// The type of assets to scan. /// A HashSet of unique component type names found. public HashSet ScanProject(ScanSource scanSource) { var settings = ProjectExporterSettings.GetOrCreateSettings(); var ignoredFolders = settings.FoldersToIgnore; var assetsToScan = new List(); if (scanSource == ScanSource.Scenes || scanSource == ScanSource.Both) { assetsToScan.AddRange(AssetDatabase.FindAssets("t:Scene", new[] { "Assets" })); } if (scanSource == ScanSource.Prefabs || scanSource == ScanSource.Both) { assetsToScan.AddRange(AssetDatabase.FindAssets("t:Prefab", new[] { "Assets" })); } var assetPaths = assetsToScan.Distinct().Select(AssetDatabase.GUIDToAssetPath).ToList(); var filteredPaths = assetPaths.Where(path => !ignoredFolders.Any(folder => path.StartsWith(folder, StringComparison.OrdinalIgnoreCase))).ToList(); var tempDir = Path.Combine("Library", "ComponentScanTemp"); if (Directory.Exists(tempDir)) { Directory.Delete(tempDir, true); } Directory.CreateDirectory(tempDir); var uniqueComponents = new HashSet(); try { EditorUtility.DisplayProgressBar("Scanning Components", "Preparing to scan...", 0f); for (int i = 0; i < filteredPaths.Count; i++) { var assetPath = filteredPaths[i]; var progress = (float)i / filteredPaths.Count; EditorUtility.DisplayProgressBar("Scanning Components", $"Processing: {Path.GetFileName(assetPath)}", progress); var jsonPath = Path.Combine(tempDir, $"{Path.GetFileNameWithoutExtension(assetPath)}_{i}.json"); if (ExecutePythonConversion(assetPath, jsonPath)) { var components = ParseComponentsFromJson(jsonPath); uniqueComponents.UnionWith(components); } } } finally { if (Directory.Exists(tempDir)) { Directory.Delete(tempDir, true); } EditorUtility.ClearProgressBar(); } return uniqueComponents; } private string FindPythonExecutable() { var venvPath = Path.Combine(ProjectRoot, "venv", "bin", "python3"); if (File.Exists(venvPath)) { return venvPath; } venvPath = Path.Combine(ProjectRoot, "venv", "bin", "python"); if (File.Exists(venvPath)) { return venvPath; } return "python3"; } /// /// Executes the Python conversion script for a given asset. /// private bool ExecutePythonConversion(string assetPath, string jsonOutputPath) { var pythonExecutable = FindPythonExecutable(); var scriptGuid = AssetDatabase.FindAssets("convert_scene").FirstOrDefault(); if (string.IsNullOrEmpty(scriptGuid)) { UnityEngine.Debug.LogError("Conversion script 'convert_scene.py' not found."); return false; } var scriptPath = Path.Combine(ProjectRoot, AssetDatabase.GUIDToAssetPath(scriptGuid)); var absoluteAssetPath = Path.Combine(ProjectRoot, assetPath); var process = new Process { StartInfo = new ProcessStartInfo { FileName = pythonExecutable, Arguments = $"\"{scriptPath}\" \"{absoluteAssetPath}\" \"{jsonOutputPath}\"", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); if (process.ExitCode == 0) return true; UnityEngine.Debug.LogError($"Failed to convert {assetPath}. Error: {error}"); return false; } /// /// Parses a temporary JSON file to extract unique component names. /// private IEnumerable ParseComponentsFromJson(string jsonPath) { var components = new HashSet(); try { var jsonContent = File.ReadAllText(jsonPath); var jsonArray = JArray.Parse(jsonContent); foreach (var item in jsonArray) { var data = item["data"]; if (data is JObject dataObject) { var componentName = dataObject.Properties().FirstOrDefault()?.Name; if (!string.IsNullOrEmpty(componentName)) { components.Add(componentName); } } } } catch (Exception e) { UnityEngine.Debug.LogError($"Failed to parse JSON file {jsonPath}. Error: {e.Message}"); } return components; } /// /// Saves the selected components to the ComponentWhitelistSettings asset. /// public void SaveWhitelist(Dictionary selection) { var settings = ComponentWhitelistSettings.GetOrCreateSettings(); settings.WhitelistedComponents.Clear(); settings.WhitelistedComponents.AddRange(selection.Where(kvp => kvp.Value).Select(kvp => kvp.Key)); EditorUtility.SetDirty(settings); AssetDatabase.SaveAssets(); EditorUtility.DisplayDialog("Success", $"Whitelist saved with {settings.WhitelistedComponents.Count} components.", "OK"); } } }