Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Commands: Fix Bug, Refactor Arguments, Add /tp #227

Merged
merged 21 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions pumpkin/src/command/arg_entities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::sync::Arc;

use async_trait::async_trait;

use crate::command::dispatcher::InvalidTreeError;
use crate::command::tree::{ConsumedArgs, RawArgs};
use crate::command::CommandSender;
use crate::server::Server;

use super::arg_player::{parse_arg_players, PlayersArgumentConsumer};
use super::tree::ArgumentConsumer;

/// todo: implement (currently just calls [`super::arg_player::PlayerArgumentConsumer`])
///
/// For selecting zero, one or multiple entities, eg. using @s, a player name, @a or @e
pub(crate) struct EntitiesArgumentConsumer;

#[async_trait]
impl ArgumentConsumer for EntitiesArgumentConsumer {
async fn consume<'a>(
&self,
src: &CommandSender<'a>,
server: &Server,
args: &mut RawArgs<'a>,
) -> Result<String, Option<String>> {
PlayersArgumentConsumer.consume(src, server, args).await
}
}

/// todo: implement (currently just calls [`super::arg_player::PlayerArgumentConsumer`])
pub(crate) async fn parse_arg_entities<'a>(
src: &mut CommandSender<'a>,
server: &Server,
arg_name: &str,
consumed_args: &ConsumedArgs<'a>,
) -> Result<Vec<Arc<crate::entity::player::Player>>, InvalidTreeError> {
parse_arg_players(src, server, arg_name, consumed_args).await
}
109 changes: 109 additions & 0 deletions pumpkin/src/command/arg_entity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std::sync::Arc;

use async_trait::async_trait;

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

use super::tree::ArgumentConsumer;

/// todo: implement for entitites that aren't players
///
/// For selecting a single entity, eg. using @s, a player name or entity uuid.
///
/// Use [`super::arg_entities::EntitiesArgumentConsumer`] when there may be multiple targets.
pub(crate) struct EntityArgumentConsumer;

#[async_trait]
impl ArgumentConsumer for EntityArgumentConsumer {
async fn consume<'a>(
&self,
src: &CommandSender<'a>,
server: &Server,
args: &mut RawArgs<'a>,
) -> Result<String, Option<String>> {
let Some(s) = args.pop() else {
return Err(None);
};

match s {
// @s is always valid when sender is a player
"@s" => {
return match src {
CommandSender::Player(..) => Ok(s.into()),
_ => Err(Some("You are not a Player".into())),
}
}
// @n/@p are always valid when sender is a player
"@n" | "@p" => {
return match src {
CommandSender::Player(..) => Ok(s.into()),
// todo: implement for non-players: how should this behave when sender is console/rcon?
_ => Err(Some("You are not a Player".into())),
};
}
// @r is valid when there is at least one player
"@r" => {
if server.has_n_players(1).await {
Ok(s.into())
} else {
Err(None)
}
}
// @a/@e/@r are not valid because we're looking for a single entity
"@a" | "@e" => Err(None),
// player name is only valid if player is online
name => {
if server.get_player_by_name(name).await.is_some() {
Ok(s.into())
} else {
Err(Some(format!("Player not found: {name}")))
}
}
}
}
}

/// todo: implement for entitites that aren't players
pub(crate) async fn parse_arg_entity<'a>(
src: &mut CommandSender<'a>,
server: &Server,
arg_name: &str,
consumed_args: &ConsumedArgs<'a>,
) -> Result<Arc<crate::entity::player::Player>, InvalidTreeError> {
let s = consumed_args
.get(arg_name)
.ok_or(InvalidConsumptionError(None))?
.as_str();

match s {
"@s" => match src {
CommandSender::Player(p) => Ok(p.clone()),
_ => Err(InvalidConsumptionError(Some(s.into()))),
},
#[allow(clippy::match_same_arms)] // todo: implement for non-players and remove this line
"@n" | "@p" => match src {
CommandSender::Player(p) => Ok(p.clone()),
// todo: implement for non-players: how should this behave when sender is console/rcon?
_ => Err(InvalidConsumptionError(Some(s.into()))),
},
"@r" => {
if let Some(p) = server.get_random_player().await {
Ok(p.clone())
} else {
Err(InvalidConsumptionError(Some(s.into())))
}
}
"@a" | "@e" => Err(InvalidConsumptionError(Some(s.into()))),
name => {
if let Some(p) = server.get_player_by_name(name).await {
Ok(p)
} else {
Err(InvalidConsumptionError(Some(s.into())))
}
}
}
}
90 changes: 54 additions & 36 deletions pumpkin/src/command/arg_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,69 +10,87 @@ use crate::server::Server;

