Rework commands

This commit is contained in:
Blue (Lukas Rieger) 2020-05-10 01:28:09 +02:00
parent cf74a70f32
commit a246da133c
24 changed files with 1198 additions and 1029 deletions

View File

@ -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 {

View File

@ -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<Vector3d> getPosition() {
Location location = getLocation();
if (location != null) {
return Optional.of(new Vector3d(location.getX(), location.getY(), location.getZ()));
}
return Optional.empty();
}
@Override
public Optional<World> 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;
}
}

View File

@ -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<CommandSender> dispatcher;
private Commands bluemapCommands;
private Collection<Command> 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<BukkitCommand> getRootCommands(){
Collection<BukkitCommand> 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<CommandSender> 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<String> 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;
}
}
}

View File

@ -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...");

View File

@ -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: |
/<command>
/<command> reload
/<command> pause
/<command> resume
/<command> render [world] [block-radius]
/<command> debug
commands:
permissions:
bluemap.*:
children:

View File

@ -1,4 +1,7 @@
dependencies {
compile 'com.mojang:brigadier:1.0.17'
compile project(':BlueMapCore')
compile project(':BlueMapAPI')
}

View File

@ -1,400 +0,0 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.plugin;
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:
*
* <ul>
* <li>/bluemap</li>
* <li>/bluemap reload</li>
* <li>/bluemap pause</li>
* <li>/bluemap resume</li>
* <li>/bluemap render [world]</li>
* <li>/bluemap render prioritize [task-uuid]</li>
* <li>/bluemap render remove [task-uuid]</li>
* <li>/bluemap debug</li>
* </ul>
*/
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<Text> createStatusMessage(){
List<Text> 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<Vector2i> filter;
if (center == null || blockRadius < 0) {
filter = c -> true;
} else {
filter = c -> c.mul(16).distanceSquared(center) <= blockRadius * blockRadius;
taskName = "radius-render";
}
Collection<Vector2i> 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<Vector2i> 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;
}
}

View File

@ -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;
}

View File

@ -0,0 +1,55 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.plugin.commands;
import 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<S> implements SuggestionProvider<S> {
@Override
public CompletableFuture<Suggestions> getSuggestions(CommandContext<S> context, SuggestionsBuilder builder) throws CommandSyntaxException {
Collection<String> 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<String> getPossibleValues();
}

View File

@ -0,0 +1,185 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.plugin.commands;
import 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<Text> createStatusMessage(){
List<Text> 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<Vector2i> filter;
if (center == null || blockRadius < 0) {
filter = c -> true;
} else {
filter = c -> c.mul(16).distanceSquared(center) <= blockRadius * blockRadius;
taskName = "radius-render";
}
Collection<Vector2i> 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<Vector2i> 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;
}
}

View File

