Steganography, imgur and Twitter
GM Version: 1.4.1757
Target Platform: Windows, Mac, Linux (possibly others, compatibility untested)
Download: Download | Mirror | Version 1.0.0, ~2mb .zip file containing:
- .exe demonstrating the code
- .gmz version of the demo (though without my authentication keys)
- .gmez for easier importing into existing projects.
A system that permits free and easy sharing of user-generated content without the need for any dedicated external servers, whilst encouraging users to share promotional material.
This system uses rate-limited APIs. Globally, the system cannot perform more than 180 searches per 15 minutes. Uploads are globally limited to 1,250 per 24 hours (approximately 13 uploads per 15 minutes).
I'm a big fan of BeepBox. I use it for all my music for game jams because it sounds good and is super quick to work in. There's a second side to BeepBox however, something that's as important as the design of the tool itself. There's a small community of enthusiasts that share their tunes on Twitter and work on each other's music. Because BeepBox tracks can be stored as URLs, all you have to do is give someone a link and they can play back your tune, move some notes around, and send it right back to you. It's an unexpectedly brilliant collaborative tool and quite unlike any music authoring tool I've ever used.
The secret to BeepBox is the smooth distribution mechanism. You can very easily share your creations with anyone, close friends or other hobbyists. When I was working on The Big Top, it struck me that puzzle games (especially tile-based ones) would benefit immensely from having this kind of social media integration. Whilst we can't do deep linking as of yet in GameMaker, we can still utilise Twitter to share content if we're a bit imaginative about how we do it.
BeepBox hides the data necessary to reconstruct a track in a URL using an alphanumeric string. Whilst this is a clever solution on the web, trying to get GM to interact with URLs is a real pain. Instead, we're going to take advantage of an ancient cryptographic technique called "steganography". By hiding information in an image, we're going to share data at the same time as we share pictures.
The steganography implemented here is called "least significant bit steganography" or LSBS for short. LSBS works by changing each pixel's RGBA channels ever so slighty in a way that's unnoticeable to humans but a computer can find. Each pixel is made up of 32 bits, each channel takes up 8 bits (8 x 4 = 32). The least significant bit is forced to be a 0 or a 1 depending on what data we're trying to hide. This means one byte of data can be hidden in two pixels in the image. LSBS goes through as many pixels as is necessary until all the data is hidden.
The amount of data you can store is limited by the number of pixels i.e. the size of the image. This can be worked out easily: bytes of storage = ( pixel width x pixel height ÷ 2 ) - 45. But why "minus 45"? To make sure we can decode the hidden data properly we have to use a header that's 45 bytes long. This header contains the size of the data, a SHA1 checksum, and a null character to terminate.
Once we've encoded our data into an image, we need to upload it somewhere so that we can link it in a tweet. It is possible to directly upload media files to Twitter itself through one of their RESTful APIs but the process is complex and requires permanent user authorisation; I'm always careful about security and this kind of permissions procedure is hard to make safe.
What we're going to do instead is upload our image to imgur instead which fortunately has an open RESTful endpoint that doesn't need lots and lots of security. This is done using a multipart HTTP request handled in a single script. After having uploaded our image file, all we have to do is ask the user to submit a tweet. We open a webpage pointed towards Twitter's web intents API to give the user a page to authorise a tweet. We can specify some content to put in the tweet as well: the link to the image file and a hashtag too. Once the user presses "ok" then the message is sent to Twitter and is available on the site within about 60 seconds.
We've shared our image, and our data, with the world. Getting this data back into a game is quite a bit more challenging. For this we'll be using Twitter's search API. Unfortunately, this is rate-limited and requires application authorisation to use freely. What this means is that we need to use some secret information that identifies our game so that Twitter knows we're not spamming their servers. This is called "requesting a token" and requires us to have a developer account on Twitter and an active application with the correct permissions.
Once we've got a valid token, we send a request to Twitter to grab a handful of tweets, up to a maximum of 100. Twitter gives us a load of data in the form of a JSON. We can ignore most of it. What we're looking for are image files (.png since we know that .jpg compression messes up the steganography) and, more specifically, links to imgur. The system downloads all the files that it thinks are likely to have relevant steganographic data hidden in them. By searching for a particular hashtag and following links to imgur uploads, it's likely that we won't get too many false results. However, there's no guarantee that an image has hidden steganographic data - this is why we use a header and a SHA1 checksum, as described above.
Having downloaded all the image files, it's a simple step to reverse the encoding process. Images that may be corrupted or do not have data in them at all are discarded. The rest are kept and are turned into buffers, ready for GM to turn into game information.
Unlike tweeting, this searching for images requires no explicit authorisation from the user. This means it can silently run in the background of your game, checking Twitter every now and again for new content that has been recently posted. Particularly skilled creators can be favourited and their output displayed with a higher priority; different hashtags can be used for different kinds of content. Do remember that the information that Twitter returns by searching is public information and won't be able to see tweets from people who have their account set to private.