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>
86 lines
3.4 KiB
C#
86 lines
3.4 KiB
C#
using AcDream.App.Input;
|
|
using AcDream.Core.Items;
|
|
using AcDream.Core.Physics;
|
|
using AcDream.Core.Physics.Motion;
|
|
|
|
namespace AcDream.App.Interaction;
|
|
|
|
internal interface IPlayerInteractionMovementSink
|
|
{
|
|
/// <summary>
|
|
/// Cancels any preceding MoveTo, invokes <paramref name="armAfterCancel"/>,
|
|
/// then installs the new movement. The callback boundary lets the intent
|
|
/// owner arm completion state before an already-facing TurnTo can finish
|
|
/// synchronously, without letting the preceding cancellation clear it.
|
|
/// </summary>
|
|
bool BeginApproach(
|
|
InteractionApproach approach,
|
|
Action<PlayerApproachToken>? armAfterCancel = null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Installs retail's client-side TurnToObject/MoveToObject prediction through
|
|
/// the same MovementManager used by authoritative movement packets.
|
|
/// </summary>
|
|
internal sealed class PlayerInteractionMovementSink(
|
|
Func<PlayerMovementController?> player,
|
|
IPlayerApproachTokenSource approachTokens)
|
|
: IPlayerInteractionMovementSink
|
|
{
|
|
private readonly Func<PlayerMovementController?> _player = player
|
|
?? throw new ArgumentNullException(nameof(player));
|
|
private readonly IPlayerApproachTokenSource _approachTokens = approachTokens
|
|
?? throw new ArgumentNullException(nameof(approachTokens));
|
|
|
|
public bool BeginApproach(
|
|
InteractionApproach approach,
|
|
Action<PlayerApproachToken>? armAfterCancel = null)
|
|
{
|
|
PlayerMovementController? controller = _player();
|
|
if (controller?.MoveTo is null)
|
|
return false;
|
|
|
|
var parameters = new MovementParameters
|
|
{
|
|
DistanceToObject = approach.UseRadius,
|
|
CanCharge = approach.CanCharge,
|
|
};
|
|
var movement = new MovementStruct
|
|
{
|
|
ObjectId = approach.Target.ServerGuid,
|
|
TopLevelId = approach.Target.ServerGuid,
|
|
Pos = new Position(
|
|
approach.Player.CellId,
|
|
approach.Target.Entity.Position,
|
|
System.Numerics.Quaternion.Identity),
|
|
Params = parameters,
|
|
Type = approach.IsCloseRange
|
|
? MovementType.TurnToObject
|
|
: MovementType.MoveToObject,
|
|
Radius = approach.TargetRadius,
|
|
Height = approach.TargetHeight,
|
|
};
|
|
|
|
if (VendorDiagnostics.DumpVendorEnabled)
|
|
{
|
|
Console.WriteLine(
|
|
$"[vendor-diag] BeginApproach guid=0x{approach.Target.ServerGuid:X8} "
|
|
+ $"movementType={movement.Type} distanceToObject={parameters.DistanceToObject} "
|
|
+ $"canCharge={parameters.CanCharge} target=0x{movement.ObjectId:X8}");
|
|
}
|
|
|
|
// PerformMovement cancels at its head. Do it explicitly before the
|
|
// intent is armed so cancellation of the preceding move cannot clear
|
|
// the new request; the internal second call is then a retail no-op.
|
|
controller.Movement.CancelMoveTo(WeenieError.ActionCancelled);
|
|
if (!_approachTokens.TryBeginApproach(out PlayerApproachToken token))
|
|
return false;
|
|
armAfterCancel?.Invoke(token);
|
|
|
|
// P1's wire MoveToObject store marks the command non-autonomous.
|
|
// The speculative local install must do the same or the next raw-input
|
|
// pump overwrites it before ACE's authoritative movement arrives.
|
|
controller.SetLastMoveWasAutonomous(false);
|
|
return controller.Movement.PerformMovement(movement) == WeenieError.None;
|
|
}
|
|
}
|