Client Development Intro
This section will cover client development topics like obfuscation, mapping, client patching, and more! If you are interested in creating and maintaining your own fork of RuneLite then these docs should be quite helpful for you!
Obfuscation
Old School RuneScape is updated every single week. Every time Jagex compiles a new version of the game, the Java bytecode is run through an obfuscator (traditionally something like Zelix KlassMaster). Obfuscation is a defensive technique used to make the compiled code nearly impossible for humans to read or reverse-engineer, deterring bot makers and malicious actors.
In the vanilla OSRS client, you won't find a Player class with a getHealth() method. Instead, you'll see something like this:
- Classes are renamed to meaningless letters (e.g.,
class a, class bk, class cj). - Methods and fields are scrambled (e.g., method
a(int x),field q). - Multiplier Obfuscation: Integers are often multiplied by large random numbers to hide their true values. A player's health might be stored internally as health *
837492739and only divided back out right before it's used by the client. - Control flow is flattened or littered with dummy parameters (opaque predicates) to confuse decompilers.
To do anything useful with the client, we first have to understand what we are looking at.
Making Sense of It All: Client Mapping
Because the game updates weekly, the names of these obfuscated classes change weekly. What was class a last week might be class f this week. Client Mapping is the process of figuring out the true identity of these obfuscated components and keeping a "dictionary" of them. We map the meaningless obfuscated names to human-readable names.
How is this done?
Since we can't rely on the names, we rely on patterns and signatures:
- Strings: If a specific class contains the string "Please enter your username", we can confidently map that class to something like the LoginScreen.
- Signatures: If a class has exactly three integer arrays and two string fields, and implements a specific interface like
PacketBufferfrom RuneLite, it can be uniquely identified. - Graph/Hierarchy matching: By analyzing how classes relate to each other (e.g., Class A extends Class B and calls Class C), automated tools can "diff" the new update against the previous week's mapped client and automatically update the dictionary.
This mapped dictionary tells us exactly where the data we need (like player coordinates, inventory items, or NPC IDs) is hiding in the current week's build.
The RuneLite Architecture: API & The Injected Client
RuneLite doesn't distribute a clean, modified version of the game because it would undo much of Jagex's hardwork to obfuscate the game code. Instead, RuneLite dynamically patches the game using a clever architecture separated into three main parts:
The Vanilla Gamepack
When Jagex publishes a game update, RuneLite goes through an internal set of processes (which are closed source):
- It downloads the highly obfuscated, untouched gamepack.jar directly from Jagex's servers
- Runs through its own mapping process to find all the fields and methods it needs to modify to be compatible with its own API interfaces
- Uses
Mixinsto inject new methods and fields into the game classes - Produces the
injected-client.jarfile containing modified and re-obfuscated game classes
In this process RuneLite doesn't just modify the vanilla game classes to implement their interfaces, they apply more advanced obfuscation techniques and add additional methods designed to detect third party clients and plugins. You can read more about the mixin process here.
The RuneLite API (runelite-api)
This is a set of clean, open-source Java interfaces and events designed by the RuneLite developers. It contains things like interface Player extends Actor, interface Client, and cleanly defined events like GameStateChanged. This API is completely separate from Jagex's code and is directly implemented by the injected client RuneLite produces through the Mixins process.
The Injected Client & Mixins
To bridge the gap between the RuneLite API and the vanilla gamepack, RuneLite uses Mixins. A Mixin is a way to inject new code into an existing class. RuneLite takes the obfuscated classes (identified by the mapping process) and forces them to implement the clean API interfaces.
For example, if mapping tells us that class bk is the Player class, a Mixin is applied to class bk so that: It now implements net.runelite.api.Player. A new method called getName() is injected into it, which internally just returns the obfuscated field bk.q. If the field is protected by an integer multiplier, the injected getter will automatically handle the math to return the true, clean value.
You can read more about the mixin process here.
Client Patching (Bytecode Manipulation)
To actually apply these Mixins and modify the client without having the original source code, we use Bytecode Manipulation tools (like ASM, or in other ecosystems, ByteBuddy). Instead of modifying Java .java text files, bytecode manipulators edit the compiled .class files directly in memory (or during a build step).
The patching process generally looks like this:
- Load the Vanilla Bytecode: The tool reads the raw bytes of the Jagex client.
- Apply Interfaces: It alters the class definitions so the obfuscated classes implement the RuneLite API interfaces.
- Inject Getters/Setters: It writes new methods into the bytecode that grab the obfuscated fields, reverses any multiplier math, and returns clean data.
- Inject Event Hooks: It finds key methods in the vanilla client (like the method that processes a chat message) and inserts a few lines of code at the very beginning of that method. This new code says: "Hey RuneLite EventBus, a chat message just arrived! Fire the ChatMessage event to all plugins!"
Run the Injected Client: The modified bytecode is loaded into the Java Virtual Machine (JVM) and the game starts.
Summary
When you write a RuneLite plugin, you are coding against a clean, well documented API. Behind the scenes, however, a massive orchestration of automated mapping identifies the weekly obfuscated Jagex code, while bytecode manipulators patch the raw game files. These patches inject mixins that force Jagex's scrambled classes to implement RuneLite's clean interfaces and broadcast events, turning a hostile, obfuscated environment into a developer-friendly sandbox.
For more information and additional reading, check out: