-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from theowiik/add-autowiring
Add autowiring & instancing
- Loading branch information
Showing
8 changed files
with
256 additions
and
11 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>net7.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>disable</Nullable> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<TargetFramework>net7.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>disable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="GodotSharp" Version="4.0.0" /> | ||
<PackageReference Include="Godot.SourceGenerators" Version="4.0.0" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="GodotSharp" Version="4.0.0"/> | ||
<PackageReference Include="Godot.SourceGenerators" Version="4.0.0"/> | ||
</ItemGroup> | ||
</Project> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" | ||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xml:space="preserve"> | ||
<s:Boolean | ||
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=godotsharper/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using System.Reflection; | ||
using Godot; | ||
|
||
namespace GodotSharper.AutoGetNode; | ||
|
||
/// <summary> | ||
/// A static class that provides extension methods to automatically get nodes in Godot game engine. | ||
/// </summary> | ||
public static class AutoGetNode | ||
{ | ||
/// <summary> | ||
/// Gets all the nodes in the specified node and wires them to the corresponding fields and properties. | ||
/// </summary> | ||
/// <param name="node">The node to get the nodes from.</param> | ||
public static void GetNodes(this Node node) | ||
{ | ||
WireMembers(node, GetFields(node)); | ||
WireMembers(node, GetProperties(node)); | ||
} | ||
|
||
/// <summary> | ||
/// Gets all the fields in the specified node. | ||
/// </summary> | ||
/// <param name="node">The node to get the fields from.</param> | ||
/// <returns>An IEnumerable of FieldInfo objects.</returns> | ||
private static IEnumerable<FieldInfo> GetFields(Node node) | ||
{ | ||
return GetMembers<FieldInfo>(node); | ||
} | ||
|
||
/// <summary> | ||
/// Gets all the properties in the specified node. | ||
/// </summary> | ||
/// <param name="node">The node to get the properties from.</param> | ||
/// <returns>An IEnumerable of PropertyInfo objects.</returns> | ||
private static IEnumerable<PropertyInfo> GetProperties(Node node) | ||
{ | ||
return GetMembers<PropertyInfo>(node); | ||
} | ||
|
||
/// <summary> | ||
/// Gets all the members of the specified type in the specified node. | ||
/// </summary> | ||
/// <typeparam name="T">The type of member to get.</typeparam> | ||
/// <param name="node">The node to get the members from.</param> | ||
/// <returns>An IEnumerable of T objects.</returns> | ||
private static IEnumerable<T> GetMembers<T>(Node node) | ||
where T : MemberInfo | ||
{ | ||
if (node == null) | ||
return new List<T>(); | ||
|
||
var members = node.GetType() | ||
.GetMembers( | ||
BindingFlags.FlattenHierarchy | ||
| BindingFlags.Public | ||
| BindingFlags.NonPublic | ||
| BindingFlags.Instance | ||
) | ||
.OfType<T>(); | ||
|
||
return new List<T>(members); | ||
} | ||
|
||
/// <summary> | ||
/// Wires all the members in the specified node to the corresponding nodes. | ||
/// </summary> | ||
/// <typeparam name="T">The type of member to wire.</typeparam> | ||
/// <param name="node">The node to wire the members to.</param> | ||
/// <param name="members">The members to wire.</param> | ||
private static void WireMembers<T>(Node node, IEnumerable<T> members) | ||
where T : MemberInfo | ||
{ | ||
foreach (var member in members) | ||
member.GetCustomAttribute<GetNodeAttribute>()?.SetNode(member, node); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
using System.Reflection; | ||
using Godot; | ||
using GodotSharper.Exceptions; | ||
|
||
namespace GodotSharper.AutoGetNode; | ||
|
||
/// <summary> | ||
/// Attribute used to automatically get a node from the scene tree. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] | ||
public sealed class GetNodeAttribute : Attribute | ||
{ | ||
private readonly string _path; | ||
|
||
private GetNodeAttribute(string nodePath) | ||
{ | ||
_path = nodePath; | ||
} | ||
|
||
/// <summary> | ||
/// Sets the node specified by the attribute on the given member of the provided node. | ||
/// </summary> | ||
/// <param name="memberInfo">The member to set the node on.</param> | ||
/// <param name="node">The node to get the child node from.</param> | ||
/// <exception cref="NodeNotFoundException">Thrown if the child node cannot be found.</exception> | ||
/// <exception cref="ArgumentException">Thrown if the child node is not of the expected type.</exception> | ||
public void SetNode(MemberInfo memberInfo, Node node) | ||
{ | ||
var childNode = node.GetNodeOrNull(_path); | ||
|
||
if (childNode == null) | ||
{ | ||
node.GetTree().Quit(); | ||
throw new NodeNotFoundException($"Cannot find Node for NodePath '{_path}'"); | ||
} | ||
|
||
var expectedType = memberInfo is FieldInfo fieldInfo | ||
? fieldInfo.FieldType | ||
: ((PropertyInfo)memberInfo).PropertyType; | ||
|
||
if (childNode.GetType() != expectedType && !childNode.GetType().IsSubclassOf(expectedType)) | ||
{ | ||
node.GetTree().Quit(); | ||
throw new ArgumentException( | ||
$"Node is not a valid type. Expected {expectedType} got {childNode.GetType()}" | ||
); | ||
} | ||
|
||
switch (memberInfo) | ||
{ | ||
case FieldInfo fieldInformation: | ||
fieldInformation.SetValue(node, childNode); | ||
break; | ||
case PropertyInfo propertyInformation: | ||
propertyInformation.SetValue(node, childNode); | ||
break; | ||
default: | ||
throw new ArgumentException( | ||
$"MemberInfo is not a valid type. Expected {nameof(FieldInfo)} or {nameof(PropertyInfo)} got {memberInfo.GetType()}" | ||
); | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/// <summary> | ||
/// Represents an exception that is thrown when a node is not found in the scene tree. | ||
/// </summary> | ||
|
||
namespace GodotSharper.Exceptions; | ||
|
||
public sealed class NodeNotFoundException : Exception | ||
{ | ||
public NodeNotFoundException(string message) | ||
: base(message) { } | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,29 @@ | ||
using Godot; | ||
|
||
namespace GoSharper; | ||
namespace GodotSharper; | ||
|
||
/// <summary> | ||
/// Extended "GD" class providing utility methods for working with Godot Engine. | ||
/// </summary> | ||
public static class GDX | ||
{ | ||
public static void Printt(string message) => GD.Print(message); | ||
/// <summary> | ||
/// Loads a resource from the specified path and returns it as a strongly-typed object. | ||
/// </summary> | ||
/// <typeparam name="T">The type of the resource to load.</typeparam> | ||
/// <param name="path">The path to the resource to load.</param> | ||
/// <returns>The loaded resource as a strongly-typed object.</returns> | ||
/// <exception cref="FileNotFoundException">Thrown if the resource could not be loaded.</exception> | ||
public static T LoadOrFail<T>(string path) | ||
where T : class | ||
{ | ||
var node = GD.Load<T>(path); | ||
|
||
if (node != null) | ||
return node; | ||
|
||
var msg = $"Could not load resource at {path}"; | ||
GD.PrintErr(msg); | ||
throw new FileNotFoundException(msg); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using Godot; | ||
|
||
namespace GodotSharper.Instancing; | ||
|
||
/// <summary> | ||
/// A static class that provides a method for instantiating Godot nodes. | ||
/// </summary> | ||
public static class Instanter | ||
{ | ||
private static readonly IDictionary<Type, string> s_typePathLookup = | ||
new Dictionary<Type, string>(); | ||
|
||
/// <summary> | ||
/// Instantiates a Godot node of the specified type. | ||
/// </summary> | ||
/// <typeparam name="T">The type of the node to instantiate.</typeparam> | ||
/// <returns>The instantiated node.</returns> | ||
/// <exception cref="FileNotFoundException">Thrown if the PackedSceneAttribute for the specified type is not found.</exception> | ||
public static T Instantiate<T>() | ||
where T : Node | ||
{ | ||
var type = typeof(T); | ||
string path; | ||
|
||
if (s_typePathLookup.TryGetValue(type, out var value)) | ||
{ | ||
path = value; | ||
} | ||
else | ||
{ | ||
var attr = (InstantiableAttribute) | ||
Attribute.GetCustomAttribute(type, typeof(InstantiableAttribute)); | ||
|
||
if (attr == null) | ||
throw new FileNotFoundException( | ||
"Could not find a PackedSceneAttribute for " + type | ||
); | ||
|
||
path = attr.Path; | ||
s_typePathLookup[type] = path; | ||
} | ||
|
||
var scene = GDX.LoadOrFail<PackedScene>(path); | ||
return scene.Instantiate<T>(); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
namespace GodotSharper.Instancing; | ||
|
||
/// <summary> | ||
/// Attribute used to mark a class as instantiable in Godot. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Class)] | ||
public sealed class InstantiableAttribute : Attribute | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="InstantiableAttribute" /> class with the specified path. | ||
/// </summary> | ||
/// <param name="path">The path to the scene file for the instantiable class.</param> | ||
public InstantiableAttribute(string path) | ||
{ | ||
Path = path; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the path to the scene file for the instantiable class. | ||
/// </summary> | ||
public string Path { get; } | ||
} |