Cloudy McCloudFace: Building a friendly Scratch API

05 June 2016

Cloudy McCloudFace is a project I made that provides an API for making realtime cloud multiplayer games in Scratch. The reasoning behind it was that Scratch makes it really, really difficult to do cloud multiplayer with its quirks, and I think Scratchers have the potential to make cool multiplayer projects if only they were able to do so without having to deal with quirks.

It’s designed a lot like APIs in other languages, with API calls and callbacks. Here’s the “user” code that runs the demo I made.

Scratchblocks for CMCF demo

This post will basically go over how it works and the design of what I consider to be a friendly Scratch API.

The API

The API itself is somewhat self explanatory. The two major premises behind it are using callbacks and (ab)using Scratch variables as a hashmap. For example, in Javascript you might see an API like this

connectToServer(function success(){}, function error(){})

Similarly, CMCF uses callbacks for connection success and error.

The hashmap part is the way the API handles cloud data. Basically, there are psuedo-variables that each connected multiplayer user has a copy of. You set your own copy of your psuedo-vars, and recieve them from other players. For example, for a 2d game you might have the psuedo-vars “x position,” “y position,” and “direction.” These are the variables that are cloud-list-encoded into each user’s cloud variable. To make the API easy to use, I wanted to allow naming these variables instead of simply indexing into the encoding list. So instead of

set my psuedo-var (1) to [something]::custom

CMCF has

cmcf.SetVar [x position] [something]::custom

where “x position” is the name of your pseudo-variable.

Obviously, Scratch is limiting, but the best APIs are the ones that give you the most freedom. And with hacked blocks, it’s possible to make things pretty friendly.

The Hacks

In terms of function, CMCF is very similar to what I did in Jetski Escape, but the code is a bit more cleaned up.

Cloud List

For cloud list encoding and decoding, I essentially built a new cloud list engine from Scratch (pun intended - heh). It’s very concise (if you don’t count the sprite that contains all the ascii code variables)

cmcf.decode block

cmcf.encode block

cmcf.encoder is a sprite that contains 2 types of variables for all ascii characters in the range 0-127

  • Variable is named for its character; value is 2 digit hexadecimal ascii code
  • Variable is named for its 2 digit hexadecimal ascii code; value is character

This allows for efficient encoding and decoding using Scratch’s variables hashmap.

I don’t think the decoder needs much explanation. 0xFF is what I use to signify a new list item, but other than that it simply goes through the variable (skipping the “0x”) and decodes by pairs of hexadecimal digits.

The encoder is a bit more complicated. It uses the list hack to allow arbitrary length lists to be encoded without the 10k character limit on the join block. Individual characters are added to the list one at a time, and the list reporter then joins them. The only catch is that inserted characters must explicitly be casted to a string. A single number as a list item will screw it all up and space separate the characters in the list reporter. The script uses the join block to cast numbers to strings.

Both blocks are hacked with menus (obviously), so feel free to use just the cloud list engine of this project if you want to. </shameless-plug>

Keeping track of users and establishing presence

CMCF needed to be robust, and that means keeping track of when people leave the room, and detecting which cloud variable you can hop on to when connecting. To keep track, I assumed that “cloud variable updated within the last 2 seconds” means that someone is on that variable. CMCF has a list called “updatedt” which counts the seconds since the last update for each cloud var. To do this, it caches the last known value of the variable and then compares.

Now here’s our second super-annoying Scratch quirk: long numbers can be “equal” when they aren’t really (because of loss of precision).

Example of long numbers being "equal"

And even more annoyingly, Scratch will aggresively cast your strings back to numbers if you aren’t careful.

Example of aggressive casting

The solution is to append letters to the beginning of the number.

Example of how to solve

Now, to make sure that your cloud variable is updated even if the psuedo-vars aren’t being updated is to add a random number to the cloud list and resend it as often as possible.

add (username) to [my list v]
add (pick random (0.01) to (1.01)) to [my list v]
add all the psuedo-vars::grey
encode::custom

Update time is kept track of using the (days since 2000) block, so the timer is free for other usage.

Sending and receiving data

