This commit is contained in:
Alexander Medvedev 2026-05-01 14:51:45 +02:00
commit 81cffe19a2
3 changed files with 104 additions and 133 deletions

View File

@ -1,144 +1,120 @@
use crate::command::{ use crate::command::argument_builder::{ArgumentBuilder, command, literal};
CommandError, CommandExecutor, CommandResult, CommandSender, use crate::command::context::command_context::CommandContext;
args::{Arg, ConsumedArgs, simple::SimpleArgConsumer}, use crate::command::node::dispatcher::CommandDispatcher;
tree::{CommandTree, builder::argument}, use crate::command::node::{CommandExecutor, CommandExecutorResult};
};
use CommandError::InvalidConsumption;
use pumpkin_data::translation; use pumpkin_data::translation;
use pumpkin_util::PermissionLvl;
use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry};
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
const NAMES: [&str; 1] = ["banlist"]; const DESCRIPTION: &str = "Prints the banlist of players, IPs, or both at once.";
const DESCRIPTION: &str = "shows the banlist";
const ARG_LIST_TYPE: &str = "ips|players"; const PERMISSION: &str = "minecraft:command.banlist";
struct ListExecutor; struct BanListEntry {
name: String,
impl CommandExecutor for ListExecutor { source: String,
fn execute<'a>( reason: String,
&'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 = &server.data.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
}
"players" => {
let lock = &server.data.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
}
_ => Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMAND_UNKNOWN_ARGUMENT,
[],
))),
}
})
}
} }
struct ListAllExecutor; struct BanListCommandExecutor {
players: bool,
ips: bool,
}
impl CommandExecutor for ListAllExecutor { impl CommandExecutor for BanListCommandExecutor {
fn execute<'a>( fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
&'a self,
sender: &'a CommandSender,
server: &'a crate::server::Server,
_args: &'a ConsumedArgs<'a>,
) -> CommandResult<'a> {
Box::pin(async move { Box::pin(async move {
let mut entries = Vec::new(); let mut entries = Vec::new();
for entry in &server.data.banned_player_list.read().await.banned_players {
entries.push(( let data = &context.server().data;
entry.name.clone(),
entry.source.clone(), if self.players {
entry.reason.clone(), let lock = data.banned_player_list.read().await;
)); for entry in &lock.banned_players {
entries.push(BanListEntry {
name: entry.name.clone(),
source: entry.source.clone(),
reason: entry.reason.clone(),
});
}
} }
for entry in &server.data.banned_ip_list.read().await.banned_ips { if self.ips {
entries.push(( let lock = data.banned_ip_list.read().await;
entry.ip.to_string(), for entry in &lock.banned_ips {
entry.source.clone(), entries.push(BanListEntry {
entry.reason.clone(), name: entry.ip.to_string(),
)); source: entry.source.clone(),
reason: entry.reason.clone(),
});
}
} }
handle_banlist(entries, sender).await let entries_len = entries.len() as i32;
let source = &context.source;
if entries.is_empty() {
source
.send_feedback(
TextComponent::translate(translation::COMMANDS_BANLIST_NONE, []),
false,
)
.await;
} else {
source
.send_feedback(
TextComponent::translate(
translation::COMMANDS_BANLIST_LIST,
[TextComponent::text(entries.len().to_string())],
),
false,
)
.await;
for entry in entries {
source
.send_feedback(
TextComponent::translate(
translation::COMMANDS_BANLIST_ENTRY,
[
TextComponent::text(entry.name),
TextComponent::text(entry.source),
TextComponent::text(entry.reason),
],
),
false,
)
.await;
}
}
Ok(entries_len)
}) })
} }
} }
/// `Vec<(name, source, reason)>` pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
async fn handle_banlist( registry.register_permission_or_panic(Permission::new(
list: Vec<(String, String, String)>, PERMISSION,
sender: &CommandSender, DESCRIPTION,
) -> Result<i32, CommandError> { PermissionDefault::Op(PermissionLvl::Three),
if list.is_empty() { ));
return Result::Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_BANLIST_NONE, dispatcher.register(
[], command("banlist", DESCRIPTION)
))); .requires(PERMISSION)
} .then(literal("ips").executes(BanListCommandExecutor {
players: false,
sender ips: true,
.send_message(TextComponent::translate( }))
translation::COMMANDS_BANLIST_LIST, .then(literal("players").executes(BanListCommandExecutor {
[TextComponent::text(list.len().to_string())], players: true,
)) ips: false,
.await; }))
.executes(BanListCommandExecutor {
let count = list.len(); players: true,
ips: true,
for (name, source, reason) in list { }),
sender );
.send_message(TextComponent::translate(
translation::COMMANDS_BANLIST_ENTRY,
[
TextComponent::text(name),
TextComponent::text(source),
TextComponent::text(reason),
],
))
.await;
}
Ok(count as i32)
}
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION)
.execute(ListAllExecutor)
.then(argument(ARG_LIST_TYPE, SimpleArgConsumer).execute(ListExecutor))
} }

View File

@ -136,7 +136,6 @@ pub async fn default_dispatcher(
dispatcher.register(plugins::init_command_tree(), "pumpkin:command.plugins"); dispatcher.register(plugins::init_command_tree(), "pumpkin:command.plugins");
dispatcher.register(ban::init_command_tree(), "minecraft:command.ban"); dispatcher.register(ban::init_command_tree(), "minecraft:command.ban");
dispatcher.register(banip::init_command_tree(), "minecraft:command.banip"); dispatcher.register(banip::init_command_tree(), "minecraft:command.banip");
dispatcher.register(banlist::init_command_tree(), "minecraft:command.banlist");
dispatcher.register(pardon::init_command_tree(), "minecraft:command.pardon"); dispatcher.register(pardon::init_command_tree(), "minecraft:command.pardon");
dispatcher.register(pardonip::init_command_tree(), "minecraft:command.pardonip"); dispatcher.register(pardonip::init_command_tree(), "minecraft:command.pardonip");
dispatcher.register( dispatcher.register(
@ -151,6 +150,7 @@ pub async fn default_dispatcher(
wrapper_dispatcher wrapper_dispatcher
}; };
banlist::register(&mut dispatcher, registry);
difficulty::register(&mut dispatcher, registry); difficulty::register(&mut dispatcher, registry);
help::register(&mut dispatcher, registry); help::register(&mut dispatcher, registry);
kill::register(&mut dispatcher, registry); kill::register(&mut dispatcher, registry);
@ -459,13 +459,6 @@ fn register_level_3_permissions(registry: &mut PermissionRegistry) {
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .unwrap();
registry
.register_permission(Permission::new(
"minecraft:command.banlist",
"Displays banned players or IP addresses",
PermissionDefault::Op(PermissionLvl::Three),
))
.unwrap();
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.pardon", "minecraft:command.pardon",

View File

@ -61,10 +61,12 @@ pub async fn start_query_handler(server: Arc<Server>, query_addr: SocketAddr) {
() = STOP_INTERRUPT.cancelled() => None, () = STOP_INTERRUPT.cancelled() => None,
}; };
let Some(Ok((_, addr))) = recv_result else { let Some(Ok((length, addr))) = recv_result else {
break; break;
}; };
buf.truncate(length);
tokio::spawn(async move { tokio::spawn(async move {
if let Err(err) = handle_packet( if let Err(err) = handle_packet(
buf, buf,