Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add more documentation #33

Merged
merged 5 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/annotated/annotated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## Annotated Commands

`command-flow` allows developers to create full command trees in a declarative way
using annotated classes and methods.

The annotated command API is just an alternative way to create `Command` instances,
we use this instead of the classic `Command.builder(String)` method.

### Comparison

You can make a side-by-side comparison of the two approaches in the following table
and links:

| Imperative | Annotated |
|--------------------------------------------------------------------------|-----------------------------------------------------------------------|
| [Basic Command Creation](../imperatively/basic.md) | [Basic Command Creation](../annotated/basic.md) |
| [Command with single Argument](../imperatively/argument.md) | [Command with single Argument](../annotated/argument.md) |
| [Command with multiple Arguments](../imperatively/multiple-arguments.md) | [Command with multiple Arguments](../annotated/multiple-arguments.md) |
| [Command with optional Arguments](../imperatively/optional-arguments.md) | [Command with optional Arguments](../annotated/optional-arguments.md) |
| [Command with Permission](../imperatively/with-permission.md) | [Command with Permission](../annotated/with-permission.md) |
| [Command with Switch Arguments](../imperatively/with-switches.md) | [Command with Switch Arguments](../annotated/with-switches.md) |
| [Command with Value Flags](../imperatively/with-value-flags.md) | [Command with Value Flags](../annotated/with-value-flags.md) |

### Elements of the Annotated Command API

The annotated command API is composed of:
- The `@Command` annotation and others.
- The `AnnotatedCommandTreeBuilder` interface
20 changes: 20 additions & 0 deletions docs/annotated/argument.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## #2 With Arguments

Check this command example with a single argument:

```java
@Command(names = "hello")
public class TestCommand implements CommandClass {

@Command(names = "")
public void run(String name) {
System.out.println("Hi " + name);
}

}
```

In this example:
- Executing `hello yusshu` will print `Hi yusshu`
- Executing `hello Fixed` will print `Hi Fixed`
- Executing `hello` will result in an error
18 changes: 18 additions & 0 deletions docs/annotated/basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## #1 Basic Command

Check this basic command example, with no arguments, no subcommands and no permissions,
using the annotated command creation approach:

```java
@Command(names = "test")
public class TestCommand implements CommandClass {

@Command(names = "")
public void run() {
System.out.println("Hello World!");
}

}
```

Executing `test` will print `Hello World!`
78 changes: 78 additions & 0 deletions docs/annotated/command-class.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
## Command Class

The annotated command API has a `CommandClass` interface used to mark a class as a container
of commands or a command itself.

To declare the commands, we use annotations:

<!--@formatter:off-->
```java
@Command(names = "teleport")
public class TeleportCommand implements CommandClass {

@Command(names = "") // empty name indicates the root command
public void run(
@Sender Player sender, // the sender player
Player target // the target player (argument)
) {
sender.teleport(target);
}

}
```
<!--@formatter:on-->

In this example we have created a `/teleport` command that takes a player name
as an argument *(command-flow will parse it and find the right Player)* and
teleports the sender to the target player.

The `@Command` annotation is used to mark a method as a command, it can be used
on methods inside a `CommandClass` or on the class itself.

See this other example:

<!--@formatter:off-->
```java
@Command(names = { "friends", "friend", "f", "fr" }) // name, ...aliases
public class FriendsCommand implements CommandClass {

@Command(names = "add")
public void add(@Sender Player sender, Player target) {
// add 'target' to 'sender' friend list
}

@Command(names = "remove")
public void remove(@Sender Player sender, Player target) {
// remove 'target' from 'sender' friend list
}

@Command(names = "list")
public void list(@Sender Player sender) {
// list all 'sender' friends and show
}

@Command(names = "broadcast")
public void broadcast(@Sender Player sender, @Text String message) {
// send 'message' to all the friends of 'sender'
}

}
```
<!--@formatter:on-->

In this example we have created a `/friends` command with 4 subcommands:
- `/friends add <player>`: add a player to the sender friend list.
- `/friends remove <player>`: remove a player from the sender friend list.
- `/friends list`: list all the sender friends.
- `/friends broadcast <...message>`: send a message to all the sender friends.

In this new example we also see these new stuff:
- `@Command(names = {...})`: multiple names! The first one is considered the actual
command name and the rest are aliases.
- `@Text`: used to mark a parameter as a text argument, this means that it will
consume all the arguments left in the command and join them with spaces.

### Registering them

To register command classes we must convert them to `Command` first, we can do
that using the `AnnotatedCommandTreeBuilder` interface, check the next page!
35 changes: 35 additions & 0 deletions docs/annotated/command-tree-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Command Builder

The `AnnotatedCommandTreeBuilder` is the interface responsible for building `Command`
instances from annotated commands and classes.

### Creating an AnnotatedCommandTreeBuilder

To create a `AnnotatedCommandTreeBuilder` we must create a `PartInjector` first and
*(optionally)* a `SubCommandInstanceCreator`. More about this in the next pages.

```java
PartInjector injector = ...;
SubCommandInstanceCreator instanceCreator = ...;

AnnotatedCommandTreeBuilder builder = AnnotatedCommandTreeBuilder.create(injector, instanceCreator);
```

### Building a Command

Now that we have a `AnnotatedCommandTreeBuilder` we can build a command (or multiple commands).
To do this we must call the `fromClass` method with the class of the command we want to build.

Note that this method returns a list of commands, since a single class may contain multiple
root commands on it.

