From a246da133c26bd5a7ed53fb45ab45f664bdf53f5 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Sun, 10 May 2020 01:28:09 +0200 Subject: [PATCH] Rework commands --- BlueMapBukkit/build.gradle | 3 +- .../bluemap/bukkit/BukkitCommandSource.java | 52 +- .../bluemap/bukkit/BukkitCommands.java | 270 +++-------- .../bluemap/bukkit/BukkitPlugin.java | 25 +- BlueMapBukkit/src/main/resources/plugin.yml | 12 +- BlueMapCommon/build.gradle | 3 + .../bluemap/common/plugin/Commands.java | 400 --------------- .../bluemap/common/plugin/Plugin.java | 6 - .../commands/AbstractSuggestionProvider.java | 55 +++ .../common/plugin/commands/CommandHelper.java | 185 +++++++ .../common/plugin/commands/Commands.java | 457 ++++++++++++++++++ .../commands/MapSuggestionProvider.java | 52 ++ .../WorldOrMapSuggestionProvider.java | 57 +++ .../commands/WorldSuggestionProvider.java | 52 ++ .../plugin/serverinterface/CommandSource.java | 15 + BlueMapForge/build.gradle | 1 + .../bluemap/forge/ForgeCommandSource.java | 5 + .../bluemap/forge/ForgeCommands.java | 231 --------- .../bluecolored/bluemap/forge/ForgeMod.java | 16 +- BlueMapSponge/build.gradle | 1 + .../bluemap/sponge/SpongeCommandSource.java | 36 +- .../bluemap/sponge/SpongeCommands.java | 282 +++++------ .../bluemap/sponge/SpongePlugin.java | 8 +- build.gradle | 3 + 24 files changed, 1198 insertions(+), 1029 deletions(-) delete mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Commands.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/AbstractSuggestionProvider.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldSuggestionProvider.java delete mode 100644 BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeCommands.java diff --git a/BlueMapBukkit/build.gradle b/BlueMapBukkit/build.gradle index 3639e3e3..279f8461 100644 --- a/BlueMapBukkit/build.gradle +++ b/BlueMapBukkit/build.gradle @@ -1,4 +1,4 @@ -repositories { +repositories { maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' @@ -31,6 +31,7 @@ build.dependsOn shadowJar { relocate 'org.apache.commons.io', 'de.bluecolored.shadow.apache.commons.io' relocate 'org.apache.commons.lang3', 'de.bluecolored.shadow.apache.commons.lang3' relocate 'org.bstats.bukkit', 'de.bluecolored.shadow.bstats.bukkit' + relocate 'com.mojang.brigadier', 'de.bluecolored.shadow.mojang.brigadier' } processResources { diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java index 386652ae..db44c40f 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java @@ -24,18 +24,29 @@ */ package de.bluecolored.bluemap.bukkit; +import java.util.Optional; + import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import com.flowpowered.math.vector.Vector3d; + +import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.core.world.World; public class BukkitCommandSource implements CommandSource { + private Plugin plugin; private CommandSender delegate; - public BukkitCommandSource(CommandSender delegate) { + public BukkitCommandSource(Plugin plugin, CommandSender delegate) { + this.plugin = plugin; this.delegate = delegate; } @@ -53,5 +64,44 @@ public void sendMessage(Text text) { delegate.sendMessage(text.toPlainString()); }); } + + @Override + public boolean hasPermission(String permission) { + return delegate.hasPermission(permission); + } + + @Override + public Optional getPosition() { + Location location = getLocation(); + + if (location != null) { + return Optional.of(new Vector3d(location.getX(), location.getY(), location.getZ())); + } + + return Optional.empty(); + } + + @Override + public Optional getWorld() { + Location location = getLocation(); + + if (location != null) { + return Optional.ofNullable(plugin.getWorld(location.getWorld().getUID())); + } + + return Optional.empty(); + } + + private Location getLocation() { + Location location = null; + if (delegate instanceof Entity) { + location = ((Entity) delegate).getLocation(); + } + if (delegate instanceof BlockCommandSender) { + location = ((BlockCommandSender) delegate).getBlock().getLocation(); + } + + return location; + } } diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java index d49d259d..8779020e 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java @@ -25,221 +25,93 @@ package de.bluecolored.bluemap.bukkit; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.UUID; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.CommandExecutor; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; +import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.TabCompleteEvent; -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestion; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.tree.CommandNode; -import de.bluecolored.bluemap.common.plugin.Commands; -import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; -import de.bluecolored.bluemap.common.plugin.text.Text; -import de.bluecolored.bluemap.common.plugin.text.TextColor; +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.commands.Commands; -public class BukkitCommands implements CommandExecutor { +public class BukkitCommands implements Listener { + + private CommandDispatcher dispatcher; - private Commands bluemapCommands; - - private Collection commands; - - public BukkitCommands(Commands commands) { - this.bluemapCommands = commands; - this.commands = new ArrayList<>(); - initCommands(); + public BukkitCommands(final Plugin plugin) { + this.dispatcher = new CommandDispatcher<>(); + + // register commands + new Commands<>(plugin, dispatcher, bukkitSender -> new BukkitCommandSource(plugin, bukkitSender)); } - private void initCommands() { + public Collection getRootCommands(){ + Collection rootCommands = new ArrayList<>(); - commands.add(new Command("bluemap.status") { - @Override - public boolean execute(CommandSender sender, CommandSource source, String[] args) { - if (args.length != 0) return false; - - bluemapCommands.executeRootCommand(source); - return true; - } - }); - - commands.add(new Command("bluemap.reload", "reload") { - @Override - public boolean execute(CommandSender sender, CommandSource source, String[] args) { - if (args.length != 0) return false; - - bluemapCommands.executeReloadCommand(source); - return true; - } - }); - - commands.add(new Command("bluemap.pause", "pause") { - @Override - public boolean execute(CommandSender sender, CommandSource source, String[] args) { - if (args.length != 0) return false; - - bluemapCommands.executePauseCommand(source); - return true; - } - }); - - commands.add(new Command("bluemap.resume", "resume") { - @Override - public boolean execute(CommandSender sender, CommandSource source, String[] args) { - if (args.length != 0) return false; - - bluemapCommands.executeResumeCommand(source); - return true; - } - }); - - commands.add(new Command("bluemap.render", "render") { - @Override - public boolean execute(CommandSender sender, CommandSource source, String[] args) { - if (sender instanceof Player) { - if (args.length > 2) return false; - Player player = (Player) sender; - - World world = null; - int radius = -1; - if (args.length >= 1) { - world = Bukkit.getWorld(args[0]); - } - if (args.length == 2 || (args.length == 1 && world == null)) { - try { - radius = Integer.parseInt(args[args.length - 1]); - } catch (NumberFormatException ex) { - return false; - } - } - if (world == null){ - world = player.getWorld(); - } - - if (radius >= 0) { - Vector2i pos = new Vector2i(player.getLocation().getBlockX(), player.getLocation().getBlockZ()); - bluemapCommands.executeRenderWorldCommand(source, world.getUID(), pos, radius); - } else { - bluemapCommands.executeRenderWorldCommand(source, world.getUID()); - } - return true; - } else { - if (args.length != 1) return false; - World world = Bukkit.getWorld(args[0]); - - bluemapCommands.executeRenderWorldCommand(source, world.getUID()); - return true; - } - } - }); - - commands.add(new Command("bluemap.render", "render", "prioritize") { - @Override - public boolean execute(CommandSender sender, CommandSource source, String[] args) { - if (args.length != 1) return false; - - try { - UUID uuid = UUID.fromString(args[0]); - bluemapCommands.executePrioritizeRenderTaskCommand(source, uuid); - return true; - } catch (IllegalArgumentException ex) { - source.sendMessage(Text.of(TextColor.RED, "'" + args[0] + "' is not a valid UUID!")); - return true; - } - } - }); - - commands.add(new Command("bluemap.render", "render", "remove") { - @Override - public boolean execute(CommandSender sender, CommandSource source, String[] args) { - if (args.length != 1) return false; - - try { - UUID uuid = UUID.fromString(args[0]); - bluemapCommands.executeRemoveRenderTaskCommand(source, uuid); - return true; - } catch (IllegalArgumentException ex) { - source.sendMessage(Text.of(TextColor.RED, "'" + args[0] + "' is not a valid UUID!")); - return true; - } - } - }); - - commands.add(new Command("bluemap.debug", "debug") { - @Override - public boolean execute(CommandSender sender, CommandSource source, String[] args) { - if (!(sender instanceof Player)) { - source.sendMessage(Text.of(TextColor.RED, "You have to be a player to use this command!")); - return true; - } - - Player player = (Player) sender; - UUID world = player.getWorld().getUID(); - Vector3i pos = new Vector3i( - player.getLocation().getBlockX(), - player.getLocation().getBlockY(), - player.getLocation().getBlockZ() - ); - - bluemapCommands.executeDebugCommand(source, world, pos); - return true; - } - }); + for (CommandNode node : this.dispatcher.getRoot().getChildren()) { + rootCommands.add(new CommandProxy(node.getName())); + } + return rootCommands; } + + @EventHandler + public void onTabComplete(TabCompleteEvent evt) { + try { + Suggestions suggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(evt.getBuffer().substring(1), evt.getSender())).get(100, TimeUnit.MILLISECONDS); + List completions = new ArrayList<>(); + for (Suggestion suggestion : suggestions.getList()) { + String text = suggestion.getText(); - @Override - public boolean onCommand(CommandSender sender, org.bukkit.command.Command bukkitCommand, String label, String[] args) { - int max = -1; - Command maxCommand = null; - for (Command command : commands) { - int matchSize = command.matches(args); - if (matchSize > max) { - maxCommand = command; - max = matchSize; - } - } - - if (maxCommand == null) return false; - - BukkitCommandSource source = new BukkitCommandSource(sender); - - if (!maxCommand.checkPermission(sender)) { - source.sendMessage(Text.of(TextColor.RED, "You don't have permission to use this command!")); - return true; - } - - return maxCommand.execute(sender, source, Arrays.copyOfRange(args, max, args.length)); - } - - private abstract class Command { - - private String[] command; - private String permission; - - public Command(String permission, String... command) { - this.command = command; - } - - public abstract boolean execute(CommandSender sender, CommandSource source, String[] args); - - public int matches(String[] args) { - if (args.length < command.length) return -1; - - for (int i = 0; i < command.length; i++) { - if (!args[i].equalsIgnoreCase(command[i])) return -1; + if (text.indexOf(' ') == -1) { + completions.add(text); + } } - return command.length; + if (!completions.isEmpty()) { + completions.sort((s1, s2) -> s1.compareToIgnoreCase(s2)); + evt.setCompletions(completions); + } + } catch (InterruptedException | ExecutionException | TimeoutException ignore) {} + } + + private class CommandProxy extends BukkitCommand { + + protected CommandProxy(String name) { + super(name); } - - public boolean checkPermission(CommandSender sender) { - if (sender.isOp()) return true; - return sender.hasPermission(permission); + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + String command = commandLabel; + if (args.length > 0) { + command += " " + StringUtils.join(args, ' '); + } + + try { + return dispatcher.execute(command, sender) > 0; + } catch (CommandSyntaxException ex) { + sender.sendMessage(ChatColor.RED + ex.getRawMessage().getString()); + + String context = ex.getContext(); + if (context != null) sender.sendMessage(ChatColor.GRAY + context); + + return false; + } } } diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index ee63b137..5a9971c0 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -34,6 +35,8 @@ import org.bstats.bukkit.MetricsLite; import org.bukkit.Bukkit; import org.bukkit.World; +import org.bukkit.command.CommandMap; +import org.bukkit.command.defaults.BukkitCommand; import org.bukkit.plugin.java.JavaPlugin; import de.bluecolored.bluemap.common.plugin.Plugin; @@ -54,7 +57,7 @@ public BukkitPlugin() { this.eventForwarder = new EventForwarder(); this.bluemap = new Plugin("bukkit", this); - this.commands = new BukkitCommands(bluemap.getCommands()); + this.commands = new BukkitCommands(this.bluemap); BukkitPlugin.instance = this; } @@ -69,9 +72,27 @@ public void onEnable() { world.save(); } + //register events getServer().getPluginManager().registerEvents(eventForwarder, this); - getCommand("bluemap").setExecutor(commands); + //register commands + try { + final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + + bukkitCommandMap.setAccessible(true); + CommandMap commandMap = (CommandMap) bukkitCommandMap.get(Bukkit.getServer()); + + for (BukkitCommand command : commands.getRootCommands()) { + commandMap.register(command.getLabel(), command); + } + } catch(NoSuchFieldException | SecurityException | IllegalAccessException e) { + Logger.global.logError("Failed to register commands!", e); + } + + //tab completions + getServer().getPluginManager().registerEvents(commands, this); + + //load bluemap getServer().getScheduler().runTaskAsynchronously(this, () -> { try { Logger.global.logInfo("Loading..."); diff --git a/BlueMapBukkit/src/main/resources/plugin.yml b/BlueMapBukkit/src/main/resources/plugin.yml index 5a0be95a..80e030be 100644 --- a/BlueMapBukkit/src/main/resources/plugin.yml +++ b/BlueMapBukkit/src/main/resources/plugin.yml @@ -5,17 +5,7 @@ version: ${version} author: "Blue (TBlueF / Lukas Rieger)" authors: [Blue (TBlueF / Lukas Rieger)] website: "https://github.com/BlueMap-Minecraft" -commands: - bluemap: - description: Root command for all bluemap commands - permission: bluemap - usage: | - / - / reload - / pause - / resume - / render [world] [block-radius] - / debug +commands: permissions: bluemap.*: children: diff --git a/BlueMapCommon/build.gradle b/BlueMapCommon/build.gradle index de0ea2ca..c70b48c1 100644 --- a/BlueMapCommon/build.gradle +++ b/BlueMapCommon/build.gradle @@ -1,4 +1,7 @@ dependencies { + compile 'com.mojang:brigadier:1.0.17' + compile project(':BlueMapCore') compile project(':BlueMapAPI') } + diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Commands.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Commands.java deleted file mode 100644 index afbc1c9a..00000000 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Commands.java +++ /dev/null @@ -1,400 +0,0 @@ -/* - * 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; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; -import java.util.function.Predicate; - -import org.apache.commons.lang3.time.DurationFormatUtils; - -import com.flowpowered.math.GenericMath; -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; -import com.google.common.collect.Lists; - -import de.bluecolored.bluemap.common.MapType; -import de.bluecolored.bluemap.common.RenderManager; -import de.bluecolored.bluemap.common.RenderTask; -import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; -import de.bluecolored.bluemap.common.plugin.text.Text; -import de.bluecolored.bluemap.common.plugin.text.TextColor; -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.mca.Chunk; -import de.bluecolored.bluemap.core.mca.ChunkAnvil112; -import de.bluecolored.bluemap.core.mca.MCAWorld; -import de.bluecolored.bluemap.core.render.hires.HiresModelManager; -import de.bluecolored.bluemap.core.world.Block; -import de.bluecolored.bluemap.core.world.World; - -/** - * Commands: - * - *
    - *
  • /bluemap
  • - *
  • /bluemap reload
  • - *
  • /bluemap pause
  • - *
  • /bluemap resume
  • - *
  • /bluemap render [world]
  • - *
  • /bluemap render prioritize [task-uuid]
  • - *
  • /bluemap render remove [task-uuid]
  • - *
  • /bluemap debug
  • - *
- */ -public class Commands { - - private Plugin bluemap; - - public Commands(Plugin bluemap) { - this.bluemap = bluemap; - } - - /** - * Command: /bluemap - */ - public void executeRootCommand(CommandSource source) { - if (!checkLoaded(source)) return; - - source.sendMessages(createStatusMessage()); - } - - /** - * Command: /bluemap debug - */ - public boolean executeDebugCommand(CommandSource source, UUID worldUuid, Vector3i playerPosition) { - if (!checkLoaded(source)) return false; - - World world = bluemap.getWorld(worldUuid); - - if (world == null) { - source.sendMessage(Text.of(TextColor.RED, "This world is not loaded with BlueMap! Maybe it is not configured?")); - return false; - } - - Block block = world.getBlock(playerPosition); - Block blockBelow = world.getBlock(playerPosition.add(0, -1, 0)); - - String blockIdMeta = ""; - String blockBelowIdMeta = ""; - - if (world instanceof MCAWorld) { - try { - Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(playerPosition)); - if (chunk instanceof ChunkAnvil112) { - blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(playerPosition) + ")"; - blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(playerPosition.add(0, -1, 0)) + ")"; - } - } catch (IOException ex) { - Logger.global.logError("Failed to read chunk for debug!", ex); - } - } - - source.sendMessages(Lists.newArrayList( - Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block, TextColor.GRAY, blockIdMeta), - Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta) - )); - - return true; - } - - /** - * Command: /bluemap reload - */ - public void executeReloadCommand(CommandSource source) { - source.sendMessage(Text.of(TextColor.GOLD, "Reloading BlueMap...")); - - new Thread(() -> { - try { - bluemap.reload(); - - if (bluemap.isLoaded()) { - source.sendMessage(Text.of(TextColor.GREEN, "BlueMap reloaded!")); - } else { - source.sendMessage(Text.of(TextColor.RED, "Could not load BlueMap! See the console for details!")); - } - - } catch (Exception ex) { - Logger.global.logError("Failed to reload BlueMap!", ex); - - source.sendMessage(Text.of(TextColor.RED, "There was an error reloading BlueMap! See the console for details!")); - } - }).start(); - - } - - /** - * Command: /bluemap pause - */ - public boolean executePauseCommand(CommandSource source) { - if (!checkLoaded(source)) return false; - - if (bluemap.getRenderManager().isRunning()) { - bluemap.getRenderManager().stop(); - source.sendMessage(Text.of(TextColor.GREEN, "BlueMap rendering paused!")); - return true; - } else { - source.sendMessage(Text.of(TextColor.RED, "BlueMap rendering are already paused!")); - return false; - } - } - - /** - * Command: /bluemap resume - */ - public boolean executeResumeCommand(CommandSource source) { - if (!checkLoaded(source)) return false; - - if (!bluemap.getRenderManager().isRunning()) { - bluemap.getRenderManager().start(); - source.sendMessage(Text.of(TextColor.GREEN, "BlueMap renders resumed!")); - return true; - } else { - source.sendMessage(Text.of(TextColor.RED, "BlueMap renders are already running!")); - return false; - } - } - - /** - * Command: /bluemap render [world,map] - */ - public boolean executeRenderCommand(CommandSource source, String mapOrWorld) { - return executeRenderCommand(source, mapOrWorld, null, -1); - } - - /** - * Command: /bluemap render [world,map] [block-radius] - */ - public boolean executeRenderCommand(CommandSource source, String mapOrWorld, Vector2i center, int blockRadius) { - if (!checkLoaded(source)) return false; - - MapType map = null; - World world = null; - for (MapType m : bluemap.getMapTypes()) { - if (mapOrWorld.equalsIgnoreCase(m.getId()) || mapOrWorld.equalsIgnoreCase(m.getName())){ - map = m; - world = map.getWorld(); - break; - } - } - - if (world == null) { - for (World w : bluemap.getWorlds()) { - if (mapOrWorld.equalsIgnoreCase(w.getName())){ - world = w; - break; - } - } - } - - if (world == null) { - source.sendMessage(Text.of(TextColor.RED, "Could not find a world or map with this name or id ", TextColor.GRAY, "('" + mapOrWorld + "')", TextColor.RED, "! Maybe it is not configured in BlueMap's config?")); - } - - world.invalidateChunkCache(); - - final World worldToRender = world; - final MapType mapToRender = map; - if (mapToRender == null) { - new Thread(() -> { - createWorldRenderTask(source, worldToRender, center, blockRadius); - }).start(); - } else { - new Thread(() -> { - createMapRenderTask(source, mapToRender, center, blockRadius); - }).start(); - } - - return true; - } - - /** - * Command: /bluemap render [world] - */ - public boolean executeRenderWorldCommand(CommandSource source, UUID worldUuid) { - return executeRenderWorldCommand(source, worldUuid, null, -1); - } - - /** - * Command: /bluemap render [world] [block-radius] - */ - public boolean executeRenderWorldCommand(CommandSource source, UUID worldUuid, Vector2i center, int blockRadius) { - if (!checkLoaded(source)) return false; - - World world = bluemap.getWorld(worldUuid); - - if (world == null) { - source.sendMessage(Text.of(TextColor.RED, "This world is not loaded with BlueMap! Maybe it is not configured in BlueMap's config?")); - return false; - } - - world.invalidateChunkCache(); - - new Thread(() -> { - createWorldRenderTask(source, world, center, blockRadius); - }).start(); - - return true; - } - - /** - * Command: /bluemap render prioritize [task-uuid] - */ - public void executePrioritizeRenderTaskCommand(CommandSource source, UUID taskUUID) { - if (!checkLoaded(source)) return; - - for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) { - if (task.getUuid().equals(taskUUID)) { - bluemap.getRenderManager().prioritizeRenderTask(task); - break; - } - } - - source.sendMessages(createStatusMessage()); - } - - /** - * Command: /bluemap render remove [task-uuid] - */ - public void executeRemoveRenderTaskCommand(CommandSource source, UUID taskUUID) { - if (!checkLoaded(source)) return; - - for (RenderTask task : bluemap.getRenderManager().getRenderTasks()) { - if (task.getUuid().equals(taskUUID)) { - bluemap.getRenderManager().removeRenderTask(task); - break; - } - } - - source.sendMessages(createStatusMessage()); - } - - private List createStatusMessage(){ - List lines = new ArrayList<>(); - - RenderManager renderer = bluemap.getRenderManager(); - - lines.add(Text.of()); - lines.add(Text.of(TextColor.BLUE, "Tile-Updates:")); - - if (renderer.isRunning()) { - lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.GREEN, "running").setHoverText(Text.of("click to pause rendering")).setClickCommand("/bluemap pause"), TextColor.GRAY, "!")); - } else { - lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.RED, "paused").setHoverText(Text.of("click to resume rendering")).setClickCommand("/bluemap resume"), TextColor.GRAY, "!")); - } - - lines.add(Text.of(TextColor.WHITE, " Scheduled tile-updates: ", Text.of(TextColor.GOLD, renderer.getQueueSize()).setHoverText(Text.of("tiles waiting for a free render-thread")), TextColor.GRAY, " + " , Text.of(TextColor.GRAY, bluemap.getUpdateHandler().getUpdateBufferCount()).setHoverText(Text.of("tiles waiting for world-save")))); - - RenderTask[] tasks = renderer.getRenderTasks(); - if (tasks.length > 0) { - RenderTask task = tasks[0]; - - long time = task.getActiveTime(); - String durationString = DurationFormatUtils.formatDurationWords(time, true, true); - double pct = (double)task.getRenderedTileCount() / (double)(task.getRenderedTileCount() + task.getRemainingTileCount()); - - long ert = (long)((time / pct) * (1d - pct)); - String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true); - - double tps = task.getRenderedTileCount() / (time / 1000.0); - - lines.add(Text.of(TextColor.BLUE, "Current task:")); - lines.add(Text.of(" ", createCancelTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())))); - lines.add(Text.of(TextColor.WHITE, " rendered ", TextColor.GOLD, task.getRenderedTileCount(), TextColor.WHITE, " tiles ", TextColor.GRAY, "(" + (Math.round(pct * 1000)/10.0) + "% | " + GenericMath.round(tps, 1) + "t/s)", TextColor.WHITE, " in ", TextColor.GOLD, durationString)); - lines.add(Text.of(TextColor.WHITE, " with ", TextColor.GOLD, task.getRemainingTileCount(), TextColor.WHITE, " tiles to go. ETA: ", TextColor.GOLD, ertDurationString)); - } - - if (tasks.length > 1) { - lines.add(Text.of(TextColor.BLUE, "Waiting tasks:")); - for (int i = 1; i < tasks.length; i++) { - RenderTask task = tasks[i]; - lines.add(Text.of(" ", createCancelTaskText(task), createPrioritizeTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())), TextColor.GRAY, " (" + task.getRemainingTileCount() + " tiles)")); - } - } - - return lines; - } - - private Text createCancelTaskText(RenderTask task) { - return Text.of(TextColor.RED, "[X]").setHoverText(Text.of(TextColor.GRAY, "click to remove this render-task")).setClickCommand("/bluemap render remove " + task.getUuid()); - } - - private Text createPrioritizeTaskText(RenderTask task) { - return Text.of(TextColor.GREEN, "[^]").setHoverText(Text.of(TextColor.GRAY, "click to prioritize this render-task")).setClickCommand("/bluemap render prioritize " + task.getUuid()); - } - - private void createWorldRenderTask(CommandSource source, World world, Vector2i center, long blockRadius) { - - for (MapType map : bluemap.getMapTypes()) { - if (!map.getWorld().getUUID().equals(world.getUUID())) continue; - - createMapRenderTask(source, map, center, blockRadius); - } - - source.sendMessage(Text.of(TextColor.GREEN, "All render tasks created! Use /bluemap to view the progress!")); - } - - private void createMapRenderTask(CommandSource source, MapType map, Vector2i center, long blockRadius) { - source.sendMessage(Text.of(TextColor.GOLD, "Creating render-task for map: " + map.getId())); - source.sendMessage(Text.of(TextColor.GOLD, "Collecting chunks...")); - - String taskName = "world-render"; - - Predicate filter; - if (center == null || blockRadius < 0) { - filter = c -> true; - } else { - filter = c -> c.mul(16).distanceSquared(center) <= blockRadius * blockRadius; - taskName = "radius-render"; - } - - Collection chunks = map.getWorld().getChunkList(filter); - - source.sendMessage(Text.of(TextColor.GREEN, chunks.size() + " chunks found!")); - source.sendMessage(Text.of(TextColor.GOLD, "Collecting tiles...")); - - HiresModelManager hmm = map.getTileRenderer().getHiresModelManager(); - Collection tiles = hmm.getTilesForChunks(chunks); - - RenderTask task = new RenderTask(taskName, map); - task.addTiles(tiles); - task.optimizeQueue(); - bluemap.getRenderManager().addRenderTask(task); - - source.sendMessage(Text.of(TextColor.GREEN, tiles.size() + " tiles found! Task created.")); - } - - private boolean checkLoaded(CommandSource source) { - if (!bluemap.isLoaded()) { - source.sendMessage(Text.of(TextColor.RED, "BlueMap is not loaded!", TextColor.GRAY, "(Try /bluemap reload)")); - return false; - } - - return true; - } - -} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java index 20229781..ed63a095 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java @@ -80,7 +80,6 @@ public class Plugin { private String implementationType; private ServerInterface serverInterface; - private Commands commands; private MainConfig config; private ResourcePack resourcePack; @@ -102,7 +101,6 @@ public Plugin(String implementationType, ServerInterface serverInterface) { this.implementationType = implementationType.toLowerCase(); this.serverInterface = serverInterface; - this.commands = new Commands(this); this.maps = new HashMap<>(); this.worlds = new HashMap<>(); @@ -393,10 +391,6 @@ public ServerInterface getServerInterface() { return serverInterface; } - public Commands getCommands() { - return commands; - } - public MainConfig getMainConfig() { return config; } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/AbstractSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/AbstractSuggestionProvider.java new file mode 100644 index 00000000..f07daa65 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/AbstractSuggestionProvider.java @@ -0,0 +1,55 @@ +/* + * 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 java.util.Collection; +import java.util.concurrent.CompletableFuture; + +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; + +public abstract class AbstractSuggestionProvider implements SuggestionProvider { + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { + Collection possibleValues = getPossibleValues(); + if(possibleValues.isEmpty()) return Suggestions.empty(); + + String remaining = builder.getRemaining().toLowerCase(); + for (String str : possibleValues) { + if (str.toLowerCase().startsWith(remaining)) { + builder.suggest(str); + } + } + + return builder.buildFuture(); + } + + public abstract Collection getPossibleValues(); + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java new file mode 100644 index 00000000..f69c62b9 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java @@ -0,0 +1,185 @@ +/* + * 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 java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Predicate; + +import org.apache.commons.lang3.time.DurationFormatUtils; + +import com.flowpowered.math.GenericMath; +import com.flowpowered.math.vector.Vector2i; + +import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.common.RenderManager; +import de.bluecolored.bluemap.common.RenderTask; +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.plugin.text.TextColor; +import de.bluecolored.bluemap.core.render.hires.HiresModelManager; +import de.bluecolored.bluemap.core.world.World; + +public class CommandHelper { + + private Plugin plugin; + + public CommandHelper(Plugin plugin) { + this.plugin = plugin; + } + + public List createStatusMessage(){ + List lines = new ArrayList<>(); + + RenderManager renderer = plugin.getRenderManager(); + + lines.add(Text.of()); + lines.add(Text.of(TextColor.BLUE, "Tile-Updates:")); + + if (renderer.isRunning()) { + lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.GREEN, "running").setHoverText(Text.of("click to pause rendering")).setClickCommand("/bluemap pause"), TextColor.GRAY, "!")); + } else { + lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.RED, "paused").setHoverText(Text.of("click to resume rendering")).setClickCommand("/bluemap resume"), TextColor.GRAY, "!")); + } + + lines.add(Text.of(TextColor.WHITE, " Scheduled tile-updates: ", Text.of(TextColor.GOLD, renderer.getQueueSize()).setHoverText(Text.of("tiles waiting for a free render-thread")), TextColor.GRAY, " + " , Text.of(TextColor.GRAY, plugin.getUpdateHandler().getUpdateBufferCount()).setHoverText(Text.of("tiles waiting for world-save")))); + + RenderTask[] tasks = renderer.getRenderTasks(); + if (tasks.length > 0) { + RenderTask task = tasks[0]; + + long time = task.getActiveTime(); + String durationString = DurationFormatUtils.formatDurationWords(time, true, true); + double pct = (double)task.getRenderedTileCount() / (double)(task.getRenderedTileCount() + task.getRemainingTileCount()); + + long ert = (long)((time / pct) * (1d - pct)); + String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true); + + double tps = task.getRenderedTileCount() / (time / 1000.0); + + lines.add(Text.of(TextColor.BLUE, "Current task:")); + lines.add(Text.of(" ", createCancelTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())))); + lines.add(Text.of(TextColor.WHITE, " rendered ", TextColor.GOLD, task.getRenderedTileCount(), TextColor.WHITE, " tiles ", TextColor.GRAY, "(" + (Math.round(pct * 1000)/10.0) + "% | " + GenericMath.round(tps, 1) + "t/s)", TextColor.WHITE, " in ", TextColor.GOLD, durationString)); + lines.add(Text.of(TextColor.WHITE, " with ", TextColor.GOLD, task.getRemainingTileCount(), TextColor.WHITE, " tiles to go. ETA: ", TextColor.GOLD, ertDurationString)); + } + + if (tasks.length > 1) { + lines.add(Text.of(TextColor.BLUE, "Waiting tasks:")); + for (int i = 1; i < tasks.length; i++) { + RenderTask task = tasks[i]; + lines.add(Text.of(" ", createCancelTaskText(task), createPrioritizeTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())), TextColor.GRAY, " (" + task.getRemainingTileCount() + " tiles)")); + } + } + + return lines; + } + + private Text createCancelTaskText(RenderTask task) { + return Text.of(TextColor.RED, "[X]").setHoverText(Text.of(TextColor.GRAY, "click to cancel this render-task")).setClickCommand("/bluemap render cancel " + task.getUuid()); + } + + private Text createPrioritizeTaskText(RenderTask task) { + return Text.of(TextColor.GREEN, "[^]").setHoverText(Text.of(TextColor.GRAY, "click to prioritize this render-task")).setClickCommand("/bluemap render prioritize " + task.getUuid()); + } + + public void createWorldRenderTask(CommandSource source, World world, Vector2i center, long blockRadius) { + + for (MapType map : plugin.getMapTypes()) { + if (!map.getWorld().getUUID().equals(world.getUUID())) continue; + + createMapRenderTask(source, map, center, blockRadius); + } + + source.sendMessage(Text.of(TextColor.GREEN, "All render tasks created! Use /bluemap to view the progress!")); + } + + public void createMapRenderTask(CommandSource source, MapType map, Vector2i center, long blockRadius) { + source.sendMessage(Text.of(TextColor.GOLD, "Creating render-task for map: " + map.getId())); + source.sendMessage(Text.of(TextColor.GOLD, "Collecting chunks...")); + + String taskName = "world-render"; + + Predicate filter; + if (center == null || blockRadius < 0) { + filter = c -> true; + } else { + filter = c -> c.mul(16).distanceSquared(center) <= blockRadius * blockRadius; + taskName = "radius-render"; + } + + Collection chunks = map.getWorld().getChunkList(filter); + + source.sendMessage(Text.of(TextColor.GREEN, chunks.size() + " chunks found!")); + source.sendMessage(Text.of(TextColor.GOLD, "Collecting tiles...")); + + HiresModelManager hmm = map.getTileRenderer().getHiresModelManager(); + Collection tiles = hmm.getTilesForChunks(chunks); + + RenderTask task = new RenderTask(taskName, map); + task.addTiles(tiles); + task.optimizeQueue(); + plugin.getRenderManager().addRenderTask(task); + + source.sendMessage(Text.of(TextColor.GREEN, tiles.size() + " tiles found! Task created.")); + } + + public Text worldHelperHover() { + StringJoiner joiner = new StringJoiner("\n"); + for (World world : plugin.getWorlds()) { + joiner.add(world.getName()); + } + + return Text.of("world").setHoverText(Text.of(TextColor.WHITE, "Available worlds: \n", TextColor.GRAY, joiner.toString())); + } + + public Text mapHelperHover() { + StringJoiner joiner = new StringJoiner("\n"); + for (MapType map : plugin.getMapTypes()) { + joiner.add(map.getId()); + } + + return Text.of("map").setHoverText(Text.of(TextColor.WHITE, "Available maps: \n", TextColor.GRAY, joiner.toString())); + } + + public boolean checkLoaded(CommandSource source) { + if (!plugin.isLoaded()) { + source.sendMessage(Text.of(TextColor.RED, "BlueMap is not loaded!", TextColor.GRAY, "(Try /bluemap reload)")); + return false; + } + + return true; + } + + public boolean checkPermission(CommandSource source, String permission) { + if (source.hasPermission(permission)) return true; + + source.sendMessage(Text.of(TextColor.RED, "You don't have the permissions to use this command!")); + return false; + } + +} 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 new file mode 100644 index 00000000..7bf1d25b --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java @@ -0,0 +1,457 @@ +/* + * 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 java.io.IOException; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Predicate; + +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3d; +import com.flowpowered.math.vector.Vector3i; +import com.google.common.collect.Lists; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.DoubleArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.LiteralCommandNode; + +import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.common.RenderTask; +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.plugin.text.TextColor; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.mca.Chunk; +import de.bluecolored.bluemap.core.mca.ChunkAnvil112; +import de.bluecolored.bluemap.core.mca.MCAWorld; +import de.bluecolored.bluemap.core.world.Block; +import de.bluecolored.bluemap.core.world.World; + +public class Commands { + + private final Plugin plugin; + private final CommandDispatcher dispatcher; + private Function commandSourceInterface; + + private CommandHelper helper; + + public Commands(Plugin plugin, CommandDispatcher dispatcher, Function commandSourceInterface) { + this.plugin = plugin; + this.dispatcher = dispatcher; + this.commandSourceInterface = commandSourceInterface; + + this.helper = new CommandHelper(plugin); + + init(); + } + + public void init() { + // commands + LiteralCommandNode baseCommand = + literal("bluemap") + .requires(requirements("bluemap.status")) + .executes(this::statusCommand) + .build(); + + LiteralCommandNode reloadCommand = + literal("reload") + .requires(requirementsUnloaded("bluemap.reload")) + .executes(this::reloadCommand) + .build(); + + LiteralCommandNode debugCommand = + literal("debug") + .requires(requirements("bluemap.debug")) + .executes(this::debugCommand) + + .then(argument("world", StringArgumentType.word()).suggests(new WorldSuggestionProvider<>(plugin)) + .then(argument("x", DoubleArgumentType.doubleArg()) + .then(argument("y", DoubleArgumentType.doubleArg()) + .then(argument("z", DoubleArgumentType.doubleArg()) + .executes(this::debugCommand) + ) + ) + ) + ) + .build(); + + LiteralCommandNode pauseCommand = + literal("pause") + .requires(requirements("bluemap.pause")) + .executes(this::pauseCommand) + .build(); + + LiteralCommandNode resumeCommand = + literal("resume") + .requires(requirements("bluemap.resume")) + .executes(this::resumeCommand) + .build(); + + LiteralCommandNode renderCommand = + literal("render") + .requires(requirements("bluemap.render")) + .executes(this::renderCommand) // /bluemap render + + .then(argument("radius", IntegerArgumentType.integer()) + .executes(this::renderCommand) // /bluemap render + ) + + .then(argument("x", DoubleArgumentType.doubleArg()) + .then(argument("z", DoubleArgumentType.doubleArg()) + .then(argument("radius", IntegerArgumentType.integer()) + .executes(this::renderCommand) // /bluemap render + ) + ) + ) + + .then(argument("world|map", StringArgumentType.word()).suggests(new WorldOrMapSuggestionProvider<>(plugin)) + .executes(this::renderCommand) // /bluemap render + + .then(argument("x", DoubleArgumentType.doubleArg()) + .then(argument("z", DoubleArgumentType.doubleArg()) + .then(argument("radius", IntegerArgumentType.integer()) + .executes(this::renderCommand) // /bluemap render + ) + ) + ) + ) + + .build(); + + LiteralCommandNode prioRenderCommand = + literal("prioritize") + .requires(requirements("bluemap.render")) + .then(argument("uuid", StringArgumentType.word()) + .executes(this::prioritizeRenderTaskCommand) + ) + .build(); + + LiteralCommandNode cancelRenderCommand = + literal("cancel") + .requires(requirements("bluemap.render")) + .then(argument("uuid", StringArgumentType.word()) + .executes(this::cancelRenderTaskCommand) + ) + .build(); + + // command tree + dispatcher.getRoot().addChild(baseCommand); + baseCommand.addChild(reloadCommand); + baseCommand.addChild(debugCommand); + baseCommand.addChild(pauseCommand); + baseCommand.addChild(resumeCommand); + baseCommand.addChild(renderCommand); + renderCommand.addChild(prioRenderCommand); + renderCommand.addChild(cancelRenderCommand); + } + + private Predicate requirements(String permission){ + return s -> { + CommandSource source = commandSourceInterface.apply(s); + return plugin.isLoaded() && source.hasPermission(permission); + }; + } + + private Predicate requirementsUnloaded(String permission){ + return s -> { + CommandSource source = commandSourceInterface.apply(s); + return source.hasPermission(permission); + }; + } + + private LiteralArgumentBuilder literal(String name){ + return LiteralArgumentBuilder.literal(name); + } + + private RequiredArgumentBuilder argument(String name, ArgumentType type){ + return RequiredArgumentBuilder.argument(name, type); + } + + private Optional getOptionalArgument(CommandContext context, String argumentName, Class type) { + try { + return Optional.of(context.getArgument(argumentName, type)); + } catch (IllegalArgumentException ex) { + return Optional.empty(); + } + } + + private Optional parseWorld(String worldName) { + for (World world : plugin.getWorlds()) { + if (world.getName().equalsIgnoreCase(worldName)) { + return Optional.of(world); + } + } + + return Optional.empty(); + } + + private Optional parseMap(String mapId) { + for (MapType map : plugin.getMapTypes()) { + if (map.getName().equalsIgnoreCase(mapId)) { + return Optional.of(map); + } + } + + return Optional.empty(); + } + + private Optional parseUUID(String uuidString) { + try { + return Optional.of(UUID.fromString(uuidString)); + } catch (IllegalArgumentException ex) { + return Optional.empty(); + } + } + + + // --- COMMANDS --- + + public int statusCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + source.sendMessages(helper.createStatusMessage()); + return 1; + } + + public int reloadCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + source.sendMessage(Text.of(TextColor.GOLD, "Reloading BlueMap...")); + + new Thread(() -> { + try { + plugin.reload(); + + if (plugin.isLoaded()) { + source.sendMessage(Text.of(TextColor.GREEN, "BlueMap reloaded!")); + } else { + source.sendMessage(Text.of(TextColor.RED, "Could not load BlueMap! See the console for details!")); + } + + } catch (Exception ex) { + Logger.global.logError("Failed to reload BlueMap!", ex); + + source.sendMessage(Text.of(TextColor.RED, "There was an error reloading BlueMap! See the console for details!")); + } + }).start(); + return 1; + } + + public int debugCommand(CommandContext context) throws CommandSyntaxException { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + // parse arguments + Optional worldName = getOptionalArgument(context, "world", String.class); + Optional x = getOptionalArgument(context, "x", Double.class); + Optional y = getOptionalArgument(context, "y", Double.class); + Optional z = getOptionalArgument(context, "z", Double.class); + + World world; + Vector3d position; + + if (worldName.isPresent() && x.isPresent() && y.isPresent() && z.isPresent()) { + world = parseWorld(worldName.get()).orElse(null); + position = new Vector3d(x.get(), y.get(), z.get()); + + if (world == null) { + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " with this name: ", TextColor.WHITE, worldName.get())); + return 0; + } + } else { + world = source.getWorld().orElse(null); + position = source.getPosition().orElse(null); + + if (world == null || position == null) { + source.sendMessage(Text.of(TextColor.RED, "Can't detect a location from this command-source, you'll have to define a world and position!")); + return 0; + } + } + + // output debug info + Vector3i blockPos = position.floor().toInt(); + Block block = world.getBlock(blockPos); + Block blockBelow = world.getBlock(blockPos.add(0, -1, 0)); + + String blockIdMeta = ""; + String blockBelowIdMeta = ""; + + if (world instanceof MCAWorld) { + try { + Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(blockPos)); + if (chunk instanceof ChunkAnvil112) { + blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos) + ")"; + blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos.add(0, -1, 0)) + ")"; + } + } catch (IOException ex) { + Logger.global.logError("Failed to read chunk for debug!", ex); + } + } + + source.sendMessages(Lists.newArrayList( + Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block, TextColor.GRAY, blockIdMeta), + Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta) + )); + + return 1; + } + + public int pauseCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + if (plugin.getRenderManager().isRunning()) { + plugin.getRenderManager().stop(); + source.sendMessage(Text.of(TextColor.GREEN, "BlueMap rendering paused!")); + return 1; + } else { + source.sendMessage(Text.of(TextColor.RED, "BlueMap rendering are already paused!")); + return 0; + } + } + + public int resumeCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + if (!plugin.getRenderManager().isRunning()) { + plugin.getRenderManager().start(); + source.sendMessage(Text.of(TextColor.GREEN, "BlueMap renders resumed!")); + return 1; + } else { + source.sendMessage(Text.of(TextColor.RED, "BlueMap renders are already running!")); + return 0; + } + } + + public int renderCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + // parse world/map argument + Optional worldOrMap = getOptionalArgument(context, "world|map", String.class); + World world = null; + MapType map = null; + if (worldOrMap.isPresent()) { + world = parseWorld(worldOrMap.get()).orElse(null); + + if (world == null) { + map = parseMap(worldOrMap.get()).orElse(null); + + if (map == null) { + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " or ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, worldOrMap.get())); + return 0; + } + } + } else { + world = source.getWorld().orElse(null); + + if (world == null) { + source.sendMessage(Text.of(TextColor.RED, "Can't detect a world from this command-source, you'll have to define a world or a map to render!").setHoverText(Text.of(TextColor.GRAY, "/bluemap render "))); + return 0; + } + } + + // parse radius and center arguments + int radius = getOptionalArgument(context, "radius", Integer.class).orElse(-1); + Vector2i center = null; + if (radius >= 0) { + Optional x = getOptionalArgument(context, "x", Double.class); + Optional z = getOptionalArgument(context, "z", Double.class); + + if (x.isPresent() && z.isPresent()) { + center = new Vector2i(x.get(), z.get()); + } else { + Vector3d position = source.getPosition().orElse(null); + if (position == null) { + source.sendMessage(Text.of(TextColor.RED, "Can't detect a position from this command-source, you'll have to define x,z coordinates to render with a radius!").setHoverText(Text.of(TextColor.GRAY, "/bluemap render " + radius))); + return 0; + } + + center = position.toVector2(true).floor().toInt(); + } + } + + // execute render + if (world != null) { + helper.createWorldRenderTask(source, world, center, radius); + } else { + helper.createMapRenderTask(source, map, center, radius); + } + + return 1; + } + + public int prioritizeRenderTaskCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + String uuidString = context.getArgument("uuid", String.class); + Optional taskUUID = parseUUID(uuidString); + if (!taskUUID.isPresent()) { + source.sendMessage(Text.of(TextColor.RED, "Not a valid UUID: " + uuidString)); + return 0; + } + + for (RenderTask task : plugin.getRenderManager().getRenderTasks()) { + if (task.getUuid().equals(taskUUID.get())) { + plugin.getRenderManager().prioritizeRenderTask(task); + + source.sendMessages(helper.createStatusMessage()); + return 1; + } + } + + source.sendMessage(Text.of(TextColor.RED, "There is no render-task with this UUID: " + uuidString)); + return 0; + } + + public int cancelRenderTaskCommand(CommandContext context) { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + String uuidString = context.getArgument("uuid", String.class); + Optional taskUUID = parseUUID(uuidString); + if (!taskUUID.isPresent()) { + source.sendMessage(Text.of(TextColor.RED, "Not a valid UUID: " + uuidString)); + return 0; + } + + for (RenderTask task : plugin.getRenderManager().getRenderTasks()) { + if (task.getUuid().equals(taskUUID.get())) { + plugin.getRenderManager().removeRenderTask(task); + + source.sendMessages(helper.createStatusMessage()); + return 1; + } + } + + source.sendMessage(Text.of(TextColor.RED, "There is no render-task with this UUID: " + uuidString)); + return 0; + } +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java new file mode 100644 index 00000000..b683a7ab --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java @@ -0,0 +1,52 @@ +/* + * 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 java.util.Collection; +import java.util.HashSet; + +import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.common.plugin.Plugin; + +public class MapSuggestionProvider extends AbstractSuggestionProvider { + + private Plugin plugin; + + public MapSuggestionProvider(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public Collection getPossibleValues() { + Collection values = new HashSet<>(); + + for (MapType map : plugin.getMapTypes()) { + values.add(map.getId()); + } + + return values; + } + +} 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 new file mode 100644 index 00000000..5bc100f1 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java @@ -0,0 +1,57 @@ +/* + * 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 java.util.Collection; +import java.util.HashSet; + +import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.core.world.World; + +public class WorldOrMapSuggestionProvider extends AbstractSuggestionProvider { + + private Plugin plugin; + + public WorldOrMapSuggestionProvider(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public Collection getPossibleValues() { + Collection values = new HashSet<>(); + + for (World world : plugin.getWorlds()) { + values.add(world.getName()); + } + + for (MapType map : plugin.getMapTypes()) { + values.add(map.getId()); + } + + return values; + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldSuggestionProvider.java new file mode 100644 index 00000000..b61259ae --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldSuggestionProvider.java @@ -0,0 +1,52 @@ +/* + * 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 java.util.Collection; +import java.util.HashSet; + +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.core.world.World; + +public class WorldSuggestionProvider extends AbstractSuggestionProvider { + + private Plugin plugin; + + public WorldSuggestionProvider(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public Collection getPossibleValues() { + Collection values = new HashSet<>(); + + for (World world : plugin.getWorlds()) { + values.add(world.getName()); + } + + return values; + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java index 99ed934d..2c6df8b0 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/CommandSource.java @@ -24,7 +24,12 @@ */ package de.bluecolored.bluemap.common.plugin.serverinterface; +import java.util.Optional; + +import com.flowpowered.math.vector.Vector3d; + import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.core.world.World; public interface CommandSource { @@ -36,4 +41,14 @@ default void sendMessages(Iterable textLines) { } } + boolean hasPermission(String permission); + + default Optional getWorld() { + return Optional.empty(); + } + + default Optional getPosition() { + return Optional.empty(); + } + } diff --git a/BlueMapForge/build.gradle b/BlueMapForge/build.gradle index 3646b912..88cf872c 100644 --- a/BlueMapForge/build.gradle +++ b/BlueMapForge/build.gradle @@ -28,6 +28,7 @@ dependencies { exclude group: 'com.google.code.gson', module: 'gson' exclude group: 'org.apache.commons', module: 'commons-lang3' exclude group: 'commons-io', module: 'commons-io' + exclude group: 'com.mojang', module: 'brigadier' } } diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java index f4967995..07ca9c8b 100644 --- a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java +++ b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java @@ -40,5 +40,10 @@ public ForgeCommandSource(net.minecraft.command.CommandSource delegate) { public void sendMessage(Text text) { delegate.sendFeedback(ITextComponent.Serializer.fromJson(text.toJSONString()), false); } + + @Override + public boolean hasPermission(String permission) { + return delegate.hasPermissionLevel(1); + } } diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeCommands.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeCommands.java deleted file mode 100644 index 90990a74..00000000 --- a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeCommands.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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.forge; - -import java.io.IOException; -import java.util.UUID; - -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.LiteralMessage; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; - -import de.bluecolored.bluemap.common.plugin.Commands; -import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.plugin.text.Text; -import de.bluecolored.bluemap.common.plugin.text.TextColor; -import net.minecraft.command.CommandSource; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.server.ServerWorld; -import net.minecraftforge.server.permission.DefaultPermissionLevel; -import net.minecraftforge.server.permission.PermissionAPI; - -public class ForgeCommands { - - private ForgeMod mod; - private Commands commands; - - public ForgeCommands(ForgeMod mod, Plugin bluemap) { - this.mod = mod; - this.commands = bluemap.getCommands(); - } - - public void registerCommands(CommandDispatcher dispatcher) { - LiteralArgumentBuilder base = literal("bluemap"); - - PermissionAPI.registerNode("bluemap.status", DefaultPermissionLevel.OP, "Permission for using /bluemap"); - base.executes(c -> { - if (!checkPermission(c, "bluemap.status")) return 0; - - commands.executeRootCommand(new ForgeCommandSource(c.getSource())); - return 1; - }); - - PermissionAPI.registerNode("bluemap.reload", DefaultPermissionLevel.OP, "Permission for using /bluemap reload"); - base.then(literal("reload").executes(c -> { - if (!checkPermission(c, "bluemap.reload")) return 0; - - commands.executeReloadCommand(new ForgeCommandSource(c.getSource())); - return 1; - })); - - PermissionAPI.registerNode("bluemap.pause", DefaultPermissionLevel.OP, "Permission for using /bluemap pause"); - base.then(literal("pause").executes(c -> { - if (!checkPermission(c, "bluemap.pause")) return 0; - - commands.executePauseCommand(new ForgeCommandSource(c.getSource())); - return 1; - })); - - PermissionAPI.registerNode("bluemap.resume", DefaultPermissionLevel.OP, "Permission for using /bluemap resume"); - base.then(literal("resume").executes(c -> { - if (!checkPermission(c, "bluemap.resume")) return 0; - - commands.executeResumeCommand(new ForgeCommandSource(c.getSource())); - return 1; - })); - - PermissionAPI.registerNode("bluemap.render", DefaultPermissionLevel.OP, "Permission for using /bluemap render"); - Command renderCommand = c -> { - if (!checkPermission(c, "bluemap.render")) return 0; - - String worldName = null; - try { - worldName = c.getArgument("world", String.class); - } catch (IllegalArgumentException ex) {} - - int blockRadius = -1; - try { - blockRadius = c.getArgument("block-radius", Integer.class); - } catch (IllegalArgumentException ex) {} - - PlayerEntity player = null; - try { - player = c.getSource().asPlayer(); - } catch (CommandSyntaxException ex) {} - - if (player == null && blockRadius != -1) { - throw new SimpleCommandExceptionType(new LiteralMessage("You can only use a block-radius if you are a player!")).create(); - } - - if (worldName == null) { - if (player == null) throw new SimpleCommandExceptionType(new LiteralMessage("You need to define a world! (/bluemap render )")).create(); - Vector2i center = new Vector2i(player.getPosition().getX(), player.getPosition().getZ()); - - UUID world; - try { - world = mod.getUUIDForWorld((ServerWorld) player.getEntityWorld()); - } catch (IOException ex) { - throw new SimpleCommandExceptionType(new LiteralMessage("Could not detect the world you are currently in, try to define a world using /bluemap render ")).create(); - } - - return commands.executeRenderWorldCommand(new ForgeCommandSource(c.getSource()), world, center, blockRadius) ? 1 : 0; - } else { - if (player == null) { - return commands.executeRenderCommand(new ForgeCommandSource(c.getSource()), worldName) ? 1 : 0; - } else { - Vector2i center = new Vector2i(player.getPosition().getX(), player.getPosition().getZ()); - return commands.executeRenderCommand(new ForgeCommandSource(c.getSource()), worldName, center, blockRadius) ? 1 : 0; - } - } - - }; - - base.then(literal("render") - .executes(renderCommand) - .then(argument("block-radius", IntegerArgumentType.integer(0)).executes(renderCommand)) - .then(argument("world", StringArgumentType.word()) - .executes(renderCommand) - .then(argument("block-radius", IntegerArgumentType.integer(0))).executes(renderCommand) - ) - - .then(literal("prioritize").then(argument("task-uuid", StringArgumentType.word()).executes(c -> { - if (!checkPermission(c, "bluemap.render")) return 0; - - try { - UUID taskUUID = UUID.fromString(c.getArgument("task-uuid", String.class)); - commands.executePrioritizeRenderTaskCommand(new ForgeCommandSource(c.getSource()), taskUUID); - return 1; - } catch (IllegalArgumentException ex) { - throw new SimpleCommandExceptionType(new LiteralMessage("Invalid task-uuid!")).create(); - } - }))) - - .then(literal("remove").then(argument("task-uuid", StringArgumentType.word()).executes(c -> { - if (!checkPermission(c, "bluemap.render")) return 0; - - try { - UUID taskUUID = UUID.fromString(c.getArgument("task-uuid", String.class)); - commands.executeRemoveRenderTaskCommand(new ForgeCommandSource(c.getSource()), taskUUID); - return 1; - } catch (IllegalArgumentException ex) { - throw new SimpleCommandExceptionType(new LiteralMessage("Invalid task-uuid!")).create(); - } - }))) - ); - - PermissionAPI.registerNode("bluemap.debug", DefaultPermissionLevel.OP, "Permission for using /bluemap debug"); - base.then(literal("debug").executes(c -> { - if (!checkPermission(c, "bluemap.debug")) return 0; - - Entity entity = c.getSource().assertIsEntity(); - BlockPos mcPos = entity.getPosition(); - Vector3i pos = new Vector3i(mcPos.getX(), mcPos.getY(), mcPos.getZ()); - - UUID world; - try { - world = mod.getUUIDForWorld((ServerWorld) entity.getEntityWorld()); - } catch (IOException e) { - throw new SimpleCommandExceptionType(new LiteralMessage("Could not detect the world you are currently in!")).create(); - } - - commands.executeDebugCommand(new ForgeCommandSource(c.getSource()), world, pos); - return 1; - })); - - dispatcher.register(base); - } - - private boolean checkPermission(CommandContext command, String permission) { - ForgeCommandSource cs = new ForgeCommandSource(command.getSource()); - - boolean hasPermission = false; - try { - if (PermissionAPI.hasPermission(command.getSource().asPlayer(), permission)) { - hasPermission = true; - } - } catch (CommandSyntaxException ex) { - if (command.getSource().hasPermissionLevel(1)) { - hasPermission = true; - } - } - - if (!hasPermission) { - cs.sendMessage(Text.of(TextColor.RED, "You don't have the permissions to use this command!")); - } - - return hasPermission; - } - - public static LiteralArgumentBuilder literal(String name){ - return LiteralArgumentBuilder.literal(name); - } - - public static RequiredArgumentBuilder argument(String name, ArgumentType type){ - return RequiredArgumentBuilder.argument(name, type); - } - -} diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index 3a386db6..1f34bac2 100644 --- a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -37,9 +37,11 @@ import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; +import net.minecraft.command.CommandSource; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.BlockEvent; @@ -53,7 +55,7 @@ public class ForgeMod implements ServerInterface { private Plugin bluemap; - private ForgeCommands commands; + private Commands commands; private Map worldUUIDs; private Collection eventListeners; @@ -61,7 +63,6 @@ public ForgeMod() { Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME)); this.bluemap = new Plugin("forge", this); - this.commands = new ForgeCommands(this, bluemap); this.worldUUIDs = new HashMap<>(); this.eventListeners = new ArrayList<>(1); @@ -85,8 +86,9 @@ public void onServerStarting(FMLServerStartingEvent event) { Logger.global.logError("Failed to save world: " + world.getProviderName(), t); } } - - this.commands.registerCommands(event.getCommandDispatcher()); + + //register commands + this.commands = new Commands<>(bluemap, event.getCommandDispatcher(), ForgeCommandSource::new); new Thread(() -> { try { @@ -182,7 +184,7 @@ public UUID getUUIDForWorld(ServerWorld world) throws IOException { worldUUIDs.put(key, uuid); } - return uuid; + return uuid; } } @@ -201,5 +203,9 @@ private File getFolderForWorld(ServerWorld world) throws IOException { public File getConfigFolder() { return new File("config/bluemap"); } + + public Commands getCommands() { + return commands; + } } diff --git a/BlueMapSponge/build.gradle b/BlueMapSponge/build.gradle index ab470430..5e5de5aa 100644 --- a/BlueMapSponge/build.gradle +++ b/BlueMapSponge/build.gradle @@ -20,6 +20,7 @@ build.dependsOn shadowJar { relocate 'net.querz.nbt', 'de.bluecolored.shadow.querz.nbt' relocate 'org.apache.commons.io', 'de.bluecolored.shadow.apache.commons.io' + relocate 'com.mojang.brigadier', 'de.bluecolored.shadow.mojang.brigadier' minimize() } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java index 8f9b80f6..21f455d3 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java @@ -24,16 +24,25 @@ */ package de.bluecolored.bluemap.sponge; -import org.spongepowered.api.text.serializer.TextSerializers; +import java.util.Optional; +import org.spongepowered.api.text.serializer.TextSerializers; +import org.spongepowered.api.world.Locatable; + +import com.flowpowered.math.vector.Vector3d; + +import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.core.world.World; public class SpongeCommandSource implements CommandSource { + private Plugin plugin; private org.spongepowered.api.command.CommandSource delegate; - public SpongeCommandSource(org.spongepowered.api.command.CommandSource delegate) { + public SpongeCommandSource(Plugin plugin, org.spongepowered.api.command.CommandSource delegate) { + this.plugin = plugin; this.delegate = delegate; } @@ -43,4 +52,27 @@ public void sendMessage(Text text) { delegate.sendMessage(spongeText); } + @Override + public boolean hasPermission(String permission) { + return delegate.hasPermission(permission); + } + + @Override + public Optional getPosition() { + if (delegate instanceof Locatable) { + return Optional.of(((Locatable) delegate).getLocation().getPosition()); + } + + return Optional.empty(); + } + + @Override + public Optional getWorld() { + if (delegate instanceof Locatable) { + return Optional.ofNullable(plugin.getWorld(((Locatable) delegate).getLocation().getExtent().getUniqueId())); + } + + return Optional.empty(); + } + } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommands.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommands.java index f932c7a5..4dda7d71 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommands.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommands.java @@ -24,194 +24,136 @@ */ package de.bluecolored.bluemap.sponge; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Optional; -import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.spongepowered.api.command.CommandCallable; +import org.spongepowered.api.command.CommandException; import org.spongepowered.api.command.CommandResult; -import org.spongepowered.api.command.args.GenericArguments; -import org.spongepowered.api.command.spec.CommandSpec; +import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.text.Text; import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.world.Locatable; import org.spongepowered.api.world.Location; -import org.spongepowered.api.world.storage.WorldProperties; +import org.spongepowered.api.world.World; -import de.bluecolored.bluemap.common.plugin.Commands; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestion; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.tree.CommandNode; + +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.commands.Commands; public class SpongeCommands { - - private Commands commands; - - public SpongeCommands(Commands commands) { - this.commands = commands; - } - - public CommandSpec createRootCommand() { - return CommandSpec.builder() - .description(Text.of("Displays BlueMaps render status")) - .permission("bluemap.status") - .childArgumentParseExceptionFallback(false) - .child(createReloadCommand(), "reload") - .child(createPauseRenderCommand(), "pause") - .child(createResumeRenderCommand(), "resume") - .child(createRenderCommand(), "render") - .child(createDebugCommand(), "debug") - .executor((source, args) -> { - commands.executeRootCommand(new SpongeCommandSource(source)); - return CommandResult.success(); - }) - .build(); - } - - public CommandSpec createReloadCommand() { - return CommandSpec.builder() - .description(Text.of("Reloads all resources and configuration-files")) - .permission("bluemap.reload") - .executor((source, args) -> { - commands.executeReloadCommand(new SpongeCommandSource(source)); - return CommandResult.success(); - }) - .build(); - } - public CommandSpec createPauseRenderCommand() { - return CommandSpec.builder() - .description(Text.of("Pauses all rendering")) - .permission("bluemap.pause") - .executor((source, args) -> { - if (commands.executePauseCommand(new SpongeCommandSource(source))) { - return CommandResult.success(); - } else { - return CommandResult.empty(); - } - }) - .build(); + private CommandDispatcher dispatcher; + + public SpongeCommands(final Plugin plugin) { + this.dispatcher = new CommandDispatcher<>(); + + // register commands + new Commands<>(plugin, dispatcher, bukkitSender -> new SpongeCommandSource(plugin, bukkitSender)); } - public CommandSpec createResumeRenderCommand() { - return CommandSpec.builder() - .description(Text.of("Resumes all paused rendering")) - .permission("bluemap.resume") - .executor((source, args) -> { - if (commands.executeResumeCommand(new SpongeCommandSource(source))) { - return CommandResult.success(); - } else { - return CommandResult.empty(); - } - }) - .build(); + public Collection getRootCommands(){ + Collection rootCommands = new ArrayList<>(); + + for (CommandNode node : this.dispatcher.getRoot().getChildren()) { + rootCommands.add(new SpongeCommandProxy(node.getName())); + } + + return rootCommands; } - public CommandSpec createRenderCommand() { - return CommandSpec.builder() - .description(Text.of("Renders the whole world")) - .permission("bluemap.render") - .childArgumentParseExceptionFallback(false) - .child(createPrioritizeTaskCommand(), "prioritize") - .child(createRemoveTaskCommand(), "remove") - .arguments( - GenericArguments.optionalWeak(GenericArguments.onlyOne(GenericArguments.world(Text.of("world")))), - GenericArguments.optional(GenericArguments.integer(Text.of("block-radius"))) - ) - .executor((source, args) -> { - WorldProperties spongeWorld = args.getOne("world").orElse(null); - - if (spongeWorld == null && source instanceof Locatable) { - Location loc = ((Locatable) source).getLocation(); - spongeWorld = loc.getExtent().getProperties(); - } - - if (spongeWorld == null){ - source.sendMessage(Text.of(TextColors.RED, "You have to define a world to render!")); - return CommandResult.empty(); - } + public class SpongeCommandProxy implements CommandCallable { - int radius = args.getOne("block-radius").orElse(-1); + private String label; + + protected SpongeCommandProxy(String label) { + this.label = label; + } + + @Override + public CommandResult process(CommandSource source, String arguments) throws CommandException { + String command = label; + if (!arguments.isEmpty()) { + command += " " + arguments; + } + + try { + return CommandResult.successCount(dispatcher.execute(command, source)); + } catch (CommandSyntaxException ex) { + source.sendMessage(Text.of(TextColors.RED, ex.getRawMessage().getString())); - if (radius >= 0) { - if (source instanceof Locatable) { - Location loc = ((Locatable) source).getLocation(); - - if (commands.executeRenderWorldCommand(new SpongeCommandSource(source), spongeWorld.getUniqueId(), loc.getBlockPosition().toVector2(true), radius)) { - return CommandResult.success(); - } else { - return CommandResult.empty(); - } - } else { - source.sendMessages( - Text.of(TextColors.RED, "Could not determine a center-location for the radius!"), - Text.of(TextColors.GRAY, "Could not determine a center-location for the radius!") - ); - return CommandResult.empty(); - } - } + String context = ex.getContext(); + if (context != null) source.sendMessage(Text.of(TextColors.GRAY, context)); - if (commands.executeRenderWorldCommand(new SpongeCommandSource(source), spongeWorld.getUniqueId())) { - return CommandResult.success(); - } else { - return CommandResult.empty(); - } - }) - .build(); - } - - public CommandSpec createPrioritizeTaskCommand() { - return CommandSpec.builder() - .description(Text.of("Prioritizes the render-task with the given uuid")) - .permission("bluemap.render") - .arguments(GenericArguments.uuid(Text.of("task-uuid"))) - .executor((source, args) -> { - Optional uuid = args.getOne("task-uuid"); - if (!uuid.isPresent()) { - source.sendMessage(Text.of(TextColors.RED, "You need to specify a task-uuid")); - return CommandResult.empty(); - } - - commands.executePrioritizeRenderTaskCommand(new SpongeCommandSource(source), uuid.get()); - return CommandResult.success(); - }) - .build(); - } - - public CommandSpec createRemoveTaskCommand() { - return CommandSpec.builder() - .description(Text.of("Removes the render-task with the given uuid")) - .permission("bluemap.render") - .arguments(GenericArguments.uuid(Text.of("task-uuid"))) - .executor((source, args) -> { - Optional uuid = args.getOne("task-uuid"); - if (!uuid.isPresent()) { - source.sendMessage(Text.of(TextColors.RED, "You need to specify a task-uuid")); - return CommandResult.empty(); - } - - commands.executeRemoveRenderTaskCommand(new SpongeCommandSource(source), uuid.get()); - return CommandResult.success(); - }) - .build(); - } - - public CommandSpec createDebugCommand() { - return CommandSpec.builder() - .permission("bluemap.debug") - .description(Text.of("Prints some debug info")) - .extendedDescription(Text.of("Prints some information about how bluemap sees the blocks at and below your position")) - .executor((source, args) -> { - if (source instanceof Locatable) { - Location loc = ((Locatable) source).getLocation(); - UUID worldUuid = loc.getExtent().getUniqueId(); - - if (commands.executeDebugCommand(new SpongeCommandSource(source), worldUuid, loc.getBlockPosition())) { - return CommandResult.success(); - } else { - return CommandResult.empty(); - } - } - - source.sendMessage(Text.of(TextColors.RED, "You can only execute this command as a player!")); return CommandResult.empty(); - }) - .build(); + } + } + + @Override + public List getSuggestions(CommandSource source, String arguments, Location targetPosition) throws CommandException { + String command = label; + if (!arguments.isEmpty()) { + command += " " + arguments; + } + + List completions = new ArrayList<>(); + + try { + Suggestions suggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(command, source)).get(100, TimeUnit.MILLISECONDS); + for (Suggestion suggestion : suggestions.getList()) { + String text = suggestion.getText(); + + if (text.indexOf(' ') == -1) { + completions.add(text); + } + } + } catch (InterruptedException | ExecutionException | TimeoutException ignore) {} + + completions.sort((s1, s2) -> s1.compareToIgnoreCase(s2)); + return completions; + } + + @Override + public boolean testPermission(CommandSource source) { + return true; + } + + @Override + public Optional getShortDescription(CommandSource source) { + return Optional.empty(); + } + + @Override + public Optional getHelp(CommandSource source) { + return Optional.empty(); + } + + @Override + public Text getUsage(CommandSource source) { + CommandNode node = dispatcher.getRoot().getChild(label); + if (node == null) return Text.of("/" + label); + + List lines = new ArrayList<>(); + for (String usageString : dispatcher.getSmartUsage(node, source).values()) { + lines.add(Text.of(TextColors.WHITE, "/", TextColors.GRAY, usageString)); + } + + return Text.joinWith(Text.NEW_LINE, lines); + } + + public String getLabel() { + return label; + } + } } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index 7cce4a0f..ca3896f8 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -45,6 +45,7 @@ import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.sponge.SpongeCommands.SpongeCommandProxy; import net.querz.nbt.CompoundTag; import net.querz.nbt.NBTUtil; @@ -65,6 +66,7 @@ public class SpongePlugin implements ServerInterface { private MetricsLite2 metrics; private Plugin bluemap; + private SpongeCommands commands; private SpongeExecutorService asyncExecutor; @@ -73,6 +75,7 @@ public SpongePlugin(org.slf4j.Logger logger) { Logger.global = new Slf4jLogger(logger); this.bluemap = new Plugin("sponge", this); + this.commands = new SpongeCommands(bluemap); } @Listener @@ -84,7 +87,10 @@ public void onServerStart(GameStartingServerEvent evt) { Sponge.getServer().saveWorldProperties(properties); } - Sponge.getCommandManager().register(this, new SpongeCommands(bluemap.getCommands()).createRootCommand(), "bluemap"); + //register commands + for(SpongeCommandProxy command : commands.getRootCommands()) { + Sponge.getCommandManager().register(this, command, command.getLabel()); + } asyncExecutor.execute(() -> { try { diff --git a/build.gradle b/build.gradle index f914f183..0b5089b0 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,9 @@ allprojects { maven { url = 'https://files.minecraftforge.net/maven/' } + maven { + url "https://libraries.minecraft.net" + } } compileJava.options.compilerArgs.add '-parameters'