# Writing a Command Handler In Minecraft, commands are the primary way for both regular players and server operators to interact with the game on a lower level. They can be used to perform a wide range of tasks, from simple chat messages to complex server management commands. In this tutorial, we will create a basic Rock-Paper-Scissors command handler that allows players to play the game against the server. Pumpkin has its own system for handling commands, which is based around each command having a 'tree', which defines the exact structure of the command and its arguments. Each node in the tree represents a command or argument, and the tree is traversed to determine the command to execute and its parameters. We would also like to thank [ploxxxy](https://github.com/ploxxxy) for writing the original [Rock-Paper-Scissors plugin](https://github.com/ploxxxy/rock-paper-scissors-mc) from which this tutorial is based. ## Adding the basics Each command in Pumpkin is defined as a structure that implements the `CommandHandler` trait. This trait requires the implementation of a `handle` method, which takes the sender, server, and consumed arguments as parameters, and returns a `-> Result<(), CommandError>`. Let's define this structure now: ```rs use pumpkin_plugin_api::{ Server, command::{CommandError, CommandSender, ConsumedArgs}, commands::CommandHandler, }; struct RockPaperScissorsExecutor; impl CommandHandler for RockPaperScissorsExecutor { fn handle( &self, _sender: CommandSender, _server: Server, _args: ConsumedArgs, ) -> Result { Ok(1) } } ``` This code defines an empty structure `RockPaperScissorsExecutor` that implements the `CommandExecutor` trait. The `handler` method is defined to return `Ok(1)` when called. ## Adding helper enums First install the `rand` crate via ```bash cargo add rand ``` To make our lives easier, we will also define a couple of enums to represent the possible choices and outcomes of the game, as well as well as a couple functions to generate random choices and check the outcome. Add these to your plugin's code. ```rs use rand::{rng, Rng}; #[derive(PartialEq, Debug, Clone, Copy)] enum Choice { Rock, Paper, Scissors, } enum Outcome { Win, Lose, Draw, } impl Choice { pub fn beats(&self, other: &Choice) -> Outcome { if self == other { return Outcome::Draw; } match (self, other) { (Choice::Rock, Choice::Scissors) => Outcome::Win, (Choice::Paper, Choice::Rock) => Outcome::Win, (Choice::Scissors, Choice::Paper) => Outcome::Win, _ => Outcome::Lose, } } } fn get_random_choice() -> Choice { let choices = [Choice::Rock, Choice::Paper, Choice::Scissors]; let index = rng().random_range(0..3); choices[index] } ``` Now we need to modify the `RockPaperScissorsExecutor` struct to accept a `Choice` parameter and implement the game logic. ```rs struct RockPaperScissorsExecutor(Choice); // [!code ++] struct RockPaperScissorsExecutor; // [!code --] impl CommandHandler for RockPaperScissorsExecutor { fn handle( &self, sender: CommandSender, _server: Server, _args: ConsumedArgs, ) -> Result { let player_choice = self.0; // [!code ++:3] let computer_choice = get_random_choice(); Ok(1) } } ``` This code will allow us to later pass in the player's choice and use it in the game logic, as well as compare it with the computer's choice to determine the outcome of the game. ## Implementing the Game Logic Now we can move on to actually implementing the game logic, and showing the outcome to the players. First we will show the player their and the computer's choice. Add this code to your plugin: ```rs impl CommandHandler for RockPaperScissorsExecutor { fn handle( &self, sender: CommandSender, _server: Server, _args: ConsumedArgs, ) -> Result { let player_choice = self.0; let computer_choice = get_random_choice(); // [!code ++:9] let you_chose = TextComponent::text("You chose: "); you_chose.add_child(TextComponent::text(&format!("{:?}", player_choice))); you_chose.color_named(NamedColor::Aqua); sender.send_message(you_chose); let i_chose = TextComponent::text("I chose: "); i_chose.add_child(TextComponent::text(&format!("{:?}", computer_choice))); i_chose.color_named(NamedColor::Gold); sender.send_message(i_chose); } } ``` Next we can compute the game outcome and show it to the player. Add this code to your plugin: ```rs impl CommandHandler for RockPaperScissorsExecutor { fn handle( &self, sender: CommandSender, _server: Server, _args: ConsumedArgs, ) -> Result { // Existing Code match player_choice.beats(&computer_choice) { // [!code ++:17] Outcome::Win => { let message = TextComponent::text("You win!"); message.color_named(NamedColor::Green); sender.send_message(message); } Outcome::Lose => { let message = TextComponent::text("You lose!"); message.color_named(NamedColor::Red); sender.send_message(message); } Outcome::Draw => { let message = TextComponent::text("It's a tie!"); message.color_named(NamedColor::Yellow); sender.send_message(message); } } Ok(1) } } ``` And that's it! The core logic is done. Now we only have one last thing to do. ## Building and registering a command tree As stated earlier, we need to build a command tree and register it with the server. This will allow players to execute our plugin's commands. Building a command tree isn't very hard, but you have to know the exact structure of the command and its arguments. In this case, we have a command named `rock-paper-scissors`, which will take one required argument (the player's choice). The command tree is initialized using the `Command::new()` function. This function takes two arguments: a list of names, where the first is the main command name, and the others are aliases for the command; and a command description which is used to describe the command in the help menu. We can then add 'branches' to the tree using the `.then()` method. This method accepts a 'leaf' which can be built with the `CommandNode::literal()`, `CommandNode::argument()`, or `CommandNode::require()` functions. For the rock-paper-scissors command, we'll create 3 separate branches, each with a `CommandNode::literal()` leaf node for the player's choice. We will also register the command tree with the server and with a `permission` of `hello-pumpkin:command.rockpaperscisors` which will allow anyone with that permission to execute the command. Add the following code to your `on_load()` method: ```rs struct HelloPlugin; impl Plugin for HelloPlugin { fn new() -> Self { HelloPlugin } fn metadata(&self) -> PluginMetadata { // Existing Code } fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { // Existing Code let command = Command::new( // [!code ++:20] &["rps".to_string(), "rockpaperscissors".to_string()], "Play Rock Paper Scissors with the server.", ); command.then(CommandNode::literal("rock").execute(RockPaperScissorsExecutor(Choice::Rock))); command .then(CommandNode::literal("paper").execute(RockPaperScissorsExecutor(Choice::Paper))); command.then( CommandNode::literal("scissors").execute(RockPaperScissorsExecutor(Choice::Scissors)), ); let permission = Permission { node: "hello-pumpkin:command.rockpaperscisors".to_string(), description: "Allows the player to play rock paper scisors".to_string(), default: PermissionDefault::Allow, children: Vec::new(), }; context.register_permission(&permission)?; context.register_command(command, "hello-pumpkin:command.rockpaperscisors"); Ok(()) } } ``` And that's it! If you compile the plugin, you can test it by running the following command: ```bash /rps rock ```