tutorial:command_argument_types
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
tutorial:command_argument_types [2022/08/08 02:20] – created (migrated from [[tutorial:commands]]) 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 section | + | Brigadier has support for custom argument types and this article |
- | 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 [enable_line_numbers=" | + | <code java> |
public class UuidArgumentType implements ArgumentType< | public class UuidArgumentType implements ArgumentType< | ||
</ | </ | ||
Line 15: | Line 16: | ||
ArgumentType requires you to implement the '' | ArgumentType requires you to implement the '' | ||
<code java> | <code java> | ||
- | @Override | + | |
- | public UUID parse(StringReader reader) throws CommandSyntaxException { | + | public UUID parse(StringReader reader) throws CommandSyntaxException { |
</ | </ | ||
- | This method is where all of your parsing will occur. Either this method will return the object based on the arguments provided in the command line or throw a CommandSyntaxException and parsing will fail. | + | This method is where all of your parsing will occur. Either this method will return the object based on the arguments provided in the command line or throw a '' |
Next you will store the current position of the cursor, this is so you can substring out only the specific argument. This will always be at the beginning of where your argument appears on the command line. | Next you will store the current position of the cursor, this is so you can substring out only the specific argument. This will always be at the beginning of where your argument appears on the command line. | ||
<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 StringReader what the current position of the cursor is an substring our argument out of the command line. | + | 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 ArgumentType: | + | ===== Example of a whole argument type ===== |
<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 107: | Line 140: | ||
} | } | ||
- | // Now we check the contents of the argument till either we hit the end of the command line (When canRead becomes false) | + | // Now we check the contents of the argument till either we hit the end of the |
- | // Otherwise we go till reach reach a space, which signifies the next argument | + | // command line (when '' |
- | while (reader.canRead() && reader.peek() | + | // Otherwise we go till reach reach a character that cannot compose a UUID |
+ | while (reader.canRead() && | ||
reader.skip(); | reader.skip(); | ||
} | } | ||
- | // Now we substring the specific part we want to see using the starting cursor position and the ends where the next argument starts. | + | // Now we substring the specific part we want to see using the starting cursor |
+ | // position and the ends where the next argument starts. | ||
String uuidString = reader.getString().substring(argBeginning, | String uuidString = reader.getString().substring(argBeginning, | ||
try { | try { | ||
UUID uuid = UUID.fromString(uuidString); | 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. | + | return uuid; |
- | } catch (Exception | + | |
+ | // 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. | // 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. | // 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); | + | |
+ | | ||
} | } | ||
} | } | ||
@Override | @Override | ||
- | public Collection< | + | public Collection< |
+ | | ||
+ | // this should contain a Collection of only the argument this type will return. | ||
+ | // This is mainly used to detect ambiguity, | ||
return EXAMPLES; | return EXAMPLES; | ||
} | } | ||
Line 133: | 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.1659925210.txt.gz · Last modified: 2022/08/08 02:20 by solidblock