Pumpkin/pumpkin/src/command/commands/whitelist.rs
Alexander Medvedev e45ab613f4 chore: parse translations at compile time
not finished but its much safer and nicer now
2026-02-08 13:22:36 +01:00

287 lines
8.8 KiB
Rust

use std::sync::atomic::Ordering;
use pumpkin_config::whitelist::WhitelistEntry;
use pumpkin_data::translation;
use pumpkin_util::text::TextComponent;
use crate::command::CommandResult;
use crate::entity::EntityBase;
use crate::{
command::{
CommandExecutor, CommandSender,
args::{Arg, ConsumedArgs, players::PlayersArgumentConsumer},
dispatcher::CommandError,
tree::{
CommandTree,
builder::{argument, literal},
},
},
data::{LoadJSONConfiguration, SaveJSONConfiguration, whitelist::WhitelistConfig},
net::DisconnectReason,
server::Server,
};
const NAMES: [&str; 1] = ["whitelist"];
const DESCRIPTION: &str = "Manage server whitelists.";
const ARG_TARGETS: &str = "targets";
async fn kick_non_whitelisted_players(server: &Server) {
let whitelist = server.data.whitelist_config.read().await;
if server.basic_config.enforce_whitelist && server.white_list.load(Ordering::Relaxed) {
for player in server.get_all_players() {
if whitelist.is_whitelisted(&player.gameprofile) {
continue;
}
player
.kick(
DisconnectReason::Kicked,
TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_NOT_WHITELISTED,
&[],
),
)
.await;
}
}
}
struct OnExecutor;
impl CommandExecutor for OnExecutor {
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 {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_WHITELIST_ALREADYON,
&[],
)))
} else {
kick_non_whitelisted_players(server).await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_ENABLED,
&[],
))
.await;
Ok(1)
}
})
}
}
struct OffExecutor;
impl CommandExecutor for OffExecutor {
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(
translation::COMMANDS_WHITELIST_DISABLED,
&[],
))
.await;
Ok(1)
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_WHITELIST_ALREADYOFF,
&[],
)))
}
})
}
}
struct ListExecutor;
impl CommandExecutor for ListExecutor {
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 = &server.data.whitelist_config.read().await.whitelist;
if whitelist.is_empty() {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_NONE,
[],
))
.await;
return Ok(0);
}
let names = whitelist
.iter()
.map(|entry| entry.name.as_str())
.collect::<Vec<&str>>()
.join(", ");
let names_len = names.len() as i32;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_LIST,
[
TextComponent::text(whitelist.len().to_string()),
TextComponent::text(names),
],
))
.await;
Ok(names_len)
})
}
}
struct ReloadExecutor;
impl CommandExecutor for ReloadExecutor {
fn execute<'a>(
&'a self,
sender: &'a CommandSender,
server: &'a crate::server::Server,
_args: &'a ConsumedArgs<'a>,
) -> CommandResult<'a> {
Box::pin(async move {
*server.data.whitelist_config.write().await = WhitelistConfig::load();
kick_non_whitelisted_players(server).await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_RELOADED,
&[],
))
.await;
Ok(1)
})
}
}
pub struct AddExecutor;
impl CommandExecutor for AddExecutor {
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 = server.data.whitelist_config.write().await;
let mut successes: i32 = 0;
for player in targets {
let profile = &player.gameprofile;
if whitelist.is_whitelisted(profile) {
continue;
}
whitelist
.whitelist
.push(WhitelistEntry::new(profile.id, profile.name.clone()));
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_ADD_SUCCESS,
[TextComponent::text(profile.name.clone())],
))
.await;
successes += 1;
}
whitelist.save();
if successes == 0 {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_WHITELIST_ADD_FAILED,
&[],
)))
} else {
Ok(successes)
}
})
}
}
pub struct RemoveExecutor;
impl CommandExecutor for RemoveExecutor {
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 = server.data.whitelist_config.write().await;
let mut successes: i32 = 0;
for player in targets {
let i = whitelist
.whitelist
.iter()
.position(|entry| entry.uuid == player.gameprofile.id);
if let Some(i) = i {
whitelist.whitelist.remove(i);
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_REMOVE_SUCCESS,
[player.get_display_name().await],
))
.await;
successes += 1;
}
}
whitelist.save();
drop(whitelist);
kick_non_whitelisted_players(server).await;
if successes == 0 {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_WHITELIST_REMOVE_FAILED,
&[],
)))
} else {
Ok(successes)
}
})
}
}
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION)
.then(literal("on").execute(OnExecutor))
.then(literal("off").execute(OffExecutor))
.then(literal("list").execute(ListExecutor))
.then(literal("reload").execute(ReloadExecutor))
.then(
literal("add")
.then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(AddExecutor)),
)
.then(
literal("remove")
.then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(RemoveExecutor)),
)
}