mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-23 09:47:38 +00:00
Merge branch 'master' into feature/mapping
This commit is contained in:
commit
7a5ea73c57
36
pom.xml
36
pom.xml
@ -355,24 +355,6 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<!-- Assembly builds .zip, etc. -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
|
||||||
<version>2.2-beta-2</version>
|
|
||||||
<configuration>
|
|
||||||
<descriptor>${basedir}/src/main/assembly/default.xml</descriptor>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>release</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- Shades -->
|
<!-- Shades -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
@ -396,6 +378,24 @@
|
|||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Assembly builds .zip, etc. -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<version>2.2-beta-2</version>
|
||||||
|
<configuration>
|
||||||
|
<descriptor>${basedir}/src/main/assembly/default.xml</descriptor>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>release</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
<!-- Release plugin -->
|
<!-- Release plugin -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
@ -30,12 +30,12 @@ import java.io.File;
|
|||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
class BukkitCommandSender implements Actor {
|
public class BukkitCommandSender implements Actor {
|
||||||
|
|
||||||
private CommandSender sender;
|
private CommandSender sender;
|
||||||
private WorldEditPlugin plugin;
|
private WorldEditPlugin plugin;
|
||||||
|
|
||||||
BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) {
|
public BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) {
|
||||||
checkNotNull(plugin);
|
checkNotNull(plugin);
|
||||||
checkNotNull(sender);
|
checkNotNull(sender);
|
||||||
checkArgument(!(sender instanceof Player), "Cannot wrap a player");
|
checkArgument(!(sender instanceof Player), "Cannot wrap a player");
|
||||||
|
@ -156,10 +156,12 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
controller.clearSessions();
|
if (controller != null) {
|
||||||
controller.getPlatformManager().unregister(server);
|
controller.clearSessions();
|
||||||
config.unload();
|
controller.getPlatformManager().unregister(server);
|
||||||
server.unregisterCommands();
|
config.unload();
|
||||||
|
server.unregisterCommands();
|
||||||
|
}
|
||||||
this.getServer().getScheduler().cancelTasks(this);
|
this.getServer().getScheduler().cancelTasks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ public class CommandContext {
|
|||||||
suggestionContext = SuggestionContext.hangingValue();
|
suggestionContext = SuggestionContext.hangingValue();
|
||||||
|
|
||||||
// Not a flag?
|
// Not a flag?
|
||||||
if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z]+$")) {
|
if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z\\?]+$")) {
|
||||||
if (!isHanging) {
|
if (!isHanging) {
|
||||||
suggestionContext = SuggestionContext.lastValue();
|
suggestionContext = SuggestionContext.lastValue();
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,13 @@
|
|||||||
|
|
||||||
package com.sk89q.minecraft.util.commands;
|
package com.sk89q.minecraft.util.commands;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
public class CommandException extends Exception {
|
public class CommandException extends Exception {
|
||||||
|
|
||||||
private static final long serialVersionUID = 870638193072101739L;
|
private static final long serialVersionUID = 870638193072101739L;
|
||||||
@ -48,7 +51,16 @@ public class CommandException extends Exception {
|
|||||||
commandStack.add(name);
|
commandStack.add(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toStackString(String prefix, String spacedSuffix) {
|
/**
|
||||||
|
* Gets the command that was called, which will include the sub-command
|
||||||
|
* (i.e. "/br sphere").
|
||||||
|
*
|
||||||
|
* @param prefix the command shebang character (such as "/") -- may be empty
|
||||||
|
* @param spacedSuffix a suffix to put at the end (optional) -- may be null
|
||||||
|
* @return the command that was used
|
||||||
|
*/
|
||||||
|
public String getCommandUsed(String prefix, @Nullable String spacedSuffix) {
|
||||||
|
checkNotNull(prefix);
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
if (prefix != null) {
|
if (prefix != null) {
|
||||||
builder.append(prefix);
|
builder.append(prefix);
|
||||||
@ -66,7 +78,7 @@ public class CommandException extends Exception {
|
|||||||
}
|
}
|
||||||
builder.append(spacedSuffix);
|
builder.append(spacedSuffix);
|
||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ public class RegionCommands {
|
|||||||
desc = "Draws a spline through selected points",
|
desc = "Draws a spline through selected points",
|
||||||
help =
|
help =
|
||||||
"Draws a spline through selected points.\n" +
|
"Draws a spline through selected points.\n" +
|
||||||
"Can only be uesd with convex polyhedral selections.\n" +
|
"Can only be used with convex polyhedral selections.\n" +
|
||||||
"Flags:\n" +
|
"Flags:\n" +
|
||||||
" -h generates only a shell",
|
" -h generates only a shell",
|
||||||
flags = "h",
|
flags = "h",
|
||||||
|
@ -60,8 +60,8 @@ public class SchematicCommands {
|
|||||||
@Command(
|
@Command(
|
||||||
aliases = { "load", "l" },
|
aliases = { "load", "l" },
|
||||||
usage = "[format] <filename>",
|
usage = "[format] <filename>",
|
||||||
desc = "Load a schematic into your clipboard",
|
desc = "Load a file into your clipboard",
|
||||||
help = "Load a schematic into your clipboard\n" +
|
help = "Load a schematic file into your clipboard\n" +
|
||||||
"Format is a format from \"//schematic formats\"\n" +
|
"Format is a format from \"//schematic formats\"\n" +
|
||||||
"If the format is not provided, WorldEdit will\n" +
|
"If the format is not provided, WorldEdit will\n" +
|
||||||
"attempt to automatically detect the format of the schematic",
|
"attempt to automatically detect the format of the schematic",
|
||||||
@ -78,8 +78,8 @@ public class SchematicCommands {
|
|||||||
@Command(
|
@Command(
|
||||||
aliases = { "save", "s" },
|
aliases = { "save", "s" },
|
||||||
usage = "[format] <filename>",
|
usage = "[format] <filename>",
|
||||||
desc = "Save a schematic into your clipboard",
|
desc = "Save your clipboard to file",
|
||||||
help = "Save a schematic into your clipboard\n" +
|
help = "Save your clipboard to file\n" +
|
||||||
"Format is a format from \"//schematic formats\"\n",
|
"Format is a format from \"//schematic formats\"\n",
|
||||||
min = 1,
|
min = 1,
|
||||||
max = 2
|
max = 2
|
||||||
@ -93,7 +93,7 @@ public class SchematicCommands {
|
|||||||
@Command(
|
@Command(
|
||||||
aliases = { "delete", "d" },
|
aliases = { "delete", "d" },
|
||||||
usage = "<filename>",
|
usage = "<filename>",
|
||||||
desc = "Delete a schematic from the schematic list",
|
desc = "Delete a saved schematic",
|
||||||
help = "Delete a schematic from the schematic list",
|
help = "Delete a schematic from the schematic list",
|
||||||
min = 1,
|
min = 1,
|
||||||
max = 1
|
max = 1
|
||||||
@ -122,7 +122,7 @@ public class SchematicCommands {
|
|||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
aliases = {"formats", "listformats", "f"},
|
aliases = {"formats", "listformats", "f"},
|
||||||
desc = "List available schematic formats",
|
desc = "List available formats",
|
||||||
max = 0
|
max = 0
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.schematic.formats")
|
@CommandPermissions("worldedit.schematic.formats")
|
||||||
@ -147,7 +147,7 @@ public class SchematicCommands {
|
|||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
aliases = {"list", "all", "ls"},
|
aliases = {"list", "all", "ls"},
|
||||||
desc = "List available schematics",
|
desc = "List saved schematics",
|
||||||
max = 0,
|
max = 0,
|
||||||
flags = "dn",
|
flags = "dn",
|
||||||
help = "List all schematics in the schematics directory\n" +
|
help = "List all schematics in the schematics directory\n" +
|
||||||
|
@ -19,26 +19,44 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.command;
|
package com.sk89q.worldedit.command;
|
||||||
|
|
||||||
import com.sk89q.minecraft.util.commands.*;
|
import com.google.common.base.Joiner;
|
||||||
import com.sk89q.worldedit.*;
|
import com.sk89q.minecraft.util.commands.Command;
|
||||||
|
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||||
|
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
||||||
|
import com.sk89q.minecraft.util.commands.Logging;
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.EntityType;
|
||||||
|
import com.sk89q.worldedit.LocalConfiguration;
|
||||||
|
import com.sk89q.worldedit.LocalSession;
|
||||||
import com.sk89q.worldedit.LocalWorld.KillFlags;
|
import com.sk89q.worldedit.LocalWorld.KillFlags;
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
import com.sk89q.worldedit.entity.Player;
|
import com.sk89q.worldedit.entity.Player;
|
||||||
import com.sk89q.worldedit.extension.platform.Actor;
|
import com.sk89q.worldedit.extension.platform.Actor;
|
||||||
|
import com.sk89q.worldedit.extension.platform.CommandManager;
|
||||||
import com.sk89q.worldedit.patterns.Pattern;
|
import com.sk89q.worldedit.patterns.Pattern;
|
||||||
import com.sk89q.worldedit.patterns.SingleBlockPattern;
|
import com.sk89q.worldedit.patterns.SingleBlockPattern;
|
||||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
import com.sk89q.worldedit.util.command.CommandCallable;
|
||||||
import com.sk89q.worldedit.util.command.CommandMapping;
|
import com.sk89q.worldedit.util.command.CommandMapping;
|
||||||
import com.sk89q.worldedit.util.command.Description;
|
|
||||||
import com.sk89q.worldedit.util.command.Dispatcher;
|
import com.sk89q.worldedit.util.command.Dispatcher;
|
||||||
|
import com.sk89q.worldedit.util.command.PrimaryAliasComparator;
|
||||||
import com.sk89q.worldedit.util.command.parametric.Optional;
|
import com.sk89q.worldedit.util.command.parametric.Optional;
|
||||||
|
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
|
||||||
|
import com.sk89q.worldedit.util.formatting.Style;
|
||||||
|
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||||
|
import com.sk89q.worldedit.util.formatting.component.Code;
|
||||||
|
import com.sk89q.worldedit.util.formatting.component.CommandListBox;
|
||||||
|
import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT;
|
import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT;
|
||||||
|
|
||||||
@ -497,59 +515,137 @@ public class UtilityCommands {
|
|||||||
help(args, we, actor);
|
help(args, we, actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void help(CommandContext args, WorldEdit we, Actor actor) {
|
private static CommandMapping detectCommand(Dispatcher dispatcher, String command, boolean isRootLevel) {
|
||||||
final Dispatcher dispatcher = we.getPlatformManager().getCommandManager().getDispatcher();
|
CommandMapping mapping;
|
||||||
|
|
||||||
if (args.argsLength() == 0) {
|
// First try the command as entered
|
||||||
SortedSet<String> commands = new TreeSet<String>(new Comparator<String>() {
|
mapping = dispatcher.get(command);
|
||||||
@Override
|
if (mapping != null) {
|
||||||
public int compare(String o1, String o2) {
|
return mapping;
|
||||||
final int ret = o1.replaceAll("/", "").compareToIgnoreCase(o2.replaceAll("/", ""));
|
}
|
||||||
if (ret == 0) {
|
|
||||||
return o1.compareToIgnoreCase(o2);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
commands.addAll(dispatcher.getPrimaryAliases());
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
// Then if we're looking at root commands and the user didn't use
|
||||||
boolean first = true;
|
// any slashes, let's try double slashes and then single slashes.
|
||||||
for (String command : commands) {
|
// However, be aware that there exists different single slash
|
||||||
if (!first) {
|
// and double slash commands in WorldEdit
|
||||||
sb.append(", ");
|
if (isRootLevel && !command.contains("/")) {
|
||||||
}
|
mapping = dispatcher.get("//" + command);
|
||||||
|
if (mapping != null) {
|
||||||
sb.append('/');
|
return mapping;
|
||||||
sb.append(command);
|
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
actor.print(sb.toString());
|
mapping = dispatcher.get("/" + command);
|
||||||
|
if (mapping != null) {
|
||||||
return;
|
return mapping;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String command = args.getJoinedStrings(0).toLowerCase().replaceAll("^/", "");
|
return null;
|
||||||
CommandMapping mapping = dispatcher.get(command);
|
}
|
||||||
|
|
||||||
if (mapping == null) {
|
public static void help(CommandContext args, WorldEdit we, Actor actor) {
|
||||||
actor.printError("Unknown command '" + command + "'.");
|
CommandCallable callable = we.getPlatformManager().getCommandManager().getDispatcher();
|
||||||
return;
|
|
||||||
|
int page = 0;
|
||||||
|
final int perPage = actor instanceof Player ? 8 : 20; // More pages for console
|
||||||
|
int effectiveLength = args.argsLength();
|
||||||
|
|
||||||
|
// Detect page from args
|
||||||
|
try {
|
||||||
|
if (args.argsLength() > 0) {
|
||||||
|
page = args.getInteger(args.argsLength() - 1);
|
||||||
|
if (page <= 0) {
|
||||||
|
page = 1;
|
||||||
|
} else {
|
||||||
|
page--;
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveLength--;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Description description = mapping.getDescription();
|
boolean isRootLevel = true;
|
||||||
|
List<String> visited = new ArrayList<String>();
|
||||||
|
|
||||||
if (description.getUsage() != null) {
|
// Drill down to the command
|
||||||
actor.printDebug("Usage: " + description.getUsage());
|
for (int i = 0; i < effectiveLength; i++) {
|
||||||
|
String command = args.getString(i);
|
||||||
|
|
||||||
|
if (callable instanceof Dispatcher) {
|
||||||
|
// Chop off the beginning / if we're are the root level
|
||||||
|
if (isRootLevel && command.length() > 1 && command.charAt(0) == '/') {
|
||||||
|
command = command.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandMapping mapping = detectCommand((Dispatcher) callable, command, isRootLevel);
|
||||||
|
if (mapping != null) {
|
||||||
|
callable = mapping.getCallable();
|
||||||
|
} else {
|
||||||
|
if (isRootLevel) {
|
||||||
|
actor.printError(String.format("The command '%s' could not be found.", args.getString(i)));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
actor.printError(String.format("The sub-command '%s' under '%s' could not be found.",
|
||||||
|
command, Joiner.on(" ").join(visited)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.add(args.getString(i));
|
||||||
|
isRootLevel = false;
|
||||||
|
} else {
|
||||||
|
actor.printError(String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)",
|
||||||
|
Joiner.on(" ").join(visited), command));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (description.getHelp() != null) {
|
// Create the message
|
||||||
actor.print(description.getHelp());
|
if (callable instanceof Dispatcher) {
|
||||||
} else if (description.getShortDescription() != null) {
|
Dispatcher dispatcher = (Dispatcher) callable;
|
||||||
actor.print(description.getShortDescription());
|
|
||||||
|
// Get a list of aliases
|
||||||
|
List<CommandMapping> aliases = new ArrayList<CommandMapping>(dispatcher.getCommands());
|
||||||
|
Collections.sort(aliases, new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN));
|
||||||
|
|
||||||
|
// Calculate pagination
|
||||||
|
int offset = perPage * page;
|
||||||
|
int pageTotal = (int) Math.ceil(aliases.size() / (double) perPage);
|
||||||
|
|
||||||
|
// Box
|
||||||
|
CommandListBox box = new CommandListBox(String.format("Help: page %d/%d ", page + 1, pageTotal));
|
||||||
|
StyledFragment contents = box.getContents();
|
||||||
|
StyledFragment tip = contents.createFragment(Style.GRAY);
|
||||||
|
|
||||||
|
if (offset >= aliases.size()) {
|
||||||
|
tip.createFragment(Style.RED).append(String.format("There is no page %d (total number of pages is %d).", page + 1, pageTotal)).newLine();
|
||||||
|
} else {
|
||||||
|
List<CommandMapping> list = aliases.subList(offset, Math.min(offset + perPage, aliases.size()));
|
||||||
|
|
||||||
|
tip.append("Type ");
|
||||||
|
tip.append(new Code().append("//help ").append("<command> [<page>]"));
|
||||||
|
tip.append(" for more information.").newLine();
|
||||||
|
|
||||||
|
// Add each command
|
||||||
|
for (CommandMapping mapping : list) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
if (isRootLevel) {
|
||||||
|
builder.append("/");
|
||||||
|
}
|
||||||
|
if (!visited.isEmpty()) {
|
||||||
|
builder.append(Joiner.on(" ").join(visited));
|
||||||
|
builder.append(" ");
|
||||||
|
}
|
||||||
|
builder.append(mapping.getPrimaryAlias());
|
||||||
|
box.appendCommand(builder.toString(), mapping.getDescription().getShortDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actor.printRaw(ColorCodeBuilder.asColorCodes(box));
|
||||||
} else {
|
} else {
|
||||||
actor.print("No further help is available.");
|
CommandUsageBox box = new CommandUsageBox(callable, Joiner.on(" ").join(visited));
|
||||||
|
actor.printRaw(ColorCodeBuilder.asColorCodes(box));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ import com.sk89q.worldedit.util.command.fluent.CommandGraph;
|
|||||||
import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
|
import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
|
||||||
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
|
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
|
||||||
import com.sk89q.worldedit.util.eventbus.Subscribe;
|
import com.sk89q.worldedit.util.eventbus.Subscribe;
|
||||||
|
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
|
||||||
|
import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
|
||||||
import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
|
import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
|
||||||
import com.sk89q.worldedit.util.logging.LogFormat;
|
import com.sk89q.worldedit.util.logging.LogFormat;
|
||||||
|
|
||||||
@ -50,6 +52,7 @@ import java.io.IOException;
|
|||||||
import java.util.logging.FileHandler;
|
import java.util.logging.FileHandler;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
@ -60,6 +63,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
*/
|
*/
|
||||||
public final class CommandManager {
|
public final class CommandManager {
|
||||||
|
|
||||||
|
public static final Pattern COMMAND_CLEAN_PATTERN = Pattern.compile("^[/]+");
|
||||||
private static final Logger logger = Logger.getLogger(CommandManager.class.getCanonicalName());
|
private static final Logger logger = Logger.getLogger(CommandManager.class.getCanonicalName());
|
||||||
private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$");
|
private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$");
|
||||||
|
|
||||||
@ -220,7 +224,17 @@ public final class CommandManager {
|
|||||||
} catch (CommandPermissionsException e) {
|
} catch (CommandPermissionsException e) {
|
||||||
actor.printError("You don't have permission to do this.");
|
actor.printError("You don't have permission to do this.");
|
||||||
} catch (InvalidUsageException e) {
|
} catch (InvalidUsageException e) {
|
||||||
actor.printError(e.getMessage() + "\nUsage: " + e.getUsage("/"));
|
if (e.isFullHelpSuggested()) {
|
||||||
|
actor.printRaw(ColorCodeBuilder.asColorCodes(new CommandUsageBox(e.getCommand(), e.getCommandUsed("/", ""), locals)));
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message != null) {
|
||||||
|
actor.printError(message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String message = e.getMessage();
|
||||||
|
actor.printError(message != null ? message : "The command was not used properly (no more help available).");
|
||||||
|
actor.printError("Usage: " + e.getSimpleUsageString("/"));
|
||||||
|
}
|
||||||
} catch (WrappedCommandException e) {
|
} catch (WrappedCommandException e) {
|
||||||
Throwable t = e.getCause();
|
Throwable t = e.getCause();
|
||||||
actor.printError("Please report this error: [See console]");
|
actor.printError("Please report this error: [See console]");
|
||||||
|
@ -21,8 +21,10 @@ package com.sk89q.worldedit.math.transform;
|
|||||||
|
|
||||||
import com.sk89q.worldedit.Vector;
|
import com.sk89q.worldedit.Vector;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
@ -60,6 +62,15 @@ public class CombinedTransform implements Transform {
|
|||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transform inverse() {
|
||||||
|
List<Transform> list = new ArrayList<Transform>();
|
||||||
|
for (int i = transforms.length - 1; i >= 0; i--) {
|
||||||
|
list.add(transforms[i].inverse());
|
||||||
|
}
|
||||||
|
return new CombinedTransform(list);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transform combine(Transform other) {
|
public Transform combine(Transform other) {
|
||||||
checkNotNull(other);
|
checkNotNull(other);
|
||||||
|
@ -31,12 +31,17 @@ public class Identity implements Transform {
|
|||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transform inverse() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transform combine(Transform other) {
|
public Transform combine(Transform other) {
|
||||||
if (other instanceof Identity) {
|
if (other instanceof Identity) {
|
||||||
return this;
|
return this;
|
||||||
} else {
|
} else {
|
||||||
return new CombinedTransform(this, other);
|
return other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,13 @@ import com.sk89q.worldedit.Vector;
|
|||||||
*/
|
*/
|
||||||
public interface Transform extends Function<Vector, Vector> {
|
public interface Transform extends Function<Vector, Vector> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new inverse transform.
|
||||||
|
*
|
||||||
|
* @return a new inverse transform
|
||||||
|
*/
|
||||||
|
Transform inverse();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link Transform} that combines this transform with another.
|
* Create a new {@link Transform} that combines this transform with another.
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.math.transform;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.util.Location;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Various utility methods related to {@link Transform}s.
|
||||||
|
*/
|
||||||
|
public final class Transforms {
|
||||||
|
|
||||||
|
private Transforms() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform a location's position with a given transform.
|
||||||
|
* </p>
|
||||||
|
* Direction is unaffected.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param transform the transform
|
||||||
|
* @return the transformed location
|
||||||
|
*/
|
||||||
|
public static Location transform(Location location, Transform transform) {
|
||||||
|
checkNotNull(location);
|
||||||
|
checkNotNull(transform);
|
||||||
|
return new Location(location.getWorld(), transform.apply(location.toVector()), location.getDirection());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -23,19 +23,11 @@ import com.sk89q.minecraft.util.commands.CommandException;
|
|||||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A command that can be executed.
|
* A command that can be executed.
|
||||||
*/
|
*/
|
||||||
public interface CommandCallable {
|
public interface CommandCallable {
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of value flags used by this command.
|
|
||||||
*
|
|
||||||
* @return a list of value flags
|
|
||||||
*/
|
|
||||||
Set<Character> getValueFlags();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the correct command based on the input.
|
* Execute the correct command based on the input.
|
||||||
|
@ -19,70 +19,37 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.util.command;
|
package com.sk89q.worldedit.util.command;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks a command registration.
|
* Provides information about a mapping between a command and its aliases.
|
||||||
*/
|
*/
|
||||||
public class CommandMapping {
|
public interface CommandMapping {
|
||||||
|
|
||||||
private final String[] aliases;
|
|
||||||
private final CommandCallable callable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance.
|
|
||||||
*
|
|
||||||
* @param callable the command callable
|
|
||||||
* @param alias a list of all aliases, where the first one is the primary one
|
|
||||||
*/
|
|
||||||
public CommandMapping(CommandCallable callable, String... alias) {
|
|
||||||
super();
|
|
||||||
this.aliases = alias;
|
|
||||||
this.callable = callable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the primary alias.
|
* Get the primary alias.
|
||||||
*
|
*
|
||||||
* @return the primary alias
|
* @return the primary alias
|
||||||
*/
|
*/
|
||||||
public String getPrimaryAlias() {
|
String getPrimaryAlias();
|
||||||
return aliases[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of all aliases.
|
* Get a list of all aliases.
|
||||||
*
|
*
|
||||||
* @return aliases
|
* @return aliases
|
||||||
*/
|
*/
|
||||||
public String[] getAllAliases() {
|
String[] getAllAliases();
|
||||||
return aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the callable
|
* Get the callable
|
||||||
*
|
*
|
||||||
* @return the callable
|
* @return the callable
|
||||||
*/
|
*/
|
||||||
public CommandCallable getCallable() {
|
CommandCallable getCallable();
|
||||||
return callable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the {@link Description} form the callable.
|
* Get the {@link Description} form the callable.
|
||||||
*
|
*
|
||||||
* @return the description
|
* @return the description
|
||||||
*/
|
*/
|
||||||
public Description getDescription() {
|
Description getDescription();
|
||||||
return getCallable().getDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "CommandMapping{" +
|
|
||||||
"aliases=" + Arrays.toString(aliases) +
|
|
||||||
", callable=" + callable +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,29 +21,87 @@ package com.sk89q.worldedit.util.command;
|
|||||||
|
|
||||||
import com.sk89q.minecraft.util.commands.CommandException;
|
import com.sk89q.minecraft.util.commands.CommandException;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when a command is not used properly.
|
* Thrown when a command is not used properly.
|
||||||
|
* </p>
|
||||||
|
* When handling this exception, print the error message if it is not null.
|
||||||
|
* Print a one line help instruction unless {@link #isFullHelpSuggested()}
|
||||||
|
* is true, which, in that case, the full help of the command should be shown.
|
||||||
|
* </p>
|
||||||
|
* If no error message is set and full help is not to be shown, then a generic
|
||||||
|
* "you used this command incorrectly" message should be shown.
|
||||||
*/
|
*/
|
||||||
public class InvalidUsageException extends CommandException {
|
public class InvalidUsageException extends CommandException {
|
||||||
|
|
||||||
private static final long serialVersionUID = -3222004168669490390L;
|
private static final long serialVersionUID = -3222004168669490390L;
|
||||||
private final Description description;
|
private final CommandCallable command;
|
||||||
|
private final boolean fullHelpSuggested;
|
||||||
|
|
||||||
public InvalidUsageException(Description description) {
|
/**
|
||||||
this.description = description;
|
* Create a new instance with no error message and with no suggestion
|
||||||
|
* that full and complete help for the command should be shown. This will
|
||||||
|
* result in a generic error message.
|
||||||
|
*
|
||||||
|
* @param command the command
|
||||||
|
*/
|
||||||
|
public InvalidUsageException(CommandCallable command) {
|
||||||
|
this(null, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InvalidUsageException(String message, Description description) {
|
/**
|
||||||
|
* Create a new instance with a message and with no suggestion
|
||||||
|
* that full and complete help for the command should be shown.
|
||||||
|
*
|
||||||
|
* @param message the message
|
||||||
|
* @param command the command
|
||||||
|
*/
|
||||||
|
public InvalidUsageException(@Nullable String message, CommandCallable command) {
|
||||||
|
this(message, command, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with a message.
|
||||||
|
*
|
||||||
|
* @param message the message
|
||||||
|
* @param command the command
|
||||||
|
* @param fullHelpSuggested true if the full help for the command should be shown
|
||||||
|
*/
|
||||||
|
public InvalidUsageException(@Nullable String message, CommandCallable command, boolean fullHelpSuggested) {
|
||||||
super(message);
|
super(message);
|
||||||
this.description = description;
|
checkNotNull(command);
|
||||||
|
this.command = command;
|
||||||
|
this.fullHelpSuggested = fullHelpSuggested;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Description getDescription() {
|
/**
|
||||||
return description;
|
* Get the command.
|
||||||
|
*
|
||||||
|
* @return the command
|
||||||
|
*/
|
||||||
|
public CommandCallable getCommand() {
|
||||||
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsage(String prefix) {
|
/**
|
||||||
return toStackString(prefix, getDescription().getUsage());
|
* Get a simple usage string.
|
||||||
|
*
|
||||||
|
* @param prefix the command shebang (such as "/") -- may be blank
|
||||||
|
* @return a usage string
|
||||||
|
*/
|
||||||
|
public String getSimpleUsageString(String prefix) {
|
||||||
|
return getCommandUsed(prefix, command.getDescription().getUsage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the full usage of the command should be shown.
|
||||||
|
*
|
||||||
|
* @return show full usage
|
||||||
|
*/
|
||||||
|
public boolean isFullHelpSuggested() {
|
||||||
|
return fullHelpSuggested;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.util.command;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the primary aliases of two {@link CommandMapping} using
|
||||||
|
* {@link String#compareTo(String)}.
|
||||||
|
*/
|
||||||
|
public final class PrimaryAliasComparator implements Comparator<CommandMapping> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of this class.
|
||||||
|
*/
|
||||||
|
public static final PrimaryAliasComparator INSTANCE = new PrimaryAliasComparator(null);
|
||||||
|
private final @Nullable Pattern removalPattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param removalPattern a regex to remove unwanted characters from the compared aliases
|
||||||
|
*/
|
||||||
|
public PrimaryAliasComparator(@Nullable Pattern removalPattern) {
|
||||||
|
this.removalPattern = removalPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String clean(String alias) {
|
||||||
|
if (removalPattern != null) {
|
||||||
|
return removalPattern.matcher(alias).replaceAll("");
|
||||||
|
}
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(CommandMapping o1, CommandMapping o2) {
|
||||||
|
return clean(o1.getPrimaryAlias()).compareTo(clean(o2.getPrimaryAlias()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.util.command;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks a command registration.
|
||||||
|
*/
|
||||||
|
public class SimpleCommandMapping implements CommandMapping {
|
||||||
|
|
||||||
|
private final String[] aliases;
|
||||||
|
private final CommandCallable callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param callable the command callable
|
||||||
|
* @param alias a list of all aliases, where the first one is the primary one
|
||||||
|
*/
|
||||||
|
public SimpleCommandMapping(CommandCallable callable, String... alias) {
|
||||||
|
super();
|
||||||
|
this.aliases = alias;
|
||||||
|
this.callable = callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPrimaryAlias() {
|
||||||
|
return aliases[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getAllAliases() {
|
||||||
|
return aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandCallable getCallable() {
|
||||||
|
return callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Description getDescription() {
|
||||||
|
return getCallable().getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CommandMapping{" +
|
||||||
|
"aliases=" + Arrays.toString(aliases) +
|
||||||
|
", callable=" + callable +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,12 +20,20 @@
|
|||||||
package com.sk89q.worldedit.util.command;
|
package com.sk89q.worldedit.util.command;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.sk89q.minecraft.util.commands.*;
|
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||||
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
|
import com.sk89q.minecraft.util.commands.CommandException;
|
||||||
import com.sk89q.worldedit.util.formatting.CommandListBox;
|
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||||
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
|
||||||
|
import com.sk89q.minecraft.util.commands.WrappedCommandException;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple implementation of {@link Dispatcher}.
|
* A simple implementation of {@link Dispatcher}.
|
||||||
@ -47,7 +55,7 @@ public class SimpleDispatcher implements Dispatcher {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerCommand(CommandCallable callable, String... alias) {
|
public void registerCommand(CommandCallable callable, String... alias) {
|
||||||
CommandMapping mapping = new CommandMapping(callable, alias);
|
CommandMapping mapping = new SimpleCommandMapping(callable, alias);
|
||||||
|
|
||||||
// Check for replacements
|
// Check for replacements
|
||||||
for (String a : alias) {
|
for (String a : alias) {
|
||||||
@ -93,11 +101,6 @@ public class SimpleDispatcher implements Dispatcher {
|
|||||||
return commands.get(alias.toLowerCase());
|
return commands.get(alias.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Character> getValueFlags() {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean call(String arguments, CommandLocals locals, String[] parentCommands) throws CommandException {
|
public boolean call(String arguments, CommandLocals locals, String[] parentCommands) throws CommandException {
|
||||||
// We have permission for this command if we have permissions for subcommands
|
// We have permission for this command if we have permissions for subcommands
|
||||||
@ -109,7 +112,7 @@ public class SimpleDispatcher implements Dispatcher {
|
|||||||
Set<String> aliases = getPrimaryAliases();
|
Set<String> aliases = getPrimaryAliases();
|
||||||
|
|
||||||
if (aliases.isEmpty()) {
|
if (aliases.isEmpty()) {
|
||||||
throw new InvalidUsageException("This command has no sub-commands.", getDescription());
|
throw new InvalidUsageException("This command has no sub-commands.", this);
|
||||||
} else if (split.length > 0) {
|
} else if (split.length > 0) {
|
||||||
String subCommand = split[0];
|
String subCommand = split[0];
|
||||||
String subArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length));
|
String subArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length));
|
||||||
@ -132,7 +135,7 @@ public class SimpleDispatcher implements Dispatcher {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidUsageException(ColorCodeBuilder.asColorCodes(getSubcommandList(locals, parentCommands)), getDescription());
|
throw new InvalidUsageException("Please choose a sub-command.", this, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -185,16 +188,4 @@ public class SimpleDispatcher implements Dispatcher {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private StyledFragment getSubcommandList(CommandLocals locals, String[] parentCommands) {
|
|
||||||
CommandListBox box = new CommandListBox("Subcommands");
|
|
||||||
String prefix = parentCommands.length > 0 ? "/" + Joiner.on(" ").join(parentCommands) + " " : "";
|
|
||||||
for (CommandMapping mapping : getCommands()) {
|
|
||||||
if (mapping.getCallable().testPermission(locals)) {
|
|
||||||
box.appendCommand(prefix + mapping.getPrimaryAlias(), mapping.getDescription().getShortDescription());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import com.sk89q.minecraft.util.commands.CommandException;
|
|||||||
import com.sk89q.worldedit.util.command.binding.PrimitiveBindings;
|
import com.sk89q.worldedit.util.command.binding.PrimitiveBindings;
|
||||||
import com.sk89q.worldedit.util.command.binding.StandardBindings;
|
import com.sk89q.worldedit.util.command.binding.StandardBindings;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -77,8 +78,8 @@ public interface Binding {
|
|||||||
* @throws ParameterException thrown if the parameter could not be formulated
|
* @throws ParameterException thrown if the parameter could not be formulated
|
||||||
* @throws CommandException on a command exception
|
* @throws CommandException on a command exception
|
||||||
*/
|
*/
|
||||||
Object bind(ParameterData parameter, ArgumentStack scoped, boolean onlyConsume)
|
Object bind(ParameterData parameter, ArgumentStack scoped, boolean onlyConsume)
|
||||||
throws ParameterException, CommandException;
|
throws ParameterException, CommandException, InvocationTargetException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of suggestions for the given parameter and user arguments.
|
* Get a list of suggestions for the given parameter and user arguments.
|
||||||
|
@ -143,7 +143,7 @@ public class BindingHelper implements Binding {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object bind(ParameterData parameter, ArgumentStack scoped,
|
public Object bind(ParameterData parameter, ArgumentStack scoped,
|
||||||
boolean onlyConsume) throws ParameterException, CommandException {
|
boolean onlyConsume) throws ParameterException, CommandException, InvocationTargetException {
|
||||||
BoundMethod binding = match(parameter);
|
BoundMethod binding = match(parameter);
|
||||||
List<Object> args = new ArrayList<Object>();
|
List<Object> args = new ArrayList<Object>();
|
||||||
args.add(scoped);
|
args.add(scoped);
|
||||||
@ -178,7 +178,7 @@ public class BindingHelper implements Binding {
|
|||||||
} else if (e.getCause() instanceof CommandException) {
|
} else if (e.getCause() instanceof CommandException) {
|
||||||
throw (CommandException) e.getCause();
|
throw (CommandException) e.getCause();
|
||||||
}
|
}
|
||||||
throw new RuntimeException(e.getCause());
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,15 +19,32 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.util.command.parametric;
|
package com.sk89q.worldedit.util.command.parametric;
|
||||||
|
|
||||||
import com.sk89q.minecraft.util.commands.*;
|
import com.google.common.base.Joiner;
|
||||||
import com.sk89q.worldedit.util.command.*;
|
import com.sk89q.minecraft.util.commands.Command;
|
||||||
|
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||||
|
import com.sk89q.minecraft.util.commands.CommandException;
|
||||||
|
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||||
|
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
||||||
|
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
|
||||||
|
import com.sk89q.minecraft.util.commands.WrappedCommandException;
|
||||||
|
import com.sk89q.worldedit.util.command.CommandCallable;
|
||||||
|
import com.sk89q.worldedit.util.command.InvalidUsageException;
|
||||||
|
import com.sk89q.worldedit.util.command.MissingParameterException;
|
||||||
|
import com.sk89q.worldedit.util.command.Parameter;
|
||||||
|
import com.sk89q.worldedit.util.command.SimpleDescription;
|
||||||
|
import com.sk89q.worldedit.util.command.UnconsumedParameterException;
|
||||||
import com.sk89q.worldedit.util.command.binding.Switch;
|
import com.sk89q.worldedit.util.command.binding.Switch;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The implementation of a {@link CommandCallable} for the {@link ParametricBuilder}.
|
* The implementation of a {@link CommandCallable} for the {@link ParametricBuilder}.
|
||||||
@ -169,6 +186,12 @@ class ParametricCallable implements CommandCallable {
|
|||||||
String[] split = CommandContext.split(calledCommand + " " + stringArguments);
|
String[] split = CommandContext.split(calledCommand + " " + stringArguments);
|
||||||
CommandContext context = new CommandContext(split, getValueFlags(), false, locals);
|
CommandContext context = new CommandContext(split, getValueFlags(), false, locals);
|
||||||
|
|
||||||
|
// Provide help if -? is specified
|
||||||
|
if (context.hasFlag('?')) {
|
||||||
|
String title = Joiner.on(" ").join(parentCommands);
|
||||||
|
throw new InvalidUsageException(null, this, true);
|
||||||
|
}
|
||||||
|
|
||||||
Object[] args = new Object[parameters.length];
|
Object[] args = new Object[parameters.length];
|
||||||
ContextArgumentStack arguments = new ContextArgumentStack(context);
|
ContextArgumentStack arguments = new ContextArgumentStack(context);
|
||||||
ParameterData parameter = null;
|
ParameterData parameter = null;
|
||||||
@ -221,14 +244,14 @@ class ParametricCallable implements CommandCallable {
|
|||||||
handler.postInvoke(handler, method, parameters, args, context);
|
handler.postInvoke(handler, method, parameters, args, context);
|
||||||
}
|
}
|
||||||
} catch (MissingParameterException e) {
|
} catch (MissingParameterException e) {
|
||||||
throw new InvalidUsageException("Too few parameters!", getDescription());
|
throw new InvalidUsageException("Too few parameters!", this);
|
||||||
} catch (UnconsumedParameterException e) {
|
} catch (UnconsumedParameterException e) {
|
||||||
throw new InvalidUsageException("Too many parameters! Unused parameters: " + e.getUnconsumed(), getDescription());
|
throw new InvalidUsageException("Too many parameters! Unused parameters: " + e.getUnconsumed(), this);
|
||||||
} catch (ParameterException e) {
|
} catch (ParameterException e) {
|
||||||
assert parameter != null;
|
assert parameter != null;
|
||||||
String name = parameter.getName();
|
String name = parameter.getName();
|
||||||
|
|
||||||
throw new InvalidUsageException("For parameter '" + name + "': " + e.getMessage(), getDescription());
|
throw new InvalidUsageException("For parameter '" + name + "': " + e.getMessage(), this);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
for (ExceptionConverter converter : builder.getExceptionConverters()) {
|
for (ExceptionConverter converter : builder.getExceptionConverters()) {
|
||||||
converter.convert(e.getCause());
|
converter.convert(e.getCause());
|
||||||
@ -250,7 +273,11 @@ class ParametricCallable implements CommandCallable {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Get a list of value flags used by this command.
|
||||||
|
*
|
||||||
|
* @return a list of value flags
|
||||||
|
*/
|
||||||
public Set<Character> getValueFlags() {
|
public Set<Character> getValueFlags() {
|
||||||
return valueFlags;
|
return valueFlags;
|
||||||
}
|
}
|
||||||
@ -343,8 +370,8 @@ class ParametricCallable implements CommandCallable {
|
|||||||
* @throws ParameterException on an error
|
* @throws ParameterException on an error
|
||||||
* @throws CommandException on an error
|
* @throws CommandException on an error
|
||||||
*/
|
*/
|
||||||
private Object getDefaultValue(int i, ContextArgumentStack scoped)
|
private Object getDefaultValue(int i, ContextArgumentStack scoped)
|
||||||
throws ParameterException, CommandException {
|
throws ParameterException, CommandException, InvocationTargetException {
|
||||||
CommandContext context = scoped.getContext();
|
CommandContext context = scoped.getContext();
|
||||||
ParameterData parameter = parameters[i];
|
ParameterData parameter = parameters[i];
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.util.formatting.component;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.util.formatting.Style;
|
||||||
|
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a fragment representing a command that is to be typed.
|
||||||
|
*/
|
||||||
|
public class Code extends StyledFragment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*/
|
||||||
|
public Code() {
|
||||||
|
super(Style.CYAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,7 +17,9 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.sk89q.worldedit.util.formatting;
|
package com.sk89q.worldedit.util.formatting.component;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.util.formatting.Style;
|
||||||
|
|
||||||
public class CommandListBox extends MessageBox {
|
public class CommandListBox extends MessageBox {
|
||||||
|
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.util.formatting.component;
|
||||||
|
|
||||||
|
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||||
|
import com.sk89q.worldedit.extension.platform.CommandManager;
|
||||||
|
import com.sk89q.worldedit.util.command.CommandCallable;
|
||||||
|
import com.sk89q.worldedit.util.command.CommandMapping;
|
||||||
|
import com.sk89q.worldedit.util.command.Description;
|
||||||
|
import com.sk89q.worldedit.util.command.Dispatcher;
|
||||||
|
import com.sk89q.worldedit.util.command.PrimaryAliasComparator;
|
||||||
|
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A box to describe usage of a command.
|
||||||
|
*/
|
||||||
|
public class CommandUsageBox extends StyledFragment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new usage box.
|
||||||
|
*
|
||||||
|
* @param command the command to describe
|
||||||
|
* @param commandString the command that was used, such as "/we" or "/brush sphere"
|
||||||
|
*/
|
||||||
|
public CommandUsageBox(CommandCallable command, String commandString) {
|
||||||
|
this(command, commandString, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new usage box.
|
||||||
|
*
|
||||||
|
* @param command the command to describe
|
||||||
|
* @param commandString the command that was used, such as "/we" or "/brush sphere"
|
||||||
|
* @param locals list of locals to use
|
||||||
|
*/
|
||||||
|
public CommandUsageBox(CommandCallable command, String commandString, @Nullable CommandLocals locals) {
|
||||||
|
checkNotNull(command);
|
||||||
|
checkNotNull(commandString);
|
||||||
|
if (command instanceof Dispatcher) {
|
||||||
|
attachDispatcherUsage((Dispatcher) command, commandString, locals);
|
||||||
|
} else {
|
||||||
|
attachCommandUsage(command.getDescription(), commandString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attachDispatcherUsage(Dispatcher dispatcher, String commandString, @Nullable CommandLocals locals) {
|
||||||
|
CommandListBox box = new CommandListBox("Subcommands");
|
||||||
|
String prefix = !commandString.isEmpty() ? commandString + " " : "";
|
||||||
|
|
||||||
|
List<CommandMapping> list = new ArrayList<CommandMapping>(dispatcher.getCommands());
|
||||||
|
Collections.sort(list, new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN));
|
||||||
|
|
||||||
|
for (CommandMapping mapping : list) {
|
||||||
|
if (locals == null || mapping.getCallable().testPermission(locals)) {
|
||||||
|
box.appendCommand(prefix + mapping.getPrimaryAlias(), mapping.getDescription().getShortDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attachCommandUsage(Description description, String commandString) {
|
||||||
|
MessageBox box = new MessageBox("Help for " + commandString);
|
||||||
|
StyledFragment contents = box.getContents();
|
||||||
|
|
||||||
|
if (description.getUsage() != null) {
|
||||||
|
contents.append(new Label().append("Usage: "));
|
||||||
|
contents.append(description.getUsage());
|
||||||
|
} else {
|
||||||
|
contents.append(new Subtle().append("Usage information is not available."));
|
||||||
|
}
|
||||||
|
|
||||||
|
contents.newLine();
|
||||||
|
|
||||||
|
if (description.getHelp() != null) {
|
||||||
|
contents.append(description.getHelp());
|
||||||
|
} else if (description.getShortDescription() != null) {
|
||||||
|
contents.append(description.getShortDescription());
|
||||||
|
} else {
|
||||||
|
contents.append(new Subtle().append("No further help is available."));
|
||||||
|
}
|
||||||
|
|
||||||
|
append(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.util.formatting.component;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.util.formatting.Style;
|
||||||
|
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a fragment representing a label.
|
||||||
|
*/
|
||||||
|
public class Label extends StyledFragment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*/
|
||||||
|
public Label() {
|
||||||
|
super(Style.YELLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,7 +17,11 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.sk89q.worldedit.util.formatting;
|
package com.sk89q.worldedit.util.formatting.component;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
|
||||||
|
import com.sk89q.worldedit.util.formatting.Style;
|
||||||
|
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.util.formatting.component;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.util.formatting.Style;
|
||||||
|
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a subtle part of the message.
|
||||||
|
*/
|
||||||
|
public class Subtle extends StyledFragment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*/
|
||||||
|
public Subtle() {
|
||||||
|
super(Style.GRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user