From 3eb0de68c4f5ebde7320e48f64693ba68b57eeb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sat, 9 May 2020 20:05:39 +0200 Subject: [PATCH 01/22] Begin working on the structure for the backup system --- .../com/plotsquared/core/backup/Backup.java | 43 ++++++++ .../core/backup/BackupManager.java | 61 ++++++++++++ .../core/backup/BackupProfile.java | 57 +++++++++++ .../core/backup/NullBackupProfile.java | 52 ++++++++++ .../core/backup/PlayerBackupProfile.java | 97 +++++++++++++++++++ 5 files changed, 310 insertions(+) create mode 100644 Core/src/main/java/com/plotsquared/core/backup/Backup.java create mode 100644 Core/src/main/java/com/plotsquared/core/backup/BackupManager.java create mode 100644 Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java create mode 100644 Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java create mode 100644 Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java diff --git a/Core/src/main/java/com/plotsquared/core/backup/Backup.java b/Core/src/main/java/com/plotsquared/core/backup/Backup.java new file mode 100644 index 000000000..3adb9acf0 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/backup/Backup.java @@ -0,0 +1,43 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.backup; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +import java.nio.file.Path; + +/** + * Object representing a plot backup. This does not actually contain the + * backup itself, it is just a pointer to an available backup + */ +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public class Backup { + + private final BackupProfile owner; + private final long creationTime; + private final Path file; + +} diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java new file mode 100644 index 000000000..16f30eeb8 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java @@ -0,0 +1,61 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.backup; + +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.plot.Plot; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Files; +import java.nio.file.Path; + +public class BackupManager { + + @Getter private final Path backupPath; + + public BackupManager() throws Exception { + this.backupPath = PlotSquared.imp().getDirectory().toPath().resolve("backups"); + if (!Files.exists(backupPath)) { + Files.createDirectory(backupPath); + } + } + + /** + * Get the backup profile for a plot based on its + * current owner (if there is one) + * + * @param plot Plot to get the backup profile for + * @return Backup profile + */ + @NotNull public BackupProfile getProfile(@NotNull final Plot plot) { + if (plot.hasOwner()) { + return new PlayerBackupProfile(plot.getOwnerAbs(), plot, this); + } + return new NullBackupProfile(); + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java new file mode 100644 index 000000000..8ba756e87 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java @@ -0,0 +1,57 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.backup; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public interface BackupProfile { + + /** + * Asynchronously populate a list of available backups under this profile + * + * @return Future that will be completed with available backups + */ + @NotNull CompletableFuture> listBackups(); + + /** + * Remove all backups stored for this profile + */ + void destroy() throws IOException; + + /** + * Get the directory containing the backups for this profile. + * This directory may not actually exist. + * + * @return Folder that contains the backups for this profile + */ + @NotNull Path getBackupDirectory(); + +} diff --git a/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java new file mode 100644 index 000000000..add8c3eb3 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java @@ -0,0 +1,52 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.backup; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * Backup profile for a plot without an owner + */ +public class NullBackupProfile implements BackupProfile { + + @Override @NotNull public CompletableFuture> listBackups() { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + @Override public void destroy(){ + } + + @Override @NotNull public Path getBackupDirectory() { + return new File(".").toPath(); + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java new file mode 100644 index 000000000..c87286093 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -0,0 +1,97 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.backup; + +import com.plotsquared.core.plot.Plot; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +/** + * A profile associated with a player (normally a plot owner) and a + * plot, which is used to store and retrieve plot backups + * {@inheritDoc} + */ +@RequiredArgsConstructor +public class PlayerBackupProfile implements BackupProfile { + + private final UUID owner; + private final Plot plot; + private final BackupManager backupManager; + + private static boolean isValidFile(@NotNull final Path path) { + final String name = path.getFileName().toString(); + return name.endsWith(".schem") || name.endsWith(".schematic"); + } + + @NotNull public CompletableFuture> listBackups() { + return CompletableFuture.supplyAsync(() -> { + final Path path = this.getBackupDirectory(); + if (!Files.exists(path)) { + try { + Files.createDirectories(path); + } catch (IOException e) { + e.printStackTrace(); + return Collections.emptyList(); + } + } + final List backups = new ArrayList<>(); + try { + Files.walk(path).filter(PlayerBackupProfile::isValidFile).forEach(file -> { + try { + final BasicFileAttributes + basicFileAttributes = Files.readAttributes(file, BasicFileAttributes.class); + backups.add(new Backup(this, basicFileAttributes.creationTime().toMillis(), file)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + return backups; + }); + } + + public void destroy() throws IOException { + Files.delete(this.getBackupDirectory()); + } + + @NotNull public Path getBackupDirectory() { + return backupManager.getBackupPath().resolve(plot.getArea().getId()) + .resolve(plot.getId().toCommaSeparatedString()).resolve(owner.toString()); + } + +} From 723e29cdd0fa81cb05568d455b1be631504ff371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 14:02:25 +0200 Subject: [PATCH 02/22] Continue working on the backup system --- .../com/plotsquared/bukkit/BukkitMain.java | 16 +++- .../java/com/plotsquared/core/IPlotMain.java | 9 +++ .../com/plotsquared/core/backup/Backup.java | 21 ++++- .../core/backup/BackupManager.java | 54 ++++++++----- .../core/backup/BackupProfile.java | 8 ++ .../core/backup/NullBackupManager.java | 61 +++++++++++++++ .../core/backup/NullBackupProfile.java | 5 ++ .../core/backup/PlayerBackupProfile.java | 21 ++++- .../core/backup/SimpleBackupManager.java | 77 +++++++++++++++++++ .../com/plotsquared/core/command/Save.java | 1 - .../core/configuration/Settings.java | 9 +++ 11 files changed, 257 insertions(+), 25 deletions(-) create mode 100644 Core/src/main/java/com/plotsquared/core/backup/NullBackupManager.java create mode 100644 Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java index 79d27ae33..85a216e9f 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java @@ -52,6 +52,9 @@ import com.plotsquared.bukkit.util.uuid.OfflineUUIDWrapper; import com.plotsquared.bukkit.util.uuid.SQLUUIDHandler; import com.plotsquared.core.IPlotMain; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.backup.BackupManager; +import com.plotsquared.core.backup.NullBackupManager; +import com.plotsquared.core.backup.SimpleBackupManager; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.configuration.ChatFormatter; import com.plotsquared.core.configuration.ConfigurationNode; @@ -136,6 +139,7 @@ import static com.plotsquared.core.util.ReflectionUtils.getRefClass; public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain { + private static final int BSTATS_ID = 1404; @Getter private static WorldEdit worldEdit; static { @@ -151,7 +155,7 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain private Method methodUnloadChunk0; private boolean methodUnloadSetup = false; private boolean metricsStarted; - private static final int BSTATS_ID = 1404; + @Getter private BackupManager backupManager; @Override public int[] getServerVersion() { if (this.version == null) { @@ -229,6 +233,15 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain e.printStackTrace(); } } + + try { + this.backupManager = new SimpleBackupManager(); + } catch (final Exception e) { + PlotSquared.log(Captions.PREFIX + "&6Failed to initialize backup manager"); + e.printStackTrace(); + PlotSquared.log(Captions.PREFIX + "&6Backup features will be disabled"); + this.backupManager = new NullBackupManager(); + } } private void unload() { @@ -886,4 +899,5 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain ((WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit")); return wePlugin.wrapCommandSender(console); } + } diff --git a/Core/src/main/java/com/plotsquared/core/IPlotMain.java b/Core/src/main/java/com/plotsquared/core/IPlotMain.java index 1b04b220f..cf6c8fb95 100644 --- a/Core/src/main/java/com/plotsquared/core/IPlotMain.java +++ b/Core/src/main/java/com/plotsquared/core/IPlotMain.java @@ -25,6 +25,7 @@ */ package com.plotsquared.core; +import com.plotsquared.core.backup.BackupManager; import com.plotsquared.core.generator.GeneratorWrapper; import com.plotsquared.core.generator.HybridUtils; import com.plotsquared.core.generator.IndependentPlotGenerator; @@ -262,4 +263,12 @@ public interface IPlotMain extends ILogger { List, Boolean>> getPluginIds(); Actor getConsole(); + + /** + * Get the backup manager instance + * + * @return Backup manager + */ + BackupManager getBackupManager(); + } diff --git a/Core/src/main/java/com/plotsquared/core/backup/Backup.java b/Core/src/main/java/com/plotsquared/core/backup/Backup.java index 3adb9acf0..a84fc497f 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/Backup.java +++ b/Core/src/main/java/com/plotsquared/core/backup/Backup.java @@ -26,18 +26,35 @@ package com.plotsquared.core.backup; import lombok.AccessLevel; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.Nullable; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; /** * Object representing a plot backup. This does not actually contain the * backup itself, it is just a pointer to an available backup */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public class Backup { +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) @Getter public class Backup { private final BackupProfile owner; private final long creationTime; - private final Path file; + @Nullable private final Path file; + + /** + * Delete the backup + */ + public void delete() { + if (file != null) { + try { + Files.deleteIfExists(file); + } catch (final IOException e) { + e.printStackTrace(); + } + } + } } diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java index 16f30eeb8..0d1842fc3 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java @@ -25,24 +25,13 @@ */ package com.plotsquared.core.backup; -import com.plotsquared.core.PlotSquared; import com.plotsquared.core.plot.Plot; -import lombok.Getter; import org.jetbrains.annotations.NotNull; -import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; -public class BackupManager { - - @Getter private final Path backupPath; - - public BackupManager() throws Exception { - this.backupPath = PlotSquared.imp().getDirectory().toPath().resolve("backups"); - if (!Files.exists(backupPath)) { - Files.createDirectory(backupPath); - } - } +public interface BackupManager { /** * Get the backup profile for a plot based on its @@ -51,11 +40,38 @@ public class BackupManager { * @param plot Plot to get the backup profile for * @return Backup profile */ - @NotNull public BackupProfile getProfile(@NotNull final Plot plot) { - if (plot.hasOwner()) { - return new PlayerBackupProfile(plot.getOwnerAbs(), plot, this); - } - return new NullBackupProfile(); - } + @NotNull BackupProfile getProfile(@NotNull final Plot plot); + + /** + * This will perform an automatic backup of the plot iff the plot has an owner + * and automatic backups are enabled. Otherwise it will complete immediately. + * + * @param plot Plot to perform the automatic backup on + * @return Future that completes when the backup is finished + */ + @NotNull CompletableFuture automaticBackup(@NotNull final Plot plot); + + /** + * Get the directory in which backups are stored + * + * @return Backup directory path + */ + @NotNull Path getBackupPath(); + + /** + * Get the maximum amount of backups that may be stored for + * a plot-owner combo + * + * @return Backup limit + */ + int getBackupLimit(); + + /** + * Returns true if (potentially) destructive actions should cause + * PlotSquared to create automatic plot backups + * + * @return True if automatic backups are enabled + */ + boolean shouldAutomaticallyBackup(); } diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java index 8ba756e87..90b4d9694 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java @@ -54,4 +54,12 @@ public interface BackupProfile { */ @NotNull Path getBackupDirectory(); + /** + * Create a backup of the plot. If the profile is at the + * maximum backup capacity, the oldest backup will be deleted. + * + * @return Future that completes with the created backup. + */ + @NotNull CompletableFuture createBackup(); + } diff --git a/Core/src/main/java/com/plotsquared/core/backup/NullBackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/NullBackupManager.java new file mode 100644 index 000000000..d5f9bd954 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/backup/NullBackupManager.java @@ -0,0 +1,61 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.backup; + +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.plot.Plot; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Path; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +/** + * {@inheritDoc} + */ +public class NullBackupManager implements BackupManager { + + @Override @NotNull public BackupProfile getProfile(@NotNull Plot plot) { + return new NullBackupProfile(); + } + + @Override @NotNull public CompletableFuture automaticBackup(@NotNull Plot plot) { + return CompletableFuture.completedFuture(null); + } + + @Override @NotNull public Path getBackupPath() { + return Objects.requireNonNull(PlotSquared.imp()).getDirectory().toPath(); + } + + @Override public int getBackupLimit() { + return 0; + } + + @Override public boolean shouldAutomaticallyBackup() { + return false; + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java index add8c3eb3..e4647592c 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java @@ -35,6 +35,7 @@ import java.util.concurrent.CompletableFuture; /** * Backup profile for a plot without an owner + * {@inheritDoc} */ public class NullBackupProfile implements BackupProfile { @@ -49,4 +50,8 @@ public class NullBackupProfile implements BackupProfile { return new File(".").toPath(); } + @Override @NotNull public CompletableFuture createBackup() { + throw new UnsupportedOperationException("Cannot create backup of an unowned plot"); + } + } diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index c87286093..f99bd8cd9 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -26,6 +26,7 @@ package com.plotsquared.core.backup; import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.util.SchematicHandler; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -56,7 +57,7 @@ public class PlayerBackupProfile implements BackupProfile { return name.endsWith(".schem") || name.endsWith(".schematic"); } - @NotNull public CompletableFuture> listBackups() { + @Override @NotNull public CompletableFuture> listBackups() { return CompletableFuture.supplyAsync(() -> { final Path path = this.getBackupDirectory(); if (!Files.exists(path)) { @@ -85,7 +86,7 @@ public class PlayerBackupProfile implements BackupProfile { }); } - public void destroy() throws IOException { + @Override public void destroy() throws IOException { Files.delete(this.getBackupDirectory()); } @@ -94,4 +95,20 @@ public class PlayerBackupProfile implements BackupProfile { .resolve(plot.getId().toCommaSeparatedString()).resolve(owner.toString()); } + @Override @NotNull public CompletableFuture createBackup() { + final CompletableFuture future = new CompletableFuture<>(); + this.listBackups().thenAcceptAsync(backups -> { + if (backups.size() == backupManager.getBackupLimit()) { + backups.get(backups.size() - 1).delete(); + } + final List plots = Collections.singletonList(plot); + final boolean result = SchematicHandler.manager.exportAll(plots, null, null, () -> + future.complete(new Backup(this, System.currentTimeMillis(), null))); + if (!result) { + future.completeExceptionally(new RuntimeException("Failed to complete the backup")); + } + }); + return future; + } + } diff --git a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java new file mode 100644 index 000000000..4f86af781 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java @@ -0,0 +1,77 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.backup; + +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.plot.Plot; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +/** + * {@inheritDoc} + */ +@RequiredArgsConstructor public class SimpleBackupManager implements BackupManager { + + @Getter private final Path backupPath; + private final boolean automaticBackup; + @Getter private final int backupLimit; + + public SimpleBackupManager() throws Exception { + this.backupPath = Objects.requireNonNull(PlotSquared.imp()).getDirectory().toPath().resolve("backups"); + if (!Files.exists(backupPath)) { + Files.createDirectory(backupPath); + } + this.automaticBackup = Settings.Backup.AUTOMATIC_BACKUPS; + this.backupLimit = Settings.Backup.BACKUP_LIMIT; + } + + @Override @NotNull public BackupProfile getProfile(@NotNull final Plot plot) { + if (plot.hasOwner()) { + return new PlayerBackupProfile(plot.getOwnerAbs(), plot, this); + } + return new NullBackupProfile(); + } + + @Override @NotNull public CompletableFuture automaticBackup(@NotNull final Plot plot) { + final BackupProfile profile; + if (!this.shouldAutomaticallyBackup() || (profile = getProfile(plot)) instanceof NullBackupProfile) { + return CompletableFuture.completedFuture(null); + } + return profile.createBackup(); + } + + @Override public boolean shouldAutomaticallyBackup() { + return this.automaticBackup; + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/command/Save.java b/Core/src/main/java/com/plotsquared/core/command/Save.java index 207ce9cbc..6196f08b7 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Save.java +++ b/Core/src/main/java/com/plotsquared/core/command/Save.java @@ -43,7 +43,6 @@ import java.util.List; import java.util.UUID; @CommandDeclaration(command = "save", - aliases = {"backup"}, description = "Save your plot", category = CommandCategory.SCHEMATIC, requiredType = RequiredType.NONE, diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java index 3b1fe6b22..6db5f8476 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java @@ -413,4 +413,13 @@ public class Settings extends Config { @Comment("Try to guess plot owners from sign data. This may decrease server performance") public static boolean GUESS_PLOT_OWNER = false; } + + @Comment("Backup related settings") + public static final class Backup { + @Comment("Automatically backup plots when destructive commands are performed") + public static boolean AUTOMATIC_BACKUPS = true; + @Comment("Maximum amount of backups associated with a plot") + public static int BACKUP_LIMIT = 3; + } + } From 8715a27a934dec7b1780314a1fae75aa995b7e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 14:41:02 +0200 Subject: [PATCH 03/22] No backups for merged plots :cow: --- .../main/java/com/plotsquared/core/backup/BackupManager.java | 5 +++-- .../com/plotsquared/core/backup/SimpleBackupManager.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java index 0d1842fc3..4077be914 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java @@ -43,8 +43,9 @@ public interface BackupManager { @NotNull BackupProfile getProfile(@NotNull final Plot plot); /** - * This will perform an automatic backup of the plot iff the plot has an owner - * and automatic backups are enabled. Otherwise it will complete immediately. + * This will perform an automatic backup of the plot iff the plot has an owner, + * automatic backups are enabled and the plot is not merged. + * Otherwise it will complete immediately. * * @param plot Plot to perform the automatic backup on * @return Future that completes when the backup is finished diff --git a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java index 4f86af781..492b5ed60 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java @@ -56,7 +56,7 @@ import java.util.concurrent.CompletableFuture; } @Override @NotNull public BackupProfile getProfile(@NotNull final Plot plot) { - if (plot.hasOwner()) { + if (plot.hasOwner() && !plot.isMerged()) { return new PlayerBackupProfile(plot.getOwnerAbs(), plot, this); } return new NullBackupProfile(); From d0dbb495b0f15aaa3809650fa5e57889e029cda1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 14:58:45 +0200 Subject: [PATCH 04/22] Add BackupProfile#restoreBackup --- .../core/backup/BackupProfile.java | 7 ++++ .../core/backup/NullBackupProfile.java | 4 +++ .../core/backup/PlayerBackupProfile.java | 34 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java index 90b4d9694..663d684f6 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java @@ -62,4 +62,11 @@ public interface BackupProfile { */ @NotNull CompletableFuture createBackup(); + /** + * Restore a backup + * + * @return Backup to restore + */ + @NotNull CompletableFuture restoreBackup(@NotNull final Backup backup); + } diff --git a/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java index e4647592c..39c833579 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java @@ -54,4 +54,8 @@ public class NullBackupProfile implements BackupProfile { throw new UnsupportedOperationException("Cannot create backup of an unowned plot"); } + @Override @NotNull public CompletableFuture restoreBackup(@NotNull final Backup backup) { + return CompletableFuture.completedFuture(null); + } + } diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index f99bd8cd9..f86201424 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -25,8 +25,12 @@ */ package com.plotsquared.core.backup; +import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.plot.schematic.Schematic; import com.plotsquared.core.util.SchematicHandler; +import com.plotsquared.core.util.task.RunnableVal; +import com.plotsquared.core.util.task.TaskManager; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -111,4 +115,34 @@ public class PlayerBackupProfile implements BackupProfile { return future; } + @Override @NotNull public CompletableFuture restoreBackup(@NotNull final Backup backup) { + final CompletableFuture future = new CompletableFuture<>(); + if (backup.getFile() == null || !Files.exists(backup.getFile())) { + future.completeExceptionally(new IllegalArgumentException("The specific backup does not exist")); + } else { + TaskManager.runTaskAsync(() -> { + Schematic schematic = null; + try { + schematic = SchematicHandler.manager.getSchematic(backup.getFile().toFile()); + } catch (SchematicHandler.UnsupportedFormatException e) { + e.printStackTrace(); + } + if (schematic == null) { + future.completeExceptionally(new IllegalArgumentException("The backup is non-existent or not in the correct format")); + } else { + SchematicHandler.manager.paste(schematic, plot, 0, 1, 0, false, new RunnableVal() { + @Override public void run(Boolean value) { + if (value) { + future.complete(null); + } else { + future.completeExceptionally(new RuntimeException(Captions.SCHEMATIC_PASTE_FAILED.toString())); + } + } + }); + } + }); + } + return future; + } + } From 2bd30af3613b26c64bcc1f93eb1d22608c3fc7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 15:12:17 +0200 Subject: [PATCH 05/22] Cache both backup profiles and backup objects --- .../core/backup/PlayerBackupProfile.java | 72 +++++++++++-------- .../core/backup/SimpleBackupManager.java | 37 +++++++++- 2 files changed, 79 insertions(+), 30 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index f86201424..b1d7f43a9 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -56,42 +56,53 @@ public class PlayerBackupProfile implements BackupProfile { private final Plot plot; private final BackupManager backupManager; + private volatile List backupCache; + private final Object backupLock = new Object(); + private static boolean isValidFile(@NotNull final Path path) { final String name = path.getFileName().toString(); return name.endsWith(".schem") || name.endsWith(".schematic"); } @Override @NotNull public CompletableFuture> listBackups() { - return CompletableFuture.supplyAsync(() -> { - final Path path = this.getBackupDirectory(); - if (!Files.exists(path)) { - try { - Files.createDirectories(path); - } catch (IOException e) { - e.printStackTrace(); - return Collections.emptyList(); - } + synchronized (this.backupLock) { + if (this.backupCache != null) { + return CompletableFuture.completedFuture(backupCache); } - final List backups = new ArrayList<>(); - try { - Files.walk(path).filter(PlayerBackupProfile::isValidFile).forEach(file -> { + return CompletableFuture.supplyAsync(() -> { + final Path path = this.getBackupDirectory(); + if (!Files.exists(path)) { try { - final BasicFileAttributes - basicFileAttributes = Files.readAttributes(file, BasicFileAttributes.class); - backups.add(new Backup(this, basicFileAttributes.creationTime().toMillis(), file)); + Files.createDirectories(path); } catch (IOException e) { e.printStackTrace(); + return Collections.emptyList(); } - }); - } catch (IOException e) { - e.printStackTrace(); - } - return backups; - }); + } + final List backups = new ArrayList<>(); + try { + Files.walk(path).filter(PlayerBackupProfile::isValidFile).forEach(file -> { + try { + final BasicFileAttributes basicFileAttributes = + Files.readAttributes(file, BasicFileAttributes.class); + backups.add( + new Backup(this, basicFileAttributes.creationTime().toMillis(), file)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + return backups; + }); + } } @Override public void destroy() throws IOException { Files.delete(this.getBackupDirectory()); + // Invalidate backup cache + this.backupCache = null; } @NotNull public Path getBackupDirectory() { @@ -102,14 +113,17 @@ public class PlayerBackupProfile implements BackupProfile { @Override @NotNull public CompletableFuture createBackup() { final CompletableFuture future = new CompletableFuture<>(); this.listBackups().thenAcceptAsync(backups -> { - if (backups.size() == backupManager.getBackupLimit()) { - backups.get(backups.size() - 1).delete(); - } - final List plots = Collections.singletonList(plot); - final boolean result = SchematicHandler.manager.exportAll(plots, null, null, () -> - future.complete(new Backup(this, System.currentTimeMillis(), null))); - if (!result) { - future.completeExceptionally(new RuntimeException("Failed to complete the backup")); + synchronized (this.backupLock) { + if (backups.size() == backupManager.getBackupLimit()) { + backups.get(backups.size() - 1).delete(); + } + final List plots = Collections.singletonList(plot); + final boolean result = SchematicHandler.manager.exportAll(plots, null, null, () -> + future.complete(new Backup(this, System.currentTimeMillis(), null))); + if (!result) { + future.completeExceptionally(new RuntimeException("Failed to complete the backup")); + } + this.backupCache = null; } }); return future; diff --git a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java index 492b5ed60..4c206b9ad 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java @@ -25,9 +25,12 @@ */ package com.plotsquared.core.backup; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.plot.Plot; +import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -36,6 +39,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; /** * {@inheritDoc} @@ -45,6 +50,8 @@ import java.util.concurrent.CompletableFuture; @Getter private final Path backupPath; private final boolean automaticBackup; @Getter private final int backupLimit; + private final Cache backupProfileCache = CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.MINUTES).build(); public SimpleBackupManager() throws Exception { this.backupPath = Objects.requireNonNull(PlotSquared.imp()).getDirectory().toPath().resolve("backups"); @@ -57,7 +64,13 @@ import java.util.concurrent.CompletableFuture; @Override @NotNull public BackupProfile getProfile(@NotNull final Plot plot) { if (plot.hasOwner() && !plot.isMerged()) { - return new PlayerBackupProfile(plot.getOwnerAbs(), plot, this); + try { + return backupProfileCache.get(new PlotCacheKey(plot), () -> new PlayerBackupProfile(plot.getOwnerAbs(), plot, this)); + } catch (ExecutionException e) { + final BackupProfile profile = new PlayerBackupProfile(plot.getOwnerAbs(), plot, this); + this.backupProfileCache.put(new PlotCacheKey(plot), profile); + return profile; + } } return new NullBackupProfile(); } @@ -74,4 +87,26 @@ import java.util.concurrent.CompletableFuture; return this.automaticBackup; } + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) private static final class PlotCacheKey { + + private final Plot plot; + + @Override public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PlotCacheKey that = (PlotCacheKey) o; + return com.google.common.base.Objects.equal(plot.getArea(), that.plot.getArea()) + && com.google.common.base.Objects.equal(plot.getId(), that.plot.getId()) + && com.google.common.base.Objects.equal(plot.getOwnerAbs(), that.plot.getOwnerAbs()); + } + + @Override public int hashCode() { + return com.google.common.base.Objects.hashCode(plot.getArea(), plot.getId(), plot.getOwnerAbs()); + } + } + } From e0fb6f54400dbe846c0c3158a87b8de6f8180788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 15:36:20 +0200 Subject: [PATCH 06/22] `/plot debug` structure --- .../com/plotsquared/core/command/Backup.java | 136 ++++++++++++++++++ .../plotsquared/core/command/MainCommand.java | 1 + .../core/configuration/Captions.java | 4 + 3 files changed, 141 insertions(+) create mode 100644 Core/src/main/java/com/plotsquared/core/command/Backup.java diff --git a/Core/src/main/java/com/plotsquared/core/command/Backup.java b/Core/src/main/java/com/plotsquared/core/command/Backup.java new file mode 100644 index 000000000..8e16758bb --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/command/Backup.java @@ -0,0 +1,136 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.command; + +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.backup.BackupProfile; +import com.plotsquared.core.backup.PlayerBackupProfile; +import com.plotsquared.core.configuration.Captions; +import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.util.task.RunnableVal2; +import com.plotsquared.core.util.task.RunnableVal3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +@CommandDeclaration(command = "backup", +usage = "/plot backup ", +description = "Manage plot backups", +category = CommandCategory.SETTINGS, +requiredType = RequiredType.PLAYER, +permission = "plots.backup") +public final class Backup extends Command { + + public Backup() { + super(MainCommand.getInstance(), true); + } + + private static boolean sendMessage(PlotPlayer player, Captions message, Object... args) { + message.send(player, args); + return true; + } + + @Override public CompletableFuture execute(PlotPlayer player, String[] args, + RunnableVal3 confirm, + RunnableVal2 whenDone) throws CommandException { + if (args.length == 0 || !Arrays.asList("save", "list", "load") + .contains(args[0].toLowerCase(Locale.ENGLISH))) { + return CompletableFuture.completedFuture(sendMessage(player, Captions.BACKUP_USAGE)); + } + return super.execute(player, args, confirm, whenDone); + } + + @Override public Collection tab(PlotPlayer player, String[] args, boolean space) { + if (args.length == 1) { + return Stream.of("save", "list", "save") + .filter(value -> value.startsWith(args[0].toLowerCase(Locale.ENGLISH))) + .map(value -> new Command(null, false, value, "", RequiredType.NONE, null) {}) + .collect(Collectors.toList()); + } else if (args[0].equalsIgnoreCase("load") && args.length == 2) { + + final Plot plot = player.getCurrentPlot(); + if (plot != null) { + final BackupProfile backupProfile = Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(plot); + if (!(backupProfile instanceof PlayerBackupProfile)) { + final CompletableFuture> backupList = backupProfile.listBackups(); + if (backupList.isDone()) { + final List backups = backupList.getNow(new ArrayList<>()); + if (backups.isEmpty()) { + return new ArrayList<>(); + } + return IntStream.range(1, 1 + backups.size()).mapToObj(i -> new Command(null, false, Integer.toString(i), + "", RequiredType.NONE, null) {}).collect(Collectors.toList()); + + } + } + } + } + return tabOf(player, args, space); + } + + @CommandDeclaration(command = "save", + usage = "/plot backup save", + description = "Create a plot backup", + category = CommandCategory.SETTINGS, + requiredType = RequiredType.PLAYER, + permission = "plots.backup.save") + public void save(final Command command, final PlotPlayer player, final String[] args, + final RunnableVal3 confirm, + final RunnableVal2 whenDone) { + } + + @CommandDeclaration(command = "list", + usage = "/plot backup list", + description = "List available plot backups", + category = CommandCategory.SETTINGS, + requiredType = RequiredType.PLAYER, + permission = "plots.backup.list") + public void list(final Command command, final PlotPlayer player, final String[] args, + final RunnableVal3 confirm, + final RunnableVal2 whenDone) { + } + + @CommandDeclaration(command = "load", + usage = "/plot backup load <#>", + description = "Restore a plot backup", + category = CommandCategory.SETTINGS, + requiredType = RequiredType.PLAYER, + permission = "plots.backup.load") + public void load(final Command command, final PlotPlayer player, final String[] args, + final RunnableVal3 confirm, + final RunnableVal2 whenDone) { + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/command/MainCommand.java b/Core/src/main/java/com/plotsquared/core/command/MainCommand.java index fb1dc68ed..03be061ca 100644 --- a/Core/src/main/java/com/plotsquared/core/command/MainCommand.java +++ b/Core/src/main/java/com/plotsquared/core/command/MainCommand.java @@ -127,6 +127,7 @@ public class MainCommand extends Command { new SetHome(); new Cluster(); new DebugImportWorlds(); + new Backup(); if (Settings.Ratings.USE_LIKES) { new Like(); diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index bbba53f6f..d63019a21 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -757,6 +757,10 @@ public enum Captions implements Caption { false, "Info"), // + // + BACKUP_USAGE("$1Usage: $2/plot backup save/list/load", "Backups"), + // + /** * Legacy Configuration Conversion */ From bbc86eba39cca776c7485d4f4687d5af6784e673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 15:51:27 +0200 Subject: [PATCH 07/22] `/plot debug` progress --- .../com/plotsquared/core/command/Backup.java | 72 +++++++++++++++++++ .../core/configuration/Captions.java | 9 +++ 2 files changed, 81 insertions(+) diff --git a/Core/src/main/java/com/plotsquared/core/command/Backup.java b/Core/src/main/java/com/plotsquared/core/command/Backup.java index 8e16758bb..f9f45756e 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Backup.java +++ b/Core/src/main/java/com/plotsquared/core/command/Backup.java @@ -27,10 +27,12 @@ package com.plotsquared.core.command; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.backup.BackupProfile; +import com.plotsquared.core.backup.NullBackupProfile; import com.plotsquared.core.backup.PlayerBackupProfile; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; @@ -109,6 +111,29 @@ public final class Backup extends Command { public void save(final Command command, final PlotPlayer player, final String[] args, final RunnableVal3 confirm, final RunnableVal2 whenDone) { + final Plot plot = player.getCurrentPlot(); + if (plot == null) { + sendMessage(player, Captions.NOT_IN_PLOT); + } else if (!plot.hasOwner()) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "unowned"); + } else if (plot.isMerged()) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "merged"); + } else if (!plot.isOwner(player.getUUID()) && !Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_BACKUP_OTHER)) { + sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_ADMIN_BACKUP_OTHER); + } else { + final BackupProfile backupProfile = Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(plot); + if (backupProfile instanceof NullBackupProfile) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "other"); + } else { + backupProfile.createBackup().whenComplete((backup, throwable) -> { + if (throwable != null) { + sendMessage(player, Captions.BACKUP_FAILED, throwable.getMessage()); + } else { + sendMessage(player, Captions.BACKUP_SAVED); + } + }); + } + } } @CommandDeclaration(command = "list", @@ -120,6 +145,25 @@ public final class Backup extends Command { public void list(final Command command, final PlotPlayer player, final String[] args, final RunnableVal3 confirm, final RunnableVal2 whenDone) { + final Plot plot = player.getCurrentPlot(); + if (plot == null) { + sendMessage(player, Captions.NOT_IN_PLOT); + } else if (!plot.hasOwner()) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "unowned"); + } else if (plot.isMerged()) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "merged"); + } else if (!plot.isOwner(player.getUUID()) && !Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_BACKUP_OTHER)) { + sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_ADMIN_BACKUP_OTHER); + } else { + final BackupProfile backupProfile = Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(plot); + if (backupProfile instanceof NullBackupProfile) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "other"); + } else { + backupProfile.listBackups().whenComplete((backups, throwable) -> { + // TODO: List backups + }); + } + } } @CommandDeclaration(command = "load", @@ -131,6 +175,34 @@ public final class Backup extends Command { public void load(final Command command, final PlotPlayer player, final String[] args, final RunnableVal3 confirm, final RunnableVal2 whenDone) { + final Plot plot = player.getCurrentPlot(); + if (plot == null) { + sendMessage(player, Captions.NOT_IN_PLOT); + } else if (!plot.hasOwner()) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "unowned"); + } else if (plot.isMerged()) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "merged"); + } else if (!plot.isOwner(player.getUUID()) && !Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_BACKUP_OTHER)) { + sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_ADMIN_BACKUP_OTHER); + } else if (args.length == 0) { + sendMessage(player, Captions.BACKUP_LOAD_USAGE); + } else { + final int number; + try { + number = Integer.parseInt(args[0]); + } catch (final Exception e) { + sendMessage(player, Captions.NOT_A_NUMBER, args[0]); + return; + } + final BackupProfile backupProfile = Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(plot); + if (backupProfile instanceof NullBackupProfile) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "other"); + } else { + backupProfile.listBackups().whenComplete((backups, throwable) -> { + // TODO: Load backups + }); + } + } } } diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index d63019a21..c5f6bc8e3 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -184,6 +184,11 @@ public enum Captions implements Caption { PERMISSION_ALIAS_SET("plots.alias.set", "static.permissions"), PERMISSION_ALIAS_REMOVE("plots.alias.remove", "static.permissions"), PERMISSION_ADMIN_CHAT_BYPASS("plots.admin.chat.bypass", "static.permissions"), + PERMISSION_BACKUP("plots.backup", "static.permissions"), + PERMISSION_BACKUP_SAVE("plots.backup.save", "static.permissions"), + PERMISSION_BACUP_LIST("plots.backup.list", "static.permissions"), + PERMISSION_BACKUP_LOAD("plots.backup.load", "static.permissions"), + PERMISSION_ADMIN_BACKUP_OTHER("plots.admin.backup.other", "static.permissions"), // // CONSOLE_JAVA_OUTDATED( @@ -759,6 +764,10 @@ public enum Captions implements Caption { // BACKUP_USAGE("$1Usage: $2/plot backup save/list/load", "Backups"), + BACKUP_IMPOSSIBLE("$2Backups are not enabled for this plot: %s", "Backups"), + BACKUP_SAVED("$1The backup was created successfully", "Backups"), + BACKUP_FAILED("$2The backup could not be created: %s", "Backups"), + BACKUP_LOAD_USAGE("$1Usage: $2/plot backup load [#]", "Backups"), // /** From 63c9037b10061f04f85fa21dcbf3a405df228dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 16:00:20 +0200 Subject: [PATCH 08/22] Fix typo --- .../main/java/com/plotsquared/core/configuration/Captions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index c5f6bc8e3..7773d006c 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -186,7 +186,7 @@ public enum Captions implements Caption { PERMISSION_ADMIN_CHAT_BYPASS("plots.admin.chat.bypass", "static.permissions"), PERMISSION_BACKUP("plots.backup", "static.permissions"), PERMISSION_BACKUP_SAVE("plots.backup.save", "static.permissions"), - PERMISSION_BACUP_LIST("plots.backup.list", "static.permissions"), + PERMISSION_BAKCUP_LIST("plots.backup.list", "static.permissions"), PERMISSION_BACKUP_LOAD("plots.backup.load", "static.permissions"), PERMISSION_ADMIN_BACKUP_OTHER("plots.admin.backup.other", "static.permissions"), // From cdab52fcbef7d57a1ac9792015186f2ddf31b28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 16:04:33 +0200 Subject: [PATCH 09/22] dumdum space go bye bye --- .../main/java/com/plotsquared/core/backup/BackupManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java index 4077be914..bfa4ad37f 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java @@ -34,7 +34,7 @@ import java.util.concurrent.CompletableFuture; public interface BackupManager { /** - * Get the backup profile for a plot based on its + * Get the backup profile for a plot based on its * current owner (if there is one) * * @param plot Plot to get the backup profile for From 59f96d44559d8392e66ee22e13e16371e18fe3b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 16:39:14 +0200 Subject: [PATCH 10/22] Translate all backup failure reasons --- .../com/plotsquared/core/command/Backup.java | 17 +++++++++-------- .../core/configuration/Captions.java | 6 ++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/command/Backup.java b/Core/src/main/java/com/plotsquared/core/command/Backup.java index f9f45756e..861b1b3d9 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Backup.java +++ b/Core/src/main/java/com/plotsquared/core/command/Backup.java @@ -115,15 +115,15 @@ public final class Backup extends Command { if (plot == null) { sendMessage(player, Captions.NOT_IN_PLOT); } else if (!plot.hasOwner()) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "unowned"); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED); } else if (plot.isMerged()) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "merged"); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_MERGED.getTranslated()); } else if (!plot.isOwner(player.getUUID()) && !Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_BACKUP_OTHER)) { sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_ADMIN_BACKUP_OTHER); } else { final BackupProfile backupProfile = Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(plot); if (backupProfile instanceof NullBackupProfile) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "other"); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_OTHER.getTranslated()); } else { backupProfile.createBackup().whenComplete((backup, throwable) -> { if (throwable != null) { @@ -149,15 +149,15 @@ public final class Backup extends Command { if (plot == null) { sendMessage(player, Captions.NOT_IN_PLOT); } else if (!plot.hasOwner()) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "unowned"); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED); } else if (plot.isMerged()) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "merged"); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_MERGED.getTranslated()); } else if (!plot.isOwner(player.getUUID()) && !Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_BACKUP_OTHER)) { sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_ADMIN_BACKUP_OTHER); } else { final BackupProfile backupProfile = Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(plot); if (backupProfile instanceof NullBackupProfile) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "other"); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_OTHER.getTranslated()); } else { backupProfile.listBackups().whenComplete((backups, throwable) -> { // TODO: List backups @@ -179,8 +179,9 @@ public final class Backup extends Command { if (plot == null) { sendMessage(player, Captions.NOT_IN_PLOT); } else if (!plot.hasOwner()) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "unowned"); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED); } else if (plot.isMerged()) { + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_MERGED.getTranslated()); sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "merged"); } else if (!plot.isOwner(player.getUUID()) && !Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_BACKUP_OTHER)) { sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_ADMIN_BACKUP_OTHER); @@ -196,7 +197,7 @@ public final class Backup extends Command { } final BackupProfile backupProfile = Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(plot); if (backupProfile instanceof NullBackupProfile) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "other"); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_OTHER.getTranslated()); } else { backupProfile.listBackups().whenComplete((backups, throwable) -> { // TODO: Load backups diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index 7773d006c..20d6193ea 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -770,6 +770,12 @@ public enum Captions implements Caption { BACKUP_LOAD_USAGE("$1Usage: $2/plot backup load [#]", "Backups"), // + // + GENERIC_OTHER("other", "Generic"), + GENERIC_MERGED("merged", "Generic"), + GENERIC_UNOWNED("unowned", "Generic"), + // + /** * Legacy Configuration Conversion */ From 079289eb74bee01d0e87c8b43f823d69336d784a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 16:40:12 +0200 Subject: [PATCH 11/22] Use #getTranslated on GENERIC_UNOWNED --- Core/src/main/java/com/plotsquared/core/command/Backup.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/command/Backup.java b/Core/src/main/java/com/plotsquared/core/command/Backup.java index 861b1b3d9..5f9522194 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Backup.java +++ b/Core/src/main/java/com/plotsquared/core/command/Backup.java @@ -115,7 +115,7 @@ public final class Backup extends Command { if (plot == null) { sendMessage(player, Captions.NOT_IN_PLOT); } else if (!plot.hasOwner()) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED.getTranslated()); } else if (plot.isMerged()) { sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_MERGED.getTranslated()); } else if (!plot.isOwner(player.getUUID()) && !Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_BACKUP_OTHER)) { @@ -149,7 +149,7 @@ public final class Backup extends Command { if (plot == null) { sendMessage(player, Captions.NOT_IN_PLOT); } else if (!plot.hasOwner()) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED.getTranslated()); } else if (plot.isMerged()) { sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_MERGED.getTranslated()); } else if (!plot.isOwner(player.getUUID()) && !Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_BACKUP_OTHER)) { @@ -179,7 +179,7 @@ public final class Backup extends Command { if (plot == null) { sendMessage(player, Captions.NOT_IN_PLOT); } else if (!plot.hasOwner()) { - sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED); + sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_UNOWNED.getTranslated()); } else if (plot.isMerged()) { sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_MERGED.getTranslated()); sendMessage(player, Captions.BACKUP_IMPOSSIBLE, "merged"); From 6fb63c9609a0305075231b8b95ab144a1e0f17de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 16:53:19 +0200 Subject: [PATCH 12/22] Implement backup restoration command. This might work, idk, we'll see. --- .../com/plotsquared/core/command/Backup.java | 27 ++++++++++++++++--- .../core/configuration/Captions.java | 7 +++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/command/Backup.java b/Core/src/main/java/com/plotsquared/core/command/Backup.java index 5f9522194..60cba6d6c 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Backup.java +++ b/Core/src/main/java/com/plotsquared/core/command/Backup.java @@ -36,6 +36,7 @@ import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -127,9 +128,9 @@ public final class Backup extends Command { } else { backupProfile.createBackup().whenComplete((backup, throwable) -> { if (throwable != null) { - sendMessage(player, Captions.BACKUP_FAILED, throwable.getMessage()); + sendMessage(player, Captions.BACKUP_SAVE_FAILED, throwable.getMessage()); } else { - sendMessage(player, Captions.BACKUP_SAVED); + sendMessage(player, Captions.BACKUP_SAVE_SUCCESS); } }); } @@ -200,7 +201,27 @@ public final class Backup extends Command { sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_OTHER.getTranslated()); } else { backupProfile.listBackups().whenComplete((backups, throwable) -> { - // TODO: Load backups + if (throwable != null) { + sendMessage(player, Captions.BACKUP_LOAD_FAILURE, throwable.getMessage()); + } else { + if (number < 1 || number > backups.size()) { + sendMessage(player, Captions.BACKUP_LOAD_FAILURE, Captions.GENERIC_INVALID_CHOICE.getTranslated()); + } else { + final com.plotsquared.core.backup.Backup backup = backups.get(number - 1); + if (backup == null || backup.getFile() == null || !Files.exists(backup.getFile())) { + sendMessage(player, Captions.BACKUP_LOAD_FAILURE, Captions.GENERIC_INVALID_CHOICE.getTranslated()); + } else { + CmdConfirm.addPending(player, "/plot backup load " + number, () -> + backupProfile.restoreBackup(backup).whenComplete((n, error) -> { + if (error != null) { + sendMessage(player, Captions.BACKUP_LOAD_FAILURE, error.getMessage()); + } else { + sendMessage(player, Captions.BACKUP_LOAD_SUCCESS); + } + })); + } + } + } }); } } diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index 20d6193ea..486a4db90 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -765,8 +765,10 @@ public enum Captions implements Caption { // BACKUP_USAGE("$1Usage: $2/plot backup save/list/load", "Backups"), BACKUP_IMPOSSIBLE("$2Backups are not enabled for this plot: %s", "Backups"), - BACKUP_SAVED("$1The backup was created successfully", "Backups"), - BACKUP_FAILED("$2The backup could not be created: %s", "Backups"), + BACKUP_SAVE_SUCCESS("$1The backup was created successfully", "Backups"), + BACKUP_SAVE_FAILED("$2The backup could not be created: %s", "Backups"), + BACKUP_LOAD_SUCCESS("$1The backup was restored successfully", "Backups"), + BACKUP_LOAD_FAILURE("$2The backup could not be restored: %s", "Backups"), BACKUP_LOAD_USAGE("$1Usage: $2/plot backup load [#]", "Backups"), // @@ -774,6 +776,7 @@ public enum Captions implements Caption { GENERIC_OTHER("other", "Generic"), GENERIC_MERGED("merged", "Generic"), GENERIC_UNOWNED("unowned", "Generic"), + GENERIC_INVALID_CHOICE("invalid choice", "Generic"), // /** From f7d6ac00e46b6a9f811c2e12f824128660eca933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 19:20:11 +0200 Subject: [PATCH 13/22] Make the backup commands work --- .../core/backup/PlayerBackupProfile.java | 24 ++++++++++++++--- .../com/plotsquared/core/command/Backup.java | 27 ++++++++++++++++--- .../core/configuration/Captions.java | 3 +++ .../com/plotsquared/core/plot/PlotId.java | 3 +++ 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index b1d7f43a9..0b551fcd6 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -41,6 +41,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -106,8 +107,24 @@ public class PlayerBackupProfile implements BackupProfile { } @NotNull public Path getBackupDirectory() { - return backupManager.getBackupPath().resolve(plot.getArea().getId()) - .resolve(plot.getId().toCommaSeparatedString()).resolve(owner.toString()); + return resolve(resolve(resolve(backupManager.getBackupPath(), Objects.requireNonNull(plot.getArea().toString(), "plot area id")), + Objects.requireNonNull(plot.getId().toDashSeparatedString(), "plot id")), Objects.requireNonNull(owner.toString(), "owner")); + } + + private static Path resolve(@NotNull final Path parent, final String child) { + Path path = parent; + try { + if (!Files.exists(parent)) { + Files.createDirectory(parent); + } + path = parent.resolve(child); + if (!Files.exists(path)) { + Files.createDirectory(path); + } + } catch (final Exception e) { + e.printStackTrace(); + } + return path; } @Override @NotNull public CompletableFuture createBackup() { @@ -118,7 +135,8 @@ public class PlayerBackupProfile implements BackupProfile { backups.get(backups.size() - 1).delete(); } final List plots = Collections.singletonList(plot); - final boolean result = SchematicHandler.manager.exportAll(plots, null, null, () -> + final boolean result = SchematicHandler.manager.exportAll(plots, getBackupDirectory().toFile(), + "%world%-%id%-%owner%-" + System.currentTimeMillis(), () -> future.complete(new Backup(this, System.currentTimeMillis(), null))); if (!result) { future.completeExceptionally(new RuntimeException("Failed to complete the backup")); diff --git a/Core/src/main/java/com/plotsquared/core/command/Backup.java b/Core/src/main/java/com/plotsquared/core/command/Backup.java index 60cba6d6c..948722e32 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Backup.java +++ b/Core/src/main/java/com/plotsquared/core/command/Backup.java @@ -37,6 +37,10 @@ import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; import java.nio.file.Files; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -77,11 +81,11 @@ public final class Backup extends Command { @Override public Collection tab(PlotPlayer player, String[] args, boolean space) { if (args.length == 1) { - return Stream.of("save", "list", "save") + return Stream.of("save", "list", "load") .filter(value -> value.startsWith(args[0].toLowerCase(Locale.ENGLISH))) .map(value -> new Command(null, false, value, "", RequiredType.NONE, null) {}) .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("load") && args.length == 2) { + } else if (args[0].equalsIgnoreCase("load")) { final Plot plot = player.getCurrentPlot(); if (plot != null) { @@ -129,6 +133,7 @@ public final class Backup extends Command { backupProfile.createBackup().whenComplete((backup, throwable) -> { if (throwable != null) { sendMessage(player, Captions.BACKUP_SAVE_FAILED, throwable.getMessage()); + throwable.printStackTrace(); } else { sendMessage(player, Captions.BACKUP_SAVE_SUCCESS); } @@ -161,7 +166,22 @@ public final class Backup extends Command { sendMessage(player, Captions.BACKUP_IMPOSSIBLE, Captions.GENERIC_OTHER.getTranslated()); } else { backupProfile.listBackups().whenComplete((backups, throwable) -> { - // TODO: List backups + if (throwable != null) { + sendMessage(player, Captions.BACKUP_LIST_FAILED, throwable.getMessage()); + throwable.printStackTrace(); + } else { + sendMessage(player, Captions.BACKUP_LIST_HEADER, plot.getId().toCommaSeparatedString()); + try { + for (int i = 0; i < backups.size(); i++) { + sendMessage(player, Captions.BACKUP_LIST_ENTRY, Integer.toString(i + 1), + DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime + .ofInstant(Instant.ofEpochMilli(backups.get(i).getCreationTime()), ZoneId + .systemDefault()))); + } + } catch (final Exception e) { + e.printStackTrace(); + } + } }); } } @@ -203,6 +223,7 @@ public final class Backup extends Command { backupProfile.listBackups().whenComplete((backups, throwable) -> { if (throwable != null) { sendMessage(player, Captions.BACKUP_LOAD_FAILURE, throwable.getMessage()); + throwable.printStackTrace(); } else { if (number < 1 || number > backups.size()) { sendMessage(player, Captions.BACKUP_LOAD_FAILURE, Captions.GENERIC_INVALID_CHOICE.getTranslated()); diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index 3d8897629..5626d877b 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -756,6 +756,9 @@ public enum Captions implements Caption { BACKUP_LOAD_SUCCESS("$1The backup was restored successfully", "Backups"), BACKUP_LOAD_FAILURE("$2The backup could not be restored: %s", "Backups"), BACKUP_LOAD_USAGE("$1Usage: $2/plot backup load [#]", "Backups"), + BACKUP_LIST_HEADER("$1Available backups for plot $2%s", "Backups"), + BACKUP_LIST_ENTRY("$3- $1#%s0 $2%s1", "Backups"), + BACKUP_LIST_FAILED("$2Backup listing failed: %s", "Backups"), // // diff --git a/Core/src/main/java/com/plotsquared/core/plot/PlotId.java b/Core/src/main/java/com/plotsquared/core/plot/PlotId.java index 3421de833..146c311c6 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/PlotId.java +++ b/Core/src/main/java/com/plotsquared/core/plot/PlotId.java @@ -198,6 +198,9 @@ public class PlotId { return this.x + "," + this.y; } + public String toDashSeparatedString() { + return this.x + "-" + this.y; + } /** From c4a70c0945c0870463fe4d38a76220c5f0dc48f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 19:42:42 +0200 Subject: [PATCH 14/22] Fix tab completion for the backup command --- .../java/com/plotsquared/core/backup/PlayerBackupProfile.java | 2 +- Core/src/main/java/com/plotsquared/core/command/Backup.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index 0b551fcd6..232602694 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -95,7 +95,7 @@ public class PlayerBackupProfile implements BackupProfile { } catch (IOException e) { e.printStackTrace(); } - return backups; + return (this.backupCache = backups); }); } } diff --git a/Core/src/main/java/com/plotsquared/core/command/Backup.java b/Core/src/main/java/com/plotsquared/core/command/Backup.java index 948722e32..9040735af 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Backup.java +++ b/Core/src/main/java/com/plotsquared/core/command/Backup.java @@ -90,7 +90,7 @@ public final class Backup extends Command { final Plot plot = player.getCurrentPlot(); if (plot != null) { final BackupProfile backupProfile = Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(plot); - if (!(backupProfile instanceof PlayerBackupProfile)) { + if (backupProfile instanceof PlayerBackupProfile) { final CompletableFuture> backupList = backupProfile.listBackups(); if (backupList.isDone()) { final List backups = backupList.getNow(new ArrayList<>()); From e0c9a802d87a7830fcf0ffa37d0ff198e74f7475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 20:31:07 +0200 Subject: [PATCH 15/22] Add automatic backups and implement them `/plot clear` and `/plot set `. --- .../core/backup/BackupManager.java | 25 ++++++-- .../core/backup/NullBackupManager.java | 8 ++- .../core/backup/PlayerBackupProfile.java | 2 + .../core/backup/SimpleBackupManager.java | 27 +++++++-- .../com/plotsquared/core/command/Clear.java | 59 ++++++++++--------- .../com/plotsquared/core/command/Set.java | 16 +++-- .../core/configuration/Captions.java | 3 + 7 files changed, 95 insertions(+), 45 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java index bfa4ad37f..4ba4a44f4 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupManager.java @@ -25,14 +25,30 @@ */ package com.plotsquared.core.backup; +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; +import java.util.Objects; public interface BackupManager { + /** + * This will perform an automatic backup of the plot iff the plot has an owner, + * automatic backups are enabled and the plot is not merged. + * Otherwise it will complete immediately. + * + * @param player Player that triggered the backup + * @param plot Plot to perform the automatic backup on + * @param whenDone Action that runs when the automatic backup has been completed + */ + static void backup(@Nullable PlotPlayer player, @NotNull final Plot plot, @NotNull Runnable whenDone) { + Objects.requireNonNull(PlotSquared.imp()).getBackupManager().automaticBackup(player, plot, whenDone); + } + /** * Get the backup profile for a plot based on its * current owner (if there is one) @@ -47,10 +63,11 @@ public interface BackupManager { * automatic backups are enabled and the plot is not merged. * Otherwise it will complete immediately. * - * @param plot Plot to perform the automatic backup on - * @return Future that completes when the backup is finished + * @param player Player that triggered the backup + * @param plot Plot to perform the automatic backup on + * @param whenDone Action that runs when the automatic backup has been completed */ - @NotNull CompletableFuture automaticBackup(@NotNull final Plot plot); + void automaticBackup(@Nullable PlotPlayer player, @NotNull final Plot plot, @NotNull Runnable whenDone); /** * Get the directory in which backups are stored diff --git a/Core/src/main/java/com/plotsquared/core/backup/NullBackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/NullBackupManager.java index d5f9bd954..2d8c04a76 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/NullBackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/NullBackupManager.java @@ -26,12 +26,13 @@ package com.plotsquared.core.backup; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.nio.file.Path; import java.util.Objects; -import java.util.concurrent.CompletableFuture; /** * {@inheritDoc} @@ -42,8 +43,9 @@ public class NullBackupManager implements BackupManager { return new NullBackupProfile(); } - @Override @NotNull public CompletableFuture automaticBackup(@NotNull Plot plot) { - return CompletableFuture.completedFuture(null); + @Override public void automaticBackup(@Nullable PlotPlayer plotPlayer, + @NotNull Plot plot, @NotNull Runnable whenDone) { + whenDone.run(); } @Override @NotNull public Path getBackupPath() { diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index 232602694..4bbc0a76b 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -40,6 +40,7 @@ import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -95,6 +96,7 @@ public class PlayerBackupProfile implements BackupProfile { } catch (IOException e) { e.printStackTrace(); } + backups.sort(Comparator.comparingLong(Backup::getCreationTime).reversed()); return (this.backupCache = backups); }); } diff --git a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java index 4c206b9ad..4f4a514bc 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java @@ -28,17 +28,20 @@ package com.plotsquared.core.backup; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.util.task.TaskManager; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -75,12 +78,28 @@ import java.util.concurrent.TimeUnit; return new NullBackupProfile(); } - @Override @NotNull public CompletableFuture automaticBackup(@NotNull final Plot plot) { + @Override public void automaticBackup(@Nullable PlotPlayer player, @NotNull final Plot plot, @NotNull Runnable whenDone) { final BackupProfile profile; if (!this.shouldAutomaticallyBackup() || (profile = getProfile(plot)) instanceof NullBackupProfile) { - return CompletableFuture.completedFuture(null); + whenDone.run(); + } else { + if (player != null) { + Captions.BACKUP_AUTOMATIC_STARTED.send(player); + } + profile.createBackup().whenComplete((backup, throwable) -> { + if (throwable != null) { + if (player != null) { + Captions.BACKUP_AUTOMATIC_FAILURE.send(player, throwable.getMessage()); + } + throwable.printStackTrace(); + } else { + if (player != null) { + Captions.BACKUP_AUTOMATIC_FINISHED.send(player); + TaskManager.runTaskAsync(whenDone); + } + } + }); } - return profile.createBackup(); } @Override public boolean shouldAutomaticallyBackup() { diff --git a/Core/src/main/java/com/plotsquared/core/command/Clear.java b/Core/src/main/java/com/plotsquared/core/command/Clear.java index cc8ab64ab..2af1c4ed5 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Clear.java +++ b/Core/src/main/java/com/plotsquared/core/command/Clear.java @@ -26,6 +26,7 @@ package com.plotsquared.core.command; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.backup.BackupManager; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.events.PlotFlagRemoveEvent; @@ -82,38 +83,40 @@ public class Clear extends Command { checkTrue(force || !Settings.Done.RESTRICT_BUILDING || !DoneFlag.isDone(plot) || Permissions .hasPermission(player, Captions.PERMISSION_CONTINUE), Captions.DONE_ALREADY_DONE); confirm.run(this, () -> { - final long start = System.currentTimeMillis(); - boolean result = plot.clear(true, false, () -> { - plot.unlink(); - GlobalBlockQueue.IMP.addEmptyTask(() -> { - plot.removeRunning(); - // If the state changes, then mark it as no longer done - if (DoneFlag.isDone(plot)) { - PlotFlag plotFlag = plot.getFlagContainer().getFlag(DoneFlag.class); - PlotFlagRemoveEvent event = - PlotSquared.get().getEventDispatcher().callFlagRemove(plotFlag, plot); - if (event.getEventResult() != Result.DENY) { - plot.removeFlag(event.getFlag()); + BackupManager.backup(player, plot, () -> { + final long start = System.currentTimeMillis(); + boolean result = plot.clear(true, false, () -> { + plot.unlink(); + GlobalBlockQueue.IMP.addEmptyTask(() -> { + plot.removeRunning(); + // If the state changes, then mark it as no longer done + if (DoneFlag.isDone(plot)) { + PlotFlag plotFlag = plot.getFlagContainer().getFlag(DoneFlag.class); + PlotFlagRemoveEvent event = + PlotSquared.get().getEventDispatcher().callFlagRemove(plotFlag, plot); + if (event.getEventResult() != Result.DENY) { + plot.removeFlag(event.getFlag()); + } } - } - if (!plot.getFlag(AnalysisFlag.class).isEmpty()) { - PlotFlag plotFlag = - plot.getFlagContainer().getFlag(AnalysisFlag.class); - PlotFlagRemoveEvent event = - PlotSquared.get().getEventDispatcher().callFlagRemove(plotFlag, plot); - if (event.getEventResult() != Result.DENY) { - plot.removeFlag(event.getFlag()); + if (!plot.getFlag(AnalysisFlag.class).isEmpty()) { + PlotFlag plotFlag = + plot.getFlagContainer().getFlag(AnalysisFlag.class); + PlotFlagRemoveEvent event = + PlotSquared.get().getEventDispatcher().callFlagRemove(plotFlag, plot); + if (event.getEventResult() != Result.DENY) { + plot.removeFlag(event.getFlag()); + } } - } - MainUtil.sendMessage(player, Captions.CLEARING_DONE, - "" + (System.currentTimeMillis() - start)); + MainUtil.sendMessage(player, Captions.CLEARING_DONE, + "" + (System.currentTimeMillis() - start)); + }); }); + if (!result) { + MainUtil.sendMessage(player, Captions.WAIT_FOR_TIMER); + } else { + plot.addRunning(); + } }); - if (!result) { - MainUtil.sendMessage(player, Captions.WAIT_FOR_TIMER); - } else { - plot.addRunning(); - } }, null); return CompletableFuture.completedFuture(true); } diff --git a/Core/src/main/java/com/plotsquared/core/command/Set.java b/Core/src/main/java/com/plotsquared/core/command/Set.java index ca301a2fa..700c0854a 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Set.java +++ b/Core/src/main/java/com/plotsquared/core/command/Set.java @@ -25,6 +25,7 @@ */ package com.plotsquared.core.command; +import com.plotsquared.core.backup.BackupManager; import com.plotsquared.core.configuration.CaptionUtility; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.player.PlotPlayer; @@ -91,12 +92,15 @@ public class Set extends SubCommand { MainUtil.sendMessage(player, Captions.WAIT_FOR_TIMER); return false; } - plot.addRunning(); - for (Plot current : plot.getConnectedPlots()) { - current.setComponent(component, pattern); - } - MainUtil.sendMessage(player, Captions.GENERATING_COMPONENT); - GlobalBlockQueue.IMP.addEmptyTask(plot::removeRunning); + + BackupManager.backup(player, plot, () -> { + plot.addRunning(); + for (Plot current : plot.getConnectedPlots()) { + current.setComponent(component, pattern); + } + MainUtil.sendMessage(player, Captions.GENERATING_COMPONENT); + GlobalBlockQueue.IMP.addEmptyTask(plot::removeRunning); + }); return true; } } diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index 5626d877b..509082221 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -759,6 +759,9 @@ public enum Captions implements Caption { BACKUP_LIST_HEADER("$1Available backups for plot $2%s", "Backups"), BACKUP_LIST_ENTRY("$3- $1#%s0 $2%s1", "Backups"), BACKUP_LIST_FAILED("$2Backup listing failed: %s", "Backups"), + BACKUP_AUTOMATIC_STARTED("$1Backing up the plot...", "Backups"), + BACKUP_AUTOMATIC_FINISHED("$1The automatic backup process finished successfully!", "Backups"), + BACKUP_AUTOMATIC_FAILURE("$2The automatic backup process failed. Your pending action has been canceled. Reason: %s", "Backups"), // // From 658361f825c1eb9e1fde940428a36072105e5dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 21:31:16 +0200 Subject: [PATCH 16/22] Destroy backups when the plot is uncalimed --- .../com/plotsquared/core/backup/BackupProfile.java | 3 +-- .../plotsquared/core/backup/PlayerBackupProfile.java | 12 ++++++++---- .../main/java/com/plotsquared/core/plot/Plot.java | 6 ++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java index 663d684f6..4d35519c4 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java @@ -27,7 +27,6 @@ package com.plotsquared.core.backup; import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -44,7 +43,7 @@ public interface BackupProfile { /** * Remove all backups stored for this profile */ - void destroy() throws IOException; + void destroy(); /** * Get the directory containing the backups for this profile. diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index 4bbc0a76b..8853f3dff 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -102,10 +102,14 @@ public class PlayerBackupProfile implements BackupProfile { } } - @Override public void destroy() throws IOException { - Files.delete(this.getBackupDirectory()); - // Invalidate backup cache - this.backupCache = null; + @Override public void destroy() { + this.listBackups().whenCompleteAsync((backups, error) -> { + if (error != null) { + error.printStackTrace(); + } + backups.forEach(Backup::delete); + this.backupCache = null; + }); } @NotNull public Path getBackupDirectory() { diff --git a/Core/src/main/java/com/plotsquared/core/plot/Plot.java b/Core/src/main/java/com/plotsquared/core/plot/Plot.java index 975c9becf..0a25e4cdd 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/Plot.java +++ b/Core/src/main/java/com/plotsquared/core/plot/Plot.java @@ -93,6 +93,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -1358,6 +1359,11 @@ public class Plot { for (PlotPlayer pp : players) { PlotListener.plotExit(pp, current); } + + // Destroy all backups when the plot is unclaimed + Objects.requireNonNull(PlotSquared.imp()).getBackupManager() + .getProfile(current).destroy(); + getArea().removePlot(getId()); DBFunc.delete(current); current.setOwnerAbs(null); From fc74c582bf3d9bf8c10117d0f42c5b25ce7730a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 21:52:57 +0200 Subject: [PATCH 17/22] Add option to disable deletion of plot backups when the plot is unclaimed --- .../java/com/plotsquared/core/configuration/Settings.java | 2 ++ Core/src/main/java/com/plotsquared/core/plot/Plot.java | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java index 6db5f8476..1e24cc1cf 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java @@ -420,6 +420,8 @@ public class Settings extends Config { public static boolean AUTOMATIC_BACKUPS = true; @Comment("Maximum amount of backups associated with a plot") public static int BACKUP_LIMIT = 3; + @Comment("Whether or not backups should be deleted when the plot is unclaimed") + public static boolean DELETE_ON_UNCLAIM = true; } } diff --git a/Core/src/main/java/com/plotsquared/core/plot/Plot.java b/Core/src/main/java/com/plotsquared/core/plot/Plot.java index 0a25e4cdd..e77861b94 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/Plot.java +++ b/Core/src/main/java/com/plotsquared/core/plot/Plot.java @@ -1360,9 +1360,10 @@ public class Plot { PlotListener.plotExit(pp, current); } - // Destroy all backups when the plot is unclaimed - Objects.requireNonNull(PlotSquared.imp()).getBackupManager() - .getProfile(current).destroy(); + if (Settings.Backup.DELETE_ON_UNCLAIM) { + // Destroy all backups when the plot is unclaimed + Objects.requireNonNull(PlotSquared.imp()).getBackupManager().getProfile(current).destroy(); + } getArea().removePlot(getId()); DBFunc.delete(current); From 702caa6febed04090f80ce32c39c90acf6bfd7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sun, 10 May 2020 22:09:56 +0200 Subject: [PATCH 18/22] Remove rogue line break --- Core/src/main/java/com/plotsquared/core/plot/PlotId.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/main/java/com/plotsquared/core/plot/PlotId.java b/Core/src/main/java/com/plotsquared/core/plot/PlotId.java index 146c311c6..41016da1f 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/PlotId.java +++ b/Core/src/main/java/com/plotsquared/core/plot/PlotId.java @@ -202,7 +202,6 @@ public class PlotId { return this.x + "-" + this.y; } - /** * The PlotId object caches the hashcode for faster mapping/fetching/sorting
* - Recalculation is required if the x/y values change From 282773a43fb772fe57febc26c28c19d9a37e05d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 11 May 2020 20:02:33 +0200 Subject: [PATCH 19/22] Replace Guava methods with stdlib equivalents --- .../com/plotsquared/core/backup/SimpleBackupManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java index 4f4a514bc..57cbea1d8 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java @@ -118,13 +118,13 @@ import java.util.concurrent.TimeUnit; return false; } final PlotCacheKey that = (PlotCacheKey) o; - return com.google.common.base.Objects.equal(plot.getArea(), that.plot.getArea()) - && com.google.common.base.Objects.equal(plot.getId(), that.plot.getId()) - && com.google.common.base.Objects.equal(plot.getOwnerAbs(), that.plot.getOwnerAbs()); + return Objects.equals(plot.getArea(), that.plot.getArea()) + && Objects.equals(plot.getId(), that.plot.getId()) + && Objects.equals(plot.getOwnerAbs(), that.plot.getOwnerAbs()); } @Override public int hashCode() { - return com.google.common.base.Objects.hashCode(plot.getArea(), plot.getId(), plot.getOwnerAbs()); + return Objects.hash(plot.getArea(), plot.getId(), plot.getOwnerAbs()); } } From f7167d5e832c3a131169af685ef851cc509f1c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 11 May 2020 20:03:26 +0200 Subject: [PATCH 20/22] Fix typo --- .../main/java/com/plotsquared/core/configuration/Captions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index 5b3fe5184..58b1638ca 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -186,7 +186,7 @@ public enum Captions implements Caption { PERMISSION_ADMIN_CHAT_BYPASS("plots.admin.chat.bypass", "static.permissions"), PERMISSION_BACKUP("plots.backup", "static.permissions"), PERMISSION_BACKUP_SAVE("plots.backup.save", "static.permissions"), - PERMISSION_BAKCUP_LIST("plots.backup.list", "static.permissions"), + PERMISSION_BACKUP_LIST("plots.backup.list", "static.permissions"), PERMISSION_BACKUP_LOAD("plots.backup.load", "static.permissions"), PERMISSION_ADMIN_BACKUP_OTHER("plots.admin.backup.other", "static.permissions"), //
From d40160d205e3a2de4318776048241850bd52571f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 11 May 2020 20:04:22 +0200 Subject: [PATCH 21/22] Fix javadoc --- .../main/java/com/plotsquared/core/backup/BackupProfile.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java index 4d35519c4..6664246e4 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java @@ -64,7 +64,8 @@ public interface BackupProfile { /** * Restore a backup * - * @return Backup to restore + * @param backup Backup to restore + * @return Future that completes when the backup has finished */ @NotNull CompletableFuture restoreBackup(@NotNull final Backup backup); From b2bc2887d2543b1d967b223da2570c80eddfc513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 11 May 2020 20:17:41 +0200 Subject: [PATCH 22/22] Fix dumdum merge --- .../core/configuration/Captions.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index c7c65b58d..1e0e50080 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -751,6 +751,29 @@ public enum Captions implements Caption { false, "Info"), // + // + BACKUP_USAGE("$1Usage: $2/plot backup save/list/load", "Backups"), + BACKUP_IMPOSSIBLE("$2Backups are not enabled for this plot: %s", "Backups"), + BACKUP_SAVE_SUCCESS("$1The backup was created successfully", "Backups"), + BACKUP_SAVE_FAILED("$2The backup could not be created: %s", "Backups"), + BACKUP_LOAD_SUCCESS("$1The backup was restored successfully", "Backups"), + BACKUP_LOAD_FAILURE("$2The backup could not be restored: %s", "Backups"), + BACKUP_LOAD_USAGE("$1Usage: $2/plot backup load [#]", "Backups"), + BACKUP_LIST_HEADER("$1Available backups for plot $2%s", "Backups"), + BACKUP_LIST_ENTRY("$3- $1#%s0 $2%s1", "Backups"), + BACKUP_LIST_FAILED("$2Backup listing failed: %s", "Backups"), + BACKUP_AUTOMATIC_STARTED("$1Backing up the plot...", "Backups"), + BACKUP_AUTOMATIC_FINISHED("$1The automatic backup process finished successfully!", "Backups"), + BACKUP_AUTOMATIC_FAILURE("$2The automatic backup process failed. Your pending action has been canceled. Reason: %s", "Backups"), + // + + // + GENERIC_OTHER("other", "Generic"), + GENERIC_MERGED("merged", "Generic"), + GENERIC_UNOWNED("unowned", "Generic"), + GENERIC_INVALID_CHOICE("invalid choice", "Generic"), + // + /** * Legacy Configuration Conversion */