diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeCommand.java new file mode 100644 index 000000000..16ecc6853 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeCommand.java @@ -0,0 +1,165 @@ +package world.bentobox.bentobox.api.commands.admin.purge; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeletedEvent; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; + +public class AdminPurgeCommand extends CompositeCommand implements Listener { + + private int count; + private boolean inPurge; + private boolean toBeConfirmed; + private Iterator it; + private User user; + private Set islands = new HashSet<>(); + + public AdminPurgeCommand(CompositeCommand parent) { + super(parent, "purge"); + getAddon().registerListener(this); + } + + @Override + public void setup() { + setPermission("admin.purge"); + setOnlyPlayer(false); + setParametersHelp("commands.admin.purge.parameters"); + setDescription("commands.admin.purge.description"); + new AdminPurgeStopCommand(this); + new AdminPurgeUnownedCommand(this); + } + + @Override + public boolean execute(User user, String label, List args) { + if (inPurge) { + user.sendMessage("commands.admin.purge.purge-in-progress"); + return false; + } + if (args.isEmpty()) { + // Show help + showHelp(this, user); + return false; + } + if (args.get(0).equalsIgnoreCase("confirm") && toBeConfirmed && this.user.equals(user)) { + removeIslands(); + return true; + } + // Clear tbc + toBeConfirmed = false; + islands.clear(); + this.user = user; + try { + Integer days = Integer.parseInt(args.get(0)); + if (days < 1) { + user.sendMessage("commands.admin.purge.days-one-or-more"); + return false; + } + islands = getOldIslands(days); + user.sendMessage("commands.admin.purge.purgable-islands", TextVariables.NUMBER, String.valueOf(islands.size())); + if (!islands.isEmpty()) { + toBeConfirmed = true; + user.sendMessage("commands.admin.purge.confirm", TextVariables.LABEL, this.getTopLabel()); + return false; + } + } catch(Exception e) { + user.sendMessage("commands.admin.purge.number-error"); + return false; + } + return true; + } + + void removeIslands() { + inPurge = true; + user.sendMessage("commands.admin.purge.see-console-for-status"); + it = islands.iterator(); + count = 0; + // Delete first island + deleteIsland(); + } + + private void deleteIsland() { + if (it.hasNext()) { + getIslands().getIslandById(it.next()).ifPresent(i -> { + getIslands().deleteIsland(i, true, null); + count++; + getPlugin().log(count + " islands purged"); + }); + } else { + user.sendMessage("commands.admin.purge.completed"); + inPurge = false; + } + + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + void onIslandDeleted(IslandDeletedEvent e) { + if (inPurge && it.hasNext()) { + getIslands().getIslandById(it.next()).ifPresent(i -> { + getIslands().deleteIsland(i, true, null); + count++; + getPlugin().log(count + " islands purged"); + }); + } else { + user.sendMessage("commands.admin.purge.completed"); + inPurge = false; + } + } + + Set getUnownedIslands() { + return getPlugin().getIslands().getIslands().stream() + .filter(i -> i.getWorld().equals(this.getWorld())) + .filter(i -> i.getOwner() == null) + .map(i -> i.getUniqueId()) + .collect(Collectors.toSet()); + + } + + Set getOldIslands(int days) { + return getPlugin().getIslands().getIslands().stream() + .filter(i -> i.getWorld().equals(this.getWorld())) + .filter(i -> i.getOwner() != null) + .filter(i -> i.getMembers().size() == 1) + .filter(i -> (System.currentTimeMillis() - Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed()) > days * 1000 * 24 * 3600) + .map(i -> i.getUniqueId()) + .collect(Collectors.toSet()); + } + + /** + * @return the inPurge + */ + boolean isInPurge() { + return inPurge; + } + + /** + * Stop the purge + */ + void stop() { + inPurge = false; + } + + /** + * @param user the user to set + */ + public void setUser(User user) { + this.user = user; + } + + /** + * @param islands the islands to set + */ + public void setIslands(Set islands) { + this.islands = islands; + } +} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java new file mode 100644 index 000000000..e8da54a9e --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java @@ -0,0 +1,38 @@ +package world.bentobox.bentobox.api.commands.admin.purge; + +import java.util.List; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; + +public class AdminPurgeStopCommand extends CompositeCommand { + + public AdminPurgeStopCommand(CompositeCommand parent) { + super(parent, "stop", "cancel"); + } + + @Override + public void setup() { + setPermission("admin.purge"); + setOnlyPlayer(false); + setDescription("commands.admin.purge.stop.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (!args.isEmpty()) { + // Show help + showHelp(this, user); + return false; + } + AdminPurgeCommand parentCommand = ((AdminPurgeCommand)getParent()); + if (parentCommand.isInPurge()) { + user.sendMessage("commands.admin.purge.stop.stopping"); + parentCommand.stop(); + return true; + } else { + user.sendMessage("commands.admin.purge.stop.no-purge-in-progress"); + return false; + } + } +} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java new file mode 100644 index 000000000..d956c5315 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java @@ -0,0 +1,47 @@ +package world.bentobox.bentobox.api.commands.admin.purge; + +import java.util.List; +import java.util.Set; + +import world.bentobox.bentobox.api.commands.ConfirmableCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; + +public class AdminPurgeUnownedCommand extends ConfirmableCommand { + + public AdminPurgeUnownedCommand(AdminPurgeCommand parent) { + super(parent, "unowned"); + } + + @Override + public void setup() { + setPermission("admin.purge"); + setOnlyPlayer(false); + setParametersHelp("commands.admin.purge.unowned.parameters"); + setDescription("commands.admin.purge.unowned.description"); + } + + @Override + public boolean execute(User user, String label, List args) { + if (!args.isEmpty()) { + // Show help + showHelp(this, user); + return false; + } + AdminPurgeCommand parentCommand = ((AdminPurgeCommand)getParent()); + if (parentCommand.isInPurge()) { + user.sendMessage("commands.admin.purge.purge-in-progress"); + return false; + } + Set unowned = parentCommand.getUnownedIslands(); + user.sendMessage("commands.admin.purge.unowned.unowned-islands", TextVariables.NUMBER, String.valueOf(unowned.size())); + if (!unowned.isEmpty()) { + this.askConfirmation(user, () -> { + parentCommand.setUser(user); + parentCommand.setIslands(unowned); + parentCommand.removeIslands(); + }); + } + return true; + } +} \ 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 9bd1ae3b4..c3b0ad5b0 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -57,6 +57,24 @@ commands: reset: description: "resets the resets of this player to 0" parameters: "" + purge: + parameters: "[days]" + description: "purge islands abandoned for more than [days]" + days-or-more: "Must be at least 1 day or more" + purgable-islands: "Found [number] purgable islands." + purge-in-progress: "&cPurging in progress. Use purge stop to cancel" + number-error: "&cArgument must be a number of days" + confirm: "&dType [label] purge confirm to start purging" + completed: "&aPurging stopped" + see-console-for-status: "Purge started. See console for status" + stop: + description: "Stop a purge in progress" + stopping: "Stopping the purge" + no-purge-in-progress: "&cNo purge in progress!" + unowned: + description: "Purge unowned islands - requires confirmation" + unowned-islands: "&dFound [number] islands" + team: add: parameters: " "