use crate::command::{ CommandError, CommandExecutor, CommandResult, CommandSender, args::{ ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, position_3d::Position3DArgumentConsumer, resource::particle::ParticleArgumentConsumer, }, tree::{CommandTree, builder::argument}, }; use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use pumpkin_world::block::entities::BlockEntity; const NAMES: [&str; 1] = ["particle"]; const DESCRIPTION: &str = "Spawns a Particle at position."; const ARG_NAME: &str = "name"; const ARG_POS: &str = "pos"; const ARG_DELTA: &str = "delta"; const ARG_SPEED: &str = "speed"; const ARG_COUNT: &str = "count"; struct Executor; impl CommandExecutor for Executor { fn execute<'a>( &'a self, sender: &'a CommandSender, server: &'a crate::server::Server, args: &'a ConsumedArgs<'a>, ) -> CommandResult<'a> { Box::pin(async move { let particle = ParticleArgumentConsumer::find_arg(args, ARG_NAME)?; let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS); let delta = Position3DArgumentConsumer::find_arg(args, ARG_DELTA); let speed = BoundedNumArgumentConsumer::::find_arg(args, ARG_SPEED); let count = BoundedNumArgumentConsumer::::find_arg(args, ARG_COUNT); let delta = delta.unwrap_or(Vector3::new(0.0, 0.0, 0.0)); let delta: Vector3 = Vector3::new(delta.x as f32, delta.y as f32, delta.z as f32); let speed = speed.unwrap_or(Ok(0.0))?; let count = count.unwrap_or(Ok(0))?; let (world, pos) = match sender { CommandSender::Console | CommandSender::Rcon(_) | CommandSender::Dummy => { let guard = server.worlds.load(); let world = guard .first() .cloned() .ok_or(CommandError::InvalidRequirement)?; // default position for spawning a player, in this case for particle let pos = { let info = &world.level_info.load(); // default position for spawning a player, in this case for mob pos.unwrap_or(Vector3::new( f64::from(info.spawn_x) + 0.5, f64::from(info.spawn_y) + 1.0, f64::from(info.spawn_z) + 0.5, )) }; (world, pos) } CommandSender::Player(player) => { let pos = pos.unwrap_or(player.living_entity.entity.pos.load()); (player.world(), pos) } CommandSender::CommandBlock(c, w) => { (w.clone(), c.get_position().to_centered_f64()) } }; world .spawn_particle(pos, delta, speed, count, *particle) .await; sender .send_message(TextComponent::translate_cross( "commands.particle.success", "commands.particle.success", [TextComponent::text(format!("{particle:?}"))], )) .await; // TODO: Add `viewers` arguments and change the logic for this result Ok(1) }) } } pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_NAME, ParticleArgumentConsumer) .execute(Executor) .then( argument(ARG_POS, Position3DArgumentConsumer) .execute(Executor) .then( argument(ARG_DELTA, Position3DArgumentConsumer) .execute(Executor) .then( argument( ARG_SPEED, BoundedNumArgumentConsumer::::new().min(0.0), ) .execute(Executor) .then( argument( ARG_COUNT, BoundedNumArgumentConsumer::::new().min(0), ) .execute(Executor), ), ), ), ), // TODO: Add NBT ) }