Client Revision Updater Guide
Preface
This guide was written to help users understand how the Old School RuneScape client works for learning, exploration, and development purposes. These documents are outside the scope of the Kraken ecosystem, its plugins, and the API.
This information contained in this guide is not meant to break Jagex or RuneLite's terms of service and is not meant to be used for malicious or harmful purposes. It is for educational and archival purposes only. It functions as a reference for developers who are interested in learning how the Old School RuneScape client works and as a knowledge base for personal learning. We are showcasing how RuneLite and the OSRS client work. Use this information at your own risk.
⚠️ Warning: This guide is not meant to be used for malicious or harmful purposes. It represents MY personal understanding and learnings from the OSRS Client. The information within these documents may not be 100% accurate
Background & Basics
If you've ever been curious about how the Old School RuneScape client works or how to tinker with it, then you've probably explored the inject-client jar file that RuneLite produces with each game update. This jar file contains the entirety of the client's source code along with RuneLite's injected mixins. If you’re unfamiliar with mixins or how they work in regard to RuneLite check out this guide to get a grasp on what RuneLite is doing.
Essentially, we are given a secondhand client because it's already got a ton of fields and methods injected, shuffled around, re-obfuscated, and mutated by RuneLite so that normal plugins can use the RuneLite API effectively. Generally, clients will take one of three approaches when it comes to further modifying the injected-client to suite their needs:
- Injection – Applying additional custom mixins on top of RuneLite's
- Reflection – Calling existing methods in the client directly using Java's reflection API
- Packets – Using reflection (or mixins) to call key methods which create and transmit click data to OSRS servers
Each approach—Injection, Reflection, and Pure Packets—solves the interaction problem at a different layer of the client's architecture.
Injection (Mixin Approach)
Injection via bytecode manipulation (e.g., using ASM) embeds your custom hooks and logic directly into the client's classes either at compile-time or load-time.
Advantages:
- Native Performance: Once loaded, injected code runs as native Java alongside the client. There is no reflection overhead, which is crucial if you are hooking into high-frequency loops like the render thread
- Deep Interception: Mixins allow you to intercept, cancel, or mutate events before the client processes them. You can bypass client-side restrictions or draw custom overlays directly within the rendering pipeline.
Disadvantages:
- Harder to Update: This is the most heavily impacted by revision updates. If the target methods are refactored, split, or their obfuscated signatures change, the mixins will fail to apply. The more you change the more you maintain.
- Maintenance Overhead: Requires a robust, automated updater to scan the bytecode and remap targets every single revision. Custom tooling like this is tricky to build and maintain.
Reflection (doAction Invocation)
Instead of modifying the bytecode, reflection dynamically locates and invokes the client's internal interaction methods (like doAction) at runtime, passing the necessary parameters (opcodes, IDs, widget coordinates) to simulate an action.
Advantages:
- Client-Native Execution: By calling doAction, your interaction flows through the exact same validation, pathfinding, and packet-queuing logic that a legitimate mouse click would. It inherently inherits any new client-side state checks Jagex adds.
- Lower Update Friction: You only need to map the specific interaction method and its parameter order (which often remains relatively static across minor revisions) rather than mapping dozens of scattered mixins or packets.
- Clean Separation of Concerns: Keeps the client bytecode pristine. The logic to extract metadata and state can sit completely separate from the execution logic.
Disadvantages:
- Performance Overhead: Reflection is inherently slower. While negligible for a 600ms game tick, it can become a bottleneck if abused in high-frequency state-polling scenarios like costly simulations based on game state.
- Limited Scope: You can only invoke what the client explicitly allows. You cannot easily cancel internal events or modify rendering pipelines without combining this with injection.
- Parameter Shuffling: Obfuscation often shuffles the order of parameters (e.g., swapping param0 and param1 or adding dummy integer arguments). Your updater still needs to resolve the correct method signature for each revision (ideally dynamically).
Pure Network Packets
This approach bypasses the client's user interface and interaction logic entirely. Your tooling reads the game state (via reflection or injection) but dispatches actions by formatting byte buffers and writing them directly to the client's active network socket.
Advantages:
- Ultimate Control & Speed: Actions are executed instantly upon dispatch. You aren't beholden to the client's UI thread, menu open states, or artificial input delays.
- Decoupled Architecture: Fingerprinting logic and metadata extraction are cleanly isolated from action execution. The automation logic can essentially function as a headless client making it ideal for running many clients simultaneously in a VM.
- Bypasses Client-Side Restrictions: Any anti-cheat or validation checks occurring in the doAction method or menu rendering logic are completely circumvented.
Disadvantages:
- Highest Risk Profile: With great power comes the highest risk of heuristic bans. If you send a malformed packet, an out-of-sequence packet, or send an interaction packet without the accompanying mouse-movement data the server expects, it is highly detectable.
- Exhaustive Mapping Required: You must map every single packet opcode, its size, and its exact byte structure (often obfuscated and mutated every revision). If the OSRS client ships with a new byte to a packet, your tool must accommodate it immediately.
- No Safety Net: Because you bypass the client, you bypass the client's pathfinding and state validation. Sending a packet to interact with an entity you logically cannot reach might flag the account (although this is just a speculation from me).
Each way of further modifying the client comes with its own set of tradeoffs and limitations, but they all boil down to using packets eventually since a network packet will always be required to automate actions in the game. If you're unfamiliar with network packets as they relate to the OSRS client, check out this guide to get a better understanding of what they are and how they work.
So what exactly do we have to do when it comes to updates and why is an "updater" necessary?
Revision Updates
Normal RuneLite updates like 1.12.20 -> 1.12.21 are not updates to the client's revision but rather updates to RuneLite's mixins or API usually in regard to a normal game update.
Revision updates happen quarterly or bi-monthly on average and are larger updates because the base OSRS client (before RuneLite mixins are applied) has its obfuscation changed. You could go through the updated client manually and find each field, method, and structure that you are interested in and write them down, but this is a tedious, time-consuming, and error-prone process that is why we need an updater!
An "updater" is nothing more than a tool that will take RuneLite's injected client scan the bytecode of the client and automatically find the fields, methods, and structures that you are interested in.
What's Next?
If you haven't read the mixins and packets guides yet, I'd suggest you check them out before moving on so that you have a clear understanding of how RuneLite works and how packets are sent in the game.
Once you have a firm grasp you can move on to the mapping guide to learn how to get started mapping the obfuscated client!