This commit is contained in:
lemz 2024-11-08 23:45:23 +00:00 committed by GitHub
commit 5479b51577
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 401 additions and 305 deletions

View file

@ -3,6 +3,7 @@ package funkin.data.dialogue.conversation;
import funkin.play.cutscene.dialogue.Conversation; import funkin.play.cutscene.dialogue.Conversation;
import funkin.play.cutscene.dialogue.ScriptedConversation; import funkin.play.cutscene.dialogue.ScriptedConversation;
@:build(funkin.util.macro.RegistryMacro.buildRegistry())
class ConversationRegistry extends BaseRegistry<Conversation, ConversationData> class ConversationRegistry extends BaseRegistry<Conversation, ConversationData>
{ {
/** /**
@ -14,74 +15,8 @@ class ConversationRegistry extends BaseRegistry<Conversation, ConversationData>
public static final CONVERSATION_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; public static final CONVERSATION_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
public static var instance(get, never):ConversationRegistry;
static var _instance:Null<ConversationRegistry> = null;
static function get_instance():ConversationRegistry
{
if (_instance == null) _instance = new ConversationRegistry();
return _instance;
}
public function new() public function new()
{ {
super('CONVERSATION', 'dialogue/conversations', CONVERSATION_DATA_VERSION_RULE); super('CONVERSATION', 'dialogue/conversations', CONVERSATION_DATA_VERSION_RULE);
} }
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<ConversationData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<ConversationData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<ConversationData>
{
var parser = new json2object.JsonParser<ConversationData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):Conversation
{
return ScriptedConversation.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedConversation.listScriptClasses();
}
} }

View file

@ -5,6 +5,7 @@ import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.freeplay.charselect.ScriptedPlayableCharacter; import funkin.ui.freeplay.charselect.ScriptedPlayableCharacter;
import funkin.save.Save; import funkin.save.Save;
@:build(funkin.util.macro.RegistryMacro.buildRegistry())
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData> class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
{ {
/** /**
@ -16,15 +17,6 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
public static final PLAYER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; public static final PLAYER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
public static var instance(get, never):PlayerRegistry;
static var _instance:Null<PlayerRegistry> = null;
static function get_instance():PlayerRegistry
{
if (_instance == null) _instance = new PlayerRegistry();
return _instance;
}
/** /**
* A mapping between stage character IDs and Freeplay playable character IDs. * A mapping between stage character IDs and Freeplay playable character IDs.
*/ */
@ -131,63 +123,6 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
return ownedCharacterIds.exists(characterId); return ownedCharacterIds.exists(characterId);
} }
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<PlayerData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<PlayerData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<PlayerData>
{
var parser = new json2object.JsonParser<PlayerData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):PlayableCharacter
{
return ScriptedPlayableCharacter.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedPlayableCharacter.listScriptClasses();
}
/** /**
* A list of all the playable characters from the base game, in order. * A list of all the playable characters from the base game, in order.
*/ */

View file

