{"id":21306075,"url":"https://github.com/chickensoft-games/autoinject","last_synced_at":"2026-05-14T02:03:17.624Z","repository":{"id":160751372,"uuid":"625660187","full_name":"chickensoft-games/AutoInject","owner":"chickensoft-games","description":"Node-based dependency injection for C# Godot scripts at build-time, including utilities for automatic node-binding, additional lifecycle hooks, and .net-inspired notification callbacks.","archived":false,"fork":false,"pushed_at":"2025-04-12T00:07:13.000Z","size":181,"stargazers_count":134,"open_issues_count":1,"forks_count":7,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-05-15T11:42:21.142Z","etag":null,"topics":["autoinject","dependency-injection","dotnet","godot"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/Chickensoft.AutoInject/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chickensoft-games.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-04-09T19:46:27.000Z","updated_at":"2025-05-06T04:03:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"dbda5b65-5c8e-4de2-8211-e191176e78a1","html_url":"https://github.com/chickensoft-games/AutoInject","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chickensoft-games%2FAutoInject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chickensoft-games%2FAutoInject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chickensoft-games%2FAutoInject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chickensoft-games%2FAutoInject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chickensoft-games","download_url":"https://codeload.github.com/chickensoft-games/AutoInject/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254573588,"owners_count":22093731,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autoinject","dependency-injection","dotnet","godot"],"created_at":"2024-11-21T16:21:21.249Z","updated_at":"2026-05-14T02:03:17.615Z","avatar_url":"https://github.com/chickensoft-games.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 💉 AutoInject\n\n[![Chickensoft Badge][chickensoft-badge]][chickensoft-website] [![Discord][discord-badge]][discord] ![line coverage][line-coverage] ![branch coverage][branch-coverage]\n\nReflection-free node-based dependency injection for C# Godot scripts, including utilities for automatic node-binding, additional lifecycle hooks, and .net-inspired notification callbacks.\n\n---\n\n\u003cp align=\"center\"\u003e\n\u003cimg alt=\"Chickensoft.AutoInject\" src=\"Chickensoft.AutoInject/icon.png\" width=\"200\"\u003e\n\u003c/p\u003e\n\n## 📘 Background\n\nGame scripts quickly become difficult to maintain when strongly coupled to each other. Various approaches to dependency injection are often used to facilitate weak coupling. For C# scripts in Godot games, AutoInject is provided to allow nodes higher in the scene tree to provide dependencies to their descendant nodes lower in the tree.\n\nAutoInject borrows the concept of a `Provider` and a `Dependent` from [other tree-based dependency provisioning systems][provider]. A `Provider` node provides values to its descendant nodes. A `Dependent` node requests values from its ancestor nodes.\n\nBecause `_Ready/OnReady` is called on node scripts further down the tree first in Godot (see [Understanding Tree Order][tree-order] for more), nodes lower in the tree often cannot access the values they need since they do not exist until their ancestors have a chance to create them in their own `_Ready/OnReady` methods. AutoInject solves this problem by temporarily subscribing to each `Provider` it finds that is still initializing from each `Dependent` until it knows the dependencies have been resolved.\n\nProviding nodes \"top-down\" over sections of the game's scene tree has a few advantages:\n\n- ✅ Dependent nodes can find the nearest ancestor that provides the value they need, allowing provided values to be overridden easily (when desired).\n- ✅ Nodes can be moved around the scene tree without needing to update their dependencies.\n- ✅ Nodes that end up under a different provider will automatically use that new provider's value.\n- ✅ Scripts don't have to know about each other.\n- ✅ The natural flow-of-data mimics the other patterns used throughout the Godot engine.\n- ✅ Dependent scripts can still be run in isolated scenes by providing default fallback values.\n- ✅ Scoping dependencies to the scene tree prevents the existence of values that are invalid above the provider node.\n- ✅ Resolution occurs in O(n), where `n` is the height of the tree above the requesting dependent node (usually only a handful of nodes to search). For deep trees, \"reflecting\" dependencies by re-providing them further down the tree speeds things up further.\n- ✅ Dependencies are resolved when the node enters the scene tree, allowing for O(1) access afterwards. Exiting and re-entering the scene tree triggers the dependency resolution process again (if you call `Node.RequestReady()` from either `IAutoOn.OnExitTree()` or `Node._ExitTree()`).\n- ✅ Scripts can be both dependents and providers.\n\n## 📼 About Mixins\n\nThe [Introspection] generator that AutoInject uses allows you to add [mixins] to an existing C# class. Mixins are similar to interfaces, but they allow a node to gain additional instance state, as well as allow the node to know which mixins are applied to it and invoke mixin handler methods — all without reflection.\n\nIn addition, AutoInject provides a few extra utilities to make working with node scripts even easier:\n\n- 🎮 `IAutoOn`: allow node scripts to implement .NET-style handler methods for Godot notifications: i.e., `OnReady`, `OnProcess`, etc.\n- 🪢 `IAutoConnect`: automatically bind properties marked with `[Node]` to a node in the scene tree — also provides access to nodes via their interfaces using [GodotNodeInterfaces].\n- 🛠 `IAutoInit`: adds an additional lifecycle method that is called before `_Ready` if (and only if) the node's `IsTesting` property is set to false. The additional lifecycle method for production code enables you to more easily unit test code by separating initialization logic from the engine lifecycle.\n- 🎁 `IProvider`: a node that provides one or more dependencies to its descendants. Providers must implement `IProvide\u003cT\u003e` for each type of value they provide, or optionally implement `IProvideAny` to handle the generic on the method level with `IProvideAny.Value\u003cT\u003e`.\n- 🔗 `IDependent`: a node that depends on one or more dependencies from its ancestors. Dependent nodes must mark their dependencies with the `[Dependency]` attribute and call `this.DependOn\u003cT\u003e()` to retrieve the value.\n- 🐤 `IAutoNode`: a mixin that applies all of the above mixins to a node script at once.\n\nWant all the functionality that AutoInject provides? Simply add this to your Godot node:\n\n```csharp\nusing Chickensoft.AutoInject;\nusing Chickensoft.Introspection;\nusing Godot;\n\n// Apply all of the AutoInject mixins at once:\n[Meta(typeof(IAutoNode))]\npublic partial class MyNode : Node\n{\n  public override void _Notification(int what) =\u003e this.Notify(what);\n}\n```\n\nAlternatively, you can use just the mixins you need from this project.\n\n```csharp\n[Meta(\n  typeof(IAutoOn),\n  typeof(IAutoConnect),\n  typeof(IAutoInit),\n  typeof(IProvider),\n  typeof(IDependent)\n)]\npublic partial class MyNode : Node\n{\n  public override void _Notification(int what) =\u003e this.Notify(what);\n}\n```\n\n\u003e [!IMPORTANT]\n\u003e For the mixins to work, you must override `_Notification` in your node script and call `this.Notify(what)` from it. This is necessary for the mixins to know when to invoke their handler methods. Unfortunately, there is no way around this since Godot must see the `_Notification` method in your script to generate handlers for it.\n\u003e\n\u003e ```csharp\n\u003e public override void _Notification(int what) =\u003e this.Notify(what);\n\u003e ```\n\n## 📦 Installation\n\nAutoInject is a source-only package that uses the [Introspection] source generator. AutoInject provides two mixins: `IDependent` and `IProvider` that must be applied with the Introspection generator's `[Meta]`.\n\nYou'll need to include `Chickensoft.GodotNodeInterfaces`, `Chickensoft.Introspection`, `Chickensoft.Introspection.Generator`, and `Chickensoft.AutoInject` in your project. All of the packages are extremely lightweight.\n\nSimply add the following to your project's `.csproj` file. Be sure to specify the appropriate versions for each package by checking on [Nuget](https://www.nuget.org/packages?q=Chickensoft).\n\n```xml\n\u003cItemGroup\u003e\n    \u003cPackageReference Include=\"Chickensoft.GodotNodeInterfaces\" Version=\"...\" /\u003e\n    \u003cPackageReference Include=\"Chickensoft.Introspection\" Version=\"...\" /\u003e\n    \u003cPackageReference Include=\"Chickensoft.Introspection.Generator\" Version=\"...\" PrivateAssets=\"all\" OutputItemType=\"analyzer\" /\u003e\n    \u003cPackageReference Include=\"Chickensoft.AutoInject\" Version=\"...\" PrivateAssets=\"all\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\nYou can also add `Chickensoft.AutoInject.Analyzers` to your project to get additional checks and code fixes for AutoInject, such as ensuring that you override `_Notification` and call `this.Provide()` from your provider nodes.\n\n```xml\n\u003cItemGroup\u003e\n    \u003cPackageReference Include=\"Chickensoft.AutoInject.Analyzers\" Version=\"...\" PrivateAssets=\"all\" OutputItemType=\"analyzer\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\n\u003e [!WARNING]\n\u003e We strongly recommend treating warning `CS9057` as an error to catch possible compiler-mismatch issues with the Introspection generator. (See the [Introspection] README for more details.) To do so, add a `WarningsAsErrors` line to your `.csproj` file's `PropertyGroup`:\n\u003e\n\u003e ```xml\n\u003e \u003cPropertyGroup\u003e\n\u003e   \u003cTargetFramework\u003enet8.0\u003c/TargetFramework\u003e\n\u003e   ...\n\u003e   \u003c!-- Catch compiler-mismatch issues with the Introspection generator --\u003e\n\u003e   \u003cWarningsAsErrors\u003eCS9057\u003c/WarningsAsErrors\u003e\n\u003e   ...\n\u003e \u003c/PropertyGroup\u003e\n\u003e ```\n\n\u003e [!TIP]\n\u003e Want to see AutoInject in action? Check out the Chickensoft [Game Demo].\n\n## 🎁 Providers\n\nTo provide values to descendant nodes, add the `IProvider` mixin to your node script and implement `IProvide\u003cT\u003e` for each value you'd like to make available, or `IProvideAny` if you want to handle the generic on the method level.\n\nOnce providers have initialized the values they provide, they must call the `this.Provide()` extension method to inform AutoInject that the provided values are now available.\n\nThe example below shows a node script that provides a `string` value to its descendants. Values are always provided by their type.\n\n\u003e [!NOTE]\n\u003e The `IProvideAny` interface will stop signals from propagating to the node's parent. This could at best mute any errors from unresolved dependencies when implemented on the root node,\n\u003e and at worst stop a dependency from being resolved if it was supposed to be fetched from an ancestor.\n\n```csharp\nnamespace MyGameProject;\n\nusing Chickensoft.AutoInject;\nusing Chickensoft.Introspection;\nusing Godot;\n\n[Meta(typeof(IAutoNode))]\npublic partial class MyProvider : Node, IProvide\u003cstring\u003e\n{\n  public override void _Notification(int what) =\u003e this.Notify(what);\n\n  string IProvide\u003cstring\u003e.Value() =\u003e \"Value\"\n\n  // Call the this.Provide() method once your dependencies have been initialized.\n  public void OnReady() =\u003e this.Provide();\n\n  public void OnProvided()\n  {\n    // You can optionally implement this method. It gets called once you call\n    // this.Provide() to inform AutoInject that the provided values are now\n    // available.\n  }\n}\n```\n\n## 🐣 Dependents\n\nTo use a provided value in a descendant node somewhere, add the `IDependent` mixin to your descendent node script and mark each dependency with the `[Dependency]` attribute. The notification method override is used to automatically tell the mixins when your node is ready and begin the dependency resolution process.\n\nOnce all of the dependencies in your dependent node are resolved, the `OnResolved()` method of your dependent node will be called (if overridden).\n\n```csharp\nnamespace MyGameProject;\n\nusing Chickensoft.Introspection;\nusing Godot;\n\n[Meta(typeof(IAutoNode))]\npublic partial class StringDependent : Node\n{\n  public override void _Notification(int what) =\u003e this.Notify(what);\n\n  [Dependency]\n  public string MyDependency =\u003e this.DependOn\u003cstring\u003e();\n\n  public void OnResolved()\n  {\n    // All of my dependencies are now available! Do whatever you want with\n    // them here.\n  }\n}\n```\n\nThe `OnResolved` method will be called after `_Ready/OnReady`, but before the first frame if (and only if) all the providers it depends on call `this.Provide()` before the first frame.\n\nEssentially, `OnResolved` is called when the slowest provider has finished\nproviding dependencies. For the best experience, do not wait until processing occurs to call `Provide` from your providers.\n\nIf you have a node script which is both a `Dependent` and a `Provider`, you can safely call `Provide` from the `OnResolved` method to allow it to provide dependencies.\n\nThe general rule of thumb for any `Provider` node is as follows: **call `Provide` as soon as you possibly can: either from `_Ready/OnReady` or from `OnResolved`.** If all providers in your project follow this rule, dependency provision will complete before processing occurs for nodes that are already in the tree. Dependent nodes added later will begin the dependency resolution process once the node receives the `Node.NotificationReady` notification.\n\n## 🙏 Tips\n\n### Keep Dependency Trees Simple\n\nFor best results, keep dependency trees simple and free from asynchronous initialization. If you try to get too fancy, you can introduce dependency resolution deadlock. Avoiding complex dependency hierarchies can often be done with a little extra experimentation as you design your game.\n\n### Listen to Dependencies\n\nInstead of subscribing to a parent node's events, consider subscribing to events emitted by the dependency values themselves.\n\n```csharp\n[Meta(typeof(IAutoNode))]\npublic partial class MyDependent : Node\n{\n  public override void _Notification(int what) =\u003e this.Notify(what);\n\n  [Dependency]\n  public MyValue Value =\u003e this.DependOn\u003cMyValue\u003e();\n\n  public void OnResolved()\n  {\n    // Setup subscriptions once dependencies are valid.\n    MyValue.OnSomeEvent += ValueUpdated\n  }\n\n  public void OnTreeExit()\n  {\n    // Clean up subscriptions here!\n    MyValue.OnSomeEvent -= ValueUpdated\n  }\n\n  public void ValueUpdated()\n  {\n    // Do something in response to the value we depend on changing.\n  }\n}\n```\n\n### Fallback Values\n\nYou can provide fallback values to use when a provider can't be found. This can make it easier to run a scene by itself from the editor without having to worry about setting up production dependencies. Naturally, the fallback value will only be used if a provider can't be found for that type above the dependent node.\n\n```csharp\n[Dependency]\npublic string MyDependency =\u003e this.DependOn\u003cstring\u003e(() =\u003e \"fallback_value\");\n```\n\n### Faking Dependencies\n\nSometimes, when testing, you may wish to \"fake\" the value of a dependency. Faked dependencies take precedence over any providers that may exist above the dependent node, as well as any provided fallback value.\n\n```csharp\n  [Test]\n  public void FakesDependency()\n  {\n    // Some dependent\n    var dependent = new MyNode();\n\n    var fakeValue = \"I'm fake!\";\n    dependent.FakeDependency(fakeValue);\n\n    TestScene.AddChild(dependent);\n\n    dependent._Notification((int)Node.NotificationReady);\n\n    dependent.OnResolvedCalled.ShouldBeTrue();\n    dependent.MyDependency.ShouldBe(fakeValue);\n\n    TestScene.RemoveChild(dependent);\n  }\n```\n\n## ❓ How AutoInject Works\n\nAutoInject uses a simple, specific algorithm to resolve dependencies.\n\n- When the Dependent mixin is added to an introspective node, the Introspection generator will generate metadata about the type which allows AutoInject to determine what properties the type has, as well as see their attributes.\n- A node script with the Dependent mixin observes its lifecycle. When it notices the `Node.NotificationReady` signal, it will begin the dependency resolution process without you having to write any code in your node script.\n- The dependency process works as follows:\n  - All properties of the node script are inspected using the metadata generated by the Introspection generator. This allows the script to introspect itself without having to resort to C#'s runtime reflection calls. Properties with the `[Dependency]` attribute are collected into the set of required dependencies.\n  - All required dependencies are added to the remaining dependencies set.\n  - The dependent node begins searching its ancestors, beginning with itself, then its parent, and so on up the tree.\n    - If the current search node implements `IProvideAny` or `IProvide\u003cT\u003e` for any of the remaining dependencies, the individual resolution process begins.\n      - The dependency stores the provider in a dictionary property in the node script.\n      - The dependency is added to the set of found dependencies.\n      - If the provider search node has not already provided its dependencies, the dependent subscribes to the `OnInitialized` event of the provider.\n      - Pending dependency provider callbacks track a counter for the dependent node that also remove that provider's dependency from the remaining dependencies set and initiate the OnResolved process if nothing is left.\n    - After checking all the remaining dependencies, the set of found dependencies are removed from the remaining dependencies set and the found dependencies set is cleared for the next search node.\n    - If all the dependencies are found, the dependent initiates the OnResolved process and finishes the search.\n    - Otherwise, the search node's parent becomes the next parent to search.\n  - Search concludes when providers for each dependency are found, or the top of the scene tree is reached.\n\nThere are some natural consequences to this algorithm, such as `OnResolved` not being invoked on a dependent until all providers have provided a value. This is intentional — providers are expected to synchronously initialize their provided values after `_Ready` has been invoked on them.\n\nAutoInject primarily exists to to locate providers from dependents and subscribe to the providers just long enough for their own `_Ready` method to be invoked — waiting longer than that to call `Provide` from a provider can introduce dependency resolution deadlock or other undesirable circumstances that are indicative of an anti-pattern.\n\nBy calling `this.Provide()` from `_Ready` in provider nodes, you ensure that the order of execution unfolds as follows, synchronously:\n\n  1. Dependent node `_Ready` (descendant of the provider, deepest nodes ready-up first).\n  2. Provider node `_Ready` (which calls `Provide`).\n  3. Dependent `OnResolved`\n  4. Frame 1 `_Process`\n  5. Frame 2 `_Process`\n  6. Etc.\n\nBy following the `this.Provide()` on `_Ready` convention, you guarantee all dependent nodes receive an `OnResolved` callback before the first process invocation occurs, guaranteeing that nodes are setup before frame processing begins ✨.\n\n\u003e [!TIP]\n\u003e If your provider is also a dependent, you can call `this.Provide()` from `OnResolved()` to allow it to provide dependencies to its subtree, which still guarantees that dependency resolution happens before the next frame is processed.\n\u003e\n\u003e In general, dependents should have access to their dependencies **before** frame processing callbacks are invoked on them.\n\n## 🪢 IAutoConnect\n\nThe `IAutoConnect` mixin automatically connects properties in your script to a declared node path or unique node name in the scene tree whenever the scene is instantiated, without reflection. It can also be used to connect nodes as interfaces.\n\nSimply apply the `[Node]` attribute to any field or property in your script that you want to automatically connect to a node in your scene.\n\nIf you don't specify a node path in the `[Node]` attribute, the name of the field or property will be converted to a [unique node identifier][unique-nodes] name in PascalCase. For example, the field name below `_my_unique_node` is converted to the unique node path name `%MyUniqueNode` by converting the property name to PascalCase and prefixing the percent sign indicator. Likewise, the property name `MyUniqueNode` is converted to `%MyUniqueNode`, which isn't much of a conversion since the property name is already in PascalCase.\n\nFor best results, use PascalCase for your node names in the scene tree (which Godot tends to do by default, anyways).\n\nIn the example below, we're using [GodotNodeInterfaces] to reference nodes as their interfaces instead of their concrete Godot types. This allows us to write a unit test where we fake the nodes in the scene tree by substituting mock nodes, allowing us to test a single node script at a time without polluting our test coverage.\n\n```csharp\nusing Chickensoft.GodotNodeInterfaces;\nusing Chickensoft.AutoInject;\nusing Chickensoft.Introspection;\nusing Godot;\n\n[Meta(typeof(IAutoConnect))]\npublic partial class MyNode : Node2D\n{\n  public override void _Notification(int what) =\u003e this.Notify(what);\n\n  [Node(\"Path/To/SomeNode\")]\n  public INode2D SomeNode { get; set; } = default!;\n\n  [Node] // Connects to \"%MyUniqueNode\" since no path was specified.\n  public INode2D MyUniqueNode { get; set; } = default!;\n\n  [Node(\"%OtherUniqueName\")]\n  public INode2D DifferentName { get; set; } = default!;\n}\n```\n\n\u003e [!TIP]\n\u003e `IAutoConnect` can only bind properties to nodes, not fields.\n\n### 🧪 Testing\n\nAutoConnect integrates seamlessly with [GodotNodeInterfaces] to facilitate unit testing Godot node scripts by allowing you to fake the node tree during testing.\n\n\u003e [!IMPORTANT]\n\u003e Starting in GodotNodeInterfaces v3 (AutoInject 2.10.0+), to use the fake node tree in your tests, you must set the static property `Chickensoft.GodotNodeInterfaces.RuntimeContext.IsTesting` to `true` when running tests. See the [Migrating](https://github.com/chickensoft-games/GodotNodeInterfaces?tab=readme-ov-file#migrating) section of the GodotNodeInterfaces README for details.\n\nWe can easily write a test for the example above by substituting mock nodes:\n\n```csharp\nnamespace Chickensoft.AutoInject.Tests;\n\nusing System.Threading.Tasks;\nusing Chickensoft.GodotNodeInterfaces;\nusing Chickensoft.GoDotTest;\nusing Chickensoft.AutoInject.Tests.Fixtures;\nusing Godot;\nusing GodotTestDriver;\nusing Moq;\nusing Shouldly;\n\n#pragma warning disable CA1001\npublic class MyNodeTest(Node testScene) : TestClass(testScene)\n{\n  private Fixture _fixture = default!;\n  private MyNode _scene = default!;\n\n  private Mock\u003cINode2D\u003e _someNode = default!;\n  private Mock\u003cINode2D\u003e _myUniqueNode = default!;\n  private Mock\u003cINode2D\u003e _otherUniqueNode = default!;\n\n  [Setup]\n  public async Task Setup()\n  {\n    _fixture = new(TestScene.GetTree());\n\n    _someNode = new();\n    _myUniqueNode = new();\n    _otherUniqueNode = new();\n\n    _scene = new MyNode();\n    _scene.FakeNodeTree(new()\n    {\n      [\"Path/To/SomeNode\"] = _someNode.Object,\n      [\"%MyUniqueNode\"] = _myUniqueNode.Object,\n      [\"%OtherUniqueName\"] = _otherUniqueNode.Object,\n    });\n\n    await _fixture.AddToRoot(_scene);\n  }\n\n  [Cleanup]\n  public async Task Cleanup() =\u003e await _fixture.Cleanup();\n\n  [Test]\n  public void UsesFakeNodeTree()\n  {\n    // Making a new instance of a node without instantiating a scene doesn't\n    // trigger NotificationSceneInstantiated, so if we want to make sure our\n    // AutoNodes get hooked up and use the FakeNodeTree, we need to do it manually.\n    _scene._Notification((int)Node.NotificationSceneInstantiated);\n\n    _scene.SomeNode.ShouldBe(_someNode.Object);\n    _scene.MyUniqueNode.ShouldBe(_myUniqueNode.Object);\n    _scene.DifferentName.ShouldBe(_otherUniqueNode.Object);\n    _scene._my_unique_node.ShouldBe(_myUniqueNode.Object);\n  }\n}\n```\n\n## 🛠 IAutoInit\n\nThe `IAutoInit` will conditionally call the `void Initialize()` method your node script has from `_Ready` if (and only if) the `IsTesting` field that it adds to your node is false. Conditionally calling the `Initialize()` method allows you to split your node's late member initialization into two-phases, allowing nodes to be more easily unit tested.\n\nWhen writing tests for your node, simply initialize any members that would need to be mocked in a test in your `Initialize()` method.\n\n```csharp\nusing Chickensoft.AutoInject;\nusing Chickensoft.Introspection;\nusing Godot;\n\n[Meta(typeof(IAutoInit), typeof(IAutoOn))]\npublic partial class MyNode : Node2D\n{\n  public override void _Notification(int what) =\u003e this.Notify(what);\n\n  public IMyObject Obj { get; set; } = default!;\n\n  public void Initialize()\n  {\n    // Initialize is called from the Ready notification if our IsTesting\n    // property (added by IAutoInit) is false.\n\n    // Initialize values which would be mocked in a unit testing method.\n    Obj = new MyObject();\n  }\n\n  public void OnReady()\n  {\n    // Guaranteed to be called after Initialize()\n\n    // Use object we setup in Initialize() method (or, if we're running in a\n    // unit test, this will use whatever the test supplied)\n    Obj.DoSomething();\n  }\n}\n```\n\nLikewise, when creating a node during a unit test, you can set the `IsTesting` property to `true` to prevent the `Initialize()` method from being called.\n\n```csharp\nvar myNode = new MyNode()\n{\n  Obj = mock.Object\n};\n\n(myNode as IAutoInit).IsTesting = true;\n```\n\nFor example tests, please see the [Game Demo] project.\n\n## 🌱 Enhanced Lifecycle\n\nAutoInject enhances the typical Godot node lifecycle by adding additional hooks that allow you to handle dependencies and initialization in a more controlled manner (primarily for making testing easier).\n\nThis is the lifecycle of a dependent node in the game environment:\n\n```text\nInitialize() -\u003e OnReady() -\u003e Setup() -\u003e OnResolved()\n```\n\nNote that this lifecycle is preserved regardless of how the node is added to the scene tree.\n\nAnd this is the lifecycle of a dependent node in a test environment:\n\n```text\nOnReady() -\u003e OnResolved()\n```\n\n## 🔋 IAutoOn\n\nThe `IAutoOn` mixin allows node scripts to implement .NET-style handler methods for Godot notifications, prefixed with `On`.\n\n```csharp\nusing Chickensoft.AutoInject;\nusing Chickensoft.Introspection;\nusing Godot;\n\n[Meta(typeof(IAutoOn))]\npublic partial class MyNode : Node2D\n{\n  public override void _Notification(int what) =\u003e this.Notify(what);\n\n  public void OnReady() {\n    // Called when the node enters the scene tree.\n    SetProcess(true);\n  }\n\n  public void OnProcess(double delta) {\n    // Called every frame.\n  }\n}\n```\n\n\u003e [!WARNING]\n\u003e Godot detects when you override `_PhysicsProcess` and automatically enables physics for that node.\n\u003e\n\u003e That won't happen when using `OnPhysicsProcess` so you have to manually turn on physics by calling:\n\u003e ```csharp\n\u003e SetPhysicsProcess(true);\n\u003e ```\n\u003e The same is true for overriding `_Process`. In that case Godot automatically enables processing. In order to use `OnProcess` you have to manually call:\n\u003e ```csharp\n\u003e SetProcess(true);\n\u003e ```\n\n## 🦾 IAutoNode\n\nThe `IAutoNode` mixin simply applies all of the mixins provided by AutoInject to a node script at once.\n\n```csharp\nusing Chickensoft.AutoInject;\nusing Chickensoft.Introspection;\nusing Godot;\n\n[Meta(typeof(IAutoNode))]\npublic partial class MyNode : Node { }\n```\n\n---\n\n🐣 Package generated from a 🐤 Chickensoft Template — \u003chttps://chickensoft.games\u003e\n\n[chickensoft-badge]: https://chickensoft.games/img/badges/chickensoft_badge.svg\n[chickensoft-website]: https://chickensoft.games\n[discord]: https://discord.gg/gSjaPgMmYW\n[discord-badge]: https://chickensoft.games/img/badges/discord_badge.svg\n[line-coverage]: Chickensoft.AutoInject.Tests/badges/line_coverage.svg\n[branch-coverage]: Chickensoft.AutoInject.Tests/badges/branch_coverage.svg\n\n[provider]: https://github.com/rrousselGit/provider\n[tree-order]: https://kidscancode.org/godot_recipes/4.x/basics/tree_ready_order/\n[Introspection]: https://github.com/chickensoft-games/Introspection\n[mixins]: https://github.com/chickensoft-games/Introspection?tab=readme-ov-file#%EF%B8%8F-mixins\n[GodotNodeInterfaces]: https://github.com/chickensoft-games/GodotNodeInterfaces\n[Game Demo]: https://github.com/chickensoft-games/GameDemo\n[unique-nodes]: https://docs.godotengine.org/en/stable/tutorials/scripting/scene_unique_nodes.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchickensoft-games%2Fautoinject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchickensoft-games%2Fautoinject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchickensoft-games%2Fautoinject/lists"}