Fix phantom entity counts left by portaled mobs#289
Merged
Conversation
Paper never fires EntityRemoveEvent for a dimension change: Entity.removeAfterChangingDimensions() passes a null Bukkit cause and CraftEventFactory.callEntityRemoveEvent() skips the event when the cause is null. The justPortaled guard therefore waited for an event that never comes, and its stale entry swallowed the entity's real death decrement instead — every mob that crossed a portal left a permanent +1 in the environment where it died (and the list grew unboundedly). Remove the guard: onEntityPortal alone transfers the count between environments, and the eventual death fires exactly one EntityRemoveEvent in the destination world. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015dbJyrv2uxHfTmiWkqGUJB
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Problem
Server admins keep seeing entity limits (e.g.
Chicken 10/10) with far fewer — or zero — of that mob actually on the island. A recount fixes it, then the count creeps back up.Root cause
onEntityPortaltransfers the count between environments and recorded the entity injustPortaled, expecting the source-world removal to fire anEntityRemoveEventthat must be skipped. That event never fires. In Paper,Entity.removeAfterChangingDimensions()callssetRemoved(RemovalReason.CHANGED_DIMENSION, null)with a null Bukkit cause, andCraftEventFactory.callEntityRemoveEvent()returns early on a null cause — with a comment saying this is exactly the dimension-change case.So the
justPortaledentry was never consumed. When the mob later genuinely died, the stale entry swallowed the death decrement, leaving a permanent phantom +1 in the environment where it died. Every mob that ever crossed a portal contributed one on death (nether chicken jockeys wandering back through portals are a steady chicken-flavoured source of these). TheListalso grew unboundedly.Fix
Delete the
justPortaledmechanism. The portal event alone moves the count between environments; the eventual death fires exactly oneEntityRemoveEvent, in the destination world, and decrements normally.Tests
testEntityPortalTransfersCountBetweenEnvironments— portal moves the count overworld → nether and updates the island mapping.testPortaledEntityDeathStillDecrements— portal then death leaves no phantom count (fails withexpected: <0> but was: <1>on the old code).testRoundTripPortalThenDeathLeavesNoPhantomCount— the reported symptom: nether round trip then overworld death returns the overworld count to zero (also fails on the old code).Full suite: 300 tests, all green. Verified both regression tests fail against the pre-fix listener.
Note for affected servers: phantom counts already in the database are corrected by running the admin
limits calcrecount once (with the island's players online so the chunks are loaded), after updating.🤖 Generated with Claude Code
https://claude.ai/code/session_015dbJyrv2uxHfTmiWkqGUJB