From b6b0b53463acfefad2c8d3d2e26921a28de71fad Mon Sep 17 00:00:00 2001 From: Baterka <3463591+Baterka@users.noreply.github.com> Date: Sat, 25 Nov 2023 17:44:58 +0100 Subject: [PATCH] Fixed `cooldown` requirement (#360) * Fixed: - `Requirement - cooldown` was not working properly Refactors: - Minor refactors to `BlockListener.process` method for better readability * Clarified that requirement value is in seconds * - Moved `setNewPhase` back to `CheckPhase` class - Fixed tests - Refactored test run args as they were weirdly formatted * Removed unused imports --------- Co-authored-by: tastybento --- pom.xml | 21 +- .../aoneblock/listeners/BlockListener.java | 617 +++++++++--------- .../aoneblock/listeners/CheckPhase.java | 101 +-- .../aoneblock/listeners/HoloListener.java | 11 +- .../listeners/ItemsAdderListener.java | 3 +- .../oneblocks/OneBlockCustomBlock.java | 1 + .../oneblocks/OneBlockCustomBlockCreator.java | 11 +- .../aoneblock/oneblocks/Requirement.java | 2 + .../oneblocks/customblock/MobCustomBlock.java | 11 +- src/main/resources/phases/0_plains.yml | 2 +- .../dataobjects/OneBlockIslandsTest.java | 5 +- .../aoneblock/listeners/CheckPhaseTest.java | 318 +++++---- .../listeners/NoBlockHandlerTest.java | 39 +- 13 files changed, 584 insertions(+), 558 deletions(-) diff --git a/pom.xml b/pom.xml index e52dca4..d29310d 100644 --- a/pom.xml +++ b/pom.xml @@ -292,26 +292,19 @@ --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED - --add-opens - java.base/java.util.stream=ALL-UNNAMED + --add-opens java.base/java.util.stream=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED - --add-opens - java.base/java.util.regex=ALL-UNNAMED - --add-opens - java.base/java.nio.channels.spi=ALL-UNNAMED + --add-opens java.base/java.util.regex=ALL-UNNAMED + --add-opens java.base/java.nio.channels.spi=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED - --add-opens - java.base/java.util.concurrent=ALL-UNNAMED + --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/sun.nio.fs=ALL-UNNAMED --add-opens java.base/sun.nio.cs=ALL-UNNAMED --add-opens java.base/java.nio.file=ALL-UNNAMED - --add-opens - java.base/java.nio.charset=ALL-UNNAMED - --add-opens - java.base/java.lang.reflect=ALL-UNNAMED - --add-opens - java.logging/java.util.logging=ALL-UNNAMED + --add-opens java.base/java.nio.charset=ALL-UNNAMED + --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.logging/java.util.logging=ALL-UNNAMED --add-opens java.base/java.lang.ref=ALL-UNNAMED --add-opens java.base/java.util.jar=ALL-UNNAMED --add-opens java.base/java.util.zip=ALL-UNNAMED diff --git a/src/main/java/world/bentobox/aoneblock/listeners/BlockListener.java b/src/main/java/world/bentobox/aoneblock/listeners/BlockListener.java index fc3ca58..d6234ec 100644 --- a/src/main/java/world/bentobox/aoneblock/listeners/BlockListener.java +++ b/src/main/java/world/bentobox/aoneblock/listeners/BlockListener.java @@ -104,90 +104,90 @@ public class BlockListener implements Listener { private final Random random = new Random(); - /** * @param addon - OneBlock */ public BlockListener(@NonNull AOneBlock addon) { - this.addon = addon; - handler = new Database<>(addon, OneBlockIslands.class); - cache = new HashMap<>(); - oneBlocksManager = addon.getOneBlockManager(); - check = new CheckPhase(addon, this); - warningSounder = new WarningSounder(addon); + this.addon = addon; + handler = new Database<>(addon, OneBlockIslands.class); + cache = new HashMap<>(); + oneBlocksManager = addon.getOneBlockManager(); + check = new CheckPhase(addon, this); + warningSounder = new WarningSounder(addon); } /** * Save the island cache */ public void saveCache() { - cache.values().forEach(handler::saveObjectAsync); + cache.values().forEach(handler::saveObjectAsync); } - // --------------------------------------------------------------------- // Section: Listeners // --------------------------------------------------------------------- - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onNewIsland(IslandCreatedEvent e) { - if (addon.inWorld(e.getIsland().getWorld())) { - setUp(e.getIsland()); - } + if (addon.inWorld(e.getIsland().getWorld())) { + setUp(e.getIsland()); + } } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onNewIsland(IslandResettedEvent e) { - if (addon.inWorld(e.getIsland().getWorld())) { - setUp(e.getIsland()); - } + if (addon.inWorld(e.getIsland().getWorld())) { + setUp(e.getIsland()); + } } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onDeletedIsland(IslandDeleteEvent e) { - if (addon.inWorld(e.getIsland().getWorld())) { - cache.remove(e.getIsland().getUniqueId()); - handler.deleteID(e.getIsland().getUniqueId()); - } + if (addon.inWorld(e.getIsland().getWorld())) { + cache.remove(e.getIsland().getUniqueId()); + handler.deleteID(e.getIsland().getUniqueId()); + } } /** * Prevents liquids flowing into magic block + * * @param e BlockFromToEvent */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlockFromTo(final BlockFromToEvent e) { - if (!addon.inWorld(e.getBlock().getWorld())) { - return; - } - Location l = e.getToBlock().getLocation(); - // Cannot flow to center block - e.setCancelled(addon.getIslands().getIslandAt(l).filter(i -> l.equals(i.getCenter())).isPresent()); + if (!addon.inWorld(e.getBlock().getWorld())) { + return; + } + Location l = e.getToBlock().getLocation(); + // Cannot flow to center block + e.setCancelled(addon.getIslands().getIslandAt(l).filter(i -> l.equals(i.getCenter())).isPresent()); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlockBreak(final BlockBreakEvent e) { - if (!addon.inWorld(e.getBlock().getWorld())) { - return; - } - Location l = e.getBlock().getLocation(); - addon.getIslands().getIslandAt(l).filter(i -> l.equals(i.getCenter())).ifPresent(i -> process(e, i, e.getPlayer(), e.getPlayer().getWorld())); + if (!addon.inWorld(e.getBlock().getWorld())) { + return; + } + Location l = e.getBlock().getLocation(); + addon.getIslands().getIslandAt(l).filter(i -> l.equals(i.getCenter())) + .ifPresent(i -> process(e, i, e.getPlayer(), e.getPlayer().getWorld())); } /** - * Handles JetsMinions. These are special armor stands. Requires Minions 6.9.3 or later + * Handles JetsMinions. These are special armor stands. Requires Minions 6.9.3 + * or later * * @param e - event */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlockBreakByMinion(final EntityInteractEvent e) { - if (!addon.inWorld(e.getBlock().getWorld()) || !e.getEntityType().equals(EntityType.ARMOR_STAND)) { - return; - } - Location l = e.getBlock().getLocation(); - addon.getIslands().getIslandAt(l).filter(i -> l.equals(i.getCenter())).ifPresent(i -> process(e, i, null, e.getBlock().getWorld())); + if (!addon.inWorld(e.getBlock().getWorld()) || !e.getEntityType().equals(EntityType.ARMOR_STAND)) { + return; + } + Location l = e.getBlock().getLocation(); + addon.getIslands().getIslandAt(l).filter(i -> l.equals(i.getCenter())) + .ifPresent(i -> process(e, i, null, e.getBlock().getWorld())); } /** @@ -197,70 +197,63 @@ public void onBlockBreakByMinion(final EntityInteractEvent e) { */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlockBreak(final PlayerBucketFillEvent e) { - if (addon.inWorld(e.getBlock().getWorld())) { - Location l = e.getBlock().getLocation(); - addon.getIslands().getIslandAt(l).filter(i -> l.equals(i.getCenter())).ifPresent(i -> process(e, i, e.getPlayer(), e.getPlayer().getWorld())); - } + if (addon.inWorld(e.getBlock().getWorld())) { + Location l = e.getBlock().getLocation(); + addon.getIslands().getIslandAt(l).filter(i -> l.equals(i.getCenter())) + .ifPresent(i -> process(e, i, e.getPlayer(), e.getPlayer().getWorld())); + } } - /** * Drop items at the top of the block. + * * @param event EntitySpawnEvent object. */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onItemStackSpawn(EntitySpawnEvent event) - { - if (!this.addon.getSettings().isDropOnTop()) - { - // Do nothing as item spawning is not interested in this case. - return; - } - - if (!EntityType.DROPPED_ITEM.equals(event.getEntityType())) - { - // We are interested only in dropped item entities. - return; - } - - if (!this.addon.inWorld(event.getLocation().getWorld())) - { - // Not correct world - return; - } - - Entity entity = event.getEntity(); - Location location = event.getLocation(); - - Optional optionalIsland = this.addon.getIslands(). - getIslandAt(location). - filter(island -> location.getBlock().getLocation().equals(island.getCenter())); - - if (optionalIsland.isPresent()) - { - // Teleport entity to the top of magic block. - entity.teleport(optionalIsland.get().getCenter().add(0.5, 1, 0.5)); - entity.setVelocity(new Vector(0, 0, 0)); - } + public void onItemStackSpawn(EntitySpawnEvent event) { + if (!this.addon.getSettings().isDropOnTop()) { + // Do nothing as item spawning is not interested in this case. + return; + } + + if (!EntityType.DROPPED_ITEM.equals(event.getEntityType())) { + // We are interested only in dropped item entities. + return; + } + + if (!this.addon.inWorld(event.getLocation().getWorld())) { + // Not correct world + return; + } + + Entity entity = event.getEntity(); + Location location = event.getLocation(); + + Optional optionalIsland = this.addon.getIslands().getIslandAt(location) + .filter(island -> location.getBlock().getLocation().equals(island.getCenter())); + + if (optionalIsland.isPresent()) { + // Teleport entity to the top of magic block. + entity.teleport(optionalIsland.get().getCenter().add(0.5, 1, 0.5)); + entity.setVelocity(new Vector(0, 0, 0)); + } } - // --------------------------------------------------------------------- // Section: Processing methods // --------------------------------------------------------------------- - private void setUp(@NonNull Island island) { - // Set the bedrock to the initial block - Util.getChunkAtAsync(Objects.requireNonNull(island.getCenter())).thenRun(() -> island.getCenter().getBlock().setType(Material.GRASS_BLOCK)); - // Create a database entry - OneBlockIslands is = new OneBlockIslands(island.getUniqueId()); - cache.put(island.getUniqueId(), is); - handler.saveObjectAsync(is); - addon.getHoloListener().setUp(island, is, true); + // Set the bedrock to the initial block + Util.getChunkAtAsync(Objects.requireNonNull(island.getCenter())) + .thenRun(() -> island.getCenter().getBlock().setType(Material.GRASS_BLOCK)); + // Create a database entry + OneBlockIslands is = new OneBlockIslands(island.getUniqueId()); + cache.put(island.getUniqueId(), is); + handler.saveObjectAsync(is); + addon.getHoloListener().setUp(island, is, true); } - /** * Main block processing method * @@ -270,219 +263,251 @@ private void setUp(@NonNull Island island) { * @param world - world where the block is being broken */ private void process(@NonNull Cancellable e, @NonNull Island i, @Nullable Player player, @NonNull World world) { - // Get island from cache or load it - OneBlockIslands is = getIsland(i); - // Get the phase for this island - OneBlockPhase phase = oneBlocksManager.getPhase(is.getBlockNumber()); - // Store the original phase in case it changes. - String originalPhase = is.getPhaseName(); - // Check for a goto - if (Objects.requireNonNull(phase).getGotoBlock() != null) { - handleGoto(is, phase); - } - // Check for new phase and run commands if required - boolean newPhase = check.checkPhase(player, i, is, Objects.requireNonNull(phase)); - if (!newPhase && is.getBlockNumber() % SAVE_EVERY == 0) { - // Save island data every MAX_LOOK_AHEAD blocks. - saveIsland(i); - } - // Check if requirements met - if (check.phaseRequirementsFail(player, i, is, phase, world)) { - e.setCancelled(true); - return; - } - if (newPhase) { - is.clearQueue(); - is.setLastPhaseChangeTime(System.currentTimeMillis()); - } - // Get the block number in this phase - int materialBlocksInQueue = (int) is.getQueue().stream().filter(obo -> obo.isMaterial() || obo.isCustomBlock()).count(); - int blockNumber = is.getBlockNumber() - (phase.getBlockNumberValue() - 1) + materialBlocksInQueue; - // Get the block that is being broken - Block block = Objects.requireNonNull(i.getCenter()).toVector().toLocation(world).getBlock(); - // Fill a 5 block queue - if (is.getQueue().isEmpty() || newPhase) { - // Add initial 5 blocks - for (int j = 0; j < MAX_LOOK_AHEAD; j++) { - is.add(phase.getNextBlock(addon, blockNumber++)); - } - } - // Manage Holograms - addon.getHoloListener().process(i, is, phase); - // Play warning sound for upcoming mobs - if (addon.getSettings().getMobWarning() > 0) { - warningSounder.play(is, block); - } - // Get the next block - OneBlockObject nextBlock = (newPhase && phase.getFirstBlock() != null) ? phase.getFirstBlock() : is.pollAndAdd(phase.getNextBlock(addon, blockNumber++)); - // Check if this is a new Phase - if (newPhase) { - // Set the biome for the block and one block above it - setBiome(block, phase.getPhaseBiome()); - // Fire new phase event - Bukkit.getPluginManager().callEvent(new MagicBlockPhaseEvent(i, player == null ? null : player.getUniqueId(), block, phase.getPhaseName(), originalPhase, is.getBlockNumber())); - } - // Entity - if (nextBlock.isEntity()) { - if (!(e instanceof EntitySpawnEvent)) e.setCancelled(true); - // Entity spawns do not increment the block number or break the block - spawnEntity(nextBlock, block); - // Fire event - Bukkit.getPluginManager().callEvent(new MagicBlockEntityEvent(i, player == null ? null : player.getUniqueId(), block, nextBlock.getEntityType())); - return; - } - // Break the block - if (e instanceof BlockBreakEvent) { - this.breakBlock(player, block, nextBlock, i); - } else if (e instanceof PlayerBucketFillEvent) { - Bukkit.getScheduler().runTask(addon.getPlugin(), () -> spawnBlock(nextBlock, block)); - // Fire event - ItemStack tool = Objects.requireNonNull(player).getInventory().getItemInMainHand(); - Bukkit.getPluginManager().callEvent(new MagicBlockEvent(i, player.getUniqueId(), tool, block, nextBlock.getMaterial())); - } else if (e instanceof EntitySpawnEvent) { - Bukkit.getScheduler().runTask(addon.getPlugin(), () -> spawnBlock(nextBlock, block)); - } else if (e instanceof EntityInteractEvent) { - // Minion breaking block - Bukkit.getScheduler().runTask(addon.getPlugin(), () -> spawnBlock(nextBlock, block)); - // Fire event - Bukkit.getPluginManager().callEvent(new MagicBlockEvent(i, null, null, block, nextBlock.getMaterial())); - } - // Increment the block number - is.incrementBlockNumber(); + // Get the block that is being broken + Block block = Objects.requireNonNull(i.getCenter()).toVector().toLocation(world).getBlock(); + + // Get oneblock island + OneBlockIslands is = getIsland(i); + + // Get the phase for current block number + OneBlockPhase phase = oneBlocksManager.getPhase(is.getBlockNumber()); + + // Save previous processing phase name + String prevPhaseName = is.getPhaseName(); + + // Check for a goto + if (Objects.requireNonNull(phase).getGotoBlock() != null) { + handleGoto(is, phase); + } + + // Get current phase name + String currPhaseName = phase.getPhaseName() == null ? "" : phase.getPhaseName(); + + // Get the phase for next block number + OneBlockPhase nextPhase = oneBlocksManager.getPhase(is.getBlockNumber() + 1); + + // Get next phase name + String nextPhaseName = nextPhase == null || nextPhase.getPhaseName() == null ? "" : nextPhase.getPhaseName(); + + // If next phase is new, log break time of the last block of this phase + if (!currPhaseName.equalsIgnoreCase(nextPhaseName)) { + is.setLastPhaseChangeTime(System.currentTimeMillis()); + } + + boolean isCurrPhaseNew = !is.getPhaseName().equalsIgnoreCase(currPhaseName); + + if (isCurrPhaseNew) { + // Check if requirements for new phase are met + if (check.phaseRequirementsFail(player, i, is, phase, world)) { + e.setCancelled(true); + return; + } + + check.setNewPhase(player, i, is, phase); + is.clearQueue(); + + // Set the biome for the block and one block above it + setBiome(block, phase.getPhaseBiome()); + + // Fire new phase event + Bukkit.getPluginManager() + .callEvent(new MagicBlockPhaseEvent(i, player == null ? null : player.getUniqueId(), block, + phase.getPhaseName(), prevPhaseName, is.getBlockNumber())); + } + + if (!isCurrPhaseNew && is.getBlockNumber() % SAVE_EVERY == 0) { + // Save island data every MAX_LOOK_AHEAD blocks. + saveIsland(i); + } + + // Get the block number in this phase + int materialBlocksInQueue = (int) is.getQueue().stream().filter(obo -> obo.isMaterial() || obo.isCustomBlock()) + .count(); + int blockNumber = is.getBlockNumber() - (phase.getBlockNumberValue() - 1) + materialBlocksInQueue; + + // Fill a 5 block queue + if (is.getQueue().isEmpty() || isCurrPhaseNew) { + // Add initial 5 blocks + for (int j = 0; j < MAX_LOOK_AHEAD; j++) { + is.add(phase.getNextBlock(addon, blockNumber++)); + } + } + + // Manage Holograms + addon.getHoloListener().process(i, is, phase); + + // Play warning sound for upcoming mobs + if (addon.getSettings().getMobWarning() > 0) { + warningSounder.play(is, block); + } + + // Get the next block + OneBlockObject nextBlock = (isCurrPhaseNew && phase.getFirstBlock() != null) ? phase.getFirstBlock() + : is.pollAndAdd(phase.getNextBlock(addon, blockNumber++)); + + // Entity + if (nextBlock.isEntity()) { + if (!(e instanceof EntitySpawnEvent)) + e.setCancelled(true); + // Entity spawns do not increment the block number or break the block + spawnEntity(nextBlock, block); + // Fire event + Bukkit.getPluginManager().callEvent(new MagicBlockEntityEvent(i, + player == null ? null : player.getUniqueId(), block, nextBlock.getEntityType())); + return; + } + + // Break the block + if (e instanceof BlockBreakEvent) { + this.breakBlock(player, block, nextBlock, i); + } else if (e instanceof PlayerBucketFillEvent) { + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> spawnBlock(nextBlock, block)); + // Fire event + ItemStack tool = Objects.requireNonNull(player).getInventory().getItemInMainHand(); + Bukkit.getPluginManager() + .callEvent(new MagicBlockEvent(i, player.getUniqueId(), tool, block, nextBlock.getMaterial())); + } else if (e instanceof EntitySpawnEvent) { + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> spawnBlock(nextBlock, block)); + } else if (e instanceof EntityInteractEvent) { + // Minion breaking block + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> spawnBlock(nextBlock, block)); + // Fire event + Bukkit.getPluginManager().callEvent(new MagicBlockEvent(i, null, null, block, nextBlock.getMaterial())); + } + + // Increment the block number + is.incrementBlockNumber(); } private void handleGoto(OneBlockIslands is, OneBlockPhase phase) { - int gotoBlock = phase.getGotoBlock(); - phase = oneBlocksManager.getPhase(gotoBlock); - // Store lifetime - is.setLifetime(is.getLifetime() + gotoBlock); - // Set current block - is.setBlockNumber(gotoBlock); + int gotoBlock = phase.getGotoBlock(); + phase = oneBlocksManager.getPhase(gotoBlock); + // Store lifetime + is.setLifetime(is.getLifetime() + gotoBlock); + // Set current block + is.setBlockNumber(gotoBlock); } private void setBiome(@NonNull Block block, @Nullable Biome biome) { - if (biome == null) { - return; - } - for (int x = -4; x <= 4; x++) { - for (int z = -4; z <= 4; z++) { - for (int y = -4; y <= 4; y++) { - block.getWorld().setBiome(block.getX() + x, block.getY() + y, block.getZ() + z, biome); - } - } - } + if (biome == null) { + return; + } + for (int x = -4; x <= 4; x++) { + for (int z = -4; z <= 4; z++) { + for (int y = -4; y <= 4; y++) { + block.getWorld().setBiome(block.getX() + x, block.getY() + y, block.getZ() + z, biome); + } + } + } } - /** - * This method is called when block is removed, and next must be spawned. - * It also teleports player above the magic block, to avoid falling in void. - * @param player Player who breaks the block. - * @param block Block that was broken. + * This method is called when block is removed, and next must be spawned. It + * also teleports player above the magic block, to avoid falling in void. + * + * @param player Player who breaks the block. + * @param block Block that was broken. * @param nextBlock Next Block that will be summoned. - * @param island Island where player is located. + * @param island Island where player is located. */ - private void breakBlock(@Nullable Player player, Block block, @NonNull OneBlockObject nextBlock, @NonNull Island island) - { - ItemStack tool = Objects.requireNonNull(player).getInventory().getItemInMainHand(); - - // Break normally and lift the player up so they don't fall - Bukkit.getScheduler().runTask(addon.getPlugin(), () -> this.spawnBlock(nextBlock, block)); - - if (player.getLocation().getBlock().equals(block)) - { - double delta = 1 - (player.getLocation().getY() - block.getY()); - player.teleport(player.getLocation().add(new Vector(0, delta, 0))); - player.setVelocity(new Vector(0, 0, 0)); - } - else if (player.getLocation().getBlock().equals(block.getRelative(BlockFace.UP))) - { - player.teleport(player.getLocation()); - player.setVelocity(new Vector(0, 0, 0)); - } - - // Fire event - Bukkit.getPluginManager().callEvent(new MagicBlockEvent(island, player.getUniqueId(), tool, block, nextBlock.getMaterial())); + private void breakBlock(@Nullable Player player, Block block, @NonNull OneBlockObject nextBlock, + @NonNull Island island) { + ItemStack tool = Objects.requireNonNull(player).getInventory().getItemInMainHand(); + + // Break normally and lift the player up so they don't fall + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> this.spawnBlock(nextBlock, block)); + + if (player.getLocation().getBlock().equals(block)) { + double delta = 1 - (player.getLocation().getY() - block.getY()); + player.teleport(player.getLocation().add(new Vector(0, delta, 0))); + player.setVelocity(new Vector(0, 0, 0)); + } else if (player.getLocation().getBlock().equals(block.getRelative(BlockFace.UP))) { + player.teleport(player.getLocation()); + player.setVelocity(new Vector(0, 0, 0)); + } + + // Fire event + Bukkit.getPluginManager() + .callEvent(new MagicBlockEvent(island, player.getUniqueId(), tool, block, nextBlock.getMaterial())); } - private void spawnBlock(@NonNull OneBlockObject nextBlock, @NonNull Block block) { - if (nextBlock.isCustomBlock()) { - nextBlock.getCustomBlock().execute(addon, block); - } else if (nextBlock.isItemsAdderBlock()) { - //Get Custom Block from ItemsAdder and place it - CustomBlock cBlock = CustomBlock.getInstance(nextBlock.getItemsAdderBlock()); - if (cBlock != null) { - block.getLocation().getBlock().setType(Material.AIR); - cBlock.place(block.getLocation()); - } - } else { - @NonNull - Material type = nextBlock.getMaterial(); - // Place new block with no physics - block.setType(type, false); - // Fill the chest - if (type.equals(Material.CHEST) && nextBlock.getChest() != null) { - fillChest(nextBlock, block); - return; - } else if (Tag.LEAVES.isTagged(type)) { - Leaves leaves = (Leaves) block.getState().getBlockData(); - leaves.setPersistent(true); - block.setBlockData(leaves); - } else if (block.getState() instanceof BrushableBlock bb) { - LootTable lt = switch(bb.getBlock().getBiome()) { - default -> { - if (random.nextDouble() < 0.8) { - yield LootTables.TRAIL_RUINS_ARCHAEOLOGY_COMMON.getLootTable(); - } else { - // 20% rare - yield LootTables.TRAIL_RUINS_ARCHAEOLOGY_RARE.getLootTable(); - } - } - case DESERT -> LootTables.DESERT_PYRAMID_ARCHAEOLOGY.getLootTable(); - case FROZEN_OCEAN -> LootTables.OCEAN_RUIN_COLD_ARCHAEOLOGY.getLootTable(); - case OCEAN -> LootTables.OCEAN_RUIN_COLD_ARCHAEOLOGY.getLootTable(); - case WARM_OCEAN -> LootTables.OCEAN_RUIN_WARM_ARCHAEOLOGY.getLootTable(); - }; - bb.setLootTable(lt); - bb.update(); - } - } + if (nextBlock.isCustomBlock()) { + nextBlock.getCustomBlock().execute(addon, block); + } else if (nextBlock.isItemsAdderBlock()) { + // Get Custom Block from ItemsAdder and place it + CustomBlock cBlock = CustomBlock.getInstance(nextBlock.getItemsAdderBlock()); + if (cBlock != null) { + block.getLocation().getBlock().setType(Material.AIR); + cBlock.place(block.getLocation()); + } + } else { + @NonNull + Material type = nextBlock.getMaterial(); + // Place new block with no physics + block.setType(type, false); + // Fill the chest + if (type.equals(Material.CHEST) && nextBlock.getChest() != null) { + fillChest(nextBlock, block); + return; + } else if (Tag.LEAVES.isTagged(type)) { + Leaves leaves = (Leaves) block.getState().getBlockData(); + leaves.setPersistent(true); + block.setBlockData(leaves); + } else if (block.getState() instanceof BrushableBlock bb) { + LootTable lt = switch (bb.getBlock().getBiome()) { + default -> { + if (random.nextDouble() < 0.8) { + yield LootTables.TRAIL_RUINS_ARCHAEOLOGY_COMMON.getLootTable(); + } else { + // 20% rare + yield LootTables.TRAIL_RUINS_ARCHAEOLOGY_RARE.getLootTable(); + } + } + case DESERT -> LootTables.DESERT_PYRAMID_ARCHAEOLOGY.getLootTable(); + case FROZEN_OCEAN -> LootTables.OCEAN_RUIN_COLD_ARCHAEOLOGY.getLootTable(); + case OCEAN -> LootTables.OCEAN_RUIN_COLD_ARCHAEOLOGY.getLootTable(); + case WARM_OCEAN -> LootTables.OCEAN_RUIN_WARM_ARCHAEOLOGY.getLootTable(); + }; + bb.setLootTable(lt); + bb.update(); + } + } } private void spawnEntity(@NonNull OneBlockObject nextBlock, @NonNull Block block) { - if (block.isEmpty()) block.setType(Material.STONE); - Location spawnLoc = block.getLocation().add(new Vector(0.5D, 1D, 0.5D)); - Entity entity = block.getWorld().spawnEntity(spawnLoc, nextBlock.getEntityType()); - // Make space for entity - this will blot out blocks - if (addon.getSettings().isClearBlocks()) { - new MakeSpace(addon).makeSpace(entity, spawnLoc); - } - block.getWorld().playSound(block.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 2F); + if (block.isEmpty()) + block.setType(Material.STONE); + Location spawnLoc = block.getLocation().add(new Vector(0.5D, 1D, 0.5D)); + Entity entity = block.getWorld().spawnEntity(spawnLoc, nextBlock.getEntityType()); + // Make space for entity - this will blot out blocks + if (addon.getSettings().isClearBlocks()) { + new MakeSpace(addon).makeSpace(entity, spawnLoc); + } + block.getWorld().playSound(block.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 2F); } - - private void fillChest(@NonNull OneBlockObject nextBlock, @NonNull Block block) { - Chest chest = (Chest) block.getState(); - nextBlock.getChest().forEach(chest.getBlockInventory()::setItem); - Color color = Color.fromBGR(0, 255, 255); // yellow - switch (nextBlock.getRarity()) { - case EPIC: - color = Color.fromBGR(255, 0, 255); // magenta - break; - case RARE: - color = Color.fromBGR(255, 255, 255); // cyan - break; - case UNCOMMON: - // Yellow - break; - default: - // No sparkles for regular chests - return; - } - block.getWorld().spawnParticle(Particle.REDSTONE, block.getLocation().add(new Vector(0.5, 1.0, 0.5)), 50, 0.5, 0, 0.5, 1, new Particle.DustOptions(color, 1)); + Chest chest = (Chest) block.getState(); + nextBlock.getChest().forEach(chest.getBlockInventory()::setItem); + Color color = Color.fromBGR(0, 255, 255); // yellow + switch (nextBlock.getRarity()) { + case EPIC: + color = Color.fromBGR(255, 0, 255); // magenta + break; + case RARE: + color = Color.fromBGR(255, 255, 255); // cyan + break; + case UNCOMMON: + // Yellow + break; + default: + // No sparkles for regular chests + return; + } + block.getWorld().spawnParticle(Particle.REDSTONE, block.getLocation().add(new Vector(0.5, 1.0, 0.5)), 50, 0.5, + 0, 0.5, 1, new Particle.DustOptions(color, 1)); } /** @@ -493,47 +518,49 @@ private void fillChest(@NonNull OneBlockObject nextBlock, @NonNull Block block) */ @NonNull public OneBlockIslands getIsland(@NonNull Island i) { - return cache.containsKey(i.getUniqueId()) ? cache.get(i.getUniqueId()) : loadIsland(i.getUniqueId()); + return cache.containsKey(i.getUniqueId()) ? cache.get(i.getUniqueId()) : loadIsland(i.getUniqueId()); } /** * Get all the OneBlockIslands from the Database + * * @return list of oneblock islands */ public List getAllIslands() { - return handler.loadObjects(); + return handler.loadObjects(); } @NonNull private OneBlockIslands loadIsland(@NonNull String uniqueId) { - if (handler.objectExists(uniqueId)) { - OneBlockIslands island = handler.loadObject(uniqueId); - if (island != null) { - // Add to cache - cache.put(island.getUniqueId(), island); - return island; - } - } - return cache.computeIfAbsent(uniqueId, OneBlockIslands::new); + if (handler.objectExists(uniqueId)) { + OneBlockIslands island = handler.loadObject(uniqueId); + if (island != null) { + // Add to cache + cache.put(island.getUniqueId(), island); + return island; + } + } + return cache.computeIfAbsent(uniqueId, OneBlockIslands::new); } /** * @return the oneBlocksManager */ public OneBlocksManager getOneBlocksManager() { - return oneBlocksManager; + return oneBlocksManager; } /** * Saves the island progress to the database async * * @param island - island - * @return CompletableFuture - true if saved or not in cache, false if save failed + * @return CompletableFuture - true if saved or not in cache, false if save + * failed */ public CompletableFuture saveIsland(@NonNull Island island) { - if (cache.containsKey(island.getUniqueId())) { - return handler.saveObjectAsync(cache.get(island.getUniqueId())); - } - return CompletableFuture.completedFuture(true); + if (cache.containsKey(island.getUniqueId())) { + return handler.saveObjectAsync(cache.get(island.getUniqueId())); + } + return CompletableFuture.completedFuture(true); } } diff --git a/src/main/java/world/bentobox/aoneblock/listeners/CheckPhase.java b/src/main/java/world/bentobox/aoneblock/listeners/CheckPhase.java index 5a72bb6..337c75e 100644 --- a/src/main/java/world/bentobox/aoneblock/listeners/CheckPhase.java +++ b/src/main/java/world/bentobox/aoneblock/listeners/CheckPhase.java @@ -43,6 +43,57 @@ public CheckPhase(AOneBlock addon, BlockListener blockListener) { } + /** + * Runs end phase commands, sets new phase and runs new phase commands + * + * @param player - player + * @param i - island + * @param is - OneBlockIslands object + * @param phase - current phase + */ + void setNewPhase( + @Nullable Player player, + @NonNull Island i, + @NonNull OneBlockIslands is, + @NonNull OneBlockPhase phase + ) { + // Handle NPCs + User user; + if (player == null || player.hasMetadata("NPC")) { + // Default to the owner + user = addon.getPlayers().getUser(i.getOwner()); + } else { + user = User.getInstance(player); + } + + String newPhaseName = phase.getPhaseName(); + + // Run previous phase end commands + oneBlocksManager.getPhase(is.getPhaseName()).ifPresent(oldPhase -> { + String oldPhaseName = oldPhase.getPhaseName() == null ? "" : oldPhase.getPhaseName(); + Util.runCommands(user, + replacePlaceholders(player, oldPhaseName, phase.getBlockNumber(), i, oldPhase.getEndCommands()), + "Commands run for end of " + oldPhaseName); + // If first time + if (is.getBlockNumber() >= is.getLifetime()) { + Util.runCommands(user, + replacePlaceholders(player, oldPhaseName, phase.getBlockNumber(), i, oldPhase.getFirstTimeEndCommands()), + "Commands run for first time completing " + oldPhaseName); + } + }); + // Set the phase name + is.setPhaseName(newPhaseName); + if (user.isPlayer() && user.isOnline() && addon.inWorld(user.getWorld())) { + user.getPlayer().sendTitle(newPhaseName, null, -1, -1, -1); + } + // Run phase start commands + Util.runCommands(user, + replacePlaceholders(player, newPhaseName, phase.getBlockNumber(), i, phase.getStartCommands()), + "Commands run for start of " + newPhaseName); + + blockListener.saveIsland(i); + } + /** * Checks whether the player can proceed to the next phase * @@ -102,56 +153,6 @@ protected boolean phaseRequirementsFail(@Nullable Player player, @NonNull Island return blocked; } - /** - * Check whether this phase is done or not. - * - * @param player - player - * @param i - island - * @param is - OneBlockIslands object - * @param phase - current phase name - * @return true if this is a new phase, false if not - */ - protected boolean checkPhase(@Nullable Player player, @NonNull Island i, @NonNull OneBlockIslands is, @NonNull OneBlockPhase phase) { - // Handle NPCs - User user; - if (player == null || player.hasMetadata("NPC")) { - // Default to the owner - user = addon.getPlayers().getUser(i.getOwner()); - } else { - user = User.getInstance(player); - } - - String phaseName = phase.getPhaseName() == null ? "" : phase.getPhaseName(); - if (!is.getPhaseName().equalsIgnoreCase(phaseName)) { - // Run previous phase end commands - oneBlocksManager.getPhase(is.getPhaseName()).ifPresent(oldPhase -> { - String oldPhaseName = oldPhase.getPhaseName() == null ? "" : oldPhase.getPhaseName(); - Util.runCommands(user, - replacePlaceholders(player, oldPhaseName, phase.getBlockNumber(), i, oldPhase.getEndCommands()), - "Commands run for end of " + oldPhaseName); - // If first time - if (is.getBlockNumber() >= is.getLifetime()) { - Util.runCommands(user, - replacePlaceholders(player, oldPhaseName, phase.getBlockNumber(), i, oldPhase.getFirstTimeEndCommands()), - "Commands run for first time completing " + oldPhaseName); - } - }); - // Set the phase name - is.setPhaseName(phaseName); - if (user.isPlayer() && user.isOnline() && addon.inWorld(user.getWorld())) { - user.getPlayer().sendTitle(phaseName, null, -1, -1, -1); - } - // Run phase start commands - Util.runCommands(user, - replacePlaceholders(player, phaseName, phase.getBlockNumber(), i, phase.getStartCommands()), - "Commands run for start of " + phaseName); - - blockListener.saveIsland(i); - return true; - } - return false; - } - /** * Replaces placeholders in commands. *
diff --git a/src/main/java/world/bentobox/aoneblock/listeners/HoloListener.java b/src/main/java/world/bentobox/aoneblock/listeners/HoloListener.java
index 8a3efb2..21f3dc7 100644
--- a/src/main/java/world/bentobox/aoneblock/listeners/HoloListener.java
+++ b/src/main/java/world/bentobox/aoneblock/listeners/HoloListener.java
@@ -1,5 +1,10 @@
 package world.bentobox.aoneblock.listeners;
 
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
 import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.World;
@@ -9,6 +14,7 @@
 import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;
 import org.eclipse.jdt.annotation.NonNull;
+
 import world.bentobox.aoneblock.AOneBlock;
 import world.bentobox.aoneblock.dataobjects.OneBlockIslands;
 import world.bentobox.aoneblock.oneblocks.OneBlockPhase;
@@ -17,11 +23,6 @@
 import world.bentobox.bentobox.database.objects.Island;
 import world.bentobox.bentobox.util.Util;
 
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
-
 /**
  * Handles Holographic elements
  *
diff --git a/src/main/java/world/bentobox/aoneblock/listeners/ItemsAdderListener.java b/src/main/java/world/bentobox/aoneblock/listeners/ItemsAdderListener.java
index a6948c7..bf4c25b 100644
--- a/src/main/java/world/bentobox/aoneblock/listeners/ItemsAdderListener.java
+++ b/src/main/java/world/bentobox/aoneblock/listeners/ItemsAdderListener.java
@@ -1,8 +1,9 @@
 package world.bentobox.aoneblock.listeners;
 
-import dev.lone.itemsadder.api.Events.ItemsAdderLoadDataEvent;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
+
+import dev.lone.itemsadder.api.Events.ItemsAdderLoadDataEvent;
 import world.bentobox.aoneblock.AOneBlock;
 
 /**
diff --git a/src/main/java/world/bentobox/aoneblock/oneblocks/OneBlockCustomBlock.java b/src/main/java/world/bentobox/aoneblock/oneblocks/OneBlockCustomBlock.java
index 4c30b8d..458eb47 100644
--- a/src/main/java/world/bentobox/aoneblock/oneblocks/OneBlockCustomBlock.java
+++ b/src/main/java/world/bentobox/aoneblock/oneblocks/OneBlockCustomBlock.java
@@ -1,6 +1,7 @@
 package world.bentobox.aoneblock.oneblocks;
 
 import org.bukkit.block.Block;
+
 import world.bentobox.aoneblock.AOneBlock;
 
 /**
diff --git a/src/main/java/world/bentobox/aoneblock/oneblocks/OneBlockCustomBlockCreator.java b/src/main/java/world/bentobox/aoneblock/oneblocks/OneBlockCustomBlockCreator.java
index 91d8c04..d46b507 100644
--- a/src/main/java/world/bentobox/aoneblock/oneblocks/OneBlockCustomBlockCreator.java
+++ b/src/main/java/world/bentobox/aoneblock/oneblocks/OneBlockCustomBlockCreator.java
@@ -1,11 +1,16 @@
 package world.bentobox.aoneblock.oneblocks;
 
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+
 import world.bentobox.aoneblock.oneblocks.customblock.BlockDataCustomBlock;
 import world.bentobox.aoneblock.oneblocks.customblock.MobCustomBlock;
 
-import java.util.*;
-import java.util.function.Function;
-
 /**
  * A creator for {@link OneBlockCustomBlock}
  *
diff --git a/src/main/java/world/bentobox/aoneblock/oneblocks/Requirement.java b/src/main/java/world/bentobox/aoneblock/oneblocks/Requirement.java
index cc4f62e..7a56ce3 100644
--- a/src/main/java/world/bentobox/aoneblock/oneblocks/Requirement.java
+++ b/src/main/java/world/bentobox/aoneblock/oneblocks/Requirement.java
@@ -1,5 +1,7 @@
 package world.bentobox.aoneblock.oneblocks;
 
+import world.bentobox.aoneblock.oneblocks.Requirement.ReqType;
+
 /**
  * Requirement for finishing a phase
  * @author tastybento
diff --git a/src/main/java/world/bentobox/aoneblock/oneblocks/customblock/MobCustomBlock.java b/src/main/java/world/bentobox/aoneblock/oneblocks/customblock/MobCustomBlock.java
index 7325949..6e95a96 100644
--- a/src/main/java/world/bentobox/aoneblock/oneblocks/customblock/MobCustomBlock.java
+++ b/src/main/java/world/bentobox/aoneblock/oneblocks/customblock/MobCustomBlock.java
@@ -1,6 +1,10 @@
 package world.bentobox.aoneblock.oneblocks.customblock;
 
-import com.google.common.base.Enums;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
 import org.bukkit.Location;
 import org.bukkit.Material;
 import org.bukkit.Sound;
@@ -9,13 +13,14 @@
 import org.bukkit.entity.EntityType;
 import org.bukkit.util.Vector;
 import org.eclipse.jdt.annotation.NonNull;
+
+import com.google.common.base.Enums;
+
 import world.bentobox.aoneblock.AOneBlock;
 import world.bentobox.aoneblock.listeners.MakeSpace;
 import world.bentobox.aoneblock.oneblocks.OneBlockCustomBlock;
 import world.bentobox.bentobox.BentoBox;
 
-import java.util.*;
-
 /**
  * A custom block that spawns mob on an underlying block
  *
diff --git a/src/main/resources/phases/0_plains.yml b/src/main/resources/phases/0_plains.yml
index 93245db..1d184a4 100644
--- a/src/main/resources/phases/0_plains.yml
+++ b/src/main/resources/phases/0_plains.yml
@@ -56,7 +56,7 @@
   #   bank-balance: 10000
   #   level: 10
   #   permission: ready.for.battle
-  #   cooldown: 60
+  #   cooldown: 60 # seconds
   
   blocks:
     PODZOL: 40
diff --git a/src/test/java/world/bentobox/aoneblock/dataobjects/OneBlockIslandsTest.java b/src/test/java/world/bentobox/aoneblock/dataobjects/OneBlockIslandsTest.java
index 772d8ef..714e915 100644
--- a/src/test/java/world/bentobox/aoneblock/dataobjects/OneBlockIslandsTest.java
+++ b/src/test/java/world/bentobox/aoneblock/dataobjects/OneBlockIslandsTest.java
@@ -1,6 +1,9 @@
 package world.bentobox.aoneblock.dataobjects;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import java.util.List;
 import java.util.Queue;
diff --git a/src/test/java/world/bentobox/aoneblock/listeners/CheckPhaseTest.java b/src/test/java/world/bentobox/aoneblock/listeners/CheckPhaseTest.java
index 6ef0b60..c5a9179 100644
--- a/src/test/java/world/bentobox/aoneblock/listeners/CheckPhaseTest.java
+++ b/src/test/java/world/bentobox/aoneblock/listeners/CheckPhaseTest.java
@@ -1,8 +1,6 @@
 package world.bentobox.aoneblock.listeners;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -55,7 +53,7 @@
  *
  */
 @RunWith(PowerMockRunner.class)
-@PrepareForTest({Bukkit.class, BentoBox.class, DatabaseSetup.class, Util.class})
+@PrepareForTest({ Bukkit.class, BentoBox.class, DatabaseSetup.class, Util.class })
 public class CheckPhaseTest {
 
     @Mock
@@ -88,60 +86,56 @@ public class CheckPhaseTest {
     @Mock
     private Level level;
 
-
-
     /**
      * @throws java.lang.Exception
      */
     @Before
     public void setUp() throws Exception {
-        // Bukkit
-        PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
-        // Set up plugin
-        Whitebox.setInternalState(BentoBox.class, "instance", plugin);
-
-        // Addon
-        when(addon.getPlugin()).thenReturn(plugin);
-        when(addon.getOneBlockManager()).thenReturn(obm);
-        when(addon.getPlayers()).thenReturn(pm);
-        when(addon.getOverWorld()).thenReturn(world);
-        when(addon.getIslands()).thenReturn(im);
-        when(addon.inWorld(world)).thenReturn(true);
-        when(addon.getBlockListener()).thenReturn(blis);
-
-        // Player
-        when(player.getUniqueId()).thenReturn(UUID.randomUUID());
-        when(player.getName()).thenReturn("tastybento");
-        when(player.isOnline()).thenReturn(true);
-        when(player.getWorld()).thenReturn(world);
-        User.setPlugin(plugin);
-        user = User.getInstance(player);
-
-        // Island
-        island = new Island();
-        island.setOwner(UUID.randomUUID());
-        island.setName("island_name");
-        // Players Manager
-        when(pm.getName(any())).thenReturn("tastybento2");
-        when(pm.getUser(any(UUID.class))).thenReturn(user);
-
-        // Bank
-        BankManager bm = mock(BankManager.class);
-        when(bank.getBankManager()).thenReturn(bm);
-        // Phat balance to start
-        when(bm.getBalance(island)).thenReturn(new Money(100000D));
-        when(addon.getAddonByName("Bank")).thenReturn(Optional.of(bank));
-        // Level
-        when(level.getIslandLevel(eq(world), any())).thenReturn(1000L);
-        when(addon.getAddonByName("Level")).thenReturn(Optional.of(level));
-
-
-        // Placeholders
-        when(plugin.getPlaceholdersManager()).thenReturn(phm);
-        when(phm.replacePlaceholders(any(), anyString())).thenAnswer(a -> (String)a.getArgument(1, String.class));
-
-
-        bl = new CheckPhase(addon, blis);
+	// Bukkit
+	PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
+	// Set up plugin
+	Whitebox.setInternalState(BentoBox.class, "instance", plugin);
+
+	// Addon
+	when(addon.getPlugin()).thenReturn(plugin);
+	when(addon.getOneBlockManager()).thenReturn(obm);
+	when(addon.getPlayers()).thenReturn(pm);
+	when(addon.getOverWorld()).thenReturn(world);
+	when(addon.getIslands()).thenReturn(im);
+	when(addon.inWorld(world)).thenReturn(true);
+	when(addon.getBlockListener()).thenReturn(blis);
+
+	// Player
+	when(player.getUniqueId()).thenReturn(UUID.randomUUID());
+	when(player.getName()).thenReturn("tastybento");
+	when(player.isOnline()).thenReturn(true);
+	when(player.getWorld()).thenReturn(world);
+	User.setPlugin(plugin);
+	user = User.getInstance(player);
+
+	// Island
+	island = new Island();
+	island.setOwner(UUID.randomUUID());
+	island.setName("island_name");
+	// Players Manager
+	when(pm.getName(any())).thenReturn("tastybento2");
+	when(pm.getUser(any(UUID.class))).thenReturn(user);
+
+	// Bank
+	BankManager bm = mock(BankManager.class);
+	when(bank.getBankManager()).thenReturn(bm);
+	// Phat balance to start
+	when(bm.getBalance(island)).thenReturn(new Money(100000D));
+	when(addon.getAddonByName("Bank")).thenReturn(Optional.of(bank));
+	// Level
+	when(level.getIslandLevel(eq(world), any())).thenReturn(1000L);
+	when(addon.getAddonByName("Level")).thenReturn(Optional.of(level));
+
+	// Placeholders
+	when(plugin.getPlaceholdersManager()).thenReturn(phm);
+	when(phm.replacePlaceholders(any(), anyString())).thenAnswer(a -> (String) a.getArgument(1, String.class));
+
+	bl = new CheckPhase(addon, blis);
     }
 
     /**
@@ -151,99 +145,107 @@ public void setUp() throws Exception {
     public void tearDown() throws Exception {
     }
 
-
     /**
-     * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+     * Test method for
+     * {@link world.bentobox.aoneblock.listeners.CheckPhase#setNewPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
      */
     @Test
-    public void testCheckPhase() {
-        // Set up that a phase has been completed
-        is = new OneBlockIslands(UUID.randomUUID().toString());
-        is.setPhaseName("Previous");
-        is.setBlockNumber(500);
-        is.setLifetime(500L);
-        // The phase the user has just moved to
-        phase = new OneBlockPhase("500");
-        phase.setPhaseName("Next Phase");
-        phase.setStartCommands(List.of("start1", "start2"));
-
-        // The previous phase
-        OneBlockPhase previous = mock(OneBlockPhase.class);
-        when(previous.getPhaseName()).thenReturn("Previous");
-
-        when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
-
-        assertTrue(bl.checkPhase(player, island, is, phase));
-        // Verify commands run
-        verify(previous).getEndCommands();
-        verify(previous).getFirstTimeEndCommands();
-        // Verify title shown
-        verify(player).sendTitle("Next Phase", null, -1, -1, -1);
+    public void testSetNewPhase() {
+	// Set up that a phase has been completed
+	is = new OneBlockIslands(UUID.randomUUID().toString());
+	is.setPhaseName("Previous");
+	is.setBlockNumber(500);
+	is.setLifetime(500L);
+	// The phase the user has just moved to
+	phase = new OneBlockPhase("500");
+	phase.setPhaseName("Next Phase");
+	phase.setStartCommands(List.of("start1", "start2"));
+
+	// The previous phase
+	OneBlockPhase previous = mock(OneBlockPhase.class);
+	when(previous.getPhaseName()).thenReturn("Previous");
+
+	when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
+
+	bl.setNewPhase(player, island, is, phase);
+	// Verify commands run
+	verify(previous).getEndCommands();
+	verify(previous).getFirstTimeEndCommands();
+	// Verify phase name change
+	assertEquals("Next Phase", is.getPhaseName());
+	// Verify title shown
+	verify(player).sendTitle("Next Phase", null, -1, -1, -1);
 
     }
 
     /**
-     * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+     * Test method for
+     * {@link world.bentobox.aoneblock.listeners.CheckPhase#setNewPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
      */
     @Test
-    public void testCheckPhaseSecondTime() {
-        // Set up that a phase has been completed
-        is = new OneBlockIslands(UUID.randomUUID().toString());
-        is.setPhaseName("Previous");
-        is.setBlockNumber(500);
-        is.setLifetime(10500L);
-        // The phase the user has just moved to
-        phase = new OneBlockPhase("500");
-        phase.setPhaseName("Next Phase");
-        phase.setStartCommands(List.of("start1", "start2"));
-
-        // The previous phase
-        OneBlockPhase previous = mock(OneBlockPhase.class);
-        when(previous.getPhaseName()).thenReturn("Previous");
-
-        when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
-
-        assertTrue(bl.checkPhase(player, island, is, phase));
-        // Verify commands run
-        verify(previous).getEndCommands();
-        verify(previous, never()).getFirstTimeEndCommands();
-        // Verify title shown
-        verify(player).sendTitle("Next Phase", null, -1, -1, -1);
+    public void testSetNewPhaseSecondTime() {
+	// Set up that a phase has been completed
+	is = new OneBlockIslands(UUID.randomUUID().toString());
+	is.setPhaseName("Previous");
+	is.setBlockNumber(500);
+	is.setLifetime(10500L);
+	// The phase the user has just moved to
+	phase = new OneBlockPhase("500");
+	phase.setPhaseName("Next Phase");
+	phase.setStartCommands(List.of("start1", "start2"));
+
+	// The previous phase
+	OneBlockPhase previous = mock(OneBlockPhase.class);
+	when(previous.getPhaseName()).thenReturn("Previous");
+
+	when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
+
+	bl.setNewPhase(player, island, is, phase);
+	// Verify commands run
+	verify(previous).getEndCommands();
+	verify(previous, never()).getFirstTimeEndCommands();
+	// Verify phase name change
+	assertEquals("Next Phase", is.getPhaseName());
+	// Verify title shown
+	verify(player).sendTitle("Next Phase", null, -1, -1, -1);
 
     }
 
     /**
-     * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+     * Test method for
+     * {@link world.bentobox.aoneblock.listeners.CheckPhase#setNewPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
      */
     @Test
-    public void testCheckPhaseNullPlayer() {
-        // Set up that a phase has been completed
-        is = new OneBlockIslands(UUID.randomUUID().toString());
-        is.setPhaseName("Previous");
-        is.setBlockNumber(500);
-        is.setLifetime(500L);
-        // The phase the user has just moved to
-        phase = new OneBlockPhase("500");
-        phase.setPhaseName("Next Phase");
-        phase.setStartCommands(List.of("start1", "start2"));
-
-        // The previous phase
-        OneBlockPhase previous = mock(OneBlockPhase.class);
-        when(previous.getPhaseName()).thenReturn("Previous");
-
-        when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
-
-        assertTrue(bl.checkPhase(null, island, is, phase));
-        // Verify commands run
-        verify(previous).getEndCommands();
-        verify(previous).getFirstTimeEndCommands();
-        // Verify title shown
-        verify(player).sendTitle("Next Phase", null, -1, -1, -1);
+    public void testSetNewPhaseNullPlayer() {
+	// Set up that a phase has been completed
+	is = new OneBlockIslands(UUID.randomUUID().toString());
+	is.setPhaseName("Previous");
+	is.setBlockNumber(500);
+	is.setLifetime(500L);
+	// The phase the user has just moved to
+	phase = new OneBlockPhase("500");
+	phase.setPhaseName("Next Phase");
+	phase.setStartCommands(List.of("start1", "start2"));
+
+	// The previous phase
+	OneBlockPhase previous = mock(OneBlockPhase.class);
+	when(previous.getPhaseName()).thenReturn("Previous");
+
+	when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
+
+	bl.setNewPhase(null, island, is, phase);
+	// Verify commands run
+	verify(previous).getEndCommands();
+	verify(previous).getFirstTimeEndCommands();
+	// Verify phase name change
+	assertEquals("Next Phase", is.getPhaseName());
+	// Verify title shown
+	verify(player).sendTitle("Next Phase", null, -1, -1, -1);
 
     }
 
     /**
-     * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+     * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#setNewPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
      */
     @Test
     public void testCheckPhaseNPCPlayer() {
@@ -264,57 +266,41 @@ public void testCheckPhaseNPCPlayer() {
 
         when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
 
-        assertTrue(bl.checkPhase(player, island, is, phase));
+        bl.setNewPhase(player, island, is, phase);
         // Verify commands run
         verify(previous).getEndCommands();
         verify(previous).getFirstTimeEndCommands();
+        // Verify phase name change
+        assertEquals("Next Phase", is.getPhaseName());
         // Verify title shown
         verify(player).sendTitle("Next Phase", null, -1, -1, -1);
 
     }
 
     /**
-     * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
-     */
-    @Test
-    public void testCheckSamePhase() {
-        is = new OneBlockIslands(UUID.randomUUID().toString());
-        is.setPhaseName("Previous");
-        is.setBlockNumber(500);
-        is.setLifetime(500L);
-        // The phase the user has just moved to
-        phase = new OneBlockPhase("500");
-        phase.setPhaseName("Previous");
-
-        assertFalse(bl.checkPhase(player, island, is, phase));
-
-    }
-
-    /**
-     * Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#replacePlaceholders(org.bukkit.entity.Player, java.lang.String, java.lang.String, world.bentobox.bentobox.database.objects.Island, java.util.List)}.
+     * Test method for
+     * {@link world.bentobox.aoneblock.listeners.BlockListener#replacePlaceholders(org.bukkit.entity.Player, java.lang.String, java.lang.String, world.bentobox.bentobox.database.objects.Island, java.util.List)}.
      */
     @Test
     public void testReplacePlaceholders() {
-        // Commands
-        /*
-         * [island] - Island name
-         * [owner] - Island owner's name
-         * [player] - The name of the player who broke the block triggering the commands
-         * [phase] - the name of this phase
-         * [blocks] - the number of blocks broken
-         * [level] - island level (Requires Levels Addon)
-         * [bank-balance] - island bank balance (Requires Bank Addon)
-         * [eco-balance] - player's economy balance (Requires Vault and an economy plugin)
-
-         */
-        List commandList = new ArrayList<>();
-
-        commandList.add("no replacement");
-        commandList.add("[island] [owner] [phase] [blocks] [level] [bank-balance] [eco-balance]");
-        List r = bl.replacePlaceholders(player, "phaseName", "1000", island, commandList);
-        assertEquals(2, r.size());
-        assertEquals("no replacement", r.get(0));
-        assertEquals("island_name tastybento2 phaseName 1000 1000 100000.0 0.0", r.get(1));
-        verify(phm, times(2)).replacePlaceholders(eq(player), any());
+	// Commands
+	/*
+	 * [island] - Island name [owner] - Island owner's name [player] - The name of
+	 * the player who broke the block triggering the commands [phase] - the name of
+	 * this phase [blocks] - the number of blocks broken [level] - island level
+	 * (Requires Levels Addon) [bank-balance] - island bank balance (Requires Bank
+	 * Addon) [eco-balance] - player's economy balance (Requires Vault and an
+	 * economy plugin)
+	 * 
+	 */
+	List commandList = new ArrayList<>();
+
+	commandList.add("no replacement");
+	commandList.add("[island] [owner] [phase] [blocks] [level] [bank-balance] [eco-balance]");
+	List r = bl.replacePlaceholders(player, "phaseName", "1000", island, commandList);
+	assertEquals(2, r.size());
+	assertEquals("no replacement", r.get(0));
+	assertEquals("island_name tastybento2 phaseName 1000 1000 100000.0 0.0", r.get(1));
+	verify(phm, times(2)).replacePlaceholders(eq(player), any());
     }
 }
diff --git a/src/test/java/world/bentobox/aoneblock/listeners/NoBlockHandlerTest.java b/src/test/java/world/bentobox/aoneblock/listeners/NoBlockHandlerTest.java
index 10c4363..5ad0baa 100644
--- a/src/test/java/world/bentobox/aoneblock/listeners/NoBlockHandlerTest.java
+++ b/src/test/java/world/bentobox/aoneblock/listeners/NoBlockHandlerTest.java
@@ -14,6 +14,7 @@
 import org.bukkit.block.Block;
 import org.bukkit.entity.Player;
 import org.bukkit.event.player.PlayerRespawnEvent;
+import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -31,16 +32,16 @@
  */
 @RunWith(PowerMockRunner.class)
 public class NoBlockHandlerTest {
-    
+
     private static final UUID ID = UUID.randomUUID();
-    
+
     @Mock
     private AOneBlock aob;
     @Mock
     private Player p;
-    
+
     private NoBlockHandler nbh;
-    
+
     @Mock
     private Block block;
     @Mock
@@ -54,7 +55,6 @@ public class NoBlockHandlerTest {
     @Mock
     private World world;
 
-
     /**
      * @throws java.lang.Exception
      */
@@ -92,36 +92,38 @@ public void tearDown() throws Exception {
     }
 
     /**
-     * Test method for {@link world.bentobox.aoneblock.listeners.NoBlockHandler#NoBlockHandler(world.bentobox.aoneblock.AOneBlock)}.
+     * Test method for
+     * {@link world.bentobox.aoneblock.listeners.NoBlockHandler#NoBlockHandler(world.bentobox.aoneblock.AOneBlock)}.
      */
     @Test
     public void testNoBlockHandler() {
-        assertNotNull(nbh);
+	assertNotNull(nbh);
     }
 
     /**
-     * Test method for {@link world.bentobox.aoneblock.listeners.NoBlockHandler#onRespawn(org.bukkit.event.player.PlayerRespawnEvent)}.
+     * Test method for
+     * {@link world.bentobox.aoneblock.listeners.NoBlockHandler#onRespawn(org.bukkit.event.player.PlayerRespawnEvent)}.
      */
     @Test
     public void testOnRespawnSolidBlock() {
-        PlayerRespawnEvent event = new PlayerRespawnEvent(p, location, false, false);
-        nbh.onRespawn(event);
-        verify(block, never()).setType(any(Material.class));
-        
+	PlayerRespawnEvent event = new PlayerRespawnEvent(p, location, false, false, RespawnReason.DEATH);
+	nbh.onRespawn(event);
+	verify(block, never()).setType(any(Material.class));
+
     }
-    
+
     /**
      * Test method for {@link world.bentobox.aoneblock.listeners.NoBlockHandler#onRespawn(org.bukkit.event.player.PlayerRespawnEvent)}.
      */
     @Test
     public void testOnRespawnAirBlock() {
         when(block.isEmpty()).thenReturn(true);
-        PlayerRespawnEvent event = new PlayerRespawnEvent(p, location, false, false);
+        PlayerRespawnEvent event = new PlayerRespawnEvent(p, location, false, false, RespawnReason.DEATH);
         nbh.onRespawn(event);
         verify(block).setType(any(Material.class));
         
     }
-    
+
     /**
      * Test method for {@link world.bentobox.aoneblock.listeners.NoBlockHandler#onRespawn(org.bukkit.event.player.PlayerRespawnEvent)}.
      */
@@ -129,12 +131,12 @@ public void testOnRespawnAirBlock() {
     public void testOnRespawnAirBlockWrongWorld() {
         when(aob.inWorld(world)).thenReturn(false);
         when(block.isEmpty()).thenReturn(true);
-        PlayerRespawnEvent event = new PlayerRespawnEvent(p, location, false, false);
+        PlayerRespawnEvent event = new PlayerRespawnEvent(p, location, false, false, RespawnReason.DEATH);
         nbh.onRespawn(event);
         verify(block, never()).setType(any(Material.class));
         
     }
-    
+
     /**
      * Test method for {@link world.bentobox.aoneblock.listeners.NoBlockHandler#onRespawn(org.bukkit.event.player.PlayerRespawnEvent)}.
      */
@@ -142,11 +144,10 @@ public void testOnRespawnAirBlockWrongWorld() {
     public void testOnRespawnAirBlockNoIsland() {
         when(im.getIsland(world, ID)).thenReturn(null);
         when(block.isEmpty()).thenReturn(true);
-        PlayerRespawnEvent event = new PlayerRespawnEvent(p, location, false, false);
+        PlayerRespawnEvent event = new PlayerRespawnEvent(p, location, false, false, RespawnReason.DEATH);
         nbh.onRespawn(event);
         verify(block, never()).setType(any(Material.class));
         
     }
 
-
 }