Password save extension (work in progress)

After seeing Encryption for progress, I got interested in what a password save system extension might look like.

Here’s a work in progress start to one possible solution:

If you’re at all interested in this, you probably want to open the blocks editor; here’s a direct link: https://arcade.makecode.com/#pub:github:jacobcarpenter/pxt-password-save.

The extension works like this:

First you define a save format:

Here we are creating one that can store a number from 0 to 255 plus one boolean value. This format definition should only be done once, and changing it (between versions of the game or anything) will make old codes stop working.

Next you might want to generate a password:

The top box shows setting some (random) values in our save format’s data. The bottom box will splash the password representing the current data.

And finally you may also want to load from a password:

You’ll notice that prompt returns a boolean, because the user may fail to type a correct password. [Inside of that ShowSaveData block, the values are read out of the save format’s data to confirm the password loaded correctly.]

Currently the password format is very simple: all the data is packed together and a checksum is added and then it’s encoded using Douglas Crockford’s flavor of Base32 encoding. The password is always shown with capital letters, but you can also use lowercase letters on the way back in.

I’d be interested to hear any feedback you have. Too complicated? Passwords too long? Need to store bigger numbers? Etc.

5 Likes

I did a bit more testing and I have some plans for modifications:

  • in my tests, passwords were ending with 0 too often, which was a result of the order I was storing the bitflags (high order bit to low order); reversing the storage looks like it’ll add more variety to pw strings.
  • passwords aren’t currently tied to a game at all; you could bring a pw from one game to another, and, if they had a similar structure, you could have a valid password that would load the wrong data for the other game. I plan to add a parameter to the create method that takes a gameId which will be hashed and mixed in with the data to tie the passwords more directly to a specific game.
  • there’s still a lot of waste in the format, resulting in longer than necessary pw strings—if you only have one or two flags, I’m still reserving a full 8 bits for it.
    • considering adding a smaller number type (4 or 5 bits) for more efficient storage of small numbers—for a lot of arcade games, this seems like a better size for storing something like a LevelNumber than the current range of 0..255.
    • with a smaller number type, it’d be easier to pack in small batches of flags without reserving a whole separate byte for them.
    • could also add support for a dynamic checksum size to better fit in with small numbers of flags.
    • also, since base32 encodes the data 5-bits at a time, I could track the significant bits count more closely and potentially trim off some of the unused space at encode time…
1 Like

The dereferencing null/undef value error doesn’t happen if I use your direct link to open it though.

Would it be possible to use 2 bytes for the numbers so you could store numbers between 0 - 65535? And maybe also include a version that allows negative numbers between -32767 and -32767? (I think those are the right numbers) It’s hard to do bit shifting in blocks.

And also if I import the extension it seems to automatically run the demo - I think the demo should go in test.ts instead of main.ts?

Also, running the demo results in a dereferencing null/undefined value too after this screen:
image

… if I import the extension it seems to automatically run the demo - I think the demo should go in test.ts instead of main.ts ?

Oh. Good tip! I’ll fix that.

(paraphrased) Would it be possible to add 2 byte signed and unsigned variants?

Yes, it certainly would, though those larger values will make the passwords noticeably longer. Do you have an example of the kind of data you might want to reserve such a large number for?

It’s hard to do bit shifting in blocks.

No argument here. You can get the same results with multiplication / division, though:

To turn a value in the range 0..65,535 into two bytes:

byte_1 = value \ 256
byte_0 = value % 256

(The backslash means integer division, instead of normal/floating point; there is a block for it, hidden behind the drop-down on the square root block.)

To turn two bytes back into a single value in the range 0..65,535:

value = byte_1 * 256 + byte_0

Also, running the demo results in a dereferencing null/undefined value too after this screen

The dereferencing null/undef value error doesn’t happen if I use your direct link to open it though.

Hm. I’ll do some testing to see if I can repro.

1 Like

Yes, forgot about the tech words signed and unsigned. (For goodness sake, it’s in my freaking username)

And I wouldn’t want to limit my score to only 255 points!

Maybe make one function with 2 drop downs deciding between signed/unsigned and byte/int?

:joy:

And I wouldn’t want to limit my score to only 255 points!

Sure… yeah; I was thinking of more of a Metroid / Castlevania / Mega Man type pw system where you’re more tracking collected items / location than a score. But I can see some games wanting to save an in-progress run’s current score behind a password.

I’ll add support for a 2 byte value. (Probably as distinct blocks rather than a drop-down though; drop-down seems like it’s more effort for the coder to get to the right type when they want to use it, rather than just picking the block they know they want.)

1 Like