# System Architecture High-level architecture guide for the Skelly project, explaining system design, architectural patterns, and design decisions. **Quick Links**: - **Coding Standards**: See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) - **Testing Protocols**: See [TESTING.md](TESTING.md) - **Component APIs**: See [UI_COMPONENTS.md](UI_COMPONENTS.md) ## Overview Skelly uses a service-oriented architecture with **autoload managers** providing global services, **scene-based components** implementing gameplay, and **signal-based communication** for loose coupling. **Key Architectural Principles**: - **Instance-based design**: No global static state for testability - **Signal-driven communication**: Loose coupling between systems - **Service layer pattern**: Autoloads as singleton services - **State machines**: Explicit state management for complex flows - **Defensive programming**: Input validation, error recovery, fallback mechanisms ## Autoload System Autoloads provide singleton services accessible globally. Each has a specific responsibility. ### GameManager **Responsibility**: Scene transitions and game state coordination **Key Features**: - Centralized scene loading with error handling - Race condition protection via `is_changing_scene` flag - Scene path constants for maintainability - Gameplay mode validation with whitelist - Never use `get_tree().change_scene_to_file()` directly **Usage**: ```gdscript # ✅ Correct GameManager.start_game_with_mode("match3") # ❌ Wrong get_tree().change_scene_to_file("res://scenes/game/Game.tscn") ``` **Files**: `src/autoloads/GameManager.gd` ### SaveManager **Responsibility**: Save/load game state with security and data integrity **Key Features**: - **Tamper Detection**: Deterministic checksums detect save file modification - **Race Condition Protection**: Save operation locking prevents concurrent conflicts - **Permissive Validation**: Auto-repair system fixes corrupted data - **Type Safety**: NaN/Infinity/bounds checking for numeric values - **Memory Protection**: File size limits prevent memory exhaustion - **Version Migration**: Backward-compatible save format upgrades - **Error Recovery**: Multi-layered backup and fallback systems **Security Model**: ```gdscript # Checksum validates data integrity save_data["_checksum"] = _calculate_checksum(save_data) # Auto-repair fixes corrupted fields if not _validate_save_data(data): data = _repair_save_data(data) # Race condition protection if _is_saving: return # Prevent concurrent saves ``` **Testing**: See [TESTING.md](TESTING.md#save-system-testing-protocols) for comprehensive test suites validating checksums, migration, and integration. **Files**: `src/autoloads/SaveManager.gd` ### SettingsManager **Responsibility**: User settings persistence and validation **Key Features**: - Input validation with NaN/Infinity checks - Bounds checking for numeric values - Security hardening against invalid inputs - Default fallback values - Type coercion with validation **Files**: `src/autoloads/SettingsManager.gd` ### DebugManager **Responsibility**: Unified logging and debug UI coordination **Key Features**: - Structured logging with log levels (TRACE, DEBUG, INFO, WARN, ERROR, FATAL) - Category-based log organization - Global debug UI toggle (F12 key) - Log level filtering for development/production - Replaces all `print()` and `push_error()` calls **Usage**: See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md#logging-standards) for logging best practices. **Files**: `src/autoloads/DebugManager.gd` ### AudioManager **Responsibility**: Music and sound effect playback **Key Features**: - Audio bus system (Music, SFX) - Volume control per bus - Loop configuration for music - UI click sounds **Files**: `src/autoloads/AudioManager.gd` ### LocalizationManager **Responsibility**: Language switching and translation management **Key Features**: - Multi-language support (English, Russian) - Runtime language switching - Translation file management **Files**: `src/autoloads/LocalizationManager.gd` ## Scene Management Pattern All scene transitions flow through **GameManager** to ensure consistent error handling and state management. ### Scene Loading Flow ``` User Action ↓ GameManager.start_game_with_mode(mode) ↓ Validate mode (whitelist check) ↓ Check is_changing_scene flag ↓ Load PackedScene with validation ↓ change_scene_to_packed() ↓ Reset is_changing_scene flag ``` ### Race Condition Prevention ```gdscript var is_changing_scene: bool = false func start_game_with_mode(gameplay_mode: String) -> void: # Prevent concurrent scene changes if is_changing_scene: DebugManager.log_warn("Scene change already in progress", "GameManager") return is_changing_scene = true # ... scene loading logic ... is_changing_scene = false ``` **Why This Matters**: Multiple rapid button clicks or input events could trigger concurrent scene loads, causing crashes or undefined state. ## Modular Gameplay System Game modes are implemented as separate gameplay modules in `scenes/game/gameplays/`. ### Gameplay Architecture ``` Game.tscn (Main scene) ↓ ├─> Match3Gameplay.tscn (Match-3 mode) ├─> ClickomaniaGameplay.tscn (Clickomania mode) └─> [Future gameplay modes] ``` **Pattern**: Each gameplay mode: - Extends Control or Node2D - Emits `score_changed` signal - Implements `_ready()` for initialization - Handles input independently - Includes optional debug menu **Files**: `scenes/game/gameplays/` ## Design Patterns Used ### Instance-Based Architecture **Problem**: Static variables prevent testing and create hidden dependencies. **Solution**: Instance-based architecture with explicit dependencies. ```gdscript # ❌ Bad: Static global state static var current_gem_pool = [0, 1, 2, 3, 4] # ✅ Good: Instance-based var active_gem_types: Array = [] func set_active_gem_types(gem_indices: Array) -> void: active_gem_types = gem_indices.duplicate() ``` **Benefits**: - Each instance isolated for testing - No hidden global state - Explicit dependencies - Thread-safe by default ### Signal-Based Communication **Pattern**: Use signals for loose coupling between systems. ```gdscript # Component emits signal signal score_changed(new_score: int) # Parent connects to signal gameplay.score_changed.connect(_on_score_changed) ``` **Benefits**: - Loose coupling - Easy to test components in isolation - Clear event flow - Flexible subscription model ### Service Layer Pattern Autoloads act as singleton services providing global functionality. **Pattern**: - Autoloads expose public API methods - Components call autoload methods - Autoloads emit signals for state changes - Never nest autoload calls deeply ### State Machine Pattern Complex workflows use explicit state machines. **Example**: Match-3 tile swapping ``` WAITING → SELECTING → SWAPPING → PROCESSING → WAITING ``` **Benefits**: - Clear state transitions - Easy to debug - Prevents invalid state combinations - Self-documenting code ## Critical Architecture Decisions ### Memory Management: queue_free() Over free() **Decision**: Always use `queue_free()` instead of `free()` for node cleanup. **Rationale**: - `free()` causes immediate deletion (crashes if referenced) - `queue_free()` waits until safe deletion point - Prevents use-after-free bugs **Implementation**: ```gdscript # ✅ Correct for child in children_to_remove: child.queue_free() await get_tree().process_frame # Wait for cleanup # ❌ Dangerous for child in children_to_remove: child.free() # Can crash ``` **Impact**: Eliminated multiple potential crash points. See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md#memory-management-standards) for standards. ### Race Condition Prevention: State Flags **Decision**: Use state flags to prevent concurrent operations. **Rationale**: - Async operations can overlap without protection - Multiple rapid inputs can trigger race conditions - State flags provide simple, effective protection **Implementation**: Used in GameManager (scene loading), SaveManager (save operations), and gameplay systems (tile swapping). ### Error Recovery: Fallback Mechanisms **Decision**: Provide fallback behavior for all critical failures. **Rationale**: - Games should degrade gracefully, not crash - User experience > strict validation - Log errors but continue operation **Examples**: - Settings file corrupted? Load defaults - Scene load failed? Return to main menu - Audio file missing? Continue without sound ### Input Validation: Whitelist Approach **Decision**: Validate all user inputs against known-good values. **Rationale**: - Security hardening - Prevent invalid state - Clear error messages - Self-documenting code **Implementation**: Used in SettingsManager (volume, language), GameManager (gameplay modes), Match3Gameplay (grid movements). ## System Interactions ### Typical Game Flow ``` Main Menu ↓ [GameManager.start_game_with_mode("match3")] ↓ Game Scene Loads ↓ Match3Gameplay initializes ↓ ├─> SettingsManager (load difficulty) ├─> AudioManager (play background music) └─> DebugManager (setup debug UI) ↓ Gameplay Loop ↓ ├─> Input handling ├─> Score updates (emit score_changed signal) ├─> SaveManager (autosave high score) └─> DebugManager (log important events) ``` ### Signal Flow Example: Score Change ``` Match3Gameplay detects match ↓ [emit score_changed(new_score)] ↓ Game.gd receives signal ↓ Updates score display UI ↓ SaveManager.save_high_score(score) ``` ### Debug System Integration ``` User presses F12 ↓ DebugManager.toggle_debug_ui() ↓ [emit debug_ui_toggled(visible)] ↓ All debug menus receive signal ↓ Show/hide debug panels ``` ## Quality Improvements The project implements high-quality code standards from the start: **Key Quality Features**: - **Memory Safety**: Uses `queue_free()` pattern for safe node cleanup - **Error Handling**: Comprehensive error handling with fallbacks - **Race Condition Protection**: State flag protection for async operations - **Instance-Based Architecture**: No global static state for testability - **Code Reuse**: Base class architecture (DebugMenuBase) for common functionality - **Input Validation**: Complete validation coverage for all user inputs These standards are enforced through the [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md#code-quality-checklist) quality checklist. ## Best Practices ### Extending the Architecture When adding new features: 1. **New autoload?** - Only if truly global and used by many systems - Consider dependency injection instead - Keep autoloads focused on single responsibility 2. **New gameplay mode?** - Create in `scenes/game/gameplays/` - Extend appropriate base class - Emit `score_changed` signal - Add to GameManager mode whitelist 3. **New UI component?** - Create in `scenes/ui/components/` - Follow [UI_COMPONENTS.md](UI_COMPONENTS.md) patterns - Support keyboard/gamepad navigation - Emit signals for state changes ### Architecture Review Checklist Before committing architectural changes: - [ ] No new global static state introduced - [ ] All autoload access justified and documented - [ ] Signal-based communication used appropriately - [ ] Error handling with fallbacks implemented - [ ] Input validation in place - [ ] Memory management uses `queue_free()` - [ ] Race condition protection if async operations - [ ] Testing strategy defined ## Related Documentation - **[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)**: Coding standards and best practices - **[TESTING.md](TESTING.md)**: Testing protocols and procedures - **[UI_COMPONENTS.md](UI_COMPONENTS.md)**: Component API reference - **[CLAUDE.md](CLAUDE.md)**: LLM assistant quick start guide