diff --git a/pom.xml b/pom.xml
index fc91f8455..696d9087e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -321,6 +321,13 @@
RC-36
provided
+
+
+ com.github.LoneDev6
+ api-itemsadder
+ 3.6.1
+ provided
+
diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java
index 1fb0ed916..bea18a9c6 100644
--- a/src/main/java/world/bentobox/bentobox/BentoBox.java
+++ b/src/main/java/world/bentobox/bentobox/BentoBox.java
@@ -22,6 +22,7 @@
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.commands.BentoBoxCommand;
import world.bentobox.bentobox.database.DatabaseSetup;
+import world.bentobox.bentobox.hooks.ItemsAdderHook;
import world.bentobox.bentobox.hooks.MultiverseCoreHook;
import world.bentobox.bentobox.hooks.MyWorldsHook;
import world.bentobox.bentobox.hooks.SlimefunHook;
@@ -235,6 +236,9 @@ private void completeSetup(long loadTime) {
// Register Slimefun
hooksManager.registerHook(new SlimefunHook());
+ // Register ItemsAdder
+ hooksManager.registerHook(new ItemsAdderHook(this));
+
// TODO: re-enable after implementation
//hooksManager.registerHook(new DynmapHook());
// TODO: re-enable after rework
diff --git a/src/main/java/world/bentobox/bentobox/api/hooks/Hook.java b/src/main/java/world/bentobox/bentobox/api/hooks/Hook.java
index 046679aca..63a6a9a25 100644
--- a/src/main/java/world/bentobox/bentobox/api/hooks/Hook.java
+++ b/src/main/java/world/bentobox/bentobox/api/hooks/Hook.java
@@ -69,5 +69,7 @@ public boolean isPluginAvailable() {
* Returns an explanation that will be sent to the user to tell them why the hook process did not succeed.
* @return the probable causes why the hook process did not succeed.
*/
- public abstract String getFailureCause();
+ public String getFailureCause() {
+ return "";
+ }
}
diff --git a/src/main/java/world/bentobox/bentobox/hooks/ItemsAdderHook.java b/src/main/java/world/bentobox/bentobox/hooks/ItemsAdderHook.java
new file mode 100644
index 000000000..9a96bd5fc
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/hooks/ItemsAdderHook.java
@@ -0,0 +1,124 @@
+package world.bentobox.bentobox.hooks;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.entity.EntityExplodeEvent;
+
+import dev.lone.itemsadder.api.CustomBlock;
+import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.api.flags.Flag;
+import world.bentobox.bentobox.api.flags.FlagListener;
+import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick;
+import world.bentobox.bentobox.api.hooks.Hook;
+import world.bentobox.bentobox.api.user.User;
+import world.bentobox.bentobox.managers.RanksManager;
+
+/**
+ * Hook to enable itemsadder blocks to be deleted when islands are deleted.
+ * It also includes a flag to track explosion access
+ */
+public class ItemsAdderHook extends Hook {
+
+ /**
+ * This flag allows to switch which island member group can use explosive items from Items Adder.
+ */
+ public static final Flag ITEMS_ADDER_EXPLOSIONS =
+ new Flag.Builder("ITEMS_ADDER_EXPLOSIONS", Material.TNT).
+ type(Flag.Type.PROTECTION).
+ defaultRank(RanksManager.MEMBER_RANK).
+ clickHandler(new CycleClick("ITEMS_ADDER_EXPLOSIONS",
+ RanksManager.VISITOR_RANK, RanksManager.OWNER_RANK))
+ .
+ build();
+
+ private BentoBox plugin;
+
+ private BlockInteractListener listener;
+
+ /**
+ * Register the hook
+ * @param plugin BentoBox
+ */
+ public ItemsAdderHook(BentoBox plugin) {
+ super("ItemsAdder", Material.NETHER_STAR);
+ this.plugin = plugin;
+ }
+
+ @Override
+ public boolean hook() {
+ // See if ItemsAdder is around
+ if (Bukkit.getPluginManager().getPlugin("ItemsAdder") == null) {
+ return false;
+ }
+ // Register listener
+ listener = new BlockInteractListener();
+ Bukkit.getPluginManager().registerEvents(listener, plugin);
+ plugin.getFlagsManager().registerFlag(ITEMS_ADDER_EXPLOSIONS);
+ return true;
+ }
+
+ /**
+ * @return the listener
+ */
+ protected BlockInteractListener getListener() {
+ return listener;
+ }
+
+ /**
+ * Remove the CustomBlock at location
+ * @param location
+ */
+ public void clearBlockInfo(Location location) {
+ CustomBlock.remove(location);
+ }
+
+ class BlockInteractListener extends FlagListener {
+
+ /**
+ * Handles explosions of ItemAdder items
+ * @param event explosion event
+ */
+ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
+ public void onExplosion(EntityExplodeEvent event)
+ {
+ if (!EntityType.PLAYER.equals(event.getEntityType())) {
+ // Ignore non-player explosions.
+ return;
+ }
+
+ Player player = (Player) event.getEntity();
+
+ if (!player.hasPermission("XXXXXX")) {
+ // Ignore players that does not have magic XXXXXX permission.
+ return;
+ }
+
+ // Use BentoBox flag processing system to validate usage.
+ // Technically not necessary as internally it should be cancelled by BentoBox.
+
+ if (!this.checkIsland(event, player, event.getLocation(), ITEMS_ADDER_EXPLOSIONS)) {
+ // Remove any blocks from the explosion list if required
+ event.blockList().removeIf(block -> this.protect(player, block.getLocation()));
+ event.setCancelled(this.protect(player, event.getLocation()));
+ }
+ }
+
+
+ /**
+ * This method returns if the protection in given location is enabled or not.
+ * @param player Player who triggers explosion.
+ * @param location Location where explosion happens.
+ * @return {@code true} if location is protected, {@code false} otherwise.
+ */
+ private boolean protect(Player player, Location location)
+ {
+ return plugin.getIslands().getProtectedIslandAt(location)
+ .map(island -> !island.isAllowed(User.getInstance(player), ITEMS_ADDER_EXPLOSIONS)).orElse(false);
+ }
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java
index 6b6a2800c..2e0c4fd8a 100644
--- a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java
+++ b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java
@@ -38,6 +38,7 @@
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.database.objects.IslandDeletion;
+import world.bentobox.bentobox.hooks.ItemsAdderHook;
import world.bentobox.bentobox.hooks.SlimefunHook;
import world.bentobox.bentobox.util.MyBiomeGrid;
@@ -194,10 +195,12 @@ private void copyChunkDataToChunk(Chunk toChunk, Chunk fromChunk, BoundingBox li
if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) {
toChunk.getBlock(x, y, z).setBiome(fromChunk.getBlock(x, y, z).getBiome());
}
- // Delete any slimefun blocks
+ // Delete any 3rd party blocks
Location loc = new Location(toChunk.getWorld(), baseX + x, y, baseZ + z);
plugin.getHooks().getHook("Slimefun")
- .ifPresent(sf -> ((SlimefunHook) sf).clearBlockInfo(loc, true));
+ .ifPresent(hook -> ((SlimefunHook) hook).clearBlockInfo(loc, true));
+ plugin.getHooks().getHook("ItemsAdder")
+ .ifPresent(hook -> ((ItemsAdderHook) hook).clearBlockInfo(loc));
}
}
}
@@ -377,10 +380,12 @@ private void copyChunkDataToChunk(Chunk chunk, ChunkGenerator.ChunkData chunkDat
if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) {
chunk.getBlock(x, y, z).setBiome(biomeGrid.getBiome(x, y, z));
}
- // Delete any slimefun blocks
+ // Delete any 3rd party blocks
Location loc = new Location(chunk.getWorld(), baseX + x, y, baseZ + z);
plugin.getHooks().getHook("Slimefun")
.ifPresent(sf -> ((SlimefunHook) sf).clearBlockInfo(loc, true));
+ plugin.getHooks().getHook("ItemsAdder")
+ .ifPresent(hook -> ((ItemsAdderHook) hook).clearBlockInfo(loc));
}
}
}
diff --git a/src/main/java/world/bentobox/bentobox/nms/SimpleWorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/SimpleWorldRegenerator.java
deleted file mode 100644
index 218c72af7..000000000
--- a/src/main/java/world/bentobox/bentobox/nms/SimpleWorldRegenerator.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package world.bentobox.bentobox.nms;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.CompletableFuture;
-
-import org.bukkit.Chunk;
-import org.bukkit.Location;
-import org.bukkit.World;
-import org.bukkit.block.data.BlockData;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.Player;
-import org.bukkit.generator.BlockPopulator;
-import org.bukkit.generator.ChunkGenerator;
-import org.bukkit.inventory.InventoryHolder;
-import org.bukkit.scheduler.BukkitRunnable;
-import org.bukkit.util.BoundingBox;
-
-import io.papermc.lib.PaperLib;
-import world.bentobox.bentobox.BentoBox;
-import world.bentobox.bentobox.api.addons.GameModeAddon;
-import world.bentobox.bentobox.database.objects.IslandDeletion;
-import world.bentobox.bentobox.hooks.SlimefunHook;
-import world.bentobox.bentobox.util.MyBiomeGrid;
-
-public abstract class SimpleWorldRegenerator implements WorldRegenerator {
- private final BentoBox plugin;
-
- protected SimpleWorldRegenerator() {
- this.plugin = BentoBox.getInstance();
- }
-
- /**
- * Update the low-level chunk information for the given block to the new block ID and data. This
- * change will not be propagated to clients until the chunk is refreshed to them.
- *
- * @param chunk - chunk to be changed
- * @param x - x coordinate within chunk 0 - 15
- * @param y - y coordinate within chunk 0 - world height, e.g. 255
- * @param z - z coordinate within chunk 0 - 15
- * @param blockData - block data to set the block
- * @param applyPhysics - apply physics or not
- */
- protected abstract void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics);
-
- @Override
- public CompletableFuture regenerate(GameModeAddon gm, IslandDeletion di, World world) {
- CompletableFuture bigFuture = new CompletableFuture<>();
- new BukkitRunnable() {
- private int chunkX = di.getMinXChunk();
- private int chunkZ = di.getMinZChunk();
- CompletableFuture currentTask = CompletableFuture.completedFuture(null);
-
- @Override
- public void run() {
- if (!currentTask.isDone()) return;
- if (isEnded(chunkX)) {
- cancel();
- bigFuture.complete(null);
- return;
- }
- List> newTasks = new ArrayList<>();
- for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) {
- if (isEnded(chunkX)) {
- break;
- }
- final int x = chunkX;
- final int z = chunkZ;
-
- // Only process non-generated chunks
- if (world.isChunkGenerated(x, z)) {
- newTasks.add(regenerateChunk(gm, di, world, x, z));
- }
-
- chunkZ++;
- if (chunkZ > di.getMaxZChunk()) {
- chunkZ = di.getMinZChunk();
- chunkX++;
- }
- }
- currentTask = CompletableFuture.allOf(newTasks.toArray(new CompletableFuture[0]));
- }
-
- private boolean isEnded(int chunkX) {
- return chunkX > di.getMaxXChunk();
- }
- }.runTaskTimer(plugin, 0L, 20L);
- return bigFuture;
- }
-
- @SuppressWarnings("deprecation")
- private CompletableFuture regenerateChunk(GameModeAddon gm, IslandDeletion di, World world, int chunkX, int chunkZ) {
- CompletableFuture chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ);
- CompletableFuture invFuture = chunkFuture.thenAccept(chunk ->
- Arrays.stream(chunk.getTileEntities()).filter(InventoryHolder.class::isInstance)
- .filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ()))
- .forEach(te -> ((InventoryHolder) te).getInventory().clear())
- );
- CompletableFuture entitiesFuture = chunkFuture.thenAccept(chunk -> {
- for (Entity e : chunk.getEntities()) {
- if (!(e instanceof Player)) {
- e.remove();
- }
- }
- });
- CompletableFuture copyFuture = chunkFuture.thenApply(chunk -> {
- // Reset blocks
- MyBiomeGrid grid = new MyBiomeGrid(chunk.getWorld().getEnvironment());
- ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "delete");
- // Will be null if use-own-generator is set to true
- if (cg != null) {
- ChunkGenerator.ChunkData cd = cg.generateChunkData(chunk.getWorld(), new Random(), chunk.getX(), chunk.getZ(), grid);
- copyChunkDataToChunk(chunk, cd, grid, di.getBox());
- for (BlockPopulator pop : cg.getDefaultPopulators(world)) {
- pop.populate(world, new Random(), chunkX, chunkZ, null);
- }
- }
- return chunk;
- });
- CompletableFuture postCopyFuture = copyFuture.thenAccept(chunk ->
- // Remove all entities in chunk, including any dropped items as a result of clearing the blocks above
- Arrays.stream(chunk.getEntities()).filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())).forEach(Entity::remove));
- return CompletableFuture.allOf(invFuture, entitiesFuture, postCopyFuture);
- }
-
- @SuppressWarnings("deprecation")
- private void copyChunkDataToChunk(Chunk chunk, ChunkGenerator.ChunkData chunkData, ChunkGenerator.BiomeGrid biomeGrid, BoundingBox limitBox) {
- double baseX = chunk.getX() << 4;
- double baseZ = chunk.getZ() << 4;
- int minHeight = chunk.getWorld().getMinHeight();
- int maxHeight = chunk.getWorld().getMaxHeight();
- for (int x = 0; x < 16; x++) {
- for (int z = 0; z < 16; z++) {
- if (!limitBox.contains(baseX + x, 0, baseZ + z)) {
- continue;
- }
- for (int y = minHeight; y < maxHeight; y++) {
- setBlockInNativeChunk(chunk, x, y, z, chunkData.getBlockData(x, y, z), false);
- // 3D biomes, 4 blocks separated
- if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) {
- chunk.getBlock(x, y, z).setBiome(biomeGrid.getBiome(x, y, z));
- }
- // Delete any slimefun blocks
- Location loc = new Location(chunk.getWorld(), baseX + x, y, baseZ + z);
- BentoBox.getInstance().logDebug(loc + " " + plugin.getHooks().getHook("Slimefun").isPresent());
- plugin.getHooks().getHook("Slimefun")
- .ifPresent(sf -> ((SlimefunHook) sf).clearBlockInfo(loc, true));
- }
- }
- }
- }
-}
diff --git a/src/test/java/world/bentobox/bentobox/hooks/ItemsAdderHookTest.java b/src/test/java/world/bentobox/bentobox/hooks/ItemsAdderHookTest.java
new file mode 100644
index 000000000..cef05e26d
--- /dev/null
+++ b/src/test/java/world/bentobox/bentobox/hooks/ItemsAdderHookTest.java
@@ -0,0 +1,218 @@
+package world.bentobox.bentobox.hooks;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginManager;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.reflect.Whitebox;
+
+import dev.lone.itemsadder.api.CustomBlock;
+import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.api.user.Notifier;
+import world.bentobox.bentobox.api.user.User;
+import world.bentobox.bentobox.database.objects.Island;
+import world.bentobox.bentobox.database.objects.Players;
+import world.bentobox.bentobox.hooks.ItemsAdderHook.BlockInteractListener;
+import world.bentobox.bentobox.managers.FlagsManager;
+import world.bentobox.bentobox.managers.IslandWorldManager;
+import world.bentobox.bentobox.managers.IslandsManager;
+import world.bentobox.bentobox.managers.LocalesManager;
+import world.bentobox.bentobox.managers.PlaceholdersManager;
+import world.bentobox.bentobox.managers.PlayersManager;
+
+/**
+ * Test class for ItemsAdder hook
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ BentoBox.class, Bukkit.class, CustomBlock.class })
+public class ItemsAdderHookTest {
+
+ @Mock
+ private BentoBox plugin;
+ private ItemsAdderHook hook;
+ @Mock
+ private PluginManager pim;
+ @Mock
+ private Plugin itemsAdder;
+ @Mock
+ private FlagsManager fm;
+ @Mock
+ private Location location;
+ @Mock
+ private Player entity;
+ @Mock
+ private IslandWorldManager iwm;
+ @Mock
+ private World world;
+ @Mock
+ private IslandsManager im;
+ @Mock
+ private Island island;
+ @Mock
+ private PlayersManager pm;
+ @Mock
+ private PlaceholdersManager phm;
+ @Mock
+ private Notifier notifier;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ // Set up plugin
+ plugin = mock(BentoBox.class);
+ Whitebox.setInternalState(BentoBox.class, "instance", plugin);
+
+ // User
+ UUID uuid = UUID.randomUUID();
+ when(entity.getUniqueId()).thenReturn(uuid);
+ User.setPlugin(plugin);
+ User.getInstance(entity);
+
+ // Flags Manager
+ when(plugin.getFlagsManager()).thenReturn(fm);
+
+ // Bukkit
+ PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
+ when(Bukkit.getPluginManager()).thenReturn(pim);
+ when(pim.getPlugin("ItemsAdder")).thenReturn(itemsAdder);
+
+ // IWM
+ when(plugin.getIWM()).thenReturn(iwm);
+ when(iwm.inWorld(location)).thenReturn(true);
+
+ // CustomBlock
+ PowerMockito.mockStatic(CustomBlock.class, Mockito.RETURNS_MOCKS);
+
+ // Location
+ when(world.getName()).thenReturn("bskyblock");
+ when(location.getWorld()).thenReturn(world);
+
+ // Island manager
+ when(plugin.getIslands()).thenReturn(im);
+
+ when(im.getProtectedIslandAt(location)).thenReturn(Optional.of(island));
+
+ // Players Manager
+ when(plugin.getPlayers()).thenReturn(pm);
+ @Nullable
+ Players playerObject = new Players();
+ playerObject.setUniqueId(uuid.toString());
+ when(pm.getPlayer(uuid)).thenReturn(playerObject);
+
+ // Locales
+ LocalesManager lm = mock(LocalesManager.class);
+ when(lm.get(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class));
+ when(plugin.getLocalesManager()).thenReturn(lm);
+
+ // Return the same string
+ when(phm.replacePlaceholders(any(), anyString()))
+ .thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class));
+ when(plugin.getPlaceholdersManager()).thenReturn(phm);
+
+ // Notifier
+ when(plugin.getNotifier()).thenReturn(notifier);
+
+ hook = new ItemsAdderHook(plugin);
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @After
+ public void tearDown() throws Exception {
+ User.clearUsers();
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.hooks.ItemsAdderHook#hook()}.
+ */
+ @Test
+ public void testHook() {
+ assertTrue(hook.hook());
+ verify(pim).registerEvents(hook.getListener(), plugin);
+ verify(fm).registerFlag(ItemsAdderHook.ITEMS_ADDER_EXPLOSIONS);
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.hooks.ItemsAdderHook#hook()}.
+ */
+ @Test
+ public void testHookFail() {
+ // No plugin
+ when(pim.getPlugin("ItemsAdder")).thenReturn(null);
+ assertFalse(hook.hook());
+ verify(pim, never()).registerEvents(any(), any());
+ verify(fm, never()).registerFlag(any());
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.hooks.ItemsAdderHook.BlockInteractListener#onExplosion(EntityExplodeEvent)}
+ */
+ @Test
+ public void testListener() {
+ // Make listener
+ assertTrue(hook.hook());
+ BlockInteractListener listener = hook.getListener();
+ when(entity.getType()).thenReturn(EntityType.PLAYER);
+ when(entity.hasPermission("XXXXXX")).thenReturn(true);
+ List list = new ArrayList<>();
+ EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0);
+ listener.onExplosion(event);
+ assertTrue(event.isCancelled());
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.hooks.ItemsAdderHook#ItemsAdderHook(world.bentobox.bentobox.BentoBox)}.
+ */
+ @Test
+ public void testItemsAdderHook() {
+ assertNotNull(hook);
+ assertEquals(Material.NETHER_STAR, hook.getIcon());
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.hooks.ItemsAdderHook#clearBlockInfo(org.bukkit.Location)}.
+ */
+ @Test
+ public void testClearBlockInfo() {
+ hook.clearBlockInfo(location);
+ PowerMockito.verifyStatic(CustomBlock.class);
+ CustomBlock.remove(location);
+ }
+
+}