Repository navigation
Roadmap
The order we build the server core in, and where it stands. What each step builds is on Server core design; this page is about sequence and progress.
We build the core in six steps, and each one ends green in public CI and, where it has one, passes its check in game. The first goal anyone can see is milestone one: log in, create a character, enter the world, walk, see another player move, and /say. Each step gets its own plan once the step before it is done, so every plan starts from code that exists rather than code we assume.
The order follows what each step needs to be tested at all.
- The codec comes before anything that sends a message, because nothing that talks to a client can be tested until messages can be declared. The codec's converter also needs the leaves split out of the old code first, so steps 1 and 2 share one plan.
- Messages are checked before the core skeleton uses them, because a message the client misreads fails silently, far from its cause. Milestone one's go first, and the skeleton starts as soon as pregame's are verified, so the project stops depending on the old server; the rest of the audit carries on alongside the build.
- The core skeleton comes before milestone one, because the milestone's checks run in the test harness, and the harness drives the server through the command layer the skeleton builds.
- The console follows milestone one straight away, since by then there's a server worth administering.
- Systems go in dependency order. Content first, because nothing happens in an empty world. Events and the read model before quests and achievements, because those consume them. Combat last, because it needs the geometry bake.
| Step | Delivers | Done when | Status |
|---|---|---|---|
| 1. Repository | The seeded repository, cut to the leaves that carry over, renamed, with nullable reference types on, tests and CI | Builds green with the leaves alone | Done on main
|
| 2. Codec | The declaration format, the source generator, a converter from the old message models, the client-facts table and its check | Every message declared; the facts check passes after fixing what it reports | Done on main: stage 3 closed after its review at c6b56e9. All 1,119 of the client's messages declared, 1,087 agree with the client's full layouts, 43 disagreements pinned. The message audit carries on alongside step 3: pregame has 41 of its 42 messages verified to their fields, world entry six, movement three and logout two |
| 3. Core skeleton | Owners and their message queues, the map scheduler, the timer wheel, the gateway and transport, the write-behind queue, the command layer and CLI, the test harness | Harness scenarios for routing, the owner protocols, saves and the allocation gate pass | Stage 1, the pregame slice, is done on main at e8da4aa: the real client logs in through our own server and creates a character, on PostgreSQL 18, with no part of the old server running. Its systems are on Host, Gateway, Owners, Persistence, Command layer and Harness, and Running a server has how to run it. Stage 2a, one character alone in the world, is approved and next: select hands the character to its world's map, which sends world entry in the client's order, keeps where the character stands, saves it, and hands it back when it leaves or logs out to character select. Map scheduler design, Timer wheel design and Owners design have how |
| 4. Milestone one | Pregame on the lifted login front door; character list, create and select; world entry; movement relay; seeing others; /say
|
The milestone scenarios pass, and an in-game pass shows no add-on errors | Planned |
| 5. Console | The web admin console and the player portal | Every command is refused without its permission and audited; the origin and second-factor rules hold | Planned |
| 6. Systems | The content pipeline and the world database conversion first; then events and the read model, quests and achievements, economy and social; the combat track with the geometry bake later | Each system's tests and in-game check | Planned |
The conversation that leads up to it, from logging in to the end of the loading screen, is on Client protocol. World entry takes these server messages and no more, sent in this order, with one report from the client before the last:
| Opcode | What it does | Note |
|---|---|---|
0x00AD EnterWorld
|
Moves the client into a world, with a point for the world to load around. The character's own position comes later, from the world | First. The client accepts it only once a character is selected |
0x0262 UnitCreate
|
Creates the player's own unit | Verified to its purpose; its fields aren't yet |
0x019B PlayerUnit
|
Names that unit as the player, the unit the client moves | Refused if the unit doesn't exist yet or has no base faction; a faction refusal can't be retried |
0x00F1 WorldSettings
|
The world's settings, among them the period of the client's movement timer | Sent on every world entry |
0x0636 MovementControl
|
Gives the client control of the unit's movement | The only way to grant it; without it the player can't move |
0x06BC PathState
|
The player's path | The one message whose absence shows an add-on error |
0x025E PlayerState
|
The player's own values: the amount of each currency, experience, item proficiencies, entitlements and the like. Most of the client's interface waits for it | Never a stub: even the first one has to be well formed, though its lists can start empty. Verified to its purpose; its fields aren't yet |
0x00F2 WorldReady, from the client |
Says the world and the player are loaded | Sent once PlayerUnit and WorldSettings have arrived |
0x0061 WorldEntryComplete
|
Ends the loading screen | The answer to WorldReady, and the only way to end it |
Seeing others adds their UnitCreate (0x0262), their movement relayed in UnitMovement (0x0638), and a destroy when they leave. /say adds 0x01C2 to the sender, which is how it sees its own line, and 0x01C8 to listeners, on the client's built-in Say channel (type 4, id 0).
The core sends a message once its purpose is verified and every member it sets is too, the rest going as defaults, and handles a client message once its fields are verified, or, on a row marked for it, once its purpose is and every member its handler reads is too (Message audit design; both decided 2026-10-18, built with step 3's second stage). So what's left of the audit's path to milestone one is the members world entry sets in UnitCreate (0x0262), PlayerState (0x025E) and UnitMovement (0x0638), the members the server reads from the client's own ClientMovement (0x0637), and chat. The tagged wrapper the client sends many of its messages in, TaggedMessage (0x038C), and the control messages around movement are verified.
Done on main:
- the repository seeded once from NexusForever's
game_reworkwith its full history, and the old server trunk deleted in one commit; - the eleven projects that carried over renamed to
NexusTomorrow.*; - the SDK pinned, central package management, and a test workflow that runs only when dispatched;
- the SRP6 session key fixed to always be 64 bytes (it came out at 63 bytes on roughly one login in 256, which would give the client and server different world ciphers);
- nullable reference types on in nine of the eleven projects, with
Network.AuthandNetwork.Worldleft for step 2, which has since turned them on; - the code standard adopted and enforced in the build, with the tree normalized, formatted and renamed to it.
Both run-time checks the nullable work left owed are done, on a machine with a client: MapGenerator extracted all 384 tables for step 3's first stage, and ClientConnector wrote its config.json from the first-run prompts and started the client. ClientConnector sits in the client's own folder, and finds the client and its settings there however it's started.
The codec is built and on main: the bit reader and writer, the hot-path switch, the declaration language, the source generator, and the client facts table with its check. All 1,119 of the client's messages are declared, and the facts check is the proof: 1,087 agree with the client's full layouts, and 43 disagreements are pinned, each with its reason. How it works is on Codec, and how it was built on Codec design.
An audit of every message carries on alongside the build. We assume every message and opcode we inherited is wrong until the client says otherwise, and check each one against the client: its layout, then what it's for and what each field means, and for milestone one's flows, the order the client expects. Each declaration records how far it's verified, a test holds it there, and until then it carries a neutral name, so nothing is used before it's checked. The design is on Message audit design, and the results are the generated protocol reference.
Where it stands: 53 messages are verified to their fields. Pregame (login, realm select and character select) has 41 of its 42, and world entry, the tagged wrapper, movement and logout the other 12. 0x0262 UnitCreate, 0x025E PlayerState, 0x0637 ClientMovement, 0x0638 UnitMovement and 0x0092 LogoutCountdown are verified to their purpose; the core may send or handle each once every member it sets or reads is verified. What's left for milestone one is under Milestone one, exactly.
There's no upstream. The repository was seeded once and is never synced: no cherry-picks, and no comparing against NexusForever's server. The trunk is replaced, so upstream's changes to it don't apply, and the leaves have already moved on.
One plan per step, written when the step before it is done.
One world. An open-world zone is one map that every player in it shares, never split into layers or shards, so the people in a place can always see each other. Capacity work goes into making a crowded map cheaper to run, and once step 3 lands a load test measures how many players one map holds. Server core design has the rule, and Map scheduler design how maps run.
Built to be hosted publicly. The core is designed, built and documented for a public deployment: Linux as the reference host, client ports hardened against hostile input, backups with a tested restore, outside monitoring, and DDoS protection at the host. A fresh install still binds everything to loopback, so going public is configuration. Server core design has the details.
Out of scope for the core:
| Area | Why |
|---|---|
| Creature AI and the volume of combat rules | The combat track's work. The core fixes the combat contract and the effect engine's shape |
| Content volume | The core builds the pipeline; filling the world is content work |
| Idea | What killed it |
|---|---|
| Keeping a playable server running through the rebuild | It would mean refactoring the old trunk in place, which the server core design rejects. The cost is a dark period until milestone one |
| Syncing with NexusForever | The trunk it changes is the part we replace, and the leaves have diverged |
| Building game systems before the core skeleton | Every system would be written against plumbing that's about to change |
| Claim | Basis |
|---|---|
| Steps 1 and 2, and step 3's first stage, build and their tests pass | The CI run in the header badge: 747 of 747, step 1's seven among them |
| The SRP6 key fix | A test that fails on the old derivation and passes on the new one |
MapGenerator still works |
Run against a real client for step 3's first stage: all 384 tables extracted |
ClientConnector still works |
Run against a real client: it asked for the host and language, wrote config.json and started the client |
| The pregame server works with the real client | The client logged in and created a character on our server; a mixed-case email logs in too; a wrong password is still to be tried with it |
| Milestone one's message list | Established from the client; the in-game pass that opens every window once is still to do |
- Whether character select needs the account messages the old server sent with the list (
0x0968and its neighbours) for the client to offer every creation option, the Expert start and the level-50 entry among them. - Which systems in step 6 come after events and the read model, beyond the order above.
| Piece | Path |
|---|---|
| What each step delivers |
Source/, Tools/
|
| The tests behind each step's status | Tests/ |
| The CI workflow that runs them | .github/workflows/test.yml |
Start here
How the server works
- Server core design
- Host
- Gateway
- Owners
- Persistence
- Command layer
- Harness
- Map scheduler design
- Timer wheel design
The protocol
Working on it