Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,15 @@ public void onCreatureSpawn(final CreatureSpawnEvent creatureSpawnEvent) {
justSpawned.remove(creatureSpawnEvent.getEntity().getUniqueId());
return;
}
// BEEHIVE: a bee leaving its hive is not a new bee — its count was decremented when it
// entered (EntityRemoveEvent ENTER_BLOCK), and the MONITOR tracker re-increments on exit,
// so the enter/exit cycle is net-zero. Cancelling the exit would strand the bee: the
// server keeps it as a hive occupant and retries every few ticks, spamming the hit-limit
// message and permanently trapping bees whenever the island is at its limit for any other
// reason. Over-limit bees (e.g. from placing a hive item with stored bees) simply block
// further spawns and breeding until the population drops.
if (creatureSpawnEvent.getSpawnReason().equals(SpawnReason.SHOULDER_ENTITY)
|| creatureSpawnEvent.getSpawnReason().equals(SpawnReason.BEEHIVE)
|| (!(creatureSpawnEvent.getEntity() instanceof Villager)
&& creatureSpawnEvent.getSpawnReason().equals(SpawnReason.BREEDING))) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,57 @@ void testCreatureSpawnDebounceSkipsSecond() throws Exception {
assertFalse(event.isCancelled());
}

// --- Bee hive tests ---

@Test
void testBeehiveExitAtLimitNotCancelled() {
// A bee leaving its hive was decremented when it entered (ENTER_BLOCK), so the exit
// must never be limit-checked — cancelling it strands the bee in the hive and the
// server retries forever, spamming the hit-limit message.
ibc.setEntityLimit(Environment.NORMAL, EntityType.BEE, 1);
ibc.incrementEntity(Environment.NORMAL, EntityType.BEE);
LivingEntity bee = mockEntity(EntityType.BEE, location);

CreatureSpawnEvent event = new CreatureSpawnEvent(bee, SpawnReason.BEEHIVE);

ell.onCreatureSpawn(event);

assertFalse(event.isCancelled());
verify(islandsManager, never()).getIslandAt(any(Location.class));
}

@Test
void testBeehiveExitStillCounted() throws Exception {
// Exempting the exit from the limit check must not exempt it from counting.
LivingEntity bee = mockEntity(EntityType.BEE, location);
CreatureSpawnEvent event = new CreatureSpawnEvent(bee, SpawnReason.BEEHIVE);

ell.onCreatureSpawnTrack(event);

assertEquals(1, ibc.getEntityCount(Environment.NORMAL, EntityType.BEE));
assertEquals("test-island-id", entityIslandMap().get(bee.getUniqueId()));
}

@Test
void testBeeHiveEnterExitCycleNetZero() throws Exception {
// Full cycle: a counted bee enters a hive (ENTER_BLOCK removal decrements) and is
// later released (BEEHIVE spawn re-increments) — the count must end where it started.
LivingEntity bee = mockEntity(EntityType.BEE, location);
ibc.incrementEntity(Environment.NORMAL, EntityType.BEE);
entityIslandMap().put(bee.getUniqueId(), "test-island-id");

ell.onEntityRemove(new EntityRemoveEvent(bee, EntityRemoveEvent.Cause.ENTER_BLOCK));
assertEquals(0, ibc.getEntityCount(Environment.NORMAL, EntityType.BEE));

LivingEntity released = mockEntity(EntityType.BEE, location);
CreatureSpawnEvent exit = new CreatureSpawnEvent(released, SpawnReason.BEEHIVE);
ell.onCreatureSpawn(exit);
assertFalse(exit.isCancelled());
ell.onCreatureSpawnTrack(exit);

assertEquals(1, ibc.getEntityCount(Environment.NORMAL, EntityType.BEE));
}

// --- Copper golem / copper chest limit tests (#276) ---

@Test
Expand Down
Loading