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
|
|
@ -52,6 +52,77 @@ public sealed class RuntimePhysicsStateTests
|
|||
Assert.True(physics.CaptureOwnership().IsDisposed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 2026-08-08 vendor-approach root fix: <c>ResolveObjectTableHost</c> is
|
||||
/// the retail <c>CObjectMaint::GetObjectA</c> seam consumed by the local
|
||||
/// player's publication-chain host. Unbound (no-window hosts) it answers
|
||||
/// exact installed hosts only; bound (the graphical host's canonical
|
||||
/// lazy resolver, installed by <c>SessionPlayerComposition</c>) it
|
||||
/// delegates every lookup — including entities that have no installed
|
||||
/// host yet, the case whose null answer left a far-use MoveToObject
|
||||
/// armed but inert. Dispose clears the binding so a retired session
|
||||
/// route cannot leak its App closure.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ResolveObjectTableHostDelegatesToTheBoundResolverAndFallsBackToInstalledHosts()
|
||||
{
|
||||
var lifetime = new RuntimeEntityObjectLifetime();
|
||||
RuntimeEntityRecord record =
|
||||
lifetime.Entities.AddActive(Spawn(0x70000001u, 1));
|
||||
EntityPhysicsHost installed = MinimalHost(record.ServerGuid);
|
||||
lifetime.Physics.InstallPhysicsHost(record, installed);
|
||||
|
||||
const uint uninstalledGuid = 0x7C95B01Cu;
|
||||
// Unbound fallback: installed hosts resolve, anything else is null.
|
||||
Assert.Same(
|
||||
installed,
|
||||
lifetime.Physics.ResolveObjectTableHost(record.ServerGuid));
|
||||
Assert.Null(lifetime.Physics.ResolveObjectTableHost(uninstalledGuid));
|
||||
|
||||
// Bound: the canonical resolver owns EVERY lookup.
|
||||
EntityPhysicsHost lazyMinimal = MinimalHost(uninstalledGuid);
|
||||
var resolvedGuids = new List<uint>();
|
||||
lifetime.Physics.BindObjectTableHostResolver(guid =>
|
||||
{
|
||||
resolvedGuids.Add(guid);
|
||||
return guid == uninstalledGuid ? lazyMinimal : null;
|
||||
});
|
||||
Assert.Same(
|
||||
lazyMinimal,
|
||||
lifetime.Physics.ResolveObjectTableHost(uninstalledGuid));
|
||||
Assert.Null(lifetime.Physics.ResolveObjectTableHost(0x70000002u));
|
||||
Assert.Equal(
|
||||
new[] { uninstalledGuid, 0x70000002u },
|
||||
resolvedGuids);
|
||||
|
||||
// Rebinding replaces (last bind wins); null clears back to the
|
||||
// exact-installed-host fallback.
|
||||
lifetime.Physics.BindObjectTableHostResolver(null);
|
||||
Assert.Null(lifetime.Physics.ResolveObjectTableHost(uninstalledGuid));
|
||||
Assert.Same(
|
||||
installed,
|
||||
lifetime.Physics.ResolveObjectTableHost(record.ServerGuid));
|
||||
|
||||
lifetime.Physics.BindObjectTableHostResolver(_ => lazyMinimal);
|
||||
lifetime.Dispose();
|
||||
Assert.Throws<ObjectDisposedException>(
|
||||
() => lifetime.Physics.ResolveObjectTableHost(uninstalledGuid));
|
||||
}
|
||||
|
||||
private static EntityPhysicsHost MinimalHost(uint guid) => new(
|
||||
guid,
|
||||
getPosition: static () => new AcDream.Core.Physics.Position(
|
||||
0u, Vector3.Zero, Quaternion.Identity),
|
||||
getVelocity: static () => Vector3.Zero,
|
||||
getRadius: static () => 0f,
|
||||
inContact: static () => true,
|
||||
minterpMaxSpeed: static () => null,
|
||||
curTime: static () => 0d,
|
||||
physicsTimerTime: static () => 0d,
|
||||
getObjectA: static _ => null,
|
||||
handleUpdateTarget: static _ => { },
|
||||
interruptCurrentMovement: static () => { });
|
||||
|
||||
[Fact]
|
||||
public void CanonicalRecordAndPhysicsOwnerOwnRemoteComponentAndWorksets()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue