Skip to content

Fix phantom entity counts left by portaled mobs#289

Merged
tastybento merged 1 commit into
developfrom
fix/portal-phantom-entity-counts
Jul 10, 2026
Merged

Fix phantom entity counts left by portaled mobs#289
tastybento merged 1 commit into
developfrom
fix/portal-phantom-entity-counts

Conversation

@tastybento

Copy link
Copy Markdown
Member

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

onEntityPortal transfers the count between environments and recorded the entity in justPortaled, expecting the source-world removal to fire an EntityRemoveEvent that must be skipped. That event never fires. In Paper, Entity.removeAfterChangingDimensions() calls setRemoved(RemovalReason.CHANGED_DIMENSION, null) with a null Bukkit cause, and CraftEventFactory.callEntityRemoveEvent() returns early on a null cause — with a comment saying this is exactly the dimension-change case.

So the justPortaled entry 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). The List also grew unboundedly.

Fix

Delete the justPortaled mechanism. The portal event alone moves the count between environments; the eventual death fires exactly one EntityRemoveEvent, 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 with expected: <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 calc recount once (with the island's players online so the chunks are loaded), after updating.

🤖 Generated with Claude Code

https://claude.ai/code/session_015dbJyrv2uxHfTmiWkqGUJB

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
@sonarqubecloud

Copy link
Copy Markdown

@tastybento tastybento merged commit 0232df3 into develop Jul 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant