Skip to content

Commit

Permalink
Commands: Fix Bug, Refactor Arguments, Add /tp (Pumpkin-MC#227)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
2 people authored and lokka30 committed Nov 7, 2024
1 parent 1422da8 commit 98442a5
Show file tree
Hide file tree
Showing 31 changed files with 1,493 additions and 428 deletions.
78 changes: 0 additions & 78 deletions pumpkin/src/command/arg_player.rs

This file was deleted.

45 changes: 0 additions & 45 deletions pumpkin/src/command/arg_position.rs

This file was deleted.

23 changes: 0 additions & 23 deletions pumpkin/src/command/arg_simple.rs

This file was deleted.

157 changes: 157 additions & 0 deletions pumpkin/src/command/args/arg_bounded_num.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 58 additions & 0 deletions pumpkin/src/command/args/arg_command.rs
Original file line number Diff line number Diff line change
@@ -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(),
))),
}
}
}
Loading

0 comments on commit 98442a5

Please sign in to comment.