feat: secure trade with other players - wire, RuntimeTradeState, the
authored gmSecureTradeUI window, and both retail open paths
Three-lane research first (docs/research/2026-08-14-trade-lane{A,B,C}):
retail gmSecureTradeUI decode, the byte-exact ACE/decomp/holtburger
three-way wire agreement, and the acdream seam map (which found both
open paths ALREADY classified by the ported policy - OpenSecureTrade on
Use-a-player, StartSecureTrade on drag-item-onto-player with the
DragItemOnPlayerOpensSecureTrade option - dead-ending at a stub toast).
- Core.Net: TradeRequests builders (0x1F6-0x204, retail's CM_Trade
senders byte-checked against ACE's readers; the ACE-discarded
AcceptTrade echo carries zero-count item lists - AD-94), corrected +
completed inbound parsers (0x1FD-0x208; the old AddToTrade parser
missed the SIDE dword, TradeFailure missed the reason), delegate-hole
registrars, six WorldSession sends. 10 golden-byte tests.
- Runtime: RuntimeTradeState, the third sibling J-owner (fellowship/
allegiance shape): session-scoped, clears at generation reset (new
stage Trade=14), staged teardown stage 11 (Identity/EntityObjects
shift 12/13, TeardownStageCount 14 - the FA2-era per-stage-flag test
caught the mapping exactly as designed), combined ownership ledger,
event routing with ACE's wrong-initiator RegisterTrade landmine
honored (partner = whichever guid is not mine). 7 conformance tests.
- App: SecureTradeUiController binds the dedicated authored LayoutDesc
0x2100000D (root 0x1000007A - gmSecureTradeUI::PostInit's exact ids):
partner name/status/count/grid, the authored 'Trade' accept toggle
(accept <-> decline withdraw), 'Clear All' (ACE clears BOTH sides -
surfaced honestly), the X close, drop-on-your-grid staging, per-mode
accept cues (partner icon's authored Highlight state + Trade button
Selected latch). Mounted via the vendor recipe (nine-slice chrome,
hidden until RegisterTrade). ItemInteractionController's two policy
arms now raise SecureTradeRequested instead of the stub toast; the
drag path queues the dragged item until the window registers
(ClientTradeSystem::AttemptToTradeItem @0x0056DF80's shape).
Register: AD-94 (accept-echo zero-count lists), AD-95 (numeric-only
count texts pending template verification).
Suites: App 4,990/3, Core.Net 905, Runtime 1,626 - all green. The
panel itself is user-gate acceptance (two-client connected trade), the
#372-class lesson: fixture-green alone is not acceptance for a mount.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
bee38b0746
commit
067cbea8a5
26 changed files with 2682 additions and 42 deletions
|
|
@ -37,8 +37,10 @@ public enum GameRuntimeTeardownStage
|
|||
// Campaign FA slice FA2 (2026-08-12): the two sibling J-owners.
|
||||
FellowshipDisposed = 1 << 9,
|
||||
AllegianceDisposed = 1 << 10,
|
||||
IdentityDisposed = 1 << 11,
|
||||
EntityObjectsDisposed = 1 << 12,
|
||||
// Secure trade (2026-08-14): third sibling J-owner, same shape.
|
||||
TradeDisposed = 1 << 11,
|
||||
IdentityDisposed = 1 << 12,
|
||||
EntityObjectsDisposed = 1 << 13,
|
||||
Complete =
|
||||
HostLeasesReleased
|
||||
| EventsDetached
|
||||
|
|
@ -51,6 +53,7 @@ public enum GameRuntimeTeardownStage
|
|||
| CommunicationDisposed
|
||||
| FellowshipDisposed
|
||||
| AllegianceDisposed
|
||||
| TradeDisposed
|
||||
| IdentityDisposed
|
||||
| EntityObjectsDisposed,
|
||||
}
|
||||
|
|
@ -94,6 +97,7 @@ internal enum GameRuntimeConstructionPoint
|
|||
CommunicationCreated,
|
||||
FellowshipCreated,
|
||||
AllegianceCreated,
|
||||
TradeCreated,
|
||||
MovementCreated,
|
||||
ActionsCreated,
|
||||
EnvironmentCreated,
|
||||
|
|
@ -111,6 +115,7 @@ internal sealed class GameRuntimeConstructionContext
|
|||
public RuntimeCommunicationState? Communication { get; set; }
|
||||
public RuntimeFellowshipState? Fellowship { get; set; }
|
||||
public RuntimeAllegianceState? Allegiance { get; set; }
|
||||
public RuntimeTradeState? Trade { get; set; }
|
||||
public RuntimeLocalPlayerMovementState? Movement { get; set; }
|
||||
public RuntimeActionState? Actions { get; set; }
|
||||
public GameRuntimeEventHub? Events { get; set; }
|
||||
|
|
@ -126,7 +131,7 @@ public sealed class GameRuntime
|
|||
IRuntimeEventSource,
|
||||
IDisposable
|
||||
{
|
||||
private const int TeardownStageCount = 13;
|
||||
private const int TeardownStageCount = 14;
|
||||
|
||||
private readonly object _lifetimeGate = new();
|
||||
private readonly Dictionary<long, string> _hostLeases = [];
|
||||
|
|
@ -248,6 +253,16 @@ public sealed class GameRuntime
|
|||
context,
|
||||
faultInjection);
|
||||
|
||||
// Secure trade (2026-08-14): third sibling J-owner —
|
||||
// session-scoped like fellowship (a disconnect closes the trade
|
||||
// server-side), clears at every generation reset.
|
||||
context.Trade = new RuntimeTradeState();
|
||||
construction.Own(context.Trade);
|
||||
Fault(
|
||||
GameRuntimeConstructionPoint.TradeCreated,
|
||||
context,
|
||||
faultInjection);
|
||||
|
||||
context.Movement = new RuntimeLocalPlayerMovementState();
|
||||
// Campaign CH slice CH2: local jump refusals (CommenceJump/
|
||||
// DoJump's WeenieError family — research doc §4.2/§6.4) reach
|
||||
|
|
@ -302,7 +317,8 @@ public sealed class GameRuntime
|
|||
context.Character,
|
||||
context.PlayerIdentity,
|
||||
context.Fellowship,
|
||||
context.Allegiance);
|
||||
context.Allegiance,
|
||||
context.Trade);
|
||||
|
||||
context.Movement.AttachPhysicsPublication(
|
||||
new RuntimeLocalPlayerPhysicsPublicationState(
|
||||
|
|
@ -357,6 +373,7 @@ public sealed class GameRuntime
|
|||
CommunicationOwner = context.Communication;
|
||||
FellowshipOwner = context.Fellowship;
|
||||
AllegianceOwner = context.Allegiance;
|
||||
TradeOwner = context.Trade;
|
||||
MovementOwner = context.Movement;
|
||||
ActionOwner = context.Actions;
|
||||
EnvironmentOwner = environment;
|
||||
|
|
@ -462,6 +479,9 @@ public sealed class GameRuntime
|
|||
public RuntimeCommunicationState CommunicationOwner { get; }
|
||||
public RuntimeFellowshipState FellowshipOwner { get; }
|
||||
public RuntimeAllegianceState AllegianceOwner { get; }
|
||||
|
||||
/// <summary>Secure trade (2026-08-14): third sibling J-owner.</summary>
|
||||
public RuntimeTradeState TradeOwner { get; }
|
||||
public RuntimeActionState ActionOwner { get; }
|
||||
public RuntimeLocalPlayerMovementState MovementOwner { get; }
|
||||
internal RuntimeLocalPlayerPhysicsPublicationState
|
||||
|
|
@ -507,6 +527,8 @@ public sealed class GameRuntime
|
|||
public IRuntimeChatView Chat => CommunicationOwner.View;
|
||||
public IRuntimeFellowshipView Fellowship => FellowshipOwner.View;
|
||||
public IRuntimeAllegianceView Allegiance => AllegianceOwner.View;
|
||||
|
||||
public IRuntimeTradeView Trade => TradeOwner.View;
|
||||
public IRuntimeActionView Actions => ActionOwner.View;
|
||||
public IRuntimeMovementView Movement => MovementOwner.View;
|
||||
public IRuntimeWorldEnvironmentView Environment => EnvironmentOwner;
|
||||
|
|
@ -607,7 +629,8 @@ public sealed class GameRuntime
|
|||
ActionOwner,
|
||||
MovementOwner,
|
||||
FellowshipOwner,
|
||||
AllegianceOwner),
|
||||
AllegianceOwner,
|
||||
TradeOwner),
|
||||
EnvironmentOwner.CaptureOwnership(),
|
||||
TransitOwner.CaptureOwnership(),
|
||||
GenerationReset.CaptureSnapshot(),
|
||||
|
|
@ -733,16 +756,22 @@ public sealed class GameRuntime
|
|||
9 => GameRuntimeTeardownStage.Complete
|
||||
& ~GameRuntimeTeardownStage.FellowshipDisposed
|
||||
& ~GameRuntimeTeardownStage.AllegianceDisposed
|
||||
& ~GameRuntimeTeardownStage.TradeDisposed
|
||||
& ~GameRuntimeTeardownStage.IdentityDisposed
|
||||
& ~GameRuntimeTeardownStage.EntityObjectsDisposed,
|
||||
10 => GameRuntimeTeardownStage.Complete
|
||||
& ~GameRuntimeTeardownStage.AllegianceDisposed
|
||||
& ~GameRuntimeTeardownStage.TradeDisposed
|
||||
& ~GameRuntimeTeardownStage.IdentityDisposed
|
||||
& ~GameRuntimeTeardownStage.EntityObjectsDisposed,
|
||||
11 => GameRuntimeTeardownStage.Complete
|
||||
& ~GameRuntimeTeardownStage.TradeDisposed
|
||||
& ~GameRuntimeTeardownStage.IdentityDisposed
|
||||
& ~GameRuntimeTeardownStage.EntityObjectsDisposed,
|
||||
12 => GameRuntimeTeardownStage.Complete
|
||||
& ~GameRuntimeTeardownStage.IdentityDisposed
|
||||
& ~GameRuntimeTeardownStage.EntityObjectsDisposed,
|
||||
13 => GameRuntimeTeardownStage.Complete
|
||||
& ~GameRuntimeTeardownStage.EntityObjectsDisposed,
|
||||
_ => GameRuntimeTeardownStage.Complete,
|
||||
};
|
||||
|
|
@ -794,9 +823,12 @@ public sealed class GameRuntime
|
|||
AllegianceOwner.Dispose();
|
||||
return AllegianceOwner.CaptureOwnership().IsConverged;
|
||||
case 11:
|
||||
TradeOwner.Dispose();
|
||||
return TradeOwner.CaptureOwnership().IsConverged;
|
||||
case 12:
|
||||
PlayerIdentity.Dispose();
|
||||
return PlayerIdentity.CaptureOwnership().IsConverged;
|
||||
case 12:
|
||||
case 13:
|
||||
EntityObjects.Dispose();
|
||||
return EntityObjects.CaptureOwnership().IsConverged
|
||||
&& EntityObjects.Physics.CaptureOwnership().IsConverged;
|
||||
|
|
@ -819,8 +851,9 @@ public sealed class GameRuntime
|
|||
8 => CommunicationOwner.CaptureOwnership().IsConverged,
|
||||
9 => FellowshipOwner.CaptureOwnership().IsConverged,
|
||||
10 => AllegianceOwner.CaptureOwnership().IsConverged,
|
||||
11 => PlayerIdentity.CaptureOwnership().IsConverged,
|
||||
12 => EntityObjects.CaptureOwnership().IsConverged
|
||||
11 => TradeOwner.CaptureOwnership().IsConverged,
|
||||
12 => PlayerIdentity.CaptureOwnership().IsConverged,
|
||||
13 => EntityObjects.CaptureOwnership().IsConverged
|
||||
&& EntityObjects.Physics.CaptureOwnership().IsConverged,
|
||||
_ => true,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue