Skip to content

Commit 274983d

Browse files
committed
feat: annotation-based commands part
1 parent 48c00ff commit 274983d

File tree

7 files changed

+555
-0
lines changed

7 files changed

+555
-0
lines changed

docs/en/annotations/annotations.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
---
2+
order: 2
3+
authors:
4+
- JorelAli
5+
---
6+
7+
# Annotations
8+
9+
This page outlines in detail the list of all annotations that the CommandAPI's annotation-based command system includes.
10+
11+
## Annotations that go on classes
12+
13+
### `@Command("commandName")`
14+
15+
The `@Command` annotation is used to declare a command. The parameter is the name of the command that will be registered.
16+
17+
<<< @/../reference-code/src/main/java/annotations/Intro.java#declareCommand
18+
19+
### `@Alias({...})`
20+
21+
The `@Alias` annotation is used to declare a list of aliases for a command. The parameter is a list of aliases which can be used for the command.
22+
23+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#aliasClassExample
24+
25+
### `@Permission("permissionNode")`
26+
27+
The `@Permission` annotation is used to add a permission node to a command. Users that want to run this command must have this permission. The parameter is the permission node required to run the command.
28+
29+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#permissionClassExample
30+
31+
### `@NeedsOp`
32+
33+
The `@NeedsOp` annotation is used to indicate that a command needs to have operator privileges to run it. This annotation has no parameters.
34+
35+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#needsOpClassExample
36+
37+
### `@Help("Full description")`
38+
39+
The `@Help` annotation is used to add a help topic to a command. This annotation can take two forms:
40+
41+
A simple form which just uses a string which is used as the full description for a command:
42+
43+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#helpClassExample
44+
45+
A form with two parameters `value` and `shortDescription`, to provide the full description (`value`) and short description (`shortDescription`) content for a command:
46+
47+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#shortHelpClassExample
48+
49+
## Annotations that go on methods
50+
51+
To use annotations on methods, **methods must be static**.
52+
53+
### `@Default`
54+
55+
The `@Default` annotation indicates that the method is _not_ a subcommand. This acts in a similar way to regular Bukkit commands. Commands with the `@Default` annotation can be used to run the main code when the command named with the `@Command` annotation is stated, such as the following:
56+
57+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#defaultMethodExample
58+
59+
The `@Default` annotation does not mean that the command can't have arguments! Arguments can still be used and declared as shown:
60+
61+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#defaultWithArgsMethodExample
62+
63+
### `@Subcommand`
64+
65+
The `@Subcommand` simply tells the CommandAPI that the declared method is a subcommand. This acts in a similar way to the regular CommandAPI's `.withSubcommand()` method. The subcommand annotation can take in a single string which is the name of the subcommand:
66+
67+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#subcommandMethodExample
68+
69+
Or, it can take in a list of strings which represent the _aliases_ that can also be used for the declared subcommand:
70+
71+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#subcommandAliasesMethodExample
72+
73+
### `@Permission`
74+
75+
The `@Permission` annotation can also be used on methods to indicate that a permission is required to execute a command.
76+
77+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#permissionMethodExample
78+
79+
### `@NeedsOp`
80+
81+
The `@NeedsOp` annotation can also be used on methods to indicate that the user must be an operator to run the command.
82+
83+
## Annotations that go on parameters
84+
85+
The annotations for arguments are really simple, there's just two things you need to know:
86+
87+
- To use an annotation argument, just add the letter `A` (for 'annotation') at the beginning of it! For example:
88+
89+
$$\begin{align}
90+
\texttt{StringArgument}&\xrightarrow{A}\texttt{@AStringArgument}\\\\
91+
\texttt{PlayerArgument}&\xrightarrow{A}\texttt{@APlayerArgument}\\\\
92+
\texttt{AdvancementArgument}&\xrightarrow{A}\texttt{@AAdvancementArgument}\\\\
93+
&\hspace{0.75em}\vdots
94+
\end{align}$$
95+
96+
For example, we use `@AStringArgument` to indicate that this command takes a `StringArgument` as its first parameter:
97+
98+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#simpleParameterExample
99+
100+
- **The name of the argument (referred to as "nodeName" in the normal CommandAPI system) is the name of the variable assigned to the parameter.** In the above code, this means that the name of the argument is `warpName`.
101+
102+
### Special argument annotations
103+
104+
Certain argument annotations have extra parameters that can be supplied to provide additional customization:
105+
106+
#### Numerical arguments
107+
108+
The following numerical arguments can take both a `min` and `max` value. Both of these are completely optional. This indicates the range of values (inclusive) that is valid for this argument. For example:
109+
110+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#numericalParameterExample
111+
112+
#### Literal arguments
113+
114+
Both the `LiteralArgument` and `MultiLiteralArgument` can be used. When these are used, the name of the variable assigned to the parameter is _ignored_ and not used as the argument's name.
115+
116+
For the `@ALiteralArgument` annotation, the parameter is the literal to be used for the command. For the `@AMultiLiteralArgument`, the parameter can be an array of multiple literals to use:
117+
118+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#literalParameterExample
119+
120+
#### Other arguments
121+
122+
The `LocationArgument`, `Location2DArgument`, `EntitySelectorArgument` and `ScoreHolderArgument` can all take an extra parameter in their constructors. As a result, the annotation-equivalent of these arguments also allow you to provide the parameter in the annotation:
123+
124+
<<< @/../reference-code/src/main/java/annotations/Annotations.java#otherParameterExample

