Skip to content

Commit

Permalink
make commands async (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
kralverde authored Oct 25, 2024
1 parent 4bb52a7 commit 7347bae
Show file tree
Hide file tree
Showing 13 changed files with 323 additions and 163 deletions.
3 changes: 3 additions & 0 deletions pumpkin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ png = "0.17.14"

# logging
simple_logger = { version = "5.0.0", features = ["threads"] }

# commands
async-trait = "0.1.83"
48 changes: 30 additions & 18 deletions pumpkin/src/commands/cmd_echest.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,44 @@
use async_trait::async_trait;
use pumpkin_inventory::OpenContainer;

use crate::commands::tree::CommandTree;

use super::RunFunctionType;

const NAMES: [&str; 2] = ["echest", "enderchest"];

const DESCRIPTION: &str =
"Show your personal enderchest (this command is used for testing container behaviour)";

#[allow(unused_variables)]
struct EchestExecutor {}

pub fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, server, _| {
#[async_trait]
impl RunFunctionType for EchestExecutor {
async fn execute(
&self,
sender: &mut super::CommandSender,
server: &crate::server::Server,
_args: &super::tree::ConsumedArgs,
) -> Result<(), super::dispatcher::InvalidTreeError> {
if let Some(player) = sender.as_player() {
let entity_id = player.entity_id();
// player.open_container.store(Some(0));
// {
// let mut open_containers = server.open_containers.write().await;
// match open_containers.get_mut(&0) {
// Some(ender_chest) => {
// ender_chest.add_player(entity_id);
// }
// None => {
// let open_container = OpenContainer::empty(entity_id);
// open_containers.insert(0, open_container);
// }
// }
// }
// player.open_container(server, "minecraft:generic_9x3");
player.open_container.store(Some(0));
{
let mut open_containers = server.open_containers.write().await;
if let Some(ender_chest) = open_containers.get_mut(&0) {
ender_chest.add_player(entity_id);
} else {
let open_container = OpenContainer::empty(entity_id);
open_containers.insert(0, open_container);
}
}
player.open_container(server, "minecraft:generic_9x3").await;
}

Ok(())
})
}
}

pub fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).execute(&EchestExecutor {})
}
122 changes: 77 additions & 45 deletions pumpkin/src/commands/cmd_gamemode.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::str::FromStr;

use async_trait::async_trait;
use num_traits::FromPrimitive;
use pumpkin_core::GameMode;

use crate::TextComponent;

use crate::commands::arg_player::{consume_arg_player, parse_arg_player};

use crate::commands::dispatcher::InvalidTreeError;
Expand All @@ -15,6 +18,8 @@ use crate::commands::CommandSender;
use crate::commands::CommandSender::Player;
use crate::server::Server;

use super::RunFunctionType;

const NAMES: [&str; 1] = ["gamemode"];

const DESCRIPTION: &str = "Change a player's gamemode.";
Expand Down Expand Up @@ -63,56 +68,83 @@ pub fn parse_arg_gamemode(consumed_args: &ConsumedArgs) -> Result<GameMode, Inva
}
}

struct GamemodeTargetSelf {}

#[async_trait]
impl RunFunctionType for GamemodeTargetSelf {
async fn execute(
&self,
sender: &mut CommandSender,
_server: &Server,
args: &ConsumedArgs,
) -> Result<(), InvalidTreeError> {
let gamemode = parse_arg_gamemode(args)?;

if let Player(target) = sender {
if target.gamemode.load() == gamemode {
target
.send_system_message(&TextComponent::text(&format!(
"You already in {gamemode:?} gamemode"
)))
.await;
} else {
target.set_gamemode(gamemode).await;
target
.send_system_message(&TextComponent::text(&format!(
"Game mode was set to {gamemode:?}"
)))
.await;
}
Ok(())
} else {
Err(InvalidRequirementError)
}
}
}

struct GamemodeTargetPlayer {}

#[async_trait]
impl RunFunctionType for GamemodeTargetPlayer {
async fn execute(
&self,
sender: &mut CommandSender,
server: &Server,
args: &ConsumedArgs,
) -> Result<(), InvalidTreeError> {
let gamemode = parse_arg_gamemode(args)?;
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;

if target.gamemode.load() == gamemode {
sender
.send_message(TextComponent::text(&format!(
"{} is already in {:?} gamemode",
target.gameprofile.name, gamemode
)))
.await;
} else {
target.set_gamemode(gamemode).await;
sender
.send_message(TextComponent::text(&format!(
"{}'s Game mode was set to {:?}",
target.gameprofile.name, gamemode
)))
.await;
}

Ok(())
}
}

