Save File Migration
Relevant source files
The following files were used as context for generating this wiki page:
The Save File Migration system is a feature of the Skid-Inc Electron application designed to import player progress from the original web-based version of the game. The web version stored save data in the browser's localStorage, whereas the Electron application uses a file-based save system. This migration process ensures a seamless transition for players moving to the desktop application by detecting, converting, and importing their old save data.
The system is composed of components in both the main and renderer processes, which communicate via IPC channels. It handles everything from automatic detection on startup to a user-initiated migration from the in-game options menu. The process is designed to be safe, creating backups of the original localStorage data before writing the new save file.
Architecture
The migration system is split between the Electron main process and the renderer process to separate backend logic from the user interface.
- Main Process: The
MigrationManagerclass contains the core logic for data conversion, validation, and file system operations. It is instantiated inmain.jsand exposes its functionality to the renderer process via IPC handlers. - Renderer Process: The
MigrationUIManagerclass is responsible for all user-facing aspects. It detects the presence oflocalStoragedata, displays a modal to the user, and communicates the user's choice back to the main process.
This diagram illustrates the high-level architecture and communication flow.
Key Components
| Class | Process | File | Description |
|---|---|---|---|
MigrationManager | Main | src/migration-manager.js | Handles data decoding, conversion, validation, and writing the migrated save file and backup. |
MigrationUIManager | Renderer | src/renderer/migration-ui-manager.js | Detects localStorage data, creates and manages the migration prompt UI, and initiates the migration process via IPC. |
ElectronApp (in main.js) | Main | src/main.js | Initializes MigrationManager and sets up all IPC handlers for migration. |
SaveMetadataManager | Main | src/save-metadata-manager.js | Used by MigrationManager to update a flag (migrationCompleted: true) in the save metadata file after a successful migration. |
Sources: src/main.js:199, src/migration-manager.js:8, src/renderer/migration-ui-manager.js:5, src/save-metadata-manager.js:154
Migration Process Flow
The migration can be triggered automatically on startup or manually by the user.
Automatic Startup Detection
On application startup, MigrationUIManager checks if migration has already been completed and if localStorage data exists. If so, it prompts the user to migrate.
The following sequence diagram shows the flow from detection to completion.
Manual Migration Trigger
The user can also initiate a migration check from the in-game options menu.
- The user clicks the "Migrate localStorage" button in the options modal.
- This calls the
manualMigrationCheck()method inMigrationUIManager. - The
manualMigrationCheck()method then executes the samecheckAndPromptMigration()flow as the automatic startup check.
Sources: app/js/options.js:155-157, src/renderer/migration-ui-manager.js:550-554, index.html:410-412
Data Handling
localStorage Data Decoding
The original game's localStorage data is stored under the key SKINC and is base64 encoded with a custom function. The MigrationManager and MigrationUIManager both contain logic to decode this data.
The decoding process is as follows:
- The base64 string is decoded.
- The resulting binary string is converted to a URI-encoded string.
decodeURIComponentis used to get the final JSON string.- The JSON string is parsed into an object.
// From src/renderer/migration-ui-manager.js
decodeLocalStorageData(encodedData) {
try {
// Custom base64 decode function matching the original game
const decodedString = decodeURIComponent(
atob(encodedData)
.split('')
.map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('')
);
return JSON.parse(decodedString);
} catch (error) {
throw new Error(`Failed to decode localStorage data: ${error.message}`);
}
}
Sources: src/renderer/migration-ui-manager.js:125-144, src/migration-manager.js:358-377
Data Conversion and Validation
Once decoded, the MigrationManager converts the old data structure into the new file-based save format. The convertDataFormat method maps old properties to new ones, providing default values for any missing data.
This table shows a partial mapping of key fields:
Old localStorage Field | New Save File Field | Default Value |
|---|---|---|
player.username | player.username | 'kiddie' |
player.money | player.money | 0 |
player.level | player.level | 1 |
script.unlocked | script.unlocked | [] |
server.owned | server.owned | [] |
battery.level | battery.level | 1 |
achievements.owned | achievements.owned | [] |
After conversion, validateImportedData checks the new object for required properties and valid data types, logging warnings for non-critical issues and errors for critical ones.
Sources: src/migration-manager.js:59-138, src/migration-manager.js:145-244
File Operations
The importFromLocalStorage method in MigrationManager performs two key file operations using FileSystemManager:
- Save Migrated Data: It writes the newly converted and validated game state to a JSON file (e.g.,
migrated_localStorage_... .json). - Backup Original Data: It saves the raw, unconverted
localStoragedata to a separate backup file (e.g.,localStorage_backup_... .json) as a safety measure.
This flow is managed within the importFromLocalStorage function.
IPC Communication
The renderer and main processes communicate using a set of dedicated IPC channels for migration. These are defined in src/main.js.
| IPC Channel | Direction | Description |
|---|---|---|
check-migration-status | Renderer -> Main | Checks if migration has already been completed. |
import-from-localstorage | Renderer -> Main | Sends the encoded localStorage data to the main process to be imported. |
get-migration-statistics | Renderer -> Main | Requests statistics about past migrations, such as number of migrated files and their sizes. |
reset-migration-status | Renderer -> Main | Resets the migrationCompleted flag in the metadata (for debugging). |
decode-localstorage-data | Renderer -> Main | Asks the main process to decode and validate data, returning a preview. |
migration-status | Main -> Renderer | An event channel used by the main process to send completion or failure status back to the renderer. |
Sources: src/main.js:706-799, src/renderer/migration-ui-manager.js:29-31
User Interface
The migration UI is handled by MigrationUIManager and consists of a modal dialog that appears over the game.
- Detection: The UI manager checks for
localStoragedata on startup. - Prompt: If data is found, a modal is displayed, showing a preview of the detected data (Game Version, Player Level, Money, Data Size).
- Actions: The user can choose to "Migrate Data" or "Skip Migration".
- Progress: If migration is accepted, the modal displays a progress bar and status updates.
- Feedback: The modal shows a success, failure, or retry message.
The UI elements for the modal and the manual trigger button are defined in index.html. The "Migrate localStorage" button in the options menu provides a way for users who initially skipped the prompt to perform the migration later.
Sources: src/renderer/migration-ui-manager.js:147-268, index.html:410-412
Conclusion
The Save File Migration system is a critical feature for user retention, providing a smooth onboarding experience for existing players transitioning to the Electron desktop application. By separating UI concerns in the renderer process from the core logic in the main process, the system maintains a responsive interface while securely handling data conversion and file I/O in the background. The inclusion of data validation, backups, and clear user feedback makes the process robust and user-friendly.
Sources: src/migration-manager.js, src/renderer/migration-ui-manager.js