use super::tree::ArgumentConsumer;

/// TODO: Seperate base functionality of these two methods into single method

/// todo: implement (so far only own name + @s/@p is implemented)
pub(crate) struct PlayerArgumentConsumer {}
/// Select zero, one or multiple players
pub(crate) struct PlayersArgumentConsumer;

#[async_trait]
impl ArgumentConsumer for PlayerArgumentConsumer {
impl ArgumentConsumer for PlayersArgumentConsumer {
async fn consume<'a>(
&self,
src: &CommandSender<'a>,
server: &Server,
args: &mut RawArgs<'a>,
) -> Result<String, Option<String>> {
if let Some(arg) = args.pop() {
match arg {
"@s" => {
if src.is_player() {
return Ok(arg.into());
}
return Err(Some("You are not a Player".into()));
let Some(s) = args.pop() else {
return Err(None);
};

match s {
// @s is always valid when sender is a player
"@s" => {
return match src {
CommandSender::Player(..) => Ok(s.into()),
_ => 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).await.is_some() {
return Ok(name.into());
}
}
return Err(Some(format!("Player not found: {arg}")));
}
// @n/@p are always valid when sender is a player
"@n" | "@p" => {
return match src {
CommandSender::Player(..) => Ok(s.into()),
// todo: implement for non-players: how should this behave when sender is console/rcon?
_ => Err(Some("You are not a Player".into())),
};
}
// @a/@e/@r are always valid
"@a" | "@e" | "@r" => Ok(s.into()),
// player name is only valid if player is online
name => {
if server.get_player_by_name(name).await.is_some() {
Ok(s.into())
} else {
Err(Some(format!("Player not found: {name}")))
}
}
}
Err(None)
}
}

/// todo: implement (so far only own name + @s/@p is implemented)
pub async fn parse_arg_player<'a>(
pub async fn parse_arg_players<'a>(
src: &mut CommandSender<'a>,
server: &Server,
arg_name: &str,
consumed_args: &ConsumedArgs<'a>,
) -> Result<Arc<crate::entity::player::Player>, InvalidTreeError> {
) -> Result<Vec<Arc<crate::entity::player::Player>>, InvalidTreeError> {
let s = consumed_args
.get(arg_name)
.ok_or(InvalidConsumptionError(None))?
.as_str();

match s {
"@s" if src.is_player() => Ok(src.as_player().unwrap()),
"@p" => todo!(),
"@r" => todo!(), // todo: implement random player target selector
"@a" | "@e" => todo!(), // todo: implement all players target selector
"@s" => match src {
CommandSender::Player(p) => Ok(vec![p.clone()]),
_ => Err(InvalidConsumptionError(Some(s.into()))),
},
#[allow(clippy::match_same_arms)] // todo: implement for non-players and remove this line
"@n" | "@p" => match src {
CommandSender::Player(p) => Ok(vec![p.clone()]),
// todo: implement for non-players: how should this behave when sender is console/rcon?
_ => Err(InvalidConsumptionError(Some(s.into()))),
},
"@r" => {
if let Some(p) = server.get_random_player().await {
Ok(vec![p.clone()])
} else {
Ok(vec![])
}
}
"@a" | "@e" => Ok(server.get_all_players().await),
name => {
for world in &server.worlds {
if let Some(player) = world.get_player_by_name(name).await {
return Ok(player);
}
if let Some(p) = server.get_player_by_name(name).await {
Ok(vec![p])
} else {
Err(InvalidConsumptionError(Some(s.into())))
}
Err(InvalidConsumptionError(Some(s.into())))
}
}
}
45 changes: 0 additions & 45 deletions pumpkin/src/command/arg_position.rs

This file was deleted.

Loading