OnlineSession duck-typed a flow onError hook that does not exist, so pre-game
losses never propagated; a failed login left a half-dead flow in place, and
main.ts only routed in-game disconnects (to a dead lobby). Concurrent login
attempts (LOG IN, ACCOUNT SETTINGS and Enter all call onLogin) replaced each
other's flow, leaving char_create bound to a flow without MCP.
- OnlineSession.handleSessionLost is the single, idempotent end of a session:
disposes the game adapter and the flow (all sockets/timers), records the
reason, enters `disconnected` and notifies onSessionLost/onDisconnect once.
Flow loss events, D2GS adapter close, in-game client errors and disconnect()
all route through it.
- Pre-game operations (create/delete/convert/select character, list games,
enter game, lobby chat) run via lobbyOp: no session -> not_connected loss,
failure caused by the loss -> SessionLostError; status errors stay on screen.
- Reasons use ENG 1.13c tbl text plus the technical detail: string#5352
(BNCS lost), string#5357 (realm/game interrupted), string#5349 (timeout),
string#5353 / patchstring#1066 (connect failed), string#5351 (not
connected).
- main.ts: one onSessionLost handler -> returnToLoginAfterSessionLoss (keeps
the typed account name, clears roster/game state); catch blocks ignore
SessionLostError; onLogin allows one attempt at a time; no bnet_lobby
routing for dead sessions. The login screen wraps long reasons.
TAG=agy
CONV=109a3012-103a-42f0-8ae1-bbc0857047ef