@ -4,6 +4,7 @@ import funkin.play.notes.notestyle.NoteStyle;
import funkin.play.notes.notestyle.ScriptedNoteStyle; import funkin.play.notes.notestyle.ScriptedNoteStyle;
import funkin.data.notestyle.NoteStyleData; import funkin.data.notestyle.NoteStyleData;
@:build(funkin.util.macro.RegistryMacro.buildRegistry())
class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData> class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData>
{ {
/** /**
@ -15,15 +16,6 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData>
public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.1.x"; public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.1.x";
public static var instance(get, never):NoteStyleRegistry;
static var _instance:Null<NoteStyleRegistry> = null;
static function get_instance():NoteStyleRegistry
{
if (_instance == null) _instance = new NoteStyleRegistry();
return _instance;
}
public function new() public function new()
{ {
super('NOTESTYLE', 'notestyles', NOTE_STYLE_DATA_VERSION_RULE); super('NOTESTYLE', 'notestyles', NOTE_STYLE_DATA_VERSION_RULE);
@ -33,61 +25,4 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData>
{ {
return fetchEntry(Constants.DEFAULT_NOTE_STYLE); return fetchEntry(Constants.DEFAULT_NOTE_STYLE);
} }
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<NoteStyleData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<NoteStyleData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<NoteStyleData>
{
var parser = new json2object.JsonParser<NoteStyleData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):NoteStyle
{
return ScriptedNoteStyle.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedNoteStyle.listScriptClasses();
}
} }

View file

@ -13,6 +13,7 @@ import funkin.util.VersionUtil;
using funkin.data.song.migrator.SongDataMigrator; using funkin.data.song.migrator.SongDataMigrator;
@:nullSafety @:nullSafety
@:build(funkin.util.macro.RegistryMacro.buildRegistry())
class SongRegistry extends BaseRegistry<Song, SongMetadata> class SongRegistry extends BaseRegistry<Song, SongMetadata>
{ {
/** /**
@ -39,19 +40,6 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return '${Constants.TITLE} - ${Constants.VERSION}'; return '${Constants.TITLE} - ${Constants.VERSION}';
} }
/**
* TODO: What if there was a Singleton macro which automatically created the property for us?
*/
public static var instance(get, never):SongRegistry;
static var _instance:Null<SongRegistry> = null;
static function get_instance():SongRegistry
{
if (_instance == null) _instance = new SongRegistry();
return _instance;
}
public function new() public function new()
{ {
super('SONG', 'songs', SONG_METADATA_VERSION_RULE); super('SONG', 'songs', SONG_METADATA_VERSION_RULE);
@ -417,16 +405,6 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
} }
} }
function createScriptedEntry(clsName:String):Song
{
return ScriptedSong.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedSong.listScriptClasses();
}
function loadEntryMetadataFile(id:String, ?variation:String):Null<JsonFile> function loadEntryMetadataFile(id:String, ?variation:String):Null<JsonFile>
{ {
variation = variation == null ? Constants.DEFAULT_VARIATION : variation; variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
@ -508,8 +486,32 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
public function listBaseGameSongIds():Array<String> public function listBaseGameSongIds():Array<String>
{ {
return [ return [
"tutorial", "bopeebo", "fresh", "dadbattle", "spookeez", "south", "monster", "pico", "philly-nice", "blammed", "satin-panties", "high", "milf", "cocoa", "tutorial",
"eggnog", "winter-horrorland", "senpai", "roses", "thorns", "ugh", "guns", "stress", "darnell", "lit-up", "2hot", "blazin" "bopeebo",
"fresh",
"dadbattle",
"spookeez",
"south",
"monster",
"pico",
"philly-nice",
"blammed",
"satin-panties",
"high",
"milf",
"cocoa",
"eggnog",
"winter-horrorland",
"senpai",
"roses",
"thorns",
"ugh",
"guns",
"stress",
"darnell",
"lit-up",
"2hot",
"blazin"
]; ];
} }

View file

@ -30,23 +30,14 @@ import funkin.util.EaseUtil;
* *
* This shit is great for modders but it's pretty elaborate for how much it'll actually be used, lolol. -Eric * This shit is great for modders but it's pretty elaborate for how much it'll actually be used, lolol. -Eric
*/ */
@:build(funkin.util.macro.RegistryMacro.buildEntry())
class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry<ConversationData> class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry<ConversationData>
{ {
/**
* The ID of the conversation.
*/
public final id:String;
/** /**
* The current state of the conversation. * The current state of the conversation.
*/ */
var state:ConversationState = ConversationState.Start; var state:ConversationState = ConversationState.Start;
/**
* Conversation data as parsed from the JSON file.
*/
public final _data:ConversationData;
/** /**
* The current entry in the dialogue. * The current entry in the dialogue.
*/ */
@ -631,16 +622,6 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl
outroTween = null; outroTween = null;
} }
} }
public override function toString():String
{
return 'Conversation($id)';
}
static function _fetchData(id:String):Null<ConversationData>
{
return ConversationRegistry.instance.parseEntryDataWithMigration(id, ConversationRegistry.instance.fetchEntryVersion(id));
}
} }
// Managing things with a single enum is a lot easier than a multitude of flags. // Managing things with a single enum is a lot easier than a multitude of flags.

View file

