mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-28 02:25:51 -05:00
Update save data format and error handling.
This commit is contained in:
parent
24ad7f4a39
commit
dff4135fc9
7 changed files with 122 additions and 38 deletions
2
hmm.json
2
hmm.json
|
@ -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"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
26
source/funkin/save/migrator/SaveData_v2_0_0.hx
Normal file
26
source/funkin/save/migrator/SaveData_v2_0_0.hx
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue