From d5465cd28e43d48b3e913fdb1161eb907e4d80d0 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 25 Aug 2023 02:34:31 -0500 Subject: [PATCH] add basic pathfinding test --- Cargo.lock | 53 ++++----- azalea-brigadier/tests/string_reader_test.rs | 20 ++-- azalea-client/src/client.rs | 4 +- azalea-client/src/lib.rs | 8 +- azalea-client/src/local_player.rs | 19 +--- azalea-client/src/movement.rs | 21 +++- azalea-core/src/bitset.rs | 32 +++--- azalea-crypto/benches/my_benchmark.rs | 4 +- azalea-entity/src/dimensions.rs | 2 +- azalea-entity/src/lib.rs | 46 +++++--- azalea-entity/src/plugin/indexing.rs | 1 + azalea-nbt/benches/compare.rs | 2 +- azalea-physics/src/lib.rs | 4 +- azalea/Cargo.toml | 1 + azalea/examples/echo.rs | 17 ++- azalea/examples/steal.rs | 79 +++++++------- azalea/examples/testbot.rs | 7 +- azalea/src/pathfinder/mod.rs | 59 +++++++++- azalea/src/pathfinder/moves.rs | 24 ++-- azalea/src/pathfinder/simulation.rs | 109 +++++++++++++++++++ 20 files changed, 341 insertions(+), 171 deletions(-) create mode 100644 azalea/src/pathfinder/simulation.rs diff --git a/Cargo.lock b/Cargo.lock index b2fc510b..ebad9585 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,7 @@ dependencies = [ "bevy_ecs", "bevy_log", "bevy_tasks", + "bevy_time", "derive_more", "futures", "futures-lite", @@ -555,9 +556,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bevy_app" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611dcfbfbb4d009597d54d3b6969a4aeac6eb2d208de79d0d3333fae4f5b9834" +checksum = "3cb660188d5d4ceaead6d5861ce22ecedc08b68c385cc8edf0a3c0c0285560bf" dependencies = [ "bevy_derive", "bevy_ecs", @@ -571,9 +572,9 @@ dependencies = [ [[package]] name = "bevy_derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6bc1c0cb7a100d8345484677abb7f10f65f59bd786d8c20ac764817983e3d2" +checksum = "c5cc78985f4d0ad1fd7b8ead06dcfaa192685775a7b1be158191c788c7d52298" dependencies = [ "bevy_macro_utils", "quote", @@ -582,9 +583,9 @@ dependencies = [ [[package]] name = "bevy_ecs" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06479bbf65542c96d2623249e1835287212cf06de524620071ab2c2e4b43bf18" +checksum = "fb6fd0ec64cd32b8fcf16157173431ba0e675b29c4643a8d6c9a9eeef6f93c32" dependencies = [ "async-channel", "bevy_ecs_macros", @@ -603,9 +604,9 @@ dependencies = [ [[package]] name = "bevy_ecs_macros" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58a7a55b3d525637755303cb5c52e7ee8058f80379f3715a68d16a4fcbd1d988" +checksum = "e13b0fd864433db6ff825efd0eb86b2690e208151905b184cc9bfd2c3ac66c3b" dependencies = [ "bevy_macro_utils", "proc-macro2", @@ -631,9 +632,9 @@ dependencies = [ [[package]] name = "bevy_macro_utils" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40e2d6a96a512787d8871c009a0a6eca0b02b5a1f52b8caed700c7ab0d57cfb1" +checksum = "d1cd460205fe05634d58b32d9bb752b1b4eaf32b2d29cbd4161ba35eb44a2f8c" dependencies = [ "quote", "rustc-hash", @@ -643,9 +644,9 @@ dependencies = [ [[package]] name = "bevy_math" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4797b608d61d315b99388479b40ad9200baa24f232a8b292f2cf14f5d8a7ab1" +checksum = "267f2ec44aa948051768b1320c2dbff0871799e0a3b746f5fe5b6ee7258fbaf5" dependencies = [ "glam", "serde", @@ -653,15 +654,15 @@ dependencies = [ [[package]] name = "bevy_ptr" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eeb9144fc8bd8676f76c7660790ffce696fc80aade804b1c4e3d36a92f9df65" +checksum = "15702dff420fac72c2ab92428a8600e079ae89c5845401c4e39b843665a3d2d0" [[package]] name = "bevy_reflect" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a0bd344cb46b48a89a36af8aad7718b69e5e05332e058169478792be4eed86" +checksum = "3ac66cccbf1457c5cfc004a0e83569bd4ddc5d6310701f4af6aa81001fe2964a" dependencies = [ "bevy_math", "bevy_ptr", @@ -680,9 +681,9 @@ dependencies = [ [[package]] name = "bevy_reflect_derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22bdf9e985f7df44712ff93156b036c301aa6d1f211aec06ee6b06599396a4f0" +checksum = "e5a2a1fa784e9a22560b9de350246a159cd59239eb61c7b66824031b3b28abb0" dependencies = [ "bevy_macro_utils", "bit-set", @@ -694,9 +695,9 @@ dependencies = [ [[package]] name = "bevy_tasks" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b89169296c682d2d33f0dfc9f09abf942148adfa2cc5e0b2e17514acc0fc87" +checksum = "9200e7b42d49c787d9a08675c425d8bd6393ba3beed2eac64be6027a44a01870" dependencies = [ "async-channel", "async-executor", @@ -708,9 +709,9 @@ dependencies = [ [[package]] name = "bevy_time" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91119de6907ff1cb58c637df235aa2e49332303abfa919a8c84cde942cfd69a4" +checksum = "2ba50bf25c4dc40296b744f77de10d39c8981b710d8dce609da9de5e54ef164b" dependencies = [ "bevy_app", "bevy_ecs", @@ -722,9 +723,9 @@ dependencies = [ [[package]] name = "bevy_utils" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a596771e2cc8f4cde4e2dddb3c5569414ee342811bb5d317d50fe8965c1d0e31" +checksum = "829eb8d0d06a0baeabc2e8bad74136ed3329b055aa1e11c5d9df09ebb9be3d85" dependencies = [ "ahash", "bevy_utils_proc_macros", @@ -739,9 +740,9 @@ dependencies = [ [[package]] name = "bevy_utils_proc_macros" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e240bb064e9a1455e98e7dc8e701e1fd972c7fd32429ed1b4e7f36ac8974b7f" +checksum = "0d104f29e231123c703e8b394e2341d2425c33c5a2e9ab8cc8d0a554bdb62a41" dependencies = [ "proc-macro2", "quote", diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs index 5008eff8..de605e99 100755 --- a/azalea-brigadier/tests/string_reader_test.rs +++ b/azalea-brigadier/tests/string_reader_test.rs @@ -3,13 +3,13 @@ use azalea_brigadier::{exceptions::BuiltInExceptions, string_reader::StringReade #[test] fn can_read() { let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.can_read(), true); + assert!(reader.can_read()); reader.skip(); // 'a' - assert_eq!(reader.can_read(), true); + assert!(reader.can_read()); reader.skip(); // 'b' - assert_eq!(reader.can_read(), true); + assert!(reader.can_read()); reader.skip(); // 'c' - assert_eq!(reader.can_read(), false); + assert!(!reader.can_read()); } #[test] @@ -27,11 +27,11 @@ fn get_remaining_length() { #[test] fn can_read_length() { let reader = StringReader::from("abc".to_string()); - assert_eq!(reader.can_read_length(1), true); - assert_eq!(reader.can_read_length(2), true); - assert_eq!(reader.can_read_length(3), true); - assert_eq!(reader.can_read_length(4), false); - assert_eq!(reader.can_read_length(5), false); + assert!(reader.can_read_length(1)); + assert!(reader.can_read_length(2)); + assert!(reader.can_read_length(3)); + assert!(!reader.can_read_length(4)); + assert!(!reader.can_read_length(5)); } #[test] @@ -580,7 +580,7 @@ fn expect_none() { #[test] fn read_boolean_correct() { let mut reader = StringReader::from("true".to_string()); - assert_eq!(reader.read_boolean().unwrap(), true); + assert!(reader.read_boolean().unwrap()); assert_eq!(reader.get_read(), "true"); } diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 39b86b98..b44e8b4e 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -7,10 +7,10 @@ use crate::{ inventory::{InventoryComponent, InventoryPlugin}, local_player::{ death_event, handle_send_packet_event, update_in_loaded_chunk, GameProfileComponent, - Hunger, LocalPlayer, PhysicsState, SendPacketEvent, + Hunger, LocalPlayer, SendPacketEvent, }, mining::{self, MinePlugin}, - movement::{LastSentLookDirection, PlayerMovePlugin}, + movement::{LastSentLookDirection, PhysicsState, PlayerMovePlugin}, packet_handling::{self, PacketHandlerPlugin, PacketReceiver}, player::retroactively_add_game_profile_component, respawn::RespawnPlugin, diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index 0321a396..4cb1eebf 100644 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -22,7 +22,7 @@ pub mod interact; pub mod inventory; mod local_player; pub mod mining; -mod movement; +pub mod movement; pub mod packet_handling; pub mod ping; mod player; @@ -36,6 +36,8 @@ pub use client::{ TickBroadcast, }; pub use events::Event; -pub use local_player::{GameProfileComponent, LocalPlayer}; -pub use movement::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection}; +pub use local_player::{GameProfileComponent, LocalPlayer, LocalPlayerInLoadedChunk}; +pub use movement::{ + PhysicsState, SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection, +}; pub use player::PlayerInfo; diff --git a/azalea-client/src/local_player.rs b/azalea-client/src/local_player.rs index a66b7ad5..2e0ccc75 100644 --- a/azalea-client/src/local_player.rs +++ b/azalea-client/src/local_player.rs @@ -20,7 +20,7 @@ use tokio::{sync::mpsc, task::JoinHandle}; use crate::{ events::{Event as AzaleaEvent, LocalPlayerEvents}, - ClientInformation, WalkDirection, + ClientInformation, }; /// This is a component for our local player entities that are probably in a @@ -51,23 +51,6 @@ pub struct LocalPlayer { pub(crate) write_packets_task: JoinHandle<()>, } -/// Component for entities that can move and sprint. Usually only in -/// [`LocalPlayer`] entities. -#[derive(Default, Component)] -pub struct PhysicsState { - /// Minecraft only sends a movement packet either after 20 ticks or if the - /// player moved enough. This is that tick counter. - pub position_remainder: u32, - pub was_sprinting: bool, - // Whether we're going to try to start sprinting this tick. Equivalent to - // holding down ctrl for a tick. - pub trying_to_sprint: bool, - - pub move_direction: WalkDirection, - pub forward_impulse: f32, - pub left_impulse: f32, -} - /// A component only present in players that contains the [`GameProfile`] (which /// you can use to get a player's name). /// diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index 0bda9b15..ad26f49c 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -1,7 +1,5 @@ use crate::client::Client; -use crate::local_player::{ - update_in_loaded_chunk, LocalPlayer, LocalPlayerInLoadedChunk, PhysicsState, -}; +use crate::local_player::{update_in_loaded_chunk, LocalPlayer, LocalPlayerInLoadedChunk}; use azalea_entity::{metadata::Sprinting, Attributes, Jumping}; use azalea_entity::{LastSentPosition, LookDirection, Physics, Position}; use azalea_physics::{force_jump_listener, PhysicsSet}; @@ -102,6 +100,23 @@ pub struct LastSentLookDirection { pub y_rot: f32, } +/// Component for entities that can move and sprint. Usually only in +/// [`LocalPlayer`] entities. +#[derive(Default, Component, Clone)] +pub struct PhysicsState { + /// Minecraft only sends a movement packet either after 20 ticks or if the + /// player moved enough. This is that tick counter. + pub position_remainder: u32, + pub was_sprinting: bool, + // Whether we're going to try to start sprinting this tick. Equivalent to + // holding down ctrl for a tick. + pub trying_to_sprint: bool, + + pub move_direction: WalkDirection, + pub forward_impulse: f32, + pub left_impulse: f32, +} + #[allow(clippy::type_complexity)] pub(crate) fn send_position( mut query: Query< diff --git a/azalea-core/src/bitset.rs b/azalea-core/src/bitset.rs index 381e86ea..b7a426c9 100755 --- a/azalea-core/src/bitset.rs +++ b/azalea-core/src/bitset.rs @@ -191,13 +191,13 @@ mod tests { #[test] fn test_bitset() { let mut bitset = BitSet::new(64); - assert_eq!(bitset.index(0), false); - assert_eq!(bitset.index(1), false); - assert_eq!(bitset.index(2), false); + assert!(!bitset.index(0)); + assert!(!bitset.index(1)); + assert!(!bitset.index(2)); bitset.set(1); - assert_eq!(bitset.index(0), false); - assert_eq!(bitset.index(1), true); - assert_eq!(bitset.index(2), false); + assert!(!bitset.index(0)); + assert!(bitset.index(1)); + assert!(!bitset.index(2)); } #[test] @@ -211,11 +211,11 @@ mod tests { bitset.clear(63, 65); - assert_eq!(bitset.index(62), true); - assert_eq!(bitset.index(63), false); - assert_eq!(bitset.index(64), false); - assert_eq!(bitset.index(65), true); - assert_eq!(bitset.index(66), true); + assert!(bitset.index(62)); + assert!(!bitset.index(63)); + assert!(!bitset.index(64)); + assert!(bitset.index(65)); + assert!(bitset.index(66)); } #[test] @@ -229,10 +229,10 @@ mod tests { bitset.clear(65, 67); - assert_eq!(bitset.index(64), true); - assert_eq!(bitset.index(65), false); - assert_eq!(bitset.index(66), false); - assert_eq!(bitset.index(67), true); - assert_eq!(bitset.index(68), true); + assert!(bitset.index(64)); + assert!(!bitset.index(65)); + assert!(!bitset.index(66)); + assert!(bitset.index(67)); + assert!(bitset.index(68)); } } diff --git a/azalea-crypto/benches/my_benchmark.rs b/azalea-crypto/benches/my_benchmark.rs index e8c4ecad..1781f471 100755 --- a/azalea-crypto/benches/my_benchmark.rs +++ b/azalea-crypto/benches/my_benchmark.rs @@ -5,8 +5,8 @@ fn bench(c: &mut Criterion) { let (mut enc, dec) = create_cipher(b"0123456789abcdef"); let mut packet = [0u8; 65536]; - for i in 0..packet.len() { - packet[i] = i as u8; + for (i, item) in packet.iter_mut().enumerate() { + *item = i as u8; } c.bench_function("Encrypt 64kb", |b| { diff --git a/azalea-entity/src/dimensions.rs b/azalea-entity/src/dimensions.rs index 1d013d10..ab5a1808 100755 --- a/azalea-entity/src/dimensions.rs +++ b/azalea-entity/src/dimensions.rs @@ -1,6 +1,6 @@ use azalea_core::{Vec3, AABB}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct EntityDimensions { pub width: f32, pub height: f32, diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index c67817f3..db42a0ba 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -125,6 +125,11 @@ impl Debug for EntityUuid { /// automatically. #[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)] pub struct Position(Vec3); +impl Position { + pub fn new(pos: Vec3) -> Self { + Self(pos) + } +} impl From<&Position> for Vec3 { fn from(value: &Position) -> Self { value.0 @@ -184,7 +189,7 @@ impl From<&LastSentPosition> for BlockPos { /// /// If this is true, the entity will try to jump every tick. (It's equivalent to /// the space key being held in vanilla.) -#[derive(Debug, Component, Clone, Deref, DerefMut)] +#[derive(Debug, Component, Clone, Deref, DerefMut, Default)] pub struct Jumping(bool); /// A component that contains the direction an entity is looking. @@ -196,7 +201,7 @@ pub struct LookDirection { /// The physics data relating to the entity, such as position, velocity, and /// bounding box. -#[derive(Debug, Component)] +#[derive(Debug, Component, Clone)] pub struct Physics { pub delta: Vec3, @@ -219,6 +224,26 @@ pub struct Physics { pub has_impulse: bool, } +impl Physics { + pub fn new(dimensions: EntityDimensions, pos: &Vec3) -> Self { + Self { + delta: Vec3::default(), + + xxa: 0., + yya: 0., + zza: 0., + + on_ground: false, + last_on_ground: false, + + bounding_box: dimensions.make_bounding_box(pos), + dimensions, + + has_impulse: false, + } + } +} + /// Marker component for entities that are dead. /// /// "Dead" means that the entity has 0 health. @@ -297,22 +322,7 @@ impl EntityBundle { world_name: InstanceName(world_name), position: Position(pos), last_sent_position: LastSentPosition(pos), - physics: Physics { - delta: Vec3::default(), - - xxa: 0., - yya: 0., - zza: 0., - - on_ground: false, - last_on_ground: false, - - // TODO: have this be based on the entity type - bounding_box: dimensions.make_bounding_box(&pos), - dimensions, - - has_impulse: false, - }, + physics: Physics::new(dimensions, &pos), eye_height: EyeHeight(eye_height), direction: LookDirection::default(), diff --git a/azalea-entity/src/plugin/indexing.rs b/azalea-entity/src/plugin/indexing.rs index 23f53c5f..3a349f5c 100644 --- a/azalea-entity/src/plugin/indexing.rs +++ b/azalea-entity/src/plugin/indexing.rs @@ -229,6 +229,7 @@ pub fn update_entity_chunk_positions( } /// Despawn entities that aren't being loaded by anything. +#[allow(clippy::type_complexity)] pub fn remove_despawned_entities_from_indexes( mut commands: Commands, mut entity_infos: ResMut, diff --git a/azalea-nbt/benches/compare.rs b/azalea-nbt/benches/compare.rs index 1634b45b..863e0b9a 100755 --- a/azalea-nbt/benches/compare.rs +++ b/azalea-nbt/benches/compare.rs @@ -25,7 +25,7 @@ pub fn bench_read_file(filename: &str, c: &mut Criterion) { group.bench_function("azalea_parse", |b| { b.iter(|| { let input = black_box(input); - let nbt = azalea_nbt::Nbt::read(&mut Cursor::new(&input)).unwrap(); + let nbt = azalea_nbt::Nbt::read(&mut Cursor::new(input)).unwrap(); black_box(nbt); }) }); diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs index 6fef7ad1..f004c0fb 100644 --- a/azalea-physics/src/lib.rs +++ b/azalea-physics/src/lib.rs @@ -427,7 +427,7 @@ mod tests { let block_state = partial_world.chunks.set_block_state( &BlockPos { x: 0, y: 69, z: 0 }, azalea_registry::Block::Stone.into(), - &mut world_lock.write().chunks, + &world_lock.write().chunks, ); assert!( block_state.is_some(), @@ -490,7 +490,7 @@ mod tests { waterlogged: false, } .into(), - &mut world_lock.write().chunks, + &world_lock.write().chunks, ); assert!( block_state.is_some(), 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>, +} + +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::(self.entity).unwrap() + } +}