@ -18,13 +18,9 @@ using funkin.data.animation.AnimationData.AnimationDataUtil;
* and provides convenience methods for building sprites based on them. * and provides convenience methods for building sprites based on them.
*/ */
@:nullSafety @:nullSafety
@:build(funkin.util.macro.RegistryMacro.buildEntry())
class NoteStyle implements IRegistryEntry<NoteStyleData> class NoteStyle implements IRegistryEntry<NoteStyleData>
{ {
/**
* The ID of the note style.
*/
public final id:String;
/** /**
* Note style data as parsed from the JSON file. * Note style data as parsed from the JSON file.
*/ */
@ -36,7 +32,8 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
*/ */
var fallback(get, never):Null<NoteStyle>; var fallback(get, never):Null<NoteStyle>;
function get_fallback():Null<NoteStyle> { function get_fallback():Null<NoteStyle>
{
if (_data == null || _data.fallback == null) return null; if (_data == null || _data.fallback == null) return null;
return NoteStyleRegistry.instance.fetchEntry(_data.fallback); return NoteStyleRegistry.instance.fetchEntry(_data.fallback);
} }
@ -880,13 +877,6 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
} }
} }
public function destroy():Void {}
public function toString():String
{
return 'NoteStyle($id)';
}
static function _fetchData(id:String):NoteStyleData static function _fetchData(id:String):NoteStyleData
{ {
var result = NoteStyleRegistry.instance.parseEntryDataWithMigration(id, NoteStyleRegistry.instance.fetchEntryVersion(id)); var result = NoteStyleRegistry.instance.parseEntryDataWithMigration(id, NoteStyleRegistry.instance.fetchEntryVersion(id));

View file

@ -28,6 +28,7 @@ import funkin.util.SortUtil;
* can be used to perform custom gameplay behaviors only on specific songs. * can be used to perform custom gameplay behaviors only on specific songs.
*/ */
@:nullSafety @:nullSafety
@:build(funkin.util.macro.RegistryMacro.buildEntry())
class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMetadata> class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMetadata>
{ {
/** /**
@ -65,19 +66,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
*/ */
public static final DEFAULT_SCROLLSPEED:Float = 1.0; public static final DEFAULT_SCROLLSPEED:Float = 1.0;
/**
* The internal ID of the song.
*/
public final id:String;
/**
* Song metadata as parsed from the JSON file.
* This is the data for the `default` variation specifically,
* and is needed for the IRegistryEntry interface.
* Will only be null if the song data could not be loaded.
*/
public final _data:Null<SongMetadata>;
// key = variation id, value = metadata // key = variation id, value = metadata
final _metadata:Map<String, SongMetadata>; final _metadata:Map<String, SongMetadata>;
@ -624,13 +612,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
} }
} }
public function toString():String
{
return 'Song($id)';
}
public function destroy():Void {}
public function onPause(event:PauseScriptEvent):Void {}; public function onPause(event:PauseScriptEvent):Void {};
public function onResume(event:ScriptEvent):Void {}; public function onResume(event:ScriptEvent):Void {};

View file

@ -10,18 +10,11 @@ import funkin.play.scoring.Scoring.ScoringRank;
* Can be scripted to override each function, for custom behavior. * Can be scripted to override each function, for custom behavior.
*/ */
@:nullSafety @:nullSafety
@:build(funkin.util.macro.RegistryMacro.buildEntry())
class PlayableCharacter implements IRegistryEntry<PlayerData> class PlayableCharacter implements IRegistryEntry<PlayerData>
{ {
/**
* The ID of the playable character.
*/
public final id:String; public final id:String;
/**
* Playable character data as parsed from the JSON file.
*/
public final _data:Null<PlayerData>;
/** /**
* @param id The ID of the JSON file to parse. * @param id The ID of the JSON file to parse.
*/ */
@ -156,25 +149,4 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
{ {
return _data?.unlocked ?? true; return _data?.unlocked ?? true;
} }
/**
* Called when the character is destroyed.
* TODO: Document when this gets called
*/
public function destroy():Void {}
public function toString():String
{
return 'PlayableCharacter($id)';
}
/**
* Retrieve and parse the JSON data for a playable character by ID.
* @param id The ID of the character
* @return The parsed player data, or null if not found or invalid
*/
static function _fetchData(id:String):Null<PlayerData>
{
return PlayerRegistry.instance.parseEntryDataWithMigration(id, PlayerRegistry.instance.fetchEntryVersion(id));
}
} }

View file

