using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Godot; #if TOOLS namespace Texty.addons.ClassExporter { [Tool] public partial class Plugin : EditorPlugin { private const string LoadTypesMenuItem = "Load types"; private const string ReloadTypesMenuItem = "Reload types"; private readonly List _registeredTypes = new(); public override void _EnterTree() { Connect("resource_saved", new Callable(this, nameof(OnResourceSaved))); AddToolMenuItem(LoadTypesMenuItem, Callable.From(() => OnReloadTypes(false))); AddToolMenuItem(ReloadTypesMenuItem, Callable.From(() => OnReloadTypes(true))); RegisterTypes(); } public override void _EnablePlugin() { RegisterTypes(); } public override void _DisablePlugin() { DeRegisterTypes(); } public override void _ExitTree() { DeRegisterTypes(); RemoveToolMenuItem(LoadTypesMenuItem); RemoveToolMenuItem(ReloadTypesMenuItem); Disconnect("resource_saved", new Callable(this, nameof(OnResourceSaved))); } // Required by Godot // ReSharper disable once UnusedParameter.Local private void OnResourceSaved(Resource resource) { RegisterTypes(); } private void OnReloadTypes(bool reload) { if (reload) DeRegisterTypes(); RegisterTypes(); } private void RegisterTypes() { DeRegisterTypes(); var assembly = Assembly.GetExecutingAssembly(); var newTypes = from type in assembly.GetTypes() where type.IsSubclassOf(typeof(Resource)) || type.IsSubclassOf(typeof(Node)) where !type.IsSubclassOf(typeof(EditorPlugin)) where _registeredTypes.Find(r => r.Equals(type)) == null select new TypeEntry(type); foreach (var type in newTypes) { GD.Print($"Exporting class {type.Name}"); AddCustomType(type.Name, type.Base, type.Script, null); _registeredTypes.Add(type); } } private void DeRegisterTypes() { foreach (var type in _registeredTypes) { GD.Print($"Removing class {type.Name}"); RemoveCustomType(type.Name); } _registeredTypes.Clear(); } private partial class TypeEntry : RefCounted, IEquatable { public readonly string Base = ""; public readonly string Name = ""; public readonly Script Script = new CSharpScript(); // Required by Godot // ReSharper disable once UnusedMember.Local public TypeEntry() { } public TypeEntry(Type type) { Name = type.Name; Base = type.BaseType?.Name ?? "Object"; Script = LoadScript(type.FullName ?? ""); } public bool Equals(Type other) { return other != null && Name == other.Name && Base == other.BaseType?.Name; } private static CSharpScript LoadScript(string fullName) { var components = fullName.Split('.'); var path = string.Join("/", components.Skip(1)) + ".cs"; return ResourceLoader.Load(path); } } } } #endif