diff --git a/Game/GameRootServiceSource.cs b/Game/LevelServiceSource.cs similarity index 87% rename from Game/GameRootServiceSource.cs rename to Game/LevelServiceSource.cs index 837120d..ed30e05 100644 --- a/Game/GameRootServiceSource.cs +++ b/Game/LevelServiceSource.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection; namespace GodotHostTest.Game; -public partial class GameRootServiceSource : RootServiceSource +public partial class LevelServiceSource : RootServiceSource { [Export] private TargetSpawner _targetSpawner = null!; diff --git a/Game/GameRootServiceSource.cs.uid b/Game/LevelServiceSource.cs.uid similarity index 100% rename from Game/GameRootServiceSource.cs.uid rename to Game/LevelServiceSource.cs.uid diff --git a/Interfaces/ILevel.cs b/Interfaces/ILevel.cs new file mode 100644 index 0000000..7f05d86 --- /dev/null +++ b/Interfaces/ILevel.cs @@ -0,0 +1,5 @@ +namespace GodotHostTest.Interfaces; + +public interface ILevel +{ +} \ No newline at end of file diff --git a/addons/GodotDI/GodotScriptPathCache.cs b/addons/GodotDI/Editor/GodotScriptPathCache.cs similarity index 80% rename from addons/GodotDI/GodotScriptPathCache.cs rename to addons/GodotDI/Editor/GodotScriptPathCache.cs index 2465221..d93a620 100644 --- a/addons/GodotDI/GodotScriptPathCache.cs +++ b/addons/GodotDI/Editor/GodotScriptPathCache.cs @@ -1,11 +1,13 @@ +#if TOOLS using System; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Loader; using Godot; -namespace GodotHostTest.addons.godot_di; +namespace GodotHostTest.GodotDI.Editor; /// /// Taken from https://github.com/godotengine/godot-proposals/issues/12294 @@ -23,7 +25,7 @@ public static class GodotScriptPathCache { if (_assemblyLoadEventHandler != null) return; Initialize(); - System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly())! + AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly())! .Unloading += _ => { Deinitialize(); }; } @@ -87,4 +89,21 @@ public static class GodotScriptPathCache InitializeIfNeeded(); return PathToType.TryGetValue(path, out type); } -} \ No newline at end of file + + public static Type? GetCSharpScriptType(this GodotObject node) + { + if (node.GetScript().AsGodotObject() is CSharpScript script && + TryGetTypeFromPath(script.ResourcePath, out var type)) + return type; + + return null; + } + + public static bool IsServiceSource(this GodotObject node) + { + return node.GetScript().AsGodotObject() is CSharpScript script && + TryGetTypeFromPath(script.ResourcePath, out var type) && + type.IsAssignableTo(typeof(ServiceSource)); + } +} +#endif \ No newline at end of file diff --git a/addons/GodotDI/GodotScriptPathCache.cs.uid b/addons/GodotDI/Editor/GodotScriptPathCache.cs.uid similarity index 100% rename from addons/GodotDI/GodotScriptPathCache.cs.uid rename to addons/GodotDI/Editor/GodotScriptPathCache.cs.uid diff --git a/addons/GodotDI/GodotDI.cs.uid b/addons/GodotDI/Editor/Plugin.cs.uid similarity index 100% rename from addons/GodotDI/GodotDI.cs.uid rename to addons/GodotDI/Editor/Plugin.cs.uid diff --git a/addons/GodotDI/ScopeInspectorPlugin.cs.uid b/addons/GodotDI/Editor/ScopeInspectorPlugin.cs.uid similarity index 100% rename from addons/GodotDI/ScopeInspectorPlugin.cs.uid rename to addons/GodotDI/Editor/ScopeInspectorPlugin.cs.uid diff --git a/addons/GodotDI/ScopePickerEditor.cs b/addons/GodotDI/Editor/ScopePickerEditor.cs similarity index 95% rename from addons/GodotDI/ScopePickerEditor.cs rename to addons/GodotDI/Editor/ScopePickerEditor.cs index 5cce9c4..6b56a9d 100644 --- a/addons/GodotDI/ScopePickerEditor.cs +++ b/addons/GodotDI/Editor/ScopePickerEditor.cs @@ -1,16 +1,12 @@ +#if TOOLS using System; using System.Linq; using Godot; -namespace GodotHostTest.GodotDI; +namespace GodotHostTest.GodotDI.Editor; public partial class ScopePickerEditor : EditorProperty { - /// - /// The "combo box" that picks a choice from the scopes. - /// - private OptionButton _optionButton = new(); - /// /// The currently chosen type. /// @@ -21,6 +17,11 @@ public partial class ScopePickerEditor : EditorProperty /// private string[] _fullyQualifiedNames; + /// + /// The "combo box" that picks a choice from the scopes. + /// + private OptionButton _optionButton = new(); + /// /// A guard against internal changes while the property is being updated. /// @@ -92,4 +93,5 @@ public partial class ScopePickerEditor : EditorProperty { _optionButton.Selected = Array.IndexOf(_fullyQualifiedNames, _currentType); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/addons/GodotDI/ScopePickerEditor.cs.uid b/addons/GodotDI/Editor/ScopePickerEditor.cs.uid similarity index 100% rename from addons/GodotDI/ScopePickerEditor.cs.uid rename to addons/GodotDI/Editor/ScopePickerEditor.cs.uid diff --git a/addons/GodotDI/EnumerableExtensions.cs b/addons/GodotDI/EnumerableExtensions.cs new file mode 100644 index 0000000..bc5af7f --- /dev/null +++ b/addons/GodotDI/EnumerableExtensions.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace GodotHostTest.GodotDI; + +public static class EnumerableExtensions +{ + public enum SingleFailureReason + { + LessThanOne, + MoreThanOne + } + + public static T? Single(this IEnumerable enumerable, out SingleFailureReason? failureReason) + where T : class + { + using var enumerator = enumerable.GetEnumerator(); + if (!enumerator.MoveNext()) + { + failureReason = SingleFailureReason.LessThanOne; + return null; + } + + var value = enumerator.Current; + if (enumerator.MoveNext()) + { + failureReason = SingleFailureReason.MoreThanOne; + return null; + } + + failureReason = null; + return value; + } +} \ No newline at end of file diff --git a/addons/GodotDI/RandomIntEditor.cs b/addons/GodotDI/RandomIntEditor.cs deleted file mode 100644 index c7033ea..0000000 --- a/addons/GodotDI/RandomIntEditor.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace GodotHostTest.addons.godot_di; - -// RandomIntEditor.cs -#if TOOLS -using Godot; - -public partial class RandomIntEditor : EditorProperty -{ - // The main control for editing the property. - private Button _propertyControl = new Button(); - - // An internal value of the property. - private int _currentValue = 0; - - // A guard against internal changes when the property is updated. - private bool _updating = false; - - public RandomIntEditor() - { - // Add the control as a direct child of EditorProperty node. - AddChild(_propertyControl); - // Make sure the control is able to retain the focus. - AddFocusable(_propertyControl); - // Setup the initial state and connect to the signal to track changes. - RefreshControlText(); - _propertyControl.Pressed += OnButtonPressed; - } - - private void OnButtonPressed() - { - // Ignore the signal if the property is currently being updated. - if (_updating) - { - return; - } - - // Generate a new random integer between 0 and 99. - _currentValue = (int)GD.Randi() % 100; - RefreshControlText(); - EmitChanged(GetEditedProperty(), _currentValue); - } - - public override void _UpdateProperty() - { - // Read the current value from the property. - var newValue = (int)GetEditedObject().Get(GetEditedProperty()); - if (newValue == _currentValue) - { - return; - } - - // Update the control with the new value. - _updating = true; - _currentValue = newValue; - RefreshControlText(); - _updating = false; - } - - private void RefreshControlText() - { - _propertyControl.Text = $"Value: {_currentValue}"; - } -} -#endif \ No newline at end of file diff --git a/addons/GodotDI/RandomIntEditor.cs.uid b/addons/GodotDI/RandomIntEditor.cs.uid deleted file mode 100644 index 25a0380..0000000 --- a/addons/GodotDI/RandomIntEditor.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://db526cjmnd4q7 diff --git a/addons/GodotDI/ServiceSource.cs b/addons/GodotDI/ServiceSource.cs index 232382a..6b16d28 100644 --- a/addons/GodotDI/ServiceSource.cs +++ b/addons/GodotDI/ServiceSource.cs @@ -9,17 +9,22 @@ namespace GodotHostTest.GodotDI; [Icon("res://addons/GodotDI/ServiceSource.svg")] public partial class ServiceSource : Node { - [Export] private string _serviceScope = typeof(RootScope).AssemblyQualifiedName!; + private static readonly string RootScopeQualifiedName = typeof(RootScope).AssemblyQualifiedName!; + [Export] private string _serviceScope = RootScopeQualifiedName; [Export] protected Node?[] InjectedNodes = []; internal void ResolveInjectedNodes(IServiceProvider serviceProvider) { // if this service source must also act as a scope, create the scope and use its service provider - if (_serviceScope != "") + if (_serviceScope != RootScopeQualifiedName) { - serviceProvider = serviceProvider - .CreateScope() - .ServiceProvider; + var serviceProviderCreator = serviceProvider.GetRequiredService>(); + var scopedProvider = serviceProvider.GetRequiredService() + .GetScopedProvider(ScopeTypesAndNames.GetType(_serviceScope)); + serviceProvider = serviceProviderCreator(scopedProvider); + // serviceProvider = serviceProvider + // .CreateScope() + // .ServiceProvider; } // resolve the injected nodes against the chosen service provider diff --git a/addons/GodotDI/plugin.cfg b/addons/GodotDI/plugin.cfg index 717f7df..8e61c65 100644 --- a/addons/GodotDI/plugin.cfg +++ b/addons/GodotDI/plugin.cfg @@ -4,4 +4,4 @@ name="GodotDI" description="" author="owof games" version="0.1" -script="GodotDI.cs" +script="Editor/Plugin.cs" diff --git a/root_scene.tscn b/root_scene.tscn index ddd7f09..0d089b5 100644 --- a/root_scene.tscn +++ b/root_scene.tscn @@ -1,142 +1,10 @@ [gd_scene format=3 uid="uid://vsburk37p4u"] -[ext_resource type="PackedScene" uid="uid://3v8kq1rfwhmr" path="res://Game/target.tscn" id="2_c77vg"] -[ext_resource type="Script" uid="uid://cxfrtglku2gfa" path="res://Game/TargetSpawner.cs" id="3_iqet6"] -[ext_resource type="Script" uid="uid://dk578xnlxjwrr" path="res://Game/GameRootScope.cs" id="3_t2jh2"] -[ext_resource type="Script" uid="uid://bgrjeadxhgmvi" path="res://Game/Score.cs" id="4_t2jh2"] -[ext_resource type="Texture2D" uid="uid://y1ros3y7hag2" path="res://icon.svg" id="5_4ty2a"] -[ext_resource type="Script" uid="uid://ce7gnp1lcrrdv" path="res://Game/Turret.cs" id="6_rckqj"] -[ext_resource type="PackedScene" uid="uid://brbufibn7hi3o" path="res://Game/projectile.tscn" id="7_rckqj"] - -[sub_resource type="Animation" id="Animation_bbjqm"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Score:offset_transform_position") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [Vector2(0, 0)] -} -tracks/1/type = "value" -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/path = NodePath("Score:offset_transform_rotation") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [0.0] -} - -[sub_resource type="Animation" id="Animation_4ty2a"] -resource_name = "wiggle" -length = 0.3 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Score:offset_transform_position") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.049999997, 0.099999994, 0.15, 0.2, 0.24999999), -"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), -"update": 0, -"values": [Vector2(0, 0), Vector2(10, -10), Vector2(-10, -10), Vector2(10, 10), Vector2(-10, 10), Vector2(0, 0)] -} -tracks/1/type = "value" -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/path = NodePath("Score:offset_transform_rotation") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/keys = { -"times": PackedFloat32Array(0, 0.049999997, 0.099999994, 0.15, 0.2, 0.24999999), -"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), -"update": 0, -"values": [0.0, 0.08726646, -0.08726646, 0.08726646, -0.08726646, 0.0] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_rckqj"] -_data = { -&"RESET": SubResource("Animation_bbjqm"), -&"wiggle": SubResource("Animation_4ty2a") -} +[ext_resource type="Script" uid="uid://bvjvojer872c8" path="res://Game/Root.cs" id="1_iqet6"] +[ext_resource type="PackedScene" uid="uid://xfd48ep5qi1k" path="res://Game/level.tscn" id="3_bbjqm"] [node name="Node2D" type="Node2D" unique_id=920849082] +script = ExtResource("1_iqet6") +_levelScene = ExtResource("3_bbjqm") -[node name="RootScope" type="Node" parent="." unique_id=1453141084 node_paths=PackedStringArray("_targetSpawner", "InjectedNodes")] -script = ExtResource("3_t2jh2") -_targetSpawner = NodePath("../Spawner") -InjectedNodes = [NodePath("../Turret"), NodePath("../Target"), NodePath("../Target2"), NodePath("../Target3"), NodePath("../Target4"), NodePath("../Target5"), NodePath("../Target6"), NodePath("../Spawner"), NodePath("../Score")] - -[node name="Target" parent="." unique_id=2101558052 instance=ExtResource("2_c77vg")] -position = Vector2(198, 143) -TimeOutDurationInSeconds = 2.0 - -[node name="Target2" parent="." unique_id=1480287846 instance=ExtResource("2_c77vg")] -position = Vector2(669, 64) -TimeOutDurationInSeconds = 2.0 - -[node name="Target3" parent="." unique_id=242951997 instance=ExtResource("2_c77vg")] -position = Vector2(1060, 263) -TimeOutDurationInSeconds = 2.0 - -[node name="Target4" parent="." unique_id=1190782819 instance=ExtResource("2_c77vg")] -position = Vector2(961, 620) -TimeOutDurationInSeconds = 2.0 - -[node name="Target5" parent="." unique_id=1183803617 instance=ExtResource("2_c77vg")] -position = Vector2(469, 591) -TimeOutDurationInSeconds = 2.0 - -[node name="Target6" parent="." unique_id=2017833482 instance=ExtResource("2_c77vg")] -position = Vector2(53, 417) -TimeOutDurationInSeconds = 2.0 - -[node name="Spawner" type="Node2D" parent="." unique_id=1331368036 node_paths=PackedStringArray("Timer")] -script = ExtResource("3_iqet6") -TimeBetweenSpawns = 1.5 -Timer = NodePath("Timer") - -[node name="Timer" type="Timer" parent="Spawner" unique_id=1142098929] - -[node name="Score" type="ColorRect" parent="." unique_id=1990204205 node_paths=PackedStringArray("_animationPlayer", "_label")] -offset_right = 200.0 -offset_bottom = 40.0 -offset_transform_enabled = true -color = Color(1, 1, 1, 0.2784314) -script = ExtResource("4_t2jh2") -_animationPlayer = NodePath("../AnimationPlayer") -_label = NodePath("Label") - -[node name="Label" type="Label" parent="Score" unique_id=1929317028] -layout_mode = 1 -anchors_preset = -1 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 10.0 -offset_right = -10.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_colors/font_color = Color(0, 0, 0, 1) -text = "Score: 0" -vertical_alignment = 1 - -[node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=1749711689] -libraries/ = SubResource("AnimationLibrary_rckqj") - -[node name="Turret" type="Sprite2D" parent="." unique_id=1932581411] -modulate = Color(1.92523e-07, 0.88004553, 0.8802661, 1) -position = Vector2(539, 307) -texture = ExtResource("5_4ty2a") -script = ExtResource("6_rckqj") -_projectilePackedScene = ExtResource("7_rckqj") - -[connection signal="timeout" from="Spawner/Timer" to="Spawner" method="OnTimer"] +[node name="Level" parent="." unique_id=1331632726 instance=ExtResource("3_bbjqm")]