Skip to content

Commit

Permalink
Developer docs
Browse files Browse the repository at this point in the history
  • Loading branch information
melontini committed Apr 18, 2024
1 parent 949b0e9 commit 9bf1dd9
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 2 deletions.
20 changes: 18 additions & 2 deletions docs/.vitepress/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,34 @@ export const en = defineConfig({
sidebar: [
{
items: [
{ text: 'Welcome!', link: '/' },
{ text: 'Welcome!', link: '/' }
]
},
{
text: 'Use',
items: [
{ text: 'Events', link: '/Events' },
{ text: 'Commands', link: '/Commands' },
{ text: 'Expressions', link: '/Expressions' },
]
},
{
text: 'Develop',
items: [
{ text: 'Events', link: '/develop/Events'},
{ text: 'Commands', link: '/develop/Commands'}
]
},
{
text: 'Meta',
items: [
{ text: 'Badges', link: 'https://github.com/constellation-mc/commander/discussions/3' }
]
}
]
],

search: {
provider: 'local',
}
}
})
58 changes: 58 additions & 0 deletions docs/develop/Commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Commands

In general, you should prefer to implement Brigadier `/` commands, but there are some cases where some additional flexibility is required.

## Creating commands

Creating new commands is easy, you'll have to implement the `Command` interface, create a [Codec](https://forge.gemwire.uk/wiki/Codecs) to serialize/deserialize the command and register the codec with `CommandTypes.register()`.

Let's create a simple command which will print a string to standard output.

```java
public record DummyCommand(String text) implements Command {

@Override
public boolean execute(EventContext context) {
System.out.println(text());
return true; //Return if execution was successful.
}

@Override
public CommandType type() {
return null;
}
}
```

Now create a [Codec](https://forge.gemwire.uk/wiki/Codecs) for your command.

```java
public static final Codec<DummyCommand> CODEC = Codec.STRING.fieldOf("text").xmap(DummyCommand::new, DummyCommand::text).codec();
```

With that done, we'll have to register the command to get our `CommandType`.
```java
public static final CommandType DUMMY = CommandType.register(new Identifier("modid", "print"), DummyCommand.CODEC);
```
Now return this type in `type()`.
```java
@Override
public CommandType type() {
return MyModInit.DUMMY;
}
```
## EventContext
EventContext allows you to retrieve the `LootContext` which is passed with the event type.
```java
context.lootContext().getWorld(); //Returns a ServerWorld.
context.lootContext().get(LootContextParameters.TOOL); //Returns the prameter or null if not present.
context.lootContext().requireParameter(LootContextParameters.TOOL); //Returns the parameter or throws an exception if not present.
```
69 changes: 69 additions & 0 deletions docs/develop/Events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Events

Commander introduces a new data pack event system, which allows data packs to listen to events just like mods.

The best practice is to have a generic Fabric event and register Commander as one of its listeners.

## Creating event types

To implement event support for your mod you must first register an event type for Commander to dispatch. You can build and register events with the `EventType.Builder` class.

```java
public static final EventType CUSTOM_EVENT = EventType.builder().build(new Identifier("modid", "custom_event"));
```

If your event requires a return type, you can specify a cancel term codec with `cancelTerm()`.

```java
EventType.builder()
.cancelTerm(Codec.INT)
.build(new Identifier("modid", "custom_event"));
```

Events can aceepts additional parameters using `extension()`. After specifying an extension you can return a custom type.

```java
EventType.builder()
.extension(Codec.STRING, subscriptions -> {
//handle data.
return /*Return listeners*/;
})
.build(new Identifier("modid", "custom_event"));
```

The default return type is `List<Command.Conditioned>`.

## Invoking the event

If you didn't specify an extension, or opted to return a `List<Command.Conditioned>`, you can use the included `EventExecutors` util.

To use the util, you have to pass the event type, the execution world, and a loot context supplier.

```java
CustomEvent.EVENT.register((world, entity) -> runVoid(CUSTOM_EVENT, world, () -> makeContext(world, entity, entity.getPos())));
```

```java
private static LootContext makeContext(ServerWorld world, Entity entity, Vec3d origin) {
LootContextParameterSet.Builder builder = new LootContextParameterSet.Builder(world);
builder.add(THIS_ENTITY, entity).add(ORIGIN, origin);
return new LootContext.Builder(builder.build(LootContextTypes.COMMAND)).build(null /*Optional.empty() in 1.20.4*/);
}
```

If you did specify an extension, or are using an unsupported return type, you'll have to write custom resolution logic.

To do that you'll simple have to create an `EventContext`. `EventContext` is used to pass execution parameters to commands.

```java
EventContext context = EventContext.builder(type)
.addParameter(EventKey.LOOT_CONTEXT, /*instance of loot context*/)
.build();
for (Command.Conditioned subscriber : subscribers) subscriber.execute(context);
```
To get the return value, you can call `getReturnValue(def)`, the default value can be null. The return type is generic.

```java
boolean val = context.getReturnValue(def);
if (val != def) return val;
```

0 comments on commit 9bf1dd9

Please sign in to comment.