Core Architecture
Relevant source files
This document explains the fundamental architectural patterns that underpin TajsMod’s implementation. It covers the hub-and-spoke orchestration model, two-phase initialization sequence, script extension mechanism, and component lifecycle management.
For details about individual manager components, see Utility Manager Components. For configuration system internals, see Configuration System. For script extension implementations, see Script Extensions. For the main orchestrator’s implementation details, see The Main Orchestrator (mod_main.gd).
Architectural Pattern: Hub-and-Spoke Orchestration
Section titled “Architectural Pattern: Hub-and-Spoke Orchestration”TajsMod uses a centralized orchestration pattern where mod_main.gd acts as the hub that initializes and coordinates 10+ independent spoke components. Each manager component is responsible for a specific feature domain and communicates through shared interfaces (Globals, Signals, ConfigManager).
The Hub: mod_main.gd
Section titled “The Hub: mod_main.gd”The main orchestrator (mod_main.gd) serves as the entry point and lifecycle coordinator for all mod functionality. It:
- Installs script extensions during
_init() - Creates and initializes manager components
- Manages shared resources (color picker, configuration)
- Provides coordination methods for inter-component communication
- Handles persistent runtime tasks in
_process()
Sources: mod_main.gd L1-L68
Manager Components (Spokes)
Section titled “Manager Components (Spokes)”The orchestrator manages these specialized components:
| Manager | Purpose | Initialized In |
|---|---|---|
ConfigManager | Settings persistence and retrieval | _init() |
ScreenshotManager | Image capture and tiling | _init() |
PaletteController | Command palette system | _init() |
WireClearHandler | Right-click wire clearing | _init() |
FocusHandler | Audio muting on focus loss | _init() |
WireColorOverrides | Custom wire color system | _init() |
GotoGroupManager | Node group navigation | _setup_for_main() |
GotoGroupPanel | UI for group navigation | _setup_for_main() |
NodeGroupZOrderFix | Z-order rendering fix | _setup_for_main() |
BuyMaxManager | Upgrade automation | _setup_for_main() |
CheatManager | Cheat panel features | _build_settings_menu() |
NotificationLogPanel | Toast history UI | _setup_notification_log() |
DisconnectedNodeHighlighter | Visual node highlighting | _setup_disconnected_highlighter() |
StickyNoteManager | Sticky notes feature | _setup_sticky_notes() |
UpgradeManager | Modifier key upgrades | _setup_upgrade_manager() |
Sources: mod_main.gd L32-L48
Component Communication
Section titled “Component Communication”flowchart TD
ModMain["mod_main.gd<br>(Hub)"]
Config["ConfigManager<br>settings storage"]
Globals["Globals<br>game state"]
Signals["Signals<br>event bus"]
Screenshot["ScreenshotManager"]
BuyMax["BuyMaxManager"]
Focus["FocusHandler"]
WireColor["WireColorOverrides"]
Palette["PaletteController"]
GotoGroup["GotoGroupManager"]
ZOrder["NodeGroupZOrderFix"]
Disconnected["DisconnectedNodeHighlighter"]
Sticky["StickyNoteManager"]
Upgrade["UpgradeManager"]
ModMain --> Screenshot
ModMain --> BuyMax
ModMain --> Focus
ModMain --> WireColor
ModMain --> Palette
ModMain --> GotoGroup
ModMain --> ZOrder
ModMain --> Disconnected
ModMain --> Sticky
ModMain --> Upgrade
Config --> ModMain
Screenshot --> Config
BuyMax --> Config
Focus --> Config
WireColor --> Config
Palette --> Config
Screenshot --> Signals
Disconnected --> Globals
BuyMax --> Globals
Upgrade --> Globals
subgraph subGraph1 ["Manager Components (Spokes)"]
Screenshot
BuyMax
Focus
WireColor
Palette
GotoGroup
ZOrder
Disconnected
Sticky
Upgrade
end
subgraph subGraph0 ["Shared Interfaces"]
Config
Globals
Signals
end
Sources: mod_main.gd L32-L48
Two-Phase Initialization
Section titled “Two-Phase Initialization”TajsMod uses a two-phase initialization sequence to ensure proper dependency ordering. This pattern separates construction-time tasks from scene-tree-dependent setup.
Phase 1: _init() - Early Initialization
Section titled “Phase 1: _init() - Early Initialization”The _init() method runs before the node tree is available. It handles:
- Script extension installation - Must happen first, before any extended classes are instantiated
- Configuration loading - Settings must be available for component initialization
- Early manager creation - Components that don’t need the scene tree
sequenceDiagram participant ModLoader participant mod_main._init() participant Script Extensions participant ConfigManager participant Early Managers ModLoader->>mod_main._init(): "Load mod" mod_main._init()->>Script Extensions: "install_script_extension() x10" note over Script Extensions: "Extensions modify base classes mod_main._init()->>ConfigManager: "new ConfigManager()" ConfigManager->>ConfigManager: "load_config()" note over ConfigManager: "Loads user://tajs_mod_config.json" mod_main._init()->>Early Managers: "new ScreenshotManager()" mod_main._init()->>Early Managers: "new PaletteController()" mod_main._init()->>Early Managers: "new WireClearHandler()" mod_main._init()->>Early Managers: "new FocusHandler()" mod_main._init()->>Early Managers: "new WireColorOverrides()" note over mod_main._init(): "Scene tree not yet available"
Code execution order:
- Install 10 script extensions mod_main.gd L73-L82
- Create
ConfigManagerinstance mod_main.gd L89 - Create early managers that don’t require scene tree mod_main.gd L92-L113
Sources: mod_main.gd L72-L113
Phase 2: _ready() - Scene Tree Setup
Section titled “Phase 2: _ready() - Scene Tree Setup”The _ready() method runs after the scene tree is available. It handles:
- Shared resource initialization - UI components like color picker
- Wire color application - Requires
Datato be loaded - Node listener setup - Waits for
Mainnode to appear - Patching operations - Injects modifications into game systems
sequenceDiagram
participant Godot Engine
participant mod_main._ready()
participant Shared UI (ColorPicker)
participant WireColorOverrides
participant SceneTree
Godot Engine->>mod_main._ready(): "_ready() called"
mod_main._ready()->>Shared UI (ColorPicker): "Create CanvasLayer + ColorPickerPanel"
note over Shared UI (ColorPicker): "picker_canvas.layer = 100
mod_main._ready()->>WireColorOverrides: "wire_colors.setup(config)"
mod_main._ready()->>WireColorOverrides: "wire_colors.apply_overrides()"
note over WireColorOverrides: "Data.connectors now available"
mod_main._ready()->>mod_main._ready(): "screenshot_manager.set_tree()"
mod_main._ready()->>mod_main._ready(): "Globals.custom_node_limit = saved_limit"
mod_main._ready()->>SceneTree: "get_tree().node_added.connect()"
note over SceneTree: "Listen for 'Main' node appearance"
mod_main._ready()->>mod_main._ready(): "call_deferred('_check_existing_main')"
Code execution order:
- Create shared color picker UI mod_main.gd L119-L143
- Apply wire color overrides mod_main.gd L145-L147
- Set scene tree references mod_main.gd L150
- Apply saved settings to
Globalsmod_main.gd L153-L155 - Inject bin window patch mod_main.gd L158
- Listen for
Mainnode mod_main.gd L162-L163
Sources: mod_main.gd L115-L163
Phase 3: _setup_for_main() - Deferred Setup
Section titled “Phase 3: _setup_for_main() - Deferred Setup”When the Main node is detected, _setup_for_main() performs UI-dependent initialization:
- Settings UI creation - Requires HUD to be available
- Late manager initialization - Components that need UI references
- Command palette setup - Needs tree and config
- Feature panel integration - Injects UI into game’s HUD
flowchart TD NodeAdded["SceneTree.node_added<br>signal"] WaitTimer["await 0.5s timer"] Setup["_setup_for_main()"] CheckDupe["Check if already setup"] InitUI["Create SettingsUI"] InitMgrs["Initialize late managers:<br>• DisconnectedNodeHighlighter<br>• GotoGroupManager<br>• BuyMaxManager<br>• NotificationLogPanel<br>• StickyNoteManager<br>• UpgradeManager"] BuildMenu["_build_settings_menu()"] InitPalette["palette_controller.initialize()"] ApplyVisuals["Apply initial settings:<br>• Extra glow<br>• UI opacity<br>• Feature states"] NodeAdded --> WaitTimer WaitTimer --> Setup Setup --> CheckDupe CheckDupe --> InitUI InitUI --> InitMgrs InitMgrs --> BuildMenu BuildMenu --> InitPalette InitPalette --> ApplyVisuals
Sources: mod_main.gd L268-L340
Script Extension System
Section titled “Script Extension System”Script extensions allow the mod to modify base game classes without replacing binary files. They are installed during _init() before any game classes are instantiated.
Extension Installation
Section titled “Extension Installation”Extensions are installed using ModLoaderMod.install_script_extension():
flowchart TD Init["mod_main._init()"] Ext1["install globals.gd"] Ext2["install windows_menu.gd"] Ext3["install schematic_container.gd"] Ext4["install popup_schematic.gd"] Ext5["install options_bar.gd"] Ext6["install window_group.gd"] Ext7["install window_bin.gd"] Ext8["install window_inventory.gd"] Ext9["install request_panel.gd"] Ext10["install requests_tab.gd"] Ready["mod_main._ready()"] Note1["Extends Globals class<br>Adds: custom_node_limit,<br>select_all_enabled, etc."] Note2["Extends WindowsMenu<br>Adds: select_all behavior"] Note3["Extends SchematicContainer<br>Adds: wire drop menu"] Note6["Extends WindowGroup<br>Adds: pattern picker, colors"] Note7["Extends WindowBin<br>Adds: THE BIN features"] Note8["Extends WindowInventory<br>Adds: 6-input support"] Init --> Ext1 Ext1 --> Ext2 Ext2 --> Ext3 Ext3 --> Ext4 Ext4 --> Ext5 Ext5 --> Ext6 Ext6 --> Ext7 Ext7 --> Ext8 Ext8 --> Ext9 Ext9 --> Ext10 Ext10 --> Ready Ext1 --> Note1 Ext2 --> Note2 Ext3 --> Note3 Ext6 --> Note6 Ext7 --> Note7 Ext8 --> Note8
Installed Extensions:
| Extension File | Base Class | Purpose |
|---|---|---|
globals.gd | Globals | Adds custom node limit, select all flag, upgrade multiplier |
windows_menu.gd | WindowsMenu | Implements Ctrl+A select all functionality |
schematic_container.gd | SchematicContainer | Wire drop menu integration |
popup_schematic.gd | PopupSchematic | Popup-related enhancements |
options_bar.gd | OptionsBar | Options bar modifications |
window_group.gd | WindowGroup | Custom colors and patterns |
window_bin.gd | WindowBin | THE BIN enhancements |
window_inventory.gd | WindowInventory | 6-input container support |
request_panel.gd | RequestPanel | Request panel modifications |
requests_tab.gd | RequestsTab | Requests tab enhancements |
Sources: mod_main.gd L73-L82
Extension Timing
Section titled “Extension Timing”Extensions must be installed in _init() because:
- Class inheritance - Extensions modify class definitions
- Instantiation order - Base game instantiates windows/nodes during startup
- No retroactive patching - Cannot modify already-instantiated objects’ class definitions
Sources: mod_main.gd L72-L86
Component Lifecycle
Section titled “Component Lifecycle”Each manager component follows a standard lifecycle pattern orchestrated by mod_main.gd.
Lifecycle Stages
Section titled “Lifecycle Stages”stateDiagram-v2
[*] --> Construction : "new Manager()"
Construction --> Setup : "setup(config, tree, ...)"
Setup --> Active : "Component ready"
Active --> Disabled : "set_enabled(true)"
Disabled --> Active : "Cleanup (rare)"
Active --> [*] : "Cleanup (rare)"
Construction Phase
Section titled “Construction Phase”Managers are created during initialization:
Early managers (created in _init()):
ConfigManager- No dependenciesScreenshotManager- Needs configPaletteController- Added as child immediatelyWireClearHandler- Needs configFocusHandler- Needs configWireColorOverrides- Standalone initially
Late managers (created in _setup_for_main()):
DisconnectedNodeHighlighter- Needs tree + configGotoGroupManager- Needs treeNodeGroupZOrderFix- Needs treeBuyMaxManager- Needs tree + configNotificationLogPanel- Needs HUD referenceStickyNoteManager- Needs tree + configUpgradeManager- Needs tree + config
Sources: mod_main.gd L88-L113
Setup Phase
Section titled “Setup Phase”Managers receive their dependencies through setup() methods:
# Example: FocusHandler setup pattern
focus_handler.setup(config)
# Example: DisconnectedNodeHighlighter setup pattern
disconnected_highlighter.setup(config, get_tree(), self)
# Example: BuyMaxManager setup pattern (async)
await get_tree().create_timer(0.1).timeoutbuy_max_manager.setup(get_tree(), config)Common setup parameters:
config: ConfigManager- For reading/writing settingstree: SceneTree- For node access and signalsmod_main: Node- For accessing debug mode or shared resources
Sources: mod_main.gd L104
Active Phase
Section titled “Active Phase”During runtime, managers:
- Respond to configuration changes via config callbacks
- Listen to game signals (e.g.,
Signals.notify) - Handle user input through input events
- Update UI as game state changes
- Enable/disable based on toggle settings
Example: Toggle visibility pattern:
func _set_goto_group_visible(visible: bool) -> void: if goto_group_panel: var container = goto_group_panel.get_parent() if container: container.visible = visibleSources: mod_main.gd L1082-L1086
Runtime Coordination
Section titled “Runtime Coordination”The _process() method handles persistent runtime tasks:
flowchart TD Process["_process(delta)"] PatchDesktop["Desktop<br>patched?"] DoPatch["Patcher.patch_desktop_script()"] UpdateLabel["_update_node_label()"] CheckBoot["Custom boot<br>screen enabled?"] PatchBoot["Patcher.patch_boot_screen()"] Done["End _process()"] Process --> PatchDesktop PatchDesktop --> DoPatch PatchDesktop --> UpdateLabel DoPatch --> CheckBoot UpdateLabel --> CheckBoot CheckBoot --> PatchBoot CheckBoot --> Done PatchBoot --> Done
Persistent patches:
- Desktop script patching mod_main.gd L180-L181
- Node counter label updates mod_main.gd L184
- Boot screen customization mod_main.gd L187-L190
Sources: mod_main.gd L178-L190
Configuration Integration
Section titled “Configuration Integration”Components access configuration through the shared ConfigManager instance:
flowchart TD
User["User changes setting"]
UI["Settings UI toggle"]
Callback["Lambda callback"]
SetValue["config.set_value(key, val)"]
Save["Auto-save to JSON"]
ApplyState["Update component state"]
Manager["Manager component"]
GetValue["config.get_value(key, default)"]
State["Apply to internal state"]
JSON["user://tajs_mod_config.json"]
Save --> JSON
JSON --> GetValue
subgraph subGraph1 ["Manager Access"]
Manager
GetValue
State
Manager --> GetValue
GetValue --> State
end
subgraph subGraph0 ["Configuration Flow"]
User
UI
Callback
SetValue
Save
ApplyState
User --> UI
UI --> Callback
Callback --> SetValue
SetValue --> Save
SetValue --> ApplyState
end
Configuration access pattern:
- Read on initialization:
var value = config.get_value("setting_key", default_value) - Write on change:
config.set_value("setting_key", new_value)(auto-saves) - Apply immediately: Component updates its behavior in response
Sources: mod_main.gd L467-L469
extensions/scripts/utilities/config_manager.gd L110-L121
Summary: Architectural Benefits
Section titled “Summary: Architectural Benefits”| Pattern | Benefit |
|---|---|
| Hub-and-Spoke | Clean separation of concerns; easy to add/remove features |
| Two-Phase Init | Proper dependency ordering; avoids null reference errors |
| Script Extensions | Non-invasive integration; survives game updates |
| Standardized Lifecycle | Predictable component behavior; consistent patterns |
| Centralized Config | Single source of truth; automatic persistence |
Sources: mod_main.gd L1-L1248