Add storages command

This commit is contained in:
Lukas Rieger (Blue) 2023-05-01 20:41:30 +02:00
parent 0fce08dd62
commit 004f296b5e
No known key found for this signature in database
GPG Key ID: 2D09EC5ED2687FF2
9 changed files with 287 additions and 65 deletions

View File

@ -38,15 +38,13 @@
import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.tree.LiteralCommandNode; 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.Plugin;
import de.bluecolored.bluemap.common.plugin.PluginState; import de.bluecolored.bluemap.common.plugin.PluginState;
import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.plugin.text.Text;
import de.bluecolored.bluemap.common.plugin.text.TextColor; import de.bluecolored.bluemap.common.plugin.text.TextColor;
import de.bluecolored.bluemap.common.plugin.text.TextFormat; import de.bluecolored.bluemap.common.plugin.text.TextFormat;
import de.bluecolored.bluemap.common.rendermanager.MapPurgeTask; import de.bluecolored.bluemap.common.rendermanager.*;
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.serverinterface.CommandSource; import de.bluecolored.bluemap.common.serverinterface.CommandSource;
import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.MinecraftVersion;
@ -54,15 +52,13 @@
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.map.MapRenderState; 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.Block;
import de.bluecolored.bluemap.core.world.World; import de.bluecolored.bluemap.core.world.World;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -88,26 +84,22 @@ public Commands(Plugin plugin, CommandDispatcher<S> dispatcher, Function<S, Comm
public void init() { public void init() {
// commands // commands
LiteralCommandNode<S> baseCommand = LiteralCommandNode<S> baseCommand = literal("bluemap")
literal("bluemap")
.requires(requirementsUnloaded("bluemap.status")) .requires(requirementsUnloaded("bluemap.status"))
.executes(this::statusCommand) .executes(this::statusCommand)
.build(); .build();
LiteralCommandNode<S> versionCommand = LiteralCommandNode<S> versionCommand = literal("version")
literal("version")
.requires(requirementsUnloaded("bluemap.version")) .requires(requirementsUnloaded("bluemap.version"))
.executes(this::versionCommand) .executes(this::versionCommand)
.build(); .build();
LiteralCommandNode<S> helpCommand = LiteralCommandNode<S> helpCommand = literal("help")
literal("help")
.requires(requirementsUnloaded("bluemap.help")) .requires(requirementsUnloaded("bluemap.help"))
.executes(this::helpCommand) .executes(this::helpCommand)
.build(); .build();
LiteralCommandNode<S> reloadCommand = LiteralCommandNode<S> reloadCommand = literal("reload")
literal("reload")
.requires(requirementsUnloaded("bluemap.reload")) .requires(requirementsUnloaded("bluemap.reload"))
.executes(context -> this.reloadCommand(context, false)) .executes(context -> this.reloadCommand(context, false))
@ -116,8 +108,7 @@ public void init() {
.build(); .build();
LiteralCommandNode<S> debugCommand = LiteralCommandNode<S> debugCommand = literal("debug")
literal("debug")
.requires(requirementsUnloaded("bluemap.debug")) .requires(requirementsUnloaded("bluemap.debug"))
.then(literal("block") .then(literal("block")
@ -147,27 +138,23 @@ public void init() {
.build(); .build();
LiteralCommandNode<S> stopCommand = LiteralCommandNode<S> stopCommand = literal("stop")
literal("stop")
.requires(requirements("bluemap.stop")) .requires(requirements("bluemap.stop"))
.executes(this::stopCommand) .executes(this::stopCommand)
.build(); .build();
LiteralCommandNode<S> startCommand = LiteralCommandNode<S> startCommand = literal("start")
literal("start")
.requires(requirements("bluemap.start")) .requires(requirements("bluemap.start"))
.executes(this::startCommand) .executes(this::startCommand)
.build(); .build();
LiteralCommandNode<S> freezeCommand = LiteralCommandNode<S> freezeCommand = literal("freeze")
literal("freeze")
.requires(requirements("bluemap.freeze")) .requires(requirements("bluemap.freeze"))
.then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin)) .then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin))
.executes(this::freezeCommand)) .executes(this::freezeCommand))
.build(); .build();
LiteralCommandNode<S> unfreezeCommand = LiteralCommandNode<S> unfreezeCommand = literal("unfreeze")
literal("unfreeze")
.requires(requirements("bluemap.freeze")) .requires(requirements("bluemap.freeze"))
.then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin)) .then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin))
.executes(this::unfreezeCommand)) .executes(this::unfreezeCommand))
@ -187,33 +174,42 @@ public void init() {
this::updateCommand this::updateCommand
).build(); ).build();
LiteralCommandNode<S> purgeCommand = LiteralCommandNode<S> purgeCommand = literal("purge")
literal("purge")
.requires(requirements("bluemap.purge")) .requires(requirements("bluemap.purge"))
.then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin)) .then(argument("map", StringArgumentType.string()).suggests(new MapSuggestionProvider<>(plugin))
.executes(this::purgeCommand)) .executes(this::purgeCommand))
.build(); .build();
LiteralCommandNode<S> cancelCommand = LiteralCommandNode<S> cancelCommand = literal("cancel")
literal("cancel")
.requires(requirements("bluemap.cancel")) .requires(requirements("bluemap.cancel"))
.executes(this::cancelCommand) .executes(this::cancelCommand)
.then(argument("task-ref", StringArgumentType.string()).suggests(new TaskRefSuggestionProvider<>(helper)) .then(argument("task-ref", StringArgumentType.string()).suggests(new TaskRefSuggestionProvider<>(helper))
.executes(this::cancelCommand)) .executes(this::cancelCommand))
.build(); .build();
LiteralCommandNode<S> worldsCommand = LiteralCommandNode<S> worldsCommand = literal("worlds")
literal("worlds")
.requires(requirements("bluemap.status")) .requires(requirements("bluemap.status"))
.executes(this::worldsCommand) .executes(this::worldsCommand)
.build(); .build();
LiteralCommandNode<S> mapsCommand = LiteralCommandNode<S> mapsCommand = literal("maps")
literal("maps")
.requires(requirements("bluemap.status")) .requires(requirements("bluemap.status"))
.executes(this::mapsCommand) .executes(this::mapsCommand)
.build(); .build();
LiteralCommandNode<S> 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 // command tree
dispatcher.getRoot().addChild(baseCommand); dispatcher.getRoot().addChild(baseCommand);
baseCommand.addChild(versionCommand); baseCommand.addChild(versionCommand);
@ -230,6 +226,7 @@ public void init() {
baseCommand.addChild(purgeCommand); baseCommand.addChild(purgeCommand);
baseCommand.addChild(worldsCommand); baseCommand.addChild(worldsCommand);
baseCommand.addChild(mapsCommand); baseCommand.addChild(mapsCommand);
baseCommand.addChild(storagesCommand);
} }
private <B extends ArgumentBuilder<S, B>> B addRenderArguments(B builder, Command<S> command) { private <B extends ArgumentBuilder<S, B>> B addRenderArguments(B builder, Command<S> command) {
@ -862,4 +859,91 @@ public int mapsCommand(CommandContext<S> context) {
return 1; return 1;
} }
public int storagesCommand(CommandContext<S> 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<S> 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<String> 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, " <empty storage>"));
} 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<S> 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;
}
} }