docs/en/annotations/intro.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
order: 1
3+
authors:
4+
- JorelAli
5+
---
6+
7+
# Annotation-based commands
8+
9+
The CommandAPI also includes a very small lightweight annotation-based command framework. This works very differently compared to previous commands shown in this documentation and **it is less feature-rich than registering commands using the other methods.**
10+
11+
In short, the CommandAPI's annotation-based system:
12+
13+
- Has no runtime overhead compared to using the regular command registration system (unlike other annotation-based frameworks such as [ACF](https://github.com/aikar/commands)).
14+
- Reduces code bloat (to an extent).
15+
- Improves readability since commands are declared declaratively instead of imperatively.
16+
- Is not as powerful as the regular command registration system.
17+
18+
:::info
19+
20+
Currently, the annotation framework is in its infancy, so any suggestions or improvements are heavily appreciated!
21+
22+
:::
23+
24+
:::danger Developer's Note:
25+
26+
As of the time of writing, annotation-based commands are **not** compatible with the Kotlin programming language! The CommandAPI does have the [Kotlin DSL](../kotlin-dsl/intro.md) instead, which is leaner, cleaner and provides a much more Kotliny experience!
27+
28+
:::
29+
30+
Before we go into too much detail, let's take a look at an example of what this annotation framework looks like, and compare this to the existing method.
31+
32+
## Example: A warp command
33+
34+
Let's say we're writing a plugin with the capability to create warps to places on the server. To do this, we'll make a simple command `/warp`, defined as follows:
35+
36+
```mccmd
37+
/warp - Shows help
38+
/warp <warp> - Teleports a player to <warp>
39+
/warp create <name> - Creates a new warp <name> at the player's location
40+
```
41+
42+
### Warp command (without annotations)
43+
44+
Using the regular CommandAPI, this is one way we can create this command. In the code below, we use StringArguments to represent the warp names. To teleport to a warp, we also populate it with suggestions (deferred so it updates), and also use a subcommand to represent `/warp create`:
45+
46+
<<< @/../reference-code/src/main/java/annotations/Intro.java#legacyExample
47+
48+
Seems fairly straightforward, given everything else covered in this documentation. Now let's compare it to using annotations!
49+
50+
### Warp command (with annotations)
51+
52+
I think it's best to show the example and the explain it afterwards:
53+
54+
<<< @/../reference-code/src/main/java/annotations/Intro.java#annotationsExample
55+
56+
<<< @/../reference-code/src/main/java/annotations/Intro.java#annotationsRegisterExample
57+
58+
As we can see, the code certainly _looks_ very different to the normal registration method. Let's take it apart piece by piece to see what exactly is going on here.
59+
60+
#### Command declaration
61+
62+
<<< @/../reference-code/src/main/java/annotations/Intro.java#declareCommand
63+
64+
Firstly, we declare our command `warp`. To do this, we use the `@Command` annotation and simply state the name of the command in the annotation. This annotation is attached to the class `WarpCommand`, which indicates that the whole class `WarpCommand` will be housing our command.
65+
66+
The annotation framework is designed in such a way that an entire command is represented by a single class. This provides a more modular approach to command declaration that allows you to easily contain the methods of a command in one location.
67+
68+
#### Default command
69+
<<< @/../reference-code/src/main/java/annotations/Intro.java#defaultExample
70+
71+
Here, declare the main command implementation using the `@Default` annotation. The `@Default` annotation informs the CommandAPI that the method it is attached to does not have any subcommands. This is effectively the same as registering a regular command without using `.withSubcommand()`.
72+
73+
Here, we simply write what happens when no arguments are run (i.e. the user just runs `/warp` on its own). As such, we don't include any parameters to our method.
74+
75+
#### Default command (again!)
76+
77+
<<< @/../reference-code/src/main/java/annotations/Intro.java#anotherDefaultExample
78+
79+
We also have a second `@Default` annotated method, which handles our `/warp <warp>` command. Because this isn't a subcommand (the warp to teleport to is not a subcommand, it's an argument), we still using the `@Default` annotation. In this method, we include an argument with this command by using the `@AStringArgument` annotation. This argument uses the `StringArgument` class, and the name of this argument is "warpName", which is extracted from the name of the variable. Simply put, **the Annotation for an argument is A** followed by the name of the argument. This is synonymous with using the following:
80+
81+
```java
82+
new StringArgument("warp")
83+
```
84+
85+
It's also very important to note the parameters for this method. The first parameter is a `Player` object, which represents our command sender. The CommandAPI's annotation system uses the fact that the command sender is a `Player` object and automatically ensures that anyone using the command must be a `Player`. In other words, non-players (such as the console or command blocks), would be unable to execute this command.
86+
87+
The second argument is a `String` object, which represents the result of our argument "warp". The CommandAPI's annotation system can also infer the return type of the argument that is provided to it (in this case, a `StringArgument` will produce a `String`) and will automatically cast and provide the result to that parameter.
88+
89+
#### Subcommand
90+
91+
<<< @/../reference-code/src/main/java/annotations/Intro.java#subcommandExample
92+
93+
Lastly, we declare a subcommand to allow us to run `/warp create <name>`. To do this, we simply use the `@Subcommand` annotation. In this example, we also apply a permission node that is required to run the command by using the `@Permission` annotation. The rest is fairly straight forward - we declare an argument, in this case it's another `StringArgument` , so we use `@AStringArgument` and then declare everything else in a similar fashion to the default command executor.
94+
95+
#### Registering the command
96+
97+
Registering the command is fairly simple and is a one liner:
98+
99+
<<< @/../reference-code/src/main/java/annotations/Intro.java#annotationsRegisterExample
100+
101+
This line can be placed in your `onEnable()` or `onLoad()` method like you were registering a normal command.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
order: 3
3+
authors:
4+
- JorelAli
5+
---
6+
7+
# Registering annotation-based commands
8+
9+
Registering annotation-based commands is _really_ simple. To do this, we use the following method, where `className` is the name of a class with a `@Command` annotation:
10+
11+
```java
12+
CommandAPI.registerCommand(className)
13+
```
14+
15+
::::tip Example: Registering a Warp command
16+
17+
Say we have a simple command `/warp` that is defined as follows:
18+
19+
<<< @/../reference-code/src/main/java/annotations/Intro.java#annotationsExample
20+
21+
We can register this in our `onLoad()` method so we can use this command from within Minecraft functions:
22+
23+
<<< @/../reference-code/src/main/java/annotations/Registration.java#registerCommand
24+
25+
::::

reference-code/pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@
6060
</execution>
6161
</executions>
6262
</plugin>
63+
<plugin>
64+
<groupId>org.apache.maven.plugins</groupId>
65+
<artifactId>maven-compiler-plugin</artifactId>
66+
<version>3.8.1</version>
67+
<configuration>
68+
<annotationProcessorPaths>
69+
<path>
70+
<groupId>dev.jorel</groupId>
71+
<artifactId>commandapi-annotations</artifactId>
72+
<version>9.6.2-SNAPSHOT</version>
73+
</path>
74+
</annotationProcessorPaths>
75+
</configuration>
76+
</plugin>
6377
</plugins>
6478
</build>
6579

@@ -104,6 +118,12 @@
104118
<artifactId>commandapi-bukkit-kotlin</artifactId>
105119
<version>${commandapi.version}</version>
106120
</dependency>
121+
<dependency>
122+
<groupId>dev.jorel</groupId>
123+
<artifactId>commandapi-annotations</artifactId>
124+
<version>${commandapi.version}</version>
125+
<scope>provided</scope>
126+
</dependency>
107127

108128
<!-- Other -->
109129
<dependency>

0 commit comments

Comments
 (0)