```java
// create the builder
AnnotatedCommandTreeBuilder builder = ...;

// build the Command list from our class
List<Command> commands = builder.fromClass(MyCommand.class);

// now we can register them using the CommandManager#registerCommands convenience method
commandManager.registerCommands(commands);
```
12 changes: 12 additions & 0 deletions docs/annotated/index.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
annotated.md
command-class.md
command-tree-builder.md
part-injector.md
subcommand-instance-creator.md
basic.md
argument.md
multiple-arguments.md
optional-arguments.md
with-permission.md
with-switches.md
with-value-flags.md
25 changes: 25 additions & 0 deletions docs/annotated/multiple-arguments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## #3 Command with Multiple Args

This example shows how to create a command with multiple arguments
using the annotated approach:

```java
@Command(names = "greet")
public class GreetingCommand implements CommandClass {

@Command(names = "")
public void run(String name, boolean formal) {
if (formal) {
System.out.println("Hello, " + name + "!");
} else {
System.out.println("Hi, " + name + "!");
}
}

}
```

- Executing `greet John false` will print `Hi, John!`
- Executing `greet John true` will print `Hello, John!`
- Executing `greet John` will result in a usage error
- Executing `greet` will result in a usage error
24 changes: 24 additions & 0 deletions docs/annotated/optional-arguments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## #4 With Optional Args

This example shows how to create a command with optional arguments
with the annotated approach:

```java
@Command(names = "greet")
public class GreetingCommand implements CommandClass {

@Command(names = "")
public void run(String name, @OptArg("Mr.") String title) {
System.out.println("Hello, " + title + " " + name + "!");
}

}
```

The `@OptArg` annotation is used to mark an argument as optional, and
it accepts a default value as a parameter, which will be used if the
argument is not present in the input.

- Executing `greet John` will print `Hello, Mr. John!`
- Executing `greet John Dr.` will print `Hello, Dr. John!`
- Executing `greet John Mr.` will print `Hello, Mr. John!`
34 changes: 34 additions & 0 deletions docs/annotated/part-injector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Part Injector

The `PartInjector` is a registry which holds the registered PartFactories
and PartModifiers.

- A `PartFactory` is a class that provides a way to create an specific type of part.
- A `PartModifier` is like a PartFactory, the difference is that it may wrap the original part
or modify it instead of creating a new one.

### Creating a PartInjector

Creating a `PartInjector` is simple, just do the following:

```java
PartInjector partInjector = PartInjector.create();

// install the default bindings!
// parts for native and core types like String, Boolean, Double, Float, Integer,
// Text(String), ArgumentStack, CommandContext, also modifiers like LimitModifier,
// OptionalModifier, ValueFlagModifier
partInjector.install(new DefaultsModule());
```

### Platform Specific

Some of the platform-specific subprojects include a `Module` for the `PartInjector`
that can be easily installed to obtain the default bindings for that platform.

For example, for Bukkit (`commandflow-bukkit` subproject):
```java
// install bindings for the default bindings for Bukkit, such as
// CommandSender, OfflinePlayer, Player, World, GameMode and @Sender Player
partInjector.install(new BukkitModule());
```
53 changes: 53 additions & 0 deletions docs/annotated/subcommand-instance-creator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
## SubCommand Instantiation

Sometimes we have a command that has subcommands declared with the `@SubCommandClasses`
annotation. In this case, the command framework needs to know how to instantiate the
subcommands.

We can specify how to instantiate the subcommands by implementing the `SubCommandInstanceCreator`
interface.

### The `SubCommandInstanceCreator` interface

Functional interface that has a single method `createInstance(Class<? extends CommandClass>, CommandClass)`,
that receives the subcommand class and its parent instance, and returns the sub command instance.

If we do not specify one, the annotated command tree builder will instantiate the subcommand
using Reflection, by looking for constructor that accepts the parent's class as the single
parameter an empty constructor.

The `SubCommandInstanceCreator` is set to the `AnnotatedCommandTreeBuilder` when instantiating
it, for example:

<!--@formatter:off-->
```java
// will use the default SubCommandInstanceCreator
AnnotatedCommandTreeBuilder commandBuilder = AnnotatedCommandTreeBuilder.create(partInjector);

// will use a custom SubCommandInstanceCreator
AnnotatedCommandTreeBuilder commandBuilder = AnnotatedCommandTreeBuilder.create(
partInjector,
new MyCustomSubCommandInstanceCreator()
);
```
<!--@formatter:on-->

### Common Implementations

It is common for `command-flow` to be used along with dependency injection frameworks, so
you can just create a `SubCommandInstanceCreator` that does the following:

**For [Google's Guice](https://github.com/google/guice) or [Unnamed Team's inject](https://github.com/unnamed/inject):**
```java
// obtain the Injector instance
Injector injector = ...;

// create the SubCommandInstanceCreator
SubCommandInstanceCreator subCommandCreator = (clazz, parent) -> injector.getInstance(clazz);

// now set it when creating an AnnotatedCommandTreeBuilder
AnnotatedCommandTreeBuilder commandBuilder = AnnotatedCommandTreeBuilder.create(
partInjector,
subCommandCreator
);
```
22 changes: 22 additions & 0 deletions docs/annotated/with-permission.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## #5 With Permission

This example shows how to create a command with a permission requirement
with the annotated approach:

```java
@Command(names = "greet", permission = "myperm.command.greet")
public class GreetingCommand implements CommandClass {

@Command(names = "")
public void run(String name) {
System.out.println("Hello, " + name + "!");
}

}
```

The `@Command` annotation accepts a `permission` parameter, which is
used to specify the permission required to execute the command.

Check the [Authorizer page](../configuration/authorizer.md) for more information
on how to specify how the `CommandManager` should check for permissions.
Loading
Loading