From 631124f3fc6cade8667f001636fe92300e02ff61 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 26 Jan 2020 12:47:41 +0000 Subject: [PATCH] Switched to _squared(), added comments, parallelised waypoint gen --- common/src/lib.rs | 2 +- common/src/path.rs | 3 +- common/src/sys/agent.rs | 153 ++++++++++++++++++++----------------- server/src/cmd.rs | 3 +- server/src/sys/waypoint.rs | 7 +- world/src/sim/mod.rs | 65 ++++++++-------- 6 files changed, 122 insertions(+), 111 deletions(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index 5bdebb171d..e9960f6567 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code)] #![type_length_limit = "1664759"] -#![feature(trait_alias, arbitrary_enum_discriminant)] +#![feature(trait_alias, arbitrary_enum_discriminant, label_break_value)] #[macro_use] extern crate serde_derive; diff --git a/common/src/path.rs b/common/src/path.rs index a47a386af2..f6244f0895 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -89,8 +89,7 @@ impl Route { } } -// Chaser: A self-contained system that attempts to chase a moving target - +/// A self-contained system that attempts to chase a moving target, only performing pathfinding if necessary #[derive(Default, Clone, Debug)] pub struct Chaser { last_search_tgt: Option>, diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 4dc019e200..a96daa166c 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -80,83 +80,93 @@ impl<'a> System<'a> for Sys { let mut do_idle = false; let mut choose_target = false; - match &mut agent.activity { - Activity::Idle(bearing) => { - *bearing += Vec2::new( - thread_rng().gen::() - 0.5, - thread_rng().gen::() - 0.5, - ) * 0.1 - - *bearing * 0.01 - - if let Some(patrol_origin) = agent.patrol_origin { - Vec2::::from(pos.0 - patrol_origin) * 0.0002 - } else { - Vec2::zero() - }; + 'activity: { + match &mut agent.activity { + Activity::Idle(bearing) => { + *bearing += Vec2::new( + thread_rng().gen::() - 0.5, + thread_rng().gen::() - 0.5, + ) * 0.1 + - *bearing * 0.01 + - if let Some(patrol_origin) = agent.patrol_origin { + Vec2::::from(pos.0 - patrol_origin) * 0.0002 + } else { + Vec2::zero() + }; - if bearing.magnitude_squared() > 0.25f32.powf(2.0) { - inputs.move_dir = bearing.normalized() * 0.65; - } - - // Sometimes try searching for new targets - if thread_rng().gen::() < 0.1 { - choose_target = true; - } - } - Activity::Follow(target, chaser) => { - if let (Some(tgt_pos), _tgt_stats) = - (positions.get(*target), stats.get(*target)) - { - let dist = pos.0.distance(tgt_pos.0); - // Follow, or return to idle - if dist > AVG_FOLLOW_DIST { - if let Some(bearing) = - chaser.chase(&*terrain, pos.0, tgt_pos.0, AVG_FOLLOW_DIST) - { - inputs.move_dir = - Vec2::from(bearing).try_normalized().unwrap_or(Vec2::zero()); - inputs.jump.set_state(bearing.z > 1.0); - } - } else { - do_idle = true; + if bearing.magnitude_squared() > 0.25f32.powf(2.0) { + inputs.move_dir = bearing.normalized() * 0.65; + } + + // Sometimes try searching for new targets + if thread_rng().gen::() < 0.1 { + choose_target = true; } - } else { - do_idle = true; } - } - Activity::Attack(target, chaser, _) => { - if let (Some(tgt_pos), _tgt_stats, tgt_alignment) = ( - positions.get(*target), - stats.get(*target), - alignments.get(*target), - ) { - // Don't attack aligned entities - if let (Some(alignment), Some(tgt_alignment)) = (alignment, tgt_alignment) { - if !tgt_alignment.hostile_towards(*alignment) { + Activity::Follow(target, chaser) => { + if let (Some(tgt_pos), _tgt_stats) = + (positions.get(*target), stats.get(*target)) + { + let dist_sqrd = pos.0.distance_squared(tgt_pos.0); + // Follow, or return to idle + if dist_sqrd > AVG_FOLLOW_DIST.powf(2.0) { + if let Some(bearing) = + chaser.chase(&*terrain, pos.0, tgt_pos.0, AVG_FOLLOW_DIST) + { + inputs.move_dir = Vec2::from(bearing) + .try_normalized() + .unwrap_or(Vec2::zero()); + inputs.jump.set_state(bearing.z > 1.0); + } + } else { do_idle = true; } + } else { + do_idle = true; } + } + Activity::Attack(target, chaser, _) => { + if let (Some(tgt_pos), _tgt_stats, tgt_alignment) = ( + positions.get(*target), + stats.get(*target), + alignments.get(*target), + ) { + // Don't attack aligned entities + // TODO: This is a bit of a hack, find a better way to do this + if let (Some(alignment), Some(tgt_alignment)) = + (alignment, tgt_alignment) + { + if !tgt_alignment.hostile_towards(*alignment) { + do_idle = true; + break 'activity; + } + } - let dist = pos.0.distance(tgt_pos.0); - if dist < MIN_ATTACK_DIST { - // Close-range attack - inputs.look_dir = tgt_pos.0 - pos.0; - inputs.move_dir = Vec2::from(tgt_pos.0 - pos.0) - .try_normalized() - .unwrap_or(Vec2::unit_y()) - * 0.01; - inputs.primary.set_state(true); - } else if dist < MAX_CHASE_DIST { - // Long-range chase - if let Some(bearing) = chaser.chase(&*terrain, pos.0, tgt_pos.0, 1.25) { - inputs.move_dir = - Vec2::from(bearing).try_normalized().unwrap_or(Vec2::zero()); - inputs.jump.set_state(bearing.z > 1.0); + let dist_sqrd = pos.0.distance_squared(tgt_pos.0); + if dist_sqrd < MIN_ATTACK_DIST.powf(2.0) { + // Close-range attack + inputs.look_dir = tgt_pos.0 - pos.0; + inputs.move_dir = Vec2::from(tgt_pos.0 - pos.0) + .try_normalized() + .unwrap_or(Vec2::unit_y()) + * 0.01; + inputs.primary.set_state(true); + } else if dist_sqrd < MAX_CHASE_DIST.powf(2.0) { + // Long-range chase + if let Some(bearing) = + chaser.chase(&*terrain, pos.0, tgt_pos.0, 1.25) + { + inputs.move_dir = Vec2::from(bearing) + .try_normalized() + .unwrap_or(Vec2::zero()); + inputs.jump.set_state(bearing.z > 1.0); + } + } else { + do_idle = true; } } else { do_idle = true; } - } else { - do_idle = true; } } } @@ -165,12 +175,15 @@ impl<'a> System<'a> for Sys { agent.activity = Activity::Idle(Vec2::zero()); } + // Choose a new target to attack: only go out of our way to attack targets we are + // hostile toward! if choose_target { - // Search for new targets + // Search for new targets (this looks expensive, but it's only run occasionally) + // TODO: Replace this with a better system that doesn't consider *all* entities let entities = (&entities, &positions, &stats, alignments.maybe()) .join() .filter(|(e, e_pos, e_stats, e_alignment)| { - (e_pos.0 - pos.0).magnitude() < SIGHT_DIST + (e_pos.0 - pos.0).magnitude_squared() < SIGHT_DIST.powf(2.0) && *e != entity && !e_stats.is_dead && alignment @@ -206,8 +219,8 @@ impl<'a> System<'a> for Sys { // Follow owner if we're too far, or if they're under attack if let Some(owner) = agent.owner { if let Some(owner_pos) = positions.get(owner) { - let dist = pos.0.distance(owner_pos.0); - if dist > MAX_FOLLOW_DIST && !agent.activity.is_follow() { + let dist_sqrd = pos.0.distance_squared(owner_pos.0); + if dist_sqrd > MAX_FOLLOW_DIST.powf(2.0) && !agent.activity.is_follow() { agent.activity = Activity::Follow(owner, Chaser::default()); } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 85d42151b1..b036ae0b1b 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1088,10 +1088,11 @@ fn handle_remove_lights( match opt_player_pos { Some(player_pos) => { let ecs = server.state.ecs(); - for (entity, pos, _, _) in ( + for (entity, pos, _, _, _) in ( &ecs.entities(), &ecs.read_storage::(), &ecs.read_storage::(), + !&ecs.read_storage::(), !&ecs.read_storage::(), ) .join() diff --git a/server/src/sys/waypoint.rs b/server/src/sys/waypoint.rs index 1dc47d9adb..a09ec4e92b 100644 --- a/server/src/sys/waypoint.rs +++ b/server/src/sys/waypoint.rs @@ -1,11 +1,8 @@ use common::comp::{Player, Pos, Waypoint, WaypointArea}; use specs::{Entities, Join, ReadStorage, System, WriteStorage}; -/// This system will handle loading generated chunks and unloading uneeded chunks. -/// 1. Inserts newly generated chunks into the TerrainGrid -/// 2. Sends new chunks to neaby clients -/// 3. Handles the chunk's supplement (e.g. npcs) -/// 4. Removes chunks outside the range of players +/// This system updates player waypoints +/// TODO: Make this faster by only considering local waypoints pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 1eb41ea613..7e376cc96c 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -1454,40 +1454,41 @@ impl WorldSim { .map(|i| { (0..WORLD_SIZE.y) .step_by(WAYPOINT_EVERY) - .filter_map(move |j| { - let mut pos = Vec2::new(i as i32, j as i32); - - // Slide the waypoints down hills - for _ in 0..32 { - let last_pos = pos; - let chunk = this.get(pos)?; - - for dir in [ - Vec2::new(1, 0), - Vec2::new(-1, 0), - Vec2::new(0, 1), - Vec2::new(0, -1), - ] - .iter() - { - const MAX_HEIGHT_DIFF: f32 = 8.0; - let tgt_chunk = this.get(pos + *dir)?; - if tgt_chunk.alt + MAX_HEIGHT_DIFF < chunk.alt - && !tgt_chunk.is_underwater - { - pos += *dir; - } - } - - if last_pos == pos { - break; - } - } - - Some(pos) - }) + .map(move |j| (i, j)) }) .flatten() + .collect::>() + .into_par_iter() + .filter_map(|(i, j)| { + let mut pos = Vec2::new(i as i32, j as i32); + + // Slide the waypoints down hills + for _ in 0..32 { + let last_pos = pos; + let chunk = this.get(pos)?; + + for dir in [ + Vec2::new(1, 0), + Vec2::new(-1, 0), + Vec2::new(0, 1), + Vec2::new(0, -1), + ] + .iter() + { + const MAX_HEIGHT_DIFF: f32 = 8.0; + let tgt_chunk = this.get(pos + *dir)?; + if tgt_chunk.alt + MAX_HEIGHT_DIFF < chunk.alt && !tgt_chunk.is_underwater { + pos += *dir; + } + } + + if last_pos == pos { + break; + } + } + + Some(pos) + }) .collect::>(); for waypoint in waypoints {