tutorial:command_argument_types
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorial:command_argument_types [2023/04/20 10:38] – solidblock | tutorial:command_argument_types [2024/04/15 07:21] (current) – [Custom argument serializer] solidblock | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ======= Command Argument Types ======= | + | ====== Command Argument Types ====== |
- | Brigadier has support for custom argument types and this article goes into showing how to create | + | Brigadier has support for custom argument types and this article goes into showing how to create |
- | Warning: Custom arguments require client mod installation to be registered correctly! If you are making a server plugin, consider using existing argument type and a custom suggestions provider instead. | + | **Warning:** Custom arguments require client mod installation to be registered correctly! If you are making a server plugin, consider using existing argument type and a custom suggestions provider instead. |
+ | ===== Parsing ==== | ||
For this example we will create a UuidArgumentType. | For this example we will create a UuidArgumentType. | ||
- | First create a class which extends '' | + | First create a class which extends '' |
<code java> | <code java> | ||
Line 24: | Line 25: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | int argBeginning = reader.getCursor(); | + | |
- | if (!reader.canRead()) { | + | if (!reader.canRead()) { |
- | reader.skip(); | + | reader.skip(); |
- | } | + | } |
</ | </ | ||
- | Now we grab the entire argument. Depending on your argument type, you may have a different criteria or be similar to some arguments where detecting a '' | + | Now we grab the entire argument. Depending on your argument type, you may have a different criteria or be similar to some arguments where detecting a '' |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | while (reader.canRead() && reader.peek() | + | |
- | reader.skip(); | + | reader.skip(); |
- | } | + | } |
</ | </ | ||
Then we will ask the '' | Then we will ask the '' | ||
- | <code java [enable_line_numbers=" | + | <code java [enable_line_numbers=" |
+ | | ||
+ | </ | ||
Now finally we check if our argument is correct and parse the specific argument to our liking, and throwing an exception if the parsing fails. | Now finally we check if our argument is correct and parse the specific argument to our liking, and throwing an exception if the parsing fails. | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | try { | + | public static final DynamicCommandExceptionType INVALID_UUID = new DynamicCommandExceptionType(o -> Text.literal(" |
- | UUID uuid = UUID.fromString(uuidString); | + | |
- | return uuid; // And we return our type, in this case the parser will consider this argument to have parsed properly and then move on. | + | @Override |
- | } catch (Exception | + | public UUID parse(StringReader reader) throws CommandSyntaxException { |
- | // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type. | + | // ... |
- | // Create with context tells Brigadier to supply some context to tell the user where the command failed at. | + | |
- | // Though normal create method could be used. | + | UUID uuid = UUID.fromString(uuidString); |
- | throw new SimpleCommandExceptionType(Text.literal(ex.getMessage())).createWithContext(reader); | + | return uuid; // And we return our type, in this case the parser will consider this argument to have parsed properly and then move on. |
- | } | + | } catch (InvalidArgumentException |
+ | // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type. | ||
+ | // Create with context tells Brigadier to supply some context to tell the user where the command failed at. | ||
+ | // Though normal create method could be used. | ||
+ | | ||
+ | throw INVALID_UUID.createWithContext(reader, | ||
+ | } | ||
+ | // ... | ||
+ | | ||
</ | </ | ||
- | The '' | + | ===== Defining Argument examples ===== |
- | Within | + | Sometimes argument should have examples. It is usually stored in an immutable collection as a static final field. Examples are used to detect ambiguities. |
+ | <code java> | ||
+ | private static final Collection< | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ); | ||
+ | |||
+ | @Override | ||
+ | public Collection< | ||
+ | // Brigadier has support to show examples for what the argument should look like, | ||
+ | // this should contain a Collection of only the argument this type will return. | ||
+ | // This is mainly used to detect ambiguity, which means an argument of this type may be parsed as another type. | ||
+ | return EXAMPLES; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Register the argument type ===== | ||
+ | |||
+ | The '' | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | ArgumentTypeRegistry.registerArgumentType(new Identifier(" | + | ArgumentTypeRegistry.registerArgumentType( |
+ | | ||
+ | | ||
// The argument should be what will create the ArgumentType. | // The argument should be what will create the ArgumentType. | ||
</ | </ | ||
- | And here is the whole code of the argument type: | + | ===== Example |
<file java UuidArgumentType.java [enable_line_numbers=" | <file java UuidArgumentType.java [enable_line_numbers=" | ||
Line 74: | Line 106: | ||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; | ||
import net.minecraft.text.Text; | import net.minecraft.text.Text; | ||
- | import net.minecraft.util.SystemUtil; | ||
import java.util.ArrayList; | import java.util.ArrayList; | ||
Line 84: | Line 115: | ||
*/ | */ | ||
public class UuidArgumentType implements ArgumentType< | public class UuidArgumentType implements ArgumentType< | ||
+ | public static final DynamicCommandExceptionType INVALID_UUID = new DynamicCommandExceptionType(o -> Text.literal(" | ||
+ | | ||
public static UuidArgumentType uuid() { | public static UuidArgumentType uuid() { | ||
return new UuidArgumentType(); | return new UuidArgumentType(); | ||
Line 94: | Line 127: | ||
} | } | ||
- | private static final Collection< | + | private static final Collection< |
- | | + | " |
- | | + | " |
- | | + | " |
- | | + | ); |
@Override | @Override | ||
Line 109: | Line 142: | ||
// Now we check the contents of the argument till either we hit the end of the | // Now we check the contents of the argument till either we hit the end of the | ||
// command line (when '' | // command line (when '' | ||
- | // Otherwise we go till reach reach a space, which signifies the next argument | + | // Otherwise we go till reach reach a character that cannot compose a UUID |
- | while (reader.canRead() && reader.peek() | + | while (reader.canRead() && |
reader.skip(); | reader.skip(); | ||
} | } | ||
Line 122: | Line 155: | ||
// And we return our type, in this case the parser will consider this | // And we return our type, in this case the parser will consider this | ||
// argument to have parsed properly and then move on. | // argument to have parsed properly and then move on. | ||
- | } catch (Exception | + | } catch (InvalidArgumentException |
- | // UUIDs can throw an exception when made by a string, so we catch the exception | + | // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type. |
- | // and repackage it into a CommandSyntaxException type. | + | // Create with context tells Brigadier to supply some context to tell the user where the command failed at. |
- | // Create with context tells Brigadier to supply some context to tell the user | + | |
- | // where the command failed at. | + | |
// Though normal create method could be used. | // Though normal create method could be used. | ||
- | throw new SimpleCommandExceptionType(Text.literal(ex.getMessage())).createWithContext(reader); | + | |
+ | | ||
} | } | ||
} | } | ||
Line 136: | Line 168: | ||
// Brigadier has support to show examples for what the argument should look like, | // Brigadier has support to show examples for what the argument should look like, | ||
// this should contain a Collection of only the argument this type will return. | // this should contain a Collection of only the argument this type will return. | ||
- | // This is mainly used to calculate ambiguous commands | + | // This is mainly used to detect ambiguity, |
return EXAMPLES; | return EXAMPLES; | ||
} | } | ||
Line 142: | Line 174: | ||
</ | </ | ||
+ | ===== Specifying suggestions ===== | ||
+ | |||
+ | Many argument types supports suggestions. Unlike custom suggestions defined in registration of commands, these suggestions are always applied when there is no custom suggestions. Suggestions can be calculated in client. For example, '' | ||
+ | |||
+ | For how to provide suggestions, | ||
+ | |||
+ | <code java> | ||
+ | @Override | ||
+ | public <S> CompletableFuture< | ||
+ | final String remaining = builder.getRemaining(); | ||
+ | if (context.getSource() instanceof FabricClientCommandSource clientCommandSource) { | ||
+ | if (clientCommandSource.getClient().crosshairTarget instanceof EntityHitResult entityHitResult) { | ||
+ | final UUID uuid = entityHitResult.getEntity().getUuid(); | ||
+ | if (CommandSource.shouldSuggest(remaining, | ||
+ | builder.suggest(uuid.toString()); | ||
+ | } | ||
+ | } | ||
+ | return CommandSource.suggestMatching(Collections2.transform(clientCommandSource.getClient().getNetworkHandler().getPlayerUuids(), | ||
+ | } | ||
+ | |||
+ | if (context.getSource() instanceof ServerCommandSource source) { | ||
+ | return CommandSource.suggestMatching(Lists.transform(source.getServer().getPlayerManager().getPlayerList(), | ||
+ | } | ||
+ | | ||
+ | return builder.buildFuture(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Custom argument serializer ====== | ||
+ | In the example above, the UUID is simple. If the argument is complex, how to make it correctly send to and understood by the client? This is where '' | ||
+ | |||
+ | '' | ||
+ | * The '' | ||
+ | * The '' | ||
+ | |||
+ | In this example, we create a new complicated argument type, which contains a boolean value and an integer value. '' | ||
+ | |||
+ | <code java> | ||
+ | public record ExampleArgumentType(CommandRegistryAccess commandRegistryAccess, | ||
+ | @Override | ||
+ | public String parse(StringReader reader) throws CommandSyntaxException { | ||
+ | ... | ||
+ | } | ||
+ | | ||
+ | public static class Serializer implements ArgumentSerializer< | ||
+ | @Override | ||
+ | public void writePacket(Properties properties, PacketByteBuf buf) { | ||
+ | // Writes the basic properties to a packet. You should ensure all properties | ||
+ | // can be in some ways stored in the packet. | ||
+ | buf.writeBoolean(properties.booleanValue).writeInt(properties.intValue); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Properties fromPacket(PacketByteBuf buf) { | ||
+ | // Reads the information in the packet. It returns the '' | ||
+ | return new Properties(buf.readBoolean(), | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void writeJson(Properties properties, JsonObject json) { | ||
+ | // Present the argument type in the format of JSON. | ||
+ | json.addProperty(" | ||
+ | json.addProperty(" | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Properties getArgumentTypeProperties(ExampleArgumentType argumentType) { | ||
+ | return new Properties(argumentType.booleanValue, | ||
+ | } | ||
+ | |||
+ | public record Properties(boolean booleanValue, | ||
+ | @Override | ||
+ | public ExampleArgumentType createType(CommandRegistryAccess commandRegistryAccess) { | ||
+ | // Only in this method, '' | ||
+ | // and the argument type that require '' | ||
+ | return new ExampleArgumentType(commandRegistryAccess, | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ArgumentSerializer< | ||
+ | // Do not create a new '' | ||
+ | return Serializer.this; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | And now you can register it like this: | ||
+ | <code java> | ||
+ | ArgumentTypeRegistry.registerArgumentType(new Identifier(" | ||
+ | </ | ||
+ | |||
+ | ==== Another possible way to define serializer ==== | ||
+ | |||
+ | If the argument does not require '' | ||
+ | |||
+ | <code java> | ||
+ | public record ExampleArgumentType(boolean booleanValue, | ||
+ | @Override | ||
+ | public String parse(StringReader reader) throws CommandSyntaxException { | ||
+ | ... | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ExampleArgumentType createType(CommandRegistryAccess commandRegistryAccess) { | ||
+ | return this; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ArgumentSerializer< | ||
+ | // always return a same object here to avoid failing to serialize. | ||
+ | return Serializer.INSTANCE; | ||
+ | } | ||
+ | |||
+ | public static class Serializer implements ArgumentSerializer< | ||
+ | public static final Serializer INSTANCE = new Serializer(); | ||
+ | private Serializer() {} | ||
+ | | ||
+ | @Override | ||
+ | public void writePacket(ExampleArgumentType properties, PacketByteBuf buf) { | ||
+ | buf.writeBoolean(properties.booleanValue).writeInt(properties.intValue); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ExampleArgumentType fromPacket(PacketByteBuf buf) { | ||
+ | return new ExampleArgumentType(buf.readBoolean(), | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void writeJson(ExampleArgumentType properties, JsonObject json) { | ||
+ | json.addProperty(" | ||
+ | json.addProperty(" | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ExampleArgumentType getArgumentTypeProperties(ExampleArgumentType argumentType) { | ||
+ | return argumentType; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | And now you can register it like this: | ||
+ | <code java> | ||
+ | ArgumentTypeRegistry.registerArgumentType(new Identifier(" | ||
+ | </ |
tutorial/command_argument_types.1681987131.txt.gz · Last modified: 2023/04/20 10:38 by solidblock