Fixed an issue with save data not loading defaults properly.

This commit is contained in:
EliteMasterEric 2024-04-03 01:01:58 -04:00
parent 2b4bf42ac4
commit f7141e7096
4 changed files with 68 additions and 6 deletions

View file

@ -14,7 +14,7 @@ import thx.semver.Version;
class Save
{
// Version 2.0.2 adds attributes to `optionsChartEditor`, that should return default values if they are null.
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.2";
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.3";
public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = "2.0.x";
// We load this version's saves from a new save path, to maintain SOME level of backwards compatibility.
@ -650,9 +650,9 @@ class Save
if (legacySaveData != null)
{
trace('[SAVE] Found legacy save data, converting...');
var gameSave = SaveDataMigrator.migrate(legacySaveData);
var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData);
@:privateAccess
FlxG.save.mergeData(gameSave.data);
FlxG.save.mergeData(gameSave.data, true);
}
else
{
@ -664,7 +664,7 @@ class Save
trace('[SAVE] Loaded save data.');
@:privateAccess
var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
FlxG.save.mergeData(gameSave.data);
FlxG.save.mergeData(gameSave.data, true);
}
}

View file

@ -3,6 +3,7 @@ package funkin.save.migrator;
import funkin.save.Save;
import funkin.save.migrator.RawSaveData_v1_0_0;
import thx.semver.Version;
import funkin.util.StructureUtil;
import funkin.util.VersionUtil;
@:nullSafety
@ -26,7 +27,7 @@ class SaveDataMigrator
if (VersionUtil.validateVersion(version, Save.SAVE_DATA_VERSION_RULE))
{
// Simply import the structured data.
var save:Save = new Save(inputData);
var save:Save = new Save(StructureUtil.deepMerge(Save.getDefault(), inputData));
return save;
}
else

View file

@ -962,7 +962,7 @@ class FreeplayState extends MusicBeatSubState
public override function destroy():Void
{
super.destroy();
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected]?.songData;
var daSong:Null<FreeplaySongData> = currentFilteredSongs[curSelected];
if (daSong != null)
{
clearDaCache(daSong.songName);

View file

@ -0,0 +1,61 @@
package funkin.util;
import haxe.DynamicAccess;
/**
* Utilities for working with anonymous structures.
*/
class StructureUtil
{
/**
* Merge two structures, with the second overwriting the first.
* Performs a SHALLOW clone, where child structures are not merged.
* @param a The base structure.
* @param b The new structure.
* @return The merged structure.
*/
public static function merge(a:Dynamic, b:Dynamic):Dynamic
{
var result:DynamicAccess<Dynamic> = Reflect.copy(a);
for (field in Reflect.fields(b))
{
result.set(field, Reflect.field(b, field));
}
return result;
}
/**
* Merge two structures, with the second overwriting the first.
* Performs a DEEP clone, where child structures are also merged recursively.
* @param a The base structure.
* @param b The new structure.
* @return The merged structure.
*/
public static function deepMerge(a:Dynamic, b:Dynamic):Dynamic
{
if (a == null) return b;
if (b == null) return null;
if (!Reflect.isObject(a) || !Reflect.isObject(b)) return b;
var result:DynamicAccess<Dynamic> = Reflect.copy(a);
for (field in Reflect.fields(b))
{
if (Reflect.isObject(b))
{
// Note that isObject also returns true for class instances,
// but we just assume that's not a problem here.
result.set(field, deepMerge(Reflect.field(result, field), Reflect.field(b, field)));
}
else
{
// If we're here, b[field] is a primitive.
result.set(field, Reflect.field(b, field));
}
}
return result;
}
}