fix(ui): night-round review — F2 HouseQuery fires at login, not tab-open
CM_House::Event_QueryHouse @0x006aaa00 (opcode 0x21e) tail-calls
unconditionally from the end of CPlayerSystem::InitializePlayer
@0x00563570 — the same once-per-session function
AttemptSendLoginCompleteNotification lives in (both guarded by the
player_initialized flag), right after that notification. Retail never
sends it from gmHouseUI::PostInit or gmMapUI::PostInit on House-tab
activation.
Moved WorldSession.SendHouseQuery() to the direct (non-portal)
first-entry completion edges — the same places acdream already sends
the analogous "initial session bootstrap" LoginComplete:
- graphical: LiveSessionRuntimeFactory's RuntimeFirstEntryDriveController
localPlayerCompleted callback
- headless: HeadlessSessionHost's equivalent callback
- headless content-less direct host: RuntimeLiveEntitySessionController.OnSpawned
Portal-space re-entries (LocalPlayerTeleportController's F751 path,
RuntimeLiveEntitySessionController.TryAdvancePortalCompletion) do NOT
resend it, matching retail's single-shot guard.
Removed the invented House-tab-open -> SendHouseQuery trigger
(InteractionRetainedUiComposition's HouseShown binding) and retired
register row AD-107, which had documented that adaptation.
Updated RuntimeLiveEntitySessionControllerTests' exact game-action
assertions for the content-less path, which now also captures the
HouseQuery send alongside LoginComplete.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6ee3d88863
commit
353ae3bb0c
8 changed files with 89 additions and 35 deletions
|
|
@ -978,22 +978,24 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
AllegianceSetUpdateSubscription: on =>
|
||||
late.GameRuntime.AllegianceSetUpdateSubscription(on),
|
||||
Trade: d.Runtime.Trade),
|
||||
// Batch C (overnight hover/UI round, 2026-08-17): HouseLines/
|
||||
// HouseShown now wired to the minimal RuntimeHouseState
|
||||
// owner (see its class doc) — HousePosition (the Map tab's
|
||||
// house marker) is deferred to #413's remaining owned-house
|
||||
// work, since it needs HouseData's Position field, not yet
|
||||
// consumed here.
|
||||
// Batch C (overnight hover/UI round, 2026-08-17): HouseLines
|
||||
// now wired to the minimal RuntimeHouseState owner (see its
|
||||
// class doc) — HousePosition (the Map tab's house marker) is
|
||||
// deferred to #413's remaining owned-house work, since it
|
||||
// needs HouseData's Position field, not yet consumed here.
|
||||
// Night-round review F2: the tab-open HouseShown ->
|
||||
// SendHouseQuery trigger (former AD-107) is REMOVED — retail
|
||||
// sends HouseQuery once, unconditionally, at
|
||||
// CM_House::Event_QueryHouse @0x006aaa00 (tail-called from
|
||||
// CPlayerSystem::InitializePlayer's login-complete path), not
|
||||
// on House-tab activation; neither gmHouseUI::PostInit nor
|
||||
// gmMapUI::PostInit sends one on tab-open. HouseShown now
|
||||
// defaults to null (HousePageController.OnShown's
|
||||
// _bindings.OnShown?.Invoke() no-ops).
|
||||
MapHouse: new MapHouseRuntimeBindings(
|
||||
CurrentCalendar: d.CurrentCalendar,
|
||||
PlayerCellId: () => d.PlayerController.Controller?.CellId ?? 0u,
|
||||
HouseLines: () => d.Runtime.HouseOwner.Lines,
|
||||
// Not a ported retail call site — neither gmHouseUI::
|
||||
// PostInit nor gmMapUI::PostInit sends an outbound
|
||||
// HouseQuery; this is the acdream "fire when the House
|
||||
// tab is shown" convenience HousePageController.Bindings.
|
||||
// OnShown's own doc already documents.
|
||||
HouseShown: () => late.Session.CurrentSession?.SendHouseQuery()),
|
||||
HouseLines: () => d.Runtime.HouseOwner.Lines),
|
||||
StackSplitQuantity: d.StackSplitQuantity,
|
||||
Plugins: d.UiRegistry,
|
||||
Persistence: persistence,
|
||||
|
|
|
|||
|
|
@ -335,7 +335,20 @@ internal sealed class LiveSessionRuntimeFactory
|
|||
_world.PlacementProjection,
|
||||
_world.PlacementRetries,
|
||||
_world.FirstEntryDrive,
|
||||
_ => session.SendGameAction(GameActionLoginComplete.Build()),
|
||||
_ =>
|
||||
{
|
||||
session.SendGameAction(GameActionLoginComplete.Build());
|
||||
// Night-round review F2: CM_House::Event_QueryHouse @0x006aaa00
|
||||
// is tail-called, unconditionally, from the end of
|
||||
// CPlayerSystem::InitializePlayer @0x00563570 — the SAME
|
||||
// once-per-session function AttemptSendLoginCompleteNotification
|
||||
// lives in (guarded by player_initialized), right after that
|
||||
// notification. This is the graphical host's direct
|
||||
// (non-portal) first-entry completion edge — the exact
|
||||
// analogue. Portal-space re-entries (LocalPlayerTeleportController)
|
||||
// do NOT resend it, matching retail's single-shot guard.
|
||||
session.SendHouseQuery();
|
||||
},
|
||||
_world.AcceptedPositionDrive,
|
||||
_world.RemotePlacementDrive);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,16 +31,19 @@ namespace AcDream.App.UI.Layout;
|
|||
/// <c>ParseUpdateRentTime</c>/<c>ParseUpdateRentPayment</c>,
|
||||
/// <c>GameEventWiring</c>'s four delegate holes, the outbound HouseQuery
|
||||
/// action). The House-tab ownership-text closer session (also 2026-08-17)
|
||||
/// wired <see cref="Bindings.Lines"/>/<see cref="Bindings.OnShown"/> to the
|
||||
/// minimal <c>RuntimeHouseState</c> owner and ported
|
||||
/// <c>DisplayPurchaseTimeText @0x004a3110</c>'s expired branch — a fresh
|
||||
/// houseless character's House tab now shows the single decomp-verified
|
||||
/// line "You may buy another house immediately." after the tab is opened,
|
||||
/// wired <see cref="Bindings.Lines"/> to the minimal <c>RuntimeHouseState</c>
|
||||
/// owner and ported <c>DisplayPurchaseTimeText @0x004a3110</c>'s expired
|
||||
/// branch — a fresh houseless character's House tab shows the single
|
||||
/// decomp-verified line "You may buy another house immediately.",
|
||||
/// live-connected-gate-verified (screenshot + structural UI-tree dump
|
||||
/// against the real <c>+Acdream</c> character on a local ACE server). The
|
||||
/// other six <c>Display*</c> line builders <c>DisplayHouseData</c> calls
|
||||
/// (owned-house-only content: buy/rent payments and times, location,
|
||||
/// warning text) remain unported — ISSUES #413's surviving scope.
|
||||
/// warning text) remain unported — ISSUES #413's surviving scope. The
|
||||
/// night-round review (F2, 2026-08-17) moved the outbound HouseQuery send
|
||||
/// from a House-tab-open trigger to retail's real login-complete edge (see
|
||||
/// <see cref="Bindings.OnShown"/>'s own doc), so by the time a player opens
|
||||
/// the House tab the data has usually already arrived.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class HousePageController
|
||||
|
|
@ -49,12 +52,20 @@ public sealed class HousePageController
|
|||
|
||||
public sealed record Bindings(
|
||||
Func<IReadOnlyList<string>> Lines,
|
||||
// Fires once when the page transitions to visible — the seam that
|
||||
// WILL send the outbound HouseQuery (0x021E, already implemented as
|
||||
// WorldSession.SendHouseQuery) once a caller wires OnShown to it —
|
||||
// see ISSUES #413. NOT a ported retail call site (PostInit never
|
||||
// triggers a query) — an acdream convention, documented as such
|
||||
// (recon doc open item).
|
||||
// Fires once when the page transitions to visible. Night-round
|
||||
// review F2 (2026-08-17): NOT wired to SendHouseQuery any more —
|
||||
// retail's HouseQuery (0x021E, CM_House::Event_QueryHouse
|
||||
// @0x006aaa00) is byte-decoded confirmed to fire exactly once at the
|
||||
// client's login-complete edge (tail-called, unconditionally, from
|
||||
// CPlayerSystem::InitializePlayer @0x00563570, right after
|
||||
// AttemptSendLoginCompleteNotification — both guarded by the SAME
|
||||
// once-per-session player_initialized flag), never from House-tab
|
||||
// activation; neither gmHouseUI::PostInit nor gmMapUI::PostInit
|
||||
// sends one on tab-open. See WorldSession.SendHouseQuery's own
|
||||
// production call sites (the graphical/headless first-entry-
|
||||
// completion edges) for where it's actually sent now. This hook
|
||||
// remains available for a genuinely page-shown concern, but no
|
||||
// current caller wires it.
|
||||
Action? OnShown = null,
|
||||
// Batch C House-ownership-text closer (2026-08-17): the ListBox's
|
||||
// OWN row template (LayoutDesc 0x21000025 element 0x100001E7,
|
||||
|
|
|
|||
|
|
@ -73,11 +73,16 @@ public sealed class MapHousePanelController : IRetainedPanelController
|
|||
_map = map;
|
||||
_house = house;
|
||||
|
||||
// House's outbound query is a fire-when-shown convenience (see
|
||||
// HousePageController.Bindings.OnShown's own doc — not a ported
|
||||
// retail trigger, an acdream one), gated the same "window shown AND
|
||||
// my tab active" conjunction the social panel's Fellowship/
|
||||
// Allegiance pages use for their own declarations.
|
||||
// Night-round review F2 (2026-08-17): this used to be House's
|
||||
// outbound-HouseQuery fire-when-shown trigger; that trigger is
|
||||
// REMOVED (former register row AD-107) now that HouseQuery is
|
||||
// byte-decoded confirmed to fire once at retail's login-complete
|
||||
// edge instead (see HousePageController.Bindings.OnShown's own
|
||||
// updated doc). The "window shown AND my tab active" plumbing stays
|
||||
// — it mirrors the social panel's Fellowship/Allegiance page-shown
|
||||
// convention and remains available for a genuinely page-shown
|
||||
// concern — but HousePageController.Bindings.OnShown is unwired
|
||||
// (null) in production, so this call chain is currently a no-op.
|
||||
_onActivePageChanged = (_, _) => FireHouseShownIfActive();
|
||||
_tabPanel.ActivePageChanged += _onActivePageChanged;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1266,6 +1266,14 @@ internal sealed class HeadlessSessionHost : IDisposable
|
|||
// RuntimeLiveEntitySessionController's onLoginCompleteSent
|
||||
// doc for the other two.
|
||||
_optionsSeeder?.NoteLoginCompleteSent();
|
||||
// Night-round review F2: CM_House::Event_QueryHouse
|
||||
// @0x006aaa00 tail-calls unconditionally from the end of
|
||||
// CPlayerSystem::InitializePlayer — the same once-per-session
|
||||
// function AttemptSendLoginCompleteNotification lives in
|
||||
// (guarded by player_initialized) — right after that
|
||||
// notification. This is the headless direct (non-portal)
|
||||
// first-entry completion edge.
|
||||
session.SendHouseQuery();
|
||||
},
|
||||
_acceptedPositionDrive);
|
||||
_eventRoute = eventRoute;
|
||||
|
|
|
|||
|
|
@ -198,6 +198,14 @@ public sealed class RuntimeLiveEntitySessionController
|
|||
// truthful terminal admission edge.
|
||||
_initialLoginCompleteSent = true;
|
||||
_session.SendGameAction(GameActionLoginComplete.Build());
|
||||
// Night-round review F2: CM_House::Event_QueryHouse
|
||||
// @0x006aaa00 tail-calls unconditionally from the end of
|
||||
// CPlayerSystem::InitializePlayer — the same once-per-session
|
||||
// function AttemptSendLoginCompleteNotification lives in
|
||||
// (guarded by player_initialized) — right after that
|
||||
// notification. This is the content-less direct host's own
|
||||
// terminal admission edge, its equivalent moment.
|
||||
_session.SendHouseQuery();
|
||||
_onLoginCompleteSent?.Invoke();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue