research: file #319 — player-parented children never get a canonical cell, and route 7's gate cannot see it
Found by asking why route 7's connected gate stayed thin (one cause=propagate
across 5-6 equipped landblock crossings) instead of recording the thinness and
moving on.
EquippedChildRenderController.cs:134 hardcodes ParentInstanceSequence: 0 for a
parented CreateObject. Correct for creatures and statics, which are genuinely
sequence 0; wrong for players, whose ObjectInstance is Character.TotalLogins
(ACE Player_Networking.cs:37). The relation files under (playerGuid, 0) while
the record carries TotalLogins, so both route-7 write sites — D1's attach
re-cell and D2's propagation lookup — key on an incarnation that never matches.
TryCommitParent does not validate the sequence, so the attach succeeds and
prints normally.
This is a cd3129e9 (route 7) REGRESSION that un-masked a latent bug: the
TickChild call route 7 deleted was keyed on the child guid alone and was
structurally immune to a wrong parent key. Scope is wider than the local
player — every remote player's equipment is affected. Proven by class: every
probe-firing parent across both captured gate logs is 0x7/0x8 (sequence 0);
the sole 0x5 player parent is the sole failure.
User-visible consequence is NIL and that was verified rather than assumed —
rendering has an explicit fallback and attached children are structurally
excluded from spatial roots, physics worksets, collision retirement, radar and
picking.
THE FINDING THAT OUTRANKS THE DEFECT, and it is a flaw in my own gate design:
route 7's owed gate accepts a session "only if cause=propagate lines appear".
A zero-cell player child emits NO line, so the defect's signature is ABSENCE,
which that criterion reads as "not exercised" rather than "broken". Two
captured gate logs contain the defect and neither flags it. A gate that cannot
fail in the presence of its own target bug is worse than no gate — it
manufactures confidence. This is the same shape as route 3's round-2
regression, which I criticised at length in the closeout while shipping this.
The fix is deliberately NOT attempted here: it has more blast radius than the
bug. The player's canonical cell does not track the player during ordinary
movement, so correcting the key alone yields a stale cell rather than a right
one; and three sites are inert only because the cell is zero and would wake on
a fix (the hydration projectionCellId filter, RestoreShadow's broadphase row,
and the initial-create residence FullCellId refusal).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0991182198
commit
195667db94
2 changed files with 566 additions and 0 deletions
|
|
@ -13260,3 +13260,75 @@ preserved verbatim, not fixed, per this row's own resolution above. Covered
|
|||
by `LandingPacket_PlayerGuid_QueueClearedNoShadowPublish_316Preserved` /
|
||||
`LandingPacket_CreatureGuid_ShadowPublishedQueueNotCleared` in
|
||||
`tests/AcDream.App.Tests/Physics/LiveEntityNetworkOnPositionCollapseMatrixTests.cs`.
|
||||
|
||||
## #319 — A player-parented child never receives a canonical cell (ParentInstanceSequence hardcoded 0)
|
||||
|
||||
**Status:** OPEN
|
||||
**Severity:** LOW for the user (no observable symptom — verified, not assumed),
|
||||
HIGH for process (it defeats route 7's own connected gate; see below)
|
||||
**Filed:** 2026-08-05
|
||||
**Component:** physics / entity lifetime / equipped children
|
||||
**Regressed by:** `cd3129e9` (C4 route 7), which un-masked a pre-existing latent
|
||||
bug rather than creating it.
|
||||
|
||||
**Root cause.** `EquippedChildRenderController.cs:134` hardcodes
|
||||
`ParentInstanceSequence: 0` for a `CreateObject` carrying a parent. Correct for
|
||||
creatures and statics, which genuinely are sequence 0. WRONG for players: ACE
|
||||
sets a player's `ObjectInstance` to `Character.TotalLogins`
|
||||
(`Player_Networking.cs:37`), which acdream parses into
|
||||
`RuntimeEntityRecord.Incarnation`. The relation is therefore filed under
|
||||
`(playerGuid, 0)` while the record carries `TotalLogins`, and BOTH route-7
|
||||
write sites key on the record's real incarnation:
|
||||
- D1 attach re-cell — `RuntimeEntityObjectLifetime.cs:1537`
|
||||
`parent.Incarnation == parentInstanceSequence` → false.
|
||||
- D2 propagation — `RuntimeEntityDirectory.cs:478-480`
|
||||
`ChildrenAttachedToParent(guid, current.Incarnation)` → empty, forever.
|
||||
|
||||
`TryCommitParent` never validates the sequence, so the attach succeeds and
|
||||
`equipment: attached` prints normally. Silent.
|
||||
|
||||
**Why route 7 owns it.** The deleted `TickChild` call reached
|
||||
`RebucketLiveEntity` → `CommitRebucket` → `SetFullCell` keyed on the CHILD guid
|
||||
alone, sourced from the parent's App-side `ParentCellId` — structurally immune
|
||||
to a wrong parent key, and it tracked the local player exactly. Its replacement
|
||||
`RebucketLiveEntityPresentationOnly` deliberately never commits canonical cell
|
||||
(`LiveEntityRuntime.cs:1026-1028`).
|
||||
|
||||
**Scope is wider than the local player: every REMOTE player's equipment too.**
|
||||
Proven by class rather than anecdote — every probe-firing parent across both
|
||||
captured gate logs is `0x7…` (static) or `0x8…` (dynamic), i.e. sequence 0; the
|
||||
sole `0x5…` player parent is the sole failure.
|
||||
|
||||
**User-visible consequence: NIL, verified.** Rendering has an explicit fallback
|
||||
(`LiveRenderProjectionJournal.cs:270-277`); attached children are structurally
|
||||
excluded from spatial roots, physics/projectile worksets, the collision
|
||||
retirement sweep, radar, and `WorldPicker`; VFX redirect to the parent;
|
||||
landblock unload parks rather than destroys. Headless bots do not misreport.
|
||||
|
||||
**THE PROCESS FINDING, which outranks the defect.** Route 7's still-owed
|
||||
connected gate accepts a session "only if `cause=propagate` lines appear". A
|
||||
zero-cell player child emits NO line, so the defect's signature is ABSENCE —
|
||||
which the criterion reads as "not exercised" rather than "broken". Two captured
|
||||
gate logs contain this defect and neither flags it. **The criterion is
|
||||
unfalsifiable in the presence of the bug it exists to catch**, which is worse
|
||||
than having no gate, because it manufactures confidence. Corrected in the
|
||||
closeout handoff: the gate must now assert a POSITIVE — the equipped child's
|
||||
`FullCellId` equals the parent's after a crossing — not merely count probe
|
||||
lines.
|
||||
|
||||
**Do NOT rush the fix; it has more blast radius than the bug.**
|
||||
1. The player's canonical cell does not track the player during ordinary
|
||||
movement (only login activation, inbound Position/ForcePosition, and
|
||||
teleport write it; WASD passes a landblock id that
|
||||
`LiveEntityRuntime.cs:935-938` explicitly preserves the old cell for). So
|
||||
correcting the key ALONE yields a stale cell, not a correct one.
|
||||
2. Three sites are inert only because the cell is zero and would wake on a fix:
|
||||
the hydration `projectionCellId != 0` filter
|
||||
(`LiveEntityHydrationController.cs:551-554`, opens a two-writer window),
|
||||
`RestoreShadow` (`LiveEntityPresentationController.cs:216-236`, installs a
|
||||
broadphase row route 7 says should not exist), and
|
||||
`RuntimeInitialCreateResidenceState.Begin`'s `FullCellId != 0` refusal
|
||||
(`:583`).
|
||||
|
||||
Full analysis, with the headless test's structural immunity explained:
|
||||
[`2026-08-05-local-player-child-propagation.md`](research/2026-08-05-local-player-child-propagation.md)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue