A Silkroad Online server rewritten in Rust, plus a client of our own built on our engine. Faithful to ECSRO 1.091 first, then independent step by step, with all content edited in one studio.
Base ECSRO 1.091 · Cap 90 · DIV01Server Rust · PostgreSQL 17Client claudeengine
Vision
Daorklis rebuilds the Silkroad Online server so that the real 1.091 client cannot tell it apart from the original. The reference client itself is always the ground truth: outside sources on opcodes or behaviour only count once they have been checked against it. Anything not yet proven carries an EXP marker in the code, so assumptions stay visible.
Alongside it runs a client of our own on our engine. All content lives in a single source. From it we generate the server data, the engine client, the PK2 files for the original client and, on request, the reference data of the original server. Everything is edited visually in the studio.
Three tracks, one after the other
1 · ECSRO 1.091
The old feel, faithful to the original. Later a level cap up to 120 or 140, Alexandria, and fixes delivered through our own DLL.
2 · vSRO
A second profile with vSRO content, layered on top of the same source.
3 · Daorklis
Completely free: our own content, our own client, our own rules. IDs from 1 000 000 upward keep it apart from the original.
Guiding model
One solution as in Visual Studio: client and server are two loosely coupled startup projects, and the shared game logic is a library both of them use.
Original values are always the default. Every deviation in the configuration is marked “changed, original: X” right next to it and can be reset.
The big picture
Everything is built centrally on a build server. The game server runs in its own container, and both share the working data. Development happens from a laptop over a VPN.
Build server: builds server and engine, hosts the editor service for web editor and studio, and an isolated sandbox for analysing the original binaries.
Game server container: login, coordinator, realm and the database. The build server builds them and restarts them there.
Update server: distributes signed packages of studio and client. The launcher only fetches files that changed.
Original server stack: the reference. A capture proxy records traffic between original client and original server.
Backups: database daily, assets weekly, code in Git hosting.
Our own client
The Daorklis client runs on our engine and loads the original worlds, models, effects and interface. It looks like Silkroad, but every pixel and every rule is ours to change. These images come straight from the client, rendered from the real world and model packs.
Jangan in daylight, original buildings, trees and lanterns.The same square at dusk: time of day comes from the server.Rain, with the sky and fog turning grey.Snow, sent by the realm per climate region.Jangan from above, with every object of the original world pack.Login over the original ship backdrop, in widescreen.Character select at the ferry harbour with an original model.Donwhang dungeon with the teleport circle effect.A modern GM window the original never had.World map with region names.Original-style interface with the quest window.Many players at once, from a load run.
What our own client makes possible
Looks
Any resolution and widescreen
Weather and time of day from the server
Modern rendering on DX12, Vulkan or GL
Interchangeable skins: original or our own
Security
Encrypted QUIC channel with a pinned certificate
Every server packet signed, numbered and time-stamped
No memory editing of an old binary to fight
Updates signed, rollback refused
Freedom
New windows and tools, like the GM window
New mechanics without client hacks
Content IDs beyond the original limits
Linux and Windows natively
The original client stays fully supported for faithfulness. The engine client is where Daorklis can grow beyond 1.091.
One content source
Instead of maintaining content in several formats, there is one source in a readable text form (RON). The crate dk-content knows the types, and the tool dkc imports, validates and exports. Every file has exactly one canonical spelling, so changes stay small and traceable in Git.
Every kind of data has two ID ranges: original up to 99 999, Daorklis from 1 000 000. Nothing is ever deleted by renumbering, only by leaving a tombstone, so old references never point at something else.
Server
Three services, connected only through the account database. Each can be restarted on its own without players in the world noticing. Only the realm saves everyone first.
The crates marked green are shared between server and engine client. The game core dk-game knows nothing about network, database or clock, and a boundary test keeps it that way. The server code is about 204 000 lines of Rust.
Run modes: with or without GUI, debug or release, in a terminal or as a background service. Stopping is always clean.
Configuration: one TOML file per area. Original values are the default; most settings reload live from the GM window.
Honest assumptions: EXP markers for unproven knowledge, TODO markers for open work, both in generated registers. When an unproven path runs, the server logs it once.
Numbers
Both servers were measured on the same physical machine, a 16-core Threadripper. Ours ran in a container with 10 vCPU and 16 GB, release build, with bots from a separate machine. The original ran in a Windows VM with 8 vCPU.
~20×faster response: 1.2 ms vs. 23 to 27 ms median for a walk click
~100×less idle CPU: 0.3 % vs. 23 to 40 % of a core
500bots in the world at once, 0 errors, 0 lost saves
~300 MBserver memory, flat as players join
Measurement
Original 1.091
Daorklis
Idle CPU, game server
23 to 40 % of a core
0.3 %
Walk response, median
23 to 27 ms
1.2 ms
Walk response, p90
38 to 57 ms
3.7 ms
Memory, game server
900 MB private
240 to 330 MB
Database memory
2.6 GB (SQL Server)
PostgreSQL, shared
Game loop
1 thread, 30 Hz tick over ~24 400 objects
event driven, multi-core
Configured shard limit
1 000
configurable
Password storage
MD5
argon2id
Load, measured
All players stand in one spot, the worst case: everyone sees everyone, so traffic grows with the square of the player count.
Players
CPU (cores)
Per player
Note
100
0.16
0.16 %
one spot
250
0.75
0.30 %
one spot
500
2.40
0.48 %
one spot, 8 million packets per minute
500
1.46
0.29 %
spread out, p95 walk 6.3 ms
A login storm of 500 accounts at once puts 482 players into the world within about 5 seconds. The limit there is the deliberately slow password hash, about 35 ms per check per core.
How many players on which machine
An estimate, not a measurement: we have measured up to 500. It assumes players spread across the world like on a real server, about 0.3 % of a core each plus headroom for crowded towns and fights, and the database on the same machine.
Machine
Daorklis, estimated
Original, for comparison
4 cores, 8 GB
~800 to 1 000
1 000 per shard by configuration; one game thread per game server, more players means more game servers and machines
10 cores, 16 GB (today)
~2 000 to 3 000
16 cores, 32 GB
~3 500 to 5 000
Logins scale with cores too: a big login storm takes longer on fewer cores, but players already in the world are not slowed down by it.
Networking
The server speaks two languages. The original 1.091 client uses the old Silkroad protocol over TCP. The engine client uses a modern, encrypted channel over QUIC. Both end up in the same game world.
The legacy frame
Every packet starts with a six-byte header, all little-endian. If the top bit of the size is set, the rest is encrypted with Blowfish and padded to 8 bytes.
sizeu16
opcodeu16
countu8
crcu8
payloaddata
Counter and checksum protect every client packet. If either is wrong, the server disconnects immediately.
From launch into the world
sequenceDiagram
autonumber
participant C as Original client
participant G as dk-login (gateway)
participant R as dk-realm (agent and world)
C->>G: 0x5000 handshake (Blowfish, key exchange)
C->>G: 0x2001 module id SR_Client
C->>G: 0x6100 version check
G-->>C: 0xA100 ok or patch list
C->>G: 0x6101 shard list
G-->>C: 0xA101 shards
C->>G: 0x6102 login
G-->>C: 0xA102 token and agent target
C->>R: 0x5000 handshake, 0x2001
C->>R: 0x6103 token
R-->>C: 0xA103 accepted
C->>R: 0x7007 character list
R-->>C: 0xB007 characters
C->>R: 0x7001 select character
R-->>C: 0xB001, then 0x3013 character data
All 378 known opcodes are defined in one place, in description files. The code is generated from them, and a test checks them against real captures.
The modern channel
Transport
QUIC with TLS. The client pins the fingerprint of the realm certificate and trusts nothing else.
Format
MessagePack. Field order is the wire format, and new fields are only ever added at the end.
Protection
After login every server packet carries an HMAC, a timestamp and a sequence number checked by a sliding replay window.
Diagnostics
A trace tap mirrors messages to a control tool, and a decrypting proxy covers the original stack.
Client kinds
Profile
What
Path
sro1091_plain
unmodified original client
legacy TCP, the yardstick for faithfulness
sro1091_patched
original with our PK2 patches
legacy TCP
sro1091_dll
plus the Daorklis DLL, every feature unlocked by a server bit
legacy TCP
vsro188
vSRO profile
legacy TCP
daorklis
engine client
QUIC + TLS
Fixes and improvements
Along the way we found bugs in the original and holes in our own first versions. Original behaviour stays the default wherever players could notice, and every deliberate fix is switchable.
Bugs of the original we fixed
Entry
Original behaviour
OF-1
Unequipping the weapon swaps the fist factors, attack values come out wrong.
OF-2
Several levels gained with one kill lose STR and INT points.
OF-3
The Berserk timer stays alive after an early end.
OF-4
Set bonus speed is not updated, movement desyncs.
OF-8
Pet hunger penalty is always active (compares 0 to 100 % with 1000).
OF-9, OF-10
Quests accepted without a re-check; delivery hands items out again after loss.
OF-11 to OF-13
Trade goods distance check never fires, stolen goods unusable, goods via pet cost no gold.
OF-20
Download resume starts again from the beginning of the file.
OF-22 to OF-24
Monster help aggro never triggers, endless approach on walking monsters, knockback through walls.
OF-26, OF-27
AI walks to point 0 in some dungeon blocks; an item beyond the inventory size locks the character out.
OF-28
Equipment can be bought in the item mall.
OF-31 to OF-38
Memo silently cut, broken teleport level check, crash on billing outage, wrong error formats, GM commands building SQL from text, wrong scroll price.
Security
Passwords: argon2id instead of MD5. Old accounts are upgraded on their next login. Unknown account names take the same time as real ones, so nobody can probe for accounts.
Dupes and exploits closed: trade, stall in both directions, pet dupe via relog, storage exploit, gold overflow, gold limits in the core.
Traffic limits: packet rate limits like the original agent, stricter at login, packets before login dropped.
Login protection: lockout after failed attempts, GM address list, optional per-address limit, a queue that answers “server busy” instead of falling over.
Server binaries: tamper detection for debuggers and injection.
Performance and operations
Location index and visibility cells instead of scanning every session: walk response p95 when spread out dropped from 22.5 to 6.3 ms.
Saving only what changed: rows written per save cycle fell from 63 907 to 1 817, lost saves fell to zero.
Send queue by bytes that merges movements; cost per packet at 500 players down from 22 to 17.6 µs.
Single-service restarts: login, coordinator or realm restart alone; the realm saves everyone in about 7 seconds.
Audit log partitioned by day and searchable, live config reload, patch publishing without breaking running downloads, daily off-site backups.
The engine
claudeengine is our own game engine in Rust, about 279 000 lines in 27 crates. Its first user is not a human but an agent: every feature can be checked as an image or a test without starting a game, and every system has its own documentation.
Deterministic
No system clock and no loose randomness in simulation and game state, all randomness from one seeded generator. The same input gives the same game, frame by frame.
Data first
Scenes, interfaces, themes and content are text files. Every kind of content has an editor panel, and the editors and the game read the same files.
Enforced rules
Layering and charter tests fail the build if a crate reaches across a layer, pulls in network code where it does not belong or duplicates a system.
Under the hood
Part
What it does
core, ecs, sim
Base types, entities with snapshots for saves, deterministic simulation
gpu, render
wgpu with DX12 or GL on Windows, Vulkan or GL elsewhere; flat, isometric and 3D views
ui
One interface system for game and tools, node trees and themes as text
scene, assets, anim
Scene format and validator, asset baking, animation format and evaluator
pack
.sk2 archives with hashed names and encrypted content, .skm bootstrap
update-core
Signed manifest, BLAKE3 per file, Ed25519 signature, parallel resumable delta downloads
plugin-api, plugin-host
Stable C interface for engine and game plugins, such as the Daorklis network plugin
app, cli
The seam between engine and game, and the one engine program
studio, editor, launcher
Desktop studio, web editor, launcher and updater
remote
Remote project source: the studio works on server files through a signed tunnel
6 081tests in the full check before every merge
181reference images, rendered identically on any machine
87 000lines in the Daorklis client
2platforms: Linux and Windows
Studio
The studio is the one place to build Daorklis. It opens the client and the server as one solution, edits every kind of content visually and plays it live.
Solution: client project and server code side by side; server files are edited and saved directly on the build server.
Content editors: quests as a graph, items, monsters, shops and item mall, spawn nests and regions on the map, camera flights, effects.
Scenes live: login, character select and the world, with Play against a development realm.
Build and release: build and restart server services with one click, a release panel exports the server data, the client package and the PK2 patch.
The Daorklis project in the studio.All 340 quests with flow, missions and rewards.
Decompile work
To match the original exactly, we take it apart. Every original binary is analysed statically in an offline sandbox with Ghidra; nothing from it is ever executed there. Live behaviour comes from read-only probes, clientless bots and captures against the original stack.
128handovers from analysis to the server
378opcodes described
30subsystems fully specified
244 / 248combat damage samples explained by the formula
What was analysed
The 1.091 reference client and every original server service: gateway, agent, shard manager, game server, global, farm, division and machine manager, download server, the control tool and its plugins, the launcher. The vSRO 1.188 server and client for comparison, plus client DLLs.
What came out
Packet formats and opcodes, combat and EXP formulas, item, social, pet, job and quest rules, crash causes, exploits and a list of original bugs. EXP and SP formulas match in 12 of 12 cases.
Results never go into the server directly. Each finding is written as a handover; the server side implements it, removes the matching EXP marker and records the commit. The goal is a complete specification from which the server could be built from scratch.
Our tools
Most of what Daorklis needs did not exist, so we built it.
Network
sro_proxy
decrypting capture proxy with recording
sro_decoder
packet and file decoder and encoder
ecsro_clientless
clientless bot, smoke test and load generator
dk-loadtest
load test of login chain and movement, CPU profile
Original client
sro_export
textures, models, maps to modern formats, web viewer
sro_ui_import
imports the original interface
sro_dll
feature DLL and PK2 patch packer
sro_launcher
replacement launcher with updates
sro_manager
Linux starter for 1.091 and vSRO
sro_overlay
GM overlay
region_patch
unlocks extra regions
Server and data
dkc
content source: import, validate, format, release
dk-migrate
original database to PostgreSQL
db_export
game data out of the original database
dk-audit
search the audit log
patch-stage
stage and publish client patches
skill-converter
skill data conversion
Engine and docs
daorklis-*.sh
regenerate models, icons, minimap, world, effects, music
studio, editor
desktop studio and web editor
launcher
updater and starter for studio and client
pruefen.sh
the full check before every merge
exp_register
EXP and TODO registers
docs_website
protocol documentation site
kali-re
offline analysis sandbox
Our web tools
Three browser tools carry a lot of the daily work. They run next to the server and need nothing installed.
Documentation site
A static website generated from our reverse-engineering notes and the maintained reference data. We use it to look up an opcode, a protocol phase or a game system rule without digging through files. It covers the original server's startup and login protocol, all 378 opcodes with field layout, evidence and maturity per field, and 37 system specifications such as combat formulas, skills and quests, all searchable.
Home page with the server communication topology.Opcode 0x7021 with field layout and maturity per field.One card per game system specification.
Asset viewer
A browser tool that reads the original client data directly: models, textures, effects and map files. We use it to check assets visually before and after converting them for our own client. It renders whole towns with terrain and buildings, plays skinned models with their animations and browses every raw file.
Map mode: Jangan from the original terrain and object files.Model mode: a skinned monster with its animation clips.
Server dashboard
The web front of our coordinator service, which keeps the list of running game realms. It shows realm health, uptime, live monster and player counts and the loaded game data tables. Further tabs show spawns, skills, drops, shops, quests and AI state, so the server's data and world can be checked at a glance.
Realm status, world status and loaded game data.Spawn view: monster nests around Jangan on the minimap.
Plan
Stage
Content
Status
E0, E1
Content format, scenes, content crates, 340 quests, editor plugins
done
R1a
Studio works on server files directly, builds and restarts from there
done
E2
All kinds of data in the content source, PK2 read and write, round trip
next
R1b
Game data synced between laptop and server, commit from the studio
planned
E3, E4
Writing editors with undo, Daorklis live in the studio
planned
E5, E6
Server and client read the content source, reload without restart
planned
E7
Export for the original client: profiles, PK2 set, release
planned
E8 to E11
Content editors, camera flights, effects, binary assets
planned
E12, E13
vSRO profile, then the switch to our own path
planned
Decisions
No.
Decision
D1
No separate repository: the gamedata in the server repository grows into the one content source.
D2
Three layers: original 1.091 as the base, vSRO and Daorklis as overlays.
D3
Tools in Rust; the existing Python tools serve as a reference.
D5
Freeze nothing; the switch to our own path is only prepared.
D6
The unmodified original client stays the yardstick.
D7
Server-only fields never go into the client package.
D10
Two ID ranges per kind: original up to 99 999, Daorklis from 1 000 000.
D11
Play in the studio with recordings first, then with a realm.
D12
Original columns are kept, texts added in German and English.
R-1 to R-3
The studio reaches the server only through an SSH tunnel, writes into allowed areas, and the access secret stays protected on the laptop.