Sending data is pretty straightforward. The API has blocks for setting your own variables, or you can directly set the (cmcf.myslot) item of the list for the psuedo-variable you want to modify. Each psuedo-variable is stored locally as a list, where indicies in the list correspond to cloud variable IDs for each user. So (☁ cloud1) is in item 1, (☁ cloud2) in item 2, etc.

When receiving data, CMCF updates the items in these lists, so they can easily be accessed (or you can use the get variable API block).

Connecting and the fallback

Here’s the last Scratch quirk: the HTTP fallback mode.

Now this mode is really annoying because when even one person is on the fallback mode, not only do they lose the ability to spam cloud updates as fast as possible, but everyone else who’s connected also loses that ability. So, we need to make sure that nobody is on the fallback mode. The connection procedure looks like this:

  • Start caching cloud variables and checking for changes
  • Pick the first open cloud var (one that hasn’t changed)
  • Wait for other players to join
  • Start spamming your cloud var for a few seconds
  • If you stop receving updates, you’re on the fallback
  • Otherwise, you’re connected!

Fallback testing is pretty simple

Fallback tester script

And here’s the script for connecting

Connecting script

I tried to make things as readable as possible. Obviously with the hacks and all it isn’t always possible, but I tried my best.

Conclusion

Basically, here are the stupid things I had to work around here

  • The 10k join limit
  • Numbers being “equal”
  • The HTTP fallback

And here are the non-stupid things I had to work around

  • Efficiently encoding and decoding cloud lists
  • Checking for people online

I encourage you to try and make a Scratch API too! There are so many things in Scratch where quirks provide a ridiculous bar for users to be able to use them in projects. Having things in fluent APIs is nice for people on Scratch who are just learning about advanced projects, and it’s a great learning experience for the maker as well :)

Comments


Don't Type Like This

09 April 2016

Seriously. Don’t.

  • It’s Against The Rules Of English Grammar
    • You Only Capitalize First Words Of Sentences And Proper Nouns
  • It Makes You Look Bad
    • People Will Think You Don’t Know English
  • It Doesn’t Look “Nice” Or “Good”; It Just Makes Everyone Super Annoyed To Have To Read Your Post
    • I Can’t Even Read My Own Blog Post Now.

Just don’t do it, please.

Comments


history.pushState for Jekyll in a few lines of code

05 April 2016

You may have noticed the fast page loads here. This site uses history.pushState combined with AJAX, which I hacked on to Jekyll. In case you don’t want to inspect element, here’s the code:

window.reparseLinks = function(){
    if(window.hpsEnabled){
        var as = document.getElementsByTagName("a");
        for(var i = 0; i < as.length; i++){
            if(as[i].hostname == "aputurk.tk"){
                as[i].onclick = window.onhpslink; // this makes all inter-page links use AJAX loading
                                                  // which overrides navigation
            }
        }
    }
}

window.postload = function(){
    document.getElementById("loader").style.opacity = 0;
    reparseLinks();
        
    // handle the search box that comes up on the 404 page
    // (that uses https://github.com/christian-fei/Simple-Jekyll-Search)
    // I use a MutationObserver there to handle any links that come up there
}

window.hpsEnabled = !!history.pushState; // can we use history.pushState?
                                         // if not, leave links as they are and use normal page loading

window.loadPage = function(pathname){
    var to = setTimeout(function(){
        document.getElementById("loader").style.opacity = 1;
    }, 200);
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
        clearTimeout(to);
        var title = this.responseXML.title;
        var res = this.responseXML.getElementById("page-wrap").innerHTML;
        document.title = title;
        history.replaceState({}, title, pathname);
        
        // handle analytics
        
        // here's where we actually insert the loaded content
        document.getElementById("page-wrap").innerHTML = res;
        
        window.postload();
        
        // handle disqus widget
    }
    xhr.open("GET", pathname);
    xhr.responseType = "document";
    xhr.send();
}

window.onhpslink = function(e){
    if(this.hostname == "aputurk.tk"){
        e.preventDefault();
        history.pushState({}, "", this.pathname + this.hash);
        window.loadPage(this.pathname + this.hash);
    }
}

