From 4d160465df91903c903b9fc5a5c578b64ff8638c Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 17 Jul 2026 21:18:03 -0700 Subject: [PATCH] fix: respect actionbar/bossbar config settings in BossBarListener (#537) The BossBarListener is registered into Bukkit by BentoBox's FlagsManager whenever the ONEBLOCK_BOSSBAR or ONEBLOCK_ACTIONBAR flag is registered. With actionbar: false and bossbar: true in config.yml, the listener was still active via the boss bar flag, and tryToShowActionBar only checked the island flag - which defaults to allowed even when unregistered - so the action bar showed despite being disabled. Guard both tryToShowActionBar and tryToShowBossBar with their config settings, and remove the explicit registerListener(bossBar) call in onEnable which duplicated the FlagsManager registration and caused the handlers to fire twice per event when both settings were enabled. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012eC7vj7ghNGSNGpxqQazxm --- .../world/bentobox/aoneblock/AOneBlock.java | 6 +- .../aoneblock/listeners/BossBarListener.java | 10 ++ .../listeners/BossBarListenerTest.java | 144 ++++++++++++++++++ 3 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/test/java/world/bentobox/aoneblock/listeners/BossBarListenerTest.java diff --git a/src/main/java/world/bentobox/aoneblock/AOneBlock.java b/src/main/java/world/bentobox/aoneblock/AOneBlock.java index 601038a..ea187e4 100644 --- a/src/main/java/world/bentobox/aoneblock/AOneBlock.java +++ b/src/main/java/world/bentobox/aoneblock/AOneBlock.java @@ -199,9 +199,9 @@ public void onEnable() { registerListener(new BlockProtect(this)); registerListener(new JoinLeaveListener(this)); registerListener(new InfoListener(this)); - if (getSettings().isBossBar() && getSettings().isActionBar()) { - registerListener(bossBar); - } + // Note: bossBar is registered as a listener by the FlagsManager when the + // ONEBLOCK_BOSSBAR or ONEBLOCK_ACTIONBAR flag is registered in onLoad, so it + // must not be registered here too or events would be handled twice // Register placeholders phManager = new AOneBlockPlaceholders(this, getPlugin().getPlaceholdersManager()); diff --git a/src/main/java/world/bentobox/aoneblock/listeners/BossBarListener.java b/src/main/java/world/bentobox/aoneblock/listeners/BossBarListener.java index 966d8d8..ece5aa2 100644 --- a/src/main/java/world/bentobox/aoneblock/listeners/BossBarListener.java +++ b/src/main/java/world/bentobox/aoneblock/listeners/BossBarListener.java @@ -91,6 +91,11 @@ public static Component bukkitToAdventure(String legacyString) { } private void tryToShowActionBar(UUID uuid, Island island) { + // The listener is registered whenever either the boss bar or action bar flag is + // registered, so the global setting must be checked here as well as the flag + if (!addon.getSettings().isActionBar()) { + return; + } User user = User.getInstance(uuid); Player player = Bukkit.getPlayer(uuid); @@ -125,6 +130,11 @@ private void tryToShowActionBar(UUID uuid, Island island) { * @param island island they are on */ private void tryToShowBossBar(UUID uuid, Island island) { + // The listener is registered whenever either the boss bar or action bar flag is + // registered, so the global setting must be checked here as well as the flag + if (!addon.getSettings().isBossBar()) { + return; + } User user = User.getInstance(uuid); // Only show if enabled for island diff --git a/src/test/java/world/bentobox/aoneblock/listeners/BossBarListenerTest.java b/src/test/java/world/bentobox/aoneblock/listeners/BossBarListenerTest.java new file mode 100644 index 0000000..8bc4bec --- /dev/null +++ b/src/test/java/world/bentobox/aoneblock/listeners/BossBarListenerTest.java @@ -0,0 +1,144 @@ +package world.bentobox.aoneblock.listeners; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.boss.BossBar; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import net.kyori.adventure.text.Component; +import world.bentobox.aoneblock.AOneBlock; +import world.bentobox.aoneblock.CommonTestSetup; +import world.bentobox.aoneblock.Settings; +import world.bentobox.aoneblock.dataobjects.OneBlockIslands; +import world.bentobox.aoneblock.events.MagicBlockEvent; +import world.bentobox.aoneblock.oneblocks.OneBlocksManager; + +/** + * Tests the display of the boss bar and action bar, and in particular that the + * config.yml settings turn them off (https://github.com/BentoBoxWorld/AOneBlock/issues/537). + * @author tastybento + */ +public class BossBarListenerTest extends CommonTestSetup { + + private AOneBlock addon; + private Settings settings; + private BossBarListener bbl; + @Mock + private OneBlocksManager obm; + @Mock + private OneBlockIslands obi; + @Mock + private BossBar bossBar; + @Mock + private Block block; + + /** + */ + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + + addon = spy(new AOneBlock()); + settings = new Settings(); + addon.setSettings(settings); + doNothing().when(addon).logError(anyString()); + doReturn(obm).when(addon).getOneBlockManager(); + doReturn(obi).when(addon).getOneBlocksIsland(any()); + + // Phase progress + when(obi.getPhaseName()).thenReturn("Plains"); + when(obm.getNextPhaseBlocks(obi)).thenReturn(100); + when(obm.getPhaseBlocks(obi)).thenReturn(500); + when(obm.getPercentageDone(obi)).thenReturn(80D); + + // Bukkit + mockedBukkit.when(() -> Bukkit.getPlayer(uuid)).thenReturn(mockPlayer); + mockedBukkit.when(() -> Bukkit.createBossBar(anyString(), any(), any())).thenReturn(bossBar); + when(bossBar.getPlayers()).thenReturn(Collections.emptyList()); + + bbl = new BossBarListener(addon); + } + + /** + */ + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + private void fireMagicBlockEvent() { + bbl.onBreakBlockEvent(new MagicBlockEvent(island, uuid, null, block, Material.STONE)); + } + + /** + * Test that the action bar is shown when enabled in the config and allowed on the island. + */ + @Test + void testActionBarShownWhenEnabled() { + when(island.isAllowed(addon.ONEBLOCK_ACTIONBAR)).thenReturn(true); + fireMagicBlockEvent(); + verify(mockPlayer).sendActionBar(any(Component.class)); + } + + /** + * Test for https://github.com/BentoBoxWorld/AOneBlock/issues/537 - the action bar + * must not be shown when disabled in config.yml, even though the boss bar flag has + * registered the listener. + */ + @Test + void testActionBarNotShownWhenDisabledInConfig() { + settings.setActionBar(false); + when(island.isAllowed(addon.ONEBLOCK_ACTIONBAR)).thenReturn(true); + fireMagicBlockEvent(); + verify(mockPlayer, never()).sendActionBar(any(Component.class)); + } + + /** + * Test that the action bar is not shown when the island flag denies it. + */ + @Test + void testActionBarNotShownWhenFlagDenied() { + // island.isAllowed is false by default in CommonTestSetup + fireMagicBlockEvent(); + verify(mockPlayer, never()).sendActionBar(any(Component.class)); + } + + /** + * Test that the boss bar is shown when enabled in the config and allowed on the island. + */ + @Test + void testBossBarShownWhenEnabled() { + when(island.isAllowed(addon.ONEBLOCK_BOSSBAR)).thenReturn(true); + fireMagicBlockEvent(); + verify(bossBar).addPlayer(mockPlayer); + } + + /** + * Test that the boss bar is not shown when disabled in config.yml. + */ + @Test + void testBossBarNotShownWhenDisabledInConfig() { + settings.setBossBar(false); + when(island.isAllowed(addon.ONEBLOCK_BOSSBAR)).thenReturn(true); + fireMagicBlockEvent(); + mockedBukkit.verify(() -> Bukkit.createBossBar(anyString(), any(), any()), never()); + verify(bossBar, never()).addPlayer(any()); + } +}