Pumpkin/crates/pumpkin/src/command/commands/waypoint.rs
2026-08-24 16:00:06 +02:00

222 lines
7.4 KiB
Rust

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)
}