Skip to content

Commit

Permalink
Support parsing players out of commands and custom error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Snowiiii committed Oct 18, 2024
1 parent 406db91 commit 5ac45b0
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 83 deletions.
10 changes: 7 additions & 3 deletions pumpkin/src/client/player_packet.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::f32::consts::PI;
use std::{f32::consts::PI, sync::Arc};

use crate::{
commands::CommandSender,
Expand Down Expand Up @@ -216,9 +216,13 @@ impl Player {
world.broadcast_packet_expect(&[self.client.id], &packet);
}

pub fn handle_chat_command(&self, server: &Server, command: SChatCommand) {
pub fn handle_chat_command(self: &Arc<Self>, server: &Server, command: SChatCommand) {
let dispatcher = server.command_dispatcher.clone();
dispatcher.handle_command(&mut CommandSender::Player(self), server, &command.command);
dispatcher.handle_command(
&mut CommandSender::Player(self.clone()),
server,
&command.command,
);
if ADVANCED_CONFIG.commands.log_console {
log::info!(
"Player ({}): executed command /{}",
Expand Down
67 changes: 38 additions & 29 deletions pumpkin/src/commands/arg_player.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,50 @@
use std::sync::Arc;

use crate::commands::dispatcher::InvalidTreeError;
use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError;
use crate::commands::tree::{ConsumedArgs, RawArgs};
use crate::commands::CommandSender;
use crate::commands::CommandSender::Player;
use crate::server::Server;

/// todo: implement (so far only own name + @s/@p is implemented)
pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option<String> {
let s = args.pop()?;

match s {
"@s" if src.is_player() => Some(s.into()),
"@p" if src.is_player() => Some(s.into()),
"@r" => None, // todo: implement random player target selector
"@a" | "@e" => None, // todo: implement all players target selector
_ => {
// todo: implement any other player than sender
if let Player(player) = src {
let profile = &player.gameprofile;
if profile.name == s {
return Some(s.into());
};
};
None
pub fn consume_arg_player(
src: &CommandSender,
server: &Server,
args: &mut RawArgs,
) -> Result<String, Option<String>> {
if let Some(arg) = args.pop() {
match arg {
"@s" => {
if src.is_player() {
return Ok(arg.into());
} else {
return Err(Some("You are not a Player".into()));
}
}
"@p" if src.is_player() => return Ok(arg.into()),
"@r" => todo!(), // todo: implement random player target selector
"@a" | "@e" => todo!(), // todo: implement all players target selector
name => {
// todo: implement any other player than sender
for world in &server.worlds {
if world.get_player_by_name(name).is_some() {
return Ok(name.into());
}
}
return Err(Some(format!("Player not found: {}", arg)));
}
}
}
Err(None)
}

/// todo: implement (so far only own name + @s/@p is implemented)
pub fn parse_arg_player<'a>(
src: &'a mut CommandSender,
_server: &Server,
pub fn parse_arg_player(
src: &mut CommandSender,
server: &Server,
arg_name: &str,
consumed_args: &ConsumedArgs,
) -> Result<&'a crate::entity::player::Player, InvalidTreeError> {
) -> Result<Arc<crate::entity::player::Player>, InvalidTreeError> {
let s = consumed_args
.get(arg_name)
.ok_or(InvalidConsumptionError(None))?
Expand All @@ -44,14 +55,12 @@ pub fn parse_arg_player<'a>(
"@p" if src.is_player() => Ok(src.as_mut_player().unwrap()),
"@r" => Err(InvalidConsumptionError(Some(s.into()))), // todo: implement random player target selector
"@a" | "@e" => Err(InvalidConsumptionError(Some(s.into()))), // todo: implement all players target selector
_ => {
// todo: implement any other player than sender
if let Player(player) = src {
let profile = &player.gameprofile;
if profile.name == s {
name => {
for world in &server.worlds {
if let Some(player) = world.get_player_by_name(name) {
return Ok(player);
};
};
}
}
Err(InvalidConsumptionError(Some(s.into())))
}
}
Expand Down
30 changes: 19 additions & 11 deletions pumpkin/src/commands/cmd_gamemode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::commands::tree::{CommandTree, ConsumedArgs, RawArgs};
use crate::commands::tree_builder::{argument, require};
use crate::commands::CommandSender;
use crate::commands::CommandSender::Player;
use crate::server::Server;

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

Expand All @@ -22,20 +23,27 @@ const DESCRIPTION: &str = "Change a player's gamemode.";
const ARG_GAMEMODE: &str = "gamemode";
const ARG_TARGET: &str = "target";

pub fn consume_arg_gamemode(_src: &CommandSender, args: &mut RawArgs) -> Option<String> {
let s = args.pop()?;

if let Ok(id) = s.parse::<u8>() {
match GameMode::from_u8(id) {
None | Some(GameMode::Undefined) => {}
Some(_) => return Some(s.into()),
pub fn consume_arg_gamemode(
_src: &CommandSender,
_server: &Server,
args: &mut RawArgs,
) -> Result<String, Option<String>> {
if let Some(arg) = args.pop() {
if let Ok(id) = arg.parse::<u8>() {
match GameMode::from_u8(id) {
None | Some(GameMode::Undefined) => {}
Some(_) => return Ok(arg.into()),
};
};
};

match GameMode::from_str(s) {
Err(_) | Ok(GameMode::Undefined) => None,
Ok(_) => Some(s.into()),
match GameMode::from_str(arg) {
Err(_) | Ok(GameMode::Undefined) => {
return Err(Some(format!("Gamemode not found: {}", arg)))
}
Ok(_) => return Ok(arg.into()),
}
}
Err(None)
}

pub fn parse_arg_gamemode(consumed_args: &ConsumedArgs) -> Result<GameMode, InvalidTreeError> {
Expand Down
9 changes: 7 additions & 2 deletions pumpkin/src/commands/cmd_help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 pumpkin_core::text::TextComponent;

const NAMES: [&str; 3] = ["help", "h", "?"];
Expand All @@ -11,12 +12,16 @@ const DESCRIPTION: &str = "Print a help message.";

const ARG_COMMAND: &str = "command";

fn consume_arg_command(_src: &CommandSender, _args: &mut RawArgs) -> Option<String> {
fn consume_arg_command(
_src: &CommandSender,
_server: &Server,
_args: &mut RawArgs,
) -> Result<String, Option<String>> {
// let s = args.pop()?;

// dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into())
// TODO
None
Err(None)
}

fn parse_arg_command<'a>(
Expand Down
13 changes: 5 additions & 8 deletions pumpkin/src/commands/cmd_kick.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
use crate::commands::arg_player::parse_arg_player;
use crate::commands::tree::CommandTree;
use crate::commands::tree::RawArgs;
use crate::commands::tree_builder::argument;
use crate::commands::CommandSender;
use pumpkin_core::text::{color::NamedColor, TextComponent};

use super::arg_player::consume_arg_player;

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

const ARG_TARGET: &str = "target";

pub fn consume_arg_target(_src: &CommandSender, args: &mut RawArgs) -> Option<String> {
consume_arg_player(_src, args)
}

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

Expand Down
9 changes: 2 additions & 7 deletions pumpkin/src/commands/cmd_kill.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
use crate::commands::tree::CommandTree;
use crate::commands::tree::RawArgs;
use crate::commands::tree_builder::argument;
use crate::commands::CommandSender;
use pumpkin_core::text::{color::NamedColor, TextComponent};

const NAMES: [&str; 1] = ["kill"];
const DESCRIPTION: &str = "Kills a target player.";

const ARG_TARGET: &str = "target";

pub fn consume_arg_target(_src: &CommandSender, args: &mut RawArgs) -> Option<String> {
consume_arg_player(_src, args)
}

pub fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).with_child(
argument(ARG_TARGET, consume_arg_target).execute(&|sender, server, args| {
argument(ARG_TARGET, consume_arg_player).execute(&|sender, server, args| {
// TODO parse entities not only players
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;
target.living_entity.kill();

Expand Down
36 changes: 20 additions & 16 deletions pumpkin/src/commands/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,17 @@ impl<'a> CommandDispatcher<'a> {
println!("Error while parsing command \"{cmd}\": a requirement that was expected was not met.");
return Err("Internal Error (See logs for details)".into());
}
Ok(is_fitting_path) => {
if is_fitting_path {
return Ok(());
Ok(is_fitting_path) => match is_fitting_path {
Ok(_) => return Ok(()),
Err(error) => {
// Custom error message or not ?
if let Some(error) = error {
return Err(error);
}
}
}
},
}
}

Err(format!("Invalid Syntax. Usage: {}", tree))
}

Expand All @@ -90,44 +93,45 @@ impl<'a> CommandDispatcher<'a> {
path: Vec<usize>,
tree: &CommandTree,
mut raw_args: RawArgs,
) -> Result<bool, InvalidTreeError> {
) -> Result<Result<(), Option<String>>, InvalidTreeError> {
let mut parsed_args: ConsumedArgs = HashMap::new();

for node in path.iter().map(|&i| &tree.nodes[i]) {
match node.node_type {
NodeType::ExecuteLeaf { run } => {
return if raw_args.is_empty() {
run(src, server, &parsed_args)?;
Ok(true)
Ok(Ok(()))
} else {
Ok(false)
Ok(Err(None))
};
}
NodeType::Literal { string, .. } => {
if raw_args.pop() != Some(string) {
return Ok(false);
return Ok(Err(None));
}
}
NodeType::Argument {
consumer: consume,
name,
..
} => {
if let Some(consumed) = consume(src, &mut raw_args) {
} => match consume(src, server, &mut raw_args) {
Ok(consumed) => {
parsed_args.insert(name, consumed);
} else {
return Ok(false);
}
}
Err(err) => {
return Ok(Err(err));
}
},
NodeType::Require { predicate, .. } => {
if !predicate(src) {
return Ok(false);
return Ok(Err(None));
}
}
}
}

Ok(false)
Ok(Err(None))
}

/// Register a command with the dispatcher.
Expand Down
8 changes: 5 additions & 3 deletions pumpkin/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::Arc;

use dispatcher::InvalidTreeError;
use pumpkin_core::text::TextComponent;
use tree::ConsumedArgs;
Expand All @@ -21,7 +23,7 @@ mod tree_format;
pub enum CommandSender<'a> {
Rcon(&'a mut Vec<String>),
Console,
Player(&'a Player),
Player(Arc<Player>),
}

impl<'a> CommandSender<'a> {
Expand Down Expand Up @@ -49,9 +51,9 @@ impl<'a> CommandSender<'a> {
CommandSender::Rcon(_) => true,
}
}
pub fn as_mut_player(&mut self) -> Option<&Player> {
pub fn as_mut_player(&mut self) -> Option<Arc<Player>> {
match self {
CommandSender::Player(player) => Some(player),
CommandSender::Player(player) => Some(player.clone()),
CommandSender::Console => None,
CommandSender::Rcon(_) => None,
}
Expand Down
6 changes: 4 additions & 2 deletions pumpkin/src/commands/tree.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::RunFunctionType;
use crate::commands::CommandSender;
use crate::{commands::CommandSender, server::Server};
use std::collections::{HashMap, VecDeque};

/// see [crate::commands::tree_builder::argument]
Expand All @@ -9,7 +9,9 @@ pub type RawArgs<'a> = Vec<&'a str>;
pub type ConsumedArgs<'a> = HashMap<&'a str, String>;

/// see [crate::commands::tree_builder::argument]
pub type ArgumentConsumer<'a> = fn(&CommandSender, &mut RawArgs) -> Option<String>;
/// Provide value or an Optional error message, If no Error message provided the default will be used
pub type ArgumentConsumer<'a> =
fn(&CommandSender, &Server, &mut RawArgs) -> Result<String, Option<String>>;

pub struct Node<'a> {
pub(crate) children: Vec<usize>,
Expand Down
4 changes: 2 additions & 2 deletions pumpkin/src/entity/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ impl Player {
}

impl Player {
pub async fn process_packets(&self, server: &Arc<Server>) {
pub async fn process_packets(self: &Arc<Self>, server: &Arc<Server>) {
let mut packets = self.client.client_packets_queue.lock();
while let Some(mut packet) = packets.pop_back() {
match self.handle_play_packet(server, &mut packet).await {
Expand All @@ -297,7 +297,7 @@ impl Player {
}

pub async fn handle_play_packet(
&self,
self: &Arc<Self>,
server: &Arc<Server>,
packet: &mut RawPacket,
) -> Result<(), Box<dyn PumpkinError>> {
Expand Down

0 comments on commit 5ac45b0

Please sign in to comment.