using System; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Godot; namespace GodotHostTest.addons.godot_di; /// /// Taken from https://github.com/godotengine/godot-proposals/issues/12294 /// public static class GodotScriptPathCache { private static readonly ConcurrentDictionary TypeToPath = new(); private static readonly ConcurrentDictionary PathToType = new(); private static readonly Type GodotObject = typeof(GodotObject); private static readonly Type ScriptPathAttribute = typeof(ScriptPathAttribute); private static AssemblyLoadEventHandler? _assemblyLoadEventHandler; private static void InitializeIfNeeded() { if (_assemblyLoadEventHandler != null) return; Initialize(); System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly())! .Unloading += _ => { Deinitialize(); }; } private static void Initialize() { GD.Print($"Initializing {nameof(GodotScriptPathCache)}"); TypeToPath.Clear(); PathToType.Clear(); _assemblyLoadEventHandler = (sender, args) => CacheScriptsInAssembly(args.LoadedAssembly); AppDomain.CurrentDomain.AssemblyLoad += _assemblyLoadEventHandler; foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) CacheScriptsInAssembly(assembly); return; void CacheScriptsInAssembly(Assembly assembly) { foreach (var type in assembly.GetTypes() .Where(t => t.IsClass) .Where(t => t.IsSubclassOf(GodotObject))) // get script path attribute if (type.GetCustomAttributes(ScriptPathAttribute, false).FirstOrDefault() is ScriptPathAttribute scriptPath) { TypeToPath[type] = scriptPath.Path; PathToType[scriptPath.Path] = type; } } } private static void Deinitialize() { GD.Print($"Deinitializing {nameof(GodotScriptPathCache)}"); AppDomain.CurrentDomain.AssemblyLoad -= _assemblyLoadEventHandler; _assemblyLoadEventHandler = null; TypeToPath.Clear(); PathToType.Clear(); } public static bool TryGetScriptPath(Type type, [MaybeNullWhen(false)] out string path) { InitializeIfNeeded(); return TypeToPath.TryGetValue(type, out path); } public static Script GetScriptFromType(Type type) { InitializeIfNeeded(); return TryGetScriptPath(type, out string path) ? GD.Load