From e3182f2759a582606a1b7d05b88944338c05ea2d Mon Sep 17 00:00:00 2001 From: 8to16 Date: Fri, 9 Jan 2026 21:52:04 +0000 Subject: [PATCH 01/21] mesh is here, kinda --- extensions/8to16/mesh.js | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 extensions/8to16/mesh.js diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js new file mode 100644 index 0000000000..8a1046063c --- /dev/null +++ b/extensions/8to16/mesh.js @@ -0,0 +1,78 @@ +// Name: Mesh +// ID: eightxtwoMesh +// Description: Communicate with other projects, without the need for a network. +// By: 8to16 +// License: MPL-2.0 + +(function (Scratch) { + "use strict"; + + if (!Scratch.extensions.unsandboxed) { + throw new Error("Mesh extension must run unsandboxed"); + } + + const bc = new BroadcastChannel("extensions.turbowarp.org/8to16/mesh"); + + // taken from local-storage + const session = (() => { + // doesn't need to be cryptographically secure and doesn't need to have excessive length + // this has 16^16 = 18446744073709551616 possible session IDs which is plenty + const soup = "0123456789abcdef"; + let id = ""; + for (let i = 0; i < 16; i++) { + id += soup[Math.floor(Math.random() * soup.length)]; + } + return id; + })(); + + bc.onmessage = ({ data }) => { + console.log(data); + if (data.session === session) { + console.log("Hi"); + return; + } + Scratch.vm.runtime.startHats("eightxtwoMesh_when", { + BROADCAST: data.name, + }); + }; + + class Mesh { + getInfo() { + return { + id: "eightxtwoMesh", + name: Scratch.translate("Mesh"), + blocks: [ + { + opcode: "broadcast", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("broadcast [BROADCAST]"), + arguments: { + BROADCAST: { + type: Scratch.ArgumentType.STRING, + defaultValue: "dango clicked", + }, + }, + }, + { + opcode: "when", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when I receive [BROADCAST]"), + isEdgeActivated: false, + arguments: { + BROADCAST: { + type: Scratch.ArgumentType.STRING, + defaultValue: "dango clicked", + }, + }, + }, + ], + }; + } + + broadcast({ BROADCAST }) { + bc.postMessage({ name: BROADCAST, session: session }); + } + } + + Scratch.extensions.register(new Mesh()); +})(Scratch); From 3b0b33896b31dcdcfdb7d653804b69f44453ca10 Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 02:10:39 +0000 Subject: [PATCH 02/21] 8to16/mesh: mvp --- docs/8to16/mesh.md | 26 ++++++++ extensions/8to16/mesh.js | 128 +++++++++++++++++++++++++++++-------- extensions/extensions.json | 1 + images/8to16/mesh.svg | 91 ++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 27 deletions(-) create mode 100644 docs/8to16/mesh.md create mode 100644 images/8to16/mesh.svg diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md new file mode 100644 index 0000000000..a6ce7c0dd8 --- /dev/null +++ b/docs/8to16/mesh.md @@ -0,0 +1,26 @@ +# Mesh + +Mesh lets you communicate with other projects running in the same browser, using a special type of message +called a mesh. + +## Setup + +To setup a connection between a project, create a mesh in one project, and then create another mesh with the +same name in another project. + +## Blocks + +```scratch +broadcast mesh ( v) :: #00acff +``` +This block lets you send a mesh to all other projects that have the extension running. It can be received +with the hat block below. + +```scratch +when I receive mesh ( v) :: #00acff hat +``` +This block will activate when another project sends you a mesh with the same name. + +> [!IMPORTANT] +> Projects cannot access variables or lists stored in each other, even if they have the same name and they +> are being used in a mesh. \ No newline at end of file diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index 8a1046063c..a8d0751e58 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -1,6 +1,6 @@ // Name: Mesh // ID: eightxtwoMesh -// Description: Communicate with other projects, without the need for a network. +// Description: Send messages to other projects. // By: 8to16 // License: MPL-2.0 @@ -10,9 +10,32 @@ if (!Scratch.extensions.unsandboxed) { throw new Error("Mesh extension must run unsandboxed"); } + const vm = Scratch.vm; const bc = new BroadcastChannel("extensions.turbowarp.org/8to16/mesh"); + vm.runtime.on("RUNTIME_DISPOSED", () => { + vm.runtime.extensionStorage.meshages = []; + }); + vm.runtime.on("PROJECT_LOADED", () => { + vm.runtime.extensionManager.refreshBlocks(); + }); + + // Spaghetti ahead! + const getMeshages = () => vm.runtime.extensionStorage?.meshages ?? []; + const addMeshage = (name) => { + if (getMeshages().includes(name)) return; + if (name === "") return; + vm.runtime.extensionStorage.meshages = [...getMeshages(), name].sort(); + vm.extensionManager.refreshBlocks(); + }; + const delMeshage = (name) => { + vm.runtime.extensionStorage.meshages = getMeshages().filter( + (n) => n !== name + ); + vm.extensionManager.refreshBlocks(); + }; + // taken from local-storage const session = (() => { // doesn't need to be cryptographically secure and doesn't need to have excessive length @@ -27,45 +50,96 @@ bc.onmessage = ({ data }) => { console.log(data); - if (data.session === session) { - console.log("Hi"); - return; - } - Scratch.vm.runtime.startHats("eightxtwoMesh_when", { + if (data.session === session) return; + vm.runtime.startHats("eightxtwoMesh_when", { BROADCAST: data.name, }); }; class Mesh { + getMeshages() { + const meshages = getMeshages(); + return meshages.length === 0 ? [""] : meshages; + } + new() { + // taken from SharkPool/Camera + // in a Button Context, ScratchBlocks always exists + ScratchBlocks.prompt( + Scratch.translate("New mesh name:"), + "", + (value) => addMeshage(value), + Scratch.translate("Make a Mesh"), + "broadcast_msg" + ); + } + remove() { + // taken from SharkPool/Camera + // in a Button Context, ScratchBlocks always exists + ScratchBlocks.prompt( + Scratch.translate("Remove mesh named:"), + "", + (value) => delMeshage(value), + Scratch.translate("Remove a Mesh"), + "broadcast_msg" + ); + } getInfo() { return { id: "eightxtwoMesh", name: Scratch.translate("Mesh"), + // todo: pick better colours + color1: "#00acff", + color2: "#0088cc", + color3: "#0078b4ff", blocks: [ { - opcode: "broadcast", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("broadcast [BROADCAST]"), - arguments: { - BROADCAST: { - type: Scratch.ArgumentType.STRING, - defaultValue: "dango clicked", - }, - }, - }, - { - opcode: "when", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when I receive [BROADCAST]"), - isEdgeActivated: false, - arguments: { - BROADCAST: { - type: Scratch.ArgumentType.STRING, - defaultValue: "dango clicked", - }, - }, + func: "new", + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate("Make a Mesh"), }, + ...(getMeshages().length !== 0 + ? [ + { + func: "remove", + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate("Remove a Mesh"), + }, + { + opcode: "broadcast", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("broadcast mesh [BROADCAST]"), + arguments: { + BROADCAST: { + type: Scratch.ArgumentType.STRING, + menu: "MESHES_ACCEPT", + }, + }, + }, + { + opcode: "when", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when I receive mesh [BROADCAST]"), + isEdgeActivated: false, + arguments: { + BROADCAST: { + type: Scratch.ArgumentType.STRING, + menu: "MESHES_NOACCEPT", + }, + }, + }, + ] + : []), ], + menus: { + MESHES_ACCEPT: { + acceptReporters: true, + items: "getMeshages", + }, + MESHES_NOACCEPT: { + acceptReporters: false, + items: "getMeshages", + }, + }, }; } diff --git a/extensions/extensions.json b/extensions/extensions.json index 442f300d59..f9c324c9f2 100644 --- a/extensions/extensions.json +++ b/extensions/extensions.json @@ -94,6 +94,7 @@ "Lily/CommentBlocks", "veggiecan/LongmanDictionary", "CubesterYT/TurboHook", + "8to16/mesh", "Alestore/nfcwarp", "steamworks", "itchio", diff --git a/images/8to16/mesh.svg b/images/8to16/mesh.svg new file mode 100644 index 0000000000..0ad873334a --- /dev/null +++ b/images/8to16/mesh.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + From 99b68878b8e73b0d71b5f2adf940bd8bc8e0b9af Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 02:25:41 +0000 Subject: [PATCH 03/21] 8to16/mesh: tweaks --- docs/8to16/mesh.md | 7 ++++--- extensions/8to16/mesh.js | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md index a6ce7c0dd8..46983c3732 100644 --- a/docs/8to16/mesh.md +++ b/docs/8to16/mesh.md @@ -13,13 +13,14 @@ same name in another project. ```scratch broadcast mesh ( v) :: #00acff ``` -This block lets you send a mesh to all other projects that have the extension running. It can be received -with the hat block below. +This block sends a mesh to all other projects that have the extension running. It triggers the hat block below +in projects that listen for the mesh. ```scratch when I receive mesh ( v) :: #00acff hat ``` -This block will activate when another project sends you a mesh with the same name. +This block will activate when another project sends you a mesh with the same name. It is triggered using the block +above in any other project. > [!IMPORTANT] > Projects cannot access variables or lists stored in each other, even if they have the same name and they diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index a8d0751e58..0f3fc5ec88 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -1,6 +1,6 @@ // Name: Mesh // ID: eightxtwoMesh -// Description: Send messages to other projects. +// Description: Send and receive messages between other projects. // By: 8to16 // License: MPL-2.0 @@ -105,25 +105,25 @@ text: Scratch.translate("Remove a Mesh"), }, { - opcode: "broadcast", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("broadcast mesh [BROADCAST]"), + opcode: "when", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when I receive mesh [BROADCAST]"), + isEdgeActivated: false, arguments: { BROADCAST: { type: Scratch.ArgumentType.STRING, - menu: "MESHES_ACCEPT", + menu: "MESHES_NOACCEPT", }, }, }, { - opcode: "when", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when I receive mesh [BROADCAST]"), - isEdgeActivated: false, + opcode: "broadcast", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("broadcast mesh [BROADCAST]"), arguments: { BROADCAST: { type: Scratch.ArgumentType.STRING, - menu: "MESHES_NOACCEPT", + menu: "MESHES_ACCEPT", }, }, }, From 86a5044cff9655c9d2feac47fac0f54878703fbc Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 02:37:52 +0000 Subject: [PATCH 04/21] 8to16/mesh: docs tweak --- docs/8to16/mesh.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md index 46983c3732..5889cb6e76 100644 --- a/docs/8to16/mesh.md +++ b/docs/8to16/mesh.md @@ -19,8 +19,8 @@ in projects that listen for the mesh. ```scratch when I receive mesh ( v) :: #00acff hat ``` -This block will activate when another project sends you a mesh with the same name. It is triggered using the block -above in any other project. +This block will activate when another project sends *this* project a mesh with the same name. It is triggered +using the block above in any other project. > [!IMPORTANT] > Projects cannot access variables or lists stored in each other, even if they have the same name and they From d4bd9771f2befdc6d0e084fff8dbcacb20db017d Mon Sep 17 00:00:00 2001 From: 8to16 <197376797+8to16@users.noreply.github.com> Date: Sat, 10 Jan 2026 11:39:35 +0000 Subject: [PATCH 05/21] 8to16/mesh: remove old debugging log --- extensions/8to16/mesh.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index 0f3fc5ec88..20de63555d 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -49,7 +49,6 @@ })(); bc.onmessage = ({ data }) => { - console.log(data); if (data.session === session) return; vm.runtime.startHats("eightxtwoMesh_when", { BROADCAST: data.name, From 253f9d2b1ae1d328b6d9bac19798dcb43d7cc37e Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 12:07:10 +0000 Subject: [PATCH 06/21] 8to16/mesh: change image --- images/8to16/mesh.svg | 92 +------------------------------------------ images/README.md | 5 ++- 2 files changed, 5 insertions(+), 92 deletions(-) diff --git a/images/8to16/mesh.svg b/images/8to16/mesh.svg index 0ad873334a..e1390d2246 100644 --- a/images/8to16/mesh.svg +++ b/images/8to16/mesh.svg @@ -1,91 +1 @@ - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/images/README.md b/images/README.md index fef54d00ef..c3344c7b7a 100644 --- a/images/README.md +++ b/images/README.md @@ -323,4 +323,7 @@ All images in this folder are licensed under the [GNU General Public License ver - Created by [@SharkPool-SP](https://github.com/SharkPool-SP/) ## DogeisCut/FormatNumbers.png - - Created by [@Dillon](https://github.com/DillonRGaming) \ No newline at end of file + - Created by [@Dillon](https://github.com/DillonRGaming) + + ## 8to16/mesh.svg + - Creatde by [@kx1bx1](https://github.com/kx1bx1) in https://github.com/TurboWarp/extensions/pull/2382#issuecomment-3731596025 \ No newline at end of file From 2737b5cc04114f2b66266bdf51969f36e4e44664 Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 12:08:17 +0000 Subject: [PATCH 07/21] 8to16/mesh: newlines are poisonous --- docs/8to16/mesh.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md index 5889cb6e76..f3abc0be12 100644 --- a/docs/8to16/mesh.md +++ b/docs/8to16/mesh.md @@ -1,27 +1,22 @@ # Mesh -Mesh lets you communicate with other projects running in the same browser, using a special type of message -called a mesh. +Mesh lets you communicate with other projects running in the same browser, using a special type of message called a mesh. ## Setup -To setup a connection between a project, create a mesh in one project, and then create another mesh with the -same name in another project. +To setup a connection between a project, create a mesh in one project, and then create another mesh with the same name in another project. ## Blocks ```scratch broadcast mesh ( v) :: #00acff ``` -This block sends a mesh to all other projects that have the extension running. It triggers the hat block below -in projects that listen for the mesh. +This block sends a mesh to all other projects that have the extension running. It triggers the hat block below in projects that listen for the mesh. ```scratch when I receive mesh ( v) :: #00acff hat ``` -This block will activate when another project sends *this* project a mesh with the same name. It is triggered -using the block above in any other project. +This block will activate when another project sends *this* project a mesh with the same name. It is triggered using the block above in any other project. > [!IMPORTANT] -> Projects cannot access variables or lists stored in each other, even if they have the same name and they -> are being used in a mesh. \ No newline at end of file +> Projects cannot access variables or lists stored in each other, even if they have the same name and they are being used in a mesh. \ No newline at end of file From 1f7c9b475bef820fd75d9c795557aa6bd3867973 Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 12:09:54 +0000 Subject: [PATCH 08/21] 8to16/mesh: works in turbowarp desktop --- docs/8to16/mesh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md index f3abc0be12..6bc95dfec9 100644 --- a/docs/8to16/mesh.md +++ b/docs/8to16/mesh.md @@ -1,6 +1,6 @@ # Mesh -Mesh lets you communicate with other projects running in the same browser, using a special type of message called a mesh. +Mesh lets you communicate with other projects running in the same browser or in TurboWarp Desktop, using a special type of message called a mesh. ## Setup From ca4ec739f7888f0f910493d37329f28d8df6a708 Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 12:11:29 +0000 Subject: [PATCH 09/21] 8to16/mesh: minor docs update --- docs/8to16/mesh.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md index 6bc95dfec9..edcdf8ea05 100644 --- a/docs/8to16/mesh.md +++ b/docs/8to16/mesh.md @@ -9,12 +9,12 @@ To setup a connection between a project, create a mesh in one project, and then ## Blocks ```scratch -broadcast mesh ( v) :: #00acff +broadcast mesh (mesh name v) :: #00acff ``` This block sends a mesh to all other projects that have the extension running. It triggers the hat block below in projects that listen for the mesh. ```scratch -when I receive mesh ( v) :: #00acff hat +when I receive mesh [mesh name v] :: #00acff hat ``` This block will activate when another project sends *this* project a mesh with the same name. It is triggered using the block above in any other project. From e15c55cec1a121fd1a64100f32b63a135a273287 Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 14:19:27 +0000 Subject: [PATCH 10/21] 8to16/mesh: WIP enhancements --- docs/8to16/mesh.md | 45 +++++- extensions/8to16/mesh.js | 333 ++++++++++++++++++++++++++++++--------- 2 files changed, 299 insertions(+), 79 deletions(-) diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md index edcdf8ea05..4b9b13fe30 100644 --- a/docs/8to16/mesh.md +++ b/docs/8to16/mesh.md @@ -1,22 +1,51 @@ # Mesh -Mesh lets you communicate with other projects running in the same browser or in TurboWarp Desktop, using a special type of message called a mesh. +Mesh lets you communicate with other projects running in the same browser or in TurboWarp Desktop, using special versions of variables and broadcasts. This allows you to for example, create a game that talks to another project running at the same time. + +> [!NOTE] +> Only projects running in the same browser as another will receive messages. However, projects running in a different window will work. ## Setup -To setup a connection between a project, create a mesh in one project, and then create another mesh with the same name in another project. +To setup a connection between a project, create a broadcast or variable in one project, and then create another of the same type and name in another project. ## Blocks +The blocks below are equivalent to other blocks in Scratch, but are received by other projects too. + +### Broadcasts + +```scratch +when I receive [message v] :: #4cdab2 hat +``` +This block will activate when another project sends *this* project a message with the same name. It is triggered using the block above in any other project. + +```scratch +broadcast (message v) :: #4cdab2 +``` +This block sends a message to all other projects that have the extension running. It triggers the hat block below in projects that listen for the message. + ```scratch -broadcast mesh (mesh name v) :: #00acff +broadcast (message v) and wait :: #4cdab2 ``` -This block sends a mesh to all other projects that have the extension running. It triggers the hat block below in projects that listen for the mesh. +This is similar to the block above, but waits for the broadcast to end before continuing to run the script. + +### Variables + +```scratch +(get (variable v) :: #4cdab2) +``` + +This block gets the value of a mesh variable. + +```scratch +set (variable v) to [value] :: #4cdab2 +``` + +This block sets the value of a mesh variable in all currently connected projects. ```scratch -when I receive mesh [mesh name v] :: #00acff hat +change (variable v) by (1) :: #4cdab2 ``` -This block will activate when another project sends *this* project a mesh with the same name. It is triggered using the block above in any other project. -> [!IMPORTANT] -> Projects cannot access variables or lists stored in each other, even if they have the same name and they are being used in a mesh. \ No newline at end of file +This block increments the value of a mesh variable in all currently connected projects by the specified number. \ No newline at end of file diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index 20de63555d..c1264bf1b6 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -13,72 +13,142 @@ const vm = Scratch.vm; const bc = new BroadcastChannel("extensions.turbowarp.org/8to16/mesh"); + let lastSuccessfulMeshage = ""; + if (!vm.runtime.extensionStorage.mesh) + vm.runtime.extensionStorage.mesh = { messages: [], variables: {} }; vm.runtime.on("RUNTIME_DISPOSED", () => { - vm.runtime.extensionStorage.meshages = []; + vm.runtime.extensionStorage.mesh = { messages: [], variables: {} }; }); vm.runtime.on("PROJECT_LOADED", () => { vm.runtime.extensionManager.refreshBlocks(); }); // Spaghetti ahead! - const getMeshages = () => vm.runtime.extensionStorage?.meshages ?? []; + const getMeshages = () => vm.runtime.extensionStorage?.mesh.messages ?? []; const addMeshage = (name) => { if (getMeshages().includes(name)) return; if (name === "") return; - vm.runtime.extensionStorage.meshages = [...getMeshages(), name].sort(); + vm.runtime.extensionStorage.mesh.messages = [...getMeshages(), name]; vm.extensionManager.refreshBlocks(); }; const delMeshage = (name) => { - vm.runtime.extensionStorage.meshages = getMeshages().filter( + if (!getMeshages().includes(name)) return; + vm.runtime.extensionStorage.mesh.messages = getMeshages().filter( (n) => n !== name ); vm.extensionManager.refreshBlocks(); }; - // taken from local-storage - const session = (() => { - // doesn't need to be cryptographically secure and doesn't need to have excessive length - // this has 16^16 = 18446744073709551616 possible session IDs which is plenty - const soup = "0123456789abcdef"; - let id = ""; - for (let i = 0; i < 16; i++) { - id += soup[Math.floor(Math.random() * soup.length)]; - } - return id; - })(); + const getMeshVars = () => vm.runtime.extensionStorage?.mesh.variables ?? {}; + const addMeshVar = (name) => { + if (Object.keys(getMeshVars()).includes(name)) return; + if (name === "") return; + vm.runtime.extensionStorage.mesh.variables = { + ...getMeshVars(), + [name]: "", + }; + vm.extensionManager.refreshBlocks(); + }; + const delMeshVar = (name) => { + if (Object.keys(getMeshVars()).includes(name)) return; + vm.runtime.extensionStorage.mesh.variables = Object.fromEntries( + Object.entries(getMeshVars()).filter((v) => v[0] !== name) + ); + vm.extensionManager.refreshBlocks(); + }; - bc.onmessage = ({ data }) => { - if (data.session === session) return; - vm.runtime.startHats("eightxtwoMesh_when", { - BROADCAST: data.name, - }); + bc.onmessage = async ({ data }) => { + switch (data.type) { + case "broadcast": { + let hats = vm.runtime.startHats("eightxtwoMesh_when", { + BROADCAST: data.name, + }); + if (data.willWait) { + await new Promise((resolve) => { + const poll = () => { + if ( + hats.filter( + (thread) => vm.runtime.threads.indexOf(thread) === -1 + ).length === 0 + ) + resolve(); + else setTimeout(poll, 25); + }; + poll(); + }); + bc.postMessage({ + type: "waitdone", + name: data.name, + }); + } + break; + } + case "var": { + vm.runtime.extensionStorage.mesh.variables[data.key] = data.value; + break; + } + case "waitdone": { + lastSuccessfulMeshage = data.name; + break; + } + } }; + // dummy message to activate the broadcastchannel + bc.postMessage({ type: "hello" }); class Mesh { - getMeshages() { - const meshages = getMeshages(); + getMeshagesForMenu() { + const meshages = getMeshages().sort(); return meshages.length === 0 ? [""] : meshages; } - new() { + getMeshVarsForMenu() { + const meshvars = Object.keys(getMeshVars()).sort(); + return meshvars.length === 0 ? [""] : meshvars; + } + newMsg() { + // taken from SharkPool/Camera + // in a Button Context, ScratchBlocks always exists + ScratchBlocks.prompt( + Scratch.translate("New message name:"), + "", + addMeshage, + Scratch.translate("Mesh Manager"), + "broadcast_msg" + ); + } + removeMsg() { // taken from SharkPool/Camera // in a Button Context, ScratchBlocks always exists ScratchBlocks.prompt( - Scratch.translate("New mesh name:"), + Scratch.translate("Remove message named:"), "", - (value) => addMeshage(value), - Scratch.translate("Make a Mesh"), + delMeshage, + Scratch.translate("Mesh Manager"), "broadcast_msg" ); } - remove() { + newVar() { // taken from SharkPool/Camera // in a Button Context, ScratchBlocks always exists ScratchBlocks.prompt( - Scratch.translate("Remove mesh named:"), + Scratch.translate( + "New variable name (this variable will be available to all sprites):" + ), "", - (value) => delMeshage(value), - Scratch.translate("Remove a Mesh"), + addMeshVar, + Scratch.translate("Mesh Manager"), + "broadcast_msg" + ); + } + removeVar() { + // taken from SharkPool/Camera + // in a Button Context, ScratchBlocks always exists + ScratchBlocks.prompt( + Scratch.translate("Remove variable named:"), + "", + delMeshVar, + Scratch.translate("Mesh Manager"), "broadcast_msg" ); } @@ -86,64 +156,185 @@ return { id: "eightxtwoMesh", name: Scratch.translate("Mesh"), - // todo: pick better colours - color1: "#00acff", - color2: "#0088cc", - color3: "#0078b4ff", + docsURI: "https://extensions.turbowarp.org/8to16/mesh", + color1: "#4cdab2", + color2: "#44cda5", + color3: "#3dc099", blocks: [ { - func: "new", + func: "newMsg", + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate("Make a Message"), + }, + { + func: "removeMsg", blockType: Scratch.BlockType.BUTTON, - text: Scratch.translate("Make a Mesh"), + text: Scratch.translate("Remove a Message"), + hideFromPalette: getMeshages().length === 0, + }, + { + opcode: "when", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when I receive [BROADCAST]"), + isEdgeActivated: false, + hideFromPalette: getMeshages().length === 0, + arguments: { + BROADCAST: { + type: Scratch.ArgumentType.STRING, + menu: "MESHES_NOACCEPT", + }, + }, + }, + { + opcode: "broadcast", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("broadcast [BROADCAST]"), + hideFromPalette: getMeshages().length === 0, + arguments: { + BROADCAST: { + type: Scratch.ArgumentType.STRING, + menu: "MESHES_ACCEPT", + }, + }, + }, + { + opcode: "broadcastWait", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("broadcast [BROADCAST] and wait"), + hideFromPalette: getMeshages().length === 0, + arguments: { + BROADCAST: { + type: Scratch.ArgumentType.STRING, + menu: "MESHES_ACCEPT", + }, + }, + }, + ...(getMeshages().length === 0 ? [] : ["---"]), + { + func: "newVar", + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate("Make a Variable"), + }, + { + func: "removeVar", + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate("Remove a Variable"), + hideFromPalette: Object.keys(getMeshVars()).length === 0, + }, + { + opcode: "getVar", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("get [VAR]"), + arguments: { + VAR: { + type: Scratch.ArgumentType.STRING, + menu: "VARS", + }, + }, + hideFromPalette: Object.keys(getMeshVars()).length === 0, + }, + { + opcode: "setVar", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("set [VAR] to [VALUE]"), + arguments: { + VAR: { + type: Scratch.ArgumentType.STRING, + menu: "VARS", + }, + VALUE: { + type: Scratch.ArgumentType.STRING, + defaultValue: "", + }, + }, + hideFromPalette: Object.keys(getMeshVars()).length === 0, + }, + { + opcode: "changeVar", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("change [VAR] by [VALUE]"), + arguments: { + VAR: { + type: Scratch.ArgumentType.STRING, + menu: "VARS", + }, + VALUE: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: "1", + }, + }, + hideFromPalette: Object.keys(getMeshVars()).length === 0, }, - ...(getMeshages().length !== 0 - ? [ - { - func: "remove", - blockType: Scratch.BlockType.BUTTON, - text: Scratch.translate("Remove a Mesh"), - }, - { - opcode: "when", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when I receive mesh [BROADCAST]"), - isEdgeActivated: false, - arguments: { - BROADCAST: { - type: Scratch.ArgumentType.STRING, - menu: "MESHES_NOACCEPT", - }, - }, - }, - { - opcode: "broadcast", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("broadcast mesh [BROADCAST]"), - arguments: { - BROADCAST: { - type: Scratch.ArgumentType.STRING, - menu: "MESHES_ACCEPT", - }, - }, - }, - ] - : []), ], menus: { MESHES_ACCEPT: { acceptReporters: true, - items: "getMeshages", + items: "getMeshagesForMenu", }, MESHES_NOACCEPT: { acceptReporters: false, - items: "getMeshages", + items: "getMeshagesForMenu", + }, + VARS: { + acceptReporters: true, + items: "getMeshVarsForMenu", }, }, }; } broadcast({ BROADCAST }) { - bc.postMessage({ name: BROADCAST, session: session }); + vm.runtime.startHats("eightxtwoMesh_when", { + BROADCAST: data.name, + }); + bc.postMessage({ + type: "broadcast", + name: BROADCAST, + willWait: false, + }); + } + async broadcastWait({ BROADCAST }) { + vm.runtime.startHats("eightxtwoMesh_when", { + BROADCAST: data.name, + }); + bc.postMessage({ + type: "broadcast", + name: BROADCAST, + willWait: true, + }); + let isDone = false; + while (!isDone) { + if (lastSuccessfulMeshage !== BROADCAST) { + await new Promise((resolve) => resolve()); + } + } + } + + getVar({ VAR }) { + return vm.runtime.extensionStorage.mesh.variables[VAR]; + } + setVar({ VAR, VALUE }) { + vm.runtime.extensionStorage.mesh.variables[VAR] = VALUE; + bc.postMessage({ + type: "var", + key: VAR, + value: VALUE, + }); + } + changeVar({ VAR, VALUE }) { + vm.runtime.extensionStorage.mesh.variables[VAR] = + vm.runtime.extensionStorage.mesh.variables[VAR] = + Scratch.Cast.toNumber( + vm.runtime.extensionStorage.mesh.variables[VAR] + ) + Scratch.Cast.toNumber(VALUE); + bc.postMessage({ + type: "var", + key: VAR, + value: + +Scratch.Cast.toNumber( + vm.runtime.extensionStorage.mesh.variables[VAR] + ) + Scratch.Cast.toNumber(VALUE), + }); } } From b414770a2edf48aa85f0d0f0f627fbc531abdeef Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 14:46:44 +0000 Subject: [PATCH 11/21] 8to16/mesh: stabilised mostly --- extensions/8to16/mesh.js | 70 ++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index c1264bf1b6..33118d001c 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -12,8 +12,10 @@ } const vm = Scratch.vm; + // Init the broadcastchannel const bc = new BroadcastChannel("extensions.turbowarp.org/8to16/mesh"); - let lastSuccessfulMeshage = ""; + let connectedUsers = 0; + let finishedWaits = {}; if (!vm.runtime.extensionStorage.mesh) vm.runtime.extensionStorage.mesh = { messages: [], variables: {} }; @@ -24,7 +26,7 @@ vm.runtime.extensionManager.refreshBlocks(); }); - // Spaghetti ahead! + // Message utilities const getMeshages = () => vm.runtime.extensionStorage?.mesh.messages ?? []; const addMeshage = (name) => { if (getMeshages().includes(name)) return; @@ -40,6 +42,7 @@ vm.extensionManager.refreshBlocks(); }; + // Var utilities const getMeshVars = () => vm.runtime.extensionStorage?.mesh.variables ?? {}; const addMeshVar = (name) => { if (Object.keys(getMeshVars()).includes(name)) return; @@ -58,29 +61,28 @@ vm.extensionManager.refreshBlocks(); }; + // Handle messages on the broadcastchannel bc.onmessage = async ({ data }) => { + console.log(data); switch (data.type) { case "broadcast": { let hats = vm.runtime.startHats("eightxtwoMesh_when", { BROADCAST: data.name, }); + // broadcast and wait handling if (data.willWait) { await new Promise((resolve) => { const poll = () => { if ( - hats.filter( - (thread) => vm.runtime.threads.indexOf(thread) === -1 - ).length === 0 + hats.filter((thread) => vm.runtime.threads.includes(thread)) + .length === 0 ) resolve(); - else setTimeout(poll, 25); + else setTimeout(poll, 5); }; poll(); }); - bc.postMessage({ - type: "waitdone", - name: data.name, - }); + bc.postMessage({ type: "done", name: data.name }); } break; } @@ -88,14 +90,24 @@ vm.runtime.extensionStorage.mesh.variables[data.key] = data.value; break; } - case "waitdone": { - lastSuccessfulMeshage = data.name; + case "done": { + console.log(finishedWaits); + ++finishedWaits[data.name]; + console.log(finishedWaits); + break; + } + case "ping": { + connectedUsers = 0; + bc.postMessage({ type: "pong" }); + break; + } + case "pong": { + connectedUsers += 1; break; } } }; - // dummy message to activate the broadcastchannel - bc.postMessage({ type: "hello" }); + setTimeout(() => bc.postMessage({ type: "ping" }), 50); class Mesh { getMeshagesForMenu() { @@ -284,9 +296,7 @@ } broadcast({ BROADCAST }) { - vm.runtime.startHats("eightxtwoMesh_when", { - BROADCAST: data.name, - }); + vm.runtime.startHats("eightxtwoMesh_when", { BROADCAST }); bc.postMessage({ type: "broadcast", name: BROADCAST, @@ -294,20 +304,20 @@ }); } async broadcastWait({ BROADCAST }) { - vm.runtime.startHats("eightxtwoMesh_when", { - BROADCAST: data.name, - }); + finishedWaits[BROADCAST] = 0; + vm.runtime.startHats("eightxtwoMesh_when", { BROADCAST }); // TODO: add wait here bc.postMessage({ type: "broadcast", name: BROADCAST, willWait: true, }); - let isDone = false; - while (!isDone) { - if (lastSuccessfulMeshage !== BROADCAST) { - await new Promise((resolve) => resolve()); - } - } + await new Promise((resolve) => { + const poll = () => { + if (finishedWaits[BROADCAST] >= connectedUsers) resolve(); + else setTimeout(poll, 25); + }; + poll(); + }); } getVar({ VAR }) { @@ -322,11 +332,9 @@ }); } changeVar({ VAR, VALUE }) { - vm.runtime.extensionStorage.mesh.variables[VAR] = - vm.runtime.extensionStorage.mesh.variables[VAR] = - Scratch.Cast.toNumber( - vm.runtime.extensionStorage.mesh.variables[VAR] - ) + Scratch.Cast.toNumber(VALUE); + vm.runtime.extensionStorage.mesh.variables[VAR] += + Scratch.Cast.toNumber(vm.runtime.extensionStorage.mesh.variables[VAR]) + + Scratch.Cast.toNumber(VALUE); bc.postMessage({ type: "var", key: VAR, From 38fe1e18b45064273cd51645ba90737fc5b399bc Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 14:50:29 +0000 Subject: [PATCH 12/21] 8to16/mesh: ready for review? --- extensions/8to16/mesh.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index 33118d001c..bc5d8202ef 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -63,7 +63,6 @@ // Handle messages on the broadcastchannel bc.onmessage = async ({ data }) => { - console.log(data); switch (data.type) { case "broadcast": { let hats = vm.runtime.startHats("eightxtwoMesh_when", { @@ -91,9 +90,7 @@ break; } case "done": { - console.log(finishedWaits); ++finishedWaits[data.name]; - console.log(finishedWaits); break; } case "ping": { @@ -318,6 +315,7 @@ }; poll(); }); + delete finishedWaits[BROADCAST]; } getVar({ VAR }) { From fcf0e388f16dedb1f96da94a7654860c5954f1d1 Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 14:51:49 +0000 Subject: [PATCH 13/21] 8to16/mesh: bugfix with change var --- extensions/8to16/mesh.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index bc5d8202ef..3ed5e74a5c 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -336,10 +336,7 @@ bc.postMessage({ type: "var", key: VAR, - value: - +Scratch.Cast.toNumber( - vm.runtime.extensionStorage.mesh.variables[VAR] - ) + Scratch.Cast.toNumber(VALUE), + value: vm.runtime.extensionStorage.mesh.variables[VAR], }); } } From 51fc2dc617891aa3bf977988f4ee73e4d2e0c599 Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 14:52:36 +0000 Subject: [PATCH 14/21] 8to16/mesh: standards --- extensions/8to16/mesh.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index 3ed5e74a5c..7c53e4549b 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -17,37 +17,37 @@ let connectedUsers = 0; let finishedWaits = {}; - if (!vm.runtime.extensionStorage.mesh) - vm.runtime.extensionStorage.mesh = { messages: [], variables: {} }; + if (!vm.runtime.extensionStorage["mesh"]) + vm.runtime.extensionStorage["mesh"] = { messages: [], variables: {} }; vm.runtime.on("RUNTIME_DISPOSED", () => { - vm.runtime.extensionStorage.mesh = { messages: [], variables: {} }; + vm.runtime.extensionStorage["mesh"] = { messages: [], variables: {} }; }); vm.runtime.on("PROJECT_LOADED", () => { vm.runtime.extensionManager.refreshBlocks(); }); // Message utilities - const getMeshages = () => vm.runtime.extensionStorage?.mesh.messages ?? []; + const getMeshages = () => vm.runtime.extensionStorage["mesh"].messages ?? []; const addMeshage = (name) => { if (getMeshages().includes(name)) return; if (name === "") return; - vm.runtime.extensionStorage.mesh.messages = [...getMeshages(), name]; + vm.runtime.extensionStorage["mesh"].messages = [...getMeshages(), name]; vm.extensionManager.refreshBlocks(); }; const delMeshage = (name) => { if (!getMeshages().includes(name)) return; - vm.runtime.extensionStorage.mesh.messages = getMeshages().filter( + vm.runtime.extensionStorage["mesh"].messages = getMeshages().filter( (n) => n !== name ); vm.extensionManager.refreshBlocks(); }; // Var utilities - const getMeshVars = () => vm.runtime.extensionStorage?.mesh.variables ?? {}; + const getMeshVars = () => vm.runtime.extensionStorage["mesh"].variables ?? {}; const addMeshVar = (name) => { if (Object.keys(getMeshVars()).includes(name)) return; if (name === "") return; - vm.runtime.extensionStorage.mesh.variables = { + vm.runtime.extensionStorage["mesh"].variables = { ...getMeshVars(), [name]: "", }; @@ -55,7 +55,7 @@ }; const delMeshVar = (name) => { if (Object.keys(getMeshVars()).includes(name)) return; - vm.runtime.extensionStorage.mesh.variables = Object.fromEntries( + vm.runtime.extensionStorage["mesh"].variables = Object.fromEntries( Object.entries(getMeshVars()).filter((v) => v[0] !== name) ); vm.extensionManager.refreshBlocks(); @@ -86,7 +86,7 @@ break; } case "var": { - vm.runtime.extensionStorage.mesh.variables[data.key] = data.value; + vm.runtime.extensionStorage["mesh"].variables[data.key] = data.value; break; } case "done": { @@ -319,10 +319,10 @@ } getVar({ VAR }) { - return vm.runtime.extensionStorage.mesh.variables[VAR]; + return vm.runtime.extensionStorage["mesh"].variables[VAR]; } setVar({ VAR, VALUE }) { - vm.runtime.extensionStorage.mesh.variables[VAR] = VALUE; + vm.runtime.extensionStorage["mesh"].variables[VAR] = VALUE; bc.postMessage({ type: "var", key: VAR, @@ -330,13 +330,14 @@ }); } changeVar({ VAR, VALUE }) { - vm.runtime.extensionStorage.mesh.variables[VAR] += - Scratch.Cast.toNumber(vm.runtime.extensionStorage.mesh.variables[VAR]) + - Scratch.Cast.toNumber(VALUE); + vm.runtime.extensionStorage["mesh"].variables[VAR] += + Scratch.Cast.toNumber( + vm.runtime.extensionStorage["mesh"].variables[VAR] + ) + Scratch.Cast.toNumber(VALUE); bc.postMessage({ type: "var", key: VAR, - value: vm.runtime.extensionStorage.mesh.variables[VAR], + value: vm.runtime.extensionStorage["mesh"].variables[VAR], }); } } From 57be6d496c1a37e78bfe229768b9dd6c99ed7b5b Mon Sep 17 00:00:00 2001 From: 8to16 Date: Sat, 10 Jan 2026 14:53:08 +0000 Subject: [PATCH 15/21] 8to16/mesh: make extensionstorage less ambiguous --- extensions/8to16/mesh.js | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index 7c53e4549b..ac752a0c59 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -17,37 +17,42 @@ let connectedUsers = 0; let finishedWaits = {}; - if (!vm.runtime.extensionStorage["mesh"]) - vm.runtime.extensionStorage["mesh"] = { messages: [], variables: {} }; + if (!vm.runtime.extensionStorage["8to16mesh"]) + vm.runtime.extensionStorage["8to16mesh"] = { messages: [], variables: {} }; vm.runtime.on("RUNTIME_DISPOSED", () => { - vm.runtime.extensionStorage["mesh"] = { messages: [], variables: {} }; + vm.runtime.extensionStorage["8to16mesh"] = { messages: [], variables: {} }; }); vm.runtime.on("PROJECT_LOADED", () => { vm.runtime.extensionManager.refreshBlocks(); }); // Message utilities - const getMeshages = () => vm.runtime.extensionStorage["mesh"].messages ?? []; + const getMeshages = () => + vm.runtime.extensionStorage["8to16mesh"].messages ?? []; const addMeshage = (name) => { if (getMeshages().includes(name)) return; if (name === "") return; - vm.runtime.extensionStorage["mesh"].messages = [...getMeshages(), name]; + vm.runtime.extensionStorage["8to16mesh"].messages = [ + ...getMeshages(), + name, + ]; vm.extensionManager.refreshBlocks(); }; const delMeshage = (name) => { if (!getMeshages().includes(name)) return; - vm.runtime.extensionStorage["mesh"].messages = getMeshages().filter( + vm.runtime.extensionStorage["8to16mesh"].messages = getMeshages().filter( (n) => n !== name ); vm.extensionManager.refreshBlocks(); }; // Var utilities - const getMeshVars = () => vm.runtime.extensionStorage["mesh"].variables ?? {}; + const getMeshVars = () => + vm.runtime.extensionStorage["8to16mesh"].variables ?? {}; const addMeshVar = (name) => { if (Object.keys(getMeshVars()).includes(name)) return; if (name === "") return; - vm.runtime.extensionStorage["mesh"].variables = { + vm.runtime.extensionStorage["8to16mesh"].variables = { ...getMeshVars(), [name]: "", }; @@ -55,7 +60,7 @@ }; const delMeshVar = (name) => { if (Object.keys(getMeshVars()).includes(name)) return; - vm.runtime.extensionStorage["mesh"].variables = Object.fromEntries( + vm.runtime.extensionStorage["8to16mesh"].variables = Object.fromEntries( Object.entries(getMeshVars()).filter((v) => v[0] !== name) ); vm.extensionManager.refreshBlocks(); @@ -86,7 +91,8 @@ break; } case "var": { - vm.runtime.extensionStorage["mesh"].variables[data.key] = data.value; + vm.runtime.extensionStorage["8to16mesh"].variables[data.key] = + data.value; break; } case "done": { @@ -319,10 +325,10 @@ } getVar({ VAR }) { - return vm.runtime.extensionStorage["mesh"].variables[VAR]; + return vm.runtime.extensionStorage["8to16mesh"].variables[VAR]; } setVar({ VAR, VALUE }) { - vm.runtime.extensionStorage["mesh"].variables[VAR] = VALUE; + vm.runtime.extensionStorage["8to16mesh"].variables[VAR] = VALUE; bc.postMessage({ type: "var", key: VAR, @@ -330,14 +336,14 @@ }); } changeVar({ VAR, VALUE }) { - vm.runtime.extensionStorage["mesh"].variables[VAR] += + vm.runtime.extensionStorage["8to16mesh"].variables[VAR] += Scratch.Cast.toNumber( - vm.runtime.extensionStorage["mesh"].variables[VAR] + vm.runtime.extensionStorage["8to16mesh"].variables[VAR] ) + Scratch.Cast.toNumber(VALUE); bc.postMessage({ type: "var", key: VAR, - value: vm.runtime.extensionStorage["mesh"].variables[VAR], + value: vm.runtime.extensionStorage["8to16mesh"].variables[VAR], }); } } From b1b16c9a08355fe719ebc895cfa6a886f194cc05 Mon Sep 17 00:00:00 2001 From: 8to16 <197376797+8to16@users.noreply.github.com> Date: Sat, 10 Jan 2026 15:38:01 +0000 Subject: [PATCH 16/21] 8to16/mesh: typo docs --- docs/8to16/mesh.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md index 4b9b13fe30..09d45e26f1 100644 --- a/docs/8to16/mesh.md +++ b/docs/8to16/mesh.md @@ -18,12 +18,12 @@ The blocks below are equivalent to other blocks in Scratch, but are received by ```scratch when I receive [message v] :: #4cdab2 hat ``` -This block will activate when another project sends *this* project a message with the same name. It is triggered using the block above in any other project. +This block will activate when another project sends *this* project a message with the same name. It is triggered using the block below in any other project. ```scratch broadcast (message v) :: #4cdab2 ``` -This block sends a message to all other projects that have the extension running. It triggers the hat block below in projects that listen for the message. +This block sends a message to all other projects that have the extension running. It triggers the hat block above in projects that listen for the message. ```scratch broadcast (message v) and wait :: #4cdab2 @@ -48,4 +48,4 @@ This block sets the value of a mesh variable in all currently connected projects change (variable v) by (1) :: #4cdab2 ``` -This block increments the value of a mesh variable in all currently connected projects by the specified number. \ No newline at end of file +This block increments the value of a mesh variable in all currently connected projects by the specified number. From 49cf14519b0ff8a19f604d2cb2e1ef7a6490bede Mon Sep 17 00:00:00 2001 From: 8to16 <197376797+8to16@users.noreply.github.com> Date: Sat, 10 Jan 2026 15:43:50 +0000 Subject: [PATCH 17/21] fix typo in image attribution --- images/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/README.md b/images/README.md index c3344c7b7a..15054fff9c 100644 --- a/images/README.md +++ b/images/README.md @@ -326,4 +326,4 @@ All images in this folder are licensed under the [GNU General Public License ver - Created by [@Dillon](https://github.com/DillonRGaming) ## 8to16/mesh.svg - - Creatde by [@kx1bx1](https://github.com/kx1bx1) in https://github.com/TurboWarp/extensions/pull/2382#issuecomment-3731596025 \ No newline at end of file + - Created by [@kx1bx1](https://github.com/kx1bx1) in https://github.com/TurboWarp/extensions/pull/2382#issuecomment-3731596025 From f14c47d3d996392cfe776496c2cdf46e323f5855 Mon Sep 17 00:00:00 2001 From: 8to16 <197376797+8to16@users.noreply.github.com> Date: Sat, 10 Jan 2026 19:17:22 +0000 Subject: [PATCH 18/21] 8to16/mesh: fix bug --- extensions/8to16/mesh.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index ac752a0c59..41e0152a35 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -337,9 +337,7 @@ } changeVar({ VAR, VALUE }) { vm.runtime.extensionStorage["8to16mesh"].variables[VAR] += - Scratch.Cast.toNumber( - vm.runtime.extensionStorage["8to16mesh"].variables[VAR] - ) + Scratch.Cast.toNumber(VALUE); + Scratch.Cast.toNumber(VALUE); bc.postMessage({ type: "var", key: VAR, From 90ad0f195f885600d277a696d7670b0560b4ddee Mon Sep 17 00:00:00 2001 From: AmpElectrecuted <266199358+ampelc@users.noreply.github.com> Date: Sun, 31 May 2026 10:26:55 +0100 Subject: [PATCH 19/21] Rename Mesh extension to Cross-Project --- extensions/8to16/mesh.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index 41e0152a35..060feac3bc 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -1,14 +1,14 @@ -// Name: Mesh +// Name: Cross-Project // ID: eightxtwoMesh // Description: Send and receive messages between other projects. -// By: 8to16 +// By: AmpElectrecuted // License: MPL-2.0 (function (Scratch) { "use strict"; if (!Scratch.extensions.unsandboxed) { - throw new Error("Mesh extension must run unsandboxed"); + throw new Error("Cross-Project Broadcast extension must run unsandboxed"); } const vm = Scratch.vm; @@ -110,7 +110,7 @@ } } }; - setTimeout(() => bc.postMessage({ type: "ping" }), 50); + setTimeout(() => bc.postMessage({ type: "ping" }), 1000); class Mesh { getMeshagesForMenu() { @@ -125,10 +125,10 @@ // taken from SharkPool/Camera // in a Button Context, ScratchBlocks always exists ScratchBlocks.prompt( - Scratch.translate("New message name:"), + Scratch.translate("New cross-project message name:"), "", addMeshage, - Scratch.translate("Mesh Manager"), + Scratch.translate("New Message"), "broadcast_msg" ); } @@ -136,10 +136,10 @@ // taken from SharkPool/Camera // in a Button Context, ScratchBlocks always exists ScratchBlocks.prompt( - Scratch.translate("Remove message named:"), + Scratch.translate("Remove cross-project message named:"), "", delMeshage, - Scratch.translate("Mesh Manager"), + Scratch.translate("Remove Message"), "broadcast_msg" ); } @@ -148,11 +148,11 @@ // in a Button Context, ScratchBlocks always exists ScratchBlocks.prompt( Scratch.translate( - "New variable name (this variable will be available to all sprites):" + "New cross-project variable name (this variable will be available to all sprites):" ), "", addMeshVar, - Scratch.translate("Mesh Manager"), + Scratch.translate("New Variable"), "broadcast_msg" ); } @@ -160,17 +160,17 @@ // taken from SharkPool/Camera // in a Button Context, ScratchBlocks always exists ScratchBlocks.prompt( - Scratch.translate("Remove variable named:"), + Scratch.translate("Remove cross-project variable named:"), "", delMeshVar, - Scratch.translate("Mesh Manager"), + Scratch.translate("Remove Variable"), "broadcast_msg" ); } getInfo() { return { id: "eightxtwoMesh", - name: Scratch.translate("Mesh"), + name: Scratch.translate("Cross-Project"), docsURI: "https://extensions.turbowarp.org/8to16/mesh", color1: "#4cdab2", color2: "#44cda5", @@ -190,7 +190,7 @@ { opcode: "when", blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when I receive [BROADCAST]"), + text: Scratch.translate("when I receive cross-project [BROADCAST]"), isEdgeActivated: false, hideFromPalette: getMeshages().length === 0, arguments: { @@ -203,7 +203,7 @@ { opcode: "broadcast", blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("broadcast [BROADCAST]"), + text: Scratch.translate("broadcast cross-project [BROADCAST]"), hideFromPalette: getMeshages().length === 0, arguments: { BROADCAST: { @@ -215,7 +215,7 @@ { opcode: "broadcastWait", blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("broadcast [BROADCAST] and wait"), + text: Scratch.translate("broadcast cross-project [BROADCAST] and wait"), hideFromPalette: getMeshages().length === 0, arguments: { BROADCAST: { @@ -239,7 +239,7 @@ { opcode: "getVar", blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("get [VAR]"), + text: Scratch.translate("get cross-project [VAR]"), arguments: { VAR: { type: Scratch.ArgumentType.STRING, @@ -251,7 +251,7 @@ { opcode: "setVar", blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("set [VAR] to [VALUE]"), + text: Scratch.translate("set cross-project [VAR] to [VALUE]"), arguments: { VAR: { type: Scratch.ArgumentType.STRING, @@ -267,7 +267,7 @@ { opcode: "changeVar", blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("change [VAR] by [VALUE]"), + text: Scratch.translate("change cross-project [VAR] by [VALUE]"), arguments: { VAR: { type: Scratch.ArgumentType.STRING, From f72135af77d83b9638b20d56967b16dca0f0cfbd Mon Sep 17 00:00:00 2001 From: AmpElectrecuted <266199358+ampelc@users.noreply.github.com> Date: Sun, 31 May 2026 10:28:06 +0100 Subject: [PATCH 20/21] 8to16/mesh: cross-project in docs --- docs/8to16/mesh.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/8to16/mesh.md b/docs/8to16/mesh.md index 09d45e26f1..4ee1164a10 100644 --- a/docs/8to16/mesh.md +++ b/docs/8to16/mesh.md @@ -1,6 +1,6 @@ -# Mesh +# Cross-Project -Mesh lets you communicate with other projects running in the same browser or in TurboWarp Desktop, using special versions of variables and broadcasts. This allows you to for example, create a game that talks to another project running at the same time. +The Cross-Project extension lets you communicate with other projects running in the same browser or in TurboWarp Desktop, using special versions of variables and broadcasts. This allows you to for example, create a game that talks to another project running at the same time. > [!NOTE] > Only projects running in the same browser as another will receive messages. However, projects running in a different window will work. @@ -16,36 +16,36 @@ The blocks below are equivalent to other blocks in Scratch, but are received by ### Broadcasts ```scratch -when I receive [message v] :: #4cdab2 hat +when I receive cross-project [message v] :: #4cdab2 hat ``` This block will activate when another project sends *this* project a message with the same name. It is triggered using the block below in any other project. ```scratch -broadcast (message v) :: #4cdab2 +broadcast cross-project (message v) :: #4cdab2 ``` This block sends a message to all other projects that have the extension running. It triggers the hat block above in projects that listen for the message. ```scratch -broadcast (message v) and wait :: #4cdab2 +broadcast cross-project (message v) and wait :: #4cdab2 ``` This is similar to the block above, but waits for the broadcast to end before continuing to run the script. ### Variables ```scratch -(get (variable v) :: #4cdab2) +(get cross-project (variable v) :: #4cdab2) ``` -This block gets the value of a mesh variable. +This block gets the value of a cross-project variable. ```scratch -set (variable v) to [value] :: #4cdab2 +set cross-project (variable v) to [value] :: #4cdab2 ``` -This block sets the value of a mesh variable in all currently connected projects. +This block sets the value of a cross-project variable in all currently connected projects. ```scratch -change (variable v) by (1) :: #4cdab2 +change cross-project (variable v) by (1) :: #4cdab2 ``` -This block increments the value of a mesh variable in all currently connected projects by the specified number. +This block increments the value of a cross-project variable in all currently connected projects by the specified number. From 55c05e90cde6586079e3bce55f1301e8aa253c62 Mon Sep 17 00:00:00 2001 From: "DangoCat[bot]" Date: Thu, 25 Jun 2026 17:17:43 +0000 Subject: [PATCH 21/21] [Automated] Format code --- extensions/8to16/mesh.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/8to16/mesh.js b/extensions/8to16/mesh.js index 060feac3bc..1c42f6bd7c 100644 --- a/extensions/8to16/mesh.js +++ b/extensions/8to16/mesh.js @@ -215,7 +215,9 @@ { opcode: "broadcastWait", blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("broadcast cross-project [BROADCAST] and wait"), + text: Scratch.translate( + "broadcast cross-project [BROADCAST] and wait" + ), hideFromPalette: getMeshages().length === 0, arguments: { BROADCAST: {