From cd9441c8c89285e2ada2aa03c45f03025768b945 Mon Sep 17 00:00:00 2001 From: KeppyMarbles Date: Fri, 7 Aug 2026 00:56:43 -0500 Subject: [PATCH 1/2] try smoothie integration --- src/LocalLevelLoader.hx | 91 +++++++++++++++++++++++++++++++++++++++++ src/Main.hx | 2 + src/Mission.hx | 6 +++ src/ResourceLoader.hx | 23 +++++++++++ 4 files changed, 122 insertions(+) create mode 100644 src/LocalLevelLoader.hx diff --git a/src/LocalLevelLoader.hx b/src/LocalLevelLoader.hx new file mode 100644 index 00000000..7731f7dd --- /dev/null +++ b/src/LocalLevelLoader.hx @@ -0,0 +1,91 @@ +package src; + +import haxe.io.Bytes; +import haxe.io.Path; +import mis.MisParser; +import src.Mission; + +class LocalLevelLoader { + public static var currentLocalPath:String = null; + public static var currentMission:Mission = null; + + public static function init() { + #if js + setupWebPostMessage(); + notifyReady(); + #end + } + + public static function notifyReady() { + #if js + if (js.Browser.window.opener != null) { + try { + Console.log("Notifying level editor that MBHaxe is ready..."); + js.Browser.window.opener.postMessage({ type: "mbhaxe-ready" }, "*"); + } catch (e:Dynamic) {} + } + try { + var channel = new js.html.BroadcastChannel("mbhaxe-level-editor"); + channel.postMessage({ type: "mbhaxe-ready" }); + } catch (e:Dynamic) {} + #end + } + + static function setupWebPostMessage() { + #if js + function handleMessage(data:Dynamic) { + if (data == null || data.type != "loadLevel" || data.files == null) return; + Console.log("Received level load message via postMessage/BroadcastChannel"); + + var mission:Mission = null; + + var filesAccess:haxe.DynamicAccess = data.files; + for (k in filesAccess.keys()) { + var fileData:Dynamic = filesAccess.get(k); + var bytes:Bytes = null; + if (Std.isOfType(fileData, String)) { + bytes = Bytes.ofString(fileData); + } else { + bytes = Bytes.ofData(fileData); + } + ResourceLoader.registerLocalFile(k, bytes); + + if (StringTools.endsWith(k.toLowerCase(), ".mis")) { + mission = createLocalMission(k, bytes.toString()); + } + } + + if (mission == null) { + Console.error("postMessage loadLevel missing .mis file in files payload"); + return; + } + + MarbleGame.instance.playMission(mission); + } + + js.Browser.window.addEventListener("message", (e:js.html.MessageEvent) -> { + handleMessage(e.data); + }); + + try { + var channel = new js.html.BroadcastChannel("mbhaxe-level-editor"); + channel.onmessage = (e:js.html.MessageEvent) -> { + handleMessage(e.data); + }; + } catch (e:Dynamic) {} + #end + } + + public static function createLocalMission(misRelPath:String, misText:String):Mission { + var misParser = new MisParser(misText); + var parsed = misParser.parse(); + var mInfo = misParser.parseMissionInfo(); + + var mission = Mission.fromMissionInfo(misRelPath, mInfo); + mission.isCustom = true; + mission.game = mInfo.game?.toLowerCase(); + + currentMission = mission; + return mission; + } +} \ No newline at end of file diff --git a/src/Main.hx b/src/Main.hx index 72c8b48d..6ab75267 100644 --- a/src/Main.hx +++ b/src/Main.hx @@ -7,6 +7,7 @@ import src.Console; import hxd.Key; import src.Util; import src.ResourceLoader; +import src.LocalLevelLoader; #if js import fs.ManifestFileSystem; import fs.ManifestBuilder; @@ -108,6 +109,7 @@ class Main extends hxd.App { new ProfilerUI(s2d); + LocalLevelLoader.init(); loaded = true; }); } catch (e) { diff --git a/src/Mission.hx b/src/Mission.hx index 62b7b6d1..bb260a2f 100644 --- a/src/Mission.hx +++ b/src/Mission.hx @@ -237,6 +237,10 @@ class Mission { #end if (!StringTools.endsWith(path, ".dif")) path += ".dif"; + if (ResourceLoader.existsInZip(path)) + return path; + if (ResourceLoader.existsInZip(fname)) + return fname; if (ResourceLoader.exists(path)) return path; if (StringTools.contains(path, 'interiors_mbg/')) @@ -254,6 +258,8 @@ class Mission { path = StringTools.replace(path, "lbinteriors", "interiors"); // This shit ew if (ResourceLoader.exists(path)) return path; + if (ResourceLoader.exists(fname)) + return fname; Console.error("Interior resource not found: " + rawElementPath); return ""; } diff --git a/src/ResourceLoader.hx b/src/ResourceLoader.hx index 7cae151d..c28fab2a 100644 --- a/src/ResourceLoader.hx +++ b/src/ResourceLoader.hx @@ -283,10 +283,16 @@ class ResourceLoader { #if (js || android) path = StringTools.replace(path, "data/", ""); #end + if (ResourceLoader.existsInZip(path)) + return path; + if (ResourceLoader.existsInZip(fname)) + return fname; if (ResourceLoader.exists(path)) return path; if (ResourceLoader.exists(dirpath + fname)) return dirpath + fname; + if (ResourceLoader.exists(fname)) + return fname; return ""; } @@ -553,6 +559,13 @@ class ResourceLoader { return fileSystem.exists(path); } + public static function existsInZip(path:String):Bool { + #if (js || android) + path = StringTools.replace(path, "data/", ""); + #end + return zipFilesystem.exists(path.toLowerCase()); + } + public static function clearInteriorResources() { interiorResources = new Map(); } @@ -610,4 +623,14 @@ class ResourceLoader { zipFilesystem.set(fname, zfe); } } + + public static function registerLocalFile(path:String, bytes:haxe.io.Bytes) { + Console.log(path); + path = StringTools.replace(path, "\\", "/").toLowerCase(); + #if (js || android) + path = StringTools.replace(path, "data/", ""); + #end + var zfe = new BytesFileEntry(path, bytes); + zipFilesystem.set(path, zfe); + } } From 7ba4cab1eeeb094efac5b6e3cb016c24aaa83c34 Mon Sep 17 00:00:00 2001 From: KeppyMarbles Date: Mon, 10 Aug 2026 20:27:03 -0500 Subject: [PATCH 2/2] don't prompt scores for local level --- src/LocalLevelLoader.hx | 4 ++++ src/Mission.hx | 1 + src/ResourceLoader.hx | 1 - src/gui/EndGameGui.hx | 4 ++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/LocalLevelLoader.hx b/src/LocalLevelLoader.hx index 7731f7dd..b9c93e72 100644 --- a/src/LocalLevelLoader.hx +++ b/src/LocalLevelLoader.hx @@ -4,6 +4,9 @@ import haxe.io.Bytes; import haxe.io.Path; import mis.MisParser; import src.Mission; +import src.ResourceLoader; +import src.MarbleGame; +import src.Console; class LocalLevelLoader { public static var currentLocalPath:String = null; @@ -83,6 +86,7 @@ class LocalLevelLoader { var mission = Mission.fromMissionInfo(misRelPath, mInfo); mission.isCustom = true; + mission.isLocal = true; mission.game = mInfo.game?.toLowerCase(); currentMission = mission; diff --git a/src/Mission.hx b/src/Mission.hx index bb260a2f..45e4e138 100644 --- a/src/Mission.hx +++ b/src/Mission.hx @@ -41,6 +41,7 @@ class Mission { public var game:String; public var hasEgg:Bool; public var isCustom:Bool; + public var isLocal:Bool = false; public var gameMode:String; #if hl public var addedAt:Int64; diff --git a/src/ResourceLoader.hx b/src/ResourceLoader.hx index c28fab2a..9e8be8aa 100644 --- a/src/ResourceLoader.hx +++ b/src/ResourceLoader.hx @@ -625,7 +625,6 @@ class ResourceLoader { } public static function registerLocalFile(path:String, bytes:haxe.io.Bytes) { - Console.log(path); path = StringTools.replace(path, "\\", "/").toLowerCase(); #if (js || android) path = StringTools.replace(path, "data/", ""); diff --git a/src/gui/EndGameGui.hx b/src/gui/EndGameGui.hx index cc293be8..c56ee2d7 100644 --- a/src/gui/EndGameGui.hx +++ b/src/gui/EndGameGui.hx @@ -396,7 +396,7 @@ class EndGameGui extends GuiControl { var rewindUsed = MarbleGame.instance.world.rewindUsed; var cheatsUsed = MarbleGame.instance.world.cheatsUsed; - if (idx <= 4) { + if (idx <= 4 && !mission.isLocal) { setButtonStates(false); var end = new EnterNameDlg(idx, (name) -> { setButtonStates(true); @@ -440,7 +440,7 @@ class EndGameGui extends GuiControl { scoreSubmitted = true; }); this.addChild(end); - } else { + } else if (!mission.isLocal) { // Check if we can submit LB scores var lbPath = mission.path; if (mission.isClaMission)