@ -0,0 +1,457 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.plugin.commands;
import 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<S> {
private final Plugin plugin;
private final CommandDispatcher<S> dispatcher;
private Function<S, CommandSource> commandSourceInterface;
private CommandHelper helper;
public Commands(Plugin plugin, CommandDispatcher<S> dispatcher, Function<S, CommandSource> commandSourceInterface) {
this.plugin = plugin;
this.dispatcher = dispatcher;
this.commandSourceInterface = commandSourceInterface;
this.helper = new CommandHelper(plugin);
init();
}
public void init() {
// commands
LiteralCommandNode<S> baseCommand =
literal("bluemap")
.requires(requirements("bluemap.status"))
.executes(this::statusCommand)
.build();
LiteralCommandNode<S> reloadCommand =
literal("reload")
.requires(requirementsUnloaded("bluemap.reload"))
.executes(this::reloadCommand)
.build();
LiteralCommandNode<S> 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<S> pauseCommand =
literal("pause")
.requires(requirements("bluemap.pause"))
.executes(this::pauseCommand)
.build();
LiteralCommandNode<S> resumeCommand =
literal("resume")
.requires(requirements("bluemap.resume"))
.executes(this::resumeCommand)
.build();
LiteralCommandNode<S> renderCommand =
literal("render")
.requires(requirements("bluemap.render"))
.executes(this::renderCommand) // /bluemap render
.then(argument("radius", IntegerArgumentType.integer())
.executes(this::renderCommand) // /bluemap render <radius>
)
.then(argument("x", DoubleArgumentType.doubleArg())
.then(argument("z", DoubleArgumentType.doubleArg())
.then(argument("radius", IntegerArgumentType.integer())
.executes(this::renderCommand) // /bluemap render <x> <z> <radius>
)
)
)
.then(argument("world|map", StringArgumentType.word()).suggests(new WorldOrMapSuggestionProvider<>(plugin))
.executes(this::renderCommand) // /bluemap render <world|map>
.then(argument("x", DoubleArgumentType.doubleArg())
.then(argument("z", DoubleArgumentType.doubleArg())
.then(argument("radius", IntegerArgumentType.integer())
.executes(this::renderCommand) // /bluemap render <world|map> <x> <z> <radius>
)
)
)
)
.build();
LiteralCommandNode<S> prioRenderCommand =
literal("prioritize")
.requires(requirements("bluemap.render"))
.then(argument("uuid", StringArgumentType.word())
.executes(this::prioritizeRenderTaskCommand)
)
.build();
LiteralCommandNode<S> 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<S> requirements(String permission){
return s -> {
CommandSource source = commandSourceInterface.apply(s);
return plugin.isLoaded() && source.hasPermission(permission);
};
}
private Predicate<S> requirementsUnloaded(String permission){
return s -> {
CommandSource source = commandSourceInterface.apply(s);
return source.hasPermission(permission);
};
}
private LiteralArgumentBuilder<S> literal(String name){
return LiteralArgumentBuilder.<S>literal(name);
}
private <T> RequiredArgumentBuilder<S, T> argument(String name, ArgumentType<T> type){
return RequiredArgumentBuilder.<S, T>argument(name, type);
}
private <T> Optional<T> getOptionalArgument(CommandContext<S> context, String argumentName, Class<T> type) {
try {
return Optional.of(context.getArgument(argumentName, type));
} catch (IllegalArgumentException ex) {
return Optional.empty();
}
}
private Optional<World> parseWorld(String worldName) {
for (World world : plugin.getWorlds()) {
if (world.getName().equalsIgnoreCase(worldName)) {
return Optional.of(world);
}
}
return Optional.empty();
}
private Optional<MapType> parseMap(String mapId) {
for (MapType map : plugin.getMapTypes()) {
if (map.getName().equalsIgnoreCase(mapId)) {
return Optional.of(map);
}
}
return Optional.empty();
}
private Optional<UUID> parseUUID(String uuidString) {
try {
return Optional.of(UUID.fromString(uuidString));
} catch (IllegalArgumentException ex) {
return Optional.empty();
}
}
// --- COMMANDS ---
public int statusCommand(CommandContext<S> context) {
CommandSource source = commandSourceInterface.apply(context.getSource());
source.sendMessages(helper.createStatusMessage());
return 1;
}
public int reloadCommand(CommandContext<S> 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<S> context) throws CommandSyntaxException {
CommandSource source = commandSourceInterface.apply(context.getSource());
// parse arguments
Optional<String> worldName = getOptionalArgument(context, "world", String.class);
Optional<Double> x = getOptionalArgument(context, "x", Double.class);
Optional<Double> y = getOptionalArgument(context, "y", Double.class);
Optional<Double> 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<S> 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<S> 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<S> context) {
CommandSource source = commandSourceInterface.apply(context.getSource());
// parse world/map argument
Optional<String> 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 <world|map>")));
return 0;
}
}
// parse radius and center arguments
int radius = getOptionalArgument(context, "radius", Integer.class).orElse(-1);
Vector2i center = null;
if (radius >= 0) {
Optional<Double> x = getOptionalArgument(context, "x", Double.class);
Optional<Double> 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 <x> <z> " + 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<S> context) {
CommandSource source = commandSourceInterface.apply(context.getSource());
String uuidString = context.getArgument("uuid", String.class);
Optional<UUID> 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<S> context) {
CommandSource source = commandSourceInterface.apply(context.getSource());
String uuidString = context.getArgument("uuid", String.class);
Optional<UUID> 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;
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.plugin.commands;
import java.util.Collection;
import java.util.HashSet;
import de.bluecolored.bluemap.common.MapType;
import de.bluecolored.bluemap.common.plugin.Plugin;
public class MapSuggestionProvider<S> extends AbstractSuggestionProvider<S> {
private Plugin plugin;
public MapSuggestionProvider(Plugin plugin) {
this.plugin = plugin;
}
@Override
public Collection<String> getPossibleValues() {
Collection<String> values = new HashSet<>();
for (MapType map : plugin.getMapTypes()) {
values.add(map.getId());
}
return values;
}
}

View File

@ -0,0 +1,57 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.plugin.commands;
import 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<S> extends AbstractSuggestionProvider<S> {
private Plugin plugin;
public WorldOrMapSuggestionProvider(Plugin plugin) {
this.plugin = plugin;
}
@Override
public Collection<String> getPossibleValues() {
Collection<String> values = new HashSet<>();
for (World world : plugin.getWorlds()) {
values.add(world.getName());
}
for (MapType map : plugin.getMapTypes()) {
values.add(map.getId());
}
return values;
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.common.plugin.commands;
import java.util.Collection;
import java.util.HashSet;
import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.core.world.World;
public class WorldSuggestionProvider<S> extends AbstractSuggestionProvider<S> {
private Plugin plugin;
public WorldSuggestionProvider(Plugin plugin) {
this.plugin = plugin;
}
@Override
public Collection<String> getPossibleValues() {
Collection<String> values = new HashSet<>();
for (World world : plugin.getWorlds()) {
values.add(world.getName());
}
return values;
}
}

View File

@ -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<Text> textLines) {
}
}
boolean hasPermission(String permission);
default Optional<World> getWorld() {
return Optional.empty();
}
default Optional<Vector3d> getPosition() {
return Optional.empty();
}
}

View File

@ -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'
}
}

