From c690e7240514217c189adf870ffcabb594755b04 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 4 May 2023 20:11:29 -0500 Subject: export brigadier from azalea --- azalea/src/lib.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'azalea/src') diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index 2e8e4fa1..bde94634 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -12,6 +12,7 @@ pub mod swarm; use app::{App, Plugin, PluginGroup}; pub use azalea_auth as auth; pub use azalea_block as blocks; +pub use azalea_brigadier as brigadier; pub use azalea_client::*; pub use azalea_core::{BlockPos, Vec3}; pub use azalea_protocol as protocol; -- cgit v1.2.3 From 8b0b86bbcfdf59ecc8e6785028cfbd43f3e4360c Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 5 May 2023 16:54:42 +0000 Subject: add Client::inventory --- azalea/src/container.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'azalea/src') diff --git a/azalea/src/container.rs b/azalea/src/container.rs index 0016caad..24774d43 100644 --- a/azalea/src/container.rs +++ b/azalea/src/container.rs @@ -21,10 +21,12 @@ impl Plugin for ContainerPlugin { pub trait ContainerClientExt { async fn open_container(&mut self, pos: BlockPos) -> Option; + fn inventory(&mut self) -> Option; } impl ContainerClientExt for Client { - /// Open a container in the world, like a chest. + /// Open a container in the world, like a chest. Use [`Client::inventory`] + /// to open your own inventory. /// /// ``` /// # use azalea::prelude::*; @@ -72,12 +74,36 @@ impl ContainerClientExt for Client { }) } } + + /// Open the player's inventory. This will return None if another + /// container is open. + /// + /// Note that this will send a packet to the server once it's dropped. Also, + /// due to how it's implemented, you could call this function multiple times + /// while another inventory handle already exists (but you shouldn't). + fn inventory(&mut self) -> Option { + let ecs = self.ecs.lock(); + let inventory = ecs + .get::(self.entity) + .expect("no inventory"); + + if inventory.id == 0 { + Some(ContainerHandle { + id: 0, + client: self.clone(), + }) + } else { + None + } + } } /// A handle to the open container. The container will be closed once this is /// dropped. pub struct ContainerHandle { - pub id: u8, + /// The id of the container. If this is 0, that means it's the player's + /// inventory. + id: u8, client: Client, } impl Drop for ContainerHandle { @@ -96,6 +122,13 @@ impl Debug for ContainerHandle { } } impl ContainerHandle { + /// Get the id of the container. If this is 0, that means it's the player's + /// inventory. Otherwise, the number isn't really meaningful since only one + /// container can be open at a time. + pub fn id(&self) -> u8 { + self.id + } + /// Returns the menu of the container. If the container is closed, this /// will return `None`. pub fn menu(&self) -> Option { @@ -103,8 +136,14 @@ impl ContainerHandle { let inventory = ecs .get::(self.client.entity) .expect("no inventory"); + + // this also makes sure we can't access the inventory while a container is open if inventory.id == self.id { - Some(inventory.container_menu.clone().unwrap()) + if self.id == 0 { + Some(inventory.inventory_menu.clone()) + } else { + Some(inventory.container_menu.clone().unwrap()) + } } else { None } -- cgit v1.2.3 From 3702b2cb2117b586c41a79d2f74864a99c144bdf Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 5 May 2023 17:01:15 +0000 Subject: rename Client::inventory to open_inventory --- azalea/src/container.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'azalea/src') diff --git a/azalea/src/container.rs b/azalea/src/container.rs index 24774d43..c8849cdd 100644 --- a/azalea/src/container.rs +++ b/azalea/src/container.rs @@ -21,7 +21,7 @@ impl Plugin for ContainerPlugin { pub trait ContainerClientExt { async fn open_container(&mut self, pos: BlockPos) -> Option; - fn inventory(&mut self) -> Option; + fn open_inventory(&mut self) -> Option; } impl ContainerClientExt for Client { @@ -81,7 +81,7 @@ impl ContainerClientExt for Client { /// Note that this will send a packet to the server once it's dropped. Also, /// due to how it's implemented, you could call this function multiple times /// while another inventory handle already exists (but you shouldn't). - fn inventory(&mut self) -> Option { + fn open_inventory(&mut self) -> Option { let ecs = self.ecs.lock(); let inventory = ecs .get::(self.entity) -- cgit v1.2.3 From df167a5a391ef2a9bf2290a24d99ef8f559d9084 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 5 May 2023 17:15:49 +0000 Subject: fix some warnings --- azalea-inventory/azalea-inventory-macros/src/location_enum.rs | 2 +- azalea-inventory/azalea-inventory-macros/src/menu_impl.rs | 4 ++-- azalea-inventory/src/item/mod.rs | 2 ++ azalea/src/container.rs | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'azalea/src') diff --git a/azalea-inventory/azalea-inventory-macros/src/location_enum.rs b/azalea-inventory/azalea-inventory-macros/src/location_enum.rs index 6cec88e6..cfcf3d48 100644 --- a/azalea-inventory/azalea-inventory-macros/src/location_enum.rs +++ b/azalea-inventory/azalea-inventory-macros/src/location_enum.rs @@ -26,7 +26,7 @@ pub fn generate(input: &DeclareMenus) -> TokenStream { name_snake_case.span(), ); let enum_name = Ident::new( - &format!("{}MenuLocation", variant_name), + &format!("{variant_name}MenuLocation"), variant_name.span(), ); menu_location_variants.extend(quote! { diff --git a/azalea-inventory/azalea-inventory-macros/src/menu_impl.rs b/azalea-inventory/azalea-inventory-macros/src/menu_impl.rs index 804f69f2..6bb37e8e 100644 --- a/azalea-inventory/azalea-inventory-macros/src/menu_impl.rs +++ b/azalea-inventory/azalea-inventory-macros/src/menu_impl.rs @@ -406,13 +406,13 @@ fn generate_menu_consts(menu: &Menu) -> TokenStream { if field.length == 1 { let field_name = Ident::new( - format!("{}_SLOT", field_name_start).as_str(), + format!("{field_name_start}_SLOT").as_str(), field.name.span(), ); menu_consts.extend(quote! { pub const #field_name: usize = #field_index_start; }); } else { let field_name = Ident::new( - format!("{}_SLOTS", field_name_start).as_str(), + format!("{field_name_start}_SLOTS").as_str(), field.name.span(), ); menu_consts.extend(quote! { pub const #field_name: RangeInclusive = #field_index_start..=#field_index_end; }); diff --git a/azalea-inventory/src/item/mod.rs b/azalea-inventory/src/item/mod.rs index 07e51363..0ad7b2c0 100644 --- a/azalea-inventory/src/item/mod.rs +++ b/azalea-inventory/src/item/mod.rs @@ -3,6 +3,8 @@ pub trait MaxStackSizeExt { /// /// This is a signed integer to be consistent with the `count` field of /// [`ItemSlotData`]. + /// + /// [`ItemSlotData`]: crate::ItemSlotData fn max_stack_size(&self) -> i8; /// Whether this item can be stacked with other items. diff --git a/azalea/src/container.rs b/azalea/src/container.rs index c8849cdd..fefcf189 100644 --- a/azalea/src/container.rs +++ b/azalea/src/container.rs @@ -25,8 +25,8 @@ pub trait ContainerClientExt { } impl ContainerClientExt for Client { - /// Open a container in the world, like a chest. Use [`Client::inventory`] - /// to open your own inventory. + /// Open a container in the world, like a chest. Use + /// [`Client::open_inventory`] to open your own inventory. /// /// ``` /// # use azalea::prelude::*; -- cgit v1.2.3 From e1e1063d15a97bf93ec6dd1a082f78b0c3191d1d Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 9 May 2023 22:05:46 -0500 Subject: astar --- azalea-nbt/src/serde/mod.rs | 1 + azalea-nbt/src/serde/serializer.rs | 205 ++++++++++++++++ azalea/src/pathfinder/astar.rs | 106 ++++++++ azalea/src/pathfinder/mod.rs | 23 +- azalea/src/pathfinder/mtdstarlite.rs | 454 ----------------------------------- 5 files changed, 326 insertions(+), 463 deletions(-) create mode 100644 azalea-nbt/src/serde/mod.rs create mode 100644 azalea-nbt/src/serde/serializer.rs create mode 100644 azalea/src/pathfinder/astar.rs delete mode 100644 azalea/src/pathfinder/mtdstarlite.rs (limited to 'azalea/src') diff --git a/azalea-nbt/src/serde/mod.rs b/azalea-nbt/src/serde/mod.rs new file mode 100644 index 00000000..21d53a0f --- /dev/null +++ b/azalea-nbt/src/serde/mod.rs @@ -0,0 +1 @@ +mod serializer; diff --git a/azalea-nbt/src/serde/serializer.rs b/azalea-nbt/src/serde/serializer.rs new file mode 100644 index 00000000..945d2b42 --- /dev/null +++ b/azalea-nbt/src/serde/serializer.rs @@ -0,0 +1,205 @@ +use serde::{ser, Serialize}; + +use crate::Nbt; + +use std; +use std::fmt::{self, Display}; + +use serde::de; + +pub type Result = std::result::Result; + +// This is a bare-bones implementation. A real library would provide additional +// information in its error type, for example the line and column at which the +// error occurred, the byte offset into the input, or the current key being +// processed. +#[derive(Debug)] +pub enum Error { + Message(String), +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Message(msg) => formatter.write_str(msg), + } + } +} + +impl ser::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl std::error::Error for Error {} + +impl<'a> ser::Serializer for &'a mut Nbt { + type Ok = (); + + type Error = Error; + + type SerializeSeq = Self; + + type SerializeTuple = Self; + + type SerializeTupleStruct = Self; + + type SerializeTupleVariant = Self; + + type SerializeMap = Self; + + type SerializeStruct = Self; + + type SerializeStructVariant = Self; + + fn serialize_bool(self, v: bool) -> Result<()> { + todo!() + } + + fn serialize_i8(self, v: i8) -> Result<()> { + todo!() + } + + fn serialize_i16(self, v: i16) -> Result<()> { + todo!() + } + + fn serialize_i32(self, v: i32) -> Result<()> { + todo!() + } + + fn serialize_i64(self, v: i64) -> Result<()> { + todo!() + } + + fn serialize_u8(self, v: u8) -> Result<()> { + todo!() + } + + fn serialize_u16(self, v: u16) -> Result<()> { + todo!() + } + + fn serialize_u32(self, v: u32) -> Result<()> { + todo!() + } + + fn serialize_u64(self, v: u64) -> Result<()> { + todo!() + } + + fn serialize_f32(self, v: f32) -> Result<()> { + todo!() + } + + fn serialize_f64(self, v: f64) -> Result<()> { + todo!() + } + + fn serialize_char(self, v: char) -> Result<()> { + todo!() + } + + fn serialize_str(self, v: &str) -> Result<()> { + todo!() + } + + fn serialize_bytes(self, v: &[u8]) -> Result<()> { + todo!() + } + + fn serialize_none(self) -> Result<()> { + todo!() + } + + fn serialize_some(self, value: &T) -> Result<()> + where + T: Serialize, + { + todo!() + } + + fn serialize_unit(self) -> Result<()> { + todo!() + } + + fn serialize_unit_struct(self, name: &'static str) -> Result<()> { + todo!() + } + + fn serialize_unit_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + ) -> Result<()> { + todo!() + } + + fn serialize_newtype_struct(self, name: &'static str, value: &T) -> Result<()> + where + T: Serialize, + { + todo!() + } + + fn serialize_newtype_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result<()> + where + T: Serialize, + { + todo!() + } + + fn serialize_seq(self, len: Option) -> Result<()> { + todo!() + } + + fn serialize_tuple(self, len: usize) -> Result<()> { + todo!() + } + + fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<()> { + todo!() + } + + fn serialize_tuple_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result<()> { + todo!() + } + + fn serialize_map(self, len: Option) -> Result<()> { + todo!() + } + + fn serialize_struct(self, name: &'static str, len: usize) -> Result<()> { + todo!() + } + + fn serialize_struct_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result<()> { + todo!() + } +} diff --git a/azalea/src/pathfinder/astar.rs b/azalea/src/pathfinder/astar.rs new file mode 100644 index 00000000..858e7720 --- /dev/null +++ b/azalea/src/pathfinder/astar.rs @@ -0,0 +1,106 @@ +use std::{cmp::Reverse, collections::HashMap, fmt::Debug, hash::Hash, ops::Add}; + +use priority_queue::PriorityQueue; + +pub fn a_star( + start: N, + heuristic: HeuristicFn, + successors: SuccessorsFn, + success: SuccessFn, +) -> Option> +where + N: Eq + Hash + Copy + Debug, + W: PartialOrd + Default + Copy + num_traits::Bounded + Debug + Add, + HeuristicFn: Fn(&N) -> W, + SuccessorsFn: Fn(&N) -> Vec>, + SuccessFn: Fn(&N) -> bool, +{ + let mut open_set = PriorityQueue::new(); + open_set.push(start, Reverse(Weight(W::default()))); + let mut nodes: HashMap> = HashMap::new(); + nodes.insert( + start, + Node { + data: start, + came_from: None, + g_score: W::default(), + f_score: W::max_value(), + }, + ); + + while let Some((current_node, _)) = open_set.pop() { + if success(¤t_node) { + return Some(reconstruct_path(&nodes, current_node)); + } + + let current_g_score = nodes + .get(¤t_node) + .map(|n| n.g_score) + .unwrap_or(W::max_value()); + + for neighbor in successors(¤t_node) { + let tentative_g_score = current_g_score + neighbor.cost; + let neighbor_g_score = nodes + .get(&neighbor.target) + .map(|n| n.g_score) + .unwrap_or(W::max_value()); + if tentative_g_score < neighbor_g_score { + let f_score = tentative_g_score + heuristic(&neighbor.target); + nodes.insert( + neighbor.target, + Node { + data: neighbor.target, + came_from: Some(current_node), + g_score: tentative_g_score, + f_score, + }, + ); + open_set.push(neighbor.target, Reverse(Weight(f_score))); + } + } + } + + None +} + +fn reconstruct_path(nodes: &HashMap>, current: N) -> Vec +where + N: Eq + Hash + Copy + Debug, + W: PartialOrd + Default + Copy + num_traits::Bounded + Debug, +{ + let mut path = vec![current]; + let mut current = current; + while let Some(node) = nodes.get(¤t) { + if let Some(came_from) = node.came_from { + path.push(came_from); + current = came_from; + } else { + break; + } + } + path.reverse(); + path +} + +pub struct Node { + pub data: N, + pub came_from: Option, + pub g_score: W, + pub f_score: W, +} + +pub struct Edge { + pub target: N, + pub cost: W, +} + +#[derive(PartialOrd, PartialEq)] +pub struct Weight(W); +impl Ord for Weight { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0 + .partial_cmp(&other.0) + .unwrap_or(std::cmp::Ordering::Equal) + } +} +impl Eq for Weight {} diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index 56c8e0ce..3f978c95 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -1,7 +1,8 @@ +mod astar; mod moves; -mod mtdstarlite; use crate::bot::{JumpEvent, LookAtEvent}; +use crate::pathfinder::astar::a_star; use crate::{SprintDirection, WalkDirection}; use crate::app::{App, CoreSchedule, IntoSystemAppConfig, Plugin}; @@ -13,6 +14,7 @@ use crate::ecs::{ schedule::IntoSystemConfig, system::{Commands, Query, Res}, }; +use astar::Edge; use azalea_client::{StartSprintEvent, StartWalkEvent}; use azalea_core::{BlockPos, CardinalDirection}; use azalea_physics::PhysicsSet; @@ -25,8 +27,6 @@ use azalea_world::{ use bevy_tasks::{AsyncComputeTaskPool, Task}; use futures_lite::future; use log::{debug, error}; -use mtdstarlite::Edge; -pub use mtdstarlite::MTDStarLite; use std::collections::VecDeque; use std::sync::Arc; @@ -152,17 +152,22 @@ fn goto_listener( edges }; - let mut pf = MTDStarLite::new( + // let mut pf = MTDStarLite::new( + // start, + // end, + // |n| goal.heuristic(n), + // successors, + // successors, + // |n| goal.success(n), + // ); + + let start_time = std::time::Instant::now(); + let p = a_star( start, - end, |n| goal.heuristic(n), successors, - successors, |n| goal.success(n), ); - - let start_time = std::time::Instant::now(); - let p = pf.find_path(); let end_time = std::time::Instant::now(); debug!("path: {p:?}"); debug!("time: {:?}", end_time - start_time); diff --git a/azalea/src/pathfinder/mtdstarlite.rs b/azalea/src/pathfinder/mtdstarlite.rs deleted file mode 100644 index ce463279..00000000 --- a/azalea/src/pathfinder/mtdstarlite.rs +++ /dev/null @@ -1,454 +0,0 @@ -//! An implementation of Moving Target D* Lite as described in -//! -//! -//! Future optimization attempt ideas: -//! - Use a different priority queue (e.g. fibonacci heap) -//! - Use `FxHash` instead of the default hasher -//! - Have `par` be a raw pointer -//! - Try borrowing vs copying the Node in several places (like `state_mut`) -//! - Store edge costs in their own map - -use priority_queue::DoublePriorityQueue; -use std::{collections::HashMap, fmt::Debug, hash::Hash, ops::Add}; - -/// Nodes are coordinates. -pub struct MTDStarLite< - N: Eq + Hash + Copy + Debug, - W: PartialOrd + Default + Copy + num_traits::Bounded + Debug, - HeuristicFn: Fn(&N) -> W, - SuccessorsFn: Fn(&N) -> Vec>, - PredecessorsFn: Fn(&N) -> Vec>, - SuccessFn: Fn(&N) -> bool, -> { - /// Returns a rough estimate of how close we are to the goal. Lower = - /// closer. - pub heuristic: HeuristicFn, - /// Returns the nodes that can be reached from the given node. - pub successors: SuccessorsFn, - /// Returns the nodes that would direct us to the given node. If the graph - /// isn't directed (i.e. you can always return to the previous node), this - /// can be the same as `successors`. - pub predecessors: PredecessorsFn, - /// Returns true if the given node is at the goal. - /// A simple implementation is to check if the given node is equal to the - /// goal. - pub success: SuccessFn, - - start: N, - goal: N, - - old_start: N, - old_goal: N, - - k_m: W, - open: DoublePriorityQueue>, - node_states: HashMap>, - updated_edge_costs: Vec>, - - /// This only exists so it can be referenced by `state()` when there's no - /// state. - default_state: NodeState, -} - -impl< - N: Eq + Hash + Copy + Debug, - W: PartialOrd + Add + Default + Copy + num_traits::Bounded + Debug, - HeuristicFn: Fn(&N) -> W, - SuccessorsFn: Fn(&N) -> Vec>, - PredecessorsFn: Fn(&N) -> Vec>, - SuccessFn: Fn(&N) -> bool, - > MTDStarLite -{ - fn calculate_key(&self, n: &N) -> Priority { - let s = self.state(n); - let min_score = if s.g < s.rhs { s.g } else { s.rhs }; - Priority( - if min_score == W::max_value() { - min_score - } else { - min_score + (self.heuristic)(n) + self.k_m - }, - min_score, - ) - } - - pub fn new( - start: N, - goal: N, - heuristic: HeuristicFn, - successors: SuccessorsFn, - predecessors: PredecessorsFn, - success: SuccessFn, - ) -> Self { - let open = DoublePriorityQueue::default(); - let k_m = W::default(); - - let known_nodes = vec![start, goal]; - - let mut pf = MTDStarLite { - heuristic, - successors, - predecessors, - success, - - start, - goal, - - old_start: start, - old_goal: goal, - - k_m, - open, - node_states: HashMap::new(), - updated_edge_costs: Vec::new(), - - default_state: NodeState::default(), - }; - - for n in &known_nodes { - *pf.state_mut(n) = NodeState::default(); - } - pf.state_mut(&start).rhs = W::default(); - pf.open.push(start, pf.calculate_key(&start)); - - pf - } - - fn update_state(&mut self, n: &N) { - let u = self.state_mut(n); - if u.g != u.rhs { - if self.open.get(n).is_some() { - self.open.change_priority(n, self.calculate_key(n)); - } else { - self.open.push(*n, self.calculate_key(n)); - } - } else if self.open.get(n).is_some() { - self.open.remove(n); - } - } - - fn compute_cost_minimal_path(&mut self) { - while { - if let Some((_, top_key)) = self.open.peek_min() { - (top_key < &self.calculate_key(&self.goal)) || { - let goal_state = self.state(&self.goal); - goal_state.rhs > goal_state.g - } - } else { - false - } - } { - let (u_node, k_old) = self.open.pop_min().unwrap(); - let k_new = self.calculate_key(&u_node); - if k_old < k_new { - self.open.change_priority(&u_node, k_new); - continue; - } - let u = self.state_mut(&u_node); - if u.g > u.rhs { - u.g = u.rhs; - self.open.remove(&u_node); - for edge in (self.successors)(&u_node) { - let s_node = edge.target; - let s = self.state(&s_node); - let u = self.state(&u_node); - if s_node != self.start && (s.rhs > u.g + edge.cost) { - let s_rhs = u.g + edge.cost; - let s = self.state_mut(&s_node); - s.par = Some(u_node); - s.rhs = s_rhs; - self.update_state(&s_node); - } - } - } else { - u.g = W::max_value(); - let u_edge = Edge { - target: u_node, - cost: W::default(), - }; - for edge in (self.successors)(&u_node) - .iter() - .chain([&u_edge].into_iter()) - { - let s_node = edge.target; - let s = self.state(&s_node); - if s_node != self.start && s.par == Some(u_node) { - let mut min_pred = u_node; - let mut min_score = W::max_value(); - - for edge in (self.predecessors)(&s_node) { - let s = self.state(&edge.target); - let score = s.g + edge.cost; - if score < min_score { - min_score = score; - min_pred = edge.target; - } - } - - let s = self.state_mut(&s_node); - s.rhs = min_score; - if s.rhs == W::max_value() { - s.par = None; - } else { - s.par = Some(min_pred); - } - } - self.update_state(&s_node); - } - } - } - } - - pub fn find_path(&mut self) -> Option> { - if (self.success)(&self.start) { - return None; - } - - // - self.k_m = self.k_m + (self.heuristic)(&self.old_goal); - - if self.old_start != self.start { - self.optimized_deletion(); - } - - while let Some(edge) = self.updated_edge_costs.pop() { - let (u_node, v_node) = (edge.predecessor, edge.successor); - // update the edge cost c(u, v); - if edge.old_cost > edge.cost { - let u_g = self.state(&u_node).g; - if v_node != self.start && self.state(&v_node).rhs > u_g + edge.cost { - let v = self.state_mut(&v_node); - v.par = Some(u_node); - v.rhs = u_g + edge.cost; - } - } else if v_node != self.start && self.state(&v_node).par == Some(u_node) { - let mut min_pred = u_node; - let mut min_score = W::max_value(); - - for edge in (self.predecessors)(&v_node) { - let s = self.state(&edge.target); - let score = s.g + edge.cost; - if score < min_score { - min_score = score; - min_pred = edge.target; - } - } - - let v = self.state_mut(&v_node); - v.rhs = min_score; - if v.rhs == W::max_value() { - v.par = None; - } else { - v.par = Some(min_pred); - } - self.update_state(&v_node); - } - } - // - - self.old_start = self.start; - self.old_goal = self.goal; - - self.compute_cost_minimal_path(); - if self.state(&self.goal).rhs == W::max_value() { - // no path exists - return None; - } - - let mut reverse_path = vec![self.goal]; - - // identify a path from sstart to sgoal using the parent pointers - let mut target = self.state(&self.goal).par; - while !(Some(self.start) == target) { - let Some(this_target) = target else { - break; - }; - // hunter follows path from start to goal; - reverse_path.push(this_target); - target = self.state(&this_target).par; - } - - // if hunter caught target { - // return None; - // } - - let path: Vec = reverse_path.into_iter().rev().collect(); - - Some(path) - } - - fn optimized_deletion(&mut self) { - let start = self.start; - self.state_mut(&start).par = None; - - let mut min_pred = self.old_start; - let mut min_score = W::max_value(); - - for edge in (self.predecessors)(&self.old_start) { - let s = self.state(&edge.target); - let score = s.g + edge.cost; - if score < min_score { - min_score = score; - min_pred = edge.target; - } - } - - let old_start = self.old_start; - let s = self.state_mut(&old_start); - s.rhs = min_score; - if s.rhs == W::max_value() { - s.par = None; - } else { - s.par = Some(min_pred); - } - self.update_state(&old_start); - } - - fn state(&self, n: &N) -> &NodeState { - self.node_states.get(n).unwrap_or(&self.default_state) - } - - fn state_mut(&mut self, n: &N) -> &mut NodeState { - self.node_states.entry(*n).or_default() - } -} - -#[derive(PartialEq, Debug)] -pub struct Priority(W, W) -where - W: PartialOrd + Debug; - -impl PartialOrd for Priority { - fn partial_cmp(&self, other: &Self) -> Option { - if self.0 < other.0 { - Some(std::cmp::Ordering::Less) - } else if self.0 > other.0 { - Some(std::cmp::Ordering::Greater) - } else if self.1 < other.1 { - Some(std::cmp::Ordering::Less) - } else if self.1 > other.1 { - Some(std::cmp::Ordering::Greater) - } else { - Some(std::cmp::Ordering::Equal) - } - } -} -impl Ord for Priority { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.partial_cmp(other) - .expect("Partial compare should not fail for Priority") - } -} -impl Eq for Priority {} - -#[derive(Debug)] -pub struct NodeState { - pub g: W, - pub rhs: W, - // future possible optimization: try making this a pointer - pub par: Option, -} - -impl Default - for NodeState -{ - fn default() -> Self { - NodeState { - g: W::max_value(), - rhs: W::max_value(), - par: None, - } - } -} - -pub struct Edge { - pub target: N, - pub cost: W, -} - -pub struct ChangedEdge { - pub predecessor: N, - pub successor: N, - pub old_cost: W, - pub cost: W, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_mtdstarlite() { - let maze = [ - [0, 1, 0, 0, 0], - [0, 1, 0, 1, 0], - [0, 0, 0, 1, 0], - [0, 1, 0, 1, 0], - [0, 0, 1, 0, 0], - ]; - let width = maze[0].len(); - let height = maze.len(); - - let goal = (4, 4); - - let heuristic = |n: &(usize, usize)| -> usize { - ((n.0 as isize - goal.0 as isize).abs() + (n.1 as isize - goal.1 as isize).abs()) - as usize - }; - let successors = |n: &(usize, usize)| -> Vec> { - let mut successors = Vec::with_capacity(4); - let (x, y) = *n; - - if x > 0 && maze[y][x - 1] == 0 { - successors.push(Edge { - target: ((x - 1, y)), - cost: 1, - }); - } - if x < width - 1 && maze[y][x + 1] == 0 { - successors.push(Edge { - target: ((x + 1, y)), - cost: 1, - }); - } - if y > 0 && maze[y - 1][x] == 0 { - successors.push(Edge { - target: ((x, y - 1)), - cost: 1, - }); - } - if y < height - 1 && maze[y + 1][x] == 0 { - successors.push(Edge { - target: ((x, y + 1)), - cost: 1, - }); - } - - successors - }; - let predecessors = - |n: &(usize, usize)| -> Vec> { successors(n) }; - - let mut pf = MTDStarLite::new((0, 0), goal, heuristic, successors, predecessors, |n| { - n == &goal - }); - let path = pf.find_path().unwrap(); - assert_eq!( - path, - vec![ - (0, 1), - (0, 2), - (1, 2), - (2, 2), - (2, 1), - (2, 0), - (3, 0), - (4, 0), - (4, 1), - (4, 2), - (4, 3), - (4, 4), - ] - ); - } -} -- cgit v1.2.3 From 80172e43640d2ec3c761611d2269d12a9abdd424 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 12 May 2023 18:53:08 +0000 Subject: fix warnings --- azalea-brigadier/src/arguments/bool_argument_type.rs | 2 +- azalea-brigadier/src/arguments/string_argument_type.rs | 2 +- azalea/src/pathfinder/astar.rs | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'azalea/src') diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index 01828c87..57fa8a03 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -12,7 +12,7 @@ impl ArgumentType for bool { } } -pub fn get_bool<'a, S>(context: &'a CommandContext, name: &str) -> Option { +pub fn get_bool(context: &CommandContext, name: &str) -> Option { context .argument(name) .unwrap() diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs index f23af3ba..27363bd4 100644 --- a/azalea-brigadier/src/arguments/string_argument_type.rs +++ b/azalea-brigadier/src/arguments/string_argument_type.rs @@ -44,7 +44,7 @@ pub fn string() -> impl ArgumentType { pub fn greedy_string() -> impl ArgumentType { StringArgument::GreedyPhrase } -pub fn get_string<'a, S>(context: &'a CommandContext, name: &str) -> Option { +pub fn get_string(context: &CommandContext, name: &str) -> Option { context .argument(name) .unwrap() diff --git a/azalea/src/pathfinder/astar.rs b/azalea/src/pathfinder/astar.rs index 858e7720..65caf337 100644 --- a/azalea/src/pathfinder/astar.rs +++ b/azalea/src/pathfinder/astar.rs @@ -94,7 +94,7 @@ pub struct Edge { pub cost: W, } -#[derive(PartialOrd, PartialEq)] +#[derive(PartialEq)] pub struct Weight(W); impl Ord for Weight { fn cmp(&self, other: &Self) -> std::cmp::Ordering { @@ -104,3 +104,8 @@ impl Ord for Weight { } } impl Eq for Weight {} +impl PartialOrd for Weight { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} -- cgit v1.2.3 From e977391b0413aaa62ea725a53e4a78c287844f4c Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 12 May 2023 23:20:23 -0500 Subject: auto respawn --- azalea-client/src/client.rs | 2 ++ azalea-client/src/lib.rs | 1 + azalea-client/src/respawn.rs | 38 ++++++++++++++++++++++++++++++++++++++ azalea/examples/testbot.rs | 9 ++------- azalea/src/auto_respawn.rs | 24 ++++++++++++++++++++++++ azalea/src/bot.rs | 2 ++ azalea/src/lib.rs | 1 + 7 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 azalea-client/src/respawn.rs create mode 100644 azalea/src/auto_respawn.rs (limited to 'azalea/src') diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 7a4285e6..f2e91dfa 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -11,6 +11,7 @@ use crate::{ movement::{LastSentLookDirection, PlayerMovePlugin}, packet_handling::{self, PacketHandlerPlugin, PacketReceiver}, player::retroactively_add_game_profile_component, + respawn::RespawnPlugin, task_pool::TaskPoolPlugin, Account, PlayerInfo, }; @@ -717,6 +718,7 @@ impl PluginGroup for DefaultPlugins { .add(DisconnectPlugin) .add(PlayerMovePlugin) .add(InteractPlugin) + .add(RespawnPlugin) .add(TickBroadcastPlugin) } } diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index c198ced3..2c08033b 100644 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -25,6 +25,7 @@ mod movement; pub mod packet_handling; pub mod ping; mod player; +pub mod respawn; pub mod task_pool; pub use account::{Account, AccountOpts}; diff --git a/azalea-client/src/respawn.rs b/azalea-client/src/respawn.rs new file mode 100644 index 00000000..4e42157c --- /dev/null +++ b/azalea-client/src/respawn.rs @@ -0,0 +1,38 @@ +use azalea_protocol::packets::game::serverbound_client_command_packet::{ + self, ServerboundClientCommandPacket, +}; +use bevy_app::{App, Plugin}; +use bevy_ecs::prelude::*; + +use crate::LocalPlayer; + +/// Tell the server that we're respawning. +#[derive(Debug, Clone)] +pub struct PerformRespawnEvent { + pub entity: Entity, +} + +/// A plugin that makes [`PerformRespawnEvent`] send the packet to respawn. +pub struct RespawnPlugin; +impl Plugin for RespawnPlugin { + fn build(&self, app: &mut App) { + app.add_event::() + .add_system(perform_respawn); + } +} + +pub fn perform_respawn( + mut events: EventReader, + mut query: Query<&mut LocalPlayer>, +) { + for event in events.iter() { + if let Ok(local_player) = query.get_mut(event.entity) { + local_player.write_packet( + ServerboundClientCommandPacket { + action: serverbound_client_command_packet::Action::PerformRespawn, + } + .get(), + ); + } + } +} diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index 3fe9253c..ae6d293b 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -8,10 +8,10 @@ use azalea::entity::{EyeHeight, Position}; use azalea::interact::HitResultComponent; use azalea::inventory::ItemSlot; use azalea::pathfinder::BlockPosGoal; +use azalea::protocol::packets::game::serverbound_client_command_packet::ServerboundClientCommandPacket; +use azalea::protocol::packets::game::ClientboundGamePacket; use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection}; use azalea::{Account, Client, Event}; -use azalea_protocol::packets::game::serverbound_client_command_packet::ServerboundClientCommandPacket; -use azalea_protocol::packets::game::ClientboundGamePacket; use std::time::Duration; #[derive(Default, Clone, Component)] @@ -212,11 +212,6 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< } } } - Event::Death(_) => { - bot.write_packet(ServerboundClientCommandPacket { - action: azalea_protocol::packets::game::serverbound_client_command_packet::Action::PerformRespawn, - }.get()); - } Event::Packet(packet) => match *packet { ClientboundGamePacket::Login(_) => { println!("login packet"); diff --git a/azalea/src/auto_respawn.rs b/azalea/src/auto_respawn.rs new file mode 100644 index 00000000..c5dd12a4 --- /dev/null +++ b/azalea/src/auto_respawn.rs @@ -0,0 +1,24 @@ +use crate::app::{App, Plugin}; +use azalea_client::packet_handling::DeathEvent; +use azalea_client::respawn::PerformRespawnEvent; +use bevy_ecs::prelude::*; + +/// A plugin that makes [`DeathEvent`]s send [`PerformRespawnEvent`]s. +#[derive(Clone, Default)] +pub struct AutoRespawnPlugin; +impl Plugin for AutoRespawnPlugin { + fn build(&self, app: &mut App) { + app.add_system(auto_respawn); + } +} + +fn auto_respawn( + mut events: EventReader, + mut perform_respawn_events: EventWriter, +) { + for event in events.iter() { + perform_respawn_events.send(PerformRespawnEvent { + entity: event.entity, + }); + } +} diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs index e5ea4c28..13b33bb0 100644 --- a/azalea/src/bot.rs +++ b/azalea/src/bot.rs @@ -1,4 +1,5 @@ use crate::app::{App, CoreSchedule, IntoSystemAppConfig, Plugin, PluginGroup, PluginGroupBuilder}; +use crate::auto_respawn::AutoRespawnPlugin; use crate::container::ContainerPlugin; use crate::ecs::{ component::Component, @@ -135,5 +136,6 @@ impl PluginGroup for DefaultBotPlugins { .add(BotPlugin) .add(PathfinderPlugin) .add(ContainerPlugin) + .add(AutoRespawnPlugin) } } diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index bde94634..604961f4 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -3,6 +3,7 @@ #![allow(incomplete_features)] #![feature(async_fn_in_trait)] +mod auto_respawn; mod bot; mod container; pub mod pathfinder; -- cgit v1.2.3