βββββββ βββ ββββββββββββββββββββ ββββββββββββββββββββββ βββ βββββββββββ ββββββββββββββββββββββββββββββββββββββββββββ ββββ βββββββββββ βββββββββββ βββ βββ βββ βββ βββββββ βββββββββββ βββββββββββ βββ βββ βββ βββ βββββ βββ ββββββββββββββββββββ βββ βββββββββββ βββ βββ βββ βββ βββββββ ββββββββ βββ ββββββββββ βββ βββ
The first fully feature-complete, multi-revision, RuneScape private server engine written in Rust -- and the first private server to build its game cache from source assets to CRCs that perfectly match the original Jagex game cache.
Important
rs-majula is the project, Cargo workspace, and canonical engine name: a
from-scratch Rust reimplementation of a RuneScape 2 game server, with
byte-identical protocol and content emulation, a single-threaded deterministic game
loop, and an async tokio host. The stock client connects and plays against
unmodified cache content.
The workspace is 19 crates organized by responsibility:
rs-engine/-- Seers-engine/README.mdfor the full technical whitepaper.rs-pack/-- the content/cache compiler (also invoked in-process at boot).rs-protocol/(+macros/) -- the logic-free wire codec.rs-server/-- the asynctokiobinary: bootstrap, sockets, the HTTP service that serves the web client, and the terminal dashboard (TUI).rs-ether/-- an Elixir sidecar for cross-world social features (git submodule). Login is gated on it -- see requirements.content/,.keys/,public/-- runtime assets (cache sources packed at boot, the RSA key pair, and the web client), already included in the repo.
| Tool | Version | Why |
|---|---|---|
Rust (via rustup) |
stable, β₯ 1.95 (MSRV), edition 2024 | Pinned by rust-toolchain.toml, so rustup installs the right toolchain automatically. Builds use -C target-cpu=native (the binary is tuned to the building machine's CPU). |
| C linker | -- | Required by any Rust build: MSVC Build Tools on Windows, cc/clang on Linux/macOS. |
| Docker + Compose | -- | Required to log in. Runs Postgres 17 (docker-compose.yml); the server authenticates/persists accounts against it. Until Postgres is connected, logins are rejected with login server offline. |
Elixir + mix |
~> 1.15 |
Required to log in. Runs the rs-ether sidecar, which the login flow depends on (cross-world login checks). The server boots and serves the web client without it, but every login attempt returns login server offline. Auto-spawned via cmd /c (Windows); on Linux/macOS start it from rs-ether/ yourself. |
| Git | -- | rs-ether is a submodule -- clone with --recursive. |
Note
To actually log in you need both Postgres (Docker) up and the ether sidecar connected. If Postgres is down or the sidecar fails to start (e.g. Elixir isn't installed), the server still serves the web client, but the client will report "login server offline" on every login.
git clone --recursive https://github.com/RustCityRS/rs-majula.git
cd rs-majula
# already cloned without submodules?
git submodule update --init --recursiveNeeds Rust + Docker + Elixir. The RSA keys and cache content ship in the repo, so there is nothing else to generate.
# 1. Start Postgres (background; data persists in a named volume)
docker compose up -d
# 2. Build & run the server (node 10 = world 1).
# On first boot the ether sidecar runs `mix deps.get` + `ecto.create` +
# `ecto.migrate` against Postgres, so Docker must be up first.
cargo run -p rs-server
# 3. Open the web client and log in
# http://localhost:8080/rs2.cgiTip
First build compiles the whole workspace and takes a while. For running a
populated world locally, prefer the dev-opt profile (near-release speed,
still has debuginfo): cargo run --profile dev-opt -p rs-server.
Tip
For maximum performance (production or benchmarking), run in release mode --
cargo run --release -p rs-server -- for full optimizations and fat LTO, at the
cost of the longest compile.
Stop Postgres when you're done with docker compose down (add -v to also wipe
the database volume).
The cargo world1 / cargo world2 aliases bring up a two-node cluster (each
auto-spawns its own ether sidecar; they mesh via the shared --cluster list):
docker compose up -d
cargo world1 # --node-id 10, cluster world10+world11
cargo world2 # --node-id 11, in a second terminal -- the cluster meshesSee rs-ether/README.md for sidecar details.
All configuration is via CLI flags (clap). This is the complete set -- run
cargo run -p rs-server -- --help for the canonical output.
| Argument | Default | Description |
|---|---|---|
--host <HOST> |
0.0.0.0 |
Bind address for the TCP game + HTTP listeners. |
--http-port <HTTP_PORT> |
8070 + node_id (8080) |
HTTP port -- web client + cache archives. |
--tcp-port <TCP_PORT> |
43584 + node_id (43594) |
TCP game-protocol port. |
--private-key <PRIVATE_KEY> |
.keys/private.pem |
RSA private key (PEM) for the login handshake. |
--members |
true |
Members world (vs free-to-play) content rules. |
--client-pathfinder |
true |
Trust client-computed movement paths. |
--no-tui |
false |
Disable the TUI dashboard; use stdout logging (auto-falls back when stdout isn't a TTY). |
--verify |
true |
Verify packed-cache byte-identity at boot (pack_all). |
--node-id <NODE_ID> |
10 |
World node ID (10 = world 1, 11 = world 2, β¦); drives the derived port scheme. |
--ether-port <ETHER_PORT> |
5000 + node_id (5010) |
Ether sidecar TCP port. |
--db-host <DB_HOST> |
localhost |
Postgres hostname. |
--db-port <DB_PORT> |
5432 |
Postgres port. |
--db-name <DB_NAME> |
postgres |
Postgres database name. |
--db-user <DB_USER> |
postgres |
Postgres username. |
--db-pass <DB_PASS> |
password |
Postgres password. |
--cluster <CLUSTER> |
"" |
Comma-separated cluster node list (e.g. world10@127.0.0.1,world11@127.0.0.1), forwarded to the sidecar mesh. |
--pepper <PEPPER> |
localhost |
Server-side pepper for password hashing. |
Note
--members, --client-pathfinder, --no-tui, and --verify are boolean flags
(defaults shown). The --db-* defaults match docker-compose.yml, so the server
connects with no extra flags.
Defined in .cargo/config.toml:
dev(default) -- unoptimized, full debuginfo; fast compiles, enables the debug-only hot-reload.dev-opt--inherits = devwithopt-level = 2; the practical profile for running a real world locally.cargo run --profile dev-opt -p rs-server.release-- maximum performance:opt-level=3, fat LTO,panic = "unwind"(load-bearing for the engine'scatch_unwindrecovery). Slowest to compile; use it for production and benchmarking.cargo run --release -p rs-server.
There are two ways to play the server.
Depending on your targeted revision, navigate to the respective /public/{REV}/client.jar file.
Run the following command:
java -cp client.jar client 10 0 highmem members 10Note
Usage: node-id, port-offset, [lowmem/highmem], [free/members], storeid
Navigate to the following address on any modern web browser:
Note
Any browser should be supported as long as it supports WebAssembly. This includes any mobile browser as well.
The engine supports multiple revision targets. The ones listed below can be targeted to your choice.
Simply change the REV located in /.cargo/config.toml and rebuild.
[env]
REV = "289"- https://runescape.wiki/w/Update:New_game_update_system
- https://runescape.wiki/w/Update:Agility_improved_and_bug_fixes
- https://runescape.wiki/w/Update:New_In-Game_Player_Moderators
- https://runescape.wiki/w/Update:Special_Attacks
- https://runescape.wiki/w/Update:Various_tweaks_to_the_game
- https://runescape.wiki/w/Update:Lots_more_improvements
- https://runescape.wiki/w/Update:Easier_to_rearrange_bank
- https://runescape.wiki/w/Update:Priest_In_Peril_Quest
- https://runescape.wiki/w/Update:Nature_Spirit_Quest
- https://runescape.wiki/w/Update:Quest_Journals_and_Chompy_Hats
- https://runescape.wiki/w/Update:Agility,_Potions_and_Parties!
- https://runescape.wiki/w/Update:New_Random_Events!
- https://runescape.wiki/w/Update:Death_Plateau_Quest
- https://runescape.wiki/w/Update:Troll_Stronghold_Quest
- https://runescape.wiki/w/Update:Herblore_Additions
- https://runescape.wiki/w/Update:Situation_in_the_Sands
- https://runescape.wiki/w/Update:Tai_Bwo_Wannai_Trio
- https://runescape.wiki/w/Update:Plague_City_Part_4_Released
- https://runescape.wiki/w/Update:Skills,_Duels_and_the_Kalphite
- https://runescape.wiki/w/Update:Eadgars_Ruse
- https://runescape.wiki/w/Update:Morytania_Expansion
- https://runescape.wiki/w/Update:Mortton_Shades_and_Mage_Armour
- https://runescape.wiki/w/Update:Mage_Armour_Updated
- https://runescape.wiki/w/Update:Treasure_Trails_and_Changes
- https://runescape.wiki/w/Update:The_Fremennik_Trials
- https://runescape.wiki/w/Update:Horror_From_The_Deep
- https://runescape.wiki/w/Update:Burthorpe_Games_Room
- https://runescape.wiki/w/Update:Throne_Of_Miscellania
- https://runescape.wiki/w/Update:Monkey_Madness
- https://runescape.wiki/w/Update:Various_Small_Changes.
- https://runescape.wiki/w/Update:More_Small_Changes
- https://runescape.wiki/w/Update:Castle_Wars
- https://runescape.wiki/w/Update:Changes_to_Castle_Wars
- https://runescape.wiki/w/Update:Santa,_Flax_and_Castlewars
- https://runescape.wiki/w/Update:The_Haunted_Mine
- https://runescape.wiki/w/Update:Troll_Romance,_Banks_and_Chat
- https://runescape.wiki/w/Update:In_Search_Of_The_Myreque
- https://runescape.wiki/w/Update:Trawler_Game_Update
- https://runescape.wiki/w/Update:Karamja_Dungeon
All are welcome to help contribute to the project. I especially am always looking for performance opportunities that could be had, whether CPU or RAM optimizations.
- Please provide benchmarks of a before & after if you open a PR for any performance improvements.
- Please provide sources of any additional engine or content changes you open a PR for.
- I am using a Windows machine and am unable to validate the project under Linux or MacOS myself, relying on Github actions.
Tip
When you open or push to a PR, a Github action will run jobs to validate that all revisions build, compile content and test. You do not have to manually validate every supported revision yourself.
Bearer of the curse, seek misery.