Repository navigation
Owners map
The map owners: which worlds get a map, what a map does each tick, what it keeps of a character and when it saves it, how a character enters, leaves and logs out, and how a map stops. How owners fit the server is on Owners, and how maps are ticked on Map scheduler.
The world catalog reads tbl/World.tbl from the operator's client data once, at start-up, and classes every world:
| A world is | When its row |
|---|---|
| Open world | Has type 0, with flag bit 0 clear |
| Held | Has type 0, with flag bit 0 set: the new-player tutorial and the maps around it |
| Instanced | Has any other type |
| Unknown | Isn't in the table, or no table could be read |
Only an open world gets a map: one shared map per world, made the first time a character enters it and placed in the scheduler's pool. A select into any other world is refused with the general reason, and at start-up the server warns about any start location that names one. With no Content:ClientDataPath, or no table under it, every world is unknown and no world is hosted.
- If the map has finished, do nothing.
- If its timer wheel is empty, advance it to just before now.
- Drain the high-priority queue in full.
- Drain the normal queue: until it's empty, or once 32 messages are handled and 5 ms of the tick have passed (
Maps:NormalDrainFloor,Maps:NormalDrainBudget). A stopping map drains everything. - Run the timers due.
- Queue the saves due, for characters still held that changed.
- If stopping, hand everyone back and finish.
- Record the tick's duration and what it allocated.
The client's movement is high priority; arrivals, departures, the client's other reports and save completions are normal. A steady tick allocates nothing.
| Arrives | The map |
|---|---|
| A character it already holds | Logs an error and disconnects the second session; nothing goes back to the realm |
| For a session that already holds a character here, or has gone | Hands the character straight back |
| Anything else | Holds it, gives its unit an id from the map's own count, starts its save timer, and sends the world entry |
The map sends, in order: EnterWorld (0x00AD), the character's UnitCreate (0x0262), PlayerUnit (0x019B), WorldSettings (0x00F1), MovementControl (0x0636), PlayerState (0x025E) and PathState (0x06BC). The unit carries the character's name, race, class, sex and faction, its position and facing, its level, and health of 1,000, our own number until stats exist. WorldSettings carries the movement interval, the tick times the update interval, 125 ms by default.
-
WorldReady(0x00F2) completes the entry once: the map keeps its zone unless it's 0, and answers withWorldEntryComplete(0x0061). -
A
MovementControlReceipt(0x0635) with the grant's ticket gives the client control; any other ticket is ignored.
Its world, zone, position and facing. The zone comes from WorldReady and every ZoneChange (0x063A), a report of zone 0 keeping the zone it had. Position and facing come from the client's movement, finite values only, once the client holds control. The map never changes a character's world.
- The first save falls at a random point from 1 ms to one save interval after arrival, and each next one an interval after the one before, whether or not that one saved.
- A save is queued only while the character has changed since its last committed save and is still held, and on every hand-back where it changed.
- A save carries the arrival it belongs to, so a save from an earlier stay can never mark this one clean.
The hand-back runs in one order: the map removes the character, cancels its timer, queues its save if it changed, posts the return to the realm, and only then lets the session go. The save is queued before the return, so a load the realm queues after it, on the same key, reads it.
Logging out follows the client's steps. A request to leave gets a countdown of zero; a request to cancel clears it. The client's confirmation, while a request is pending, hands the character back and sends WorldLogout (0x0594) to return the client to character select on the same connection. A confirmation with no request pending is dropped and counted.
Closing the connection hands the character back the same way.
A stopping map's last tick takes no more messages, drains what's queued, hands every character back, an arrival still queued included, and disconnects each with "the map stopped". After that it does nothing.
| Throws | The map |
|---|---|
| A handler for a session's message | Logs it, counts a fault, disconnects the session and hands its character back |
| A handler for an arrival | Returns the character as it arrived, or hands it back if this session holds it |
| A timer or a save | Logs it and counts a fault; the timers still due run on the next tick |
| Id | Level | When |
|---|---|---|
| 7003 | Error | A map's handler threw |
| 7004 | Error | A map's timers or saves threw |
| 7005 | Debug | A character entered a map |
| 7006 | Debug | A character was handed back |
| 7007 | Error | A map was asked to hold a character it already holds |
| 7008 | Warning | A select named a world no map can host |
| 7009 | Warning | A start location names a world no map can host |
| 7010 | Warning | The world table couldn't be read |
| 7011 | Warning | No client data path is set, so no world is hosted |
| 7012 | Debug | A map dropped a message it read |
- Stats beyond health and level, the look and gear, and the dash and sprint resources aren't sent yet.
- Repeated faults don't stop a map yet (Owners design).
| Piece | Path |
|---|---|
| The map owner, the player and the world entry |
Source/NexusTomorrow.Maps/MapOwner.cs, Source/NexusTomorrow.Maps/Player.cs, Source/NexusTomorrow.Maps/WorldEntry.cs
|
| The map directory | Source/NexusTomorrow.Maps/MapDirectory.cs |
| The world catalog | Source/NexusTomorrow.Maps/Worlds/ |
| The log events | Source/NexusTomorrow.Maps/MapLog.cs |
| The messages between realm, gateway and map | Source/NexusTomorrow.Owners/World/ |
| Tests | Tests/NexusTomorrow.Maps.Tests/ |
Start here
How the server works
The protocol
Working on it