#[allow(clippy::redundant_closure_for_method_calls)]
pub fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).with_child(
require(&|sender| sender.permission_lvl() >= 2).with_child(
argument(ARG_GAMEMODE, consume_arg_gamemode)
.with_child(require(&|sender| sender.is_player()).execute(&GamemodeTargetSelf {}))
.with_child(
require(&|sender| sender.is_player()).execute(&|sender, _, args| {
let gamemode = parse_arg_gamemode(args)?;

if let Player(target) = sender {
if target.gamemode.load() == gamemode {
// target.send_system_message(&TextComponent::text(&format!(
// "You already in {:?} gamemode",
// gamemode
// )));
} else {
// TODO
// target.set_gamemode(gamemode);
// target.send_system_message(&TextComponent::text(&format!(
// "Game mode was set to {:?}",
// gamemode
// )));
}
Ok(())
} else {
Err(InvalidRequirementError)
}
}),
)
.with_child(argument(ARG_TARGET, consume_arg_player).execute(
&|sender, server, args| {
let gamemode = parse_arg_gamemode(args)?;
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;

if target.gamemode.load() == gamemode {
// sender.send_message(TextComponent::text(&format!(
// "{} is already in {:?} gamemode",
// target.gameprofile.name, gamemode
// )));
} else {
// TODO
// target.set_gamemode(gamemode);
// sender.send_message(TextComponent::text(&format!(
// "{}'s Game mode was set to {:?}",
// target.gameprofile.name, gamemode
// )));
}

Ok(())
},
)),
argument(ARG_TARGET, consume_arg_player).execute(&GamemodeTargetPlayer {}),
),
),
)
}
97 changes: 63 additions & 34 deletions pumpkin/src/commands/cmd_help.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use async_trait::async_trait;
use pumpkin_core::text::TextComponent;

use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError;
use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError};
use crate::commands::tree::{Command, CommandTree, ConsumedArgs, RawArgs};
use crate::commands::tree_builder::argument;
use crate::commands::CommandSender;
use crate::server::Server;

use super::RunFunctionType;

const NAMES: [&str; 3] = ["help", "h", "?"];

const DESCRIPTION: &str = "Print a help message.";
Expand Down Expand Up @@ -36,41 +41,65 @@ fn parse_arg_command<'a>(
.map_err(|_| InvalidConsumptionError(Some(command_name.into())))
}

#[allow(unused_variables)]
struct BaseHelpExecutor {}

#[async_trait]
impl RunFunctionType for BaseHelpExecutor {
async fn execute(
&self,
sender: &mut CommandSender,
server: &Server,
args: &ConsumedArgs,
) -> Result<(), InvalidTreeError> {
let tree = parse_arg_command(args, &server.command_dispatcher)?;

sender
.send_message(TextComponent::text(&format!(
"{} - {} Usage: {}",
tree.names.join("/"),
tree.description,
tree
)))
.await;

Ok(())
}
}

struct CommandHelpExecutor {}

#[async_trait]
impl RunFunctionType for CommandHelpExecutor {
async fn execute(
&self,
sender: &mut CommandSender,
server: &Server,
_args: &ConsumedArgs,
) -> Result<(), InvalidTreeError> {
let mut keys: Vec<&str> = server.command_dispatcher.commands.keys().copied().collect();
keys.sort_unstable();

for key in keys {
let Command::Tree(tree) = &server.command_dispatcher.commands[key] else {
continue;
};

sender
.send_message(TextComponent::text(&format!(
"{} - {} Usage: {}",
tree.names.join("/"),
tree.description,
tree
)))
.await;
}

Ok(())
}
}

pub fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION)
.with_child(
argument(ARG_COMMAND, consume_arg_command).execute(&|sender, server, args| {
let tree = parse_arg_command(args, &server.command_dispatcher)?;

// sender.send_message(TextComponent::text(&format!(
// "{} - {} Usage: {}",
// tree.names.join("/"),
// tree.description,
// tree
// )));

Ok(())
}),
)
.execute(&|sender, server, _args| {
let mut keys: Vec<&str> = server.command_dispatcher.commands.keys().copied().collect();
keys.sort_unstable();

for key in keys {
let Command::Tree(tree) = &server.command_dispatcher.commands[key] else {
continue;
};

// sender.send_message(TextComponent::text(&format!(
// "{} - {} Usage: {}",
// tree.names.join("/"),
// tree.description,
// tree
// )));
}

Ok(())
})
.with_child(argument(ARG_COMMAND, consume_arg_command).execute(&BaseHelpExecutor {}))
.execute(&CommandHelpExecutor {})
}
44 changes: 31 additions & 13 deletions pumpkin/src/commands/cmd_kick.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
use async_trait::async_trait;
use pumpkin_core::text::color::NamedColor;
use pumpkin_core::text::TextComponent;

use crate::commands::arg_player::parse_arg_player;
use crate::commands::tree::CommandTree;
use crate::commands::tree_builder::argument;

use super::arg_player::consume_arg_player;
use super::RunFunctionType;

const NAMES: [&str; 1] = ["kick"];
const DESCRIPTION: &str = "Kicks the target player from the server.";

const ARG_TARGET: &str = "target";

#[expect(unused)]
struct KickExecutor {}

#[async_trait]
impl RunFunctionType for KickExecutor {
async fn execute(
&self,
sender: &mut super::CommandSender,
server: &crate::server::Server,
args: &super::tree::ConsumedArgs,
) -> Result<(), super::dispatcher::InvalidTreeError> {
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;
target
.kick(TextComponent::text("Kicked by an operator"))
.await;

sender
.send_message(
TextComponent::text("Player has been kicked.").color_named(NamedColor::Blue),
)
.await;

Ok(())
}
}

pub fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).with_child(
argument(ARG_TARGET, consume_arg_player).execute(&|sender, server, args| {
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;
// target.kick(TextComponent::text("Kicked by an operator"));

// sender.send_message(
// TextComponent::text("Player has been kicked.").color_named(NamedColor::Blue),
// );

Ok(())
}),
)
CommandTree::new(NAMES, DESCRIPTION)
.with_child(argument(ARG_TARGET, consume_arg_player).execute(&KickExecutor {}))
}
Loading

0 comments on commit 7347bae

Please sign in to comment.