using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using UnityEditor; namespace RunlevelSystems.DevPlus1.EditorTools { public static class RLScriptAnalyzer { private static readonly string[] LifecycleMethods = { "Awake", "OnEnable", "Start", "Update", "FixedUpdate", "LateUpdate", "OnDisable", "OnDestroy", "OnTriggerEnter", "OnTriggerExit", "OnCollisionEnter", "OnCollisionExit" }; private static readonly string[] UnityApiMarkers = { "SceneManager", "Instantiate", "Destroy", "GetComponent", "FindObjectOfType", "FindFirstObjectByType", "Resources.Load", "Addressables", "Input", "UnityEvent" }; public static List AnalyzeScripts() { List results = new List(); string[] guids = AssetDatabase.FindAssets("t:MonoScript"); foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); if (path.StartsWith("Assets/RL_DevPlus1/Editor/")) { continue; } MonoScript monoScript = AssetDatabase.LoadAssetAtPath(path); if (monoScript == null) { continue; } string text = File.Exists(path) ? File.ReadAllText(path) : string.Empty; System.Type scriptClass = monoScript.GetClass(); RLScriptInfo info = new RLScriptInfo(); info.scriptPath = path; info.className = scriptClass != null ? scriptClass.Name : GuessClassName(text); info.baseClass = scriptClass != null && scriptClass.BaseType != null ? scriptClass.BaseType.Name : GuessBaseClass(text); info.isMonoBehaviour = scriptClass != null && typeof(UnityEngine.MonoBehaviour).IsAssignableFrom(scriptClass); info.isScriptableObject = scriptClass != null && typeof(UnityEngine.ScriptableObject).IsAssignableFrom(scriptClass); info.lifecycleMethods.AddRange(FindLifecycleMethods(text)); info.publicMethods.AddRange(FindPublicMethods(text)); info.serializedFields.AddRange(FindSerializedFields(text)); info.publicFields.AddRange(FindPublicFields(text)); info.unityApiUsage.AddRange(FindUnityApiUsage(text)); info.possibleRole = GuessRole(path, info); results.Add(info); } return results; } private static string GuessClassName(string text) { Match match = Regex.Match(text, @"class\s+([A-Za-z_][A-Za-z0-9_]*)"); return match.Success ? match.Groups[1].Value : "not detected"; } private static string GuessBaseClass(string text) { Match match = Regex.Match(text, @"class\s+[A-Za-z_][A-Za-z0-9_]*\s*:\s*([A-Za-z_][A-Za-z0-9_]*)"); return match.Success ? match.Groups[1].Value : "not detected"; } private static List FindLifecycleMethods(string text) { List results = new List(); foreach (string method in LifecycleMethods) { if (Regex.IsMatch(text, @"\b" + method + @"\s*\(")) { results.Add(method); } } return results; } private static List FindPublicMethods(string text) { return UniqueMatches(text, @"public\s+[A-Za-z_][A-Za-z0-9_<>,\[\]\?]*\s+([A-Za-z_][A-Za-z0-9_]*)\s*\("); } private static List FindSerializedFields(string text) { return UniqueMatches(text, @"\[SerializeField\]\s*(private|protected|public)?\s*[A-Za-z_][A-Za-z0-9_<>,\[\]]*\s+([A-Za-z_][A-Za-z0-9_]*)", 2); } private static List FindPublicFields(string text) { return UniqueMatches(text, @"public\s+[A-Za-z_][A-Za-z0-9_<>,\[\]]*\s+([A-Za-z_][A-Za-z0-9_]*)\s*(=|;)"); } private static List FindUnityApiUsage(string text) { List results = new List(); foreach (string marker in UnityApiMarkers) { if (text.Contains(marker)) { results.Add(marker); } } return results; } private static List UniqueMatches(string text, string pattern, int group = 1) { List results = new List(); foreach (Match match in Regex.Matches(text, pattern)) { string value = match.Groups[group].Value; if (!string.IsNullOrEmpty(value) && !results.Contains(value)) { results.Add(value); } } return results; } private static string GuessRole(string path, RLScriptInfo info) { string value = (path + " " + info.className).ToLowerInvariant(); if (value.Contains("player")) return "Possible player controller"; if (value.Contains("enemy") || value.Contains("ai")) return "Possible enemy AI"; if (value.Contains("ui") || value.Contains("menu") || value.Contains("hud")) return "Possible UI controller"; if (value.Contains("game") && value.Contains("manager")) return "Possible game manager"; if (value.Contains("spawner") || value.Contains("spawn")) return "Possible spawner"; if (value.Contains("inventory")) return "Possible inventory system"; if (value.Contains("health") || value.Contains("damage")) return "Possible health/damage system"; if (value.Contains("camera")) return "Possible camera controller"; if (value.Contains("audio") || value.Contains("music")) return "Possible audio manager"; if (value.Contains("scene")) return "Possible scene loader"; if (info.isScriptableObject) return "Possible data/config asset"; return "Possible utility or gameplay script"; } } }