From 9e4e417e50e9fae033270f427f3c8301361a0f39 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 16 Jan 2023 22:58:20 -0600 Subject: [PATCH] try to add a .entity_by function still doesn't work because i want to make the predicate magic --- azalea-client/src/client.rs | 15 ++------ azalea-client/src/entity_query.rs | 57 +++++++++++++++++++++++++++++++ azalea-client/src/lib.rs | 2 ++ azalea/examples/pvp.rs | 12 ++++--- azalea/src/swarm/mod.rs | 10 +++--- bot/src/main.rs | 18 +++++----- 6 files changed, 85 insertions(+), 29 deletions(-) create mode 100644 azalea-client/src/entity_query.rs diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 39cbbb28..56314118 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -37,13 +37,14 @@ use azalea_world::{ }; use bevy_app::App; use bevy_ecs::{ - query::WorldQuery, + query::{ReadOnlyWorldQuery, WorldQuery}, schedule::{IntoSystemDescriptor, Schedule, Stage, SystemSet}, + world::EntityRef, }; use bevy_time::TimePlugin; use iyes_loopless::prelude::*; use log::{debug, error}; -use parking_lot::{Mutex, RwLock}; +use parking_lot::{Mutex, MutexGuard, RwLock}; use std::{fmt::Debug, io, net::SocketAddr, ops::DerefMut, sync::Arc, time::Duration}; use thiserror::Error; use tokio::{sync::mpsc, time}; @@ -476,16 +477,6 @@ impl Client { Ok(()) } - - /// A convenience function for getting components of our player's entity. - pub fn query<'w, Q: WorldQuery>( - &self, - ecs: &'w mut bevy_ecs::world::World, - ) -> ::Item<'w> { - ecs.query::() - .get_mut(ecs, self.entity) - .expect("Our client is missing a required component.") - } } #[doc(hidden)] diff --git a/azalea-client/src/entity_query.rs b/azalea-client/src/entity_query.rs new file mode 100644 index 00000000..c7dc3e6b --- /dev/null +++ b/azalea-client/src/entity_query.rs @@ -0,0 +1,57 @@ +use azalea_world::entity::Entity; +use bevy_ecs::query::{ReadOnlyWorldQuery, WorldQuery}; + +use crate::Client; + +impl Client { + /// A convenience function for getting components of our player's entity. + pub fn query<'w, Q: WorldQuery>( + &self, + ecs: &'w mut bevy_ecs::world::World, + ) -> ::Item<'w> { + ecs.query::() + .get_mut(ecs, self.entity) + .expect("Our client is missing a required component.") + } + + /// Return a lightweight [`Entity`] for the entity that matches the given + /// predicate function. + /// + /// You can then use [`Self::map_entity`] to get components from this + /// entity. + pub fn entity_by<'a, F: ReadOnlyWorldQuery, Q: ReadOnlyWorldQuery>( + &mut self, + mut predicate: impl EntityPredicate<'a, Q>, + ) -> Option { + let mut ecs = self.ecs.lock(); + let mut query = ecs.query_filtered::<(Entity, Q), F>(); + let entity = query + .iter_mut(&mut ecs) + .find(|(_, q)| predicate.matches(q)) + .map(|(entity, _)| entity); + entity + } +} + +pub trait EntityPredicate<'a, Q: ReadOnlyWorldQuery> { + fn matches(&self, components: &::Item<'a>) -> bool; +} +impl<'a, F, Q> EntityPredicate<'a, Q> for F +where + F: Fn(Q) -> bool, + Q: ReadOnlyWorldQuery, +{ + fn matches(&self, query: &::Item<'a>) -> bool { + (self)(query) + } +} +// impl<'a, F, Q1, Q2> EntityPredicate<'a, (Q1, Q2)> for F +// where +// F: Fn(Q1, Q2) -> bool, +// Q1: WorldQuery = Q1>, +// Q2: WorldQuery = Q2>, +// { +// fn matches(&self, query: <(Q1, Q2) as WorldQuery>::Item<'_>) -> bool { +// (self)(query.0, query.1) +// } +// } diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index f07513ce..ce430a10 100644 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -13,6 +13,7 @@ mod account; mod chat; mod client; +mod entity_query; mod get_mc_dir; mod local_player; mod movement; @@ -22,6 +23,7 @@ mod player; mod plugins; pub use account::Account; +pub use bevy_ecs as ecs; pub use client::{start_ecs, ChatPacket, Client, ClientInformation, Event, JoinError}; pub use local_player::LocalPlayer; pub use movement::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection}; diff --git a/azalea/examples/pvp.rs b/azalea/examples/pvp.rs index 7dfd2b7a..cc38b38a 100755 --- a/azalea/examples/pvp.rs +++ b/azalea/examples/pvp.rs @@ -46,12 +46,16 @@ async fn swarm_handle( ) -> anyhow::Result<()> { match event { SwarmEvent::Tick => { - // choose an arbitrary player within render distance to target - if let Some(target) = swarm.worlds.read().entity_by(|_: &Player| true) { + if let Some(target_entity) = + swarm.entity_by::(|name: &Name| name == "Herobrine") + { + let target_bounding_box = + swarm.map_entity(target_entity, |bb: &BoundingBox| bb.clone()); + for (bot, bot_state) in swarm { - bot.tick_goto_goal(pathfinder::Goals::Reach(target.bounding_box)); + bot.tick_goto_goal(pathfinder::Goals::Reach(target_bounding_box)); // if target.bounding_box.distance(bot.eyes) < bot.reach_distance() { - if azalea::entities::can_reach(bot.entity(), target.bounding_box) { + if azalea::entities::can_reach(bot.entity(), target_bounding_box) { bot.swing(); } if !bot.using_held_item() && bot.hunger() <= 17 { diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index 40196910..bd626164 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -16,7 +16,7 @@ use log::error; use parking_lot::{Mutex, RwLock}; use std::{future::Future, net::SocketAddr, sync::Arc, time::Duration}; use thiserror::Error; -use tokio::sync::mpsc::{self, UnboundedSender}; +use tokio::sync::mpsc; /// A helper macro that generates a [`SwarmPlugins`] struct from a list of /// objects that implement [`SwarmPlugin`]. @@ -57,10 +57,10 @@ pub struct Swarm { /// Plugins that are set for new bots plugins: Plugins, - bots_tx: UnboundedSender<(Option, (Client, S))>, - swarm_tx: UnboundedSender, + bots_tx: mpsc::UnboundedSender<(Option, (Client, S))>, + swarm_tx: mpsc::UnboundedSender, - run_schedule_sender: UnboundedSender<()>, + run_schedule_sender: mpsc::Sender<()>, } /// An event about something that doesn't have to do with a single bot. @@ -239,7 +239,7 @@ pub async fn start_swarm< let (bots_tx, mut bots_rx) = mpsc::unbounded_channel(); let (swarm_tx, mut swarm_rx) = mpsc::unbounded_channel(); - let (run_schedule_sender, run_schedule_receiver) = mpsc::unbounded_channel(); + let (run_schedule_sender, run_schedule_receiver) = mpsc::channel(1); let ecs_lock = start_ecs(run_schedule_receiver, run_schedule_sender.clone()); let mut swarm = Swarm { diff --git a/bot/src/main.rs b/bot/src/main.rs index e5a119f6..0a30803f 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -1,3 +1,4 @@ +use azalea::ecs::query::With; use azalea::entity::metadata::Player; use azalea::pathfinder::BlockPosGoal; // use azalea::ClientInformation; @@ -92,13 +93,14 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< return Ok(()) }; let mut ecs = bot.ecs.lock(); - let entity = bot - .ecs - .lock() - .query::<&Player>() - .iter(&mut ecs) - .find(|e| e.name() == Some(sender)); - // let entity = None; + // let entity = bot + // .ecs + // .lock() + // .query::<&Player>() + // .iter(&mut ecs) + // .find(|e| e.name() == Some(sender)); + // let entity = bot.entity_by::>(|name: &Name| name == sender); + let entity = bot.entity_by(|name: &Name| name == sender); if let Some(entity) = entity { if m.content() == "goto" { let target_pos_vec3 = entity.pos(); @@ -108,7 +110,7 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< let target_pos_vec3 = entity.pos(); let target_pos: BlockPos = target_pos_vec3.into(); println!("target_pos: {:?}", target_pos); - bot.look_at(&target_pos.center()); + bot.look_at(target_pos.center()); } else if m.content() == "jump" { bot.set_jumping(true); } else if m.content() == "walk" {