Resolve metadata loading issues.

This commit is contained in:
EliteMasterEric 2023-09-25 23:24:18 -04:00
parent 42bb50882d
commit be4fd74d40
3 changed files with 88 additions and 56 deletions

View file

@ -47,7 +47,7 @@ class SongMetadata
@:jignored @:jignored
public var variation:String; public var variation:String;
public function new(songName:String, artist:String, variation:String = 'default') public function new(songName:String, artist:String, ?variation:String)
{ {
this.version = SongRegistry.SONG_METADATA_VERSION; this.version = SongRegistry.SONG_METADATA_VERSION;
this.songName = songName; this.songName = songName;
@ -64,7 +64,7 @@ class SongMetadata
this.playData.noteSkin = 'funkin'; this.playData.noteSkin = 'funkin';
this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY; this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY;
// Variation ID. // Variation ID.
this.variation = variation; this.variation = (variation == null) ? Constants.DEFAULT_VARIATION : variation;
} }
/** /**
@ -90,12 +90,13 @@ class SongMetadata
* Serialize this SongMetadata into a JSON string. * Serialize this SongMetadata into a JSON string.
* @return The JSON string. * @return The JSON string.
*/ */
public function serialize(?pretty:Bool = true):String public function serialize(pretty:Bool = true):String
{ {
var writer = new json2object.JsonWriter<SongMetadata>(); var writer = new json2object.JsonWriter<SongMetadata>();
var output = this.clone(); // I believe @:jignored should be iggnored by the writer?
output.variation = null; // Not sure how to make a field optional on the reader and ignored on the writer. // var output = this.clone();
return writer.write(output, pretty ? ' ' : null); // output.variation = null; // Not sure how to make a field optional on the reader and ignored on the writer.
return writer.write(this, pretty ? ' ' : null);
} }
/** /**
@ -230,7 +231,7 @@ class SongMusicData
* Defaults to `default` or `''`. Populated later. * Defaults to `default` or `''`. Populated later.
*/ */
@:jignored @:jignored
public var variation:String = 'default'; public var variation:String = Constants.DEFAULT_VARIATION;
public function new(songName:String, artist:String, variation:String = 'default') public function new(songName:String, artist:String, variation:String = 'default')
{ {
@ -243,7 +244,7 @@ class SongMusicData
this.looped = false; this.looped = false;
this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY; this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY;
// Variation ID. // Variation ID.
this.variation = variation; this.variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
} }
public function clone(?newVariation:String = null):SongMusicData public function clone(?newVariation:String = null):SongMusicData
@ -374,6 +375,9 @@ class SongChartData
@:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY) @:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY)
public var generatedBy:String; public var generatedBy:String;
@:jignored
public var variation:String;
public function new(scrollSpeed:Map<String, Float>, events:Array<SongEventData>, notes:Map<String, Array<SongNoteData>>) public function new(scrollSpeed:Map<String, Float>, events:Array<SongEventData>, notes:Map<String, Array<SongNoteData>>)
{ {
this.version = SongRegistry.SONG_CHART_DATA_VERSION; this.version = SongRegistry.SONG_CHART_DATA_VERSION;
@ -418,7 +422,7 @@ class SongChartData
/** /**
* Convert this SongChartData into a JSON string. * Convert this SongChartData into a JSON string.
*/ */
public function serialize(?pretty:Bool = true):String public function serialize(pretty:Bool = true):String
{ {
var writer = new json2object.JsonWriter<SongChartData>(); var writer = new json2object.JsonWriter<SongChartData>();
return writer.write(this, pretty ? ' ' : null); return writer.write(this, pretty ? ' ' : null);

View file

@ -120,10 +120,9 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return parseEntryMetadataRaw(contents); return parseEntryMetadataRaw(contents);
} }
public function parseEntryMetadata(id:String, variation:String = ""):Null<SongMetadata> public function parseEntryMetadata(id:String, ?variation:String):Null<SongMetadata>
{ {
// JsonParser does not take type parameters, variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<SongMetadata>(); var parser = new json2object.JsonParser<SongMetadata>();
switch (loadEntryMetadataFile(id, variation)) switch (loadEntryMetadataFile(id, variation))
@ -139,11 +138,13 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
printErrors(parser.errors, id); printErrors(parser.errors, id);
return null; return null;
} }
return parser.value; return cleanMetadata(parser.value, variation);
} }
public function parseEntryMetadataRaw(contents:String, ?fileName:String = 'raw'):Null<SongMetadata> public function parseEntryMetadataRaw(contents:String, ?fileName:String = 'raw', ?variation:String):Null<SongMetadata>
{ {
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser<SongMetadata>(); var parser = new json2object.JsonParser<SongMetadata>();
parser.fromJson(contents, fileName); parser.fromJson(contents, fileName);
@ -152,23 +153,25 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
printErrors(parser.errors, fileName); printErrors(parser.errors, fileName);
return null; return null;
} }
return parser.value; return cleanMetadata(parser.value, variation);
} }
public function parseEntryMetadataWithMigration(id:String, variation:String = '', version:thx.semver.Version):Null<SongMetadata> public function parseEntryMetadataWithMigration(id:String, ?variation:String, version:thx.semver.Version):Null<SongMetadata>
{ {
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
// If a version rule is not specified, do not check against it. // If a version rule is not specified, do not check against it.
if (SONG_METADATA_VERSION_RULE == null || VersionUtil.validateVersion(version, SONG_METADATA_VERSION_RULE)) if (SONG_METADATA_VERSION_RULE == null || VersionUtil.validateVersion(version, SONG_METADATA_VERSION_RULE))
{ {
return parseEntryMetadata(id); return parseEntryMetadata(id, variation);
} }
else if (VersionUtil.validateVersion(version, "2.0.x")) else if (VersionUtil.validateVersion(version, "2.0.x"))
{ {
return parseEntryMetadata_v2_0_0(id); return parseEntryMetadata_v2_0_0(id, variation);
} }
else else
{ {
throw '[${registryId}] Metadata entry ${id}:${variation == '' ? 'default' : variation} does not support migration to version ${SONG_METADATA_VERSION_RULE}.'; throw '[${registryId}] Metadata entry ${id}:${variation} does not support migration to version ${SONG_METADATA_VERSION_RULE}.';
} }
} }
@ -191,8 +194,8 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
function parseEntryMetadata_v2_0_0(id:String, variation:String = ""):Null<SongMetadata> function parseEntryMetadata_v2_0_0(id:String, variation:String = ""):Null<SongMetadata>
{ {
// JsonParser does not take type parameters, variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<SongMetadata_v2_0_0>(); var parser = new json2object.JsonParser<SongMetadata_v2_0_0>();
switch (loadEntryMetadataFile(id)) switch (loadEntryMetadataFile(id))
{ {
@ -222,10 +225,9 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return parser.value.migrate(); return parser.value.migrate();
} }
public function parseMusicData(id:String, variation:String = ""):Null<SongMusicData> public function parseMusicData(id:String, ?variation:String):Null<SongMusicData>
{ {
// JsonParser does not take type parameters, variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<SongMusicData>(); var parser = new json2object.JsonParser<SongMusicData>();
switch (loadMusicDataFile(id, variation)) switch (loadMusicDataFile(id, variation))
@ -257,8 +259,10 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return parser.value; return parser.value;
} }
public function parseMusicDataWithMigration(id:String, variation:String = '', version:thx.semver.Version):Null<SongMusicData> public function parseMusicDataWithMigration(id:String, ?variation:String, version:thx.semver.Version):Null<SongMusicData>
{ {
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
// If a version rule is not specified, do not check against it. // If a version rule is not specified, do not check against it.
if (SONG_MUSIC_DATA_VERSION_RULE == null || VersionUtil.validateVersion(version, SONG_MUSIC_DATA_VERSION_RULE)) if (SONG_MUSIC_DATA_VERSION_RULE == null || VersionUtil.validateVersion(version, SONG_MUSIC_DATA_VERSION_RULE))
{ {
@ -266,7 +270,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
} }
else else
{ {
throw '[${registryId}] Chart entry ${id}:${variation == '' ? 'default' : variation} does not support migration to version ${SONG_CHART_DATA_VERSION_RULE}.'; throw '[${registryId}] Chart entry ${id}:${variation} does not support migration to version ${SONG_CHART_DATA_VERSION_RULE}.';
} }
} }
@ -283,10 +287,10 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
} }
} }
public function parseEntryChartData(id:String, variation:String = ''):Null<SongChartData> public function parseEntryChartData(id:String, ?variation:String):Null<SongChartData>
{ {
// JsonParser does not take type parameters, variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<SongChartData>(); var parser = new json2object.JsonParser<SongChartData>();
switch (loadEntryChartFile(id, variation)) switch (loadEntryChartFile(id, variation))
@ -302,11 +306,13 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
printErrors(parser.errors, id); printErrors(parser.errors, id);
return null; return null;
} }
return parser.value; return cleanChartData(parser.value, variation);
} }
public function parseEntryChartDataRaw(contents:String, ?fileName:String = 'raw'):Null<SongChartData> public function parseEntryChartDataRaw(contents:String, ?fileName:String = 'raw', ?variation:String):Null<SongChartData>
{ {
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser<SongChartData>(); var parser = new json2object.JsonParser<SongChartData>();
parser.fromJson(contents, fileName); parser.fromJson(contents, fileName);
@ -315,11 +321,13 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
printErrors(parser.errors, fileName); printErrors(parser.errors, fileName);
return null; return null;
} }
return parser.value; return cleanChartData(parser.value, variation);
} }
public function parseEntryChartDataWithMigration(id:String, variation:String = '', version:thx.semver.Version):Null<SongChartData> public function parseEntryChartDataWithMigration(id:String, ?variation:String, version:thx.semver.Version):Null<SongChartData>
{ {
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
// If a version rule is not specified, do not check against it. // If a version rule is not specified, do not check against it.
if (SONG_CHART_DATA_VERSION_RULE == null || VersionUtil.validateVersion(version, SONG_CHART_DATA_VERSION_RULE)) if (SONG_CHART_DATA_VERSION_RULE == null || VersionUtil.validateVersion(version, SONG_CHART_DATA_VERSION_RULE))
{ {
@ -327,7 +335,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
} }
else else
{ {
throw '[${registryId}] Chart entry ${id}:${variation == '' ? 'default' : variation} does not support migration to version ${SONG_CHART_DATA_VERSION_RULE}.'; throw '[${registryId}] Chart entry ${id}:${variation} does not support migration to version ${SONG_CHART_DATA_VERSION_RULE}.';
} }
} }
@ -354,9 +362,10 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return ScriptedSong.listScriptClasses(); return ScriptedSong.listScriptClasses();
} }
function loadEntryMetadataFile(id:String, variation:String = ''):Null<BaseRegistry.JsonFile> function loadEntryMetadataFile(id:String, ?variation:String):Null<BaseRegistry.JsonFile>
{ {
var entryFilePath:String = Paths.json('$dataFilePath/$id/$id${variation == '' ? '' : '-$variation'}-metadata'); variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var entryFilePath:String = Paths.json('$dataFilePath/$id/$id-metadata${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}');
if (!openfl.Assets.exists(entryFilePath)) return null; if (!openfl.Assets.exists(entryFilePath)) return null;
var rawJson:Null<String> = openfl.Assets.getText(entryFilePath); var rawJson:Null<String> = openfl.Assets.getText(entryFilePath);
if (rawJson == null) return null; if (rawJson == null) return null;
@ -364,9 +373,10 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return {fileName: entryFilePath, contents: rawJson}; return {fileName: entryFilePath, contents: rawJson};
} }
function loadMusicDataFile(id:String, variation:String = ''):Null<BaseRegistry.JsonFile> function loadMusicDataFile(id:String, ?variation:String):Null<BaseRegistry.JsonFile>
{ {
var entryFilePath:String = Paths.file('music/$id/$id${variation == '' ? '' : '-$variation'}-metadata.json'); variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var entryFilePath:String = Paths.file('music/$id/$id-metadata${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.json');
if (!openfl.Assets.exists(entryFilePath)) return null; if (!openfl.Assets.exists(entryFilePath)) return null;
var rawJson:String = openfl.Assets.getText(entryFilePath); var rawJson:String = openfl.Assets.getText(entryFilePath);
if (rawJson == null) return null; if (rawJson == null) return null;
@ -374,9 +384,10 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return {fileName: entryFilePath, contents: rawJson}; return {fileName: entryFilePath, contents: rawJson};
} }
function loadEntryChartFile(id:String, variation:String = ''):Null<BaseRegistry.JsonFile> function loadEntryChartFile(id:String, ?variation:String):Null<BaseRegistry.JsonFile>
{ {
var entryFilePath:String = Paths.json('$dataFilePath/$id/$id${variation == '' ? '' : '-$variation'}-chart'); variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var entryFilePath:String = Paths.json('$dataFilePath/$id/$id-chart${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}');
if (!openfl.Assets.exists(entryFilePath)) return null; if (!openfl.Assets.exists(entryFilePath)) return null;
var rawJson:String = openfl.Assets.getText(entryFilePath); var rawJson:String = openfl.Assets.getText(entryFilePath);
if (rawJson == null) return null; if (rawJson == null) return null;
@ -384,20 +395,36 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return {fileName: entryFilePath, contents: rawJson}; return {fileName: entryFilePath, contents: rawJson};
} }
public function fetchEntryMetadataVersion(id:String, variation:String = ''):Null<thx.semver.Version> public function fetchEntryMetadataVersion(id:String, ?variation:String):Null<thx.semver.Version>
{ {
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var entryStr:Null<String> = loadEntryMetadataFile(id, variation)?.contents; var entryStr:Null<String> = loadEntryMetadataFile(id, variation)?.contents;
var entryVersion:thx.semver.Version = VersionUtil.getVersionFromJSON(entryStr); var entryVersion:thx.semver.Version = VersionUtil.getVersionFromJSON(entryStr);
return entryVersion; return entryVersion;
} }
public function fetchEntryChartVersion(id:String, variation:String = ''):Null<thx.semver.Version> public function fetchEntryChartVersion(id:String, ?variation:String):Null<thx.semver.Version>
{ {
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var entryStr:Null<String> = loadEntryChartFile(id, variation)?.contents; var entryStr:Null<String> = loadEntryChartFile(id, variation)?.contents;
var entryVersion:thx.semver.Version = VersionUtil.getVersionFromJSON(entryStr); var entryVersion:thx.semver.Version = VersionUtil.getVersionFromJSON(entryStr);
return entryVersion; return entryVersion;
} }
function cleanMetadata(metadata:SongMetadata, variation:String):SongMetadata
{
metadata.variation = variation;
return metadata;
}
function cleanChartData(chartData:SongChartData, variation:String):SongChartData
{
chartData.variation = variation;
return chartData;
}
/** /**
* A list of all the story weeks from the base game, in order. * A list of all the story weeks from the base game, in order.
* TODO: Should this be hardcoded? * TODO: Should this be hardcoded?

View file

@ -92,6 +92,15 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
_metadata = _data == null ? [] : [_data]; _metadata = _data == null ? [] : [_data];
variations.clear();
variations.push(Constants.DEFAULT_VARIATION);
if (_data != null && _data.playData != null)
{
for (vari in _data.playData.songVariations)
variations.push(vari);
}
for (meta in fetchVariationMetadata(id)) for (meta in fetchVariationMetadata(id))
_metadata.push(meta); _metadata.push(meta);
@ -101,15 +110,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
return; return;
} }
variations.clear(); populateDifficulties();
variations.push('default');
if (_data != null && _data.playData != null)
{
for (vari in _data.playData.songVariations)
variations.push(vari);
populateFromMetadata();
}
} }
@:allow(funkin.play.song.Song) @:allow(funkin.play.song.Song)
@ -128,7 +129,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
result.difficultyIds.clear(); result.difficultyIds.clear();
result.populateFromMetadata(); result.populateDifficulties();
for (variation => chartData in charts) for (variation => chartData in charts)
result.applyChartData(chartData, variation); result.applyChartData(chartData, variation);
@ -144,10 +145,10 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
} }
/** /**
* Populate the song data from the provided metadata, * Populate the difficulty data from the provided metadata.
* including data from individual difficulties. Does not load chart data. * Does not load chart data (that is triggered later when we want to play the song).
*/ */
function populateFromMetadata():Void function populateDifficulties():Void
{ {
if (_metadata == null || _metadata.length == 0) return; if (_metadata == null || _metadata.length == 0) return;
@ -314,7 +315,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
trace('Fetching song metadata for $id'); trace('Fetching song metadata for $id');
var version:Null<thx.semver.Version> = SongRegistry.instance.fetchEntryMetadataVersion(id); var version:Null<thx.semver.Version> = SongRegistry.instance.fetchEntryMetadataVersion(id);
if (version == null) return null; if (version == null) return null;
return SongRegistry.instance.parseEntryMetadataWithMigration(id, '', version); return SongRegistry.instance.parseEntryMetadataWithMigration(id, Constants.DEFAULT_VARIATION, version);
} }
function fetchVariationMetadata(id:String):Array<SongMetadata> function fetchVariationMetadata(id:String):Array<SongMetadata>