-
-
Notifications
You must be signed in to change notification settings - Fork 48
Building commands
This page outlines the core feature of Lamp, how to build and compose commands. Other features, such as auto-completions, conditions and resolvers have their own dedicated wiki pages, and will not be mentioned here.
Command examples:
- Command actors represent the individual responsible for executing a command. This can be a CLI, a player, a platform-supported one, such as org.bukkit.Player, or a custom sender type.
- In cases of non-subclasses of CommandActor, the parameter that represents the sender must come first in the command! Otherwise, it will be interpreted as a non-sender, and will look for another resolver
- When the first parameter in the command should not be inferred as the sender, it can be annotated with
@NotSender
.
Commands are registered by class instances. A class may contain one or more command, and each is represented by a method.
- The command method has to be annotated with
@Command
,@Subcommand
or@Default
to be considered a command method, otherwise it is dismissed. - Commands must have a known parent at compile-time, through the
@Command
annotation, whether on the class or the method. In cases where the parent is only known at runtime, use orphan commands. - To register commands, use the generic
commandHandler.register(Object...)
method. - In cases of inner or nested classes, the parent classes are respected and checked for annotations.
Commands in Lamp are identified by paths, which are very similar to the concept of file paths on your computer.
- Subcommands must have a parent, either through
@Command
annotation on the containing class or method, or the orphan commands API which allows supplying a parent at runtime. -
@Command
annotations can be placed on classes and methods. In cases of inner or nested classes, the parent classes are respected and checked for annotations as well. -
@Command
and@Subcommand
accept spaces, which represent nested paths. For example,@Command("foo bar")
will be afoo
command, which has abar
subcommand. It is effectively the same as@Command("foo") @Subcommand("bar")
- Every command (and category) has a unique path, and can be retrieved from the CommandHandler using
commandHandler.getCommand(CommandPath)
andcommandHandler.getCategory(CommandPath)
. -
CommandPath
is the official representation of command paths. The path offoo bar
is equivalent toCommandPath.get("foo", "bar")
.
The following will contain examples on building simple commands.
Any pre-registering is included in each command, where the command handler is a ConsoleCommandHandler
and is represented by the variable handler
. Although, all command handler implementations should work similarly.
A command that returns the text inputted to it.
@Command({"ping", "echo"})
public void echo(CommandActor actor, @Default("") String message) {
actor.reply(message);
}
A simpler implementation
handler.registerResponseHandler(String.class, (response, actor, command) -> actor.reply(response));
Any String
returned from methods will automatically be sent to the actor.
@Command({"ping", "echo"})
public String echo(@Default("") String message) {
return message;
}
> echo Hello world!
Hello world!
> ping
(empty message)
A command that reads the content of a file.
We will use java.nio.Path
to read files.
handler.registerValueResolver(Path.class, context -> Paths.get(context.popForParameter()));
@Command("read")
public void readFile(CommandActor actor, Path file) throws IOException {
if (!Files.exists(file))
throw new CommandErrorException("No such file: " + file);
Files.readAllLines(file).forEach(actor::reply);
}
Executes:
> read
You must specify a value for the file!
> read not-exists.txt
No such file: not-exists.txt
> read example.yml
Hello, Lamp!
- A command for zipping the current directory
handler.registerValueResolver(File.class, context -> new File(context.popForParameter()));
public enum Compression {
GZIP,
ZLIB
}
@Command("zip pack")
public void zipFile(
CommandActor actor,
@Flag("output") @Optional File output,
@Flag("compression") @Default("GZIP") Compression compression
) throws IOException {
if (output == null) { // output was not specified
File runningDirectory = new File("dummy").getParentFile();
output = new File(runningDirectory, runningDirectory.getName() + ".zip");
}
if (output.exists())
throw new CommandErrorException("A file with name '" + output.getName() + "' already exists!");
output.createNewFile();
File directory = output.getParentFile();
// create ZIP here...
}
Execution:
> zip pack
(creates a zip with the dir name and GZIP)
> zip pack -output foo.zip
(zips with GZIP)
> zip pack -output foo.zip -compression zlib
(zips with zlib)
- A command for unzipping files
@Command("zip unpack")
public void unzipFile(
CommandActor actor,
File zipFile,
@Flag("compression") @Default("GZIP") Compression compression
) {
if (!zipFile.exists())
throw new CommandErrorException("No such file: " + zipFile.getName());
// unpack ZIP here...
}
Executes:
> zip unpack file.zip
(unzips with GZIP)
> zip unpack file.zip -compression zlib
(unzips with zlib)
handler.setSwitchPrefix("--");
handler.registerResponseHandler(String.class, (response, actor, command) -> actor.reply(response));
@Command("formatjson")
public String formatJSON(
String json,
@Switch("pretty-printing") boolean prettyPrinting,
@Switch("disable-html-escaping") boolean disableHtmlEscaping
) {
GsonBuilder builder = new GsonBuilder();
if (prettyPrinting)
builder.setPrettyPrinting();
if (disableHtmlEscaping)
builder.disableHtmlEscaping();
Gson gson = builder.create();
JsonElement element = JsonParser.parseString(json);
return gson.toJson(element);
}
Execution:
> formatjson [1,2,3]
[1,2,3]
> formatjson [1,2,3] --pretty-printing
[
1,
2,
3
]
👋 If you're having trouble, need support, or just feel like chatting, feel free to hop by our Discord server!