ClockworkRS Docs

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:

  1. Forks a mature open-source base and gets client ↔ server connected, logged in, character in-world on a recent Leagues-era cache.
  2. Stands up the interface plumbing so we can open and drive if3 interfaces via varps.
  3. 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 if3 panels. 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)

LayerChoice
Base serverFork rsmod v2 (github.com/rsmod/rsmod) — Kotlin 2.2 / Java 21 / Guice / Gradle KTS
NetworkingRSProtnet.rsprot:osrs-233-* (Maven Central; rsmod's native version)
ClientRSProx (auto-patching proxy + packet logger)
CacheOpenRS2 cache 2293 (build 233) + its keys.json — rsmod's native cache
Interface/config referenceJoshua-F/osrs-dumps + .sym symbol files
Cross-reference baseAlterRSPS/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 *V2 zone/interface packet families — doable, see retarget-227 branch / PORTING.md (archived, internal)), but then hit a cache-structure wall: rev 227 predates the gameval archive (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 if3 authoring 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:

  1. 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.
  2. Author our own custom if3 interfaces. Edit .if3 text → 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.
  3. 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/rsmodserver/.
  • ✅ Stock build compiles (Gradle 9 / Kotlin 2.2 / JVM 21).
  • gradlew install green: cache 2293 + XTEA downloaded & packed; symbols generated; RSA keypair created (.data/game.key private, .data/client.key public).
  • ✅ 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.key private, .data/client.key public modulus).
  • ✅ RSProx wired: ~/.rsprox/proxy-targets.yaml → target OSRS 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 255 master 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 under scripts/.

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-down for 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 .sym name (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 the IF3_BUTTON click 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 if3 panels 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 (Gradle run, 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.yaml and 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)

  1. 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.
  2. Server debug state stream — on demand dump of player position, varp/varbit values, open interfaces, inventory.
  3. 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).
  4. Web debug dashboarddropped. (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

  1. 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.
  2. 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.
  3. 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.

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:

  1. Bump the base to the target rev (e.g. swap RSProt artifact + cache + symbols).
  2. Port the Leagues if3 interfaces 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.

On this page