Skip to content

Owners design

rebaserHEAD edited this page Oct 17, 2026 · 2 revisions

design, published verified 2026-10-17

The design for the owners: the account owner, the realm owner and one owner per map instance, each holding its side of the game on one thread and reached only through its message queue. Step 3's first stage builds the owner base, the account owner and the realm owner; the map owners come with the map side, and how maps are scheduled and timed stays on Server core design. Roadmap says how far the build has got.

The idea

In short

An owner is one thread over one message queue. It holds its side's state and changes it only while handling a message, so nothing else ever touches that state and nothing needs a lock. Work that crosses owners, or waits on the database, goes out as a message and comes back as one.

Why

The client already splits the game three ways: most of its messages travel under one of three routing tags, fixed per opcode, which we read as the account, the realm and the world. An owner per side follows that split, and one thread per owner makes each side's rules easy to reason about, because only one message changes its state at a time.

Decisions

Locked

An owner is one thread over one Channel<T>. It holds its side's state and changes it only while handling a message. A reply to a caller outside the owner never continues on the owner's thread.

No owner waits on the database. Its reads and writes go to the write-behind queue and come back as messages, as Persistence design describes.

Each message queue drains by priority. High-priority messages (movement, casts, targeting) drain in full every tick, held in check by their rate limits so they can't starve the rest. Normal messages drain within a time budget per tick with a guaranteed floor, and the leftovers wait a tick.

Queued work holds ids, never references. Queued messages, timers and continuations look their entity up again when they run, and drop out if it's gone.

A stalled owner is reported, not waited on. A watchdog reports an owner that stops completing ticks or draining its message queue, with its stack; in the test harness, a stall fails the test.

A failure costs one session or one entity, not the server. A message handler that throws is logged with the message and the session, that session is disconnected, and the owner goes on to the next message. A map update that throws skips that entity for the tick and counts the fault; repeated faults stop the map, disconnecting its players with their state as of the last confirmed save. No catch hides an error.

A character belongs to the realm at rest and to its map while in the world. On world entry the realm hands the character off to the map, which owns it until it leaves or logs out and then hands it back. Maps feed the realm a location record for /who, friends and local chat.

Seven request-and-reply protocols cover every message whose first check and owner differ. The client tags a message by where its first check runs, not by who owns the result, so forwarding is normal and the boundary doesn't move.

Protocol Direction Used by
P1 Map to realm, after a local check Groups, matching
P2 Realm to map Local chat, item links, guild notices
P3 Escrow, map to realm Sending mail, listing on the market
P4 Release, realm to map Taking mail, market fills, the guild bank
P5 Account spend The storefront, account currency
P6 Character handoff, realm to map and back World entry and exit
P7 Map to map, through the realm Housing donations and placement

Every account change a map asks for goes reserve, confirm, apply, commit: the Try-Confirm/Cancel (TCC) pattern.

The realm is built as modules: presence, chat, social, guilds, groups, housing communities and the realm core, each owning its data and calling the others directly on the realm's thread. Moving one into an owner of its own later is a table edit and a thread, taken when the load test shows the realm is the bottleneck, presence and chat first. A map keeps the same kind of seams (positions, combat, inventory and economy, progression, housing) on its one thread.

The account owner

It holds who may play, and who is playing: game tokens and realm session keys, each single use and expiring after about a minute; who is in the world, where a second login replaces the first; and the account's suspensions.

It never reads the database itself. The sign-on exchange hands it the account's credentials as they were read at login, and the command layer tells it about suspensions made while the server runs. Creating an account and changing a suspension go to the write-behind queue, and each answer waits for its commit. A suspension that applies now ends the account's world session at once.

The realm owner

It keeps each online account's characters, loaded once per world session through the write-behind queue, and answers character select: list, create, delete, rename and select. Every answer that depends on a write waits for its commit, so the client never hears about a character that isn't saved.

A character name is 5 to 31 characters, with no space at either end and at least one space between its parts, each part 1 to 29 letters. The lengths are the client's own limits for a name; letters only is our rule. A name in any case is the same name, and a name already taken or waiting to be saved, in any case, is refused as taken.

Creation offers the options that are enabled, aren't costume-only, don't start at level 50, and cost no entitlement or currency, since the first stage has neither. A new character's level is the one its option's experience reaches, and it stands at its faction's configured start location.

A character's look travels as item visuals. The character list and entity creates show equipped item visuals, not the look choices made at creation; the server turns those choices into item visuals through the client's character customization table. That arrives with the map side, so until then a new character shows without its chosen face and hair.

Selecting a character is refused with the general reason until the map side exists to take it into the world.

Knobs

Knob Default Sets
MaxCharactersPerAccount 12 How many characters one account may hold on the realm
Start locations none: world 0 at the origin Where a new character of each faction stands

Open

  • Start locations come from content once the content pipeline exists.

Rejected

Idea What killed it
An actor framework for the owners None of them has a tick boundary, and the map tick is the unit everything else is timed against

Verification status

Claim Basis
The three routing tag values, fixed per opcode Established from the client and verified by the message audit
What each tag stands for Read from which messages travel under it; not yet established by the audit
The name lengths Established from the client
That a selected character can't enter the world yet By design until the map side; the client shows its general selection message

Open questions

  • Whether character select needs the account messages the old server sent with the list (0x0968 and its neighbours) for the client to offer every creation option, the Expert start and the level-50 entry among them. A real login against the first stage shows it.

Clone this wiki locally