early custom classes
This commit is contained in:
@@ -27,7 +27,6 @@ using StringTools;
|
||||
@:allow(funkin.desktop.editors.CharacterEditor)
|
||||
@:allow(funkin.game.StrumLine)
|
||||
@:allow(funkin.game.PlayState)
|
||||
@:build(funkin.macros.SoftcodedClassMacro.build())
|
||||
class Character extends FunkinSprite implements IBeatReceiver implements IOffsetCompatible
|
||||
{
|
||||
private var __stunnedTime:Float = 0;
|
||||
|
||||
@@ -16,6 +16,7 @@ class ScriptsMacro {
|
||||
|
||||
// FOR ABSTRACTS
|
||||
Compiler.addGlobalMetadata('haxe.xml', '@:build(hscript.UsingHandler.build())');
|
||||
Compiler.addGlobalMetadata('haxe.CallStack', '@:build(hscript.UsingHandler.build())');
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -1,19 +1,35 @@
|
||||
package funkin.macros;
|
||||
|
||||
using StringTools;
|
||||
class SoftcodedClassMacro {
|
||||
#if macro
|
||||
public static function init() {
|
||||
trace("cock and balls");
|
||||
// Compiler.addGlobalMetadata('funkin.game', '@:build(funkin.macros.SoftcodedClassMacro.build())');
|
||||
Compiler.addGlobalMetadata('funkin', '@:build(funkin.macros.SoftcodedClassMacro.build())');
|
||||
Compiler.addGlobalMetadata('flixel', '@:build(funkin.macros.SoftcodedClassMacro.build())');
|
||||
}
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
var cl = Context.getLocalClass();
|
||||
|
||||
var funcs:Array<Field> = [];
|
||||
if (cl != null) {
|
||||
var scriptClassType = TPath({
|
||||
pack: ['funkin', 'scripting'],
|
||||
name: 'Script',
|
||||
sub: 'ScriptClass'
|
||||
});
|
||||
|
||||
var funcs:Array<Field> = [];
|
||||
funcs.push({
|
||||
pos: Context.currentPos(),
|
||||
name: '__scriptClass',
|
||||
kind: FVar(scriptClassType, macro null)
|
||||
});
|
||||
var c = cl.get();
|
||||
trace(c.name);
|
||||
if (c.name.endsWith('__Softcoded')) return fields;
|
||||
if (c.params.length > 0) return fields; // NOT SUPPORTED
|
||||
if (c.isAbstract) return fields;
|
||||
|
||||
for(m in c.meta.get()) {
|
||||
@@ -25,7 +41,19 @@ class SoftcodedClassMacro {
|
||||
}
|
||||
}
|
||||
|
||||
for(f in fields) {
|
||||
var shadowClass = macro class {
|
||||
|
||||
};
|
||||
|
||||
shadowClass.name = '${c.name}__Softcoded';
|
||||
shadowClass.fields = funcs;
|
||||
shadowClass.pack = c.pack; // todo
|
||||
shadowClass.kind = TDClass({
|
||||
pack: c.pack,
|
||||
name: c.name
|
||||
}, [], false, false, false);
|
||||
|
||||
for(f in [for(f in fields) f]) {
|
||||
var valid = true;
|
||||
if (f.access != null) for(e in f.access) {
|
||||
switch (e) {
|
||||
@@ -56,12 +84,60 @@ class SoftcodedClassMacro {
|
||||
}] : [])
|
||||
}};
|
||||
};
|
||||
|
||||
var isVoid = fun.ret.match(TPath({name: "Void"}));
|
||||
|
||||
if (!isVoid)
|
||||
funcExpr = macro return ${funcExpr};
|
||||
|
||||
var accessors = [];
|
||||
if (f.access != null) for(f in f.access) accessors.push(f);
|
||||
accessors.remove(AOverride);
|
||||
|
||||
if (f.name != "new") {
|
||||
if (f.name == "new") {
|
||||
var constructorExpr = macro {
|
||||
${{
|
||||
expr: ECall({
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent("super"))
|
||||
}, fun.args != null ? [for(a in fun.args) {
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(a.name))
|
||||
}] : []),
|
||||
pos: Context.currentPos()
|
||||
}}
|
||||
|
||||
__scriptClass = scrObj;
|
||||
|
||||
__scriptClass.call("new", ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EArrayDecl([for(a in fun.args) {
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(a.name))
|
||||
}])
|
||||
}}, this);
|
||||
|
||||
}
|
||||
var func:Function = {
|
||||
expr: constructorExpr,
|
||||
args: [for(a in fun.args) a]
|
||||
};
|
||||
func.args.insert(0, {
|
||||
name: "scrObj",
|
||||
type: scriptClassType
|
||||
});
|
||||
funcs.push({
|
||||
pos: Context.currentPos(),
|
||||
name: 'new',
|
||||
kind: FFun(func),
|
||||
access: [for(a in accessors) a]
|
||||
});
|
||||
|
||||
|
||||
var printer = new Printer();
|
||||
|
||||
continue;
|
||||
} else {
|
||||
var func:Function = {
|
||||
expr: funcExpr,
|
||||
args: fun.args
|
||||
@@ -70,13 +146,13 @@ class SoftcodedClassMacro {
|
||||
pos: Context.currentPos(),
|
||||
name: '__super__${f.name}',
|
||||
kind: FFun(func),
|
||||
access: accessors
|
||||
access: [for(a in accessors) a]
|
||||
});
|
||||
}
|
||||
|
||||
accessors.push(AOverride);
|
||||
|
||||
var overrideFuncExpr = macro {
|
||||
var overrideFuncExpr = isVoid ? macro {
|
||||
var fieldName = ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CString(f.name, DoubleQuotes))
|
||||
@@ -88,7 +164,21 @@ class SoftcodedClassMacro {
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(a.name))
|
||||
}])
|
||||
}});
|
||||
}}, this);
|
||||
} else ${funcExpr}
|
||||
} : macro {
|
||||
var fieldName = ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CString(f.name, DoubleQuotes))
|
||||
}};
|
||||
if (__scriptClass != null && __scriptClass.hasField(fieldName) && Reflect.isFunction(__scriptClass.get(fieldName))) {
|
||||
return __scriptClass.call(fieldName, ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EArrayDecl([for(a in fun.args) {
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(a.name))
|
||||
}])
|
||||
}}, this);
|
||||
} else ${funcExpr}
|
||||
};
|
||||
|
||||
@@ -103,14 +193,28 @@ class SoftcodedClassMacro {
|
||||
access: accessors
|
||||
});
|
||||
|
||||
|
||||
|
||||
var printer = new Printer();
|
||||
trace(printer.printField(funcs[funcs.length - 1]));
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var imports = [for(i in Context.getLocalImports()) i];
|
||||
imports.push({
|
||||
path: [for(p in c.module.split(".")) {
|
||||
pos: Context.currentPos(),
|
||||
name: p
|
||||
}],
|
||||
mode: IAll
|
||||
});
|
||||
imports.push({
|
||||
path: [for(p in c.module.split(".")) {
|
||||
pos: Context.currentPos(),
|
||||
name: p
|
||||
}],
|
||||
mode: INormal
|
||||
});
|
||||
|
||||
Context.defineModule(c.module + "__Softcoded", [shadowClass], imports);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.scripting;
|
||||
|
||||
import haxe.io.Path;
|
||||
import funkin.scripting.Script.ScriptClass;
|
||||
import hscript.Expr.ClassDecl;
|
||||
import hscript.Expr.ModuleDecl;
|
||||
@@ -14,6 +15,7 @@ class HScript extends Script {
|
||||
public var expr:Expr;
|
||||
public var decls:Array<ModuleDecl> = null;
|
||||
public var code:String;
|
||||
public var folderlessPath:String;
|
||||
|
||||
public static function initParser() {
|
||||
var parser = new Parser();
|
||||
@@ -29,7 +31,10 @@ class HScript extends Script {
|
||||
|
||||
code = Assets.getText(path);
|
||||
parser = initParser();
|
||||
folderlessPath = Path.directory(path);
|
||||
|
||||
interp.errorHandler = _errorHandler;
|
||||
interp.importFailedCallback = importFailedCallback;
|
||||
interp.staticVariables = Script.staticVariables;
|
||||
interp.allowStaticVariables = interp.allowPublicVariables = true;
|
||||
|
||||
@@ -49,6 +54,25 @@ class HScript extends Script {
|
||||
}
|
||||
}
|
||||
|
||||
private function importFailedCallback(cl:Array<String>):Bool {
|
||||
var path = cl.join("/");
|
||||
|
||||
var scr = Script.create(Paths.script('classes/$path', null, false));
|
||||
if (!(scr is DummyScript)) {
|
||||
// script is valid
|
||||
var cla = scr.getClass(cl.last());
|
||||
if (cla != null) {
|
||||
interp.variables.set(cl.last(), Script.createCustomClass(cla));
|
||||
} else {
|
||||
interp.variables.set(cl.last(), scr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function _errorHandler(error:Error) {
|
||||
|
||||
var fn = '$fileName:${error.line}: ';
|
||||
@@ -131,16 +155,18 @@ class HScript extends Script {
|
||||
if (decls == null) {
|
||||
decls = parser.parseModule(code, fileName);
|
||||
}
|
||||
var imports:Array<Array<String>> = [];
|
||||
for(d in decls) {
|
||||
switch(d) {
|
||||
case DPackage(path):
|
||||
// ignore
|
||||
case DImport(path, everything):
|
||||
// ignore
|
||||
imports.push(path);
|
||||
case DClass(c):
|
||||
if (c.name == name) {
|
||||
|
||||
}
|
||||
trace(c.name);
|
||||
trace(name);
|
||||
if (c.name == name)
|
||||
return new HScriptClass(c, interp, imports, folderlessPath);
|
||||
case DTypedef(c):
|
||||
// ignore
|
||||
}
|
||||
@@ -221,17 +247,23 @@ class HScriptClass extends ScriptClass {
|
||||
|
||||
var interp:Interp;
|
||||
|
||||
public function new(decl:ClassDecl, sInterp:Interp) {
|
||||
public function new(decl:ClassDecl, sInterp:Interp, classesToImport:Array<Array<String>>, forderlessPath:String) {
|
||||
super();
|
||||
this.name = decl.name;
|
||||
switch(decl.extend) {
|
||||
case CTPath(p, params):
|
||||
this.classPath = p.join(".");
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
interp = new Interp();
|
||||
interp.errorHandler = sInterp.errorHandler;
|
||||
interp.staticVariables = sInterp.staticVariables;
|
||||
interp.allowStaticVariables = interp.allowPublicVariables = false;
|
||||
// interp.execute({ e : EBlock(decl.fields), pmin : 0, pmax : 0, origin : fileName, line : 0 });
|
||||
interp.allowStaticVariables = interp.allowPublicVariables = true;
|
||||
interp.importFailedCallback = importFailedCallback;
|
||||
|
||||
var parser = HScript.initParser();
|
||||
|
||||
@@ -246,7 +278,22 @@ class HScriptClass extends ScriptClass {
|
||||
}
|
||||
}
|
||||
|
||||
public override function hasField(field:String) {
|
||||
return interp.variables.exists(field);
|
||||
}
|
||||
public override function get(field:String) {
|
||||
return interp.variables[field];
|
||||
}
|
||||
|
||||
public override function set(field:String, v:Dynamic) {
|
||||
interp.variables[field] = v;
|
||||
}
|
||||
|
||||
public override function onCall(field:String, parameters:Array<Dynamic>, parent:Dynamic) {
|
||||
interp.scriptObject = parent;
|
||||
var v = interp.variables[field];
|
||||
if (v != null && Reflect.isFunction(v))
|
||||
return (parameters != null && parameters.length > 0) ? Reflect.callMethod(null, v, parameters) : v();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package funkin.scripting;
|
||||
import lime.app.Application;
|
||||
import flixel.util.FlxDestroyUtil.IFlxDestroyable;
|
||||
import haxe.io.Path;
|
||||
import hscript.IHScriptCustomConstructor;
|
||||
|
||||
/**
|
||||
* Class used for scripting.
|
||||
@@ -154,6 +155,15 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
return new DummyScript(path);
|
||||
}
|
||||
|
||||
public static function createCustomClass(s:ScriptClass) {
|
||||
var t:Class<IScriptClassInstance> = cast Type.resolveClass('${s.classPath}__Softcoded');
|
||||
if (t == null) {
|
||||
Logs.trace('Class at ${s.classPath} was not found.', ERROR);
|
||||
return null;
|
||||
}
|
||||
return new ScriptCustomClass(s, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the script class.
|
||||
* @param path
|
||||
@@ -277,11 +287,64 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
}
|
||||
|
||||
class ScriptClass {
|
||||
public var classPath:String = null;
|
||||
public var folderlessPath:String = null;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function get(field:String):Dynamic {return null;}
|
||||
|
||||
public function set(field:String, v:Dynamic) {}
|
||||
|
||||
public function call(field:String, ?args:Array<Dynamic>) {return null;}
|
||||
public function hasField(field:String):Bool {return false;}
|
||||
|
||||
/**
|
||||
* Calls the function `func` defined in the class.
|
||||
* @param func Name of the function
|
||||
* @param parameters (Optional) Parameters of the function.
|
||||
* @return Result (if void, then null)
|
||||
*/
|
||||
public function call(func:String, parameters:Array<Dynamic>, parent:Dynamic):Dynamic {
|
||||
var result = onCall(func, parameters == null ? [] : parameters, parent);
|
||||
return result;
|
||||
}
|
||||
|
||||
public function onCall(func:String, parameters:Array<Dynamic>, parent:Dynamic):Dynamic {return null;}
|
||||
|
||||
private function importFailedCallback(cl:Array<String>):Bool {
|
||||
var path = cl.join("/");
|
||||
|
||||
var scr = Script.create(Paths.script('$folderlessPath$path', null, true));
|
||||
if (!(scr is DummyScript)) {
|
||||
// script is valid
|
||||
var cla = scr.getClass(cl.last());
|
||||
if (cla != null) {
|
||||
set(cl.last(), Script.createCustomClass(cla));
|
||||
} else {
|
||||
set(cl.last(), scr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ScriptCustomClass implements IHScriptCustomConstructor {
|
||||
var scrClass:ScriptClass;
|
||||
var scrType:Class<IScriptClassInstance>;
|
||||
public function new(scrClass:ScriptClass, scrType:Class<IScriptClassInstance>) {
|
||||
this.scrClass = scrClass;
|
||||
this.scrType = scrType;
|
||||
}
|
||||
public function hnew(...args:Dynamic):Dynamic {
|
||||
var a = [for(arg in args) arg];
|
||||
a.insert(0, scrClass);
|
||||
return Type.createInstance(scrType, a);
|
||||
}
|
||||
}
|
||||
|
||||
interface IScriptClassInstance {
|
||||
|
||||
}
|
||||
@@ -21,12 +21,16 @@ import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import flash.geom.ColorTransform;
|
||||
import funkin.chart.Chart;
|
||||
import haxe.CallStack;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@:allow(funkin.game.PlayState)
|
||||
class CoolUtil
|
||||
{
|
||||
public static function getLastExceptionStack():String {
|
||||
return CallStack.toString(CallStack.exceptionStack());
|
||||
}
|
||||
/*
|
||||
* Returns `v` if not null, `defaultValue` otherwise.
|
||||
* @param v The value
|
||||
|
||||
Reference in New Issue
Block a user