tutorial:commands
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
tutorial:commands [2019/08/22 03:55] – [A very basic command] Change ArgumentBuilders to CommandManager i509vcb | tutorial:commands [2020/05/16 01:57] – Reword some parts of the tutorial, includes a few gramatical corrections i509vcb | ||
---|---|---|---|
Line 2: | Line 2: | ||
Creating commands can allow a mod developer to add functionality that a player can use through a command. | Creating commands can allow a mod developer to add functionality that a player can use through a command. | ||
- | This tutorial will teach you how to register commands, and the command structure of Brigadier | + | This tutorial will teach you how to register commands, and the command structure of Brigadier. |
+ | |||
+ | Note: All code written here was written for 1.14.4. Some mappings may have changed in yarn, but all code should still be applicable. | ||
===== Registering Commands ===== | ===== Registering Commands ===== | ||
- | If you just want to see how to register commands you've come to the right place here. | + | Registering commands is done by registering a new listener in the '' |
- | + | The event should be registered | |
- | Registering commands is done through '' | + | |
- | + | ||
- | The '' | + | |
- | + | ||
- | The dedicated flag if set to true will tell Fabric to only register the command on a '' | + | |
+ | The dedicated parameter if true will tell event listeners that the server commands are being registered on is a '' | ||
Below are a few examples of how the commands can be registered. | Below are a few examples of how the commands can be registered. | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | CommandRegistry.INSTANCE.register(false, dispatcher -> TutorialCommands.register(dispatcher)); // All commands are registered in a single class that references every command. | + | // Method reference |
- | + | CommandRegistrationCallback.EVENT.register(TutorialCommands::register); | |
- | CommandRegistry.INSTANCE.register(false, | + | |
+ | // Using a lambda | ||
+ | CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) | ||
+ | | ||
TutorialCommand.register(dispatcher); | TutorialCommand.register(dispatcher); | ||
- | TutorialHelpCommand.register(dispatcher); | + | |
+ | // This command will only be registered on a dedicated server | ||
+ | | ||
+ | } else { | ||
+ | // This command will only be registered on an integrated server. | ||
+ | // Commands which call client only classes and methods should be registered in your ClientModInitializer | ||
+ | IntegratedTutorialHelpCommand.register(dispatcher); | ||
+ | } | ||
}); | }); | ||
- | + | ||
- | CommandRegistry.INSTANCE.register(true, dispatcher -> { // Or directly registering the command to the dispatcher. | + | // Or register directly |
- | dispatcher.register(LiteralArgumentBuilder.literal(" | + | CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) |
+ | dispatcher.register(LiteralArgumentBuilder.literal(" | ||
}); | }); | ||
</ | </ | ||
Line 38: | Line 47: | ||
// Then add an argument named bar that is an integer | // Then add an argument named bar that is an integer | ||
.then(CommandManager.argument(" | .then(CommandManager.argument(" | ||
- | // The command to be executed if the command " | + | |
.executes(ctx -> { | .executes(ctx -> { | ||
System.out.println(" | System.out.println(" | ||
- | | + | |
return 1; | return 1; | ||
- | | + | |
// The command " | // The command " | ||
.executes(ctx -> { | .executes(ctx -> { | ||
Line 52: | Line 61: | ||
</ | </ | ||
- | The main process registers the command | + | The main process registers the command |
- | Since the root node must be literal, The sender must enter the exact same sequence of letters to execute the command, so "Foo", "fOo" | + | Since the root node must be literal, The sender must enter the exact same sequence of letters to execute the command, so '' |
===== Brigadier Explained ===== | ===== Brigadier Explained ===== | ||
- | Brigadier starts with the '' | + | Brigadier starts with the '' |
- | The trunk of the tree is the CommandDispatcher. | + | |
- | The register(LiteralArgumentBuilder) methods specify | + | Command nodes are similar to the branches |
- | The executes blocks can be seen at the leaves of the tree where it ends and also supplies the outcome of the system. | + | |
- | The execute blocks specify the command to be ran. As Brigadier' | + | The execute blocks specify the command to be ran. As Brigadier' |
==== CommandContexts ==== | ==== CommandContexts ==== | ||
- | When a command is ran, Brigadier provides a CommandContext to the command that is ran. | + | When a command is ran, Brigadier provides a '' |
The CommandContext contains all arguments and other objects such as the inputted String and the '' | The CommandContext contains all arguments and other objects such as the inputted String and the '' | ||
==== Arguments ==== | ==== Arguments ==== | ||
- | The arguments | + | Arguments |
- | Minecraft creates some special | + | Minecraft creates some special |
+ | ==== Static Imports ==== | ||
You could do the long method of typing '' | You could do the long method of typing '' | ||
This also works for getting arguments, which shortens the already long '' | This also works for getting arguments, which shortens the already long '' | ||
Line 86: | Line 95: | ||
import static net.minecraft.server.command.CommandManager.*; | import static net.minecraft.server.command.CommandManager.*; | ||
</ | </ | ||
+ | |||
+ | Note: Please be sure you use the '' | ||
Brigadier' | Brigadier' | ||
Line 101: | Line 112: | ||
</ | </ | ||
- | Loot tables specify their own SuggestionProvider inside LootCommand for example. | + | Loot tables specify their own SuggestionProvider inside |
The example below is a dynamically changing SuggestionProvider that lists several words for a StringArgumentType to demonstrate how it works: | The example below is a dynamically changing SuggestionProvider that lists several words for a StringArgumentType to demonstrate how it works: | ||
Line 125: | Line 136: | ||
</ | </ | ||
- | The SuggestionProvider is a FunctionalInterface | + | The SuggestionProvider is a functional interface |
- | Though remember these are suggestions. The inputted command may not contain an argument you suggested so you still have to parse check inside the command to see if the argument is what you want if it's a String | + | Though remember these are suggestions. The inputted command may not contain an argument you suggested so arguments are parsed without consideration |
- | To use the suggestion you would append it right after the argument you want to recommend | + | To use the suggestion you would append it right after the argument you want to suggest possible |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
Line 137: | Line 148: | ||
</ | </ | ||
- | ==== Requires | + | ==== Requiring Permissions |
Lets say you have a command you only want operators to be able to execute. This is where the '' | Lets say you have a command you only want operators to be able to execute. This is where the '' | ||
Line 153: | Line 164: | ||
This command will only execute if the Source of the command is a level 4 operator at minimum. If the predicate returns false, then the command will not execute. Also this has the side effect of not showing this command in tab completion to anyone who is not a level 4 operator. | This command will only execute if the Source of the command is a level 4 operator at minimum. If the predicate returns false, then the command will not execute. Also this has the side effect of not showing this command in tab completion to anyone who is not a level 4 operator. | ||
+ | |||
+ | Nothing prevents someone from specifying calls to permissions implementations within the '' | ||
==== Exceptions ==== | ==== Exceptions ==== | ||
- | Brigadier supports command exceptions which can be used to end a command such as if an argument didn't parse properly or the command failed to execute. | + | Brigadier supports command exceptions which can be used to end a command such as if an argument didn't parse properly or the command failed to execute, as well as including richer details of the failure. |
- | All the exceptions from Brigadier are based on the CommandSyntaxException. The two main types of exceptions Brigadier provides are Dynamic and Simple exception types, of which you must '' | + | All the exceptions from Brigadier are based on the CommandSyntaxException. The two main types of exceptions Brigadier provides are Dynamic and Simple exception types, of which you must '' |
Below is a coin flip command to show an example of exceptions in use. | Below is a coin flip command to show an example of exceptions in use. | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
dispatcher.register(CommandManager.literal(" | dispatcher.register(CommandManager.literal(" | ||
- | .executes(ctx -> { | + | |
- | Random random = new Random(); | + | Random random = new Random(); |
- | if(random.nextBoolean()) { // If heads succeed. | + | |
- | ctx.getSource().sendMessage(new TranslateableText(" | + | ctx.getSource().sendMessage(new TranslateableText(" |
- | return Command.SINGLE_SUCCESS; | + | return Command.SINGLE_SUCCESS; |
- | } | + | } |
- | throw new SimpleCommandExceptionType(new TranslateableText(" | + | |
- | })); | + | |
+ | })); | ||
</ | </ | ||
- | Though you are not just limited to a single type of exception as Brigadier also supplies Dynamic exceptions. | + | Though you are not just limited to a single type of exception as Brigadier also supplies Dynamic exceptions |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
DynamicCommandExceptionType used_name = new DynamicCommandExceptionType(name -> { | DynamicCommandExceptionType used_name = new DynamicCommandExceptionType(name -> { | ||
- | return new LiteralText(" | + | |
}); | }); | ||
</ | </ | ||
Line 193: | Line 207: | ||
LiteralCommandNode node = registerMain(dispatcher); | LiteralCommandNode node = registerMain(dispatcher); | ||
dispatcher.register(literal(" | dispatcher.register(literal(" | ||
- | .redirect(node)); | + | |
dispatcher.register(literal(" | dispatcher.register(literal(" | ||
- | .redirect(node)); | + | |
} | } | ||
public static LiteralCommandNode registerMain(CommandDispatcher< | public static LiteralCommandNode registerMain(CommandDispatcher< | ||
return dispatcher.register(literal(" | return dispatcher.register(literal(" | ||
- | .then(argument(" | + | |
- | | + | .then(argument(" |
- | .executes(ctx -> { | + | .executes(ctx -> { |
- | | + | return execute(ctx.getSource(), |
- | })))); | + | })))); |
} | } | ||
</ | </ | ||
- | The redirect | + | The redirect |
- | Redirects | + | ==== Redirects |
+ | Commands | ||
+ | |||
+ | <code java [enable_line_numbers=" | ||
+ | LiteralCommandNode< | ||
+ | LiteralCommandNode< | ||
+ | // You can register under the same literal more than once, it will just register new parts of the branch as shown below if you register a duplicate | ||
+ | .then(literal(" | ||
+ | .then(literal(" | ||
+ | .redirect(root, | ||
+ | .then(literal(" | ||
+ | .redirect(root, | ||
+ | | ||
+ | .executes(ctx -> { | ||
+ | ctx.getSource().sendFeedback(new LiteralText(" | ||
+ | return Command.SINGLE_SUCCESS; | ||
+ | }))); | ||
+ | </ | ||
+ | The redirect can also modify the CommandSource by use of a '' | ||
+ | |||
+ | <code java [enable_line_numbers=" | ||
+ | .redirect(rootNode, | ||
+ | return ((ServerCommandSource) context.getSource()).withLookingAt(Vec3ArgumentType.getVec3(context, | ||
+ | }) | ||
+ | </ | ||
===== ServerCommandSource ===== | ===== ServerCommandSource ===== | ||
- | What if you wanted | + | A server command source provides some additional implementation specific context when a command |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | ServerCommandSource source = ctx.getSource(); | + | final ServerCommandSource source = ctx.getSource(); |
// Get the source. This will always work. | // Get the source. This will always work. | ||
- | Entity sender = source.getEntity(); | + | final Entity sender = source.getEntity(); |
// Unchecked, may be null if the sender was the console. | // Unchecked, may be null if the sender was the console. | ||
- | Entity sender2 = source.getEntityOrThrow(); | + | final Entity sender2 = source.getEntityOrThrow(); |
// Will end the command if the source of the command was not an Entity. | // Will end the command if the source of the command was not an Entity. | ||
// The result of this could contain a player. Also will send feedback telling the sender of the command that they must be an entity. | // The result of this could contain a player. Also will send feedback telling the sender of the command that they must be an entity. | ||
Line 229: | Line 267: | ||
// The entity options in ServerCommandSource could return a CommandBlock entity, any living entity or a player. | // The entity options in ServerCommandSource could return a CommandBlock entity, any living entity or a player. | ||
- | ServerPlayerEntity player = source.getPlayer(); | + | final ServerPlayerEntity player = source.getPlayer(); |
// Will end the command if the source of the command was not explicitly a Player. Also will send feedback telling the sender of the command that they must be a player. | // Will end the command if the source of the command was not explicitly a Player. Also will send feedback telling the sender of the command that they must be a player. | ||
- | </ | ||
- | The ServerCommandSource also provides other information about the sender of the command. | ||
- | |||
- | <code java [enable_line_numbers=" | ||
source.getPosition(); | source.getPosition(); | ||
// Get's the sender' | // Get's the sender' | ||
Line 255: | Line 289: | ||
</ | </ | ||
- | ===== Some actual | + | ===== A few examples ===== |
- | + | ||
- | Just a few to show: | + | |
=== Broadcast a message === | === Broadcast a message === | ||
Line 264: | Line 296: | ||
public static void register(CommandDispatcher< | public static void register(CommandDispatcher< | ||
dispatcher.register(literal(" | dispatcher.register(literal(" | ||
- | .requires(source -> source.hasPermissionLevel(2)) // Must be a game master to use the command. Command will not show up in tab completion or execute to non op' | + | |
- | | + | .then(argument(" |
- | .then(argument(" | + | .then(argument(" |
- | | + | .executes(ctx -> broadcast(ctx.getSource(), |
} | } | ||
public static int broadcast(ServerCommandSource source, Formatting formatting, String message) { | public static int broadcast(ServerCommandSource source, Formatting formatting, String message) { | ||
- | Text text = new LiteralText(message).formatting(formatting); | + | |
source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, | ||
Line 293: | Line 325: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
public static int giveDiamond(CommandContext< | public static int giveDiamond(CommandContext< | ||
- | ServerCommandSource source = ctx.getSource(); | + | |
- | PlayerEntity self = source.getPlayer(); | + | |
</ | </ | ||
Line 302: | Line 334: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
if(!player.inventory.insertStack(new ItemStack(Items.DIAMOND))){ | if(!player.inventory.insertStack(new ItemStack(Items.DIAMOND))){ | ||
- | throw new SimpleCommandExceptionType(new | + | throw new SimpleCommandExceptionType(new |
- | } | + | } |
return 1; | return 1; | ||
} | } | ||
Line 320: | Line 353: | ||
dispatcher.register(literal(" | dispatcher.register(literal(" | ||
.then(required(" | .then(required(" | ||
- | | + | |
- | .executes(ctx -> antioch(ctx.getSource(), | + | .executes(ctx -> antioch(ctx.getSource(), |
} | } | ||
</ | </ | ||
Line 328: | Line 361: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | public static int antioch(ServerCommandSource source, BlockPos blockPos) throws CommandSyntaxException { | + | public static int antioch(ServerCommandSource source, BlockPos blockPos) throws CommandSyntaxException { |
- | + | if(blockPos == null) { | |
- | if(blockPos==null) { | + | // For the case of no inputted argument we calculate the cursor position of the player or throw an error if the nearest position is too far or is outside of the world. |
- | | + | // This class is used as an example and actually doesn' |
+ | blockPos = LocationUtil.calculateCursorOrThrow(source, | ||
} | } | ||
- | + | ||
- | TntEntity tnt = new TntEntity(source.getWorld(), | + | |
+ | tnt.setFuse(3); | ||
| | ||
source.getMinecraftServer().getPlayerManager().broadcastChatMessage(new LiteralText(" | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(new LiteralText(" | ||
Line 355: | Line 390: | ||
.executes(ctx -> execute(ctx.getSource(), | .executes(ctx -> execute(ctx.getSource(), | ||
.executes(ctx -> execute(ctx.getSource(), | .executes(ctx -> execute(ctx.getSource(), | ||
- | // Register redirect | + | // Register |
dispatcher.register(literal(" | dispatcher.register(literal(" | ||
.redirect(basenode)); | .redirect(basenode)); | ||
} | } | ||
- | // Beginning of the method | + | |
private static int execute(ServerCommandSource source, Identifier biomeId, int range) throws CommandSyntaxException { | private static int execute(ServerCommandSource source, Identifier biomeId, int range) throws CommandSyntaxException { | ||
Biome biome = Registry.BIOME.get(biomeId); | Biome biome = Registry.BIOME.get(biomeId); | ||
| | ||
- | if(biome == null) { // Since the argument is an Identifier we need to check if the identifier actually exists in the registry | + | if(biome == null) { |
+ | | ||
throw new SimpleCommandExceptionType(new TranslatableText(" | throw new SimpleCommandExceptionType(new TranslatableText(" | ||
} | } | ||
Line 369: | Line 405: | ||
List< | List< | ||
bio.add(biome); | bio.add(biome); | ||
- | | + | |
ServerWorld world = source.getWorld(); | ServerWorld world = source.getWorld(); | ||
- | | ||
BiomeSource bsource = world.getChunkManager().getChunkGenerator().getBiomeSource(); | BiomeSource bsource = world.getChunkManager().getChunkGenerator().getBiomeSource(); | ||
- | | ||
BlockPos loc = new BlockPos(source.getPosition()); | BlockPos loc = new BlockPos(source.getPosition()); | ||
- | | + | |
+ | | ||
BlockPos pos = bsource.locateBiome(loc.getX(), | BlockPos pos = bsource.locateBiome(loc.getX(), | ||
| | ||
- | // Since this method can return | + | // If null, a biome was not found |
if(pos == null) { | if(pos == null) { | ||
throw new SimpleCommandExceptionType(new TranslatableText(" | throw new SimpleCommandExceptionType(new TranslatableText(" | ||
} | } | ||
- | | + | |
int distance = MathHelper.floor(getDistance(loc.getX(), | int distance = MathHelper.floor(getDistance(loc.getX(), | ||
// Popup text that can suggest commands. This is the exact same system that /locate uses. | // Popup text that can suggest commands. This is the exact same system that /locate uses. | ||
- | Text teleportButtonPopup = Texts.bracketed(new TranslatableText(" | + | Text teleportButtonPopup = Texts.bracketed(new TranslatableText(" |
- | | + | |
}); | }); | ||
| | ||
Line 404: | Line 439: | ||
public static class BiomeCompletionProvider { | public static class BiomeCompletionProvider { | ||
- | // This provides suggestions of what biomes can be selected. Since this uses the registry, mods that add new biomes will work without | + | // This provides suggestions of what biomes can be selected. Since this uses the registry, mods that add new biomes will work without |
public static final SuggestionProvider< | public static final SuggestionProvider< | ||
Registry.BIOME.getIds().stream().forEach(identifier -> builder.suggest(identifier.toString(), | Registry.BIOME.getIds().stream().forEach(identifier -> builder.suggest(identifier.toString(), | ||
Line 413: | Line 448: | ||
</ | </ | ||
- | ===== FAQ ===== | + | ===== Custom Argument Types ===== |
- | === What else can I send feedback | + | Brigadier has support for custom argument types and this section goes into showing how to create a simple argument type. |
- | You can choose between Brigadier' | + | 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. |
- | === Why does my IDE complain saying that a method executed by my command needs to catch or throw a CommandSyntaxException === | + | For this example we will create |
- | The solution to this is just to make the methods throw a CommandSyntaxException down the whole chain as the executes block handles the exceptions. | + | First create a class which extends '' |
- | === Can I register commands in run time? === | + | <code java [enable_line_numbers=" |
+ | public class UuidArgumentType implements ArgumentType< | ||
+ | </ | ||
- | You can do this but it is not reccomended. You would get the instance of the CommandManager and add anything | + | ArgumentType requires |
+ | <code java> | ||
+ | @Override | ||
+ | public UUID parse(StringReader reader) throws CommandSyntaxException { | ||
+ | </ | ||
- | After that you will need to send the command | + | This method is where all of your parsing |
- | === Can I unregister commands in run time? === | + | 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. |
- | You can also do this but it is very unstable and could cause unwanted side effects. Lets just say it involves a bunch of Reflection. | + | <code java [enable_line_numbers=" |
+ | int argBeginning = reader.getCursor(); | ||
+ | if (!reader.canRead()) { | ||
+ | reader.skip(); | ||
+ | } | ||
+ | </ | ||
- | Once again you will need to send the command tree to every player again using '' | + | Now we grab the entire argument. Depending on your argument type, you may have a different criteria or be similar |
- | === Can I register client side commands? === | + | <code java [enable_line_numbers=" |
+ | while (reader.canRead() && reader.peek() != ' ') { // peek provides the character at the current cursor position. | ||
+ | reader.skip(); | ||
+ | } | ||
+ | </ | ||
- | Well Fabric currently doesn' | + | Then we will ask the StringReader what the current position of the cursor is an substring our argument out of the command line. |
- | https:// | + | |
- | If you only want the command to only be visible on the integrated server like '' | + | <code java [enable_line_numbers=" |
- | <code java> | + | 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. |
- | dispatcher.register(literal(" | + | |
- | // The permission level 4 on integrated server is the equivalent of having cheats enabled. | + | <code java [enable_line_numbers=" |
- | .requires(source -> source.getMinecraftServer().isSinglePlayer() && source.hasPermissionLevel(4))); | + | try { |
+ | UUID uuid = UUID.fromString(uuidString); // Now our actual logic. | ||
+ | | ||
+ | } catch (Exception ex) { | ||
+ | // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type. | ||
+ | | ||
+ | // Though normal create method could be used. | ||
+ | throw new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader); | ||
+ | } | ||
</ | </ | ||
- | === I want to access X from my mod when a command is ran === | + | The ArgumentType is done, however your client will refuse the parse the argument and throw an error. This is because the server will tell the client what argument type the command |
+ | Within your ModInitializer. For more complex argument types, you may need to create your own ArgumentSerializer. | ||
- | This is going to require a way to statically access your mod with a '' | + | <code java [enable_line_numbers=" |
+ | ArgumentTypes.register(" | ||
+ | // The argument should be what will create the ArgumentType. | ||
+ | </ | ||
- | <code java> | + | And here is the whole ArgumentType: |
- | private static Type instance; | + | |
- | static { // Static option on class initalize for seperate API class for example | + | <file java UuidArgumentType.java [enable_line_numbers=" |
- | | + | |
- | } | + | |
- | public void onInitalize() { // If within your mod initalizer | + | import com.mojang.brigadier.StringReader; |
- | instance = this; | + | import com.mojang.brigadier.arguments.ArgumentType; |
- | } | + | import com.mojang.brigadier.context.CommandContext; |
+ | import com.mojang.brigadier.exceptions.CommandSyntaxException; | ||
+ | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; | ||
+ | import net.minecraft.text.LiteralText; | ||
+ | import net.minecraft.util.SystemUtil; | ||
- | public static | + | import java.util.ArrayList; |
- | return | + | import java.util.Collection; |
+ | import java.util.UUID; | ||
+ | |||
+ | /** | ||
+ | * Represents an ArgumentType that will return a UUID. | ||
+ | */ | ||
+ | public class UuidArgumentType implements ArgumentType< | ||
+ | | ||
+ | return new UuidArgumentType(); | ||
+ | | ||
+ | |||
+ | public static <S> UUID getUuid(String name, CommandContext< | ||
+ | // Note that you should assume the CommandSource wrapped inside of the CommandContext will always be a generic type. | ||
+ | // If you need to access the ServerCommandSource make sure you verify the source is a server command source before casting. | ||
+ | | ||
+ | } | ||
+ | |||
+ | private static final Collection< | ||
+ | list.add(" | ||
+ | list.add(" | ||
+ | list.add(" | ||
+ | }); | ||
+ | |||
+ | @Override | ||
+ | public UUID parse(StringReader reader) throws CommandSyntaxException { | ||
+ | int argBeginning = reader.getCursor(); | ||
+ | if (!reader.canRead()) { | ||
+ | reader.skip(); | ||
+ | } | ||
+ | |||
+ | // Now we check the contents of the argument till either we hit the end of the command line (When canRead becomes false) | ||
+ | // Otherwise we go till reach reach a space, which signifies the next argument | ||
+ | while (reader.canRead() && reader.peek() != ' ') { // peek provides the character at the current cursor position. | ||
+ | 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. | ||
+ | String uuidString = reader.getString().substring(argBeginning, | ||
+ | try { | ||
+ | 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. | ||
+ | } catch (Exception ex) { | ||
+ | // 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 new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Collection< | ||
+ | return EXAMPLES; | ||
+ | } | ||
} | } | ||
- | </code> | + | </file> |
+ | ===== FAQ ===== | ||
+ | |||
+ | === What else can I send feedback to the CommandSource? | ||
+ | |||
+ | You use the Text classes (LiteralText, | ||
+ | |||
+ | === Why does my IDE complain saying that a method executed by my command needs to catch or throw a CommandSyntaxException === | ||
+ | |||
+ | The solution to this is just to make the methods throw a CommandSyntaxException down the whole chain as the executes block handles the exceptions. | ||
+ | |||
+ | === Can I register commands runtime? === | ||
+ | |||
+ | You can do this but it is not reccomended. You would get the instance of the CommandManager and add anything you wish to the CommandDispatcher within it. | ||
+ | |||
+ | After that you will need to send the command tree to every player again using '' | ||
+ | |||
+ | === Can I unregister commands runtime? === | ||
+ | |||
+ | You can also do this but it is very unstable and could cause unwanted side effects. Lets just say it involves a bunch of Reflection. | ||
+ | |||
+ | Once again you will need to send the command tree to every player again using '' | ||
+ | |||
+ | === Can I register client side commands? === | ||
+ | |||
+ | Well Fabric currently doesn' | ||
+ | https:// |
tutorial/commands.txt · Last modified: 2024/02/23 14:22 by allen1210