We’re going to try something new! Interest has been expressed in devblogs, even when there are no pretty pictures or lovely Kyna narration. So here’s trying! I’ll explain what I’m doing as I complete a project I’ve been putting off for a while: converting relics to a cache instead of real items.

Disclaimer
Tiur is a chemical engineer. While he has code training, he is not the kind of programmer you should base your education on. Anything you think you may have learned from this, double check it’s not madness.

Overview

This Devblog is about breaking down the code so the wider community can understand the intricate details that go into even the most simple IDEA from the idea list.

Goals

  1. Provide a look behind the curtain to what programming Rapture looks like
  2. Provide amusement, Rapture has quirks!
  3. Help the community to understand why even simple things can be an undertaking
  4. Gauge interest in this content when it’s not teasing something amazing
  5. Reduce one of the largest uses of player inventory

Why Make the Thing

Why? Databases. Most everything in Aetolia is either a replica, room, or persona/player. Each of those entries comes with all the flags, variables, bytes, datas, etc that could be possible on that object… so they can get pretty large! The space is reserved, even if the numbers are all 0. While you might be annoyed at how many things named “piece#####” you hold in your inventory, the upstairs are annoyed because every single one of those little things is holding 170+ flags, 40+ bytes, etc. It’s not that big of a deal in a modern computer but it does cause bloat. And when we need to iterate through every replica in the game looking for certain things? Huge waste of time to iterate over multiple tens of thousand piece######’s. This used to be a huge problem with herbs in pocketbelts!

Also, I’ve been a player and having a backpack with 200+ relic pieces annoyed me too.

So our goal is to delete all the pieces, freeing up a ton of replica numbers, and reducing a large number of unnecessary memory usage. How? We’ll make another database! (Tiur’s answer to every problem is to make another database, you’ll see that if these continue.) This one will store the relevant information about a relic piece, and a quantity. Much easier to store a list of “Bob has 47 pieces of relic XYZ” than 47 instances of extra nonsense. This could be just as bloated if we save too much information… but our relic system was actually made by a Good Coder (yay Razmael!) and has some clever tricks.

How

Every single relic piece is the same thing, just with a different name and a number pointing at what piece it represents. Each piece is a database that includes a few things; what it should look like and which relic it’s meant for. Then the relics store what they build into, and how many of each piece# they require!

The summary of all that nonsense? The relic cache database only needs to say this:

dbalias rpiececache
{
    Persona        => variable[1],
    Rpiece         => variable[2],
    Quantity       => variable[3],
}

Thanks to Raz thinking ahead that’s super easy! Who owns the thing, what piece it represents, and how many! The variable bit there is me telling Rapture that the number could be anything inside a signed 16bit integer. It’s reserving 3*16bit, which is totally no big deal… or more, I’m kinda terrible at real architecture and there’s more to it than that… but w/e. Also, to those of you who think that’s not fancy, when Rapture was made it was impressive to be able to label things like that!

After that it’s just like any other programming language. We need a validator, an accessor, and a mutator. At least one of each.

WTH is a validator?
Most languages don’t need to you validate every single database entry constantly with a validator, but Rapture has a serious problem with trying to poke a database member that doesn’t exist. It gets MAD! Untrapped errors happen! Razmael hits you! In Rapture, those basically look like this:

 function valid_rpiececache(db)
{
if db > rpiececache.maxrecord then return 0;
if !db then return 0;
if !rpiececache[db].Persona then return 0;
return 1;
}


See, now every time we need to reference a database entry, we can validate the input with our validator like that. That’s the source of “If you see this message, please contact the Admin!” error messages.

And an Accessor?
In Rapture, it is very easy to access the database, just:


database[record].field;


And it will return that information. But that can be terribly not useful. For instance… what good is knowing the rpiece number if everyone in the game will use the rpiece name? So we can write an accessor like this:


function rpiececache_get_type$(db)
{
if !valid_rpiececache(db) then return "";
return rpiece_short$(rpiececache[db].Rpiece);
}


Easy! Rpiece_short$ is a command that translates a specific piece into its short name, like “a shiny coin” and we used our validator from above to prevent anything from throwing an error. Also, the $ lets you know that the output should be a string. Not all languages make you be specific like that.

Mutator?

A mutator is the same thing as an accessor, except backwards. We COULD


database[record].field = value;


But that has no validation or protection. What if you’re writing to an incorrect database record? We also wouldn’t want to pass it an impossible value, like say, a quantity of -1. So a function or subroutine (functions return values, subroutines just do their jam and shut up) needs to exist to keep us careful. Commonly in Aetolia we follow one of two methods: either we write a mutator for each field in the database, or we write one giant mutator that parses input. In this case the changes will be made by the game itself, so it is better to have the first kind. If, for instance, it were a minipet, where I write them in the game and need the game to build the object for me, the second is better. Except… today we get to cheat. The persona will be set when the database record is made, the quantity will only ever move up and down via other code that can check itself, so we just need the rpiece… but that already exists because we use those all the time.

So, this is getting long and we’ll do a part 2 if there’s interest. But first, the most important and the only thing I could teach effectively: the thought process. I make a list of everything we need today’s code to do, then a sublist of how that’s done.

Summary

We need:

  • database
    • mutators
    • accessors
    • validators
    • creator
    • destroyer
  • A way to put thing into our cache
    • verb
    • add # to cache
    • delete old replcia
  • A way to look at the cache
    • verb
    • a subroutine to output the cache
  • A way to get things out of the cache
    • verb
    • reduce # from cache
    • create replica(s)
    • check if we should delete the cache entry, no one needs 0s
  • A way to force every piece in the game into the cache
    • protected verb
    • look over inventory and use above add method
    • send a message to the player so they won’t freak out

From there a real programmer would make pseudocode… but for me, that might as well be pseudocode. I do this often enough that I already know how many of each thing I’d make for a command and database. The above is important because it is TERRIBLE to get to the end of a project and realize you skipped an entire thing the project needs. You could have to code it twice! Or three times! Not that I would ever make those sorts of mistakes all the time.

Final

You know what we didn’t list up there? Loyalty! That’s an important part of the pieces we’ll need to preserve. Tune in for Part II to see how I add that without having to redo everything I’ve made already. Also, I’ll explain verbs.

1 Comment

  1. […] Aetolia DevBlog […]