docs(link): protocol 6 as built — a guarantee, and the kind that pays for it
`link/v6.md` is the spec of record. It is the first bump that adds a promise rather than data: a command carrying an `idempotencyKey` is executed at most once, and a repeat is answered with the original reply. Also in this PR: - `INTEGRATION.md` — §2 gains v6, the write plane gains a "retrying a command safely" section with the four rules a caller needs, §7 gains 425 and the paragraph on why it is not 409, and the event catalog gains `champ.boss.killed`. - `PLAN.md` — §5.5 records that a per-KILL damage table needs no override even though a per-HIT event does, which is the observation the new kind rests on; §7 documents the key on the inbound frames. - `EVENTS.md` — §A row 7 and two §G capability rows close, and the failure table's "an action succeeds but the ack is lost" loses its "does not exist today". - `EVENTS_PLAN.md` — Phase 11 splits into 11a and 11b, and `installer` leaves the phase: PLAN.md §7.4 made sure no protocol version is hardcoded there, so the bundle gate is version-agnostic and needed no change for 5 either. The live walk's defect is written up in v6.md §6.1 because the naive check confirms it: an active ChampionSpawn registers an unnamed region over its own spawn area, so the innermost region containing a champion boss is guaranteed to have no name — and region registration is deferred, so a lookup at spawn time answers "Britain" while one at the kill does not. CRLF preserved throughout; links and anchors checked by hand, this repo has no CI. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
24
link/PLAN.md
24
link/PLAN.md
@@ -178,10 +178,16 @@ Payload per transition: house serial, `from`→`to` level, `X/Y/Z`, `Map`, `BanL
|
||||
| Player death | `PlayerDeath` | low | |
|
||||
| Murder | `PlayerMurdered` | low | High-signal for the website. |
|
||||
| Killer attribution | `OnKilledBy` | medium | `Killed` + `KilledBy`. Better than `PlayerDeath` for PvP feeds. |
|
||||
| Creature death | `CreatureDeath` | **high** | Every mob kill. Filter or aggregate. |
|
||||
| Creature death | `CreatureDeath` | **high** | Every mob kill. Filter or aggregate. **Protocol 6 uses it for `champ.boss.killed`**: the handler's first act is to decide this is not a champion, by type, which is one `is` check per death. |
|
||||
| **Damage table at a kill** | `Mobile.DamageEntries`, read inside `CreatureDeath` | — | Not a stream: the accumulated per-attacker totals, readable **only at the death**, since ServUO discards them with the creature. This is what makes a boss kill attributable without a per-hit event. |
|
||||
| Aggression | `AggressiveAction` | med-high | Per aggression state change, **not** per swing. |
|
||||
|
||||
> ⚑ **No per-hit damage event.** Damage numbers require overriding `Mobile.Damage` / weapon `OnHit`, not an EventSink.
|
||||
>
|
||||
> But per-*kill* attribution needs no override at all, and Protocol 6 is where that was noticed:
|
||||
> `Mobile.DamageEntries` already holds a running total per attacker, and a `CreatureDeath` handler
|
||||
> reads it before the creature is disposed of. A whole participation signal, for free, at the one
|
||||
> moment it is worth having.
|
||||
|
||||
### 5.6 Progression & activity
|
||||
|
||||
@@ -295,6 +301,22 @@ Newline-delimited JSON, one object per line, `serial` as the primary key.
|
||||
|
||||
Every inbound handler marshals to the Core thread before touching world state.
|
||||
|
||||
### Any inbound command may carry an `idempotencyKey` (Protocol 6)
|
||||
|
||||
```jsonc
|
||||
{"kind":"admin.broadcast","reqId":"r-17","actor":"event:412","text":"…","idempotencyKey":"5f2c…"}
|
||||
```
|
||||
|
||||
The shard executes a key **at most once** and answers a repeat with the original reply, restamped
|
||||
with the repeat's correlation id and marked `"replayed": true`. That is what makes a world-writing
|
||||
command safe to retry after a lost acknowledgement, and it is a precondition for every world verb
|
||||
the event system adds later — see [`v6.md`](v6.md) §2.1.
|
||||
|
||||
The gate sits in `BridgeBoot`'s inbound dispatch rather than in each handler, so it covers every
|
||||
kind including ones a later protocol adds. A command with no key behaves exactly as it did before.
|
||||
One new outbound kind comes with it, **`bridge.busy`**: a repeat arrived while the original is still
|
||||
in flight, nothing ran, come back.
|
||||
|
||||
### `server.hello` is per-connection, not per-boot
|
||||
|
||||
The sidecar restarts independently of the shard, so anything it needs up front must be re-sent on **every** connect. An earlier draft emitted `server.started` once at `EventSink.ServerStarted`; a sidecar that came up second never received it and had no idea which shard it was attached to.
|
||||
|
||||
Reference in New Issue
Block a user