361 lines
13 KiB
Rust
361 lines
13 KiB
Rust
use pumpkin_data::translation;
|
|
use pumpkin_util::math::position::BlockPos;
|
|
use pumpkin_util::math::vector3::Vector3;
|
|
use pumpkin_util::text::TextComponent;
|
|
|
|
use crate::command::CommandError;
|
|
use crate::command::CommandResult;
|
|
use crate::command::args::ConsumedArgs;
|
|
use crate::command::args::FindArg;
|
|
use crate::command::args::entities::EntitiesArgumentConsumer;
|
|
use crate::command::args::entity::EntityArgumentConsumer;
|
|
use crate::command::args::position_3d::Position3DArgumentConsumer;
|
|
use crate::command::args::rotation::RotationArgumentConsumer;
|
|
use crate::command::tree::CommandTree;
|
|
use crate::command::tree::builder::{argument, literal};
|
|
use crate::command::{CommandExecutor, CommandSender};
|
|
use crate::entity::EntityBase;
|
|
use crate::world::World;
|
|
|
|
const NAMES: [&str; 2] = ["teleport", "tp"];
|
|
const DESCRIPTION: &str = "Teleports entities, including players."; // todo
|
|
|
|
/// position
|
|
const ARG_LOCATION: &str = "location";
|
|
|
|
/// single entity
|
|
const ARG_DESTINATION: &str = "destination";
|
|
|
|
/// multiple entities
|
|
const ARG_TARGETS: &str = "targets";
|
|
|
|
/// rotation: yaw/pitch
|
|
const ARG_ROTATION: &str = "rotation";
|
|
|
|
/// single entity
|
|
const ARG_FACING_ENTITY: &str = "facingEntity";
|
|
|
|
/// position
|
|
const ARG_FACING_LOCATION: &str = "facingLocation";
|
|
|
|
fn yaw_pitch_facing_position(
|
|
looking_from: &Vector3<f64>,
|
|
looking_towards: &Vector3<f64>,
|
|
) -> (f32, f32) {
|
|
let direction_vector = (looking_towards.sub(looking_from)).normalize();
|
|
|
|
let yaw_radians = -direction_vector.x.atan2(direction_vector.z);
|
|
let pitch_radians = (-direction_vector.y).asin();
|
|
|
|
let yaw_degrees = yaw_radians.to_degrees();
|
|
let pitch_degrees = pitch_radians.to_degrees();
|
|
|
|
(yaw_degrees as f32, pitch_degrees as f32)
|
|
}
|
|
|
|
fn resolve_sender_world(
|
|
sender: &CommandSender,
|
|
server: &crate::server::Server,
|
|
) -> std::sync::Arc<World> {
|
|
sender
|
|
.world()
|
|
.unwrap_or_else(|| server.worlds.load().first().unwrap().clone())
|
|
}
|
|
|
|
struct EntitiesToEntityExecutor;
|
|
|
|
impl CommandExecutor for EntitiesToEntityExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
_sender: &'a CommandSender,
|
|
_server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
|
|
|
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
|
|
let destination = destination.get_entity();
|
|
let pos = destination.pos.load();
|
|
let yaw = destination.yaw.load();
|
|
let pitch = destination.pitch.load();
|
|
let world = destination.world.load_full();
|
|
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
|
|
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
[],
|
|
)));
|
|
}
|
|
for target in targets {
|
|
target
|
|
.clone()
|
|
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
|
.await;
|
|
}
|
|
|
|
Ok(targets.len() as i32)
|
|
})
|
|
}
|
|
}
|
|
|
|
struct EntitiesToPosFacingPosExecutor;
|
|
|
|
impl CommandExecutor for EntitiesToPosFacingPosExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
|
|
|
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
|
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
|
|
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
[],
|
|
)));
|
|
}
|
|
let facing_pos = Position3DArgumentConsumer::find_arg(args, ARG_FACING_LOCATION)?;
|
|
let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_pos);
|
|
let world = resolve_sender_world(sender, server);
|
|
|
|
for target in targets {
|
|
target
|
|
.clone()
|
|
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
|
.await;
|
|
}
|
|
|
|
Ok(targets.len() as i32)
|
|
})
|
|
}
|
|
}
|
|
|
|
struct EntitiesToPosFacingEntityExecutor;
|
|
|
|
impl CommandExecutor for EntitiesToPosFacingEntityExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
|
|
|
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
|
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
|
|
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
[],
|
|
)));
|
|
}
|
|
let facing_entity = EntityArgumentConsumer::find_arg(args, ARG_FACING_ENTITY)?;
|
|
let (yaw, pitch) =
|
|
yaw_pitch_facing_position(&pos, &facing_entity.get_entity().pos.load());
|
|
let world = resolve_sender_world(sender, server);
|
|
|
|
for target in targets {
|
|
target
|
|
.clone()
|
|
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
|
.await;
|
|
}
|
|
|
|
Ok(targets.len() as i32)
|
|
})
|
|
}
|
|
}
|
|
|
|
struct EntitiesToPosWithRotationExecutor;
|
|
|
|
impl CommandExecutor for EntitiesToPosWithRotationExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
|
|
|
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
|
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
|
|
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
[],
|
|
)));
|
|
}
|
|
// Note: Rotation returns (yaw, is_yaw_relative, pitch, is_pitch_relative)
|
|
// For teleport, we use absolute values only (ignore relative flags)
|
|
let (yaw, _, pitch, _) = RotationArgumentConsumer::find_arg(args, ARG_ROTATION)?;
|
|
let world = resolve_sender_world(sender, server);
|
|
|
|
for target in targets {
|
|
target
|
|
.clone()
|
|
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
|
.await;
|
|
}
|
|
|
|
Ok(targets.len() as i32)
|
|
})
|
|
}
|
|
}
|
|
|
|
struct EntitiesToPosExecutor;
|
|
|
|
impl CommandExecutor for EntitiesToPosExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
|
|
|
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
|
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
|
|
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
[],
|
|
)));
|
|
}
|
|
let world = resolve_sender_world(sender, server);
|
|
for target in targets {
|
|
let yaw = target.get_entity().yaw.load();
|
|
let pitch = target.get_entity().pitch.load();
|
|
target
|
|
.clone()
|
|
.teleport(pos, Some(yaw), Some(pitch), world.clone())
|
|
.await;
|
|
}
|
|
|
|
Ok(targets.len() as i32)
|
|
})
|
|
}
|
|
}
|
|
|
|
struct SelfToEntityExecutor;
|
|
|
|
impl CommandExecutor for SelfToEntityExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
_server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
|
|
let destination = destination.get_entity();
|
|
let pos = destination.pos.load();
|
|
let yaw = destination.yaw.load();
|
|
let pitch = destination.pitch.load();
|
|
let world = destination.world.load_full();
|
|
|
|
match sender {
|
|
CommandSender::Player(player) => {
|
|
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
|
|
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
[],
|
|
)));
|
|
}
|
|
player
|
|
.clone()
|
|
.teleport(pos, Some(yaw), Some(pitch), world)
|
|
.await;
|
|
|
|
Ok(1)
|
|
}
|
|
_ => Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::PERMISSIONS_REQUIRES_PLAYER,
|
|
translation::java::PERMISSIONS_REQUIRES_PLAYER,
|
|
[],
|
|
))),
|
|
}
|
|
})
|
|
}
|
|
}
|
|
struct SelfToPosExecutor;
|
|
|
|
impl CommandExecutor for SelfToPosExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
_server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
match sender {
|
|
CommandSender::Player(player) => {
|
|
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
|
let yaw = player.living_entity.entity.yaw.load();
|
|
let pitch = player.living_entity.entity.pitch.load();
|
|
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
|
|
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
|
|
[],
|
|
)));
|
|
}
|
|
player
|
|
.clone()
|
|
.teleport(pos, Some(yaw), Some(pitch), player.world().clone())
|
|
.await;
|
|
|
|
Ok(1)
|
|
}
|
|
_ => Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
|
translation::java::PERMISSIONS_REQUIRES_PLAYER,
|
|
translation::java::PERMISSIONS_REQUIRES_PLAYER,
|
|
[],
|
|
))),
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn init_command_tree() -> CommandTree {
|
|
CommandTree::new(NAMES, DESCRIPTION)
|
|
.then(argument(ARG_LOCATION, Position3DArgumentConsumer).execute(SelfToPosExecutor))
|
|
.then(argument(ARG_DESTINATION, EntityArgumentConsumer).execute(SelfToEntityExecutor))
|
|
.then(
|
|
argument(ARG_TARGETS, EntitiesArgumentConsumer)
|
|
.then(
|
|
argument(ARG_LOCATION, Position3DArgumentConsumer)
|
|
.execute(EntitiesToPosExecutor)
|
|
.then(
|
|
argument(ARG_ROTATION, RotationArgumentConsumer)
|
|
.execute(EntitiesToPosWithRotationExecutor),
|
|
)
|
|
.then(
|
|
literal("facing")
|
|
.then(
|
|
literal("entity").then(
|
|
argument(ARG_FACING_ENTITY, EntityArgumentConsumer)
|
|
.execute(EntitiesToPosFacingEntityExecutor),
|
|
),
|
|
)
|
|
.then(
|
|
argument(ARG_FACING_LOCATION, Position3DArgumentConsumer)
|
|
.execute(EntitiesToPosFacingPosExecutor),
|
|
),
|
|
),
|
|
)
|
|
.then(
|
|
argument(ARG_DESTINATION, EntityArgumentConsumer)
|
|
.execute(EntitiesToEntityExecutor),
|
|
),
|
|
)
|
|
}
|