Specification and vision

Daorklis

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 square with dragon statue, gates and lanterns in daylight
Jangan in daylight, original buildings, trees and lanterns.
The same Jangan square at dusk with a golden sky
The same square at dusk: time of day comes from the server.
Jangan square in rain with grey sky
Rain, with the sky and fog turning grey.
Jangan square in falling snow
Snow, sent by the realm per climate region.
Jangan seen from high above, the whole town with walls, palace and gardens
Jangan from above, with every object of the original world pack.
Login screen over the ship backdrop of the original
Login over the original ship backdrop, in widescreen.
Character select at the ferry harbour with a character model
Character select at the ferry harbour with an original model.
Dark dungeon with a glowing teleport circle
Donwhang dungeon with the teleport circle effect.
Modern GM tools window with tabs for quick actions, physics, spawner, world and server
A modern GM window the original never had.
World map of the region around Jangan
World map with region names.
Interface in the original style with the quest window open
Original-style interface with the quest window.
Many characters on the square of Jangan
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
MeasurementOriginal 1.091Daorklis
Idle CPU, game server23 to 40 % of a core0.3 %
Walk response, median23 to 27 ms1.2 ms
Walk response, p9038 to 57 ms3.7 ms
Memory, game server900 MB private240 to 330 MB
Database memory2.6 GB (SQL Server)PostgreSQL, shared
Game loop1 thread, 30 Hz tick over ~24 400 objectsevent driven, multi-core
Configured shard limit1 000configurable
Password storageMD5argon2id

Load, measured

All players stand in one spot, the worst case: everyone sees everyone, so traffic grows with the square of the player count.

PlayersCPU (cores)Per playerNote
1000.160.16 %one spot
2500.750.30 %one spot
5002.400.48 %one spot, 8 million packets per minute
5001.460.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.

MachineDaorklis, estimatedOriginal, for comparison
4 cores, 8 GB~800 to 1 0001 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.

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

ProfileWhatPath
sro1091_plainunmodified original clientlegacy TCP, the yardstick for faithfulness
sro1091_patchedoriginal with our PK2 patcheslegacy TCP
sro1091_dllplus the Daorklis DLL, every feature unlocked by a server bitlegacy TCP
vsro188vSRO profilelegacy TCP
daorklisengine clientQUIC + 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

EntryOriginal behaviour
OF-1Unequipping the weapon swaps the fist factors, attack values come out wrong.
OF-2Several levels gained with one kill lose STR and INT points.
OF-3The Berserk timer stays alive after an early end.
OF-4Set bonus speed is not updated, movement desyncs.
OF-8Pet hunger penalty is always active (compares 0 to 100 % with 1000).
OF-9, OF-10Quests accepted without a re-check; delivery hands items out again after loss.
OF-11 to OF-13Trade goods distance check never fires, stolen goods unusable, goods via pet cost no gold.
OF-20Download resume starts again from the beginning of the file.
OF-22 to OF-24Monster help aggro never triggers, endless approach on walking monsters, knockback through walls.
OF-26, OF-27AI walks to point 0 in some dungeon blocks; an item beyond the inventory size locks the character out.
OF-28Equipment can be bought in the item mall.
OF-31 to OF-38Memo 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

PartWhat it does
core, ecs, simBase types, entities with snapshots for saves, deterministic simulation
gpu, renderwgpu with DX12 or GL on Windows, Vulkan or GL elsewhere; flat, isometric and 3D views
uiOne interface system for game and tools, node trees and themes as text
scene, assets, animScene format and validator, asset baking, animation format and evaluator
pack.sk2 archives with hashed names and encrypted content, .skm bootstrap
update-coreSigned manifest, BLAKE3 per file, Ed25519 signature, parallel resumable delta downloads
plugin-api, plugin-hostStable C interface for engine and game plugins, such as the Daorklis network plugin
app, cliThe seam between engine and game, and the one engine program
studio, editor, launcherDesktop studio, web editor, launcher and updater
remoteRemote 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.

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.

Documentation site home page with the server communication topology
Home page with the server communication topology.
Opcode entry 0x7021 client movement with field layout
Opcode 0x7021 with field layout and maturity per field.
Overview of the reference specifications as cards
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.

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.

Plan

StageContentStatus
E0, E1Content format, scenes, content crates, 340 quests, editor pluginsdone
R1aStudio works on server files directly, builds and restarts from theredone
E2All kinds of data in the content source, PK2 read and write, round tripnext
R1bGame data synced between laptop and server, commit from the studioplanned
E3, E4Writing editors with undo, Daorklis live in the studioplanned
E5, E6Server and client read the content source, reload without restartplanned
E7Export for the original client: profiles, PK2 set, releaseplanned
E8 to E11Content editors, camera flights, effects, binary assetsplanned
E12, E13vSRO profile, then the switch to our own pathplanned

Decisions

No.Decision
D1No separate repository: the gamedata in the server repository grows into the one content source.
D2Three layers: original 1.091 as the base, vSRO and Daorklis as overlays.
D3Tools in Rust; the existing Python tools serve as a reference.
D5Freeze nothing; the switch to our own path is only prepared.
D6The unmodified original client stays the yardstick.
D7Server-only fields never go into the client package.
D10Two ID ranges per kind: original up to 99 999, Daorklis from 1 000 000.
D11Play in the studio with recordings first, then with a realm.
D12Original columns are kept, texts added in German and English.
R-1 to R-3The studio reaches the server only through an SSH tunnel, writes into allowed areas, and the access secret stays protected on the laptop.