references/autoload-patterns.md
# AutoLoad registration (basic)
> **Do NOT Load** this file for expert architecture work — Project Settings → AutoLoad
> registration and typed-var hygiene are covered by official docs. Open only when a
> teammate needs a beginner checklist.
## Register a Node Autoload
1. Create `res://autoloads/game_manager.gd` extending `Node`.
2. Project → Project Settings → Autoload → Add → name `GameManager`.
3. Verify `project.godot`:
```ini
[autoload]
GameManager="*res://autoloads/game_manager.gd"
```
## Access
```gdscript
func _ready() -> void:
GameManager.game_started.connect(_on_started)
```
Prefer signals for state changes; do not reach into Autoload children from gameplay
scenes. For boot-order, service locator, and safe scene switch — return to SKILL.md
and the scripts under `scripts/`.
references/expert-patterns.md
# Expert Patterns
## Best Practices
### 1. Use Static Typing
```gdscript
# ✅ Good
var score: int = 0
# ❌ Bad
var score = 0
```
### 2. Emit Signals for State Changes
```gdscript
# ✅ Good - allows decoupled listeners
signal score_changed(new_score: int)
func add_score(points: int) -> void:
score += points
score_changed.emit(score)
# ❌ Bad - tight coupling
func add_score(points: int) -> void:
score += points
ui.update_score(score) # Don't directly call UI
```
### 3. Organize AutoLoads by Feature
```
res://autoloads/
game_manager.gd
audio_manager.gd
scene_transitioner.gd
save_manager.gd
```
### 4. Scene Transitioning Pattern
```gdscript
# scene_transitioner.gd
extends Node
signal scene_changed(scene_path: String)
func change_scene(scene_path: String) -> void:
# Fade out effect (optional)
await get_tree().create_timer(0.3).timeout
get_tree().change_scene_to_file(scene_path)
scene_changed.emit(scene_path)
```
## Testing AutoLoads
Since AutoLoads are always loaded, **avoid heavy initialization in `_ready()`**. Use lazy initialization or explicit init functions:
```gdscript
var _initialized: bool = false
func initialize() -> void:
if _initialized:
return
_initialized = true
# Heavy setup here
```
## Expert Architecture Patterns
### 1. Service-Locator-Pattern (Dynamic Registration)
Lightweight alternative to hardcoded Autoloads for dependency management.
- **Why**: Standard Autoloads must be `Node` types, which incur memory and SceneTree overhead [4]. For pure data systems, use `Engine.register_singleton()`.
- **The Script**: Create a `ServiceLocator` autoload at the top of the list.
- **Registration**: Register lightweight `RefCounted` objects globally into the engine's scope [5, 6].
```gdscript
# ServiceLocator.gd (Autoload)
func register_service(name: StringName, service: Object) -> void:
if not Engine.has_singleton(name):
Engine.register_singleton(name, service)
func _exit_tree() -> void:
# Cleanup to prevent dangling pointers [6]
if Engine.has_singleton(&"CombatService"):
Engine.unregister_singleton(&"CombatService")
```
- **Consumption**: Other systems fetch services via `Engine.get_singleton(&"Name")`. This bypasses the global variable namespace and allows for O(1) lookups of non-node systems [7].
### 2. Singleton-Dependency-Diagram (Visual Mapping)
Managing the initialization order and coupling of global systems.
- **The Rule**: Autoloads are initialized sequentially in the order they appear in the Project Settings [2]. Singletons at the top of the list MUST NOT depend on those below them.
- **The Template**: Use a Mermaid diagram to map out "Who initializes whom".
```mermaid
graph TD
subgraph SceneTree [SceneTree Execution]
A[OS & Servers Initialize] --> B
subgraph Autoloads [Project Settings: Autoload Order]
B[1. GlobalAudio.gd] -->|Initialized First| C[2. ServiceLocator.gd]
C -->|Initialized Second| D[3. QuestManager.gd]
end
D --> E[Current Active Scene]
end
%% Dependency Coupling
E -->|Queries| C
D -->|Registers self into| C
E -->|Plays sound via| B
```
- **Verification**: If `SaveManager` (pos 1) calls `PlayerManager` (pos 5) in `_ready()`, it will receive a null reference. Always move managers with dependencies to the bottom of the list.
### 3. Singleton-Health-Check (State Verification)
Automated verification to ensure global states are initialized correctly.
- **The Pattern**: Create a specialized test utility that verifies core singletons are non-null and have their default values reset.
- **Validation**: Use `assert()` for debug-time crashes and `is_instance_valid()` for runtime safety checks [8, 9].
```gdscript
func run_health_checks() -> void:
# 1. Verify Autoload Node Existence
var player_vars := get_tree().root.get_node_or_null("PlayerVariables")
assert(player_vars != null, "Critical Error: PlayerVariables Autoload missing!")
# 2. Verify Dynamic Service Registration
assert(Engine.has_singleton(&"CombatService"), "Critical Error: CombatService not registered!")
# 3. Verify Memory Safety
assert(is_instance_valid(player_vars), "Critical Error: PlayerVariables instance invalid!")
```
- **Integration**: Run these checks during game boot (if in debug mode) or within a CI/CD test suite like GUT to prevent state regression.
references/migration-notes.md
# Migration notes: godot-autoload-architecture
Incremental upgrade for topics this skill covers. Apply **one hop**, stabilize/test, then next. Never skip hops.
If the project is **< 4.0**, follow [godot-version-migration](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-version-migration/SKILL.md) era bridges (legacy → 3→4) until 4.0, then these hops. Official 3→4: [Upgrading from Godot 3 to Godot 4](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.html).
## 3.x → 4.0
Official: [Upgrading from Godot 3 to Godot 4](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.html)
- Autoload scripts: fix `super()` in lifecycle; replace Tween nodes.
- `Engine.editor_hint` → `Engine.is_editor_hint()`.
- `randomize()` auto on load — set seeds explicitly for determinism.
- Group calls immediate by default — audit Autoload broadcasts.
## 4.0 → 4.1
Official: [Upgrading to Godot 4.1](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.1.html)
- `Object.get_meta_list` return type is `Array[StringName]` (was PackedStringArray).
- `WorkerThreadPool.wait_for_task_completion` now returns `Error`.
- `Basis`/`Transform3D.looking_at` and `Node3D.look_at*` gain optional `use_model_front`.
## 4.1 → 4.2
Official: [Upgrading to Godot 4.2](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.2.html)
- `NOTIFICATION_NODE_RECACHE_REQUESTED` removed from Node.
## 4.2 → 4.3
Official: [Upgrading to Godot 4.3](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.3.html)
- Binary serialization of scripted Objects/typed Arrays changed — re-test save/load of custom Resources.
- `PackedByteArray` may use compact base64 storage; older editors may not open 4.3 resources with large byte arrays.
## 4.3 → 4.4
Official: [Upgrading to Godot 4.4](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.4.html)
- `@export_file` stores `uid://` paths from the Inspector (breaking vs `res://` expectations).
- `FileAccess.store_*` methods return `bool` success.
- `Curve` enforces `min_value`/`max_value` — adjust curves that used points outside `[0, 1]`.
- `OS.read_string_from_stdin` requires `buffer_size`.
- `@export_file` Inspector assignments become `uid://` — scripts expecting `res://` strings must resolve UIDs or use `@export_file_path` (4.5+).
## 4.4 → 4.5
Official: [Upgrading to Godot 4.5](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.5.html)
- `Resource.duplicate(true)` deep-duplicates **only internal** resources; use `duplicate_deep(DEEP_DUPLICATE_ALL)` for old behavior.
- `Node.get_rpc_config` renamed to `get_node_rpc_config`.
- `JSONRPC.set_scope` replaced by `set_method`.
- `ProjectSettings.add_property_info` warns on invalid/`usage` keys.
- Resource deep-duplicate and UID export-file changes affect inventory/quest/economy Resource graphs.
## 4.5 → 4.6
Official: [Upgrading to Godot 4.6](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.6.html)
- TSCN gains unique node IDs (large VCS diffs on first 4.6 save — expected).
- `FileAccess.get_as_text` drops `skip_cr` parameter.
- `Performance.add_custom_monitor` gains optional `type`.
## 4.6 → 4.7
Official: [Upgrading to Godot 4.7](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.7.html)
- `Object.is_class` takes `StringName`.
- Setting an element of a packed array property no longer calls the property setter for the whole array.
- Overrides of methods with typed returns must actually `return` (add `return null` if needed).
- New projects default stretch `canvas_items` + aspect `expand` (was `disabled`/`keep`).
- Re-validate Resource pipelines after packed-array setter and typed-return GDScript changes.
- Packed array element assignment no longer triggers whole-property setter.
- Typed-return overrides require an explicit return statement.
scripts/autoload_bootstrapper.gd
class_name AutoLoadBootstrapper
extends Node
## Expert AutoLoad Bootstrapper (Godot 4.7).
## Orchestrates two-phase initialization across all Singletons.
## PLACE THIS LAST IN THE PROJECT SETTINGS AUTOLOAD LIST.
func _ready() -> void:
var root := get_tree().root
var services: Array[Node] = []
# 1. Discovery
for child in root.get_children():
if child.has_method("init_service") and child != self:
services.append(child)
# 2. Phase 1: Dependency Resolution
for s in services:
s.call("init_service")
# 3. Phase 2: Execution Start
for s in services:
if s.has_method("start_service"):
s.call("start_service")
print("[BOOTSTRAP]: All global services synchronized and started.")
## [SKILL NOTICE]: This pattern resolves circular dependencies where
## AutoLoad A needs AutoLoad B's 'ready' state to initialize.
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/overridable_functions.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/scene_tree.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md — defer cross-singleton start until peers are ready
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md — Autoload list order for bootstrap dependencies
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md — call_deferred / ready gates
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/autoload_init_order_diag.gd
# autoload_init_order_diag.gd
# Checking Autoload initialization sequence
extends Node
# EXPERT NOTE: Autoloads initialize in the order they appear in
# Project Settings -> AutoLoad. Use this for dependency debugging.
func _ready():
print("[AutoLoad Diagnostic] Initialized: ", name)
# Check for dependencies. If 'GlobalConfig' must be first:
if not get_tree().root.has_node("GlobalConfig"):
push_error("CRITICAL: GlobalConfig Autoload missing or loaded after %s!" % name)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/scene_tree.html
# - https://docs.godotengine.org/en/stable/classes/class_projectsettings.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md — escalate boot-order hangs
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md — Project Settings Autoload list
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-testing-patterns/SKILL.md — assert dependency order in CI
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/autoload_initializer.gd
# skills/autoload-architecture/scripts/autoload_initializer.gd
extends Node
## AutoLoad Initializer Expert Pattern
## Manages explicit initialization order and dependency injection for AutoLoads.
class_name AutoLoadInitializer
var _initialized: Dictionary = {}
var _init_order: Array[StringName] = []
func register_autoload(autoload_name: StringName, init_callback: Callable) -> void:
_init_order.append(autoload_name)
_initialized[autoload_name] = {
"callback": init_callback,
"complete": false
}
func initialize_all() -> void:
print("=== Initializing AutoLoads ===")
for autoload_name in _init_order:
var data: Dictionary = _initialized[autoload_name]
if data["complete"]:
continue
print("Initializing: %s" % autoload_name)
data["callback"].call()
data["complete"] = true
func is_initialized(autoload_name: StringName) -> bool:
return _initialized.get(autoload_name, {}).get("complete", false)
func wait_for_autoload(autoload_name: StringName) -> void:
while not is_initialized(autoload_name):
await get_tree().process_frame
## EXPERT USAGE:
## In each AutoLoad's _ready():
## AutoLoadInitializer.register_autoload(&"GameManager", initialize)
##
## func initialize() -> void:
## # Heavy initialization here
## pass
##
## Then in main scene:
## AutoLoadInitializer.initialize_all()
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/overridable_functions.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/autoloads_versus_regular_nodes.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-scene-management/SKILL.md — call initialize_all from main scene boot
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md — keep heavy work out of Autoload _ready
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md — registration before lazy init
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/autoload_reference_checker.gd
# autoload_reference_checker.gd
# Validating singleton availability before access
extends Node
# EXPERT NOTE: Using 'get_node("/root/Name")' is safer than using the
# global name if you code for packages/plugins that might lack the Autoload.
static func get_events(tree: SceneTree) -> Node:
var path = "/root/GlobalEvents"
if tree.root.has_node(path):
return tree.root.get_node(path)
push_warning("GlobalEvents Autoload not found!")
return null
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/scene_tree.html
# - https://docs.godotengine.org/en/stable/classes/class_node.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md — optional Autoload names for plugins
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-testing-patterns/SKILL.md — null-safe getters in tests without full registry
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md — NodePath / get_node_or_null patterns
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/cross_autoload_comms.gd
# cross_autoload_comms.gd
# Rules for Autoload-to-Autoload communication
extends Node
# EXPERT NOTE: Avoid circular dependencies between Autoloads.
# If A needs B and B needs A, your project will likely hang on boot.
func _ready():
# Use 'await' if checking for a sibling Autoload's node tree
if not get_tree().root.has_node("SaveManager"):
await get_tree().process_frame # Give other singletons time to init
_initialize_hooks()
func _initialize_hooks():
# Connecting to another Singleton safely
if get_tree().root.has_node("SaveManager"):
var sm = get_node("/root/SaveManager")
sm.save_requested.connect(_on_save)
func _on_save():
pass
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/getting_started/step_by_step/signals.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/overridable_functions.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md — connect after peer Autoloads exist
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-save-load-systems/SKILL.md — typical SaveManager hook target
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md — diagnose null peer at boot
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/debug_console_autoload.gd
# debug_console_autoload.gd
# Universal debug overlay accessible from any scene
extends CanvasLayer
# EXPERT NOTE: UI Autoloads should use CanvasLayer to ensure they
# always draw on top of game scenes.
@onready var label = $Label
func _ready():
process_mode = PROCESS_MODE_ALWAYS # Console works even when paused
func log_message(msg: String):
label.text += "\n" + msg
print("[Debug] ", msg)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/pausing_games.html
# - https://docs.godotengine.org/en/stable/classes/class_canvaslayer.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md — in-game console overlay workflows
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-containers/SKILL.md — layout for always-on debug HUD
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-input-handling/SKILL.md — toggle key while tree paused
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/global_event_bus.gd
# global_event_bus.gd
# Centralized signal routing to decouple systems
extends Node
# EXPERT NOTE: An Event Bus should ideally hold no state.
# It only acts as a post office for signals.
signal level_started(id: int)
signal enemy_defeated(type: String, points: int)
signal game_paused(is_paused: bool)
func notify_enemy_killed(type: String, val: int):
enemy_defeated.emit(type, val)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/getting_started/step_by_step/signals.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/instancing_with_signals.html
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/logic_preferences.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md — typed bus contracts, no stored state
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-combat-system/SKILL.md — enemy_defeated / score fan-out
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-quest-system/SKILL.md — level_started listeners
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/global_game_state.gd
class_name GlobalGameState
extends Node
## Expert Global State Machine (Godot 4.7).
## Uses deferred transitions to prevent frame-locked race conditions.
signal state_changed(old_state: State, new_state: State)
enum State { MENU, LOADING, IN_GAME, PAUSED, GAME_OVER }
var current_state: State = State.MENU
var _is_transitioning: bool = false
func request_transition(new_state: State) -> void:
if _is_transitioning or current_state == new_state:
return
_is_transitioning = true
# Use call_deferred to ensure physics/logic have finished current frame
call_deferred("_apply_transition", new_state)
func _apply_transition(new_state: State) -> void:
var old := current_state
current_state = new_state
_is_transitioning = false
state_changed.emit(old, current_state)
## [SKILL NOTICE]: NEVER change global state inside a physics callback
## without deferring, or related systems may read stale/conflicting data.
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/pausing_games.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/logic_preferences.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-state-machine-advanced/SKILL.md — MENU/PLAYING/PAUSED ownership
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-scene-management/SKILL.md — state changes that trigger scene loads
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md — state_changed emit contracts
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/lazy_loaded_singleton.gd
# lazy_loaded_singleton.gd
# Creating "Autoloads" on demand to save memory
extends Node
# EXPERT NOTE: If a singleton is rarely used, don't put it in
# Project Settings. Load it manually when needed.
static var _instance: Node = null
static func get_instance(tree: SceneTree) -> Node:
if not is_instance_valid(_instance):
_instance = load("res://systems/heavy_system.tscn").instantiate()
tree.root.add_child(_instance)
return _instance
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/autoloads_versus_regular_nodes.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/nodes_and_scene_instances.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/scene_tree.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md — defer heavy systems until first use
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-resource-data-patterns/SKILL.md — preload vs load tradeoffs
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-scene-management/SKILL.md — root.add_child lifecycle
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/persistent_data_holder.gd
# persistent_data_holder.gd
# Keeping data alive across scene changes
extends Node
# EXPERT NOTE: Values in Autoloads survive SceneTree.change_scene_to_file().
# Use for player inventory, settings, and quest progress.
var inventory: Array[String] = []
var settings: Dictionary = {"volume": 0.8, "fullscreen": false}
func add_item(item: String):
inventory.append(item)
print("Items persistent: ", inventory)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/change_scenes_manually.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-save-load-systems/SKILL.md — serialize inventory/settings from this holder
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-inventory-system/SKILL.md — cross-scene inventory consumer
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-scene-management/SKILL.md — data that must survive change_scene
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/safe_scene_switcher.gd
# safe_scene_switcher.gd
# Robust scene transitioning via Autoload
extends Node
# EXPERT NOTE: Managing scenes in an Autoload prevents data loss
# during transition and ensures proper cleanup of the current scene.
var current_scene: Node = null
func _ready() -> void:
# Autoloads are the first children. The active game scene is the last child.
current_scene = get_tree().root.get_child(-1)
func goto_scene(path: String) -> void:
# NEVER free the current scene while it's executing (e.g., inside a signal).
# Use call_deferred to wait until the end of the frame.
call_deferred("_deferred_goto_scene", path)
func _deferred_goto_scene(path: String) -> void:
# Safety: Free current scene before loading new one
if is_instance_valid(current_scene):
current_scene.free()
var next_scene_res = ResourceLoader.load(path) as PackedScene
current_scene = next_scene_res.instantiate()
get_tree().root.add_child(current_scene)
# Set as current for get_tree().current_scene access
get_tree().current_scene = current_scene
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/change_scenes_manually.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/scene_tree.html
# - https://docs.godotengine.org/en/stable/classes/class_scenetree.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-scene-management/SKILL.md — primary owner of load/unload UX
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-resource-data-patterns/SKILL.md — PackedScene / ResourceLoader paths
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md — fade wrappers around switch
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/service_locator.gd
# service_locator.gd
# Expert Service Locator pattern using Godot 4.1+ static variables.
# Decouples system discovery from hardcoded Autoloads.
extends Node
class_name ServiceLocator
## Global registry of services, accessible via static methods.
static var _services: Dictionary = {}
## Registers a service provider (Node or RefCounted).
static func register_service(id: String, provider: Object) -> void:
if _services.has(id):
push_warning("Service Locator: Overwriting existing service '%s'." % id)
_services[id] = provider
print("Service Locator: Registered '%s' (%s)" % [id, provider.get_class()])
## Retrieves a registered service. Returns null if not found.
static func get_service(id: String) -> Object:
if not _services.has(id):
push_error("Service Locator: Service '%s' not found!" % id)
return null
return _services[id]
## Removes a service from the registry.
static func unregister_service(id: String) -> void:
if _services.erase(id):
print("Service Locator: Unregistered '%s'" % id)
## Clears all services (useful for unit test teardown).
static func clear_all() -> void:
_services.clear()
print("Service Locator: All services cleared.")
## Usage Expert Tip:
## Instead of using hardcoded Autoloads, have your managers register themselves:
## func _ready():
## ServiceLocator.register_service("save_manager", self)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_engine.html
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/autoloads_versus_regular_nodes.html
# - https://docs.godotengine.org/en/stable/classes/class_refcounted.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-composition/SKILL.md — register components without Node Autoload spam
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md — StringName keys / static helpers
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-testing-patterns/SKILL.md — swap fakes via clear/register
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/service_registry.gd
class_name ServiceRegistry
extends Node
## Expert Service Locator (Godot 4.7).
## Prevents Global Namespace Pollution by centralizing dependencies.
var _services: Dictionary = {}
func register(service_name: StringName, instance: Object) -> void:
if _services.has(service_name):
push_warning("[SERVICE]: %s already registered. Overwriting." % service_name)
_services[service_name] = instance
if instance is Node and not instance.is_inside_tree():
add_child(instance)
func get_service(service_name: StringName) -> Object:
return _services.get(service_name)
func unregister(service_name: StringName) -> void:
var service = _services.get(service_name)
if service:
_services.erase(service_name)
if service is Node and service.get_parent() == self:
service.queue_free()
## [SKILL NOTICE]: Use StringName (&"Name") for keys to ensure O(1)
## dictionary lookups at the engine-core level.
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_engine.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/godot_interfaces.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-composition/SKILL.md — named service interfaces vs hard Autoloads
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md — StringName O(1) dictionary keys
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md — missing-service diagnostics
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/singleton_dependency_diagram.gd
# singleton_dependency_diagram.gd
# Utility to visualize Singleton dependencies and generate a Mermaid diagram.
# Expert tool for managing initialization order.
extends RefCounted
class_name SingletonDependencyDiagram
## Generates a Mermaid 'graph TD' diagram of active Autoloads.
static func generate_mermaid_diagram() -> String:
var diagram := "graph TD\n"
# Get all children of root (this includes Autoloads)
var root = Engine.get_main_loop().root
var autoloads := []
for child in root.get_children():
# Filter for typical Autoload nodes (exclude the main scene)
if child.name != "root" and child != Engine.get_main_loop().root.get_child(-1):
autoloads.append(child)
if autoloads.is_empty():
return "No Autoloads detected."
for node in autoloads:
diagram += " %s[%s]\n" % [node.name, node.name]
# Expert logic: Analyze signals and cross-references (simplified example)
# In a full implementation, you'd scan script properties for other Autoload names.
diagram += "\n %% Manual annotations or analysis result follows\n"
return diagram
## Prints the Mermaid code to console for use in documentation.
static func print_diagram() -> void:
print(generate_mermaid_diagram())
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/scene_organization.html
# - https://docs.godotengine.org/en/stable/classes/class_projectsettings.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md — document Autoload order as source of truth
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md — visualize circular boot deps
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-builder/SKILL.md — architecture docs for agent planning
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/singleton_health_check_test.gd
# singleton_health_check_test.gd
# Template for verifying global singleton state using expert unit testing patterns.
# Designed for compatibility with GUT (Godot Unit Test) or GdUnit4.
extends Node
## Health Check: Verify that core singletons are correctly initialized.
func test_singleton_initialization():
# Verify ServiceLocator
var root = Engine.get_main_loop().root
assert_not_null(root.get_node_or_null("GameManager"), "GameManager must be registered as an Autoload.")
# Verify default states
# var gm = root.get_node("GameManager")
# assert_eq(gm.score, 0, "GameManager score should start at 0.")
print("Health Check: Singletons are stable.")
## Helper for GUT (if using)
func assert_not_null(obj, msg):
if obj == null:
push_error(msg)
else:
print("PASSED: ", msg)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# - https://docs.godotengine.org/en/stable/classes/class_engine.html
# - https://docs.godotengine.org/en/stable/classes/class_object.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-testing-patterns/SKILL.md — GUT asserts for Autoload presence
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md — boot-time health gate
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md — expected singleton names
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/stateless_bus.gd
# skills/autoload-architecture/code/stateless_bus.gd
extends Node
## Stateless Signal Bus Expert Pattern
## Optimized for decoupling and lazy-loading of systems.
# 1. Defined Semantic Signals
# Avoid 'generic' signals. Be specific about the domain.
signal player_health_changed(new_health: int, max_health: int)
signal level_completed(id: String, score: int)
signal system_booted(id: String)
func _ready() -> void:
# 2. Boot-time Priorities
# Autoloads initialize in order. Use signals to notify
# other singletons that this generic hub is ready.
system_booted.emit("StatelessBus")
func notify_health(h: int, m: int) -> void:
player_health_changed.emit(h, m)
## EXPERT NOTE:
## NEVER store state (e.g. current_health) in the Signal Bus.
## The bus is a 'Post Office' - it delivers messages (Signals),
## it does not store packages (State).
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/getting_started/step_by_step/signals.html
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/logic_preferences.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md — post-office bus, no stored packages
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-rpg-stats/SKILL.md — health_changed listeners without bus state
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-containers/SKILL.md — HUD binds to bus signals
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/static_state_manager.gd
# static_state_manager.gd
# Using static variables for high-performance global state
extends RefCounted
class_name GlobalState
# EXPERT NOTE: static var is shared across all instances of the class.
# It does NOT require an Autoload node in the SceneTree.
# Access via: GlobalState.score += 10
static var score: int = 0
static var player_name: String = "Player1"
static var unlocked_levels: Array[int] = [1]
static func add_score(val: int) -> void:
score += val
static func is_level_unlocked(lvl: int) -> bool:
return unlocked_levels.has(lvl)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/autoloads_versus_regular_nodes.html
# - https://docs.godotengine.org/en/stable/tutorials/best_practices/what_are_godot_classes.html
# - https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md — static var / class_name without SceneTree
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-resource-data-patterns/SKILL.md — when a Resource beats static globals
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md — avoid Node Autoload overhead for pure data
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
scripts/thread_safe_global_access.gd
# thread_safe_global_access.gd
# Handling global data from background threads
extends Node
# EXPERT NOTE: Modifying Autoload nodes or SceneTree properties
# from threads is UNSAFE. Use Mutex for data or call_deferred for nodes.
var _shared_data: Dictionary = {}
var _lock: Mutex = Mutex.new()
func update_data_safely(key: String, val: Variant):
_lock.lock()
_shared_data[key] = val
_lock.unlock()
func get_data_safely(key: String) -> Variant:
_lock.lock()
var res = _shared_data.get(key)
_lock.unlock()
return res
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/performance/thread_safe_apis.html
# - https://docs.godotengine.org/en/stable/tutorials/performance/using_multiple_threads.html
# - https://docs.godotengine.org/en/stable/classes/class_mutex.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md — WorkerThreadPool + Autoload shared maps
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md — call_deferred back to main thread
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-procedural-generation/SKILL.md — typical background writer into global caches
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md
# =============================================================================
SKILL.md
---
name: godot-autoload-architecture
description: "Expert patterns for Godot AutoLoad (singleton) architecture including global state management, scene transitions, signal-based communication, dependency injection, autoload initialization order, and anti-patterns to avoid. Use for game managers, save systems, audio controllers, or cross-scene resources. Trigger keywords: AutoLoad, singleton, GameManager, SceneTransitioner, SaveManager, global_state, autoload_order, signal_bus, dependency_injection."
---
## Available Scripts
### [autoload_init_order_diag.gd](scripts/autoload_init_order_diag.gd)
**MANDATORY** before trusting a multi-Autoload dependency graph — verifies boot sequence.
### [singleton_dependency_diagram.gd](scripts/singleton_dependency_diagram.gd)
**MANDATORY** with the mermaid/order diagram — maps who may call whom at boot.
### [global_event_bus.gd](scripts/global_event_bus.gd)
**MANDATORY** before a cross-system Autoload bus (Achievements, UI, Save events).
### [safe_scene_switcher.gd](scripts/safe_scene_switcher.gd)
**MANDATORY** before Autoload-owned scene transitions (deferred free / root management).
### [service_locator.gd](scripts/service_locator.gd) / [service_registry.gd](scripts/service_registry.gd)
**MANDATORY** before `Engine.register_singleton` DI for non-Node services.
### [persistent_data_holder.gd](scripts/persistent_data_holder.gd)
Data that must survive `change_scene_to_file()` (inventory, settings).
### [static_state_manager.gd](scripts/static_state_manager.gd)
`static var` global state when you do **not** need a SceneTree Node.
### [lazy_loaded_singleton.gd](scripts/lazy_loaded_singleton.gd)
On-demand instantiate instead of eager boot cost.
### [cross_autoload_comms.gd](scripts/cross_autoload_comms.gd)
Safe cross-singleton calls after both are ready.
### [thread_safe_global_access.gd](scripts/thread_safe_global_access.gd)
Mutex / `call_deferred` for background threads touching Autoload state.
### [autoload_reference_checker.gd](scripts/autoload_reference_checker.gd) / [singleton_health_check_test.gd](scripts/singleton_health_check_test.gd)
Validate registration + defaults (debug / CI).
### [autoload_bootstrapper.gd](scripts/autoload_bootstrapper.gd) / [autoload_initializer.gd](scripts/autoload_initializer.gd)
Ordered init helpers when `_ready` is too early for heavy work.
### [debug_console_autoload.gd](scripts/debug_console_autoload.gd)
`PROCESS_MODE_ALWAYS` CanvasLayer console.
### [global_game_state.gd](scripts/global_game_state.gd) / [stateless_bus.gd](scripts/stateless_bus.gd)
State holder vs pure event bus split.
## NEVER Do in AutoLoad Architecture
- **NEVER access AutoLoads in `_init()`** — AutoLoads are initialized sequentially. Accessing one in `_init()` may find a null reference.
- **NEVER modify a Singleton's size or children in `_ready()`** — If multiple Singletons refer to each other's trees during boot, it can cause layout/sorting errors.
- **NEVER store highly localized, scene-specific data in AutoLoads** — This creates "God Objects" and introduces global side effects that are hard to debug.
- **NEVER use `Parent.method()` calls from an Autoload** — Autoloads sit at the root. They are the ultimate "top". Use signals to talk to the active scene.
- **NEVER use an Autoload for pure data containers** — If you don't need `_process()` or signals, use a `static var` in a `class_name` script instead.
- **NEVER create circular dependencies between Singletons** — If A needs B and B needs A, Godot will hang during the splash screen.
- **NEVER free an Autoload node manually** — Removing a singleton from the root can leave dangling references that crash the engine.
- **NEVER use AutoLoads for UI elements that aren't global** — Popups that only exist in one level should be in that level, not a global singleton.
- **NEVER assume `get_tree().current_scene` is accurate in `_ready()`** — In Autoloads, the active scene might still be initializing. Access it via `get_tree().root.get_child(-1)`.
- **NEVER skip `process_mode` configuration** — If your global console or music manager needs to work while the game is paused, set `process_mode = PROCESS_MODE_ALWAYS`.
---
## When to Use AutoLoads
**Good:** Game/Audio/Save managers, SceneTransitioner, global score/inventory, cross-scene EventBus.
**Avoid:** Scene-specific logic, temporary state, pure data (prefer `static` / Resource), over-architecting tiny projects.
---
## Expert Architecture Patterns
### 1. Boot order & dependency diagram
> **MANDATORY**: Read [autoload_init_order_diag.gd](scripts/autoload_init_order_diag.gd) and [singleton_dependency_diagram.gd](scripts/singleton_dependency_diagram.gd) before drawing or trusting any Autoload order.
Autoloads initialize **top → bottom** in Project Settings. Upper singletons must not call lower ones in `_ready()`. Move dependents down the list.
```mermaid
graph TD
subgraph Autoloads [Project Settings order]
B[1. GlobalAudio] --> C[2. ServiceLocator]
C --> D[3. QuestManager]
end
D --> E[Current Scene]
E -->|Queries| C
```
### 2. Service locator (non-Node DI)
> **MANDATORY**: [service_locator.gd](scripts/service_locator.gd) / [service_registry.gd](scripts/service_registry.gd) before `Engine.register_singleton`.
Use for lightweight `RefCounted` services; unregister in `_exit_tree` to avoid dangling engine singletons.
### 3. Event bus vs state holder
> **MANDATORY**: [global_event_bus.gd](scripts/global_event_bus.gd) for cross-system past-tense events. Keep mutable run state in [persistent_data_holder.gd](scripts/persistent_data_holder.gd) / [global_game_state.gd](scripts/global_game_state.gd) — not on the bus.
### 4. Safe scene switching from Autoload
> **MANDATORY**: [safe_scene_switcher.gd](scripts/safe_scene_switcher.gd) — deferred free + root ownership. Pair with `godot-scene-management` for threaded loads.
### 5. Health checks
> **MANDATORY** in debug/CI: [singleton_health_check_test.gd](scripts/singleton_health_check_test.gd) / [autoload_reference_checker.gd](scripts/autoload_reference_checker.gd) — `assert` presence + `Engine.has_singleton` for registered services.
## Expert insights (WHY — keep in body)
- **Boot order** — WHY: Autoloads init top→bottom in Project Settings. Upper singletons must not call lower ones in `_ready()` ([autoload_init_order_diag.gd](scripts/autoload_init_order_diag.gd)).
- **Service locator vs Node Autoload** — WHY: `RefCounted` services avoid SceneTree overhead; register via `Engine.register_singleton` and unregister in `_exit_tree` ([service_locator.gd](scripts/service_locator.gd)).
- **Event bus vs state** — WHY: buses emit past-tense events; mutable run state belongs in [persistent_data_holder.gd](scripts/persistent_data_holder.gd), not on the bus.
- **`current_scene` in `_ready()`** — WHY: active scene may still be mounting; use `get_tree().root.get_child(-1)` or defer until scene ready.
## Deep recipes (on demand)
| Topic | Reference / script |
|-------|-------------------|
| Service locator / boot diagram / health checks | [expert-patterns.md](references/expert-patterns.md) |
| Beginner registration only | [autoload-patterns.md](references/autoload-patterns.md) |
## Reference
> Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
### Official Documentation
- [Singletons (AutoLoad)](https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html) — How AutoLoads register under `/root`, become global names, and why boot order matches Project Settings list order.
- [Autoloads versus regular nodes](https://docs.godotengine.org/en/stable/tutorials/best_practices/autoloads_versus_regular_nodes.html) — Decision guide for when a global singleton is justified versus a scene-owned node or static helper.
- [Scene organization](https://docs.godotengine.org/en/stable/tutorials/best_practices/scene_organization.html) — Keep scene-local data out of AutoLoads so managers do not become God Objects.
- [Logic preferences](https://docs.godotengine.org/en/stable/tutorials/best_practices/logic_preferences.html) — Prefer signals and ownership edges over reaching into Autoload trees for gameplay orchestration.
- [Using SceneTree](https://docs.godotengine.org/en/stable/tutorials/scripting/scene_tree.html) — Why `current_scene` can be unreliable during Autoload `_ready()` and how root children relate to the active scene.
- [Change scenes manually](https://docs.godotengine.org/en/stable/tutorials/scripting/change_scenes_manually.html) — Deferred free + root reparent patterns behind safe global scene switchers.
- [Pausing games](https://docs.godotengine.org/en/stable/tutorials/scripting/pausing_games.html) — `process_mode` / `PROCESS_MODE_ALWAYS` for consoles, music, and managers that must run while `get_tree().paused`.
- [Overridable functions](https://docs.godotengine.org/en/stable/tutorials/scripting/overridable_functions.html) — `_init` vs `_ready` timing so cross-Autoload access does not hit nulls during sequential boot.
- [Using signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/signals.html) — Emit/connect model for Autoload event buses that decouple scenes without hard node paths.
- [Engine](https://docs.godotengine.org/en/stable/classes/class_engine.html) — `register_singleton` / `get_singleton` for lightweight service locators that are not SceneTree Nodes.
- [Thread-safe APIs](https://docs.godotengine.org/en/stable/tutorials/performance/thread_safe_apis.html) — Which engine APIs need Mutex/`call_deferred` when background threads touch global Autoload state.
- [Saving games](https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html) — Persistence patterns for inventory/settings held in long-lived Autoload data holders.
### Related Skills
#### Prerequisites
- [godot-project-foundations](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md) — AutoLoad entries live in Project Settings / `project.godot`; get registration and naming right before wiring managers.
- [godot-gdscript-mastery](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md) — Typed signals, `static var` / `class_name`, and deferred calls are the language tools this skill’s patterns assume.
- [godot-signal-architecture](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md) — Event-bus and Signal-Up contracts for Autoload mediators without circular emit chains.
#### Complements
- [godot-scene-management](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-scene-management/SKILL.md) — Pair with safe scene switchers so transitions own loading/unload while AutoLoads keep cross-scene state.
- [godot-save-load-systems](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-save-load-systems/SKILL.md) — Serialize what persistent Autoload holders store; do not invent a second save path inside GameManager.
- [godot-resource-data-patterns](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-resource-data-patterns/SKILL.md) — Prefer Resources for shared config; reserve AutoLoads for lifecycle + signals, not duplicated data blobs.
- [godot-composition](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-composition/SKILL.md) — Component ownership alternative when a “manager Autoload” is really scene-scoped behavior in disguise.
- [godot-audio-systems](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-audio-systems/SKILL.md) — Music/SFX pools are classic Autoload homes; use this skill for ownership and boot order around those managers.
- [godot-state-machine-advanced](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-state-machine-advanced/SKILL.md) — Global MENU/PLAYING/PAUSED FSMs belong here when the Autoload is only the owner, not the whole game logic dump.
- [godot-debugging-profiling](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md) — Init-order diagnostics and singleton health checks escalate into debugger/profiler workflows when boot hangs.
#### Downstream / consumers
- [godot-performance-optimization](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md) — Escalate when too many Node Autoloads, eager preloads, or per-frame manager work show up in profilers.
- [godot-testing-patterns](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-testing-patterns/SKILL.md) — GUT/CI health checks for registered singletons and reset of global state between tests.
- [godot-multiplayer-networking](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md) — Global state Autoloads become authority/replication hazards; consume this skill’s DI patterns carefully online.
- [godot-inventory-system](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-inventory-system/SKILL.md) — Typical consumer of persistent Autoload holders for inventory that must survive `change_scene_to_file()`.
#### Master
- [godot-master](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-master/SKILL.md) — Library router and mirrored module entry; open when discovering which Domain Skill owns a cross-cutting singleton concern.