Update save data format and error handling.

This commit is contained in:
EliteMasterEric 2024-10-18 17:11:09 -04:00 committed by Cameron Taylor
parent 24ad7f4a39
commit dff4135fc9
7 changed files with 122 additions and 38 deletions

View file

@ -11,7 +11,7 @@
"name": "flixel", "name": "flixel",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "f2b090d6c608471e730b051c8ee22b8b378964b1", "ref": "ffa691cb2d2d81de35b900a4411e4062ac84ab58",
"url": "https://github.com/FunkinCrew/flixel" "url": "https://github.com/FunkinCrew/flixel"
}, },
{ {

View file

@ -1,25 +1,23 @@
package funkin.save; package funkin.save;
import flixel.util.FlxSave; import flixel.util.FlxSave;
import funkin.util.FileUtil;
import funkin.input.Controls.Device; import funkin.input.Controls.Device;
import funkin.play.scoring.Scoring; import funkin.play.scoring.Scoring;
import funkin.play.scoring.Scoring.ScoringRank; import funkin.play.scoring.Scoring.ScoringRank;
import funkin.save.migrator.RawSaveData_v1_0_0; import funkin.save.migrator.RawSaveData_v1_0_0;
import funkin.save.migrator.SaveDataMigrator; import funkin.save.migrator.SaveDataMigrator;
import funkin.save.migrator.SaveDataMigrator;
import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle; import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle;
import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme; import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
import funkin.ui.debug.stageeditor.StageEditorState.StageEditorTheme; import funkin.ui.debug.stageeditor.StageEditorState.StageEditorTheme;
import funkin.util.FileUtil;
import funkin.util.SerializerUtil; import funkin.util.SerializerUtil;
import thx.semver.Version; import thx.semver.Version;
import thx.semver.Version;
@:nullSafety @:nullSafety
class Save class Save
{ {
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.6"; public static final SAVE_DATA_VERSION:thx.semver.Version = "2.1.0";
public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = "2.0.x"; public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = ">=2.1.0 <2.2.0";
// We load this version's saves from a new save path, to maintain SOME level of backwards compatibility. // We load this version's saves from a new save path, to maintain SOME level of backwards compatibility.
static final SAVE_PATH:String = 'FunkinCrew'; static final SAVE_PATH:String = 'FunkinCrew';
@ -965,32 +963,57 @@ class Save
FlxG.save.bind('$SAVE_NAME${slot}', SAVE_PATH); FlxG.save.bind('$SAVE_NAME${slot}', SAVE_PATH);
if (FlxG.save.isEmpty()) switch (FlxG.save.status)
{ {
trace('[SAVE] Save data is empty, checking for legacy save data...'); case EMPTY:
var legacySaveData = fetchLegacySaveData(); trace('[SAVE] Save data in slot ${slot} is empty, checking for legacy save data...');
if (legacySaveData != null) var legacySaveData = fetchLegacySaveData();
{ if (legacySaveData != null)
trace('[SAVE] Found legacy save data, converting...'); {
var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData); trace('[SAVE] Found legacy save data, converting...');
var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData);
FlxG.save.mergeData(gameSave.data, true);
return gameSave;
}
else
{
trace('[SAVE] No legacy save data found.');
var gameSave = new Save();
FlxG.save.mergeData(gameSave.data, true);
return gameSave;
}
case ERROR(_):
return handleSaveDataError(slot);
case BOUND(_, _):
trace('[SAVE] Loaded existing save data in slot ${slot}.');
var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
FlxG.save.mergeData(gameSave.data, true); FlxG.save.mergeData(gameSave.data, true);
return gameSave; return gameSave;
} }
else }
{
trace('[SAVE] No legacy save data found.'); /**
var gameSave = new Save(); * Call this when there is an error loading the save data in slot X.
FlxG.save.mergeData(gameSave.data, true); */
return gameSave; static function handleSaveDataError(slot:Int):Save
} {
var msg = 'There was an error loading your save data in slot ${slot}.';
msg += '\nPlease report this issue to the developers.';
lime.app.Application.current.window.alert(msg, "Save Data Failure");
// Don't touch that slot anymore.
// Instead, load the next available slot.
var nextSlot = slot + 1;
if (nextSlot < 1000)
{
return loadFromSlot(nextSlot);
} }
else else
{ {
trace('[SAVE] Found existing save data.'); throw "End of save data slots. Can't load any more.";
var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
FlxG.save.mergeData(gameSave.data, true);
return gameSave;
} }
} }
@ -1055,7 +1078,15 @@ class Save
{ {
var targetSaveData = new FlxSave(); var targetSaveData = new FlxSave();
targetSaveData.bind('$SAVE_NAME${slot}', SAVE_PATH); targetSaveData.bind('$SAVE_NAME${slot}', SAVE_PATH);
return !targetSaveData.isEmpty(); switch (targetSaveData.status)
{
case EMPTY:
return false;
case ERROR(_):
return false;
case BOUND(_, _):
return true;
}
} }
/** /**

View file

@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.1.0] - 2024-10-18
This version introduces changes to save data loading in order to improve compatibility with older versions.
### Changed
- `optionsStageEditor.theme` converted from an Enum to a String to fix save data compatibility issues.
- In the future, Enum values should not be used in order to prevent incompatibilities caused by introducing new types to the save data that older versions cannot parse.
- `optionsChartEditor.theme` converted from an Enum to a String to fix save data compatibility issues.
- `optionsChartEditor.chartEditorLiveInputStyle` converted from an Enum to a String to fix save data compatibility issues.
## [2.0.6] - 2024-10-11 ## [2.0.6] - 2024-10-11
### Added ### Added
- `optionsStageEditor` to `Save` for storing user preferences for the stage editor. - `optionsStageEditor` to `Save` for storing user preferences for the stage editor.

View file

@ -32,6 +32,10 @@ class SaveDataMigrator
var save:Save = new Save(saveDataWithDefaults); var save:Save = new Save(saveDataWithDefaults);
return save; return save;
} }
else if (VersionUtil.validateVersion(version, "2.0.x"))
{
return migrate_v2_0_0(inputData);
}
else else
{ {
var message:String = 'Error migrating save data, expected ${Save.SAVE_DATA_VERSION}.'; var message:String = 'Error migrating save data, expected ${Save.SAVE_DATA_VERSION}.';
@ -43,6 +47,20 @@ class SaveDataMigrator
} }
} }
static function migrate_v2_0_0(inputData:Dynamic):Save
{
// Import the structured data.
var saveDataWithDefaults:RawSaveData = cast thx.Objects.deepCombine(Save.getDefault(), inputData);
// Reset these values to valid ones.
saveDataWithDefaults.optionsChartEditor.chartEditorLiveInputStyle = funkin.ui.debug.charting.ChartEditorLiveInputStyle.None;
saveDataWithDefaults.optionsChartEditor.theme = funkin.ui.debug.charting.ChartEditorTheme.Light;
saveDataWithDefaults.optionsStageEditor.theme = funkin.ui.debug.stageeditor.StageEditorTheme.Light;
var save:Save = new Save(saveDataWithDefaults);
return save;
}
/** /**
* Migrate from 1.x to the latest version. * Migrate from 1.x to the latest version.
*/ */

