OSRS Leagues Private Server — Design & Build Plan
Build an ambitious OSRS private server that:
The plan of record. See RESEARCH.md for the evidence behind every decision. Status: pre-Phase-0 (research complete, no code yet). Last updated: 2026-06-28.
1. Goal
Build an ambitious OSRS private server that:
- Forks a mature open-source base and gets client ↔ server connected, logged in, character in-world on a recent Leagues-era cache.
- Stands up the interface plumbing so we can open and drive
if3interfaces via varps. - Implements a custom Leagues recreation — region locking, relics, tasks, point/multiplier systems — as our own server-side ruleset, reusing Jagex's Leagues V UI assets where convenient and authoring custom interfaces where we want our own systems.
Long-term: our own Leagues system (custom relics/regions/mechanics/interfaces), not just a clone.
Two MVPs, in order
- Slice MVP (immediate goal): prove the whole stack + harness + interface pipeline end-to-end
with no gameplay logic. Game launches on rev 227, AI harnesses in, we open the Leagues menu,
spawn into the Leagues V tutorial interfaces, and pick the first relic (with its animation)
— all using Jagex's existing Leagues V
if3panels. This validates that everything loads, runs, and is AI-drivable before we build content. - Full MVP (roadmap after the slice): all skills working, real Leagues mechanics, NPCs/monsters spawned, monster AI, etc. Roadmapped in §5 Full MVP roadmap.
2. The stack (decided)
| Layer | Choice |
|---|---|
| Base server | Fork rsmod v2 (github.com/rsmod/rsmod) — Kotlin 2.2 / Java 21 / Guice / Gradle KTS |
| Networking | RSProt — net.rsprot:osrs-233-* (Maven Central; rsmod's native version) |
| Client | RSProx (auto-patching proxy + packet logger) |
| Cache | OpenRS2 cache 2293 (build 233) + its keys.json — rsmod's native cache |
| Interface/config reference | Joshua-F/osrs-dumps + .sym symbol files |
| Cross-reference base | AlterRSPS/Alter (rev 228) |
Rationale and rejected alternatives: RESEARCH.md §1–2.
3. Target revision decision
Decision: target rev 233 / cache 2293 — rsmod's native build (stock, no retarget).
Revised 2026-06-28 during Phase 0. We originally chose rev 227 (Leagues V live cache) on the premise that it was the only cache containing Leagues assets. That premise was wrong — and retargeting rsmod 233→227 hit escalating structural incompatibilities. We pivoted to stock 233.
Why the original 227 plan was abandoned:
- Retargeting rsmod (built for 233) back to 227 required porting rsprot protocol drift (the
*V2zone/interface packet families — doable, seeretarget-227branch / PORTING.md (archived, internal)), but then hit a cache-structure wall: rev 227 predates thegamevalarchive (index 24) that rsmod's entire type system depends on. Rev 227 has 22 indexes (0–15, 17–21, 255); no index 24. This is load-bearing, not cosmetic. - The Leagues assets are NOT exclusive to the 227 cache. Jagex retains Leagues interfaces in
the cache across years — the rev-239 dump still contains
league_3_*(Shattered Relics, Jan 2022) and every later edition. Verified: the stock rev-233 cache (2293) contains the full Leagues UI —league_relics(interface 655),league_tutorial(653),league_tutorial_main(677),league_side_panel(656),league_tasks(657),league_combat_mastery(311), plus ~4,971 league-named cs2 scripts/procs and named components (league_relics:close_button, etc.). Interface IDs match the rev-239 osrs-dumps exactly, so osrs-dumps is a direct reference.
Why stock 233 wins:
- Zero back-porting. rsmod runs exactly as designed/tested → maximum stability, no fragile ports.
- Has
gameval→ install/type-resolution works out of the box (verified: install green). - Has the Leagues interfaces we need for the slice (verified above).
- Newest base-game content — aligns with the earlier preference for a newer revision; we are not "tied to something old just for leagues."
- Custom
if3authoring is unaffected — we can still build bespoke panels for our own systems.
The rev-227 work is preserved on branch retarget-227 (compiles; protocol ported) if we ever
want the exact event-time cache. Going even newer (rev 239) remains a future option (a 233→239
retarget, likely smoother than →227 since it moves with the V2 direction rsmod already uses).
4. Interface strategy (the project's core question)
Three distinct capabilities, all available to us:
- Reuse Jagex's Leagues V interfaces (rev 227 cache). Already present; reference by name via
.sym; drive via varps. This is the MVP default for relic selector, region map, task journal, Combat Masteries. - Author our own custom
if3interfaces. Edit.if3text → compile into the cache with the rsmod/RuneStar packer. For systems unique to our Leagues (custom relics, our own panels). Works on any revision. This is how we go beyond Jagex's design. - Port Jagex interfaces forward into a newer cache (deferred). Possible but is a dependency-graph migration: extract interface + transitive deps (sprites/models/cs2/enums), remap IDs to dodge collisions, recompile cs2 to the target rev. Real engineering — only if/when we migrate the base game to a newer revision.
Driving any interface (reused or custom) is the same pipeline: IF_OPENTOP/IF_OPENSUB to
display → set the varps components transmit on (cross-ref dump.varbit for bit packing) → push
strings via IF_SETTEXT → behavior via RUNCLIENTSCRIPT. State is server-side; the interface is a
view. Details: RESEARCH.md §4.
5. Phased build plan
Phase 0 — Foundation ✅ COMPLETE (2026-06-28)
Fork rsmod v2 and stand it up on its native rev 233 (cache 2293). No retarget.
- ✅ Cloned
rsmod/rsmod→server/. - ✅ Stock build compiles (Gradle 9 / Kotlin 2.2 / JVM 21).
- ✅
gradlew installgreen: cache 2293 + XTEA downloaded & packed; symbols generated; RSA keypair created (.data/game.keyprivate,.data/client.keypublic). - ✅ Leagues interfaces verified present in cache 2293 (see §3).
- Exit criteria met: builds + cache loads + Leagues assets confirmed.
- (Detour: a rev-227 retarget was attempted and abandoned — see §3; preserved on branch
retarget-227.)
Phase 1 — Login & in-world ✅ COMPLETE (2026-06-28)
- ✅ RSA keypair generated by
install(.data/game.keyprivate,.data/client.keypublic modulus). - ✅ RSProx wired:
~/.rsprox/proxy-targets.yaml→ targetOSRS Leagues Dev, rev 233, our modulus (256-hex),game_server_port: 43594,jav_config_url: jav_local_233.ws. Native client (not RuneLite — RuneLite ignores the revision override). - ✅ RSProx patched + launched the rev-233 native client (
osclient-233.1.exe), connected to our server, JS5 streaming the cache from our server (archive 255master index). - ✅ Logged in (dev realm:
ignorePasswords=true, auto display names) → character in-world, moving + chatting. Two ESTABLISHED sockets on :43594 (server ↔ RSProx). - Exit criteria met. Harness scripts (
dev-up/dev-down/dev-status/client-up) + screenshot (shot.ps1) + click (click.ps1) helpers landed underscripts/.
Phase 2 — AI Development & Debugging Harness 🟡 IN PROGRESS
Make the whole build → run → observe → act → verify loop AI-drivable with no hijacking of the physical mouse. Formalizes the ad-hoc scripts from Phases 0–1 into a real harness. Full architecture in §6.
Done so far: dev-up/dev-down/dev-status/client-up scripts; shot.ps1 (screenshots) +
click.ps1 (mouse, with consent); RSProx packet logging available (transcriber). Action layer
seeded: in-game admin commands ::iface/::leagues/::relics/::ltut to open interfaces
(content/.../commands/LeagueDevCommands.kt, loads at runtime).
Remaining: network/stdin debug-command channel so the AI can trigger actions without the GUI
client; wire RSProx transcriber output into a readable trace the AI asserts against.
- Build/run orchestration: one-command
dev-up/dev-downfor server + client; structured logs the AI tails. - Auth automation: keypair gen wired into both sides; seeded test accounts for non-interactive login.
- Observation (AI's eyes): RSProx packet trace → JSONL the AI reads; server debug state stream; window-scoped screenshots; optional web debug dashboard (drivable via Claude-in-Chrome).
- Action (AI's hands, no mouse): server admin/debug command channel + a headless scripted test client on RSProt that sends client→server packets programmatically.
- Closed verify loop + regression assertions for critical flows.
- Exit criteria: the AI can, unattended, start the stack, log in a test account, open an interface via a debug command, read the packet trace, and assert the resulting varp/interface state — then tear down.
Phase 3 — Interface pipeline + the Slice MVP 🟡 STARTED (immediate goal endpoint)
No gameplay logic — just drive Jagex's existing Leagues V interfaces to prove the pipeline.
Started: interface-open commands ready (::relics etc.); confirmed the relic UI's driving
varps/varbits exist in the rev-233 cache. Runbook + findings in SLICE.md. Next:
verify ::relics opens on screen, then set the varps to populate the 3 relic choices and handle
the confirm button + animation.
- Exercise the
IF_OPENTOP/OPENSUB/SETTEXT/RUNCLIENTSCRIPT+ varp pipeline; reference components by.symname (league_side_panel,league_tutorial,league_relics, …). - Open the Leagues menu (the league side panel tab).
- Spawn into the Leagues tutorial interface set (
league_tutorial/league_tutorial_main). - Pick the first relic with its animation (
league_relics): open the relic selector, handle theIF3_BUTTONclick via the harness's scripted client, set the relic varp, and trigger the selection animation (cs2 / spotanim). Visual confirm via screenshot (+ mouse-on-request for the live click). - Slice MVP exit criteria: from a cold
dev-up, log in, open the Leagues menu, walk through the tutorial interfaces, and select relic #1 with the animation playing — verified in the packet trace and on screen. Everything loads, runs, and is AI-drivable.
Full MVP roadmap
(After the slice — this is what we'll flesh out into detailed, sequenced work.)
Built on top of rsmod v2, which already ships partial skills (cooking, fishing, magic, woodcutting), bank/emotes interfaces, canoe travel, and NPC/obj/loc config tomls — a content pattern to extend. Rough tracks (to be ordered into a real roadmap next):
- Skills: bring all skills to working state (extend rsmod's pattern; gathering → processing → combat skills).
- NPCs / monsters: spawn definitions, spawn zones, loot tables; populate the world.
- Monster AI: aggression, pathing/retreat, combat scripts, attack styles, death/respawn.
- Combat system: PvM formulas (accuracy/max-hit), prayer, equipment bonuses, hitsplats.
- Leagues mechanics (the real ruleset): progression var schema → task engine → region gating → relic framework → PlayerModifiers pipeline → scoring/leaderboard → onboarding (RESEARCH.md §5). IV-style clean core first; V's Combat Masteries + Echo content additive.
- Custom interfaces: author our own
if3panels for systems unique to our Leagues. - Persistence, content tooling, world content (objects/spawns/dialogue) as needed.
6. AI Development & Debugging Harness (architecture)
Claude drives both server and client development, so the harness is foundational, not optional. The guiding principle: maximize the work that can be verified at the packet/log/protocol layer (text, deterministic, fully AI-drivable), use a headless scripted client for round-trips, and reserve the rendered GUI + human-in-the-loop for final visual confirmation only.
A. Build / run orchestration (CLI)
scripts/dev-up/dev-down: reproducibly start/stop server (Gradlerun, backgrounded) and client (RSProx). Server writes a structured log (ideally JSONL) the AI tails; emits a "ready" signal.- Everything launches via Bash/PowerShell
run_in_background— no GUI needed to build/run/restart.
B. Authentication automation
- Script generates our RSA keypair, wires the public modulus into RSProx's
proxy-targets.yamland the private key into the server. - Seeded test accounts (e.g.
test/test) provisioned in the player store for non-interactive login; optional auto-login flag on the scripted client.
C. Observation — the AI's "eyes" (semantic first, pixels last)
- RSProx packet trace → JSONL (primary). RSProx logs every server↔client packet; we render it to structured text the AI reads. Verifying "the relic interface opened and varp 7 became 3" is a text assertion, not a screenshot. Highest signal-to-noise channel.
- Server debug state stream — on demand dump of player position, varp/varbit values, open interfaces, inventory.
- Window-scoped screenshots — capture only the client's window handle to a PNG the AI views (visual confirmation of rendering; scoped so it never images the user's whole screen).
Web debug dashboard— dropped. (A server-hosted control page drivable via Claude-in-Chrome would only control the server, not the Java game client — Chrome can't see into the OpenGL client. Redundant given 1–3 + scripted client; not worth the build.)
D. Action — the AI's "hands" without the physical mouse
- Server admin/debug command channel (workhorse) — telnet/HTTP/stdin console:
iface open <name>,varp set <id> <val>,task complete <id>,region unlock <id>,tele <x,y>,spawn npc, etc. Exercises the server→client interface pipeline and Leagues logic with zero GUI input. - Headless scripted test client on RSProt — programmatically logs in and sends client→server
packets (
IF3_BUTTON= "click relic option 3", walk, continue-dialogue). Round-trips interface interactions with no real mouse; doubles as automated regression tests. - GUI mouse/keyboard, on request — the user grants real-cursor control when the AI asks. The
mechanism is a generated PowerShell script using the Win32 API (
SetCursorPos/mouse_event, keystrokes) + window-scoped screenshots (System.Drawing.CopyFromScreen). It moves the physical cursor, so the AI always asks before running one. Used only when a test needs the rendered GUI (clicking an in-game interface element) — the slice's final relic-click visual check is the canonical case. No isolated-desktop/VM setup unless we later want unattended GUI automation.
E. The closed verify loop
Standard recipe, encoded as repeatable scripts: AI issues a debug command → reads the packet trace + (optional) screenshot → asserts expected interface/varp state → reports pass/fail. "Does the Leagues tab populate?" becomes a one-command check.
F. Regression harness
Golden packet traces / assertions for critical flows (login, open each interface, complete a task) so iterative changes don't silently break them.
Recommended default
Lean on C1 (packet trace) + D1 (server debug commands) + D2 (headless scripted client) as the primary autonomous loop — no mouse, no GUI required. Use C3 (window screenshots) for visual confirmation and D3 (mouse-on-request, with the user's OK) for the rare in-game click test.
7. Future option: migrate base game to a newer revision
If we later want the newest base-game content and the Leagues UI, the path is:
- Bump the base to the target rev (e.g. swap RSProt artifact + cache + symbols).
- Port the Leagues
if3interfaces forward (dependency-graph extraction + ID remap + cs2 recompile — RESEARCH.md §4). Deferred until after the MVP; documented so the door stays open.
8. Open items / to verify during Phase 0
- Confirm rsmod v2 builds cleanly against
osrs-227(API differs <235 vs 235+; 227 is in the earlier band — watch for API drift vs rsmod's 233 assumptions). - Confirm cache 1974 symbol generation produces the Leagues V interface/component names we need.
- Decide persistence (rsmod's default player store vs. our own) — defer to Phase 1.
- Licensing hygiene: rsmod is ISC (permissive); keep attribution. Avoid pulling AGPL code (2009scape) into the tree.