mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
remove async_trait from Commands
This decreased compile time by 30%, Plugins also are not required anymore to use async_trait when adding custom Commands
This commit is contained in:
58
Cargo.lock
generated
58
Cargo.lock
generated
@@ -493,9 +493,9 @@ checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.7.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
@@ -812,22 +812,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||
checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||
checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
@@ -1373,9 +1374,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.18"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
|
||||
checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -1587,9 +1588,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -1636,9 +1637,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
@@ -2132,9 +2133,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.14.2"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "101fec8d036f8d9d4a1e8ebf90d566d1d798f3b1aa379d2576a54a0d9acea5bd"
|
||||
checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
@@ -2142,9 +2143,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.14.2"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2d93e596a829ebe00afa41c3a056e6308d6b8a4c7d869edf184e2c91b1ba564"
|
||||
checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -2155,9 +2156,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.14.2"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d7b7346e150de32340ae3390b8b3ffa37ad93ec31fb5dad86afe817619e4e7"
|
||||
checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
@@ -2555,6 +2556,15 @@ version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
@@ -2668,6 +2678,12 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -3441,14 +3457,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"md-5",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ futures = { version = "0.3", default-features = false, features = ["executor"] }
|
||||
rayon = "1.11"
|
||||
crossbeam = "0.8"
|
||||
|
||||
uuid = { version = "1.18", features = ["serde", "v3", "v4"] }
|
||||
uuid = { version = "1.19", features = ["serde", "v3", "v4"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::tag::{RegistryKey, get_tag_ids};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::{command::dispatcher::CommandError, server::Server};
|
||||
|
||||
use super::{
|
||||
@@ -26,25 +26,18 @@ impl GetClientSideArgParser for BlockArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for BlockArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
Some(Arg::Block(s))
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
) -> ConsumeResult<'a> {
|
||||
let block = args.pop();
|
||||
match block {
|
||||
Some(s) => Box::pin(async move { Some(Arg::Block(s)) }),
|
||||
None => Box::pin(async move { None }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,25 +94,18 @@ impl GetClientSideArgParser for BlockPredicateArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for BlockPredicateArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
Some(Arg::BlockPredicate(s))
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
) -> ConsumeResult<'a> {
|
||||
let block = args.pop();
|
||||
match block {
|
||||
Some(s) => Box::pin(async move { Some(Arg::BlockPredicate(s)) }),
|
||||
None => Box::pin(async move { None }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::{Arg, ArgumentConsumer, FindArg, GetClientSideArgParser};
|
||||
use crate::command::args::{Arg, ArgumentConsumer, ConsumeResult, FindArg, GetClientSideArgParser};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
pub struct BoolArgConsumer;
|
||||
|
||||
@@ -18,30 +17,25 @@ impl GetClientSideArgParser for BoolArgConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for BoolArgConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
match s {
|
||||
"false" => Some(Arg::Bool(false)),
|
||||
"true" => Some(Arg::Bool(true)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
let result: Option<Arg<'a>> = s_opt.map_or_else(
|
||||
|| None,
|
||||
|s| match s {
|
||||
"false" => Some(Arg::Bool(false)),
|
||||
"true" => Some(Arg::Bool(true)),
|
||||
_ => None,
|
||||
},
|
||||
);
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::{
|
||||
Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
|
||||
Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
|
||||
SuggestResult,
|
||||
};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
use crate::world::bossbar::BossbarColor;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
|
||||
pub struct BossbarColorArgumentConsumer;
|
||||
@@ -22,42 +22,50 @@ impl GetClientSideArgParser for BossbarColorArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for BossbarColorArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
let color = match s {
|
||||
"blue" => Some(BossbarColor::Blue),
|
||||
"green" => Some(BossbarColor::Green),
|
||||
"pink" => Some(BossbarColor::Pink),
|
||||
"purple" => Some(BossbarColor::Purple),
|
||||
"red" => Some(BossbarColor::Red),
|
||||
"white" => Some(BossbarColor::White),
|
||||
"yellow" => Some(BossbarColor::Yellow),
|
||||
_ => None,
|
||||
};
|
||||
let result: Option<Arg<'a>> = s_opt.map_or_else(
|
||||
|| None,
|
||||
|s| {
|
||||
let color = match s {
|
||||
"blue" => Some(BossbarColor::Blue),
|
||||
"green" => Some(BossbarColor::Green),
|
||||
"pink" => Some(BossbarColor::Pink),
|
||||
"purple" => Some(BossbarColor::Purple),
|
||||
"red" => Some(BossbarColor::Red),
|
||||
"white" => Some(BossbarColor::White),
|
||||
"yellow" => Some(BossbarColor::Yellow),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
color.map(Arg::BossbarColor)
|
||||
color.map(Arg::BossbarColor)
|
||||
},
|
||||
);
|
||||
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
let colors = ["blue", "green", "pink", "purple", "red", "white", "yellow"];
|
||||
let suggestions: Vec<CommandSuggestion> = colors
|
||||
.iter()
|
||||
.map(|color| CommandSuggestion::new((*color).to_string(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
) -> SuggestResult<'a> {
|
||||
Box::pin(async move {
|
||||
let colors = ["blue", "green", "pink", "purple", "red", "white", "yellow"];
|
||||
let suggestions: Vec<CommandSuggestion> = colors
|
||||
.iter()
|
||||
.map(|color| CommandSuggestion::new((*color).to_string(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::{
|
||||
Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
|
||||
Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
|
||||
SuggestResult,
|
||||
};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
use crate::world::bossbar::BossbarDivisions;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
|
||||
pub struct BossbarStyleArgumentConsumer;
|
||||
@@ -22,46 +22,54 @@ impl GetClientSideArgParser for BossbarStyleArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for BossbarStyleArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
let style = match s {
|
||||
"notched_10" => Some(BossbarDivisions::Notches10),
|
||||
"notched_12" => Some(BossbarDivisions::Notches12),
|
||||
"notched_20" => Some(BossbarDivisions::Notches20),
|
||||
"notched_6" => Some(BossbarDivisions::Notches6),
|
||||
"progress" => Some(BossbarDivisions::NoDivision),
|
||||
_ => None,
|
||||
};
|
||||
let result: Option<Arg<'a>> = s_opt.map_or_else(
|
||||
|| None,
|
||||
|s| {
|
||||
let style = match s {
|
||||
"notched_10" => Some(BossbarDivisions::Notches10),
|
||||
"notched_12" => Some(BossbarDivisions::Notches12),
|
||||
"notched_20" => Some(BossbarDivisions::Notches20),
|
||||
"notched_6" => Some(BossbarDivisions::Notches6),
|
||||
"progress" => Some(BossbarDivisions::NoDivision),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
style.map(Arg::BossbarStyle)
|
||||
style.map(Arg::BossbarStyle)
|
||||
},
|
||||
);
|
||||
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
let styles = [
|
||||
"notched_10",
|
||||
"notched_12",
|
||||
"notched_20",
|
||||
"notched_6",
|
||||
"progress",
|
||||
];
|
||||
let suggestions: Vec<CommandSuggestion> = styles
|
||||
.iter()
|
||||
.map(|style| CommandSuggestion::new((*style).to_string(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
) -> SuggestResult<'a> {
|
||||
Box::pin(async move {
|
||||
let styles = [
|
||||
"notched_10",
|
||||
"notched_12",
|
||||
"notched_20",
|
||||
"notched_6",
|
||||
"progress",
|
||||
];
|
||||
let suggestions: Vec<CommandSuggestion> = styles
|
||||
.iter()
|
||||
.map(|style| CommandSuggestion::new((*style).to_string(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ use core::f64;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion};
|
||||
use pumpkin_protocol::java::client::play::ArgumentType;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
@@ -21,47 +21,44 @@ pub struct BoundedNumArgumentConsumer<T: ToFromNumber> {
|
||||
name: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: ToFromNumber> ArgumentConsumer for BoundedNumArgumentConsumer<T>
|
||||
where
|
||||
Self: GetClientSideArgParser,
|
||||
{
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_src: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let x = args.pop()?.parse::<T>().ok()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
// 1. Perform the synchronous mutable operation (pop) outside the Future.
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
if let Some(max) = self.max_inclusive
|
||||
&& x > max
|
||||
{
|
||||
return Some(Arg::Num(Err(NotInBounds::UpperBound(
|
||||
x.to_number(),
|
||||
max.to_number(),
|
||||
))));
|
||||
}
|
||||
// 2. Process the entire argument and check bounds synchronously.
|
||||
let result: Option<Arg<'a>> = s_opt
|
||||
// Replace args.pop()?.parse::<T>().ok()?
|
||||
.and_then(|s| s.parse::<T>().ok())
|
||||
.map(|x| {
|
||||
// Check Upper Bound (max_inclusive)
|
||||
if let Some(max) = self.max_inclusive
|
||||
&& x > max
|
||||
{
|
||||
return Arg::Num(Err(NotInBounds::UpperBound(x.to_number(), max.to_number())));
|
||||
}
|
||||
|
||||
if let Some(min) = self.min_inclusive
|
||||
&& x < min
|
||||
{
|
||||
return Some(Arg::Num(Err(NotInBounds::LowerBound(
|
||||
x.to_number(),
|
||||
min.to_number(),
|
||||
))));
|
||||
}
|
||||
// Check Lower Bound (min_inclusive)
|
||||
if let Some(min) = self.min_inclusive
|
||||
&& x < min
|
||||
{
|
||||
return Arg::Num(Err(NotInBounds::LowerBound(x.to_number(), min.to_number())));
|
||||
}
|
||||
|
||||
Some(Arg::Num(Ok(x.to_number())))
|
||||
}
|
||||
// Success case
|
||||
Arg::Num(Ok(x.to_number()))
|
||||
});
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
// 3. Return a Future that immediately resolves to the calculated result.
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{
|
||||
ArgumentType, CommandSuggestion, StringProtoArgBehavior, SuggestionProviders,
|
||||
};
|
||||
@@ -6,7 +5,7 @@ use pumpkin_protocol::java::client::play::{
|
||||
use crate::{
|
||||
command::{
|
||||
CommandSender,
|
||||
args::SplitSingleWhitespaceIncludingEmptyParts,
|
||||
args::{ConsumeResult, SplitSingleWhitespaceIncludingEmptyParts, SuggestResult},
|
||||
dispatcher::CommandError,
|
||||
tree::{CommandTree, RawArgs},
|
||||
},
|
||||
@@ -27,40 +26,49 @@ impl GetClientSideArgParser for CommandTreeArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for CommandTreeArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
let dispatcher = server.command_dispatcher.read().await;
|
||||
dispatcher
|
||||
.get_tree(s)
|
||||
.map_or_else(|_| None, |tree| Some(Arg::CommandTree(tree.clone())))
|
||||
let Some(s) = s_opt else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
Box::pin(async move {
|
||||
let dispatcher = server.command_dispatcher.read().await;
|
||||
|
||||
dispatcher
|
||||
.get_tree(s)
|
||||
.ok()
|
||||
.map(|tree| Arg::CommandTree(tree.clone()))
|
||||
})
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
server: &'a Server,
|
||||
input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
let Some(input) = input.split_single_whitespace_including_empty_parts().last() else {
|
||||
return Ok(None);
|
||||
};
|
||||
) -> SuggestResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(input) = input.split_single_whitespace_including_empty_parts().last() else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let dispatcher = server.command_dispatcher.read().await;
|
||||
let suggestions = dispatcher
|
||||
.commands
|
||||
.keys()
|
||||
.filter(|suggestion| suggestion.starts_with(input))
|
||||
.map(|suggestion| CommandSuggestion::new(suggestion.clone(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
let dispatcher = server.command_dispatcher.read().await;
|
||||
let suggestions = dispatcher
|
||||
.commands
|
||||
.keys()
|
||||
.filter(|suggestion| suggestion.starts_with(input))
|
||||
.map(|suggestion| CommandSuggestion::new(suggestion.clone(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_util::Difficulty;
|
||||
|
||||
use crate::{
|
||||
command::{CommandSender, dispatcher::CommandError, tree::RawArgs},
|
||||
command::{
|
||||
CommandSender,
|
||||
args::{ConsumeResult, SuggestResult},
|
||||
dispatcher::CommandError,
|
||||
tree::RawArgs,
|
||||
},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
@@ -25,32 +29,35 @@ impl GetClientSideArgParser for DifficultyArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for DifficultyArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
Difficulty::from_str(s)
|
||||
.map_or_else(|_| None, |difficulty| Some(Arg::Difficulty(difficulty)))
|
||||
let result: Option<Arg<'a>> =
|
||||
s_opt.and_then(|s| Difficulty::from_str(s).map(Arg::Difficulty).ok());
|
||||
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
let difficulties = ["easy", "normal", "hard", "peaceful"];
|
||||
let suggestions: Vec<CommandSuggestion> = difficulties
|
||||
.iter()
|
||||
.map(|difficulty| CommandSuggestion::new((*difficulty).to_string(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
) -> SuggestResult<'a> {
|
||||
Box::pin(async move {
|
||||
let difficulties = ["easy", "normal", "hard", "peaceful"];
|
||||
let suggestions: Vec<CommandSuggestion> = difficulties
|
||||
.iter()
|
||||
.map(|difficulty| CommandSuggestion::new((*difficulty).to_string(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@ use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::GameMode;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -250,35 +250,34 @@ impl GetClientSideArgParser for EntitiesArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for EntitiesArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
src: &CommandSender,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
let Some(s) = s_opt else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
let entity_selector = match s.parse::<TargetSelector>() {
|
||||
Ok(selector) => selector,
|
||||
Err(e) => {
|
||||
log::debug!("Failed to parse target selector '{s}': {e}");
|
||||
return None;
|
||||
return Box::pin(async move { None }); // Return a Future resolving to None
|
||||
}
|
||||
};
|
||||
// todo: command context
|
||||
let entities = server.select_entities(&entity_selector, Some(src)).await;
|
||||
|
||||
Some(Arg::Entities(entities))
|
||||
}
|
||||
Box::pin(async move {
|
||||
// todo: command context
|
||||
// This is the required asynchronous operation.
|
||||
let entities = server.select_entities(&entity_selector, Some(sender)).await;
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Some(Arg::Entities(entities))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::command::args::entities::TargetSelector;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
use super::super::args::ArgumentConsumer;
|
||||
use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser};
|
||||
@@ -32,41 +32,38 @@ impl GetClientSideArgParser for EntityArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for EntityArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
src: &CommandSender,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
let Some(s) = s_opt else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
let entity_selector = match s.parse::<TargetSelector>() {
|
||||
Ok(selector) => selector,
|
||||
Err(e) => {
|
||||
log::debug!("Failed to parse target selector '{s}': {e}");
|
||||
return None;
|
||||
return Box::pin(async move { None });
|
||||
}
|
||||
};
|
||||
|
||||
if entity_selector.get_limit() > 1 {
|
||||
log::debug!("Target selector '{s}' has limit > 1, expected single entity");
|
||||
return None;
|
||||
return Box::pin(async move { None });
|
||||
}
|
||||
// todo: command context
|
||||
let entities = server.select_entities(&entity_selector, Some(src)).await;
|
||||
|
||||
// Take first
|
||||
entities.into_iter().next().map(Arg::Entity)
|
||||
}
|
||||
Box::pin(async move {
|
||||
// todo: command context
|
||||
let entities = server.select_entities(&entity_selector, Some(sender)).await;
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
entities.into_iter().next().map(Arg::Entity)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::GameMode;
|
||||
|
||||
use crate::{
|
||||
command::{CommandSender, dispatcher::CommandError, tree::RawArgs},
|
||||
command::{CommandSender, args::ConsumeResult, dispatcher::CommandError, tree::RawArgs},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
@@ -23,32 +22,26 @@ impl GetClientSideArgParser for GamemodeArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for GamemodeArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
if let Ok(id) = s.parse::<i8>()
|
||||
&& let Ok(gamemode) = GameMode::try_from(id)
|
||||
{
|
||||
return Some(Arg::GameMode(gamemode));
|
||||
}
|
||||
let result: Option<Arg<'a>> = s_opt.and_then(|s| {
|
||||
if let Ok(id) = s.parse::<i8>()
|
||||
&& let Ok(gamemode) = GameMode::try_from(id)
|
||||
{
|
||||
return Some(Arg::GameMode(gamemode));
|
||||
}
|
||||
|
||||
GameMode::from_str(s).map_or_else(|_| None, |gamemode| Some(Arg::GameMode(gamemode)))
|
||||
}
|
||||
GameMode::from_str(s).map(Arg::GameMode).ok() // Convert Result to Option<T>
|
||||
});
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{
|
||||
ArgumentType, CommandSuggestion, StringProtoArgBehavior, SuggestionProviders,
|
||||
ArgumentType, StringProtoArgBehavior, SuggestionProviders,
|
||||
};
|
||||
|
||||
use crate::{command::dispatcher::CommandError, server::Server};
|
||||
use crate::{
|
||||
command::{args::ConsumeResult, dispatcher::CommandError},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
@@ -26,31 +28,26 @@ impl GetClientSideArgParser for MsgArgConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for MsgArgConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let mut msg = args.pop()?.to_string();
|
||||
) -> ConsumeResult<'a> {
|
||||
let first_word_opt = args.pop();
|
||||
|
||||
let mut msg = match first_word_opt {
|
||||
Some(word) => word.to_string(),
|
||||
None => return Box::pin(async move { None }),
|
||||
};
|
||||
|
||||
while let Some(word) = args.pop() {
|
||||
msg.push(' ');
|
||||
msg.push_str(word);
|
||||
}
|
||||
|
||||
Some(Arg::Msg(msg))
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { Some(Arg::Msg(msg)) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::pin::Pin;
|
||||
use std::{collections::HashMap, hash::Hash, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bounded_num::{NotInBounds, Number};
|
||||
use pumpkin_data::Enchantment;
|
||||
use pumpkin_data::damage::DamageType;
|
||||
@@ -51,24 +51,30 @@ pub mod textcomponent;
|
||||
pub mod time;
|
||||
|
||||
/// see [`crate::commands::tree::builder::argument`]
|
||||
#[async_trait]
|
||||
pub trait ArgumentConsumer: Sync + GetClientSideArgParser {
|
||||
async fn consume<'a>(
|
||||
pub type ConsumeResult<'a> = Pin<Box<dyn Future<Output = Option<Arg<'a>>> + Send + 'a>>;
|
||||
|
||||
pub type SuggestResult<'a> =
|
||||
Pin<Box<dyn Future<Output = Result<Option<Vec<CommandSuggestion>>, CommandError>> + Send + 'a>>;
|
||||
|
||||
pub trait ArgumentConsumer: Sync + Send + GetClientSideArgParser {
|
||||
fn consume<'a>(
|
||||
&'a self,
|
||||
sender: &CommandSender,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg>;
|
||||
) -> ConsumeResult<'a>;
|
||||
|
||||
/// Used for tab completion (but only if argument suggestion type is "`minecraft:ask_server`"!).
|
||||
///
|
||||
/// NOTE: This is called after this consumer's [`ArgumentConsumer::consume`] method returned None, so if args is used here, make sure [`ArgumentConsumer::consume`] never returns None after mutating args.
|
||||
async fn suggest<'a>(
|
||||
fn suggest<'a>(
|
||||
&'a self,
|
||||
sender: &CommandSender,
|
||||
server: &'a Server,
|
||||
input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError>;
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> SuggestResult<'a> {
|
||||
Box::pin(async move { Ok(None) })
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetClientSideArgParser {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::entity::player::Player;
|
||||
@@ -28,45 +28,49 @@ impl GetClientSideArgParser for PlayersArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for PlayersArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
src: &CommandSender,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
let players = match s {
|
||||
"@s" => match src {
|
||||
let Some(s) = s_opt else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
let sync_result: Option<Vec<Arc<Player>>> = match s {
|
||||
"@s" => match sender {
|
||||
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 {
|
||||
"@n" | "@p" => match sender {
|
||||
CommandSender::Player(p) => Some(vec![p.clone()]),
|
||||
// todo: implement for non-players: how should this behave when sender is console/rcon?
|
||||
_ => None,
|
||||
},
|
||||
"@r" => {
|
||||
(server.get_random_player().await).map_or_else(|| Some(vec![]), |p| Some(vec![p]))
|
||||
}
|
||||
"@a" | "@e" => Some(server.get_all_players().await),
|
||||
name => server.get_player_by_name(name).await.map(|p| vec![p]),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
players.map(Arg::Players)
|
||||
}
|
||||
if let Some(players) = sync_result {
|
||||
return Box::pin(async move { Some(Arg::Players(players)) });
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move {
|
||||
let players = match s {
|
||||
"@r" => server
|
||||
.get_random_player()
|
||||
.await
|
||||
.map_or_else(|| Some(vec![]), |p| Some(vec![p])),
|
||||
"@a" | "@e" => Some(server.get_all_players().await),
|
||||
name => server.get_player_by_name(name).await.map(|p| vec![p]),
|
||||
};
|
||||
|
||||
players.map(Arg::Players)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
@@ -27,28 +27,25 @@ impl GetClientSideArgParser for Position2DArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for Position2DArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
src: &CommandSender,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let pos = MaybeRelativePosition2D::try_new(args.pop()?, args.pop()?)?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let x_str_opt = args.pop();
|
||||
let z_str_opt = args.pop();
|
||||
|
||||
let vec2 = pos.try_to_absolute(src.position())?;
|
||||
let (Some(x_str), Some(z_str)) = (x_str_opt, z_str_opt) else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
Some(Arg::Pos2D(vec2))
|
||||
}
|
||||
let result: Option<Arg<'a>> = MaybeRelativePosition2D::try_new(x_str, z_str)
|
||||
.and_then(|pos| pos.try_to_absolute(sender.position()))
|
||||
.map(Arg::Pos2D);
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
@@ -24,28 +24,26 @@ impl GetClientSideArgParser for Position3DArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for Position3DArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
src: &CommandSender,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let pos = MaybeRelativePosition3D::try_new(args.pop()?, args.pop()?, args.pop()?)?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let x_str_opt = args.pop();
|
||||
let y_str_opt = args.pop();
|
||||
let z_str_opt = args.pop();
|
||||
|
||||
let vec3 = pos.try_to_absolute(src.position())?;
|
||||
let (Some(x_str), Some(y_str), Some(z_str)) = (x_str_opt, y_str_opt, z_str_opt) else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
Some(Arg::Pos3D(vec3))
|
||||
}
|
||||
let result: Option<Arg<'a>> = MaybeRelativePosition3D::try_new(x_str, y_str, z_str)
|
||||
.and_then(|pos| pos.try_to_absolute(sender.position()))
|
||||
.map(Arg::Pos3D);
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
@@ -25,28 +25,26 @@ impl GetClientSideArgParser for BlockPosArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for BlockPosArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
src: &CommandSender,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let pos = MaybeRelativeBlockPos::try_new(args.pop()?, args.pop()?, args.pop()?)?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let x_str_opt = args.pop();
|
||||
let y_str_opt = args.pop();
|
||||
let z_str_opt = args.pop();
|
||||
|
||||
let vec3 = pos.try_to_absolute(src.position())?;
|
||||
let (Some(x_str), Some(y_str), Some(z_str)) = (x_str_opt, y_str_opt, z_str_opt) else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
Some(Arg::BlockPos(vec3))
|
||||
}
|
||||
let result: Option<Arg<'a>> = MaybeRelativeBlockPos::try_new(x_str, y_str, z_str)
|
||||
.and_then(|pos| pos.try_to_absolute(sender.position()))
|
||||
.map(Arg::BlockPos);
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::damage::DamageType;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
use crate::command::{
|
||||
CommandSender,
|
||||
args::{
|
||||
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
GetClientSideArgParser,
|
||||
},
|
||||
dispatcher::CommandError,
|
||||
@@ -27,30 +26,24 @@ impl GetClientSideArgParser for DamageTypeArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for DamageTypeArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let name = args.pop()?;
|
||||
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let name_opt: Option<&'a str> = args.pop();
|
||||
|
||||
// Create a static damage type first
|
||||
let damage_type = DamageType::from_name(name)?;
|
||||
// Find matching static damage type from values array
|
||||
Some(Arg::DamageType(damage_type))
|
||||
}
|
||||
let result: Option<Arg<'a>> = name_opt.map_or_else(
|
||||
|| None,
|
||||
|name| {
|
||||
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||
DamageType::from_name(name).map(Arg::DamageType)
|
||||
},
|
||||
);
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::effect::StatusEffect;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
use crate::command::{
|
||||
CommandSender,
|
||||
args::{
|
||||
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
GetClientSideArgParser,
|
||||
},
|
||||
dispatcher::CommandError,
|
||||
@@ -29,27 +29,22 @@ impl GetClientSideArgParser for EffectTypeArgumentConsumer {
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for EffectTypeArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let name = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let status_effect: Option<&'a str> = args.pop();
|
||||
|
||||
// Create a static damage type first
|
||||
let damage_type = StatusEffect::from_name(name.strip_prefix("minecraft:").unwrap_or(name))?;
|
||||
// Find matching static damage type from values array
|
||||
Some(Arg::Effect(damage_type))
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
match status_effect {
|
||||
Some(name) => Box::pin(async move {
|
||||
let status_effect =
|
||||
StatusEffect::from_name(name.strip_prefix("minecraft:").unwrap_or(name))?;
|
||||
Some(Arg::Effect(status_effect))
|
||||
}),
|
||||
None => Box::pin(async move { None }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::Enchantment;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
use crate::command::{
|
||||
CommandSender,
|
||||
args::{
|
||||
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
GetClientSideArgParser,
|
||||
},
|
||||
dispatcher::CommandError,
|
||||
@@ -29,27 +29,19 @@ impl GetClientSideArgParser for EnchantmentArgumentConsumer {
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for EnchantmentArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let name = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let name_opt: Option<&'a str> = args.pop();
|
||||
|
||||
// Create a static damage type first
|
||||
let enchantment = Enchantment::from_name(name)?;
|
||||
// Find matching static damage type from values array
|
||||
Some(Arg::Enchantment(enchantment))
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
let result: Option<Arg<'a>> = name_opt.map_or_else(
|
||||
|| None,
|
||||
|name| Enchantment::from_name(name).map(Arg::Enchantment),
|
||||
);
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::{
|
||||
CommandSender,
|
||||
args::{
|
||||
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
GetClientSideArgParser,
|
||||
},
|
||||
dispatcher::CommandError,
|
||||
@@ -28,23 +28,17 @@ impl GetClientSideArgParser for ItemArgumentConsumer {
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for ItemArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
// todo: get an actual item
|
||||
Some(Arg::Item(args.pop()?))
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
) -> ConsumeResult<'a> {
|
||||
let item = args.pop();
|
||||
match item {
|
||||
Some(s) => Box::pin(async move { Some(Arg::Item(s)) }),
|
||||
None => Box::pin(async move { None }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::particle::Particle;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
use crate::command::{
|
||||
CommandSender,
|
||||
args::{
|
||||
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg,
|
||||
GetClientSideArgParser,
|
||||
},
|
||||
dispatcher::CommandError,
|
||||
@@ -29,27 +29,23 @@ impl GetClientSideArgParser for ParticleArgumentConsumer {
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for ParticleArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let name = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let name_opt: Option<&'a str> = args.pop();
|
||||
|
||||
// Create a static damage type first
|
||||
let particle = Particle::from_name(name.strip_prefix("minecraft:").unwrap_or(name))?;
|
||||
// Find matching static damage type from values array
|
||||
Some(Arg::Particle(particle))
|
||||
}
|
||||
let result: Option<Arg<'a>> = name_opt.map_or_else(
|
||||
|| None,
|
||||
|name| {
|
||||
Particle::from_name(name.strip_prefix("minecraft:").unwrap_or(name))
|
||||
.map(Arg::Particle)
|
||||
},
|
||||
);
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::{
|
||||
Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
|
||||
Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
|
||||
};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
pub struct ResourceLocationArgumentConsumer {
|
||||
autocomplete: bool,
|
||||
@@ -22,39 +21,43 @@ impl GetClientSideArgParser for ResourceLocationArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for ResourceLocationArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
Some(Arg::ResourceLocation(args.pop()?))
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
if !self.autocomplete {
|
||||
return Ok(None);
|
||||
return Box::pin(async move { None });
|
||||
}
|
||||
// TODO
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
// let suggestions = server
|
||||
// .bossbars
|
||||
// .lock()
|
||||
// .await
|
||||
// .custom_bossbars
|
||||
// .keys()
|
||||
// .map(|suggestion| CommandSuggestion::new(suggestion, None))
|
||||
// .collect();
|
||||
|
||||
Ok(None)
|
||||
Box::pin(async move { s_opt.map(Arg::ResourceLocation) })
|
||||
}
|
||||
|
||||
// async fn suggest<'a>(
|
||||
// &'a self,
|
||||
// _sender: &CommandSender,
|
||||
// _server: &'a Server,
|
||||
// _input: &'a str,
|
||||
// ) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
// if !self.autocomplete {
|
||||
// return Ok(None);
|
||||
// }
|
||||
// // TODO
|
||||
|
||||
// // let suggestions = server
|
||||
// // .bossbars
|
||||
// // .lock()
|
||||
// // .await
|
||||
// // .custom_bossbars
|
||||
// // .keys()
|
||||
// // .map(|suggestion| CommandSuggestion::new(suggestion, None))
|
||||
// // .collect();
|
||||
|
||||
// Ok(None)
|
||||
// }
|
||||
}
|
||||
|
||||
impl DefaultNameArgConsumer for ResourceLocationArgumentConsumer {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::ConsumeResult;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
@@ -22,39 +22,36 @@ impl GetClientSideArgParser for RotationArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for RotationArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_src: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let yaw = args.pop()?;
|
||||
let pitch = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let yaw_str_opt = args.pop();
|
||||
let pitch_str_opt = args.pop();
|
||||
|
||||
let mut yaw = yaw.parse::<f32>().ok()?;
|
||||
let mut pitch = pitch.parse::<f32>().ok()?;
|
||||
let (Some(yaw_str), Some(pitch_str)) = (yaw_str_opt, pitch_str_opt) else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
yaw %= 360.0;
|
||||
if yaw >= 180.0 {
|
||||
yaw -= 360.0;
|
||||
}
|
||||
pitch %= 360.0;
|
||||
if pitch >= 180.0 {
|
||||
pitch -= 360.0;
|
||||
}
|
||||
let result: Option<Arg<'a>> = yaw_str.parse::<f32>().ok().and_then(|mut yaw| {
|
||||
pitch_str.parse::<f32>().ok().map(|mut pitch| {
|
||||
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))
|
||||
}
|
||||
Arg::Rotation(yaw, pitch)
|
||||
})
|
||||
});
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{
|
||||
ArgumentType, CommandSuggestion, StringProtoArgBehavior, SuggestionProviders,
|
||||
ArgumentType, StringProtoArgBehavior, SuggestionProviders,
|
||||
};
|
||||
|
||||
use crate::{command::dispatcher::CommandError, server::Server};
|
||||
use crate::{
|
||||
command::{args::ConsumeResult, dispatcher::CommandError},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
@@ -27,24 +29,16 @@ impl GetClientSideArgParser for SimpleArgConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for SimpleArgConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
Some(Arg::Simple(args.pop()?))
|
||||
}
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { s_opt.map(Arg::Simple) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::sound::Sound;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::{command::dispatcher::CommandError, server::Server};
|
||||
use crate::{
|
||||
command::{args::ConsumeResult, dispatcher::CommandError},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
@@ -25,25 +27,16 @@ impl GetClientSideArgParser for SoundArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for SoundArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
Some(Arg::Block(s))
|
||||
}
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { s_opt.map(Arg::Block) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::{
|
||||
Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
|
||||
Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
|
||||
SuggestResult,
|
||||
};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::sound::SoundCategory;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
|
||||
@@ -24,51 +24,54 @@ impl GetClientSideArgParser for SoundCategoryArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for SoundCategoryArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
// Convert string input to SoundCategory enum
|
||||
// Uses lowercase to make the command case-insensitive
|
||||
let category = match s.to_lowercase().as_str() {
|
||||
"master" => Some(SoundCategory::Master), // Default category, affects all sounds
|
||||
"music" => Some(SoundCategory::Music), // Background music
|
||||
// i don't use SoundCategory::from_name because its is record and not records :c
|
||||
"record" => Some(SoundCategory::Records), // Music discs
|
||||
"weather" => Some(SoundCategory::Weather), // Rain, thunder
|
||||
"block" => Some(SoundCategory::Blocks), // Block sounds
|
||||
"hostile" => Some(SoundCategory::Hostile), // Hostile mob sounds
|
||||
"neutral" => Some(SoundCategory::Neutral), // Neutral mob sounds
|
||||
"player" => Some(SoundCategory::Players), // Player sounds
|
||||
"ambient" => Some(SoundCategory::Ambient), // Ambient environment
|
||||
"voice" => Some(SoundCategory::Voice), // Voice/speech
|
||||
_ => None,
|
||||
};
|
||||
let result: Option<Arg<'a>> = s_opt.and_then(|s| {
|
||||
let category = match s.to_lowercase().as_str() {
|
||||
"master" => Some(SoundCategory::Master),
|
||||
"music" => Some(SoundCategory::Music),
|
||||
// i don't use SoundCategory::from_name because its is record and not records :c
|
||||
"record" => Some(SoundCategory::Records),
|
||||
"weather" => Some(SoundCategory::Weather),
|
||||
"block" => Some(SoundCategory::Blocks),
|
||||
"hostile" => Some(SoundCategory::Hostile),
|
||||
"neutral" => Some(SoundCategory::Neutral),
|
||||
"player" => Some(SoundCategory::Players),
|
||||
"ambient" => Some(SoundCategory::Ambient),
|
||||
"voice" => Some(SoundCategory::Voice),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
category.map(Arg::SoundCategory) // Simplified by removing redundant closure
|
||||
category.map(Arg::SoundCategory)
|
||||
});
|
||||
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
|
||||
async fn suggest<'a>(
|
||||
fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
let categories = [
|
||||
"master", "music", "record", "weather", "block", "hostile", "neutral", "player",
|
||||
"ambient", "voice",
|
||||
];
|
||||
let suggestions: Vec<CommandSuggestion> = categories
|
||||
.iter()
|
||||
.map(|cat| CommandSuggestion::new((*cat).to_string(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
) -> SuggestResult<'a> {
|
||||
Box::pin(async move {
|
||||
let categories = [
|
||||
"master", "music", "record", "weather", "block", "hostile", "neutral", "player",
|
||||
"ambient", "voice",
|
||||
];
|
||||
let suggestions: Vec<CommandSuggestion> = categories
|
||||
.iter()
|
||||
.map(|cat| CommandSuggestion::new((*cat).to_string(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::{command::dispatcher::CommandError, server::Server};
|
||||
use crate::{
|
||||
command::{args::ConsumeResult, dispatcher::CommandError},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
@@ -27,23 +30,15 @@ impl GetClientSideArgParser for SummonableEntitiesArgumentConsumer {
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for SummonableEntitiesArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
Some(Arg::Block(s))
|
||||
}
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Box::pin(async move { s_opt.map(Arg::Block) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::command::CommandSender;
|
||||
use crate::command::args::{Arg, ArgumentConsumer, FindArg, GetClientSideArgParser};
|
||||
use crate::command::args::{Arg, ArgumentConsumer, ConsumeResult, FindArg, GetClientSideArgParser};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::RawArgs;
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
pub struct TextComponentArgConsumer;
|
||||
@@ -19,36 +18,32 @@ impl GetClientSideArgParser for TextComponentArgConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for TextComponentArgConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
let text_component = parse_text_component(s);
|
||||
|
||||
let Some(text_component) = text_component else {
|
||||
if s.starts_with('"') && s.ends_with('"') {
|
||||
let s = s.replace('"', "");
|
||||
return Some(Arg::TextComponent(TextComponent::text(s)));
|
||||
}
|
||||
return None;
|
||||
let Some(s) = s_opt else {
|
||||
return Box::pin(async move { None });
|
||||
};
|
||||
|
||||
Some(Arg::TextComponent(text_component))
|
||||
}
|
||||
let text_component_opt = parse_text_component(s);
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
let final_arg: Option<Arg<'a>> = text_component_opt.map_or_else(
|
||||
|| {
|
||||
(s.starts_with('"') && s.ends_with('"')).then(|| {
|
||||
let s_owned = s.replace('"', "");
|
||||
Arg::TextComponent(TextComponent::text(s_owned))
|
||||
})
|
||||
},
|
||||
|text_component| Some(Arg::TextComponent(text_component)),
|
||||
);
|
||||
|
||||
Box::pin(async move { final_arg })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||
use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders};
|
||||
|
||||
use crate::command::{
|
||||
CommandSender,
|
||||
args::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser},
|
||||
args::{
|
||||
Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg,
|
||||
GetClientSideArgParser,
|
||||
},
|
||||
dispatcher::CommandError,
|
||||
tree::RawArgs,
|
||||
};
|
||||
@@ -21,47 +23,39 @@ impl GetClientSideArgParser for TimeArgumentConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ArgumentConsumer for TimeArgumentConsumer {
|
||||
async fn consume<'a>(
|
||||
fn consume<'a, 'b>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a Server,
|
||||
args: &mut RawArgs<'a>,
|
||||
) -> Option<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
args: &'b mut RawArgs<'a>,
|
||||
) -> ConsumeResult<'a> {
|
||||
let s_opt: Option<&'a str> = args.pop();
|
||||
|
||||
// Parse number and unit
|
||||
let (num_str, unit) = s
|
||||
.find(|c: char| c.is_alphabetic())
|
||||
.map_or((s, "t"), |pos| (&s[..pos], &s[pos..]));
|
||||
let result: Option<Arg<'a>> = s_opt.and_then(|s| {
|
||||
let (num_str, unit) = s
|
||||
.find(|c: char| c.is_alphabetic())
|
||||
.map_or((s, "t"), |pos| (&s[..pos], &s[pos..]));
|
||||
|
||||
let number = num_str.parse::<f32>().ok()?;
|
||||
if number < 0.0 {
|
||||
return None;
|
||||
}
|
||||
let number = num_str.parse::<f32>().ok()?; // Replaces .ok()?
|
||||
|
||||
// Convert to ticks based on unit
|
||||
let ticks = match unit {
|
||||
"d" => number * 24000.0,
|
||||
"s" => number * 20.0,
|
||||
"t" => number,
|
||||
_ => return None,
|
||||
};
|
||||
if number < 0.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Round to nearest integer
|
||||
let ticks = ticks.round() as i32;
|
||||
let ticks = match unit {
|
||||
"d" => number * 24000.0,
|
||||
"s" => number * 20.0,
|
||||
"t" => number,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Arg::Time(ticks))
|
||||
}
|
||||
let ticks = ticks.round() as i32;
|
||||
|
||||
async fn suggest<'a>(
|
||||
&'a self,
|
||||
_sender: &CommandSender,
|
||||
_server: &'a Server,
|
||||
_input: &'a str,
|
||||
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||
Ok(None)
|
||||
Some(Arg::Time(ticks))
|
||||
});
|
||||
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::command::CommandResult;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::{
|
||||
command::{
|
||||
@@ -13,7 +14,6 @@ use crate::{
|
||||
net::DisconnectReason,
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const NAMES: [&str; 1] = ["ban"];
|
||||
@@ -24,43 +24,45 @@ const ARG_REASON: &str = "reason";
|
||||
|
||||
struct NoReasonExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for NoReasonExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
ban_player(sender, &targets[0], None).await;
|
||||
Ok(())
|
||||
ban_player(sender, &targets[0], None).await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ReasonExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ReasonExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
let Some(Arg::Msg(reason)) = args.get(ARG_REASON) else {
|
||||
return Err(InvalidConsumption(Some(ARG_REASON.into())));
|
||||
};
|
||||
let Some(Arg::Msg(reason)) = args.get(ARG_REASON) else {
|
||||
return Err(InvalidConsumption(Some(ARG_REASON.into())));
|
||||
};
|
||||
|
||||
ban_player(sender, &targets[0], Some(reason.clone())).await;
|
||||
Ok(())
|
||||
ban_player(sender, &targets[0], Some(reason.clone())).await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{net::IpAddr, str::FromStr};
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{Arg, ConsumedArgs, message::MsgArgConsumer, simple::SimpleArgConsumer},
|
||||
tree::{CommandTree, builder::argument},
|
||||
},
|
||||
@@ -13,7 +13,6 @@ use crate::{
|
||||
server::Server,
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const NAMES: [&str; 1] = ["ban-ip"];
|
||||
@@ -37,43 +36,45 @@ async fn parse_ip(target: &str, server: &Server) -> Option<IpAddr> {
|
||||
|
||||
struct NoReasonExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for NoReasonExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
ban_ip(sender, server, target, None).await;
|
||||
Ok(())
|
||||
ban_ip(sender, server, target, None).await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ReasonExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ReasonExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
let Some(Arg::Msg(reason)) = args.get(ARG_REASON) else {
|
||||
return Err(InvalidConsumption(Some(ARG_REASON.into())));
|
||||
};
|
||||
let Some(Arg::Msg(reason)) = args.get(ARG_REASON) else {
|
||||
return Err(InvalidConsumption(Some(ARG_REASON.into())));
|
||||
};
|
||||
|
||||
ban_ip(sender, server, target, Some(reason.clone())).await;
|
||||
Ok(())
|
||||
ban_ip(sender, server, target, Some(reason.clone())).await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use crate::{
|
||||
command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{Arg, ConsumedArgs, simple::SimpleArgConsumer},
|
||||
tree::CommandTree,
|
||||
tree::builder::argument,
|
||||
tree::{CommandTree, builder::argument},
|
||||
},
|
||||
data::{banned_ip_data::BANNED_IP_LIST, banned_player_data::BANNED_PLAYER_LIST},
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const NAMES: [&str; 1] = ["banlist"];
|
||||
@@ -18,91 +16,93 @@ const ARG_LIST_TYPE: &str = "ips|players";
|
||||
|
||||
struct ListExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ListExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(list_type)) = args.get(&ARG_LIST_TYPE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_LIST_TYPE.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(list_type)) = args.get(&ARG_LIST_TYPE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_LIST_TYPE.into())));
|
||||
};
|
||||
|
||||
match *list_type {
|
||||
"ips" => {
|
||||
let lock = &BANNED_IP_LIST.read().await;
|
||||
let entries = lock
|
||||
.banned_ips
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
(
|
||||
entry.ip.to_string(),
|
||||
entry.source.clone(),
|
||||
entry.reason.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
match *list_type {
|
||||
"ips" => {
|
||||
let lock = &BANNED_IP_LIST.read().await;
|
||||
let entries = lock
|
||||
.banned_ips
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
(
|
||||
entry.ip.to_string(),
|
||||
entry.source.clone(),
|
||||
entry.reason.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
handle_banlist(entries, sender).await;
|
||||
handle_banlist(entries, sender).await;
|
||||
}
|
||||
"players" => {
|
||||
let lock = &BANNED_PLAYER_LIST.read().await;
|
||||
let entries = lock
|
||||
.banned_players
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
(
|
||||
entry.name.clone(),
|
||||
entry.source.clone(),
|
||||
entry.reason.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
handle_banlist(entries, sender).await;
|
||||
}
|
||||
_ => {
|
||||
return Err(CommandError::CommandFailed(Box::new(TextComponent::text(
|
||||
"Incorrect argument for command".to_string(),
|
||||
))));
|
||||
}
|
||||
}
|
||||
"players" => {
|
||||
let lock = &BANNED_PLAYER_LIST.read().await;
|
||||
let entries = lock
|
||||
.banned_players
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
(
|
||||
entry.name.clone(),
|
||||
entry.source.clone(),
|
||||
entry.reason.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
handle_banlist(entries, sender).await;
|
||||
}
|
||||
_ => {
|
||||
return Err(CommandError::CommandFailed(Box::new(TextComponent::text(
|
||||
"Incorrect argument for command".to_string(),
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ListAllExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ListAllExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut entries = Vec::new();
|
||||
for entry in &BANNED_PLAYER_LIST.read().await.banned_players {
|
||||
entries.push((
|
||||
entry.name.clone(),
|
||||
entry.source.clone(),
|
||||
entry.reason.clone(),
|
||||
));
|
||||
}
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let mut entries = Vec::new();
|
||||
for entry in &BANNED_PLAYER_LIST.read().await.banned_players {
|
||||
entries.push((
|
||||
entry.name.clone(),
|
||||
entry.source.clone(),
|
||||
entry.reason.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
for entry in &BANNED_IP_LIST.read().await.banned_ips {
|
||||
entries.push((
|
||||
entry.ip.to_string(),
|
||||
entry.source.clone(),
|
||||
entry.reason.clone(),
|
||||
));
|
||||
}
|
||||
for entry in &BANNED_IP_LIST.read().await.banned_ips {
|
||||
entries.push((
|
||||
entry.ip.to_string(),
|
||||
entry.source.clone(),
|
||||
entry.reason.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
handle_banlist(entries, sender).await;
|
||||
Ok(())
|
||||
handle_banlist(entries, sender).await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,13 @@ use crate::command::args::players::PlayersArgumentConsumer;
|
||||
use crate::command::args::resource_location::ResourceLocationArgumentConsumer;
|
||||
|
||||
use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
|
||||
use crate::command::args::textcomponent::TextComponentArgConsumer;
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, argument_default_name, literal};
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use crate::server::Server;
|
||||
use crate::command::{CommandExecutor, CommandResult, CommandSender};
|
||||
use crate::world::bossbar::Bossbar;
|
||||
use crate::world::custom_bossbar::BossbarUpdateError;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::color::{Color, NamedColor};
|
||||
use pumpkin_util::text::hover::HoverEvent;
|
||||
@@ -55,361 +52,276 @@ enum CommandValueSet {
|
||||
|
||||
struct AddExecuter;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for AddExecuter {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut namespace = non_autocomplete_consumer()
|
||||
.find_arg_default_name(args)?
|
||||
.to_string();
|
||||
if !namespace.contains(':') {
|
||||
namespace = format!("minecraft:{namespace}");
|
||||
}
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let mut namespace = non_autocomplete_consumer()
|
||||
.find_arg_default_name(args)?
|
||||
.to_string();
|
||||
if !namespace.contains(':') {
|
||||
namespace = format!("minecraft:{namespace}");
|
||||
}
|
||||
|
||||
let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?;
|
||||
let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?;
|
||||
|
||||
if server.bossbars.lock().await.has_bossbar(&namespace) {
|
||||
send_error_message(
|
||||
sender,
|
||||
TextComponent::translate(
|
||||
"commands.bossbar.create.failed",
|
||||
[TextComponent::text(namespace.clone())],
|
||||
),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
if server.bossbars.lock().await.has_bossbar(&namespace) {
|
||||
send_error_message(
|
||||
sender,
|
||||
TextComponent::translate(
|
||||
"commands.bossbar.create.failed",
|
||||
[TextComponent::text(namespace.clone())],
|
||||
),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let bossbar = Bossbar::new(text_component);
|
||||
server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.create_bossbar(namespace.clone(), bossbar.clone());
|
||||
let bossbar = Bossbar::new(text_component);
|
||||
server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.create_bossbar(namespace.clone(), bossbar.clone());
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.create.success",
|
||||
[bossbar_prefix(bossbar.title.clone(), namespace.clone())],
|
||||
))
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.create.success",
|
||||
[bossbar_prefix(bossbar.title.clone(), namespace.clone())],
|
||||
))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct GetExecuter(CommandValueGet);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for GetExecuter {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let namespace = autocomplete_consumer()
|
||||
.find_arg_default_name(args)?
|
||||
.to_string();
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let namespace = autocomplete_consumer()
|
||||
.find_arg_default_name(args)?
|
||||
.to_string();
|
||||
|
||||
let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else {
|
||||
handle_bossbar_error(
|
||||
sender,
|
||||
BossbarUpdateError::InvalidResourceLocation(namespace.clone()),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else {
|
||||
handle_bossbar_error(
|
||||
sender,
|
||||
BossbarUpdateError::InvalidResourceLocation(namespace.clone()),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match self.0 {
|
||||
CommandValueGet::Max => {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.get.max",
|
||||
[
|
||||
bossbar_prefix(bossbar.bossbar_data.title.clone(), namespace.clone()),
|
||||
TextComponent::text(bossbar.max.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
match self.0 {
|
||||
CommandValueGet::Max => {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.get.max",
|
||||
[
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.clone(),
|
||||
),
|
||||
TextComponent::text(bossbar.max.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
CommandValueGet::Players => {}
|
||||
CommandValueGet::Value => {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.get.value",
|
||||
[
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.clone(),
|
||||
),
|
||||
TextComponent::text(bossbar.value.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
CommandValueGet::Visible => {
|
||||
let state = if bossbar.visible {
|
||||
"commands.bossbar.get.visible.visible"
|
||||
} else {
|
||||
"commands.bossbar.get.visible.hidden"
|
||||
};
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
state,
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.clone(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
CommandValueGet::Players => {}
|
||||
CommandValueGet::Value => {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.get.value",
|
||||
[
|
||||
bossbar_prefix(bossbar.bossbar_data.title.clone(), namespace.clone()),
|
||||
TextComponent::text(bossbar.value.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
CommandValueGet::Visible => {
|
||||
let state = if bossbar.visible {
|
||||
"commands.bossbar.get.visible.visible"
|
||||
} else {
|
||||
"commands.bossbar.get.visible.hidden"
|
||||
};
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
state,
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.clone(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ListExecuter;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ListExecuter {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let bossbars = server.bossbars.lock().await.get_all_bossbars();
|
||||
let Some(bossbars) = bossbars else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.list.bars.none",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
if bossbars.is_empty() {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.list.bars.none",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let bossbars = server.bossbars.lock().await.get_all_bossbars();
|
||||
let Some(bossbars) = bossbars else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.list.bars.none",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
if bossbars.is_empty() {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.list.bars.none",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut bossbars_text = TextComponent::text("");
|
||||
for (i, bossbar) in bossbars.iter().enumerate() {
|
||||
if i == 0 {
|
||||
bossbars_text = bossbars_text.add_child(bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
bossbar.namespace.clone(),
|
||||
));
|
||||
} else {
|
||||
bossbars_text =
|
||||
bossbars_text.add_child(TextComponent::text(", ").add_child(bossbar_prefix(
|
||||
let mut bossbars_text = TextComponent::text("");
|
||||
for (i, bossbar) in bossbars.iter().enumerate() {
|
||||
if i == 0 {
|
||||
bossbars_text = bossbars_text.add_child(bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
bossbar.namespace.clone(),
|
||||
)));
|
||||
));
|
||||
} else {
|
||||
bossbars_text = bossbars_text.add_child(TextComponent::text(", ").add_child(
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
bossbar.namespace.clone(),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.list.bars.some",
|
||||
[
|
||||
TextComponent::text(bossbars.len().to_string()),
|
||||
bossbars_text,
|
||||
],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.list.bars.some",
|
||||
[
|
||||
TextComponent::text(bossbars.len().to_string()),
|
||||
bossbars_text,
|
||||
],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct RemoveExecuter;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for RemoveExecuter {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let namespace = autocomplete_consumer()
|
||||
.find_arg_default_name(args)?
|
||||
.to_string();
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let namespace = autocomplete_consumer()
|
||||
.find_arg_default_name(args)?
|
||||
.to_string();
|
||||
|
||||
let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else {
|
||||
handle_bossbar_error(
|
||||
sender,
|
||||
BossbarUpdateError::InvalidResourceLocation(namespace),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.remove.success",
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.clone(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.remove_bossbar(server, namespace.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else {
|
||||
handle_bossbar_error(
|
||||
sender,
|
||||
BossbarUpdateError::InvalidResourceLocation(namespace),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.remove.success",
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.clone(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.remove_bossbar(server, namespace.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct SetExecuter(CommandValueSet);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for SetExecuter {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let namespace = autocomplete_consumer().find_arg_default_name(args)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let namespace = autocomplete_consumer().find_arg_default_name(args)?;
|
||||
|
||||
let Some(bossbar) = server.bossbars.lock().await.get_bossbar(namespace) else {
|
||||
handle_bossbar_error(
|
||||
sender,
|
||||
BossbarUpdateError::InvalidResourceLocation(namespace.to_string()),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
let Some(bossbar) = server.bossbars.lock().await.get_bossbar(namespace) else {
|
||||
handle_bossbar_error(
|
||||
sender,
|
||||
BossbarUpdateError::InvalidResourceLocation(namespace.to_string()),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match self.0 {
|
||||
CommandValueSet::Color => {
|
||||
let color = BossbarColorArgumentConsumer.find_arg_default_name(args)?;
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_color(server, namespace.to_string(), color.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.color.success",
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Max => {
|
||||
let Ok(max_value) = max_value_consumer().find_arg_default_name(args)? else {
|
||||
send_error_message(
|
||||
sender,
|
||||
TextComponent::translate(
|
||||
"parsing.int.invalid",
|
||||
[TextComponent::text(i32::MAX.to_string())],
|
||||
),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_health(
|
||||
server,
|
||||
namespace.to_string(),
|
||||
max_value as u32,
|
||||
bossbar.value,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.max.success",
|
||||
[
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
),
|
||||
TextComponent::text(max_value.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Name => {
|
||||
let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?;
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_name(server, namespace, text_component.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.name.success",
|
||||
[bossbar_prefix(text_component, namespace.to_string())],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Players(has_players) => {
|
||||
if !has_players {
|
||||
match self.0 {
|
||||
CommandValueSet::Color => {
|
||||
let color = BossbarColorArgumentConsumer.find_arg_default_name(args)?;
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_players(server, namespace.to_string(), vec![])
|
||||
.update_color(server, namespace.to_string(), color.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
@@ -420,158 +332,255 @@ impl CommandExecutor for SetExecuter {
|
||||
}
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.players.success.none",
|
||||
"commands.bossbar.set.color.success",
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Max => {
|
||||
let Ok(max_value) = max_value_consumer().find_arg_default_name(args)? else {
|
||||
send_error_message(
|
||||
sender,
|
||||
TextComponent::translate(
|
||||
"parsing.int.invalid",
|
||||
[TextComponent::text(i32::MAX.to_string())],
|
||||
),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
//TODO: Confirm that this is the vanilla way
|
||||
let targets = PlayersArgumentConsumer.find_arg_default_name(args)?;
|
||||
let players: Vec<Uuid> =
|
||||
targets.iter().map(|player| player.gameprofile.id).collect();
|
||||
let count = players.len();
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_health(
|
||||
server,
|
||||
namespace.to_string(),
|
||||
max_value as u32,
|
||||
bossbar.value,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_players(server, namespace.to_string(), players)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.max.success",
|
||||
[
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
),
|
||||
TextComponent::text(max_value.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Name => {
|
||||
let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?;
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_name(server, namespace, text_component.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.name.success",
|
||||
[bossbar_prefix(text_component, namespace.to_string())],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Players(has_players) => {
|
||||
if !has_players {
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_players(server, namespace.to_string(), vec![])
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.players.success.none",
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
//TODO: Confirm that this is the vanilla way
|
||||
let targets = PlayersArgumentConsumer.find_arg_default_name(args)?;
|
||||
let players: Vec<Uuid> =
|
||||
targets.iter().map(|player| player.gameprofile.id).collect();
|
||||
let count = players.len();
|
||||
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_players(server, namespace.to_string(), players)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let player_names: Vec<String> = targets
|
||||
.iter()
|
||||
.map(|player| player.gameprofile.name.clone())
|
||||
.collect();
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.players.success.some",
|
||||
[
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
),
|
||||
TextComponent::text(count.to_string()),
|
||||
TextComponent::text(player_names.join(", ").clone()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let player_names: Vec<String> = targets
|
||||
.iter()
|
||||
.map(|player| player.gameprofile.name.clone())
|
||||
.collect();
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.players.success.some",
|
||||
[
|
||||
bossbar_prefix(
|
||||
CommandValueSet::Style => {
|
||||
let style = BossbarStyleArgumentConsumer.find_arg_default_name(args)?;
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_division(server, namespace.to_string(), style.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.style.success",
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Value => {
|
||||
let Ok(value) = value_consumer().find_arg_default_name(args)? else {
|
||||
send_error_message(
|
||||
sender,
|
||||
TextComponent::translate(
|
||||
"parsing.int.invalid",
|
||||
[TextComponent::text(i32::MAX.to_string())],
|
||||
),
|
||||
TextComponent::text(count.to_string()),
|
||||
TextComponent::text(player_names.join(", ").clone()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Style => {
|
||||
let style = BossbarStyleArgumentConsumer.find_arg_default_name(args)?;
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_division(server, namespace.to_string(), style.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.style.success",
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Value => {
|
||||
let Ok(value) = value_consumer().find_arg_default_name(args)? else {
|
||||
send_error_message(
|
||||
sender,
|
||||
TextComponent::translate(
|
||||
"parsing.int.invalid",
|
||||
[TextComponent::text(i32::MAX.to_string())],
|
||||
),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
};
|
||||
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_health(server, namespace.to_string(), bossbar.max, value as u32)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_health(server, namespace.to_string(), bossbar.max, value as u32)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.value.success",
|
||||
[
|
||||
bossbar_prefix(
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.set.value.success",
|
||||
[
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
),
|
||||
TextComponent::text(value.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Visible => {
|
||||
let visibility = BoolArgConsumer::find_arg(args, ARG_VISIBLE)?;
|
||||
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_visibility(server, namespace.to_string(), visibility)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let state = if visibility {
|
||||
"commands.bossbar.set.visible.success.visible"
|
||||
} else {
|
||||
"commands.bossbar.set.visible.success.hidden"
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
state,
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
),
|
||||
TextComponent::text(value.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
CommandValueSet::Visible => {
|
||||
let visibility = BoolArgConsumer::find_arg(args, ARG_VISIBLE)?;
|
||||
|
||||
match server
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.update_visibility(server, namespace.to_string(), visibility)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
handle_bossbar_error(sender, err).await;
|
||||
return Ok(());
|
||||
}
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let state = if visibility {
|
||||
"commands.bossbar.set.visible.success.visible"
|
||||
} else {
|
||||
"commands.bossbar.set.visible.success.hidden"
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
state,
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::color::NamedColor;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
@@ -9,7 +8,7 @@ use crate::command::args::players::PlayersArgumentConsumer;
|
||||
use crate::command::args::{Arg, ConsumedArgs};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, require};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
use CommandError::InvalidConsumption;
|
||||
@@ -71,57 +70,59 @@ async fn clear_command_text_output(item_count: u64, targets: &[Arc<Player>]) ->
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
let mut item_count = 0;
|
||||
for target in targets {
|
||||
item_count += clear_player(target).await;
|
||||
}
|
||||
let mut item_count = 0;
|
||||
for target in targets {
|
||||
item_count += clear_player(target).await;
|
||||
}
|
||||
|
||||
let msg = clear_command_text_output(item_count, targets).await;
|
||||
let msg = clear_command_text_output(item_count, targets).await;
|
||||
|
||||
sender.send_message(msg).await;
|
||||
sender.send_message(msg).await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct SelfExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for SelfExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?;
|
||||
|
||||
let item_count = clear_player(&target).await;
|
||||
let item_count = clear_player(&target).await;
|
||||
|
||||
let hold_target = [target];
|
||||
let msg = clear_command_text_output(item_count, &hold_target).await;
|
||||
let hold_target = [target];
|
||||
let msg = clear_command_text_output(item_count, &hold_target).await;
|
||||
|
||||
sender.send_message(msg).await;
|
||||
sender.send_message(msg).await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues
|
||||
// #[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues
|
||||
pub fn init_command_tree() -> CommandTree {
|
||||
CommandTree::new(NAMES, DESCRIPTION)
|
||||
.then(argument(ARG_TARGET, PlayersArgumentConsumer).execute(Executor))
|
||||
.then(require(|sender| sender.is_player()).execute(SelfExecutor))
|
||||
.then(require(super::super::CommandSender::is_player).execute(SelfExecutor))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::damage::DamageType;
|
||||
use pumpkin_util::text::{
|
||||
TextComponent,
|
||||
@@ -6,7 +5,7 @@ use pumpkin_util::text::{
|
||||
};
|
||||
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandExecutor, CommandResult, CommandSender,
|
||||
args::{
|
||||
Arg, ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer,
|
||||
entity::EntityArgumentConsumer, position_3d::Position3DArgumentConsumer,
|
||||
@@ -59,100 +58,104 @@ async fn send_damage_result(
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for LocationExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let target = EntityArgumentConsumer::find_arg(args, ARG_TARGET)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let target = EntityArgumentConsumer::find_arg(args, ARG_TARGET)?;
|
||||
|
||||
let Ok(Ok(amount)) = BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_AMOUNT) else {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Invalid damage amount")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
let Ok(Ok(amount)) = BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_AMOUNT)
|
||||
else {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Invalid damage amount")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let damage_type =
|
||||
args.get(ARG_DAMAGE_TYPE)
|
||||
.map_or(DamageType::GENERIC, |arg| match arg {
|
||||
Arg::DamageType(dt) => *dt,
|
||||
_ => DamageType::GENERIC,
|
||||
});
|
||||
|
||||
let location = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
|
||||
let success = target
|
||||
.damage_with_context(
|
||||
target.clone(),
|
||||
amount,
|
||||
damage_type,
|
||||
Some(location),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let damage_type = args
|
||||
.get(ARG_DAMAGE_TYPE)
|
||||
.map_or(DamageType::GENERIC, |arg| match arg {
|
||||
Arg::DamageType(dt) => *dt,
|
||||
_ => DamageType::GENERIC,
|
||||
});
|
||||
send_damage_result(sender, success, amount, target.get_display_name().await).await;
|
||||
|
||||
let location = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
|
||||
let success = target
|
||||
.damage_with_context(
|
||||
target.clone(),
|
||||
amount,
|
||||
damage_type,
|
||||
Some(location),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
send_damage_result(sender, success, amount, target.get_display_name().await).await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for EntityExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let target = EntityArgumentConsumer::find_arg(args, ARG_TARGET)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let target = EntityArgumentConsumer::find_arg(args, ARG_TARGET)?;
|
||||
|
||||
let Ok(Ok(amount)) = BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_AMOUNT) else {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Invalid damage amount")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
let Ok(Ok(amount)) = BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_AMOUNT)
|
||||
else {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Invalid damage amount")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let damage_type =
|
||||
args.get(ARG_DAMAGE_TYPE)
|
||||
.map_or(DamageType::GENERIC, |arg| match arg {
|
||||
Arg::DamageType(dt) => *dt,
|
||||
_ => DamageType::GENERIC,
|
||||
});
|
||||
|
||||
let source = EntityArgumentConsumer::find_arg(args, ARG_ENTITY).ok();
|
||||
let cause = if self.0 {
|
||||
EntityArgumentConsumer::find_arg(args, ARG_CAUSE).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let success = target
|
||||
.damage_with_context(
|
||||
target.clone(),
|
||||
amount,
|
||||
damage_type,
|
||||
None,
|
||||
source.as_ref().map(|e| e.as_ref() as &dyn EntityBase),
|
||||
cause.as_ref().map(|e| e.as_ref() as &dyn EntityBase),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let damage_type = args
|
||||
.get(ARG_DAMAGE_TYPE)
|
||||
.map_or(DamageType::GENERIC, |arg| match arg {
|
||||
Arg::DamageType(dt) => *dt,
|
||||
_ => DamageType::GENERIC,
|
||||
});
|
||||
send_damage_result(sender, success, amount, target.get_display_name().await).await;
|
||||
|
||||
let source = EntityArgumentConsumer::find_arg(args, ARG_ENTITY).ok();
|
||||
let cause = if self.0 {
|
||||
EntityArgumentConsumer::find_arg(args, ARG_CAUSE).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let success = target
|
||||
.damage_with_context(
|
||||
target.clone(),
|
||||
amount,
|
||||
damage_type,
|
||||
None,
|
||||
source.as_ref().map(|e| e.as_ref() as &dyn EntityBase),
|
||||
cause.as_ref().map(|e| e.as_ref() as &dyn EntityBase),
|
||||
)
|
||||
.await;
|
||||
|
||||
send_damage_result(sender, success, amount, target.get_display_name().await).await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::args::entity::EntityArgumentConsumer;
|
||||
use crate::command::tree::builder::literal;
|
||||
use crate::command::{
|
||||
@@ -7,7 +8,6 @@ use crate::command::{
|
||||
};
|
||||
use crate::entity::NBTStorage;
|
||||
use CommandError::InvalidConsumption;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
@@ -20,23 +20,24 @@ const ARG_ENTITY: &str = "entity";
|
||||
|
||||
struct GetEntityDataExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for GetEntityDataExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Entity(entity)) = args.get(&ARG_ENTITY) else {
|
||||
return Err(InvalidConsumption(Some(ARG_ENTITY.into())));
|
||||
};
|
||||
let data_storage = entity.as_nbt_storage();
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Entity(entity)) = args.get(&ARG_ENTITY) else {
|
||||
return Err(InvalidConsumption(Some(ARG_ENTITY.into())));
|
||||
};
|
||||
let data_storage = entity.as_nbt_storage();
|
||||
|
||||
sender
|
||||
.send_message(display_data(data_storage, entity.get_display_name().await).await?)
|
||||
.await;
|
||||
Ok(())
|
||||
sender
|
||||
.send_message(display_data(data_storage, entity.get_display_name().await).await?)
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::args::gamemode::GamemodeArgumentConsumer;
|
||||
use crate::command::args::{Arg, GetCloned};
|
||||
use crate::command::dispatcher::CommandError::InvalidConsumption;
|
||||
use crate::command::tree::builder::argument;
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use crate::command::{CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree};
|
||||
use pumpkin_config::BASIC_CONFIG;
|
||||
use pumpkin_util::GameMode;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
@@ -22,38 +20,39 @@ pub struct DefaultGamemode {
|
||||
|
||||
struct DefaultGamemodeExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for DefaultGamemodeExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_GAMEMODE.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_GAMEMODE.into())));
|
||||
};
|
||||
|
||||
if BASIC_CONFIG.force_gamemode {
|
||||
for player in server.get_all_players().await {
|
||||
player.set_gamemode(gamemode).await;
|
||||
if BASIC_CONFIG.force_gamemode {
|
||||
for player in server.get_all_players().await {
|
||||
player.set_gamemode(gamemode).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let gamemode_string = format!("{gamemode:?}").to_lowercase();
|
||||
let gamemode_string = format!("gameMode.{gamemode_string}");
|
||||
let gamemode_string = format!("{gamemode:?}").to_lowercase();
|
||||
let gamemode_string = format!("gameMode.{gamemode_string}");
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.defaultgamemode.success",
|
||||
[TextComponent::translate(gamemode_string, [])],
|
||||
))
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.defaultgamemode.success",
|
||||
[TextComponent::translate(gamemode_string, [])],
|
||||
))
|
||||
.await;
|
||||
|
||||
//Change the default gamemode (not in configuration.toml)
|
||||
server.defaultgamemode.lock().await.gamemode = gamemode;
|
||||
//Change the default gamemode (not in configuration.toml)
|
||||
server.defaultgamemode.lock().await.gamemode = gamemode;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::command::CommandResult;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::{
|
||||
command::{
|
||||
@@ -9,7 +10,6 @@ use crate::{
|
||||
data::{SaveJSONConfiguration, op_data::OPERATOR_CONFIG},
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const NAMES: [&str; 1] = ["deop"];
|
||||
@@ -18,44 +18,45 @@ const ARG_TARGETS: &str = "targets";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut config = OPERATOR_CONFIG.write().await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let mut config = OPERATOR_CONFIG.write().await;
|
||||
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
for player in targets {
|
||||
if let Some(op_index) = config
|
||||
.ops
|
||||
.iter()
|
||||
.position(|o| o.uuid == player.gameprofile.id)
|
||||
{
|
||||
config.ops.remove(op_index);
|
||||
}
|
||||
config.save();
|
||||
|
||||
{
|
||||
let command_dispatcher = server.command_dispatcher.read().await;
|
||||
player
|
||||
.set_permission_lvl(pumpkin_util::PermissionLvl::Zero, &command_dispatcher)
|
||||
.await;
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
let msg = TextComponent::translate(
|
||||
"commands.deop.success",
|
||||
[player.get_display_name().await],
|
||||
);
|
||||
sender.send_message(msg).await;
|
||||
}
|
||||
Ok(())
|
||||
for player in targets {
|
||||
if let Some(op_index) = config
|
||||
.ops
|
||||
.iter()
|
||||
.position(|o| o.uuid == player.gameprofile.id)
|
||||
{
|
||||
config.ops.remove(op_index);
|
||||
}
|
||||
config.save();
|
||||
|
||||
{
|
||||
let command_dispatcher = server.command_dispatcher.read().await;
|
||||
player
|
||||
.set_permission_lvl(pumpkin_util::PermissionLvl::Zero, &command_dispatcher)
|
||||
.await;
|
||||
};
|
||||
|
||||
let msg = TextComponent::translate(
|
||||
"commands.deop.success",
|
||||
[player.get_display_name().await],
|
||||
);
|
||||
sender.send_message(msg).await;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::args::difficulty::DifficultyArgumentConsumer;
|
||||
use crate::command::args::{Arg, GetCloned};
|
||||
use crate::command::dispatcher::CommandError::InvalidConsumption;
|
||||
use crate::command::tree::builder::argument;
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use crate::command::{CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree};
|
||||
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
@@ -16,45 +14,46 @@ const DESCRIPTION: &str = "Change the difficulty of the world.";
|
||||
pub const ARG_DIFFICULTY: &str = "difficulty";
|
||||
struct DifficultyExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for DifficultyExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Difficulty(difficulty)) = args.get_cloned(&ARG_DIFFICULTY) else {
|
||||
return Err(InvalidConsumption(Some(ARG_DIFFICULTY.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Difficulty(difficulty)) = args.get_cloned(&ARG_DIFFICULTY) else {
|
||||
return Err(InvalidConsumption(Some(ARG_DIFFICULTY.into())));
|
||||
};
|
||||
|
||||
let difficulty_string = format!("{difficulty:?}").to_lowercase();
|
||||
let translation_key = format!("options.difficulty.{difficulty_string}");
|
||||
let difficulty_string = format!("{difficulty:?}").to_lowercase();
|
||||
let translation_key = format!("options.difficulty.{difficulty_string}");
|
||||
|
||||
{
|
||||
let level_info = server.level_info.read().await;
|
||||
{
|
||||
let level_info = server.level_info.read().await;
|
||||
|
||||
if level_info.difficulty == difficulty {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.difficulty.failure",
|
||||
[TextComponent::translate(translation_key, [])],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
if level_info.difficulty == difficulty {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.difficulty.failure",
|
||||
[TextComponent::translate(translation_key, [])],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server.set_difficulty(difficulty, Some(true)).await;
|
||||
server.set_difficulty(difficulty, Some(true)).await;
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.difficulty.success",
|
||||
[TextComponent::translate(translation_key, [])],
|
||||
))
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.difficulty.success",
|
||||
[TextComponent::translate(translation_key, [])],
|
||||
))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,11 @@ use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
|
||||
use crate::command::args::players::PlayersArgumentConsumer;
|
||||
use crate::command::args::resource::effect::EffectTypeArgumentConsumer;
|
||||
use crate::command::args::{Arg, ConsumedArgs, FindArgDefaultName};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::dispatcher::CommandError::InvalidConsumption;
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, literal};
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandExecutor, CommandResult, CommandSender};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::potion::Effect;
|
||||
use pumpkin_util::text::color::{Color, NamedColor};
|
||||
|
||||
@@ -41,218 +38,223 @@ enum Amplifier {
|
||||
|
||||
struct GiveExecutor(Time, Amplifier, bool);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for GiveExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
let Some(Arg::Effect(effect)) = args.get(ARG_EFFECT) else {
|
||||
return Err(InvalidConsumption(Some(ARG_EFFECT.into())));
|
||||
};
|
||||
|
||||
//duration is in tick, so * 20 (not for the infinite because -1*20 cause visual glitch)
|
||||
let second = match self.0 {
|
||||
Time::Base => 30 * 20,
|
||||
Time::Specified => {
|
||||
BoundedNumArgumentConsumer::new()
|
||||
.name("seconds")
|
||||
.min(1)
|
||||
.max(1_000_000)
|
||||
.find_arg_default_name(args)??
|
||||
* 20
|
||||
}
|
||||
Time::Infinite => -1,
|
||||
};
|
||||
|
||||
let amplifier: i32 = match self.1 {
|
||||
Amplifier::Base => 0,
|
||||
Amplifier::Specified => BoundedNumArgumentConsumer::new()
|
||||
.name("amplifier")
|
||||
.min(0)
|
||||
.max(255)
|
||||
.find_arg_default_name(args)??,
|
||||
};
|
||||
|
||||
let mut hide_particles = self.2;
|
||||
//if false -> parameter is referred
|
||||
if !hide_particles {
|
||||
let Some(Arg::Bool(hide_particle)) = args.get(ARG_HIDE_PARTICLE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_HIDE_PARTICLE.into())));
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
let Some(Arg::Effect(effect)) = args.get(ARG_EFFECT) else {
|
||||
return Err(InvalidConsumption(Some(ARG_EFFECT.into())));
|
||||
};
|
||||
|
||||
hide_particles = *hide_particle;
|
||||
}
|
||||
//duration is in tick, so * 20 (not for the infinite because -1*20 cause visual glitch)
|
||||
let second = match self.0 {
|
||||
Time::Base => 30 * 20,
|
||||
Time::Specified => {
|
||||
BoundedNumArgumentConsumer::new()
|
||||
.name("seconds")
|
||||
.min(1)
|
||||
.max(1_000_000)
|
||||
.find_arg_default_name(args)??
|
||||
* 20
|
||||
}
|
||||
Time::Infinite => -1,
|
||||
};
|
||||
|
||||
let mut failed = 0;
|
||||
let amplifier: i32 = match self.1 {
|
||||
Amplifier::Base => 0,
|
||||
Amplifier::Specified => BoundedNumArgumentConsumer::new()
|
||||
.name("amplifier")
|
||||
.min(0)
|
||||
.max(255)
|
||||
.find_arg_default_name(args)??,
|
||||
};
|
||||
|
||||
for target in targets {
|
||||
if target.living_entity.has_effect(effect).await
|
||||
&& target
|
||||
.living_entity
|
||||
.get_effect(effect)
|
||||
.await
|
||||
.unwrap()
|
||||
.amplifier
|
||||
> amplifier as u8
|
||||
{
|
||||
failed += 1;
|
||||
let mut hide_particles = self.2;
|
||||
//if false -> parameter is referred
|
||||
if !hide_particles {
|
||||
let Some(Arg::Bool(hide_particle)) = args.get(ARG_HIDE_PARTICLE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_HIDE_PARTICLE.into())));
|
||||
};
|
||||
|
||||
hide_particles = *hide_particle;
|
||||
}
|
||||
|
||||
let mut failed = 0;
|
||||
|
||||
for target in targets {
|
||||
if target.living_entity.has_effect(effect).await
|
||||
&& target
|
||||
.living_entity
|
||||
.get_effect(effect)
|
||||
.await
|
||||
.unwrap()
|
||||
.amplifier
|
||||
> amplifier as u8
|
||||
{
|
||||
failed += 1;
|
||||
} else {
|
||||
target
|
||||
.add_effect(Effect {
|
||||
effect_type: effect,
|
||||
duration: second,
|
||||
amplifier: amplifier as u8,
|
||||
ambient: false, //this is not a beacon effect
|
||||
show_particles: hide_particles,
|
||||
show_icon: true,
|
||||
blend: true, //Currently only used in the DARKNESS effect to apply extra void fog and adjust the gamma value for lighting.
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
let translation_name = TextComponent::translate(effect.translation_key.to_string(), []);
|
||||
|
||||
if failed == targets.len() {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.effect.give.failed", [])
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
} else if targets.len() == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.give.success.single",
|
||||
[translation_name, targets[0].get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
target
|
||||
.add_effect(Effect {
|
||||
effect_type: effect,
|
||||
duration: second,
|
||||
amplifier: amplifier as u8,
|
||||
ambient: false, //this is not a beacon effect
|
||||
show_particles: hide_particles,
|
||||
show_icon: true,
|
||||
blend: true, //Currently only used in the DARKNESS effect to apply extra void fog and adjust the gamma value for lighting.
|
||||
})
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.give.success.multiple",
|
||||
[
|
||||
translation_name,
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
let translation_name = TextComponent::translate(effect.translation_key.to_string(), []);
|
||||
|
||||
if failed == targets.len() {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.effect.give.failed", [])
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
} else if targets.len() == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.give.success.single",
|
||||
[translation_name, targets[0].get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.give.success.multiple",
|
||||
[
|
||||
translation_name,
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ClearExecutor(bool); //the param -> true = delete every effect, false = only one
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ClearExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
let effect;
|
||||
//Only one effect
|
||||
if self.0 {
|
||||
let mut effect_number = 0;
|
||||
for target in targets {
|
||||
let effect_number_temp = target.remove_all_effect().await;
|
||||
if effect_number_temp > effect_number {
|
||||
effect_number = effect_number_temp;
|
||||
}
|
||||
}
|
||||
|
||||
//if the player or everyplayer don't have any effect
|
||||
if effect_number == 0 {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.effect.clear.everything.failed", [])
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
//a player have at least 1 effect
|
||||
else if targets.len() == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.clear.everything.success.single",
|
||||
[targets[0].get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.clear.everything.success.multiple",
|
||||
[TextComponent::text(targets.len().to_string())],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
let Some(Arg::Effect(effect_type)) = args.get(ARG_EFFECT) else {
|
||||
return Err(InvalidConsumption(Some(ARG_EFFECT.into())));
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
effect = *effect_type;
|
||||
let mut has_effect = vec![];
|
||||
|
||||
for target in targets {
|
||||
if !target.living_entity.has_effect(effect).await {
|
||||
target.remove_effect(effect).await;
|
||||
let effect;
|
||||
//Only one effect
|
||||
if self.0 {
|
||||
let mut effect_number = 0;
|
||||
for target in targets {
|
||||
let effect_number_temp = target.remove_all_effect().await;
|
||||
if effect_number_temp > effect_number {
|
||||
effect_number = effect_number_temp;
|
||||
}
|
||||
}
|
||||
has_effect.push(target.living_entity.has_effect(effect).await);
|
||||
}
|
||||
|
||||
if has_effect.contains(&false) {
|
||||
//contain false for 1 player == don't have
|
||||
if targets.len() == 1 || !has_effect.contains(&true) {
|
||||
//if the player or everyplayer don't have any effect
|
||||
if effect_number == 0 {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.effect.clear.specific.failed", [])
|
||||
TextComponent::translate("commands.effect.clear.everything.failed", [])
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
//true for 1 player = have the effect
|
||||
if targets.len() == 1 {
|
||||
//a player have at least 1 effect
|
||||
else if targets.len() == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.clear.specific.success.single",
|
||||
[
|
||||
TextComponent::translate(effect.translation_key, []),
|
||||
targets[0].get_display_name().await,
|
||||
],
|
||||
"commands.effect.clear.everything.success.single",
|
||||
[targets[0].get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.clear.everything.success.multiple",
|
||||
[TextComponent::text(targets.len().to_string())],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
//contain true for everyplayer = at least 1 player have the effect
|
||||
else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.clear.specific.success.multiple",
|
||||
[
|
||||
TextComponent::translate(effect.translation_key, []),
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
let Some(Arg::Effect(effect_type)) = args.get(ARG_EFFECT) else {
|
||||
return Err(InvalidConsumption(Some(ARG_EFFECT.into())));
|
||||
};
|
||||
|
||||
effect = *effect_type;
|
||||
let mut has_effect = vec![];
|
||||
|
||||
for target in targets {
|
||||
if !target.living_entity.has_effect(effect).await {
|
||||
target.remove_effect(effect).await;
|
||||
}
|
||||
has_effect.push(target.living_entity.has_effect(effect).await);
|
||||
}
|
||||
|
||||
if has_effect.contains(&false) {
|
||||
//contain false for 1 player == don't have
|
||||
if targets.len() == 1 || !has_effect.contains(&true) {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate(
|
||||
"commands.effect.clear.specific.failed",
|
||||
[],
|
||||
)
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
//true for 1 player = have the effect
|
||||
if targets.len() == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.clear.specific.success.single",
|
||||
[
|
||||
TextComponent::translate(effect.translation_key, []),
|
||||
targets[0].get_display_name().await,
|
||||
],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
//contain true for everyplayer = at least 1 player have the effect
|
||||
else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.effect.clear.specific.success.multiple",
|
||||
[
|
||||
TextComponent::translate(effect.translation_key, []),
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::args::bounded_num::{BoundedNumArgumentConsumer, NotInBounds};
|
||||
@@ -7,7 +6,7 @@ use crate::command::args::resource::enchantment::EnchantmentArgumentConsumer;
|
||||
use crate::command::args::{ConsumedArgs, FindArgDefaultName};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::argument_default_name;
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender};
|
||||
use pumpkin_data::data_component_impl::EnchantmentsImpl;
|
||||
|
||||
const NAMES: [&str; 1] = ["enchant"];
|
||||
@@ -15,134 +14,135 @@ const DESCRIPTION: &str = "Adds an enchantment to a player's selected item, subj
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = EntitiesArgumentConsumer.find_arg_default_name(args)?;
|
||||
let enchantment = EnchantmentArgumentConsumer.find_arg_default_name(args)?;
|
||||
let level = match enchantment_level_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 1,
|
||||
Ok(Ok(level)) => level,
|
||||
Ok(Err(err)) => {
|
||||
let err_msg = match err {
|
||||
NotInBounds::LowerBound(val, min) => TextComponent::translate(
|
||||
"argument.integer.low",
|
||||
&[
|
||||
TextComponent::text(min.to_string()),
|
||||
TextComponent::text(val.to_string()),
|
||||
],
|
||||
),
|
||||
NotInBounds::UpperBound(val, max) => TextComponent::translate(
|
||||
"argument.integer.big",
|
||||
&[
|
||||
TextComponent::text(max.to_string()),
|
||||
TextComponent::text(val.to_string()),
|
||||
],
|
||||
),
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = EntitiesArgumentConsumer.find_arg_default_name(args)?;
|
||||
let enchantment = EnchantmentArgumentConsumer.find_arg_default_name(args)?;
|
||||
let level = match enchantment_level_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 1,
|
||||
Ok(Ok(level)) => level,
|
||||
Ok(Err(err)) => {
|
||||
let err_msg = match err {
|
||||
NotInBounds::LowerBound(val, min) => TextComponent::translate(
|
||||
"argument.integer.low",
|
||||
&[
|
||||
TextComponent::text(min.to_string()),
|
||||
TextComponent::text(val.to_string()),
|
||||
],
|
||||
),
|
||||
NotInBounds::UpperBound(val, max) => TextComponent::translate(
|
||||
"argument.integer.big",
|
||||
&[
|
||||
TextComponent::text(max.to_string()),
|
||||
TextComponent::text(val.to_string()),
|
||||
],
|
||||
),
|
||||
};
|
||||
|
||||
return Err(CommandError::CommandFailed(Box::new(err_msg)));
|
||||
}
|
||||
};
|
||||
|
||||
if level > enchantment.max_level {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.failed.level",
|
||||
[
|
||||
TextComponent::text(level.to_string()),
|
||||
TextComponent::text(enchantment.max_level.to_string()),
|
||||
],
|
||||
);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
|
||||
let only_one = targets.len() == 1;
|
||||
let mut success = 0;
|
||||
|
||||
for target in targets {
|
||||
// let Some(target) = target.get_living_entity() else {
|
||||
// if only_one {
|
||||
// let msg = TextComponent::translate(
|
||||
// "commands.enchant.failed.entity",
|
||||
// [targets[0].get_display_name().await],
|
||||
// );
|
||||
// sender.send_message(msg).await;
|
||||
// return Ok(());
|
||||
// }
|
||||
// continue;
|
||||
// };
|
||||
// let lock = target.entity_equipment.lock().await.get(&EquipmentSlot::MAIN_HAND); TODO this dont work
|
||||
let Some(player) = target.get_player() else {
|
||||
todo!()
|
||||
return Err(CommandError::CommandFailed(Box::new(err_msg)));
|
||||
}
|
||||
};
|
||||
let lock = player.inventory.held_item();
|
||||
let mut item = lock.lock().await;
|
||||
if item.is_empty() {
|
||||
if only_one {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.failed.itemless",
|
||||
[targets[0].get_display_name().await],
|
||||
);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
continue;
|
||||
|
||||
if level > enchantment.max_level {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.failed.level",
|
||||
[
|
||||
TextComponent::text(level.to_string()),
|
||||
TextComponent::text(enchantment.max_level.to_string()),
|
||||
],
|
||||
);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
if !enchantment.can_enchant(item.item) {
|
||||
if only_one {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.failed.incompatible",
|
||||
[item.item.translated_name()],
|
||||
);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
|
||||
let only_one = targets.len() == 1;
|
||||
let mut success = 0;
|
||||
|
||||
for target in targets {
|
||||
// let Some(target) = target.get_living_entity() else {
|
||||
// if only_one {
|
||||
// let msg = TextComponent::translate(
|
||||
// "commands.enchant.failed.entity",
|
||||
// [targets[0].get_display_name().await],
|
||||
// );
|
||||
// sender.send_message(msg).await;
|
||||
// return Ok(());
|
||||
// }
|
||||
// continue;
|
||||
// };
|
||||
// let lock = target.entity_equipment.lock().await.get(&EquipmentSlot::MAIN_HAND); TODO this dont work
|
||||
let Some(player) = target.get_player() else {
|
||||
todo!()
|
||||
};
|
||||
let lock = player.inventory.held_item();
|
||||
let mut item = lock.lock().await;
|
||||
if item.is_empty() {
|
||||
if only_one {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.failed.itemless",
|
||||
[targets[0].get_display_name().await],
|
||||
);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if let Some(data) = item.get_data_component::<EnchantmentsImpl>() {
|
||||
if enchantment.is_enchantment_compatible(data) {
|
||||
if !enchantment.can_enchant(item.item) {
|
||||
if only_one {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.failed.incompatible",
|
||||
[item.item.translated_name()],
|
||||
);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if let Some(data) = item.get_data_component::<EnchantmentsImpl>() {
|
||||
if enchantment.is_enchantment_compatible(data) {
|
||||
item.enchant(enchantment, level);
|
||||
success += 1;
|
||||
} else if only_one {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.failed.incompatible",
|
||||
[item.item.translated_name()],
|
||||
);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
} else {
|
||||
item.enchant(enchantment, level);
|
||||
success += 1;
|
||||
} else if only_one {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.failed.incompatible",
|
||||
[item.item.translated_name()],
|
||||
);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
} else {
|
||||
item.enchant(enchantment, level);
|
||||
success += 1;
|
||||
}
|
||||
}
|
||||
if success == 0 {
|
||||
let msg = TextComponent::translate("commands.enchant.failed", []);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
if only_one {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.success.single",
|
||||
[
|
||||
enchantment.get_fullname(level),
|
||||
targets[0].get_display_name().await,
|
||||
],
|
||||
);
|
||||
sender.send_message(msg).await;
|
||||
} else {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.success.multiple",
|
||||
[
|
||||
enchantment.get_fullname(level),
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
);
|
||||
sender.send_message(msg).await;
|
||||
}
|
||||
Ok(())
|
||||
if success == 0 {
|
||||
let msg = TextComponent::translate("commands.enchant.failed", []);
|
||||
return Err(CommandError::CommandFailed(Box::new(msg)));
|
||||
}
|
||||
if only_one {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.success.single",
|
||||
[
|
||||
enchantment.get_fullname(level),
|
||||
targets[0].get_display_name().await,
|
||||
],
|
||||
);
|
||||
sender.send_message(msg).await;
|
||||
} else {
|
||||
let msg = TextComponent::translate(
|
||||
"commands.enchant.success.multiple",
|
||||
[
|
||||
enchantment.get_fullname(level),
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
);
|
||||
sender.send_message(msg).await;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::math::experience;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::color::{Color, NamedColor};
|
||||
@@ -10,7 +9,7 @@ use crate::command::args::players::PlayersArgumentConsumer;
|
||||
use crate::command::args::{ConsumedArgs, FindArg};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, literal};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandExecutor, CommandResult, CommandSender};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
|
||||
@@ -196,76 +195,77 @@ impl Executor {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
match self.mode {
|
||||
Mode::Query => {
|
||||
if targets.len() != 1 {
|
||||
// TODO: Add proper error message for multiple players in query mode
|
||||
return Ok(());
|
||||
}
|
||||
self.handle_query(sender, &targets[0], self.exp_type.unwrap())
|
||||
.await;
|
||||
}
|
||||
Mode::Add | Mode::Set => {
|
||||
let Ok(amount) = BoundedNumArgumentConsumer::<i32>::find_arg(args, ARG_AMOUNT)?
|
||||
else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.experience.set.points.invalid",
|
||||
[],
|
||||
))
|
||||
match self.mode {
|
||||
Mode::Query => {
|
||||
if targets.len() != 1 {
|
||||
// TODO: Add proper error message for multiple players in query mode
|
||||
return Ok(());
|
||||
}
|
||||
self.handle_query(sender, &targets[0], self.exp_type.unwrap())
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if self.mode == Mode::Set && amount < 0 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.experience.set.points.invalid",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
Mode::Add | Mode::Set => {
|
||||
let Ok(amount) = BoundedNumArgumentConsumer::<i32>::find_arg(args, ARG_AMOUNT)?
|
||||
else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.experience.set.points.invalid",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
for target in targets {
|
||||
match self
|
||||
.handle_modify(target, amount, self.exp_type.unwrap(), self.mode)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
let msg = Self::get_success_message(
|
||||
self.mode,
|
||||
self.exp_type.unwrap(),
|
||||
amount,
|
||||
targets.len(),
|
||||
Some(target.get_display_name().await),
|
||||
);
|
||||
sender.send_message(msg).await;
|
||||
}
|
||||
Err(error_msg) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate(error_msg, [])
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
if self.mode == Mode::Set && amount < 0 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.experience.set.points.invalid",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for target in targets {
|
||||
match self
|
||||
.handle_modify(target, amount, self.exp_type.unwrap(), self.mode)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
let msg = Self::get_success_message(
|
||||
self.mode,
|
||||
self.exp_type.unwrap(),
|
||||
amount,
|
||||
targets.len(),
|
||||
Some(target.get_display_name().await),
|
||||
);
|
||||
sender.send_message(msg).await;
|
||||
}
|
||||
Err(error_msg) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate(error_msg, [])
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@ use crate::command::args::position_block::BlockPosArgumentConsumer;
|
||||
use crate::command::args::{ConsumedArgs, FindArg};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, literal};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
@@ -50,94 +49,69 @@ fn not_in_filter(filter: &BlockPredicate, old_block: &Block) -> bool {
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
|
||||
let block_state_id = block.default_state.id;
|
||||
let from = BlockPosArgumentConsumer::find_arg(args, ARG_FROM)?;
|
||||
let to = BlockPosArgumentConsumer::find_arg(args, ARG_TO)?;
|
||||
let option_filter = BlockPredicateArgumentConsumer::find_arg(args, ARG_FILTER)?;
|
||||
let mode = self.0;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
|
||||
let block_state_id = block.default_state.id;
|
||||
let from = BlockPosArgumentConsumer::find_arg(args, ARG_FROM)?;
|
||||
let to = BlockPosArgumentConsumer::find_arg(args, ARG_TO)?;
|
||||
let option_filter = BlockPredicateArgumentConsumer::find_arg(args, ARG_FILTER)?;
|
||||
let mode = self.0;
|
||||
|
||||
let start_x = from.0.x.min(to.0.x);
|
||||
let start_y = from.0.y.min(to.0.y);
|
||||
let start_z = from.0.z.min(to.0.z);
|
||||
let start_x = from.0.x.min(to.0.x);
|
||||
let start_y = from.0.y.min(to.0.y);
|
||||
let start_z = from.0.z.min(to.0.z);
|
||||
|
||||
let end_x = from.0.x.max(to.0.x);
|
||||
let end_y = from.0.y.max(to.0.y);
|
||||
let end_z = from.0.z.max(to.0.z);
|
||||
// TODO: check isInWorldBounds and throw argument.pos.outofbounds
|
||||
let end_x = from.0.x.max(to.0.x);
|
||||
let end_y = from.0.y.max(to.0.y);
|
||||
let end_z = from.0.z.max(to.0.z);
|
||||
// TODO: check isInWorldBounds and throw argument.pos.outofbounds
|
||||
|
||||
let world = sender.world().ok_or(CommandError::InvalidRequirement)?;
|
||||
let mut placed_blocks = 0;
|
||||
let mut to_update = Vec::new();
|
||||
match mode {
|
||||
Mode::Destroy => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
let world = sender.world().ok_or(CommandError::InvalidRequirement)?;
|
||||
let mut placed_blocks = 0;
|
||||
let mut to_update = Vec::new();
|
||||
match mode {
|
||||
Mode::Destroy => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
world
|
||||
.break_block(
|
||||
&block_position,
|
||||
None,
|
||||
BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
placed_blocks += 1;
|
||||
to_update.push(block_position);
|
||||
}
|
||||
world
|
||||
.break_block(
|
||||
&block_position,
|
||||
None,
|
||||
BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
placed_blocks += 1;
|
||||
to_update.push(block_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Replace => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
placed_blocks += 1;
|
||||
to_update.push(block_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Keep => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
let old_state = world.get_block_state(&block_position).await;
|
||||
if old_state.is_air() {
|
||||
Mode::Replace => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
@@ -156,24 +130,93 @@ impl CommandExecutor for Executor {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Hollow => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
let is_edge = x == start_x
|
||||
|| x == end_x
|
||||
|| y == start_y
|
||||
|| y == end_y
|
||||
|| z == start_z
|
||||
|| z == end_z;
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
Mode::Keep => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
let old_state = world.get_block_state(&block_position).await;
|
||||
if old_state.is_air() {
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(
|
||||
filter,
|
||||
world.get_block(&block_position).await,
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
placed_blocks += 1;
|
||||
to_update.push(block_position);
|
||||
}
|
||||
}
|
||||
if is_edge {
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Hollow => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
let is_edge = x == start_x
|
||||
|| x == end_x
|
||||
|| y == start_y
|
||||
|| y == end_y
|
||||
|| z == start_z
|
||||
|| z == end_z;
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if is_edge {
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
0,
|
||||
BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
placed_blocks += 1;
|
||||
to_update.push(block_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Outline => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
let is_edge = x == start_x
|
||||
|| x == end_x
|
||||
|| y == start_y
|
||||
|| y == end_y
|
||||
|| z == start_z
|
||||
|| z == end_z;
|
||||
if !is_edge {
|
||||
continue;
|
||||
}
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
@@ -181,85 +224,49 @@ impl CommandExecutor for Executor {
|
||||
BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
placed_blocks += 1;
|
||||
to_update.push(block_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Strict => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
world
|
||||
.set_block_state(&block_position, 0, BlockFlags::FORCE_STATE)
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
block_state_id,
|
||||
BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
|
||||
)
|
||||
.await;
|
||||
placed_blocks += 1;
|
||||
}
|
||||
placed_blocks += 1;
|
||||
to_update.push(block_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Outline => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
let is_edge = x == start_x
|
||||
|| x == end_x
|
||||
|| y == start_y
|
||||
|| y == end_y
|
||||
|| z == start_z
|
||||
|| z == end_z;
|
||||
if !is_edge {
|
||||
continue;
|
||||
}
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE,
|
||||
)
|
||||
.await;
|
||||
placed_blocks += 1;
|
||||
to_update.push(block_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in to_update {
|
||||
world.update_neighbors(&i, None).await;
|
||||
}
|
||||
Mode::Strict => {
|
||||
for x in start_x..=end_x {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3::new(x, y, z));
|
||||
if let Some(filter) = &option_filter
|
||||
&& not_in_filter(filter, world.get_block(&block_position).await)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
world
|
||||
.set_block_state(
|
||||
&block_position,
|
||||
block_state_id,
|
||||
BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
|
||||
)
|
||||
.await;
|
||||
placed_blocks += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in to_update {
|
||||
world.update_neighbors(&i, None).await;
|
||||
}
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.fill.success",
|
||||
[TextComponent::text(placed_blocks.to_string())],
|
||||
))
|
||||
.await;
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.fill.success",
|
||||
[TextComponent::text(placed_blocks.to_string())],
|
||||
))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::command::args::GetCloned;
|
||||
use crate::command::args::gamemode::GamemodeArgumentConsumer;
|
||||
|
||||
@@ -9,13 +7,11 @@ use crate::command::args::players::PlayersArgumentConsumer;
|
||||
|
||||
use crate::command::CommandSender::Player;
|
||||
use crate::command::args::{Arg, ConsumedArgs};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::dispatcher::CommandError::{InvalidConsumption, InvalidRequirement};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, require};
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandExecutor, CommandResult, CommandSender};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::server::Server;
|
||||
|
||||
const NAMES: [&str; 1] = ["gamemode"];
|
||||
|
||||
@@ -26,82 +22,84 @@ const ARG_TARGET: &str = "target";
|
||||
|
||||
struct TargetSelfExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TargetSelfExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_GAMEMODE.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_GAMEMODE.into())));
|
||||
};
|
||||
|
||||
if let Player(target) = sender {
|
||||
if target.gamemode.load() != gamemode {
|
||||
target.set_gamemode(gamemode).await;
|
||||
let gamemode_string = format!("{gamemode:?}").to_lowercase();
|
||||
let gamemode_string = format!("gameMode.{gamemode_string}");
|
||||
target
|
||||
.send_system_message(&TextComponent::translate(
|
||||
"commands.gamemode.success.self",
|
||||
[TextComponent::translate(gamemode_string, [])],
|
||||
))
|
||||
.await;
|
||||
if let Player(target) = sender {
|
||||
if target.gamemode.load() != gamemode {
|
||||
target.set_gamemode(gamemode).await;
|
||||
let gamemode_string = format!("{gamemode:?}").to_lowercase();
|
||||
let gamemode_string = format!("gameMode.{gamemode_string}");
|
||||
target
|
||||
.send_system_message(&TextComponent::translate(
|
||||
"commands.gamemode.success.self",
|
||||
[TextComponent::translate(gamemode_string, [])],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InvalidRequirement)
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InvalidRequirement)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct TargetPlayerExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TargetPlayerExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_GAMEMODE.into())));
|
||||
};
|
||||
let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_GAMEMODE.into())));
|
||||
};
|
||||
let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
let target_count = targets.len();
|
||||
let target_count = targets.len();
|
||||
|
||||
for target in targets {
|
||||
if target.gamemode.load() != gamemode {
|
||||
target.set_gamemode(gamemode).await;
|
||||
let gamemode_string = format!("{gamemode:?}").to_lowercase();
|
||||
let gamemode_string = format!("gameMode.{gamemode_string}");
|
||||
target
|
||||
.send_system_message(&TextComponent::translate(
|
||||
"gameMode.changed",
|
||||
[TextComponent::translate(gamemode_string.clone(), [])],
|
||||
))
|
||||
.await;
|
||||
if target_count == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.gamemode.success.other",
|
||||
[
|
||||
target.get_display_name().await,
|
||||
TextComponent::translate(gamemode_string, []),
|
||||
],
|
||||
for target in targets {
|
||||
if target.gamemode.load() != gamemode {
|
||||
target.set_gamemode(gamemode).await;
|
||||
let gamemode_string = format!("{gamemode:?}").to_lowercase();
|
||||
let gamemode_string = format!("gameMode.{gamemode_string}");
|
||||
target
|
||||
.send_system_message(&TextComponent::translate(
|
||||
"gameMode.changed",
|
||||
[TextComponent::translate(gamemode_string.clone(), [])],
|
||||
))
|
||||
.await;
|
||||
if target_count == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.gamemode.success.other",
|
||||
[
|
||||
target.get_display_name().await,
|
||||
TextComponent::translate(gamemode_string, []),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::game_rules::{GameRule, GameRuleRegistry, GameRuleValue};
|
||||
|
||||
use crate::command::args::FindArg;
|
||||
@@ -8,11 +7,9 @@ use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
|
||||
use crate::TextComponent;
|
||||
|
||||
use crate::command::args::ConsumedArgs;
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, literal};
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use crate::server::Server;
|
||||
use crate::command::{CommandExecutor, CommandResult, CommandSender};
|
||||
|
||||
const NAMES: [&str; 1] = ["gamerule"];
|
||||
|
||||
@@ -22,64 +19,66 @@ const ARG_NAME: &str = "value";
|
||||
|
||||
struct QueryExecutor(GameRule);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for QueryExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let key = TextComponent::text(self.0.to_string());
|
||||
let level_info = server.level_info.read().await;
|
||||
let value = TextComponent::text(level_info.game_rules.get(&self.0).to_string());
|
||||
drop(level_info);
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let key = TextComponent::text(self.0.to_string());
|
||||
let level_info = server.level_info.read().await;
|
||||
let value = TextComponent::text(level_info.game_rules.get(&self.0).to_string());
|
||||
drop(level_info);
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.gamerule.query",
|
||||
[key, value],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.gamerule.query",
|
||||
[key, value],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct SetExecutor(GameRule);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for SetExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let key = TextComponent::text(self.0.to_string());
|
||||
let mut level_info = server.level_info.write().await;
|
||||
let raw_value = level_info.game_rules.get_mut(&self.0);
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let key = TextComponent::text(self.0.to_string());
|
||||
let mut level_info = server.level_info.write().await;
|
||||
let raw_value = level_info.game_rules.get_mut(&self.0);
|
||||
|
||||
let value = TextComponent::text(match raw_value {
|
||||
GameRuleValue::Int(value) => {
|
||||
let arg_value = BoundedNumArgumentConsumer::<i64>::find_arg(args, ARG_NAME)??;
|
||||
*value = arg_value;
|
||||
arg_value.to_string()
|
||||
}
|
||||
GameRuleValue::Bool(value) => {
|
||||
let arg_value = BoolArgConsumer::find_arg(args, ARG_NAME)?;
|
||||
*value = arg_value;
|
||||
arg_value.to_string()
|
||||
}
|
||||
});
|
||||
drop(level_info);
|
||||
let value = TextComponent::text(match raw_value {
|
||||
GameRuleValue::Int(value) => {
|
||||
let arg_value = BoundedNumArgumentConsumer::<i64>::find_arg(args, ARG_NAME)??;
|
||||
*value = arg_value;
|
||||
arg_value.to_string()
|
||||
}
|
||||
GameRuleValue::Bool(value) => {
|
||||
let arg_value = BoolArgConsumer::find_arg(args, ARG_NAME)?;
|
||||
*value = arg_value;
|
||||
arg_value.to_string()
|
||||
}
|
||||
});
|
||||
drop(level_info);
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.gamerule.set",
|
||||
[key, value],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.gamerule.set",
|
||||
[key, value],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::data_component::DataComponent::MaxStackSize;
|
||||
use pumpkin_data::data_component_impl::{MaxStackSizeImpl, get};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
@@ -12,7 +11,7 @@ use crate::command::args::resource::item::ItemArgumentConsumer;
|
||||
use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, argument_default_name};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandExecutor, CommandResult, CommandSender};
|
||||
use crate::entity::EntityBase;
|
||||
|
||||
const NAMES: [&str; 1] = ["give"];
|
||||
@@ -30,93 +29,96 @@ fn item_count_consumer() -> BoundedNumArgumentConsumer<i32> {
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = PlayersArgumentConsumer.find_arg_default_name(args)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = PlayersArgumentConsumer.find_arg_default_name(args)?;
|
||||
|
||||
let (item_name, item) = ItemArgumentConsumer::find_arg(args, ARG_ITEM)?;
|
||||
let (item_name, item) = ItemArgumentConsumer::find_arg(args, ARG_ITEM)?;
|
||||
|
||||
let item_count = match item_count_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 1,
|
||||
Ok(Ok(count)) => count,
|
||||
Ok(Err(err)) => {
|
||||
let err_msg = match err {
|
||||
NotInBounds::LowerBound(_, min) => {
|
||||
format!("Can't give less than {min} of {item_name}")
|
||||
}
|
||||
NotInBounds::UpperBound(_, max) => {
|
||||
format!("Can't give more than {max} of {item_name}")
|
||||
}
|
||||
};
|
||||
let item_count = match item_count_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 1,
|
||||
Ok(Ok(count)) => count,
|
||||
Ok(Err(err)) => {
|
||||
let err_msg = match err {
|
||||
NotInBounds::LowerBound(_, min) => {
|
||||
format!("Can't give less than {min} of {item_name}")
|
||||
}
|
||||
NotInBounds::UpperBound(_, max) => {
|
||||
format!("Can't give more than {max} of {item_name}")
|
||||
}
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::text(err_msg).color(Color::Named(NamedColor::Red)))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
for target in targets {
|
||||
let max_stack = i32::from(
|
||||
item.components
|
||||
.iter()
|
||||
.find_map(|(id, component)| {
|
||||
(id == &MaxStackSize).then(|| get::<MaxStackSizeImpl>(*component).size)
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
let mut remaining = item_count;
|
||||
|
||||
while remaining > 0 {
|
||||
let take = remaining.min(max_stack);
|
||||
let mut stack = ItemStack::new(take as u8, item);
|
||||
target.inventory().insert_stack_anywhere(&mut stack).await;
|
||||
if !stack.is_empty() {
|
||||
target.drop_item(stack).await;
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(err_msg).color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
remaining -= take;
|
||||
}
|
||||
}
|
||||
let msg = if targets.len() == 1 {
|
||||
TextComponent::translate(
|
||||
"commands.give.success.single",
|
||||
[
|
||||
TextComponent::text(item_count.to_string()),
|
||||
TextComponent::text("[")
|
||||
.add_child(item.translated_name())
|
||||
.add_child(TextComponent::text("]"))
|
||||
.hover_event(HoverEvent::ShowItem {
|
||||
id: item_name.to_string().into(),
|
||||
count: Some(item_count),
|
||||
}),
|
||||
targets[0].get_display_name().await,
|
||||
],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate(
|
||||
"commands.give.success.multiple",
|
||||
[
|
||||
TextComponent::text(item_count.to_string()),
|
||||
TextComponent::text("[")
|
||||
.add_child(item.translated_name())
|
||||
.add_child(TextComponent::text("]"))
|
||||
.hover_event(HoverEvent::ShowItem {
|
||||
id: item_name.to_string().into(),
|
||||
count: Some(item_count),
|
||||
}),
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
)
|
||||
};
|
||||
sender.send_message(msg).await;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
for target in targets {
|
||||
let max_stack = i32::from(
|
||||
item.components
|
||||
.iter()
|
||||
.find_map(|(id, component)| {
|
||||
(id == &MaxStackSize).then(|| get::<MaxStackSizeImpl>(*component).size)
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
let mut remaining = item_count;
|
||||
|
||||
while remaining > 0 {
|
||||
let take = remaining.min(max_stack);
|
||||
let mut stack = ItemStack::new(take as u8, item);
|
||||
target.inventory().insert_stack_anywhere(&mut stack).await;
|
||||
if !stack.is_empty() {
|
||||
target.drop_item(stack).await;
|
||||
}
|
||||
remaining -= take;
|
||||
}
|
||||
}
|
||||
let msg = if targets.len() == 1 {
|
||||
TextComponent::translate(
|
||||
"commands.give.success.single",
|
||||
[
|
||||
TextComponent::text(item_count.to_string()),
|
||||
TextComponent::text("[")
|
||||
.add_child(item.translated_name())
|
||||
.add_child(TextComponent::text("]"))
|
||||
.hover_event(HoverEvent::ShowItem {
|
||||
id: item_name.to_string().into(),
|
||||
count: Some(item_count),
|
||||
}),
|
||||
targets[0].get_display_name().await,
|
||||
],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate(
|
||||
"commands.give.success.multiple",
|
||||
[
|
||||
TextComponent::text(item_count.to_string()),
|
||||
TextComponent::text("[")
|
||||
.add_child(item.translated_name())
|
||||
.add_child(TextComponent::text("]"))
|
||||
.hover_event(HoverEvent::ShowItem {
|
||||
id: item_name.to_string().into(),
|
||||
count: Some(item_count),
|
||||
}),
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
)
|
||||
};
|
||||
sender.send_message(msg).await;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::click::ClickEvent;
|
||||
@@ -7,12 +6,10 @@ use pumpkin_util::text::color::{Color, NamedColor};
|
||||
use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
|
||||
use crate::command::args::command::CommandTreeArgumentConsumer;
|
||||
use crate::command::args::{Arg, ConsumedArgs, FindArgDefaultName};
|
||||
use crate::command::dispatcher::CommandError;
|
||||
use crate::command::dispatcher::CommandError::InvalidConsumption;
|
||||
use crate::command::tree::builder::{argument, argument_default_name};
|
||||
use crate::command::tree::{Command, CommandTree};
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use crate::server::Server;
|
||||
use crate::command::{CommandExecutor, CommandResult, CommandSender};
|
||||
|
||||
const NAMES: [&str; 3] = ["help", "h", "?"];
|
||||
|
||||
@@ -28,207 +25,214 @@ fn page_number_consumer() -> BoundedNumArgumentConsumer<i32> {
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::CommandTree(tree)) = args.get(&ARG_COMMAND) else {
|
||||
return Err(InvalidConsumption(Some(ARG_COMMAND.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::CommandTree(tree)) = args.get(&ARG_COMMAND) else {
|
||||
return Err(InvalidConsumption(Some(ARG_COMMAND.into())));
|
||||
};
|
||||
|
||||
let command_names = tree.names.join(", /");
|
||||
let usage = format!("{tree}");
|
||||
let description = &tree.description;
|
||||
let command_names = tree.names.join(", /");
|
||||
let usage = format!("{tree}");
|
||||
let description = &tree.description;
|
||||
|
||||
let header_text = format!(" Help - /{} ", tree.names[0]);
|
||||
let header_text = format!(" Help - /{} ", tree.names[0]);
|
||||
|
||||
let mut message = TextComponent::text("")
|
||||
.add_child(
|
||||
TextComponent::text("-".repeat((52 - header_text.len()) / 2) + " ")
|
||||
.color_named(NamedColor::Yellow),
|
||||
)
|
||||
.add_child(TextComponent::text(header_text.clone()))
|
||||
.add_child(
|
||||
TextComponent::text(
|
||||
" ".to_owned() + &"-".repeat((52 - header_text.len()) / 2) + "\n",
|
||||
let mut message = TextComponent::text("")
|
||||
.add_child(
|
||||
TextComponent::text("-".repeat((52 - header_text.len()) / 2) + " ")
|
||||
.color_named(NamedColor::Yellow),
|
||||
)
|
||||
.color_named(NamedColor::Yellow),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::text("Command: ")
|
||||
.color_named(NamedColor::Aqua)
|
||||
.add_child(
|
||||
TextComponent::text(format!("/{command_names}"))
|
||||
.color_named(NamedColor::Gold)
|
||||
.bold(),
|
||||
.add_child(TextComponent::text(header_text.clone()))
|
||||
.add_child(
|
||||
TextComponent::text(
|
||||
" ".to_owned() + &"-".repeat((52 - header_text.len()) / 2) + "\n",
|
||||
)
|
||||
.add_child(TextComponent::text("\n").color_named(NamedColor::White))
|
||||
.click_event(ClickEvent::SuggestCommand {
|
||||
command: format!("/{}", tree.names[0]).into(),
|
||||
}),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::text("Description: ")
|
||||
.color_named(NamedColor::Aqua)
|
||||
.add_child(
|
||||
TextComponent::text(format!("{description}\n"))
|
||||
.color_named(NamedColor::White),
|
||||
),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::text("Usage: ")
|
||||
.color_named(NamedColor::Aqua)
|
||||
.add_child(
|
||||
TextComponent::text(format!("{usage}\n")).color_named(NamedColor::White),
|
||||
)
|
||||
.click_event(ClickEvent::SuggestCommand {
|
||||
command: format!("{tree}").into(),
|
||||
}),
|
||||
);
|
||||
.color_named(NamedColor::Yellow),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::text("Command: ")
|
||||
.color_named(NamedColor::Aqua)
|
||||
.add_child(
|
||||
TextComponent::text(format!("/{command_names}"))
|
||||
.color_named(NamedColor::Gold)
|
||||
.bold(),
|
||||
)
|
||||
.add_child(TextComponent::text("\n").color_named(NamedColor::White))
|
||||
.click_event(ClickEvent::SuggestCommand {
|
||||
command: format!("/{}", tree.names[0]).into(),
|
||||
}),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::text("Description: ")
|
||||
.color_named(NamedColor::Aqua)
|
||||
.add_child(
|
||||
TextComponent::text(format!("{description}\n"))
|
||||
.color_named(NamedColor::White),
|
||||
),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::text("Usage: ")
|
||||
.color_named(NamedColor::Aqua)
|
||||
.add_child(
|
||||
TextComponent::text(format!("{usage}\n"))
|
||||
.color_named(NamedColor::White),
|
||||
)
|
||||
.click_event(ClickEvent::SuggestCommand {
|
||||
command: format!("{tree}").into(),
|
||||
}),
|
||||
);
|
||||
|
||||
message =
|
||||
message.add_child(TextComponent::text("-".repeat(52)).color_named(NamedColor::Yellow));
|
||||
message = message
|
||||
.add_child(TextComponent::text("-".repeat(52)).color_named(NamedColor::Yellow));
|
||||
|
||||
sender.send_message(message).await;
|
||||
sender.send_message(message).await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct BaseHelpExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for BaseHelpExecutor {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let page_number = match page_number_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 1,
|
||||
Ok(Ok(number)) => number,
|
||||
Ok(Err(_)) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Invalid page number.")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let dispatcher = server.command_dispatcher.read().await;
|
||||
let commands: Vec<&CommandTree> = dispatcher
|
||||
.commands
|
||||
.values()
|
||||
.filter_map(|cmd| match cmd {
|
||||
Command::Tree(tree) => Some(tree),
|
||||
Command::Alias(_) => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut commands: Vec<&CommandTree> = futures::stream::iter(commands.iter())
|
||||
.filter(|tree| async {
|
||||
if let Some(perm) = dispatcher.permissions.get(&tree.names[0]) {
|
||||
return sender.has_permission(perm.as_str()).await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let page_number = match page_number_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 1,
|
||||
Ok(Ok(number)) => number,
|
||||
Ok(Err(_)) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Invalid page number.")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
false
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
};
|
||||
|
||||
commands.sort_by(|a, b| a.names[0].cmp(&b.names[0]));
|
||||
|
||||
let total_pages = (commands.len() as i32 + COMMANDS_PER_PAGE - 1) / COMMANDS_PER_PAGE;
|
||||
let page = page_number.min(total_pages);
|
||||
|
||||
let start = (page - 1) * COMMANDS_PER_PAGE;
|
||||
let end = start + COMMANDS_PER_PAGE;
|
||||
let page_commands = &commands[start as usize..end.min(commands.len() as i32) as usize];
|
||||
|
||||
let arrow_left = if page > 1 {
|
||||
let cmd = format!("/help {}", page - 1);
|
||||
TextComponent::text("<<<")
|
||||
.color(Color::Named(NamedColor::Aqua))
|
||||
.click_event(ClickEvent::RunCommand {
|
||||
command: cmd.into(),
|
||||
let dispatcher = server.command_dispatcher.read().await;
|
||||
let commands: Vec<&CommandTree> = dispatcher
|
||||
.commands
|
||||
.values()
|
||||
.filter_map(|cmd| match cmd {
|
||||
Command::Tree(tree) => Some(tree),
|
||||
Command::Alias(_) => None,
|
||||
})
|
||||
} else {
|
||||
TextComponent::text("<<<").color(Color::Named(NamedColor::Gray))
|
||||
};
|
||||
.collect();
|
||||
|
||||
let arrow_right = if page < total_pages {
|
||||
let cmd = format!("/help {}", page + 1);
|
||||
TextComponent::text(">>>")
|
||||
.color(Color::Named(NamedColor::Aqua))
|
||||
.click_event(ClickEvent::RunCommand {
|
||||
command: cmd.into(),
|
||||
let mut commands: Vec<&CommandTree> = futures::stream::iter(commands.iter())
|
||||
.filter(|tree| async {
|
||||
if let Some(perm) = dispatcher.permissions.get(&tree.names[0]) {
|
||||
return sender.has_permission(perm.as_str()).await;
|
||||
}
|
||||
false
|
||||
})
|
||||
} else {
|
||||
TextComponent::text(">>>").color(Color::Named(NamedColor::Gray))
|
||||
};
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let header_text = format!(" Help - Page {page}/{total_pages} ");
|
||||
commands.sort_by(|a, b| a.names[0].cmp(&b.names[0]));
|
||||
|
||||
let mut message = TextComponent::text("")
|
||||
.add_child(
|
||||
TextComponent::text("-".repeat((52 - header_text.len() - 3) / 2) + " ")
|
||||
.color_named(NamedColor::Yellow),
|
||||
)
|
||||
.add_child(arrow_left.clone())
|
||||
.add_child(TextComponent::text(header_text.clone()))
|
||||
.add_child(arrow_right.clone())
|
||||
.add_child(
|
||||
TextComponent::text(
|
||||
" ".to_owned() + &"-".repeat((52 - header_text.len() - 3) / 2) + "\n",
|
||||
let total_pages = (commands.len() as i32 + COMMANDS_PER_PAGE - 1) / COMMANDS_PER_PAGE;
|
||||
let page = page_number.min(total_pages);
|
||||
|
||||
let start = (page - 1) * COMMANDS_PER_PAGE;
|
||||
let end = start + COMMANDS_PER_PAGE;
|
||||
let page_commands = &commands[start as usize..end.min(commands.len() as i32) as usize];
|
||||
|
||||
let arrow_left = if page > 1 {
|
||||
let cmd = format!("/help {}", page - 1);
|
||||
TextComponent::text("<<<")
|
||||
.color(Color::Named(NamedColor::Aqua))
|
||||
.click_event(ClickEvent::RunCommand {
|
||||
command: cmd.into(),
|
||||
})
|
||||
} else {
|
||||
TextComponent::text("<<<").color(Color::Named(NamedColor::Gray))
|
||||
};
|
||||
|
||||
let arrow_right = if page < total_pages {
|
||||
let cmd = format!("/help {}", page + 1);
|
||||
TextComponent::text(">>>")
|
||||
.color(Color::Named(NamedColor::Aqua))
|
||||
.click_event(ClickEvent::RunCommand {
|
||||
command: cmd.into(),
|
||||
})
|
||||
} else {
|
||||
TextComponent::text(">>>").color(Color::Named(NamedColor::Gray))
|
||||
};
|
||||
|
||||
let header_text = format!(" Help - Page {page}/{total_pages} ");
|
||||
|
||||
let mut message = TextComponent::text("")
|
||||
.add_child(
|
||||
TextComponent::text("-".repeat((52 - header_text.len() - 3) / 2) + " ")
|
||||
.color_named(NamedColor::Yellow),
|
||||
)
|
||||
.color_named(NamedColor::Yellow),
|
||||
);
|
||||
|
||||
for tree in page_commands {
|
||||
message = message.add_child(
|
||||
TextComponent::text("/".to_owned() + &tree.names.join(", /"))
|
||||
.color_named(NamedColor::Gold)
|
||||
.add_child(TextComponent::text(" - ").color_named(NamedColor::Yellow))
|
||||
.add_child(
|
||||
TextComponent::text(tree.description.clone() + "\n")
|
||||
.color_named(NamedColor::White),
|
||||
.add_child(arrow_left.clone())
|
||||
.add_child(TextComponent::text(header_text.clone()))
|
||||
.add_child(arrow_right.clone())
|
||||
.add_child(
|
||||
TextComponent::text(
|
||||
" ".to_owned() + &"-".repeat((52 - header_text.len() - 3) / 2) + "\n",
|
||||
)
|
||||
.add_child(TextComponent::text(" Usage: ").color_named(NamedColor::Yellow))
|
||||
.add_child(
|
||||
TextComponent::text(format!("{tree}")).color_named(NamedColor::White),
|
||||
.color_named(NamedColor::Yellow),
|
||||
);
|
||||
|
||||
for tree in page_commands {
|
||||
message = message.add_child(
|
||||
TextComponent::text("/".to_owned() + &tree.names.join(", /"))
|
||||
.color_named(NamedColor::Gold)
|
||||
.add_child(TextComponent::text(" - ").color_named(NamedColor::Yellow))
|
||||
.add_child(
|
||||
TextComponent::text(tree.description.clone() + "\n")
|
||||
.color_named(NamedColor::White),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::text(" Usage: ").color_named(NamedColor::Yellow),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::text(format!("{tree}")).color_named(NamedColor::White),
|
||||
)
|
||||
.add_child(TextComponent::text("\n").color_named(NamedColor::White))
|
||||
.click_event(ClickEvent::SuggestCommand {
|
||||
command: format!("/{}", tree.names[0]).into(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let footer_text = format!(" Page {page}/{total_pages} ");
|
||||
message = message
|
||||
.add_child(
|
||||
TextComponent::text("-".repeat((52 - footer_text.len() - 3) / 2) + " ")
|
||||
.color_named(NamedColor::Yellow),
|
||||
)
|
||||
.add_child(arrow_left)
|
||||
.add_child(TextComponent::text(footer_text.clone()))
|
||||
.add_child(arrow_right)
|
||||
.add_child(
|
||||
TextComponent::text(
|
||||
" ".to_owned() + &"-".repeat((52 - footer_text.len() - 3) / 2),
|
||||
)
|
||||
.add_child(TextComponent::text("\n").color_named(NamedColor::White))
|
||||
.click_event(ClickEvent::SuggestCommand {
|
||||
command: format!("/{}", tree.names[0]).into(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let footer_text = format!(" Page {page}/{total_pages} ");
|
||||
message = message
|
||||
.add_child(
|
||||
TextComponent::text("-".repeat((52 - footer_text.len() - 3) / 2) + " ")
|
||||
.color_named(NamedColor::Yellow),
|
||||
)
|
||||
.add_child(arrow_left)
|
||||
.add_child(TextComponent::text(footer_text.clone()))
|
||||
.add_child(arrow_right)
|
||||
.add_child(
|
||||
TextComponent::text(" ".to_owned() + &"-".repeat((52 - footer_text.len() - 3) / 2))
|
||||
.color_named(NamedColor::Yellow),
|
||||
);
|
||||
);
|
||||
|
||||
sender.send_message(message).await;
|
||||
sender.send_message(message).await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::color::NamedColor;
|
||||
|
||||
use crate::command::CommandError;
|
||||
use crate::command::args::message::MsgArgConsumer;
|
||||
use crate::command::args::players::PlayersArgumentConsumer;
|
||||
use crate::command::args::{Arg, ConsumedArgs};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::argument;
|
||||
use crate::command::{CommandError, CommandResult};
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::net::DisconnectReason;
|
||||
@@ -22,31 +21,32 @@ const ARG_REASON: &str = "reason";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
let reason = match args.get(&ARG_REASON) {
|
||||
Some(Arg::Msg(r)) => TextComponent::text(r.clone()),
|
||||
_ => TextComponent::translate("multiplayer.disconnect.kicked", []),
|
||||
};
|
||||
let reason = match args.get(&ARG_REASON) {
|
||||
Some(Arg::Msg(r)) => TextComponent::text(r.clone()),
|
||||
_ => TextComponent::translate("multiplayer.disconnect.kicked", []),
|
||||
};
|
||||
|
||||
for target in targets {
|
||||
target.kick(DisconnectReason::Kicked, reason.clone()).await;
|
||||
let mut msg = TextComponent::text("Kicked: ");
|
||||
msg = msg.add_child(target.get_display_name().await);
|
||||
sender.send_message(msg.color_named(NamedColor::Blue)).await;
|
||||
}
|
||||
for target in targets {
|
||||
target.kick(DisconnectReason::Kicked, reason.clone()).await;
|
||||
let mut msg = TextComponent::text("Kicked: ");
|
||||
msg = msg.add_child(target.get_display_name().await);
|
||||
sender.send_message(msg.color_named(NamedColor::Blue)).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::args::entities::EntitiesArgumentConsumer;
|
||||
use crate::command::args::{Arg, ConsumedArgs};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, require};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender};
|
||||
use crate::entity::EntityBase;
|
||||
use CommandError::InvalidConsumption;
|
||||
|
||||
@@ -16,62 +15,64 @@ const ARG_TARGET: &str = "target";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Entities(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Entities(targets)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
let target_count = targets.len();
|
||||
for target in targets {
|
||||
target.kill(target.clone()).await;
|
||||
}
|
||||
let target_count = targets.len();
|
||||
for target in targets {
|
||||
target.kill(target.clone()).await;
|
||||
}
|
||||
|
||||
let msg = if target_count == 1 {
|
||||
TextComponent::translate(
|
||||
"commands.kill.success.single",
|
||||
[targets[0].get_display_name().await],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate(
|
||||
"commands.kill.success.multiple",
|
||||
[TextComponent::text(target_count.to_string())],
|
||||
)
|
||||
};
|
||||
let msg = if target_count == 1 {
|
||||
TextComponent::translate(
|
||||
"commands.kill.success.single",
|
||||
[targets[0].get_display_name().await],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate(
|
||||
"commands.kill.success.multiple",
|
||||
[TextComponent::text(target_count.to_string())],
|
||||
)
|
||||
};
|
||||
|
||||
sender.send_message(msg).await;
|
||||
sender.send_message(msg).await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct SelfExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for SelfExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?;
|
||||
target.kill(target.clone()).await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?;
|
||||
target.kill(target.clone()).await;
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.kill.success.single",
|
||||
[target.get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.kill.success.single",
|
||||
[target.get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_config::BASIC_CONFIG;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
CommandExecutor, CommandResult, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
},
|
||||
entity::player::Player,
|
||||
};
|
||||
@@ -17,28 +16,29 @@ const DESCRIPTION: &str = "Print the list of online players.";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.list.players",
|
||||
[
|
||||
TextComponent::text(players.len().to_string()),
|
||||
TextComponent::text(BASIC_CONFIG.max_players.to_string()),
|
||||
TextComponent::text(get_player_names(players)),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.list.players",
|
||||
[
|
||||
TextComponent::text(players.len().to_string()),
|
||||
TextComponent::text(BASIC_CONFIG.max_players.to_string()),
|
||||
TextComponent::text(get_player_names(players)),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::world::EMOTE_COMMAND;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{Arg, ConsumedArgs, message::MsgArgConsumer},
|
||||
tree::CommandTree,
|
||||
tree::builder::argument,
|
||||
tree::{CommandTree, builder::argument},
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
|
||||
@@ -18,27 +16,28 @@ const ARG_MESSAGE: &str = "action";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_MESSAGE.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_MESSAGE.into())));
|
||||
};
|
||||
|
||||
server
|
||||
.broadcast_message(
|
||||
&TextComponent::text(msg.clone()),
|
||||
&TextComponent::text(format!("{sender}")),
|
||||
EMOTE_COMMAND,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
server
|
||||
.broadcast_message(
|
||||
&TextComponent::text(msg.clone()),
|
||||
&TextComponent::text(format!("{sender}")),
|
||||
EMOTE_COMMAND,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::world::{MSG_COMMAND_INCOMING, MSG_COMMAND_OUTGOING};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{
|
||||
Arg, ConsumedArgs, FindArgDefaultName, message::MsgArgConsumer,
|
||||
players::PlayersArgumentConsumer,
|
||||
},
|
||||
tree::CommandTree,
|
||||
tree::builder::{argument, argument_default_name},
|
||||
tree::{
|
||||
CommandTree,
|
||||
builder::{argument, argument_default_name},
|
||||
},
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
use CommandError::InvalidConsumption;
|
||||
@@ -22,42 +23,43 @@ const ARG_MESSAGE: &str = "message";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_MESSAGE.into())));
|
||||
};
|
||||
let targets = PlayersArgumentConsumer.find_arg_default_name(args)?;
|
||||
let player = sender.as_player().ok_or(CommandError::InvalidRequirement)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_MESSAGE.into())));
|
||||
};
|
||||
let targets = PlayersArgumentConsumer.find_arg_default_name(args)?;
|
||||
let player = sender.as_player().ok_or(CommandError::InvalidRequirement)?;
|
||||
|
||||
for target in targets {
|
||||
player
|
||||
.send_message(
|
||||
&TextComponent::text(msg.clone()),
|
||||
MSG_COMMAND_OUTGOING,
|
||||
&player.get_display_name().await,
|
||||
Some(&target.get_display_name().await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
for target in targets {
|
||||
target
|
||||
.send_message(
|
||||
&TextComponent::text(msg.clone()),
|
||||
MSG_COMMAND_INCOMING,
|
||||
&player.get_display_name().await,
|
||||
Some(&target.get_display_name().await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
for target in targets {
|
||||
player
|
||||
.send_message(
|
||||
&TextComponent::text(msg.clone()),
|
||||
MSG_COMMAND_OUTGOING,
|
||||
&player.get_display_name().await,
|
||||
Some(&target.get_display_name().await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
for target in targets {
|
||||
target
|
||||
.send_message(
|
||||
&TextComponent::text(msg.clone()),
|
||||
MSG_COMMAND_INCOMING,
|
||||
&player.get_display_name().await,
|
||||
Some(&target.get_display_name().await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::command::CommandResult;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::{
|
||||
command::{
|
||||
@@ -9,7 +10,6 @@ use crate::{
|
||||
data::{SaveJSONConfiguration, op_data::OPERATOR_CONFIG},
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_config::{BASIC_CONFIG, op::Op};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
@@ -19,66 +19,67 @@ const ARG_TARGETS: &str = "targets";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut config = OPERATOR_CONFIG.write().await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let mut config = OPERATOR_CONFIG.write().await;
|
||||
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
for player in targets {
|
||||
let new_level = BASIC_CONFIG
|
||||
.op_permission_level
|
||||
.min(sender.permission_lvl());
|
||||
|
||||
if player.permission_lvl.load() == new_level {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.op.failed", []))
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(op) = config
|
||||
.ops
|
||||
.iter_mut()
|
||||
.find(|o| o.uuid == player.gameprofile.id)
|
||||
{
|
||||
op.level = new_level;
|
||||
} else {
|
||||
let op_entry = Op::new(
|
||||
player.gameprofile.id,
|
||||
player.gameprofile.name.clone(),
|
||||
new_level,
|
||||
false,
|
||||
);
|
||||
config.ops.push(op_entry);
|
||||
}
|
||||
|
||||
config.save();
|
||||
|
||||
{
|
||||
let command_dispatcher = server.command_dispatcher.read().await;
|
||||
player
|
||||
.set_permission_lvl(new_level, &command_dispatcher)
|
||||
.await;
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.op.success",
|
||||
[player.get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
for player in targets {
|
||||
let new_level = BASIC_CONFIG
|
||||
.op_permission_level
|
||||
.min(sender.permission_lvl());
|
||||
|
||||
Ok(())
|
||||
if player.permission_lvl.load() == new_level {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.op.failed", []))
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(op) = config
|
||||
.ops
|
||||
.iter_mut()
|
||||
.find(|o| o.uuid == player.gameprofile.id)
|
||||
{
|
||||
op.level = new_level;
|
||||
} else {
|
||||
let op_entry = Op::new(
|
||||
player.gameprofile.id,
|
||||
player.gameprofile.name.clone(),
|
||||
new_level,
|
||||
false,
|
||||
);
|
||||
config.ops.push(op_entry);
|
||||
}
|
||||
|
||||
config.save();
|
||||
|
||||
{
|
||||
let command_dispatcher = server.command_dispatcher.read().await;
|
||||
player
|
||||
.set_permission_lvl(new_level, &command_dispatcher)
|
||||
.await;
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.op.success",
|
||||
[player.get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use crate::{
|
||||
command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{Arg, ConsumedArgs, simple::SimpleArgConsumer},
|
||||
tree::CommandTree,
|
||||
tree::builder::argument,
|
||||
tree::{CommandTree, builder::argument},
|
||||
},
|
||||
data::{SaveJSONConfiguration, banned_player_data::BANNED_PLAYER_LIST},
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const NAMES: [&str; 1] = ["pardon"];
|
||||
@@ -18,43 +16,44 @@ const ARG_TARGET: &str = "player";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
let target = (*target).to_string();
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
let target = (*target).to_string();
|
||||
|
||||
let mut lock = BANNED_PLAYER_LIST.write().await;
|
||||
let mut lock = BANNED_PLAYER_LIST.write().await;
|
||||
|
||||
if let Some(idx) = lock
|
||||
.banned_players
|
||||
.iter()
|
||||
.position(|entry| entry.name == target)
|
||||
{
|
||||
lock.banned_players.remove(idx);
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.pardon.failed", []))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lock.save();
|
||||
|
||||
if let Some(idx) = lock
|
||||
.banned_players
|
||||
.iter()
|
||||
.position(|entry| entry.name == target)
|
||||
{
|
||||
lock.banned_players.remove(idx);
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.pardon.failed", []))
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.pardon.success",
|
||||
[TextComponent::text(target)],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lock.save();
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.pardon.success",
|
||||
[TextComponent::text(target)],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,13 @@ use std::{net::IpAddr, str::FromStr};
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{Arg, ConsumedArgs, simple::SimpleArgConsumer},
|
||||
tree::CommandTree,
|
||||
tree::builder::argument,
|
||||
tree::{CommandTree, builder::argument},
|
||||
},
|
||||
data::{SaveJSONConfiguration, banned_ip_data::BANNED_IP_LIST},
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const NAMES: [&str; 1] = ["pardon-ip"];
|
||||
@@ -20,45 +18,46 @@ const ARG_TARGET: &str = "ip";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else {
|
||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||
};
|
||||
|
||||
let Ok(ip) = IpAddr::from_str(target) else {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.pardonip.invalid", []))
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut lock = BANNED_IP_LIST.write().await;
|
||||
|
||||
if let Some(idx) = lock.banned_ips.iter().position(|entry| entry.ip == ip) {
|
||||
lock.banned_ips.remove(idx);
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.pardonip.failed", []))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lock.save();
|
||||
|
||||
let Ok(ip) = IpAddr::from_str(target) else {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.pardonip.invalid", []))
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.pardonip.success",
|
||||
[TextComponent::text(ip.to_string())],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut lock = BANNED_IP_LIST.write().await;
|
||||
|
||||
if let Some(idx) = lock.banned_ips.iter().position(|entry| entry.ip == ip) {
|
||||
lock.banned_ips.remove(idx);
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.pardonip.failed", []))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lock.save();
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.pardonip.success",
|
||||
[TextComponent::text(ip.to_string())],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::{math::vector3::Vector3, text::TextComponent};
|
||||
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{
|
||||
ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer,
|
||||
position_3d::Position3DArgumentConsumer, resource::particle::ParticleArgumentConsumer,
|
||||
@@ -22,63 +21,64 @@ const ARG_COUNT: &str = "count";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let particle = ParticleArgumentConsumer::find_arg(args, ARG_NAME)?;
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS);
|
||||
let delta = Position3DArgumentConsumer::find_arg(args, ARG_DELTA);
|
||||
let speed = BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_SPEED);
|
||||
let count = BoundedNumArgumentConsumer::<i32>::find_arg(args, ARG_COUNT);
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let particle = ParticleArgumentConsumer::find_arg(args, ARG_NAME)?;
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS);
|
||||
let delta = Position3DArgumentConsumer::find_arg(args, ARG_DELTA);
|
||||
let speed = BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_SPEED);
|
||||
let count = BoundedNumArgumentConsumer::<i32>::find_arg(args, ARG_COUNT);
|
||||
|
||||
let delta = delta.unwrap_or(Vector3::new(0.0, 0.0, 0.0));
|
||||
let delta: Vector3<f32> = Vector3::new(delta.x as f32, delta.y as f32, delta.z as f32);
|
||||
let speed = speed.unwrap_or(Ok(0.0))?;
|
||||
let count = count.unwrap_or(Ok(0))?;
|
||||
let (world, pos) = match sender {
|
||||
CommandSender::Console | CommandSender::Rcon(_) => {
|
||||
let guard = server.worlds.read().await;
|
||||
let world = guard
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or(CommandError::InvalidRequirement)?;
|
||||
// default position for spawning a player, in this case for particle
|
||||
let pos = {
|
||||
let info = &world.level_info.read().await;
|
||||
// default position for spawning a player, in this case for mob
|
||||
pos.unwrap_or(Vector3::new(
|
||||
f64::from(info.spawn_x) + 0.5,
|
||||
f64::from(info.spawn_y) + 1.0,
|
||||
f64::from(info.spawn_z) + 0.5,
|
||||
))
|
||||
};
|
||||
let delta = delta.unwrap_or(Vector3::new(0.0, 0.0, 0.0));
|
||||
let delta: Vector3<f32> = Vector3::new(delta.x as f32, delta.y as f32, delta.z as f32);
|
||||
let speed = speed.unwrap_or(Ok(0.0))?;
|
||||
let count = count.unwrap_or(Ok(0))?;
|
||||
let (world, pos) = match sender {
|
||||
CommandSender::Console | CommandSender::Rcon(_) => {
|
||||
let guard = server.worlds.read().await;
|
||||
let world = guard
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or(CommandError::InvalidRequirement)?;
|
||||
// default position for spawning a player, in this case for particle
|
||||
let pos = {
|
||||
let info = &world.level_info.read().await;
|
||||
// default position for spawning a player, in this case for mob
|
||||
pos.unwrap_or(Vector3::new(
|
||||
f64::from(info.spawn_x) + 0.5,
|
||||
f64::from(info.spawn_y) + 1.0,
|
||||
f64::from(info.spawn_z) + 0.5,
|
||||
))
|
||||
};
|
||||
|
||||
(world, pos)
|
||||
}
|
||||
CommandSender::Player(player) => {
|
||||
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
|
||||
(world, pos)
|
||||
}
|
||||
CommandSender::Player(player) => {
|
||||
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
|
||||
|
||||
(player.world().clone(), pos)
|
||||
}
|
||||
};
|
||||
(player.world().clone(), pos)
|
||||
}
|
||||
};
|
||||
|
||||
world
|
||||
.spawn_particle(pos, delta, speed, count, *particle)
|
||||
.await;
|
||||
world
|
||||
.spawn_particle(pos, delta, speed, count, *particle)
|
||||
.await;
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.particle.success",
|
||||
[TextComponent::text(format!("{particle:?}"))],
|
||||
))
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.particle.success",
|
||||
[TextComponent::text(format!("{particle:?}"))],
|
||||
))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::sound::SoundCategory;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use rand::{Rng, rng};
|
||||
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandExecutor, CommandResult, CommandSender,
|
||||
args::{
|
||||
Arg, ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer,
|
||||
players::PlayersArgumentConsumer, position_3d::Position3DArgumentConsumer,
|
||||
sound::SoundArgumentConsumer, sound_category::SoundCategoryArgumentConsumer,
|
||||
},
|
||||
tree::CommandTree,
|
||||
tree::builder::argument,
|
||||
tree::{CommandTree, builder::argument},
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
|
||||
@@ -60,109 +58,112 @@ fn min_volume_consumer() -> BoundedNumArgumentConsumer<f32> {
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
// Get required sound argument
|
||||
let sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
// Get required sound argument
|
||||
let sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND)?;
|
||||
|
||||
// Get optional sound category, defaults to Master
|
||||
let source = args
|
||||
.get(ARG_SOURCE)
|
||||
.map_or(SoundCategory::Master, |arg| match arg {
|
||||
Arg::SoundCategory(category) => *category,
|
||||
_ => SoundCategory::Master,
|
||||
});
|
||||
// Get optional sound category, defaults to Master
|
||||
let source = args
|
||||
.get(ARG_SOURCE)
|
||||
.map_or(SoundCategory::Master, |arg| match arg {
|
||||
Arg::SoundCategory(category) => *category,
|
||||
_ => SoundCategory::Master,
|
||||
});
|
||||
|
||||
// Get target players, defaults to sender if not specified
|
||||
let targets = if let Ok(players) = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS) {
|
||||
players
|
||||
} else if let Some(player) = sender.as_player() {
|
||||
&[player]
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
// Get target players, defaults to sender if not specified
|
||||
let targets = if let Ok(players) = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)
|
||||
{
|
||||
players
|
||||
} else if let Some(player) = sender.as_player() {
|
||||
&[player]
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Get optional position, defaults to target's position
|
||||
let position = Position3DArgumentConsumer::find_arg(args, ARG_POS).ok();
|
||||
// Get optional position, defaults to target's position
|
||||
let position = Position3DArgumentConsumer::find_arg(args, ARG_POS).ok();
|
||||
|
||||
// Get optional volume parameter
|
||||
let volume = match BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_VOLUME) {
|
||||
Ok(Ok(v)) => v,
|
||||
_ => 1.0, // Default volume
|
||||
};
|
||||
// Get optional volume parameter
|
||||
let volume = match BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_VOLUME) {
|
||||
Ok(Ok(v)) => v,
|
||||
_ => 1.0, // Default volume
|
||||
};
|
||||
|
||||
// Get optional pitch parameter
|
||||
let pitch = match BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_PITCH) {
|
||||
Ok(Ok(p)) => p.max(0.5), // Values below 0.5 are clamped
|
||||
_ => 1.0, // Default pitch
|
||||
};
|
||||
// Get optional pitch parameter
|
||||
let pitch = match BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_PITCH) {
|
||||
Ok(Ok(p)) => p.max(0.5), // Values below 0.5 are clamped
|
||||
_ => 1.0, // Default pitch
|
||||
};
|
||||
|
||||
// Get optional minimum volume (currently unused in implementation)
|
||||
let min_volume = match BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_MIN_VOLUME) {
|
||||
Ok(Ok(v)) => v,
|
||||
_ => 0.0, // Default minimum volume
|
||||
};
|
||||
// Get optional minimum volume (currently unused in implementation)
|
||||
let min_volume = match BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_MIN_VOLUME)
|
||||
{
|
||||
Ok(Ok(v)) => v,
|
||||
_ => 0.0, // Default minimum volume
|
||||
};
|
||||
|
||||
// Use same random seed for all targets to ensure sound synchronization
|
||||
let seed = rng().random::<f64>();
|
||||
// Use same random seed for all targets to ensure sound synchronization
|
||||
let seed = rng().random::<f64>();
|
||||
|
||||
// Track how many players actually received the sound
|
||||
let mut players_who_heard = 0;
|
||||
// Track how many players actually received the sound
|
||||
let mut players_who_heard = 0;
|
||||
|
||||
// Play sound for each target player
|
||||
for target in targets {
|
||||
let pos = position.unwrap_or(target.living_entity.entity.pos.load());
|
||||
// Play sound for each target player
|
||||
for target in targets {
|
||||
let pos = position.unwrap_or(target.living_entity.entity.pos.load());
|
||||
|
||||
// Check if player can hear the sound based on volume and distance
|
||||
let player_pos = target.living_entity.entity.pos.load();
|
||||
let distance = player_pos.squared_distance_to_vec(pos);
|
||||
let max_distance: f64 = (16.0 * volume).into(); // 16 blocks is base distance at volume 1.0
|
||||
// Check if player can hear the sound based on volume and distance
|
||||
let player_pos = target.living_entity.entity.pos.load();
|
||||
let distance = player_pos.squared_distance_to_vec(pos);
|
||||
let max_distance: f64 = (16.0 * volume).into(); // 16 blocks is base distance at volume 1.0
|
||||
|
||||
if distance <= max_distance || min_volume > 0.0 {
|
||||
target
|
||||
.play_sound(sound as u16, source, &pos, volume, pitch, seed)
|
||||
.await;
|
||||
players_who_heard += 1;
|
||||
if distance <= max_distance || min_volume > 0.0 {
|
||||
target
|
||||
.play_sound(sound as u16, source, &pos, volume, pitch, seed)
|
||||
.await;
|
||||
players_who_heard += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send appropriate message based on results
|
||||
if players_who_heard == 0 {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.playsound.failed", []))
|
||||
.await;
|
||||
} else {
|
||||
let sound_name = sound.to_name();
|
||||
if players_who_heard == 1 {
|
||||
// Send appropriate message based on results
|
||||
if players_who_heard == 0 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.playsound.success.single",
|
||||
[
|
||||
TextComponent::text(sound_name),
|
||||
targets[0].get_display_name().await,
|
||||
],
|
||||
))
|
||||
.send_message(TextComponent::translate("commands.playsound.failed", []))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.playsound.success.multiple",
|
||||
[
|
||||
TextComponent::text(sound_name),
|
||||
TextComponent::text(players_who_heard.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
let sound_name = sound.to_name();
|
||||
if players_who_heard == 1 {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.playsound.success.single",
|
||||
[
|
||||
TextComponent::text(sound_name),
|
||||
targets[0].get_display_name().await,
|
||||
],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.playsound.success.multiple",
|
||||
[
|
||||
TextComponent::text(sound_name),
|
||||
TextComponent::text(players_who_heard.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::path::Path;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::{
|
||||
PermissionLvl,
|
||||
text::{TextComponent, color::NamedColor, hover::HoverEvent},
|
||||
@@ -9,7 +8,7 @@ use pumpkin_util::{
|
||||
use crate::{
|
||||
PLUGIN_MANAGER,
|
||||
command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandExecutor, CommandResult, CommandSender,
|
||||
args::{Arg, ConsumedArgs, simple::SimpleArgConsumer},
|
||||
tree::{
|
||||
CommandTree,
|
||||
@@ -28,143 +27,154 @@ const PLUGIN_NAME: &str = "plugin_name";
|
||||
|
||||
struct ListExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ListExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let plugins = PLUGIN_MANAGER.active_plugins().await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let plugins = PLUGIN_MANAGER.active_plugins().await;
|
||||
|
||||
let message_text = if plugins.is_empty() {
|
||||
"There are no loaded plugins.".to_string()
|
||||
} else if plugins.len() == 1 {
|
||||
"There is 1 plugin loaded:\n".to_string()
|
||||
} else {
|
||||
format!("There are {} plugins loaded:\n", plugins.len())
|
||||
};
|
||||
let mut message = TextComponent::text(message_text);
|
||||
|
||||
for (i, metadata) in plugins.clone().into_iter().enumerate() {
|
||||
let fmt = if i == plugins.len() - 1 {
|
||||
metadata.name.to_string()
|
||||
let message_text = if plugins.is_empty() {
|
||||
"There are no loaded plugins.".to_string()
|
||||
} else if plugins.len() == 1 {
|
||||
"There is 1 plugin loaded:\n".to_string()
|
||||
} else {
|
||||
format!("{}, ", metadata.name)
|
||||
format!("There are {} plugins loaded:\n", plugins.len())
|
||||
};
|
||||
let hover_text = format!(
|
||||
"Version: {}\nAuthors: {}\nDescription: {}",
|
||||
metadata.version, metadata.authors, metadata.description
|
||||
);
|
||||
let component = TextComponent::text(fmt)
|
||||
.color_named(NamedColor::Green)
|
||||
.hover_event(HoverEvent::show_text(TextComponent::text(hover_text)));
|
||||
let mut message = TextComponent::text(message_text);
|
||||
|
||||
message = message.add_child(component);
|
||||
}
|
||||
for (i, metadata) in plugins.clone().into_iter().enumerate() {
|
||||
let fmt = if i == plugins.len() - 1 {
|
||||
metadata.name.to_string()
|
||||
} else {
|
||||
format!("{}, ", metadata.name)
|
||||
};
|
||||
let hover_text = format!(
|
||||
"Version: {}\nAuthors: {}\nDescription: {}",
|
||||
metadata.version, metadata.authors, metadata.description
|
||||
);
|
||||
let component = TextComponent::text(fmt)
|
||||
.color_named(NamedColor::Green)
|
||||
.hover_event(HoverEvent::show_text(TextComponent::text(hover_text)));
|
||||
|
||||
sender.send_message(message).await;
|
||||
message = message.add_child(component);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
sender.send_message(message).await;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct LoadExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for LoadExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(plugin_name)) = args.get(PLUGIN_NAME) else {
|
||||
return Err(InvalidConsumption(Some(PLUGIN_NAME.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(plugin_name)) = args.get(PLUGIN_NAME) else {
|
||||
return Err(InvalidConsumption(Some(PLUGIN_NAME.into())));
|
||||
};
|
||||
|
||||
if PLUGIN_MANAGER.is_plugin_active(plugin_name).await {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!("Plugin {plugin_name} is already loaded"))
|
||||
.color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let result = PLUGIN_MANAGER.try_load_plugin(Path::new(plugin_name)).await;
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
if PLUGIN_MANAGER.is_plugin_active(plugin_name).await {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!("Plugin {plugin_name} loaded successfully"))
|
||||
.color_named(NamedColor::Green),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!("Failed to load plugin {plugin_name}: {e}"))
|
||||
TextComponent::text(format!("Plugin {plugin_name} is already loaded"))
|
||||
.color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
let result = PLUGIN_MANAGER.try_load_plugin(Path::new(plugin_name)).await;
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!(
|
||||
"Plugin {plugin_name} loaded successfully"
|
||||
))
|
||||
.color_named(NamedColor::Green),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!(
|
||||
"Failed to load plugin {plugin_name}: {e}"
|
||||
))
|
||||
.color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct UnloadExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for UnloadExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(plugin_name)) = args.get(PLUGIN_NAME) else {
|
||||
return Err(InvalidConsumption(Some(PLUGIN_NAME.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(plugin_name)) = args.get(PLUGIN_NAME) else {
|
||||
return Err(InvalidConsumption(Some(PLUGIN_NAME.into())));
|
||||
};
|
||||
|
||||
if !PLUGIN_MANAGER.is_plugin_active(plugin_name).await {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!("Plugin {plugin_name} is not loaded"))
|
||||
.color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let result = PLUGIN_MANAGER.unload_plugin(plugin_name).await;
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
if !PLUGIN_MANAGER.is_plugin_active(plugin_name).await {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!("Plugin {plugin_name} unloaded successfully",))
|
||||
.color_named(NamedColor::Green),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!("Failed to unload plugin {plugin_name}: {e}"))
|
||||
TextComponent::text(format!("Plugin {plugin_name} is not loaded"))
|
||||
.color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
let result = PLUGIN_MANAGER.unload_plugin(plugin_name).await;
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!(
|
||||
"Plugin {plugin_name} unloaded successfully",
|
||||
))
|
||||
.color_named(NamedColor::Green),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text(format!(
|
||||
"Failed to unload plugin {plugin_name}: {e}"
|
||||
))
|
||||
.color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::{TextComponent, color::NamedColor, hover::HoverEvent};
|
||||
|
||||
use crate::{
|
||||
PLUGIN_MANAGER,
|
||||
command::{
|
||||
CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
CommandExecutor, CommandResult, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,44 +13,45 @@ const DESCRIPTION: &str = "List all available plugins.";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let plugins = PLUGIN_MANAGER.active_plugins().await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let plugins = PLUGIN_MANAGER.active_plugins().await;
|
||||
|
||||
let message_text = if plugins.is_empty() {
|
||||
"There are no loaded plugins.".to_string()
|
||||
} else if plugins.len() == 1 {
|
||||
"There is 1 plugin loaded:\n".to_string()
|
||||
} else {
|
||||
format!("There are {} plugins loaded:\n", plugins.len())
|
||||
};
|
||||
let mut message = TextComponent::text(message_text);
|
||||
|
||||
for (i, metadata) in plugins.clone().into_iter().enumerate() {
|
||||
let fmt = if i == plugins.len() - 1 {
|
||||
metadata.name.to_string()
|
||||
let message_text = if plugins.is_empty() {
|
||||
"There are no loaded plugins.".to_string()
|
||||
} else if plugins.len() == 1 {
|
||||
"There is 1 plugin loaded:\n".to_string()
|
||||
} else {
|
||||
format!("{}, ", metadata.name)
|
||||
format!("There are {} plugins loaded:\n", plugins.len())
|
||||
};
|
||||
let hover_text = format!(
|
||||
"Version: {}\nAuthors: {}\nDescription: {}",
|
||||
metadata.version, metadata.authors, metadata.description
|
||||
);
|
||||
let component = TextComponent::text(fmt)
|
||||
.color_named(NamedColor::Green)
|
||||
.hover_event(HoverEvent::show_text(TextComponent::text(hover_text)));
|
||||
message = message.add_child(component);
|
||||
}
|
||||
let mut message = TextComponent::text(message_text);
|
||||
|
||||
sender.send_message(message).await;
|
||||
for (i, metadata) in plugins.clone().into_iter().enumerate() {
|
||||
let fmt = if i == plugins.len() - 1 {
|
||||
metadata.name.to_string()
|
||||
} else {
|
||||
format!("{}, ", metadata.name)
|
||||
};
|
||||
let hover_text = format!(
|
||||
"Version: {}\nAuthors: {}\nDescription: {}",
|
||||
metadata.version, metadata.authors, metadata.description
|
||||
);
|
||||
let component = TextComponent::text(fmt)
|
||||
.color_named(NamedColor::Green)
|
||||
.hover_event(HoverEvent::show_text(TextComponent::text(hover_text)));
|
||||
message = message.add_child(component);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
sender.send_message(message).await;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::color::NamedColor;
|
||||
|
||||
use crate::command::CommandResult;
|
||||
|
||||
use crate::command::args::ConsumedArgs;
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandExecutor, CommandSender};
|
||||
use crate::{HEAP_PROFILER, stop_server};
|
||||
|
||||
const NAMES: [&str; 1] = ["mem_profile"];
|
||||
@@ -13,27 +14,29 @@ const DESCRIPTION: &str = "Stop the server and dump a memory profile.";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.stop.stopping", []).color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.stop.stopping", [])
|
||||
.color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut profiler = HEAP_PROFILER.lock().await;
|
||||
let p = profiler.take().unwrap();
|
||||
// Do the actual profiling
|
||||
drop(p);
|
||||
let mut profiler = HEAP_PROFILER.lock().await;
|
||||
let p = profiler.take().unwrap();
|
||||
drop(p);
|
||||
|
||||
stop_server();
|
||||
Ok(())
|
||||
stop_server();
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::packet::CURRENT_MC_PROTOCOL;
|
||||
use pumpkin_util::text::click::ClickEvent;
|
||||
use pumpkin_util::text::hover::HoverEvent;
|
||||
@@ -7,9 +6,8 @@ use pumpkin_util::translation::get_translation_text;
|
||||
use pumpkin_world::CURRENT_MC_VERSION;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
};
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::{CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree};
|
||||
|
||||
const NAMES: [&str; 2] = ["pumpkin", "version"];
|
||||
|
||||
@@ -19,117 +17,122 @@ struct Executor;
|
||||
|
||||
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
#[async_trait]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let locale = sender.get_locale().await;
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.version",
|
||||
locale,
|
||||
vec![TextComponent::text(CARGO_PKG_VERSION)],
|
||||
)
|
||||
.hover_event(HoverEvent::show_text(TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.version.hover",
|
||||
locale,
|
||||
vec![],
|
||||
)))
|
||||
.click_event(ClickEvent::CopyToClipboard {
|
||||
value: Cow::from(
|
||||
get_translation_text(
|
||||
"pumpkin:commands.pumpkin.version",
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let locale = sender.get_locale().await;
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.version",
|
||||
locale,
|
||||
vec![TextComponent::text(CARGO_PKG_VERSION)],
|
||||
)
|
||||
.hover_event(HoverEvent::show_text(TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.version.hover",
|
||||
locale,
|
||||
vec![],
|
||||
)))
|
||||
.click_event(ClickEvent::CopyToClipboard {
|
||||
value: Cow::from(
|
||||
get_translation_text(
|
||||
"pumpkin:commands.pumpkin.version",
|
||||
locale,
|
||||
vec![TextComponent::text(CARGO_PKG_VERSION).0],
|
||||
)
|
||||
.replace('\n', ""),
|
||||
),
|
||||
})
|
||||
.color_named(NamedColor::Green)
|
||||
.add_child(
|
||||
TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.description",
|
||||
locale,
|
||||
vec![TextComponent::text(CARGO_PKG_VERSION).0],
|
||||
vec![],
|
||||
)
|
||||
.replace('\n', ""),
|
||||
),
|
||||
})
|
||||
.color_named(NamedColor::Green)
|
||||
.add_child(
|
||||
TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.description",
|
||||
locale,
|
||||
vec![],
|
||||
)
|
||||
.click_event(ClickEvent::CopyToClipboard {
|
||||
value: Cow::from(
|
||||
get_translation_text(
|
||||
"pumpkin:commands.pumpkin.description",
|
||||
locale,
|
||||
vec![],
|
||||
)
|
||||
.replace('\n', ""),
|
||||
),
|
||||
})
|
||||
.hover_event(HoverEvent::show_text(TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.description.hover",
|
||||
locale,
|
||||
vec![],
|
||||
)))
|
||||
.color_named(NamedColor::White),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.minecraft_version",
|
||||
locale,
|
||||
vec![
|
||||
TextComponent::text(CURRENT_MC_VERSION),
|
||||
TextComponent::text(format!("{CURRENT_MC_PROTOCOL}")),
|
||||
],
|
||||
)
|
||||
.click_event(ClickEvent::CopyToClipboard {
|
||||
value: Cow::from(
|
||||
get_translation_text(
|
||||
"pumpkin:commands.pumpkin.minecraft_version",
|
||||
locale,
|
||||
vec![
|
||||
TextComponent::text(CURRENT_MC_VERSION).0,
|
||||
TextComponent::text(format!("{CURRENT_MC_PROTOCOL}")).0,
|
||||
],
|
||||
)
|
||||
.replace('\n', ""),
|
||||
),
|
||||
})
|
||||
.hover_event(HoverEvent::show_text(TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.minecraft_version.hover",
|
||||
locale,
|
||||
vec![],
|
||||
)))
|
||||
.color_named(NamedColor::Gold),
|
||||
)
|
||||
// https://pumpkinmc.org/
|
||||
.add_child(
|
||||
TextComponent::custom("pumpkin", "commands.pumpkin.github", locale, vec![])
|
||||
.click_event(ClickEvent::OpenUrl {
|
||||
url: Cow::from("https://github.com/Pumpkin-MC/Pumpkin"),
|
||||
.click_event(ClickEvent::CopyToClipboard {
|
||||
value: Cow::from(
|
||||
get_translation_text(
|
||||
"pumpkin:commands.pumpkin.description",
|
||||
locale,
|
||||
vec![],
|
||||
)
|
||||
.replace('\n', ""),
|
||||
),
|
||||
})
|
||||
.hover_event(HoverEvent::show_text(TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.github.hover",
|
||||
"commands.pumpkin.description.hover",
|
||||
locale,
|
||||
vec![],
|
||||
)))
|
||||
.color_named(NamedColor::Blue)
|
||||
.bold()
|
||||
.underlined(),
|
||||
)
|
||||
// Added docs. and a space for spacing
|
||||
.add_child(TextComponent::text(" "))
|
||||
.add_child(
|
||||
TextComponent::custom("pumpkin", "commands.pumpkin.website", locale, vec![])
|
||||
.color_named(NamedColor::White),
|
||||
)
|
||||
.add_child(
|
||||
TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.minecraft_version",
|
||||
locale,
|
||||
vec![
|
||||
TextComponent::text(CURRENT_MC_VERSION),
|
||||
TextComponent::text(format!("{CURRENT_MC_PROTOCOL}")),
|
||||
],
|
||||
)
|
||||
.click_event(ClickEvent::CopyToClipboard {
|
||||
value: Cow::from(
|
||||
get_translation_text(
|
||||
"pumpkin:commands.pumpkin.minecraft_version",
|
||||
locale,
|
||||
vec![
|
||||
TextComponent::text(CURRENT_MC_VERSION).0,
|
||||
TextComponent::text(format!("{CURRENT_MC_PROTOCOL}")).0,
|
||||
],
|
||||
)
|
||||
.replace('\n', ""),
|
||||
),
|
||||
})
|
||||
.hover_event(HoverEvent::show_text(TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.minecraft_version.hover",
|
||||
locale,
|
||||
vec![],
|
||||
)))
|
||||
.color_named(NamedColor::Gold),
|
||||
)
|
||||
// https://pumpkinmc.org/
|
||||
.add_child(
|
||||
TextComponent::custom("pumpkin", "commands.pumpkin.github", locale, vec![])
|
||||
.click_event(ClickEvent::OpenUrl {
|
||||
url: Cow::from("https://github.com/Pumpkin-MC/Pumpkin"),
|
||||
})
|
||||
.hover_event(HoverEvent::show_text(TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.github.hover",
|
||||
locale,
|
||||
vec![],
|
||||
)))
|
||||
.color_named(NamedColor::Blue)
|
||||
.bold()
|
||||
.underlined(),
|
||||
)
|
||||
// Added docs. and a space for spacing
|
||||
.add_child(TextComponent::text(" "))
|
||||
.add_child(
|
||||
TextComponent::custom(
|
||||
"pumpkin",
|
||||
"commands.pumpkin.website",
|
||||
locale,
|
||||
vec![],
|
||||
)
|
||||
.click_event(ClickEvent::OpenUrl {
|
||||
url: Cow::from("https://pumpkinmc.org/"),
|
||||
})
|
||||
@@ -142,10 +145,11 @@ impl CommandExecutor for Executor {
|
||||
.color_named(NamedColor::Blue)
|
||||
.bold()
|
||||
.underlined(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
),
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::world::SAY_COMMAND;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{Arg, ConsumedArgs, message::MsgArgConsumer},
|
||||
tree::CommandTree,
|
||||
tree::builder::argument,
|
||||
tree::{CommandTree, builder::argument},
|
||||
};
|
||||
use CommandError::InvalidConsumption;
|
||||
|
||||
@@ -18,27 +16,28 @@ const ARG_MESSAGE: &str = "message";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_MESSAGE.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_MESSAGE.into())));
|
||||
};
|
||||
|
||||
server
|
||||
.broadcast_message(
|
||||
&TextComponent::text(msg.clone()),
|
||||
&TextComponent::text(format!("{sender}")),
|
||||
SAY_COMMAND,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
server
|
||||
.broadcast_message(
|
||||
&TextComponent::text(msg.clone()),
|
||||
&TextComponent::text(format!("{sender}")),
|
||||
SAY_COMMAND,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::click::ClickEvent;
|
||||
use pumpkin_util::text::hover::HoverEvent;
|
||||
use pumpkin_util::text::{TextComponent, color::NamedColor};
|
||||
@@ -13,43 +13,44 @@ const DESCRIPTION: &str = "Displays the world seed.";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let seed = match sender {
|
||||
CommandSender::Player(player) => player.living_entity.entity.world.level.seed.0,
|
||||
// TODO: Maybe ask player for world, or get the current world
|
||||
_ => match server.worlds.read().await.first() {
|
||||
Some(world) => world.level.seed.0,
|
||||
None => {
|
||||
return Err(CommandError::CommandFailed(Box::new(TextComponent::text(
|
||||
"Unable to get Seed",
|
||||
))));
|
||||
}
|
||||
},
|
||||
};
|
||||
let seed = (seed as i64).to_string();
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let seed = match sender {
|
||||
CommandSender::Player(player) => player.living_entity.entity.world.level.seed.0,
|
||||
// TODO: Maybe ask player for world, or get the current world
|
||||
_ => match server.worlds.read().await.first() {
|
||||
Some(world) => world.level.seed.0,
|
||||
None => {
|
||||
return Err(CommandError::CommandFailed(Box::new(TextComponent::text(
|
||||
"Unable to get Seed",
|
||||
))));
|
||||
}
|
||||
},
|
||||
};
|
||||
let seed = (seed as i64).to_string();
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.seed.success",
|
||||
[TextComponent::text(seed.clone())
|
||||
.hover_event(HoverEvent::show_text(TextComponent::translate(
|
||||
Cow::from("chat.copy.click"),
|
||||
[],
|
||||
)))
|
||||
.click_event(ClickEvent::CopyToClipboard {
|
||||
value: Cow::from(seed),
|
||||
})
|
||||
.color_named(NamedColor::Green)],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.seed.success",
|
||||
[TextComponent::text(seed.clone())
|
||||
.hover_event(HoverEvent::show_text(TextComponent::translate(
|
||||
Cow::from("chat.copy.click"),
|
||||
[],
|
||||
)))
|
||||
.click_event(ClickEvent::CopyToClipboard {
|
||||
value: Cow::from(seed),
|
||||
})
|
||||
.color_named(NamedColor::Green)],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
@@ -7,7 +6,7 @@ use crate::command::args::position_block::BlockPosArgumentConsumer;
|
||||
use crate::command::args::{ConsumedArgs, FindArg};
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::tree::builder::{argument, literal};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender};
|
||||
|
||||
const NAMES: [&str; 1] = ["setblock"];
|
||||
|
||||
@@ -30,57 +29,35 @@ enum Mode {
|
||||
|
||||
struct Executor(Mode);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
|
||||
let block_state_id = block.default_state.id;
|
||||
let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?;
|
||||
let mode = self.0;
|
||||
let world = match sender {
|
||||
CommandSender::Console | CommandSender::Rcon(_) => {
|
||||
let guard = server.worlds.read().await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
|
||||
let block_state_id = block.default_state.id;
|
||||
let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?;
|
||||
let mode = self.0;
|
||||
let world = match sender {
|
||||
CommandSender::Console | CommandSender::Rcon(_) => {
|
||||
let guard = server.worlds.read().await;
|
||||
|
||||
guard
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or(CommandError::InvalidRequirement)?
|
||||
}
|
||||
CommandSender::Player(player) => player.world().clone(),
|
||||
};
|
||||
let success = match mode {
|
||||
Mode::Destroy => {
|
||||
world
|
||||
.clone()
|
||||
.break_block(&pos, None, BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE)
|
||||
.await;
|
||||
world
|
||||
.set_block_state(
|
||||
&pos,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS,
|
||||
)
|
||||
.await;
|
||||
true
|
||||
}
|
||||
Mode::Replace => {
|
||||
world
|
||||
.set_block_state(
|
||||
&pos,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS,
|
||||
)
|
||||
.await;
|
||||
true
|
||||
}
|
||||
Mode::Keep => {
|
||||
let old_state = world.get_block_state(&pos).await;
|
||||
if old_state.is_air() {
|
||||
guard
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or(CommandError::InvalidRequirement)?
|
||||
}
|
||||
CommandSender::Player(player) => player.world().clone(),
|
||||
};
|
||||
let success = match mode {
|
||||
Mode::Destroy => {
|
||||
world
|
||||
.clone()
|
||||
.break_block(&pos, None, BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE)
|
||||
.await;
|
||||
world
|
||||
.set_block_state(
|
||||
&pos,
|
||||
@@ -89,28 +66,51 @@ impl CommandExecutor for Executor {
|
||||
)
|
||||
.await;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
Mode::Replace => {
|
||||
world
|
||||
.set_block_state(
|
||||
&pos,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS,
|
||||
)
|
||||
.await;
|
||||
true
|
||||
}
|
||||
Mode::Keep => {
|
||||
let old_state = world.get_block_state(&pos).await;
|
||||
if old_state.is_air() {
|
||||
world
|
||||
.set_block_state(
|
||||
&pos,
|
||||
block_state_id,
|
||||
BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS,
|
||||
)
|
||||
.await;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sender
|
||||
.send_message(if success {
|
||||
TextComponent::translate(
|
||||
"commands.setblock.success",
|
||||
[
|
||||
TextComponent::text(pos.0.x.to_string()),
|
||||
TextComponent::text(pos.0.y.to_string()),
|
||||
TextComponent::text(pos.0.z.to_string()),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate("commands.setblock.failed", [])
|
||||
})
|
||||
.await;
|
||||
sender
|
||||
.send_message(if success {
|
||||
TextComponent::translate(
|
||||
"commands.setblock.success",
|
||||
[
|
||||
TextComponent::text(pos.0.x.to_string()),
|
||||
TextComponent::text(pos.0.y.to_string()),
|
||||
TextComponent::text(pos.0.z.to_string()),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate("commands.setblock.failed", [])
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::dispatcher::CommandError::InvalidConsumption;
|
||||
use crate::command::{
|
||||
CommandExecutor, CommandSender,
|
||||
@@ -9,7 +10,6 @@ use crate::command::{
|
||||
tree::{CommandTree, builder::argument},
|
||||
};
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_registry::VanillaDimensionType;
|
||||
use pumpkin_util::{math::position::BlockPos, text::TextComponent};
|
||||
|
||||
@@ -23,72 +23,75 @@ const ARG_ANGLE: &str = "angle";
|
||||
|
||||
struct NoArgsWorldSpawnExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for NoArgsWorldSpawnExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(block_pos) = sender.position() else {
|
||||
let level_info_guard = server.level_info.read().await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.setworldspawn.success",
|
||||
[
|
||||
TextComponent::text(level_info_guard.spawn_x.to_string()),
|
||||
TextComponent::text(level_info_guard.spawn_y.to_string()),
|
||||
TextComponent::text(level_info_guard.spawn_z.to_string()),
|
||||
TextComponent::text(level_info_guard.spawn_angle.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(block_pos) = sender.position() else {
|
||||
let level_info_guard = server.level_info.read().await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.setworldspawn.success",
|
||||
[
|
||||
TextComponent::text(level_info_guard.spawn_x.to_string()),
|
||||
TextComponent::text(level_info_guard.spawn_y.to_string()),
|
||||
TextComponent::text(level_info_guard.spawn_z.to_string()),
|
||||
TextComponent::text(level_info_guard.spawn_angle.to_string()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
|
||||
return Ok(());
|
||||
};
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
setworldspawn(sender, server, block_pos.to_block_pos(), 0.0).await
|
||||
setworldspawn(sender, server, block_pos.to_block_pos(), 0.0).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultWorldSpawnExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for DefaultWorldSpawnExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into())));
|
||||
};
|
||||
|
||||
setworldspawn(sender, server, *block_pos, 0.0).await
|
||||
setworldspawn(sender, server, *block_pos, 0.0).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct AngleWorldSpawnExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for AngleWorldSpawnExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into())));
|
||||
};
|
||||
|
||||
let Some(Arg::Rotation(_, yaw)) = args.get(ARG_ANGLE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_ANGLE.into())));
|
||||
};
|
||||
let Some(Arg::Rotation(_, yaw)) = args.get(ARG_ANGLE) else {
|
||||
return Err(InvalidConsumption(Some(ARG_ANGLE.into())));
|
||||
};
|
||||
|
||||
setworldspawn(sender, server, *block_pos, *yaw).await
|
||||
setworldspawn(sender, server, *block_pos, *yaw).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::color::NamedColor;
|
||||
|
||||
use crate::command::args::ConsumedArgs;
|
||||
use crate::command::tree::CommandTree;
|
||||
use crate::command::{CommandError, CommandExecutor, CommandSender};
|
||||
use crate::command::{CommandExecutor, CommandResult, CommandSender};
|
||||
use crate::stop_server;
|
||||
|
||||
const NAMES: [&str; 1] = ["stop"];
|
||||
@@ -13,21 +12,23 @@ const DESCRIPTION: &str = "Stop the server.";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.stop.stopping", []).color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
stop_server();
|
||||
Ok(())
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.stop.stopping", [])
|
||||
.color_named(NamedColor::Red),
|
||||
)
|
||||
.await;
|
||||
stop_server();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use crate::command::{
|
||||
CommandExecutor, CommandSender,
|
||||
CommandExecutor, CommandResult, CommandSender,
|
||||
args::{
|
||||
ConsumedArgs, FindArg, players::PlayersArgumentConsumer, sound::SoundArgumentConsumer,
|
||||
sound_category::SoundCategoryArgumentConsumer,
|
||||
},
|
||||
dispatcher::CommandError,
|
||||
tree::{CommandTree, builder::argument},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::resource_location::ResourceLocation;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
@@ -20,54 +18,55 @@ const ARG_SOUND: &str = "sound";
|
||||
|
||||
pub struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let category = SoundCategoryArgumentConsumer::find_arg(args, ARG_SOURCE);
|
||||
let sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND);
|
||||
let category = SoundCategoryArgumentConsumer::find_arg(args, ARG_SOURCE);
|
||||
let sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND);
|
||||
|
||||
for target in targets {
|
||||
target
|
||||
.stop_sound(
|
||||
sound
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.map(|s| ResourceLocation::vanilla(s.to_name()))
|
||||
.ok(),
|
||||
category.as_ref().map(|s| **s).ok(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
let text = match (category, sound) {
|
||||
(Ok(c), Ok(s)) => TextComponent::translate(
|
||||
"commands.stopsound.success.source.sound",
|
||||
[
|
||||
TextComponent::text(s.to_name()),
|
||||
TextComponent::text(c.to_name()),
|
||||
],
|
||||
),
|
||||
(Ok(c), Err(_)) => TextComponent::translate(
|
||||
"commands.stopsound.success.source.any",
|
||||
[TextComponent::text(c.to_name())],
|
||||
),
|
||||
(Err(_), Ok(s)) => TextComponent::translate(
|
||||
"commands.stopsound.success.sourceless.sound",
|
||||
[TextComponent::text(s.to_name())],
|
||||
),
|
||||
(Err(_), Err(_)) => {
|
||||
TextComponent::translate("commands.stopsound.success.sourceless.any", [])
|
||||
for target in targets {
|
||||
target
|
||||
.stop_sound(
|
||||
sound
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.map(|s| ResourceLocation::vanilla(s.to_name()))
|
||||
.ok(),
|
||||
category.as_ref().map(|s| **s).ok(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
};
|
||||
sender.send_message(text).await;
|
||||
let text = match (category, sound) {
|
||||
(Ok(c), Ok(s)) => TextComponent::translate(
|
||||
"commands.stopsound.success.source.sound",
|
||||
[
|
||||
TextComponent::text(s.to_name()),
|
||||
TextComponent::text(c.to_name()),
|
||||
],
|
||||
),
|
||||
(Ok(c), Err(_)) => TextComponent::translate(
|
||||
"commands.stopsound.success.source.any",
|
||||
[TextComponent::text(c.to_name())],
|
||||
),
|
||||
(Err(_), Ok(s)) => TextComponent::translate(
|
||||
"commands.stopsound.success.sourceless.sound",
|
||||
[TextComponent::text(s.to_name())],
|
||||
),
|
||||
(Err(_), Err(_)) => {
|
||||
TextComponent::translate("commands.stopsound.success.sourceless.any", [])
|
||||
}
|
||||
};
|
||||
sender.send_message(text).await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::{math::vector3::Vector3, text::TextComponent};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{
|
||||
ConsumedArgs, FindArg, position_3d::Position3DArgumentConsumer,
|
||||
summonable_entities::SummonableEntitiesArgumentConsumer,
|
||||
@@ -23,52 +22,53 @@ const ARG_POS: &str = "pos";
|
||||
|
||||
struct Executor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let entity = SummonableEntitiesArgumentConsumer::find_arg(args, ARG_ENTITY)?;
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS);
|
||||
let (world, pos) = match sender {
|
||||
CommandSender::Console | CommandSender::Rcon(_) => {
|
||||
let guard = server.worlds.read().await;
|
||||
let world = guard
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or(CommandError::InvalidRequirement)?;
|
||||
let pos = {
|
||||
let info = &world.level_info.read().await;
|
||||
// default position for spawning a player, in this case for mob
|
||||
pos.unwrap_or(Vector3::new(
|
||||
f64::from(info.spawn_x) + 0.5,
|
||||
f64::from(info.spawn_y) + 1.0,
|
||||
f64::from(info.spawn_z) + 0.5,
|
||||
))
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let entity = SummonableEntitiesArgumentConsumer::find_arg(args, ARG_ENTITY)?;
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS);
|
||||
let (world, pos) = match sender {
|
||||
CommandSender::Console | CommandSender::Rcon(_) => {
|
||||
let guard = server.worlds.read().await;
|
||||
let world = guard
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or(CommandError::InvalidRequirement)?;
|
||||
let pos = {
|
||||
let info = &world.level_info.read().await;
|
||||
// default position for spawning a player, in this case for mob
|
||||
pos.unwrap_or(Vector3::new(
|
||||
f64::from(info.spawn_x) + 0.5,
|
||||
f64::from(info.spawn_y) + 1.0,
|
||||
f64::from(info.spawn_z) + 0.5,
|
||||
))
|
||||
};
|
||||
|
||||
(world, pos)
|
||||
}
|
||||
CommandSender::Player(player) => {
|
||||
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
|
||||
(world, pos)
|
||||
}
|
||||
CommandSender::Player(player) => {
|
||||
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
|
||||
|
||||
(player.world().clone(), pos)
|
||||
}
|
||||
};
|
||||
let mob = from_type(entity, pos, &world, Uuid::new_v4()).await;
|
||||
world.spawn_entity(mob).await;
|
||||
(player.world().clone(), pos)
|
||||
}
|
||||
};
|
||||
let mob = from_type(entity, pos, &world, Uuid::new_v4()).await;
|
||||
world.spawn_entity(mob).await;
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.summon.success",
|
||||
[TextComponent::text(format!("{entity:?}"))],
|
||||
))
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.summon.success",
|
||||
[TextComponent::text(format!("{entity:?}"))],
|
||||
))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::CommandError;
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::args::ConsumedArgs;
|
||||
use crate::command::args::FindArg;
|
||||
use crate::command::args::entities::EntitiesArgumentConsumer;
|
||||
@@ -53,256 +53,264 @@ fn yaw_pitch_facing_position(
|
||||
|
||||
struct EntitiesToEntityExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for EntitiesToEntityExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
|
||||
let pos = destination.get_entity().pos.load();
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
for target in targets {
|
||||
let base_entity = target.get_entity();
|
||||
let yaw = base_entity.yaw.load();
|
||||
let pitch = base_entity.pitch.load();
|
||||
let world = base_entity.world.clone();
|
||||
target
|
||||
.clone()
|
||||
.teleport(pos, yaw.into(), pitch.into(), world)
|
||||
.await;
|
||||
}
|
||||
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
|
||||
let pos = destination.get_entity().pos.load();
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
for target in targets {
|
||||
let base_entity = target.get_entity();
|
||||
let yaw = base_entity.yaw.load();
|
||||
let pitch = base_entity.pitch.load();
|
||||
let world = base_entity.world.clone();
|
||||
target
|
||||
.clone()
|
||||
.teleport(pos, yaw.into(), pitch.into(), world)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct EntitiesToPosFacingPosExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for EntitiesToPosFacingPosExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
let facing_pos = Position3DArgumentConsumer::find_arg(args, ARG_FACING_LOCATION)?;
|
||||
let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_pos);
|
||||
//todo
|
||||
let world = match sender {
|
||||
CommandSender::Rcon(_) | CommandSender::Console => {
|
||||
server.worlds.read().await.first().unwrap().clone()
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
CommandSender::Player(player) => player.world().clone(),
|
||||
};
|
||||
let facing_pos = Position3DArgumentConsumer::find_arg(args, ARG_FACING_LOCATION)?;
|
||||
let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_pos);
|
||||
//todo
|
||||
let world = match sender {
|
||||
CommandSender::Rcon(_) | CommandSender::Console => {
|
||||
server.worlds.read().await.first().unwrap().clone()
|
||||
}
|
||||
CommandSender::Player(player) => player.world().clone(),
|
||||
};
|
||||
|
||||
for target in targets {
|
||||
target
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
||||
.await;
|
||||
}
|
||||
for target in targets {
|
||||
target
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct EntitiesToPosFacingEntityExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for EntitiesToPosFacingEntityExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
let facing_entity = EntityArgumentConsumer::find_arg(args, ARG_FACING_ENTITY)?;
|
||||
let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_entity.get_entity().pos.load());
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
let facing_entity = EntityArgumentConsumer::find_arg(args, ARG_FACING_ENTITY)?;
|
||||
let (yaw, pitch) =
|
||||
yaw_pitch_facing_position(&pos, &facing_entity.get_entity().pos.load());
|
||||
|
||||
for target in targets {
|
||||
target
|
||||
.clone()
|
||||
.teleport(
|
||||
pos,
|
||||
Some(yaw),
|
||||
Some(pitch),
|
||||
facing_entity.get_entity().world.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
for target in targets {
|
||||
target
|
||||
.clone()
|
||||
.teleport(
|
||||
pos,
|
||||
Some(yaw),
|
||||
Some(pitch),
|
||||
facing_entity.get_entity().world.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct EntitiesToPosWithRotationExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for EntitiesToPosWithRotationExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
_sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
let (yaw, pitch) = RotationArgumentConsumer::find_arg(args, ARG_ROTATION)?;
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
let (yaw, pitch) = RotationArgumentConsumer::find_arg(args, ARG_ROTATION)?;
|
||||
|
||||
// todo command context
|
||||
let world = server.worlds.read().await.first().unwrap().clone();
|
||||
for target in targets {
|
||||
target
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
||||
.await;
|
||||
}
|
||||
// todo command context
|
||||
let world = server.worlds.read().await.first().unwrap().clone();
|
||||
for target in targets {
|
||||
target
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct EntitiesToPosExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for EntitiesToPosExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
// todo command context
|
||||
let world = match sender {
|
||||
CommandSender::Rcon(_) | CommandSender::Console => {
|
||||
server.worlds.read().await.first().unwrap().clone()
|
||||
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
// todo command context
|
||||
let world = match sender {
|
||||
CommandSender::Rcon(_) | CommandSender::Console => {
|
||||
server.worlds.read().await.first().unwrap().clone()
|
||||
}
|
||||
CommandSender::Player(player) => player.world().clone(),
|
||||
};
|
||||
for target in targets {
|
||||
let yaw = target.get_entity().yaw.load();
|
||||
let pitch = target.get_entity().pitch.load();
|
||||
target
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
||||
.await;
|
||||
}
|
||||
CommandSender::Player(player) => player.world().clone(),
|
||||
};
|
||||
for target in targets {
|
||||
let yaw = target.get_entity().yaw.load();
|
||||
let pitch = target.get_entity().pitch.load();
|
||||
target
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct SelfToEntityExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for SelfToEntityExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
|
||||
let pos = destination.get_entity().pos.load();
|
||||
let world = destination.get_entity().world.clone();
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
|
||||
let pos = destination.get_entity().pos.load();
|
||||
let world = destination.get_entity().world.clone();
|
||||
|
||||
match sender {
|
||||
CommandSender::Player(player) => {
|
||||
let yaw = player.living_entity.entity.yaw.load();
|
||||
let pitch = player.living_entity.entity.pitch.load();
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
match sender {
|
||||
CommandSender::Player(player) => {
|
||||
let yaw = player.living_entity.entity.yaw.load();
|
||||
let pitch = player.living_entity.entity.pitch.load();
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
player
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), world)
|
||||
.await;
|
||||
}
|
||||
_ => {
|
||||
sender
|
||||
.send_message(TextComponent::translate("permissions.requires.player", []))
|
||||
.await;
|
||||
}
|
||||
player
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), world)
|
||||
.await;
|
||||
}
|
||||
_ => {
|
||||
sender
|
||||
.send_message(TextComponent::translate("permissions.requires.player", []))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
struct SelfToPosExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for SelfToPosExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
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();
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
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();
|
||||
if !World::is_valid(pos) {
|
||||
return Err(CommandError::CommandFailed(Box::new(
|
||||
TextComponent::translate("argument.pos.outofbounds", []),
|
||||
)));
|
||||
}
|
||||
player
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), player.world().clone())
|
||||
.await;
|
||||
}
|
||||
_ => {
|
||||
sender
|
||||
.send_message(TextComponent::translate("permissions.requires.player", []))
|
||||
.await;
|
||||
}
|
||||
player
|
||||
.clone()
|
||||
.teleport(pos, Some(yaw), Some(pitch), player.world().clone())
|
||||
.await;
|
||||
}
|
||||
_ => {
|
||||
sender
|
||||
.send_message(TextComponent::translate("permissions.requires.player", []))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{
|
||||
Arg, ConsumedArgs, FindArg, players::PlayersArgumentConsumer,
|
||||
textcomponent::TextComponentArgConsumer,
|
||||
},
|
||||
tree::CommandTree,
|
||||
tree::builder::argument,
|
||||
tree::{CommandTree, builder::argument},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
const NAMES: [&str; 1] = ["tellraw"];
|
||||
|
||||
@@ -19,23 +17,24 @@ const ARG_MESSAGE: &str = "message";
|
||||
|
||||
struct TellRawExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TellRawExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
_sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
_sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
let text = TextComponentArgConsumer::find_arg(args, ARG_MESSAGE)?;
|
||||
for target in targets {
|
||||
target.send_system_message(&text).await;
|
||||
}
|
||||
Ok(())
|
||||
let text = TextComponentArgConsumer::find_arg(args, ARG_MESSAGE)?;
|
||||
for target in targets {
|
||||
target.send_system_message(&text).await;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_config::BASIC_CONFIG;
|
||||
use pumpkin_util::text::{
|
||||
TextComponent,
|
||||
@@ -7,7 +6,7 @@ use pumpkin_util::text::{
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::command::{
|
||||
CommandExecutor, CommandSender,
|
||||
CommandExecutor, CommandResult, CommandSender,
|
||||
args::{
|
||||
ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, time::TimeArgumentConsumer,
|
||||
},
|
||||
@@ -199,104 +198,110 @@ impl TickExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TickExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let manager = &server.tick_rate_manager;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let manager = &server.tick_rate_manager;
|
||||
|
||||
match self.0 {
|
||||
SubCommand::Query => self.handle_query(sender, server, manager).await,
|
||||
SubCommand::Rate => {
|
||||
let rate = BoundedNumArgumentConsumer::<f32>::find_arg(args, "rate")??;
|
||||
manager.set_tick_rate(server, rate).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.tick.rate.success",
|
||||
[TextComponent::text(format!("{rate:.1}"))],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
SubCommand::RateLiteral(rate) => {
|
||||
manager.set_tick_rate(server, rate).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.tick.rate.success",
|
||||
[TextComponent::text(format!("{rate:.1}"))],
|
||||
))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
SubCommand::Freeze(freeze) => {
|
||||
manager.set_frozen(server, freeze).await;
|
||||
let message_key = if freeze {
|
||||
"commands.tick.status.frozen"
|
||||
} else {
|
||||
"commands.tick.status.running"
|
||||
};
|
||||
sender
|
||||
.send_message(TextComponent::translate(message_key, []))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
SubCommand::StepDefault => self.handle_step_command(sender, server, manager, 1).await,
|
||||
SubCommand::StepTimed => {
|
||||
let ticks = TimeArgumentConsumer::find_arg(args, "time")?;
|
||||
self.handle_step_command(sender, server, manager, ticks)
|
||||
.await
|
||||
}
|
||||
SubCommand::StepLiteral(ticks) => {
|
||||
self.handle_step_command(sender, server, manager, ticks)
|
||||
.await
|
||||
}
|
||||
SubCommand::StepStop => {
|
||||
if manager.stop_stepping(server).await {
|
||||
match self.0 {
|
||||
SubCommand::Query => self.handle_query(sender, server, manager).await,
|
||||
SubCommand::Rate => {
|
||||
let rate = BoundedNumArgumentConsumer::<f32>::find_arg(args, "rate")??;
|
||||
manager.set_tick_rate(server, rate).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.tick.step.stop.success",
|
||||
[],
|
||||
"commands.tick.rate.success",
|
||||
[TextComponent::text(format!("{rate:.1}"))],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.tick.step.stop.fail", []))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
SubCommand::SprintTimed => {
|
||||
let ticks = TimeArgumentConsumer::find_arg(args, "time")?;
|
||||
self.handle_sprint_command(sender, server, manager, ticks)
|
||||
.await
|
||||
}
|
||||
SubCommand::SprintLiteral(ticks) => {
|
||||
self.handle_sprint_command(sender, server, manager, ticks)
|
||||
.await
|
||||
}
|
||||
SubCommand::SprintStop => {
|
||||
if manager.stop_sprinting(server).await {
|
||||
SubCommand::RateLiteral(rate) => {
|
||||
manager.set_tick_rate(server, rate).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.tick.sprint.stop.success",
|
||||
[],
|
||||
"commands.tick.rate.success",
|
||||
[TextComponent::text(format!("{rate:.1}"))],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
SubCommand::Freeze(freeze) => {
|
||||
manager.set_frozen(server, freeze).await;
|
||||
let message_key = if freeze {
|
||||
"commands.tick.status.frozen"
|
||||
} else {
|
||||
"commands.tick.status.running"
|
||||
};
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.tick.sprint.stop.fail", [])
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.send_message(TextComponent::translate(message_key, []))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
SubCommand::StepDefault => {
|
||||
self.handle_step_command(sender, server, manager, 1).await
|
||||
}
|
||||
SubCommand::StepTimed => {
|
||||
let ticks = TimeArgumentConsumer::find_arg(args, "time")?;
|
||||
self.handle_step_command(sender, server, manager, ticks)
|
||||
.await
|
||||
}
|
||||
SubCommand::StepLiteral(ticks) => {
|
||||
self.handle_step_command(sender, server, manager, ticks)
|
||||
.await
|
||||
}
|
||||
SubCommand::StepStop => {
|
||||
if manager.stop_stepping(server).await {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.tick.step.stop.success",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.tick.step.stop.fail",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
SubCommand::SprintTimed => {
|
||||
let ticks = TimeArgumentConsumer::find_arg(args, "time")?;
|
||||
self.handle_sprint_command(sender, server, manager, ticks)
|
||||
.await
|
||||
}
|
||||
SubCommand::SprintLiteral(ticks) => {
|
||||
self.handle_sprint_command(sender, server, manager, ticks)
|
||||
.await
|
||||
}
|
||||
SubCommand::SprintStop => {
|
||||
if manager.stop_sprinting(server).await {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.tick.sprint.stop.success",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::translate("commands.tick.sprint.stop.fail", [])
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::color::{Color, NamedColor};
|
||||
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::args::{FindArg, time::TimeArgumentConsumer};
|
||||
use crate::command::tree::builder::{argument, literal};
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender, ConsumedArgs, tree::CommandTree,
|
||||
};
|
||||
use crate::command::{CommandExecutor, CommandSender, ConsumedArgs, tree::CommandTree};
|
||||
|
||||
const NAMES: [&str; 1] = ["time"];
|
||||
const DESCRIPTION: &str = "Query the world time.";
|
||||
@@ -46,107 +44,109 @@ enum QueryMode {
|
||||
|
||||
struct QueryExecutor(QueryMode);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for QueryExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let mode = self.0;
|
||||
// TODO: Maybe ask player for world, or get the current world
|
||||
let worlds = server.worlds.read().await;
|
||||
let world = worlds
|
||||
.first()
|
||||
.expect("There should always be at least one world");
|
||||
let level_time = world.level_time.lock().await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let mode = self.0;
|
||||
// TODO: Maybe ask player for world, or get the current world
|
||||
let worlds = server.worlds.read().await;
|
||||
let world = worlds
|
||||
.first()
|
||||
.expect("There should always be at least one world");
|
||||
let level_time = world.level_time.lock().await;
|
||||
|
||||
let msg = match mode {
|
||||
QueryMode::DayTime => {
|
||||
let curr_time = level_time.query_daytime();
|
||||
TextComponent::translate(
|
||||
"commands.time.query",
|
||||
[TextComponent::text(curr_time.to_string())],
|
||||
)
|
||||
}
|
||||
QueryMode::GameTime => {
|
||||
let curr_time = level_time.query_gametime();
|
||||
TextComponent::translate(
|
||||
"commands.time.query",
|
||||
[TextComponent::text(curr_time.to_string())],
|
||||
)
|
||||
}
|
||||
QueryMode::Day => {
|
||||
let curr_time = level_time.query_day();
|
||||
TextComponent::translate(
|
||||
"commands.time.query",
|
||||
[TextComponent::text(curr_time.to_string())],
|
||||
)
|
||||
}
|
||||
};
|
||||
let msg = match mode {
|
||||
QueryMode::DayTime => {
|
||||
let curr_time = level_time.query_daytime();
|
||||
TextComponent::translate(
|
||||
"commands.time.query",
|
||||
[TextComponent::text(curr_time.to_string())],
|
||||
)
|
||||
}
|
||||
QueryMode::GameTime => {
|
||||
let curr_time = level_time.query_gametime();
|
||||
TextComponent::translate(
|
||||
"commands.time.query",
|
||||
[TextComponent::text(curr_time.to_string())],
|
||||
)
|
||||
}
|
||||
QueryMode::Day => {
|
||||
let curr_time = level_time.query_day();
|
||||
TextComponent::translate(
|
||||
"commands.time.query",
|
||||
[TextComponent::text(curr_time.to_string())],
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
sender.send_message(msg).await;
|
||||
Ok(())
|
||||
sender.send_message(msg).await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ChangeExecutor(Mode);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ChangeExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let time_count = if let Mode::Set(Some(preset)) = &self.0 {
|
||||
preset.to_ticks()
|
||||
} else if let Ok(ticks) = TimeArgumentConsumer::find_arg(args, ARG_TIME) {
|
||||
ticks
|
||||
} else {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Invalid time specified.")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let time_count = if let Mode::Set(Some(preset)) = &self.0 {
|
||||
preset.to_ticks()
|
||||
} else if let Ok(ticks) = TimeArgumentConsumer::find_arg(args, ARG_TIME) {
|
||||
ticks
|
||||
} else {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Invalid time specified.")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mode = self.0;
|
||||
// TODO: Maybe ask player for world, or get the current world
|
||||
let worlds = server.worlds.read().await;
|
||||
let world = worlds
|
||||
.first()
|
||||
.expect("There should always be at least one world");
|
||||
let mut level_time = world.level_time.lock().await;
|
||||
let mode = self.0;
|
||||
// TODO: Maybe ask player for world, or get the current world
|
||||
let worlds = server.worlds.read().await;
|
||||
let world = worlds
|
||||
.first()
|
||||
.expect("There should always be at least one world");
|
||||
let mut level_time = world.level_time.lock().await;
|
||||
|
||||
let msg = match mode {
|
||||
Mode::Add => {
|
||||
// add
|
||||
level_time.add_time(time_count.into());
|
||||
level_time.send_time(world).await;
|
||||
let curr_time = level_time.query_daytime();
|
||||
TextComponent::translate(
|
||||
"commands.time.set",
|
||||
[TextComponent::text(curr_time.to_string())],
|
||||
)
|
||||
}
|
||||
Mode::Set(_) => {
|
||||
// set
|
||||
level_time.set_time(time_count.into());
|
||||
level_time.send_time(world).await;
|
||||
TextComponent::translate(
|
||||
"commands.time.set",
|
||||
[TextComponent::text(time_count.to_string())],
|
||||
)
|
||||
}
|
||||
};
|
||||
let msg = match mode {
|
||||
Mode::Add => {
|
||||
// add
|
||||
level_time.add_time(time_count.into());
|
||||
level_time.send_time(world).await;
|
||||
let curr_time = level_time.query_daytime();
|
||||
TextComponent::translate(
|
||||
"commands.time.set",
|
||||
[TextComponent::text(curr_time.to_string())],
|
||||
)
|
||||
}
|
||||
Mode::Set(_) => {
|
||||
// set
|
||||
level_time.set_time(time_count.into());
|
||||
level_time.send_time(world).await;
|
||||
TextComponent::translate(
|
||||
"commands.time.set",
|
||||
[TextComponent::text(time_count.to_string())],
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
sender.send_message(msg).await;
|
||||
Ok(())
|
||||
sender.send_message(msg).await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::java::client::play::CClearTitle;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::CommandResult;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::{
|
||||
command::{
|
||||
@@ -27,82 +27,84 @@ const ARG_TITLE: &str = "title";
|
||||
/// bool: Whether to reset or not
|
||||
struct ClearOrResetExecutor(bool);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ClearOrResetExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
let reset = self.0;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
let reset = self.0;
|
||||
|
||||
for target in targets {
|
||||
target.client.enqueue_packet(&CClearTitle::new(reset)).await;
|
||||
}
|
||||
sender
|
||||
.send_message(if targets.len() == 1 {
|
||||
let text = if reset {
|
||||
"commands.title.reset.single"
|
||||
for target in targets {
|
||||
target.client.enqueue_packet(&CClearTitle::new(reset)).await;
|
||||
}
|
||||
sender
|
||||
.send_message(if targets.len() == 1 {
|
||||
let text = if reset {
|
||||
"commands.title.reset.single"
|
||||
} else {
|
||||
"commands.title.cleared.single"
|
||||
};
|
||||
TextComponent::translate(text, [targets[0].get_display_name().await])
|
||||
} else {
|
||||
"commands.title.cleared.single"
|
||||
};
|
||||
TextComponent::translate(text, [targets[0].get_display_name().await])
|
||||
} else {
|
||||
let text = if reset {
|
||||
"commands.title.reset.multiple"
|
||||
} else {
|
||||
"commands.title.cleared.multiple"
|
||||
};
|
||||
TextComponent::translate(text, [TextComponent::text(targets.len().to_string())])
|
||||
})
|
||||
.await;
|
||||
let text = if reset {
|
||||
"commands.title.reset.multiple"
|
||||
} else {
|
||||
"commands.title.cleared.multiple"
|
||||
};
|
||||
TextComponent::translate(text, [TextComponent::text(targets.len().to_string())])
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct TitleExecutor(TitleMode);
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TitleExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
let text = TextComponentArgConsumer::find_arg(args, ARG_TITLE)?;
|
||||
let text = TextComponentArgConsumer::find_arg(args, ARG_TITLE)?;
|
||||
|
||||
let mode = &self.0;
|
||||
let mode = &self.0;
|
||||
|
||||
for target in targets {
|
||||
target.show_title(&text, mode).await;
|
||||
}
|
||||
for target in targets {
|
||||
target.show_title(&text, mode).await;
|
||||
}
|
||||
|
||||
let mode_name = format!("{mode:?}").to_lowercase();
|
||||
sender
|
||||
.send_message(if targets.len() == 1 {
|
||||
TextComponent::translate(
|
||||
format!("commands.title.show.{mode_name}.single"),
|
||||
[targets[0].get_display_name().await],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate(
|
||||
format!("commands.title.show.{mode_name}.multiple"),
|
||||
[TextComponent::text(targets.len().to_string())],
|
||||
)
|
||||
})
|
||||
.await;
|
||||
let mode_name = format!("{mode:?}").to_lowercase();
|
||||
sender
|
||||
.send_message(if targets.len() == 1 {
|
||||
TextComponent::translate(
|
||||
format!("commands.title.show.{mode_name}.single"),
|
||||
[targets[0].get_display_name().await],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate(
|
||||
format!("commands.title.show.{mode_name}.multiple"),
|
||||
[TextComponent::text(targets.len().to_string())],
|
||||
)
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_protocol::java::client::play::CTransfer;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::color::{Color, NamedColor};
|
||||
|
||||
use crate::command::CommandResult;
|
||||
use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
|
||||
use crate::command::args::players::PlayersArgumentConsumer;
|
||||
use crate::command::args::simple::SimpleArgConsumer;
|
||||
use crate::command::args::{Arg, FindArgDefaultName};
|
||||
use crate::command::dispatcher::CommandError::{InvalidConsumption, InvalidRequirement};
|
||||
use crate::command::tree::builder::{argument, argument_default_name, require};
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree,
|
||||
};
|
||||
use crate::command::{CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree};
|
||||
|
||||
const NAMES: [&str; 1] = ["transfer"];
|
||||
|
||||
@@ -31,89 +29,91 @@ fn port_consumer() -> BoundedNumArgumentConsumer<i32> {
|
||||
|
||||
struct TargetSelfExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TargetSelfExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(hostname)) = args.get(ARG_HOSTNAME) else {
|
||||
return Err(InvalidConsumption(Some(ARG_HOSTNAME.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(hostname)) = args.get(ARG_HOSTNAME) else {
|
||||
return Err(InvalidConsumption(Some(ARG_HOSTNAME.into())));
|
||||
};
|
||||
|
||||
let port = match port_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 25565,
|
||||
Ok(Ok(count)) => count,
|
||||
Ok(Err(_)) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Port must be between 1 and 65535.")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
let port = match port_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 25565,
|
||||
Ok(Ok(count)) => count,
|
||||
Ok(Err(_)) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Port must be between 1 and 65535.")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
if let CommandSender::Player(player) = sender {
|
||||
let name = &player.gameprofile.name;
|
||||
log::info!("[{name}: Transferring {name} to {hostname}:{port}]");
|
||||
player
|
||||
.client
|
||||
.enqueue_packet(&CTransfer::new(hostname, VarInt(port)))
|
||||
.await;
|
||||
return Ok(());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InvalidRequirement)
|
||||
}
|
||||
};
|
||||
|
||||
if let CommandSender::Player(player) = sender {
|
||||
let name = &player.gameprofile.name;
|
||||
log::info!("[{name}: Transferring {name} to {hostname}:{port}]");
|
||||
player
|
||||
.client
|
||||
.enqueue_packet(&CTransfer::new(hostname, VarInt(port)))
|
||||
.await;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InvalidRequirement)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct TargetPlayerExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for TargetPlayerExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Simple(hostname)) = args.get(ARG_HOSTNAME) else {
|
||||
return Err(InvalidConsumption(Some(ARG_HOSTNAME.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Simple(hostname)) = args.get(ARG_HOSTNAME) else {
|
||||
return Err(InvalidConsumption(Some(ARG_HOSTNAME.into())));
|
||||
};
|
||||
|
||||
let port = match port_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 25565,
|
||||
Ok(Ok(count)) => count,
|
||||
Ok(Err(_)) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Port must be between 1 and 65535.")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
let port = match port_consumer().find_arg_default_name(args) {
|
||||
Err(_) => 25565,
|
||||
Ok(Ok(count)) => count,
|
||||
Ok(Err(_)) => {
|
||||
sender
|
||||
.send_message(
|
||||
TextComponent::text("Port must be between 1 and 65535.")
|
||||
.color(Color::Named(NamedColor::Red)),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let Some(Arg::Players(players)) = args.get(ARG_PLAYERS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_PLAYERS.into())));
|
||||
};
|
||||
|
||||
for p in players {
|
||||
p.client
|
||||
.enqueue_packet(&CTransfer::new(hostname, VarInt(port)))
|
||||
.await;
|
||||
return Ok(());
|
||||
log::info!(
|
||||
"[{sender}: Transferring {} to {hostname}:{port}]",
|
||||
p.gameprofile.name
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let Some(Arg::Players(players)) = args.get(ARG_PLAYERS) else {
|
||||
return Err(InvalidConsumption(Some(ARG_PLAYERS.into())));
|
||||
};
|
||||
|
||||
for p in players {
|
||||
p.client
|
||||
.enqueue_packet(&CTransfer::new(hostname, VarInt(port)))
|
||||
.await;
|
||||
log::info!(
|
||||
"[{sender}: Transferring {} to {hostname}:{port}]",
|
||||
p.gameprofile.name
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::{
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
CommandError, CommandExecutor, CommandResult, CommandSender,
|
||||
args::{ConsumedArgs, FindArg, time::TimeArgumentConsumer},
|
||||
tree::CommandTree,
|
||||
tree::builder::{argument, literal},
|
||||
tree::{
|
||||
CommandTree,
|
||||
builder::{argument, literal},
|
||||
},
|
||||
};
|
||||
|
||||
const NAMES: [&str; 1] = ["weather"];
|
||||
@@ -22,53 +23,54 @@ enum WeatherMode {
|
||||
Thunder,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Executor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let duration = TimeArgumentConsumer::find_arg(args, ARG_DURATION).unwrap_or(6000);
|
||||
let world = {
|
||||
let guard = server.worlds.read().await;
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let duration = TimeArgumentConsumer::find_arg(args, ARG_DURATION).unwrap_or(6000);
|
||||
let world = {
|
||||
let guard = server.worlds.read().await;
|
||||
|
||||
guard
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or(CommandError::InvalidRequirement)?
|
||||
};
|
||||
let mut weather = world.weather.lock().await;
|
||||
guard
|
||||
.first()
|
||||
.cloned()
|
||||
.ok_or(CommandError::InvalidRequirement)?
|
||||
};
|
||||
let mut weather = world.weather.lock().await;
|
||||
|
||||
match self.mode {
|
||||
WeatherMode::Clear => {
|
||||
weather
|
||||
.set_weather_parameters(&world, duration, 0, false, false)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.weather.set.clear", []))
|
||||
.await;
|
||||
match self.mode {
|
||||
WeatherMode::Clear => {
|
||||
weather
|
||||
.set_weather_parameters(&world, duration, 0, false, false)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.weather.set.clear", []))
|
||||
.await;
|
||||
}
|
||||
WeatherMode::Rain => {
|
||||
weather
|
||||
.set_weather_parameters(&world, 0, duration, true, false)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.weather.set.rain", []))
|
||||
.await;
|
||||
}
|
||||
WeatherMode::Thunder => {
|
||||
weather
|
||||
.set_weather_parameters(&world, 0, duration, true, true)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.weather.set.thunder", []))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
WeatherMode::Rain => {
|
||||
weather
|
||||
.set_weather_parameters(&world, 0, duration, true, false)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.weather.set.rain", []))
|
||||
.await;
|
||||
}
|
||||
WeatherMode::Thunder => {
|
||||
weather
|
||||
.set_weather_parameters(&world, 0, duration, true, true)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.weather.set.thunder", []))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_config::{BASIC_CONFIG, whitelist::WhitelistEntry};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
use crate::command::CommandResult;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::{
|
||||
command::{
|
||||
@@ -46,205 +46,211 @@ async fn kick_non_whitelisted_players(server: &Server) {
|
||||
|
||||
struct OnExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for OnExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let previous = server.white_list.swap(true, Ordering::Relaxed);
|
||||
if previous {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.alreadyOn",
|
||||
&[],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
kick_non_whitelisted_players(server).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.whitelist.enabled", &[]))
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let previous = server.white_list.swap(true, Ordering::Relaxed);
|
||||
if previous {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.alreadyOn",
|
||||
&[],
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
kick_non_whitelisted_players(server).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.whitelist.enabled", &[]))
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct OffExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for OffExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let previous = server.white_list.swap(false, Ordering::Relaxed);
|
||||
if previous {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.whitelist.disabled", &[]))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.alreadyOff",
|
||||
&[],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let previous = server.white_list.swap(false, Ordering::Relaxed);
|
||||
if previous {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.whitelist.disabled", &[]))
|
||||
.await;
|
||||
} else {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.alreadyOff",
|
||||
&[],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ListExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ListExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let whitelist = &WHITELIST_CONFIG.read().await.whitelist;
|
||||
if whitelist.is_empty() {
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let whitelist = &WHITELIST_CONFIG.read().await.whitelist;
|
||||
if whitelist.is_empty() {
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.whitelist.none", []))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let names = whitelist
|
||||
.iter()
|
||||
.map(|entry| entry.name.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(", ");
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.whitelist.none", []))
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.list",
|
||||
[
|
||||
TextComponent::text(whitelist.len().to_string()),
|
||||
TextComponent::text(names),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let names = whitelist
|
||||
.iter()
|
||||
.map(|entry| entry.name.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(", ");
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.list",
|
||||
[
|
||||
TextComponent::text(whitelist.len().to_string()),
|
||||
TextComponent::text(names),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ReloadExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for ReloadExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
*WHITELIST_CONFIG.write().await = WhitelistConfig::load();
|
||||
kick_non_whitelisted_players(server).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.whitelist.reloaded", &[]))
|
||||
.await;
|
||||
Ok(())
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
*WHITELIST_CONFIG.write().await = WhitelistConfig::load();
|
||||
kick_non_whitelisted_players(server).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate("commands.whitelist.reloaded", &[]))
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AddExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for AddExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
_server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
_server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
let mut whitelist = WHITELIST_CONFIG.write().await;
|
||||
for player in targets {
|
||||
let profile = &player.gameprofile;
|
||||
if whitelist.is_whitelisted(profile) {
|
||||
let mut whitelist = WHITELIST_CONFIG.write().await;
|
||||
for player in targets {
|
||||
let profile = &player.gameprofile;
|
||||
if whitelist.is_whitelisted(profile) {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.add.failed",
|
||||
&[],
|
||||
))
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
whitelist
|
||||
.whitelist
|
||||
.push(WhitelistEntry::new(profile.id, profile.name.clone()));
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.add.failed",
|
||||
&[],
|
||||
"commands.whitelist.add.success",
|
||||
[TextComponent::text(profile.name.clone())],
|
||||
))
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
whitelist
|
||||
.whitelist
|
||||
.push(WhitelistEntry::new(profile.id, profile.name.clone()));
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.add.success",
|
||||
[TextComponent::text(profile.name.clone())],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
whitelist.save();
|
||||
Ok(())
|
||||
whitelist.save();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RemoveExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for RemoveExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a crate::server::Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
||||
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
||||
};
|
||||
|
||||
let mut whitelist = WHITELIST_CONFIG.write().await;
|
||||
for player in targets {
|
||||
let i = whitelist
|
||||
.whitelist
|
||||
.iter()
|
||||
.position(|entry| entry.uuid == player.gameprofile.id);
|
||||
let mut whitelist = WHITELIST_CONFIG.write().await;
|
||||
for player in targets {
|
||||
let i = whitelist
|
||||
.whitelist
|
||||
.iter()
|
||||
.position(|entry| entry.uuid == player.gameprofile.id);
|
||||
|
||||
match i {
|
||||
Some(i) => {
|
||||
whitelist.whitelist.remove(i);
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.remove.success",
|
||||
[player.get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
None => {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.remove.failed",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
match i {
|
||||
Some(i) => {
|
||||
whitelist.whitelist.remove(i);
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.remove.success",
|
||||
[player.get_display_name().await],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
None => {
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.whitelist.remove.failed",
|
||||
[],
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
whitelist.save();
|
||||
drop(whitelist);
|
||||
whitelist.save();
|
||||
drop(whitelist);
|
||||
|
||||
kick_non_whitelisted_players(server).await;
|
||||
Ok(())
|
||||
kick_non_whitelisted_players(server).await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -64,7 +64,7 @@ pub struct CommandDispatcher {
|
||||
impl CommandDispatcher {
|
||||
pub async fn handle_command<'a>(
|
||||
&'a self,
|
||||
sender: &mut CommandSender,
|
||||
sender: &CommandSender,
|
||||
server: &'a Server,
|
||||
cmd: &'a str,
|
||||
) {
|
||||
@@ -242,7 +242,7 @@ impl CommandDispatcher {
|
||||
/// Execute a command using its corresponding [`CommandTree`].
|
||||
pub(crate) async fn dispatch<'a>(
|
||||
&'a self,
|
||||
src: &mut CommandSender,
|
||||
src: &CommandSender,
|
||||
server: &'a Server,
|
||||
cmd: &'a str,
|
||||
) -> Result<(), CommandError> {
|
||||
@@ -302,7 +302,7 @@ impl CommandDispatcher {
|
||||
}
|
||||
|
||||
async fn try_is_fitting_path<'a>(
|
||||
src: &mut CommandSender,
|
||||
src: &'a CommandSender,
|
||||
server: &'a Server,
|
||||
path: &[usize],
|
||||
tree: &'a CommandTree,
|
||||
@@ -357,7 +357,7 @@ impl CommandDispatcher {
|
||||
}
|
||||
|
||||
async fn try_find_suggestions_on_path<'a>(
|
||||
src: &CommandSender,
|
||||
src: &'a CommandSender,
|
||||
server: &'a Server,
|
||||
path: &[usize],
|
||||
tree: &'a CommandTree,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::fmt;
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -6,7 +7,6 @@ use crate::entity::player::Player;
|
||||
use crate::server::Server;
|
||||
use crate::world::World;
|
||||
use args::ConsumedArgs;
|
||||
use async_trait::async_trait;
|
||||
|
||||
use dispatcher::CommandError;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
@@ -118,12 +118,13 @@ impl CommandSender {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait CommandExecutor: Sync {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
server: &Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError>;
|
||||
pub type CommandResult<'a> = Pin<Box<dyn Future<Output = Result<(), CommandError>> + Send + 'a>>;
|
||||
|
||||
pub trait CommandExecutor: Sync + Send {
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
sender: &'a CommandSender,
|
||||
server: &'a Server,
|
||||
args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a>;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl CommandTree {
|
||||
///
|
||||
/// Also see [`NonLeafNodeBuilder::execute`].
|
||||
#[must_use]
|
||||
pub fn execute(mut self, executor: impl CommandExecutor + 'static + Send) -> Self {
|
||||
pub fn execute(mut self, executor: impl CommandExecutor + 'static) -> Self {
|
||||
let node = Node {
|
||||
node_type: NodeType::ExecuteLeaf {
|
||||
executor: Arc::new(executor),
|
||||
@@ -116,7 +116,7 @@ impl NonLeafNodeBuilder {
|
||||
///
|
||||
/// Also see [`CommandTree::execute`].
|
||||
#[must_use]
|
||||
pub fn execute(mut self, executor: impl CommandExecutor + 'static + Send) -> Self {
|
||||
pub fn execute(mut self, executor: impl CommandExecutor + 'static) -> Self {
|
||||
self.leaf_nodes.push(LeafNodeBuilder {
|
||||
node_type: NodeType::ExecuteLeaf {
|
||||
executor: Arc::new(executor),
|
||||
@@ -149,7 +149,7 @@ pub fn literal(string: impl Into<String>) -> NonLeafNodeBuilder {
|
||||
/// reversed, so [`Vec::pop`] can be used to obtain args in ltr order.
|
||||
pub fn argument(
|
||||
name: impl Into<String>,
|
||||
consumer: impl ArgumentConsumer + 'static + Send,
|
||||
consumer: impl ArgumentConsumer + 'static,
|
||||
) -> NonLeafNodeBuilder {
|
||||
NonLeafNodeBuilder {
|
||||
node_type: NodeType::Argument {
|
||||
|
||||
@@ -489,7 +489,7 @@ async fn setup_stdin_console(server: Arc<Server>) {
|
||||
'after: {
|
||||
let dispatcher = &server.command_dispatcher.read().await;
|
||||
dispatcher
|
||||
.handle_command(&mut command::CommandSender::Console, &server, command.as_str())
|
||||
.handle_command(&command::CommandSender::Console, &server, command.as_str())
|
||||
.await;
|
||||
};
|
||||
}}
|
||||
@@ -522,7 +522,7 @@ fn setup_console(rl: Readline, server: Arc<Server>) {
|
||||
let dispatcher = server.command_dispatcher.read().await;
|
||||
|
||||
dispatcher
|
||||
.handle_command(&mut command::CommandSender::Console, &server, &line)
|
||||
.handle_command(&command::CommandSender::Console, &server, &line)
|
||||
.await;
|
||||
rl.add_history_entry(line).unwrap();
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ impl BedrockClient {
|
||||
let dispatcher = server_clone.command_dispatcher.read().await;
|
||||
dispatcher
|
||||
.handle_command(
|
||||
&mut CommandSender::Player(player_clone),
|
||||
&CommandSender::Player(player_clone),
|
||||
&server_clone,
|
||||
&command_clone,
|
||||
)
|
||||
|
||||
@@ -544,7 +544,7 @@ impl JavaClient {
|
||||
server.spawn_task(async move {
|
||||
server_clone.command_dispatcher.read().await
|
||||
.handle_command(
|
||||
&mut CommandSender::Player(player_clone),
|
||||
&CommandSender::Player(player_clone),
|
||||
&server_clone,
|
||||
&command_clone,
|
||||
)
|
||||
|
||||
@@ -131,7 +131,7 @@ impl RCONClient {
|
||||
.read()
|
||||
.await
|
||||
.handle_command(
|
||||
&mut crate::command::CommandSender::Rcon(output_clone),
|
||||
&crate::command::CommandSender::Rcon(output_clone),
|
||||
&server_clone,
|
||||
&packet_body,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user