mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Add Entity AI
This commit is contained in:
@@ -70,7 +70,7 @@ and customizable experience. It prioritizes performance and player enjoyment whi
|
||||
- [x] Players
|
||||
- [x] Mobs
|
||||
- [x] Animals
|
||||
- [ ] Entity AI
|
||||
- [x] Entity AI
|
||||
- [ ] Boss
|
||||
- Server
|
||||
- [ ] Plugins
|
||||
|
||||
@@ -42,6 +42,17 @@ impl<T: Math + Copy> Vector3<T> {
|
||||
z: self.z * z,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn squared_distance_to_vec(&self, other: Self) -> T {
|
||||
self.squared_distance_to(other.x, other.y, other.z)
|
||||
}
|
||||
|
||||
pub fn squared_distance_to(&self, x: T, y: T, z: T) -> T {
|
||||
let delta_x = self.x - x;
|
||||
let delta_y = self.y - y;
|
||||
let delta_z = self.z - z;
|
||||
delta_x * delta_x + delta_y * delta_y + delta_z * delta_z
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Math + Copy + Float> Vector3<T> {
|
||||
|
||||
@@ -1 +1,56 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::entity::{mob::MobEntity, player::Player};
|
||||
|
||||
use super::Goal;
|
||||
|
||||
pub struct LookAtEntityGoal {
|
||||
// TODO: make this an entity
|
||||
target: Mutex<Option<Arc<Player>>>,
|
||||
range: f64,
|
||||
}
|
||||
|
||||
impl LookAtEntityGoal {
|
||||
#[must_use]
|
||||
pub fn new(range: f64) -> Self {
|
||||
Self {
|
||||
target: Mutex::new(None),
|
||||
range,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Goal for LookAtEntityGoal {
|
||||
async fn can_start(&self, mob: &crate::entity::mob::MobEntity) -> bool {
|
||||
// TODO: make this an entity
|
||||
let mut target = self.target.lock().await;
|
||||
|
||||
*target = mob
|
||||
.living_entity
|
||||
.entity
|
||||
.world
|
||||
.get_closest_player(mob.living_entity.entity.pos.load(), self.range)
|
||||
.await;
|
||||
target.is_some()
|
||||
}
|
||||
|
||||
async fn should_continue(&self, mob: &MobEntity) -> bool {
|
||||
if let Some(target) = self.target.lock().await.as_ref() {
|
||||
let mob_pos = mob.living_entity.entity.pos.load();
|
||||
let target_pos = target.living_entity.entity.pos.load();
|
||||
return mob_pos.squared_distance_to_vec(target_pos) <= (self.range * self.range);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
async fn tick(&self, mob: &MobEntity) {
|
||||
if let Some(target) = self.target.lock().await.as_ref() {
|
||||
let target_pos = target.living_entity.entity.pos.load();
|
||||
mob.living_entity.entity.look_at(target_pos).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::entity::mob::MobEntity;
|
||||
|
||||
pub mod look_at_entity;
|
||||
|
||||
pub trait Goal {
|
||||
#[async_trait]
|
||||
pub trait Goal: Send + Sync {
|
||||
/// How Should the Goal initially start?
|
||||
fn can_start(&self) -> bool;
|
||||
async fn can_start(&self, mob: &MobEntity) -> bool;
|
||||
/// When its started, How it should Continue to run
|
||||
fn should_continue() -> bool;
|
||||
async fn should_continue(&self, mob: &MobEntity) -> bool;
|
||||
/// If the Goal is running, this gets called every tick
|
||||
fn tick(&self);
|
||||
async fn tick(&self, mob: &MobEntity);
|
||||
}
|
||||
|
||||
@@ -1 +1,54 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_core::math::vector3::Vector3;
|
||||
use pumpkin_entity::entity_type::EntityType;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
use zombie::Zombie;
|
||||
|
||||
use crate::{server::Server, world::World};
|
||||
|
||||
use super::{ai::goal::Goal, living::LivingEntity};
|
||||
|
||||
pub mod zombie;
|
||||
|
||||
pub struct MobEntity {
|
||||
pub living_entity: Arc<LivingEntity>,
|
||||
pub goals: Mutex<Vec<(Arc<dyn Goal>, bool)>>,
|
||||
}
|
||||
|
||||
impl MobEntity {
|
||||
pub async fn tick(&self) {
|
||||
let mut goals = self.goals.lock().await;
|
||||
for (goal, running) in goals.iter_mut() {
|
||||
if *running {
|
||||
if goal.should_continue(self).await {
|
||||
goal.tick(self).await;
|
||||
} else {
|
||||
*running = false;
|
||||
}
|
||||
} else {
|
||||
*running = goal.can_start(self).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_type(
|
||||
entity_type: EntityType,
|
||||
server: &Server,
|
||||
position: Vector3<f64>,
|
||||
world: &Arc<World>,
|
||||
) -> (Arc<MobEntity>, Uuid) {
|
||||
match entity_type {
|
||||
EntityType::Zombie => Zombie::make(server, position, world).await,
|
||||
// TODO
|
||||
_ => server.add_mob_entity(entity_type, position, world).await,
|
||||
}
|
||||
}
|
||||
|
||||
impl MobEntity {
|
||||
pub async fn goal<T: Goal + 'static>(&self, goal: T) {
|
||||
self.goals.lock().await.push((Arc::new(goal), false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,25 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_core::math::vector3::Vector3;
|
||||
use pumpkin_entity::entity_type::EntityType;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{entity::ai::goal::look_at_entity::LookAtEntityGoal, server::Server, world::World};
|
||||
|
||||
use super::MobEntity;
|
||||
|
||||
pub struct Zombie;
|
||||
|
||||
impl Zombie {
|
||||
pub async fn make(
|
||||
server: &Server,
|
||||
position: Vector3<f64>,
|
||||
world: &Arc<World>,
|
||||
) -> (Arc<MobEntity>, Uuid) {
|
||||
let (zombie_entity, uuid) = server
|
||||
.add_mob_entity(EntityType::Zombie, position, world)
|
||||
.await;
|
||||
zombie_entity.goal(LookAtEntityGoal::new(8.0)).await;
|
||||
(zombie_entity, uuid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use pumpkin_core::math::{
|
||||
};
|
||||
use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId};
|
||||
use pumpkin_protocol::{
|
||||
client::play::{CSetEntityMetadata, CTeleportEntity, Metadata},
|
||||
client::play::{CHeadRot, CSetEntityMetadata, CTeleportEntity, CUpdateEntityRot, Metadata},
|
||||
codec::var_int::VarInt,
|
||||
};
|
||||
|
||||
@@ -67,23 +67,29 @@ pub struct Entity {
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
entity_id: EntityId,
|
||||
entity_uuid: uuid::Uuid,
|
||||
world: Arc<World>,
|
||||
position: Vector3<f64>,
|
||||
entity_type: EntityType,
|
||||
standing_eye_height: f32,
|
||||
bounding_box: AtomicCell<BoundingBox>,
|
||||
bounding_box_size: AtomicCell<BoundingBoxSize>,
|
||||
) -> Self {
|
||||
let floor_x = position.x.floor() as i32;
|
||||
let floor_y = position.y.floor() as i32;
|
||||
let floor_z = position.z.floor() as i32;
|
||||
|
||||
Self {
|
||||
entity_id,
|
||||
entity_uuid,
|
||||
entity_type,
|
||||
on_ground: AtomicBool::new(false),
|
||||
pos: AtomicCell::new(Vector3::new(0.0, 0.0, 0.0)),
|
||||
block_pos: AtomicCell::new(WorldPosition(Vector3::new(0, 0, 0))),
|
||||
chunk_pos: AtomicCell::new(Vector2::new(0, 0)),
|
||||
pos: AtomicCell::new(position),
|
||||
block_pos: AtomicCell::new(WorldPosition(Vector3::new(floor_x, floor_y, floor_z))),
|
||||
chunk_pos: AtomicCell::new(Vector2::new(floor_x, floor_z)),
|
||||
sneaking: AtomicBool::new(false),
|
||||
world,
|
||||
// TODO: Load this from previous instance
|
||||
@@ -141,7 +147,7 @@ impl Entity {
|
||||
}
|
||||
|
||||
/// Changes this entity's pitch and yaw to look at target
|
||||
pub fn look_at(&self, target: Vector3<f64>) {
|
||||
pub async fn look_at(&self, target: Vector3<f64>) {
|
||||
let position = self.pos.load();
|
||||
let delta = target.sub(&position);
|
||||
let root = delta.x.hypot(delta.z);
|
||||
@@ -149,6 +155,21 @@ impl Entity {
|
||||
let yaw = wrap_degrees((delta.z.atan2(delta.x) as f32 * 180.0 / f32::consts::PI) - 90.0);
|
||||
self.pitch.store(pitch);
|
||||
self.yaw.store(yaw);
|
||||
|
||||
// send packet
|
||||
let yaw = (yaw * 256.0 / 360.0).rem_euclid(256.0);
|
||||
let pitch = (pitch * 256.0 / 360.0).rem_euclid(256.0);
|
||||
self.world
|
||||
.broadcast_packet_all(&CUpdateEntityRot::new(
|
||||
self.entity_id.into(),
|
||||
yaw as u8,
|
||||
pitch as u8,
|
||||
self.on_ground.load(std::sync::atomic::Ordering::Relaxed),
|
||||
))
|
||||
.await;
|
||||
self.world
|
||||
.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) {
|
||||
|
||||
@@ -158,6 +158,7 @@ impl Player {
|
||||
entity_id,
|
||||
player_uuid,
|
||||
world,
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
EntityType::Player,
|
||||
1.62,
|
||||
AtomicCell::new(BoundingBox::new_default(&bounding_box_size)),
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::num::NonZeroU8;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::block::block_manager::BlockActionResult;
|
||||
use crate::entity::mob;
|
||||
use crate::net::PlayerConfig;
|
||||
use crate::world::World;
|
||||
use crate::{
|
||||
command::CommandSender,
|
||||
entity::player::{ChatMode, Hand, Player},
|
||||
@@ -664,7 +664,7 @@ impl Player {
|
||||
return;
|
||||
}
|
||||
entity_victim.kill().await;
|
||||
World::remove_living_entity(entity_victim, world.clone()).await;
|
||||
world.clone().remove_mob_entity(entity_victim).await;
|
||||
// TODO: block entities should be checked here (signs)
|
||||
} else {
|
||||
log::error!(
|
||||
@@ -1006,19 +1006,24 @@ impl Player {
|
||||
if let Some(spawn_item_name) = get_entity_id(&item_t) {
|
||||
let head_yaw = 10.0;
|
||||
let world_pos = WorldPosition(location.0 + face.to_offset());
|
||||
let pos = Vector3::new(
|
||||
f64::from(world_pos.0.x),
|
||||
f64::from(world_pos.0.y),
|
||||
f64::from(world_pos.0.z),
|
||||
);
|
||||
|
||||
// TODO: this should not be hardcoded
|
||||
let (mob, _world, uuid) = server.add_living_entity(EntityType::Chicken).await;
|
||||
let (mob, uuid) = mob::from_type(EntityType::Zombie, server, pos, self.world()).await;
|
||||
|
||||
let opposite_yaw = self.living_entity.entity.yaw.load() + 180.0;
|
||||
server
|
||||
.broadcast_packet_all(&CSpawnEntity::new(
|
||||
VarInt(mob.entity.entity_id),
|
||||
VarInt(mob.living_entity.entity.entity_id),
|
||||
uuid,
|
||||
VarInt((*spawn_item_name).into()),
|
||||
f64::from(world_pos.0.x) + f64::from(cursor_pos.x),
|
||||
f64::from(world_pos.0.y),
|
||||
f64::from(world_pos.0.z) + f64::from(cursor_pos.z),
|
||||
pos.x + f64::from(cursor_pos.x),
|
||||
pos.y,
|
||||
pos.z + f64::from(cursor_pos.z),
|
||||
10.0,
|
||||
head_yaw,
|
||||
opposite_yaw,
|
||||
@@ -1075,7 +1080,7 @@ impl Player {
|
||||
|
||||
let block_bounding_box = BoundingBox::from_block(&world_pos);
|
||||
let mut intersects = false;
|
||||
for player in world.get_nearby_players(entity.pos.load(), 20).await {
|
||||
for player in world.get_nearby_players(entity.pos.load(), 20.0).await {
|
||||
let bounding_box = player.1.living_entity.entity.bounding_box.load();
|
||||
if bounding_box.intersects(&block_bounding_box) {
|
||||
intersects = true;
|
||||
|
||||
@@ -5,6 +5,7 @@ use pumpkin_config::BASIC_CONFIG;
|
||||
use pumpkin_core::math::boundingbox::{BoundingBox, BoundingBoxSize};
|
||||
use pumpkin_core::math::position::WorldPosition;
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use pumpkin_core::math::vector3::Vector3;
|
||||
use pumpkin_core::GameMode;
|
||||
use pumpkin_entity::entity_type::EntityType;
|
||||
use pumpkin_entity::EntityId;
|
||||
@@ -32,6 +33,7 @@ use uuid::Uuid;
|
||||
use crate::block::block_manager::BlockManager;
|
||||
use crate::block::default_block_manager;
|
||||
use crate::entity::living::LivingEntity;
|
||||
use crate::entity::mob::MobEntity;
|
||||
use crate::entity::Entity;
|
||||
use crate::net::EncryptionError;
|
||||
use crate::world::custom_bossbar::CustomBossbars;
|
||||
@@ -194,7 +196,22 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new living entity to the server.
|
||||
pub async fn add_mob_entity(
|
||||
&self,
|
||||
entity_type: EntityType,
|
||||
position: Vector3<f64>,
|
||||
world: &Arc<World>,
|
||||
) -> (Arc<MobEntity>, Uuid) {
|
||||
let (living_entity, uuid) = self.add_living_entity(position, entity_type, world);
|
||||
|
||||
let mob = Arc::new(MobEntity {
|
||||
living_entity,
|
||||
goals: Mutex::new(vec![]),
|
||||
});
|
||||
world.add_mob_entity(uuid, mob.clone()).await;
|
||||
(mob, uuid)
|
||||
}
|
||||
/// Adds a new living entity to the server. This does not Spawn the entity
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
@@ -203,27 +220,25 @@ impl Server {
|
||||
/// - `Arc<LivingEntity>`: A reference to the newly created living entity.
|
||||
/// - `Arc<World>`: A reference to the world that the living entity was added to.
|
||||
/// - `Uuid`: The uuid of the newly created living entity to be used to send to the client.
|
||||
pub async fn add_living_entity(
|
||||
fn add_living_entity(
|
||||
&self,
|
||||
position: Vector3<f64>,
|
||||
entity_type: EntityType,
|
||||
) -> (Arc<LivingEntity>, Arc<World>, Uuid) {
|
||||
world: &Arc<World>,
|
||||
) -> (Arc<LivingEntity>, Uuid) {
|
||||
let entity_id = self.new_entity_id();
|
||||
// TODO: select current
|
||||
let world = &self.worlds[0];
|
||||
|
||||
// TODO: this should be resolved to a integer using a macro when calling this function
|
||||
let bounding_box_size: BoundingBoxSize;
|
||||
if let Some(entity) = get_entity_by_id(entity_type.clone() as u16) {
|
||||
bounding_box_size = BoundingBoxSize {
|
||||
width: f64::from(entity.dimension[0]),
|
||||
height: f64::from(entity.dimension[1]),
|
||||
};
|
||||
} else {
|
||||
bounding_box_size = BoundingBoxSize {
|
||||
let bounding_box_size = get_entity_by_id(entity_type.clone() as u16).map_or(
|
||||
BoundingBoxSize {
|
||||
width: 0.6,
|
||||
height: 1.8,
|
||||
};
|
||||
}
|
||||
},
|
||||
|entity| BoundingBoxSize {
|
||||
width: f64::from(entity.dimension[0]),
|
||||
height: f64::from(entity.dimension[1]),
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: standing eye height should be per mob
|
||||
let new_uuid = uuid::Uuid::new_v4();
|
||||
@@ -231,15 +246,14 @@ impl Server {
|
||||
entity_id,
|
||||
new_uuid,
|
||||
world.clone(),
|
||||
position,
|
||||
entity_type,
|
||||
1.62,
|
||||
AtomicCell::new(BoundingBox::new_default(&bounding_box_size)),
|
||||
AtomicCell::new(bounding_box_size),
|
||||
)));
|
||||
|
||||
world.add_living_entity(new_uuid, mob.clone()).await;
|
||||
|
||||
(mob, world.clone(), new_uuid)
|
||||
(mob, new_uuid)
|
||||
}
|
||||
|
||||
pub async fn try_get_container(
|
||||
|
||||
@@ -5,7 +5,7 @@ pub mod player_chunker;
|
||||
|
||||
use crate::{
|
||||
command::client_cmd_suggestions,
|
||||
entity::{living::LivingEntity, player::Player, Entity},
|
||||
entity::{living::LivingEntity, mob::MobEntity, player::Player, Entity},
|
||||
error::PumpkinError,
|
||||
server::Server,
|
||||
};
|
||||
@@ -94,6 +94,8 @@ pub struct World {
|
||||
pub level: Arc<Level>,
|
||||
/// A map of active players within the world, keyed by their unique UUID.
|
||||
pub current_players: Arc<Mutex<HashMap<uuid::Uuid, Arc<Player>>>>,
|
||||
/// A map of active mob entities within the world, keyed by their unique UUID.
|
||||
pub current_living_mobs: Arc<Mutex<HashMap<uuid::Uuid, Arc<MobEntity>>>>,
|
||||
/// The world's scoreboard, used for tracking scores, objectives, and display information.
|
||||
pub scoreboard: Mutex<Scoreboard>,
|
||||
/// The world's worldborder, defining the playable area and controlling its expansion or contraction.
|
||||
@@ -102,8 +104,6 @@ pub struct World {
|
||||
pub level_time: Mutex<LevelTime>,
|
||||
/// The type of dimension the world is in
|
||||
pub dimension_type: DimensionType,
|
||||
/// A map of active entities within the world, keyed by their unique UUID.
|
||||
pub current_living_entities: Arc<Mutex<HashMap<uuid::Uuid, Arc<LivingEntity>>>>,
|
||||
// TODO: entities
|
||||
}
|
||||
|
||||
@@ -113,11 +113,11 @@ impl World {
|
||||
Self {
|
||||
level: Arc::new(level),
|
||||
current_players: Arc::new(Mutex::new(HashMap::new())),
|
||||
current_living_mobs: Arc::new(Mutex::new(HashMap::new())),
|
||||
scoreboard: Mutex::new(Scoreboard::new()),
|
||||
worldborder: Mutex::new(Worldborder::new(0.0, 0.0, 29_999_984.0, 0, 0, 0)),
|
||||
level_time: Mutex::new(LevelTime::new()),
|
||||
dimension_type,
|
||||
current_living_entities: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,16 +198,21 @@ impl World {
|
||||
|
||||
pub async fn tick(&self) {
|
||||
// world ticks
|
||||
let mut level_time = self.level_time.lock().await;
|
||||
level_time.tick_time();
|
||||
if level_time.world_age % 20 == 0 {
|
||||
level_time.send_time(self).await;
|
||||
{
|
||||
let mut level_time = self.level_time.lock().await;
|
||||
level_time.tick_time();
|
||||
if level_time.world_age % 20 == 0 {
|
||||
level_time.send_time(self).await;
|
||||
}
|
||||
}
|
||||
// player ticks
|
||||
let current_players = self.current_players.lock().await;
|
||||
for player in current_players.values() {
|
||||
for player in self.current_players.lock().await.values() {
|
||||
player.tick().await;
|
||||
}
|
||||
// entites tick
|
||||
for entity in self.current_living_mobs.lock().await.values() {
|
||||
entity.tick().await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the y position of the first non air block from the top down
|
||||
@@ -609,7 +614,8 @@ impl World {
|
||||
|
||||
/// Gets a Living Entity by entity id
|
||||
pub async fn get_living_entity_by_entityid(&self, id: EntityId) -> Option<Arc<LivingEntity>> {
|
||||
for living_entity in self.current_living_entities.lock().await.values() {
|
||||
for mob_entity in self.current_living_mobs.lock().await.values() {
|
||||
let living_entity = &mob_entity.living_entity;
|
||||
if living_entity.entity_id() == id {
|
||||
return Some(living_entity.clone());
|
||||
}
|
||||
@@ -682,27 +688,42 @@ impl World {
|
||||
pub async fn get_nearby_players(
|
||||
&self,
|
||||
pos: Vector3<f64>,
|
||||
radius: u16,
|
||||
radius: f64,
|
||||
) -> HashMap<uuid::Uuid, Arc<Player>> {
|
||||
let radius_squared = (f64::from(radius)).powi(2);
|
||||
let radius_squared = radius.powi(2);
|
||||
|
||||
let mut found_players = HashMap::new();
|
||||
for player in self.current_players.lock().await.iter() {
|
||||
let player_pos = player.1.living_entity.entity.pos.load();
|
||||
self.current_players
|
||||
.lock()
|
||||
.await
|
||||
.iter()
|
||||
.filter_map(|(id, player)| {
|
||||
let player_pos = player.living_entity.entity.pos.load();
|
||||
(player_pos.squared_distance_to_vec(pos) <= radius_squared)
|
||||
.then(|| (*id, player.clone()))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
let diff = Vector3::new(
|
||||
player_pos.x - pos.x,
|
||||
player_pos.y - pos.y,
|
||||
player_pos.z - pos.z,
|
||||
);
|
||||
|
||||
let distance_squared = diff.x.powi(2) + diff.y.powi(2) + diff.z.powi(2);
|
||||
if distance_squared <= radius_squared {
|
||||
found_players.insert(*player.0, player.1.clone());
|
||||
}
|
||||
}
|
||||
|
||||
found_players
|
||||
pub async fn get_closest_player(&self, pos: Vector3<f64>, radius: f64) -> Option<Arc<Player>> {
|
||||
let players = self.get_nearby_players(pos, radius).await;
|
||||
players
|
||||
.iter()
|
||||
.min_by(|a, b| {
|
||||
a.1.living_entity
|
||||
.entity
|
||||
.pos
|
||||
.load()
|
||||
.squared_distance_to_vec(pos)
|
||||
.partial_cmp(
|
||||
&b.1.living_entity
|
||||
.entity
|
||||
.pos
|
||||
.load()
|
||||
.squared_distance_to_vec(pos),
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.map(|p| p.1.clone())
|
||||
}
|
||||
|
||||
/// Adds a player to the world and broadcasts a join message if enabled.
|
||||
@@ -780,19 +801,19 @@ impl World {
|
||||
///
|
||||
/// * `uuid`: The unique UUID of the living entity to add.
|
||||
/// * `living_entity`: A `Arc<LivingEntity>` reference to the living entity object.
|
||||
pub async fn add_living_entity(&self, uuid: uuid::Uuid, living_entity: Arc<LivingEntity>) {
|
||||
let mut current_living_entities = self.current_living_entities.lock().await;
|
||||
pub async fn add_mob_entity(&self, uuid: uuid::Uuid, living_entity: Arc<MobEntity>) {
|
||||
let mut current_living_entities = self.current_living_mobs.lock().await;
|
||||
current_living_entities.insert(uuid, living_entity);
|
||||
}
|
||||
|
||||
pub async fn remove_living_entity(living_entity: Arc<LivingEntity>, world: Arc<Self>) {
|
||||
let mut current_living_entities = world.current_living_entities.lock().await.clone();
|
||||
pub async fn remove_mob_entity(self: Arc<Self>, living_entity: Arc<LivingEntity>) {
|
||||
let mut current_living_entities = self.current_living_mobs.lock().await.clone();
|
||||
current_living_entities.remove(&living_entity.entity.entity_uuid);
|
||||
// TODO: does this work with collisions?
|
||||
living_entity.entity.set_pose(EntityPose::Dying).await;
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await;
|
||||
world.remove_entity(&living_entity.entity).await;
|
||||
current_living_entities.remove(&living_entity.entity.entity_uuid);
|
||||
self.remove_entity(&living_entity.entity).await;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user