mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Support Teleport to different world (#532)
* make entity world RwLock * Add teleport function * Mark client as not loaded before change dim * clippy * Post-merge issues * FIx post merge issues * Make world.remove_player fire event conditionally * Remove extra StartWaitingChunks packet * Add world.send_world_info()
This commit is contained in:
@@ -109,11 +109,13 @@ impl ChestBlock {
|
||||
if state == ChestState::IsClosed && num_players == 0 {
|
||||
player
|
||||
.world()
|
||||
.await
|
||||
.play_block_sound(Sound::BlockChestClose, SoundCategory::Blocks, location)
|
||||
.await;
|
||||
} else if state == ChestState::IsOpened && num_players == 1 {
|
||||
player
|
||||
.world()
|
||||
.await
|
||||
.play_block_sound(Sound::BlockChestOpen, SoundCategory::Blocks, location)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl PumpkinBlock for JukeboxBlock {
|
||||
_server: &Server,
|
||||
) {
|
||||
// For now just stop the music at this position
|
||||
let world = &player.living_entity.entity.world;
|
||||
let world = &player.living_entity.entity.world.read().await;
|
||||
|
||||
world.stop_record(location).await;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ impl PumpkinBlock for JukeboxBlock {
|
||||
item: &Item,
|
||||
_server: &Server,
|
||||
) -> BlockActionResult {
|
||||
let world = &player.living_entity.entity.world;
|
||||
let world = &player.living_entity.entity.world.read().await;
|
||||
|
||||
let Some(jukebox_playable) = &item.components.jukebox_playable else {
|
||||
return BlockActionResult::Continue;
|
||||
@@ -59,7 +59,7 @@ impl PumpkinBlock for JukeboxBlock {
|
||||
|
||||
async fn broken(&self, _block: &Block, player: &Player, location: BlockPos, _server: &Server) {
|
||||
// For now just stop the music at this position
|
||||
let world = &player.living_entity.entity.world;
|
||||
let world = &player.living_entity.entity.world.read().await;
|
||||
|
||||
world.stop_record(location).await;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ pub async fn standard_on_broken_with_container(
|
||||
|
||||
/// The standard open container creates a new container if a container of the same block
|
||||
/// type does not exist at the selected block location. If a container of the same type exists, the player
|
||||
/// is added to the currently connected players to that container.
|
||||
/// is added to the currently connected players to that container.
|
||||
pub async fn standard_open_container<C: Container + Default + 'static>(
|
||||
block: &Block,
|
||||
player: &Player,
|
||||
@@ -113,7 +113,7 @@ pub async fn standard_open_container_unique<C: Container + Default + 'static>(
|
||||
|
||||
pub async fn close_all_in_container(player: &Player, container: &OpenContainer) {
|
||||
for id in container.all_player_ids() {
|
||||
if let Some(remote_player) = player.world().get_player_by_id(id).await {
|
||||
if let Some(remote_player) = player.world().await.get_player_by_id(id).await {
|
||||
remote_player.close_container().await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,10 @@ impl CommandExecutor for SetblockExecutor {
|
||||
let end_y = from.0.y.max(to.0.y);
|
||||
let end_z = from.0.z.max(to.0.z);
|
||||
|
||||
let world = sender.world().ok_or(CommandError::InvalidRequirement)?;
|
||||
let world = sender
|
||||
.world()
|
||||
.await
|
||||
.ok_or(CommandError::InvalidRequirement)?;
|
||||
let mut placed_blocks = 0;
|
||||
|
||||
match mode {
|
||||
|
||||
@@ -22,7 +22,9 @@ impl CommandExecutor for PumpkinExecutor {
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let seed = match sender {
|
||||
CommandSender::Player(player) => player.living_entity.entity.world.level.seed.0,
|
||||
CommandSender::Player(player) => {
|
||||
player.living_entity.entity.world.read().await.level.seed.0
|
||||
}
|
||||
// TODO: Maybe ask player for world, or get the current world
|
||||
_ => match server.worlds.read().await.first() {
|
||||
Some(world) => world.level.seed.0,
|
||||
|
||||
@@ -42,7 +42,10 @@ impl CommandExecutor for SetblockExecutor {
|
||||
let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?;
|
||||
let mode = self.0;
|
||||
// TODO: allow console to use the command (seed sender.world)
|
||||
let world = sender.world().ok_or(CommandError::InvalidRequirement)?;
|
||||
let world = sender
|
||||
.world()
|
||||
.await
|
||||
.ok_or(CommandError::InvalidRequirement)?;
|
||||
|
||||
let success = match mode {
|
||||
Mode::Destroy => {
|
||||
|
||||
@@ -37,8 +37,8 @@ impl CommandExecutor for SummonExecutor {
|
||||
// TODO: Make this work in console
|
||||
if let Some(player) = sender.as_player() {
|
||||
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
|
||||
let mob = mob::from_type(entity, server, pos, player.world()).await;
|
||||
player.world().spawn_entity(mob).await;
|
||||
let mob = mob::from_type(entity, server, pos, &player.world().await).await;
|
||||
player.world().await.spawn_entity(mob).await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.summon.success",
|
||||
|
||||
@@ -30,14 +30,17 @@ impl CommandExecutor for WeatherExecutor {
|
||||
_server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let world = sender.world().ok_or(CommandError::InvalidRequirement)?;
|
||||
let world = sender
|
||||
.world()
|
||||
.await
|
||||
.ok_or(CommandError::InvalidRequirement)?;
|
||||
let duration = TimeArgumentConsumer::find_arg(args, ARG_DURATION).unwrap_or(6000);
|
||||
let mut weather = world.weather.lock().await;
|
||||
|
||||
match self.mode {
|
||||
WeatherMode::Clear => {
|
||||
weather
|
||||
.set_weather_parameters(world, duration, 0, false, false)
|
||||
.set_weather_parameters(&world, duration, 0, false, false)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
@@ -48,7 +51,7 @@ impl CommandExecutor for WeatherExecutor {
|
||||
}
|
||||
WeatherMode::Rain => {
|
||||
weather
|
||||
.set_weather_parameters(world, 0, duration, true, false)
|
||||
.set_weather_parameters(&world, 0, duration, true, false)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
@@ -59,7 +62,7 @@ impl CommandExecutor for WeatherExecutor {
|
||||
}
|
||||
WeatherMode::Thunder => {
|
||||
weather
|
||||
.set_weather_parameters(world, 0, duration, true, true)
|
||||
.set_weather_parameters(&world, 0, duration, true, true)
|
||||
.await;
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
|
||||
@@ -97,11 +97,11 @@ impl CommandSender<'_> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn world(&self) -> Option<&Arc<World>> {
|
||||
pub async fn world(&self) -> Option<Arc<World>> {
|
||||
match self {
|
||||
// TODO: maybe return first world when console
|
||||
CommandSender::Console | CommandSender::Rcon(..) => None,
|
||||
CommandSender::Player(p) => Some(&p.living_entity.entity.world),
|
||||
CommandSender::Player(p) => Some(p.living_entity.entity.world.read().await.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ impl Goal for LookAtEntityGoal {
|
||||
.living_entity
|
||||
.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.get_closest_player(mob.living_entity.entity.pos.load(), self.range)
|
||||
.await;
|
||||
target.is_some()
|
||||
|
||||
@@ -34,6 +34,8 @@ impl Goal for TargetGoal {
|
||||
.living_entity
|
||||
.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.get_closest_player(mob.living_entity.entity.pos.load(), self.range)
|
||||
.await;
|
||||
// we can't use filter, because of async clousrers
|
||||
|
||||
@@ -72,6 +72,8 @@ impl Navigator {
|
||||
entity
|
||||
.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CUpdateEntityPos::new(
|
||||
entity.entity.entity_id.into(),
|
||||
Vector3::new(
|
||||
|
||||
@@ -87,6 +87,8 @@ impl LivingEntity {
|
||||
|
||||
self.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CDamageEvent::new(
|
||||
self.entity.entity_id.into(),
|
||||
damage_type.id.into(),
|
||||
@@ -181,11 +183,15 @@ impl LivingEntity {
|
||||
// Spawns death smoke particles
|
||||
self.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 60))
|
||||
.await;
|
||||
// Plays the death sound and death animation
|
||||
self.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 3))
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ use pumpkin_util::math::{
|
||||
wrap_degrees,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::world::World;
|
||||
|
||||
@@ -61,7 +62,7 @@ pub struct Entity {
|
||||
/// The type of entity (e.g., player, zombie, item)
|
||||
pub entity_type: EntityType,
|
||||
/// The world in which the entity exists.
|
||||
pub world: Arc<World>,
|
||||
pub world: Arc<RwLock<Arc<World>>>,
|
||||
/// The entity's current position in the world
|
||||
pub pos: AtomicCell<Vector3<f64>>,
|
||||
/// The entity's position rounded to the nearest block coordinates
|
||||
@@ -124,7 +125,7 @@ impl Entity {
|
||||
block_pos: AtomicCell::new(BlockPos(Vector3::new(floor_x, floor_y, floor_z))),
|
||||
chunk_pos: AtomicCell::new(Vector2::new(floor_x, floor_z)),
|
||||
sneaking: AtomicBool::new(false),
|
||||
world,
|
||||
world: Arc::new(RwLock::new(world)),
|
||||
// TODO: Load this from previous instance
|
||||
sprinting: AtomicBool::new(false),
|
||||
fall_flying: AtomicBool::new(false),
|
||||
@@ -210,6 +211,8 @@ impl Entity {
|
||||
let yaw = (yaw * 256.0 / 360.0).rem_euclid(256.0);
|
||||
let pitch = (pitch * 256.0 / 360.0).rem_euclid(256.0);
|
||||
self.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CUpdateEntityRot::new(
|
||||
self.entity_id.into(),
|
||||
yaw as u8,
|
||||
@@ -218,12 +221,16 @@ impl Entity {
|
||||
))
|
||||
.await;
|
||||
self.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CHeadRot::new(self.entity_id.into(), yaw as u8))
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn teleport(&self, position: Vector3<f64>, yaw: f32, pitch: f32) {
|
||||
self.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CTeleportEntity::new(
|
||||
self.entity_id.into(),
|
||||
position,
|
||||
@@ -248,7 +255,7 @@ impl Entity {
|
||||
|
||||
/// Removes the Entity from their current World
|
||||
pub async fn remove(&self) {
|
||||
self.world.remove_entity(self).await;
|
||||
self.world.read().await.remove_entity(self).await;
|
||||
}
|
||||
|
||||
pub fn create_spawn_packet(&self) -> CSpawnEntity {
|
||||
@@ -337,6 +344,8 @@ impl Entity {
|
||||
/// Plays sound at this entity's position with the entity's sound category
|
||||
pub async fn play_sound(&self, sound: Sound) {
|
||||
self.world
|
||||
.read()
|
||||
.await
|
||||
.play_sound(sound, SoundCategory::Neutral, &self.pos.load())
|
||||
.await;
|
||||
}
|
||||
@@ -346,6 +355,8 @@ impl Entity {
|
||||
T: Serialize,
|
||||
{
|
||||
self.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CSetEntityMetadata::new(self.entity_id.into(), meta))
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ use pumpkin_protocol::{
|
||||
client::play::{
|
||||
CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, CGameEvent, CHurtAnimation,
|
||||
CKeepAlive, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition,
|
||||
CSetExperience, CSetHealth, CSubtitle, CSystemChatMessage, CTitleText, GameEvent,
|
||||
MetaDataType, PlayerAction,
|
||||
CRespawn, CSetExperience, CSetHealth, CSubtitle, CSystemChatMessage, CTitleText,
|
||||
CUnloadChunk, GameEvent, MetaDataType, PlayerAction,
|
||||
},
|
||||
server::play::{
|
||||
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
|
||||
@@ -233,10 +233,10 @@ impl Player {
|
||||
/// Removes the Player out of the current World
|
||||
#[allow(unused_variables)]
|
||||
pub async fn remove(self: Arc<Self>) {
|
||||
let world = self.world();
|
||||
let world = self.world().await;
|
||||
self.cancel_tasks.notify_waiters();
|
||||
|
||||
world.remove_player(self.clone()).await;
|
||||
world.remove_player(self.clone(), true).await;
|
||||
|
||||
let cylindrical = self.watched_section.load();
|
||||
|
||||
@@ -272,7 +272,7 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn attack(&self, victim: Arc<dyn EntityBase>) {
|
||||
let world = self.world();
|
||||
let world = self.world().await;
|
||||
let victim_entity = victim.get_entity();
|
||||
let victim_living_entity = victim.get_living_entity();
|
||||
let attacker_entity = &self.living_entity.entity;
|
||||
@@ -341,7 +341,7 @@ impl Player {
|
||||
|
||||
let attack_type = AttackType::new(self, attack_cooldown_progress as f32).await;
|
||||
|
||||
player_attack_sound(&pos, world, attack_type).await;
|
||||
player_attack_sound(&pos, &world, attack_type).await;
|
||||
|
||||
if matches!(attack_type, AttackType::Critical) {
|
||||
damage *= 1.5;
|
||||
@@ -357,13 +357,13 @@ impl Player {
|
||||
match attack_type {
|
||||
AttackType::Knockback => knockback_strength += 1.0,
|
||||
AttackType::Sweeping => {
|
||||
combat::spawn_sweep_particle(attacker_entity, world, &pos).await;
|
||||
combat::spawn_sweep_particle(attacker_entity, &world, &pos).await;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if config.knockback {
|
||||
combat::handle_knockback(attacker_entity, world, victim_entity, knockback_strength)
|
||||
combat::handle_knockback(attacker_entity, &world, victim_entity, knockback_strength)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -499,8 +499,8 @@ impl Player {
|
||||
self.living_entity.entity.entity_id
|
||||
}
|
||||
|
||||
pub const fn world(&self) -> &Arc<World> {
|
||||
&self.living_entity.entity.world
|
||||
pub async fn world(&self) -> Arc<World> {
|
||||
self.living_entity.entity.world.read().await.clone()
|
||||
}
|
||||
|
||||
pub fn position(&self) -> Vector3<f64> {
|
||||
@@ -577,6 +577,93 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
async fn unload_watched_chunks(&self, world: &World) {
|
||||
let radial_chunks = self.watched_section.load().all_chunks_within();
|
||||
let level = &world.level;
|
||||
let chunks_to_clean = level.mark_chunks_as_not_watched(&radial_chunks);
|
||||
level.clean_chunks(&chunks_to_clean).await;
|
||||
let client = self.client.clone();
|
||||
tokio::spawn(async move {
|
||||
for chunk in chunks_to_clean {
|
||||
if client.closed.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
client
|
||||
.send_packet(&CUnloadChunk::new(chunk.x, chunk.z))
|
||||
.await;
|
||||
}
|
||||
});
|
||||
self.watched_section.store(Cylindrical::new(
|
||||
Vector2::new(i32::MAX >> 1, i32::MAX >> 1),
|
||||
unsafe { NonZeroU8::new_unchecked(1) },
|
||||
));
|
||||
}
|
||||
|
||||
/// Teleports the player to a different world or dimension with an optional position, yaw, and pitch.
|
||||
pub async fn teleport_world(
|
||||
self: Arc<Self>,
|
||||
new_world: Arc<World>,
|
||||
position: Option<Vector3<f64>>,
|
||||
yaw: Option<f32>,
|
||||
pitch: Option<f32>,
|
||||
) {
|
||||
self.set_client_loaded(false);
|
||||
let current_world = self.living_entity.entity.world.read().await.clone();
|
||||
let uuid = self.gameprofile.id;
|
||||
current_world.remove_player(self.clone(), false).await;
|
||||
*self.living_entity.entity.world.write().await = new_world.clone();
|
||||
new_world.players.lock().await.insert(uuid, self.clone());
|
||||
self.unload_watched_chunks(¤t_world).await;
|
||||
let last_pos = self.living_entity.last_pos.load();
|
||||
let death_dimension = self.world().await.dimension_type.name();
|
||||
let death_location = BlockPos(Vector3::new(
|
||||
last_pos.x.round() as i32,
|
||||
last_pos.y.round() as i32,
|
||||
last_pos.z.round() as i32,
|
||||
));
|
||||
self.client
|
||||
.send_packet(&CRespawn::new(
|
||||
(new_world.dimension_type as u8).into(),
|
||||
new_world.dimension_type.name(),
|
||||
0, // seed
|
||||
self.gamemode.load() as u8,
|
||||
self.gamemode.load() as i8,
|
||||
false,
|
||||
false,
|
||||
Some((death_dimension, death_location)),
|
||||
0.into(),
|
||||
0.into(),
|
||||
1,
|
||||
))
|
||||
.await;
|
||||
self.send_abilities_update().await;
|
||||
self.send_permission_lvl_update().await;
|
||||
let info = &new_world.level.level_info;
|
||||
let position = if let Some(pos) = position {
|
||||
pos
|
||||
} else {
|
||||
Vector3::new(
|
||||
f64::from(info.spawn_x),
|
||||
f64::from(
|
||||
new_world
|
||||
.get_top_block(Vector2::new(
|
||||
f64::from(info.spawn_x) as i32,
|
||||
f64::from(info.spawn_x) as i32,
|
||||
))
|
||||
.await
|
||||
+ 1,
|
||||
),
|
||||
f64::from(info.spawn_z),
|
||||
)
|
||||
};
|
||||
let yaw = yaw.unwrap_or(info.spawn_angle);
|
||||
let pitch = pitch.unwrap_or(10.0);
|
||||
self.request_teleport(position, yaw, pitch).await;
|
||||
self.living_entity.last_pos.store(position);
|
||||
|
||||
new_world.send_world_info(&self, position, yaw, pitch).await;
|
||||
}
|
||||
|
||||
/// Yaw and Pitch in degrees
|
||||
/// Rarly used, For example when waking up player from bed or first time spawn. Otherwise entity teleport is used
|
||||
/// Player should respond with the `SConfirmTeleport` packet
|
||||
@@ -727,6 +814,8 @@ impl Player {
|
||||
self.living_entity
|
||||
.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CPlayerInfoUpdate::new(
|
||||
0x04,
|
||||
&[pumpkin_protocol::client::play::Player {
|
||||
@@ -785,10 +874,10 @@ impl Player {
|
||||
let entity = server.add_entity(
|
||||
self.living_entity.entity.pos.load(),
|
||||
EntityType::ITEM,
|
||||
self.world(),
|
||||
&self.world().await,
|
||||
);
|
||||
let item_entity = Arc::new(ItemEntity::new(entity, &item.clone()));
|
||||
self.world().spawn_entity(item_entity.clone()).await;
|
||||
self.world().await.spawn_entity(item_entity.clone()).await;
|
||||
item_entity.send_meta_packet().await;
|
||||
// decrase item in hotbar
|
||||
inv.decrease_current_stack(1);
|
||||
|
||||
@@ -19,7 +19,7 @@ const POWER: f32 = 1.5;
|
||||
impl PumpkinItem for EggItem {
|
||||
async fn normal_use(&self, _block: &Item, player: &Player, server: &Server) {
|
||||
let position = player.position();
|
||||
let world = player.world();
|
||||
let world = player.world().await;
|
||||
world
|
||||
.play_sound(
|
||||
Sound::EntityEggThrow,
|
||||
@@ -28,7 +28,7 @@ impl PumpkinItem for EggItem {
|
||||
)
|
||||
.await;
|
||||
// TODO: Implement eggs the right way, so there is a chance of spawning chickens
|
||||
let entity = server.add_entity(position, EntityType::EGG, world);
|
||||
let entity = server.add_entity(position, EntityType::EGG, &world);
|
||||
let snowball = ThrownItemEntity::new(entity, &player.living_entity.entity);
|
||||
let yaw = player.living_entity.entity.yaw.load();
|
||||
let pitch = player.living_entity.entity.pitch.load();
|
||||
|
||||
@@ -19,7 +19,7 @@ const POWER: f32 = 1.5;
|
||||
impl PumpkinItem for SnowBallItem {
|
||||
async fn normal_use(&self, _block: &Item, player: &Player, server: &Server) {
|
||||
let position = player.position();
|
||||
let world = player.world();
|
||||
let world = player.world().await;
|
||||
world
|
||||
.play_sound(
|
||||
Sound::EntitySnowballThrow,
|
||||
@@ -27,7 +27,7 @@ impl PumpkinItem for SnowBallItem {
|
||||
&position,
|
||||
)
|
||||
.await;
|
||||
let entity = server.add_entity(position, EntityType::SNOWBALL, world);
|
||||
let entity = server.add_entity(position, EntityType::SNOWBALL, &world);
|
||||
let snowball = ThrownItemEntity::new(entity, &player.living_entity.entity);
|
||||
let yaw = player.living_entity.entity.yaw.load();
|
||||
let pitch = player.living_entity.entity.pitch.load();
|
||||
|
||||
@@ -490,6 +490,8 @@ impl Player {
|
||||
.living_entity
|
||||
.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.players
|
||||
.lock()
|
||||
.await
|
||||
|
||||
@@ -171,7 +171,7 @@ impl Player {
|
||||
|
||||
let entity_id = entity.entity_id;
|
||||
let Vector3 { x, y, z } = position;
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
|
||||
// let delta = Vector3::new(x - lastx, y - lasty, z - lastz);
|
||||
// let velocity = self.velocity;
|
||||
@@ -266,7 +266,7 @@ impl Player {
|
||||
let yaw = (entity.yaw.load() * 256.0 / 360.0).rem_euclid(256.0);
|
||||
let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0);
|
||||
// let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor();
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
|
||||
// let delta = Vector3::new(x - lastx, y - lasty, z - lastz);
|
||||
// let velocity = self.velocity;
|
||||
@@ -348,7 +348,7 @@ impl Player {
|
||||
let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0);
|
||||
// let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0);
|
||||
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
let packet =
|
||||
CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, rotation.ground);
|
||||
world
|
||||
@@ -416,7 +416,8 @@ impl Player {
|
||||
return;
|
||||
}
|
||||
|
||||
let Ok(block) = self.world().get_block(&pick_item.pos).await else {
|
||||
let world = self.world().await;
|
||||
let Ok(block) = world.get_block(&pick_item.pos).await else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -563,7 +564,7 @@ impl Player {
|
||||
};
|
||||
|
||||
let id = self.entity_id();
|
||||
let world = self.world();
|
||||
let world = self.world().await;
|
||||
world
|
||||
.broadcast_packet_except(
|
||||
&[self.gameprofile.id],
|
||||
@@ -592,7 +593,7 @@ impl Player {
|
||||
log::info!("<chat>{}: {}", gameprofile.name, message);
|
||||
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
world
|
||||
.broadcast_packet_all(&CPlayerChatMessage::new(
|
||||
gameprofile.id,
|
||||
@@ -700,7 +701,10 @@ impl Player {
|
||||
if self.living_entity.health.load() > 0.0 {
|
||||
return;
|
||||
}
|
||||
self.world().respawn_player(&self.clone(), false).await;
|
||||
self.world()
|
||||
.await
|
||||
.respawn_player(&self.clone(), false)
|
||||
.await;
|
||||
|
||||
// Restore abilities based on gamemode after respawn
|
||||
let mut abilities = self.abilities.lock().await;
|
||||
@@ -745,7 +749,7 @@ impl Player {
|
||||
|
||||
// TODO: set as camera entity when specator
|
||||
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
let player_victim = world.get_player_by_id(entity_id.0).await;
|
||||
if entity_id.0 == self.entity_id() {
|
||||
// this can't be triggered from a non-modded client.
|
||||
@@ -822,7 +826,7 @@ impl Player {
|
||||
let location = player_action.location;
|
||||
// Block break & block break sound
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
let block = world.get_block(&location).await;
|
||||
|
||||
world
|
||||
@@ -867,7 +871,7 @@ impl Player {
|
||||
}
|
||||
// Block break & block break sound
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
let block = world.get_block(&location).await;
|
||||
|
||||
world
|
||||
@@ -956,7 +960,7 @@ impl Player {
|
||||
|
||||
let mut inventory = self.inventory().lock().await;
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
let slot_id = inventory.get_selected();
|
||||
let mut state_id = inventory.state_id;
|
||||
let item_slot = *inventory.held_item_mut();
|
||||
@@ -1053,7 +1057,7 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn handle_sign_update(&self, sign_data: SUpdateSign) {
|
||||
let world = &self.living_entity.entity.world;
|
||||
let world = &self.living_entity.entity.world.read().await;
|
||||
let updated_sign = Sign::new(
|
||||
sign_data.location,
|
||||
sign_data.is_front_text,
|
||||
@@ -1202,13 +1206,13 @@ impl Player {
|
||||
// create rotation like Vanilla
|
||||
let yaw = wrap_degrees(rand::random::<f32>() * 360.0) % 360.0;
|
||||
|
||||
let world = self.world();
|
||||
let world = self.world().await;
|
||||
// create new mob and uuid based on spawn egg id
|
||||
let mob = mob::from_type(
|
||||
EntityType::from_raw(entity_type.id).unwrap(),
|
||||
server,
|
||||
pos,
|
||||
world,
|
||||
&world,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1243,7 +1247,7 @@ impl Player {
|
||||
face: &BlockDirection,
|
||||
) -> Result<bool, Box<dyn PumpkinError>> {
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world;
|
||||
let world = &entity.world.read().await;
|
||||
|
||||
let clicked_block_pos = BlockPos(location.0);
|
||||
let clicked_block_state = world.get_block_state(&clicked_block_pos).await?;
|
||||
|
||||
@@ -69,7 +69,7 @@ pub async fn update_position(player: &Arc<Player>) {
|
||||
|
||||
// Make sure the watched section and the chunk watcher updates are async atomic. We want to
|
||||
// ensure what we unload when the player disconnects is correct
|
||||
let level = &entity.world.level;
|
||||
let level = &entity.world.read().await.level;
|
||||
level.mark_chunks_as_newly_watched(&loading_chunks);
|
||||
let chunks_to_clean = level.mark_chunks_as_not_watched(&unloading_chunks);
|
||||
player.watched_section.store(new_cylindrical);
|
||||
@@ -93,9 +93,11 @@ pub async fn update_position(player: &Arc<Player>) {
|
||||
}
|
||||
|
||||
if !loading_chunks.is_empty() {
|
||||
entity
|
||||
.world
|
||||
.spawn_world_chunks(player.clone(), loading_chunks, new_chunk_center);
|
||||
entity.world.read().await.spawn_world_chunks(
|
||||
player.clone(),
|
||||
loading_chunks,
|
||||
new_chunk_center,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,9 +513,55 @@ impl World {
|
||||
player.send_mobs(self).await;
|
||||
}
|
||||
|
||||
pub async fn send_world_info(
|
||||
&self,
|
||||
player: &Arc<Player>,
|
||||
position: Vector3<f64>,
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
) {
|
||||
self.worldborder
|
||||
.lock()
|
||||
.await
|
||||
.init_client(&player.client)
|
||||
.await;
|
||||
|
||||
// TODO: World spawn (compass stuff)
|
||||
|
||||
player
|
||||
.client
|
||||
.send_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0))
|
||||
.await;
|
||||
|
||||
let entity = &player.living_entity.entity;
|
||||
|
||||
self.broadcast_packet_except(
|
||||
&[player.gameprofile.id],
|
||||
// TODO: add velo
|
||||
&CSpawnEntity::new(
|
||||
entity.entity_id.into(),
|
||||
player.gameprofile.id,
|
||||
i32::from(EntityType::PLAYER.id).into(),
|
||||
position,
|
||||
pitch,
|
||||
yaw,
|
||||
yaw,
|
||||
0.into(),
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
player.send_client_information().await;
|
||||
|
||||
chunker::player_join(player).await;
|
||||
// update commands
|
||||
|
||||
player.set_health(20.0).await;
|
||||
}
|
||||
|
||||
pub async fn respawn_player(&self, player: &Arc<Player>, alive: bool) {
|
||||
let last_pos = player.living_entity.last_pos.load();
|
||||
let death_dimension = player.world().dimension_type.name();
|
||||
let death_dimension = player.world().await.dimension_type.name();
|
||||
let death_location = BlockPos(Vector3::new(
|
||||
last_pos.x.round() as i32,
|
||||
last_pos.y.round() as i32,
|
||||
@@ -566,43 +612,7 @@ impl World {
|
||||
|
||||
// TODO: difficulty, exp bar, status effect
|
||||
|
||||
self.worldborder
|
||||
.lock()
|
||||
.await
|
||||
.init_client(&player.client)
|
||||
.await;
|
||||
|
||||
// TODO: world spawn (compass stuff)
|
||||
|
||||
player
|
||||
.client
|
||||
.send_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0))
|
||||
.await;
|
||||
|
||||
let entity = &player.living_entity.entity;
|
||||
|
||||
self.broadcast_packet_except(
|
||||
&[player.gameprofile.id],
|
||||
// TODO: add velo
|
||||
&CSpawnEntity::new(
|
||||
entity.entity_id.into(),
|
||||
player.gameprofile.id,
|
||||
i32::from(EntityType::PLAYER.id).into(),
|
||||
position,
|
||||
pitch,
|
||||
yaw,
|
||||
yaw,
|
||||
0.into(),
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
player.send_client_information().await;
|
||||
|
||||
chunker::player_join(player).await;
|
||||
// update commands
|
||||
|
||||
player.set_health(20.0).await;
|
||||
self.send_world_info(player, position, yaw, pitch).await;
|
||||
}
|
||||
|
||||
/// IMPORTANT: Chunks have to be non-empty
|
||||
@@ -654,11 +664,11 @@ impl World {
|
||||
}
|
||||
|
||||
let (world, chunk) = if level.is_chunk_watched(&position) {
|
||||
(player.world().clone(), chunk)
|
||||
(player.world().await.clone(), chunk)
|
||||
} else {
|
||||
send_cancellable! {{
|
||||
ChunkSave {
|
||||
world: player.world().clone(),
|
||||
world: player.world().await.clone(),
|
||||
chunk,
|
||||
cancelled: false,
|
||||
};
|
||||
@@ -890,12 +900,13 @@ impl World {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `player`: A reference to the `Player` object to be removed.
|
||||
/// * `fire_event`: A boolean flag indicating whether to fire a `PlayerLeaveEvent` event.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// - This function assumes `broadcast_packet_expect` and `remove_entity` are defined elsewhere.
|
||||
/// - The disconnect message sending is currently optional. Consider making it a configurable option.
|
||||
pub async fn remove_player(&self, player: Arc<Player>) {
|
||||
pub async fn remove_player(&self, player: Arc<Player>, fire_event: bool) {
|
||||
self.players
|
||||
.lock()
|
||||
.await
|
||||
@@ -910,25 +921,27 @@ impl World {
|
||||
self.broadcast_packet_all(&CRemoveEntities::new(&[player.entity_id().into()]))
|
||||
.await;
|
||||
|
||||
let msg_comp = TextComponent::translate(
|
||||
"multiplayer.player.left",
|
||||
[TextComponent::text(player.gameprofile.name.clone())].into(),
|
||||
)
|
||||
.color_named(NamedColor::Yellow);
|
||||
let event = PlayerLeaveEvent::new(player.clone(), msg_comp);
|
||||
if fire_event {
|
||||
let msg_comp = TextComponent::translate(
|
||||
"multiplayer.player.left",
|
||||
[TextComponent::text(player.gameprofile.name.clone())].into(),
|
||||
)
|
||||
.color_named(NamedColor::Yellow);
|
||||
let event = PlayerLeaveEvent::new(player.clone(), msg_comp);
|
||||
|
||||
let event = PLUGIN_MANAGER
|
||||
.lock()
|
||||
.await
|
||||
.fire::<PlayerLeaveEvent>(event)
|
||||
.await;
|
||||
let event = PLUGIN_MANAGER
|
||||
.lock()
|
||||
.await
|
||||
.fire::<PlayerLeaveEvent>(event)
|
||||
.await;
|
||||
|
||||
if !event.cancelled {
|
||||
let players = self.players.lock().await;
|
||||
for player in players.values() {
|
||||
player.send_system_message(&event.leave_message).await;
|
||||
if !event.cancelled {
|
||||
let players = self.players.lock().await;
|
||||
for player in players.values() {
|
||||
player.send_system_message(&event.leave_message).await;
|
||||
}
|
||||
log::info!("{}", event.leave_message.clone().to_pretty_console());
|
||||
}
|
||||
log::info!("{}", event.leave_message.clone().to_pretty_console());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user