Skip to content

Dependency Injection System

The plugin includes a lightweight, opt‑in dependency injection (DI) mechanism that removes most manual wiring between gameplay nodes and the Grid Building systems. It centers around two core pieces:

  1. GBCompositionContainer – A per-context (e.g., per player / world / session) resource that owns configured settings, templates, actions, states, contexts, and logging.
  2. GBInjectorSystem – A scene system that discovers nodes needing dependencies and injects the shared GBCompositionContainer into them at runtime.

  • Centralize access to configuration, states, and contexts.
  • Avoid deep node path lookups or duplicate singletons.
  • Allow multiple independent grid building contexts to coexist (e.g., splitscreen players) by scoping injection roots.
  • Support late / dynamic node creation without extra boilerplate.
  • Provide consistent logging and validation gateways.

Any node that implements:

func resolve_gb_dependencies(p_config: GBCompositionContainer) -> void:
# Pull what you need from p_config
var logger := p_config.get_logger()
var targeting_state := p_config.get_targeting_state()

…will automatically receive the active composition container when it enters an injection scope.

No interface registration is required—presence of the method name resolve_gb_dependencies is the marker.


GBCompositionContainer is the authoritative gateway to all major systems:

  • Settings (get_settings(), get_visual_settings(), get_manipulation_settings(), get_actions(), get_messages()).
  • Templates (get_templates()).
  • Contexts (get_contexts()) and sub-context accessors like get_systems_context() or get_indicator_context().
  • Mutable runtime state holders (get_states() returning building / targeting / manipulation / mode states).
  • Logging (get_logger()) – with graceful fallback when configuration is incomplete.
  • Validation (validate_configuration(), validate_runtime_configuration(), validate_and_log_issues()).

It lazy‑constructs internal components so that missing or partially configured resources can still emit structured warnings via the logger.

Being a Resource allows cloning or per‑player instancing while keeping memory light and avoiding tight coupling to any scene tree node. It also simplifies tooling and export workflows.

GBConfig is the single authoritative configuration resource consumed by GBCompositionContainer. It aggregates previously scattered exported fields (grid sizing, placement & validation toggles, indicator visuals, manipulation tuning, action/message resources, debug flags) into one asset.

Key aspects:

  • Acts as the root object passed into composition; other sub-resources (settings, templates, messages) are referenced from it.
  • Enables cloning per player/session by duplicating a single resource tree.
  • Reduces risk of version skew (all tuning lives together, easier diff/merge in VCS).
  • Validation runs against GBConfig via GBConfigurationValidator before/after injection.
  • Eliminates brittle node export wiring; systems retrieve needed values through container accessors which delegate to GBConfig.

