From 7c1e195eaffb29344c414958ddc9d90921dd21ce Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 12 Jan 2019 10:31:25 -0800 Subject: [PATCH 1/8] Switch to timer-based chunk deletion --- .../bentobox/bentobox/database/Database.java | 2 +- .../database/objects/DeletedIslandDO.java | 172 ++++++++++++++++++ .../bentobox/managers/IslandsManager.java | 11 +- .../bentobox/util/DeleteIslandChunks.java | 68 +++---- 4 files changed, 213 insertions(+), 40 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java diff --git a/src/main/java/world/bentobox/bentobox/database/Database.java b/src/main/java/world/bentobox/bentobox/database/Database.java index 35f4d8d33..9e97be8dc 100644 --- a/src/main/java/world/bentobox/bentobox/database/Database.java +++ b/src/main/java/world/bentobox/bentobox/database/Database.java @@ -109,7 +109,7 @@ public class Database { handler.deleteObject(object); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | IntrospectionException e) { - logger.severe(() -> "Could not delete config! Error: " + e.getMessage()); + logger.severe(() -> "Could not delete object! Error: " + e.getMessage()); } } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java b/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java new file mode 100644 index 000000000..22de17b59 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java @@ -0,0 +1,172 @@ +package world.bentobox.bentobox.database.objects; + +import org.bukkit.Location; +import org.bukkit.World; + +import com.google.gson.annotations.Expose; + +import world.bentobox.bentobox.util.Util; + +/** + * Data object to store islands in deletion + * + */ +public class DeletedIslandDO implements DataObject { + + @Expose + private String uniqueId = ""; + + @Expose + private World world; + + @Expose + private int minXChunk; + + @Expose + private int maxXChunk; + + @Expose + private int minZChunk; + + @Expose + private int maxZChunk; + + public DeletedIslandDO() {} + + public DeletedIslandDO(Location location, int minXChunk, int maxXChunk, int minZChunk, int maxZChunk) { + this.uniqueId = Util.getStringLocation(location); + this.world = location.getWorld(); + this.minXChunk = minXChunk; + this.maxXChunk = maxXChunk; + this.minZChunk = minZChunk; + this.maxZChunk = maxZChunk; + } + + public DeletedIslandDO(Island island) { + uniqueId = Util.getStringLocation(island.getCenter()); + world = island.getCenter().getWorld(); + minXChunk = island.getMinX() >> 4; + maxXChunk = (island.getRange() * 2 + island.getMinX() - 1) >> 4; + minZChunk = island.getMinZ() >> 4; + maxZChunk = (island.getRange() * 2 + island.getMinZ() - 1) >> 4; + } + + @Override + public String getUniqueId() { + return uniqueId; + } + + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + /** + * @return the world + */ + public World getWorld() { + return world; + } + + /** + * @return the minXChunk + */ + public int getMinXChunk() { + return minXChunk; + } + + /** + * @return the maxXChunk + */ + public int getMaxXChunk() { + return maxXChunk; + } + + /** + * @return the minZChunk + */ + public int getMinZChunk() { + return minZChunk; + } + + /** + * @return the maxZChunk + */ + public int getMaxZChunk() { + return maxZChunk; + } + + /** + * @param world the world to set + */ + public void setWorld(World world) { + this.world = world; + } + + /** + * @param minXChunk the minXChunk to set + */ + public void setMinXChunk(int minXChunk) { + this.minXChunk = minXChunk; + } + + /** + * @param maxXChunk the maxXChunk to set + */ + public void setMaxXChunk(int maxXChunk) { + this.maxXChunk = maxXChunk; + } + + /** + * @param minZChunk the minZChunk to set + */ + public void setMinZChunk(int minZChunk) { + this.minZChunk = minZChunk; + } + + /** + * @param maxZChunk the maxZChunk to set + */ + public void setMaxZChunk(int maxZChunk) { + this.maxZChunk = maxZChunk; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof DeletedIslandDO)) { + return false; + } + DeletedIslandDO other = (DeletedIslandDO) obj; + if (uniqueId == null) { + if (other.uniqueId != null) { + return false; + } + } else if (!uniqueId.equals(other.uniqueId)) { + return false; + } + return true; + } + + +} + diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index ec7f5cd90..d8530c71b 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -24,9 +24,13 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.events.IslandBaseEvent; +import world.bentobox.bentobox.api.events.island.IslandEvent; +import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.Database; +import world.bentobox.bentobox.database.objects.DeletedIslandDO; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.island.IslandCache; @@ -244,6 +248,11 @@ public class IslandsManager { if (island == null) { return; } + // Fire event + IslandBaseEvent event = IslandEvent.builder().island(island).reason(Reason.DELETE).build(); + if (event.isCancelled()) { + return; + } // Set the owner of the island to no one. island.setOwner(null); island.setFlag(Flags.LOCK, RanksManager.VISITOR_RANK); @@ -255,7 +264,7 @@ public class IslandsManager { // Remove players from island removePlayersFromIsland(island); // Remove blocks from world - new DeleteIslandChunks(plugin, island); + new DeleteIslandChunks(plugin, new DeletedIslandDO(island)); } } diff --git a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java index 8fac63fdc..7aa610498 100644 --- a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java +++ b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java @@ -1,12 +1,12 @@ package world.bentobox.bentobox.util; -import org.bukkit.World; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitTask; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; -import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.database.objects.DeletedIslandDO; /** * Deletes islands fast using chunk regeneration @@ -16,42 +16,34 @@ import world.bentobox.bentobox.database.objects.Island; */ public class DeleteIslandChunks { - /** - * Deletes the island - * @param plugin - plugin object - * @param island - island to delete - */ - @SuppressWarnings("deprecation") - public DeleteIslandChunks(final BentoBox plugin, final Island island) { - // Fire event - IslandBaseEvent event = IslandEvent.builder().island(island).reason(Reason.DELETE).build(); - if (event.isCancelled()) { - return; - } - final World world = island.getCenter().getWorld(); - if (world == null) { - return; - } - int minXChunk = island.getMinX() >> 4; - int maxXChunk = (island.getRange() * 2 + island.getMinX() - 1) >> 4; - int minZChunk = island.getMinZ() >> 4; - int maxZChunk = (island.getRange() * 2 + island.getMinZ() - 1) >> 4; - for (int x = minXChunk; x <= maxXChunk; x++) { - for (int z = minZChunk; z<=maxZChunk; z++) { - world.regenerateChunk(x, z); - //System.out.println("regenerating = " + x + "," + z); - if (plugin.getIWM().isNetherGenerate(world) && plugin.getIWM().isNetherIslands(world)) { - plugin.getIWM().getNetherWorld(world).regenerateChunk(x, z); + private int x; + private int z; + private BukkitTask task; - } - if (plugin.getIWM().isEndGenerate(world) && plugin.getIWM().isEndIslands(world)) { - plugin.getIWM().getEndWorld(world).regenerateChunk(x, z); + @SuppressWarnings("deprecation") + public DeleteIslandChunks(BentoBox plugin, DeletedIslandDO di) { + x = di.getMinXChunk(); + z = di.getMinZChunk(); + task = Bukkit.getScheduler().runTaskTimer(plugin, () -> { + di.getWorld().regenerateChunk(x, z); + //System.out.println("regenerating = " + x + "," + z); + if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) { + plugin.getIWM().getNetherWorld(di.getWorld()).regenerateChunk(x, z); + + } + if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) { + plugin.getIWM().getEndWorld(di.getWorld()).regenerateChunk(x, z); + } + z++; + if (z > di.getMaxZChunk()) { + z = di.getMinZChunk(); + x++; + if (x > di.getMaxXChunk()) { + task.cancel(); + // Fire event + IslandEvent.builder().location(Util.getLocationString(di.getUniqueId())).reason(Reason.DELETED).build(); } } - } - // Fire event - IslandEvent.builder().island(island).reason(Reason.DELETED).build(); + }, 0L, 1L); - } - -} \ No newline at end of file + }} \ No newline at end of file From 7a4ca8b48d8f1526b32b2df6fd017ef044f47a3c Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 12 Jan 2019 17:04:53 -0800 Subject: [PATCH 2/8] Fixed JavaDoc error --- src/main/java/world/bentobox/bentobox/schems/Clipboard.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/schems/Clipboard.java b/src/main/java/world/bentobox/bentobox/schems/Clipboard.java index 961103f0a..f5928200a 100644 --- a/src/main/java/world/bentobox/bentobox/schems/Clipboard.java +++ b/src/main/java/world/bentobox/bentobox/schems/Clipboard.java @@ -353,8 +353,8 @@ public class Clipboard { /** * Sets any entity that is in this location - * @param location - locaton - * @param config - config section + * @param location - location + * @param en - config section */ private void setEntity(Location location, ConfigurationSection en) { en.getKeys(false).forEach(k -> { From 501c3257edef316128e6890bf652f70d9b86c053 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 12 Jan 2019 17:12:30 -0800 Subject: [PATCH 3/8] Island delete enhancements Island deletion is done a few chunks at a time per tick. Current speed is 5 chunks per tick per world (e.g., 15 chunks per tick if nether and end islands are used). Chunks are deleted based on the all-time maximum protection range of the island. This is because the protection range can grow bigger or smaller over the island's lifetime. To ensure all possible chunks are erased, the largest every protection range is remembered and used. Very large protection ranges will take a long time to fully delete. Info on islands that are being deleted is stored in the database. If the server shuts down mid-deletion, deletion will restart when the server restarts. While an island is being deleted, new islands cannot occupy that spot and the spot cannot be reserved by the admin. In addition, async approaches to island saving and player saving were removed. Async will be implemented another way. Now, instead of saving the full island or player database, individual database entries are saved instead to be more efficient. --- .../world/bentobox/bentobox/BentoBox.java | 29 ++- .../commands/admin/AdminRegisterCommand.java | 8 +- .../admin/range/AdminRangeResetCommand.java | 14 ++ .../admin/range/AdminRangeSetCommand.java | 14 ++ .../team/IslandTeamInviteAcceptCommand.java | 2 +- .../team/IslandTeamSetownerCommand.java | 7 +- .../bentobox/api/events/IslandBaseEvent.java | 2 +- .../api/events/island/IslandEvent.java | 47 +++- .../database/AbstractDatabaseHandler.java | 7 + .../bentobox/bentobox/database/Database.java | 11 + .../database/json/JSONDatabaseHandler.java | 42 ++-- .../mongodb/MongoDBDatabaseHandler.java | 16 +- .../database/mysql/MySQLDatabaseHandler.java | 26 ++- .../database/objects/DeletedIslandDO.java | 213 +++++++++--------- .../bentobox/database/objects/Island.java | 40 +++- .../database/yaml/YamlDatabaseHandler.java | 41 ++-- .../managers/IslandDeleteManager.java | 84 +++++++ .../bentobox/managers/IslandsManager.java | 61 ++--- .../bentobox/managers/PlayersManager.java | 15 +- .../bentobox/managers/island/IslandCache.java | 4 +- .../bentobox/managers/island/NewIsland.java | 2 +- .../bentobox/util/DeleteIslandChunks.java | 43 ++-- src/main/resources/locales/en-US.yml | 2 + .../admin/AdminRegisterCommandTest.java | 43 +++- .../bentobox/managers/IslandsManagerTest.java | 41 +++- .../bentobox/managers/PlayersManagerTest.java | 5 +- .../bentobox/util/DeleteIslandChunksTest.java | 152 ------------- 27 files changed, 562 insertions(+), 409 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/managers/IslandDeleteManager.java delete mode 100644 src/test/java/world/bentobox/bentobox/util/DeleteIslandChunksTest.java diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 89529ddb0..eda37b7cb 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -25,6 +25,7 @@ import world.bentobox.bentobox.managers.AddonsManager; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.FlagsManager; import world.bentobox.bentobox.managers.HooksManager; +import world.bentobox.bentobox.managers.IslandDeleteManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.LocalesManager; @@ -67,6 +68,8 @@ public class BentoBox extends JavaPlugin { private boolean isLoaded; + private IslandDeleteManager islandDeletionManager; + @Override public void onEnable(){ if (!ServerCompatibility.getInstance().checkCompatibility(this).isCanLaunch()) { @@ -142,10 +145,10 @@ public class BentoBox extends JavaPlugin { // Load islands from database - need to wait until all the worlds are loaded islandsManager.load(); - // Save islands & players data asynchronously every X minutes + // Save islands & players data every X minutes instance.getServer().getScheduler().runTaskTimer(instance, () -> { - playersManager.save(true); - islandsManager.save(true); + playersManager.saveAll(); + islandsManager.saveAll(); }, getSettings().getDatabaseBackupPeriod() * 20 * 60L, getSettings().getDatabaseBackupPeriod() * 20 * 60L); // Make sure all flag listeners are registered. @@ -169,13 +172,15 @@ public class BentoBox extends JavaPlugin { // Setup the Placeholders manager placeholdersManager = new PlaceholdersManager(this); - // Fire plugin ready event - isLoaded = true; - Bukkit.getServer().getPluginManager().callEvent(new BentoBoxReadyEvent()); - + // Show banner User.getInstance(Bukkit.getConsoleSender()).sendMessage("successfully-loaded", TextVariables.VERSION, instance.getDescription().getVersion(), "[time]", String.valueOf(System.currentTimeMillis() - startMillis)); + + // Fire plugin ready event - this should go last after everything else + isLoaded = true; + Bukkit.getServer().getPluginManager().callEvent(new BentoBoxReadyEvent()); + }); } @@ -196,6 +201,9 @@ public class BentoBox extends JavaPlugin { manager.registerEvents(new BannedVisitorCommands(this), this); // Death counter manager.registerEvents(new DeathListener(this), this); + // Island Delete Manager + islandDeletionManager = new IslandDeleteManager(this); + manager.registerEvents(islandDeletionManager, this); } @Override @@ -356,4 +364,11 @@ public class BentoBox extends JavaPlugin { public PlaceholdersManager getPlaceholdersManager() { return placeholdersManager; } + + /** + * @return the islandDeletionManager + */ + public IslandDeleteManager getIslandDeletionManager() { + return islandDeletionManager; + } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommand.java index 46a3654d0..a8c0a0461 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommand.java @@ -51,6 +51,12 @@ public class AdminRegisterCommand extends ConfirmableCommand { return false; } + // Check if this spot is still being deleted + Location closestIsland = getClosestIsland(user.getLocation()); + if (getPlugin().getIslandDeletionManager().inDeletion(closestIsland)) { + user.sendMessage("commands.admin.register.in-deletion"); + return false; + } // Check if island is owned Optional island = getIslands().getIslandAt(user.getLocation()); if (island.map(i -> i.getOwner() != null).orElse(false)) { @@ -69,7 +75,7 @@ public class AdminRegisterCommand extends ConfirmableCommand { user.sendMessage("commands.admin.register.no-island-here"); this.askConfirmation(user, () -> { // Make island here - Island i = getIslands().createIsland(getClosestIsland(user.getLocation()), targetUUID); + Island i = getIslands().createIsland(closestIsland, targetUUID); getIslands().setOwner(user, targetUUID, i); getWorld().getBlockAt(i.getCenter()).setType(Material.BEDROCK); user.sendMessage("commands.admin.register.registered-island", "[xyz]", Util.xyz(i.getCenter().toVector())); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeResetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeResetCommand.java index e835559a5..e89da5cce 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeResetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeResetCommand.java @@ -1,12 +1,15 @@ package world.bentobox.bentobox.api.commands.admin.range; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.util.Util; public class AdminRangeResetCommand extends CompositeCommand { @@ -50,4 +53,15 @@ public class AdminRangeResetCommand extends CompositeCommand { return true; } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommand.java index db420855a..7c28e3b68 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommand.java @@ -1,6 +1,8 @@ package world.bentobox.bentobox.api.commands.admin.range; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; import org.apache.commons.lang.StringUtils; @@ -9,6 +11,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.util.Util; public class AdminRangeSetCommand extends CompositeCommand { @@ -72,4 +75,15 @@ public class AdminRangeSetCommand extends CompositeCommand { return true; } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index cd6b84aad..cd324876d 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -106,7 +106,7 @@ public class IslandTeamInviteAcceptCommand extends CompositeCommand { if (inviter != null) { inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, user.getName()); } - getIslands().save(false); + getIslands().save(island); return true; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java index 43c0e889c..d743818fc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java @@ -10,6 +10,7 @@ import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.team.TeamEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.util.Util; public class IslandTeamSetownerCommand extends CompositeCommand { @@ -59,9 +60,9 @@ public class IslandTeamSetownerCommand extends CompositeCommand { return false; } // Fire event so add-ons can run commands, etc. + Island island = getIslands().getIsland(getWorld(), playerUUID); IslandBaseEvent event = TeamEvent.builder() - .island(getIslands() - .getIsland(getWorld(), playerUUID)) + .island(island) .reason(TeamEvent.Reason.SETOWNER) .involvedPlayer(targetUUID) .build(); @@ -70,7 +71,7 @@ public class IslandTeamSetownerCommand extends CompositeCommand { return false; } getIslands().setOwner(getWorld(), user, targetUUID); - getIslands().save(true); + getIslands().save(island); return true; } diff --git a/src/main/java/world/bentobox/bentobox/api/events/IslandBaseEvent.java b/src/main/java/world/bentobox/bentobox/api/events/IslandBaseEvent.java index c0002f2fc..eb8395394 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/IslandBaseEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/IslandBaseEvent.java @@ -43,7 +43,7 @@ public class IslandBaseEvent extends PremadeEvent implements Cancellable { } /** - * @return the island involved in this event + * @return the island involved in this event. This may be null in the case of deleted islands, so use location instead */ public Island getIsland(){ return island; diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java index 84fced873..70152b0b1 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java @@ -6,6 +6,7 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import world.bentobox.bentobox.api.events.IslandBaseEvent; +import world.bentobox.bentobox.database.objects.DeletedIslandDO; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; @@ -58,9 +59,14 @@ public class IslandEvent extends IslandBaseEvent { */ CREATED, /** - * Fired just before any island chunks are to be deleted. + * Fired when an island is to be deleted. Note an island can be deleted without having + * chunks removed. */ DELETE, + /** + * Fired when island chunks are going to be deleted + */ + DELETE_CHUNKS, /** * Fired after all island chunks have been deleted or set for regeneration by the server */ @@ -168,14 +174,39 @@ public class IslandEvent extends IslandBaseEvent { super(island, player, admin, location); } } + /** + * Fired when an island chunks are going to be deleted. + * May be cancelled. + * + */ + public static class IslandDeleteChunksEvent extends IslandBaseEvent { + private final DeletedIslandDO deletedIslandInfo; + + private IslandDeleteChunksEvent(Island island, UUID player, boolean admin, Location location, DeletedIslandDO deletedIsland) { + // Final variables have to be declared in the constructor + super(island, player, admin, location); + this.deletedIslandInfo = deletedIsland; + } + + public DeletedIslandDO getDeletedIslandInfo() { + return deletedIslandInfo; + } + } /** * Fired when an island is deleted. * */ public static class IslandDeletedEvent extends IslandBaseEvent { - private IslandDeletedEvent(Island island, UUID player, boolean admin, Location location) { + private final DeletedIslandDO deletedIslandInfo; + + private IslandDeletedEvent(Island island, UUID player, boolean admin, Location location, DeletedIslandDO deletedIsland) { // Final variables have to be declared in the constructor super(island, player, admin, location); + this.deletedIslandInfo = deletedIsland; + } + + public DeletedIslandDO getDeletedIslandInfo() { + return deletedIslandInfo; } } /** @@ -256,6 +287,7 @@ public class IslandEvent extends IslandBaseEvent { private Reason reason = Reason.UNKNOWN; private boolean admin; private Location location; + private DeletedIslandDO deletedIslandInfo; public IslandEventBuilder island(Island island) { this.island = island; @@ -295,6 +327,11 @@ public class IslandEvent extends IslandBaseEvent { return this; } + public IslandEventBuilder deletedIslandInfo(DeletedIslandDO deletedIslandInfo) { + this.deletedIslandInfo = deletedIslandInfo; + return this; + } + public IslandBaseEvent build() { // Call the generic event for developers who just want one event and use the Reason enum Bukkit.getServer().getPluginManager().callEvent(new IslandEvent(island, player, admin, location, reason)); @@ -316,8 +353,12 @@ public class IslandEvent extends IslandBaseEvent { IslandDeleteEvent delete = new IslandDeleteEvent(island, player, admin, location); Bukkit.getServer().getPluginManager().callEvent(delete); return delete; + case DELETE_CHUNKS: + IslandDeleteChunksEvent deleteChunks = new IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo); + Bukkit.getServer().getPluginManager().callEvent(deleteChunks); + return deleteChunks; case DELETED: - IslandDeletedEvent deleted = new IslandDeletedEvent(island, player, admin, location); + IslandDeletedEvent deleted = new IslandDeletedEvent(island, player, admin, location, deletedIslandInfo); Bukkit.getServer().getPluginManager().callEvent(deleted); return deleted; case ENTER: diff --git a/src/main/java/world/bentobox/bentobox/database/AbstractDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/AbstractDatabaseHandler.java index a56eae483..95772b0ad 100644 --- a/src/main/java/world/bentobox/bentobox/database/AbstractDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/AbstractDatabaseHandler.java @@ -110,4 +110,11 @@ public abstract class AbstractDatabaseHandler { * Closes the database */ public abstract void close(); + + /** + * Attempts to delete the object with the uniqueId + * @param uniqueId - uniqueId of object + * @return true if successful, false if there is no such uniqueId + */ + public abstract boolean deleteID(String uniqueId); } diff --git a/src/main/java/world/bentobox/bentobox/database/Database.java b/src/main/java/world/bentobox/bentobox/database/Database.java index 9e97be8dc..21d1739e0 100644 --- a/src/main/java/world/bentobox/bentobox/database/Database.java +++ b/src/main/java/world/bentobox/bentobox/database/Database.java @@ -100,6 +100,15 @@ public class Database { return handler.objectExists(name); } + /** + * Attempts to delete the object with the uniqueId + * @param uniqueId - uniqueId of object + * @return true if successful, false if there is no such uniqueId + */ + public boolean deleteID(String uniqueId) { + return handler.deleteID(uniqueId); + } + /** * Delete object from database * @param object - object to delete @@ -120,4 +129,6 @@ public class Database { handler.close(); } + + } \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseHandler.java index 8f0ce46f2..4f3f8e9cf 100644 --- a/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseHandler.java @@ -122,6 +122,28 @@ public class JSONDatabaseHandler extends AbstractJSONDatabaseHandler { } } + @Override + public boolean deleteID(String uniqueId) { + // The filename of the JSON file is the value of uniqueId field plus .json. Sometimes the .json is already appended. + if (!uniqueId.endsWith(JSON)) { + uniqueId = uniqueId + JSON; + } + // Get the database and table folders + File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME); + File tableFolder = new File(dataFolder, dataObject.getSimpleName()); + if (tableFolder.exists()) { + // Obtain the file and delete it + File file = new File(tableFolder, uniqueId); + try { + Files.delete(file.toPath()); + return true; + } catch (IOException e) { + plugin.logError("Could not delete json database object! " + file.getName() + " - " + e.getMessage()); + } + } + return false; + } + @Override public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException { // Null check @@ -137,25 +159,7 @@ public class JSONDatabaseHandler extends AbstractJSONDatabaseHandler { // Obtain the value of uniqueId within the instance (which must be a DataObject) PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject); Method method = propertyDescriptor.getReadMethod(); - String fileName = (String) method.invoke(instance); - - // The filename of the JSON file is the value of uniqueId field plus .json. Sometimes the .json is already appended. - if (!fileName.endsWith(JSON)) { - fileName = fileName + JSON; - } - - // Get the database and table folders - File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME); - File tableFolder = new File(dataFolder, dataObject.getSimpleName()); - if (tableFolder.exists()) { - // Obtain the file and delete it - File file = new File(tableFolder, fileName); - try { - Files.delete(file.toPath()); - } catch (IOException e) { - plugin.logError("Could not delete json database object! " + file.getName() + " - " + e.getMessage()); - } - } + deleteID((String) method.invoke(instance)); } @Override diff --git a/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java index 05e031f61..b5a60efdb 100644 --- a/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java @@ -106,6 +106,16 @@ public class MongoDBDatabaseHandler extends AbstractJSONDatabaseHandler { } } + @Override + public boolean deleteID(String uniqueId) { + try { + return collection.findOneAndDelete(new Document(MONGO_ID, uniqueId)) == null ? false : true; + } catch (Exception e) { + plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage()); + return false; + } + } + @Override public void deleteObject(T instance) { // Null check @@ -117,11 +127,7 @@ public class MongoDBDatabaseHandler extends AbstractJSONDatabaseHandler { plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); return; } - try { - collection.findOneAndDelete(new Document(MONGO_ID, ((DataObject)instance).getUniqueId())); - } catch (Exception e) { - plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage()); - } + deleteID(((DataObject)instance).getUniqueId()); } @Override 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 6f1f5bf72..3ec2d216c 100644 --- a/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java @@ -141,6 +141,22 @@ public class MySQLDatabaseHandler extends AbstractJSONDatabaseHandler { } } + @Override + public boolean deleteID(String uniqueId) { + String sb = "DELETE FROM `" + + dataObject.getCanonicalName() + + "` WHERE uniqueId = ?"; + try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) { + // UniqueId needs to be placed in quotes + preparedStatement.setString(1, "\"" + uniqueId + "\""); + preparedStatement.execute(); + return preparedStatement.getUpdateCount() > 0; + } catch (Exception e) { + plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage()); + return false; + } + } + @Override public void deleteObject(T instance) { // Null check @@ -152,15 +168,9 @@ public class MySQLDatabaseHandler extends AbstractJSONDatabaseHandler { plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); return; } - String sb = "DELETE FROM `" + - dataObject.getCanonicalName() + - "` WHERE uniqueId = ?"; - try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) { + try { Method getUniqueId = dataObject.getMethod("getUniqueId"); - String uniqueId = (String) getUniqueId.invoke(instance); - // UniqueId needs to be placed in quotes - preparedStatement.setString(1, "\"" + uniqueId + "\""); - preparedStatement.execute(); + deleteID((String) getUniqueId.invoke(instance)); } catch (Exception e) { plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage()); } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java b/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java index 22de17b59..57bdd621d 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java @@ -1,12 +1,12 @@ package world.bentobox.bentobox.database.objects; +import java.util.UUID; + import org.bukkit.Location; import org.bukkit.World; import com.google.gson.annotations.Expose; -import world.bentobox.bentobox.util.Util; - /** * Data object to store islands in deletion * @@ -17,7 +17,7 @@ public class DeletedIslandDO implements DataObject { private String uniqueId = ""; @Expose - private World world; + private Location location; @Expose private int minXChunk; @@ -33,115 +33,24 @@ public class DeletedIslandDO implements DataObject { public DeletedIslandDO() {} - public DeletedIslandDO(Location location, int minXChunk, int maxXChunk, int minZChunk, int maxZChunk) { - this.uniqueId = Util.getStringLocation(location); - this.world = location.getWorld(); - this.minXChunk = minXChunk; - this.maxXChunk = maxXChunk; - this.minZChunk = minZChunk; - this.maxZChunk = maxZChunk; - } - public DeletedIslandDO(Island island) { - uniqueId = Util.getStringLocation(island.getCenter()); - world = island.getCenter().getWorld(); - minXChunk = island.getMinX() >> 4; - maxXChunk = (island.getRange() * 2 + island.getMinX() - 1) >> 4; - minZChunk = island.getMinZ() >> 4; - maxZChunk = (island.getRange() * 2 + island.getMinZ() - 1) >> 4; + uniqueId = UUID.randomUUID().toString(); + location = island.getCenter(); + minXChunk = (location.getBlockX() - island.getMaxEverProtectionRange()) >> 4; + maxXChunk = (island.getMaxEverProtectionRange() + location.getBlockX() - 1) >> 4; + minZChunk = (location.getBlockZ() - island.getMaxEverProtectionRange()) >> 4; + maxZChunk = (island.getMaxEverProtectionRange() + location.getBlockZ() - 1) >> 4; } - @Override - public String getUniqueId() { - return uniqueId; - } - - @Override - public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; - } - - /** - * @return the world - */ - public World getWorld() { - return world; - } - - /** - * @return the minXChunk - */ - public int getMinXChunk() { - return minXChunk; - } - - /** - * @return the maxXChunk - */ - public int getMaxXChunk() { - return maxXChunk; - } - - /** - * @return the minZChunk - */ - public int getMinZChunk() { - return minZChunk; - } - - /** - * @return the maxZChunk - */ - public int getMaxZChunk() { - return maxZChunk; - } - - /** - * @param world the world to set - */ - public void setWorld(World world) { - this.world = world; - } - - /** - * @param minXChunk the minXChunk to set - */ - public void setMinXChunk(int minXChunk) { + public DeletedIslandDO(Location location, int minXChunk, int maxXChunk, int minZChunk, int maxZChunk) { + this.uniqueId = UUID.randomUUID().toString(); + this.location = location; this.minXChunk = minXChunk; - } - - /** - * @param maxXChunk the maxXChunk to set - */ - public void setMaxXChunk(int maxXChunk) { this.maxXChunk = maxXChunk; - } - - /** - * @param minZChunk the minZChunk to set - */ - public void setMinZChunk(int minZChunk) { this.minZChunk = minZChunk; - } - - /** - * @param maxZChunk the maxZChunk to set - */ - public void setMaxZChunk(int maxZChunk) { this.maxZChunk = maxZChunk; } - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); - return result; - } - /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @@ -167,6 +76,104 @@ public class DeletedIslandDO implements DataObject { return true; } + /** + * @return the location + */ + public Location getLocation() { + return location; + } + + /** + * @return the maxXChunk + */ + public int getMaxXChunk() { + return maxXChunk; + } + + /** + * @return the maxZChunk + */ + public int getMaxZChunk() { + return maxZChunk; + } + + /** + * @return the minXChunk + */ + public int getMinXChunk() { + return minXChunk; + } + + /** + * @return the minZChunk + */ + public int getMinZChunk() { + return minZChunk; + } + + @Override + public String getUniqueId() { + return uniqueId; + } + + /** + * @return the world + */ + public World getWorld() { + return location.getWorld(); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } + + /** + * @param location the location to set + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * @param maxXChunk the maxXChunk to set + */ + public void setMaxXChunk(int maxXChunk) { + this.maxXChunk = maxXChunk; + } + + /** + * @param maxZChunk the maxZChunk to set + */ + public void setMaxZChunk(int maxZChunk) { + this.maxZChunk = maxZChunk; + } + + /** + * @param minXChunk the minXChunk to set + */ + public void setMinXChunk(int minXChunk) { + this.minXChunk = minXChunk; + } + + /** + * @param minZChunk the minZChunk to set + */ + public void setMinZChunk(int minZChunk) { + this.minZChunk = minZChunk; + } + + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/Island.java b/src/main/java/world/bentobox/bentobox/database/objects/Island.java index f354de540..943fa68e1 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -24,11 +24,11 @@ import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.logs.LogEntry; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.adapters.Adapter; import world.bentobox.bentobox.database.objects.adapters.FlagSerializer; import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter; -import world.bentobox.bentobox.api.logs.LogEntry; import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Pair; @@ -60,6 +60,10 @@ public class Island implements DataObject { @Expose private int protectionRange; + // Maximum ever protection range - used in island deletion + @Expose + private int maxEverProtectionRange; + // World the island started in. This may be different from the island location @Expose private World world; @@ -113,6 +117,7 @@ public class Island implements DataObject { center = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()); range = BentoBox.getInstance().getIWM().getIslandDistance(world); this.protectionRange = protectionRange; + this.maxEverProtectionRange = protectionRange; } /** @@ -284,6 +289,20 @@ public class Island implements DataObject { return protectionRange; } + /** + * @return the maxEverProtectionRange or the protection range, whichever is larger + */ + public int getMaxEverProtectionRange() { + return Math.max(protectionRange, maxEverProtectionRange); + } + + /** + * @param maxEverProtectionRange the maxEverProtectionRange to set + */ + public void setMaxEverProtectionRange(int maxEverProtectionRange) { + this.maxEverProtectionRange = maxEverProtectionRange; + } + /** * @return true if the island is protected from the Purge, otherwise false */ @@ -533,6 +552,10 @@ public class Island implements DataObject { */ public void setProtectionRange(int protectionRange) { this.protectionRange = protectionRange; + // Ratchet up the maximum protection range + if (protectionRange > this.maxEverProtectionRange) { + this.maxEverProtectionRange = protectionRange; + } } /** @@ -660,14 +683,14 @@ public class Island implements DataObject { // Fixes #getLastPlayed() returning 0 when it is the owner's first connection. long lastPlayed = (plugin.getServer().getOfflinePlayer(owner).getLastPlayed() != 0) ? plugin.getServer().getOfflinePlayer(owner).getLastPlayed() : plugin.getServer().getOfflinePlayer(owner).getFirstPlayed(); - user.sendMessage("commands.admin.info.last-login","[date]", new Date(lastPlayed).toString()); + user.sendMessage("commands.admin.info.last-login","[date]", new Date(lastPlayed).toString()); - user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner))); - String resets = String.valueOf(plugin.getPlayers().getResets(world, owner)); - String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world)); - user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total); - // Show team members - showMembers(user); + user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner))); + String resets = String.valueOf(plugin.getPlayers().getResets(world, owner)); + String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world)); + user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total); + // Show team members + showMembers(user); } Vector location = center.toVector(); user.sendMessage("commands.admin.info.island-location", "[xyz]", Util.xyz(location)); @@ -675,6 +698,7 @@ public class Island implements DataObject { Vector to = center.toVector().add(new Vector(range-1, 0, range-1)).setY(center.getWorld().getMaxHeight()); user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(from), "[xz2]", Util.xyz(to)); user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(protectionRange)); + user.sendMessage("commands.admin.info.max-protection-range", "[range]", String.valueOf(maxEverProtectionRange)); Vector pfrom = center.toVector().subtract(new Vector(protectionRange, 0, protectionRange)).setY(0); Vector pto = center.toVector().add(new Vector(protectionRange-1, 0, protectionRange-1)).setY(center.getWorld().getMaxHeight()); user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(pfrom), "[xz2]", Util.xyz(pto)); diff --git a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java index 3a80910e1..78292f23e 100644 --- a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java @@ -553,6 +553,28 @@ public class YamlDatabaseHandler extends AbstractDatabaseHandler { return value; } + @Override + public boolean deleteID(String uniqueId) { + // The filename of the YAML file is the value of uniqueId field plus .yml. Sometimes the .yml is already appended. + if (!uniqueId.endsWith(YML)) { + uniqueId = uniqueId + YML; + } + // Get the database and table folders + File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME); + File tableFolder = new File(dataFolder, dataObject.getSimpleName()); + if (tableFolder.exists()) { + // Obtain the file and delete it + File file = new File(tableFolder, uniqueId); + try { + Files.delete(file.toPath()); + return true; + } catch (IOException e) { + plugin.logError("Could not delete yml database object! " + file.getName() + " - " + e.getMessage()); + } + } + return false; + } + /* (non-Javadoc) * @see world.bentobox.bentobox.database.AbstractDatabaseHandler#deleteObject(java.lang.Object) */ @@ -571,23 +593,8 @@ public class YamlDatabaseHandler extends AbstractDatabaseHandler { // Obtain the value of uniqueId within the instance (which must be a DataObject) PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject); Method method = propertyDescriptor.getReadMethod(); - String fileName = (String) method.invoke(instance); - // The filename of the YAML file is the value of uniqueId field plus .yml. Sometimes the .yml is already appended. - if (!fileName.endsWith(YML)) { - fileName = fileName + YML; - } - // Get the database and table folders - File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME); - File tableFolder = new File(dataFolder, dataObject.getSimpleName()); - if (tableFolder.exists()) { - // Obtain the file and delete it - File file = new File(tableFolder, fileName); - try { - Files.delete(file.toPath()); - } catch (IOException e) { - plugin.logError("Could not delete yml database object! " + file.getName() + " - " + e.getMessage()); - } - } + deleteID((String) method.invoke(instance)); + } @Override diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandDeleteManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandDeleteManager.java new file mode 100644 index 000000000..49d9ca58e --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/managers/IslandDeleteManager.java @@ -0,0 +1,84 @@ +package world.bentobox.bentobox.managers; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.events.BentoBoxReadyEvent; +import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteChunksEvent; +import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeletedEvent; +import world.bentobox.bentobox.database.Database; +import world.bentobox.bentobox.database.objects.DeletedIslandDO; +import world.bentobox.bentobox.util.DeleteIslandChunks; +import world.bentobox.bentobox.util.Util; + +/** + * Listens for island deletions and adds them to the database. Removes them when the island is deleted. + * @author tastybento + * + */ +public class IslandDeleteManager implements Listener { + + /** + * Queue of islands to delete + */ + private BentoBox plugin; + private Database handler; + private Set inDeletion; + + + public IslandDeleteManager(BentoBox plugin) { + this.plugin = plugin; + handler = new Database<>(plugin, DeletedIslandDO.class); + inDeletion = new HashSet<>(); + } + + /** + * When BentoBox is fully loaded, load the islands that still need to be deleted and kick them off + * @param e + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBentoBoxReady(BentoBoxReadyEvent e) { + // Load list of islands that were mid deletion and delete them + List toBeDeleted = handler.loadObjects(); + if (toBeDeleted != null && toBeDeleted.size() > 0) { + plugin.log("There are " + toBeDeleted.size() + " islands pending deletion."); + toBeDeleted.forEach(di -> { + plugin.log("Resuming deletion of island at " + di.getLocation().getWorld().getName() + " " + Util.xyz(di.getLocation().toVector())); + inDeletion.add(di.getLocation()); + new DeleteIslandChunks(plugin, di); + }); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onIslandDelete(IslandDeleteChunksEvent e) { + // Store location + inDeletion.add(e.getDeletedIslandInfo().getLocation()); + // Save to database + handler.saveObject(e.getDeletedIslandInfo()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onIslandDeleted(IslandDeletedEvent e) { + // Delete + inDeletion.remove(e.getDeletedIslandInfo().getLocation()); + // Delete from database + handler.deleteID(e.getDeletedIslandInfo().getUniqueId()); + } + + /** + * Check if an island location is in deletion + * @param location - center of location + * @return true if island is in the process of being deleted + */ + public boolean inDeletion(Location location) { + return inDeletion.contains(location); + } +} diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index d8530c71b..6e3675946 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -64,9 +64,6 @@ public class IslandsManager { // Island Cache private IslandCache islandCache; - // Async database saving semaphore - private boolean midSave; - /** * Islands Manager * @param plugin - plugin @@ -694,8 +691,7 @@ public class IslandsManager { * @param user - user */ public void removePlayer(World world, User user) { - islandCache.removePlayer(world, user.getUniqueId()); - save(true); + removePlayer(world, user.getUniqueId()); } /** @@ -704,8 +700,10 @@ public class IslandsManager { * @param uuid - user's uuid */ public void removePlayer(World world, UUID uuid) { - islandCache.removePlayer(world, uuid); - save(true); + Island island = islandCache.removePlayer(world, uuid); + if (island != null) { + handler.saveObject(island); + } } /** @@ -736,36 +734,18 @@ public class IslandsManager { } /** - * Save the islands to the database - * @param async - if true, saving will be done async + * Save the all the islands to the database */ - public void save(boolean async){ - if (midSave) { - // If it's already saving, then do nothing - return; - } + public void saveAll(){ Collection collection = islandCache.getIslands(); - if(async) { - Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { - midSave = true; - for(Island island : collection){ - try { - handler.saveObject(island); - } catch (Exception e) { - plugin.logError("Could not save island to database when running async! " + e.getMessage()); - } - } - midSave = false; - }); - } else { - for(Island island : collection){ - try { - handler.saveObject(island); - } catch (Exception e) { - plugin.logError("Could not save island to database when running sync! " + e.getMessage()); - } + for(Island island : collection){ + try { + handler.saveObject(island); + } catch (Exception e) { + plugin.logError("Could not save island to database when running sync! " + e.getMessage()); } } + } /** @@ -777,8 +757,8 @@ public class IslandsManager { // Add player to new island teamIsland.addMember(playerUUID); islandCache.addPlayer(playerUUID, teamIsland); - // Save the database - save(false); + // Save the island + handler.saveObject(teamIsland); } public void setLast(Location last) { @@ -798,7 +778,7 @@ public class IslandsManager { public void shutdown(){ // Remove all coop associations islandCache.getIslands().stream().forEach(i -> i.getMembers().values().removeIf(p -> p == RanksManager.COOP_RANK)); - save(false); + saveAll(); islandCache.clear(); handler.close(); } @@ -876,4 +856,13 @@ public class IslandsManager { islandCache.getIslands().stream().forEach(i -> i.getMembers().entrySet().removeIf(e -> e.getKey().equals(uniqueId) && e.getValue() == rank)); } + /** + * Save the island to the database + * @param island - island + */ + public void save(Island island) { + handler.saveObject(island); + + } + } diff --git a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java index 2a731a127..ddacfc833 100644 --- a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java @@ -1,6 +1,5 @@ package world.bentobox.bentobox.managers; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -8,7 +7,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; @@ -63,20 +61,13 @@ public class PlayersManager { /** * Save all players - * @param async - if true, save async */ - public void save(boolean async){ - Collection set = Collections.unmodifiableCollection(playerCache.values()); - if(async) { - Runnable save = () -> set.forEach(handler::saveObject); - Bukkit.getScheduler().runTaskAsynchronously(plugin, save); - } else { - set.forEach(handler::saveObject); - } + public void saveAll(){ + Collections.unmodifiableCollection(playerCache.values()).forEach(handler::saveObject); } public void shutdown(){ - save(false); + saveAll(); playerCache.clear(); handler.close(); } diff --git a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java index aa697d170..046e4dd63 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java @@ -169,8 +169,9 @@ public class IslandCache { * The island is removed from the islandsByUUID map, but kept in the location map. * @param world - world * @param uuid - player's UUID + * @return island player had or null if none */ - public void removePlayer(World world, UUID uuid) { + public Island removePlayer(World world, UUID uuid) { world = Util.getWorld(world); islandsByUUID.putIfAbsent(world, new HashMap<>()); Island island = islandsByUUID.get(world).get(uuid); @@ -185,6 +186,7 @@ public class IslandCache { } } islandsByUUID.get(world).remove(uuid); + return island; } /** 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 9b4281fd3..e26702ab9 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java @@ -244,7 +244,7 @@ public class NewIsland { */ private Result isIsland(Location location){ location = Util.getClosestIsland(location); - if (plugin.getIslands().getIslandAt(location).isPresent()) { + if (plugin.getIslands().getIslandAt(location).isPresent() || plugin.getIslandDeletionManager().inDeletion(location)) { return Result.ISLAND_FOUND; } diff --git a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java index 7aa610498..2a56105de 100644 --- a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java +++ b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java @@ -16,34 +16,43 @@ import world.bentobox.bentobox.database.objects.DeletedIslandDO; */ public class DeleteIslandChunks { + /** + * This is how many chunks per world will be done in one tick. + */ + private final static int SPEED = 5; private int x; private int z; private BukkitTask task; @SuppressWarnings("deprecation") public DeleteIslandChunks(BentoBox plugin, DeletedIslandDO di) { + // Fire event + IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETE_CHUNKS).build(); x = di.getMinXChunk(); z = di.getMinZChunk(); task = Bukkit.getScheduler().runTaskTimer(plugin, () -> { - di.getWorld().regenerateChunk(x, z); - //System.out.println("regenerating = " + x + "," + z); - if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) { - plugin.getIWM().getNetherWorld(di.getWorld()).regenerateChunk(x, z); + for (int i = 0; i < SPEED; i++) { + di.getWorld().regenerateChunk(x, z); + if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) { + plugin.getIWM().getNetherWorld(di.getWorld()).regenerateChunk(x, z); - } - if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) { - plugin.getIWM().getEndWorld(di.getWorld()).regenerateChunk(x, z); - } - z++; - if (z > di.getMaxZChunk()) { - z = di.getMinZChunk(); - x++; - if (x > di.getMaxXChunk()) { - task.cancel(); - // Fire event - IslandEvent.builder().location(Util.getLocationString(di.getUniqueId())).reason(Reason.DELETED).build(); + } + if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) { + plugin.getIWM().getEndWorld(di.getWorld()).regenerateChunk(x, z); + } + z++; + if (z > di.getMaxZChunk()) { + z = di.getMinZChunk(); + x++; + if (x > di.getMaxXChunk()) { + task.cancel(); + // Fire event + IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build(); + } } } }, 0L, 1L); - }} \ No newline at end of file + } + +} \ No newline at end of file diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 35bf52dbd..1823bb7a5 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -109,6 +109,7 @@ commands: registered-island: "&aRegistered player to island at [xyz]." already-owned: "&cIsland is already owned by another player!" no-island-here: "&cThere is no island here. Confirm to make one." + in-deletion: "&cThis island space is currently being deleted. Try later." unregister: parameters: "" description: "unregister owner from island, but keep island blocks" @@ -128,6 +129,7 @@ commands: island-location: "Island location: [xyz]" island-coords: "Island coordinates: [xz1] to [xz2]" protection-range: "Protection range: [range]" + max-protection-range: "Largest historical protection range: [range]" protection-coords: "Protection coordinates: [xz1] to [xz2]" is-spawn: "Island is a spawn island" banned-players: "Banned players:" diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommandTest.java index 95e8e5cc8..131b47908 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommandTest.java @@ -30,6 +30,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandDeleteManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.LocalesManager; @@ -50,6 +51,7 @@ public class AdminRegisterCommandTest { private IslandsManager im; private PlayersManager pm; private UUID notUUID; + private IslandDeleteManager idm; /** * @throws java.lang.Exception @@ -111,11 +113,16 @@ public class AdminRegisterCommandTest { LocalesManager lm = mock(LocalesManager.class); when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation"); when(plugin.getLocalesManager()).thenReturn(lm); + + // Deletion Manager + idm = mock(IslandDeleteManager.class); + when(idm.inDeletion(Mockito.any())).thenReturn(false); + when(plugin.getIslandDeletionManager()).thenReturn(idm); } /** - * Test method for . + * Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}. */ @Test public void testExecuteNoTarget() { @@ -125,7 +132,7 @@ public class AdminRegisterCommandTest { } /** - * Test method for . + * Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}. */ @Test public void testExecuteUnknownPlayer() { @@ -137,7 +144,7 @@ public class AdminRegisterCommandTest { } /** - * Test method for . + * Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}. */ @Test public void testExecutePlayerHasIsland() { @@ -151,7 +158,7 @@ public class AdminRegisterCommandTest { } /** - * Test method for . + * Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}. */ @Test public void testExecuteInTeam() { @@ -165,7 +172,7 @@ public class AdminRegisterCommandTest { } /** - * Test method for . + * Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}. */ @Test public void testExecuteAlreadyOwnedIsland() { @@ -186,6 +193,32 @@ public class AdminRegisterCommandTest { Mockito.verify(user).sendMessage("commands.admin.register.already-owned"); } + /** + * Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}. + */ + @Test + public void testExecuteInDeletionIsland() { + when(idm.inDeletion(Mockito.any())).thenReturn(true); + when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false); + when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + String[] name = {"tastybento"}; + when(pm.getUUID(Mockito.any())).thenReturn(notUUID); + Location loc = mock(Location.class); + + // Island has owner + Island is = mock(Island.class); + when(is.getOwner()).thenReturn(uuid); + Optional opi = Optional.of(is); + when(im.getIslandAt(Mockito.any())).thenReturn(opi); + when(user.getLocation()).thenReturn(loc); + AdminRegisterCommand itl = new AdminRegisterCommand(ac); + assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + Mockito.verify(user).sendMessage("commands.admin.register.in-deletion"); + } + + /** + * Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}. + */ @Test public void testExecuteSuccess() { when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false); diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index e04e2575b..fc0b020c3 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -23,6 +23,7 @@ import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Server; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -37,6 +38,7 @@ import org.bukkit.entity.Player; import org.bukkit.entity.Slime; import org.bukkit.entity.Wither; import org.bukkit.entity.Zombie; +import org.bukkit.plugin.PluginManager; import org.bukkit.scheduler.BukkitScheduler; import org.junit.Before; import org.junit.Test; @@ -54,6 +56,7 @@ import com.google.common.collect.ImmutableSet.Builder; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.api.configuration.WorldSettings; +import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteEvent; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; @@ -79,6 +82,7 @@ public class IslandsManagerTest { private IslandCache islandCache; private Optional optionalIsland; private Island is; + private PluginManager pim; /** * @throws java.lang.Exception @@ -122,8 +126,7 @@ public class IslandsManagerTest { pm = mock(PlayersManager.class); when(plugin.getPlayers()).thenReturn(pm); - // Server & Scheduler - + // Scheduler BukkitScheduler sch = mock(BukkitScheduler.class); PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getScheduler()).thenReturn(sch); @@ -174,6 +177,11 @@ public class IslandsManagerTest { // User location when(user.getLocation()).thenReturn(location); + // Server for events + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + pim = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pim); } @@ -432,16 +440,37 @@ public class IslandsManagerTest { * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#deleteIsland(world.bentobox.bentobox.database.objects.Island, boolean)}. */ @Test - public void testDeleteIslandIslandBoolean() { + public void testDeleteIslandIslandBooleanNull() { IslandsManager im = new IslandsManager(plugin); - im.deleteIsland((Island)null, true); + Mockito.verify(pim, Mockito.never()).callEvent(Mockito.any()); + } + + /** + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#deleteIsland(world.bentobox.bentobox.database.objects.Island, boolean)}. + */ + @Test + public void testDeleteIslandIslandBooleanNoBlockRemoval() { + IslandsManager im = new IslandsManager(plugin); UUID owner = UUID.randomUUID(); Island island = im.createIsland(location, owner); im.deleteIsland(island, false); - island = im.createIsland(location, owner); + assertNull(island.getOwner()); + Mockito.verify(pim, Mockito.times(2)).callEvent(Mockito.any(IslandDeleteEvent.class)); + } + + /** + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#deleteIsland(world.bentobox.bentobox.database.objects.Island, boolean)}. + */ + @Test + public void testDeleteIslandIslandBooleanRemoveBlocks() { + Mockito.verify(pim, Mockito.never()).callEvent(Mockito.any()); + IslandsManager im = new IslandsManager(plugin); + UUID owner = UUID.randomUUID(); + Island island = im.createIsland(location, owner); im.deleteIsland(island, true); - assertNull(island); + assertNull(island.getOwner()); + Mockito.verify(pim, Mockito.times(4)).callEvent(Mockito.any(IslandDeleteEvent.class)); } /** diff --git a/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java index b42d47e79..e73adcd43 100644 --- a/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java @@ -175,13 +175,12 @@ public class PlayersManagerTest { } /** - * Test method for {@link world.bentobox.bentobox.managers.PlayersManager#save(boolean)}. + * Test method for {@link world.bentobox.bentobox.managers.PlayersManager#saveAll()}. */ @Test public void testSaveBoolean() { PlayersManager pm = new PlayersManager(plugin); - pm.save(false); - pm.save(true); + pm.saveAll(); } /** diff --git a/src/test/java/world/bentobox/bentobox/util/DeleteIslandChunksTest.java b/src/test/java/world/bentobox/bentobox/util/DeleteIslandChunksTest.java deleted file mode 100644 index c914f493f..000000000 --- a/src/test/java/world/bentobox/bentobox/util/DeleteIslandChunksTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/** - * - */ -package world.bentobox.bentobox.util; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.plugin.PluginManager; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.database.objects.Island; -import world.bentobox.bentobox.managers.IslandWorldManager; - -/** - * Tests the island delete class - * @author tastybento - * - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class}) -public class DeleteIslandChunksTest { - - private BentoBox plugin; - private Island island; - private Location location; - private World world; - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - PowerMockito.mockStatic(Bukkit.class); - Server server = mock(Server.class); - PluginManager pim = mock(PluginManager.class); - when(server.getPluginManager()).thenReturn(pim); - when(Bukkit.getServer()).thenReturn(server); - plugin = mock(BentoBox.class); - IslandWorldManager iwm = mock(IslandWorldManager.class); - // No Nether or End by default - when(iwm.isNetherGenerate(Mockito.any())).thenReturn(false); - when(iwm.isNetherIslands(Mockito.any())).thenReturn(false); - when(iwm.isEndGenerate(Mockito.any())).thenReturn(false); - when(iwm.isEndIslands(Mockito.any())).thenReturn(false); - - when(plugin.getIWM()).thenReturn(iwm); - // World - //world = mock(World.class, Mockito.withSettings().verboseLogging()); - world = mock(World.class); - - island = new Island(); - island.setRange(64); - - location = mock(Location.class); - - when(location.getWorld()).thenReturn(world); - - } - - /** - * Test method for {@link world.bentobox.bentobox.util.DeleteIslandChunks#DeleteIslandChunks(world.bentobox.bentobox.BentoBox, world.bentobox.bentobox.database.objects.Island)}. - */ - @SuppressWarnings("deprecation") - @Test - public void testDeleteIslandChunksNegativeX() { - - // Island adjacent to an island at 0,0 - Location location2 = mock(Location.class); - - when(location2.getWorld()).thenReturn(world); - when(location2.getBlockX()).thenReturn(-128); - when(location2.getBlockY()).thenReturn(120); - when(location2.getBlockZ()).thenReturn(0); - island.setCenter(location2); - - new DeleteIslandChunks(plugin, island); - Mockito.verify(world, Mockito.times(64)).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); - } - - /** - * Test method for {@link world.bentobox.bentobox.util.DeleteIslandChunks#DeleteIslandChunks(world.bentobox.bentobox.BentoBox, world.bentobox.bentobox.database.objects.Island)}. - */ - @SuppressWarnings("deprecation") - @Test - public void testDeleteIslandChunksNegativeXX() { - - // Island adjacent to an island at 0,0 - Location location2 = mock(Location.class); - - when(location2.getWorld()).thenReturn(world); - when(location2.getBlockX()).thenReturn(-256); - when(location2.getBlockY()).thenReturn(120); - when(location2.getBlockZ()).thenReturn(0); - island.setCenter(location2); - - new DeleteIslandChunks(plugin, island); - Mockito.verify(world, Mockito.times(64)).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); - } - - /** - * Test method for {@link world.bentobox.bentobox.util.DeleteIslandChunks#DeleteIslandChunks(world.bentobox.bentobox.BentoBox, world.bentobox.bentobox.database.objects.Island)}. - */ - @SuppressWarnings("deprecation") - @Test - public void testDeleteIslandChunksIslandPositiveX() { - - // Island adjacent to an island at 0,0 - Location location2 = mock(Location.class); - - when(location2.getWorld()).thenReturn(world); - when(location2.getBlockX()).thenReturn(0); - when(location2.getBlockY()).thenReturn(120); - when(location2.getBlockZ()).thenReturn(0); - island.setCenter(location2); - - new DeleteIslandChunks(plugin, island); - Mockito.verify(world, Mockito.times(64)).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); - } - - /** - * Test method for {@link world.bentobox.bentobox.util.DeleteIslandChunks#DeleteIslandChunks(world.bentobox.bentobox.BentoBox, world.bentobox.bentobox.database.objects.Island)}. - */ - @SuppressWarnings("deprecation") - @Test - public void testDeleteIslandChunksPositiveXX() { - - // Island adjacent to an island at 0,0 - Location location2 = mock(Location.class); - - when(location2.getWorld()).thenReturn(world); - when(location2.getBlockX()).thenReturn(256); - when(location2.getBlockY()).thenReturn(120); - when(location2.getBlockZ()).thenReturn(0); - island.setCenter(location2); - - new DeleteIslandChunks(plugin, island); - Mockito.verify(world, Mockito.times(64)).regenerateChunk(Mockito.anyInt(), Mockito.anyInt()); - } - -} From 7854187448dff411d60b3e85124632f7d832c7ac Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 12 Jan 2019 18:18:22 -0800 Subject: [PATCH 4/8] Added async saving to YAML and MYSQL Both seem to work fine, but probably need more real-world testing. --- .../database/mysql/MySQLDatabaseHandler.java | 17 +++++++++--- .../database/yaml/YamlDatabaseConnector.java | 27 ++++++++----------- .../database/yaml/YamlDatabaseHandler.java | 22 +++++++++------ 3 files changed, 38 insertions(+), 28 deletions(-) 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 3ec2d216c..f332405b1 100644 --- a/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java @@ -9,6 +9,8 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.List; +import org.bukkit.Bukkit; + import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; @@ -128,11 +130,18 @@ public class MySQLDatabaseHandler extends AbstractJSONDatabaseHandler { "`" + dataObject.getCanonicalName() + "` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?"; - // Replace into is used so that any data in the table will be replaced with updated data - // The table name is the canonical name, so that add-ons can be sure of a unique table in the database + + Gson gson = getGson(); + String toStore = gson.toJson(instance); + if (plugin.isEnabled()) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> store(instance, toStore, sb)); + } else { + store(instance, toStore, sb); + } + } + + private void store(T instance, String toStore, String sb) { try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) { - Gson gson = getGson(); - String toStore = gson.toJson(instance); preparedStatement.setString(1, toStore); preparedStatement.setString(2, toStore); preparedStatement.execute(); diff --git a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseConnector.java b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseConnector.java index 57c83e333..4e0c7f407 100644 --- a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseConnector.java +++ b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseConnector.java @@ -6,8 +6,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.sql.Connection; import java.util.ArrayList; import java.util.List; @@ -18,6 +19,8 @@ import java.util.UUID; import org.bukkit.configuration.file.YamlConfiguration; +import com.google.common.base.Charsets; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.database.DatabaseConnector; @@ -78,29 +81,21 @@ public class YamlDatabaseConnector implements DatabaseConnector { return config; } - public void saveYamlFile(YamlConfiguration yamlConfig, String tableName, String fileName, Map commentMap) { - if (!fileName.endsWith(YML)) { - fileName = fileName + YML; - } + public void saveYamlFile(String data, String tableName, String fileName, Map commentMap) { + String name = fileName.endsWith(YML) ? fileName : fileName + YML; File tableFolder = new File(plugin.getDataFolder(), tableName); - File file = new File(tableFolder, fileName); + File file = new File(tableFolder, name); if (!tableFolder.exists()) { tableFolder.mkdirs(); } - try { - File tmpFile = new File(tableFolder, fileName + ".bak"); - if (file.exists()) { - // Make a backup of file - Files.copy(file.toPath(), tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - yamlConfig.save(file.toPath().toString()); - Files.deleteIfExists(tmpFile.toPath()); - } catch (Exception e) { + try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) { + writer.write(data); + } catch (IOException e) { plugin.logError("Could not save yml file: " + tableName + " " + fileName + " " + e.getMessage()); return; } if (commentMap != null && !commentMap.isEmpty()) { - commentFile(new File(tableFolder, fileName), commentMap); + commentFile(new File(tableFolder, name), commentMap); } } diff --git a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java index 78292f23e..4e94eb09e 100644 --- a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java @@ -22,6 +22,7 @@ import java.util.Objects; import java.util.Set; import java.util.UUID; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.configuration.MemorySection; @@ -307,18 +308,14 @@ public class YamlDatabaseHandler extends AbstractDatabaseHandler { // This is the Yaml Configuration that will be used and saved at the end YamlConfiguration config = new YamlConfiguration(); - // The file name of the Yaml file. - String filename = ""; - String path = DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName(); // Comments for the file Map yamlComments = new HashMap<>(); // Only allow storing in an arbitrary place if it is a config object. Otherwise it is in the database StoreAt storeAt = instance.getClass().getAnnotation(StoreAt.class); - if (storeAt != null) { - path = storeAt.path(); - filename = storeAt.filename(); - } + String path = storeAt == null ? DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName() : storeAt.path(); + String filename = storeAt == null ? "" : storeAt.filename(); + // See if there are any top-level comments // See if there are multiple comments ConfigComment.Line comments = instance.getClass().getAnnotation(ConfigComment.Line.class); @@ -443,7 +440,16 @@ public class YamlDatabaseHandler extends AbstractDatabaseHandler { throw new IllegalArgumentException("No uniqueId in class"); } - ((YamlDatabaseConnector)databaseConnector).saveYamlFile(config, path, filename, yamlComments); + // Save + String name = filename; + String data = config.saveToString(); + if (plugin.isEnabled()) { + // Async + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> ((YamlDatabaseConnector)databaseConnector).saveYamlFile(data, path, name, yamlComments)); + } else { + // Sync for shutdown + ((YamlDatabaseConnector)databaseConnector).saveYamlFile(data, path, name, yamlComments); + } } private void setComment(ConfigComment comment, YamlConfiguration config, Map yamlComments, String parent) { From 0f253c35ef2253ab48cd7b5c832e9787a2bf3e26 Mon Sep 17 00:00:00 2001 From: Florian CUNY Date: Sun, 13 Jan 2019 10:09:35 +0100 Subject: [PATCH 5/8] Renamed IslandDeleteManager to IslandDeletionManager --- src/main/java/world/bentobox/bentobox/BentoBox.java | 8 ++++---- ...landDeleteManager.java => IslandDeletionManager.java} | 9 ++++----- .../api/commands/admin/AdminRegisterCommandTest.java | 6 +++--- 3 files changed, 11 insertions(+), 12 deletions(-) rename src/main/java/world/bentobox/bentobox/managers/{IslandDeleteManager.java => IslandDeletionManager.java} (96%) diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index eda37b7cb..cfdcb8f8e 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -25,7 +25,7 @@ import world.bentobox.bentobox.managers.AddonsManager; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.FlagsManager; import world.bentobox.bentobox.managers.HooksManager; -import world.bentobox.bentobox.managers.IslandDeleteManager; +import world.bentobox.bentobox.managers.IslandDeletionManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.LocalesManager; @@ -68,7 +68,7 @@ public class BentoBox extends JavaPlugin { private boolean isLoaded; - private IslandDeleteManager islandDeletionManager; + private IslandDeletionManager islandDeletionManager; @Override public void onEnable(){ @@ -202,7 +202,7 @@ public class BentoBox extends JavaPlugin { // Death counter manager.registerEvents(new DeathListener(this), this); // Island Delete Manager - islandDeletionManager = new IslandDeleteManager(this); + islandDeletionManager = new IslandDeletionManager(this); manager.registerEvents(islandDeletionManager, this); } @@ -368,7 +368,7 @@ public class BentoBox extends JavaPlugin { /** * @return the islandDeletionManager */ - public IslandDeleteManager getIslandDeletionManager() { + public IslandDeletionManager getIslandDeletionManager() { return islandDeletionManager; } } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandDeleteManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java similarity index 96% rename from src/main/java/world/bentobox/bentobox/managers/IslandDeleteManager.java rename to src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java index 49d9ca58e..62eaba622 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandDeleteManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java @@ -21,19 +21,18 @@ import world.bentobox.bentobox.util.Util; /** * Listens for island deletions and adds them to the database. Removes them when the island is deleted. * @author tastybento - * + * @since 1.1 */ -public class IslandDeleteManager implements Listener { +public class IslandDeletionManager implements Listener { + private BentoBox plugin; /** * Queue of islands to delete */ - private BentoBox plugin; private Database handler; private Set inDeletion; - - public IslandDeleteManager(BentoBox plugin) { + public IslandDeletionManager(BentoBox plugin) { this.plugin = plugin; handler = new Database<>(plugin, DeletedIslandDO.class); inDeletion = new HashSet<>(); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommandTest.java index 131b47908..794e08660 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminRegisterCommandTest.java @@ -30,7 +30,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.CommandsManager; -import world.bentobox.bentobox.managers.IslandDeleteManager; +import world.bentobox.bentobox.managers.IslandDeletionManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.LocalesManager; @@ -51,7 +51,7 @@ public class AdminRegisterCommandTest { private IslandsManager im; private PlayersManager pm; private UUID notUUID; - private IslandDeleteManager idm; + private IslandDeletionManager idm; /** * @throws java.lang.Exception @@ -115,7 +115,7 @@ public class AdminRegisterCommandTest { when(plugin.getLocalesManager()).thenReturn(lm); // Deletion Manager - idm = mock(IslandDeleteManager.class); + idm = mock(IslandDeletionManager.class); when(idm.inDeletion(Mockito.any())).thenReturn(false); when(plugin.getIslandDeletionManager()).thenReturn(idm); } From dc7d5b40f2b77ef67d2451fee9ac079fc6ab0504 Mon Sep 17 00:00:00 2001 From: Florian CUNY Date: Sun, 13 Jan 2019 10:21:06 +0100 Subject: [PATCH 6/8] Renamed DeletedIslandDO to IslandDeletion --- .../api/events/island/IslandEvent.java | 18 +++++++++--------- ...eletedIslandDO.java => IslandDeletion.java} | 17 ++++++++--------- .../managers/IslandDeletionManager.java | 8 ++++---- .../bentobox/managers/IslandsManager.java | 4 ++-- .../bentobox/util/DeleteIslandChunks.java | 4 ++-- 5 files changed, 25 insertions(+), 26 deletions(-) rename src/main/java/world/bentobox/bentobox/database/objects/{DeletedIslandDO.java => IslandDeletion.java} (90%) diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java index 70152b0b1..89448eb1c 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java @@ -6,7 +6,7 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import world.bentobox.bentobox.api.events.IslandBaseEvent; -import world.bentobox.bentobox.database.objects.DeletedIslandDO; +import world.bentobox.bentobox.database.objects.IslandDeletion; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; @@ -180,15 +180,15 @@ public class IslandEvent extends IslandBaseEvent { * */ public static class IslandDeleteChunksEvent extends IslandBaseEvent { - private final DeletedIslandDO deletedIslandInfo; + private final IslandDeletion deletedIslandInfo; - private IslandDeleteChunksEvent(Island island, UUID player, boolean admin, Location location, DeletedIslandDO deletedIsland) { + private IslandDeleteChunksEvent(Island island, UUID player, boolean admin, Location location, IslandDeletion deletedIsland) { // Final variables have to be declared in the constructor super(island, player, admin, location); this.deletedIslandInfo = deletedIsland; } - public DeletedIslandDO getDeletedIslandInfo() { + public IslandDeletion getDeletedIslandInfo() { return deletedIslandInfo; } } @@ -197,15 +197,15 @@ public class IslandEvent extends IslandBaseEvent { * */ public static class IslandDeletedEvent extends IslandBaseEvent { - private final DeletedIslandDO deletedIslandInfo; + private final IslandDeletion deletedIslandInfo; - private IslandDeletedEvent(Island island, UUID player, boolean admin, Location location, DeletedIslandDO deletedIsland) { + private IslandDeletedEvent(Island island, UUID player, boolean admin, Location location, IslandDeletion deletedIsland) { // Final variables have to be declared in the constructor super(island, player, admin, location); this.deletedIslandInfo = deletedIsland; } - public DeletedIslandDO getDeletedIslandInfo() { + public IslandDeletion getDeletedIslandInfo() { return deletedIslandInfo; } } @@ -287,7 +287,7 @@ public class IslandEvent extends IslandBaseEvent { private Reason reason = Reason.UNKNOWN; private boolean admin; private Location location; - private DeletedIslandDO deletedIslandInfo; + private IslandDeletion deletedIslandInfo; public IslandEventBuilder island(Island island) { this.island = island; @@ -327,7 +327,7 @@ public class IslandEvent extends IslandBaseEvent { return this; } - public IslandEventBuilder deletedIslandInfo(DeletedIslandDO deletedIslandInfo) { + public IslandEventBuilder deletedIslandInfo(IslandDeletion deletedIslandInfo) { this.deletedIslandInfo = deletedIslandInfo; return this; } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java b/src/main/java/world/bentobox/bentobox/database/objects/IslandDeletion.java similarity index 90% rename from src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java rename to src/main/java/world/bentobox/bentobox/database/objects/IslandDeletion.java index 57bdd621d..2f3a59aba 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/DeletedIslandDO.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/IslandDeletion.java @@ -9,9 +9,10 @@ import com.google.gson.annotations.Expose; /** * Data object to store islands in deletion - * + * @author tastybento + * @since 1.1 */ -public class DeletedIslandDO implements DataObject { +public class IslandDeletion implements DataObject { @Expose private String uniqueId = ""; @@ -31,9 +32,9 @@ public class DeletedIslandDO implements DataObject { @Expose private int maxZChunk; - public DeletedIslandDO() {} + public IslandDeletion() {} - public DeletedIslandDO(Island island) { + public IslandDeletion(Island island) { uniqueId = UUID.randomUUID().toString(); location = island.getCenter(); minXChunk = (location.getBlockX() - island.getMaxEverProtectionRange()) >> 4; @@ -42,7 +43,7 @@ public class DeletedIslandDO implements DataObject { maxZChunk = (island.getMaxEverProtectionRange() + location.getBlockZ() - 1) >> 4; } - public DeletedIslandDO(Location location, int minXChunk, int maxXChunk, int minZChunk, int maxZChunk) { + public IslandDeletion(Location location, int minXChunk, int maxXChunk, int minZChunk, int maxZChunk) { this.uniqueId = UUID.randomUUID().toString(); this.location = location; this.minXChunk = minXChunk; @@ -62,10 +63,10 @@ public class DeletedIslandDO implements DataObject { if (obj == null) { return false; } - if (!(obj instanceof DeletedIslandDO)) { + if (!(obj instanceof IslandDeletion)) { return false; } - DeletedIslandDO other = (DeletedIslandDO) obj; + IslandDeletion other = (IslandDeletion) obj; if (uniqueId == null) { if (other.uniqueId != null) { return false; @@ -173,7 +174,5 @@ public class DeletedIslandDO implements DataObject { public void setUniqueId(String uniqueId) { this.uniqueId = uniqueId; } - - } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java index 62eaba622..5a1708dd8 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java @@ -14,7 +14,7 @@ import world.bentobox.bentobox.api.events.BentoBoxReadyEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteChunksEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeletedEvent; import world.bentobox.bentobox.database.Database; -import world.bentobox.bentobox.database.objects.DeletedIslandDO; +import world.bentobox.bentobox.database.objects.IslandDeletion; import world.bentobox.bentobox.util.DeleteIslandChunks; import world.bentobox.bentobox.util.Util; @@ -29,12 +29,12 @@ public class IslandDeletionManager implements Listener { /** * Queue of islands to delete */ - private Database handler; + private Database handler; private Set inDeletion; public IslandDeletionManager(BentoBox plugin) { this.plugin = plugin; - handler = new Database<>(plugin, DeletedIslandDO.class); + handler = new Database<>(plugin, IslandDeletion.class); inDeletion = new HashSet<>(); } @@ -45,7 +45,7 @@ public class IslandDeletionManager implements Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBentoBoxReady(BentoBoxReadyEvent e) { // Load list of islands that were mid deletion and delete them - List toBeDeleted = handler.loadObjects(); + List toBeDeleted = handler.loadObjects(); if (toBeDeleted != null && toBeDeleted.size() > 0) { plugin.log("There are " + toBeDeleted.size() + " islands pending deletion."); toBeDeleted.forEach(di -> { diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 6e3675946..a5153d206 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -30,7 +30,7 @@ import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.Database; -import world.bentobox.bentobox.database.objects.DeletedIslandDO; +import world.bentobox.bentobox.database.objects.IslandDeletion; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.island.IslandCache; @@ -261,7 +261,7 @@ public class IslandsManager { // Remove players from island removePlayersFromIsland(island); // Remove blocks from world - new DeleteIslandChunks(plugin, new DeletedIslandDO(island)); + new DeleteIslandChunks(plugin, new IslandDeletion(island)); } } diff --git a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java index 2a56105de..41f3d5612 100644 --- a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java +++ b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java @@ -6,7 +6,7 @@ import org.bukkit.scheduler.BukkitTask; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; -import world.bentobox.bentobox.database.objects.DeletedIslandDO; +import world.bentobox.bentobox.database.objects.IslandDeletion; /** * Deletes islands fast using chunk regeneration @@ -25,7 +25,7 @@ public class DeleteIslandChunks { private BukkitTask task; @SuppressWarnings("deprecation") - public DeleteIslandChunks(BentoBox plugin, DeletedIslandDO di) { + public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) { // Fire event IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETE_CHUNKS).build(); x = di.getMinXChunk(); From 9cf63611e426d117a0e0221bb1b21594c43ca8b8 Mon Sep 17 00:00:00 2001 From: Florian CUNY Date: Sun, 13 Jan 2019 10:27:36 +0100 Subject: [PATCH 7/8] Added some NonNull/Nullable in database code --- .../bentobox/database/AbstractDatabaseHandler.java | 10 ++++++++-- .../world/bentobox/bentobox/database/Database.java | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/database/AbstractDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/AbstractDatabaseHandler.java index 95772b0ad..e94e867e9 100644 --- a/src/main/java/world/bentobox/bentobox/database/AbstractDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/AbstractDatabaseHandler.java @@ -4,6 +4,8 @@ import java.beans.IntrospectionException; import java.lang.reflect.InvocationTargetException; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.Addon; @@ -38,12 +40,14 @@ public abstract class AbstractDatabaseHandler { /** * The addon that is accessing the database, if any. */ + @Nullable private Addon addon; /** * Get the addon that is accessing the database, if any. May be null. * @return the addon */ + @Nullable public Addon getAddon() { return addon; } @@ -52,7 +56,7 @@ public abstract class AbstractDatabaseHandler { * Set the addon that is accessing the database, if any. * @param addon the addon to set */ - public void setAddon(Addon addon) { + public void setAddon(@Nullable Addon addon) { this.addon = addon; } @@ -84,7 +88,8 @@ public abstract class AbstractDatabaseHandler { * @param uniqueId - unique ID * @return */ - public abstract T loadObject(String uniqueId) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException; + @Nullable + public abstract T loadObject(@NonNull String uniqueId) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException; /** * Save T into the corresponding database @@ -115,6 +120,7 @@ public abstract class AbstractDatabaseHandler { * Attempts to delete the object with the uniqueId * @param uniqueId - uniqueId of object * @return true if successful, false if there is no such uniqueId + * @since 1.1 */ public abstract boolean deleteID(String uniqueId); } diff --git a/src/main/java/world/bentobox/bentobox/database/Database.java b/src/main/java/world/bentobox/bentobox/database/Database.java index 21d1739e0..343848edf 100644 --- a/src/main/java/world/bentobox/bentobox/database/Database.java +++ b/src/main/java/world/bentobox/bentobox/database/Database.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; +import org.eclipse.jdt.annotation.NonNull; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.Addon; @@ -44,6 +45,7 @@ public class Database { * Load all the config objects and supply them as a list * @return list of config objects or an empty list if they cannot be loaded */ + @NonNull public List loadObjects() { List result = new ArrayList<>(); try { From 6f4d3aeaed06a1f16c9bcbba2e80218ec80b44a2 Mon Sep 17 00:00:00 2001 From: Florian CUNY Date: Sun, 13 Jan 2019 10:28:49 +0100 Subject: [PATCH 8/8] Added some more javadoc --- src/main/java/world/bentobox/bentobox/database/Database.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/world/bentobox/bentobox/database/Database.java b/src/main/java/world/bentobox/bentobox/database/Database.java index 343848edf..aadd5b5e9 100644 --- a/src/main/java/world/bentobox/bentobox/database/Database.java +++ b/src/main/java/world/bentobox/bentobox/database/Database.java @@ -106,6 +106,7 @@ public class Database { * Attempts to delete the object with the uniqueId * @param uniqueId - uniqueId of object * @return true if successful, false if there is no such uniqueId + * @since 1.1 */ public boolean deleteID(String uniqueId) { return handler.deleteID(uniqueId);