View File

@ -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);
}
}

View File

@ -1,231 +0,0 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.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<CommandSource> dispatcher) {
LiteralArgumentBuilder<CommandSource> 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<CommandSource> 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 <world>)")).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 <world>")).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<CommandSource> 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<CommandSource> literal(String name){
return LiteralArgumentBuilder.<CommandSource>literal(name);
}
public static <S extends CommandSource, T> RequiredArgumentBuilder<S, T> argument(String name, ArgumentType<T> type){
return RequiredArgumentBuilder.<S, T>argument(name, type);
}
}

View File

@ -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<CommandSource> commands;
private Map<String, UUID> worldUUIDs;
private Collection<ServerEventListener> 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<CommandSource> getCommands() {
return commands;
}
}

View File

@ -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()
}

View File

@ -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<Vector3d> getPosition() {
if (delegate instanceof Locatable) {
return Optional.of(((Locatable) delegate).getLocation().getPosition());
}
return Optional.empty();
}
@Override
public Optional<World> getWorld() {
if (delegate instanceof Locatable) {
return Optional.ofNullable(plugin.getWorld(((Locatable) delegate).getLocation().getExtent().getUniqueId()));
}
return Optional.empty();
}
}

View File

@ -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<CommandSource> 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<SpongeCommandProxy> getRootCommands(){
Collection<SpongeCommandProxy> rootCommands = new ArrayList<>();
for (CommandNode<CommandSource> 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.<WorldProperties>getOne("world").orElse(null);
if (spongeWorld == null && source instanceof Locatable) {
Location<org.spongepowered.api.world.World> 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.<Integer>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<org.spongepowered.api.world.World> 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> uuid = args.<UUID>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> uuid = args.<UUID>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<org.spongepowered.api.world.World> 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<String> getSuggestions(CommandSource source, String arguments, Location<World> targetPosition) throws CommandException {
String command = label;
if (!arguments.isEmpty()) {
command += " " + arguments;
}
List<String> 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<Text> getShortDescription(CommandSource source) {
return Optional.empty();
}
@Override
public Optional<Text> getHelp(CommandSource source) {
return Optional.empty();
}
@Override
public Text getUsage(CommandSource source) {
CommandNode<CommandSource> node = dispatcher.getRoot().getChild(label);
if (node == null) return Text.of("/" + label);
List<Text> 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;
}
}
}

View File

@ -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 {

View File

@ -21,6 +21,9 @@ allprojects {
maven {
url = 'https://files.minecraftforge.net/maven/'
}
maven {
url "https://libraries.minecraft.net"
}
}
compileJava.options.compilerArgs.add '-parameters'