Pumpkin/pumpkin/src/command/commands/setblock.rs
2025-06-22 21:28:14 +02:00

128 lines
4.0 KiB
Rust

use async_trait::async_trait;
use pumpkin_util::text::TextComponent;
use pumpkin_world::world::BlockFlags;
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};
const NAMES: [&str; 1] = ["setblock"];
const DESCRIPTION: &str = "Place a block.";
const ARG_BLOCK: &str = "block";
const ARG_BLOCK_POS: &str = "pos";
#[derive(Clone, Copy)]
enum Mode {
/// with particles + item drops
Destroy,
/// only replaces air
Keep,
/// default; without particles
Replace,
}
struct Executor(Mode);
#[async_trait]
impl CommandExecutor for Executor {
async fn execute<'a>(
&self,
sender: &mut CommandSender,
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 pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?;
let mode = self.0;
let world = match sender {
CommandSender::Console | CommandSender::Rcon(_) => {
let guard = server.worlds.read().await;
guard
.first()
.cloned()
.ok_or(CommandError::InvalidRequirement)?
}
CommandSender::Player(player) => player.world().await,
};
let success = match mode {
Mode::Destroy => {
world
.clone()
.break_block(&pos, None, BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE)
.await;
world
.set_block_state(
&pos,
block_state_id,
BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
true
}
Mode::Replace => {
world
.set_block_state(
&pos,
block_state_id,
BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
true
}
Mode::Keep => {
let old_state = world.get_block_state(&pos).await;
if old_state.is_air() {
world
.set_block_state(
&pos,
block_state_id,
BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
true
} else {
false
}
}
};
sender
.send_message(if success {
TextComponent::translate(
"commands.setblock.success",
[
TextComponent::text(pos.0.x.to_string()),
TextComponent::text(pos.0.y.to_string()),
TextComponent::text(pos.0.z.to_string()),
],
)
} else {
TextComponent::translate("commands.setblock.failed", [])
})
.await;
Ok(())
}
}
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_BLOCK_POS, BlockPosArgumentConsumer).then(
argument(ARG_BLOCK, BlockArgumentConsumer)
.then(literal("replace").execute(Executor(Mode::Replace)))
.then(literal("destroy").execute(Executor(Mode::Destroy)))
.then(literal("keep").execute(Executor(Mode::Keep)))
.execute(Executor(Mode::Replace)),
),
)
}