use pumpkin_data::translation; use pumpkin_protocol::java::client::play::{CWaypoint, WaypointIcon}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; use crate::command::args::{ FindArg, entity::EntityArgumentConsumer, hex_color::HexColorArgumentConsumer, resource_location::ResourceLocationArgumentConsumer, team_color::TeamColorArgumentConsumer, }; use crate::command::tree::builder::{argument, literal}; use crate::command::{ CommandError, CommandExecutor, CommandResult, CommandSender, ConsumedArgs, tree::CommandTree, }; const NAMES: [&str; 1] = ["waypoint"]; const DESCRIPTION: &str = "List or modify waypoints."; const ARG_WAYPOINT: &str = "waypoint"; const ARG_COLOR: &str = "color"; const ARG_STYLE: &str = "style"; 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 worlds = server.worlds.load(); let world = worlds.first().ok_or(CommandError::InvalidRequirement)?; let dimension = world.dimension.minecraft_name.to_string(); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WAYPOINT_LIST_EMPTY, translation::java::COMMANDS_WAYPOINT_LIST_EMPTY, TextComponent::text(dimension) )) .await; Ok(0) }) } } enum ColorAction { Named, Hex, Reset, } struct ColorExecutor(ColorAction); impl CommandExecutor for ColorExecutor { fn execute<'a>( &'a self, sender: &'a CommandSender, _server: &'a crate::server::Server, args: &'a ConsumedArgs<'a>, ) -> CommandResult<'a> { Box::pin(async move { let waypoint_entity = EntityArgumentConsumer::find_arg(args, ARG_WAYPOINT)?; let entity = waypoint_entity.get_entity(); let pos = entity.pos.load(); let block_pos = BlockPos::new( pos.x.floor() as i32, pos.y.floor() as i32, pos.z.floor() as i32, ); let uuid = entity.entity_uuid; let color_val = match self.0 { ColorAction::Named => { let color = TeamColorArgumentConsumer::find_arg(args, ARG_COLOR)?; let rgb = color.to_rgb(); i32::from_be_bytes([0, rgb.red, rgb.green, rgb.blue]) } ColorAction::Hex => HexColorArgumentConsumer::find_arg(args, ARG_COLOR)? as i32, ColorAction::Reset => 0xFFFFFF, }; let packet = CWaypoint::update_position( uuid, Some(WaypointIcon { style: None, color: color_val, }), block_pos, ); if let Some(player) = sender.as_player() { player.send_client_packet(&packet).await; } match self.0 { ColorAction::Named => { let color = TeamColorArgumentConsumer::find_arg(args, ARG_COLOR)?; sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WAYPOINT_MODIFY_COLOR, translation::java::COMMANDS_WAYPOINT_MODIFY_COLOR, TextComponent::text(color.name()).color_named(color) )) .await; } ColorAction::Hex => { let color_val = HexColorArgumentConsumer::find_arg(args, ARG_COLOR)?; let hex_str = format!("{:06X}", color_val & 0xFFFFFF); sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WAYPOINT_MODIFY_COLOR, translation::java::COMMANDS_WAYPOINT_MODIFY_COLOR, TextComponent::text(hex_str) )) .await; } ColorAction::Reset => { sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WAYPOINT_MODIFY_COLOR_RESET, translation::java::COMMANDS_WAYPOINT_MODIFY_COLOR_RESET )) .await; } } Ok(0) }) } } enum StyleAction { Set, Reset, } struct StyleExecutor(StyleAction); impl CommandExecutor for StyleExecutor { fn execute<'a>( &'a self, sender: &'a CommandSender, _server: &'a crate::server::Server, args: &'a ConsumedArgs<'a>, ) -> CommandResult<'a> { Box::pin(async move { let waypoint_entity = EntityArgumentConsumer::find_arg(args, ARG_WAYPOINT)?; let entity = waypoint_entity.get_entity(); let pos = entity.pos.load(); let block_pos = BlockPos::new( pos.x.floor() as i32, pos.y.floor() as i32, pos.z.floor() as i32, ); let uuid = entity.entity_uuid; let style_owned = match self.0 { StyleAction::Set => { let style = ResourceLocationArgumentConsumer::find_arg(args, ARG_STYLE)?; Some(style.to_string()) } StyleAction::Reset => None, }; let style_str = style_owned.as_deref(); let packet = CWaypoint::update_position( uuid, Some(WaypointIcon { style: style_str, color: 0xFFFFFF, }), block_pos, ); if let Some(player) = sender.as_player() { player.send_client_packet(&packet).await; } sender .send_message(pumpkin_macros::translate_cross!( translation::java::COMMANDS_WAYPOINT_MODIFY_STYLE, translation::java::COMMANDS_WAYPOINT_MODIFY_STYLE )) .await; Ok(0) }) } } pub fn init_command_tree() -> CommandTree { let color_node = literal("color") .then( argument(ARG_COLOR, TeamColorArgumentConsumer) .execute(ColorExecutor(ColorAction::Named)), ) .then(literal("hex").then( argument(ARG_COLOR, HexColorArgumentConsumer).execute(ColorExecutor(ColorAction::Hex)), )) .then(literal("reset").execute(ColorExecutor(ColorAction::Reset))); let style_node = literal("style") .then(literal("reset").execute(StyleExecutor(StyleAction::Reset))) .then( literal("set").then( argument(ARG_STYLE, ResourceLocationArgumentConsumer) .execute(StyleExecutor(StyleAction::Set)), ), ); let modify_node = literal("modify").then( argument(ARG_WAYPOINT, EntityArgumentConsumer) .then(color_node) .then(style_node), ); CommandTree::new(NAMES, DESCRIPTION) .then(literal("list").execute(ListExecutor)) .then(modify_node) }