diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 0ab4c954e..527ea6cd5 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -214,7 +214,7 @@ public class BentoBox extends JavaPlugin { if (islandsManager != null) { islandsManager.shutdown(); } - // Save settings + // Save settings - ensures admins always have the latest config file if (settings != null) { new Config<>(this, Settings.class).saveConfigObject(settings); } diff --git a/src/main/java/world/bentobox/bentobox/Settings.java b/src/main/java/world/bentobox/bentobox/Settings.java index 6dab1ca20..01eaaa209 100644 --- a/src/main/java/world/bentobox/bentobox/Settings.java +++ b/src/main/java/world/bentobox/bentobox/Settings.java @@ -140,6 +140,11 @@ public class Settings implements DataObject { @ConfigEntry(path = "island.name.max-length") private int nameMaxLength = 20; + @ConfigComment("Number of blocks to paste per tick when pasting a schem") + @ConfigComment("Smaller values will help reduce noticeable lag but will make pasting take longer") + @ConfigEntry(path = "island.paste-speed") + private int pasteSpeed = 1000; + // Ranks @ConfigEntry(path = "island.custom-ranks", experimental = true) private Map customRanks = new HashMap<>(); @@ -359,6 +364,20 @@ public class Settings implements DataObject { this.nameMaxLength = nameMaxLength; } + /** + * @param pasteSpeed the pasteSpeed to set + */ + public void setPasteSpeed(int pasteSpeed) { + this.pasteSpeed = pasteSpeed; + } + + /** + * @return paste speed in blocks per tick + */ + public int getPasteSpeed() { + return this.pasteSpeed; + } + public Map getCustomRanks() { return customRanks; } @@ -383,4 +402,6 @@ public class Settings implements DataObject { this.uniqueId = uniqueId; } + + } \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandResetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandResetCommand.java index 0baf1bb6d..cfa819d85 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandResetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandResetCommand.java @@ -90,6 +90,7 @@ public class IslandResetCommand extends ConfirmableCommand { // Reset the island Player player = user.getPlayer(); player.setGameMode(GameMode.SPECTATOR); + user.sendMessage("commands.island.create.creating-island"); // Get the player's old island Island oldIsland = getIslands().getIsland(getWorld(), player.getUniqueId()); // Remove them from this island (it still exists and will be deleted later) diff --git a/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java index 27e4a383b..718f15399 100644 --- a/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java @@ -10,10 +10,11 @@ import java.util.ArrayList; import java.util.List; import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.database.json.AbstractJSONDatabaseHandler; import world.bentobox.bentobox.database.DatabaseConnector; +import world.bentobox.bentobox.database.json.AbstractJSONDatabaseHandler; import world.bentobox.bentobox.database.objects.DataObject; /** @@ -72,7 +73,17 @@ public class MySQLDatabaseHandler extends AbstractJSONDatabaseHandler { // Load all the results Gson gson = getGson(); while (resultSet.next()) { - list.add(gson.fromJson(resultSet.getString("json"), dataObject)); + String json = resultSet.getString("json"); + if (json != null) { + try { + T gsonResult = gson.fromJson(json, dataObject); + if (gsonResult != null) { + list.add(gsonResult); + } + } catch (JsonSyntaxException ex) { + plugin.logError("Could not load object " + ex.getMessage()); + } + } } } } catch (SQLException e) { diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index ff7729d52..c2fa2cda0 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -564,6 +564,7 @@ public class IslandsManager { user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, String.valueOf(number)); } // Exit spectator mode if in it + if (player.getGameMode().equals(GameMode.SPECTATOR)) { player.setGameMode(plugin.getIWM().getDefaultGameMode(world)); } @@ -679,25 +680,25 @@ public class IslandsManager { * @param island to remove players from */ public void removePlayersFromIsland(Island island) { - // Teleport players away - for (Player player : Bukkit.getOnlinePlayers()) { - if (island.inIslandSpace(player.getLocation().getBlockX(), player.getLocation().getBlockZ())) { - // Teleport island players to their island home - if (hasIsland(island.getWorld(), player.getUniqueId()) || plugin.getIslands().inTeam(island.getWorld(), player.getUniqueId())) { - homeTeleport(island.getWorld(), player); + World w = island.getWorld(); + Bukkit.getOnlinePlayers().stream() + .filter(p -> p.getGameMode().equals(plugin.getIWM().getDefaultGameMode(island.getWorld()))) + .filter(p -> island.onIsland(p.getLocation())).forEach(p -> { + // Teleport island players to their island home + if (!island.getMemberSet().contains(p.getUniqueId()) && (hasIsland(w, p.getUniqueId()) || inTeam(w, p.getUniqueId()))) { + homeTeleport(w, p); + } else { + // Move player to spawn + if (spawn.containsKey(w)) { + // go to island spawn + p.teleport(spawn.get(w).getSpawnPoint(w.getEnvironment())); } else { - // Move player to spawn - if (spawn.containsKey(island.getWorld())) { - // go to island spawn - player.teleport(spawn.get(island.getWorld()).getSpawnPoint(island.getWorld().getEnvironment())); - } else { - plugin.logWarning("During island deletion player " + player.getName() + " could not be sent home so was placed into spectator mode."); - player.setGameMode(GameMode.SPECTATOR); - player.getPlayer().setFlying(true); - } + plugin.logWarning("During island deletion player " + p.getName() + " could not be sent home so was placed into spectator mode."); + p.setGameMode(GameMode.SPECTATOR); + p.setFlying(true); } } - } + }); } /** @@ -797,13 +798,13 @@ public class IslandsManager { */ public void setOwner(User user, UUID targetUUID, Island island) { islandCache.setOwner(island, targetUUID); - user.sendMessage("commands.island.team.setowner.name-is-the-owner", "[name]", plugin.getPlayers().getName(targetUUID)); + user.sendMessage("commands.island.team.setowner.name-is-the-owner", "[name]", plugin.getPlayers().getName(targetUUID)); plugin.getIWM().getAddon(island.getWorld()).ifPresent(addon -> { User target = User.getInstance(targetUUID); // Tell target. If they are offline, then they may receive a message when they login target.sendMessage("commands.island.team.setowner.you-are-the-owner"); // Permission checks for range changes only work when the target is online - if (target.isOnline()) { + if (target.isOnline()) { // Check if new owner has a different range permission than the island size int range = target.getPermissionValue( addon.getPermissionPrefix() + "island.range", diff --git a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java index c8fcb07bc..eb6f33c5d 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java @@ -46,11 +46,7 @@ public class NewIsland { this.reason = reason; this.world = world; this.name = name; - newIsland(); - if (oldIsland != null) { - // Delete the old island - plugin.getIslands().deleteIsland(oldIsland, true); - } + newIsland(oldIsland); } /** @@ -121,8 +117,9 @@ public class NewIsland { /** * Makes an island. + * @param oldIsland */ - public void newIsland() { + public void newIsland(Island oldIsland) { Location next = getNextIsland(); if (next == null) { plugin.logError("Failed to make island - no unoccupied spot found"); @@ -160,11 +157,21 @@ public class NewIsland { plugin.getPlayers().setHomeLocation(user, island.getSpawnPoint(Environment.NORMAL), 1); } // Stop the player from falling or moving if they are - user.getPlayer().setVelocity(new Vector(0,0,0)); - user.getPlayer().setFallDistance(0F); + if (user.isOnline()) { + user.getPlayer().setVelocity(new Vector(0,0,0)); + user.getPlayer().setFallDistance(0F); - // Teleport player after this island is built - plugin.getIslands().homeTeleport(world, user.getPlayer(), true); + // Teleport player after this island is built + plugin.getIslands().homeTeleport(world, user.getPlayer(), true); + } else { + // Remove the player again to completely clear the data + User.removePlayer(user.getPlayer()); + } + // Delete old island + if (oldIsland != null) { + // Delete the old island + plugin.getIslands().deleteIsland(oldIsland, true); + } }); // Make nether island if (plugin.getIWM().isNetherGenerate(world) && plugin.getIWM().isNetherIslands(world) && plugin.getIWM().getNetherWorld(world) != null) { diff --git a/src/main/java/world/bentobox/bentobox/schems/Clipboard.java b/src/main/java/world/bentobox/bentobox/schems/Clipboard.java index d52eaa5c3..961103f0a 100644 --- a/src/main/java/world/bentobox/bentobox/schems/Clipboard.java +++ b/src/main/java/world/bentobox/bentobox/schems/Clipboard.java @@ -10,6 +10,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -41,7 +42,9 @@ import org.bukkit.entity.Tameable; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; +import org.bukkit.material.Attachable; import org.bukkit.material.Colorable; +import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; import world.bentobox.bentobox.BentoBox; @@ -55,9 +58,21 @@ import world.bentobox.bentobox.util.Util; */ public class Clipboard { + enum PasteState { + BLOCKS, + ATTACHMENTS, + ENTITIES, + DONE + } + + // Speed of pasting + private int pasteSpeed; + private PasteState pasteState; + // Commonly used texts along this class. - private static final String ATTACHED = "attached"; - private static final String BLOCK = "blocks"; + private static final String ATTACHED_YAML_PREFIX = "attached."; + private static final String ENTITIES_YAML_PREFIX = "entities."; + private static final String BLOCKS_YAML_PREFIX = "blocks."; private static final String BEDROCK = "bedrock"; private static final String INVENTORY = "inventory"; private static final String ENTITY = "entity"; @@ -73,6 +88,8 @@ public class Clipboard { private File schemFolder; + private BukkitTask pastingTask; + public Clipboard(BentoBox plugin, File schemFolder) { super(); this.plugin = plugin; @@ -80,6 +97,8 @@ public class Clipboard { schemFolder.mkdirs(); } this.schemFolder = schemFolder; + pasteSpeed = plugin.getSettings().getPasteSpeed(); + pasteState = PasteState.BLOCKS; } /** @@ -187,15 +206,66 @@ public class Clipboard { // Calculate location for pasting Location loc = island.getCenter().toVector().subtract(off).toLocation(world); // Paste - if (blockConfig.contains(BLOCK)) { - blockConfig.getConfigurationSection(BLOCK).getKeys(false).forEach(b -> pasteBlock(world, island, loc, blockConfig.getConfigurationSection(BLOCK + "." + b))); - } else { + paste(world, island, loc, task); + } + + private void paste(World world, Island island, Location loc, Runnable task) { + if (!blockConfig.contains(BLOCKS_YAML_PREFIX)) { plugin.logError("Clipboard has no block data in it to paste!"); + return; } - // Run follow on task if it exists - if (task != null) { - Bukkit.getScheduler().runTaskLater(plugin, task, 2L); - } + // Iterators for the various schem sections + Iterator it = blockConfig.getConfigurationSection(BLOCKS_YAML_PREFIX).getKeys(false).iterator(); + Iterator it2 = blockConfig.contains(ATTACHED_YAML_PREFIX) ? blockConfig.getConfigurationSection(ATTACHED_YAML_PREFIX).getKeys(false).iterator() : null; + Iterator it3 = blockConfig.contains(ENTITIES_YAML_PREFIX) ? blockConfig.getConfigurationSection(ENTITIES_YAML_PREFIX).getKeys(false).iterator() : null; + + // Initial state & speed + pasteState = PasteState.BLOCKS; + pasteSpeed = plugin.getSettings().getPasteSpeed(); + + pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { + int count = 0; + while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && it.hasNext()) { + pasteBlock(world, island, loc, blockConfig.getConfigurationSection(BLOCKS_YAML_PREFIX + it.next())); + count++; + } + while (it2 != null && pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && it2.hasNext()) { + pasteBlock(world, island, loc, blockConfig.getConfigurationSection(ATTACHED_YAML_PREFIX + it2.next())); + count++; + } + while (it3 != null && pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && it3.hasNext()) { + pasteEntity(world, island, loc, blockConfig.getConfigurationSection(ENTITIES_YAML_PREFIX + it3.next())); + count++; + } + // STATE SHIFT + if (pasteState.equals(PasteState.BLOCKS) && !it.hasNext()) { + // Blocks done. + if (it2 == null && it3 == null) { + // No attachments or entities + pasteState = PasteState.DONE; + } else { + // Next paste attachments, otherwise skip to entities + pasteState = it2 != null ? PasteState.ATTACHMENTS : PasteState.ENTITIES; + } + } + if (pasteState.equals(PasteState.ATTACHMENTS) && !it2.hasNext()) { + // Attachments done. Next paste entities, otherwise done + pasteState = it3 != null ? PasteState.ENTITIES : PasteState.DONE; + } + if (pasteState.equals(PasteState.ENTITIES) && !it3.hasNext()) { + pasteState = PasteState.DONE; + } + if (pasteState.equals(PasteState.DONE)) { + // All done. Cancel task + pastingTask.cancel(); + if (task != null) { + // Run follow on task if it exists + Bukkit.getScheduler().runTaskLater(plugin, task, 2L); + } + } + }, 0L, 1L); + + } /** @@ -203,8 +273,9 @@ public class Clipboard { * @param location - location */ public void pasteClipboard(Location location) { - if (blockConfig.contains(BLOCK)) { - blockConfig.getConfigurationSection(BLOCK).getKeys(false).forEach(b -> pasteBlock(location.getWorld(), null, location, blockConfig.getConfigurationSection(BLOCK + "." + b))); + if (blockConfig.contains(BLOCKS_YAML_PREFIX)) { + paste(location.getWorld(), null, location, null); + //blockConfig.getConfigurationSection(BLOCKS_YAML_PREFIX).getKeys(false).forEach(b -> pasteBlock(location.getWorld(), null, location, blockConfig.getConfigurationSection(BLOCKS_YAML_PREFIX + "." + b))); } else { plugin.logError("Clipboard has no block data in it to paste!"); } @@ -229,7 +300,7 @@ public class Clipboard { plugin.getIWM().getAddon(island.getWorld()).ifPresent(addon -> { lines.clear(); for (int i = 0; i < 4; i++) { - lines.add(ChatColor.translateAlternateColorCodes('&', plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()), + lines.add(ChatColor.translateAlternateColorCodes('&', plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()), addon.getDescription().getName().toLowerCase() + ".sign.line" + i,""))); } }); @@ -257,18 +328,22 @@ public class Clipboard { Block block = world.getBlockAt(x, y, z); String blockData = config.getString("bd"); if (blockData != null) { - if (config.getBoolean(ATTACHED)) { - plugin.getServer().getScheduler().runTask(plugin, () -> setBlock(island, block, config, blockData)); - } else { - setBlock(island, block, config, blockData); - } + setBlock(island, block, config, blockData); } - // Entities + // Entities (legacy) if (config.isConfigurationSection(ENTITY)) { - setEntity(block.getLocation(), config); + setEntity(block.getLocation(), config.getConfigurationSection(ENTITY)); } } + private void pasteEntity(World world, Island island, Location location, ConfigurationSection config) { + String[] pos = config.getName().split(","); + int x = location.getBlockX() + Integer.valueOf(pos[0]); + int y = location.getBlockY() + Integer.valueOf(pos[1]); + int z = location.getBlockZ() + Integer.valueOf(pos[2]); + setEntity(new Location(world, x, y, z), config); + } + private void setBlock(Island island, Block block, ConfigurationSection config, String blockData) { // Set the block data block.setBlockData(Bukkit.createBlockData(blockData)); @@ -281,11 +356,11 @@ public class Clipboard { * @param location - locaton * @param config - config section */ - private void setEntity(Location location, ConfigurationSection config) { - ConfigurationSection en = config.getConfigurationSection(ENTITY); + private void setEntity(Location location, ConfigurationSection en) { en.getKeys(false).forEach(k -> { ConfigurationSection ent = en.getConfigurationSection(k); - Location center = location.add(new Vector(0.5, 0.0, 0.5)); + // Center, and just a bit high + Location center = location.add(new Vector(0.5, 0.5, 0.5)); LivingEntity e = (LivingEntity)location.getWorld().spawnEntity(center, EntityType.valueOf(ent.getString("type", "PIG"))); if (e != null) { e.setCustomName(ent.getString("name")); @@ -369,11 +444,11 @@ public class Clipboard { String pos = x + "," + y + "," + z; // Position defines the section - ConfigurationSection s = blockConfig.createSection(BLOCK + "." + pos); + ConfigurationSection s = blockConfig.createSection(BLOCKS_YAML_PREFIX + "." + pos); // Set entities for (LivingEntity e: entities) { - ConfigurationSection en = s.createSection("entity." + e.getUniqueId()); + ConfigurationSection en = blockConfig.createSection(ENTITIES_YAML_PREFIX + pos + "." + e.getUniqueId()); en.set("type", e.getType().name()); en.set("name", e.getCustomName()); if (e instanceof Colorable) { @@ -413,15 +488,34 @@ public class Clipboard { return true; } + // Block state + BlockState bs = block.getState(); + // Set block data - s.set("bd", block.getBlockData().getAsString()); + if (bs.getData() instanceof Attachable) { + ConfigurationSection a = blockConfig.createSection(ATTACHED_YAML_PREFIX + pos); + a.set("bd", block.getBlockData().getAsString()); + // Placeholder for attachment + s.set("bd", "minecraft:air"); + // Signs + if (bs instanceof Sign) { + Sign sign = (Sign)bs; + a.set("lines", Arrays.asList(sign.getLines())); + } + return true; + } else { + s.set("bd", block.getBlockData().getAsString()); + // Signs + if (bs instanceof Sign) { + Sign sign = (Sign)bs; + s.set("lines", Arrays.asList(sign.getLines())); + } + } if (block.getType().equals(Material.BEDROCK)) { blockConfig.set(BEDROCK, x + "," + y + "," + z); } - // Block state - BlockState bs = block.getState(); // Chests if (bs instanceof InventoryHolder) { InventoryHolder ih = (InventoryHolder)bs; @@ -432,11 +526,7 @@ public class Clipboard { } } } - // Signs - if (bs instanceof Sign) { - Sign sign = (Sign)bs; - s.set("lines", Arrays.asList(sign.getLines())); - } + if (bs instanceof CreatureSpawner) { CreatureSpawner spawner = (CreatureSpawner)bs; s.set("spawnedType",spawner.getSpawnedType().name()); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 9b8d7ef57..d79ee1063 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -72,6 +72,9 @@ island: # These set the minimum and maximum size of a name. min-length: 4 max-length: 20 + # Number of blocks to paste per tick when pasting a schem + # Smaller values will help reduce noticeable lag but will make pasting take longer + paste-speed: 1000 # /!\ This feature is experimental and might not work as expected or might not work at all. custom-ranks: {} # These settings should not be edited diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 4a60eb074..e4a572f3b 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -247,7 +247,7 @@ commands: parameters: "" too-many-islands: "&cThere are too many islands in this world: there isn't enough room for yours to be created." unable-create-island: "&cYour island could not be generated, please contact an administrator." - creating-island: "&aCreating your island..." + creating-island: "&aCreating your island, please wait a moment..." pick-world: "&cPick a world from [worlds]." unknown-schem: "&cThat schem has not been loaded yet." info: diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminClearresetsallCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminClearresetsallCommandTest.java index c7e5e9417..c1c24cad1 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminClearresetsallCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminClearresetsallCommandTest.java @@ -108,6 +108,7 @@ public class AdminClearresetsallCommandTest { /** * Test method for . */ + @SuppressWarnings("deprecation") @Test public void testExecuteCheckConfirm() { AdminClearresetsallCommand itl = new AdminClearresetsallCommand(ac); diff --git a/src/test/java/world/bentobox/bentobox/schems/ClipboardTest.java b/src/test/java/world/bentobox/bentobox/schems/ClipboardTest.java index 056809897..17b905a9b 100644 --- a/src/test/java/world/bentobox/bentobox/schems/ClipboardTest.java +++ b/src/test/java/world/bentobox/bentobox/schems/ClipboardTest.java @@ -36,6 +36,7 @@ import org.bukkit.entity.Sheep; import org.bukkit.inventory.HorseInventory; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.util.Vector; import org.junit.Before; @@ -47,10 +48,12 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; +@SuppressWarnings("deprecation") @RunWith(PowerMockRunner.class) @PrepareForTest({Bukkit.class}) public class ClipboardTest { @@ -76,7 +79,7 @@ public class ClipboardTest { block = mock(Block.class); when(block.getType()).thenReturn(Material.GRASS); when(block.getLocation()).thenReturn(loc); - + BlockData bd = mock(BlockData.class); when(bd.getAsString()).thenReturn("Block_data"); when(block.getBlockData()).thenReturn(bd); @@ -87,7 +90,7 @@ public class ClipboardTest { when(loc.getBlockZ()).thenReturn(3); when(loc.getBlock()).thenReturn(block); when(loc.toVector()).thenReturn(new Vector(1,2,3)); - + loc2 = mock(Location.class); when(loc2.getWorld()).thenReturn(world); when(loc2.getBlockX()).thenReturn(2); @@ -95,7 +98,7 @@ public class ClipboardTest { when(loc2.getBlockZ()).thenReturn(4); when(loc2.getBlock()).thenReturn(block); // Living entities - + List ents = new ArrayList<>(); Pig pig = mock(Pig.class); Player player = mock(Player.class); @@ -109,7 +112,7 @@ public class ClipboardTest { when(player.getLocation()).thenReturn(loc); when(sheep.getLocation()).thenReturn(loc); when(horse.getLocation()).thenReturn(loc); - + when(pig.getType()).thenReturn(EntityType.PIG); when(player.getType()).thenReturn(EntityType.PLAYER); when(cow.getType()).thenReturn(EntityType.COW); @@ -122,7 +125,7 @@ public class ClipboardTest { HorseInventory inv = mock(HorseInventory.class); when(horse.getInventory()).thenReturn(inv); - + // UUIDs (I'm going to assume these will all be unique (prays to god of randomness) when(creeper.getUniqueId()).thenReturn(UUID.randomUUID()); when(player.getUniqueId()).thenReturn(UUID.randomUUID()); @@ -138,29 +141,40 @@ public class ClipboardTest { ents.add(sheep); ents.add(horse); when(world.getLivingEntities()).thenReturn(ents); - + user = mock(User.class); User.setPlugin(plugin); when(user.getLocation()).thenReturn(loc); - + // Scheduler PowerMockito.mockStatic(Bukkit.class); sched = mock(BukkitScheduler.class); when(Bukkit.getScheduler()).thenReturn(sched); - + + // Settings + Settings settings = mock(Settings.class); + when(settings.getPasteSpeed()).thenReturn(200); + when(plugin.getSettings()).thenReturn(settings); + + // Default block state + BlockState bs = mock(BlockState.class); + when(block.getState()).thenReturn(bs); + MaterialData md = mock(MaterialData.class); + when(bs.getData()).thenReturn(md); + } @Test public void testClipboard() { when(schemFolder.exists()).thenReturn(false); - new Clipboard(plugin, schemFolder); + new Clipboard(plugin, schemFolder); Mockito.verify(schemFolder).mkdirs(); } @Test public void testSetGetPos1() { - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); assertNull(cb.getPos1()); cb.setPos1(loc); assertEquals(loc, cb.getPos1()); @@ -169,7 +183,7 @@ public class ClipboardTest { @Test public void testSetGetPos2() { - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); assertNull(cb.getPos2()); cb.setPos2(loc); assertEquals(loc, cb.getPos2()); @@ -177,7 +191,7 @@ public class ClipboardTest { @Test public void testSetGetOrigin() { - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); assertNull(cb.getOrigin()); cb.setOrigin(loc); assertEquals(loc, cb.getOrigin()); @@ -185,28 +199,28 @@ public class ClipboardTest { @Test public void testCopyNoPos1Pos2() { - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); cb.copy(user, false); Mockito.verify(user).sendMessage(Mockito.eq("commands.admin.schem.need-pos1-pos2")); } - + @Test public void testCopyNoPos2() { - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); cb.setPos1(loc); cb.copy(user, false); Mockito.verify(user).sendMessage(Mockito.eq("commands.admin.schem.need-pos1-pos2")); } - + @Test public void testCopy() { - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); cb.setPos1(loc); cb.setPos2(loc2); cb.copy(user, false); Mockito.verify(user).sendMessage("commands.admin.schem.copied-blocks", TextVariables.NUMBER, "8"); } - + @Test public void testCopySigns() { when(block.getType()).thenReturn(Material.SIGN); @@ -214,7 +228,7 @@ public class ClipboardTest { String[] lines = {"line1", "line2", "line3", "line4"}; when(bs.getLines()).thenReturn(lines); when(block.getState()).thenReturn(bs); - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); cb.setPos1(loc); cb.setPos2(loc2); cb.copy(user, false); @@ -232,7 +246,7 @@ public class ClipboardTest { when(inv.getContents()).thenReturn(contents); when(bs.getInventory()).thenReturn(inv); when(block.getState()).thenReturn(bs); - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); cb.setPos1(loc); cb.setPos2(loc2); cb.copy(user, false); @@ -240,14 +254,14 @@ public class ClipboardTest { // Every block is a sign, so this should be called 8 times Mockito.verify(bs, Mockito.times(8)).getInventory(); } - + @Test public void testCopyCreatureSpawners() { when(block.getType()).thenReturn(Material.SPAWNER); CreatureSpawner bs = mock(CreatureSpawner.class); when(bs.getSpawnedType()).thenReturn(EntityType.CAVE_SPIDER); when(block.getState()).thenReturn(bs); - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); cb.setPos1(loc); cb.setPos2(loc2); cb.copy(user, false); @@ -255,7 +269,7 @@ public class ClipboardTest { // Every block is a sign, so this should be called 8 times Mockito.verify(bs, Mockito.times(8)).getMaxNearbyEntities(); } - + @Test public void testCopyAir() { // No entities @@ -263,7 +277,7 @@ public class ClipboardTest { when(block.getType()).thenReturn(Material.AIR); BlockState bs = mock(BlockState.class); when(block.getState()).thenReturn(bs); - Clipboard cb = new Clipboard(plugin, schemFolder); + Clipboard cb = new Clipboard(plugin, schemFolder); cb.setPos1(loc); cb.setPos2(loc2); // Do not copy air @@ -273,7 +287,7 @@ public class ClipboardTest { Mockito.verify(user).sendMessage("commands.admin.schem.copied-blocks", TextVariables.NUMBER, "8"); } - + @Test public void testPasteIslandNoData() { Clipboard cb = new Clipboard(plugin, schemFolder); @@ -281,10 +295,10 @@ public class ClipboardTest { when(island.getCenter()).thenReturn(loc); cb.pasteIsland(world, island, () -> {}); Mockito.verify(plugin).logError(Mockito.eq("Clipboard has no block data in it to paste!")); - // Verify the task is run - Mockito.verify(sched).runTaskLater(Mockito.eq(plugin), Mockito.any(Runnable.class), Mockito.eq(2L)); + // Verify the task is run + Mockito.verify(sched, Mockito.never()).runTaskTimer(Mockito.any(), Mockito.any(Runnable.class), Mockito.eq(0L), Mockito.eq(1L)); } - + @Test public void testPasteIslandWithData() { Clipboard cb = new Clipboard(plugin, schemFolder); @@ -294,19 +308,8 @@ public class ClipboardTest { cb.setPos2(loc2); cb.copy(user, false); cb.pasteIsland(world, island, () -> {}); - // This is set just once because the coords of the block are always the same - Mockito.verify(block).setBlockData(Mockito.any()); - // Verify the entities are spawned - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.PIG)); - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.CREEPER)); - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.HORSE)); - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.SHEEP)); - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.COW)); - // Player should NOT spawn!! - Mockito.verify(world, Mockito.never()).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.PLAYER)); - // Verify the task is run - Mockito.verify(sched).runTaskLater(Mockito.eq(plugin), Mockito.any(Runnable.class), Mockito.eq(2L)); + Mockito.verify(sched).runTaskTimer(Mockito.any(), Mockito.any(Runnable.class), Mockito.eq(0L), Mockito.eq(1L)); } @Test @@ -315,7 +318,7 @@ public class ClipboardTest { cb.pasteClipboard(loc); Mockito.verify(plugin).logError(Mockito.eq("Clipboard has no block data in it to paste!")); } - + @Test public void testPasteClipboard() { Clipboard cb = new Clipboard(plugin, schemFolder); @@ -323,14 +326,8 @@ public class ClipboardTest { cb.setPos2(loc2); cb.copy(user, false); cb.pasteClipboard(loc); - // This is set just once because the coords of the block are always the same - Mockito.verify(block).setBlockData(Mockito.any()); - // Verify the entities are spawned - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.PIG)); - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.CREEPER)); - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.HORSE)); - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.SHEEP)); - Mockito.verify(world).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.COW)); + // Verify the task is run + Mockito.verify(sched).runTaskTimer(Mockito.any(), Mockito.any(Runnable.class), Mockito.eq(0L), Mockito.eq(1L)); // Player should NOT spawn!! Mockito.verify(world, Mockito.never()).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.PLAYER)); }