128 lines
4.0 KiB
Rust
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)),
|
|
),
|
|
)
|
|
}
|