mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Commands: Fix Bug, Refactor Arguments, Add /tp (#227)
* fix command dispatch, add/refactor arguments & add tp command * implement teleport facing location/facing entity * implement teleport facing location/facing entity * implement arg_player/arg_entity and refactor commands accordingly * implement ~ coordinate notation * refactor arg_position_3d * major command refactor * add documentation * add default names to more argument types * add argument names to arg_bounded_nums * change command argument api --------- Co-authored-by: user622628252416 <nnew8715@gmail.com>
This commit is contained in:
@@ -1,78 +0,0 @@
|
||||
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: 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 {}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for PlayerArgumentConsumer {
|
||||
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()));
|
||||
}
|
||||
"@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}")));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// todo: implement (so far only own name + @s/@p is implemented)
|
||||
pub async fn parse_arg_player<'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" 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
|
||||
name => {
|
||||
for world in &server.worlds {
|
||||
if let Some(player) = world.get_player_by_name(name).await {
|
||||
return Ok(player);
|
||||
}
|
||||
}
|
||||
Err(InvalidConsumptionError(Some(s.into())))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
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::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 PositionArgumentConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for PositionArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_src: &CommandSender<'a>,
|
||||
_server: &Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Result<String, Option<String>> {
|
||||
let Some(arg) = args.pop() else {
|
||||
return Err(None);
|
||||
};
|
||||
|
||||
// TODO implement ~ ^ notations
|
||||
let value = arg.parse::<f64>().map_err(|err| Some(err.to_string()))?;
|
||||
Ok(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_arg_position(
|
||||
arg_name: &str,
|
||||
consumed_args: &ConsumedArgs<'_>,
|
||||
) -> Result<f64, InvalidTreeError> {
|
||||
let s = consumed_args
|
||||
.get(arg_name)
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?;
|
||||
|
||||
let value = s
|
||||
.parse::<f64>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
Ok(value)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::server::Server;
|
||||
|
||||
use super::{
|
||||
tree::{ArgumentConsumer, RawArgs},
|
||||
CommandSender,
|
||||
};
|
||||
|
||||
/// Should never be a permanent solution
|
||||
pub(crate) struct SimpleArgConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for SimpleArgConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_sender: &CommandSender<'a>,
|
||||
_server: &Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Result<String, Option<String>> {
|
||||
args.pop().ok_or(None).map(ToString::to_string)
|
||||
}
|
||||
}
|
||||
157
pumpkin/src/command/args/arg_bounded_num.rs
Normal file
157
pumpkin/src/command/args/arg_bounded_num.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use core::f64;
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::command::CommandSender;
|
||||
use crate::server::Server;
|
||||
|
||||
use super::super::args::ArgumentConsumer;
|
||||
use super::{Arg, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
/// Consumes a single generic num, but only if it's in bounds.
|
||||
pub(crate) struct BoundedNumArgumentConsumer<T: ArgNum> {
|
||||
min_inclusive: Option<T>,
|
||||
max_inclusive: Option<T>,
|
||||
name: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: ArgNum> ArgumentConsumer for BoundedNumArgumentConsumer<T> {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_src: &CommandSender<'a>,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let x = args.pop()?.parse::<T>().ok()?;
|
||||
|
||||
if let Some(max) = self.max_inclusive {
|
||||
if x > max {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(min) = self.min_inclusive {
|
||||
if x < min {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(x.to_arg())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ArgNum> FindArg<'a> for BoundedNumArgumentConsumer<T> {
|
||||
type Data = T;
|
||||
|
||||
fn find_arg(args: &super::ConsumedArgs, name: &str) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(arg) => match T::from_arg(arg) {
|
||||
Some(x) => Ok(x),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
},
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArgNum> BoundedNumArgumentConsumer<T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
min_inclusive: None,
|
||||
max_inclusive: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn min(mut self, min_inclusive: T) -> Self {
|
||||
self.min_inclusive = Some(min_inclusive);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) const fn max(mut self, max_inclusive: T) -> Self {
|
||||
self.max_inclusive = Some(max_inclusive);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) const fn name(mut self, name: &'static str) -> Self {
|
||||
self.name = Some(name);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ArgNum: PartialOrd + Copy + Send + Sync + FromStr {
|
||||
fn to_arg<'a>(self) -> Arg<'a>;
|
||||
fn from_arg(arg: &Arg<'_>) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl ArgNum for f64 {
|
||||
fn to_arg<'a>(self) -> Arg<'a> {
|
||||
Arg::F64(self)
|
||||
}
|
||||
|
||||
fn from_arg(arg: &Arg<'_>) -> Option<Self> {
|
||||
match arg {
|
||||
Arg::F64(x) => Some(*x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArgNum for f32 {
|
||||
fn to_arg<'a>(self) -> Arg<'a> {
|
||||
Arg::F32(self)
|
||||
}
|
||||
|
||||
fn from_arg(arg: &Arg<'_>) -> Option<Self> {
|
||||
match arg {
|
||||
Arg::F32(x) => Some(*x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArgNum for i32 {
|
||||
fn to_arg<'a>(self) -> Arg<'a> {
|
||||
Arg::I32(self)
|
||||
}
|
||||
|
||||
fn from_arg(arg: &Arg<'_>) -> Option<Self> {
|
||||
match arg {
|
||||
Arg::I32(x) => Some(*x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArgNum for u32 {
|
||||
fn to_arg<'a>(self) -> Arg<'a> {
|
||||
Arg::U32(self)
|
||||
}
|
||||
|
||||
fn from_arg(arg: &Arg<'_>) -> Option<Self> {
|
||||
match arg {
|
||||
Arg::U32(x) => Some(*x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArgNum> DefaultNameArgConsumer for BoundedNumArgumentConsumer<T> {
|
||||
fn default_name(&self) -> &'static str {
|
||||
// setting a single default name for all BoundedNumArgumentConsumer variants is probably a bad idea since it would lead to confusion
|
||||
self.name.expect("Only use *_default variants of methods with a BoundedNumArgumentConsumer that has a name.")
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
self
|
||||
}
|
||||
}
|
||||
58
pumpkin/src/command/args/arg_command.rs
Normal file
58
pumpkin/src/command/args/arg_command.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
dispatcher::InvalidTreeError,
|
||||
tree::{CommandTree, RawArgs},
|
||||
CommandSender,
|
||||
},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
use super::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
pub(crate) struct CommandTreeArgumentConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for CommandTreeArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_sender: &CommandSender<'a>,
|
||||
server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
|
||||
let dispatcher = &server.command_dispatcher;
|
||||
return match dispatcher.get_tree(s) {
|
||||
Ok(tree) => Some(Arg::CommandTree(tree)),
|
||||
Err(_) => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for CommandTreeArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"cmd"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&CommandTreeArgumentConsumer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for CommandTreeArgumentConsumer {
|
||||
type Data = &'a CommandTree<'a>;
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::CommandTree(tree)) => Ok(tree),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
59
pumpkin/src/command/args/arg_entities.rs
Normal file
59
pumpkin/src/command/args/arg_entities.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::command::CommandSender;
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
|
||||
use super::super::args::ArgumentConsumer;
|
||||
use super::arg_player::PlayersArgumentConsumer;
|
||||
use super::{Arg, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
/// 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: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
match PlayersArgumentConsumer.consume(src, server, args).await {
|
||||
Some(Arg::Players(p)) => Some(Arg::Entities(p)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for EntitiesArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"targets"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&EntitiesArgumentConsumer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for EntitiesArgumentConsumer {
|
||||
type Data = &'a [Arc<Player>];
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::Entities(data)) => Ok(data),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
80
pumpkin/src/command/args/arg_entity.rs
Normal file
80
pumpkin/src/command/args/arg_entity.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::command::CommandSender;
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
|
||||
use super::super::args::ArgumentConsumer;
|
||||
use super::{Arg, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
/// 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: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
|
||||
let entity = match s {
|
||||
// @s is always valid when sender is a player
|
||||
"@s" => match src {
|
||||
CommandSender::Player(p) => Some(p.clone()),
|
||||
_ => None,
|
||||
},
|
||||
// @n/@p are always valid when sender is a player
|
||||
#[allow(clippy::match_same_arms)] // todo: implement for non-players
|
||||
"@n" | "@p" => match src {
|
||||
CommandSender::Player(p) => Some(p.clone()),
|
||||
// todo: implement for non-players: how should this behave when sender is console/rcon?
|
||||
_ => None,
|
||||
},
|
||||
// @r is valid when there is at least one player
|
||||
"@r" => server.get_random_player().await,
|
||||
// @a/@e/@r are not valid because we're looking for a single entity
|
||||
"@a" | "@e" => None,
|
||||
// player name is only valid if player is online
|
||||
name => server.get_player_by_name(name).await,
|
||||
};
|
||||
|
||||
entity.map(Arg::Entity)
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for EntityArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"target"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&EntityArgumentConsumer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for EntityArgumentConsumer {
|
||||
type Data = Arc<Player>;
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::Entity(data)) => Ok(data.clone()),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
64
pumpkin/src/command/args/arg_gamemode.rs
Normal file
64
pumpkin/src/command/args/arg_gamemode.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_core::GameMode;
|
||||
|
||||
use crate::{
|
||||
command::{dispatcher::InvalidTreeError, tree::RawArgs, CommandSender},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
use super::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
pub(crate) struct GamemodeArgumentConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for GamemodeArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_sender: &CommandSender<'a>,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
|
||||
if let Ok(id) = s.parse::<u8>() {
|
||||
match GameMode::from_u8(id) {
|
||||
None | Some(GameMode::Undefined) => {}
|
||||
Some(gamemode) => return Some(Arg::GameMode(gamemode)),
|
||||
};
|
||||
};
|
||||
|
||||
match GameMode::from_str(s) {
|
||||
Err(_) | Ok(GameMode::Undefined) => None,
|
||||
Ok(gamemode) => Some(Arg::GameMode(gamemode)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for GamemodeArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"gamemode"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&GamemodeArgumentConsumer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for GamemodeArgumentConsumer {
|
||||
type Data = GameMode;
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::GameMode(data)) => Ok(*data),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
59
pumpkin/src/command/args/arg_message.rs
Normal file
59
pumpkin/src/command/args/arg_message.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{command::dispatcher::InvalidTreeError, server::Server};
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
args::{ArgumentConsumer, RawArgs},
|
||||
CommandSender,
|
||||
},
|
||||
Arg, DefaultNameArgConsumer, FindArg,
|
||||
};
|
||||
|
||||
/// Consumes all remaining words/args. Does not consume if there is no word.
|
||||
pub(crate) struct MsgArgConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for MsgArgConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_sender: &CommandSender<'a>,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let mut msg = args.pop()?.to_string();
|
||||
|
||||
while let Some(word) = args.pop() {
|
||||
msg.push(' ');
|
||||
msg.push_str(word);
|
||||
}
|
||||
|
||||
Some(Arg::Msg(msg))
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for MsgArgConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"msg"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&MsgArgConsumer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for MsgArgConsumer {
|
||||
type Data = &'a str;
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::Msg(data)) => Ok(data),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
76
pumpkin/src/command/args/arg_player.rs
Normal file
76
pumpkin/src/command/args/arg_player.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::command::CommandSender;
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
|
||||
use super::super::args::ArgumentConsumer;
|
||||
use super::{Arg, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
/// Select zero, one or multiple players
|
||||
pub(crate) struct PlayersArgumentConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for PlayersArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
src: &CommandSender<'a>,
|
||||
server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let players = match args.pop()? {
|
||||
"@s" => match src {
|
||||
CommandSender::Player(p) => Some(vec![p.clone()]),
|
||||
_ => None,
|
||||
},
|
||||
#[allow(clippy::match_same_arms)]
|
||||
// todo: implement for non-players and remove this line
|
||||
"@n" | "@p" => match src {
|
||||
CommandSender::Player(p) => Some(vec![p.clone()]),
|
||||
// todo: implement for non-players: how should this behave when sender is console/rcon?
|
||||
_ => None,
|
||||
},
|
||||
"@r" => {
|
||||
if let Some(p) = server.get_random_player().await {
|
||||
Some(vec![p.clone()])
|
||||
} else {
|
||||
Some(vec![])
|
||||
}
|
||||
}
|
||||
"@a" | "@e" => Some(server.get_all_players().await),
|
||||
name => server.get_player_by_name(name).await.map(|p| vec![p]),
|
||||
};
|
||||
|
||||
players.map(Arg::Players)
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for PlayersArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"player"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&PlayersArgumentConsumer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for PlayersArgumentConsumer {
|
||||
type Data = &'a [Arc<Player>];
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::Players(data)) => Ok(data),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
56
pumpkin/src/command/args/arg_position_2d.rs
Normal file
56
pumpkin/src/command/args/arg_position_2d.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::command::CommandSender;
|
||||
use crate::server::Server;
|
||||
|
||||
use super::super::args::ArgumentConsumer;
|
||||
use super::{Arg, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
/// x and z coordinates only
|
||||
///
|
||||
/// todo: implememnt ~ ^ notations
|
||||
pub(crate) struct Position2DArgumentConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for Position2DArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_src: &CommandSender<'a>,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let x = args.pop()?.parse::<f64>().ok()?;
|
||||
let z = args.pop()?.parse::<f64>().ok()?;
|
||||
|
||||
Some(Arg::Pos2D(Vector2::new(x, z)))
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for Position2DArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"pos2d"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&Position2DArgumentConsumer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for Position2DArgumentConsumer {
|
||||
type Data = Vector2<f64>;
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::Pos2D(data)) => Ok(*data),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
104
pumpkin/src/command/args/arg_position_3d.rs
Normal file
104
pumpkin/src/command/args/arg_position_3d.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use std::num::ParseFloatError;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_core::math::vector3::Vector3;
|
||||
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::command::CommandSender;
|
||||
use crate::server::Server;
|
||||
|
||||
use super::super::args::ArgumentConsumer;
|
||||
use super::{Arg, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
/// x, y and z coordinates
|
||||
pub(crate) struct Position3DArgumentConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for Position3DArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
src: &CommandSender<'a>,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let pos = Position3D::try_new(args.pop(), args.pop(), args.pop())?;
|
||||
|
||||
let vec3 = pos.try_get_values(src.position())?;
|
||||
|
||||
Some(Arg::Pos3D(vec3))
|
||||
}
|
||||
}
|
||||
|
||||
struct Position3D(Coordinate, Coordinate, Coordinate);
|
||||
|
||||
impl Position3D {
|
||||
fn try_new(x: Option<&str>, y: Option<&str>, z: Option<&str>) -> Option<Self> {
|
||||
Some(Self(
|
||||
x?.try_into().ok()?,
|
||||
y?.try_into().ok()?,
|
||||
z?.try_into().ok()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn try_get_values(self, origin: Option<Vector3<f64>>) -> Option<Vector3<f64>> {
|
||||
Some(Vector3::new(
|
||||
self.0.value(origin.map(|o| o.x))?,
|
||||
self.1.value(origin.map(|o| o.y))?,
|
||||
self.2.value(origin.map(|o| o.z))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for Position3DArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"pos"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&Position3DArgumentConsumer
|
||||
}
|
||||
}
|
||||
|
||||
enum Coordinate {
|
||||
Absolute(f64),
|
||||
Relative(f64),
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Coordinate {
|
||||
type Error = ParseFloatError;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
if let Some(s) = s.strip_prefix('~') {
|
||||
let offset = if s.is_empty() { 0.0 } else { s.parse()? };
|
||||
Ok(Self::Relative(offset))
|
||||
} else {
|
||||
Ok(Self::Absolute(s.parse()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Coordinate {
|
||||
fn value(self, origin: Option<f64>) -> Option<f64> {
|
||||
match self {
|
||||
Self::Absolute(v) => Some(v),
|
||||
Self::Relative(offset) => Some(origin? + offset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for Position3DArgumentConsumer {
|
||||
type Data = Vector3<f64>;
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::Pos3D(data)) => Ok(*data),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
62
pumpkin/src/command/args/arg_rotation.rs
Normal file
62
pumpkin/src/command/args/arg_rotation.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::command::CommandSender;
|
||||
use crate::server::Server;
|
||||
|
||||
use super::super::args::ArgumentConsumer;
|
||||
use super::{Arg, DefaultNameArgConsumer, FindArg};
|
||||
|
||||
/// yaw and pitch
|
||||
pub(crate) struct RotationArgumentConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for RotationArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_src: &CommandSender<'a>,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let mut yaw = args.pop()?.parse::<f32>().ok()?;
|
||||
let mut pitch = args.pop()?.parse::<f32>().ok()?;
|
||||
|
||||
yaw %= 360.0;
|
||||
if yaw >= 180.0 {
|
||||
yaw -= 360.0;
|
||||
};
|
||||
pitch %= 360.0;
|
||||
if pitch >= 180.0 {
|
||||
pitch -= 360.0;
|
||||
};
|
||||
|
||||
Some(Arg::Rotation(yaw, pitch))
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for RotationArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"rotation"
|
||||
}
|
||||
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
|
||||
&RotationArgumentConsumer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for RotationArgumentConsumer {
|
||||
type Data = (f32, f32);
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::Rotation(yaw, pitch)) => Ok((*yaw, *pitch)),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
43
pumpkin/src/command/args/arg_simple.rs
Normal file
43
pumpkin/src/command/args/arg_simple.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{command::dispatcher::InvalidTreeError, server::Server};
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
args::{ArgumentConsumer, RawArgs},
|
||||
CommandSender,
|
||||
},
|
||||
Arg, FindArg,
|
||||
};
|
||||
|
||||
/// Should never be a permanent solution
|
||||
#[allow(unused)]
|
||||
pub(crate) struct SimpleArgConsumer;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for SimpleArgConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_sender: &CommandSender<'a>,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
Some(Arg::Simple(args.pop()?.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindArg<'a> for SimpleArgConsumer {
|
||||
type Data = &'a str;
|
||||
|
||||
fn find_arg(
|
||||
args: &'a super::ConsumedArgs,
|
||||
name: &'a str,
|
||||
) -> Result<Self::Data, InvalidTreeError> {
|
||||
match args.get(name) {
|
||||
Some(Arg::Simple(data)) => Ok(data),
|
||||
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
|
||||
name.to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
95
pumpkin/src/command/args/mod.rs
Normal file
95
pumpkin/src/command/args/mod.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use std::{collections::HashMap, hash::Hash, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_core::{
|
||||
math::{vector2::Vector2, vector3::Vector3},
|
||||
GameMode,
|
||||
};
|
||||
|
||||
use crate::{entity::player::Player, server::Server};
|
||||
|
||||
use super::{
|
||||
dispatcher::InvalidTreeError,
|
||||
tree::{CommandTree, RawArgs},
|
||||
CommandSender,
|
||||
};
|
||||
|
||||
pub(crate) mod arg_bounded_num;
|
||||
pub(crate) mod arg_command;
|
||||
pub(crate) mod arg_entities;
|
||||
pub(crate) mod arg_entity;
|
||||
pub(crate) mod arg_gamemode;
|
||||
pub(crate) mod arg_message;
|
||||
pub(crate) mod arg_player;
|
||||
pub(crate) mod arg_position_2d;
|
||||
pub(crate) mod arg_position_3d;
|
||||
pub(crate) mod arg_rotation;
|
||||
pub(crate) mod arg_simple;
|
||||
|
||||
/// see [`crate::commands::tree_builder::argument`]
|
||||
/// Provide value or an Optional error message, If no Error message provided the default will be used
|
||||
#[async_trait]
|
||||
pub(crate) trait ArgumentConsumer: Sync {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
sender: &CommandSender<'a>,
|
||||
server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>>;
|
||||
}
|
||||
|
||||
pub(crate) trait DefaultNameArgConsumer: ArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str;
|
||||
|
||||
/// needed because trait upcasting is not stable
|
||||
fn get_argument_consumer(&self) -> &dyn ArgumentConsumer;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum Arg<'a> {
|
||||
Entities(Vec<Arc<Player>>),
|
||||
Entity(Arc<Player>),
|
||||
Players(Vec<Arc<Player>>),
|
||||
Pos3D(Vector3<f64>),
|
||||
Pos2D(Vector2<f64>),
|
||||
Rotation(f32, f32),
|
||||
GameMode(GameMode),
|
||||
CommandTree(&'a CommandTree<'a>),
|
||||
Msg(String),
|
||||
F64(f64),
|
||||
F32(f32),
|
||||
I32(i32),
|
||||
#[allow(unused)]
|
||||
U32(u32),
|
||||
#[allow(unused)]
|
||||
Simple(String),
|
||||
}
|
||||
|
||||
/// see [`crate::commands::tree_builder::argument`] and [`CommandTree::execute`]/[`crate::commands::tree_builder::NonLeafNodeBuilder::execute`]
|
||||
pub(crate) type ConsumedArgs<'a> = HashMap<&'a str, Arg<'a>>;
|
||||
|
||||
pub(crate) trait GetCloned<K, V: Clone> {
|
||||
fn get_cloned(&self, key: &K) -> Option<V>;
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash, V: Clone> GetCloned<K, V> for HashMap<K, V> {
|
||||
fn get_cloned(&self, key: &K) -> Option<V> {
|
||||
self.get(key).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait FindArg<'a> {
|
||||
type Data;
|
||||
|
||||
fn find_arg(args: &'a ConsumedArgs, name: &'a str) -> Result<Self::Data, InvalidTreeError>;
|
||||
}
|
||||
|
||||
pub(crate) trait FindArgDefaultName<'a, T> {
|
||||
fn find_arg_default_name(&self, args: &'a ConsumedArgs) -> Result<T, InvalidTreeError>;
|
||||
}
|
||||
|
||||
impl<'a, T, C: FindArg<'a, Data = T> + DefaultNameArgConsumer> FindArgDefaultName<'a, T> for C {
|
||||
fn find_arg_default_name(&self, args: &'a ConsumedArgs) -> Result<T, InvalidTreeError> {
|
||||
C::find_arg(args, self.default_name())
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
||||
use pumpkin_inventory::OpenContainer;
|
||||
|
||||
use crate::command::{
|
||||
tree::CommandTree, tree::ConsumedArgs, CommandExecutor, CommandSender, InvalidTreeError,
|
||||
args::ConsumedArgs, tree::CommandTree, CommandExecutor, CommandSender, InvalidTreeError,
|
||||
};
|
||||
|
||||
const NAMES: [&str; 2] = ["echest", "enderchest"];
|
||||
@@ -10,7 +10,7 @@ const NAMES: [&str; 2] = ["echest", "enderchest"];
|
||||
const DESCRIPTION: &str =
|
||||
"Show your personal enderchest (this command is used for testing container behaviour)";
|
||||
|
||||
struct EchestExecutor {}
|
||||
struct EchestExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for EchestExecutor {
|
||||
@@ -42,5 +42,5 @@ impl CommandExecutor for EchestExecutor {
|
||||
}
|
||||
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION).execute(&EchestExecutor {})
|
||||
CommandTree::new(NAMES, DESCRIPTION).execute(&EchestExecutor)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_core::GameMode;
|
||||
|
||||
use crate::command::args::arg_gamemode::GamemodeArgumentConsumer;
|
||||
use crate::command::args::GetCloned;
|
||||
|
||||
use crate::TextComponent;
|
||||
|
||||
use crate::command::arg_player::{parse_arg_player, PlayerArgumentConsumer};
|
||||
use crate::command::args::arg_player::PlayersArgumentConsumer;
|
||||
|
||||
use crate::command::args::{Arg, ConsumedArgs};
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::dispatcher::InvalidTreeError::{
|
||||
InvalidConsumptionError, InvalidRequirementError,
|
||||
};
|
||||
use crate::command::tree::{ArgumentConsumer, CommandTree, ConsumedArgs, RawArgs};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree_builder::{argument, require};
|
||||
use crate::command::CommandSender::Player;
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
@@ -25,54 +25,7 @@ const DESCRIPTION: &str = "Change a player's gamemode.";
|
||||
const ARG_GAMEMODE: &str = "gamemode";
|
||||
const ARG_TARGET: &str = "target";
|
||||
|
||||
struct GamemodeArgumentConsumer {}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for GamemodeArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_sender: &CommandSender<'a>,
|
||||
_server: &Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> 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(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> {
|
||||
let s = consumed_args
|
||||
.get(ARG_GAMEMODE)
|
||||
.ok_or(InvalidConsumptionError(None))?;
|
||||
|
||||
if let Ok(id) = s.parse::<u8>() {
|
||||
match GameMode::from_u8(id) {
|
||||
None | Some(GameMode::Undefined) => {}
|
||||
Some(gamemode) => return Ok(gamemode),
|
||||
};
|
||||
};
|
||||
|
||||
match GameMode::from_str(s) {
|
||||
Err(_) | Ok(GameMode::Undefined) => Err(InvalidConsumptionError(Some(s.into()))),
|
||||
Ok(gamemode) => Ok(gamemode),
|
||||
}
|
||||
}
|
||||
|
||||
struct GamemodeTargetSelf {}
|
||||
struct GamemodeTargetSelf;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for GamemodeTargetSelf {
|
||||
@@ -82,7 +35,9 @@ impl CommandExecutor for GamemodeTargetSelf {
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let gamemode = parse_arg_gamemode(args)?;
|
||||
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
|
||||
return Err(InvalidConsumptionError(Some(ARG_GAMEMODE.into())));
|
||||
};
|
||||
|
||||
if let Player(target) = sender {
|
||||
if target.gamemode.load() == gamemode {
|
||||
@@ -106,34 +61,46 @@ impl CommandExecutor for GamemodeTargetSelf {
|
||||
}
|
||||
}
|
||||
|
||||
struct GamemodeTargetPlayer {}
|
||||
struct GamemodeTargetPlayer;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for GamemodeTargetPlayer {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
server: &Server,
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let gamemode = parse_arg_gamemode(args)?;
|
||||
let target = parse_arg_player(sender, server, ARG_TARGET, args).await?;
|
||||
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
|
||||
return Err(InvalidConsumptionError(Some(ARG_GAMEMODE.into())));
|
||||
};
|
||||
let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else {
|
||||
return Err(InvalidConsumptionError(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
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;
|
||||
let target_count = targets.len();
|
||||
|
||||
for target in targets {
|
||||
if target.gamemode.load() == gamemode {
|
||||
if target_count == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::text(&format!(
|
||||
"{} is already in {:?} gamemode",
|
||||
target.gameprofile.name, gamemode
|
||||
)))
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
target.set_gamemode(gamemode).await;
|
||||
if target_count == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::text(&format!(
|
||||
"{}'s Game mode was set to {:?}",
|
||||
target.gameprofile.name, gamemode
|
||||
)))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -144,11 +111,10 @@ impl CommandExecutor for GamemodeTargetPlayer {
|
||||
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, &GamemodeArgumentConsumer {})
|
||||
.with_child(require(&|sender| sender.is_player()).execute(&GamemodeTargetSelf {}))
|
||||
argument(ARG_GAMEMODE, &GamemodeArgumentConsumer)
|
||||
.with_child(require(&|sender| sender.is_player()).execute(&GamemodeTargetSelf))
|
||||
.with_child(
|
||||
argument(ARG_TARGET, &PlayerArgumentConsumer {})
|
||||
.execute(&GamemodeTargetPlayer {}),
|
||||
argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&GamemodeTargetPlayer),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
|
||||
use crate::command::args::arg_command::CommandTreeArgumentConsumer;
|
||||
use crate::command::args::{Arg, ConsumedArgs};
|
||||
use crate::command::dispatcher::InvalidTreeError;
|
||||
use crate::command::dispatcher::InvalidTreeError::InvalidConsumptionError;
|
||||
use crate::command::dispatcher::{CommandDispatcher, InvalidTreeError};
|
||||
use crate::command::tree::{ArgumentConsumer, Command, CommandTree, ConsumedArgs, RawArgs};
|
||||
use crate::command::tree::{Command, CommandTree};
|
||||
use crate::command::tree_builder::argument;
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use crate::server::Server;
|
||||
@@ -14,48 +16,19 @@ const DESCRIPTION: &str = "Print a help message.";
|
||||
|
||||
const ARG_COMMAND: &str = "command";
|
||||
|
||||
struct CommandArgumentConsumer {}
|
||||
struct CommandHelpExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for CommandArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
_sender: &CommandSender<'a>,
|
||||
_server: &Server,
|
||||
_args: &mut RawArgs<'a>,
|
||||
) -> Result<String, Option<String>> {
|
||||
//let s = args.pop()?;
|
||||
|
||||
// dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into())
|
||||
// TODO: Implement this
|
||||
Err(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_arg_command<'a>(
|
||||
consumed_args: &'a ConsumedArgs,
|
||||
dispatcher: &'a CommandDispatcher,
|
||||
) -> Result<&'a CommandTree<'a>, InvalidTreeError> {
|
||||
let command_name = consumed_args
|
||||
.get(ARG_COMMAND)
|
||||
.ok_or(InvalidConsumptionError(None))?;
|
||||
|
||||
dispatcher
|
||||
.get_tree(command_name)
|
||||
.map_err(|_| InvalidConsumptionError(Some(command_name.into())))
|
||||
}
|
||||
|
||||
struct BaseHelpExecutor {}
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for BaseHelpExecutor {
|
||||
impl CommandExecutor for CommandHelpExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
server: &Server,
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let tree = parse_arg_command(args, &server.command_dispatcher)?;
|
||||
let Some(Arg::CommandTree(tree)) = args.get(&ARG_COMMAND) else {
|
||||
return Err(InvalidConsumptionError(Some(ARG_COMMAND.into())));
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::text(&format!(
|
||||
@@ -70,10 +43,10 @@ impl CommandExecutor for BaseHelpExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
struct CommandHelpExecutor {}
|
||||
struct BaseHelpExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for CommandHelpExecutor {
|
||||
impl CommandExecutor for BaseHelpExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
@@ -105,7 +78,7 @@ impl CommandExecutor for CommandHelpExecutor {
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION)
|
||||
.with_child(
|
||||
argument(ARG_COMMAND, &CommandArgumentConsumer {}).execute(&BaseHelpExecutor {}),
|
||||
argument(ARG_COMMAND, &CommandTreeArgumentConsumer).execute(&CommandHelpExecutor),
|
||||
)
|
||||
.execute(&CommandHelpExecutor {})
|
||||
.execute(&BaseHelpExecutor)
|
||||
}
|
||||
|
||||
@@ -2,37 +2,48 @@ use async_trait::async_trait;
|
||||
use pumpkin_core::text::color::NamedColor;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
|
||||
use crate::command::arg_player::{parse_arg_player, PlayerArgumentConsumer};
|
||||
use crate::command::args::arg_player::PlayersArgumentConsumer;
|
||||
use crate::command::args::{Arg, ConsumedArgs};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree_builder::argument;
|
||||
use crate::command::InvalidTreeError;
|
||||
use crate::command::{tree::ConsumedArgs, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use InvalidTreeError::InvalidConsumptionError;
|
||||
|
||||
const NAMES: [&str; 1] = ["kick"];
|
||||
const DESCRIPTION: &str = "Kicks the target player from the server.";
|
||||
|
||||
const ARG_TARGET: &str = "target";
|
||||
|
||||
struct KickExecutor {}
|
||||
struct KickExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for KickExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
server: &crate::server::Server,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let target = parse_arg_player(sender, server, ARG_TARGET, args).await?;
|
||||
target
|
||||
.kick(TextComponent::text("Kicked by an operator"))
|
||||
.await;
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumptionError(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Player has been kicked.").color_named(NamedColor::Blue),
|
||||
)
|
||||
.await;
|
||||
let target_count = targets.len();
|
||||
|
||||
for target in targets {
|
||||
target
|
||||
.kick(TextComponent::text("Kicked by an operator"))
|
||||
.await;
|
||||
}
|
||||
|
||||
let msg = if target_count == 1 {
|
||||
TextComponent::text("Player has been kicked.")
|
||||
} else {
|
||||
TextComponent::text_string(format!("{target_count} players have been kicked."))
|
||||
};
|
||||
|
||||
sender.send_message(msg.color_named(NamedColor::Blue)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -40,5 +51,5 @@ impl CommandExecutor for KickExecutor {
|
||||
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION)
|
||||
.with_child(argument(ARG_TARGET, &PlayerArgumentConsumer {}).execute(&KickExecutor {}))
|
||||
.with_child(argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&KickExecutor))
|
||||
}
|
||||
|
||||
@@ -2,41 +2,73 @@ use async_trait::async_trait;
|
||||
use pumpkin_core::text::color::NamedColor;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
|
||||
use crate::command::arg_player::{parse_arg_player, PlayerArgumentConsumer};
|
||||
use crate::command::args::arg_entities::EntitiesArgumentConsumer;
|
||||
use crate::command::args::{Arg, ConsumedArgs};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree_builder::argument;
|
||||
use crate::command::{tree::ConsumedArgs, CommandExecutor, CommandSender, InvalidTreeError};
|
||||
use crate::command::tree_builder::{argument, require};
|
||||
use crate::command::{CommandExecutor, CommandSender, InvalidTreeError};
|
||||
use InvalidTreeError::InvalidConsumptionError;
|
||||
|
||||
const NAMES: [&str; 1] = ["kill"];
|
||||
const DESCRIPTION: &str = "Kills a target player.";
|
||||
const DESCRIPTION: &str = "Kills all target entities.";
|
||||
|
||||
const ARG_TARGET: &str = "target";
|
||||
|
||||
struct KillExecutor {}
|
||||
struct KillExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for KillExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
server: &crate::server::Server,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
// TODO parse entities not only players
|
||||
let target = parse_arg_player(sender, server, ARG_TARGET, args).await?;
|
||||
target.living_entity.kill().await;
|
||||
let Some(Arg::Entities(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumptionError(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Player has been killed.").color_named(NamedColor::Blue),
|
||||
)
|
||||
.await;
|
||||
let target_count = targets.len();
|
||||
|
||||
for target in targets {
|
||||
target.living_entity.kill().await;
|
||||
}
|
||||
|
||||
let msg = if target_count == 1 {
|
||||
TextComponent::text("Enitity has been killed.")
|
||||
} else {
|
||||
TextComponent::text_string(format!("{target_count} entities have been killed."))
|
||||
};
|
||||
|
||||
sender.send_message(msg.color_named(NamedColor::Blue)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct KillSelfExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for KillSelfExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let target = sender
|
||||
.as_player()
|
||||
.ok_or(InvalidTreeError::InvalidRequirementError)?;
|
||||
|
||||
target.living_entity.kill().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION)
|
||||
.with_child(argument(ARG_TARGET, &PlayerArgumentConsumer {}).execute(&KillExecutor {}))
|
||||
.with_child(argument(ARG_TARGET, &EntitiesArgumentConsumer).execute(&KillExecutor))
|
||||
.with_child(require(&|sender| sender.is_player()).execute(&KillSelfExecutor))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use pumpkin_protocol::CURRENT_MC_PROTOCOL;
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
tree::CommandTree, tree::ConsumedArgs, CommandExecutor, CommandSender, InvalidTreeError,
|
||||
args::ConsumedArgs, tree::CommandTree, CommandExecutor, CommandSender, InvalidTreeError,
|
||||
},
|
||||
server::CURRENT_MC_VERSION,
|
||||
};
|
||||
@@ -13,7 +13,7 @@ const NAMES: [&str; 1] = ["pumpkin"];
|
||||
|
||||
const DESCRIPTION: &str = "Display information about Pumpkin.";
|
||||
|
||||
struct PumpkinExecutor {}
|
||||
struct PumpkinExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for PumpkinExecutor {
|
||||
@@ -35,5 +35,5 @@ impl CommandExecutor for PumpkinExecutor {
|
||||
}
|
||||
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION).execute(&PumpkinExecutor {})
|
||||
CommandTree::new(NAMES, DESCRIPTION).execute(&PumpkinExecutor)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_protocol::client::play::CSystemChatMessage;
|
||||
|
||||
use crate::command::{
|
||||
arg_simple::SimpleArgConsumer,
|
||||
tree::{CommandTree, ConsumedArgs},
|
||||
args::{arg_message::MsgArgConsumer, Arg, ConsumedArgs},
|
||||
tree::CommandTree,
|
||||
tree_builder::{argument, require},
|
||||
CommandExecutor, CommandSender, InvalidTreeError,
|
||||
};
|
||||
use InvalidTreeError::InvalidConsumptionError;
|
||||
|
||||
const NAMES: [&str; 1] = ["say"];
|
||||
|
||||
@@ -15,7 +16,7 @@ const DESCRIPTION: &str = "Broadcast a message to all Players.";
|
||||
|
||||
const ARG_MESSAGE: &str = "message";
|
||||
|
||||
struct SayExecutor {}
|
||||
struct SayExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for SayExecutor {
|
||||
@@ -31,9 +32,13 @@ impl CommandExecutor for SayExecutor {
|
||||
CommandSender::Player(player) => &player.gameprofile.name,
|
||||
};
|
||||
|
||||
let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else {
|
||||
return Err(InvalidConsumptionError(Some(ARG_MESSAGE.into())));
|
||||
};
|
||||
|
||||
server
|
||||
.broadcast_packet_all(&CSystemChatMessage::new(
|
||||
&TextComponent::text(&format!("[{}] {}", sender, args.get("message").unwrap())),
|
||||
&TextComponent::text(&format!("[{sender}] {msg}")),
|
||||
false,
|
||||
))
|
||||
.await;
|
||||
@@ -44,6 +49,6 @@ impl CommandExecutor for SayExecutor {
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION).with_child(
|
||||
require(&|sender| sender.permission_lvl() >= 2)
|
||||
.with_child(argument(ARG_MESSAGE, &SimpleArgConsumer {}).execute(&SayExecutor {})),
|
||||
.with_child(argument(ARG_MESSAGE, &MsgArgConsumer).execute(&SayExecutor)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,15 +2,16 @@ use async_trait::async_trait;
|
||||
use pumpkin_core::text::color::NamedColor;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
|
||||
use crate::command::args::ConsumedArgs;
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree_builder::require;
|
||||
use crate::command::{tree::ConsumedArgs, CommandExecutor, CommandSender, InvalidTreeError};
|
||||
use crate::command::{CommandExecutor, CommandSender, InvalidTreeError};
|
||||
|
||||
const NAMES: [&str; 1] = ["stop"];
|
||||
|
||||
const DESCRIPTION: &str = "Stop the server.";
|
||||
|
||||
struct StopExecutor {}
|
||||
struct StopExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for StopExecutor {
|
||||
@@ -31,5 +32,5 @@ impl CommandExecutor for StopExecutor {
|
||||
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION)
|
||||
.with_child(require(&|sender| sender.permission_lvl() >= 4).execute(&StopExecutor {}))
|
||||
.with_child(require(&|sender| sender.permission_lvl() >= 4).execute(&StopExecutor))
|
||||
}
|
||||
|
||||
277
pumpkin/src/command/commands/cmd_teleport.rs
Normal file
277
pumpkin/src/command/commands/cmd_teleport.rs
Normal file
@@ -0,0 +1,277 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_core::math::vector3::Vector3;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
|
||||
use crate::command::args::arg_entities::EntitiesArgumentConsumer;
|
||||
use crate::command::args::arg_entity::EntityArgumentConsumer;
|
||||
use crate::command::args::arg_position_3d::Position3DArgumentConsumer;
|
||||
use crate::command::args::arg_rotation::RotationArgumentConsumer;
|
||||
use crate::command::args::ConsumedArgs;
|
||||
use crate::command::args::FindArg;
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree_builder::{argument, literal, require};
|
||||
use crate::command::InvalidTreeError;
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
|
||||
const NAMES: [&str; 2] = ["teleport", "tp"];
|
||||
const DESCRIPTION: &str = "Teleports entities, including players."; // todo
|
||||
|
||||
/// position
|
||||
const ARG_LOCATION: &str = "location";
|
||||
|
||||
/// single entity
|
||||
const ARG_DESTINATION: &str = "destination";
|
||||
|
||||
/// multiple entities
|
||||
const ARG_TARGETS: &str = "targets";
|
||||
|
||||
/// rotation: yaw/pitch
|
||||
const ARG_ROTATION: &str = "rotation";
|
||||
|
||||
/// single entity
|
||||
const ARG_FACING_ENTITY: &str = "facingEntity";
|
||||
|
||||
/// position
|
||||
const ARG_FACING_LOCATION: &str = "facingLocation";
|
||||
|
||||
fn yaw_pitch_facing_position(
|
||||
looking_from: &Vector3<f64>,
|
||||
looking_towards: &Vector3<f64>,
|
||||
) -> (f32, f32) {
|
||||
let direction_vector = (looking_towards.sub(looking_from)).normalize();
|
||||
|
||||
let yaw_radians = -direction_vector.x.atan2(direction_vector.z);
|
||||
let pitch_radians = (-direction_vector.y).asin();
|
||||
|
||||
let yaw_degrees = yaw_radians * 180.0 / std::f64::consts::PI;
|
||||
let pitch_degrees = pitch_radians * 180.0 / std::f64::consts::PI;
|
||||
|
||||
(yaw_degrees as f32, pitch_degrees as f32)
|
||||
}
|
||||
|
||||
struct TpEntitiesToEntityExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TpEntitiesToEntityExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
|
||||
let pos = destination.living_entity.entity.pos.load();
|
||||
|
||||
for target in targets {
|
||||
let yaw = target.living_entity.entity.yaw.load();
|
||||
let pitch = target.living_entity.entity.pitch.load();
|
||||
target.teleport(pos, yaw, pitch).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct TpEntitiesToPosFacingPosExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TpEntitiesToPosFacingPosExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
|
||||
let facing_pos = Position3DArgumentConsumer::find_arg(args, ARG_FACING_LOCATION)?;
|
||||
let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_pos);
|
||||
|
||||
for target in targets {
|
||||
target.teleport(pos, yaw, pitch).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct TpEntitiesToPosFacingEntityExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TpEntitiesToPosFacingEntityExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
|
||||
let facing_entity = &EntityArgumentConsumer::find_arg(args, ARG_FACING_ENTITY)?
|
||||
.living_entity
|
||||
.entity;
|
||||
let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_entity.pos.load());
|
||||
|
||||
for target in targets {
|
||||
target.teleport(pos, yaw, pitch).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct TpEntitiesToPosWithRotationExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TpEntitiesToPosWithRotationExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
|
||||
let (yaw, pitch) = RotationArgumentConsumer::find_arg(args, ARG_ROTATION)?;
|
||||
|
||||
for target in targets {
|
||||
target.teleport(pos, yaw, pitch).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct TpEntitiesToPosExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TpEntitiesToPosExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
|
||||
for target in targets {
|
||||
let yaw = target.living_entity.entity.yaw.load();
|
||||
let pitch = target.living_entity.entity.pitch.load();
|
||||
target.teleport(pos, yaw, pitch).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct TpSelfToEntityExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TpSelfToEntityExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
|
||||
let pos = destination.living_entity.entity.pos.load();
|
||||
|
||||
match sender {
|
||||
CommandSender::Player(player) => {
|
||||
let yaw = player.living_entity.entity.yaw.load();
|
||||
let pitch = player.living_entity.entity.pitch.load();
|
||||
player.teleport(pos, yaw, pitch).await;
|
||||
}
|
||||
_ => {
|
||||
sender
|
||||
.send_message(TextComponent::text(
|
||||
"Only players may execute this command.",
|
||||
))
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct TpSelfToPosExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TpSelfToPosExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), InvalidTreeError> {
|
||||
match sender {
|
||||
CommandSender::Player(player) => {
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
let yaw = player.living_entity.entity.yaw.load();
|
||||
let pitch = player.living_entity.entity.pitch.load();
|
||||
player.teleport(pos, yaw, pitch).await;
|
||||
}
|
||||
_ => {
|
||||
sender
|
||||
.send_message(TextComponent::text(
|
||||
"Only players may execute this command.",
|
||||
))
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION).with_child(
|
||||
require(&|sender| sender.permission_lvl() >= 2)
|
||||
.with_child(
|
||||
argument(ARG_TARGETS, &EntitiesArgumentConsumer)
|
||||
.with_child(
|
||||
argument(ARG_LOCATION, &Position3DArgumentConsumer)
|
||||
.execute(&TpEntitiesToPosExecutor)
|
||||
.with_child(
|
||||
argument(ARG_ROTATION, &RotationArgumentConsumer)
|
||||
.execute(&TpEntitiesToPosWithRotationExecutor),
|
||||
)
|
||||
.with_child(
|
||||
literal("facing")
|
||||
.with_child(
|
||||
literal("entity").with_child(
|
||||
argument(ARG_FACING_ENTITY, &EntityArgumentConsumer)
|
||||
.execute(&TpEntitiesToPosFacingEntityExecutor),
|
||||
),
|
||||
)
|
||||
.with_child(
|
||||
argument(ARG_FACING_LOCATION, &Position3DArgumentConsumer)
|
||||
.execute(&TpEntitiesToPosFacingPosExecutor),
|
||||
),
|
||||
),
|
||||
)
|
||||
.with_child(
|
||||
argument(ARG_DESTINATION, &EntityArgumentConsumer)
|
||||
.execute(&TpEntitiesToEntityExecutor),
|
||||
),
|
||||
)
|
||||
.with_child(
|
||||
argument(ARG_LOCATION, &Position3DArgumentConsumer).execute(&TpSelfToPosExecutor),
|
||||
)
|
||||
.with_child(
|
||||
argument(ARG_DESTINATION, &EntityArgumentConsumer).execute(&TpSelfToEntityExecutor),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_core::text::{
|
||||
color::{Color, NamedColor},
|
||||
TextComponent,
|
||||
use pumpkin_core::{
|
||||
math::vector2::Vector2,
|
||||
text::{
|
||||
color::{Color, NamedColor},
|
||||
TextComponent,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
arg_position::{parse_arg_position, PositionArgumentConsumer},
|
||||
arg_simple::SimpleArgConsumer,
|
||||
tree::{CommandTree, ConsumedArgs},
|
||||
tree_builder::{argument, literal},
|
||||
args::{
|
||||
arg_bounded_num::BoundedNumArgumentConsumer,
|
||||
arg_position_2d::Position2DArgumentConsumer, ConsumedArgs, FindArgDefaultName,
|
||||
},
|
||||
tree::CommandTree,
|
||||
tree_builder::{argument_default_name, literal},
|
||||
CommandExecutor, CommandSender, InvalidTreeError,
|
||||
},
|
||||
server::Server,
|
||||
@@ -61,11 +66,7 @@ impl CommandExecutor for WorldborderSetExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let distance = args
|
||||
.get("distance")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<f64>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let distance = DISTANCE_CONSUMER.find_arg_default_name(args)?;
|
||||
|
||||
if (distance - border.new_diameter).abs() < f64::EPSILON {
|
||||
sender
|
||||
@@ -103,16 +104,8 @@ impl CommandExecutor for WorldborderSetTimeExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let distance = args
|
||||
.get("distance")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<f64>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let time = args
|
||||
.get("time")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<i32>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let distance = DISTANCE_CONSUMER.find_arg_default_name(args)?;
|
||||
let time = TIME_CONSUMER.find_arg_default_name(args)?;
|
||||
|
||||
match distance.total_cmp(&border.new_diameter) {
|
||||
std::cmp::Ordering::Equal => {
|
||||
@@ -161,11 +154,7 @@ impl CommandExecutor for WorldborderAddExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let distance = args
|
||||
.get("distance")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<f64>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let distance = DISTANCE_CONSUMER.find_arg_default_name(args)?;
|
||||
|
||||
if distance == 0.0 {
|
||||
sender
|
||||
@@ -205,16 +194,8 @@ impl CommandExecutor for WorldborderAddTimeExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let distance = args
|
||||
.get("distance")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<f64>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let time = args
|
||||
.get("time")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<i32>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let distance = DISTANCE_CONSUMER.find_arg_default_name(args)?;
|
||||
let time = TIME_CONSUMER.find_arg_default_name(args)?;
|
||||
|
||||
let distance = distance + border.new_diameter;
|
||||
|
||||
@@ -265,8 +246,7 @@ impl CommandExecutor for WorldborderCenterExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let x = parse_arg_position("x", args)?;
|
||||
let z = parse_arg_position("z", args)?;
|
||||
let Vector2 { x, z } = Position2DArgumentConsumer.find_arg_default_name(args)?;
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::text(&format!(
|
||||
@@ -294,11 +274,7 @@ impl CommandExecutor for WorldborderDamageAmountExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let damage_per_block = args
|
||||
.get("damage_per_block")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<f32>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let damage_per_block = DAMAGE_PER_BLOCK_CONSUMER.find_arg_default_name(args)?;
|
||||
|
||||
if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON {
|
||||
sender
|
||||
@@ -338,11 +314,7 @@ impl CommandExecutor for WorldborderDamageBufferExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let buffer = args
|
||||
.get("distance")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<f32>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let buffer = DAMAGE_BUFFER_CONSUMER.find_arg_default_name(args)?;
|
||||
|
||||
if (buffer - border.buffer).abs() < f32::EPSILON {
|
||||
sender
|
||||
@@ -382,11 +354,7 @@ impl CommandExecutor for WorldborderWarningDistanceExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let distance = args
|
||||
.get("distance")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<i32>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let distance = WARNING_DISTANCE_CONSUMER.find_arg_default_name(args)?;
|
||||
|
||||
if distance == border.warning_blocks {
|
||||
sender
|
||||
@@ -426,11 +394,7 @@ impl CommandExecutor for WorldborderWarningTimeExecutor {
|
||||
.expect("There should always be atleast one world");
|
||||
let mut border = world.worldborder.lock().await;
|
||||
|
||||
let time = args
|
||||
.get("time")
|
||||
.ok_or(InvalidTreeError::InvalidConsumptionError(None))?
|
||||
.parse::<i32>()
|
||||
.map_err(|err| InvalidTreeError::InvalidConsumptionError(Some(err.to_string())))?;
|
||||
let time = TIME_CONSUMER.find_arg_default_name(args)?;
|
||||
|
||||
if time == border.warning_time {
|
||||
sender
|
||||
@@ -454,33 +418,48 @@ impl CommandExecutor for WorldborderWarningTimeExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
static DISTANCE_CONSUMER: BoundedNumArgumentConsumer<f64> =
|
||||
BoundedNumArgumentConsumer::new().min(0.0).name("distance");
|
||||
|
||||
static TIME_CONSUMER: BoundedNumArgumentConsumer<i32> =
|
||||
BoundedNumArgumentConsumer::new().min(0).name("time");
|
||||
|
||||
static DAMAGE_PER_BLOCK_CONSUMER: BoundedNumArgumentConsumer<f32> =
|
||||
BoundedNumArgumentConsumer::new()
|
||||
.min(0.0)
|
||||
.name("damage_per_block");
|
||||
|
||||
static DAMAGE_BUFFER_CONSUMER: BoundedNumArgumentConsumer<f32> =
|
||||
BoundedNumArgumentConsumer::new().min(0.0).name("buffer");
|
||||
|
||||
static WARNING_DISTANCE_CONSUMER: BoundedNumArgumentConsumer<i32> =
|
||||
BoundedNumArgumentConsumer::new().min(0).name("distance");
|
||||
|
||||
pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(NAMES, DESCRIPTION)
|
||||
.with_child(
|
||||
literal("add").with_child(
|
||||
argument("distance", &SimpleArgConsumer)
|
||||
argument_default_name(&DISTANCE_CONSUMER)
|
||||
.execute(&WorldborderAddExecutor)
|
||||
.with_child(
|
||||
argument("time", &SimpleArgConsumer).execute(&WorldborderAddTimeExecutor),
|
||||
argument_default_name(&TIME_CONSUMER).execute(&WorldborderAddTimeExecutor),
|
||||
),
|
||||
),
|
||||
)
|
||||
.with_child(literal("center").with_child(
|
||||
argument("x", &PositionArgumentConsumer).with_child(
|
||||
argument("z", &PositionArgumentConsumer).execute(&WorldborderCenterExecutor),
|
||||
),
|
||||
argument_default_name(&Position2DArgumentConsumer).execute(&WorldborderCenterExecutor),
|
||||
))
|
||||
.with_child(
|
||||
literal("damage")
|
||||
.with_child(
|
||||
literal("amount").with_child(
|
||||
argument("damage_per_block", &SimpleArgConsumer)
|
||||
argument_default_name(&DAMAGE_PER_BLOCK_CONSUMER)
|
||||
.execute(&WorldborderDamageAmountExecutor),
|
||||
),
|
||||
)
|
||||
.with_child(
|
||||
literal("buffer").with_child(
|
||||
argument("distance", &SimpleArgConsumer)
|
||||
argument_default_name(&DAMAGE_BUFFER_CONSUMER)
|
||||
.execute(&WorldborderDamageBufferExecutor),
|
||||
),
|
||||
),
|
||||
@@ -488,10 +467,10 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
.with_child(literal("get").execute(&WorldborderGetExecutor))
|
||||
.with_child(
|
||||
literal("set").with_child(
|
||||
argument("distance", &SimpleArgConsumer)
|
||||
argument_default_name(&DISTANCE_CONSUMER)
|
||||
.execute(&WorldborderSetExecutor)
|
||||
.with_child(
|
||||
argument("time", &SimpleArgConsumer).execute(&WorldborderSetTimeExecutor),
|
||||
argument_default_name(&TIME_CONSUMER).execute(&WorldborderSetTimeExecutor),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -499,12 +478,12 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
literal("warning")
|
||||
.with_child(
|
||||
literal("distance").with_child(
|
||||
argument("distance", &SimpleArgConsumer)
|
||||
argument_default_name(&WARNING_DISTANCE_CONSUMER)
|
||||
.execute(&WorldborderWarningDistanceExecutor),
|
||||
),
|
||||
)
|
||||
.with_child(literal("time").with_child(
|
||||
argument("time", &SimpleArgConsumer).execute(&WorldborderWarningTimeExecutor),
|
||||
argument_default_name(&TIME_CONSUMER).execute(&WorldborderWarningTimeExecutor),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,4 +6,5 @@ pub mod cmd_kill;
|
||||
pub mod cmd_pumpkin;
|
||||
pub mod cmd_say;
|
||||
pub mod cmd_stop;
|
||||
pub mod cmd_teleport;
|
||||
pub mod cmd_worldborder;
|
||||
|
||||
@@ -3,11 +3,13 @@ use pumpkin_core::text::TextComponent;
|
||||
use crate::command::dispatcher::InvalidTreeError::{
|
||||
InvalidConsumptionError, InvalidRequirementError,
|
||||
};
|
||||
use crate::command::tree::{Command, CommandTree, ConsumedArgs, NodeType, RawArgs};
|
||||
use crate::command::tree::{Command, CommandTree, NodeType, RawArgs};
|
||||
use crate::command::CommandSender;
|
||||
use crate::server::Server;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::args::ConsumedArgs;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum InvalidTreeError {
|
||||
/// This error means that there was an error while parsing a previously consumed argument.
|
||||
@@ -29,7 +31,7 @@ impl<'a> CommandDispatcher<'a> {
|
||||
pub async fn handle_command(
|
||||
&'a self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
server: &Server,
|
||||
server: &'a Server,
|
||||
cmd: &'a str,
|
||||
) {
|
||||
if let Err(err) = self.dispatch(sender, server, cmd).await {
|
||||
@@ -46,7 +48,7 @@ impl<'a> CommandDispatcher<'a> {
|
||||
pub(crate) async fn dispatch(
|
||||
&'a self,
|
||||
src: &mut CommandSender<'a>,
|
||||
server: &Server,
|
||||
server: &'a Server,
|
||||
cmd: &'a str,
|
||||
) -> Result<(), String> {
|
||||
// Other languages dont use the ascii whitespace
|
||||
@@ -67,15 +69,11 @@ 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) => match is_fitting_path {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(error) => {
|
||||
// Custom error message or not ?
|
||||
if let Some(error) = error {
|
||||
return Err(error);
|
||||
}
|
||||
Ok(is_fitting_path) => {
|
||||
if is_fitting_path {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(format!("Invalid Syntax. Usage: {tree}"))
|
||||
@@ -98,11 +96,11 @@ impl<'a> CommandDispatcher<'a> {
|
||||
|
||||
async fn try_is_fitting_path(
|
||||
src: &mut CommandSender<'a>,
|
||||
server: &Server,
|
||||
server: &'a Server,
|
||||
path: &[usize],
|
||||
tree: &CommandTree<'a>,
|
||||
mut raw_args: RawArgs<'a>,
|
||||
) -> Result<Result<(), Option<String>>, InvalidTreeError> {
|
||||
) -> Result<bool, InvalidTreeError> {
|
||||
let mut parsed_args: ConsumedArgs = HashMap::new();
|
||||
|
||||
for node in path.iter().map(|&i| &tree.nodes[i]) {
|
||||
@@ -110,35 +108,33 @@ impl<'a> CommandDispatcher<'a> {
|
||||
NodeType::ExecuteLeaf { executor } => {
|
||||
return if raw_args.is_empty() {
|
||||
executor.execute(src, server, &parsed_args).await?;
|
||||
Ok(Ok(()))
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(Err(None))
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
NodeType::Literal { string, .. } => {
|
||||
if raw_args.pop() != Some(string) {
|
||||
return Ok(Err(None));
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
NodeType::Argument { consumer, name, .. } => {
|
||||
match consumer.consume(src, server, &mut raw_args).await {
|
||||
Ok(consumed) => {
|
||||
Some(consumed) => {
|
||||
parsed_args.insert(name, consumed);
|
||||
}
|
||||
Err(err) => {
|
||||
return Ok(Err(err));
|
||||
}
|
||||
None => return Ok(false),
|
||||
}
|
||||
}
|
||||
NodeType::Require { predicate, .. } => {
|
||||
if !predicate(src) {
|
||||
return Ok(Err(None));
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Err(None))
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Register a command with the dispatcher.
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use args::ConsumedArgs;
|
||||
use async_trait::async_trait;
|
||||
use commands::{
|
||||
cmd_echest, cmd_gamemode, cmd_help, cmd_kick, cmd_kill, cmd_pumpkin, cmd_say, cmd_stop,
|
||||
cmd_worldborder,
|
||||
cmd_teleport, cmd_worldborder,
|
||||
};
|
||||
use dispatcher::InvalidTreeError;
|
||||
use pumpkin_core::math::vector3::Vector3;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use tree::ConsumedArgs;
|
||||
|
||||
use crate::command::dispatcher::CommandDispatcher;
|
||||
use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
|
||||
mod arg_player;
|
||||
mod arg_position;
|
||||
mod arg_simple;
|
||||
|
||||
pub mod args;
|
||||
mod commands;
|
||||
pub mod dispatcher;
|
||||
mod tree;
|
||||
@@ -60,6 +58,14 @@ impl<'a> CommandSender<'a> {
|
||||
pub const fn permission_lvl(&self) -> i32 {
|
||||
4
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn position(&self) -> Option<Vector3<f64>> {
|
||||
match self {
|
||||
CommandSender::Console | CommandSender::Rcon(..) => None,
|
||||
CommandSender::Player(p) => Some(p.living_entity.entity.pos.load()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -75,6 +81,7 @@ pub fn default_dispatcher<'a>() -> Arc<CommandDispatcher<'a>> {
|
||||
dispatcher.register(cmd_kill::init_command_tree());
|
||||
dispatcher.register(cmd_kick::init_command_tree());
|
||||
dispatcher.register(cmd_worldborder::init_command_tree());
|
||||
dispatcher.register(cmd_teleport::init_command_tree());
|
||||
|
||||
Arc::new(dispatcher)
|
||||
}
|
||||
|
||||
@@ -1,27 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::CommandExecutor;
|
||||
use crate::{command::CommandSender, server::Server};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use super::{args::ArgumentConsumer, CommandExecutor};
|
||||
use crate::command::CommandSender;
|
||||
use std::{collections::VecDeque, fmt::Debug};
|
||||
|
||||
/// see [`crate::commands::tree_builder::argument`]
|
||||
pub type RawArgs<'a> = Vec<&'a str>;
|
||||
|
||||
/// see [`crate::commands::tree_builder::argument`] and [`CommandTree::execute`]/[`crate::commands::tree_builder::NonLeafNodeBuilder::execute`]
|
||||
pub type ConsumedArgs<'a> = HashMap<&'a str, String>;
|
||||
|
||||
/// see [`crate::commands::tree_builder::argument`]
|
||||
/// Provide value or an Optional error message, If no Error message provided the default will be used
|
||||
#[async_trait]
|
||||
pub(crate) trait ArgumentConsumer: Sync {
|
||||
async fn consume<'a>(
|
||||
&self,
|
||||
sender: &CommandSender<'a>,
|
||||
server: &Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Result<String, Option<String>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Node<'a> {
|
||||
pub(crate) children: Vec<usize>,
|
||||
pub(crate) node_type: NodeType<'a>,
|
||||
@@ -43,11 +27,30 @@ pub enum NodeType<'a> {
|
||||
},
|
||||
}
|
||||
|
||||
impl Debug for NodeType<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::ExecuteLeaf { .. } => f
|
||||
.debug_struct("ExecuteLeaf")
|
||||
.field("executor", &"..")
|
||||
.finish(),
|
||||
Self::Literal { string } => f.debug_struct("Literal").field("string", string).finish(),
|
||||
Self::Argument { name, .. } => f
|
||||
.debug_struct("Argument")
|
||||
.field("name", name)
|
||||
.field("consumer", &"..")
|
||||
.finish(),
|
||||
Self::Require { .. } => f.debug_struct("Require").field("predicate", &"..").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Command<'a> {
|
||||
Tree(CommandTree<'a>),
|
||||
Alias(&'a str),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CommandTree<'a> {
|
||||
pub(crate) nodes: Vec<Node<'a>>,
|
||||
pub(crate) children: Vec<usize>,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::command::tree::{ArgumentConsumer, CommandTree, Node, NodeType};
|
||||
use crate::command::args::ArgumentConsumer;
|
||||
use crate::command::tree::{CommandTree, Node, NodeType};
|
||||
use crate::command::CommandSender;
|
||||
|
||||
use super::args::DefaultNameArgConsumer;
|
||||
use super::CommandExecutor;
|
||||
|
||||
impl<'a> CommandTree<'a> {
|
||||
@@ -147,6 +149,18 @@ pub fn argument<'a>(name: &'a str, consumer: &'a dyn ArgumentConsumer) -> NonLea
|
||||
}
|
||||
}
|
||||
|
||||
/// same as [`crate::command::tree_builder::argument`], but uses default arg name of consumer
|
||||
pub fn argument_default_name(consumer: &dyn DefaultNameArgConsumer) -> NonLeafNodeBuilder<'_> {
|
||||
NonLeafNodeBuilder {
|
||||
node_type: NodeType::Argument {
|
||||
name: consumer.default_name(),
|
||||
consumer: consumer.get_argument_consumer(),
|
||||
},
|
||||
child_nodes: Vec::new(),
|
||||
leaf_nodes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// ```predicate``` should return ```false``` if requirement for reaching following [Node]s is not
|
||||
/// met.
|
||||
pub fn require(predicate: &(dyn Fn(&CommandSender) -> bool + Sync)) -> NonLeafNodeBuilder {
|
||||
|
||||
@@ -9,6 +9,7 @@ use pumpkin_protocol::client::login::CEncryptionRequest;
|
||||
use pumpkin_protocol::{client::config::CPluginMessage, ClientPacket};
|
||||
use pumpkin_registry::Registry;
|
||||
use pumpkin_world::dimension::Dimension;
|
||||
use rand::prelude::SliceRandom;
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
sync::{
|
||||
@@ -200,6 +201,26 @@ impl Server {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns all players from all worlds.
|
||||
pub async fn get_all_players(&self) -> Vec<Arc<Player>> {
|
||||
let mut players = Vec::<Arc<Player>>::new();
|
||||
|
||||
for world in &self.worlds {
|
||||
for (_, player) in world.current_players.lock().await.iter() {
|
||||
players.push(player.clone());
|
||||
}
|
||||
}
|
||||
|
||||
players
|
||||
}
|
||||
|
||||
/// Returns a random player from any of the worlds or None if all worlds are empty.
|
||||
pub async fn get_random_player(&self) -> Option<Arc<Player>> {
|
||||
let players = self.get_all_players().await;
|
||||
|
||||
players.choose(&mut rand::thread_rng()).map(Arc::<_>::clone)
|
||||
}
|
||||
|
||||
/// Searches for a player by their UUID across all worlds.
|
||||
///
|
||||
/// This function iterates through each world managed by the server and attempts to find a player with the specified UUID.
|
||||
@@ -236,6 +257,18 @@ impl Server {
|
||||
count
|
||||
}
|
||||
|
||||
/// Similar to [`Server::get_player_count`] >= n, but may be more efficient since it stops it's iteration through all worlds as soon as n players were found.
|
||||
pub async fn has_n_players(&self, n: usize) -> bool {
|
||||
let mut count = 0;
|
||||
for world in &self.worlds {
|
||||
count += world.current_players.lock().await.len();
|
||||
if count >= n {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Generates a new entity id
|
||||
/// This should be global
|
||||
pub fn new_entity_id(&self) -> EntityId {
|
||||
|
||||
Reference in New Issue
Block a user