Skip to content

Commit 28dffbd

Browse files
committed
feat: finish internal and tips part, fix all broken links
1 parent 383dd4c commit 28dffbd

File tree

15 files changed

+875
-5
lines changed

15 files changed

+875
-5
lines changed

docs/.vitepress/config.mts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ const vitepressOptions: UserConfig = {
3434
description: "Website of CommandAPI",
3535
cleanUrls: true,
3636
metaChunk: true,
37-
ignoreDeadLinks: true, // TODO remove when all things are done
3837
rewrites: {
3938
'en/:rest*': ':rest*'
4039
},

docs/.vitepress/theme/style/global.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@
2525
}
2626

2727
.mermaid {
28-
display:flex; justify-content:center;align-items:center
28+
display: flex;
29+
justify-content: center;
30+
align-items: center;
2931
}

docs/en/create-commands/arguments/suggestions/suggestions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Because argument suggestions are arguably the most powerful feature that the Com
2626

2727
## The `ArgumentSuggestions` interface
2828

29-
The two methods above require an `ArgumentSuggestions` object, which is a functional interface that takes in a `SuggestionInfo` record and the current Brigadier `SuggestionsBuilder` and returns a `CompletableFuture<Suggestions>` object. This may sound a bit complicated, but this allows you to implement very powerful suggestions using a combination of the CommandAPI and raw Brigadier API methods. More information about using Brigadier-level suggestions can be found in the [brigadier suggestions](./brigadiersuggestions.md) section.
29+
The two methods above require an `ArgumentSuggestions` object, which is a functional interface that takes in a `SuggestionInfo` record and the current Brigadier `SuggestionsBuilder` and returns a `CompletableFuture<Suggestions>` object. This may sound a bit complicated, but this allows you to implement very powerful suggestions using a combination of the CommandAPI and raw Brigadier API methods. More information about using Brigadier-level suggestions can be found in the [brigadier suggestions](../../../internal/brigadier-suggestions) section.
3030

3131
To simplify this, the CommandAPI provides a number of methods to generate suggestions:
3232

docs/en/create-commands/requirements.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ So now we've added the ability to create a party if we're not already in it. Now
103103
<<< @/../reference-code/src/main/kotlin/createcommands/Requirements.kt#partySystemExampleStep4
104104
:::
105105

106-
Notice something here? There's some code repetition for the `withRequirement` method - this is the same predicate that we used earlier, except we remove the negation. If you are interested, you can view the section [Predicate tips](./predicatetips.md) for a method to improve code reuse.
106+
Notice something here? There's some code repetition for the `withRequirement` method - this is the same predicate that we used earlier, except we remove the negation. If you are interested, you can view the section [Predicate tips](../tips/predicate-tips) for a method to improve code reuse.
107107