Minimal setup workflow:

  1. Create a GBConfig resource (e.g., res://config/grid_building_config.tres).
  2. Populate its sub-sections (grid settings, visual settings, manipulation, actions/messages, templates references).
  3. Assign the resource to a GBCompositionContainer (inspector or factory method).
  4. Provide that container to GBInjectorSystem.

Benefits vs scattered exports:

Previous Pattern (4.x)Centralized in 5.0.0 (GBConfig)Outcome
Dozens of per-system exported tuning fieldsSingle resource with structured sectionsFaster onboarding & consistent defaults
Hard to validate completenessValidator can traverse one treeEarly warning & logging
Duplicate tweaks across multiple scenesReuse one shared assetLower maintenance cost

See the API: GBConfig and GBConfigurationValidator for field-level detail.


  1. Initialization (_ready_initialize):
    • Verifies a composition_container is assigned; otherwise disables itself (push_error).
    • Captures debug settings from the container (_debug).
    • Performs an initial recursive injection over each configured injection root (or the scene tree root if none).
    • Emits initial_injection_completed after the first pass.
  2. Runtime Monitoring:
    • Connects to SceneTree.node_added to intercept dynamically added nodes.
    • Connects child_entered_tree per injection root to scope additions.
    • Connects tree_exiting per injected node for cleanup / meta removal and optional verbose exit logging.
  3. Injection Execution (inject_node):
    • Skips nodes not yet inside the tree or already injected by this injector instance.
    • If the node exposes resolve_gb_dependencies, calls it with the shared composition_container.
    • Attaches metadata (gb_injection_meta) with injector id, weakref, timestamp, and stored signal callbacks.
    • Emits node_injected and logs a verbose message (if logging level allows).
  4. Validation:
    • validate_and_log_issues() defers to the container’s validation methods, reporting warnings through the logger.

Specify one or more injection_roots on the GBInjectorSystem to restrict which subtrees participate. If left empty, the entire scene tree root is used.

Scope check logic:

  • A newly added node is injected only if it is (or is a descendant of) one of the root nodes.
  • Multiple roots can coexist; each is monitored independently.

This enables patterns like separate player worlds sharing the same code paths but isolated state.


A node stores gb_injection_meta after successful injection. Subsequent passes (e.g., from other signal hooks) short‑circuit if the stored injector id matches, preventing duplicate dependency assignments or log spam.

Metadata fields (representative):

  • injector_ref: Weak reference to the injector.
  • injector_id: Numeric instance id snapshot.
  • timestamp_ms: Time of injection for debugging.
  • Internal keys for connected callbacks (_child_entered_cb, _tree_exiting_cb).

On node exit, callbacks are disconnected and metadata removed (or trimmed) to avoid memory / reference leaks.


The injector defers most formatting to GBLogger retrieved from the container. Verbose per-node lines (e.g., [GBInjectorSystem] Injected: NodeName at /root/...) appear only when the debug level threshold permits. Exit events optionally log a one‑time debug line via log_debug_once to reduce noise.

If logger acquisition fails early, the injector falls back to standard printing until the container can provide a logger.


Typical startup order:

  1. Create or assign a GBCompositionContainer with at least a GBConfig resource.
  2. Add a GBInjectorSystem node, set its composition_container, optionally define injection_roots.
  3. Let _ready() trigger initial injection; nodes with resolve_gb_dependencies now have access to configuration.
  4. Call validate_and_log_issues() on the injector or directly on the container to surface configuration warnings (misconfigured templates, missing settings, etc.).

Runtime validations (e.g., after loading a level scene) can re-run to surface dynamic setup issues.


# In a scene script needing grid building systems
extends Node
var _logger : GBLogger
var _targeting_state : GridTargetingState
func resolve_gb_dependencies(p_config: GBCompositionContainer) -> void:
_logger = p_config.get_logger()
_targeting_state = p_config.get_targeting_state()
_logger.log_debug(self, "Dependencies resolved for %s" % name)

Unit tests often construct an injector with:

var injector := GBInjectorSystem.create_with_injection(container)

Or use factory helpers (e.g., UnifiedTestFactory.create_test_injector) to streamline container + system creation, guaranteeing a fresh context for each test case.


  • Extremely simple helper nodes that only need a single exported reference.
  • One-off editor tools where direct resource assignment is clearer.
  • Performance‑critical inner loops (avoid extra method indirection during hot path creation if profiling reveals overhead).

  • Keep resolve_gb_dependencies fast; perform heavy initialization lazily afterwards if needed.
  • Avoid storing the entire GBCompositionContainer on every node—pull only what you need to reduce coupling.
  • Prefer using accessor methods (e.g., get_targeting_state()) instead of reaching deeply into nested resources.
  • Use validation early in development scenes to catch configuration gaps.
  • Scope injection roots to smallest reasonable subtree in large worlds to reduce signal churn.


Future Enhancements (Planned / Considerations)

Section titled “Future Enhancements (Planned / Considerations)”
  • Automatic parameter type discovery for richer docs (parsing argument annotations in resolve_gb_dependencies).
  • Optional interface tagging (annotation or signal-based) to reduce reliance on method name convention.
  • Hot-reload re-injection hooks for editor tooling.

If you have additional needs, open an issue or propose an extension in the contributor docs.