diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 4a8cd1e9..b0021167 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -24,6 +24,41 @@ What does NOT go here: - Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending. - Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed. +## #390 — UI windows stranded off-screen when the resolution shrinks (no retail reposition/clamp on display change) + +**Status:** OPEN — filed 2026-08-13 (user gate report: "If I go from a high +resolution to a low, the GUI will be outside of the screen and I have to +resize the window to get it"). Floating retail-UI windows keep absolute +pixel positions across resolution changes; a panel parked at x=2000 on a +2560-wide window is unreachable after a pick down to 1280. Retail keeps +windows reachable across display changes — the exact retail mechanism +(clamp-into-bounds vs proportional reposition vs per-resolution layout +sets) must be pulled from the named decomp (UIElementManager / +UIRegionManager display-change handling) BEFORE implementing; do not +invent a clamp rule. Part of the display work block with #376/#377/#388/ +#389. + +## #389 — World-camera FOV is an invented aspect-independent constant; retail is SmartboxFOV (vFOV = gameFOV / (aspect − 0.1)) + +**Status:** OPEN — filed 2026-08-13 (user gate report: "meant to run on an +old aspect ratio... modern screens feels weird... some resolutions feels +like it is just squished"). Decomp-verified retail law: +`Render::SetFOVRad(SmartBox::m_fGameFOV / (RenderDevice::m_ViewportAspectRatio − 0.1))` +(two sites, `0x00452b2f` and `0x00453b14`; the same expression feeds a +`tan` at `0x00451c0e`), with `m_fGameFOV` defaulting to **π/2 = 90°** +(`0x00454649`) and set in DEGREES by the Field of View option +(`× 0.0174533`, `0x00451e6a`). Net effect: retail holds the HORIZONTAL +view roughly constant (~85–90°) across aspect ratios and narrows the +vertical FOV on wide screens (16:9 → vFOV ≈ 53.6°, 4:3 → ≈ 73°). acdream +instead hardcodes `ChaseCamera.FovY = π/3 = 60°` (FlyCamera likewise) +with no aspect coupling and no retail anchor — wider aspects balloon the +horizontal view and every aspect gets a different feel than retail. +Fix: port the smartbox formula into the world cameras' aspect path +(recompute applied vFOV on every SetAspect), drive `gameFOV` from the +Config tab's Field of View value in retail's degree mapping (default 90), +and delete the π/3 constant. Register row AD-89 tracks the divergence +until the port lands. + ## #388 — CRASH: unhandled GlfwException "Failed to set video mode: Graphics mode not supported" during a fullscreen-state resolution/settings apply; and fullscreen/maximized windows silently ignore resolution picks **Status:** OPEN — filed 2026-08-13 from the user's live gate session diff --git a/docs/architecture/retail-divergence-register.md b/docs/architecture/retail-divergence-register.md index 85368629..fd048f6a 100644 --- a/docs/architecture/retail-divergence-register.md +++ b/docs/architecture/retail-divergence-register.md @@ -62,7 +62,7 @@ accepted-divergence entries (#96, #49, #50). --- -## 2. Adaptation (AD) — 68 active rows (AD-88 filed 2026-08-13 at the #385 dropdown fix — the vendor category dropdown keeps G5's fixed 6-row scrollable window although its authored popup ListBox is edge-docked, the condition that arms retail's `RecalculatePopupSize` size-to-content resize; classification UNCLEAR pending a retail side-by-side (ISSUES #386); AD-87 filed 2026-08-12 at Campaign FA slice FA6 — the allegiance-swear half of the two-bot headless gate is written+wired but `AllegianceGateEnabled=false` (disabled by default), unverified end-to-end over the wire because ACE returns nothing to the `0x001D` swear (ISSUES #384); the FELLOWSHIP two-session gate passed live and ships as FA6's automated proof; AD-86 filed 2026-08-12 at Campaign FA slice FA5, item 4 — ACE's deliberate zeroing of officers/officer titles/MOTD/MOTD-set-by/name-last-set-time/lock/approved-vassal/timeOnline/allegianceAge, dropped past acdream's own parse layer to match retail's own no-widget presentation; AD-85 filed 2026-08-12 at Campaign FA slice FA5 — the Allegiance page's numeric-only fields and its three local confirmation dialogs' unsubstituted-verbatim-or-bare-name text, the same unported `StringInfo` gap AD-81 filed for Fellowship; AD-84 filed 2026-08-12 at Campaign FA slice FA5 — the Swear button's missing "target is a player" gate, the same class as AD-83's Recruit-button gap; AD-83 filed 2026-08-12 at the Campaign FA slice FA4 fix round (mechanism MUST-FIX 5) — the Recruit button's missing "target is a player" gate, previously an inline comment not a row; AD-82 filed 2026-08-12 at the Campaign FA slice FA4 fix round (mechanism MUST-FIX 4/5) — the invented leader-tint/selection-tint colors, the name-text-only row click target, and the page-local (not generic-`UiTemplateListBox`) world→panel selection sync; AD-81 filed 2026-08-12 at Campaign FA slice FA4 — the fellowship roster/create-flow text-composition gap (unported `StringInfo` variable substitution + `ACCharGenData::FormatName`); AD-80 filed 2026-08-12 at Campaign FA slice FA4, D5 — the panel's retail-exact XP-share percentage display versus the currently-targeted ACE server's slightly different actual grant; AD-79 filed 2026-08-12 at Campaign FA slice FA3, D1 — the social panel's Friends/Squelch page action buttons (add/remove friend, appear offline, squelch add/remove/clear) are honest INERT, no wire implemented this campaign; AD-78 filed 2026-08-11 at Campaign OP's gate-2 follow-up (user-directed, verbatim "mark all options that are not implemented now, so I can clearly see what is not implemented") — the shared store-only-caption-dimming convention across the Character/Config option tabs and Configure Keyboard; AD-77 filed 2026-08-11 at the Campaign OP OP3 review-fix round — the client-wide floating-only `gmPanelUI` host divergence (retail also exposes a docked `0x21000017` host) the plan's §5 delegated to the OP3 dual review, scoped to every main panel not just Options; AD-76/AD-75/AD-74 filed 2026-08-11 at Campaign OP slice OP3 — the Options panel's Exit to Character Selection "behaves as Exit Game" adaptation (D6), the Urgent Assistance/Report Abuse dead-URL interface-text short-circuit (D5), and In-Game Help Files' asset-missing inert button (D5); AD-73 filed 2026-08-11 at the Campaign OP OP2 rework — `UiTabPanel`'s dormant-until-`ActivateTabBehavior()` activation model, replacing retail's unconditional per-instance tab-table wiring, so the four already-shipped Type-8 hosts keep their existing controller-owned switching without a double-driver race; AD-72 filed 2026-08-08 at the Slice 5.3 review corrections — `VendorPricing`'s double-precision narrowing versus retail's x87 extended precision, same class as AD-33; AD-65 RETIRED and AD-69 FILED 2026-08-07 at Campaign S S4 — the away-arm now snaps per retail @0x00509c50, while AD-66's byte-confirmed sibling landing is WITHHELD pending #341's measurement-anomaly apparatus, and AD-69 records the seam-frame dist gap the same pass discovered; AD-56 RESTORED 2026-08-07 — the a8a7d64b revert had collaterally DELETED it, the inverse of the AD-55 zombie it also created; its plumb-fall-freeze condition is live again since TS-4’s real retirement at Slice 2B; AD-55 RE-RETIRED 2026-08-07 — its 2026-07-30 retirement at 252e8068 was collaterally resurrected by the a8a7d64b revert of the unrelated TS-4 commit; the code kept the cos(10°) fix throughout; AD-68 filed 2026-08-07 at the #338 closure — the async-residency placeholder mover shape (0.4/0.4 steps + capsule) has no retail counterpart because retail loads synchronously; AD-67 filed 2026-08-07 at the #32 closeout — the narrowed `SetContactPlane` keeps its per-write `ContactPlaneCellId`, which retail writes only at `init_contact_plane`; AD-49 filed 2026-08-06 at the #334 fix — the BSP part-array flood runs its outdoor cell rectangle at seed time rather than only from retail’s residency-gated walk, keeping both registration floods on one residency rule; AD-64 filed 2026-08-05 at the C5b architecture review's D1 fix — AD-60's W2 wire-cell REACHABILITY decision is expressed once per host because the two hosts run parallel non-shared inbound routes; the committed VALUE is single-sourced at `RuntimeEntityObjectLifetime.CommitWireCellRebucket`, and unification is filed as #324; AD-60 CORRECTED the same day — its surviving-channel enumeration presented "the local force path, the missile arm" as exhaustive when the entire no-window host belonged in it; AD-1 RETIRED 2026-08-05, C5a deletion sweep — the legacy outdoor demote/restore lift this row described was `PhysicsEngine.Resolve`'s own body, deleted with zero production callers; AD-42 DELETED 2026-08-04, C4 route 3 — its last surviving citation, the headless portal-arrival resync's two-call Resolve/ResolvePlacement split, was retired by the canonical `RuntimeAcceptedPositionDriveController` portal arm; AD-2 amended same route with the deferred-place timing adaptation, the T8 tolerated-overwrite note, and the leash-anchor nuance; AD-63 filed 2026-08-04, cancelled-park presentation rollback — the rollback restores every presentation registration the park's Withdraw removed EXCEPT the player's selection, which is user intent rather than a projection; AD-62 filed 2026-08-03, C4 route 2 round 2 — a deferred ForcePosition retired without committing is not re-applied and its ack is not sent; AD-61 filed 2026-08-02, C3c review round 1 — the #270 settle compression now covers the local player; AD-59/AD-60 filed 2026-08-02, continuation-executor slice) +## 2. Adaptation (AD) — 69 active rows (AD-89 filed 2026-08-13 at the display gate session — the world cameras' invented aspect-independent 60° vertical FOV versus retail's SmartboxFOV law `gameFOV/(aspect−0.1)` with 90° default set in degrees by the Field of View option (ISSUES #389, port planned); AD-88 filed 2026-08-13 at the #385 dropdown fix — the vendor category dropdown keeps G5's fixed 6-row scrollable window although its authored popup ListBox is edge-docked, the condition that arms retail's `RecalculatePopupSize` size-to-content resize; classification UNCLEAR pending a retail side-by-side (ISSUES #386); AD-87 filed 2026-08-12 at Campaign FA slice FA6 — the allegiance-swear half of the two-bot headless gate is written+wired but `AllegianceGateEnabled=false` (disabled by default), unverified end-to-end over the wire because ACE returns nothing to the `0x001D` swear (ISSUES #384); the FELLOWSHIP two-session gate passed live and ships as FA6's automated proof; AD-86 filed 2026-08-12 at Campaign FA slice FA5, item 4 — ACE's deliberate zeroing of officers/officer titles/MOTD/MOTD-set-by/name-last-set-time/lock/approved-vassal/timeOnline/allegianceAge, dropped past acdream's own parse layer to match retail's own no-widget presentation; AD-85 filed 2026-08-12 at Campaign FA slice FA5 — the Allegiance page's numeric-only fields and its three local confirmation dialogs' unsubstituted-verbatim-or-bare-name text, the same unported `StringInfo` gap AD-81 filed for Fellowship; AD-84 filed 2026-08-12 at Campaign FA slice FA5 — the Swear button's missing "target is a player" gate, the same class as AD-83's Recruit-button gap; AD-83 filed 2026-08-12 at the Campaign FA slice FA4 fix round (mechanism MUST-FIX 5) — the Recruit button's missing "target is a player" gate, previously an inline comment not a row; AD-82 filed 2026-08-12 at the Campaign FA slice FA4 fix round (mechanism MUST-FIX 4/5) — the invented leader-tint/selection-tint colors, the name-text-only row click target, and the page-local (not generic-`UiTemplateListBox`) world→panel selection sync; AD-81 filed 2026-08-12 at Campaign FA slice FA4 — the fellowship roster/create-flow text-composition gap (unported `StringInfo` variable substitution + `ACCharGenData::FormatName`); AD-80 filed 2026-08-12 at Campaign FA slice FA4, D5 — the panel's retail-exact XP-share percentage display versus the currently-targeted ACE server's slightly different actual grant; AD-79 filed 2026-08-12 at Campaign FA slice FA3, D1 — the social panel's Friends/Squelch page action buttons (add/remove friend, appear offline, squelch add/remove/clear) are honest INERT, no wire implemented this campaign; AD-78 filed 2026-08-11 at Campaign OP's gate-2 follow-up (user-directed, verbatim "mark all options that are not implemented now, so I can clearly see what is not implemented") — the shared store-only-caption-dimming convention across the Character/Config option tabs and Configure Keyboard; AD-77 filed 2026-08-11 at the Campaign OP OP3 review-fix round — the client-wide floating-only `gmPanelUI` host divergence (retail also exposes a docked `0x21000017` host) the plan's §5 delegated to the OP3 dual review, scoped to every main panel not just Options; AD-76/AD-75/AD-74 filed 2026-08-11 at Campaign OP slice OP3 — the Options panel's Exit to Character Selection "behaves as Exit Game" adaptation (D6), the Urgent Assistance/Report Abuse dead-URL interface-text short-circuit (D5), and In-Game Help Files' asset-missing inert button (D5); AD-73 filed 2026-08-11 at the Campaign OP OP2 rework — `UiTabPanel`'s dormant-until-`ActivateTabBehavior()` activation model, replacing retail's unconditional per-instance tab-table wiring, so the four already-shipped Type-8 hosts keep their existing controller-owned switching without a double-driver race; AD-72 filed 2026-08-08 at the Slice 5.3 review corrections — `VendorPricing`'s double-precision narrowing versus retail's x87 extended precision, same class as AD-33; AD-65 RETIRED and AD-69 FILED 2026-08-07 at Campaign S S4 — the away-arm now snaps per retail @0x00509c50, while AD-66's byte-confirmed sibling landing is WITHHELD pending #341's measurement-anomaly apparatus, and AD-69 records the seam-frame dist gap the same pass discovered; AD-56 RESTORED 2026-08-07 — the a8a7d64b revert had collaterally DELETED it, the inverse of the AD-55 zombie it also created; its plumb-fall-freeze condition is live again since TS-4’s real retirement at Slice 2B; AD-55 RE-RETIRED 2026-08-07 — its 2026-07-30 retirement at 252e8068 was collaterally resurrected by the a8a7d64b revert of the unrelated TS-4 commit; the code kept the cos(10°) fix throughout; AD-68 filed 2026-08-07 at the #338 closure — the async-residency placeholder mover shape (0.4/0.4 steps + capsule) has no retail counterpart because retail loads synchronously; AD-67 filed 2026-08-07 at the #32 closeout — the narrowed `SetContactPlane` keeps its per-write `ContactPlaneCellId`, which retail writes only at `init_contact_plane`; AD-49 filed 2026-08-06 at the #334 fix — the BSP part-array flood runs its outdoor cell rectangle at seed time rather than only from retail’s residency-gated walk, keeping both registration floods on one residency rule; AD-64 filed 2026-08-05 at the C5b architecture review's D1 fix — AD-60's W2 wire-cell REACHABILITY decision is expressed once per host because the two hosts run parallel non-shared inbound routes; the committed VALUE is single-sourced at `RuntimeEntityObjectLifetime.CommitWireCellRebucket`, and unification is filed as #324; AD-60 CORRECTED the same day — its surviving-channel enumeration presented "the local force path, the missile arm" as exhaustive when the entire no-window host belonged in it; AD-1 RETIRED 2026-08-05, C5a deletion sweep — the legacy outdoor demote/restore lift this row described was `PhysicsEngine.Resolve`'s own body, deleted with zero production callers; AD-42 DELETED 2026-08-04, C4 route 3 — its last surviving citation, the headless portal-arrival resync's two-call Resolve/ResolvePlacement split, was retired by the canonical `RuntimeAcceptedPositionDriveController` portal arm; AD-2 amended same route with the deferred-place timing adaptation, the T8 tolerated-overwrite note, and the leash-anchor nuance; AD-63 filed 2026-08-04, cancelled-park presentation rollback — the rollback restores every presentation registration the park's Withdraw removed EXCEPT the player's selection, which is user intent rather than a projection; AD-62 filed 2026-08-03, C4 route 2 round 2 — a deferred ForcePosition retired without committing is not re-applied and its ack is not sent; AD-61 filed 2026-08-02, C3c review round 1 — the #270 settle compression now covers the local player; AD-59/AD-60 filed 2026-08-02, continuation-executor slice) Recent retirements: AD-3/AD-4 retired 2026-07-31 by exact active/per-candidate visible-cell availability, full-catalog containment-root validation, and the @@ -183,6 +183,7 @@ readiness/requeue adaptation. See | AD-86 | **Filed 2026-08-12 at Campaign FA slice FA5, item 4.** ACE deliberately zeroes or empties NINE `AllegianceProfile`/`AllegianceData` fields on the wire — officers, officer titles, MOTD, MOTD-set-by, name-last-set-time, lock state, and approved vassal are always empty/false/zero regardless of the allegiance's real state; `timeOnline`/`allegianceAge` (the remaining two) are hard-coded 0 forever (lane C §5.1). acdream's FA1 parser reads all of these (to keep the byte cursor aligned for the fields after them) but drops most at increasing layers: `AllegianceMemberRecord` never surfaces `timeOnline`/`allegianceAge` as fields at all; `RuntimeAllegianceState.ApplyUpdate` (FA2) does not forward `Motd`/`MotdSetBy`/`ChatRoomId`/`NameLastSetTime`/`IsLocked`/`ApprovedVassal` from the parsed `AllegianceUpdate` record to `RuntimeAllegianceSnapshot` even though the C# record itself carries them; retail's own `gmAllegianceUI` (FA5) has no widget for any of the seven either (lane A §3.3: "No allegiance MOTD / officer / ban / hometown UI" — they are chat-verb-only in the 2013 client, out of this campaign's scope per the plan's §4). | `src/AcDream.Core.Net/Messages/ClientCommandResponses.cs` (`ReadAllegianceProfileBody`, `AllegianceMemberRecord`, `AllegianceUpdate`); `src/AcDream.Runtime/Gameplay/RuntimeAllegianceState.cs` (`ApplyUpdate`) | Retail's own client renders nothing for these seven fields either (no panel widget consumes them) — dropping them past the parse layer matches retail's OWN presentation exactly, and is strictly safer than surfacing values that are always wrong/empty against ACE. | Any FUTURE consumer (the chat-verb-only officer/MOTD/lock/ban management features, §2 master table features #11-31 of the allegiance wire research, explicitly out of Campaign FA's scope) that reads these fields off the Runtime layer will find them permanently zero/empty against ACE regardless of the allegiance's real server-side state — do not chase this as a parser bug; it is ACE's own zeroing. | ACE `Network/Structure/AllegianceHierarchy.cs:53-56,62-64,74-75,78-83,86-89,153-155` (broadcast counters/isLocked/officers/officerTitles/motd/approvedVassal); ACE `Network/Structure/AllegianceData.cs:59-60,86-89,111-112` (timeOnline/allegianceAge); `docs/research/2026-08-11-fa-allegiance-wire.md` §5.1 | | AD-87 | **Filed 2026-08-12 at Campaign FA slice FA6.** The allegiance-swear half of the two-bot headless connected gate (`FellowshipAllegianceLeaderBotPolicy`/`FellowshipAllegianceRecruitBotPolicy`) is written and wired end-to-end (proximity, `0x001D` swear, the confirmation-relay seam, `0x0020` tree-reseed assertions, break, reconnect-idempotence) but has never actually been verified to complete over the wire — `AllegianceGateEnabled = false` in both classes keeps it unreachable by default. Six live runs against local ACE all reproduced the same result: the fellowship half passes decisively (the Recruit bot's own `RuntimeFellowshipState` flips, proven three separate times), but ACE returns nothing at all to the `0x001D` swear (no `0x0274` confirmation, no `0x0020`, no error) even at 0.005 m separation — see docs/ISSUES.md #384 for the full evidence trail. So while the FELLOWSHIP two-session machinery is proven live, the ALLEGIANCE two-session machinery (Runtime commands, wire builders, `RuntimeAllegianceState` reseed) remains unverified end-to-end over a real connection — only its unit/fixture-level tests and its (successful) LOCAL echo on the swearer's own client are exercised. | `src/AcDream.Headless/Policies/HeadlessBotPolicy.cs` (`FellowshipAllegianceLeaderBotPolicy.AllegianceGateEnabled`, `FellowshipAllegianceRecruitBotPolicy.AllegianceGateEnabled`, both `false`) | Shipping the fellowship gate ALONE (rather than blocking the whole slice on the allegiance blocker) matches the campaign's own D8/item-6 split — fellowship and allegiance are independent retail systems with independent wire families, and the fellowship half's proof stands on its own regardless of the allegiance outcome. Disabling rather than deleting the allegiance code keeps a reviewed-quality, ready-to-run harness in place for whoever closes #384. | Anyone reading "the FA6 bot-vs-ACE gate passed" without the qualifier could assume the allegiance swear/break/reconnect path is proven over the wire when it is not — only its LOCAL send-and-echo behavior is proven; ACE's actual acceptance of the swear is the open question #384 tracks. | docs/ISSUES.md #384; `docs/research/2026-08-11-fa-allegiance-wire.md` §1.3 (the expected `0x0274`/`0x0275`/`0x0020` handshake); run6 evidence (0.005 m distance, zero inbound after swear) | | AD-88 | **Filed 2026-08-13 at the #385 dropdown fix (classification: UNCLEAR).** The vendor category dropdown ships G5's fixed 6-row scrollable popup window, but its authored popup ListBox (`0x21000043/0x10000350`) is edge-docked on all four sides (L=T=R=B=1, measured by menuprobe3 `OptionsPanelLiveMountProbeTests.ProbeMenuPopupSizingAndTextStyle`) — the exact authored condition that arms retail `UIElement_Menu::RecalculatePopupSize @0x0046caf0`, which resizes the popup to the ListBox's summed content height, uncapped (`0x0046e5f4..0046e66c`). The Config option-menus' identical docked shape now drives `UiMenu.PopupSizeToContent=true` (#385); vendor deliberately keeps `false`. | `src/AcDream.App/UI/Layout/VendorUiController.cs` (its UiMenu wiring leaves `PopupSizeToContent` at the class-default false) | The G5 vendor-gate retail screenshot was read as a ~6-row-with-scrollbar look and the vendor connected gate USER-PASSED on that shape — reworking a user-gated surface on decomp inference alone would invert the retail-oracle rule. The two pieces of evidence conflict; the row records the conflict rather than silently picking a side. | If retail actually opens the category popup full-height, our vendor dropdown shows a 6-row scroll window where retail shows every category at once — visible at any vendor with >6 categories. If retail truly shows 6 rows, the mechanism question (why the docked ListBox does not trigger RecalculatePopupSize there) is unanswered and could mislead the next dropdown port. | docs/ISSUES.md #386 (the retail side-by-side to run + the two candidate resolutions); #385 (the Config fix that exposed the conflict) | +| AD-89 | **Filed 2026-08-13 at the display-handling gate session (classification: stopgap, port planned).** The world cameras hardcode an aspect-independent vertical FOV (`ChaseCamera.FovY = MathF.PI / 3f` = 60°, `FlyCamera` likewise) with no retail anchor. Retail computes the applied FOV per aspect: `Render::SetFOVRad(m_fGameFOV / (ViewportAspectRatio − 0.1))` (`0x00452b2f`/`0x00453b14`), `m_fGameFOV` default π/2 (`0x00454649`), set in degrees by the Field of View option (`0x00451e6a`) — holding the horizontal view roughly constant across aspects while narrowing vertical on wide screens. | `src/AcDream.App/Rendering/ChaseCamera.cs:15` (FovY constant), `src/AcDream.App/Rendering/FlyCamera.cs` (same), `src/AcDream.App/Settings/RuntimeSettingsTargets.cs` (`ApplyFieldOfView`) | The constant predates the FOV research; nothing chose it against retail — it was simply never anchored. Filed as a row instead of silently patched because the port must also re-map the Config Field of View slider to retail's degree semantics in the same change. | Every aspect ratio renders a different framing than retail — widescreen shows too much horizontal world, 4:3 too little vertical; the user reports it as "squished"/"weird on modern screens" (ISSUES #389). | docs/ISSUES.md #389; decomp sites above | ---