View File

@ -0,0 +1,44 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* 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<S> extends AbstractSuggestionProvider<S> {
private final Plugin plugin;
public StorageSuggestionProvider(Plugin plugin) {
this.plugin = plugin;
}
@Override
public Collection<String> getPossibleValues() {
return plugin.getBlueMap().getConfigs().getStorageConfigs().keySet();
}
}

View File

@ -28,7 +28,7 @@
public class TaskRefSuggestionProvider<S> extends AbstractSuggestionProvider<S> { public class TaskRefSuggestionProvider<S> extends AbstractSuggestionProvider<S> {
private CommandHelper helper; private final CommandHelper helper;
public TaskRefSuggestionProvider(CommandHelper helper) { public TaskRefSuggestionProvider(CommandHelper helper) {
this.helper = helper; this.helper = helper;

View File

@ -24,16 +24,15 @@
*/ */
package de.bluecolored.bluemap.common.plugin.commands; 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.common.plugin.Plugin;
import de.bluecolored.bluemap.core.world.World; import de.bluecolored.bluemap.core.world.World;
import java.util.Collection;
import java.util.HashSet;
public class WorldOrMapSuggestionProvider<S> extends AbstractSuggestionProvider<S> { public class WorldOrMapSuggestionProvider<S> extends AbstractSuggestionProvider<S> {
private Plugin plugin; private final Plugin plugin;
public WorldOrMapSuggestionProvider(Plugin plugin) { public WorldOrMapSuggestionProvider(Plugin plugin) {
this.plugin = plugin; this.plugin = plugin;

View File

@ -50,6 +50,7 @@ public void doWork() throws Exception {
if (!this.hasMoreWork) return; if (!this.hasMoreWork) return;
this.hasMoreWork = false; this.hasMoreWork = false;
} }
if (this.cancelled) return;
// save lowres-tile-manager to clear/flush any buffered data // save lowres-tile-manager to clear/flush any buffered data
this.map.getLowresTileManager().save(); this.map.getLowresTileManager().save();
@ -97,7 +98,7 @@ public boolean contains(RenderTask task) {
@Override @Override
public String getDescription() { public String getDescription() {
return "Purge Map " + map.getId(); return "Purge map " + map.getId();
} }
} }

View File

@ -0,0 +1,96 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* 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;
}
}

View File

@ -58,8 +58,6 @@ public abstract class Storage implements Closeable {
public abstract Collection<String> collectMapIds() throws IOException; public abstract Collection<String> collectMapIds() throws IOException;
public abstract long estimateMapSize(String mapId) throws IOException;
public MapStorage mapStorage(final String mapId) { public MapStorage mapStorage(final String mapId) {
return new MapStorage(mapId); return new MapStorage(mapId);
} }

View File

@ -29,7 +29,6 @@
import de.bluecolored.bluemap.core.storage.*; import de.bluecolored.bluemap.core.storage.*;
import de.bluecolored.bluemap.core.util.DeletingPathVisitor; import de.bluecolored.bluemap.core.util.DeletingPathVisitor;
import de.bluecolored.bluemap.core.util.FileHelper; import de.bluecolored.bluemap.core.util.FileHelper;
import de.bluecolored.bluemap.core.util.SizeCollectingPathVisitor;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
@ -227,13 +226,6 @@ public Collection<String> collectMapIds() throws IOException {
} }
} }
@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){ public Path getFilePath(String mapId, int lod, Vector2i tile){
String path = "x" + tile.getX() + "z" + tile.getY(); String path = "x" + tile.getX() + "z" + tile.getY();
char[] cs = path.toCharArray(); char[] cs = path.toCharArray();

View File

@ -417,12 +417,20 @@ public void purgeMap(String mapId, Function<ProgressInfo, Boolean> onProgress) t
@Override @Override
public Collection<String> collectMapIds() throws IOException { public Collection<String> collectMapIds() throws IOException {
return Collections.emptyList(); //TODO try {
return recoveringConnection(connection -> {
ResultSet result = executeQuery(connection,
"SELECT `map_id` FROM `bluemap_map`"
);
Collection<String> mapIds = new ArrayList<>();
while (result.next()) {
mapIds.add(result.getString("map_id"));
}
return mapIds;
}, 2);
} catch (SQLException ex) {
throw new IOException(ex);
} }
@Override
public long estimateMapSize(String mapId) throws IOException {
return 0; //TODO
} }
@SuppressWarnings("UnusedAssignment") @SuppressWarnings("UnusedAssignment")