fix(#171): gate NPC UP hard-snaps while stuck — the sticky/snap tug-of-war (gate-1 residuals)
Gate 1 (2026-07-04): "better in general" + three residuals — monsters pushed INTO the player, attacks with stale facing, position "flashing/flapping instead of gliding". All three are ONE mechanism: the legacy NPC UpdatePosition handler hard-snaps position, orientation, and velocity/cycle UNCONDITIONALLY, fighting the armed sticky every UP (ACE's authoritative rest pose sits ~0.6 m out and its server-side facing lags the strafing target; the client stick holds 0.3 m + live facing — oscillation at UP cadence). Retail is immune by architecture, not by tuning: UP corrections flow through the InterpolationManager into the SAME per-tick PositionManager::adjust_offset chain where StickyManager::adjust_offset OVERWRITES them while armed (0x00555190 chain order; 0x00555430 assigns m_fOrigin). A server correction can never fight an armed stick frame-by-frame. The remote-player branch already has exactly that (queue -> combiner -> sticky overwrite); the legacy NPC path snaps outside the chain. Fix: suppress the NPC UP position/orientation/velocity-adoption snaps while the entity is stuck (PositionManager.GetStickyObjectId() != 0) — the retail chain semantics translated to the snap architecture. LastServerPos/Time + cell bookkeeping still record; server truth reasserts on the first UP after unstick, bounded by the 1 s sticky lease. Register row TS-44 (same commit); retires with the S6/R6 interp-queue unification of the NPC path. Apparatus: ACDREAM_PROBE_STICKY=1 — per-guid [sticky] lifecycle lines (STICK / UNSTICK / LEASE-EXPIRE / TARGET-status teardown), per-armed- tick steer lines (signed gap dist, applied delta, heading delta, live resolve), and [sticky-snap-skip] at the suppressed-snap site. PhysicsDiagnostics.ProbeStickyEnabled owns the flag (rule #5). Full suite 4038 green. Awaiting gate 2 (pack melee vs retail). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5bd2b8bc8b
commit
7a82317614
5 changed files with 90 additions and 4 deletions
|
|
@ -82,6 +82,10 @@ public sealed class StickyManager
|
|||
if (TargetId == 0)
|
||||
return;
|
||||
|
||||
if (PhysicsDiagnostics.ProbeStickyEnabled)
|
||||
Console.WriteLine(FormattableString.Invariant(
|
||||
$"[sticky] guid=0x{_host.Id:X8} UNSTICK target=0x{TargetId:X8}"));
|
||||
|
||||
TargetId = 0;
|
||||
Initialized = false;
|
||||
_host.ClearTarget();
|
||||
|
|
@ -120,6 +124,11 @@ public sealed class StickyManager
|
|||
TargetId = objectId;
|
||||
Initialized = false;
|
||||
StickyTimeoutTime = _host.CurTime + StickyTime;
|
||||
|
||||
if (PhysicsDiagnostics.ProbeStickyEnabled)
|
||||
Console.WriteLine(FormattableString.Invariant(
|
||||
$"[sticky] guid=0x{_host.Id:X8} STICK target=0x{objectId:X8} tgtR={targetRadius:F2} ownR={_host.Radius:F2} lease={StickyTime:F1}s"));
|
||||
|
||||
// set_target(context_id=0, objectId, radius=0.5, quantum=0.5).
|
||||
_host.SetTarget(0, objectId, 0.5f, 0.5);
|
||||
}
|
||||
|
|
@ -140,6 +149,10 @@ public sealed class StickyManager
|
|||
// C0|C3 clear = cur_time > timeout; ACE `>` too), not >=.
|
||||
if (_host.CurTime > StickyTimeoutTime)
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbeStickyEnabled)
|
||||
Console.WriteLine(FormattableString.Invariant(
|
||||
$"[sticky] guid=0x{_host.Id:X8} LEASE-EXPIRE target=0x{TargetId:X8}"));
|
||||
|
||||
TargetId = 0;
|
||||
Initialized = false;
|
||||
_host.ClearTarget();
|
||||
|
|
@ -170,6 +183,10 @@ public sealed class StickyManager
|
|||
|
||||
if (TargetId != 0)
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbeStickyEnabled)
|
||||
Console.WriteLine(FormattableString.Invariant(
|
||||
$"[sticky] guid=0x{_host.Id:X8} TARGET-{info.Status} teardown target=0x{TargetId:X8}"));
|
||||
|
||||
TargetId = 0;
|
||||
Initialized = false;
|
||||
_host.ClearTarget();
|
||||
|
|
@ -233,5 +250,9 @@ public sealed class StickyManager
|
|||
if (heading < -MoveToMath.Epsilon)
|
||||
heading += 360f;
|
||||
offset.SetHeading(heading);
|
||||
|
||||
if (PhysicsDiagnostics.ProbeStickyEnabled)
|
||||
Console.WriteLine(FormattableString.Invariant(
|
||||
$"[sticky] guid=0x{_host.Id:X8} ADJ dist={dist:F3} delta={delta:F3} speed={speed:F1} hdgDelta={heading:F1} live={(target is not null ? 1 : 0)}"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,20 @@ public static class PhysicsDiagnostics
|
|||
public static bool ProbeCellSetEnabled { get; set; }
|
||||
= Environment.GetEnvironmentVariable("ACDREAM_PROBE_CELLSET") == "1";
|
||||
|
||||
/// <summary>
|
||||
/// R5-V3 #171 residuals (2026-07-04) — sticky-melee timeline probe.
|
||||
/// One <c>[sticky]</c> line per StickyManager lifecycle event (STICK /
|
||||
/// UNSTICK / LEASE-EXPIRE / TARGET-status teardown) and per armed
|
||||
/// <c>AdjustOffset</c> tick (guid, signed gap distance, applied delta,
|
||||
/// heading delta), plus <c>[sticky-snap-skip]</c> lines at the NPC
|
||||
/// UpdatePosition handler when a server hard-snap is suppressed because
|
||||
/// the entity is stuck. Heavy while a pack is stuck (~60 Hz × stuck
|
||||
/// count); capture-session only. All lines carry the guid
|
||||
/// (feedback_probe_identity_attribution).
|
||||
/// </summary>
|
||||
public static bool ProbeStickyEnabled { get; set; }
|
||||
= Environment.GetEnvironmentVariable("ACDREAM_PROBE_STICKY") == "1";
|
||||
|
||||
public static void LogCellSetBuild(
|
||||
uint seedCellId,
|
||||
System.Numerics.Vector3 sphereCenter,
|
||||
|
|
@ -562,6 +576,7 @@ public static class PhysicsDiagnostics
|
|||
ProbeCellEnabled = false;
|
||||
ProbeBuildingEnabled = false;
|
||||
ProbeCellSetEnabled = false;
|
||||
ProbeStickyEnabled = false;
|
||||
ProbeAutoWalkEnabled = false;
|
||||
ProbeUseabilityFallbackEnabled= false;
|
||||
DumpSteepRoofEnabled = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue