Visibility, Exploration Maps, Coordinates & Inventory
2.1 Recommendation: Yes — mandatory from Phase 1
There is no need to transmit full updates for distant entities. Every successful MMO uses interest management (AOI). Without it:
- Bandwidth scales O(players × entities) instead of O(players × nearby entities)
- Encode cost on the server becomes the bottleneck before sim does
- 100 players × 500 NPCs @ 60 Hz is untenable; 100 × ~15 nearby is fine
Rule: A client receives high-frequency tick deltas only for entities inside their network horizon. Everything else is omitted (not even “stale” updates).
2.2 Layered rings — omnidirectional
The character looks all around — no forward-facing cone for AOI or exploration. Interest zones are cylindrical or spherical around the observer's position (x,y,z):
top-down (all directions equal)
beyond
╭─────────────────╮
│ horizon │
│ ╭─────────╮ │
│ │ core │ │
│ │ ● │ │ ← player; radius, not facing
│ ╰─────────╯ │
╰─────────────────╯
| Ring | Radius (tunable) | Update rate | Contents |
|---|---|---|---|
| Core | scale-aware (§2.3) | Every sim tick (30–60 Hz) | Positions, combat state, animations, projectiles |
| Horizon | 2× core (typical) | 5–10 Hz or on change | Position + identity only; no fine combat detail |
| Beyond | outside horizon | None | No entity stream; optional ambient events (see §2.5) |
Radii are per-Area config and may scale with observer body size class. Cartography skill does not extend combat AOI — only map revelation efficiency (§4).
Implemented (2026-07-11): entity AOI uses horizontal radius AOI_CORE_RADIUS_M (32 m) and vertical half-extent AOI_Z_EXTENT_M (6 m) — entities on adjacent z-bands (platforms, cliffs) are visible; distant floors are filtered. See sim module query_radius_z.
2.3 Multi-scale spatial grid (decided)
A single fixed chunk size cannot represent a child-sized human, a giant monster, and a city building equally well. Use a hierarchical grid with a small micro-cell base and larger macro-cells for big footprints and terrain LOD.
Scale classes
| Class | Example entities | Footprint (indicative) |
|---|---|---|
| Tiny | Child, small creature, familiar | ≤ 1 m |
| Small | Human adolescent | 1–2 m |
| Medium | Adult human, wolf | 2–4 m |
| Large | Horse, ogre | 4–12 m |
| Huge | Giant, dragon, house | 12–64 m |
| Structure | Building, wall segment, terrain tile | 64 m+ |
Grid levels
| Level | Cell size (default) | Used for |
|---|---|---|
| L0 micro | 1 m | Tiny/Small/Medium entity AOI, fine exploration reveal |
| L1 meso | 8 m | Large entities, interior rooms, combat clustering |
| L2 macro | 64 m | Huge entities, buildings, terrain blocks, map LOD |
Rules:
- Every entity registers in all levels its axis-aligned bounding volume touches
- AOI subscription: player at L0 cell + rings in all directions; also subscribe L1/L2 cells overlapping horizon radius
- Giants span many L1/L2 cells — one entity, many grid keys; fan-out uses entity keys, not point sampling
- Buildings / terrain are static at L2 (and L1 for doorways); streamed when cartography or AOI includes that cell
- Per-Area manifest can tune cell sizes (dungeon: smaller L2; overworld: larger L2)
World position (47.3, 12.8, z=2) → L0 (47,12) L1 (5,1) L2 (0,0) // example floor
Giant bbox 48m × 40m → occupies L2 cells in 1×1 or more
Child bbox 0.8m → single L0 cell
Why this satisfies the scale requirement
- Small characters resolve to 1 m cells — movement and reveal are precise, not swallowed by 32 m buckets
- Giants don't force the whole world onto 1 m grids — they register at L1/L2 without per-tick full-map scans
- Buildings and terrain live at macro levels; micro exploration still records "I walked past this wall" at L0 for cartography
Implementation sketch:
CellKey { area: w, level: L0|L1|L2, cx, cy, cz }
EntityId → set<CellKey> // from bbox at each level
PlayerId → subscribed cells = omnidirectional horizon ∩ union of levels
Per tick: dirty entities fan-out to players subscribed to any overlapping cell
2.4 Spatial indexing (summary)
- Omnidirectional radius + hierarchical cells — not a forward cone
- Cell crossing triggers subscribe/unsubscribe — amortizes work across ticks
- Cartography revelation uses primarily L0 (and promotes aggregates to L1/L2 for map display); AOI uses all relevant levels
2.5 What about “I know the map but nothing is near me?”
Exploration knowledge (§3–4) does not imply live entity streams. A player with a full cartography ledger for an Area still only receives AOI-filtered entity updates. Their map view shows ledger terrain; live dots for players/NPCs appear only in core/horizon.
Optional ambient channel (low bandwidth): non-positional hints beyond horizon — e.g. “distant explosion”, weather front, faction alert. Gossip, not per-entity state.
2.6 Join, leave, teleport
- Entity enters horizon: reliable
Spawnsnapshot (full component subset), then tick deltas - Entity leaves horizon:
Despawn(client keeps exploration data; removes live entity) - Teleport (w changes): only when changing realm (OSS); within one world, use movement/portals (
01)
2.7 Protocol impact
- Per-client interest set maintained server-side (authoritative)
- Tick packets are per-client filtered — same sim, different views
- Metrics:
interest_set_size,bytes_per_client_tick,subscription_churn
2.8 Design feedback
| Idea | Verdict |
|---|---|
| Horizon for performance | Strong yes — non-negotiable for targets in 00-initial-vision.html §5 |
| Send all entities “just in case” | No — wastes bandwidth; clients cannot use the data |
| Same radius for map fog and network | No — decouple; fog is knowledge, horizon is presence |
| Horizon only (no exploration) | Possible for Phase 1 spike; add fog in Phase 2 |
| Omnidirectional AOI | Yes — no facing requirement; radius-based | | Single global chunk size | No — use L0/L1/L2 hierarchy (§2.3) | | Horizon only (no cartography) | Possible for Phase 1 spike; ledger in Phase 2 |
6.1 Definition (decided)
| Dim | Name | Type | Meaning |
|---|---|---|---|
| x | easting | f32 or fixed | Horizontal axis within Area |
| y | northing | f32 or fixed | Horizontal axis within Area |
| z | elevation / layer | i32 or f32 | Vertical — floors, caves, sky layers |
| w | realm / deployment | RealmId — 0 = live world; separate OSS deployments use other ids |
Which Area shard |
| t | time slice | i64 |
Fixed at 0 for all gameplay until redesigned |
struct WorldCoord {
x: f32,
y: f32,
z: i32,
w: AreaId,
t: i64, // always 0 for now; field reserved in protocol
}
Use w (not a) in docs and APIs.
6.2 Semantics (current)
- Teleportation changes
w; sets arrival(x,y,z) t = 0— single live timeline; no time travel, historical maps, or temporal shards in scope- Protocol and DB columns include
tfor forward compatibility but servers rejectt != 0
6.3 Spatial indexing
Live sim indexes (w, level, cx, cy, cz) only — t not part of spatial keys.
6.4 Future t (out of scope)
When revisited, see prior notes: temporal maps, instanced flashbacks, separate sim per epoch. No implementation until explicitly replanned.
| Idea | Current verdict |
|---|---|
x,y,z,w |
Active |
t in protocol as reserved 0 |
Yes |
t gameplay |
Deferred indefinitely |
7. How the systems compose
Player at WorldCoord (x,y,z,w, t=0)
│
├─► Multi-scale grid (L0/L1/L2) + omnidirectional AOI ──► tick deltas
│
├─► Cartography ledger(w) ──► permanent fog / map UI
│
├─► Map items ──► optional add-to-cartography (permanent merge)
│
├─► Cartography skill ──► reveal efficiency & craft quality
│
└─► Inventory ──► map items as trade goods until added
Effective map view = cartography ledger for w (not ephemeral item reads).
8. Phasing
| Phase | Deliverable |
|---|---|
| 1 | L0/L1/L2 grid + omnidirectional AOI; (x,y,z,w), t=0 |
| 2 | Cartography ledger + 360° reveal on movement; fog the terminal client |
| 3 | Cartography skill; map items; add-to-cartography; craft for trade |
| 4 | Horizon LOD ring; NPC/drop/quest authored maps |
| 5 | Line-of-sight trim (optional); party reveal (optional) |
10. ADR candidates
Parent doc: 00-initial-vision.html — update cross-references when ADRs land.