From f2144ab2a42ffac92fceb056e7d11422cf15e5ba Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 14 Aug 2026 12:24:43 +0200 Subject: [PATCH] fix: trade gate round 2 - the trade window is state-managed, never layout-persisted; deeper [trade] wire probes A session that ended mid-trade saved Visible=true for "secure-trade" in the window-layout file, restoring an empty open trade window at every launch. Added to the stateManagedVisibilityWindows set beside Combat/JumpPowerbar/ExternalContainer/Vendor - visibility belongs to RuntimeTradeState's open/closed lifecycle exclusively. Also carries the round-2 [trade] probes for the still-open "open never registers" diagnosis: the round-1 log proved the request seam fires (4x "request partner=0x50000001 item=0 open=False") but no window ever opened - the new probes bracket the command router (open-cmd sent flag), the wire send (wire-open seq), and the inbound RegisterTrade apply, so the next gate log pinpoints whether the send leaves the client and whether ACE replies. App suite 4,990/3 skips. Co-Authored-By: Claude Fable 5 --- src/AcDream.App/Net/LiveSessionCommandRouter.cs | 10 ++++++++-- src/AcDream.App/UI/RetailUiRuntime.cs | 6 ++++++ src/AcDream.Core.Net/WorldSession.cs | 2 ++ src/AcDream.Runtime/Gameplay/RuntimeTradeState.cs | 4 ++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/AcDream.App/Net/LiveSessionCommandRouter.cs b/src/AcDream.App/Net/LiveSessionCommandRouter.cs index 0b0b44d4..46b0e042 100644 --- a/src/AcDream.App/Net/LiveSessionCommandRouter.cs +++ b/src/AcDream.App/Net/LiveSessionCommandRouter.cs @@ -229,8 +229,14 @@ internal sealed class LiveSessionCommandRouter : ILiveSessionCommandRouting commands.Register( command => SendIfActive(() => bindings.AddFriend(command.Name))); commands.Register( - command => SendIfActive(() => - bindings.OpenTradeNegotiations(command.PartnerGuid))); + command => + { + bool sent = SendIfActive(() => + bindings.OpenTradeNegotiations(command.PartnerGuid)); + // TEMPORARY [trade] probe (2026-08-14 gate round 2). + Console.WriteLine( + $"[trade] open-cmd partner=0x{command.PartnerGuid:X8} sent={sent}"); + }); commands.Register( _ => SendIfActive(bindings.CloseTradeNegotiations)); commands.Register( diff --git a/src/AcDream.App/UI/RetailUiRuntime.cs b/src/AcDream.App/UI/RetailUiRuntime.cs index a4d2c167..24ac66f2 100644 --- a/src/AcDream.App/UI/RetailUiRuntime.cs +++ b/src/AcDream.App/UI/RetailUiRuntime.cs @@ -500,6 +500,12 @@ public sealed class RetailUiRuntime : IDisposable WindowNames.JumpPowerbar, WindowNames.ExternalContainer, WindowNames.Vendor, + // Trade gate round 2 (2026-08-14): the trade window is + // transient — visibility belongs to RuntimeTradeState's + // open/closed lifecycle, never to the layout file (a + // session that ended mid-trade was restoring an empty + // open window at every launch). + WindowNames.SecureTrade, ]); } diff --git a/src/AcDream.Core.Net/WorldSession.cs b/src/AcDream.Core.Net/WorldSession.cs index 6833825b..6bbfcfb6 100644 --- a/src/AcDream.Core.Net/WorldSession.cs +++ b/src/AcDream.Core.Net/WorldSession.cs @@ -2568,6 +2568,8 @@ public sealed class WorldSession : IDisposable public void SendOpenTradeNegotiations(uint partnerGuid) { uint seq = NextGameActionSequence(); + // TEMPORARY [trade] probe (2026-08-14 gate round 2). + Console.WriteLine($"[trade] wire-open partner=0x{partnerGuid:X8} seq={seq}"); SendGameAction(TradeRequests.BuildOpenTradeNegotiations(seq, partnerGuid)); } diff --git a/src/AcDream.Runtime/Gameplay/RuntimeTradeState.cs b/src/AcDream.Runtime/Gameplay/RuntimeTradeState.cs index ab61ba74..fee1a7b6 100644 --- a/src/AcDream.Runtime/Gameplay/RuntimeTradeState.cs +++ b/src/AcDream.Runtime/Gameplay/RuntimeTradeState.cs @@ -87,6 +87,10 @@ public sealed class RuntimeTradeState : IDisposable /// public void ApplyRegister(GameEvents.RegisterTrade update, uint selfGuid) { + // TEMPORARY [trade] probe (2026-08-14 gate round 2). + Console.WriteLine( + $"[trade] register initiator=0x{update.Initiator:X8} " + + $"partner=0x{update.Partner:X8} self=0x{selfGuid:X8}"); lock (_gate) { ObjectDisposedException.ThrowIf(_disposed, this);