So, today I made a Pico-8 paint program... in Pico-8.
You might be wondering why I would do such a thing, since the Pico-8 development platform comes with a built-in paint program that's almost exactly the same, except it can use the mouse instead of clunky keyboard controls. The simple answer is... This is the beginning of what might be the biggest thing I'm likely to do on the Pico-8. Ever.
Imagine a Pico-8 game shared by a Dropbox folder or similar system, where every user gets their own character and their own room, and every object in the game world was uniquely painted up by users, held, given, and placed in those rooms to furnish them with things, and all of it was maintained by a slow, asynchronous system of a multitude of carts, set up in such a way that multiple copies of the game being run by tens of players should never cause data collisions.
Welcome to Pico Hotel, an oldschool collaborative world-building and interaction experience.
Now after some serious thought about it, you're bound to be thinking "But JTE, if we're going all MUD here, doesn't that mean you need to make an easy chat system of some kind?" Well, that's true. And I have a solution.
Press Esc like you normally would to end the program. Type
say "Hello, World!"
into the console and press enter. That's it! The program will resume running on it's own, update the world state, and soon enough your chat text will be bubbling up the activity chain.
That's right, since this is an asynchronously networked game, I can run a game loop from any console command and not miss a beat. This opens the possibilities to even more advanced features in the future, like setting the game to a specific development state and telling the user to go ahead and type a large string (or maybe even a Lua script!) into the script editor before sending a specific console command to commit it to memory and make advanced interactive objects or minigames.
... In fact, now that I've come to this point I feel kind of stupid for making the paint program my first step, considering I now realize that I could have just as easily (if not even more easily) made the game load up an empty cart and ask you to doodle whatever you like in it and use a command to process and commit the graphic and resume. Ha ha, to live and learn, I suppose!
I hit a minor hitch, in that I have no idea how save data decides when to save and when not to save when you're running a cartridge. I spent all day trying to figure out what the heck is going on, and this is my nearest conclusion:
When you run() the program, it sets a special internal flag that says it's running from the start. Nothing else sets this flag, not even resume. When the program is terminated while this magic flag is set, several things happen: The flag itself is unset, the music is stopped, and any save data is dumped to the harddrive.
Unfortunately, this means that I can only test for, read, and write to save data in the initial run of the program, as it seemingly disappears when the program is terminated and then run again. (More worrysome still, is that calling run() from within the program at any point seemingly skips the step of saving the data and resets it to 0!) This means that, for instance, I have to ask for you to enter your user name using an old-school RPG alphabet keyboard where you press arrow keys to select letters and the Z key to add them to the string.
That works rather well, right? Well, it's not ideal, when you actually have a full command prompt with regular keyboard input available for everything else... So I suppose this will just have to be the real first step to breaking the boundaries. Your user name will determine where your data is stored in the shared Dropbox folder. I might add an internal randomized "user ID" key or something else to use up more of that tiny, private data space. Everything else, is public records stored in multiple, organized Pico-8 carts buried somewhere in the shared Dropbox folder.
Here's some design goals:
After loading and running the main cart, the user should be prevented (as much as reasonably possible) from accidentally fucking up the mounds of external data carts that the program will be loading, reading from, and writing to all willy-nilly all over the Dropbox folder. This will be accomplished primarily by always "saving" an empty cart to a shared "scratchpad.p8.png" file after use, which will redirect the Ctrl+S quick-save shortcut. The "save", "load", and "cd" commands will also be dummied out and taken out of the user's control so they can't accidentally confuse the system into thinking it's running in a different folder or whatever mid-way through execution. (Don't worry, if you want to use your Pico-8 for something else, just "reboot"!)
Everything should be accomplished in a streamlined interface that leaves you with specific instructions whenever it dumps you back to the editing modes of the console. For example, if the user wants to edit their own character sprites, the program should ask exactly which sprite they want to edit (facing direction, frame of animation), showing them their options, before returning them to the console with the instruction to type "save" when they're done editing, with an empty cart loaded that contains only the specific sprite they requested to edit. When the user types "save", the program will assume control again, review the sprite, make sure the user is happy with those changes and wishes to commmit them, then fiddle with the data carts to make it happen. The user doesn't need to see the magic behind the curtain, no manually creating new carts or anything like that, there should be as many interfaces for dealing with the data built into it as possible so even users with no intimate Pico-8 experience can do it.
- All data should be separated into users, objects, and rooms. Users can only manipulate themselves, objects they own, and rooms they own. Users can exist in one room at a time, even if they don't own it. Objects can be created by one user, duplicated by that user, and passed to another user as a limited copy. Rooms consist of two data carts, one which holds the state of the room itself and objects in the room, and the other which keeps track of the activities of users in the room, eg. their fine location in the room and recent chat lines. A central data cart of some kind should keep track of user activity timestamps, and maybe which room each user exists in? I'm not entirely clear on the specifics that will make it work the best yet.
Today I implemented standard version 4 UUIDs as best I could, with an additional filesystem check to make sure the UUID that comes up is, in fact, unique. This might be the only version of UUIDs possible in Pico-8, considering how limited access to outside information the Pico-8 has -- There isn't even a way to get the system clock, unless you count figuring out how to brute-force the rng into giving up what seed it happened to be started with when the program ran.
Everything will have a UUID. Users, rooms, objects. They will all refer to eachother by UUID. Every UUID will be its own cartridge file in the Dropbox disk space, containing all the data it can hold and, potentially, a UUID pointing to an additional cartridge to extend the data space, if needed. Infinitely long cartridge chains means infinite data, the only limit is how much of it we can display at once and how detailed the programming can be which handles it.
I've also determined a potential list of object types, although I'm still fleshing out the object data format. It will have optimized RLE compression (which I have done by myself just fine, thank you), all images are automatically cropped and can be as small as 1 pixel or as large as the entire 128x128 canvas of sprite data, and there can be multiple frames in an object, used for things like different facing directions and limited animation, and different transparent colors on a per-frame basis.
As far as security goes, I don't think we can even have any because it's just a Dropbox and everyone has write permission? So I'm not gonna worry about "user accounts" and "passwords" or anything. You'll just get your assigned UUID to hold your data and that's it, no questions asked.
You may want to look into cstore() for an alternate persistent data location. It updates the actual cartridge data (so saving the cart saves the data, etc), so theoretically you could set a byte before each input prompt, then quit, then jump via lookup table when the program starts again and return to that code line. 0x5f80 was going to be 64 bytes of persistent cart data, but with the new 64x 32-bit save data I have a feeling this area will be empty by legacy.
This is a Dropbox folder project. Everything else is using cstore to save data. The reason I'm using cartdata to store your user name/id specifically is so that on the local client side (which is intentionally not part of the Dropbox folder) the Pico-8 will remember which of the many users is "you".
Everything else will be generic shared data everyone can access and view.
This is insanely ambitious. Bravo - we do not accomplish great things unless we actually try to.
Security. Hmm - I can think of a couple of possible solutions, mainly to ensure impersonation (me spoofing an id of JTE) is super difficult. I will ponder this, and look forward to your alpha.
-- Biggles, defender of the meek
[Please log in to post a comment]