155 lines
4.8 KiB
Rust
155 lines
4.8 KiB
Rust
use crate::command::CommandResult;
|
|
use crate::command::dispatcher::CommandError::InvalidConsumption;
|
|
use crate::command::{
|
|
CommandExecutor, CommandSender,
|
|
args::{
|
|
Arg, ConsumedArgs, position_block::BlockPosArgumentConsumer,
|
|
rotation::RotationArgumentConsumer,
|
|
},
|
|
dispatcher::CommandError,
|
|
tree::{CommandTree, builder::argument},
|
|
};
|
|
use crate::server::Server;
|
|
use pumpkin_data::dimension::Dimension;
|
|
use pumpkin_util::{math::position::BlockPos, text::TextComponent};
|
|
|
|
const NAMES: [&str; 1] = ["setworldspawn"];
|
|
|
|
const DESCRIPTION: &str = "Sets the world spawn point.";
|
|
|
|
const ARG_BLOCK_POS: &str = "position";
|
|
|
|
const ARG_ANGLE: &str = "angle";
|
|
|
|
struct NoArgsWorldSpawnExecutor;
|
|
|
|
impl CommandExecutor for NoArgsWorldSpawnExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
server: &'a crate::server::Server,
|
|
_args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let Some(block_pos) = sender.position() else {
|
|
let level_info_guard = server.level_info.read().await;
|
|
sender
|
|
.send_message(TextComponent::translate(
|
|
"commands.setworldspawn.success",
|
|
[
|
|
TextComponent::text(level_info_guard.spawn_x.to_string()),
|
|
TextComponent::text(level_info_guard.spawn_y.to_string()),
|
|
TextComponent::text(level_info_guard.spawn_z.to_string()),
|
|
TextComponent::text(level_info_guard.spawn_angle.to_string()),
|
|
],
|
|
))
|
|
.await;
|
|
|
|
return Ok(());
|
|
};
|
|
|
|
setworldspawn(sender, server, block_pos.to_block_pos(), 0.0).await
|
|
})
|
|
}
|
|
}
|
|
|
|
struct DefaultWorldSpawnExecutor;
|
|
|
|
impl CommandExecutor for DefaultWorldSpawnExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else {
|
|
return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into())));
|
|
};
|
|
|
|
setworldspawn(sender, server, *block_pos, 0.0).await
|
|
})
|
|
}
|
|
}
|
|
|
|
struct AngleWorldSpawnExecutor;
|
|
|
|
impl CommandExecutor for AngleWorldSpawnExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else {
|
|
return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into())));
|
|
};
|
|
|
|
let Some(Arg::Rotation(_, yaw)) = args.get(ARG_ANGLE) else {
|
|
return Err(InvalidConsumption(Some(ARG_ANGLE.into())));
|
|
};
|
|
|
|
setworldspawn(sender, server, *block_pos, *yaw).await
|
|
})
|
|
}
|
|
}
|
|
|
|
async fn setworldspawn(
|
|
sender: &CommandSender,
|
|
server: &Server,
|
|
block_pos: BlockPos,
|
|
yaw: f32,
|
|
) -> Result<(), CommandError> {
|
|
let Some(world) = sender.world() else {
|
|
return Err(CommandError::CommandFailed(TextComponent::text(
|
|
"Failed to get world.",
|
|
)));
|
|
};
|
|
if world.dimension != Dimension::OVERWORLD && world.dimension != Dimension::OVERWORLD_CAVES {
|
|
sender
|
|
.send_message(TextComponent::translate(
|
|
"commands.setworldspawn.failure.not_overworld",
|
|
[],
|
|
))
|
|
.await;
|
|
return Ok(());
|
|
}
|
|
|
|
let mut level_info_guard = server.level_info.write().await;
|
|
level_info_guard.spawn_x = block_pos.0.x;
|
|
level_info_guard.spawn_y = block_pos.0.y;
|
|
level_info_guard.spawn_z = block_pos.0.z;
|
|
|
|
level_info_guard.spawn_angle = yaw;
|
|
|
|
drop(level_info_guard);
|
|
|
|
sender
|
|
.send_message(TextComponent::translate(
|
|
"commands.setworldspawn.success",
|
|
[
|
|
TextComponent::text(block_pos.0.x.to_string()),
|
|
TextComponent::text(block_pos.0.y.to_string()),
|
|
TextComponent::text(block_pos.0.z.to_string()),
|
|
TextComponent::text(yaw.to_string()),
|
|
],
|
|
))
|
|
.await;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn init_command_tree() -> CommandTree {
|
|
CommandTree::new(NAMES, DESCRIPTION)
|
|
.execute(NoArgsWorldSpawnExecutor)
|
|
.then(
|
|
argument(ARG_BLOCK_POS, BlockPosArgumentConsumer)
|
|
.execute(DefaultWorldSpawnExecutor)
|
|
.then(
|
|
argument(ARG_ANGLE, RotationArgumentConsumer).execute(AngleWorldSpawnExecutor),
|
|
),
|
|
)
|
|
}
|