aboutsummaryrefslogtreecommitdiff
path: root/azalea/src/pathfinder
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2023-08-25 02:34:31 -0500
committermat <git@matdoes.dev>2023-08-25 02:34:31 -0500
commitd5465cd28e43d48b3e913fdb1161eb907e4d80d0 (patch)
treeb0962ac1bd09b434c67296c038ef3b26245ce6d7 /azalea/src/pathfinder
parent9c31f8033f006d5f505ce97e359638d6c1136859 (diff)
downloadazalea-drasl-d5465cd28e43d48b3e913fdb1161eb907e4d80d0.tar.xz
add basic pathfinding test
Diffstat (limited to 'azalea/src/pathfinder')
-rw-r--r--azalea/src/pathfinder/mod.rs59
-rw-r--r--azalea/src/pathfinder/moves.rs24
-rw-r--r--azalea/src/pathfinder/simulation.rs109
3 files changed, 178 insertions, 14 deletions
diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs
index 58b59fd4..d59f0046 100644
--- a/azalea/src/pathfinder/mod.rs
+++ b/azalea/src/pathfinder/mod.rs
@@ -1,6 +1,7 @@
mod astar;
pub mod goals;
mod moves;
+pub mod simulation;
use crate::bot::{JumpEvent, LookAtEvent};
use crate::pathfinder::astar::a_star;
@@ -22,7 +23,7 @@ use azalea_entity::Local;
use azalea_entity::{Physics, Position};
use azalea_physics::PhysicsSet;
use azalea_world::{InstanceContainer, InstanceName};
-use bevy_app::{FixedUpdate, Update};
+use bevy_app::{FixedUpdate, PreUpdate, Update};
use bevy_ecs::prelude::Event;
use bevy_ecs::query::Changed;
use bevy_ecs::schedule::IntoSystemConfigs;
@@ -44,11 +45,11 @@ impl Plugin for PathfinderPlugin {
// (every 50 milliseconds).
tick_execute_path.before(PhysicsSet),
)
+ .add_systems(PreUpdate, add_default_pathfinder)
.add_systems(
Update,
(
goto_listener,
- add_default_pathfinder,
(handle_tasks, path_found_listener).chain(),
stop_pathfinding_on_instance_change,
),
@@ -342,3 +343,57 @@ impl Node {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use std::sync::Arc;
+
+ use azalea_core::{BlockPos, ChunkPos, Vec3};
+ use azalea_world::{Chunk, ChunkStorage, PartialChunkStorage};
+ use bevy_log::LogPlugin;
+
+ use super::{
+ goals::BlockPosGoal,
+ simulation::{SimulatedPlayerBundle, Simulation},
+ GotoEvent,
+ };
+
+ #[test]
+ fn test_simple_forward() {
+ let mut chunks = ChunkStorage::default();
+ let mut partial_chunks = PartialChunkStorage::default();
+ partial_chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut chunks,
+ );
+ chunks.set_block_state(
+ &BlockPos::new(0, 70, 0),
+ azalea_registry::Block::Stone.into(),
+ );
+ chunks.set_block_state(
+ &BlockPos::new(0, 70, 1),
+ azalea_registry::Block::Stone.into(),
+ );
+ let player = SimulatedPlayerBundle::new(Vec3::new(0.5, 71., 0.5));
+ let mut simulation = Simulation::new(chunks, player);
+ simulation.app.add_plugins(LogPlugin {
+ level: bevy_log::Level::DEBUG,
+ filter: "".to_string(),
+ });
+
+ simulation.app.world.send_event(GotoEvent {
+ entity: simulation.entity,
+ goal: Arc::new(BlockPosGoal::from(BlockPos::new(0, 71, 1))),
+ });
+
+ for _ in 0..20 {
+ simulation.tick();
+ }
+
+ assert_eq!(
+ BlockPos::from(simulation.position()),
+ BlockPos::new(0, 71, 1)
+ );
+ }
+}
diff --git a/azalea/src/pathfinder/moves.rs b/azalea/src/pathfinder/moves.rs
index 04e2e725..0cc211ac 100644
--- a/azalea/src/pathfinder/moves.rs
+++ b/azalea/src/pathfinder/moves.rs
@@ -192,17 +192,17 @@ mod tests {
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
azalea_registry::Block::Stone.into(),
- &mut chunk_storage,
+ &chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::AIR,
- &mut chunk_storage,
+ &chunk_storage,
);
let world = chunk_storage.into();
- assert_eq!(is_block_passable(&BlockPos::new(0, 0, 0), &world), false);
- assert_eq!(is_block_passable(&BlockPos::new(0, 1, 0), &world), true);
+ assert!(!is_block_passable(&BlockPos::new(0, 0, 0), &world));
+ assert!(is_block_passable(&BlockPos::new(0, 1, 0), &world));
}
#[test]
@@ -217,17 +217,17 @@ mod tests {
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
azalea_registry::Block::Stone.into(),
- &mut chunk_storage,
+ &chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::AIR,
- &mut chunk_storage,
+ &chunk_storage,
);
let world = chunk_storage.into();
- assert_eq!(is_block_solid(&BlockPos::new(0, 0, 0), &world), true);
- assert_eq!(is_block_solid(&BlockPos::new(0, 1, 0), &world), false);
+ assert!(is_block_solid(&BlockPos::new(0, 0, 0), &world));
+ assert!(!is_block_solid(&BlockPos::new(0, 1, 0), &world));
}
#[test]
@@ -242,22 +242,22 @@ mod tests {
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
azalea_registry::Block::Stone.into(),
- &mut chunk_storage,
+ &chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::AIR,
- &mut chunk_storage,
+ &chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 2, 0),
BlockState::AIR,
- &mut chunk_storage,
+ &chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 3, 0),
BlockState::AIR,
- &mut chunk_storage,
+ &chunk_storage,
);
let world = chunk_storage.into();
diff --git a/azalea/src/pathfinder/simulation.rs b/azalea/src/pathfinder/simulation.rs
new file mode 100644
index 00000000..372a8a3b
--- /dev/null
+++ b/azalea/src/pathfinder/simulation.rs
@@ -0,0 +1,109 @@
+use std::{sync::Arc, time::Duration};
+
+use azalea_client::PhysicsState;
+use azalea_core::{ResourceLocation, Vec3};
+use azalea_entity::{
+ attributes::AttributeInstance, metadata::Sprinting, Attributes, EntityDimensions, Physics,
+ Position,
+};
+use azalea_world::{ChunkStorage, Instance, InstanceContainer, InstanceName, MinecraftEntityId};
+use bevy_app::{App, FixedUpdate};
+use bevy_ecs::prelude::*;
+use bevy_time::fixed_timestep::FixedTime;
+use parking_lot::RwLock;
+
+#[derive(Bundle, Clone)]
+pub struct SimulatedPlayerBundle {
+ pub position: Position,
+ pub physics: Physics,
+ pub physics_state: PhysicsState,
+ pub attributes: Attributes,
+}
+
+impl SimulatedPlayerBundle {
+ pub fn new(position: Vec3) -> Self {
+ let dimensions = EntityDimensions {
+ width: 0.6,
+ height: 1.8,
+ };
+
+ SimulatedPlayerBundle {
+ position: Position::new(position),
+ physics: Physics::new(dimensions, &position),
+ physics_state: PhysicsState::default(),
+ attributes: Attributes {
+ speed: AttributeInstance::new(0.1),
+ attack_speed: AttributeInstance::new(4.0),
+ },
+ }
+ }
+}
+
+/// Simulate the Minecraft world to see if certain movements would be possible.
+pub struct Simulation {
+ pub app: App,
+ pub entity: Entity,
+ _instance: Arc<RwLock<Instance>>,
+}
+
+impl Simulation {
+ pub fn new(chunks: ChunkStorage, player: SimulatedPlayerBundle) -> Self {
+ let instance_name = ResourceLocation::new("azalea:simulation");
+
+ let instance = Arc::new(RwLock::new(Instance {
+ chunks,
+ ..Default::default()
+ }));
+
+ let mut app = App::new();
+ // we don't use all the default azalea plugins because we don't need all of them
+ app.add_plugins((
+ azalea_physics::PhysicsPlugin,
+ azalea_entity::EntityPlugin,
+ azalea_client::movement::PlayerMovePlugin,
+ super::PathfinderPlugin,
+ crate::BotPlugin,
+ azalea_client::task_pool::TaskPoolPlugin::default(),
+ ))
+ // make sure it doesn't do fixed ticks without us telling it to
+ .insert_resource(FixedTime::new(Duration::from_secs(60)))
+ .insert_resource(InstanceContainer {
+ worlds: [(instance_name.clone(), Arc::downgrade(&instance.clone()))]
+ .iter()
+ .cloned()
+ .collect(),
+ });
+
+ app.edit_schedule(bevy_app::Main, |schedule| {
+ schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded);
+ });
+
+ let entity = app
+ .world
+ .spawn((
+ MinecraftEntityId(0),
+ InstanceName(instance_name),
+ azalea_entity::Local,
+ azalea_client::LocalPlayerInLoadedChunk,
+ azalea_entity::Jumping::default(),
+ azalea_entity::LookDirection::default(),
+ Sprinting(true),
+ azalea_entity::metadata::Player,
+ player,
+ ))
+ .id();
+
+ Self {
+ app,
+ entity,
+ _instance: instance,
+ }
+ }
+ pub fn tick(&mut self) {
+ self.app.world.run_schedule(FixedUpdate);
+ self.app.update();
+ }
+ pub fn position(&self) -> Vec3 {
+ **self.app.world.get::<Position>(self.entity).unwrap()
+ }
+}