Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Path finding #459

Merged
merged 2 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion pumpkin-core/src/math/vector3.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bytes::BufMut;
use std::ops::{Add, Div, Mul, Sub};
use std::ops::{Add, AddAssign, Div, Mul, Sub};

use num_traits::Float;

Expand Down Expand Up @@ -92,6 +92,14 @@ impl<T: Math + Copy> Add for Vector3<T> {
}
}

impl<T: Math + Copy> AddAssign for Vector3<T> {
fn add_assign(&mut self, other: Self) {
self.x += other.x;
self.y += other.y;
self.z += other.z;
}
}

/*
impl<T: Math + Copy> Neg for Vector3<T> {
type Output = Self;
Expand Down Expand Up @@ -124,6 +132,7 @@ pub trait Math:
Mul<Output = Self>
//+ Neg<Output = Self>
+ Add<Output = Self>
+ AddAssign<>
+ Div<Output = Self>
+ Sub<Output = Self>
+ Sized
Expand Down
1 change: 1 addition & 0 deletions pumpkin/src/entity/ai/goal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use async_trait::async_trait;
use crate::entity::mob::MobEntity;

pub mod look_at_entity;
pub mod target_goal;

#[async_trait]
pub trait Goal: Send + Sync {
Expand Down
63 changes: 63 additions & 0 deletions pumpkin/src/entity/ai/goal/target_goal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::sync::Arc;

use async_trait::async_trait;
use tokio::sync::Mutex;

use crate::entity::{ai::path::NavigatorGoal, mob::MobEntity, player::Player};

use super::Goal;

pub struct TargetGoal {
// TODO: make this an entity
target: Mutex<Option<Arc<Player>>>,
range: f64,
}

impl TargetGoal {
#[must_use]
pub fn new(range: f64) -> Self {
Self {
target: Mutex::new(None),
range,
}
}
}

#[async_trait]
impl Goal for TargetGoal {
async fn can_start(&self, mob: &MobEntity) -> bool {
// TODO: make this an entity
let mut target = self.target.lock().await;

// gets the closest entity (currently player)
*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 an entity is found, lets check so its in range
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 mut navigator = mob.navigator.lock().await;
let target_player = target.living_entity.entity.pos.load();

navigator.set_progress(NavigatorGoal {
current_progress: mob.living_entity.entity.pos.load(),
destination: target_player,
speed: 0.1,
});
}
}
}
1 change: 1 addition & 0 deletions pumpkin/src/entity/ai/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod goal;
pub mod path;
107 changes: 107 additions & 0 deletions pumpkin/src/entity/ai/path/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use pumpkin_core::math::vector3::Vector3;
use pumpkin_protocol::client::play::CUpdateEntityPos;

use crate::entity::living::LivingEntity;

#[derive(Default)]
pub struct Navigator {
current_goal: Option<NavigatorGoal>,
}

pub struct NavigatorGoal {
pub current_progress: Vector3<f64>,
pub destination: Vector3<f64>,
pub speed: f64,
}

impl Navigator {
pub fn set_progress(&mut self, goal: NavigatorGoal) {
self.current_goal = Some(goal);
}

pub fn cancel(&mut self) {
self.current_goal = None;
}

pub async fn tick(&mut self, entity: &LivingEntity) {
if let Some(goal) = &mut self.current_goal {
// first lets check if we reached destination
if goal.current_progress == goal.destination {
// if yes, we are done here
self.current_goal = None;
return;
}

// A star algorithm
let mut best_move = Vector3::new(0.0, 0.0, 0.0);
let mut lowest_cost = f64::MAX;

for x in -1..=1 {
for z in -1..=1 {
let x = f64::from(x);
let z = f64::from(z);
let potential_pos = Vector3::new(
goal.current_progress.x + x,
goal.current_progress.y,
goal.current_progress.z + z,
);
let node = Node::new(potential_pos);
let cost = node.get_expense(goal.destination);

if cost < lowest_cost {
lowest_cost = cost;
best_move = Vector3::new(x, 0.0, z);
}
}
}

// this is important, first this saves us many packets when we don't actually move, and secound this prevents division using zero
// when normalize
if best_move.x == 0.0 && best_move.z == 0.0 {
return;
}
// Update current progress based on the best move
goal.current_progress += best_move.normalize() * goal.speed;

// now lets move
entity.set_pos(goal.current_progress);
let pos = entity.entity.pos.load();
let last_pos = entity.last_pos.load();

entity
.entity
.world
.broadcast_packet_all(&CUpdateEntityPos::new(
entity.entity.entity_id.into(),
Vector3::new(
pos.x.mul_add(4096.0, -(last_pos.x * 4096.0)) as i16,
pos.y.mul_add(4096.0, -(last_pos.y * 4096.0)) as i16,
pos.z.mul_add(4096.0, -(last_pos.z * 4096.0)) as i16,
),
entity
.entity
.on_ground
.load(std::sync::atomic::Ordering::Relaxed),
))
.await;
}
}
}

pub struct Node {
pub location: Vector3<f64>,
}

impl Node {
#[must_use]
pub fn new(location: Vector3<f64>) -> Self {
Self { location }
}
/// How expensive is it to go to a location
///
/// Returns a f64, Higher = More Expensive
#[must_use]
pub fn get_expense(&self, end: Vector3<f64>) -> f64 {
self.location.squared_distance_to_vec(end).sqrt()
}
}
8 changes: 7 additions & 1 deletion pumpkin/src/entity/mob/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ use zombie::Zombie;

use crate::{server::Server, world::World};

use super::{ai::goal::Goal, living::LivingEntity};
use super::{
ai::{goal::Goal, path::Navigator},
living::LivingEntity,
};

pub mod zombie;

pub struct MobEntity {
pub living_entity: Arc<LivingEntity>,
pub goals: Mutex<Vec<(Arc<dyn Goal>, bool)>>,
pub navigator: Mutex<Navigator>,
}

impl MobEntity {
Expand All @@ -31,6 +35,8 @@ impl MobEntity {
*running = goal.can_start(self).await;
}
}
let mut navigator = self.navigator.lock().await;
navigator.tick(&self.living_entity).await;
}
}

Expand Down
7 changes: 6 additions & 1 deletion pumpkin/src/entity/mob/zombie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ 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 crate::{
entity::ai::goal::{look_at_entity::LookAtEntityGoal, target_goal::TargetGoal},
server::Server,
world::World,
};

use super::MobEntity;

Expand All @@ -20,6 +24,7 @@ impl Zombie {
.add_mob_entity(EntityType::Zombie, position, world)
.await;
zombie_entity.goal(LookAtEntityGoal::new(8.0)).await;
zombie_entity.goal(TargetGoal::new(16.0)).await;
(zombie_entity, uuid)
}
}
1 change: 1 addition & 0 deletions pumpkin/src/entity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ impl Entity {
self.yaw.store(yaw);

// send packet
// TODO: do caching, only send packet when needed
let yaw = (yaw * 256.0 / 360.0).rem_euclid(256.0);
let pitch = (pitch * 256.0 / 360.0).rem_euclid(256.0);
self.world
Expand Down
2 changes: 2 additions & 0 deletions pumpkin/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use uuid::Uuid;

use crate::block::block_manager::BlockManager;
use crate::block::default_block_manager;
use crate::entity::ai::path::Navigator;
use crate::entity::living::LivingEntity;
use crate::entity::mob::MobEntity;
use crate::entity::Entity;
Expand Down Expand Up @@ -207,6 +208,7 @@ impl Server {
let mob = Arc::new(MobEntity {
living_entity,
goals: Mutex::new(vec![]),
navigator: Mutex::new(Navigator::default()),
});
world.add_mob_entity(uuid, mob.clone()).await;
(mob, uuid)
Expand Down
Loading