Changed bossbar title to TextComponent instead of String (#435)

* Changed bossbar title to TextComponent instead of String

* Clippy and fmt
This commit is contained in:
Nico Göbel
2024-12-30 22:30:12 +01:00
committed by GitHub
parent 9d770e9fb6
commit 72a9670ba9
5 changed files with 177 additions and 92 deletions

View File

@@ -0,0 +1,81 @@
use crate::command::args::{Arg, ArgumentConsumer, FindArg, GetClientSideArgParser};
use crate::command::dispatcher::CommandError;
use crate::command::tree::RawArgs;
use crate::command::CommandSender;
use crate::server::Server;
use async_trait::async_trait;
use pumpkin_core::text::TextComponent;
use pumpkin_protocol::client::play::{
CommandSuggestion, ProtoCmdArgParser, ProtoCmdArgSuggestionType,
};
pub(crate) struct TextComponentArgConsumer;
impl GetClientSideArgParser for TextComponentArgConsumer {
fn get_client_side_parser(&self) -> ProtoCmdArgParser {
ProtoCmdArgParser::Component
}
fn get_client_side_suggestion_type_override(&self) -> Option<ProtoCmdArgSuggestionType> {
None
}
}
#[async_trait]
impl ArgumentConsumer for TextComponentArgConsumer {
async fn consume<'a>(
&'a self,
_sender: &CommandSender<'a>,
_server: &'a Server,
args: &mut RawArgs<'a>,
) -> Option<Arg<'a>> {
let s = 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;
};
Some(Arg::TextComponent(text_component))
}
async fn suggest<'a>(
&'a self,
_sender: &CommandSender<'a>,
_server: &'a Server,
_input: &'a str,
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
Ok(None)
}
}
impl FindArg<'_> for TextComponentArgConsumer {
type Data = TextComponent;
fn find_arg(args: &super::ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::TextComponent(data)) => Ok(data.clone()),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),
}
}
}
fn parse_text_component(input: &str) -> Option<TextComponent> {
if input.starts_with('[') && input.ends_with(']') {
let text_component_array: Option<Vec<TextComponent>> =
serde_json::from_str(input).unwrap_or(None);
let mut text_component_array = text_component_array?;
let mut constructed_text_component = text_component_array[0].clone();
text_component_array.remove(0);
constructed_text_component.extra = text_component_array;
Some(constructed_text_component)
} else {
serde_json::from_str(input).unwrap_or(None)
}
}

View File