if(window.hpsEnabled){
    window.onpopstate = function(){
        window.loadPage(location.pathname);
    }
}
window.postload();

Because we can’t change what Jekyll serves, it’s impossible to prevent loading the entire HTML for a page over AJAX. But using history.pushState() allows you not to have to reload all the random scripts and styles and background images that come with a normal page load. For me, the pages usually load so fast, they don’t trigger the loading animation that displays after 200ms of loading.


On a more off-topic note: here’s the GIF I made for Zro’s April 1st “maymays” thread on the A(O)Ts, if anyone wants it:
Thisandagain-themed deal with it GIF

Comments


The spread of misinformation

28 March 2016

Recently, I came across this video via friends at OpenSprites:

I stopped watching at around 1:30.

It suffices to say (fortunately) that everyone on OpenSprites immediately identified it as utter nonsense. (See here and section 3.1 here, if you’re interested).

The same cannot be said for some commenters on the video and most upvoters. And that’s absolutely infuriating for me. I always see nonsense being spread online, and for some reason people just believe it without questioning.

Image of comment on that video

Your middle school librarian’s worst nightmare

Remember how you’re supposed to validate online sources? Stay unbiased? These are things anyone who’s been through middle school should have learned. You don’t just believe anything you see on the Internet, as people emphasize by humorously misquoting historical figures. I’m sure everyone who upvoted or commented on that video knew that the Internet is unreliable. So then, why did they believe this YouTube video, of all things, without a single doubt?

It’s a combination of psuedoscience and bias. First, psuedoscience: the same sort of nonsensical statements people spread about “X chemical being in your food and also in industrial whatnot.” I’m sure you’ve seen it. The issue is that it’s so believable to people at first glance. It seems like results from some sort of legitimate study or experiment, when really it’s just some guy on the Internet compiling dodgy evidence and acting like it’s significant.
There’s another part to it: bias. If people start slightly leaning towards one side of something, they’re inclined to accept evidence from that side even if it’s a stretch, and reject all evidence from the other side, no matter how conclusive it might be. This is, again, something we see over and over on the Internet. In the case of that video, it might be because non-Americans are envious, or Americans are dissatisfied with the government that they jump so quickly on the video as “conclusive evidence” that Apollo was faked. But they completely ignore the piles of evidence in favor of Apollo (the retroreflective mirror that’s now placed on the moon, the samples brought back, the landing sites you can see with powerful telescopes…).

Trolling the “sheeple”

When people call Internet-browsers “sheeple,” it might be insulting but it’s not exactly wrong. As we see, people’s opinions can easily be played to start arguments (eg: the current state of the comments section on that video). The issue is, content creators know this. We can easily find evidence that the uploader of this video was a troll. Just read the description, to start:

NASA will finally admit we have never gone past the Van Allen Radiation Belt. “ISS” which is fake, will show astroNot explaining how we have never gone above 238 miles.

This video completely debunks NASA by their own submission. The trolls that are thumbing this down are just that.

“astroNot,” “trolls thumbing this down”… It’s very likely that this person knew that he was spewing nonsense. If he wanted to convice most people that Apollo was fake, he would have tried a bit harder to make his evidence sound more legit—for example, by not using the word “astroNots.” This video seems to have been made solely to play the people who the creator knew would believe it against the people who know it’s nonsense. And, it succeeded. The comments are basically a bunch of people flaming against each other, at this point.

That’s the problem. People being easy to sway, combined with trolls who know how to take advantage of it is a dangerous combination, especially in this age where information travels so quickly. It’s the reason we see the annoying propoganda spreading about vaccines causing autism, or about Obama raising the debt more than all other presidents combined (a quick google search can show you lots of evidence against both). It’s an issue that I don’t think we’ve really seen in the past, and something that needs to be solved somehow before society can move forward.

I guess more people need to raise their nonsense-meters and be a bit more skeptical about what they hear on the Internet. Don’t be afraid to call nonsense out like Will Smith in “I, Robot”(Warning: edgy).

Comments


Sample Text

26 March 2016

Yay, a first post!

It’s a start ¯\_(ツ)_/¯

Comments