From a4f0fa8aa4b6f7f07cfa70b2f77045bb954c92be Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Tue, 27 Jul 2021 11:17:32 -0500 Subject: [PATCH] Round 2: Refactor to futures, attaching tasks as components. --- Cargo.toml | 2 +- src/lib.rs | 2 +- src/pathfinding.rs | 191 +++++++++++++++++++++++++-------------------- 3 files changed, 107 insertions(+), 88 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f75652..d196167 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,8 @@ bevy_openal = { path = "../bevy_openal" } bevy_rapier2d = "0.10" bevy_tts = { path = "../bevy_tts" } coord_2d = "0.3" -crossbeam-channel = "0.5" derive_more = "0.99" +futures-lite = "1" gilrs = "0.8" mapgen = { git = "https://github.com/ndarilek/mapgen.rs" } maze_generator = "1" diff --git a/src/lib.rs b/src/lib.rs index 833607d..3da47ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,10 +8,10 @@ pub use bevy_rapier2d; pub use bevy_tts; #[macro_use] pub mod core; -pub use crossbeam_channel; pub use derive_more; pub mod error; pub mod exploration; +pub use futures_lite; pub use gilrs; pub mod log; pub mod map; diff --git a/src/pathfinding.rs b/src/pathfinding.rs index 19ebafd..960b864 100644 --- a/src/pathfinding.rs +++ b/src/pathfinding.rs @@ -1,13 +1,16 @@ use std::collections::HashMap; -use bevy::{prelude::*, tasks::prelude::*}; +use bevy::{ + prelude::*, + tasks::{prelude::*, Task}, +}; use bevy_rapier2d::{ na::UnitComplex, prelude::*, rapier::data::{ComponentSet, ComponentSetOption, Index}, }; -use crossbeam_channel::{unbounded, Receiver, Sender}; use derive_more::{Deref, DerefMut}; +use futures_lite::future; use pathfinding::prelude::*; use crate::{ @@ -16,6 +19,9 @@ use crate::{ navigation::{RotationSpeed, Speed}, }; +#[derive(Debug, Deref, DerefMut)] +struct Calculating(Task>); + #[derive(Clone, Copy, Debug, Default, Deref, DerefMut, Eq, Hash, PartialEq, Reflect)] #[reflect(Component)] pub struct Destination(pub (i32, i32)); @@ -129,59 +135,46 @@ impl ComponentSetOption for StaticColliderComponentsSet { } fn find_path_for_shape( - pool: &AsyncComputeTaskPool, - query_pipeline: QueryPipeline, initiator: Entity, start: &dyn PointLike, destination: &dyn PointLike, + query_pipeline: &QueryPipeline, map: &Map, - collider_query: &QueryPipelineColliderComponentsQuery, - obstructions: &Query<&MapObstruction>, + collider_set: StaticColliderComponentsSet, shape: &SharedShape, - channel: &Sender>, -) { - let collider_set: StaticColliderComponentsSet = (collider_query, obstructions).into(); - let start = start.i32(); - let destination = destination.i32(); - let map_clone = map.clone(); - let shape_clone = shape.clone(); - let channel_clone = channel.clone(); - pool.spawn(async move { - let path = astar( - &start, - |p| { - let mut successors: Vec<((i32, i32), u32)> = vec![]; - for tile in map_clone.get_available_exits(p.0 as usize, p.1 as usize) { - let mut should_push = true; - let shape_pos = Isometry::new(vector![tile.0 as f32, tile.1 as f32], 0.); - query_pipeline.intersections_with_shape( - &collider_set, - &shape_pos, - &*shape_clone, - InteractionGroups::all(), - Some(&|v| v.entity() != initiator), - |_handle| { - should_push = false; - false - }, - ); - if should_push { - successors.push(((tile.0 as i32, tile.1 as i32), (tile.2 * 100.) as u32)); - } +) -> Option { + let path = astar( + &start.i32(), + |p| { + let mut successors: Vec<((i32, i32), u32)> = vec![]; + for tile in map.get_available_exits(p.0 as usize, p.1 as usize) { + let mut should_push = true; + let shape_pos = Isometry::new(vector![tile.0 as f32, tile.1 as f32], 0.); + query_pipeline.intersections_with_shape( + &collider_set, + &shape_pos, + &**shape, + InteractionGroups::all(), + Some(&|v| v.entity() != initiator), + |_handle| { + should_push = false; + false + }, + ); + if should_push { + successors.push(((tile.0 as i32, tile.1 as i32), (tile.2 * 100.) as u32)); } - successors - }, - |p| (p.distance_squared(&destination) * 100.) as u32, - |p| *p == destination, - ); - let rv = if let Some(path) = path { - Some(Path(path.0)) - } else { - None - }; - channel_clone.send(rv).ok(); - }) - .detach(); + } + successors + }, + |p| (p.distance_squared(destination) * 100.) as u32, + |p| *p == destination.i32(), + ); + if let Some(path) = path { + Some(Path(path.0)) + } else { + None + } } fn calculate_path( @@ -190,54 +183,68 @@ fn calculate_path( query_pipeline: Res, obstructions: Query<&MapObstruction>, collider_query: QueryPipelineColliderComponentsQuery, - mut calculating: Local>>>, - query: Query<(Entity, &Destination, &Coordinates, &ColliderShape), Changed>, - destinations: Query<&Destination, Without>, + query: Query< + (Entity, &Destination, &Coordinates, &ColliderShape), + (Without, Without, Without), + >, map: Query<&Map>, ) { - let calculating_clone = calculating.clone(); - for (entity, rx) in calculating_clone.iter() { - if destinations.get(*entity).is_ok() { - if let Ok(msg) = rx.try_recv() { - if let Some(path) = msg { - commands.entity(*entity).insert(path); - } else { - println!("No path, tagging {:?}", entity); - commands.entity(*entity).insert(NoPath); - } - calculating.remove(&entity); - } - } else { - calculating.remove(&entity); - } - } for (entity, destination, coordinates, shape) in query.iter() { - if calculating.contains_key(&entity) { - continue; - } if coordinates.i32() == **destination { println!("Removing path1: {:?}", entity); commands .entity(entity) .remove::() + .remove::() .remove::(); continue; } - let (tx, rx) = unbounded(); - calculating.insert(entity, rx); for map in map.iter() { - find_path_for_shape( - &*pool, - query_pipeline.clone(), - entity, - coordinates, - destination, - &map, - &collider_query, - &obstructions, - shape, - &tx, - ); + let coordinates_clone = *coordinates; + let destination_clone = *destination; + let query_pipeline_clone = query_pipeline.clone(); + let map_clone = map.clone(); + let shape_clone = shape.clone(); + let collider_set: StaticColliderComponentsSet = (&collider_query, &obstructions).into(); + println!("Spawning pathfinding task for {:?}", entity); + let task = pool.spawn(async move { + find_path_for_shape( + entity, + &coordinates_clone, + &destination_clone, + &query_pipeline_clone, + &map_clone, + collider_set, + &shape_clone, + ) + }); + commands.entity(entity).insert(Calculating(task)); + println!("Inserted"); + } + } +} + +fn poll_tasks( + mut commands: Commands, + mut query: Query<(Entity, &mut Calculating)>, + destinations: Query<&Destination>, +) { + for (entity, mut calculating) in query.iter_mut() { + println!("Got a calculating task for {:?}", entity); + if destinations.get(entity).is_ok() { + if let Some(result) = future::block_on(future::poll_once(&mut **calculating)) { + println!("Got a result, setting and returning"); + if let Some(path) = result { + commands.entity(entity).insert(path); + } else { + commands.entity(entity).insert(NoPath); + } + println!("Removing calculation task"); + commands.entity(entity).remove::(); + } + } else { + println!("Destination was removed, clearing task"); + commands.entity(entity).remove::(); } } } @@ -312,6 +319,16 @@ fn negotiate_path( } } +fn remove_calculating( + mut commands: Commands, + query: Query, With)>, +) { + for entity in query.iter() { + println!("Probably shouldn't happen"); + commands.entity(entity).remove::(); + } +} + fn remove_no_path( mut commands: Commands, query: Query, Changed)>, @@ -325,8 +342,10 @@ pub struct PathfindingPlugin; impl Plugin for PathfindingPlugin { fn build(&self, app: &mut AppBuilder) { - app.add_system_to_stage(CoreStage::PostUpdate, calculate_path.system()) + app.add_system(calculate_path.system()) + .add_system(poll_tasks.system()) .add_system(negotiate_path.system()) + .add_system_to_stage(CoreStage::PostUpdate, remove_calculating.system()) .add_system(remove_no_path.system()); } }