@@ -2,6 +2,7 @@ use std::{collections::HashMap, hash::Hash, sync::Arc};
use arg_bounded_num::{NotInBounds, Number};
use async_trait::async_trait;
use pumpkin_core::text::TextComponent;
use pumpkin_core::{
math::{position::WorldPosition, vector2::Vector2, vector3::Vector3},
GameMode,
@@ -36,6 +37,7 @@ pub(crate) mod arg_position_block;
pub(crate) mod arg_resource_location;
pub(crate) mod arg_rotation;
pub(crate) mod arg_simple;
pub(crate) mod arg_textcomponent;
mod coordinate;
/// see [`crate::commands::tree_builder::argument`]
@@ -87,6 +89,7 @@ pub(crate) enum Arg<'a> {
BossbarColor(BossbarColor),
BossbarStyle(BossbarDivisions),
Msg(String),
TextComponent(TextComponent),
Num(Result<Number, NotInBounds>),
Bool(bool),
#[allow(unused)]

View File

@@ -4,12 +4,11 @@ use crate::command::args::arg_bossbar_style::BossbarStyleArgumentConsumer;
use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::arg_players::PlayersArgumentConsumer;
use crate::command::args::arg_resource_location::ResourceLocationArgumentConsumer;
use crate::command::args::arg_simple::SimpleArgConsumer;
use crate::command::args::{
Arg, ConsumedArgs, DefaultNameArgConsumer, FindArg, FindArgDefaultName,
};
use crate::command::args::{ConsumedArgs, DefaultNameArgConsumer, FindArg, FindArgDefaultName};
use crate::command::dispatcher::CommandError;
use crate::command::dispatcher::CommandError::InvalidConsumption;
use crate::command::args::arg_textcomponent::TextComponentArgConsumer;
use crate::command::tree::CommandTree;
use crate::command::tree_builder::{argument, argument_default_name, literal};
use crate::command::{CommandExecutor, CommandSender};
@@ -55,7 +54,6 @@ enum CommandValueSet {
struct BossbarAddExecuter;
#[async_trait]
#[expect(clippy::inefficient_to_string)]
impl CommandExecutor for BossbarAddExecuter {
async fn execute<'a>(
&self,
@@ -64,9 +62,7 @@ impl CommandExecutor for BossbarAddExecuter {
args: &ConsumedArgs<'a>,
) -> Result<(), CommandError> {
let namespace = non_autocomplete_consumer().find_arg_default_name(args)?;
let Some(Arg::Simple(name)) = args.get(ARG_NAME) else {
return Err(InvalidConsumption(Some(ARG_NAME.into())));
};
let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?;
if server.bossbars.lock().await.has_bossbar(namespace) {
send_error_message(
@@ -77,13 +73,21 @@ impl CommandExecutor for BossbarAddExecuter {
return Ok(());
}
let bossbar = Bossbar::new(name.to_string());
let bossbar = Bossbar::new(text_component);
server
.bossbars
.lock()
.await
.create_bossbar(namespace.to_string(), bossbar.clone());
sender
.send_message(
TextComponent::text("Created custom bossbar [")
.add_child(bossbar.title)
.add_child(TextComponent::text("]")),
)
.await;
Ok(())
}
}
@@ -111,36 +115,30 @@ impl CommandExecutor for BossbarGetExecuter {
match self.0 {
CommandValueGet::Max => {
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] has a maximum of {}",
bossbar.bossbar_data.title, bossbar.max
),
bossbar.bossbar_data.title,
format!("has a maximum of {}", bossbar.max),
)
.await;
return Ok(());
}
CommandValueGet::Players => {}
CommandValueGet::Value => {
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] has a value of {}",
bossbar.bossbar_data.title, bossbar.value
),
bossbar.bossbar_data.title,
format!("has a value of {}", bossbar.value),
)
.await;
return Ok(());
}
CommandValueGet::Visible => {
let state = if bossbar.visible { "shown" } else { "hidden" };
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] is currently {state}",
bossbar.bossbar_data.title
),
bossbar.bossbar_data.title,
format!("is currently {state}"),
)
.await;
return Ok(());
@@ -167,24 +165,25 @@ impl CommandExecutor for BossbarListExecuter {
return Ok(());
};
let mut bossbars_string = String::new();
let mut bossbars_text = TextComponent::text(format!(
"There are {} custom bossbar(s) active: ",
bossbars.len()
));
for (i, bossbar) in bossbars.iter().enumerate() {
if i == 0 {
bossbars_string += format!("[{}]", bossbar.bossbar_data.title).as_str();
bossbars_text = bossbars_text
.add_child(TextComponent::text("["))
.add_child(bossbar.bossbar_data.title.clone())
.add_child(TextComponent::text("]"));
} else {
bossbars_string += format!(", [{}]", bossbar.bossbar_data.title).as_str();
bossbars_text = bossbars_text
.add_child(TextComponent::text(", ["))
.add_child(bossbar.bossbar_data.title.clone())
.add_child(TextComponent::text("]"));
}
}
send_success_message(
sender,
format!(
"There are {} custom bossbar(s) active: {}",
bossbars.len(),
bossbars_string
),
)
.await;
sender.send_message(bossbars_text).await;
Ok(())
}
}
@@ -266,12 +265,10 @@ impl CommandExecutor for BossbarSetExecuter {
return Ok(());
}
}
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] has changed color",
bossbar.bossbar_data.title
),
bossbar.bossbar_data.title,
String::from("has changed color"),
)
.await;
Ok(())
@@ -305,25 +302,21 @@ impl CommandExecutor for BossbarSetExecuter {
}
}
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] has changed maximum to {}",
bossbar.bossbar_data.title, max_value
),
bossbar.bossbar_data.title,
format!("has changed maximum to {max_value}"),
)
.await;
Ok(())
}
CommandValueSet::Name => {
let Some(Arg::Simple(name)) = args.get(ARG_NAME) else {
return Err(InvalidConsumption(Some(ARG_NAME.into())));
};
let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?;
match server
.bossbars
.lock()
.await
.update_name(server, namespace, name)
.update_name(server, namespace, text_component.clone())
.await
{
Ok(()) => {}
@@ -333,8 +326,12 @@ impl CommandExecutor for BossbarSetExecuter {
}
}
send_success_message(sender, format!("Custom bossbar [{name}] has been renamed"))
.await;
send_prefix_success_message(
sender,
text_component,
String::from("has been renamed"),
)
.await;
Ok(())
}
CommandValueSet::Players(has_players) => {
@@ -352,12 +349,10 @@ impl CommandExecutor for BossbarSetExecuter {
return Ok(());
}
}
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] no longer has any players",
bossbar.bossbar_data.title
),
bossbar.bossbar_data.title,
String::from("no longer has any players"),
)
.await;
return Ok(());
@@ -388,13 +383,10 @@ impl CommandExecutor for BossbarSetExecuter {
.map(|player| player.gameprofile.name.clone())
.collect();
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] now has {count} player(s): {}",
bossbar.bossbar_data.title,
player_names.join(", ")
),
bossbar.bossbar_data.title,
format!("now has {count} player(s): {}", player_names.join(", ")),
)
.await;
Ok(())
@@ -414,12 +406,10 @@ impl CommandExecutor for BossbarSetExecuter {
return Ok(());
}
}
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] has changed style",
bossbar.bossbar_data.title
),
bossbar.bossbar_data.title,
String::from("has changed style"),
)
.await;
Ok(())
@@ -448,12 +438,10 @@ impl CommandExecutor for BossbarSetExecuter {
}
}
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] has changed value to {}",
bossbar.bossbar_data.title, value
),
bossbar.bossbar_data.title,
format!("changed value to {value}"),
)
.await;
Ok(())
@@ -476,12 +464,10 @@ impl CommandExecutor for BossbarSetExecuter {
}
let state = if visibility { "visible" } else { "hidden" };
send_success_message(
send_prefix_success_message(
sender,
format!(
"Custom bossbar [{}] is now {state}",
bossbar.bossbar_data.title
),
bossbar.bossbar_data.title,
format!("is now {state}"),
)
.await;
Ok(())
@@ -500,12 +486,11 @@ fn value_consumer() -> BoundedNumArgumentConsumer<i32> {
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION)
.with_child(
literal("add").with_child(
argument_default_name(non_autocomplete_consumer())
.with_child(argument(ARG_NAME, SimpleArgConsumer).execute(BossbarAddExecuter)),
.with_child(literal("add").with_child(
argument_default_name(non_autocomplete_consumer()).with_child(
argument(ARG_NAME, TextComponentArgConsumer).execute(BossbarAddExecuter),
),
)
))
.with_child(
literal("get").with_child(
argument_default_name(autocomplete_consumer())
@@ -542,7 +527,7 @@ pub fn init_command_tree() -> CommandTree {
)
.with_child(
literal("name").with_child(
argument(ARG_NAME, SimpleArgConsumer)
argument(ARG_NAME, TextComponentArgConsumer)
.execute(BossbarSetExecuter(CommandValueSet::Name)),
),
)
@@ -576,6 +561,21 @@ pub fn init_command_tree() -> CommandTree {
)
}
fn bossbar_prefix(title: TextComponent, trailing: &str) -> TextComponent {
TextComponent::text("Custom bossbar [")
.add_child(title)
.add_child(TextComponent::text(format!("] {trailing}")))
}
async fn send_prefix_success_message(
sender: &CommandSender<'_>,
title: TextComponent,
message: String,
) {
sender
.send_message(bossbar_prefix(title, message.as_str()))
.await;
}
async fn send_success_message(sender: &CommandSender<'_>, message: String) {
sender.send_message(TextComponent::text(message)).await;
}

View File

@@ -34,7 +34,7 @@ pub enum BossbarFlags {
#[derive(Clone)]
pub struct Bossbar {
pub uuid: Uuid,
pub title: String,
pub title: TextComponent,
pub health: f32,
pub color: BossbarColor,
pub division: BossbarDivisions,
@@ -43,7 +43,7 @@ pub struct Bossbar {
impl Bossbar {
#[must_use]
pub fn new(title: String) -> Self {
pub fn new(title: TextComponent) -> Self {
let uuid = Uuid::new_v4();
Self {
@@ -63,7 +63,7 @@ impl Player {
// Maybe this section could be implemented. feel free to change
let bossbar = bossbar.clone();
let boss_action = BosseventAction::Add {
title: TextComponent::text(bossbar.title),
title: bossbar.title,
health: bossbar.health,
color: (bossbar.color as u8).into(),
division: (bossbar.division as u8).into(),
@@ -87,8 +87,8 @@ impl Player {
self.client.send_packet(&packet).await;
}
pub async fn update_bossbar_title(&self, uuid: &Uuid, title: String) {
let boss_action = BosseventAction::UpdateTile(TextComponent::text(title));
pub async fn update_bossbar_title(&self, uuid: &Uuid, title: TextComponent) {
let boss_action = BosseventAction::UpdateTile(title);
let packet = CBossEvent::new(uuid, boss_action);
self.client.send_packet(&packet).await;

View File

@@ -2,6 +2,7 @@ use crate::command::args::GetCloned;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::bossbar::{Bossbar, BossbarColor, BossbarDivisions};
use pumpkin_core::text::TextComponent;
use std::collections::HashMap;
use std::sync::Arc;
use thiserror::Error;
@@ -236,7 +237,7 @@ impl CustomBossbars {
&mut self,
server: &Server,
resource_location: &str,
new_title: &str,
new_title: TextComponent,
) -> Result<(), BossbarUpdateError> {
let bossbar = self.custom_bossbars.get_mut(resource_location);
if let Some(bossbar) = bossbar {
@@ -246,7 +247,7 @@ impl CustomBossbars {
)));
}
bossbar.bossbar_data.title = new_title.to_string();
bossbar.bossbar_data.title = new_title;
if !bossbar.visible {
return Ok(());