163 lines
4.6 KiB
Rust
163 lines
4.6 KiB
Rust
use crate::command::CommandResult;
|
|
use crate::{
|
|
command::{
|
|
CommandError, CommandExecutor, CommandSender,
|
|
args::{
|
|
Arg, ConsumedArgs,
|
|
gameprofile::{GameProfileSuggestionMode, GameProfilesArgumentConsumer},
|
|
message::MsgArgConsumer,
|
|
},
|
|
tree::{CommandTree, builder::argument},
|
|
},
|
|
data::{SaveJSONConfiguration, banlist_serializer::BannedPlayerEntry},
|
|
net::{DisconnectReason, GameProfile},
|
|
};
|
|
use CommandError::InvalidConsumption;
|
|
use pumpkin_data::translation;
|
|
use pumpkin_util::text::TextComponent;
|
|
|
|
const NAMES: [&str; 1] = ["ban"];
|
|
const DESCRIPTION: &str = "bans a player";
|
|
|
|
const ARG_TARGET: &str = "player";
|
|
const ARG_REASON: &str = "reason";
|
|
|
|
struct NoReasonExecutor;
|
|
|
|
impl CommandExecutor for NoReasonExecutor {
|
|
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::GameProfiles(targets)) = args.get(&ARG_TARGET) else {
|
|
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
|
};
|
|
|
|
ban_players(sender, server, targets.as_slice(), None).await
|
|
})
|
|
}
|
|
}
|
|
|
|
struct ReasonExecutor;
|
|
|
|
impl CommandExecutor for ReasonExecutor {
|
|
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::GameProfiles(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())));
|
|
};
|
|
|
|
ban_players(sender, server, targets.as_slice(), Some(reason)).await
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Returns the number of players successfully banned.
|
|
async fn ban_players(
|
|
sender: &CommandSender,
|
|
server: &crate::server::Server,
|
|
targets: &[GameProfile],
|
|
reason: Option<&String>,
|
|
) -> Result<i32, CommandError> {
|
|
let mut count: usize = 0;
|
|
for target in targets {
|
|
if ban_profile(sender, server, target, reason.cloned()).await {
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
if count == 0 {
|
|
Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_BAN_FAILED,
|
|
translation::bedrock::COMMANDS_BAN_FAILED,
|
|
[],
|
|
)))
|
|
} else {
|
|
Ok(count as i32)
|
|
}
|
|
}
|
|
|
|
/// Returns `true` if the player was successfully banned.
|
|
async fn ban_profile(
|
|
sender: &CommandSender,
|
|
server: &crate::server::Server,
|
|
profile: &GameProfile,
|
|
reason: Option<String>,
|
|
) -> bool {
|
|
let mut banned_players = server.data.banned_player_list.write().await;
|
|
|
|
let reason = reason.unwrap_or_else(|| "Banned by an operator.".to_string());
|
|
|
|
if let Some(entry) = banned_players
|
|
.banned_players
|
|
.iter_mut()
|
|
.find(|entry| entry.uuid == profile.id)
|
|
{
|
|
if entry.name != profile.name {
|
|
entry.name.clone_from(&profile.name);
|
|
banned_players.save();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
banned_players.banned_players.push(BannedPlayerEntry::new(
|
|
profile,
|
|
sender.to_string(),
|
|
None,
|
|
reason.clone(),
|
|
));
|
|
|
|
banned_players.save();
|
|
drop(banned_players);
|
|
|
|
// Send messages
|
|
sender
|
|
.send_message(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_BAN_SUCCESS,
|
|
translation::bedrock::COMMANDS_BAN_SUCCESS,
|
|
[
|
|
TextComponent::text(profile.name.clone()),
|
|
TextComponent::text(reason),
|
|
],
|
|
))
|
|
.await;
|
|
|
|
if let Some(player) = server.get_player_by_uuid(profile.id) {
|
|
player
|
|
.kick(
|
|
DisconnectReason::Kicked,
|
|
TextComponent::translate_cross(
|
|
translation::java::MULTIPLAYER_DISCONNECT_BANNED,
|
|
translation::bedrock::DISCONNECTIONSCREEN_TITLE_BANNEDBYHOST,
|
|
[],
|
|
),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
pub fn init_command_tree() -> CommandTree {
|
|
CommandTree::new(NAMES, DESCRIPTION).then(
|
|
argument(
|
|
ARG_TARGET,
|
|
GameProfilesArgumentConsumer::new(GameProfileSuggestionMode::OnlinePlayers, true),
|
|
)
|
|
.execute(NoReasonExecutor)
|
|
.then(argument(ARG_REASON, MsgArgConsumer).execute(ReasonExecutor)),
|
|
)
|
|
}
|