When I first started working on my 2009 remake project, 2009scape, I had absolutely no idea what I was doing. All I knew was that as a kid I loved playing RuneScape and wanted a way to relive the true RuneScape of my youth. Jagex has come out with Old School RuneScape, but it doesn’t really hit the spot for me. They’ve made countless changes, both to aesthetics and mechanics, and it just feels like a new RuneScape game with old RuneScape graphics.
So, I embarked on my journey and joined up with a fellow named Red Bracket to start contributing to 2009scape. There were so many odd and confusing things about the codebase at the time, and clearly it was due to a lack of understanding (in hindsight) by the parties who previously touched the codebase.
One of the things that always confused me in the beginning is that in the code there were nebulous magic numbers being “set” to other nebulous magic numbers with nonsensical values, and they were referred to as “configs.” You would quite often see something like this:
Now clearly this information is of no help or use to pretty much anyone. You do not understand what this line does and neither did I. So, I endeavored to understand it. I remember I sat down one afternoon after I got off work and just started… playing with the numbers, and seeing what changed.
Sometimes nothing would change.
Sometimes, though, really cool things would happen, like an arrow on my interface would suddenly appear and start blinking.
This only served to drive my curiosity further. I would eventually notice that multiple different numbers would often do the exact same thing. And that’s when it hit me: there must be something going on with the binary side of things!
So I cracked open my trusty calculator and punched in the first number that I knew did a specific thing, took note of the binary, and then punched in another number that did the same thing. One thing I noticed is that while the binary definitely was different, several bits were identical.
I then proceeded to chop out all the differing bits until I was left only with what was the same. (Note that the only bit “flipped” below is the one shared between the two above)
I then sent the resulting decimal equivalent, and sure enough, it did the same exact thing as the other numbers. I knew I was on to something at this point. (Bear in mind, I had never communicated with any other members of the RS reverse engineering community, so I had no idea this was already known about.)
This only drove me to desire an even better understanding of what I had discovered. I started flipping specific bits intentionally and seeing if it made any difference in behavior. Sometimes, it would. From this I came to the conclusion that whatever I was playing with, it contained multiple bits of data that controlled things within the client.
Eventually I decided that I had a solid enough understanding and decided to create a new system for setting these “configs”, one that would allow you to more explicitly access and set values at certain bit offsets so the magic numbers would be slightly less magic. The system was by no means perfect, but it was an improvement:
As time went on I did in fact meet people who had some more functional knowledge of RuneScape, and more specifically of the cache. They helped me to refine and improve the cache decoders we had in the server, and eventually I stumbled across something.
Some NPCs and Objects seemed to reference the magic numbers I had been playing with in their cache definitions. So, I added debug printouts when you clicked on these NPCs that would display their associated magic numbers, and discovered that setting those magic numbers would change the appearance of those NPCs/Objects. “Cool,” I thought, “another interesting use.”
About 2 months later my friend discovered another chunk of data in the cache that seemed to also reference these magic numbers. In fact, it seemed to reference not only the magic numbers, but certain bit offsets within those magic numbers.
At first these would be dubbed ConfigDefinitions, but later would be renamed to VarbitDefinitions.
From there the rest is pretty much history, the more I worked with the magic numbers, the more I understood them, until eventually I ran across someone who had already worked them out and had referred to them with the names I mention below.
Oh, and for completeness’s sake, here’s something closer to how the most recent uses of varbits in the codebase look:
setVarbit(player, Vars.DESCRIPTIVE_NAME_9999, 15)
In the very small but very dedicated RuneScape reverse engineering community, we’ve arrived at a few simple terms that describe the various kinds of bitfields utilized by RuneScape’s RuneTek engine. I will endeavor to list and describe them below:
- Varbit: A varbit is a single addressable unit of information. You could more aptly think of it as a bitpacked variable. RuneScape uses these to communicate client-local state from the server in many different ways. I will try to cover them later.
- Varp: Varps are the “containers” that hold varbits. Varbits are bitpacked variables, and they are packed into varps, which are 32-bit integers. You will almost always have multiple varbits per varp, though sometimes a varp exists independently with no properly addressable varbits packed inside of it. I am unsure as to why this quirk exists. It is commonly agreed upon that varp stands for player variable. These (and, consequentially, the varbits they contain) generally refer to player state.
- Varc: These are much like varps, except they are never intended to persist, do not contain individually addressable varbits, and pertain entirely to client state rather than player state. Thus, they have a name derived from client variable.
There are many more tyes of bitfields than these in RuneScape, but these are by far the most common, and so far at least, the only ones with wieldy handles.
Below are some more terms that will likely get their own post in the future, and are summarized here for your understanding and convenience:
- Cache: This is the blob of data that the client receives when connecting to the RuneScape servers. It is the only way we have been able to preserve any information about older versions of RuneScape, other than the client itself. We have managed to reverse engineer the format and can read data from it quite successfully, though the format has been known to change a lot. If you would like to look at some caches check out the OpenRS2 Archives which is an excellent preservation effort being undertaken by the entire community. My Rust library IDX can read data from most 400s-600s revision caches.
- CS2: This refers to the pre-compiled scripts that are interpreted by the client and stored in the cache. The client uses these for a wide array of things, but all you need to know for now is that they are scripts that the client can execute to do things within the context of the client.
Each type of bitfield can have quite a number of uses. I will try to dive into some of this below:
These are the most widely used type of bitfield, and are used in a wide range of ways. Some of the most common ways are overriding the NPC variant presented to a player, overriding the object variant presented to a player, triggering certain interface events, storing data that gets displayed on interfaces, tracking progress in quests and many other things. Below, I’ll go into the specifics of how the client accomplishes some of these tasks. There are of course more uses than I will cover in depth here, but these are the most common:
I’ve lumped these into the same subheading because they both function the same way. So, in RuneScape, NPCs and Objects (referred to internally as entities and locations by Jagex) have definitions that are written into the cache. These definitions define things like idle animation, models linked to the NPC, combat level, etc. Both NPCs and Objects have what we refer to as wrapper definitions. These are definitions that are not themselves an NPC/Object (though you can and should spawn them in), but point to one or more other NPCs/Objects, referred to hereon as children. This is where the role of varbits/varps comes in within this context. These wrapper definitions will have an associated varbit, and that varbit represents the index in that wrapper’s array of children that should be shown to the player.
I mentioned before that I had added debug printouts, well these days I have created better tools for seeing such things, like rendering the information directly over in-world entities’ heads:
Below I’ve also included some visual examples that demonstrate NPC wrappers. On the left, you can see Varps being updated, and then you can see the corresponding change in the NPC wrapper when it occurs.
You may have noticed that the text above the wrapper that describes some info about it displays
Vb: 4312. This means that varbit 4312 is associated with this NPC wrapper and dictates what NPC is being shown by the wrapper.
If we take 4312 and see what the varp and related bits within that varp it points to are, we get this:
4312: VarbitDefinition [id=4312, varp=1181, startBit=9, endBit=9]
Which means this varbit points to varp 1181, specifically the 9th bit. If you flip the 9th bit on your favorite calculator, you’ll see the decimal equivalent is 512, which corresponds to the values we saw in the above wrapper demonstration.
RuneScape’s interface system is a marvel of mid-2000s lightweight java game engineering. They are modular and dynamic, all while being pre-compiled and packed into the cache as just mere data. These interfaces have full support for CS2, meaning Jagex can and does use CS2 scripts to have interfaces change, react, etc. The most common use for varbits/varps within the contex of interfaces is when a CS2 script has hooked itself to a certain varbit/varp and listens for updates to it. When the value of the hooked varbit/varp changes, the script triggers and will do many things depending on what it’s written to do. From changing the color of text on the interface (as it would with quests in the quest list or songs in the track list), to completely changing the appearance and functionality of the interface. Being able to decompile and read CS2 scripts, therefor, is almost essential to being able to authentically implement many of the game’s interfaces on the server side of things.
These are a little more nebulous and have almost no server-side uses in earlier revisions. They largely control state of the client, but sometimes overstep into controlling a little bit of temporary player state as well. Generally, a person implementing authentic content won’t have to worry about these. Unfortunately I cannot provide too much info on their use, because not much info is known. The most interesting use I have personally seen is within the 578 revision, where they are used to specify what container within the client’s list of containers pertains to the currently opened shop.