docs(#171): file the group-melee interpenetration issue + R5-V3 handoff/pickup
User report during the #170 gate: pack-melee monsters partly inside each other with slightly stale facings vs retail on the same ACE. Investigation (report-only) pinned two code-grounded causes, both already-tracked R5-V3 scope, fix APPROVED by the user: 1. TS-39 — sticky melee is a no-op: ACE arms melee chases with Sticky (Monster_Navigation.cs:416); retail's arrival hands off to PositionManager::StickTo -> StickyManager::adjust_offset (0x00555430, per-tick 0.3m edge gap + facing tracking); acdream's StickTo/Unstick seams are unbound so attackers freeze at stale arrival poses. 2. Arrival radii are zero: getOwnRadius () => 0f (the R5-V3 pin) and RouteServerMoveTo never sets MovementStruct.Radius/Height — retail/ACE arrive edge-to-edge with setup-derived radii (ACE PhysicsObj.MoveToObject reads the TARGET's PartArray radius/height). Ruled out: the between-snap collision sweep (remotes run full ResolveWithTransition; CollisionExemption keeps creatures collidable). Caveat recorded: ACE has no server-side monster avoidance — acceptance is retail PARITY, not zero overlap. ISSUES #171 filed; handoff docs/research/2026-07-04-171-sticky-melee-handoff.md + pickup docs/research/2026-07-04-171-pickup-prompt.md; memory index updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4cad626ff5
commit
d413ac2a29
3 changed files with 222 additions and 0 deletions
|
|
@ -46,6 +46,50 @@ Copy this block when adding a new issue:
|
|||
|
||||
---
|
||||
|
||||
## #171 — Group melee: monsters interpenetrate + facing drifts (sticky melee unbound, arrival radii = 0)
|
||||
|
||||
**Status:** OPEN — investigated + fix APPROVED 2026-07-04 (user: "Ok I approve").
|
||||
**Severity:** MEDIUM (visual — group combat feel vs retail)
|
||||
**Filed:** 2026-07-04 (user report during the #170 visual gate: "some monsters were
|
||||
partly inside other monsters while hitting me… some orientation of the monsters
|
||||
are a bit off")
|
||||
**Component:** remote entity, MoveTo/PositionManager, R5-V3
|
||||
|
||||
**Description:** in a pack melee, acdream attackers end up partly inside each
|
||||
other with slightly stale facings vs retail on the same ACE. Two code-grounded
|
||||
causes (both already-tracked R5-V3 scope), plus one server-side caveat:
|
||||
|
||||
1. **Sticky melee is a no-op (register TS-39).** ACE arms every melee chase with
|
||||
`Sticky|UseFinalHeading|MoveAway|FailWalk`
|
||||
(`references/ACE/…/Monster_Navigation.cs:416`). Retail's sticky arrival hands
|
||||
off to `PositionManager::StickTo` → `StickyManager::adjust_offset`
|
||||
(0x00555430): per-tick 0.3 m edge-gap + facing tracking against the moving
|
||||
target. Our `MoveToManager.BeginNextNode` invokes the `StickTo` seam — unbound
|
||||
— so attackers complete-and-freeze at stale arrival poses until the next wire
|
||||
re-arm.
|
||||
2. **Arrival radii are zero.** ACE/retail arrive edge-to-edge
|
||||
(`cylinder_distance`, own + target radii from the PartArray/setup —
|
||||
`ACE PhysicsObj.MoveToObject` reads `GetRadius()/GetHeight()` from the TARGET).
|
||||
acdream binds `getOwnRadius: () => 0f` (GameWindow `EnsureRemoteMotionBindings`,
|
||||
the explicit R5-V3 pin) AND `RouteServerMoveTo` never sets
|
||||
`MovementStruct.Radius/Height` → both radii 0 → every attacker closes ~one
|
||||
body-radius deeper than retail → dogpile.
|
||||
3. Caveat: ACE has no server-side monster-vs-monster avoidance — some overlap is
|
||||
server-authoritative and shows on retail too. Acceptance = retail parity, not
|
||||
zero overlap.
|
||||
|
||||
Ruled out: the between-snap collision sweep (remotes run full
|
||||
`ResolveWithTransition`; `CollisionExemption` keeps creature cylinders collidable
|
||||
for non-viewer movers).
|
||||
|
||||
**Approved fix (R5-V3):** port `StickyManager` + bind `PositionManager`
|
||||
`StickTo`/`UnStick` seams + thread setup-derived cylsphere radii through the
|
||||
MoveTo distance math. **SSOT:** `docs/research/2026-07-04-171-sticky-melee-handoff.md`
|
||||
+ pickup `docs/research/2026-07-04-171-pickup-prompt.md`.
|
||||
|
||||
**Acceptance:** side-by-side vs retail in a scamp pack: attackers hold separation
|
||||
+ face the player while it strafes; user visual gate.
|
||||
|
||||
## #170 — Remote creature chase+attack renders wrong vs retail (glide, over-frequency, uniform attack anims)
|
||||
|
||||
**Status:** DONE (2026-07-04) — **user visual gate PASSED** ("looks good, as close
|
||||
|
|
|
|||
52
docs/research/2026-07-04-171-pickup-prompt.md
Normal file
52
docs/research/2026-07-04-171-pickup-prompt.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Pickup prompt — #171 sticky melee / R5-V3 (paste into a fresh session)
|
||||
|
||||
Read `docs/research/2026-07-04-171-sticky-melee-handoff.md` first — it is the
|
||||
SSOT for this task (root causes, retail anchors, approved scope, gotchas).
|
||||
Then implement R5-V3.
|
||||
|
||||
**Context:** #170 (chase slide) is CLOSED — gate passed at `4cad626f`; do not
|
||||
reopen that machinery. During its gate the user observed #171: in a pack
|
||||
melee, monsters sit partly inside each other with slightly stale facings vs
|
||||
retail on the same ACE. Investigated + **fix approved by the user 2026-07-04**.
|
||||
|
||||
**The two causes (both R5-V3 scope, already register-tracked):**
|
||||
1. Sticky melee is a no-op (TS-39): ACE arms melee chases with `Sticky`;
|
||||
retail's arrival hands off to `PositionManager::StickTo` →
|
||||
`StickyManager::adjust_offset` (0x00555430, per-tick 0.3 m gap + facing
|
||||
tracking). Our `MoveToManager.StickTo`/`Unstick` seams are unbound —
|
||||
attackers freeze at stale arrival poses.
|
||||
2. Arrival radii are zero: `getOwnRadius: () => 0f` (the R5-V3 pin in
|
||||
`EnsureRemoteMotionBindings`) + `RouteServerMoveTo` never sets
|
||||
`MovementStruct.Radius/Height` — retail/ACE arrive edge-to-edge with
|
||||
setup-derived radii (ACE `PhysicsObj.MoveToObject` reads the TARGET's
|
||||
PartArray radius/height).
|
||||
|
||||
**Do, in order:**
|
||||
1. Grep the named decomp for `StickyManager::` + `PositionManager::` bodies
|
||||
(adjust_offset 0x00555430; per-tick anchors already pinned:
|
||||
`PositionManager::adjust_offset` @0x00512d0e inside UpdatePositionInternal,
|
||||
`PositionManager::UseTime` @0x005159b3 in UpdateObjectInternal).
|
||||
Pseudocode, then port verbatim; cross-check
|
||||
`references/ACE/Source/ACE.Server/Physics/Managers/{StickyManager,PositionManager}.cs`.
|
||||
Groundwork already in tree: `MoveToMath.CylinderDistanceNoZ` /
|
||||
`GlobalToLocalVec` / `NormalizeCheckSmall`; `ConstraintManager` (R5-V1) is
|
||||
the port-style reference. Do NOT arm ConstraintManager (#167 separate).
|
||||
2. Bind `StickTo`/`UnStick` in `EnsureRemoteMotionBindings` AND the player
|
||||
wiring; wire PositionManager into the tick at the retail-matching points.
|
||||
3. Thread real cylsphere radii (own from the entity's Setup; target resolved
|
||||
at `RouteServerMoveTo`'s MoveToObject branch). Sweep remote + player.
|
||||
4. Same-commit register bookkeeping: retire TS-39, update the radius pin.
|
||||
5. Extend `RemoteChaseEndToEndHarnessTests` with a sticky scenario (arrive →
|
||||
target strafes → follower tracks gap + facing; UnStick on next
|
||||
PerformMovement). Harness bodies MUST mirror the live RemoteMotion
|
||||
construction (InWorld=true, RemoteWeenie — see RemoteChaseDrainBisectTests).
|
||||
6. `dotnet build` + full `dotnet test` green → commit → launch for the user's
|
||||
visual gate: pack melee side-by-side vs retail, strafe around the pack;
|
||||
attackers should reshuffle + keep facing like retail. Acceptance is retail
|
||||
PARITY — some overlap is ACE-server-side and shows on retail too.
|
||||
|
||||
**Gotchas:** revert-prone remote-motion area — verbatim port, no redesign;
|
||||
per-entity probes must print the guid (memory
|
||||
`feedback_probe_identity_attribution`); PowerShell Tee logs are UTF-16
|
||||
(`tr -d '\000'` before grep); the user manages client lifecycle (graceful
|
||||
close); both clients hit the same local ACE (127.0.0.1:9000).
|
||||
126
docs/research/2026-07-04-171-sticky-melee-handoff.md
Normal file
126
docs/research/2026-07-04-171-sticky-melee-handoff.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# Handoff — #171 group-melee interpenetration + facing drift (R5-V3: StickyManager + arrival radii) — 2026-07-04
|
||||
|
||||
Fresh session picks up HERE for #171. Worktree `vigorous-joliot-f0c3ad`, branch
|
||||
`claude/vigorous-joliot-f0c3ad`, tree CLEAN after the #170 close-out
|
||||
(`4cad626f`). The fix scope below is USER-APPROVED ("Ok I approve",
|
||||
2026-07-04). Pickup prompt: `docs/research/2026-07-04-171-pickup-prompt.md`.
|
||||
|
||||
## Symptom (user report, #170 gate session)
|
||||
|
||||
In a pack melee vs the player, acdream monsters end up **partly inside each
|
||||
other** with **slightly stale facings**, vs retail on the same local ACE.
|
||||
The #170 chase itself (turn → sustained run → plant → swing) is CLOSED and
|
||||
gate-passed — do not reopen that machinery.
|
||||
|
||||
## Root causes (code-grounded, investigated 2026-07-04)
|
||||
|
||||
1. **Sticky melee is a no-op — register TS-39.** ACE arms EVERY melee chase
|
||||
with `Sticky | UseFinalHeading | MoveAway | FailWalk`
|
||||
(`references/ACE/Source/ACE.Server/WorldObjects/Monster_Navigation.cs:406-419`).
|
||||
The retail arrival chain: `MoveToManager::BeginNextNode` (sticky bit set,
|
||||
queue empty) reads `SoughtObjectId/Radius/Height` BEFORE `CleanUp` zeroes
|
||||
them (that ordering IS already ported — `MoveToManager.cs` ~700-714) and
|
||||
calls `PositionManager::StickTo(tlid, radius, height)`; from then on
|
||||
`StickyManager::adjust_offset` (0x00555430) runs per tick and holds a
|
||||
~0.3 m edge gap + facing against the moving target. Every
|
||||
`MovementManager::PerformMovement` head calls `unstick_from_object` →
|
||||
`PositionManager::UnStick` (our `MoveToManager.PerformMovement:414`
|
||||
invokes the seam). acdream's `StickTo`/`Unstick` seams are UNBOUND
|
||||
(GameWindow `EnsureRemoteMotionBindings` + the player wiring bind
|
||||
neither) → attackers complete-and-freeze at stale arrival poses until the
|
||||
next wire re-arm. This is the direct cause of BOTH observed symptoms.
|
||||
|
||||
2. **Arrival radii are zero.** Retail/ACE arrive EDGE-TO-EDGE: ACE
|
||||
`PhysicsObj.MoveToObject` (`references/ACE/…/Physics/PhysicsObj.cs:936-956`)
|
||||
reads the TARGET's `PartArray.GetRadius()/GetHeight()` and passes them into
|
||||
`MoveToObject_Internal`; the mover's own radius feeds
|
||||
`GetCurrentDistance`'s `cylinder_distance` when `UseSpheres` (ACE default
|
||||
TRUE, `DistanceToObject` default 0.6). acdream:
|
||||
- `GameWindow.EnsureRemoteMotionBindings` binds `getOwnRadius/getOwnHeight
|
||||
: () => 0f` — the comment IS the R5-V3 pin ("setup cylsphere radius lands
|
||||
with R5-V3");
|
||||
- `GameWindow.RouteServerMoveTo` never sets `MovementStruct.Radius/Height`
|
||||
(target radii) → 0.
|
||||
Net: every attacker closes ~one body-radius deeper than retail → dogpile.
|
||||
|
||||
3. **Server-side caveat:** ACE has no monster-vs-monster avoidance; UP
|
||||
hard-snaps plant bodies where ACE says (retail hard-snaps identically).
|
||||
Some overlap is server-authoritative and visible on retail too —
|
||||
**acceptance is retail parity, not zero overlap.**
|
||||
|
||||
Ruled out: the between-snap collision sweep. Remotes run the full
|
||||
`ResolveWithTransition` (GameWindow ~10100) and `CollisionExemption` keeps
|
||||
creature cylinders collidable for non-viewer movers.
|
||||
|
||||
## Approved fix scope (= the R5-V3 slice, roadmap-aligned)
|
||||
|
||||
1. **Port `StickyManager` verbatim** (grep `docs/research/named-retail/
|
||||
acclient_2013_pseudo_c.txt` for `StickyManager::` first — `StickTo`,
|
||||
`UnStick`, `adjust_offset` 0x00555430, `HandleUpdateTarget`; cross-check
|
||||
`references/ACE/Source/ACE.Server/Physics/Managers/StickyManager.cs`).
|
||||
Groundwork ALREADY in tree (`MoveToMath`): `CylinderDistanceNoZ` (signed —
|
||||
the adjust_offset gap math), `GlobalToLocalVec`, `NormalizeCheckSmall` —
|
||||
all documented as adjust_offset consumers. `ConstraintManager` (the sibling
|
||||
PositionManager member) was ported R5-V1 (TS-35, unarmed — #167 is
|
||||
SEPARATE, do not arm it here).
|
||||
2. **PositionManager**: the facade that owns Sticky + Constraint
|
||||
(`references/ACE/…/Managers/PositionManager.cs`). Retail per-tick anchors
|
||||
pinned this session from the named decomp:
|
||||
- `PositionManager::adjust_offset` called inside
|
||||
`CPhysicsObj::UpdatePositionInternal` @0x00512d0e (BEFORE process_hooks);
|
||||
- `PositionManager::UseTime` called in `UpdateObjectInternal` @0x005159b3
|
||||
(AFTER `CPartArray::HandleMovement`).
|
||||
Wire the acdream equivalents at the matching points of the remote tick
|
||||
(GameWindow `TickAnimations`) and the player path.
|
||||
3. **Bind the seams**: `MoveToManager.StickTo` → PositionManager.StickTo;
|
||||
`MoveToManager.Unstick` → PositionManager.UnStick — in
|
||||
`EnsureRemoteMotionBindings` AND the player wiring (`EnterPlayerModeNow`
|
||||
area, ~13128). The TargetManager voyeur fan-out already delivers target
|
||||
info; the R5-V2 comment at GameWindow ~4327 says exactly this:
|
||||
"PositionManager sticky joins the fan-out in V3" — StickyManager is a
|
||||
voyeur consumer in retail (its own HandleUpdateTarget).
|
||||
4. **Thread real radii**: own radius/height from the entity's Setup (the
|
||||
spawn path already reads `setup.Radius`/`setup.Height` for collision
|
||||
registration, GameWindow ~4187); target radius/height at the
|
||||
`RouteServerMoveTo` MoveToObject branch (resolve the target entity's
|
||||
setup — retail reads the TARGET object, not the wire). Sweep BOTH the
|
||||
remote and player MoveToManager bindings.
|
||||
5. **Register bookkeeping (same commit):** retire TS-39; retire/narrow the
|
||||
radius pin note; new rows for any adaptation introduced.
|
||||
6. **Conformance:** extend `RemoteChaseEndToEndHarnessTests` with a sticky
|
||||
scenario — arm a sticky MoveToObject, arrive, then MOVE the target
|
||||
sideways: assert the follower tracks (gap ≈ stick distance, facing follows)
|
||||
and `UnStick` fires on the next PerformMovement head. The harness already
|
||||
models the full tick order; add a PositionManager step where retail has it.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Revert-prone area** (remote motion). Follow the workflow: grep named
|
||||
decomp FIRST, pseudocode, port verbatim, ACE cross-check, harness, then
|
||||
wire. No redesigns; the two prior #170 reverts were redesigns.
|
||||
- The harness gotcha from #170: any new harness body MUST replicate the live
|
||||
`RemoteMotion` construction (`InWorld=true`, `RemoteWeenie`,
|
||||
Contact|OnWalkable|Active) or the TS-40 guard strips links and everything
|
||||
wedges (`RemoteChaseDrainBisectTests` documents the shape).
|
||||
- Per-entity probes must print the guid (memory:
|
||||
`feedback_probe_identity_attribution` — the #170 misread).
|
||||
- `StickyManager::adjust_offset` sign semantics: inside the gap the signed
|
||||
distance goes NEGATIVE and the per-tick delta inverts (backs off) — ACE
|
||||
StickyManager.cs:156, already noted on `MoveToMath.CylinderDistanceNoZ`.
|
||||
- Do NOT arm ConstraintManager (#167 — two unknown x87 constants).
|
||||
- Visual gate at the end: pack melee side-by-side vs retail; the user strafes
|
||||
around the pack; attackers should reshuffle + keep facing like retail.
|
||||
|
||||
## Where things are (quick map)
|
||||
|
||||
- `MoveToManager.cs`: `PerformMovement:411` (Unstick head), `BeginNextNode`
|
||||
sticky arrival ~700-714, `GetCurrentDistance` (UseSpheres cylinder math).
|
||||
- `MoveToMath.cs`: `CylinderDistance`, `CylinderDistanceNoZ`,
|
||||
`GlobalToLocalVec`, `NormalizeCheckSmall`.
|
||||
- `GameWindow.cs`: `EnsureRemoteMotionBindings` ~4251 (seams + radius 0f pin),
|
||||
`RouteServerMoveTo` ~4457 (MovementStruct — Radius/Height unset), remote
|
||||
tick `TickAnimations` (PositionManager wiring points), player wiring ~13128.
|
||||
- `ConstraintManager.cs` (R5-V1) — PositionManager sibling, reference for the
|
||||
port style.
|
||||
- Register rows: TS-39 (sticky seams), TS-35 (ConstraintManager), TS-41/42
|
||||
(#170 rows, adjacent machinery).
|
||||
Loading…
Add table
Add a link
Reference in a new issue