From 004f296b5edc0125e36595bf416b678efa3f1559 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Mon, 1 May 2023 20:41:30 +0200 Subject: [PATCH] Add storages command --- .../common/plugin/commands/Commands.java | 168 +++++++++++++----- .../commands/StorageSuggestionProvider.java | 44 +++++ .../commands/TaskRefSuggestionProvider.java | 2 +- .../WorldOrMapSuggestionProvider.java | 9 +- .../common/rendermanager/MapPurgeTask.java | 3 +- .../rendermanager/StorageDeleteTask.java | 96 ++++++++++ .../bluemap/core/storage/Storage.java | 2 - .../core/storage/file/FileStorage.java | 8 - .../bluemap/core/storage/sql/SQLStorage.java | 20 ++- 9 files changed, 287 insertions(+), 65 deletions(-) create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/StorageSuggestionProvider.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/StorageDeleteTask.java diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java index 854dc7fe..291fd4dd 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java @@ -38,15 +38,13 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.tree.LiteralCommandNode; +import de.bluecolored.bluemap.common.config.ConfigurationException; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.PluginState; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.plugin.text.TextColor; import de.bluecolored.bluemap.common.plugin.text.TextFormat; -import de.bluecolored.bluemap.common.rendermanager.MapPurgeTask; -import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask; -import de.bluecolored.bluemap.common.rendermanager.RenderTask; -import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask; +import de.bluecolored.bluemap.common.rendermanager.*; import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -54,15 +52,13 @@ import de.bluecolored.bluemap.core.debug.StateDumper; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.map.MapRenderState; +import de.bluecolored.bluemap.core.storage.Storage; import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.World; import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.function.Function; import java.util.function.Predicate; @@ -88,26 +84,22 @@ public class Commands { public void init() { // commands - LiteralCommandNode baseCommand = - literal("bluemap") + LiteralCommandNode baseCommand = literal("bluemap") .requires(requirementsUnloaded("bluemap.status")) .executes(this::statusCommand) .build(); - LiteralCommandNode versionCommand = - literal("version") + LiteralCommandNode versionCommand = literal("version") .requires(requirementsUnloaded("bluemap.version")) .executes(this::versionCommand) .build(); - LiteralCommandNode helpCommand = - literal("help") + LiteralCommandNode helpCommand = literal("help") .requires(requirementsUnloaded("bluemap.help")) .executes(this::helpCommand) .build(); - LiteralCommandNode reloadCommand = - literal("reload") + LiteralCommandNode reloadCommand = literal("reload") .requires(requirementsUnloaded("bluemap.reload")) .executes(context -> this.reloadCommand(context, false)) @@ -116,8 +108,7 @@ public class Commands { .build(); - LiteralCommandNode debugCommand = - literal("debug") + LiteralCommandNode debugCommand = literal("debug") .requires(requirementsUnloaded("bluemap.debug")) .then(literal("block") @@ -147,31 +138,27 @@ public class Commands { .build(); - LiteralCommandNode stopCommand = - literal("stop") + LiteralCommandNode stopCommand = literal("stop") .requires(requirements("bluemap.stop")) .executes(this::stopCommand) .build(); - LiteralCommandNode startCommand = - literal("start") + LiteralCommandNode startCommand = literal("start") .requires(requirements("bluemap.start")) .executes(this::startCommand) .build(); - LiteralCommandNode freezeCommand = - literal("freeze") + LiteralCommandNode freezeCommand = literal("freeze") .requires(requirements("bluemap.freeze")) .then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin)) .executes(this::freezeCommand)) .build(); - LiteralCommandNode unfreezeCommand = - literal("unfreeze") - .requires(requirements("bluemap.freeze")) - .then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin)) - .executes(this::unfreezeCommand)) - .build(); + LiteralCommandNode unfreezeCommand = literal("unfreeze") + .requires(requirements("bluemap.freeze")) + .then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin)) + .executes(this::unfreezeCommand)) + .build(); LiteralCommandNode forceUpdateCommand = addRenderArguments( @@ -187,33 +174,42 @@ public class Commands { this::updateCommand ).build(); - LiteralCommandNode purgeCommand = - literal("purge") - .requires(requirements("bluemap.purge")) - .then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin)) - .executes(this::purgeCommand)) - .build(); + LiteralCommandNode purgeCommand = literal("purge") + .requires(requirements("bluemap.purge")) + .then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin)) + .executes(this::purgeCommand)) + .build(); - LiteralCommandNode cancelCommand = - literal("cancel") + LiteralCommandNode cancelCommand = literal("cancel") .requires(requirements("bluemap.cancel")) .executes(this::cancelCommand) .then(argument("task-ref", StringArgumentType.string()).suggests(new TaskRefSuggestionProvider<>(helper)) .executes(this::cancelCommand)) .build(); - LiteralCommandNode worldsCommand = - literal("worlds") + LiteralCommandNode worldsCommand = literal("worlds") .requires(requirements("bluemap.status")) .executes(this::worldsCommand) .build(); - LiteralCommandNode mapsCommand = - literal("maps") + LiteralCommandNode mapsCommand = literal("maps") .requires(requirements("bluemap.status")) .executes(this::mapsCommand) .build(); + LiteralCommandNode storagesCommand = literal("storages") + .requires(requirements("bluemap.status")) + .executes(this::storagesCommand) + + .then(argument("storage", StringArgumentType.string()).suggests(new StorageSuggestionProvider<>(plugin)) + .executes(this::storagesInfoCommand) + + .then(literal("delete") + .then(argument("map", StringArgumentType.string()) + .executes(this::storagesDeleteMapCommand)))) + + .build(); + // command tree dispatcher.getRoot().addChild(baseCommand); baseCommand.addChild(versionCommand); @@ -230,6 +226,7 @@ public class Commands { baseCommand.addChild(purgeCommand); baseCommand.addChild(worldsCommand); baseCommand.addChild(mapsCommand); + baseCommand.addChild(storagesCommand); } private > B addRenderArguments(B builder, Command command) { @@ -862,4 +859,91 @@ public class Commands { return 1; } + public int storagesCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + source.sendMessage(Text.of(TextColor.BLUE, "Storages loaded by BlueMap:")); + for (var entry : plugin.getBlueMap().getConfigs().getStorageConfigs().entrySet()) { + source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, entry.getKey()) + .setHoverText(Text.of(entry.getValue().getStorageType().name())) + .setClickAction(Text.ClickAction.RUN_COMMAND, "/bluemap storages " + entry.getKey()) + ); + } + + return 1; + } + + public int storagesInfoCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + String storageId = context.getArgument("storage", String.class); + + Storage storage; + try { + storage = plugin.getBlueMap().getStorage(storageId); + } catch (ConfigurationException ex) { + source.sendMessage(Text.of(TextColor.RED, ex.getMessage())); + return 0; + } + + Collection mapIds; + try { + mapIds = storage.collectMapIds(); + } catch (IOException ex) { + source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to access this storage. Please check the console for more details...")); + Logger.global.logError("Unexpected exception trying to load mapId's from storage '" + storageId + "'!", ex); + return 0; + } + + source.sendMessage(Text.of(TextColor.BLUE, "Storage '", storageId, "':")); + if (mapIds.isEmpty()) { + source.sendMessage(Text.of(TextColor.GRAY, " ")); + } else { + for (String mapId : mapIds) { + BmMap map = plugin.getMaps().get(mapId); + boolean isLoaded = map != null && map.getStorage().equals(storage); + + if (isLoaded) { + source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, mapId, TextColor.GREEN, TextFormat.ITALIC, " (loaded)")); + } else { + source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, mapId, TextColor.DARK_GRAY, TextFormat.ITALIC, " (unloaded/static/remote)")); + } + } + } + + return 1; + } + + public int storagesDeleteMapCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + String storageId = context.getArgument("storage", String.class); + String mapId = context.getArgument("map", String.class); + + Storage storage; + try { + storage = plugin.getBlueMap().getStorage(storageId); + } catch (ConfigurationException ex) { + source.sendMessage(Text.of(TextColor.RED, ex.getMessage())); + return 0; + } + + BmMap map = plugin.getMaps().get(mapId); + boolean isLoaded = map != null && map.getStorage().equals(storage); + if (isLoaded) { + Text purgeCommand = Text.of(TextColor.WHITE, "/bluemap purge " + mapId) + .setClickAction(Text.ClickAction.SUGGEST_COMMAND, "/bluemap purge " + mapId); + source.sendMessage(Text.of(TextColor.RED, "Can't delete a loaded map!\n" + + "Unload the map by removing it's config-file first,\n" + + "or use ", purgeCommand, " if you want to purge it.")); + return 0; + } + + // delete map + StorageDeleteTask deleteTask = new StorageDeleteTask(storage, mapId); + + plugin.getRenderManager().scheduleRenderTaskNext(deleteTask); + source.sendMessage(Text.of(TextColor.GREEN, "Created new Task to delete map '" + mapId + "' from storage '" + storageId + "'")); + + return 1; + } + } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/StorageSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/StorageSuggestionProvider.java new file mode 100644 index 00000000..7e73d5b7 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/StorageSuggestionProvider.java @@ -0,0 +1,44 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.common.plugin.commands; + +import de.bluecolored.bluemap.common.plugin.Plugin; + +import java.util.Collection; + +public class StorageSuggestionProvider extends AbstractSuggestionProvider { + + private final Plugin plugin; + + public StorageSuggestionProvider(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public Collection getPossibleValues() { + return plugin.getBlueMap().getConfigs().getStorageConfigs().keySet(); + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/TaskRefSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/TaskRefSuggestionProvider.java index 237410b3..2c6b8247 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/TaskRefSuggestionProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/TaskRefSuggestionProvider.java @@ -28,7 +28,7 @@ import java.util.Collection; public class TaskRefSuggestionProvider extends AbstractSuggestionProvider { - private CommandHelper helper; + private final CommandHelper helper; public TaskRefSuggestionProvider(CommandHelper helper) { this.helper = helper; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java index 3ff603ee..4035c560 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java @@ -24,16 +24,15 @@ */ package de.bluecolored.bluemap.common.plugin.commands; -import java.util.Collection; -import java.util.HashSet; - -import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.core.world.World; +import java.util.Collection; +import java.util.HashSet; + public class WorldOrMapSuggestionProvider extends AbstractSuggestionProvider { - private Plugin plugin; + private final Plugin plugin; public WorldOrMapSuggestionProvider(Plugin plugin) { this.plugin = plugin; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapPurgeTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapPurgeTask.java index bdd25283..e071fd52 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapPurgeTask.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapPurgeTask.java @@ -50,6 +50,7 @@ public class MapPurgeTask implements RenderTask { if (!this.hasMoreWork) return; this.hasMoreWork = false; } + if (this.cancelled) return; // save lowres-tile-manager to clear/flush any buffered data this.map.getLowresTileManager().save(); @@ -97,7 +98,7 @@ public class MapPurgeTask implements RenderTask { @Override public String getDescription() { - return "Purge Map " + map.getId(); + return "Purge map " + map.getId(); } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/StorageDeleteTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/StorageDeleteTask.java new file mode 100644 index 00000000..7a9b26dc --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/StorageDeleteTask.java @@ -0,0 +1,96 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.common.rendermanager; + +import de.bluecolored.bluemap.api.debug.DebugDump; +import de.bluecolored.bluemap.core.storage.Storage; + +import java.util.Objects; + +public class StorageDeleteTask implements RenderTask { + + private final Storage storage; + private final String mapId; + + private volatile double progress; + private volatile boolean hasMoreWork; + private volatile boolean cancelled; + + public StorageDeleteTask(Storage storage, String mapId) { + this.storage = Objects.requireNonNull(storage); + this.mapId = Objects.requireNonNull(mapId); + this.progress = 0d; + this.hasMoreWork = true; + this.cancelled = false; + } + + @Override + public void doWork() throws Exception { + synchronized (this) { + if (!this.hasMoreWork) return; + this.hasMoreWork = false; + } + if (this.cancelled) return; + + // purge the map + storage.purgeMap(mapId, progressInfo -> { + this.progress = progressInfo.getProgress(); + return !this.cancelled; + }); + } + + @Override + public boolean hasMoreWork() { + return this.hasMoreWork && !this.cancelled; + } + + @Override + @DebugDump + public double estimateProgress() { + return this.progress; + } + + @Override + public void cancel() { + this.cancelled = true; + } + + @Override + public boolean contains(RenderTask task) { + if (task == this) return true; + if (task instanceof StorageDeleteTask) { + StorageDeleteTask sTask = (StorageDeleteTask) task; + return storage.equals(sTask.storage) && mapId.equals(sTask.mapId); + } + + return false; + } + + @Override + public String getDescription() { + return "Delete map " + mapId; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/Storage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/Storage.java index 0152c141..cb1bcf39 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/Storage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/Storage.java @@ -58,8 +58,6 @@ public abstract class Storage implements Closeable { public abstract Collection collectMapIds() throws IOException; - public abstract long estimateMapSize(String mapId) throws IOException; - public MapStorage mapStorage(final String mapId) { return new MapStorage(mapId); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java index de1daaba..74c0244f 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java @@ -29,7 +29,6 @@ import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.core.storage.*; import de.bluecolored.bluemap.core.util.DeletingPathVisitor; import de.bluecolored.bluemap.core.util.FileHelper; -import de.bluecolored.bluemap.core.util.SizeCollectingPathVisitor; import java.io.*; import java.nio.file.Files; @@ -227,13 +226,6 @@ public class FileStorage extends Storage { } } - @Override - public long estimateMapSize(String mapId) throws IOException { - SizeCollectingPathVisitor visitor = new SizeCollectingPathVisitor(); - Files.walkFileTree(getFilePath(mapId), visitor); - return visitor.getSize(); - } - public Path getFilePath(String mapId, int lod, Vector2i tile){ String path = "x" + tile.getX() + "z" + tile.getY(); char[] cs = path.toCharArray(); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java index d84d52c4..2ec91c36 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java @@ -417,12 +417,20 @@ public class SQLStorage extends Storage { @Override public Collection collectMapIds() throws IOException { - return Collections.emptyList(); //TODO - } - - @Override - public long estimateMapSize(String mapId) throws IOException { - return 0; //TODO + try { + return recoveringConnection(connection -> { + ResultSet result = executeQuery(connection, + "SELECT `map_id` FROM `bluemap_map`" + ); + Collection mapIds = new ArrayList<>(); + while (result.next()) { + mapIds.add(result.getString("map_id")); + } + return mapIds; + }, 2); + } catch (SQLException ex) { + throw new IOException(ex); + } } @SuppressWarnings("UnusedAssignment")