diff --git a/hGameTest/src/engine/macros/ConVarDecorators.hx b/hGameTest/src/engine/macros/ConVarDecorators.hx index 131a5160..45bcdb24 100644 --- a/hGameTest/src/engine/macros/ConVarDecorators.hx +++ b/hGameTest/src/engine/macros/ConVarDecorators.hx @@ -1,136 +1,266 @@ package engine.macros; +#if macro import haxe.macro.Expr; import haxe.macro.Context; +import haxe.macro.Type; import haxe.macro.Expr.Field; +import haxe.macro.Expr.Function; +import haxe.macro.Expr.Access; private typedef CVarMetadata = { - name: Expr, - type: String, - value: Expr, - flags: Expr, - help: Expr, - ?callback: Expr + name:Expr, + type:String, + value:Expr, + flags:Expr, + help:Expr, + ?callback:Expr } class ConVarDecorators { static function extractCVarMetadata(params:Array):CVarMetadata { - var metadata:CVarMetadata = null; + if (params == null || params.length == 0) return null; + var metadata:CVarMetadata = { + name: macro "", + type: "CString", + value: macro 0, + flags: macro "FCVAR_NONE", + help: macro "", + callback: macro null + }; + switch (params[0].expr) { case EObjectDecl(fields): - metadata = { - name: macro "", - type: "CString", - value: macro 0, - flags: macro "FCVAR_NONE", - help: macro "", - callback: macro null - }; - + var fieldMap = [ + "name" => function(expr) metadata.name = expr, + "type" => function(expr) metadata.type = switch (expr.expr) { + case EConst(CString(s)): s; + case _: "CString"; + }, + "value" => function(expr) metadata.value = expr, + "flags" => function(expr) metadata.flags = expr, + "help" => function(expr) metadata.help = expr, + "callback" => function(expr) metadata.callback = expr + ]; + for (field in fields) { - switch (field.field) { - case "name": metadata.name = field.expr; - case "type": metadata.type = switch(field.expr.expr) { - case EConst(CString(s)): s; - case _: "CString"; - } - case "value": metadata.value = field.expr; - case "flags": metadata.flags = field.expr; - case "help": metadata.help = field.expr; - case "callback": metadata.callback = field.expr; - case _: + if (fieldMap.exists(field.field)) { + fieldMap[field.field](field.expr); + } else { + Context.error("Unexpected field: " + field.field, field.expr.pos); } } case _: } return metadata; } + + // Process command fields + private static function processCommandField(field:Field, cmdMeta:MetadataEntry, extraFields:Array):Void { + if (!isFieldKind(field, "FFun")) { + Context.error("The @:concmd metadata can only be applied to functions", field.pos); + return; + } + + var pos = Context.currentPos(); + var functionReference = getFunctionFromField(field); + if (functionReference == null) return; + + var name = cmdMeta.params.length > 0 ? getStringFromExpr(cmdMeta.params[0]) ?? field.name : field.name; + var functionExpression = macro function(args:Array) ${functionReference.expr}; + + var initExpr = macro { + var cmd = engine.ConVar.registerCCmd($v{name}, $functionExpression); + cmd; + }; + + var initName = "__init__cmd_" + name; + extraFields.push(createStaticInitField(initName, initExpr, pos)); + } + + // Process convar fields + private static function processConVarField(field:Field, cvarMeta:MetadataEntry, extraFields:Array):Void { + if (!isFieldKind(field, "FVar")) { + Context.error("The @:convar metadata can only be applied to variables", field.pos); + return; + } + + var pos = Context.currentPos(); + var meta = extractCVarMetadata(cvarMeta.params); + if (meta == null) { + Context.error("Metadata extraction failed for field: " + field.name, field.pos); + return; + } + + var cvarName = getStringFromMetaExpr(meta.name) ?? field.name; + var fieldName = field.name; + var isStatic = hasAccess(field, AStatic); + + var varType = getVarType(field); + + if (isStatic) { + // Handle static fields + var staticInitExpr = macro { + var cvar = $i{fieldName} = engine.ConVar.registerCVar( + ${meta.name}, + ${Context.parse('engine.enums.console.CVarType.' + meta.type, pos)}, + ${meta.value}, + ${Context.parse('engine.enums.console.CVarFlag.' + switch (meta.flags.expr) { + case EConst(CString(s)): s; + case _: "FCVAR_NONE"; + }, pos)}, + ${meta.help}, + ${meta.callback ?? macro null} + ); + trace($v{"Registered CVar: "} + $e{meta.name}); + cvar; + }; + + extraFields.push(createStaticInitField("__init__" + cvarName, staticInitExpr, pos)); + } else { + // Handle instance fields + updateFieldInitialValue(field, macro null); + + var instanceInitExpr = macro { + if (Reflect.field(this, $v{fieldName}) == null) { + Reflect.setField(this, $v{fieldName}, engine.ConVar.registerCVar( + ${meta.name}, + ${Context.parse('engine.enums.console.CVarType.' + meta.type, pos)}, + ${meta.value}, + ${Context.parse('engine.enums.console.CVarFlag.' + switch (meta.flags.expr) { + case EConst(CString(s)): s; + case _: "FCVAR_NONE"; + }, pos)}, + ${meta.help}, + ${meta.callback ?? macro null} + )); + trace($v{"Registered CVar: "} + $e{meta.name}); + } + }; + + ensureConstructorWithInit(extraFields, instanceInitExpr, pos); + } + } + + // Utility functions + private static function isFieldKind(field:Field, kind:String):Bool { + return switch (field.kind) { + case FFun(_) if (kind == "FFun"): true; + case FVar(_, _) if (kind == "FVar"): true; + case FProp(_, _, _, _) if (kind == "FProp"): true; + case _: false; + } + } + + private static function getFunctionFromField(field:Field):Function { + return switch (field.kind) { + case FFun(f): f; + case _: null; + } + } + + private static function getVarType(field:Field):ComplexType { + return switch (field.kind) { + case FVar(t, _): t; + case _: null; + } + } + + private static function updateFieldInitialValue(field:Field, expr:Expr):Void { + switch (field.kind) { + case FVar(t, _): field.kind = FVar(t, expr); + case _: + } + } + + private static function getStringFromExpr(expr:Expr):String { + return switch (expr.expr) { + case EConst(CString(s)): s; + case _: null; + } + } + + private static function getStringFromMetaExpr(expr:Expr):String { + return switch (expr.expr) { + case EConst(CString(s)): s; + case _: null; + } + } + + private static function hasAccess(field:Field, access:Access):Bool { + return field.access != null && field.access.indexOf(access) >= 0; + } + + private static function createStaticInitField(name:String, initExpr:Expr, pos:Position):Field { + return { + name: name, + access: [AStatic], + kind: FVar(null, initExpr), + pos: pos, + doc: null, + meta: [{ name: ":keep", params: [], pos: pos }] + }; + } + + private static function ensureConstructorWithInit(fields:Array, initExpr:Expr, pos:Position):Void { + var cls = Context.getLocalClass().get(); + var hasSuperClass = cls.superClass != null; + + var constructor = Lambda.find(fields, function(f) return f.name == "new"); + if (constructor == null) { + constructor = { + name: "new", + access: [APublic], + kind: FFun({ + args: [], + expr: if (hasSuperClass) macro { + super(); + $initExpr; + } else macro { + $initExpr; + }, + ret: null + }), + pos: pos, + meta: [] + }; + fields.push(constructor); + } else { + switch (constructor.kind) { + case FFun(f): + switch(f.expr.expr) { + case EBlock(exprs): + exprs.push(initExpr); + case _: + f.expr = macro { + ${f.expr}; + $initExpr; + } + } + case _: + } + } + } public static macro function build():Array { var fields = Context.getBuildFields(); - var pos = Context.currentPos(); - + var extraFields:Array = []; + for (field in fields) { - var cmdMeta = field.meta != null ? Lambda.find(field.meta, function(m) return m.name == ":concmd") : null; - var cvarMeta = field.meta != null ? Lambda.find(field.meta, function(m) return m.name == ":convar") : null; - + if (field.meta == null) continue; + + var cmdMeta = Lambda.find(field.meta, function(m) return m.name == ":concmd"); + var cvarMeta = Lambda.find(field.meta, function(m) return m.name == ":convar"); + if (cmdMeta != null) { - switch field.kind { - case FFun(f): - var name = cmdMeta.params.length > 0 ? switch (cmdMeta.params[0].expr) { - case EConst(CString(s)): s; - case _: field.name; - } : field.name; - - var fnExpr = macro function(args:Array) ${f.expr}; - - var initExpr = macro { - var cmd = engine.ConVar.registerCCmd($v{name}, $fnExpr); - cmd; // Return the command reference - }; - - // Generate a safe initialization field name - var initName = "__init__" + name; - - fields.push({ - name: initName, - access: [AStatic], - kind: FVar(null, initExpr), - pos: pos, - doc: null, - meta: [{ - name: ":keep", - params: [], - pos: pos - }] - }); - case _: - } + processCommandField(field, cmdMeta, extraFields); } else if (cvarMeta != null && cvarMeta.params.length > 0) { - switch (field.kind) { - case FVar(t, e): - var meta = extractCVarMetadata(cvarMeta.params); - if (meta == null) continue; - - // Extract name from metadata expression - var cvarName = switch(meta.name.expr) { - case EConst(CString(s)): s; - case _: field.name; - }; - - var initExpr = macro { - var cvar = $i{field.name} = engine.ConVar.registerCVar( - ${meta.name}, - ${Context.parse('engine.enums.console.CVarType.' + meta.type, pos)}, - ${meta.value}, - ${Context.parse('engine.enums.console.CVarFlag.' + switch(meta.flags.expr) { - case EConst(CString(s)): s; - case _: "FCVAR_NONE"; - }, pos)}, - ${meta.help}, - ${meta.callback ?? macro null} - ); - trace($v{"Registered CVar: "} + $e{meta.name}); - cvar; // Return the CVar reference - }; - - fields.push({ - name: "__init__" + cvarName, - access: [AStatic], - kind: FVar(null, initExpr), - pos: pos, - doc: null, - meta: [{ - name: ":keep", - params: [], - pos: pos - }] - }); - case _: - } + processConVarField(field, cvarMeta, extraFields); } } - return fields; + + return fields.concat(extraFields); } } +#end \ No newline at end of file diff --git a/hGameTest/src/game/ui/console/Console.hx b/hGameTest/src/game/ui/console/Console.hx index 824c6b4a..555d6a43 100644 --- a/hGameTest/src/game/ui/console/Console.hx +++ b/hGameTest/src/game/ui/console/Console.hx @@ -26,12 +26,13 @@ class Console extends Sprite { public var cAutoComp:TextField; public static var consoleInstance:Console; - @:convar({ - name: "mat_consolebg", - type: "CString", - value: "0x222222", - help: "console background color", - callback: () -> { + + @:convar({ + name: "mat_consolebg", + type: "CString", + value: "0x222222", + help: "console background color", + callback: () -> { if (Console.consoleInstance != null){ Console.consoleInstance.cOut.backgroundColor = Std.parseInt(Console.consoleInstance.cvar_mat_consolebg.value); Console.consoleInstance.cIn.backgroundColor = Std.parseInt(Console.consoleInstance.cvar_mat_consolebg.value);