From 1f913154b4f6738d44a2975204c72c0e8bcd3580 Mon Sep 17 00:00:00 2001 From: TheZoroForce240 <86524550+TheZoroForce240@users.noreply.github.com> Date: Sat, 25 Jul 2026 07:52:11 +0100 Subject: [PATCH 1/3] hardcoded scripts wip --- modSource/scripts/import.hx | 84 ++ project.xml | 4 + source/funkin/backend/scripting/HScript.hx | 17 + .../backend/scripting/HardcodedScript.hx | 99 +++ .../scripting/HardcodedScriptPrinter.hx | 749 ++++++++++++++++++ .../scripting/HardcodedScriptRegistry.hx | 48 ++ source/funkin/backend/scripting/Script.hx | 30 + source/funkin/backend/system/Flags.hx | 5 + .../macros/HardcodedScriptRegistryMacro.hx | 54 ++ .../system/macros/HardcodedScriptVarMacro.hx | 137 ++++ source/funkin/backend/system/macros/Macros.hx | 5 + source/funkin/game/PlayState.hx | 6 + 12 files changed, 1238 insertions(+) create mode 100644 modSource/scripts/import.hx create mode 100644 source/funkin/backend/scripting/HardcodedScript.hx create mode 100644 source/funkin/backend/scripting/HardcodedScriptPrinter.hx create mode 100644 source/funkin/backend/scripting/HardcodedScriptRegistry.hx create mode 100644 source/funkin/backend/system/macros/HardcodedScriptRegistryMacro.hx create mode 100644 source/funkin/backend/system/macros/HardcodedScriptVarMacro.hx diff --git a/modSource/scripts/import.hx b/modSource/scripts/import.hx new file mode 100644 index 0000000000..464b67d17c --- /dev/null +++ b/modSource/scripts/import.hx @@ -0,0 +1,84 @@ +#if !macro +package scripts; +import funkin.backend.scripting.HardcodedScript; +import funkin.backend.scripting.events.*; +import funkin.backend.scripting.events.character.*; +import funkin.backend.scripting.events.dialogue.*; +import funkin.backend.scripting.events.discord.*; +import funkin.backend.scripting.events.gameover.*; +import funkin.backend.scripting.events.gameplay.*; +import funkin.backend.scripting.events.healthicon.*; +import funkin.backend.scripting.events.menu.*; +import funkin.backend.scripting.events.note.*; +import funkin.backend.scripting.events.shader.*; +import funkin.backend.scripting.events.soundtray.*; +import funkin.backend.scripting.events.splash.*; +import funkin.backend.scripting.events.sprite.*; +import funkin.backend.scripting.events.stage.*; + +import StringTools; +import haxe.Json; +import Xml; + +import openfl.utils.Assets; +import lime.app.Application; +import funkin.backend.system.Main; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.FlxBasic; +import flixel.FlxCamera; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.sound.FlxSound; +import flixel.system.FlxAssets; +import flixel.math.FlxMath; +import flixel.group.FlxGroup; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.group.FlxSpriteGroup; +import flixel.addons.text.FlxTypeText; +import flixel.text.FlxText; +import flixel.util.FlxTimer; +import flixel.math.FlxPoint; +import flixel.util.FlxAxes; +import flixel.util.FlxColor; +import funkin.backend.scripting.ModState; +import funkin.backend.scripting.ModSubState; +import funkin.game.PlayState; +import funkin.game.GameOverSubstate; +import funkin.game.HealthIcon; +import funkin.game.HudCamera; +import funkin.game.Note; +import funkin.game.Strum; +import funkin.game.StrumLine; +import funkin.game.Character; +import funkin.menus.PauseSubState; +import funkin.menus.FreeplayState; +import funkin.menus.MainMenuState; +import funkin.menus.PauseSubState; +import funkin.menus.StoryMenuState; +import funkin.menus.TitleState; +import funkin.options.Options; +import funkin.backend.assets.Paths; +import funkin.backend.system.Conductor; +import funkin.backend.shaders.FunkinShader; +import funkin.backend.shaders.CustomShader; +import funkin.backend.FunkinText; +import animate.FlxAnimate; +import funkin.backend.FunkinSprite; +import funkin.menus.ui.Alphabet; +import funkin.backend.system.Flags; + +import funkin.backend.utils.CoolUtil; +import funkin.backend.utils.IniUtil; +import funkin.backend.utils.XMLUtil; +#if sys +import funkin.backend.utils.ZipUtil; +#end +import funkin.backend.utils.MarkdownUtil; +import funkin.backend.utils.EngineUtil; +import funkin.backend.utils.ThreadUtil; +import funkin.backend.utils.MemoryUtil; +import funkin.backend.utils.BitmapUtil; +import funkin.backend.utils.TranslationUtil; +#end \ No newline at end of file diff --git a/project.xml b/project.xml index b129dbb377..9afdad34de 100644 --- a/project.xml +++ b/project.xml @@ -118,6 +118,9 @@ + + + @@ -150,6 +153,7 @@ + diff --git a/source/funkin/backend/scripting/HScript.hx b/source/funkin/backend/scripting/HScript.hx index c3656478c6..4ad2e52a27 100644 --- a/source/funkin/backend/scripting/HScript.hx +++ b/source/funkin/backend/scripting/HScript.hx @@ -13,6 +13,10 @@ class HScript extends Script { //public var folderlessPath:String; var __importedPaths:Array; + #if HARDCODED_SCRIPTS + public var trackedSets:Array = []; + #end + public static function initParser() { var parser = new Parser(); parser.allowJSON = parser.allowMetadata = parser.allowTypes = true; @@ -146,6 +150,10 @@ class HScript extends Script { if (expr != null) { interp.execute(expr); call("new", []); + + #if HARDCODED_SCRIPTS + if (Flags.CONVERT_HSCRIPT_TO_SOURCE) HardcodedScriptPrinter.convertHscript(this); + #end } #if GLOBAL_SCRIPT @@ -195,6 +203,15 @@ class HScript extends Script { public override function set(val:String, value:Dynamic) { interp.variables.set(val, value); + #if HARDCODED_SCRIPTS + if (Flags.CONVERT_HSCRIPT_TO_SOURCE) { + if (!trackedSets.contains(val)) { + if (!(value is Class)) { + trackedSets.push(val); + } + } + } + #end } public override function trace(v:Dynamic) { diff --git a/source/funkin/backend/scripting/HardcodedScript.hx b/source/funkin/backend/scripting/HardcodedScript.hx new file mode 100644 index 0000000000..79404cfef2 --- /dev/null +++ b/source/funkin/backend/scripting/HardcodedScript.hx @@ -0,0 +1,99 @@ +package funkin.backend.scripting; + +#if HARDCODED_SCRIPTS +class HardcodedScript extends Script { + private var parent:T = null; + private var handleErrors:Bool = true; + private var disableAfterError:Bool = false; + private var publicVariables:Map = []; + private var dynamicVariables:Map = []; + + public override function get(v:String) { + if (Reflect.getProperty(this, v) != null) return Reflect.getProperty(this, v); + if (Reflect.getProperty(parent, v) != null) return Reflect.getProperty(parent, v); + return dynamicVariables.get(v); + } + public override function set(v:String, v2:Dynamic) { + if (v2 is Class) return; + + if (Reflect.getProperty(this, v) != null) { + Reflect.setProperty(this, v, v2); + return; + } + if (Reflect.getProperty(parent, v) != null) { + Reflect.setProperty(parent, v, v2); + return; + } + dynamicVariables.set(v, v2); + } + + public function getDynamic(v:String) { + return dynamicVariables.get(v); + } + public function setDynamic(v:String, v2:Dynamic) { + dynamicVariables.set(v, v2); + } + public function getPublic(v:String) { + return publicVariables.get(v); + } + public function setPublic(v:String, v2:Dynamic) { + publicVariables.set(v, v2); + } + public function getStatic(v:String) { + return Script.staticVariables.get(v); + } + public function setStatic(v:String, v2:Dynamic) { + Script.staticVariables.set(v, v2); + } + /*public function importScript(path:String) { + //TODO + }*/ + + public override function setParent(variable:Dynamic) { parent = variable; } + public override function setPublicMap(map:Map) { this.publicVariables = map; } + + public override function onCall(method:String, parameters:Array):Dynamic { + var func = Reflect.getProperty(this, method); + if (Reflect.isFunction(func)) { + if (handleErrors) { + var ret = null; + try { + ret = (parameters != null && parameters.length > 0) ? Reflect.callMethod(null, func, parameters) : func(); + } catch(e) { + _errorHandler(e); + } + return ret; + } + return (parameters != null && parameters.length > 0) ? Reflect.callMethod(null, func, parameters) : func(); + } + return null; + } + private function _errorHandler(error:haxe.Exception) { + var fileName = this.fileName; + for (item in error.stack) { + switch(item) { + case FilePos(s, file, line, col): + fileName = file + ":" + line; + default: + } + break; + } + + Logs.traceColored([ + Logs.logText(fileName + ': ', GREEN), + Logs.logText(error.toString(), RED) + ], ERROR); + + if (disableAfterError) { + active = false; + } + } + + public override function load() { + if (!didLoad) { + onCall("initVars", []); //initVars generated from HardcodedScriptVarMacro.hx + } + super.load(); + } +} +#end \ No newline at end of file diff --git a/source/funkin/backend/scripting/HardcodedScriptPrinter.hx b/source/funkin/backend/scripting/HardcodedScriptPrinter.hx new file mode 100644 index 0000000000..7bc77eadd9 --- /dev/null +++ b/source/funkin/backend/scripting/HardcodedScriptPrinter.hx @@ -0,0 +1,749 @@ +/* + * Copyright (C)2008-2017 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package funkin.backend.scripting; +#if HARDCODED_SCRIPTS + +import hscript.Tools; +import hscript.Expr; +import hscript.Printer; + +class HardcodedScriptPrinter { + + var script:HScript; + var parentInstanceFields:Array = []; + var currentFuncParams:Array> = []; + var currentForParams:Array> = []; + + var staticFields:Array = []; + var publicFields:Array = []; + + var usedPublic:Array = []; + var usedPublicTypes:Array = []; + var usedStatic:Array = []; + var usedStaticTypes:Array = []; + var usedDynamic:Array = []; + var usedDynamicTypes:Array = []; + + var imports:Array = []; + var usings:Array = []; + var funcStack:Int = 0; + + var funcsToForcePublicOverride = [ + "update", "draw", "destroy" + ]; + + var defaultFuncArgTypes:Map> = [ + "preUpdate" => ["Float"], + "update" => ["Float"], + "postUpdate" => ["Float"], + "stepHit" => ["Int"], + "beatHit" => ["Int"], + "measureHit" => ["Int"], + "draw" => ["DrawEvent"], + "postDraw" => ["DrawEvent"], + "onResize" => ["ResizeEvent"], + "gameResized" => ["Int", "Int"], + "preStateCreate" => ["FlxState"], + "onDance" => ["DanceEvent"], + "onTryDance" => ["CancellableEvent"], + "onPlaySingAnim" => ["DirectionAnimEvent"], + "playSingAnimUnsafe" => ["DirectionAnimEvent"], + "onPlayAnim" => ["PlayAnimEvent"], + "onGetCamPos" => ["PointEvent"], + "onPreGenerateStrums" => ["AmountEvent"], + "onPostGenerateStrums" => ["AmountEvent"], + "onRatingUpdate" => ["RatingUpdateEvent"], + "onStartCountdown" => ["CancellableEvent"], + "onCountdown" => ["CountdownEvent"], + "onPostCountdown" => ["CountdownEvent"], + "onSubstateOpen" => ["StateEvent"], + "onSubstateClose" => ["StateEvent"], + "onStateSwitch" => ["StateEvent"], + "onGamePause" => ["CancellableEvent"], + "onCameraMove" => ["CamMoveEvent"], + "onEvent" => ["EventGameEvent"], + "onPostEvent" => ["EventGameEvent"], + "onGameOver" => ["GameOverEvent"], + "onPostGameOver" => ["GameOverEvent"], + "onSongEnd" => ["CancellableEvent"], + "onPlayerMiss" => ["NoteMissEvent"], + "onPostPlayerMiss" => ["NoteMissEvent"], + "onNoteHit" => ["NoteHitEvent"], + "onPlayerHit" => ["NoteHitEvent"], + "onDadHit" => ["NoteHitEvent"], + "onHealthIconAnimChange" => ["HealthIconChangeEvent"], + "onNoteCreation" => ["NoteCreationEvent"], + "onPostNoteCreation" => ["NoteCreationEvent"], + "onSplashShown" => ["SplashShowEvent"], + "onStageXMLParsed" => ["StageXMLEvent"], + "onStageNodeParsed" => ["StageNodeEvent"], + "onPostStageCreation" => ["StageXMLEvent"], + "onStageDestroy" => ["Stage"], + "onInputUpdate" => ["InputSystemEvent"], + "onStrumCreation" => ["StrumCreationEvent"], + "onPostStrumCreation" => ["StrumCreationEvent"] + ]; + + var buf : StringBuf; + var tabs : String; + + public function new(script:HScript) { + this.script = script; + if (script != null && script.interp.scriptObject != null) { + parentInstanceFields = Type.getInstanceFields(Type.getClass(script.interp.scriptObject)); + } + } + + public function exprToString( e : Expr ):String { + buf = new StringBuf(); + tabs = ""; + expr(e); + return buf.toString(); + } + + public function typeToString( t : CType ):String { + buf = new StringBuf(); + tabs = ""; + type(t); + return buf.toString(); + } + + inline function add(s:T):Void buf.add(s); + + function type( t : CType ):Void { + switch( t ) { + case CTOpt(t): + add('?'); + type(t); + case CTPath(path, params): + add(path.join(".")); + if( params != null ) { + add("<"); + var first = true; + for( p in params ) { + if( first ) first = false else add(", "); + type(p); + } + add(">"); + } + case CTNamed(name, t): + add(name); + add(':'); + type(t); + case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))): + add('('); + for (a in args) + switch a { + case CTNamed(_, _): type(a); + default: type(CTNamed('_', a)); + } + add(')->'); + type(ret); + case CTFun(args, ret): + if( args.length == 0 ) + add("Void -> "); + else { + for( a in args ) { + type(a); + add(" -> "); + } + } + type(ret); + case CTAnon(fields): + add("{"); + var first = true; + for( f in fields ) { + if( first ) { first = false; add(" "); } else add(", "); + add(f.name + " : "); + type(f.t); + } + add(first ? "}" : " }"); + case CTParent(t): + add("("); + type(t); + add(")"); + case CTExpr(e): + expr(e); + } + } + + function addType( t : CType ):Void { + if( t != null ) { + add(" : "); + type(t); + } + } + + function expr( e : Expr ):Void { + if( e == null ) { + add("??NULL??"); + return; + } + switch(Tools.expr(e)) { + case EPackage(n): + /* + add('package'); + if(n != null) + add(' $n'); + add(';\n'); + */ + case EImport(c, n, u): + //var str = '${u ? 'using' : 'import'} $c'; + //if(n != null) + //str += ' as $n'; + + if (u) { + usings.push(c + (n != null ? ' as $n' : '')); + } else { + imports.push(c); + } + + //add('${u ? 'using' : 'import'} $c'); + //if(n != null) + //add(' as $n'); + case EClass(name, fields, extend, interfaces, fnal): + /* + var isFinal = fnal != null && fnal; + if(isFinal) + add('final '); + add('class $name'); + if (extend != null) + add(' extends $extend'); + for(_interface in interfaces) { + add(' implements $_interface'); + } + tabs += "\t"; + add(" {\n"); + for( e in fields ) { + add(tabs); + expr(e); + //add(";\n"); + } + //for(field in fields) { + // expr(field); + //} + + tabs = tabs.substr(1); + add("}"); + */ + case EEnum(en, _): + /* + add('enum ${en.name}'); + if(en.fields.length == 0) { + add(' {}'); + return; + } + tabs += "\t"; + add(" {\n"); + + for(e in en.fields) { + add(tabs); + add(e.name); + if(e.args.length > 0) { + add("("); + var first = true; + for( a in e.args ) { + if( first ) first = false else add(", "); + if( a.opt ) add("?"); + add(a.name); + addType(a.t); + } + add(')'); + } + add(";\n"); + } + + tabs = tabs.substr(1); + add("}"); + */ + case ECast(e, t): + var safe = t != null; + add("cast "); + if(safe) add("("); + expr(e); + if(safe) { + add(", "); + addType(t); + add(")"); + } + case ERegex(e, f): + add('~/$e/$f'); + add(';\n'); + case EConst(c): + switch( c ) { + case CInt(i): add(i); + case CFloat(f): add(f); + case CString(s): add('"'); add(s.split('"').join('\\"').split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")); add('"'); + } + case EIdent(v): + + var isLocal:Bool = false; + for (f in currentFuncParams) { + if (f.contains(v)) {isLocal = true; break;} + } + for (f in currentForParams) { + if (f.contains(v)) {isLocal = true; break;} + } + + if (v == "this") v = "parent"; + + if (isLocal) { + add(v); + } else if (parentInstanceFields.contains(v) || parentInstanceFields.contains("get_" + v)) { + add("parent."); + add(v); + } else if (script.interp.publicVariables.exists(v)) { + add(v); + if (!usedPublic.contains(v)) { + usedPublic.push(v); + usedPublicTypes.push(getVarTypeName(script.interp.publicVariables.get(v))); + } + } else if (Script.staticVariables.exists(v)) { + add(v); + if (!usedStatic.contains(v)) { + usedStatic.push(v); + usedStaticTypes.push(getVarTypeName(Script.staticVariables.get(v))); + } + } else if (script.trackedSets.contains(v)) { + add(v); + if (!usedDynamic.contains(v)) { + usedDynamic.push(v); + usedDynamicTypes.push(getVarTypeName(script.get(v))); + } + } else { + add(v); + } + case EVar(n, t, e, p, s, pr, isFinal, isInline, get, set, _): + var name = n; + if (funcStack == 0) { + if (s) { + add("@:staticVar "); + staticFields.push(n); + } else if (p) { + add("@:publicVar "); + publicFields.push(n); + } + + if(p) add(""); //disable + else if(pr) add("private "); + if(s) add(""); //disable + if(isInline) add("inline "); + } + if(isFinal) add("final " + name); + else add("var " + name); + + if(get != null || set != null) { + add("("); + switch(get) { + case ADefault: add("default, "); + case ANull: add("null, "); + case AGet: add("get, "); + case ADynamic: add("dynamic, "); + case ANever: add("never, "); + default: + } + switch(set) { + case ADefault: add("default"); + case ANull: add("null"); + case ASet: add("set"); + case ADynamic: add("dynamic"); + case ANever: add("never"); + default: + } + add(")"); + } + + addType(t); + if( e != null ) { + add(" = "); + expr(e); + } + case EParent(e): + add("("); expr(e); add(")"); + case EBlock(el): + if( el.length == 0 ) { + add("{}"); + } else { + tabs += "\t"; + add("{\n"); + for( e in el ) { + add(tabs); + expr(e); + add(";\n"); + } + tabs = tabs.substr(1); + add(tabs); + add("}"); + } + case EField(e, f, s): + expr(e); + add((s == true ? "?." : ".") + f); + case EBinop(op, e1, e2): + expr(e1); + add(" " + op + " "); + expr(e2); + case EUnop(op, pre, e): + if( pre ) { + add(op); + expr(e); + } else { + expr(e); + add(op); + } + case ECall(e, args): + if( e == null ) + expr(e); + else switch( Tools.expr(e)) { + case EField(_), EIdent(_), EConst(_): + expr(e); + default: + add("("); + expr(e); + add(")"); + } + add("("); + var first = true; + for( a in args ) { + if( first ) first = false else add(", "); + expr(a); + } + add(")"); + case EIf(cond,e1,e2): + add("if( "); + expr(cond); + add(" ) "); + expr(e1); + if( e2 != null ) { + add(" else "); + expr(e2); + } + case EWhile(cond,e): + add("while( "); + expr(cond); + add(" ) "); + expr(e); + case EDoWhile(cond,e): + add("do "); + expr(e); + add(" while ( "); + expr(cond); + add(" )"); + case EFor(v, it, e, ithv): + + if(ithv != null) { + add("for( "+ithv+" => "+v+" in "); + currentForParams.push([ithv, v]); + } else { + add("for( "+v+" in "); + currentForParams.push([v]); + } + expr(it); + add(" ) "); + expr(e); + currentForParams.pop(); + case EBreak: + add("break"); + case EContinue: + add("continue"); + case EFunction(params, e, n, ret, p, s, o, pr, isFinal, isInline): + var name = n; + if (funcStack == 0) { + if (s) { + add("@:staticVar "); + staticFields.push(n); + } else if (p) { + add("@:publicVar "); + publicFields.push(n); + } + + if (funcsToForcePublicOverride.contains(n)) { + o = true; + p = true; + } + + if(o) add("override "); + if (p) add("public "); + else if(pr) add("private "); + if(s) add(""); //disable + if(isInline) add("inline "); + if(isFinal) add("final "); + } + add("function"); + if( name != null ) + add(" " + name); + add("("); + var first = true; + var funcParamNames:Array = []; + for( i => a in params ) { + if( first ) first = false else add(", "); + if( a.opt ) add("?"); + add(a.name); + trace(name + ":" + a.name + ":" + a.t); + if (a.t == null) { + if (defaultFuncArgTypes.exists(name)) { + var arr = defaultFuncArgTypes.get(name); + trace(arr); + if (i < arr.length) add(" : " + arr[i]); + } + } else { + addType(a.t); + } + + funcParamNames.push(a.name); + } + currentFuncParams.push(funcParamNames); + add(")"); + addType(ret); + add(" "); + funcStack++; + expr(e); + funcStack--; + currentFuncParams.pop(); + case EReturn(e): + add("return"); + if( e != null ) { + add(" "); + expr(e); + } + case EArray(e,index): + expr(e); + add("["); + expr(index); + add("]"); + case EArrayDecl(el, _): + add("["); + var first = true; + for( e in el ) { + if( first ) first = false else add(", "); + expr(e); + } + add("]"); + case ENew(cl, args, params): + add("new " + cl); + if(params != null) { + add("<"); + var first = true; + for( p in params ) { + if( first ) first = false else add(", "); + type(p); + } + add(">"); + } + add("("); + var first = true; + for( e in args ) { + if( first ) first = false else add(", "); + expr(e); + } + add(")"); + case EThrow(e): + add("throw "); + expr(e); + case ETry(e, v, t, ecatch): + add("try "); + expr(e); + add(" catch( " + v); + addType(t); + add(") "); + expr(ecatch); + case EObject(fl): + if( fl.length == 0 ) { + add("{}"); + } else { + tabs += "\t"; + add("{\n"); + for( f in fl ) { + add(tabs); + add(f.name+" : "); + expr(f.e); + add(",\n"); + } + tabs = tabs.substr(1); + add(tabs); + add("}"); + } + case ETernary(c,e1,e2): + expr(c); + add(" ? "); + expr(e1); + add(" : "); + expr(e2); + case ESwitch(e, cases, def): + add("switch( "); + expr(e); + add(") {\n"); + tabs += "\t"; + for( c in cases ) { + add(tabs); + add("case "); + var first = true; + for( v in c.values ) { + if( first ) first = false else add(", "); + expr(v); + } + add(": "); + expr(c.expr); + add(";\n"); + } + if( def != null ) { + add(tabs); + add("default: "); + expr(def); + add(";\n"); + } + tabs = tabs.substr(1); + add(tabs); + add("}"); + case EMeta(name, args, e): + add("@"); + add(name); + if( args != null && args.length > 0 ) { + add("("); + var first = true; + for( a in args ) { + if( first ) first = false else add(", "); + expr(e); + } + add(")"); + } + add(" "); + expr(e); + case ECheckType(e, t): + add("("); + expr(e); + add(" : "); + addType(t); + add(")"); + } + } + + public static function errorToString( e : Error ):String { + var message = switch( #if hscriptPos e.e #else e #end ) { + case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")"; + case EUnexpected(s): "Unexpected token: \""+s+"\""; + case EUnterminatedString: "Unterminated string"; + case EUnterminatedComment: "Unterminated comment"; + case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")"; + case EUnknownVariable(v): "Unknown variable: "+v; + case EInvalidIterator(v): "Invalid iterator: "+v; + case EInvalidOp(op): "Invalid operator: "+op; + case EInvalidAccess(f): "Invalid access to field " + f; + case ECustom(msg): msg; + case EInvalidClass(cla): "Invalid class: " + cla + " was not found."; + case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.'; + }; + #if hscriptPos + return e.origin + ":" + e.line + ": " + message; + #else + return message; + #end + } + + private function getVarTypeName(obj:Dynamic) { + switch(Type.typeof(obj)) { + case TBool: + return "Bool"; + case TInt: + return "Int"; + case TFloat: + return "Float"; + case TFunction: + return "Dynamic"; //use dynamic for now + case TObject: + return "Dynamic"; + case TEnum(c): + return Type.getEnumName(c); + case TClass(c): + return Type.getClassName(c); + default: + return Type.getClassName(Type.getClass(obj)); + } + return "Dynamic"; + } + + public static function convertHscript(script:HScript) { + var printer = new HardcodedScriptPrinter(script); + var savePath = "scriptOutput/" + script.path.replace("assets/", ""); + var str = printer.exprToString(script.expr); + + var scriptClassName = "Script"; // + script.path.replace("assets/", "").replace("/", "_").replace(" ", "_").replace(".hx", "").replace(".", ""); + var parentClassName:String = Type.getClassName(Type.getClass(script.interp.scriptObject)); + if (parentClassName != null && parentClassName != "null" ) { + if (!printer.imports.contains(parentClassName)) printer.imports.push(parentClassName); + var splitPackage = parentClassName.split("."); + parentClassName = splitPackage[splitPackage.length-1]; + } else { + parentClassName = "Dynamic"; + } + + var scriptPackageName = script.path.replace("assets/", "").replace(".hx", ""); + scriptPackageName = scriptPackageName.substring(0, scriptPackageName.lastIndexOf("/")); + scriptPackageName = scriptPackageName.replace("/", ".").replace(" ", "_"); + + var redirects = Script.getDefaultImportRedirects(); + var importBlacklist:Array = ["Int", "String", "Float"]; + + var headerStr = "package scripts." + scriptPackageName + ";\n\n"; + for (imp in printer.imports) { + var i = imp; + if (importBlacklist.contains(imp)) continue; + if (redirects.exists(imp)) i = redirects.get(imp); + headerStr += "import " + i + ";\n"; + } + for (imp in printer.usings) { + headerStr += "using " + imp + ";\n"; + } + headerStr += "import funkin.backend.scripting.HardcodedScript;\n"; + headerStr += "\n"; + headerStr += '@:registerScript("' + script.path.replace("assets/", "") + '")\n'; + headerStr += "class " + scriptClassName + " extends HardcodedScript<" + parentClassName + ">\n"; + + var extraClassCode:String = ""; + + for (i => field in printer.usedPublic) { + if (!printer.publicFields.contains(field)) { + if (extraClassCode == "") extraClassCode = "\n"; + extraClassCode += "\t@:publicVarRef var " + field + ":" + printer.usedPublicTypes[i] + ";\n"; + } + } + for (i => field in printer.usedStatic) { + if (!printer.staticFields.contains(field)) { + if (extraClassCode == "") extraClassCode = "\n"; + extraClassCode += "\t@:staticVarRef var " + field + ":" + printer.usedStaticTypes[i] + ";\n"; + } + } + for (i => field in printer.usedDynamic) { + if (extraClassCode == "") extraClassCode = "\n"; + extraClassCode += "\t@:dynamicVarRef var " + field + ":" + printer.usedDynamicTypes[i] + ";\n"; + } + + str = str.replace("\t;\n", ""); + if (str.charAt(0) == "{") { + str = "{" + extraClassCode + str.substring(1); + } else { + str = "{\n\t" + extraClassCode + str + "\n}"; + } + + CoolUtil.safeSaveFile(savePath, headerStr + str); + } +} +#end \ No newline at end of file diff --git a/source/funkin/backend/scripting/HardcodedScriptRegistry.hx b/source/funkin/backend/scripting/HardcodedScriptRegistry.hx new file mode 100644 index 0000000000..8afdffb18a --- /dev/null +++ b/source/funkin/backend/scripting/HardcodedScriptRegistry.hx @@ -0,0 +1,48 @@ +package funkin.backend.scripting; + +import haxe.io.Path; + +#if HARDCODED_SCRIPTS +@:rtti +@:build(funkin.backend.system.macros.HardcodedScriptRegistryMacro.build()) +class HardcodedScriptRegistry { + + private static var data:Map = null; + + public static function getRegisteredScripts():Map { + if (data == null) { + data = []; + + var rtti = haxe.rtti.Rtti.getRtti(HardcodedScriptRegistry); + var registeredScripts:Array = []; + var registeredClasses:Array = []; + for (metadata in rtti.meta) { + if (metadata.name == "registeredScripts") { + registeredScripts = metadata.params; + } else if (metadata.name == "registeredClasses") { + registeredClasses = metadata.params; + } + } + for (i in 0...registeredScripts.length) { + var scriptPath = registeredScripts[i].replace("\"", "").trim(); + var classPath = registeredClasses[i].replace("\"", "").trim(); + if (scriptPath.endsWith(".hx")) scriptPath = Path.withoutExtension(scriptPath); + data.set(scriptPath, classPath); + } + } + return data; + } + + public static function getFolderScripts(path:String):Array { + var registeredScripts = getRegisteredScripts(); + var scripts:Array = []; + path = Path.addTrailingSlash(path); + for (key => val in registeredScripts) { + if (Path.addTrailingSlash(Path.directory(key)) == path) scripts.push(key); + } + return scripts; + } +} +#end + + diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx index 4a03275f2a..efdb50be98 100644 --- a/source/funkin/backend/scripting/Script.hx +++ b/source/funkin/backend/scripting/Script.hx @@ -215,6 +215,12 @@ class Script extends FlxBasic implements IFlxDestroyable { * @param path Path in assets */ public static function create(path:String):Script { + #if HARDCODED_SCRIPTS + if (Flags.PREFER_HARDCODED_SCRIPTS) { + var script = tryHardcodedScript(path); + if (script != null) return script; + } + #end if (Assets.exists(path)) { return switch(Path.extension(path).toLowerCase()) { case "hx" | "hscript" | "hsc" | "hxs": @@ -229,9 +235,33 @@ class Script extends FlxBasic implements IFlxDestroyable { new DummyScript(path); } } + #if HARDCODED_SCRIPTS + if (!Flags.PREFER_HARDCODED_SCRIPTS) { + var script = tryHardcodedScript(path); + if (script != null) return script; + } + #end return new DummyScript(path); } + #if HARDCODED_SCRIPTS + private static function tryHardcodedScript(path:String):Script { + var hardcodedScripts = HardcodedScriptRegistry.getRegisteredScripts(); + var hardcodedPath = Path.withoutExtension(path).replace("assets/", ""); + for (p in [hardcodedPath, hardcodedPath.replace("/LIB_assets", "")]) { + if (hardcodedScripts.exists(p)) { + var cl = Type.resolveClass(hardcodedScripts.get(p)); + if (cl != null) { + return Type.createInstance(cl, [path]); + } else { + trace("hardcoded script class is null?", p, hardcodedScripts.get(p)); + } + } + } + return null; + } + #end + /** * Creates a script from the string. The language is determined based on the path. * @param code code diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 37b74b92e9..960a9805d5 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -282,6 +282,11 @@ class Flags { public static var DEFAULT_GLSL_VERSION:String = "120"; @:also(funkin.backend.utils.HttpUtil.userAgent) public static var USER_AGENT:String = 'request'; + + #if HARDCODED_SCRIPTS + public static var CONVERT_HSCRIPT_TO_SOURCE:Bool = true; + public static var PREFER_HARDCODED_SCRIPTS:Bool = false; + #end // -- End of Codename's Default Flags -- /** diff --git a/source/funkin/backend/system/macros/HardcodedScriptRegistryMacro.hx b/source/funkin/backend/system/macros/HardcodedScriptRegistryMacro.hx new file mode 100644 index 0000000000..8c62809420 --- /dev/null +++ b/source/funkin/backend/system/macros/HardcodedScriptRegistryMacro.hx @@ -0,0 +1,54 @@ +package funkin.backend.system.macros; + +#if HARDCODED_SCRIPTS +#if macro +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.ExprTools; +#end + +class HardcodedScriptRegistryMacro { + #if macro + public static function build() { + var fields = Context.getBuildFields(); + Context.onGenerate(findRegisteredScripts); + return fields; + } + + static function findRegisteredScripts(allTypes:Array) { + var registeredScripts:Array = []; + var registeredClasses:Array = []; + for (type in allTypes) { + switch (type) { + case TInst(cl,_): + var c = cl.get(); + var meta = c.meta.get(); + for (m in meta) { + if (m.name == ":registerScript") { + var classPath:String = c.module; + if (classPath.indexOf(".") != -1) { + classPath = classPath.substring(0, classPath.lastIndexOf(".")+1) + c.name; + } + registeredClasses.push(macro $v{classPath}); + registeredScripts.push(m.params[0]); + } + } + default: + } + } + + //store within metadata, which we can get at runtime + //based on how this lib does it: https://github.com/jasononeil/compiletime/blob/master/src/CompileTime.hx#L268 + switch (Context.getType("funkin.backend.scripting.HardcodedScriptRegistry")) { + case TInst(cl, _): + var c = cl.get(); + if (c.meta.has('registeredScripts')) c.meta.remove('registeredScripts'); + c.meta.add('registeredScripts', registeredScripts, Context.currentPos()); + if (c.meta.has('registeredClasses')) c.meta.remove('registeredClasses'); + c.meta.add('registeredClasses', registeredClasses, Context.currentPos()); + default: + } + } + #end +} +#end \ No newline at end of file diff --git a/source/funkin/backend/system/macros/HardcodedScriptVarMacro.hx b/source/funkin/backend/system/macros/HardcodedScriptVarMacro.hx new file mode 100644 index 0000000000..613e734559 --- /dev/null +++ b/source/funkin/backend/system/macros/HardcodedScriptVarMacro.hx @@ -0,0 +1,137 @@ +package funkin.backend.system.macros; + +import haxe.macro.TypeTools; +#if HARDCODED_SCRIPTS +#if macro +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.ExprTools; +using haxe.macro.Tools; +#end + +class HardcodedScriptVarMacro { + + #if macro + + public static function build() + { + var fields = Context.getBuildFields(); + + var newFields:Array = []; + + var varInits:Array = []; + + for (field in fields) { + for (meta in field.meta) { + + if (meta.name == ":publicVar" || meta.name == ":publicVarRef" || meta.name == ":staticVar" || meta.name == ":staticVarRef" || meta.name == ":dynamicVar" || meta.name == ":dynamicVarRef") { + var name:String = field.name; + var ret:Null; + switch(field.kind) { + case FVar(t, e): + if (t != null) { + ret = t; + field.kind = FieldType.FProp("get", "set", t); + } + if (meta.name == ":publicVar" && e != null) { + varInits.push(macro {setPublic($v{name}, ${e});}); + } else if (meta.name == ":staticVar" && e != null) { + varInits.push(macro {setStatic($v{name}, ${e});}); + } else if (meta.name == ":dynamicVar" && e != null) { + varInits.push(macro {setDynamic($v{name}, ${e});}); + } + case FFun(f): + field.name = "__" + field.name; + var argTypes:Array = []; + for (a in f.args) { + argTypes.push(a.type); + } + ret = macro : Dynamic; //just do dynamic for now, should still work + //ret = TFunction(argTypes, f.ret); + //ret = TypeTools.toComplexType() + newFields.push({ + name: name, + access: [Access.APrivate], + kind: FieldType.FProp("get", "set", ret), + pos: Context.currentPos(), + }); + + if (meta.name == ":publicVar") { + varInits.push(macro {setPublic($v{name}, $i{field.name});}); + } else if (meta.name == ":staticVar") { + varInits.push(macro {setStatic($v{name}, $i{field.name});}); + } else if (meta.name == ":dynamicVar") { + varInits.push(macro {setDynamic($v{name}, $i{field.name});}); + } + default: + } + + var getFuncExpr:Expr = null; + var setFuncExpr:Expr = null; + + if (meta.name == ":publicVar" || meta.name == ":publicVarRef") { + getFuncExpr = macro return getPublic($v{name}); + setFuncExpr = macro { + setPublic($v{name}, value); + return value; + }; + } else if (meta.name == ":staticVar" || meta.name == ":staticVarRef") { + getFuncExpr = macro return getStatic($v{name}); + setFuncExpr = macro { + setStatic($v{name}, value); + return value; + }; + } else if (meta.name == ":dynamicVar" || meta.name == ":dynamicVarRef") { + getFuncExpr = macro return getDynamic($v{name}); + setFuncExpr = macro { + setDynamic($v{name}, value); + return value; + }; + } + + var getterField:Field = { + name: "get_" + name, + access: [Access.APrivate, Access.AInline], + kind: FieldType.FFun({ + expr: getFuncExpr, + ret: ret, + args: [], + }), + pos: Context.currentPos(), + }; + var setterField:Field = { + name: "set_" + name, + access: [Access.APrivate, Access.AInline], + kind: FieldType.FFun({ + expr: setFuncExpr, + ret: ret, + args: [{name: "value", type: ret}] + }), + pos: Context.currentPos(), + }; + newFields.push(getterField); + newFields.push(setterField); + } + } + } + for (field in newFields) fields.push(field); + + fields.push({ + pos: Context.currentPos(), + name: "initVars", + kind: FFun({ + args: [], + expr: { + pos: Context.currentPos(), + expr: EBlock(varInits) + } + }), + access: [Access.APrivate] + }); + + return fields; + } + + #end +} +#end \ No newline at end of file diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx index 801eda0c17..823f77190c 100644 --- a/source/funkin/backend/system/macros/Macros.hx +++ b/source/funkin/backend/system/macros/Macros.hx @@ -22,6 +22,7 @@ class Macros { #if THREE_D_SUPPORT "away3d", "flx3d", #end #if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end #if NAPE_ENABLED "nape", "flixel.addons.nape", #end + #if HARDCODED_SCRIPTS "scripts", #end // BASE HAXE "DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting", "animate" ]) @@ -63,6 +64,10 @@ class Macros { if (Context.defined("hscript_improved") && !Context.defined("hscript")) { Compiler.define('hscript'); } + + #if HARDCODED_SCRIPTS + Compiler.addGlobalMetadata('scripts', '@:build(funkin.backend.system.macros.HardcodedScriptVarMacro.build())'); + #end } public static function buildLimeAssetLibrary():Array { diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index b4e7a29099..fa4f8bf7b2 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -750,6 +750,12 @@ class PlayState extends MusicBeatState addScript(file); } + + #if HARDCODED_SCRIPTS + for (script in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts(folder)) { + scripts.add(Script.create("assets/" + script)); + } + #end } var songEvents:Array = []; From 59015d6149e7b7e7c783c0a0bda7a22d7ddab54b Mon Sep 17 00:00:00 2001 From: TheZoroForce240 <86524550+TheZoroForce240@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:03:23 +0100 Subject: [PATCH 2/3] setup event and note type loading for hardcoded scripts --- .../scripting/HardcodedScriptPrinter.hx | 2 -- .../scripting/HardcodedScriptRegistry.hx | 6 +++++ .../editors/charter/CharterNoteTypesList.hx | 6 +++++ source/funkin/game/PlayState.hx | 27 ++++++++++++++----- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/source/funkin/backend/scripting/HardcodedScriptPrinter.hx b/source/funkin/backend/scripting/HardcodedScriptPrinter.hx index 7bc77eadd9..6d1ec73dc0 100644 --- a/source/funkin/backend/scripting/HardcodedScriptPrinter.hx +++ b/source/funkin/backend/scripting/HardcodedScriptPrinter.hx @@ -497,11 +497,9 @@ class HardcodedScriptPrinter { if( first ) first = false else add(", "); if( a.opt ) add("?"); add(a.name); - trace(name + ":" + a.name + ":" + a.t); if (a.t == null) { if (defaultFuncArgTypes.exists(name)) { var arr = defaultFuncArgTypes.get(name); - trace(arr); if (i < arr.length) add(" : " + arr[i]); } } else { diff --git a/source/funkin/backend/scripting/HardcodedScriptRegistry.hx b/source/funkin/backend/scripting/HardcodedScriptRegistry.hx index 8afdffb18a..56734ddab9 100644 --- a/source/funkin/backend/scripting/HardcodedScriptRegistry.hx +++ b/source/funkin/backend/scripting/HardcodedScriptRegistry.hx @@ -42,6 +42,12 @@ class HardcodedScriptRegistry { } return scripts; } + + public static function exists(path:String) { + var registeredScripts = getRegisteredScripts(); + if (path.startsWith("assets/")) path = path.replace("assets/", ""); + return registeredScripts.exists(path); + } } #end diff --git a/source/funkin/editors/charter/CharterNoteTypesList.hx b/source/funkin/editors/charter/CharterNoteTypesList.hx index d9a2b66f30..75e1181ae3 100644 --- a/source/funkin/editors/charter/CharterNoteTypesList.hx +++ b/source/funkin/editors/charter/CharterNoteTypesList.hx @@ -55,6 +55,12 @@ class CharterNoteTypesList extends UISubstateWindow { var file:String = Path.withoutDirectory(path); list.pushOnce(file); } + #if HARDCODED_SCRIPTS + for (path in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts(pathString)) { + var file:String = Path.withoutDirectory(path); + list.pushOnce(file); + } + #end return list; } diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index c4fb163b7c..d73225ceac 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -752,8 +752,10 @@ class PlayState extends MusicBeatState } #if HARDCODED_SCRIPTS - for (script in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts(folder)) { - scripts.add(Script.create("assets/" + script)); + for (file in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts(folder)) { + if (!scripts.contains("assets/" + file)) { + scripts.add(Script.create("assets/" + file)); + } } #end } @@ -767,6 +769,15 @@ class PlayState extends MusicBeatState addScript(file); } } + + #if HARDCODED_SCRIPTS + for (file in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts('data/events/')) { + var fileName:String = CoolUtil.getFilename(file); + if (EventsData.eventsList.contains(fileName) && songEvents.contains(fileName) && !scripts.contains("assets/" + file)) { + scripts.add(Script.create("assets/" + file)); + } + } + #end } } @@ -792,11 +803,13 @@ class PlayState extends MusicBeatState for(noteType in SONG.noteTypes) { var scriptPath = Paths.script('data/notes/${noteType}'); - if (Assets.exists(scriptPath) && !scripts.contains(scriptPath)) { - var script = Script.create(scriptPath); - if (!(script is DummyScript)) { - scripts.add(script); - script.load(); + if (!scripts.contains(scriptPath)) { + if (Assets.exists(scriptPath) #if HARDCODED_SCRIPTS || funkin.backend.scripting.HardcodedScriptRegistry.exists(scriptPath) #end) { + var script = Script.create(scriptPath); + if (!(script is DummyScript)) { + scripts.add(script); + script.load(); + } } } } From 79b707a2ef852cae8aa10e2fd807abd1fa2aac3b Mon Sep 17 00:00:00 2001 From: TheZoroForce240 <86524550+TheZoroForce240@users.noreply.github.com> Date: Sat, 1 Aug 2026 13:54:28 +0100 Subject: [PATCH 3/3] more hardcoded script stuff switch to using macro autobuild instead of adding global metadata for the scripts package, clean up some other stuff and add optional flag to auto disable scripts on error --- source/funkin/backend/scripting/HScript.hx | 1 + .../backend/scripting/HardcodedScript.hx | 69 +++++++++---------- source/funkin/backend/scripting/Script.hx | 5 ++ source/funkin/backend/system/Flags.hx | 2 + source/funkin/backend/system/macros/Macros.hx | 4 -- 5 files changed, 40 insertions(+), 41 deletions(-) diff --git a/source/funkin/backend/scripting/HScript.hx b/source/funkin/backend/scripting/HScript.hx index 4ad2e52a27..55f74ab2b2 100644 --- a/source/funkin/backend/scripting/HScript.hx +++ b/source/funkin/backend/scripting/HScript.hx @@ -120,6 +120,7 @@ class HScript extends Script { Logs.logText(fn, GREEN), Logs.logText(err, RED) ], ERROR); + _onError(); } private function _warnHandler(error:Error) { diff --git a/source/funkin/backend/scripting/HardcodedScript.hx b/source/funkin/backend/scripting/HardcodedScript.hx index 79404cfef2..c266b4e953 100644 --- a/source/funkin/backend/scripting/HardcodedScript.hx +++ b/source/funkin/backend/scripting/HardcodedScript.hx @@ -1,56 +1,45 @@ package funkin.backend.scripting; #if HARDCODED_SCRIPTS +@:autoBuild(funkin.backend.system.macros.HardcodedScriptVarMacro.build()) class HardcodedScript extends Script { private var parent:T = null; + private var instanceFields:Array = []; private var handleErrors:Bool = true; - private var disableAfterError:Bool = false; private var publicVariables:Map = []; private var dynamicVariables:Map = []; + public var importScript:String->Script = null; + public var disableScript:Void->Void = null; + + public function new(path:String) { + instanceFields = Type.getInstanceFields(Type.getClass(this)); + super(path); + } + public override function get(v:String) { - if (Reflect.getProperty(this, v) != null) return Reflect.getProperty(this, v); - if (Reflect.getProperty(parent, v) != null) return Reflect.getProperty(parent, v); + if (instanceFields.contains(v) || instanceFields.contains("get_" + v)) return Reflect.getProperty(this, v); return dynamicVariables.get(v); } public override function set(v:String, v2:Dynamic) { if (v2 is Class) return; - if (Reflect.getProperty(this, v) != null) { + if (instanceFields.contains(v) || instanceFields.contains("set_" + v)) { Reflect.setProperty(this, v, v2); return; } - if (Reflect.getProperty(parent, v) != null) { - Reflect.setProperty(parent, v, v2); - return; - } dynamicVariables.set(v, v2); } - public function getDynamic(v:String) { - return dynamicVariables.get(v); - } - public function setDynamic(v:String, v2:Dynamic) { - dynamicVariables.set(v, v2); - } - public function getPublic(v:String) { - return publicVariables.get(v); - } - public function setPublic(v:String, v2:Dynamic) { - publicVariables.set(v, v2); - } - public function getStatic(v:String) { - return Script.staticVariables.get(v); - } - public function setStatic(v:String, v2:Dynamic) { - Script.staticVariables.set(v, v2); - } - /*public function importScript(path:String) { - //TODO - }*/ + public inline function getDynamic(v:String) { return dynamicVariables.get(v); } + public inline function setDynamic(v:String, v2:Dynamic) { dynamicVariables.set(v, v2); } + public inline function getPublic(v:String) { return publicVariables.get(v); } + public inline function setPublic(v:String, v2:Dynamic) { publicVariables.set(v, v2); } + public inline function getStatic(v:String) { return Script.staticVariables.get(v); } + public inline function setStatic(v:String, v2:Dynamic) { Script.staticVariables.set(v, v2); } public override function setParent(variable:Dynamic) { parent = variable; } - public override function setPublicMap(map:Map) { this.publicVariables = map; } + public override function setPublicMap(map:Map) { publicVariables = map; } public override function onCall(method:String, parameters:Array):Dynamic { var func = Reflect.getProperty(this, method); @@ -69,24 +58,30 @@ class HardcodedScript extends Script { return null; } private function _errorHandler(error:haxe.Exception) { - var fileName = this.fileName; + var fileName:String = ""; + var fileLine:Int = 0; + var fileMethod:Null = null; + for (item in error.stack) { switch(item) { case FilePos(s, file, line, col): - fileName = file + ":" + line; + fileName = file; + fileLine = line; + switch(s) { + case Method(classname, method): + fileMethod = method; + default: + } default: } break; } Logs.traceColored([ - Logs.logText(fileName + ': ', GREEN), + Logs.logText(fileName + (fileMethod != null ? ":" + fileMethod : "") + ":" + fileLine + ': ', GREEN), Logs.logText(error.toString(), RED) ], ERROR); - - if (disableAfterError) { - active = false; - } + _onError(); } public override function load() { diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx index efdb50be98..6b6e6ba94a 100644 --- a/source/funkin/backend/scripting/Script.hx +++ b/source/funkin/backend/scripting/Script.hx @@ -403,6 +403,11 @@ class Script extends FlxBasic implements IFlxDestroyable { Logs.logText(fileName, RED), Logs.logText(text) ], ERROR); + _onError(); + } + + private function _onError() { + if (Flags.DISABLE_SCRIPT_ON_ERROR) active = false; } override public function toString():String { diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 73eb2362f0..91df28c35b 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -290,6 +290,8 @@ class Flags { public static var CONVERT_HSCRIPT_TO_SOURCE:Bool = true; public static var PREFER_HARDCODED_SCRIPTS:Bool = false; #end + public static var DISABLE_SCRIPT_ON_ERROR:Bool = false; + // -- End of Codename's Default Flags -- /** diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx index 823f77190c..b1cfd5390d 100644 --- a/source/funkin/backend/system/macros/Macros.hx +++ b/source/funkin/backend/system/macros/Macros.hx @@ -64,10 +64,6 @@ class Macros { if (Context.defined("hscript_improved") && !Context.defined("hscript")) { Compiler.define('hscript'); } - - #if HARDCODED_SCRIPTS - Compiler.addGlobalMetadata('scripts', '@:build(funkin.backend.system.macros.HardcodedScriptVarMacro.build())'); - #end } public static function buildLimeAssetLibrary():Array {