View file

@ -0,0 +1,26 @@
package funkin.save.migrator;
// Internal enums used to ensure old save data can be parsed by the default Haxe unserializer.
// In the future, only primitive types and abstract enums should be used in save data!
@:native("funkin.ui.debug.stageeditor.StageEditorTheme")
enum StageEditorTheme
{
Light;
Dark;
}
@:native("funkin.ui.debug.charting.ChartEditorTheme")
enum ChartEditorTheme
{
Light;
Dark;
}
@:native("funkin.ui.debug.charting.ChartEditorLiveInputStyle")
enum ChartEditorLiveInputStyle
{
None;
NumberKeys;
WASDKeys;
}

View file

@ -5654,7 +5654,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
function handleHelpKeybinds():Void function handleHelpKeybinds():Void
{ {
// F1 = Open Help // F1 = Open Help
if (FlxG.keys.justPressed.F1 && !isHaxeUIDialogOpen) { if (FlxG.keys.justPressed.F1 && !isHaxeUIDialogOpen)
{
this.openUserGuideDialog(); this.openUserGuideDialog();
} }
} }
@ -6543,22 +6544,22 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
/** /**
* Available input modes for the chart editor state. Numbers/arrows/WASD available for other keybinds. * Available input modes for the chart editor state. Numbers/arrows/WASD available for other keybinds.
*/ */
enum ChartEditorLiveInputStyle enum abstract ChartEditorLiveInputStyle(String)
{ {
/** /**
* No hotkeys to place notes at the playbar. * No hotkeys to place notes at the playbar.
*/ */
None; var None;
/** /**
* 1/2/3/4 to place notes on opponent's side, 5/6/7/8 to place notes on player's side. * 1/2/3/4 to place notes on opponent's side, 5/6/7/8 to place notes on player's side.
*/ */
NumberKeys; var NumberKeys;
/** /**
* WASD to place notes on opponent's side, Arrow keys to place notes on player's side. * WASD to place notes on opponent's side, Arrow keys to place notes on player's side.
*/ */
WASDKeys; var WASDKeys;
} }
typedef ChartEditorParams = typedef ChartEditorParams =
@ -6577,15 +6578,15 @@ typedef ChartEditorParams =
/** /**
* Available themes for the chart editor state. * Available themes for the chart editor state.
*/ */
enum ChartEditorTheme enum abstract ChartEditorTheme(String)
{ {
/** /**
* The default theme for the chart editor. * The default theme for the chart editor.
*/ */
Light; var Light;
/** /**
* A theme which introduces darker colors. * A theme which introduces darker colors.
*/ */
Dark; var Dark;
} }

View file

@ -1458,17 +1458,17 @@ class StageEditorState extends UIState
/** /**
* Available themes for the stage editor state. * Available themes for the stage editor state.
*/ */
enum StageEditorTheme enum abstract StageEditorTheme(String)
{ {
/** /**
* The default theme for the stage editor. * The default theme for the stage editor.
*/ */
Light; var Light;
/** /**
* A theme which introduces stage colors. * A theme which introduces stage colors.
*/ */
Dark; var Dark;
} }
enum StageEditorDialogType enum StageEditorDialogType