diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index 994cebf14..ecdd055a0 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use pumpkin_data::entity::EntityType; +use pumpkin_protocol::{client::play::CSpawnEntity, codec::var_int::VarInt}; use pumpkin_util::math::vector3::Vector3; use tokio::sync::Mutex; use uuid::Uuid; @@ -60,4 +61,25 @@ impl MobEntity { pub async fn goal(&self, goal: T) { self.goals.lock().await.push((Arc::new(goal), false)); } + + pub fn create_spawn_entity_packet(&self, uuid: Uuid) -> CSpawnEntity { + let e = &self.living_entity.entity; + let entity_loc = e.pos.load(); + let entity_vel = e.velocity.load(); + CSpawnEntity::new( + VarInt(e.entity_id), + uuid, + VarInt((e.entity_type) as i32), + entity_loc.x, + entity_loc.y, + entity_loc.z, + e.pitch.load(), + e.yaw.load(), + e.yaw.load(), // todo: head_yaw and yaw are swapped, find out why + 0.into(), + entity_vel.x as f32, + entity_vel.y as f32, + entity_vel.z as f32, + ) + } } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index a13e7ddea..6b81dd7fe 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -532,6 +532,17 @@ impl Player { .await; } + /// Sends the mobs to just the player. + // TODO: This should be optimized for larger servers based on current player chunk + pub async fn send_mobs(&self, world: &World) { + let mobs = world.current_living_mobs.lock().await.clone(); + for (uuid, mob) in mobs { + self.client + .send_packet(&mob.create_spawn_entity_packet(uuid)) + .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 diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index a9125c095..9f665a7e6 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -16,7 +16,7 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::world::CHAT; use pumpkin_inventory::player::PlayerInventory; use pumpkin_inventory::InventoryError; -use pumpkin_protocol::client::play::{CSetContainerSlot, CSetHeldItem, CSpawnEntity}; +use pumpkin_protocol::client::play::{CSetContainerSlot, CSetHeldItem}; use pumpkin_protocol::codec::slot::Slot; use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse; @@ -1092,8 +1092,10 @@ impl Player { f64::from(world_pos.0.y), f64::from(world_pos.0.z) + 0.5, ); + // create rotation like Vanilla + let yaw = wrap_degrees(rand::random::() * 360.0) % 360.0; - // TODO: this should not be hardcoded + // create new mob and uuid based on spawn egg id let (mob, uuid) = mob::from_type( EntityType::from_raw(*spawn_item_id).unwrap(), server, @@ -1101,25 +1103,13 @@ impl Player { self.world(), ) .await; - let yaw = wrap_degrees(rand::random::() * 360.0) % 360.0; + + // set the rotation mob.living_entity.entity.set_rotation(yaw, 0.0); + // broadcast new mob to all players server - .broadcast_packet_all(&CSpawnEntity::new( - VarInt(mob.living_entity.entity.entity_id), - uuid, - VarInt((*spawn_item_id).into()), - pos.x, - pos.y, - pos.z, - 0.0, - yaw, - yaw, - 0.into(), - 0.0, - 0.0, - 0.0, - )) + .broadcast_packet_all(&mob.create_spawn_entity_packet(uuid)) .await; // TODO: send/configure additional commands/data based on type of entity (horse, slime, etc) diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 70b0d732a..9d7551da8 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -462,6 +462,8 @@ impl World { // player.send_bossbar(bossbar).await; // } // } + + player.send_mobs(self).await; } pub async fn respawn_player(&self, player: &Arc, alive: bool) { @@ -865,13 +867,18 @@ impl World { } pub async fn remove_mob_entity(self: Arc, living_entity: Arc) { - let mut current_living_entities = self.current_living_mobs.lock().await.clone(); - current_living_entities.remove(&living_entity.entity.entity_uuid); + let mut current_living_entities = self.current_living_mobs.lock().await; + current_living_entities + .remove(&living_entity.entity.entity_uuid) + .unwrap(); + // TODO: does this work with collisions? living_entity.entity.set_pose(EntityPose::Dying).await; + + let world = self.clone(); tokio::spawn(async move { tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; - self.remove_entity(&living_entity.entity).await; + world.remove_entity(&living_entity.entity).await; }); }