@ -0,0 +1,365 @@
package funkin.util.macro;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.ExprTools;
using haxe.macro.TypeTools;
using StringTools;
class RegistryMacro
{
public static macro function buildRegistry():Array<Field>
{
var fields = Context.getBuildFields();
var cls = Context.getLocalClass().get();
if (!cls.name.endsWith('Registry'))
{
throw '${cls.module}.${cls.name} needs to end with "Registry"';
}
var typeParams = getTypeParams(cls);
var entryCls = typeParams.entryCls;
var jsonCls = typeParams.jsonCls;
var scriptedEntryCls = getScriptedEntryClass(entryCls);
fields = fields.concat(buildRegistryVariables(cls));
fields = fields.concat(buildRegistryMethods(cls));
buildEntryImpl(entryCls, cls);
buildRegistryImpl(cls, entryCls, scriptedEntryCls, jsonCls);
return fields;
}
public static macro function buildEntry():Array<Field>
{
var fields = Context.getBuildFields();
var cls = Context.getLocalClass().get();
var entryData = getEntryData(cls);
// since the registries also use a build macro
// the fields aren't callable unless we first get
// the class type of the registry
makeFieldsCallable(cls);
fields = fields.concat(buildEntryVariables(cls, entryData));
fields = fields.concat(buildEntryMethods(cls));
return fields;
}
#if macro
static function makeFieldsCallable(cls:ClassType)
{
// TODO: lets not have this if statement
// like what the hell is wrong with this
if (cls.name == 'Song')
{
MacroUtil.getClassTypeFromExpr(macro funkin.data.song.SongRegistry);
return;
}
var registries:Array<String> = [];
for (localImport in Context.getLocalImports())
{
var names = [];
for (path in localImport.path)
{
names.push(path.name);
}
var fullName = names.join('.');
if (fullName.endsWith('Registry'))
{
registries.push(fullName);
}
}
for (registry in registries)
{
MacroUtil.getClassTypeFromExpr(Context.parse(registry, Context.currentPos()));
}
}
static function fieldAlreadyExists(name:String):Bool
{
for (field in Context.getBuildFields())
{
if (field.name == name && !((field.access ?? []).contains(Access.AAbstract)))
{
return true;
}
}
function fieldAlreadyExistsSuper(name:String, superClass:Null<ClassType>)
{
if (superClass == null)
{
return false;
}
for (field in superClass.fields.get())
{
if (field.name == name && !field.isAbstract)
{
return true;
}
}
// recursively check superclasses
return fieldAlreadyExistsSuper(name, superClass.superClass?.t.get());
}
return fieldAlreadyExistsSuper(name, Context.getLocalClass().get().superClass?.t.get());
}
static function getTypeParams(cls:ClassType):RegistryTypeParamsNew
{
switch (cls.superClass.t.get().kind)
{
case KGenericInstance(_, params):
var typeParams:Array<Dynamic> = [];
for (param in params)
{
switch (param)
{
case TInst(t, _):
typeParams.push(t.get());
case TType(t, _):
typeParams.push(t.get());
default:
throw 'Not a class';
}
}
return {entryCls: typeParams[0], jsonCls: typeParams[1]};
default:
throw 'Not in the correct format';
}
}
static function getScriptedEntryClass(entryCls:ClassType):ClassType
{
var scriptedEntryClsName = entryCls.pack.join('.') + '.Scripted' + entryCls.name;
switch (Context.getType(scriptedEntryClsName))
{
case Type.TInst(t, _):
return t.get();
default:
throw 'Not A Class (${scriptedEntryClsName})';
};
}
static function getEntryData(cls:ClassType):Dynamic // DefType or ClassType
{
switch (cls.interfaces[0].params[0])
{
case Type.TInst(t, _):
return t.get();
case Type.TType(t, _):
return t.get();
default:
throw 'Entry Data is not a class or typedef';
}
}
static function buildRegistryVariables(cls:ClassType):Array<Field>
{
var clsType:ComplexType = Context.getType('${cls.module}.${cls.name}').toComplexType();
var newInstance:String = 'new ${cls.module}.${cls.name}()';
return (macro class TempClass
{
static var _instance:Null<$clsType>;
public static var instance(get, never):$clsType;
static function get_instance():$clsType
{
if (_instance == null)
{
_instance = ${Context.parse(newInstance, Context.currentPos())};
}
return _instance;
}
}).fields.filter((field) -> return !fieldAlreadyExists(field.name));
}
static function buildRegistryMethods(cls:ClassType):Array<Field>
{
var impl:String = 'funkin.macro.impl._${cls.name}_Impl';
return (macro class TempClass
{
function getScriptedClassNames()
{
return ${Context.parse(impl, Context.currentPos())}.getScriptedClassNames(this);
}
function createScriptedEntry(clsName:String)
{
return ${Context.parse(impl, Context.currentPos())}.createScriptedEntry(this, clsName);
}
public function parseEntryData(id:String)
{
return ${Context.parse(impl, Context.currentPos())}.parseEntryData(this, id);
}
public function parseEntryDataRaw(contents:String, ?fileName:String)
{
return ${Context.parse(impl, Context.currentPos())}.parseEntryDataRaw(this, contents, fileName);
}
}).fields.filter((field) -> return !fieldAlreadyExists(field.name));
}
static function buildEntryVariables(cls:ClassType, entryData:Dynamic):Array<Field>
{
var entryDataType:ComplexType = Context.getType('${entryData.module}.${entryData.name}').toComplexType();
return (macro class TempClass
{
public final id:String;
public final _data:Null<$entryDataType>;
}).fields.filter((field) -> return !fieldAlreadyExists(field.name));
}
static function buildEntryMethods(cls:ClassType):Array<Field>
{
var impl:String = 'funkin.macro.impl._${cls.name}_Impl';
return (macro class TempClass
{
public function _fetchData(id:String)
{
return ${Context.parse(impl, Context.currentPos())}._fetchData(this, id);
}
public function toString()
{
return ${Context.parse(impl, Context.currentPos())}.toString(this);
}
public function destroy()
{
${Context.parse(impl, Context.currentPos())}.destroy(this);
}
}).fields.filter((field) -> !fieldAlreadyExists(field.name));
}
static function buildRegistryImpl(cls:ClassType, entryCls:ClassType, scriptedEntryCls:ClassType, jsonCls:Dynamic):Void
{
var clsType:ComplexType = Context.getType('${cls.module}.${cls.name}').toComplexType();
var getScriptedClassName:String = '${scriptedEntryCls.module}.${scriptedEntryCls.name}';
var createScriptedEntry:String = '${scriptedEntryCls.module}.${scriptedEntryCls.name}.init(clsName, "unknown")';
var newJsonParser:String = 'new json2object.JsonParser<${jsonCls.module}.${jsonCls.name}>()';
Context.defineType(
{
pos: Context.currentPos(),
pack: ['funkin', 'macro', 'impl'],
name: '_${cls.name}_Impl',
kind: TypeDefKind.TDClass(null, [], false, false, false),
fields: (macro class TempClass
{
public static inline function getScriptedClassNames(me:$clsType)
{
return ${Context.parse(getScriptedClassName, Context.currentPos())}.listScriptClasses();
}
public static inline function createScriptedEntry(me:$clsType, clsName:String)
{
return ${Context.parse(createScriptedEntry, Context.currentPos())};
}
public static inline function parseEntryData(me:$clsType, id:String)
{
var parser = ${Context.parse(newJsonParser, Context.currentPos())};
parser.ignoreUnknownVariables = false;
@:privateAccess
switch (me.loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
@:privateAccess
me.printErrors(parser.errors, id);
return null;
}
return parser.value;
}
public static inline function parseEntryDataRaw(me:$clsType, contents:String, ?fileName:String)
{
var parser = ${Context.parse(newJsonParser, Context.currentPos())};
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
@:privateAccess
me.printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
}).fields
});
}
static function buildEntryImpl(cls:ClassType, registryCls:ClassType):Void
{
var clsType:ComplexType = Context.getType('${cls.module}.${cls.name}').toComplexType();
var registry:String = '${registryCls.module}.${registryCls.name}';
Context.defineType(
{
pos: Context.currentPos(),
pack: ['funkin', 'macro', 'impl'],
name: '_${cls.name}_Impl',
kind: TypeDefKind.TDClass(null, [], false, false, false),
fields: (macro class TempClass
{
public static inline function _fetchData(me:$clsType, id:String)
{
return $
{
Context.parse(registry, Context.currentPos())
}.instance.parseEntryDataWithMigration(id, ${Context.parse(registry, Context.currentPos())}.instance.fetchEntryVersion(id));
}
public static inline function toString(me:$clsType)
{
return $v{cls.name} + '(' + me.id + ')';
}
public static inline function destroy(me:$clsType) {}
}).fields
});
}
#end
}
#if macro
typedef RegistryTypeParamsNew =
{
var entryCls:ClassType;
var jsonCls:Dynamic; // DefType or ClassType
}
#end