Pumpkin/pumpkin/src/command/commands/setblock.rs
2026-05-03 21:31:46 +02:00

148 lines
5.1 KiB
Rust

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, CommandResult, 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,
/// places block without triggering updates around it
Strict,
}
struct Executor(Mode);
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 block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
let block_state_id = block.default_state.id;
let mode = self.0;
let world = match sender {
CommandSender::Console | CommandSender::Rcon(_) | CommandSender::Dummy => {
let guard = server.worlds.load();
guard
.first()
.cloned()
.ok_or(CommandError::InvalidRequirement)?
}
CommandSender::Player(player) => player.world().clone(),
CommandSender::CommandBlock(_, w) => w.clone(),
};
let pos = BlockPosArgumentConsumer::find_loaded_arg(args, ARG_BLOCK_POS, &world)?;
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
}
}
Mode::Strict => {
world
.set_block_state(
&pos,
block_state_id,
BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
)
.await;
true
}
};
if success {
sender
.send_message(TextComponent::translate_cross(
"commands.setblock.success",
"commands.setblock.success",
[
TextComponent::text(pos.0.x.to_string()),
TextComponent::text(pos.0.y.to_string()),
TextComponent::text(pos.0.z.to_string()),
],
))
.await;
Ok(1)
} else {
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.setblock.failed",
"commands.setblock.failed",
[],
)))
}
})
}
}
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)))
.then(literal("strict").execute(Executor(Mode::Strict)))
.execute(Executor(Mode::Replace)),
),
)
}