Skip to content

Commit 5ac45b0

Browse files
committed
Support parsing players out of commands and custom error messages
1 parent 406db91 commit 5ac45b0

File tree

10 files changed

+109
-83
lines changed

10 files changed

+109
-83
lines changed

pumpkin/src/client/player_packet.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::f32::consts::PI;
1+
use std::{f32::consts::PI, sync::Arc};
22

33
use crate::{
44
commands::CommandSender,
@@ -216,9 +216,13 @@ impl Player {
216216
world.broadcast_packet_expect(&[self.client.id], &packet);
217217
}
218218

219-
pub fn handle_chat_command(&self, server: &Server, command: SChatCommand) {
219+
pub fn handle_chat_command(self: &Arc<Self>, server: &Server, command: SChatCommand) {
220220
let dispatcher = server.command_dispatcher.clone();
221-
dispatcher.handle_command(&mut CommandSender::Player(self), server, &command.command);
221+
dispatcher.handle_command(
222+
&mut CommandSender::Player(self.clone()),
223+
server,
224+
&command.command,
225+
);
222226
if ADVANCED_CONFIG.commands.log_console {
223227
log::info!(
224228
"Player ({}): executed command /{}",

pumpkin/src/commands/arg_player.rs

+38-29
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,50 @@
1+
use std::sync::Arc;
2+
13
use crate::commands::dispatcher::InvalidTreeError;
24
use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError;
35
use crate::commands::tree::{ConsumedArgs, RawArgs};
46
use crate::commands::CommandSender;
5-
use crate::commands::CommandSender::Player;
67
use crate::server::Server;
78

89
/// todo: implement (so far only own name + @s/@p is implemented)
9-
pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option<String> {
10-
let s = args.pop()?;
11-
12-
match s {
13-
"@s" if src.is_player() => Some(s.into()),
14-
"@p" if src.is_player() => Some(s.into()),
15-
"@r" => None, // todo: implement random player target selector
16-
"@a" | "@e" => None, // todo: implement all players target selector
17-
_ => {
18-
// todo: implement any other player than sender
19-
if let Player(player) = src {
20-
let profile = &player.gameprofile;
21-
if profile.name == s {
22-
return Some(s.into());
23-
};
24-
};
25-
None
10+
pub fn consume_arg_player(
11+
src: &CommandSender,
12+
server: &Server,
13+
args: &mut RawArgs,
14+
) -> Result<String, Option<String>> {
15+
if let Some(arg) = args.pop() {
16+
match arg {
17+
"@s" => {
18+
if src.is_player() {
19+
return Ok(arg.into());
20+
} else {
21+
return Err(Some("You are not a Player".into()));
22+
}
23+
}
24+
"@p" if src.is_player() => return Ok(arg.into()),
25+
"@r" => todo!(), // todo: implement random player target selector
26+
"@a" | "@e" => todo!(), // todo: implement all players target selector
27+
name => {
28+
// todo: implement any other player than sender
29+
for world in &server.worlds {
30+
if world.get_player_by_name(name).is_some() {
31+
return Ok(name.into());
32+
}
33+
}
34+
return Err(Some(format!("Player not found: {}", arg)));
35+
}
2636
}
2737
}
38+
Err(None)
2839
}
2940

3041
/// todo: implement (so far only own name + @s/@p is implemented)
31-
pub fn parse_arg_player<'a>(
32-
src: &'a mut CommandSender,
33-
_server: &Server,
42+
pub fn parse_arg_player(
43+
src: &mut CommandSender,
44+
server: &Server,
3445
arg_name: &str,
3546
consumed_args: &ConsumedArgs,
36-
) -> Result<&'a crate::entity::player::Player, InvalidTreeError> {
47+
) -> Result<Arc<crate::entity::player::Player>, InvalidTreeError> {
3748
let s = consumed_args
3849
.get(arg_name)
3950
.ok_or(InvalidConsumptionError(None))?
@@ -44,14 +55,12 @@ pub fn parse_arg_player<'a>(
4455
"@p" if src.is_player() => Ok(src.as_mut_player().unwrap()),
4556
"@r" => Err(InvalidConsumptionError(Some(s.into()))), // todo: implement random player target selector
4657
"@a" | "@e" => Err(InvalidConsumptionError(Some(s.into()))), // todo: implement all players target selector
47-
_ => {
48-
// todo: implement any other player than sender
49-
if let Player(player) = src {
50-
let profile = &player.gameprofile;
51-
if profile.name == s {
58+
name => {
59+
for world in &server.worlds {
60+
if let Some(player) = world.get_player_by_name(name) {
5261
return Ok(player);
53-
};
54-
};
62+
}
63+
}
5564
Err(InvalidConsumptionError(Some(s.into())))
5665
}
5766
}

pumpkin/src/commands/cmd_gamemode.rs

+19-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::commands::tree::{CommandTree, ConsumedArgs, RawArgs};
1414
use crate::commands::tree_builder::{argument, require};
1515
use crate::commands::CommandSender;
1616
use crate::commands::CommandSender::Player;
17+
use crate::server::Server;
1718

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

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

25-
pub fn consume_arg_gamemode(_src: &CommandSender, args: &mut RawArgs) -> Option<String> {
26-
let s = args.pop()?;
27-
28-
if let Ok(id) = s.parse::<u8>() {
29-
match GameMode::from_u8(id) {
30-
None | Some(GameMode::Undefined) => {}
31-
Some(_) => return Some(s.into()),
26+
pub fn consume_arg_gamemode(
27+
_src: &CommandSender,
28+
_server: &Server,
29+
args: &mut RawArgs,
30+
) -> Result<String, Option<String>> {
31+
if let Some(arg) = args.pop() {
32+
if let Ok(id) = arg.parse::<u8>() {
33+
match GameMode::from_u8(id) {
34+
None | Some(GameMode::Undefined) => {}
35+
Some(_) => return Ok(arg.into()),
36+
};
3237
};
33-
};
3438

35-
match GameMode::from_str(s) {
36-
Err(_) | Ok(GameMode::Undefined) => None,
37-
Ok(_) => Some(s.into()),
39+
match GameMode::from_str(arg) {
40+
Err(_) | Ok(GameMode::Undefined) => {
41+
return Err(Some(format!("Gamemode not found: {}", arg)))
42+
}
43+
Ok(_) => return Ok(arg.into()),
44+
}
3845
}
46+
Err(None)
3947
}
4048

4149
pub fn parse_arg_gamemode(consumed_args: &ConsumedArgs) -> Result<GameMode, InvalidTreeError> {

pumpkin/src/commands/cmd_help.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError};
33
use crate::commands::tree::{Command, CommandTree, ConsumedArgs, RawArgs};
44
use crate::commands::tree_builder::argument;
55
use crate::commands::CommandSender;
6+
use crate::server::Server;
67
use pumpkin_core::text::TextComponent;
78

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

1213
const ARG_COMMAND: &str = "command";
1314

14-
fn consume_arg_command(_src: &CommandSender, _args: &mut RawArgs) -> Option<String> {
15+
fn consume_arg_command(
16+
_src: &CommandSender,
17+
_server: &Server,
18+
_args: &mut RawArgs,
19+
) -> Result<String, Option<String>> {
1520
// let s = args.pop()?;
1621

1722
// dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into())
1823
// TODO
19-
None
24+
Err(None)
2025
}
2126

2227
fn parse_arg_command<'a>(

pumpkin/src/commands/cmd_kick.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
1-
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
1+
use crate::commands::arg_player::parse_arg_player;
22
use crate::commands::tree::CommandTree;
3-
use crate::commands::tree::RawArgs;
43
use crate::commands::tree_builder::argument;
5-
use crate::commands::CommandSender;
64
use pumpkin_core::text::{color::NamedColor, TextComponent};
75

6+
use super::arg_player::consume_arg_player;
7+
88
const NAMES: [&str; 1] = ["kick"];
99
const DESCRIPTION: &str = "Kicks the target player from the server.";
1010

1111
const ARG_TARGET: &str = "target";
1212

13-
pub fn consume_arg_target(_src: &CommandSender, args: &mut RawArgs) -> Option<String> {
14-
consume_arg_player(_src, args)
15-
}
16-
1713
pub fn init_command_tree<'a>() -> CommandTree<'a> {
1814
CommandTree::new(NAMES, DESCRIPTION).with_child(
19-
argument(ARG_TARGET, consume_arg_target).execute(&|sender, server, args| {
15+
argument(ARG_TARGET, consume_arg_player).execute(&|sender, server, args| {
16+
dbg!("aa");
2017
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;
2118
target.kick(TextComponent::text("Kicked by an operator"));
2219

pumpkin/src/commands/cmd_kill.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
22
use crate::commands::tree::CommandTree;
3-
use crate::commands::tree::RawArgs;
43
use crate::commands::tree_builder::argument;
5-
use crate::commands::CommandSender;
64
use pumpkin_core::text::{color::NamedColor, TextComponent};
75

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

119
const ARG_TARGET: &str = "target";
1210

13-
pub fn consume_arg_target(_src: &CommandSender, args: &mut RawArgs) -> Option<String> {
14-
consume_arg_player(_src, args)
15-
}
16-
1711
pub fn init_command_tree<'a>() -> CommandTree<'a> {
1812
CommandTree::new(NAMES, DESCRIPTION).with_child(
19-
argument(ARG_TARGET, consume_arg_target).execute(&|sender, server, args| {
13+
argument(ARG_TARGET, consume_arg_player).execute(&|sender, server, args| {
14+
// TODO parse entities not only players
2015
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;
2116
target.living_entity.kill();
2217

pumpkin/src/commands/dispatcher.rs

+20-16
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,17 @@ impl<'a> CommandDispatcher<'a> {
5858
println!("Error while parsing command \"{cmd}\": a requirement that was expected was not met.");
5959
return Err("Internal Error (See logs for details)".into());
6060
}
61-
Ok(is_fitting_path) => {
62-
if is_fitting_path {
63-
return Ok(());
61+
Ok(is_fitting_path) => match is_fitting_path {
62+
Ok(_) => return Ok(()),
63+
Err(error) => {
64+
// Custom error message or not ?
65+
if let Some(error) = error {
66+
return Err(error);
67+
}
6468
}
65-
}
69+
},
6670
}
6771
}
68-
6972
Err(format!("Invalid Syntax. Usage: {}", tree))
7073
}
7174

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

9699
for node in path.iter().map(|&i| &tree.nodes[i]) {
97100
match node.node_type {
98101
NodeType::ExecuteLeaf { run } => {
99102
return if raw_args.is_empty() {
100103
run(src, server, &parsed_args)?;
101-
Ok(true)
104+
Ok(Ok(()))
102105
} else {
103-
Ok(false)
106+
Ok(Err(None))
104107
};
105108
}
106109
NodeType::Literal { string, .. } => {
107110
if raw_args.pop() != Some(string) {
108-
return Ok(false);
111+
return Ok(Err(None));
109112
}
110113
}
111114
NodeType::Argument {
112115
consumer: consume,
113116
name,
114117
..
115-
} => {
116-
if let Some(consumed) = consume(src, &mut raw_args) {
118+
} => match consume(src, server, &mut raw_args) {
119+
Ok(consumed) => {
117120
parsed_args.insert(name, consumed);
118-
} else {
119-
return Ok(false);
120121
}
121-
}
122+
Err(err) => {
123+
return Ok(Err(err));
124+
}
125+
},
122126
NodeType::Require { predicate, .. } => {
123127
if !predicate(src) {
124-
return Ok(false);
128+
return Ok(Err(None));
125129
}
126130
}
127131
}
128132
}
129133

130-
Ok(false)
134+
Ok(Err(None))
131135
}
132136

133137
/// Register a command with the dispatcher.

pumpkin/src/commands/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::sync::Arc;
2+
13
use dispatcher::InvalidTreeError;
24
use pumpkin_core::text::TextComponent;
35
use tree::ConsumedArgs;
@@ -21,7 +23,7 @@ mod tree_format;
2123
pub enum CommandSender<'a> {
2224
Rcon(&'a mut Vec<String>),
2325
Console,
24-
Player(&'a Player),
26+
Player(Arc<Player>),
2527
}
2628

2729
impl<'a> CommandSender<'a> {
@@ -49,9 +51,9 @@ impl<'a> CommandSender<'a> {
4951
CommandSender::Rcon(_) => true,
5052
}
5153
}
52-
pub fn as_mut_player(&mut self) -> Option<&Player> {
54+
pub fn as_mut_player(&mut self) -> Option<Arc<Player>> {
5355
match self {
54-
CommandSender::Player(player) => Some(player),
56+
CommandSender::Player(player) => Some(player.clone()),
5557
CommandSender::Console => None,
5658
CommandSender::Rcon(_) => None,
5759
}

pumpkin/src/commands/tree.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::RunFunctionType;
2-
use crate::commands::CommandSender;
2+
use crate::{commands::CommandSender, server::Server};
33
use std::collections::{HashMap, VecDeque};
44

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

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

1416
pub struct Node<'a> {
1517
pub(crate) children: Vec<usize>,

pumpkin/src/entity/player.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ impl Player {
274274
}
275275

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

299299
pub async fn handle_play_packet(
300-
&self,
300+
self: &Arc<Self>,
301301
server: &Arc<Server>,
302302
packet: &mut RawPacket,
303303
) -> Result<(), Box<dyn PumpkinError>> {

0 commit comments

Comments
 (0)