diff options
| author | mat <git@matdoes.dev> | 2023-08-25 02:34:31 -0500 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2023-08-25 02:34:31 -0500 |
| commit | d5465cd28e43d48b3e913fdb1161eb907e4d80d0 (patch) | |
| tree | b0962ac1bd09b434c67296c038ef3b26245ce6d7 /azalea | |
| parent | 9c31f8033f006d5f505ce97e359638d6c1136859 (diff) | |
| download | azalea-drasl-d5465cd28e43d48b3e913fdb1161eb907e4d80d0.tar.xz | |
add basic pathfinding test
Diffstat (limited to 'azalea')
| -rw-r--r-- | azalea/Cargo.toml | 1 | ||||
| -rwxr-xr-x | azalea/examples/echo.rs | 17 | ||||
| -rw-r--r-- | azalea/examples/steal.rs | 79 | ||||
| -rw-r--r-- | azalea/examples/testbot.rs | 7 | ||||
| -rw-r--r-- | azalea/src/pathfinder/mod.rs | 59 | ||||
| -rw-r--r-- | azalea/src/pathfinder/moves.rs | 24 | ||||
| -rw-r--r-- | azalea/src/pathfinder/simulation.rs | 109 |
7 files changed, 227 insertions, 69 deletions
diff --git a/azalea/Cargo.toml b/azalea/Cargo.toml index 527088f8..3e7aaaa2 100644 --- a/azalea/Cargo.toml +++ b/azalea/Cargo.toml @@ -41,6 +41,7 @@ tokio = "^1.31.0" uuid = "1.4.1" bevy_log = "0.11.1" azalea-entity = { version = "0.1.0", path = "../azalea-entity" } +bevy_time = "0.11.2" [features] default = ["log"] diff --git a/azalea/examples/echo.rs b/azalea/examples/echo.rs index dbf56a31..01390982 100755 --- a/azalea/examples/echo.rs +++ b/azalea/examples/echo.rs @@ -18,16 +18,13 @@ async fn main() { pub struct State {} async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> { - match event { - Event::Chat(m) => { - if let (Some(sender), content) = m.split_sender_and_content() { - if sender == bot.profile.name { - return Ok(()); // ignore our own messages - } - bot.chat(&content); - }; - } - _ => {} + if let Event::Chat(m) = event { + if let (Some(sender), content) = m.split_sender_and_content() { + if sender == bot.profile.name { + return Ok(()); // ignore our own messages + } + bot.chat(&content); + }; } Ok(()) diff --git a/azalea/examples/steal.rs b/azalea/examples/steal.rs index 7a7ee4bb..9bbda945 100644 --- a/azalea/examples/steal.rs +++ b/azalea/examples/steal.rs @@ -24,52 +24,49 @@ struct State { } async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> { - match event { - Event::Chat(m) => { - if m.username() == Some(bot.profile.name.clone()) { - return Ok(()); - }; - if m.content() != "go" { - return Ok(()); - } - { - state.checked_chests.lock().clear(); - } + if let Event::Chat(m) = event { + if m.username() == Some(bot.profile.name.clone()) { + return Ok(()); + }; + if m.content() != "go" { + return Ok(()); + } + { + state.checked_chests.lock().clear(); + } - let chest_block = bot - .world() - .read() - .find_block(bot.position(), &azalea::Block::Chest.into()); - // TODO: update this when find_blocks is implemented - let Some(chest_block) = chest_block else { - bot.chat("No chest found"); - return Ok(()); - }; - // bot.goto(BlockPosGoal::from(chest_block)); - let Some(chest) = bot.open_container(chest_block).await else { - println!("Couldn't open chest"); - return Ok(()); - }; + let chest_block = bot + .world() + .read() + .find_block(bot.position(), &azalea::Block::Chest.into()); + // TODO: update this when find_blocks is implemented + let Some(chest_block) = chest_block else { + bot.chat("No chest found"); + return Ok(()); + }; + // bot.goto(BlockPosGoal::from(chest_block)); + let Some(chest) = bot.open_container(chest_block).await else { + println!("Couldn't open chest"); + return Ok(()); + }; - println!("Getting contents"); - for (index, slot) in chest - .contents() - .expect("we just opened the chest") - .iter() - .enumerate() - { - println!("Checking slot {index}: {slot:?}"); - if let ItemSlot::Present(item) = slot { - if item.kind == azalea::Item::Diamond { - println!("clicking slot ^"); - chest.click(QuickMoveClick::Left { slot: index as u16 }); - } + println!("Getting contents"); + for (index, slot) in chest + .contents() + .expect("we just opened the chest") + .iter() + .enumerate() + { + println!("Checking slot {index}: {slot:?}"); + if let ItemSlot::Present(item) = slot { + if item.kind == azalea::Item::Diamond { + println!("clicking slot ^"); + chest.click(QuickMoveClick::Left { slot: index as u16 }); } } - - println!("Done"); } - _ => {} + + println!("Done"); } Ok(()) diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index 3d566410..14800e9c 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -284,12 +284,11 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< _ => {} } } - Event::Packet(packet) => match *packet { - ClientboundGamePacket::Login(_) => { + Event::Packet(packet) => { + if let ClientboundGamePacket::Login(_) = *packet { println!("login packet"); } - _ => {} - }, + } _ => {} } 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() + } +} |
