Discuss Scratch

cs2975871
Scratcher
100+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

Some odd stuff is going on with the variables of my sprites when imported. Variables are switching around, displaying the name of the sprite then the variable's actual name or doing things like turning global and having a 2 besides the name for no particular reason.

This problem isn't quite new, it's been plaguing a project I've been working on for months now very subtly (primarialy the latter,) and I assume whatever the heck is causing it is taking up so much space it refuses to upload, but I can't be certain.

Can anyone please take a look at this? I don't think it's haunted, but it's certainly acting like it is.

If someone wants to root around in the larger project I have and dig out whatever the heck is mucking around in there, I'll give you the link on request.

Project with example:
https://scratch-mit-edu.ezproxyberklee.flo.org/projects/1105094832/
nembence
Scratcher
100+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

cs2975871 wrote:

Project with example:
https://scratch-mit-edu.ezproxyberklee.flo.org/projects/1105094832/
There are many blocks in that sprite that have an invalid parent block ID, or a parent which doesn't reference them
Here is a list (maybe not complete because of matching strings that don't actually mean a block):
["!!","%,","%S","(Bf8)TWdd,]CT2;[usWU","-bh{Q@BPKxN~x4_O!OT5","54LG5o65fTwNigVgwt4:","6~=8kSSdk4IIl)x1KEV9","@HPr:GC4nLy%PNYQ_(#N","BX","D)","IT","IU","J+","J-","Ji","KQ","M?","O@","Rr%U8u)z9IE1nX^^X+QC","TG","Te","WMsk7XMmvibpy5^,2KW;","W}","Y;","_%TT((1:CS=X@6,),mP+","a*","aBA","aM","a`","a`Y","agj","agk","agl","agm","agn","ago","ai(","ai:","ai@","ai]","al,","alj","bK","bhE","c","cF#","cF%","cF(","cF)","cF*","cF,","cF-","cF.","cF/","cF:","cF;","cF=","cF?","cF@","cF[","cF]","cF^","cF_","cF`","cF{","cF|","cF}","cF~","cGa","cGb","cGc","cGd","cGe","cGf","cGg","cJ","cm)","cmH","cmN","cz","d","d.","d@","dA","dE","d[","dpbPyC3qg0l-Mi!k_6zG","eY","f!","*","fQ","gU","hL","i","iB","i|","j-","jD","k(","mf","oS","qK[q9rtYkE/`i~6~|gb[","qN","q^","qa","ug","x","zf","zy","{+"]
Among these there are scripts that use the ghost variables, for example the script with the topmost valid block “c” should be referenced by the block “gw” which doesn't exist and it uses the variable “(NPC) Serafina Mattea: costumeNumber2”. The script is hidden because it doesn't have a top level block.
I think that when you upload the sprite, Scratch checks every script for missing variables including the hidden scripts and then creates these variables, but when it's already uploaded it only checks visible scripts so it doesn't recreate the variables every time you click on the sprite.

Last edited by nembence (Dec. 12, 2024 15:33:07)

cs2975871
Scratcher
100+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

nembence wrote:

cs2975871 wrote:

Project with example:
https://scratch-mit-edu.ezproxyberklee.flo.org/projects/1105094832/
There are many blocks in that sprite that have an invalid parent block ID, or a parent which doesn't reference them
Here is a list (maybe not complete because of matching strings that don't actually mean a block):
["!!","%,","%S","(Bf8)TWdd,]CT2;[usWU","-bh{Q@BPKxN~x4_O!OT5","54LG5o65fTwNigVgwt4:","6~=8kSSdk4IIl)x1KEV9","@HPr:GC4nLy%PNYQ_(#N","BX","D)","IT","IU","J+","J-","Ji","KQ","M?","O@","Rr%U8u)z9IE1nX^^X+QC","TG","Te","WMsk7XMmvibpy5^,2KW;","W}","Y;","_%TT((1:CS=X@6,),mP+","a*","aBA","aM","a`","a`Y","agj","agk","agl","agm","agn","ago","ai(","ai:","ai@","ai]","al,","alj","bK","bhE","c","cF#","cF%","cF(","cF)","cF*","cF,","cF-","cF.","cF/","cF:","cF;","cF=","cF?","cF@","cF[","cF]","cF^","cF_","cF`","cF{","cF|","cF}","cF~","cGa","cGb","cGc","cGd","cGe","cGf","cGg","cJ","cm)","cmH","cmN","cz","d","d.","d@","dA","dE","d[","dpbPyC3qg0l-Mi!k_6zG","eY","f!","*","fQ","gU","hL","i","iB","i|","j-","jD","k(","mf","oS","qK[q9rtYkE/`i~6~|gb[","qN","q^","qa","ug","x","zf","zy","{+"]
Among these there are scripts that use the ghost variables, for example the script with the topmost valid block “c” should be referenced by the block “gw” which doesn't exist and it uses the variable “(NPC) Serafina Mattea: costumeNumber2”. The script is hidden because it doesn't have a top level block.
I think that when you upload the sprite, Scratch checks every script for missing variables including the hidden scripts and then creates these variables, but when it's already uploaded it only checks visible scripts so it doesn't recreate the variables every time you click on the sprite.
So… the solution is literally just to turn ALL the variables visible?

Last edited by cs2975871 (Dec. 12, 2024 17:51:47)

nembence
Scratcher
100+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

cs2975871 wrote:

So… the solution is literally just to turn ALL the variables visible?
Sadly no, because the problem is with hidden scripts, not hidden variables.
Maybe a project.json compressor that removes unused scripts would work

Last edited by nembence (Dec. 12, 2024 18:04:58)

cs2975871
Scratcher
100+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

nembence wrote:

cs2975871 wrote:

So… the solution is literally just to turn ALL the variables visible?
Sadly no, because the problem is with hidden scripts, not hidden variables.
Maybe a project.json compressor that removes unused scripts would work

That's fantastic. Hidden scripts that are lurking around my project and preventing the poor thing from uploading with their sheer bulk, like an invisible elephant in one's living room. No idea where they came from, no idea how to get rid of them.

Hopefully there's a solution out there- but I think my problem is quite novel….
nembence
Scratcher
100+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

I made this ghost script remover and it looks like it works now
<!DOCTYPE html>
<!-- ghost script remover by @nembence -->
<!-- Put this code in a .html file eg. with Notepad, then open it with your browser -->
<html>
<head>
<title>ghost script remover</title>
</head>
<body>
<h1>ghost script remover</h1>
<div>Paste the project.json here, click the button, then copy the fixed project.json</div>
<div><textarea style="width: 400px; height: 300px;"></textarea></div>
<div><input type="button" value="Fix project.json" onclick="onClick();"/></div>
<script>
// make errors show popups
window.addEventListener("error", function(event) {
    alert(event.error);
});
 
var textarea = document.querySelector("textarea");
function onClick() {
    let project=JSON.parse(textarea.value);
    fixProject(project);
    textarea.value=JSON.stringify(project);
}
 
function fixProject(project) {
    for(let i=0; i<project.targets.length; i++) {
        fixSprite(project.targets[i]);
    }
}
function fixSprite(sprite) {
    let referenced = new Set();
    for(let id in sprite.blocks) {
        let block = sprite.blocks[id];
        if(block.topLevel) addToReferenced(sprite.blocks, id, referenced, new Set());
    }
    let count=0;
    for(let id in sprite.blocks) {
        if(!referenced.has(id)) {
            delete sprite.blocks[id];
            count++;
        }
    }
    if(count>0) console.log("%d blocks removed from %s", count, sprite.name);
}
// adds all blocks in a script to the "referenced" set
function addToReferenced(allBlocks, id, referenced, visited) {
    if(id === null) return;
    if(!(id in allBlocks)) return;
    if(visited.has(id)) return; // prevent being stuck in an infinite loop
    visited.add(id);
    let block=allBlocks[id];
    addToReferenced(allBlocks, block.next, referenced, visited);
    if(typeof block.inputs === "object") {
        for(let name in block.inputs) {
            let input = block.inputs[name];
            // an input can contain 2 blocks: a dragged-in and a shadow block
            for(let i=0; i<input.length; i++) {
                if(typeof input[i] === "string") {
                    addToReferenced(allBlocks, input[i], referenced, visited);
                }
            }
        }
    }
    referenced.add(id);
}
</script>
</body>
</html>
I hope it helps
Make sure to keep the original project in case it corrupts the project or deletes a script that it shouldn't

Last edited by nembence (Dec. 12, 2024 21:41:10)

cs2975871
Scratcher
100+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

nembence wrote:

I made this ghost script remover and it looks like it works now
<!DOCTYPE html>
<!-- ghost script remover by @nembence -->
<!-- Put this code in a .html file eg. with Notepad, then open it with your browser -->
<html>
<head>
<title>ghost script remover</title>
</head>
<body>
<h1>ghost script remover</h1>
<div>Paste the project.json here, click the button, then copy the fixed project.json</div>
<div><textarea style="width: 400px; height: 300px;"></textarea></div>
<div><input type="button" value="Fix project.json" onclick="onClick();"/></div>
<script>
// make errors show popups
window.addEventListener("error", function(event) {
    alert(event.error);
});
 
var textarea = document.querySelector("textarea");
function onClick() {
    let project=JSON.parse(textarea.value);
    fixProject(project);
    textarea.value=JSON.stringify(project);
}
 
function fixProject(project) {
    for(let i=0; i<project.targets.length; i++) {
        fixSprite(project.targets[i]);
    }
}
function fixSprite(sprite) {
    let referenced = new Set();
    for(let id in sprite.blocks) {
        let block = sprite.blocks[id];
        if(block.topLevel) addToReferenced(sprite.blocks, id, referenced, new Set());
    }
    let count=0;
    for(let id in sprite.blocks) {
        if(!referenced.has(id)) {
            delete sprite.blocks[id];
            count++;
        }
    }
    if(count>0) console.log("%d blocks removed from %s", count, sprite.name);
}
// adds all blocks in a script to the "referenced" set
function addToReferenced(allBlocks, id, referenced, visited) {
    if(id === null) return;
    if(!(id in allBlocks)) return;
    if(visited.has(id)) return; // prevent being stuck in an infinite loop
    visited.add(id);
    let block=allBlocks[id];
    addToReferenced(allBlocks, block.next, referenced, visited);
    if(typeof block.inputs === "object") {
        for(let name in block.inputs) {
            let input = block.inputs[name];
            // an input can contain 2 blocks: a dragged-in and a shadow block
            for(let i=0; i<input.length; i++) {
                if(typeof input[i] === "string") {
                    addToReferenced(allBlocks, input[i], referenced, visited);
                }
            }
        }
    }
    referenced.add(id);
}
</script>
</body>
</html>
I hope it helps
Make sure to keep the original project in case it corrupts the project or deletes a script that it shouldn't
ncaught SyntaxError: Unexpected token ‘<’
Got this error on turbowarp. Does it work on Scratch or turbowarp?
Maximouse
Scratcher
1000+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

cs2975871 wrote:

ncaught SyntaxError: Unexpected token ‘<’
Got this error on turbowarp. Does it work on Scratch or turbowarp?
You need to save it as a .html file.
cs2975871
Scratcher
100+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

Y'all, thank you SO much. That removed almost 2 megabytes of broken space, so it can upload now!

I really appreciate those that took the time out of their day to do this. Really warms my heart.
cosmosaura
Scratch Team
1000+ posts

Weird duplicating "ghost" variables that appear without rhyme or reason

Glad you found an answer! Since this is resolved, I'll close it to help highlight the answer and prevent future responses. If you need it re-opened, though, you can report this and ask.

Powered by DjangoBB