|
@@ -0,0 +1,187 @@
|
|
|
|
+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
|
|
|
|
+{
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Handles the core logic for scanning the project and managing the component whitelist.
|
|
|
|
+ /// </summary>
|
|
|
|
+ public class ComponentWhitelistController
|
|
|
|
+ {
|
|
|
|
+ private static readonly string ProjectRoot = Path.GetDirectoryName(Application.dataPath);
|
|
|
|
+
|
|
|
|
+ public enum ScanSource
|
|
|
|
+ {
|
|
|
|
+ Scenes,
|
|
|
|
+ Prefabs,
|
|
|
|
+ Both
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Scans the project for components based on the selected source.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="scanSource">The type of assets to scan.</param>
|
|
|
|
+ /// <returns>A HashSet of unique component type names found.</returns>
|
|
|
|
+ public HashSet<string> ScanProject(ScanSource scanSource)
|
|
|
|
+ {
|
|
|
|
+ var settings = ProjectExporterSettings.GetOrCreateSettings();
|
|
|
|
+ var ignoredFolders = settings.FoldersToIgnore;
|
|
|
|
+ var assetsToScan = new List<string>();
|
|
|
|
+
|
|
|
|
+ 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<string>();
|
|
|
|
+
|
|
|
|
+ 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";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Executes the Python conversion script for a given asset.
|
|
|
|
+ /// </summary>
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Parses a temporary JSON file to extract unique component names.
|
|
|
|
+ /// </summary>
|
|
|
|
+ private IEnumerable<string> ParseComponentsFromJson(string jsonPath)
|
|
|
|
+ {
|
|
|
|
+ var components = new HashSet<string>();
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Saves the selected components to the ComponentWhitelistSettings asset.
|
|
|
|
+ /// </summary>
|
|
|
|
+ public void SaveWhitelist(Dictionary<string, bool> 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");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|