From 809c272bd8d0839ed972a7e8a0ffbff01590ceb1 Mon Sep 17 00:00:00 2001 From: Reimar Date: Tue, 14 Oct 2025 14:49:36 +0200 Subject: [PATCH] destroy event listeners when code is stopped, refactor away codestopper --- src/code_runner.js | 15 +++-- src/code_stopper.js | 24 -------- src/gamelib.js | 130 +++++++++++++++++++++++++------------------- src/index.js | 11 ++-- 4 files changed, 87 insertions(+), 93 deletions(-) delete mode 100644 src/code_stopper.js diff --git a/src/code_runner.js b/src/code_runner.js index ffa38d0..0eddc7c 100644 --- a/src/code_runner.js +++ b/src/code_runner.js @@ -1,9 +1,10 @@ export class CodeRunner { - constructor(console, codeStopper) { + constructor(console, gamelib) { this.console = console; - this.codeStopper = codeStopper; + this.gamelib = gamelib; this.isRunning = false; this.evalScope = {}; + this.abortController = new AbortController(); globalThis.playgroundConsole = this.console; } @@ -14,12 +15,13 @@ export class CodeRunner { async run() { this.isRunning = true; + this.abortController = new AbortController(); + + this.gamelib.init(); this.console.clear(); this.console.log("Running code..."); - this.codeStopper.start(); - // Use RNG to prevent caching const encodedText = encodeURIComponent( `let console=playgroundConsole;${this.code}/*(tph): ${Math.random()}*/`, @@ -35,7 +37,10 @@ export class CodeRunner { stop() { this.isRunning = false; - this.codeStopper.stop(); + this.abortController.abort(); + + this.gamelib.destroy(); + this.console.log("Stopping code..."); } diff --git a/src/code_stopper.js b/src/code_stopper.js deleted file mode 100644 index 72d92ab..0000000 --- a/src/code_stopper.js +++ /dev/null @@ -1,24 +0,0 @@ -export class CodeStopper { - constructor() { - this.abortController = new AbortController(); - } - - start() { - this.abortController = new AbortController(); - } - - stop() { - this.abortController.abort(); - } - - isStopped() { - return this.abortController.signal.aborted; - } - - addListener(fn) { - this.abortController.signal - .addEventListener("abort", () => { - fn(); - }); - } -} diff --git a/src/gamelib.js b/src/gamelib.js index ba195d4..3b38b55 100644 --- a/src/gamelib.js +++ b/src/gamelib.js @@ -1,13 +1,5 @@ export class Gamelib { - keysPressed = new Set(); - keyPressHandlers = new Map(); - keyReleaseHandlers = new Map(); - mouseMoveHandler = null; - mouseButtonsPressed = new Set(); - mouseDownHandlers = new Map(); - mouseUpHandlers = new Map(); - mouseX = null; - mouseY = null; + #loopInterval; MouseButton = { Left: 0, @@ -15,9 +7,8 @@ export class Gamelib { Middle: 2, }; - constructor(console, codeStopper, assetProvider, canvasElement) { + constructor(console, assetProvider, canvasElement) { this.console = console; - this.codeStopper = codeStopper; this.assetProvider = assetProvider; this.canvas = canvasElement; @@ -28,50 +19,37 @@ export class Gamelib { this.cx = this.canvas.getContext("2d"); this.cx.imageSmootingEnabled = false; - document.body.addEventListener("keydown", (ev) => { - this.keysPressed.add(ev.key); - this.keyPressHandlers.get(ev.key)?.(); - }); + this.keysPressed = new Set(); + this.keyPressHandlers = new Map(); + this.keyReleaseHandlers = new Map(); + this.mouseMoveHandler = null; + this.mouseButtonsPressed = new Set(); + this.mouseDownHandlers = new Map(); + this.mouseUpHandlers = new Map(); + this.mouseX = null; + this.mouseY = null; + } - document.body.addEventListener("keyup", (ev) => { - this.keysPressed.delete(ev.key); - this.keyReleaseHandlers.get(ev.key)?.(); - }); + init() { + document.body.addEventListener("keydown", this.#keydownListener.bind(this)); + document.body.addEventListener("keyup", this.#keyupListener.bind(this)); - canvasElement.addEventListener("mousemove", (ev) => { - const ratioX = canvasElement.width / canvasElement.clientWidth; - const ratioY = canvasElement.height / canvasElement.clientHeight; + this.canvas.addEventListener("mousemove", this.#mousemoveListener.bind(this)); + this.canvas.addEventListener("mousedown", this.#mousedownListener.bind(this)); + this.canvas.addEventListener("mouseup", this.#mouseupListener.bind(this)); + this.canvas.addEventListener("contextmenu", this.#contextMenuListener.bind(this)); + } - this.mouseX = ev.offsetX * ratioX; - this.mouseY = ev.offsetY * ratioY; + destroy() { + document.body.removeEventListener("keydown", this.#keydownListener); + document.body.removeEventListener("keyup", this.#keyupListener); - this.mouseMoveHandler?.( - ev.offsetX * ratioX, - ev.offsetY * ratioY, - ev.movementX * ratioX, - ev.movementY * ratioY, - ); - }); + this.canvas.removeEventListener("mousemove", this.#mousemoveListener); + this.canvas.removeEventListener("mousedown", this.#mousedownListener); + this.canvas.removeEventListener("mouseup", this.#mouseupListener); + this.canvas.removeEventListener("contextmenu", this.#contextMenuListener); - canvasElement.addEventListener("mousedown", (ev) => { - const ratioX = canvasElement.width / canvasElement.clientWidth; - const ratioY = canvasElement.height / canvasElement.clientHeight; - - this.mouseButtonsPressed.add(ev.button); - this.mouseDownHandlers.get(ev.button)?.(ev.offsetX * ratioX, ev.offsetY * ratioY); - }); - - canvasElement.addEventListener("mouseup", (ev) => { - const ratioX = canvasElement.width / canvasElement.clientWidth; - const ratioY = canvasElement.height / canvasElement.clientHeight; - - this.mouseButtonsPressed.delete(ev.button); - this.mouseUpHandlers.get(ev.button)?.(ev.offsetX * ratioX, ev.offsetY * ratioY); - }); - - canvasElement.addEventListener("contextmenu", (ev) => { - ev.preventDefault(); - }); + clearInterval(this.#loopInterval); } println(msg) { @@ -80,7 +58,8 @@ export class Gamelib { startGameLoop(loopFunction) { let before = Date.now(); - const loopInterval = setInterval(() => { + + this.#loopInterval = setInterval(() => { const now = Date.now(); const deltaT = (now - before) / 1000; before = now; @@ -90,14 +69,16 @@ export class Gamelib { this.console.error(error); } }, 16); + } - if (this.codeStopper?.isStopped()) { - clearInterval(loopInterval); - } + #keydownListener(ev) { + this.keysPressed.add(ev.key); + this.keyPressHandlers.get(ev.key)?.(); + } - this.codeStopper?.addListener(() => { - clearInterval(loopInterval); - }); + #keyupListener(ev) { + this.keysPressed.delete(ev.key); + this.keyReleaseHandlers.get(ev.key)?.(); } isPressed(key) { @@ -112,6 +93,37 @@ export class Gamelib { this.keyReleaseHandlers.set(key, handlerFunction); } + #mousemoveListener(ev) { + const ratioX = this.canvas.width / this.canvas.clientWidth; + const ratioY = this.canvas.height / this.canvas.clientHeight; + + this.mouseX = ev.offsetX * ratioX; + this.mouseY = ev.offsetY * ratioY; + + this.mouseMoveHandler?.( + ev.offsetX * ratioX, + ev.offsetY * ratioY, + ev.movementX * ratioX, + ev.movementY * ratioY, + ); + } + + #mousedownListener(ev) { + const ratioX = this.canvas.width / this.canvas.clientWidth; + const ratioY = this.canvas.height / this.canvas.clientHeight; + + this.mouseButtonsPressed.add(ev.button); + this.mouseDownHandlers.get(ev.button)?.(ev.offsetX * ratioX, ev.offsetY * ratioY); + } + + #mouseupListener(ev) { + const ratioX = this.canvas.width / this.canvas.clientWidth; + const ratioY = this.canvas.height / this.canvas.clientHeight; + + this.mouseButtonsPressed.delete(ev.button); + this.mouseUpHandlers.get(ev.button)?.(ev.offsetX * ratioX, ev.offsetY * ratioY); + } + onMouseMove(handlerFunction) { this.mouseMoveHandler = handlerFunction; } @@ -128,6 +140,10 @@ export class Gamelib { this.mouseUpHandlers.set(button, handlerFunction); } + #contextMenuListener(ev) { + ev.preventDefault(); + } + rgb(red, green, blue) { return `rgb(${red}, ${green}, ${blue})`; } diff --git a/src/index.js b/src/index.js index 4b6f2db..ff92dca 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,6 @@ import { CodeRunner } from "./code_runner.js"; import { AssetEditor } from "./asset_editor.js"; import { AssetProvider } from "./asset_provider.js"; import { Gamelib } from "./gamelib.js"; -import { CodeStopper } from "./code_stopper.js"; import { KarlkoderCodec } from "./karlkoder_codec.js"; import { promptUpload } from "./prompt_upload.js"; import { GamelibCompleter } from "./gamelib_completer.js"; @@ -40,20 +39,14 @@ addEventListener("DOMContentLoaded", () => { playgroundConsole.getInterface().log("Karlkode 1.0"); }); -const codeStopper = new CodeStopper(); - const assetProvider = new AssetProvider(); -const codeRunner = new CodeRunner(playgroundConsole.getInterface(), codeStopper); const assetEditor = new AssetEditor(document.querySelector("#asset-editor-container"), []); -new ConsoleInput(document.querySelector("#console-input"), playgroundConsole, codeRunner); - const htmlExporter = new HtmlExporter(assetProvider); const gamelib = new Gamelib( playgroundConsole.getInterface(), - codeStopper, assetProvider, document.querySelector("canvas"), ); @@ -64,6 +57,10 @@ globalThis.karlkoder = { }, }; +const codeRunner = new CodeRunner(playgroundConsole.getInterface(), gamelib); + +new ConsoleInput(document.querySelector("#console-input"), playgroundConsole, codeRunner); + const runButton = document.querySelector("#run-button"); const toggleAssetEditorButton = document.querySelector("#toggle-asset-editor-button"); const projectName = document.querySelector("#project-name");