acdream/tests/AcDream.Runtime.Tests/Session
Erik d233f81dce feat(session): the in-world logoff — LogOut animation, reverse wormhole, live return to character select
Retires AD-74 (Exit to Character Selection 'behaves as Exit Game') and
files AD-110 (the composed handoff edge) — register rows in this commit.

Retail derivation (named decomp):
- gmGamePlayUI::UseTime @0x004EA3A0: confirmed Yes drains into
  CPlayerSystem::LogOffCharacter(0) when grounded (transient_state &
  CONTACT); the grounded three-way branch now also covers the
  indicator-bar end-session control (it was Options-only).
- CPlayerSystem::LogOffCharacter @0x00563520: SaveToServer FIRST (the
  existing pre-logoff flush hook), then RequestLogOff @0x00562DD0:
  'Logging off...' chat (type 0), 0xF653 via Proto_UI::LogOffCharacter
  @0x00546A20, logOffRequestTime = now + 3.0 (+20.0 when
  IsPlayerKiller @0x0058C910 — PWD bits 0x20|0x2000000), and
  CommandInterpreter::HandleLogOff @0x006B3330 -> Disable.
- The log-off ANIMATION is server-driven: ACE broadcasts
  MotionCommand.LogOut (0x1000011E, Player.cs:596 SendMotionAsCommands)
  and it plays on the local player through the existing inbound
  unpack_movement funnel during the 3 s hold — retail plays nothing
  locally; Disable() is the whole client-side effect.
- gmSmartBoxUI::UseTime @0x004D6E64: hold elapsed ->
  BeginTeleportAnimation(TAS_WORLD_FADE_OUT) @0x004D6E83 (enter cue
  @0x004D638E, unconditional) -> TunnelFadeIn -> Tunnel. The tunnel
  plays the SAME forward 40 fps animation; nothing renders backwards,
  and NO exit cue ever fires on logout (the char-select swap preempts
  the TunnelContinue/FadeOut tail).
- Inbound 0xF653 echo (dispatch case 3 @0x0055C963) ->
  ExecuteLogOff @0x0055D780: world teardown with the LOGON CONNECTION
  KEPT (ExitWorldDisconnect @0x00541E00 removes every connection
  except logonRecID_ — one connection against ACE) and
  Proto_UI::SetEventCounter(0) @0x00541E79; the fresh CharacterList in
  the same batch re-shows character management (gmGamePlayUI::Update
  @0x004E9CD0 -> QueueUIMode(0x1000000a)). ACE mirrors it:
  SendFinalLogOffMessages (Session.cs:249) sends 0xF653 + CharacterList
  + ServerName >=6 s after the request and leaves the session
  AuthConnected — a second EnterWorld needs no re-handshake.

Implementation:
- RuntimeWorldTransitState: the canonical logout lifecycle
  (Requested/PresentationActive/Confirmed, retail 3 s/+20 s holds,
  cancel/reset/ownership convergence).
- WorldSession: RequestCharacterLogOff (non-blocking 0xF653),
  IsCharacterLogOffConfirmed, ReturnToCharacterSelect (InWorld ->
  InCharacterSelect + game-action sequence reset; transport untouched).
- LiveSessionController: BeginCharacterLogOff (flush-first request) and
  CompleteCharacterLogOff — the return-to-selection transaction
  (ReconnectCore minus the transport swap: retire the world
  generation's routes, host reset, state flip, fresh generation
  re-bind, roster re-applied from the pushed CharacterList; failures
  degrade to the full StopCore teardown).
- RuntimeLocalPlayerMovementState.DisableCommandInterpreter +
  DispatcherMovementInputSource gate: retail's Disable() — held keys
  produce no movement while the server LogOut motion plays; cleared by
  the generation reset.
- LocalPlayerTeleportController: the logout pump as the third arm of
  the one wormhole machine (request/hold/wormhole/confirmed handoff;
  teleport starts refused during logout; the handoff runs the session
  transaction whose world reset retires the tunnel as the fresh
  selection state re-shows the character screen).
- UI: both end-session surfaces share the retail three-way grounded
  gate and now run the REAL flow; Options' Exit Game keeps the app
  exit (window close -> the existing graceful-shutdown logoff).

Tests: +5 transit lifecycle, +4 session transaction, +7 logout pump.
Runtime 1756/0 (baseline 1747), App live-DAT 5523/3 (baseline 5512/3
+ 11 this round), Core.Net 1004/0, full solution green (0 failures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 14:02:40 +02:00
..
DirectGameRuntimeCommandAdapterTests.cs docs: Campaign LA — pinned launch-contract schema COMMITTED into plan LA1 2026-08-14 16:04:32 +02:00
LiveSessionControllerCharacterCreationTests.cs fix(chargen): Campaign CC CC7 review fix round — F1-F9 — REVIEW-CLOSED 2026-08-16 03:13:02 +02:00
LiveSessionControllerTests.cs feat(session): the in-world logoff — LogOut animation, reverse wormhole, live return to character select 2026-08-17 14:02:40 +02:00
LiveSessionEventRouterTests.cs fix(net,runtime): FA2 fix-round SHOULD-FIX -- fellowship mechanism parity, lookup reuse, router test, checkpoint defaults 2026-08-12 02:17:56 +02:00
LiveSessionHostTests.cs feat(runtime): share chat commands and run login sequence 2026-08-14 20:27:45 +02:00
LiveSessionLifecycleHostTests.cs feat(app): Campaign CC slice CC4 — chargen screen shell + Heritage/Profession/Skills/Town pages 2026-08-15 17:45:51 +02:00
LiveSessionSubscriptionSetTests.cs refactor(runtime): move session lifetime and ordered transport 2026-07-25 19:39:24 +02:00
RuntimeAcceptedPositionDriveControllerTests.cs docs: Campaign LA — pinned launch-contract schema COMMITTED into plan LA1 2026-08-14 16:04:32 +02:00
RuntimeCharacterSelectionStateTests.cs fix(ui,net): Campaign LA gate round 2 — char-select exit confirmation, authored row justify, world name 2026-08-15 11:50:45 +02:00
RuntimeLiveEntitySessionControllerTests.cs fix(ui): night-round review — F2 HouseQuery fires at login, not tab-open 2026-08-17 04:28:46 +02:00
RuntimeLiveSessionNoWindowTests.cs docs: Campaign LA — pinned launch-contract schema COMMITTED into plan LA1 2026-08-14 16:04:32 +02:00
RuntimeRemotePlacementDriveControllerTests.cs fix(physics): C4 route 5 — projectile authoritative placement (#276 partial) 2026-08-04 21:03:41 +02:00
SessionStatusWriterTests.cs CC2 review fix round: latch scope narrowed, AD-100, creationFailed reason key 2026-08-15 13:10:24 +02:00
TurbineChatDisplayNamesTests.cs feat(chat): Campaign CH slice CH1 — retail LogTextType color table 2026-08-09 15:24:09 +02:00