mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-11-25 21:55:55 +00:00
Merge remote-tracking branch 'secret/bugfix/html5-save-data' into loadingbar-fix
This commit is contained in:
commit
58415d49c9
|
|
@ -128,6 +128,7 @@
|
||||||
|
|
||||||
|
|
||||||
<haxelib name="json2object" /> <!-- JSON parsing -->
|
<haxelib name="json2object" /> <!-- JSON parsing -->
|
||||||
|
<haxelib name="thx.core" /> <!-- General utility library, "the lodash of Haxe" -->
|
||||||
<haxelib name="thx.semver" /> <!-- Version string handling -->
|
<haxelib name="thx.semver" /> <!-- Version string handling -->
|
||||||
|
|
||||||
<haxelib name="hxcpp-debug-server" if="desktop debug" /> <!-- VSCode debug support -->
|
<haxelib name="hxcpp-debug-server" if="desktop debug" /> <!-- VSCode debug support -->
|
||||||
|
|
|
||||||
2
hmm.json
2
hmm.json
|
|
@ -153,7 +153,7 @@
|
||||||
"name": "polymod",
|
"name": "polymod",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "8553b800965f225bb14c7ab8f04bfa9cdec362ac",
|
"ref": "bfbe30d81601b3543d80dce580108ad6b7e182c7",
|
||||||
"url": "https://github.com/larsiusprime/polymod"
|
"url": "https://github.com/larsiusprime/polymod"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import flixel.system.debug.watch.Tracker;
|
||||||
// These are great.
|
// These are great.
|
||||||
using Lambda;
|
using Lambda;
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
using thx.Arrays;
|
||||||
using funkin.util.tools.ArraySortTools;
|
using funkin.util.tools.ArraySortTools;
|
||||||
using funkin.util.tools.ArrayTools;
|
using funkin.util.tools.ArrayTools;
|
||||||
using funkin.util.tools.FloatTools;
|
using funkin.util.tools.FloatTools;
|
||||||
|
|
|
||||||
|
|
@ -997,7 +997,7 @@ class Controls extends FlxActionSet
|
||||||
for (control in Control.createAll())
|
for (control in Control.createAll())
|
||||||
{
|
{
|
||||||
var inputs:Array<Int> = Reflect.field(data, control.getName());
|
var inputs:Array<Int> = Reflect.field(data, control.getName());
|
||||||
inputs = inputs.unique();
|
inputs = inputs.distinct();
|
||||||
if (inputs != null)
|
if (inputs != null)
|
||||||
{
|
{
|
||||||
if (inputs.length == 0) {
|
if (inputs.length == 0) {
|
||||||
|
|
@ -1050,7 +1050,7 @@ class Controls extends FlxActionSet
|
||||||
if (inputs.length == 0) {
|
if (inputs.length == 0) {
|
||||||
inputs = [FlxKey.NONE];
|
inputs = [FlxKey.NONE];
|
||||||
} else {
|
} else {
|
||||||
inputs = inputs.unique();
|
inputs = inputs.distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
Reflect.setField(data, control.getName(), inputs);
|
Reflect.setField(data, control.getName(), inputs);
|
||||||
|
|
|
||||||
|
|
@ -439,12 +439,16 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
// so we have to map it to the actual difficulty names.
|
// so we have to map it to the actual difficulty names.
|
||||||
// We also filter out difficulties that don't match the variation or that don't exist.
|
// We also filter out difficulties that don't match the variation or that don't exist.
|
||||||
|
|
||||||
var diffFiltered:Array<String> = difficulties.keys().array().map(function(diffId:String):Null<String> {
|
var diffFiltered:Array<String> = difficulties.keys()
|
||||||
var difficulty:Null<SongDifficulty> = difficulties.get(diffId);
|
.array()
|
||||||
if (difficulty == null) return null;
|
.map(function(diffId:String):Null<String> {
|
||||||
if (variationIds.length > 0 && !variationIds.contains(difficulty.variation)) return null;
|
var difficulty:Null<SongDifficulty> = difficulties.get(diffId);
|
||||||
return difficulty.difficulty;
|
if (difficulty == null) return null;
|
||||||
}).nonNull().unique();
|
if (variationIds.length > 0 && !variationIds.contains(difficulty.variation)) return null;
|
||||||
|
return difficulty.difficulty;
|
||||||
|
})
|
||||||
|
.filterNull()
|
||||||
|
.distinct();
|
||||||
|
|
||||||
diffFiltered = diffFiltered.filter(function(diffId:String):Bool {
|
diffFiltered = diffFiltered.filter(function(diffId:String):Bool {
|
||||||
if (showHidden) return true;
|
if (showHidden) return true;
|
||||||
|
|
|
||||||
|
|
@ -746,6 +746,7 @@ class Save
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An anonymous structure containingg all the user's save data.
|
* An anonymous structure containingg all the user's save data.
|
||||||
|
* Isn't stored with JSON, stored with some sort of Haxe built-in serialization?
|
||||||
*/
|
*/
|
||||||
typedef RawSaveData =
|
typedef RawSaveData =
|
||||||
{
|
{
|
||||||
|
|
@ -756,8 +757,6 @@ typedef RawSaveData =
|
||||||
/**
|
/**
|
||||||
* A semantic versioning string for the save data format.
|
* A semantic versioning string for the save data format.
|
||||||
*/
|
*/
|
||||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
|
||||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
|
||||||
var version:Version;
|
var version:Version;
|
||||||
|
|
||||||
var api:SaveApiData;
|
var api:SaveApiData;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package funkin.save.migrator;
|
||||||
import funkin.save.Save;
|
import funkin.save.Save;
|
||||||
import funkin.save.migrator.RawSaveData_v1_0_0;
|
import funkin.save.migrator.RawSaveData_v1_0_0;
|
||||||
import thx.semver.Version;
|
import thx.semver.Version;
|
||||||
import funkin.util.StructureUtil;
|
|
||||||
import funkin.util.VersionUtil;
|
import funkin.util.VersionUtil;
|
||||||
|
|
||||||
@:nullSafety
|
@:nullSafety
|
||||||
|
|
@ -24,16 +23,20 @@ class SaveDataMigrator
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Sometimes the Haxe serializer has issues with the version so we fix it here.
|
||||||
|
version = VersionUtil.repairVersion(version);
|
||||||
if (VersionUtil.validateVersion(version, Save.SAVE_DATA_VERSION_RULE))
|
if (VersionUtil.validateVersion(version, Save.SAVE_DATA_VERSION_RULE))
|
||||||
{
|
{
|
||||||
// Simply import the structured data.
|
// Import the structured data.
|
||||||
var save:Save = new Save(StructureUtil.deepMerge(Save.getDefault(), inputData));
|
var saveDataWithDefaults:RawSaveData = cast thx.Objects.deepCombine(Save.getDefault(), inputData);
|
||||||
|
var save:Save = new Save(saveDataWithDefaults);
|
||||||
return save;
|
return save;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('[SAVE] Invalid save data version! Returning blank data.');
|
var message:String = 'Error migrating save data, expected ${Save.SAVE_DATA_VERSION}.';
|
||||||
trace(inputData);
|
lime.app.Application.current.window.alert(message, "Save Data Failure");
|
||||||
|
trace('[SAVE] ' + message);
|
||||||
return new Save(Save.getDefault());
|
return new Save(Save.getDefault());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,10 @@ class LevelProp extends Bopper
|
||||||
// Only reset the prop if the asset path has changed.
|
// Only reset the prop if the asset path has changed.
|
||||||
if (propData == null || value?.assetPath != propData?.assetPath)
|
if (propData == null || value?.assetPath != propData?.assetPath)
|
||||||
{
|
{
|
||||||
this.visible = (value != null);
|
|
||||||
this.propData = value;
|
|
||||||
danceEvery = this.propData?.danceEvery ?? 0;
|
|
||||||
applyData();
|
applyData();
|
||||||
}
|
}
|
||||||
|
this.visible = (value != null);
|
||||||
|
danceEvery = this.propData?.danceEvery ?? 0;
|
||||||
|
|
||||||
return this.propData;
|
return this.propData;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
package funkin.util;
|
|
||||||
|
|
||||||
import funkin.util.tools.MapTools;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function toMap(a:Dynamic):haxe.ds.Map<String, Dynamic>
|
|
||||||
{
|
|
||||||
var result:haxe.ds.Map<String, Dynamic> = [];
|
|
||||||
|
|
||||||
for (field in Reflect.fields(a))
|
|
||||||
{
|
|
||||||
result.set(field, Reflect.field(a, field));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function isMap(a:Dynamic):Bool
|
|
||||||
{
|
|
||||||
return Std.isOfType(a, haxe.Constraints.IMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function isObject(a:Dynamic):Bool
|
|
||||||
{
|
|
||||||
switch (Type.typeof(a))
|
|
||||||
{
|
|
||||||
case TObject:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function isPrimitive(a:Dynamic):Bool
|
|
||||||
{
|
|
||||||
switch (Type.typeof(a))
|
|
||||||
{
|
|
||||||
case TInt | TFloat | TBool:
|
|
||||||
return true;
|
|
||||||
case TClass(c):
|
|
||||||
return false;
|
|
||||||
case TEnum(e):
|
|
||||||
return false;
|
|
||||||
case TObject:
|
|
||||||
return false;
|
|
||||||
case TFunction:
|
|
||||||
return false;
|
|
||||||
case TNull:
|
|
||||||
return true;
|
|
||||||
case TUnknown:
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 (isPrimitive(a) && isPrimitive(b)) return b;
|
|
||||||
if (isMap(b))
|
|
||||||
{
|
|
||||||
if (isMap(a))
|
|
||||||
{
|
|
||||||
return MapTools.merge(a, b);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return StructureUtil.toMap(a).merge(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!Reflect.isObject(a) || !Reflect.isObject(b)) return b;
|
|
||||||
if (Std.isOfType(b, haxe.ds.StringMap))
|
|
||||||
{
|
|
||||||
if (Std.isOfType(a, haxe.ds.StringMap))
|
|
||||||
{
|
|
||||||
return MapTools.merge(a, b);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return StructureUtil.toMap(a).merge(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -32,6 +32,25 @@ class VersionUtil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function repairVersion(version:thx.semver.Version):thx.semver.Version
|
||||||
|
{
|
||||||
|
var versionData:thx.semver.Version.SemVer = version;
|
||||||
|
|
||||||
|
if (thx.Types.isAnonymousObject(versionData.version))
|
||||||
|
{
|
||||||
|
// This is bad! versionData.version should be an array!
|
||||||
|
versionData.version = [versionData.version[0], versionData.version[1], versionData.version[2]];
|
||||||
|
|
||||||
|
var fixedVersion:thx.semver.Version = versionData;
|
||||||
|
return fixedVersion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No need for repair.
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that a given verison number satisisfies a given version rule.
|
* Checks that a given verison number satisisfies a given version rule.
|
||||||
* Version rule can be complex, e.g. "1.0.x" or ">=1.0.0,<1.1.0", or anything NPM supports.
|
* Version rule can be complex, e.g. "1.0.x" or ">=1.0.0,<1.1.0", or anything NPM supports.
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class InlineMacro
|
||||||
var fields:Array<haxe.macro.Expr.Field> = haxe.macro.Context.getBuildFields();
|
var fields:Array<haxe.macro.Expr.Field> = haxe.macro.Context.getBuildFields();
|
||||||
|
|
||||||
// Find the field with the given name.
|
// Find the field with the given name.
|
||||||
var targetField:Null<haxe.macro.Expr.Field> = fields.find(function(f) return f.name == field
|
var targetField:Null<haxe.macro.Expr.Field> = thx.Arrays.find(fields, function(f) return f.name == field
|
||||||
&& (MacroUtil.isFieldStatic(f) == isStatic));
|
&& (MacroUtil.isFieldStatic(f) == isStatic));
|
||||||
|
|
||||||
// If the field was not found, throw an error.
|
// If the field was not found, throw an error.
|
||||||
|
|
|
||||||
|
|
@ -5,72 +5,6 @@ package funkin.util.tools;
|
||||||
*/
|
*/
|
||||||
class ArrayTools
|
class ArrayTools
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Returns a copy of the array with all duplicate elements removed.
|
|
||||||
* @param array The array to remove duplicates from.
|
|
||||||
* @return A copy of the array with all duplicate elements removed.
|
|
||||||
*/
|
|
||||||
public static function unique<T>(array:Array<T>):Array<T>
|
|
||||||
{
|
|
||||||
var result:Array<T> = [];
|
|
||||||
for (element in array)
|
|
||||||
{
|
|
||||||
if (!result.contains(element))
|
|
||||||
{
|
|
||||||
result.push(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a copy of the array with all `null` elements removed.
|
|
||||||
* @param array The array to remove `null` elements from.
|
|
||||||
* @return A copy of the array with all `null` elements removed.
|
|
||||||
*/
|
|
||||||
public static function nonNull<T>(array:Array<Null<T>>):Array<T>
|
|
||||||
{
|
|
||||||
var result:Array<T> = [];
|
|
||||||
for (element in array)
|
|
||||||
{
|
|
||||||
if (element != null)
|
|
||||||
{
|
|
||||||
result.push(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the first element of the array that satisfies the predicate, or null if none do.
|
|
||||||
* @param input The array to search
|
|
||||||
* @param predicate The predicate to call
|
|
||||||
* @return The result
|
|
||||||
*/
|
|
||||||
public static function find<T>(input:Array<T>, predicate:T->Bool):Null<T>
|
|
||||||
{
|
|
||||||
for (element in input)
|
|
||||||
{
|
|
||||||
if (predicate(element)) return element;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the index of the first element of the array that satisfies the predicate, or `-1` if none do.
|
|
||||||
* @param input The array to search
|
|
||||||
* @param predicate The predicate to call
|
|
||||||
* @return The index of the result
|
|
||||||
*/
|
|
||||||
public static function findIndex<T>(input:Array<T>, predicate:T->Bool):Int
|
|
||||||
{
|
|
||||||
for (index in 0...input.length)
|
|
||||||
{
|
|
||||||
if (predicate(input[index])) return index;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Push an element to the array if it is not already present.
|
* Push an element to the array if it is not already present.
|
||||||
* @param input The array to push to
|
* @param input The array to push to
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue