Save/Load System Architecture
Relevant source files
The following files were used as context for generating this wiki page:The Save/Load system in Skid-Inc is a robust, multi-component architecture designed for the Electron version of the game. It provides automatic and manual saving, backup management, error handling with recovery mechanisms, and performance optimizations. The system is split between the Electron main process for file system operations and the renderer process for UI interaction and game state management.
This architecture ensures data integrity and a seamless user experience by handling saves in the background, recovering from errors, and providing a user-friendly interface for managing save files. It includes features like periodic auto-saves, change-based quick saves, backup rotation, and startup optimizations like save file preloading.
Core Components
The system is composed of several key modules that collaborate across Electron's main and renderer processes.
This diagram shows the high-level architecture, separating components by their process and showing their primary interactions.
Sources: src/main.js, src/renderer/save-manager.js, src/auto-save-manager.js, src/filesystem-manager.js, app/js/save-manager-ui.js
Component Descriptions
| Component | Location | Responsibility |
|---|---|---|
ElectronApp | src/main.js | Initializes all main process managers, creates the main window, and sets up IPC handlers for communication with the renderer process. |
SaveStateManager | src/renderer/save-manager.js | Acts as the bridge in the renderer process. It reads the game state from the window.skidinc object, communicates with the main process via IPC, and applies loaded state back to the game. |
saveManagerUI | app/js/save-manager-ui.js | Manages the Save Management modal, handling user interactions for listing, loading, deleting, and creating saves and backups. |
AutoSaveManager | src/auto-save-manager.js | Orchestrates the entire save process in the main process. Manages periodic saves, change-based quick saves, backup creation, and cleanup. It uses other managers for file I/O and error handling. |
FileSystemManager | src/filesystem-manager.js | Handles all low-level file system operations. This includes reading, writing, deleting, and listing save files, as well as managing the save directory and validating disk space. |
SaveErrorHandler | src/save-error-handler.js | Implements a robust error handling strategy, including retries with exponential backoff, a "degraded mode" for persistent failures, and backup recovery. |
StartupOptimizer | src/startup-optimizer.js | Optimizes application startup by preloading recent save files and caching metadata to reduce initial load times. |
SaveMetadataManager | src/save-metadata-manager.js | Manages a metadata.json file to track individual save files, their checksums for integrity, and other statistics. |
Sources: src/main.js:10-18, src/renderer/save-manager.js:5-6, app/js/save-manager-ui.js:5-6, src/auto-save-manager.js:8-9, src/filesystem-manager.js:8-9, src/save-error-handler.js:6-7, src/startup-optimizer.js:6-7, src/save-metadata-manager.js:6-7
Data Flow and Processes
Game Save Process
The save process can be initiated manually by the user or automatically by the AutoSaveManager. The following diagram illustrates the sequence for a manual save triggered from the UI.
This sequence shows how a save request flows from the renderer UI to the main process, where it is handled by the AutoSaveManager and written to disk by the FileSystemManager.
Sources: app/js/save-manager-ui.js:300-307, src/renderer/save-manager.js:605-626, src/main.js:304-332, src/auto-save-manager.js:487-503, src/filesystem-manager.js:132-187
Auto-Save Mechanism
The AutoSaveManager implements two types of automatic saving:
- Periodic Save: Occurs at a regular interval (default 30 seconds). It saves the last known game state.
- Quick Save: Triggered by significant changes in the game state. A debounce timer (default 5 seconds) prevents excessive saves during rapid state changes.
The hasStateChanged method in SaveStateManager determines if a change is significant enough to trigger a save by comparing critical properties of the game state object.
Sources: src/auto-save-manager.js:23-24, src/renderer/save-manager.js:203-222
Game Load Process
Loading a game can happen automatically at startup or be initiated manually by the user. The startup process is optimized to load the most recent save quickly.
This diagram shows the optimized startup load process, where StartupOptimizer is used to potentially leverage preloaded save data.
Sources: src/renderer/save-manager.js:109-173, src/main.js:743-780, src/startup-optimizer.js:301-340, src/renderer/save-manager.js:401-483
Error Handling and Recovery
The SaveErrorHandler provides a safety net for file operations. When a save fails, it initiates a recovery process.
This flow demonstrates the robust error handling, which includes retries, exponential backoff, recovery strategies (like cleaning files or using a backup location), and a "degraded mode" to prevent further issues during persistent failures.
Sources: src/save-error-handler.js:46-70, src/save-error-handler.js:77-111, src/save-error-handler.js:118-149
IPC Communication
Communication between the renderer and main processes is handled via Electron's IPC mechanism. The preload.js script exposes the electronAPI, and src/main.js defines the handlers.
| Channel | Direction | Description |
|---|---|---|
save-game-state | Renderer -> Main | Sends the current game state object to be saved to a file. |
load-game-state | Renderer -> Main | Requests to load the most recent game state. |
get-save-list | Renderer -> Main | Requests a list of all available save files with metadata. |
delete-save | Renderer -> Main | Requests the deletion of a specific save file. |
load-specific-save | Renderer -> Main | Requests to load a game state from a specific filename. |
create-manual-backup | Renderer -> Main | Requests the creation of a manual backup file. |
get-save-statistics | Renderer -> Main | Retrieves comprehensive statistics about the save system. |
start-auto-save | Renderer -> Main | Starts the auto-save service. |
stop-auto-save | Renderer -> Main | Stops the auto-save service. |
load-save-optimized | Renderer -> Main | Requests to load a save file using the StartupOptimizer. |
save-status | Main -> Renderer | Notifies the renderer of the status (success/failure) of a save operation. |
auto-save-event | Main -> Renderer | Forwards events from the AutoSaveManager (e.g., save-success, backup-created). |
Sources: src/main.js:304-620
Data Structures
Game State Object
The SaveStateManager is responsible for gathering the current game state into a single object for serialization. This is done in the getCurrentGameState function.
The structure is as follows:
{
"version": "0.33",
"timestamp": 167... ,
"player": { ... },
"script": { ... },
"server": { ... },
"battery": { ... },
"achievements": { ... },
"autoscript": { ... },
"options": { ... },
"tutorial": { "finish": true },
"console": { ... }
}
Sources: src/renderer/save-manager.js:231-306
Save File Metadata
In addition to the game state, the AutoSaveManager injects metadata into the save file before writing it to disk. This helps with debugging and tracking.
-
saveMetadata(inside save file): Contains information about the specific save operation.timestamp: When the save was created.saveType:periodic,quick, ormanual.version: Game version.retryCount: Number of retries it took to save.wasRepaired: Boolean indicating if data was repaired before saving.
Sources: src/auto-save-manager.js:419-428
-
metadata.json(in save directory): Managed bySaveMetadataManager, this file tracks all save files and their integrity.saves: An object where keys are filenames.checksum: SHA-256 hash of the file content for integrity checks.size: File size in bytes.created: Timestamp of creation.lastModified: Timestamp of last modification.
statistics: Global stats like total save/load operations.
Sources: src/save-metadata-manager.js:10-28, src/save-metadata-manager.js:110-125
User Interface
The save management UI is a Bootstrap modal defined in index.html and controlled by app/js/save-manager-ui.js.
Key UI features:
- List Saves: Displays a filterable list of all save files and backups.
- Actions: Provides buttons to
Load,Duplicate, andDeletesaves. - Manual Operations: Buttons to
Create Manual SaveandCreate Backup. - Statistics: Shows the total count of saves and backups.
The UI interacts with the SaveStateManager to perform these actions, which then communicates with the main process.
Sources: index.html:451-497, app/js/save-manager-ui.js.
Conclusion
The Skid-Inc Save/Load system is a comprehensive and resilient architecture that goes beyond simple file I/O. By separating concerns into distinct managers for file operations, auto-saving, error handling, and metadata, it achieves a high degree of robustness. Features like startup optimization, backup rotation, and a "degraded mode" ensure a smooth user experience and protect player data against corruption and loss. The clear separation between the main and renderer processes, linked by a well-defined IPC API, makes the system modular and maintainable.