Created
June 22, 2022 13:00
-
-
Save derkork/e21ca4eaf8b6e7bf9249fc09d2eaa635 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using JetBrains.Annotations; | |
namespace YoHDot.Addons.CustomScripts | |
{ | |
using System; | |
using System.Runtime.CompilerServices; | |
/// <summary> | |
/// Marks the script as used (because Rider cannot see usages in TSCN files). | |
/// If <see cref="Icon"/> is given or <see cref="RegisterAsNode"/> is set to true, | |
/// will also register the script as a custom node in the editor. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Class, Inherited = false)] | |
[MeansImplicitUse] | |
public sealed class CustomScriptAttribute : Attribute | |
{ | |
public CustomScriptAttribute(string icon = null, bool registerAsNode = false, [CallerFilePath] string path = "") | |
{ | |
Icon = icon; | |
RegisterAsNode = registerAsNode || icon != null; | |
Path = path; | |
} | |
public string Icon { get; } | |
public bool RegisterAsNode { get; } | |
public string Path { get; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if TOOLS | |
using System; | |
using System.Linq; | |
using System.Reflection; | |
using Godot; | |
using Godot.Collections; | |
using YoHDot.Utilities; | |
namespace YoHDot.Addons.CustomScripts | |
{ | |
[Tool] | |
public class CustomScriptsPlugin : EditorPlugin, ISerializationListener | |
{ | |
// this is a godot array to survive reloads and not get any errors about unmarshallable types | |
private Array<string> _loadedNodes; | |
public override void _EnterTree() | |
{ | |
RegisterCustomNodes(); | |
} | |
public override void _ExitTree() | |
{ | |
UnloadCustomNodes(); | |
} | |
private void UnloadCustomNodes() | |
{ | |
if (_loadedNodes == null) | |
{ | |
return; | |
} | |
foreach (var type in _loadedNodes) | |
{ | |
RemoveCustomType(type); | |
} | |
GD.Print($"Unloaded {_loadedNodes.Count} custom nodes."); | |
_loadedNodes = null; | |
} | |
public void OnBeforeSerialize() | |
{ | |
} | |
public void OnAfterDeserialize() | |
{ | |
// Refresh custom nodes | |
UnloadCustomNodes(); | |
RegisterCustomNodes(); | |
} | |
private void RegisterCustomNodes() | |
{ | |
try | |
{ | |
var customNodes = Assembly.GetAssembly(typeof(CustomScriptsPlugin)).GetExportedTypes() | |
.Select(it => (it, it.GetCustomAttribute<CustomScriptAttribute>())) | |
.Where(it => it.Item2 is {RegisterAsNode: true}).ToList(); | |
_loadedNodes = new Array<string>(); | |
foreach (var (type, attribute) in customNodes) | |
{ | |
GD.Print("Loading " + type.Name); | |
if (string.IsNullOrEmpty(attribute.Path)) | |
{ | |
GD.PushError( | |
$"In class '{type.Name}': script path is missing" + | |
" in CustomNode attribute. I will not register this custom node."); | |
} | |
var script = CustomScript.GetScript(type); | |
if (!(script is Script theScript)) | |
{ | |
GD.PushError( | |
$"In class '{type.Name}': given path '{attribute.Path}' is is not " + | |
"a valid script. I will not register this custom node."); | |
continue; | |
} | |
Resource theIcon = null; | |
if (!string.IsNullOrEmpty(attribute.Icon)) | |
{ | |
theIcon = GD.Load(attribute.Icon); | |
if (!(theIcon is Texture)) | |
{ | |
GD.PushError( | |
$"In class '{type.Name}': given icon '{attribute.Icon}' is is not " + | |
"a valid texture. I will register this custom node without an icon."); | |
} | |
} | |
var baseType = type.BaseType; | |
Debug.Assert(baseType != null, "type.BaseType != null"); | |
// workaround for https://github.com/godotengine/godot/issues/41211 | |
// go up the hierarchy until you find something built-in into godot. | |
while ( baseType != null && (!baseType.Namespace?.StartsWith("Godot") ?? false)) | |
{ | |
baseType = baseType.BaseType; | |
} | |
// worst case, go with "Node" | |
var baseTypeName = baseType?.Name ?? "Node"; | |
AddCustomType(type.Name, baseTypeName, theScript, (Texture) theIcon); | |
_loadedNodes.Add(type.Name); | |
} | |
GD.Print($"Loaded {_loadedNodes.Count} custom nodes."); | |
} | |
catch (Exception e) | |
{ | |
GD.PushError("Problem when loading custom nodes."); | |
GD.PushError(e.Message); | |
} | |
} | |
} | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[plugin] | |
name="CustomScripts" | |
description="Custom Scripts for C#" | |
author="" | |
version="" | |
script="CustomScriptsPlugin.cs" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment