fix(vendor): evidence-based pass — max-first stack ceiling; the local player resolves never-animated MoveTo targets
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Both chains pinned by the live [vendor-diag] run (vendor-diag.log) after three code-reading rounds each failed: The split bar: ACE serializes descStackSize=1 for EVERY browse row (live wire, log 343-348) — the R1-era "ACE never populates desc" claim is retracted with the line quoted. Retail's vendor sites read pwd._maxStackSize directly (four sites, incl. UpdateItemsList @0x004c1ea0 stamping min(remaining, _maxStackSize)); ResolveAuthoredStackSize flips to max-first for its vendor-only consumers. Taper ceiling 1000, scarab 100, seed 1 for exempt. Pricing still reads the desc (per-1 values on ACE). Walk-to-use: the local player's getObjectA seam was bound to TryGetPhysicsHost, which resolves only INSTALLED physics hosts — a never-animated vendor has none, so TargetManager.SetTarget got null, the MoveToObject armed with zero nodes, and UseTime never dispatched. The log's natural=False completions were the user's own movement keys (retail-correct input-edge cancels); attempt 4 worked because the greeting animation had installed a host. RuntimePhysicsState gains the retail CObjectMaint::GetObjectA seam (bound canonical resolver with installed-host fallback); the graphical host binds the SAME lazy-minimal-host resolver every remote already uses — whose own doc comment names this exact never-animated hazard. The reservation release was already correct (2b premise refuted with evidence); the production-wiring invariants are now pinned by four new tests including the pre-fix pathology as a permanent sabotage control. AP-169 rewritten a second time, honestly. The [vendor-diag] probe family (ACDREAM_DUMP_VENDOR) lands env-gated for future live triage. Clean-room complete solution: 11,536 passed / 4 skipped / 0 failed. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d003449bb4
commit
02b735ba4a
21 changed files with 1139 additions and 102 deletions
|
|
@ -173,10 +173,17 @@ internal sealed class SelectionInteractionController
|
|||
// Use. Equipped-child picking makes that click reachable, so the gate
|
||||
// ships with it.
|
||||
if (useImmediately && !_query.IsWieldedByPlayer(guid))
|
||||
{
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] route=dblclick-world PickAndStoreSelection guid=0x{guid:X8} enqueue=Activate");
|
||||
}
|
||||
EnqueueIdentityBound(
|
||||
RuntimeQueuedInteractionKind.Activate,
|
||||
guid,
|
||||
requireLiveEntity: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -205,6 +212,11 @@ internal sealed class SelectionInteractionController
|
|||
_toast?.Invoke("Nothing selected");
|
||||
return;
|
||||
}
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] route=keyboard-use UseCurrentSelection guid=0x{selected:X8} enqueue=Use");
|
||||
}
|
||||
EnqueueIdentityBound(
|
||||
RuntimeQueuedInteractionKind.Use,
|
||||
selected,
|
||||
|
|
@ -260,10 +272,22 @@ internal sealed class SelectionInteractionController
|
|||
bool ownedByPlayer = _items.IsOwnedByPlayer(serverGuid);
|
||||
bool useable = ownedByPlayer || _query.IsUseable(serverGuid);
|
||||
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] RequestUse entry guid=0x{serverGuid:X8} ownedByPlayer={ownedByPlayer} useable={useable}");
|
||||
}
|
||||
|
||||
if (useable
|
||||
&& _query.TryGetApproach(serverGuid, out InteractionApproach approach)
|
||||
&& !approach.IsCloseRange)
|
||||
{
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] RequestUse guid=0x{serverGuid:X8} branch=approach-armed useRadius={approach.UseRadius} isCloseRange={approach.IsCloseRange}");
|
||||
}
|
||||
|
||||
// Genuinely out of range (a real walk, not just a turn) —
|
||||
// mirror SendPickup's arrival-gated shape: arm the transaction
|
||||
// on the approach token BEFORE the movement starts (so a
|
||||
|
|
@ -283,7 +307,17 @@ internal sealed class SelectionInteractionController
|
|||
token.ControllerLifetime,
|
||||
token.ApproachGeneration),
|
||||
out _);
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] TryArmPostArrivalUse guid=0x{serverGuid:X8} armed={armed} approachToken=({token.ControllerLifetime},{token.ApproachGeneration})");
|
||||
}
|
||||
});
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] BeginApproach-result guid=0x{serverGuid:X8} started={started} armed={armed} stopDistance={approach.UseRadius} target=0x{approach.Target.ServerGuid:X8}");
|
||||
}
|
||||
if (!started || !armed)
|
||||
{
|
||||
// Release whatever got captured (or the caller's own
|
||||
|
|
@ -306,6 +340,11 @@ internal sealed class SelectionInteractionController
|
|||
// — keep retail's immediate send; ACE's own "already within use
|
||||
// distance" branch (Player_Move.cs:65-87) calls back synchronously,
|
||||
// so there is no arrival gap to race here.
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] RequestUse guid=0x{serverGuid:X8} branch=immediate-dispatch ownedByPlayer={ownedByPlayer} useable={useable}");
|
||||
}
|
||||
RuntimeInteractionDispatchResult result =
|
||||
_transactions.TryDispatchUse(
|
||||
serverGuid,
|
||||
|
|
@ -314,6 +353,11 @@ internal sealed class SelectionInteractionController
|
|||
reservation,
|
||||
_transport,
|
||||
out uint sequence);
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] RequestUse guid=0x{serverGuid:X8} TryDispatchUse verdict={result} seq={sequence}");
|
||||
}
|
||||
if (result == RuntimeInteractionDispatchResult.NotInWorld)
|
||||
_toast?.Invoke("Not in world");
|
||||
if (result == RuntimeInteractionDispatchResult.Dispatched)
|
||||
|
|
@ -545,6 +589,22 @@ internal sealed class SelectionInteractionController
|
|||
RuntimePendingUse pending,
|
||||
bool accepted)
|
||||
{
|
||||
if (VendorDiagnostics.DumpVendorEnabled)
|
||||
{
|
||||
// Diagnostic-only re-query — TryGetApproach is a pure read with
|
||||
// no side effects, so an extra call here (gated off in
|
||||
// production) cannot change RequestUse's own dispatch outcome.
|
||||
string distanceText = "n/a";
|
||||
if (_query.TryGetApproach(pending.ServerGuid, out InteractionApproach diagApproach))
|
||||
{
|
||||
float dx = diagApproach.Target.Entity.Position.X - diagApproach.Player.Position.X;
|
||||
float dy = diagApproach.Target.Entity.Position.Y - diagApproach.Player.Position.Y;
|
||||
distanceText = MathF.Sqrt(dx * dx + dy * dy).ToString("F2");
|
||||
}
|
||||
Console.WriteLine(
|
||||
$"[vendor-diag] HandleUseApproachCompletion guid=0x{pending.ServerGuid:X8} accepted={accepted} playerToTargetDist={distanceText}");
|
||||
}
|
||||
|
||||
if (!accepted)
|
||||
{
|
||||
pending.Reservation?.CancelBeforeDispatch();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue