Pumpkin/pumpkin/src/command/commands/fill.rs
Alexander Medvedev be44ccc8df Don't require server when spawning a entity
Makes it much simpler
2025-03-02 16:05:31 +01:00

175 lines
6.6 KiB
Rust

use crate::command::args::block::BlockArgumentConsumer;
use crate::command::args::position_block::BlockPosArgumentConsumer;
use crate::command::args::{ConsumedArgs, FindArg};
use crate::command::tree::CommandTree;
use crate::command::tree::builder::{argument, literal};
use crate::command::{CommandError, CommandExecutor, CommandSender};
use async_trait::async_trait;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::text::TextComponent;
const NAMES: [&str; 1] = ["fill"];
const DESCRIPTION: &str = "Fills all or parts of a region with a specific block.";
const ARG_BLOCK: &str = "block";
const ARG_FROM: &str = "from";
const ARG_TO: &str = "to";
#[derive(Clone, Copy, Default)]
enum Mode {
/// Destroys blocks with particles and item drops
Destroy,
/// Leaves only the outer layer of blocks, removes the inner ones (creates a hollow space)
Hollow,
/// Only replaces air blocks, keeping non-air blocks unchanged
Keep,
/// Like Hollow but doesn't replace inner blocks with air, just the outline
Outline,
/// Replaces all blocks with the new block state, without particles
#[default]
Replace,
}
struct Executor(Mode);
#[expect(clippy::too_many_lines)]
#[async_trait]
impl CommandExecutor for Executor {
async fn execute<'a>(
&self,
sender: &mut CommandSender<'a>,
_server: &crate::server::Server,
args: &ConsumedArgs<'a>,
) -> Result<(), CommandError> {
let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
let block_state_id = block.default_state_id;
let from = BlockPosArgumentConsumer::find_arg(args, ARG_FROM)?;
let to = BlockPosArgumentConsumer::find_arg(args, ARG_TO)?;
let mode = self.0;
let start_x = from.0.x.min(to.0.x);
let start_y = from.0.y.min(to.0.y);
let start_z = from.0.z.min(to.0.z);
let end_x = from.0.x.max(to.0.x);
let end_y = from.0.y.max(to.0.y);
let end_z = from.0.z.max(to.0.z);
let world = sender
.world()
.await
.ok_or(CommandError::InvalidRequirement)?;
let mut placed_blocks = 0;
match mode {
Mode::Destroy => {
for x in start_x..=end_x {
for y in start_y..=end_y {
for z in start_z..=end_z {
let block_position = BlockPos(Vector3 { x, y, z });
world.break_block(&block_position, None, false).await;
world.set_block_state(&block_position, block_state_id).await;
placed_blocks += 1;
}
}
}
}
Mode::Replace => {
for x in start_x..=end_x {
for y in start_y..=end_y {
for z in start_z..=end_z {
let block_position = BlockPos(Vector3 { x, y, z });
world.set_block_state(&block_position, block_state_id).await;
placed_blocks += 1;
}
}
}
}
Mode::Keep => {
for x in start_x..=end_x {
for y in start_y..=end_y {
for z in start_z..=end_z {
let block_position = BlockPos(Vector3 { x, y, z });
match world.get_block_state(&block_position).await {
Ok(old_state) if old_state.air => {
world.set_block_state(&block_position, block_state_id).await;
placed_blocks += 1;
}
_ => {}
}
}
}
}
}
Mode::Hollow => {
for x in start_x..=end_x {
for y in start_y..=end_y {
for z in start_z..=end_z {
let block_position = BlockPos(Vector3::new(x, y, z));
let is_edge = x == start_x
|| x == end_x
|| y == start_y
|| y == end_y
|| z == start_z
|| z == end_z;
if is_edge {
world.set_block_state(&block_position, block_state_id).await;
} else {
world.set_block_state(&block_position, 0).await;
}
placed_blocks += 1;
}
}
}
}
Mode::Outline => {
for x in start_x..=end_x {
for y in start_y..=end_y {
for z in start_z..=end_z {
let block_position = BlockPos(Vector3::new(x, y, z));
let is_edge = x == start_x
|| x == end_x
|| y == start_y
|| y == end_y
|| z == start_z
|| z == end_z;
if is_edge {
world.set_block_state(&block_position, block_state_id).await;
placed_blocks += 1;
}
}
}
}
}
};
sender
.send_message(TextComponent::translate(
"commands.fill.success",
[TextComponent::text(placed_blocks.to_string())],
))
.await;
Ok(())
}
}
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_FROM, BlockPosArgumentConsumer).then(
argument(ARG_TO, BlockPosArgumentConsumer).then(
argument(ARG_BLOCK, BlockArgumentConsumer)
.then(literal("destroy").execute(Executor(Mode::Destroy)))
.then(literal("hollow").execute(Executor(Mode::Hollow)))
.then(literal("keep").execute(Executor(Mode::Keep)))
.then(literal("outline").execute(Executor(Mode::Outline)))
.then(literal("replace").execute(Executor(Mode::Replace)))
.execute(Executor(Mode::Replace)),
),
),
)
}