fix(characters): stop extrapolating a dying entity's movement state into an NPE - #5380
fix(characters): stop extrapolating a dying entity's movement state into an NPE#5380soloturn wants to merge 1 commit into
Conversation
…nto an NPE Fixes #4969. ServerCharacterPredictionSystem.onDestroy (BeforeDeactivateComponent, gated on CharacterMovementComponent/LocationComponent/AliveCharacterComponent) fires as soon as any one of those three is removed - e.g. an entity losing AliveCharacterComponent mid-death, before the rest of it is torn down. It only queues the entity into characterStatesToRemove rather than removing it from characterStates immediately; that queue is drained once per update() call. In between, restoreToPresent() and lagCompensate() both iterate characterStates unconditionally and can call setToTime -> setToExtrapolateState -> extrapolateCharacterMovementComponent on an entity whose CharacterMovementComponent is already gone, which unconditionally dereferenced the null getComponent() result. Two changes: - restoreToPresent()/lagCompensate() now skip entries already queued in characterStatesToRemove, matching what update()'s own replication loop already did for the same reason. - setToExtrapolateState() now checks for both components up front and returns if either is missing, mirroring setToState()'s existing guard in the same class - defense in depth in case some other path reaches it with a torn-down entity. Regression test added: CharacterMovementSystemUtilityTest. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughSummary by CodeRabbit
WalkthroughCharacter movement extrapolation now checks for required components before updating state. Server prediction skips entities queued for deferred removal during lag compensation and state restoration. Regression tests cover missing-component and valid-component behavior. ChangesCharacter prediction safeguards
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The change prevents movement-state processing from dereferencing components that have already been removed during entity teardown, while preserving normal movement updates. No actionable merge-blocking risk remains after normal checks and review. Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Fixes #4969 - server crash (
NullPointerExceptioninCharacterMovementSystemUtility.extrapolateCharacterMovementComponent) when a character entity dies while another client still has it queued for a state update.ServerCharacterPredictionSystem.onDestroy(BeforeDeactivateComponent, gated onCharacterMovementComponent/LocationComponent/AliveCharacterComponent) fires as soon as any one of those three components is removed - e.g. an entity losingAliveCharacterComponentmid-death, before the rest of it is torn down. It only queues the entity intocharacterStatesToRemoverather than removing it fromcharacterStatesimmediately; that queue is drained once perupdate()call. In between,restoreToPresent()andlagCompensate()both iteratecharacterStatesunconditionally and can reachsetToTime -> setToExtrapolateState -> extrapolateCharacterMovementComponentfor an entity whoseCharacterMovementComponentis already gone - which unconditionally dereferenced thenullgetComponent()result, exactly the reported traceback.Changes
restoreToPresent()/lagCompensate()now skip entries already queued incharacterStatesToRemove, matching whatupdate()'s own replication loop already did for the same reason.setToExtrapolateState()now checks for both components up front and returns if either is missing, mirroringsetToState()'s existing guard in the same class - defense in depth in case some other path reaches it with a torn-down entity.Test plan
CharacterMovementSystemUtilityTest(3/3 pass): reproduces the exact NPE without the fix, confirms it's gone with it, and confirms a normal (both-components-present) call still updates the entity as before.logic.characters.*unit tests (KinematicCharacterMoverTest,VisualCharacterSystemTest) still pass.