From f751946b40f544a87c39baf5406eeed8a15f93fb Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:47:16 -0800 Subject: [PATCH 01/13] Create Pause-Utilities.js --- extensions/SharkPool/Pause-Utilities.js | 347 ++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 extensions/SharkPool/Pause-Utilities.js diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js new file mode 100644 index 0000000000..958a040e53 --- /dev/null +++ b/extensions/SharkPool/Pause-Utilities.js @@ -0,0 +1,347 @@ +// Name: Pause Utilities +// ID: SPPause +// Description: Pause the Project and certain Scripts +// By: SharkPool + +// Version V.1.6.0 + +(function (Scratch) { + "use strict"; + if (!Scratch.extensions.unsandboxed) alert("Pause Utilities Extension must run unsandboxed!"); + const vm = Scratch.vm; + const runtime = Scratch.vm.runtime; + const Cast = Scratch.Cast; + let storedScripts = {}; + let projectPaused = false; + + const menuIconURI = +"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTgiIHdpZHRoPSIxOCI+PHBhdGggZD0iTTIzMS40MjkgMTg4LjkyOVYxNzEuMDdoNC4yODV2MTcuODU4em0xMi4xNDIgMFYxNzEuMDdoNC4yODZ2MTcuODU4eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzMwOSAwIDAgLjk1NDI3IC0yMzguNTczIC0xNjIuNzY5KSIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsPSIjZmZhZTAwIiBzdHJva2U9IiNkODk0MDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOm5vcm1hbCIvPjwvc3ZnPg=="; + + const blockIconURI = +"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMS40NzcxNCIgaGVpZ2h0PSIzMS40NzcxNCIgdmlld0JveD0iMCwwLDMxLjQ3NzE0LDMxLjQ3NzE0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI0LjI2MTQzLC0xNjQuMjYxNDMpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0yMjQuMjYxNDMsMTk1LjczODU3di0zMS40NzcxNGgzMS40NzcxNHYzMS40NzcxNHoiIGZpbGw9IiM1ZjViNDkiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTIzMS41MjgxOSwxODguNDk5MTN2LTE3LjA0MjMxaDQuNDI2Nzl2MTcuMDQxMzV6TTI0NC4wNzE5NiwxODguNDk5MTN2LTE3LjA0MjMxaDQuNDI3ODJ2MTcuMDQxMzV6IiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9IiNmZmFlMDAiIHN0cm9rZT0iI2Q4OTQwMCIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9nPjwvZz48L3N2Zz4="; + + runtime.on("PROJECT_STOP_ALL", () => { storedScripts = {} }); + + function beginScan() { + let isChecking = false; + const check = () => { + if (isChecking) return; + const isPM = Scratch.extensions.isPenguinMod; + isChecking = true; + try { + projectPaused = runtime.ioDevices.clock._paused; + if (!projectPaused && !isPM) return; + const allUtils = vm.runtime.targets; + for (let i = 0; i < allUtils.length; i++) { + const util = allUtils[i]; + const object = util.blocks._blocks; + const keysV = getKeysByValue(object, "opcode", "SPPause_whenProjectPaused"); + for (const keyV of keysV) { + if (projectPaused) vm.setEditingTarget(util.id); + if (runtime.threads) { + let threadExists = runtime.threads.some(thread => thread.topBlock === object[keyV].id); + if (!threadExists) { + if (projectPaused) { + vm.runtime.toggleScript(object[keyV].id, util); + checkReset(object[keyV].id); + } else if (isPM) { checkReset(object[keyV].id) } + } else { checkReset(object[keyV].id) } + } + } + } + if (isPM) vm.runtime._step(); + } catch {} finally { + isChecking = false; + setTimeout(check, 10); + } + }; + check(); + } + function getKeysByValue(object, name, value) { + const keys = []; + for (const key in object) { + if (object[key][name] === value) keys.push(key); + } + return keys; + } + function checkReset(ID) { + const threadExists = runtime.threads.find(thread => thread.topBlock === ID); + if (threadExists.status === undefined) threadExists.status = 4; + } + + class SPPause { + getInfo() { + return { + id: "SPPause", + name: "Pause Utilities", + color1: "#5f5b49", + menuIconURI, + blockIconURI, + blocks: [ + { blockType: Scratch.BlockType.LABEL, text: "Project Control" }, + { + opcode: "pause", + blockType: Scratch.BlockType.COMMAND, + text: "pause project" + }, + { + opcode: "unpause", + blockType: Scratch.BlockType.COMMAND, + text: "unpause project" + }, + { + opcode: "whenProjectPaused", + blockType: Scratch.BlockType.EVENT, + text: "when project is paused", + isEdgeActivated: false + }, + { + opcode: "isProjectPaused", + blockType: Scratch.BlockType.BOOLEAN, + text: "is project paused?" + }, + { blockType: Scratch.BlockType.LABEL, text: "Sprite Control" }, + { + opcode: "pauseSprite", + blockType: Scratch.BlockType.COMMAND, + text: "pause [SPRITE]", + arguments: { + SPRITE: { + type: Scratch.ArgumentType.STRING, + menu: "TARGETS" + } + } + }, + { + opcode: "unpauseSprite", + blockType: Scratch.BlockType.COMMAND, + text: "unpause [SPRITE]", + arguments: { + SPRITE: { + type: Scratch.ArgumentType.STRING, + menu: "TARGETS" + } + } + }, + "---", + { + opcode: "pauseClones", + blockType: Scratch.BlockType.COMMAND, + text: "pause clones of [SPRITE] with [VAR] set to [NUM]", + arguments: { + SPRITE: { + type: Scratch.ArgumentType.STRING, + menu: "TARGETS2" + }, + VAR: { + type: Scratch.ArgumentType.STRING, + defaultValue: "my variable" + }, + NUM: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 0 + } + } + }, + { + opcode: "unpauseClones", + blockType: Scratch.BlockType.COMMAND, + text: "unpause clones of [SPRITE] with [VAR] set to [NUM]", + arguments: { + SPRITE: { + type: Scratch.ArgumentType.STRING, + menu: "TARGETS2" + }, + VAR: { + type: Scratch.ArgumentType.STRING, + defaultValue: "my variable" + }, + NUM: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 0 + } + } + }, + { blockType: Scratch.BlockType.LABEL, text: "Script Control" }, + { + opcode: "pauseLoop", + blockType: Scratch.BlockType.COMMAND, + text: "pause this script with ID [NAME]", + arguments: { + NAME: { + type: Scratch.ArgumentType.STRING, + defaultValue: "my script" + } + } + }, + { + opcode: "breakLoop", + blockType: Scratch.BlockType.COMMAND, + text: "unpause script with ID [NAME]", + arguments: { + NAME: { + type: Scratch.ArgumentType.STRING, + defaultValue: "my script" + } + } + }, + { + opcode: "breakAll", + blockType: Scratch.BlockType.COMMAND, + text: "unpause all scripts" + }, + "---", + { + opcode: "pauseLoopCon", + blockType: Scratch.BlockType.COMMAND, + text: "if [CON] pause this script with ID [NAME]", + arguments: { + CON: { type: Scratch.ArgumentType.BOOLEAN }, + NAME: { + type: Scratch.ArgumentType.STRING, + defaultValue: "my script" + } + } + }, + { + opcode: "breakLoopCon", + blockType: Scratch.BlockType.COMMAND, + text: "if [CON] unpause script with ID [NAME]", + arguments: { + CON: { type: Scratch.ArgumentType.BOOLEAN }, + NAME: { + type: Scratch.ArgumentType.STRING, + defaultValue: "my script" + } + } + }, + "---", + { + opcode: "isPaused", + blockType: Scratch.BlockType.BOOLEAN, + text: "is script with ID [NAME] paused?", + arguments: { + NAME: { + type: Scratch.ArgumentType.STRING, + defaultValue: "my script" + } + } + }, + { + opcode: "allPausedScripts", + blockType: Scratch.BlockType.REPORTER, + text: "all paused scripts", + disableMonitor: true + }, + ], + menus: { + TARGETS: { acceptReporters: true, items: this._getTargets(0) }, + TARGETS2: { acceptReporters: true, items: this._getTargets(1) } + } + }; + } + + _getTargets(ind) { + const spriteNames = []; + const targets = Scratch.vm.runtime.targets; + for (let index = ind; index < targets.length; index++) { + const target = targets[index]; + if (target.isOriginal) spriteNames.push(target.getName()); + } + return spriteNames.length > 0 ? spriteNames : [""]; + } + + pause() { + if (Scratch.extensions.isPenguinMod) { + runtime.pause(); + } else { + const pauseButton = document.querySelector( + runtime.isPackaged ? `[class*="pause-button"]` : + "img.pause-btn.addons-display-none-pause" + ); + if (pauseButton) { + pauseButton.click(); + } else { console.log("Pause button not found") } + } + } + + unpause() { + if (Scratch.extensions.isPenguinMod) { + runtime.play() + } else { + this.pause(); + } + } + + // by itself, the block is useless, but with the event block it is :D + isProjectPaused() { return projectPaused } + + pauseSprite(args) { + const target = args.SPRITE === "Stage" ? runtime.getTargetForStage() : runtime.getSpriteTargetByName(args.SPRITE); + if (target) this.searchThreads(target.id, 1); + } + unpauseSprite(args) { + const target = args.SPRITE === "Stage" ? runtime.getTargetForStage() : runtime.getSpriteTargetByName(args.SPRITE); + if (target) this.searchThreads(target.id, 0); + } + pauseClones(args) { this.modifyClones.call(this, args, 1) } + unpauseClones(args) { this.modifyClones.call(this, args, 0) } + + searchThreads(target, cntrl) { + const thread = runtime.threads; + thread.forEach(t => { + if (t.target.id === target && t.status !== cntrl) t.status = cntrl; + }); + } + modifyClones(args, cntrl) { + const target = runtime.getSpriteTargetByName(args.SPRITE); + if (target) { + const clones = target.sprite.clones; + const varName = args.VAR; + const numValue = args.NUM; + for (let i = 1; i < clones.length; i++) { + const variable = clones[i].lookupVariableByNameAndType(varName, ""); + if (variable && variable.value === numValue) this.searchThreads(clones[i].id, cntrl); + } + } + } + + pauseLoopCon(args, util) { if (Cast.toBoolean(args.CON)) this.pauseLoop(args, util) } + + breakLoopCon(args) { if (Cast.toBoolean(args.CON)) this.breakLoop(args) } + + pauseLoop(args, util) { + const scriptName = Cast.toString(args.NAME); + const state = util.stackFrame.pausedScript; + if (!state) { + storedScripts[scriptName] = true; + util.stackFrame.pausedScript = scriptName; + util.yield(); + } else if (state in storedScripts) { + util.yield(); + } + } + + breakLoop(args) { + const scriptName = Cast.toString(args.NAME); + if (scriptName in storedScripts) delete storedScripts[scriptName]; + } + + breakAll() { + const allScripts = Object.keys(storedScripts); + for (let i = 0; i < allScripts.length; i++) { + this.breakLoop({ NAME : allScripts[i] }); + } + } + + isPaused(args) { + const scriptName = Cast.toString(args.NAME); + return scriptName in storedScripts; + } + + allPausedScripts() { return JSON.stringify(Object.keys(storedScripts)) } + } + + beginScan() + Scratch.extensions.register(new SPPause()); +})(Scratch); From 8fc3ab5d2a56c0ad4c130b3004ae4bb4fac324d1 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:49:23 -0800 Subject: [PATCH 02/13] Update Pause-Utilities.js --- extensions/SharkPool/Pause-Utilities.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index 958a040e53..fe3e13cd1a 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -3,7 +3,7 @@ // Description: Pause the Project and certain Scripts // By: SharkPool -// Version V.1.6.0 +// Version V.1.6.0 (TW Version) (function (Scratch) { "use strict"; @@ -26,11 +26,10 @@ let isChecking = false; const check = () => { if (isChecking) return; - const isPM = Scratch.extensions.isPenguinMod; isChecking = true; try { projectPaused = runtime.ioDevices.clock._paused; - if (!projectPaused && !isPM) return; + if (!projectPaused) return; const allUtils = vm.runtime.targets; for (let i = 0; i < allUtils.length; i++) { const util = allUtils[i]; @@ -44,12 +43,11 @@ if (projectPaused) { vm.runtime.toggleScript(object[keyV].id, util); checkReset(object[keyV].id); - } else if (isPM) { checkReset(object[keyV].id) } + } } else { checkReset(object[keyV].id) } } } } - if (isPM) vm.runtime._step(); } catch {} finally { isChecking = false; setTimeout(check, 10); From 04bfc6b0a78df0c5d35d2fcc75c6b021bd0fb8df Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:58:30 -0800 Subject: [PATCH 03/13] Create Pause-Utilities.svg --- images/SharkPool/Pause-Utilities.svg | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 images/SharkPool/Pause-Utilities.svg diff --git a/images/SharkPool/Pause-Utilities.svg b/images/SharkPool/Pause-Utilities.svg new file mode 100644 index 0000000000..730e9e3acc --- /dev/null +++ b/images/SharkPool/Pause-Utilities.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From 740539b7452d2eeac2ce655d1c6a89afb0e9a5e7 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:24:40 -0800 Subject: [PATCH 04/13] Use Push Thread --- extensions/SharkPool/Pause-Utilities.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index fe3e13cd1a..a0418905d1 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -36,12 +36,11 @@ const object = util.blocks._blocks; const keysV = getKeysByValue(object, "opcode", "SPPause_whenProjectPaused"); for (const keyV of keysV) { - if (projectPaused) vm.setEditingTarget(util.id); if (runtime.threads) { let threadExists = runtime.threads.some(thread => thread.topBlock === object[keyV].id); if (!threadExists) { if (projectPaused) { - vm.runtime.toggleScript(object[keyV].id, util); + vm.runtime._pushThread(object[keyV].id, util); checkReset(object[keyV].id); } } else { checkReset(object[keyV].id) } From 62381d5d575099e82955f926bc45ab37d781f4c3 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:55:51 -0800 Subject: [PATCH 05/13] Update Pause-Utilities.js --- extensions/SharkPool/Pause-Utilities.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index a0418905d1..bf9241bd28 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -135,8 +135,8 @@ defaultValue: "my variable" }, NUM: { - type: Scratch.ArgumentType.NUMBER, - defaultValue: 0 + type: Scratch.ArgumentType.STRING, + defaultValue: "0" } } }, @@ -154,8 +154,8 @@ defaultValue: "my variable" }, NUM: { - type: Scratch.ArgumentType.NUMBER, - defaultValue: 0 + type: Scratch.ArgumentType.STRING, + defaultValue: "0" } } }, @@ -256,18 +256,14 @@ runtime.isPackaged ? `[class*="pause-button"]` : "img.pause-btn.addons-display-none-pause" ); - if (pauseButton) { - pauseButton.click(); - } else { console.log("Pause button not found") } + if (pauseButton) pauseButton.click(); + else console.log("Pause button not found"); } } unpause() { - if (Scratch.extensions.isPenguinMod) { - runtime.play() - } else { - this.pause(); - } + if (Scratch.extensions.isPenguinMod) runtime.play() + else this.pause(); } // by itself, the block is useless, but with the event block it is :D From 4464da07640bc5f28dd329112b9540bb4703a5a5 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:54:38 -0800 Subject: [PATCH 06/13] Update Pause-Utilities.js --- extensions/SharkPool/Pause-Utilities.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index bf9241bd28..124d9f5f27 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -3,7 +3,7 @@ // Description: Pause the Project and certain Scripts // By: SharkPool -// Version V.1.6.0 (TW Version) +// Version V.1.6.4 (TW Version) (function (Scratch) { "use strict"; @@ -253,7 +253,7 @@ runtime.pause(); } else { const pauseButton = document.querySelector( - runtime.isPackaged ? `[class*="pause-button"]` : + typeof scaffolding !== "undefined" ? `[class*="pause-button"]` : "img.pause-btn.addons-display-none-pause" ); if (pauseButton) pauseButton.click(); From 9f99b0b6c8f517fc814af02677c1534d8e5a5db2 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Thu, 23 May 2024 21:34:48 -0700 Subject: [PATCH 07/13] Pause-Utilities -- Faster & Better Code --- extensions/SharkPool/Pause-Utilities.js | 202 ++++++++++-------------- 1 file changed, 85 insertions(+), 117 deletions(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index 124d9f5f27..e536aab268 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -3,16 +3,15 @@ // Description: Pause the Project and certain Scripts // By: SharkPool -// Version V.1.6.4 (TW Version) +// Version V.1.7.0 (function (Scratch) { "use strict"; - if (!Scratch.extensions.unsandboxed) alert("Pause Utilities Extension must run unsandboxed!"); + if (!Scratch.extensions.unsandboxed) alert("Pause Utilities must run unsandboxed!"); + const vm = Scratch.vm; - const runtime = Scratch.vm.runtime; + const runtime = vm.runtime; const Cast = Scratch.Cast; - let storedScripts = {}; - let projectPaused = false; const menuIconURI = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTgiIHdpZHRoPSIxOCI+PHBhdGggZD0iTTIzMS40MjkgMTg4LjkyOVYxNzEuMDdoNC4yODV2MTcuODU4em0xMi4xNDIgMFYxNzEuMDdoNC4yODZ2MTcuODU4eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzMwOSAwIDAgLjk1NDI3IC0yMzguNTczIC0xNjIuNzY5KSIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsPSIjZmZhZTAwIiBzdHJva2U9IiNkODk0MDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOm5vcm1hbCIvPjwvc3ZnPg=="; @@ -20,51 +19,57 @@ const blockIconURI = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMS40NzcxNCIgaGVpZ2h0PSIzMS40NzcxNCIgdmlld0JveD0iMCwwLDMxLjQ3NzE0LDMxLjQ3NzE0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI0LjI2MTQzLC0xNjQuMjYxNDMpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0yMjQuMjYxNDMsMTk1LjczODU3di0zMS40NzcxNGgzMS40NzcxNHYzMS40NzcxNHoiIGZpbGw9IiM1ZjViNDkiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTIzMS41MjgxOSwxODguNDk5MTN2LTE3LjA0MjMxaDQuNDI2Nzl2MTcuMDQxMzV6TTI0NC4wNzE5NiwxODguNDk5MTN2LTE3LjA0MjMxaDQuNDI3ODJ2MTcuMDQxMzV6IiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9IiNmZmFlMDAiIHN0cm9rZT0iI2Q4OTQwMCIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9nPjwvZz48L3N2Zz4="; + // Inject Pause Event + Object.defineProperty(runtime.ioDevices.clock, "_paused", { + set: function(value) { + this._pausedValue = value; + if (value) this.SP_whilePaused(); + else this.SP_unpaused(); + }, + get: function() { return this._pausedValue; } + }); + + runtime.ioDevices.clock.SP_whilePaused = function() { + if (this.pauseEventInterval) return; + this.pauseEventInterval = setInterval(() => { + if (this._pausedValue) runtime.emit("SP_PROJECT_PAUSED", true); + }, 10); + }; + runtime.ioDevices.clock.SP_unpaused = function() { + if (this.pauseEventInterval) { + clearInterval(this.pauseEventInterval); + this.pauseEventInterval = null; + // Delayed to Prevent Started Threads while Paused (rare condition, Failsafe) + setTimeout(function() { runtime.emit("SP_PROJECT_UNPAUSED", true) }, 10); + } + }; + + let storedScripts = {}; + let projectPaused = false; + runtime.on("PROJECT_STOP_ALL", () => { storedScripts = {} }); + runtime.on("SP_PROJECT_UNPAUSED", () => { + runtime.startHats("SPPause_whenProjectUnPaused"); + // Fix Paused Threads (Rare and Shouldnt Happen, but Failsafe) + for (let i = 0; i < runtime.threads.length; i++) { + const thread = runtime.threads[i]; + if (thread.status === undefined) thread.status = 4; + } + }); - function beginScan() { - let isChecking = false; - const check = () => { - if (isChecking) return; - isChecking = true; - try { - projectPaused = runtime.ioDevices.clock._paused; - if (!projectPaused) return; - const allUtils = vm.runtime.targets; - for (let i = 0; i < allUtils.length; i++) { - const util = allUtils[i]; - const object = util.blocks._blocks; - const keysV = getKeysByValue(object, "opcode", "SPPause_whenProjectPaused"); - for (const keyV of keysV) { - if (runtime.threads) { - let threadExists = runtime.threads.some(thread => thread.topBlock === object[keyV].id); - if (!threadExists) { - if (projectPaused) { - vm.runtime._pushThread(object[keyV].id, util); - checkReset(object[keyV].id); - } - } else { checkReset(object[keyV].id) } - } - } - } - } catch {} finally { - isChecking = false; - setTimeout(check, 10); + runtime.on("SP_PROJECT_PAUSED", () => { + projectPaused = runtime.ioDevices.clock._paused; + runtime.allScriptsByOpcodeDo("SPPause_whenProjectPaused", (script, target) => { + const topBlockId = script.blockId; + const threadExists = runtime.threads.find(thread => thread.topBlock === topBlockId); + if (!threadExists) { + setTimeout(function() { + // Offset to not pause the generator + runtime._pushThread(topBlockId, target); + }, 1); } - }; - check(); - } - function getKeysByValue(object, name, value) { - const keys = []; - for (const key in object) { - if (object[key][name] === value) keys.push(key); - } - return keys; - } - function checkReset(ID) { - const threadExists = runtime.threads.find(thread => thread.topBlock === ID); - if (threadExists.status === undefined) threadExists.status = 4; - } + }); + }); class SPPause { getInfo() { @@ -92,6 +97,12 @@ text: "when project is paused", isEdgeActivated: false }, + { + opcode: "whenProjectUnPaused", + blockType: Scratch.BlockType.EVENT, + text: "when project is unpaused", + isEdgeActivated: false + }, { opcode: "isProjectPaused", blockType: Scratch.BlockType.BOOLEAN, @@ -103,10 +114,7 @@ blockType: Scratch.BlockType.COMMAND, text: "pause [SPRITE]", arguments: { - SPRITE: { - type: Scratch.ArgumentType.STRING, - menu: "TARGETS" - } + SPRITE: { type: Scratch.ArgumentType.STRING, menu: "TARGETS" } } }, { @@ -114,10 +122,7 @@ blockType: Scratch.BlockType.COMMAND, text: "unpause [SPRITE]", arguments: { - SPRITE: { - type: Scratch.ArgumentType.STRING, - menu: "TARGETS" - } + SPRITE: { type: Scratch.ArgumentType.STRING, menu: "TARGETS" } } }, "---", @@ -126,18 +131,9 @@ blockType: Scratch.BlockType.COMMAND, text: "pause clones of [SPRITE] with [VAR] set to [NUM]", arguments: { - SPRITE: { - type: Scratch.ArgumentType.STRING, - menu: "TARGETS2" - }, - VAR: { - type: Scratch.ArgumentType.STRING, - defaultValue: "my variable" - }, - NUM: { - type: Scratch.ArgumentType.STRING, - defaultValue: "0" - } + SPRITE: { type: Scratch.ArgumentType.STRING, menu: "TARGETS2" }, + VAR: { type: Scratch.ArgumentType.STRING, defaultValue: "my variable" }, + NUM: { type: Scratch.ArgumentType.STRING, defaultValue: 0 } } }, { @@ -145,18 +141,9 @@ blockType: Scratch.BlockType.COMMAND, text: "unpause clones of [SPRITE] with [VAR] set to [NUM]", arguments: { - SPRITE: { - type: Scratch.ArgumentType.STRING, - menu: "TARGETS2" - }, - VAR: { - type: Scratch.ArgumentType.STRING, - defaultValue: "my variable" - }, - NUM: { - type: Scratch.ArgumentType.STRING, - defaultValue: "0" - } + SPRITE: { type: Scratch.ArgumentType.STRING, menu: "TARGETS2" }, + VAR: { type: Scratch.ArgumentType.STRING, defaultValue: "my variable" }, + NUM: { type: Scratch.ArgumentType.STRING, defaultValue: 0 } } }, { blockType: Scratch.BlockType.LABEL, text: "Script Control" }, @@ -165,10 +152,7 @@ blockType: Scratch.BlockType.COMMAND, text: "pause this script with ID [NAME]", arguments: { - NAME: { - type: Scratch.ArgumentType.STRING, - defaultValue: "my script" - } + NAME: { type: Scratch.ArgumentType.STRING, defaultValue: "my script" } } }, { @@ -176,10 +160,7 @@ blockType: Scratch.BlockType.COMMAND, text: "unpause script with ID [NAME]", arguments: { - NAME: { - type: Scratch.ArgumentType.STRING, - defaultValue: "my script" - } + NAME: { type: Scratch.ArgumentType.STRING, defaultValue: "my script" } } }, { @@ -188,40 +169,12 @@ text: "unpause all scripts" }, "---", - { - opcode: "pauseLoopCon", - blockType: Scratch.BlockType.COMMAND, - text: "if [CON] pause this script with ID [NAME]", - arguments: { - CON: { type: Scratch.ArgumentType.BOOLEAN }, - NAME: { - type: Scratch.ArgumentType.STRING, - defaultValue: "my script" - } - } - }, - { - opcode: "breakLoopCon", - blockType: Scratch.BlockType.COMMAND, - text: "if [CON] unpause script with ID [NAME]", - arguments: { - CON: { type: Scratch.ArgumentType.BOOLEAN }, - NAME: { - type: Scratch.ArgumentType.STRING, - defaultValue: "my script" - } - } - }, - "---", { opcode: "isPaused", blockType: Scratch.BlockType.BOOLEAN, text: "is script with ID [NAME] paused?", arguments: { - NAME: { - type: Scratch.ArgumentType.STRING, - defaultValue: "my script" - } + NAME: { type: Scratch.ArgumentType.STRING, defaultValue: "my script" } } }, { @@ -230,6 +183,22 @@ text: "all paused scripts", disableMonitor: true }, + { + opcode: "pauseLoopCon", blockType: Scratch.BlockType.COMMAND, + hideFromPalette: true, // Deprecated + text: "if [CON] pause this script with ID [NAME]", + arguments: { + CON: { type: Scratch.ArgumentType.BOOLEAN }, NAME: { type: Scratch.ArgumentType.STRING, defaultValue: "my script" } + } + }, + { + opcode: "breakLoopCon", blockType: Scratch.BlockType.COMMAND, + hideFromPalette: true, // Deprecated + text: "if [CON] unpause script with ID [NAME]", + arguments: { + CON: { type: Scratch.ArgumentType.BOOLEAN }, NAME: { type: Scratch.ArgumentType.STRING, defaultValue: "my script" } + } + } ], menus: { TARGETS: { acceptReporters: true, items: this._getTargets(0) }, @@ -335,6 +304,5 @@ allPausedScripts() { return JSON.stringify(Object.keys(storedScripts)) } } - beginScan() Scratch.extensions.register(new SPPause()); })(Scratch); From 40ba33605ff65b43772bf3da5dc6b2ce7e24b2e3 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Thu, 23 May 2024 21:52:15 -0700 Subject: [PATCH 08/13] Pause-Utilities -- Add OG func Listener (I forgor) --- extensions/SharkPool/Pause-Utilities.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index e536aab268..e9a1211ffd 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -20,9 +20,12 @@ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMS40NzcxNCIgaGVpZ2h0PSIzMS40NzcxNCIgdmlld0JveD0iMCwwLDMxLjQ3NzE0LDMxLjQ3NzE0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI0LjI2MTQzLC0xNjQuMjYxNDMpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0yMjQuMjYxNDMsMTk1LjczODU3di0zMS40NzcxNGgzMS40NzcxNHYzMS40NzcxNHoiIGZpbGw9IiM1ZjViNDkiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTIzMS41MjgxOSwxODguNDk5MTN2LTE3LjA0MjMxaDQuNDI2Nzl2MTcuMDQxMzV6TTI0NC4wNzE5NiwxODguNDk5MTN2LTE3LjA0MjMxaDQuNDI3ODJ2MTcuMDQxMzV6IiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9IiNmZmFlMDAiIHN0cm9rZT0iI2Q4OTQwMCIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9nPjwvZz48L3N2Zz4="; // Inject Pause Event + // Save original function if it exists + let ogPauseFunc = Object.getOwnPropertyDescriptor(runtime.ioDevices.clock, "_paused")?.set; Object.defineProperty(runtime.ioDevices.clock, "_paused", { set: function(value) { this._pausedValue = value; + if (ogPauseFunc) ogPauseFunc.call(this, value); if (value) this.SP_whilePaused(); else this.SP_unpaused(); }, @@ -54,6 +57,7 @@ for (let i = 0; i < runtime.threads.length; i++) { const thread = runtime.threads[i]; if (thread.status === undefined) thread.status = 4; + if (thread.status === 5) thread.status = 0; // PenguinMod } }); @@ -69,6 +73,7 @@ }, 1); } }); + if (Scratch.extensions.isPenguinMod && projectPaused) runtime._step(); }); class SPPause { From 9a0cd167720ef8e8730446a720bc983cbc5fef71 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:10:22 -0700 Subject: [PATCH 09/13] Pause-Utilities -- Rename Block for Clarity --- extensions/SharkPool/Pause-Utilities.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index e9a1211ffd..6a3aa6fd35 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -3,7 +3,7 @@ // Description: Pause the Project and certain Scripts // By: SharkPool -// Version V.1.7.0 +// Version V.1.7.01 (function (Scratch) { "use strict"; @@ -99,7 +99,7 @@ { opcode: "whenProjectPaused", blockType: Scratch.BlockType.EVENT, - text: "when project is paused", + text: "while project is paused", isEdgeActivated: false }, { From 5c7abc86eb7d5494a5a48f7b7c4aac43e265b844 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Sat, 3 Aug 2024 08:23:45 -0700 Subject: [PATCH 10/13] Pause-Utilities -- rewrite and fixes --- extensions/SharkPool/Pause-Utilities.js | 100 +++++++++--------------- 1 file changed, 38 insertions(+), 62 deletions(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index 6a3aa6fd35..5abb7af74b 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -3,7 +3,7 @@ // Description: Pause the Project and certain Scripts // By: SharkPool -// Version V.1.7.01 +// Version V.1.7.02 (function (Scratch) { "use strict"; @@ -14,10 +14,15 @@ const Cast = Scratch.Cast; const menuIconURI = -"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTgiIHdpZHRoPSIxOCI+PHBhdGggZD0iTTIzMS40MjkgMTg4LjkyOVYxNzEuMDdoNC4yODV2MTcuODU4em0xMi4xNDIgMFYxNzEuMDdoNC4yODZ2MTcuODU4eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzMwOSAwIDAgLjk1NDI3IC0yMzguNTczIC0xNjIuNzY5KSIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsPSIjZmZhZTAwIiBzdHJva2U9IiNkODk0MDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOm5vcm1hbCIvPjwvc3ZnPg=="; - +"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTgiIHdpZHRoPSIxOCI+PHBhdGggZD0iTTIzMS40MjkgMTg4LjkyOVYxNzEuMDdoNC4yODV2MTcuODU4em0xMi4xNDIgMFYxNzEuMDdoNC4yODZ2MTcuODU4eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzMwOSAwIDAgLjk1NDI3IC0yMzguNTczIC0xNjIuNzY5KSIgZmlsbD0iI2ZmYWUwMCIgc3Ryb2tlPSIjZDg5NDAwIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz48L3N2Zz4="; const blockIconURI = -"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMS40NzcxNCIgaGVpZ2h0PSIzMS40NzcxNCIgdmlld0JveD0iMCwwLDMxLjQ3NzE0LDMxLjQ3NzE0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI0LjI2MTQzLC0xNjQuMjYxNDMpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0yMjQuMjYxNDMsMTk1LjczODU3di0zMS40NzcxNGgzMS40NzcxNHYzMS40NzcxNHoiIGZpbGw9IiM1ZjViNDkiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTIzMS41MjgxOSwxODguNDk5MTN2LTE3LjA0MjMxaDQuNDI2Nzl2MTcuMDQxMzV6TTI0NC4wNzE5NiwxODguNDk5MTN2LTE3LjA0MjMxaDQuNDI3ODJ2MTcuMDQxMzV6IiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9IiNmZmFlMDAiIHN0cm9rZT0iI2Q4OTQwMCIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9nPjwvZz48L3N2Zz4="; +"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMS40NzciIGhlaWdodD0iMzEuNDc3IiB2aWV3Qm94PSIwIDAgMzEuNDc3IDMxLjQ3NyI+PGcgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIj48cGF0aCBkPSJNMCAzMS40NzhWLS4wMDFoMzEuNDc4djMxLjQ3OHoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNNy4yNjcgMjQuMjM5VjcuMTk2aDQuNDI3djE3LjA0MXptMTIuNTQ0IDBWNy4xOTZoNC40Mjh2MTcuMDQxeiIgZmlsbD0iI2ZmYWUwMCIgc3Ryb2tlPSIjZDg5NDAwIi8+PC9nPjwvc3ZnPg=="; + + let storedScripts = {}; + let paused = false; + let pausedThreadState = new WeakMap(); + let pauseNewThreads = false, steppingThread = null; + let audioContextStateChange = Promise.resolve(); // Inject Pause Event // Save original function if it exists @@ -31,24 +36,24 @@ }, get: function() { return this._pausedValue; } }); - - runtime.ioDevices.clock.SP_whilePaused = function() { + runtime.ioDevices.clock.SP_whilePaused = function () { if (this.pauseEventInterval) return; this.pauseEventInterval = setInterval(() => { if (this._pausedValue) runtime.emit("SP_PROJECT_PAUSED", true); }, 10); }; - runtime.ioDevices.clock.SP_unpaused = function() { + runtime.ioDevices.clock.SP_unpaused = function () { if (this.pauseEventInterval) { clearInterval(this.pauseEventInterval); this.pauseEventInterval = null; // Delayed to Prevent Started Threads while Paused (rare condition, Failsafe) - setTimeout(function() { runtime.emit("SP_PROJECT_UNPAUSED", true) }, 10); + setTimeout(() => { runtime.emit("SP_PROJECT_UNPAUSED", true) }, 10); } }; - let storedScripts = {}; - let projectPaused = false; + // Inject Pause Module (minified) + // https://github.com/TurboWarp/scratch-gui/blob/develop/src/addons/addons/debugger/module.js#L407 + const isPaused=()=>paused,pauseThread=e=>{if(e.updateMonitor||pausedThreadState.has(e))return;let t={time:runtime.currentMSecs,status:e.status};pausedThreadState.set(e,t),e.status=1},ensurePausedThreadIsStillPaused=e=>{if(4===e.status)return;let t=pausedThreadState.get(e);t&&1!==e.status&&(t.status=e.status,e.status=1)},setSteppingThread=e=>{steppingThread=e},compensateForTimePassedWhilePaused=(e,t)=>{e.timer&&(e.timer.startTime+=runtime.currentMSecs-t.time),e.compatibilityStackFrame&&e.compatibilityStackFrame.timer&&(e.compatibilityStackFrame.timer.startTime+=runtime.currentMSecs-t.time);let r=e.peekStackFrame();r&&r.executionContext&&r.executionContext.timer&&(r.executionContext.timer.startTime+=runtime.currentMSecs-t.time)},stepUnsteppedThreads=e=>{let t=runtime.threads,r=getThreadIndex(e);if(-1!==r)for(let a=r;a{let t=paused!==e;if(t&&(paused=e),paused){audioContextStateChange=audioContextStateChange.then(()=>runtime.audioEngine.audioContext.suspend()),runtime.ioDevices.clock._paused||runtime.ioDevices.clock.pause(),runtime.threads.forEach(pauseThread);let r=runtime.sequencer.activeThread;r&&setSteppingThread(r)}if(!paused&&t){for(let a of(audioContextStateChange=audioContextStateChange.then(()=>runtime.audioEngine.audioContext.resume()),runtime.ioDevices.clock.resume(),runtime.threads)){let s=pausedThreadState.get(a);s&&(compensateForTimePassedWhilePaused(a,s),a.status=s.status)}pausedThreadState=new WeakMap;let i=steppingThread;stepUnsteppedThreads(i),steppingThread=null}},getRunningThread=()=>steppingThread,singleStepThread=e=>{if(4===e.status||e.isCompiled)return!1;let t=e.peekStack();if(!t&&(e.popStack(),0===e.stack.length))return e.status=4,!1;pauseNewThreads=!0,runtime.sequencer.activeThread=e;let r=["special error used by Scratch Addons for implementing single-stepping"];Object.defineProperty(e,"blockGlowInFrame",{set(e){throw r}});try{e.status=0,e.warpTimer&&e.warpTimer.start();try{runtime.sequencer.stepThread(e)}catch(a){if(a!==r)throw a}if(0!==e.status)return!1;for(e.peekStack()===t&&e.goToNextBlock();!e.peekStack();){if(e.popStack(),0===e.stack.length)return e.status=4,!1;let s=e.peekStackFrame();if(s.isLoop){if(e.peekStackFrame().warpMode)continue;return!1}if(s.waitingReporter)return!1;e.goToNextBlock()}return!0}finally{pauseNewThreads=!1,runtime.sequencer.activeThread=null,Object.defineProperty(e,"blockGlowInFrame",{value:t,configurable:!0,enumerable:!0,writable:!0}),4!==e.status&&(e.status=1)}},getRealStatus=e=>{let t=pausedThreadState.get(e);return t?t.status:e.status},getThreadIndex=e=>e?runtime.threads.findIndex(t=>t.target===e.target&&t.topBlock===e.topBlock&&t.stackClick===e.stackClick&&t.updateMonitor===e.updateMonitor):-1,findNewSteppingThread=e=>{let t=runtime.threads;for(let r=e;r{if(steppingThread){let e=pausedThreadState.get(steppingThread);compensateForTimePassedWhilePaused(steppingThread,e),e.time=runtime.currentMSecs;let t=singleStepThread(steppingThread);t||(steppingThread=findNewSteppingThread(getThreadIndex(steppingThread)+1))}if(!steppingThread){setSteppingThread(findNewSteppingThread(0)),runtime.ioDevices.clock._pausedTime+=runtime.currentStepTime;let r=runtime.audioEngine.audioContext;for(let a of runtime.targets)for(let s of Object.keys(a.sprite.soundBank.soundPlayers)){let i=a.sprite.soundBank.soundPlayers[s];i.outputNode&&(i.outputNode.stop(r.currentTime),i._createSource(),i.outputNode.start(r.currentTime,r.currentTime-i.startingUntil+runtime.currentStepTime/1e3),i.startingUntil-=runtime.currentStepTime/1e3)}for(let n of runtime.threads)pausedThreadState.has(n)&&(pausedThreadState.get(n).time+=runtime.currentStepTime);pauseNewThreads=!0;let u=runtime._hats;for(let o in u){if(!Object.prototype.hasOwnProperty.call(u,o))continue;let l=u[o];l.edgeActivated&&runtime.startHats(o)}pauseNewThreads=!1}},setup=()=>{let e=vm,t=e.runtime.sequencer.stepThreads;e.runtime.sequencer.stepThreads=function(){if(isPaused())for(let e of this.runtime.threads)ensurePausedThreadIsStillPaused(e);return t.call(this)};let r=e.runtime.greenFlag;e.runtime.greenFlag=function(){return setPaused(!1),r.call(this)};let a=e.runtime.startHats;e.runtime.startHats=function(...e){let t=e[0],r="event_whenbroadcastreceived"===t||"control_start_as_clone"===t;if(pauseNewThreads){if(!r&&!this.getIsEdgeActivatedHat(t))return[];let s=a.apply(this,e);for(let i of s)pauseThread(i);return s}return paused&&!r?[]:a.apply(this,e)};let s=e.runtime._getMonitorThreadCount;e.runtime._getMonitorThreadCount=function(e){let t=s.call(this,e);if(paused)for(let r of e)pausedThreadState.has(r)&&t++;return t}}; runtime.on("PROJECT_STOP_ALL", () => { storedScripts = {} }); runtime.on("SP_PROJECT_UNPAUSED", () => { @@ -57,23 +62,20 @@ for (let i = 0; i < runtime.threads.length; i++) { const thread = runtime.threads[i]; if (thread.status === undefined) thread.status = 4; - if (thread.status === 5) thread.status = 0; // PenguinMod + if (thread.status === 5) thread.status = 0; } }); runtime.on("SP_PROJECT_PAUSED", () => { - projectPaused = runtime.ioDevices.clock._paused; + paused = runtime.ioDevices.clock._paused; runtime.allScriptsByOpcodeDo("SPPause_whenProjectPaused", (script, target) => { const topBlockId = script.blockId; const threadExists = runtime.threads.find(thread => thread.topBlock === topBlockId); if (!threadExists) { - setTimeout(function() { - // Offset to not pause the generator - runtime._pushThread(topBlockId, target); - }, 1); + // Offset to not pause the generator, use Timeout as AFTER_EXECUTE isnt smooth + setTimeout(() => { runtime._pushThread(topBlockId, target) }, 1); } }); - if (Scratch.extensions.isPenguinMod && projectPaused) runtime._step(); }); class SPPause { @@ -187,22 +189,6 @@ blockType: Scratch.BlockType.REPORTER, text: "all paused scripts", disableMonitor: true - }, - { - opcode: "pauseLoopCon", blockType: Scratch.BlockType.COMMAND, - hideFromPalette: true, // Deprecated - text: "if [CON] pause this script with ID [NAME]", - arguments: { - CON: { type: Scratch.ArgumentType.BOOLEAN }, NAME: { type: Scratch.ArgumentType.STRING, defaultValue: "my script" } - } - }, - { - opcode: "breakLoopCon", blockType: Scratch.BlockType.COMMAND, - hideFromPalette: true, // Deprecated - text: "if [CON] unpause script with ID [NAME]", - arguments: { - CON: { type: Scratch.ArgumentType.BOOLEAN }, NAME: { type: Scratch.ArgumentType.STRING, defaultValue: "my script" } - } } ], menus: { @@ -214,7 +200,7 @@ _getTargets(ind) { const spriteNames = []; - const targets = Scratch.vm.runtime.targets; + const targets = runtime.targets; for (let index = ind; index < targets.length; index++) { const target = targets[index]; if (target.isOriginal) spriteNames.push(target.getName()); @@ -222,26 +208,29 @@ return spriteNames.length > 0 ? spriteNames : [""]; } + searchThreads(target, cntrl) { + const thread = runtime.threads; + thread.forEach(t => { + if (t.target.id === target && t.status !== cntrl) t.status = cntrl; + }); + } + pause() { - if (Scratch.extensions.isPenguinMod) { - runtime.pause(); - } else { - const pauseButton = document.querySelector( - typeof scaffolding !== "undefined" ? `[class*="pause-button"]` : - "img.pause-btn.addons-display-none-pause" - ); - if (pauseButton) pauseButton.click(); - else console.log("Pause button not found"); - } + const btn = document.querySelector(typeof scaffolding !== "undefined" ? `[class*="pause-button"]` : "img.pause-btn.addons-display-none-pause"); + if (btn) btn.click(); + else setPaused(true); } unpause() { - if (Scratch.extensions.isPenguinMod) runtime.play() - else this.pause(); + const btn = document.querySelector(typeof scaffolding !== "undefined" ? `[class*="pause-button"]` : "img.pause-btn.addons-display-none-pause"); + // Ignore "generator is running" error. It lies :0 + try { + if (btn) btn.click(); + else setPaused(false); + } catch {} } - // by itself, the block is useless, but with the event block it is :D - isProjectPaused() { return projectPaused } + isProjectPaused() { return paused } pauseSprite(args) { const target = args.SPRITE === "Stage" ? runtime.getTargetForStage() : runtime.getSpriteTargetByName(args.SPRITE); @@ -254,12 +243,6 @@ pauseClones(args) { this.modifyClones.call(this, args, 1) } unpauseClones(args) { this.modifyClones.call(this, args, 0) } - searchThreads(target, cntrl) { - const thread = runtime.threads; - thread.forEach(t => { - if (t.target.id === target && t.status !== cntrl) t.status = cntrl; - }); - } modifyClones(args, cntrl) { const target = runtime.getSpriteTargetByName(args.SPRITE); if (target) { @@ -273,10 +256,6 @@ } } - pauseLoopCon(args, util) { if (Cast.toBoolean(args.CON)) this.pauseLoop(args, util) } - - breakLoopCon(args) { if (Cast.toBoolean(args.CON)) this.breakLoop(args) } - pauseLoop(args, util) { const scriptName = Cast.toString(args.NAME); const state = util.stackFrame.pausedScript; @@ -301,10 +280,7 @@ } } - isPaused(args) { - const scriptName = Cast.toString(args.NAME); - return scriptName in storedScripts; - } + isPaused(args) { return Cast.toString(args.NAME) in storedScripts } allPausedScripts() { return JSON.stringify(Object.keys(storedScripts)) } } From 7de282c26f75f5fec7646977984a727ac76e2a67 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Sat, 3 Aug 2024 08:28:22 -0700 Subject: [PATCH 11/13] shut up prettier --- extensions/SharkPool/Pause-Utilities.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index 5abb7af74b..42378bf146 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -52,7 +52,8 @@ }; // Inject Pause Module (minified) - // https://github.com/TurboWarp/scratch-gui/blob/develop/src/addons/addons/debugger/module.js#L407 + // https://github.com/TurboWarp/scratch-gui/blob/develop/src/addons/addons/debugger/module.js + // eslint-disable-next-line const isPaused=()=>paused,pauseThread=e=>{if(e.updateMonitor||pausedThreadState.has(e))return;let t={time:runtime.currentMSecs,status:e.status};pausedThreadState.set(e,t),e.status=1},ensurePausedThreadIsStillPaused=e=>{if(4===e.status)return;let t=pausedThreadState.get(e);t&&1!==e.status&&(t.status=e.status,e.status=1)},setSteppingThread=e=>{steppingThread=e},compensateForTimePassedWhilePaused=(e,t)=>{e.timer&&(e.timer.startTime+=runtime.currentMSecs-t.time),e.compatibilityStackFrame&&e.compatibilityStackFrame.timer&&(e.compatibilityStackFrame.timer.startTime+=runtime.currentMSecs-t.time);let r=e.peekStackFrame();r&&r.executionContext&&r.executionContext.timer&&(r.executionContext.timer.startTime+=runtime.currentMSecs-t.time)},stepUnsteppedThreads=e=>{let t=runtime.threads,r=getThreadIndex(e);if(-1!==r)for(let a=r;a{let t=paused!==e;if(t&&(paused=e),paused){audioContextStateChange=audioContextStateChange.then(()=>runtime.audioEngine.audioContext.suspend()),runtime.ioDevices.clock._paused||runtime.ioDevices.clock.pause(),runtime.threads.forEach(pauseThread);let r=runtime.sequencer.activeThread;r&&setSteppingThread(r)}if(!paused&&t){for(let a of(audioContextStateChange=audioContextStateChange.then(()=>runtime.audioEngine.audioContext.resume()),runtime.ioDevices.clock.resume(),runtime.threads)){let s=pausedThreadState.get(a);s&&(compensateForTimePassedWhilePaused(a,s),a.status=s.status)}pausedThreadState=new WeakMap;let i=steppingThread;stepUnsteppedThreads(i),steppingThread=null}},getRunningThread=()=>steppingThread,singleStepThread=e=>{if(4===e.status||e.isCompiled)return!1;let t=e.peekStack();if(!t&&(e.popStack(),0===e.stack.length))return e.status=4,!1;pauseNewThreads=!0,runtime.sequencer.activeThread=e;let r=["special error used by Scratch Addons for implementing single-stepping"];Object.defineProperty(e,"blockGlowInFrame",{set(e){throw r}});try{e.status=0,e.warpTimer&&e.warpTimer.start();try{runtime.sequencer.stepThread(e)}catch(a){if(a!==r)throw a}if(0!==e.status)return!1;for(e.peekStack()===t&&e.goToNextBlock();!e.peekStack();){if(e.popStack(),0===e.stack.length)return e.status=4,!1;let s=e.peekStackFrame();if(s.isLoop){if(e.peekStackFrame().warpMode)continue;return!1}if(s.waitingReporter)return!1;e.goToNextBlock()}return!0}finally{pauseNewThreads=!1,runtime.sequencer.activeThread=null,Object.defineProperty(e,"blockGlowInFrame",{value:t,configurable:!0,enumerable:!0,writable:!0}),4!==e.status&&(e.status=1)}},getRealStatus=e=>{let t=pausedThreadState.get(e);return t?t.status:e.status},getThreadIndex=e=>e?runtime.threads.findIndex(t=>t.target===e.target&&t.topBlock===e.topBlock&&t.stackClick===e.stackClick&&t.updateMonitor===e.updateMonitor):-1,findNewSteppingThread=e=>{let t=runtime.threads;for(let r=e;r{if(steppingThread){let e=pausedThreadState.get(steppingThread);compensateForTimePassedWhilePaused(steppingThread,e),e.time=runtime.currentMSecs;let t=singleStepThread(steppingThread);t||(steppingThread=findNewSteppingThread(getThreadIndex(steppingThread)+1))}if(!steppingThread){setSteppingThread(findNewSteppingThread(0)),runtime.ioDevices.clock._pausedTime+=runtime.currentStepTime;let r=runtime.audioEngine.audioContext;for(let a of runtime.targets)for(let s of Object.keys(a.sprite.soundBank.soundPlayers)){let i=a.sprite.soundBank.soundPlayers[s];i.outputNode&&(i.outputNode.stop(r.currentTime),i._createSource(),i.outputNode.start(r.currentTime,r.currentTime-i.startingUntil+runtime.currentStepTime/1e3),i.startingUntil-=runtime.currentStepTime/1e3)}for(let n of runtime.threads)pausedThreadState.has(n)&&(pausedThreadState.get(n).time+=runtime.currentStepTime);pauseNewThreads=!0;let u=runtime._hats;for(let o in u){if(!Object.prototype.hasOwnProperty.call(u,o))continue;let l=u[o];l.edgeActivated&&runtime.startHats(o)}pauseNewThreads=!1}},setup=()=>{let e=vm,t=e.runtime.sequencer.stepThreads;e.runtime.sequencer.stepThreads=function(){if(isPaused())for(let e of this.runtime.threads)ensurePausedThreadIsStillPaused(e);return t.call(this)};let r=e.runtime.greenFlag;e.runtime.greenFlag=function(){return setPaused(!1),r.call(this)};let a=e.runtime.startHats;e.runtime.startHats=function(...e){let t=e[0],r="event_whenbroadcastreceived"===t||"control_start_as_clone"===t;if(pauseNewThreads){if(!r&&!this.getIsEdgeActivatedHat(t))return[];let s=a.apply(this,e);for(let i of s)pauseThread(i);return s}return paused&&!r?[]:a.apply(this,e)};let s=e.runtime._getMonitorThreadCount;e.runtime._getMonitorThreadCount=function(e){let t=s.call(this,e);if(paused)for(let r of e)pausedThreadState.has(r)&&t++;return t}}; runtime.on("PROJECT_STOP_ALL", () => { storedScripts = {} }); From da2e8f238f719b7f5b32d980e9612f0d7d3136b2 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:54:40 -0700 Subject: [PATCH 12/13] Pause-Utilities -- Minor Fix --- extensions/SharkPool/Pause-Utilities.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index 42378bf146..9eedb55f40 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -3,7 +3,7 @@ // Description: Pause the Project and certain Scripts // By: SharkPool -// Version V.1.7.02 +// Version V.1.7.03 (function (Scratch) { "use strict"; @@ -52,12 +52,12 @@ }; // Inject Pause Module (minified) - // https://github.com/TurboWarp/scratch-gui/blob/develop/src/addons/addons/debugger/module.js - // eslint-disable-next-line + // https://github.com/TurboWarp/scratch-gui/blob/develop/src/addons/addons/debugger/module.js#L407 const isPaused=()=>paused,pauseThread=e=>{if(e.updateMonitor||pausedThreadState.has(e))return;let t={time:runtime.currentMSecs,status:e.status};pausedThreadState.set(e,t),e.status=1},ensurePausedThreadIsStillPaused=e=>{if(4===e.status)return;let t=pausedThreadState.get(e);t&&1!==e.status&&(t.status=e.status,e.status=1)},setSteppingThread=e=>{steppingThread=e},compensateForTimePassedWhilePaused=(e,t)=>{e.timer&&(e.timer.startTime+=runtime.currentMSecs-t.time),e.compatibilityStackFrame&&e.compatibilityStackFrame.timer&&(e.compatibilityStackFrame.timer.startTime+=runtime.currentMSecs-t.time);let r=e.peekStackFrame();r&&r.executionContext&&r.executionContext.timer&&(r.executionContext.timer.startTime+=runtime.currentMSecs-t.time)},stepUnsteppedThreads=e=>{let t=runtime.threads,r=getThreadIndex(e);if(-1!==r)for(let a=r;a{let t=paused!==e;if(t&&(paused=e),paused){audioContextStateChange=audioContextStateChange.then(()=>runtime.audioEngine.audioContext.suspend()),runtime.ioDevices.clock._paused||runtime.ioDevices.clock.pause(),runtime.threads.forEach(pauseThread);let r=runtime.sequencer.activeThread;r&&setSteppingThread(r)}if(!paused&&t){for(let a of(audioContextStateChange=audioContextStateChange.then(()=>runtime.audioEngine.audioContext.resume()),runtime.ioDevices.clock.resume(),runtime.threads)){let s=pausedThreadState.get(a);s&&(compensateForTimePassedWhilePaused(a,s),a.status=s.status)}pausedThreadState=new WeakMap;let i=steppingThread;stepUnsteppedThreads(i),steppingThread=null}},getRunningThread=()=>steppingThread,singleStepThread=e=>{if(4===e.status||e.isCompiled)return!1;let t=e.peekStack();if(!t&&(e.popStack(),0===e.stack.length))return e.status=4,!1;pauseNewThreads=!0,runtime.sequencer.activeThread=e;let r=["special error used by Scratch Addons for implementing single-stepping"];Object.defineProperty(e,"blockGlowInFrame",{set(e){throw r}});try{e.status=0,e.warpTimer&&e.warpTimer.start();try{runtime.sequencer.stepThread(e)}catch(a){if(a!==r)throw a}if(0!==e.status)return!1;for(e.peekStack()===t&&e.goToNextBlock();!e.peekStack();){if(e.popStack(),0===e.stack.length)return e.status=4,!1;let s=e.peekStackFrame();if(s.isLoop){if(e.peekStackFrame().warpMode)continue;return!1}if(s.waitingReporter)return!1;e.goToNextBlock()}return!0}finally{pauseNewThreads=!1,runtime.sequencer.activeThread=null,Object.defineProperty(e,"blockGlowInFrame",{value:t,configurable:!0,enumerable:!0,writable:!0}),4!==e.status&&(e.status=1)}},getRealStatus=e=>{let t=pausedThreadState.get(e);return t?t.status:e.status},getThreadIndex=e=>e?runtime.threads.findIndex(t=>t.target===e.target&&t.topBlock===e.topBlock&&t.stackClick===e.stackClick&&t.updateMonitor===e.updateMonitor):-1,findNewSteppingThread=e=>{let t=runtime.threads;for(let r=e;r{if(steppingThread){let e=pausedThreadState.get(steppingThread);compensateForTimePassedWhilePaused(steppingThread,e),e.time=runtime.currentMSecs;let t=singleStepThread(steppingThread);t||(steppingThread=findNewSteppingThread(getThreadIndex(steppingThread)+1))}if(!steppingThread){setSteppingThread(findNewSteppingThread(0)),runtime.ioDevices.clock._pausedTime+=runtime.currentStepTime;let r=runtime.audioEngine.audioContext;for(let a of runtime.targets)for(let s of Object.keys(a.sprite.soundBank.soundPlayers)){let i=a.sprite.soundBank.soundPlayers[s];i.outputNode&&(i.outputNode.stop(r.currentTime),i._createSource(),i.outputNode.start(r.currentTime,r.currentTime-i.startingUntil+runtime.currentStepTime/1e3),i.startingUntil-=runtime.currentStepTime/1e3)}for(let n of runtime.threads)pausedThreadState.has(n)&&(pausedThreadState.get(n).time+=runtime.currentStepTime);pauseNewThreads=!0;let u=runtime._hats;for(let o in u){if(!Object.prototype.hasOwnProperty.call(u,o))continue;let l=u[o];l.edgeActivated&&runtime.startHats(o)}pauseNewThreads=!1}},setup=()=>{let e=vm,t=e.runtime.sequencer.stepThreads;e.runtime.sequencer.stepThreads=function(){if(isPaused())for(let e of this.runtime.threads)ensurePausedThreadIsStillPaused(e);return t.call(this)};let r=e.runtime.greenFlag;e.runtime.greenFlag=function(){return setPaused(!1),r.call(this)};let a=e.runtime.startHats;e.runtime.startHats=function(...e){let t=e[0],r="event_whenbroadcastreceived"===t||"control_start_as_clone"===t;if(pauseNewThreads){if(!r&&!this.getIsEdgeActivatedHat(t))return[];let s=a.apply(this,e);for(let i of s)pauseThread(i);return s}return paused&&!r?[]:a.apply(this,e)};let s=e.runtime._getMonitorThreadCount;e.runtime._getMonitorThreadCount=function(e){let t=s.call(this,e);if(paused)for(let r of e)pausedThreadState.has(r)&&t++;return t}}; runtime.on("PROJECT_STOP_ALL", () => { storedScripts = {} }); runtime.on("SP_PROJECT_UNPAUSED", () => { + paused = runtime.ioDevices.clock._paused; runtime.startHats("SPPause_whenProjectUnPaused"); // Fix Paused Threads (Rare and Shouldnt Happen, but Failsafe) for (let i = 0; i < runtime.threads.length; i++) { From f050b937c7b5f09eb1ac6d3e251ded11de333523 Mon Sep 17 00:00:00 2001 From: SharkPool <139097378+SharkPool-SP@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:56:53 -0700 Subject: [PATCH 13/13] shut up prettier --- extensions/SharkPool/Pause-Utilities.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/SharkPool/Pause-Utilities.js b/extensions/SharkPool/Pause-Utilities.js index 9eedb55f40..f2596c7ee2 100644 --- a/extensions/SharkPool/Pause-Utilities.js +++ b/extensions/SharkPool/Pause-Utilities.js @@ -52,7 +52,8 @@ }; // Inject Pause Module (minified) - // https://github.com/TurboWarp/scratch-gui/blob/develop/src/addons/addons/debugger/module.js#L407 + // https://github.com/TurboWarp/scratch-gui/blob/develop/src/addons/addons/debugger/module.js + // eslint-disable-next-line const isPaused=()=>paused,pauseThread=e=>{if(e.updateMonitor||pausedThreadState.has(e))return;let t={time:runtime.currentMSecs,status:e.status};pausedThreadState.set(e,t),e.status=1},ensurePausedThreadIsStillPaused=e=>{if(4===e.status)return;let t=pausedThreadState.get(e);t&&1!==e.status&&(t.status=e.status,e.status=1)},setSteppingThread=e=>{steppingThread=e},compensateForTimePassedWhilePaused=(e,t)=>{e.timer&&(e.timer.startTime+=runtime.currentMSecs-t.time),e.compatibilityStackFrame&&e.compatibilityStackFrame.timer&&(e.compatibilityStackFrame.timer.startTime+=runtime.currentMSecs-t.time);let r=e.peekStackFrame();r&&r.executionContext&&r.executionContext.timer&&(r.executionContext.timer.startTime+=runtime.currentMSecs-t.time)},stepUnsteppedThreads=e=>{let t=runtime.threads,r=getThreadIndex(e);if(-1!==r)for(let a=r;a{let t=paused!==e;if(t&&(paused=e),paused){audioContextStateChange=audioContextStateChange.then(()=>runtime.audioEngine.audioContext.suspend()),runtime.ioDevices.clock._paused||runtime.ioDevices.clock.pause(),runtime.threads.forEach(pauseThread);let r=runtime.sequencer.activeThread;r&&setSteppingThread(r)}if(!paused&&t){for(let a of(audioContextStateChange=audioContextStateChange.then(()=>runtime.audioEngine.audioContext.resume()),runtime.ioDevices.clock.resume(),runtime.threads)){let s=pausedThreadState.get(a);s&&(compensateForTimePassedWhilePaused(a,s),a.status=s.status)}pausedThreadState=new WeakMap;let i=steppingThread;stepUnsteppedThreads(i),steppingThread=null}},getRunningThread=()=>steppingThread,singleStepThread=e=>{if(4===e.status||e.isCompiled)return!1;let t=e.peekStack();if(!t&&(e.popStack(),0===e.stack.length))return e.status=4,!1;pauseNewThreads=!0,runtime.sequencer.activeThread=e;let r=["special error used by Scratch Addons for implementing single-stepping"];Object.defineProperty(e,"blockGlowInFrame",{set(e){throw r}});try{e.status=0,e.warpTimer&&e.warpTimer.start();try{runtime.sequencer.stepThread(e)}catch(a){if(a!==r)throw a}if(0!==e.status)return!1;for(e.peekStack()===t&&e.goToNextBlock();!e.peekStack();){if(e.popStack(),0===e.stack.length)return e.status=4,!1;let s=e.peekStackFrame();if(s.isLoop){if(e.peekStackFrame().warpMode)continue;return!1}if(s.waitingReporter)return!1;e.goToNextBlock()}return!0}finally{pauseNewThreads=!1,runtime.sequencer.activeThread=null,Object.defineProperty(e,"blockGlowInFrame",{value:t,configurable:!0,enumerable:!0,writable:!0}),4!==e.status&&(e.status=1)}},getRealStatus=e=>{let t=pausedThreadState.get(e);return t?t.status:e.status},getThreadIndex=e=>e?runtime.threads.findIndex(t=>t.target===e.target&&t.topBlock===e.topBlock&&t.stackClick===e.stackClick&&t.updateMonitor===e.updateMonitor):-1,findNewSteppingThread=e=>{let t=runtime.threads;for(let r=e;r{if(steppingThread){let e=pausedThreadState.get(steppingThread);compensateForTimePassedWhilePaused(steppingThread,e),e.time=runtime.currentMSecs;let t=singleStepThread(steppingThread);t||(steppingThread=findNewSteppingThread(getThreadIndex(steppingThread)+1))}if(!steppingThread){setSteppingThread(findNewSteppingThread(0)),runtime.ioDevices.clock._pausedTime+=runtime.currentStepTime;let r=runtime.audioEngine.audioContext;for(let a of runtime.targets)for(let s of Object.keys(a.sprite.soundBank.soundPlayers)){let i=a.sprite.soundBank.soundPlayers[s];i.outputNode&&(i.outputNode.stop(r.currentTime),i._createSource(),i.outputNode.start(r.currentTime,r.currentTime-i.startingUntil+runtime.currentStepTime/1e3),i.startingUntil-=runtime.currentStepTime/1e3)}for(let n of runtime.threads)pausedThreadState.has(n)&&(pausedThreadState.get(n).time+=runtime.currentStepTime);pauseNewThreads=!0;let u=runtime._hats;for(let o in u){if(!Object.prototype.hasOwnProperty.call(u,o))continue;let l=u[o];l.edgeActivated&&runtime.startHats(o)}pauseNewThreads=!1}},setup=()=>{let e=vm,t=e.runtime.sequencer.stepThreads;e.runtime.sequencer.stepThreads=function(){if(isPaused())for(let e of this.runtime.threads)ensurePausedThreadIsStillPaused(e);return t.call(this)};let r=e.runtime.greenFlag;e.runtime.greenFlag=function(){return setPaused(!1),r.call(this)};let a=e.runtime.startHats;e.runtime.startHats=function(...e){let t=e[0],r="event_whenbroadcastreceived"===t||"control_start_as_clone"===t;if(pauseNewThreads){if(!r&&!this.getIsEdgeActivatedHat(t))return[];let s=a.apply(this,e);for(let i of s)pauseThread(i);return s}return paused&&!r?[]:a.apply(this,e)};let s=e.runtime._getMonitorThreadCount;e.runtime._getMonitorThreadCount=function(e){let t=s.call(this,e);if(paused)for(let r of e)pausedThreadState.has(r)&&t++;return t}}; runtime.on("PROJECT_STOP_ALL", () => { storedScripts = {} });