Skip to content

Count multi-block placements (beds, doors) once, not twice (#86)#265

Merged
tastybento merged 2 commits into
developfrom
fix/86-bed-double-count
Jun 12, 2026
Merged

Count multi-block placements (beds, doors) once, not twice (#86)#265
tastybento merged 2 commits into
developfrom
fix/86-bed-double-count

Conversation

@tastybento

Copy link
Copy Markdown
Member

Fixes #86.

The bug

A bed counts as 2 on placement and leaks +1 on every place/break cycle, eventually blocking placement against a phantom limit. Reported by @Espantonius as still reproducing: "If you add a bed it counts as 2 but if you delete it, it only counts as 1."

Root cause

BlockMultiPlaceEvent extends BlockPlaceEvent and does not declare its own HandlerList (verified in the Paper 1.21.11 API source). Bukkit dispatches it on BlockPlaceEvent's shared handler list, and the per-handler isAssignableFrom guard passes for both registered handlers — so a single bed placement is delivered to both onBlock(BlockPlaceEvent) and onBlock(BlockMultiPlaceEvent), each calling process(block, true). That double-counts the placement, while breaking the bed fires a single BlockBreakEvent and decrements once → a permanent +1 leak.

This affects any multi-block placement that fires BlockMultiPlaceEvent: beds, doors, double-tall plants, etc.

Fix

Skip BlockMultiPlaceEvent instances in the BlockPlaceEvent handler and let the dedicated onBlock(BlockMultiPlaceEvent) count them once — the same subtype guard onBlock(BlockFormEvent) already uses for EntityBlockFormEvent/BlockSpreadEvent.

Tests

Two new tests dispatch through the real plugin manager (a direct handler call would hide the double delivery):

  • testBedPlacedViaMultiPlaceCountsOnce — a bed counts once (was 2 before the fix)
  • testBedPlaceThenBreakLeavesZero — place + break returns to 0 (was 1 before the fix)

Both fail red on the current code and pass with the fix. Full suite: 235 tests, 0 failures.

🤖 Generated with Claude Code

tastybento and others added 2 commits June 12, 2026 08:01
BlockMultiPlaceEvent extends BlockPlaceEvent and does not declare its own
HandlerList, so a single multi-place dispatch is delivered to BOTH
onBlock(BlockPlaceEvent) and onBlock(BlockMultiPlaceEvent) — each calling
process(block, true). A bed (or door, double-tall plant) was therefore
counted twice on placement, while breaking it fired a single BlockBreakEvent
and decremented once, permanently leaking +1 per place/break cycle until the
island hit a phantom limit.

Skip BlockMultiPlaceEvent instances in the BlockPlaceEvent handler and let
the dedicated handler count them once — the same subtype-guard pattern
onBlock(BlockFormEvent) already uses for its subclasses.

Tests dispatch through the real plugin manager (so the double delivery
actually occurs) and assert a bed counts once and returns to zero after a
break.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@tastybento tastybento merged commit 03b1166 into develop Jun 12, 2026
3 checks passed
@tastybento tastybento deleted the fix/86-bed-double-count branch June 12, 2026 15:37
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.

Bed issue

1 participant