Skip to content

Commit cb97d77

Browse files
committed
wip: requirements.md
1 parent e6c46a2 commit cb97d77

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
---
2+
order: 7
3+
authors:
4+
- JorelAli
5+
- willkroboth
6+
- DerEchtePilz
7+
---
8+
9+
# Requirements
10+
11+
Requirements is a feature that allows you to put a constraint on commands and arguments. Similar to permissions, a requirement is something that must be fulfilled in order to use a given command or argument.
12+
13+
This section is broken up into four parts:
14+
15+
- Adding requirements to commands
16+
- Adding requirements to arguments
17+
- Updating requirements
18+
- Multiple requirements
19+
20+
**Please don't skip the section on updating requirements** - the last section is necessary to get requirements to work as you'd want!
21+
22+
## Adding requirements to commands
23+
24+
To add a requirement to a command, similar to adding permissions to commands, use the `withRequirement` method:
25+
26+
```java
27+
CommandAPICommand withRequirement(Predicate<CommandSender> sender);
28+
```
29+
30+
The `withRequirement` method requires a predicate that determines if the sender is able to run the command – if the predicate is satisfied, then the command sender will be able to execute that command.
31+
32+
::::tip Example – Perks based on a player's level
33+
34+
Say we have a perks-based command system that depends on a player's level. For example, if a player has over 30 levels of experience, they would then be able to run a command that lets them repair the item in their hand in exchange for 30 levels. As such, we'll use the following command syntax:
35+
36+
```mccmd
37+
/repair
38+
```
39+
40+
We want to put a requirement on this command that the player needs to have at least 30 levels of experience in order to run the command – if the player has less than 30 levels, the player should not be able to run the command. The easiest way to make the player not able to run the command is to literally tell the user that the command doesn't exist. That's what requirements do in the CommandAPI:
41+
42+
:::tabs
43+
===Java
44+
45+
===Kotlin
46+
47+
:::
48+
49+
It's important to note that in this example, we case the `sender` to a `player` for the requirement method. We know that the sender is definitely a player because we use `executesPlayer()`, which ensures that this is the case. Now that we've got this, **we need to make sure we update the player's requirements _when their exp changes_**. This is covered in more detail in the section about updating requirements below.
50+
51+
::::
52+
53+
## Adding requirements to arguments
54+
55+
In a similar way that you can restrict certain arguments by adding permissions to them, you can restrict them by using arbitrary predicates by using the `withRequirement` method on the arguments themselves.
56+
57+
::::tip Example – A party creation and teleportation system
58+
59+
Let's say that we're working on a plugin that has a system to form groups of players called "parties". If you are not already in a party, you can create one of your own and if you are in a party, you can teleport to any other member in your party.
60+
61+
For this example, we'll use the following command syntax:
62+
63+
```mccmd
64+
/party create <partyName>
65+
/party tp <player>
66+
```
67+
68+
To represent our party in code, we'll use a simple `Map` called `partyMembers` which maps the player's UUID to the name of their registered party:
69+
70+
:::tabs
71+
===Java
72+
73+
===Kotlin
74+
75+
:::
76+
77+
To begin with, let's create the `/party create <partyName>` command. First, we must declare our arguments:
78+
79+
:::tabs
80+
===Java
81+
82+
===Kotlin
83+
84+
:::
85+
86+
In this argument declaration, we put a requirement on the literal `create`, where the player does not have a party. In other words, if the player does not have a party, they are allowed to run `/party create <partyName>`. If a player already has a party, then they won't be allowed to run this command.
87+
88+
Now that we've declared our arguments, we can now declare our main command `/party create <partyName>`. We populate it with the arguments, and we create an entry in our `partyMembers` with the player's UUID and the name of the party that they created. Since this updates the requirements of the player, we'll have to make sure we update it (which is covered in more detail in the section about updating requirements below) – until then, I'll omit this from the code:
89+
90+
:::tabs
91+
===Java
92+
93+
===Kotlin
94+
95+
:::
96+
97+
So now we've added the ability to create a party if we're not already in it. Now we need to implement our `party tp <player>` command. Again, we must start by declaring our arguments:
98+
99+
:::tabs
100+
===Java
101+
102+
===Kotlin
103+
104+
:::
105+
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.
107+
108+
Once the arguments have been declared, we can now implement our party teleportation command:
109+
110+
:::tabs
111+
===Java
112+
113+
===Kotlin
114+
115+
:::
116+
117+
What's important to note in this example is that if you spend the time to set up the arguments properly, it severely decreases the amount of code required to write your command. This makes the commands you declare easier to understand and follow and you don't end up having to put all of these checks in the body of your command executor.
118+
119+
::::
120+
121+
## Updating requirements
122+
123+
Finally, the part you've all been waiting for - how to update requirements. With the way requirements work, they need to be updated manually. To illustrate why this is the case, I'll explain using [the example of the /repair command](#example-perks-based-on-a-players-level):
124+
125+
When a player joins the game, the server tells the client the list of all commands that the client can run _(don't worry, this is completely normal, as declared [here](https://wiki.vg/Protocol#Declare_Commands))_. Let's say that the player has joined and has less than 30 levels.
126+
127+
When a player has less than 30 levels, they are unable to execute the `/repair` command, because the list of commands that the server sent to the client did not contain the `/repair` command. Eventually, the player will fight some mobs or mine some ores and eventually will reach 30 levels. Despite this, the player's client doesn't actually know that they're now able to use the `/repair` command until the server tells them. As such, the server needs to somehow update the requirements that a player has so a player knows they can run the command.
128+
129+
The CommandAPI handles this in a very simple method call:
130+
131+
```java
132+
CommandAPI.updateRequirements(player);
133+
```
134+
135+
<div class="warning">
136+
137+
**Developer's Note:**
138+
139+
The `CommandAPI.updateRequirements(player);` method can be used anywhere, **except** for the `withRequirement` method. Using it inside this method will crash the server. This is by design – just make sure you don't use it within the `withRequirement` method and everything will be fine!
140+
141+
</div>
142+
143+
To illustrate how to use this, we'll go over the two examples above:
144+
145+
::::tip Example - Updating requirements for xp changes
146+
147+
In [the example of requirements with the /repair command](#example-perks-based-on-a-players-level), we needed to ensure that the player's requirements update when their experience changes. To do this, we'll simply use a normal event to check this:
148+
149+
```java
150+
@EventHandler
151+
public void onExpChange(PlayerExpChangeEvent event) {
152+
CommandAPI.updateRequirements(event.getPlayer());
153+
}
154+
```
155+
156+
And of course, you have to ensure that this event is registered in your `onEnable()` method:
157+
158+
```java
159+
@Override
160+
public void onEnable() {
161+
getServer().getPluginManager().registerEvents(this, this);
162+
}
163+
```
164+
165+
:::info
166+
167+
I'm assuming you already know how to register events and don't need me to go into great detail how to do so, take the code above with a pinch of salt – I know how much everyone likes to divide their event handlers and listeners to organise their code.
168+
169+
:::
170+
171+
::::
172+
173+
::::tip Example - Updating requirements for the party creation example
174+
175+
In the [example for a party creation](#example-a-party-creation-and-teleportation-system), we declared two commands:
176+
177+
```mccmd
178+
/party create <partyName>
179+
/party tp <player>
180+
```
181+
182+
When a player creates a new party, we need to ensure that their requirements are updated _when they create the party_. As such, we simply add this to our party creation command executor:
183+
184+
:::tabs
185+
===Java
186+
187+
===Kotlin
188+
189+
:::
190+
191+
That's it!
192+
193+
::::
194+
195+
## Multiple requirements
196+
197+
The CommandAPI lets you handle multiple requirements really easily! The `withRequirement` method can be called multiple times, so you don't have to worry about shoving everything in one expression.
198+
199+
::::tip Example – Using multiple requirements
200+
201+
For example, you can apply multiple requirements for a command by calling the `withRequirement` method multiple times:
202+
203+
:::tabs
204+
===Java
205+
206+
===Kotlin
207+
208+
:::
209+
210+
::::

0 commit comments

Comments
 (0)