108108
Once the arguments have been declared, we can now implement our party teleportation command:
109109

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
order: 2
3+
authors:
4+
- JorelAli
5+
- willkroboth
6+
- DerEchtePilz
7+
---
8+
9+
# Brigadier + CommandAPI
10+
11+
So far, we've been using only the CommandAPI to register commands. As a result, this makes the CommandAPI's features limited by whatever the CommandAPI has implemented. To push past these limits, the CommandAPI includes some extra methods to help with invoking brigadier methods. Of course, to use these methods, brigadier is required. The brigadier dependency's installation instructions can be found [here](https://github.com/Mojang/brigadier#installation).
12+
13+
> **Developer's Note:**
14+
>
15+
> For those that are unaware, [brigadier](https://github.com/Mojang/brigadier) is Mojang's command parser and dispatching framework. This is what the CommandAPI wraps around and is the main underlying source of its functionality.
16+
17+
The CommandAPI has been designed in such a way that you shouldn't have to access NMS in order to make use of the more "advanced" arguments and features - if you find that NMS is required to do something, [please make a new issue](https://github.com/CommandAPI/CommandAPI/issues/new/choose)!
18+
19+
## Brigadier support functions
20+
21+
The CommandAPI offers the following methods in the `dev.jorel.commandapi.Brigadier` class:
22+
23+
```java
24+
public static CommandDispatcher getCommandDispatcher();
25+
public static RootCommandNode getRootNode();
26+
public static LiteralArgumentBuilder fromLiteralArgument(LiteralArgument literalArgument);
27+
public static RedirectModifier fromPredicate(BiPredicate<CommandSender, Object[]> predicate, List<Argument> args);
28+
public static Command fromCommand(CommandAPICommand command);
29+
public static RequiredArgumentBuilder fromArgument(List<Argument> args, Argument<?> argument);
30+
public static RequiredArgumentBuilder fromArgument(Argument argument);
31+
public static SuggestionProvider toSuggestions(Argument<?> argument, List<Argument> args);
32+
public static Object[] parseArguments(CommandContext cmdCtx, List<Argument> args);
33+
public static Object getBrigadierSourceFromCommandSender(CommandSender sender);
34+
public static CommandSender getBukkitCommandSenderFromContext(CommandContext cmdCtx);
35+
```
36+
37+
Briefly, here's what each of these functions do (you can view the JavaDocs for more information):
38+
39+
| Method | Description |
40+
|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
41+
| `getCommandDispatcher` | Returns the Minecraft command dispatcher graph |
42+
| `getRootNode` | Returns the root node of the command dispatcher.<br>This is equivalent to using<br />`getCommandDispatcher().getRoot();` |
43+
| `fromLiteralArgument` | Creates a `LiteralArgumentBuilder` from a `LiteralArgument` |
44+
| `fromPredicate` | Converts a predicate and some arguments into a `RedirectModifier`. This can be used for the `fork` method in brigadier's `ArgumentBuilder` |
45+
| `fromCommand` | Converts a `CommandAPICommand` into a brigadier `Command` object |
46+
| `fromArgument` | Converts an argument, or a list of arguments, into a `RequiredArgumentBuilder` |
47+
| `toSuggestions` | Converts an argument's suggestions into brigadier's `SuggestionProvider`, with a list of previously declared arguments |
48+
| `parseArguments` | Parses a list of CommandAPI arguments into their respective objects for a provided `CommandContext` |
49+
| `getBrigadierSourceFromCommandSender` | Converts a Bukkit `CommandSender` into the NMS command sender source object |
50+
| `getBukkitCommandSenderFromContext` | Converts a Brigadier `CommandContext` into a Bukkit `CommandSender` |
51+
52+
## Examples
53+
54+
I hope these examples help understand how the CommandAPI can help with registering more "powerful" commands with the use of brigadier as well! Please bear with with it - these examples can be long, but I'm certain that they've been explained well and will be useful!
55+
56+
::::tip Example - Adding a predicate to the 'execute' command
57+
58+
Say we wanted to add a predicate to the `/execute` command. In this example, we'll create a predicate which handles random chances. To illustrate this, we want to be able to run commands such as:
59+
60+
```mccmd
61+
/execute if randomchance 1 4 run say Hello!
62+
```
63+
64+
In this scenario, if we ran this command, we would expect "Hello!" to appear in the chat with a $\frac{1}{4}$ chance. In particular, this is what we're trying to achieve:
65+
66+
- We want to create a predicate (true/false value) for the following syntax:
67+
68+
```mccmd
69+
randomchance <numerator> <denominator>
70+
```
71+
72+
- We also want this predicate to come _after_ `execute if`:
73+
74+
```mermaid
75+
graph TD
76+
A(execute) --> B(if)
77+
B --> C("randomchance&nbsp;&lt;numerator&gt;&nbsp;&lt;denominator&gt;")
78+
```
79+
80+
- After entering our predicate, we want to route back to `execute` (because the argument after `execute` is `run`, which is used in our example command above):
81+
82+
```mermaid
83+
graph TD
84+
A(execute) --> B(if)
85+
B --> C("randomchance&nbsp;&lt;numerator&gt;&nbsp;&lt;denominator&gt;")
86+
C --> D(execute)
87+
```
88+
89+
#### Writing the code
90+
91+
Now that we've established what we want, we can finally begin writing the code! First we want to create a literal `randomchance`. It's a literal because literal values don't change (similar to say `run` or `if` from the `/execute` command). To create a literal, we'll use the `fromLiteralArgument` method described above, and then build it using the `.build()` method:
92+
93+
:::tabs
94+
===Java
95+
<<< @/../reference-code/src/main/java/internal/BrigadierPlusCommandAPI.java#addPredicateExampleStep1
96+
===Kotlin
97+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierPlusCommandAPI.kt#addPredicateExampleStep1
98+
:::
99+
100+
With that completed, we can now create our "argument" to this predicate. To do this, we'll use the regular declaration of arguments that we would normally use for commands. In this example, because we're computing $\frac{numerator}{denominator}$, we want our numerator to be 0 or greater and our denominator to be 1 or greater (we don't want any negative numbers or division by zero!):
101+
102+
:::tabs
103+
===Java
104+
<<< @/../reference-code/src/main/java/internal/BrigadierPlusCommandAPI.java#addPredicateExampleStep2
105+
===Kotlin
106+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierPlusCommandAPI.kt#addPredicateExampleStep2
107+
:::
108+
109+
Now we're going to get into the very nitty-gritty part - the predicate declaration. First, we'll create some variables `numerator` and `denominator` to represent the brigadier instances of these arguments. This can be handled by using the `Brigadier.argBuildOf` function:
110+
111+
:::tabs
112+
===Java
113+
<<< @/../reference-code/src/main/java/internal/BrigadierPlusCommandAPI.java#addPredicateExampleStep3
114+
===Kotlin
115+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierPlusCommandAPI.kt#addPredicateExampleStep3
116+
:::
117+
118+
Now we'll define our predicate. Since this is sort of a "meta-command" (it directly affects the outcome of the `run` command), we need to use the `ArgumentBuilder`'s `fork` method. Remember that after we run this predicate, we want to link back to `execute` again, so our first argument is the `CommandNode` for `execute`, which we can get using `Brigadier.getRootNode().getChild("execute")`. Then, we can simply use `Brigadier.fromPredicate` to finish our declaration:
119+
120+
:::tabs
121+
===Java
122+
<<< @/../reference-code/src/main/java/internal/BrigadierPlusCommandAPI.java#addPredicateExampleStep4
123+
===Kotlin
124+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierPlusCommandAPI.kt#addPredicateExampleStep4
125+
:::
126+
127+
Finally, we can now link everything up. We know that `numerator` comes first, **then** `denominator`, so we have to have `numerator.then(denominator)`. We also know that these arguments are the **children** of the `randomChance` literal, so we use the following code to state all of this:
128+
129+
:::tabs
130+
===Java
131+
<<< @/../reference-code/src/main/java/internal/BrigadierPlusCommandAPI.java#addPredicateExampleStep5
132+
===Kotlin
133+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierPlusCommandAPI.kt#addPredicateExampleStep5
134+
:::
135+
136+
Finally, we "register" the command. In this case, we're actually just adding the `randomChance` node under `execute → if`, which we can add using the following code:
137+
138+
:::tabs
139+
===Java
140+
<<< @/../reference-code/src/main/java/internal/BrigadierPlusCommandAPI.java#addPredicateExampleStep6
141+
===Kotlin
142+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierPlusCommandAPI.kt#addPredicateExampleStep6
143+
:::
144+
145+
#### Code summary
146+
147+
So, hopefully that wasn't too confusing! If you're still lost, here's the whole code that we wrote:
148+
149+
:::tabs
150+
===Java
151+
<<< @/../reference-code/src/main/java/internal/BrigadierPlusCommandAPI.java#addPredicateExample
152+
===Kotlin
153+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierPlusCommandAPI.kt#addPredicateExample
154+
:::
155+
156+
::::
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
order: 3
3+
authors:
4+
- JorelAli
5+
- willkroboth
6+
- DerEchtePilz
7+
---
8+
9+
# Brigadier Suggestions
10+
11+
As described in [The ArgumentSuggestions interface](../create-commands/arguments/suggestions/suggestions#the-argumentsuggestions-interface), the `ArgumentSuggestions` interface has the following default method:
12+
13+
```java
14+
@FunctionalInterface
15+
public interface ArgumentSuggestions<CommandSender> {
16+
17+
/**
18+
* Create a {@link CompletableFuture} resolving onto a brigadier {@link Suggestions} object.
19+
* @param info The suggestions info
20+
* @param builder The Brigadier {@link SuggestionsBuilder} object
21+
* @return a {@link CompletableFuture} resolving onto a brigadier {@link Suggestions} object.
22+
*
23+
* @throws CommandSyntaxException if there is an error making suggestions
24+
*/
25+
CompletableFuture<Suggestions> suggest(SuggestionInfo<CommandSender> info, SuggestionsBuilder builder)
26+
throws CommandSyntaxException;
27+
28+
}
29+
```
30+
31+
This allows you to use Brigadier's `SuggestionsBuilder` and `Suggestions` classes to create more powerful suggestions beyond the basic capabilities of the CommandAPI.
32+
33+
In order to use this, you will need the Brigadier dependency, which you can find under the [Brigadier installation instructions](https://github.com/Mojang/brigadier#installation).
34+
35+
::::tip Example - Making an emoji broadcasting message
36+
37+
Say we want to let users broadcast a message, but also allow them to enter emojis into the message they're typing:
38+
39+
![A gif showcasing a command where emojis are suggested when typing a message](/images/emojimsg.gif)
40+
41+
For this command, we'll use a `GreedyStringArgument` as if we were making a generic broadcasted message. We create a map of emojis to their descriptions to use as tooltips and then we use Brigadier to display the suggestions at the end of the message where the cursor is.
42+
43+
:::tabs
44+
===Java
45+
<<< @/../reference-code/src/main/java/internal/BrigadierSuggestions.java#emojiCommandExample
46+
===Kotlin
47+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierSuggestions.kt#emojiCommandExample
48+
:::
49+
50+
In this example, we simply create the `GreedyStringArgument` and use `replaceSuggestions()` to specify our suggestion rules. We create an offset using the current builder to make suggestions start at the last character (the current builder start `builder.getStart()` and the current length of what the user has already typed `info.currentArg().length()`). Finally, we build the suggestions with `builder.buildFuture()` and then register our command as normal.
51+
52+
::::
53+
54+
::::tip Example - Using a Minecraft command as an argument
55+
56+
:::info
57+
58+
This example has been superseded by the [Command argument](../create-commands/arguments/types/command-arguments). This example is still present as it gives an example of much more complicated brigadier suggestions which may be useful for readers!
59+
60+
:::
61+
62+
Courtesy of [469512345](https://github.com/469512345), the following example shows how using Brigadier's suggestions and parser can be combined with the CommandAPI to create an argument which suggests valid Minecraft commands. This could be used for example as a `sudo` command, to run a command as another player.
63+
64+
![A gif showcasing a command suggestion for the /give command](/images/commandargument.gif)
65+
66+
For this command, we'll use a `GreedyStringArgument` because that allows users to enter any combination of characters (which therefore, allows users to enter any command). First, we start by defining the suggestions that we'll use for the `GreedyStringArgument`. We'll use the `ArgumentSuggestions` functional interface described above:
67+
68+
:::tabs
69+
===Java
70+
<<< @/../reference-code/src/main/java/internal/BrigadierSuggestions.java#commandArgumentsExampleStep1
71+
===Kotlin
72+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierSuggestions.kt#commandArgumentsExampleStep1
73+
:::
74+
75+
There's a lot to unpack there, but it's generally split up into 4 key sections:
76+
77+
- **Finding the start of the argument**. We find the start of the argument so we know where the beginning of our command suggestion is. This is done easily using `builder.getStart()`, but we also have to take into account any spaces if our command argument contains spaces.
78+
79+
- **Parsing the command argument**. We make use of Brigadier's `parse()` method to parse the argument and generate some `ParseResults`.
80+
81+
- **Reporting parsing errors**. This is actually an optional step, but in general it's good practice to handle exceptions stored in `ParseResults`. While [Brigadier doesn't actually handle suggestion exceptions](https://github.com/Mojang/brigadier/blob/master/src/main/java/com/mojang/brigadier/CommandDispatcher.java#L599), this has been included in this example to showcase exception handling.
82+
83+
- **Generating suggestions from parse results**. We use our parse results with Brigadier's `getCompletionSuggestions()` method to generate some suggestions based on the parse results and the suggestion string range.
84+
85+
Now that we've declared our arguments suggestions, we can then create our simple command with the following syntax:
86+
87+
```mccmd
88+
/commandargument <command>
89+
```
90+
91+
We use the command suggestions declared above by using the `replaceSuggestions` method in our `GreedyStringArgument`, and write a simple executor which runs the command that the user provided:
92+
93+
:::tabs
94+
===Java
95+
<<< @/../reference-code/src/main/java/internal/BrigadierSuggestions.java#commandArgumentsExampleStep2
96+
===Kotlin
97+
<<< @/../reference-code/src/main/kotlin/internal/BrigadierSuggestions.kt#commandArgumentsExampleStep2
98+
:::
99+
100+
::::

0 commit comments

Comments
 (0)