OSRS Leagues Private Server — Research Findings
At recent revisions (~221–239) the open-source OSRS server community has standardized on a
Consolidated research backing the project. Sources are linked inline; figures verified against OpenRS2's live archive, the cloned reference repos, and the OSRS Wiki. Last compiled: 2026-06-28.
1. The modern stack (what the ecosystem converged on)
At recent revisions (~221–239) the open-source OSRS server community has standardized on a single stack. Building from scratch is not recommended — the hard, error-prone parts (login handshake, RSA, ISAAC, JS5 cache protocol, player/NPC update masks, per-rev interface wiring) are exactly what these libraries already solve.
| Layer | Choice | Notes |
|---|---|---|
| Base server | rsmod v2 — github.com/rsmod/rsmod | Kotlin 2.2, Java 21, Guice DI, Gradle KTS. Ships at build 233, cache 2293. ISC license. Login + in-world works out of the box. Not accepting external contributions → treat as a hard fork. |
| Networking | RSProt — github.com/blurite/rsprot | Per-revision Maven artifacts net.rsprot:osrs-<rev>-{api,shared,desktop,common}. Handles Netty, PoW login, RSA, ISAAC, XTEA login block, JS5 serving. Published revs: 221–235+ confirmed on Maven Central (incl. 227). |
| Client | RSProx — github.com/blurite/rsprox | Auto-patches the client every launch (RSA modulus, world list, jav_config, host whitelist). Logs every packet — invaluable for reverse-engineering interface behavior. Supports rev 223+. Officially recommended by both rsmod v2 and Alter. |
| Cache source | OpenRS2 archive — archive.openrs2.org | Exact historical caches + XTEA keys by download URL. openrs2-cache + openrs2-buffer libs used directly by rsmod v2. |
| Interface/config data | Joshua-F/osrs-dumps | if3 interface dumps, config dumps, 9.6k cs2 scripts, .sym symbol files. HEAD tracks rev 239. Lets you reference interfaces/components by name. |
| Cross-reference base | AlterRSPS/Alter (rev 228) | Second rsmod-derived base; good for content patterns. |
Cache library alternatives
- OpenRS2 (
org.openrs2:openrs2-cache) — revision-agnostic, used by rsmod v2. - Displee (
com.displee:rs-cache-library) — reads/writes all modern OSRS cache types; popular alternative.
Bases evaluated and rejected (all structurally locked to pre-Leagues revisions)
- 2009scape (rev 530, 2009; custom Java NIO; AGPL-3.0 copyleft) — wrong era.
- Apollo (rev 377/317; dormant ~6 yrs; ISC) — wrong era.
- RuneJS (rev 435, 2006; TypeScript/Node; GPL-3.0) — predates OSRS entirely.
- rsmod v1 (
Tomm0017/rsmod, rev 180/181; archived) — useful only as a learning reference for the hand-rolled Netty/ISAAC/RSA stack.
2. Caches & Leagues data availability
OSRS Leagues are seasonal event modes. Mapping each to its client revision and the launch-day
OpenRS2 cache (verified against archive.openrs2.org/caches.json):
| League | Launch | Revision | Launch cache ID | XTEA keys | On modern stack (RSProt ≥221)? |
|---|---|---|---|---|---|
| III – Shattered Relics | Jan 2022 | 202 | 768 | ✅ ~1851 | ❌ below floor |
| IV – Trailblazer Reloaded | Nov 2023 | 218 | 1605 | ✅ ~2200 | ❌ below floor |
| V – Raging Echoes | Nov 2024 | 227 | 1974 | ✅ ~2328 | ✅ supported |
| VI – Demonic Pacts | Apr 2026 | 237 | 2518 | ❌ 0 keys archived | ✅ supported but no map keys |
Conclusion: Leagues V (rev 227, cache 1974) is the only edition that combines real
in-cache Leagues UI assets + available XTEA keys + a supported RSProt protocol band.
Download URLs (pattern, scope = runescape)
https://archive.openrs2.org/caches/runescape/<id>/disk.zip # Jagex disk store — use this
https://archive.openrs2.org/caches/runescape/<id>/keys.json # XTEA map keys — REQUIRED for maps
https://archive.openrs2.org/caches/runescape/<id>/flat-file.tar.gz # one file per group (diffing/inspection)
https://archive.openrs2.org/caches/runescape/<id>/map.png # rendered world map (sanity check)Leagues V: .../runescape/1974/disk.zip + .../runescape/1974/keys.json.
What's in the cache vs. server-side
In the cache (client-side — you NEED the matching cache to render any of it):
- Interfaces / components (
if3, index 3): Leagues tab, relic-selection screen, region/area unlock map, task journal, Combat Masteries panel, end-of-league summary. - ClientScripts / cs2 (index 12): the scripting that populates those interfaces.
- Sprites (index 8) & textures (index 9): relic icons, league logos, region art, masteries icons.
- Enums & structs (index 2): lookup tables for relic/task/region definitions (modern Leagues lean on enums/structs/DBTables).
- Varbit/varp definitions (index 2): the declarations of bits tracking unlocks/progress.
Server-side only (we implement 100% of this):
- All gameplay logic: task completion detection, point awards, relic effects, XP multipliers, drop-rate changes, region-lock enforcement, teleport/area restrictions.
- Per-player state: varbit values (which relics/regions/masteries unlocked). The cache defines the varbits; the server pushes the values; the interface renders from them.
- Live task lists, leaderboards, point totals.
The architectural through-line: progression state lives server-side as varps/varbits; the cache interfaces are thin renderers that read those vars. Loading the rev-227 cache makes the Leagues panels draw; making them work is entirely our server code.
Cache caveats
- XTEA map keys — pull
keys.jsonfor the cache or map loc data stays encrypted. III/IV/V have keys; VI (build 237+) currently has none on OpenRS2. - Index count grew across revs (rev 202 = 21 indexes, 218/227 = 22, 230+ = 25) — the cache lib must not hardcode it. OpenRS2/Displee handle this.
- Match client ↔ cache ↔ revision exactly or you get CRC/version mismatches (
CLIENT_OUT_OF_DATE). - RSA login keys — generate your own keypair; put the public modulus where the client reads it, keep the private key server-side.
3. Login / networking protocol (recent revisions)
All handled by RSProt — documented here so we understand what's happening, not to hand-roll it.
Three services on one port, selected by the first byte:
15→ JS5 / filestore (streams cache groups to the client; no ISAAC obfuscation).14→ login/game gateway.- within login:
16= new login,18= reconnect.
Login flow (modern):
- Client sends
14. - Proof of Work (mandatory since ~rev 178): server sends a ~495-byte block + difficulty;
client brute-forces a nonce so
SHA-256(block‖nonce)has ≥difficulty leading zero bits. Reconnects skip PoW. Exists to make login-flood/RSA-decrypt DoS expensive. - Client sends login type byte (
16/18) + length + login block. - Login block = plaintext header + RSA-encrypted section + XTEA-encrypted section.
- RSA section: verification byte (
1in modern OSRS, not legacy10), the 4 XTEA key ints (which double as the ISAAC seed), server seed, auth type (2FA/TOTP), password. - XTEA section (decrypted with those 4 ints): username, client settings, dimensions, uid bytes, machine info, and N×i32 cache archive CRCs.
- RSA section: verification byte (
ISAAC obfuscates packet opcodes (not payloads) on the post-login game stream:
seed = [key0,key1,key2,key3] // the 4 ints from the RSA block
inCipher = ISAAC(seed) // decrypt opcodes from client
for i in 0..3: seed[i] += 50 // the +50 offset is universal
outCipher = ISAAC(seed) // encrypt opcodes to client
opcode_on_wire = (real_opcode + isaac.nextInt()) & 0xFFPer-revision gotchas: opcode→message mappings reshuffle almost every build (regenerate prot tables per cache rev); RSA key size grew to 4096-bit; RSProt's API differs significantly <235 vs 235+. Pin one revision.
4. Interfaces — if3 format & how the server drives the UI
if3 vs legacy
- Legacy (
.if) — old RS2/317-style widgets (62 remain in dumps). if3— modern system (895 interfaces in dumps). Each component has atype(layer,graphic,model,text,rectangle,line…), declarative/responsive positioning (x/y+xmode/ymode∈ {abs_left, abs_centre, abs_right, abs_top, abs_bottom} +widthmode/heightmode∈ {abs, minus, proportion}), nesting vialayer=<parent>, and behavior hooksonload,events,op1..opN,if_setonvartransmit(...).- Components are addressed as a packed
(interfaceId << 16) | componentIdint — the same packing sent in server packets.
Example (interface/account.if3 from osrs-dumps)
// 109:0
[universe]
type=layer
widthmode=minus
xmode=abs_centre
onload=account_init(event_com, account:tabs)
// 109:3
[tab_1_1_backing]
type=graphic
width=20 height=26
ymode=abs_bottom
layer=account_tab
graphic="tabs_tall,12"How the server drives the UI (by function — opcode numbers are per-rev & ISAAC-obfuscated)
IF_OPENTOP— set the top-level interface (e.g.toplevelid 548). Required to pass login.IF_OPENSUB/IF_CLOSESUB— open/close a sub-interface into a target component (modal/overlay).IF_SETTEXT— set a string on atextcomponent.IF_SETEVENTS— enable which clicks/drags a component (range of slots) accepts.IF_SETHIDE / SETMODEL / SETANIM / SETNPCHEAD / SETOBJECT / SETSCROLLPOS / SETCOLOUR / SETPOSITION.RUNCLIENTSCRIPT— invoke a cs2 client script.- State (numbers/booleans) flows through varps, not a "set component value" packet:
VARP_SMALL(1-byte) /VARP_LARGE(4-byte) — set a player variable.- Varbits are not sent directly in OSRS — a varbit is
(basevar varp, startbit, endbit); the server sets the underlying varp, the client extracts the bits.config/dump.varbittells you exactly which varp+bits each varbit occupies. if_setonvartransmit("script{var_a,...}", component)directives tell you which varps a component listens to — to update a UI element, set the varp it transmits on.
Authoring / porting interfaces (answers the project's key question)
- Custom interfaces ARE buildable. Edit
.if3text → compile into the cache with the rsmod/RuneStar packer. A bespoke relic selector, map unlocker, task journal — all authorable on any revision. We are not limited to Jagex's assets. - Porting Jagex interfaces between caches is possible but is a dependency-graph migration: an interface references sprites (idx 8), models (idx 7), cs2 (idx 12), enums/structs/params (idx 2) by numeric ID. To move a Leagues panel from the rev-227 cache into a newer cache you must (a) extract the interface + its full transitive dependency tree, (b) remap IDs to avoid collisions with content already in the target cache, and (c) recompile cs2 to the target rev's bytecode. Known practice (RuneStar cs2, OpenRS2/RuneLite cache tools, Displee), but real work — deferred, not MVP.
Tooling
- OpenRS2 — cache toolkit + archive + XTEA keys; programmatic read/write/rebuild.
- RuneLite cache tools (
net.runelite.cache) — dump configs/sprites/models. - RuneStar —
cs2decompiler/compiler + the.symname infrastructure osrs-dumps is built on. - RSProx — records real client↔server traffic (adds the decoders RSProt lacks).
5. Leagues mechanics — common core vs. per-edition
Common core engine (build once, reusable across editions)
- Progression var schema — league points, task count, unlocked-region bitfield,
relicTier[1..8], trophy tier, edition-module slot. - Task engine — tasks as data
{id, name, difficulty, points, regionTag, completionCondition}; an event-bus matcher hooking skill/combat/inventory/quest/location events against active task predicates; per-player completion bitset; dual counters (points unlock relics; task count unlocks regions). - Region / content gating — tag map zones with region IDs; check the unlocked set at transitions, teleport destinations, respawns, and assignment generators (NOT per-tile walls). Same mechanism generalizes to "locked skills/bosses" (Leagues III).
- Relic / skill-tree framework — tiered, threshold-gated, permanent 1-of-N node selection driving a modifier set. (Reused directly by Leagues V Combat Masteries.)
- PlayerModifiers pipeline — a central object recomputed whenever a relic/mastery changes;
every XP grant, drop roll, run-energy/farming tick, minigame-point award reads from it.
finalXP = baseXP * globalXpMult * combatXpMult(if combat) * relicSkillBonus. Boosted drops use a per-table eligibility whitelist, not a blanket buff. - Interface layer — Leagues tab, task list (filterable), relic screen, region map, reward shop; thin views over the var schema; click-opcodes validated server-side.
- Scoring / leaderboard — points persistence, trophy thresholds (pluggable: fixed vs percentile), HiScores publish, post-league point→shop conversion.
- Onboarding — cinematic/tutorial, starting-region picker, starter stat/item grants, auto-unlocked quest prerequisites, Ironman + instant-restock shop rules.
Per-edition modules (the swappable "power fantasy")
- III – Shattered Relics: fragment collection (RNG, XP-scaled), 7-slot loadout interface, per-fragment leveling, set-effect evaluator; Sage's Renown currency unlocks skills/bosses; no map locking (content-locking instead); percentile trophy cutoffs. Most divergent — treat as a distinct module.
- IV – Trailblazer Reloaded: the clean baseline — pure region + relic + task core, no extra module. 8 relic tiers, permanent 1-of-3 picks; region unlocks at 60/140/300 tasks; fixed trophy thresholds. Best reference architecture for a first implementation.
- V – Raging Echoes: adds Combat Masteries (Melee/Ranged/Magic × 6 tiers, 10-point pool from 10 one-off tasks, sequential gating, shared cross-style passives — reuses the relic framework) + Echo Bosses (orb-gated access, echo drop tables) + waystone travel + Varlamore region.
Recommended mechanics sequencing
Build the IV-style clean core first (region + relic + task + modifiers + scoring + UI), then V is additive (drop in masteries + echo content). This is independent of cache revision — the ruleset is 100% server-side. We target the rev-227 cache for its UI assets, while implementing whatever ruleset we choose on top.
Key links
- rsmod v2: https://github.com/rsmod/rsmod · Alter: https://github.com/AlterRSPS/Alter
- RSProt: https://github.com/blurite/rsprot · RSProx: https://github.com/blurite/rsprox
- OpenRS2: https://github.com/openrs2/openrs2 · Archive: https://archive.openrs2.org
- osrs-dumps: https://github.com/Joshua-F/osrs-dumps · RuneStar cs2: https://github.com/RuneStar/cs2
- Leagues V (Raging Echoes): https://oldschool.runescape.wiki/w/Raging_Echoes_League
- Trailblazer Reloaded: https://oldschool.runescape.wiki/w/Trailblazer_Reloaded_League
- Shattered Relics: https://oldschool.runescape.wiki/w/Shattered_Relics_League