feat(runtime): own character selection flow
This commit is contained in:
parent
6c4cd2bbc6
commit
0e82cbf700
20 changed files with 2396 additions and 33 deletions
|
|
@ -219,6 +219,9 @@ src/
|
|||
generation + teardown
|
||||
Session/ -> J2 canonical session lifetime, ordered
|
||||
inbound routing + retryable teardown
|
||||
RuntimeCharacterSelectionState.cs -> sole generation-scoped pre-world
|
||||
roster/highlight/delete/restore/error owner;
|
||||
borrowed view + ordered deltas + typed commands
|
||||
Entities/
|
||||
RuntimeEntityDirectory.cs -> sole GUID/incarnation/local-ID authority
|
||||
RuntimeEntityRecord.cs -> presentation-free accepted entity state
|
||||
|
|
|
|||
|
|
@ -467,7 +467,7 @@ useful ordering seam, but its ownership status is **partial**.
|
|||
| Area | Status | Current truth |
|
||||
|---|---|---|
|
||||
| Startup options | **Complete** | `RuntimeOptions` owns startup configuration (`eda936dc`). Remaining direct environment reads are legacy runtime diagnostics, not startup configuration. |
|
||||
| Network session | **Complete Runtime ownership** | `RuntimeLiveSessionController` owns the sole `WorldSession` generation and resolve/create/Connect/selection/EnterWorld/Tick/stop/reconnect/disposal transaction. Runtime route owners preserve exact inbound/outbound ordering and retryable teardown. App supplies immutable options, graphical/domain callbacks, and one borrowed inertable UI command projection—no mirrored session or reset plan (`75930787`). |
|
||||
| Network session | **Complete Runtime ownership** | `LiveSessionController` owns the sole `WorldSession` generation and resolve/create/Connect/pre-world selection/EnterWorld/Tick/stop/reconnect/disposal transaction. Its `RuntimeCharacterSelectionState` owns the full active roster (including greyed entries and retained wire slots), highlight, delete confirmation, restore/delete/error state, generation/lifecycle, borrowed view, ordered deltas, and typed commands. A selector-free graphical launch pauses on that owner; explicit and headless selection retain the established fallback. Runtime route owners preserve exact inbound/outbound ordering and retryable teardown. App supplies immutable options, graphical/domain callbacks, and borrowed projections—no mirrored session, selection state, or reset plan. |
|
||||
| World environment | **J6.1 complete Runtime ownership** | `RuntimeWorldEnvironmentState` owns the instance-scoped Dereth calendar, synchronized clock, weather progression/state, selected day group, AdminEnvirons state, and typed debug overrides. App converts immutable DAT sky definitions once and projects the borrowed Runtime snapshot into rendering; no process-global Region origin or second App clock/weather owner remains (`902076c0`). TS-54/TS-55 register the remaining centered UI sound and full fog/ambient/radar behavior gaps. |
|
||||
| Live identity/lifetime | **J3 complete** | `RuntimeEntityObjectLifetime` owns the sole `RuntimeEntityDirectory`, live `ClientObjectTable`, direct views, and ordered entity/object stream. The directory owns canonical GUID/incarnation/local-ID identity, accepted snapshots/timestamps, parent state, operation versions, and tombstones. `LiveEntityProjectionStore` owns App graphical sidecars by exact `RuntimeEntityKey`; hydration, presentation components, `GpuWorldState` residence/visibility, and retryable teardown preserve that key without another authority. Exact receipts precede fallible callbacks, re-entrant commits drain synchronously in sequence, and stable reset/disposal must converge the complete ledger to zero (`f46ddb5c`, `420e5eea`, `e937cc36`, `5ef8b537`, `ce3ac310`, `119b7c11`). |
|
||||
| Inbound/object-frame order | **Complete App orchestration** | `UpdateFrameOrchestrator` owns the complete typed host phase graph; `RetailInboundEventDispatcher`, `RetailLiveFrameCoordinator`, `LiveObjectFrameController`, `LiveSpatialPresentationReconciler`, streaming/input/teleport/player-mode/camera owners preserve the accepted order. `GameWindow.OnUpdate` is one profiler-scoped handoff (`e91f3102`). |
|
||||
|
|
|
|||
109
docs/research/2026-08-14-la7b-character-selection-runtime.md
Normal file
109
docs/research/2026-08-14-la7b-character-selection-runtime.md
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# LA7b character-selection Runtime evidence
|
||||
|
||||
Date: 2026-08-14
|
||||
|
||||
This note records the retail evidence and the presentation-independent state
|
||||
and flow implemented by Campaign LA slice LA7b. The retained character screen
|
||||
is deliberately deferred to LA8.
|
||||
|
||||
## Named-retail evidence
|
||||
|
||||
The implementation was derived from
|
||||
`docs/research/named-retail/acclient_2013_pseudo_c.txt` before code was
|
||||
written. The controlling functions are:
|
||||
|
||||
- `gmCharacterManagementUI::ResetPreviouslySelectedCharacterSlot`
|
||||
(`0x004ebff0`): clears the persisted selected avatar, selected guid, list
|
||||
index, and character-generation slot.
|
||||
- `gmCharacterManagementUI::SelectCharacter` (`0x004ec160`): resolves the
|
||||
clicked guid back to the `CharacterSet` slot and persists that exact slot.
|
||||
- `gmCharacterManagementUI::UpdateButtons` (`0x004ec240`): a missing or
|
||||
greyed selection disables Enter and Delete; a selected greyed character
|
||||
hides Delete and shows Restore.
|
||||
- `gmCharacterManagementUI::RebuildCharacterList` (`0x004ec3a0`): emits every
|
||||
active `CharacterSet` entry, records the previous/slot selection, chooses
|
||||
the first non-greyed entry as the ordinary fallback (the first row remains
|
||||
the all-grey fallback), sorts names with `wcscmp`, and then moves greyed
|
||||
rows to the tail without losing their identity.
|
||||
- `gmCharacterManagementUI::MakeDeleteCharacterConfirmationDialog`
|
||||
(`0x004ecca0`) and `CloseDeleteCharacterDialog` (`0x004ed4a0`): deletion is
|
||||
modal, and confirmation sends the persisted selected avatar.
|
||||
- `gmCharacterManagementUI::ListenToElementMessage` (`0x004ed5a0`): Restore
|
||||
sends the selected guid, Enter is accepted from the button and list-row
|
||||
activation, and both delete/restore open retail's wait dialog.
|
||||
- `gmCharacterManagementUI::EnterGame` (`0x004ed440`): entry is allowed only
|
||||
for a non-zero selected guid whose `GetGreyedOutFor(GetSlot(guid))` value is
|
||||
zero.
|
||||
- `CharacterSet::GetSlot` (`0x004fdf60`) and `GetGreyedOutFor`
|
||||
(`0x004fdfa0`) prove that delete/enter retain the original wire slot even
|
||||
after display sorting, and that any non-zero grey value is disabled.
|
||||
- `CPlayerSystem::Handle_CharacterError` (`0x0055d5d0`) clears the ready and
|
||||
awaiting-logon latches before forwarding the typed error. The downstream
|
||||
`gmUIFlow::RecvNotice_CharacterError` (`0x0047a7c0`) bounds its real display
|
||||
switch to enum values below `CHAR_ERROR_NUM_ERRORS`; the `0x19` member is a
|
||||
count sentinel, not a message.
|
||||
|
||||
Equivalent state pseudocode:
|
||||
|
||||
```text
|
||||
on roster:
|
||||
remember selected guid
|
||||
rows = every active CharacterSet entry, retaining original slot
|
||||
fallback = first row, replaced by first non-greyed row in wire order
|
||||
sort rows by ordinal/wcscmp name
|
||||
stable-partition non-greyed before greyed
|
||||
selected = previous guid if still present, otherwise fallback
|
||||
|
||||
buttons(selected):
|
||||
none or greyed -> Enter disabled, Delete disabled
|
||||
greyed -> Delete hidden, Restore visible/enabled
|
||||
active -> Delete visible/enabled, Restore hidden
|
||||
|
||||
enter(selected):
|
||||
require guid != 0 and grey == 0
|
||||
use retained wire slot through the existing EnterWorld transaction
|
||||
```
|
||||
|
||||
The `CharacterList.DeletedCharacters` array is not projected into this list:
|
||||
retail rebuilds from the active `CharacterSet`; pending-delete identities are
|
||||
the greyed entries in that active set. No entry is filtered merely because it
|
||||
is greyed.
|
||||
|
||||
## Runtime ownership and flow
|
||||
|
||||
`GameRuntime` owns one `LiveSessionController`, which owns one
|
||||
`RuntimeCharacterSelectionState` for the exact `WorldSession` generation.
|
||||
The owner contains the canonical roster, wire slots, highlight, delete
|
||||
confirmation, delete/restore progress, mapped error, revision, lifecycle, and
|
||||
generation. App receives only the borrowed `IRuntimeCharacterSelectionView`;
|
||||
all mutation uses synchronous generation-gated typed commands. Ordered deltas
|
||||
use one monotonic sequence and preserve re-entrant publication order while
|
||||
isolating observer failures.
|
||||
|
||||
A graphical launch with no explicit selector sets
|
||||
`AwaitCharacterSelection`. Connect reports and adopts the roster, starts the
|
||||
sole pre-world receive loop, and returns `AwaitingCharacterSelection` without
|
||||
calling EnterWorld. A typed Enter command continues the same session and
|
||||
generation through the established two-phase EnterWorld path. Explicit
|
||||
graphical selectors and direct/headless callers retain the established
|
||||
first-available fallback because the new flag defaults to false.
|
||||
|
||||
Immediate/headless entry keeps the original blocking receive/sweep pump until
|
||||
ServerReady. Only a host that actually pauses at character selection starts
|
||||
the asynchronous pre-world receiver. This preserves reliable-transport
|
||||
resend/NAK timing and avoids two concurrent socket readers.
|
||||
|
||||
LA7a's wire contracts are routed as follows:
|
||||
|
||||
- delete `0xF655`: account plus retained active slot, LoginQueue;
|
||||
- restore `0xF7D9`: selected guid, ControlQueue (AD-97 remains the recorded
|
||||
guid-only adaptation);
|
||||
- delete ack, restore `0xF643`, refreshed `CharacterList`, and
|
||||
`CharacterError 0xF659`: ordered UIQueue input.
|
||||
|
||||
ACE can silently return from restore for an unknown guid. Restore is therefore
|
||||
a fire-and-observe command: it installs no command gate and never waits for a
|
||||
reply. A later matching response updates the same owner; a fresh roster,
|
||||
entry attempt, reset, reconnect, or disposal disarms stale response
|
||||
correlation. `CharacterError.NumErrors` is ignored without a revision or
|
||||
delta and can never become presentation text.
|
||||
Loading…
Add table
Add a link
Reference in a new issue