mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Don't parallelize spatial grid construction.
Instead, let the single threaded systems run more in parallel. This is the beginning of an effort to make physics run concurrently with almost all other systems.
This commit is contained in:
parent
cc6d904c75
commit
cead27989b
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -6718,6 +6718,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"slab",
|
"slab",
|
||||||
"slotmap 1.0.6",
|
"slotmap 1.0.6",
|
||||||
|
"smallvec",
|
||||||
"specs",
|
"specs",
|
||||||
"spin_sleep",
|
"spin_sleep",
|
||||||
"structopt",
|
"structopt",
|
||||||
@ -6861,7 +6862,7 @@ dependencies = [
|
|||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.9.1",
|
"hashbrown 0.12.3",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lz-fear",
|
"lz-fear",
|
||||||
"prometheus",
|
"prometheus",
|
||||||
|
@ -71,9 +71,9 @@ petgraph = { version = "0.6", optional = true }
|
|||||||
kiddo = { version = "0.1", optional = true }
|
kiddo = { version = "0.1", optional = true }
|
||||||
|
|
||||||
# Data structures
|
# Data structures
|
||||||
dashmap = { version = "5.4.0", features = ["rayon"] }
|
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
||||||
slotmap = { version = "1.0", features = ["serde"] }
|
slotmap = { version = "1.0", features = ["serde"] }
|
||||||
|
smallvec = { version = "1.9.0", features = ["union", "const_generics", "const_new", "specialization", "may_dangle"] }
|
||||||
indexmap = { version = "1.3.0", features = ["rayon"] }
|
indexmap = { version = "1.3.0", features = ["rayon"] }
|
||||||
slab = "0.4.2"
|
slab = "0.4.2"
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ impl Default for CachedSpatialGrid {
|
|||||||
let lg2_large_cell_size = 6; // 64
|
let lg2_large_cell_size = 6; // 64
|
||||||
let radius_cutoff = 8;
|
let radius_cutoff = 8;
|
||||||
|
|
||||||
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff).into_read_only();
|
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff, (0, 0)).into_read_only();
|
||||||
|
|
||||||
Self(spatial_grid)
|
Self(spatial_grid)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ pub use self::{
|
|||||||
player::DisconnectReason,
|
player::DisconnectReason,
|
||||||
player::{AliasError, Player, MAX_ALIAS_LEN},
|
player::{AliasError, Player, MAX_ALIAS_LEN},
|
||||||
poise::{Poise, PoiseChange, PoiseState},
|
poise::{Poise, PoiseChange, PoiseState},
|
||||||
projectile::{Projectile, ProjectileConstructor},
|
projectile::{Projectile, ProjectileConstructor, ProjectileOwned},
|
||||||
shockwave::{Shockwave, ShockwaveHitEntities},
|
shockwave::{Shockwave, ShockwaveHitEntities},
|
||||||
skillset::{
|
skillset::{
|
||||||
skills::{self, Skill},
|
skills::{self, Skill},
|
||||||
|
@ -23,11 +23,6 @@ pub enum Effect {
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Projectile {
|
pub struct Projectile {
|
||||||
// TODO: use SmallVec for these effects
|
|
||||||
pub hit_solid: Vec<Effect>,
|
|
||||||
pub hit_entity: Vec<Effect>,
|
|
||||||
/// Time left until the projectile will despawn
|
|
||||||
pub time_left: Duration,
|
|
||||||
pub owner: Option<Uid>,
|
pub owner: Option<Uid>,
|
||||||
/// Whether projectile collides with entities in the same group as its
|
/// Whether projectile collides with entities in the same group as its
|
||||||
/// owner
|
/// owner
|
||||||
@ -38,10 +33,26 @@ pub struct Projectile {
|
|||||||
pub is_point: bool,
|
pub is_point: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ProjectileOwned {
|
||||||
|
/// TODO: use SmallVec for these effects
|
||||||
|
pub hit_solid: Vec<Effect>,
|
||||||
|
pub hit_entity: Vec<Effect>,
|
||||||
|
/// Time left until the projectile will despawn
|
||||||
|
///
|
||||||
|
/// TODO: Remove this, we can calculate this from an initial time which we
|
||||||
|
/// can store in the regular component.
|
||||||
|
pub time_left: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for Projectile {
|
impl Component for Projectile {
|
||||||
type Storage = specs::DenseVecStorage<Self>;
|
type Storage = specs::DenseVecStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Component for ProjectileOwned {
|
||||||
|
type Storage = specs::DenseVecStorage<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum ProjectileConstructor {
|
pub enum ProjectileConstructor {
|
||||||
Arrow {
|
Arrow {
|
||||||
@ -103,7 +114,7 @@ impl ProjectileConstructor {
|
|||||||
crit_chance: f32,
|
crit_chance: f32,
|
||||||
crit_mult: f32,
|
crit_mult: f32,
|
||||||
buff_strength: f32,
|
buff_strength: f32,
|
||||||
) -> Projectile {
|
) -> (ProjectileOwned, Projectile) {
|
||||||
let instance = rand::random();
|
let instance = rand::random();
|
||||||
use ProjectileConstructor::*;
|
use ProjectileConstructor::*;
|
||||||
match self {
|
match self {
|
||||||
@ -145,15 +156,19 @@ impl ProjectileConstructor {
|
|||||||
.with_effect(knockback)
|
.with_effect(knockback)
|
||||||
.with_combo_increment();
|
.with_combo_increment();
|
||||||
|
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Stick, Effect::Bonk],
|
hit_solid: vec![Effect::Stick, Effect::Bonk],
|
||||||
hit_entity: vec![Effect::Attack(attack), Effect::Vanish],
|
hit_entity: vec![Effect::Attack(attack), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(15),
|
time_left: Duration::from_secs(15),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
Fireball {
|
Fireball {
|
||||||
damage,
|
damage,
|
||||||
@ -193,15 +208,19 @@ impl ProjectileConstructor {
|
|||||||
reagent: Some(Reagent::Red),
|
reagent: Some(Reagent::Red),
|
||||||
min_falloff,
|
min_falloff,
|
||||||
};
|
};
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
Frostball {
|
Frostball {
|
||||||
damage,
|
damage,
|
||||||
@ -227,15 +246,19 @@ impl ProjectileConstructor {
|
|||||||
reagent: Some(Reagent::White),
|
reagent: Some(Reagent::White),
|
||||||
min_falloff,
|
min_falloff,
|
||||||
};
|
};
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
Poisonball {
|
Poisonball {
|
||||||
damage,
|
damage,
|
||||||
@ -274,15 +297,19 @@ impl ProjectileConstructor {
|
|||||||
reagent: Some(Reagent::Purple),
|
reagent: Some(Reagent::Purple),
|
||||||
min_falloff,
|
min_falloff,
|
||||||
};
|
};
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
NecroticSphere {
|
NecroticSphere {
|
||||||
damage,
|
damage,
|
||||||
@ -308,25 +335,33 @@ impl ProjectileConstructor {
|
|||||||
reagent: Some(Reagent::Purple),
|
reagent: Some(Reagent::Purple),
|
||||||
min_falloff,
|
min_falloff,
|
||||||
};
|
};
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Possess => Projectile {
|
)
|
||||||
|
},
|
||||||
|
Possess => (
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Stick],
|
hit_solid: vec![Effect::Stick],
|
||||||
hit_entity: vec![Effect::Stick, Effect::Possess],
|
hit_entity: vec![Effect::Stick, Effect::Possess],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: false,
|
ignore_group: false,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
},
|
},
|
||||||
|
),
|
||||||
ClayRocket {
|
ClayRocket {
|
||||||
damage,
|
damage,
|
||||||
radius,
|
radius,
|
||||||
@ -363,15 +398,19 @@ impl ProjectileConstructor {
|
|||||||
reagent: Some(Reagent::Red),
|
reagent: Some(Reagent::Red),
|
||||||
min_falloff,
|
min_falloff,
|
||||||
};
|
};
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
Snowball {
|
Snowball {
|
||||||
damage,
|
damage,
|
||||||
@ -396,15 +435,19 @@ impl ProjectileConstructor {
|
|||||||
reagent: Some(Reagent::White),
|
reagent: Some(Reagent::White),
|
||||||
min_falloff,
|
min_falloff,
|
||||||
};
|
};
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![],
|
hit_solid: vec![],
|
||||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(120),
|
time_left: Duration::from_secs(120),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: false,
|
is_sticky: false,
|
||||||
is_point: false,
|
is_point: false,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
ExplodingPumpkin {
|
ExplodingPumpkin {
|
||||||
damage,
|
damage,
|
||||||
@ -453,15 +496,19 @@ impl ProjectileConstructor {
|
|||||||
reagent: Some(Reagent::Red),
|
reagent: Some(Reagent::Red),
|
||||||
min_falloff,
|
min_falloff,
|
||||||
};
|
};
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
DagonBomb {
|
DagonBomb {
|
||||||
damage,
|
damage,
|
||||||
@ -510,15 +557,19 @@ impl ProjectileConstructor {
|
|||||||
reagent: Some(Reagent::Blue),
|
reagent: Some(Reagent::Blue),
|
||||||
min_falloff,
|
min_falloff,
|
||||||
};
|
};
|
||||||
Projectile {
|
(
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||||
time_left: Duration::from_secs(10),
|
time_left: Duration::from_secs(10),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner,
|
owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ pub enum ServerEvent {
|
|||||||
dir: Dir,
|
dir: Dir,
|
||||||
body: comp::Body,
|
body: comp::Body,
|
||||||
light: Option<comp::LightEmitter>,
|
light: Option<comp::LightEmitter>,
|
||||||
projectile: comp::Projectile,
|
projectile: (comp::ProjectileOwned, comp::Projectile),
|
||||||
speed: f32,
|
speed: f32,
|
||||||
object: Option<comp::Object>,
|
object: Option<comp::Object>,
|
||||||
},
|
},
|
||||||
@ -148,7 +148,7 @@ pub enum ServerEvent {
|
|||||||
anchor: Option<comp::Anchor>,
|
anchor: Option<comp::Anchor>,
|
||||||
loot: LootSpec<String>,
|
loot: LootSpec<String>,
|
||||||
rtsim_entity: Option<RtSimEntity>,
|
rtsim_entity: Option<RtSimEntity>,
|
||||||
projectile: Option<comp::Projectile>,
|
projectile: Option<(comp::ProjectileOwned, comp::Projectile)>,
|
||||||
},
|
},
|
||||||
CreateShip {
|
CreateShip {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
inventory::loadout_builder::{self, LoadoutBuilder},
|
inventory::loadout_builder::{self, LoadoutBuilder},
|
||||||
skillset::skills,
|
skillset::skills,
|
||||||
Behavior, BehaviorCapability, CharacterState, Projectile, StateUpdate,
|
Behavior, BehaviorCapability, CharacterState, Projectile, ProjectileOwned, StateUpdate,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -162,15 +162,17 @@ impl CharacterBehavior for Data {
|
|||||||
.0;
|
.0;
|
||||||
|
|
||||||
// If a duration is specified, create a projectile component for the npc
|
// If a duration is specified, create a projectile component for the npc
|
||||||
let projectile = self.static_data.duration.map(|duration| Projectile {
|
let projectile = self.static_data.duration.map(|duration| (ProjectileOwned {
|
||||||
hit_solid: Vec::new(),
|
hit_solid: Vec::new(),
|
||||||
hit_entity: Vec::new(),
|
hit_entity: Vec::new(),
|
||||||
time_left: duration,
|
time_left: duration,
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner: Some(*data.uid),
|
owner: Some(*data.uid),
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: false,
|
is_sticky: false,
|
||||||
is_point: false,
|
is_point: false,
|
||||||
});
|
}));
|
||||||
|
|
||||||
// Send server event to create npc
|
// Send server event to create npc
|
||||||
output_events.emit_server(ServerEvent::CreateNpc {
|
output_events.emit_server(ServerEvent::CreateNpc {
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
use core::sync::atomic::AtomicU32;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub type MapMut = dashmap::DashMap<Vec2<i32>, Vec<specs::Entity>>;
|
// NOTE: (Vec2<i32>, [specs::Entity; 6]) should fit in a cacheline, reducing false sharing if we
|
||||||
pub type MapRef = dashmap::ReadOnlyView<Vec2<i32>, Vec<specs::Entity>>;
|
// ever decide to directly update the SmallVecs.
|
||||||
|
type EntityVec = SmallVec<[specs::Entity; 6]>;
|
||||||
|
|
||||||
|
pub type MapMut = /*dashmap::DashMap*/hashbrown::HashMap<Vec2<i32>, EntityVec>;
|
||||||
|
pub type MapRef = /*dashmap::ReadOnlyView*/hashbrown::HashMap<Vec2<i32>, EntityVec>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SpatialGridInner<Map, Radius> {
|
pub struct SpatialGridInner<Map, Radius> {
|
||||||
@ -32,10 +37,10 @@ pub type SpatialGrid = SpatialGridInner<MapMut, AtomicU32>;
|
|||||||
pub type SpatialGridRef = SpatialGridInner<MapRef, u32>;
|
pub type SpatialGridRef = SpatialGridInner<MapRef, u32>;
|
||||||
|
|
||||||
impl SpatialGrid {
|
impl SpatialGrid {
|
||||||
pub fn new(lg2_cell_size: usize, lg2_large_cell_size: usize, radius_cutoff: u32) -> Self {
|
pub fn new(lg2_cell_size: usize, lg2_large_cell_size: usize, radius_cutoff: u32, (capacity, large_capacity): (usize, usize)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
grid: Default::default(),
|
grid: MapMut::with_capacity(capacity),
|
||||||
large_grid: Default::default(),
|
large_grid: MapMut::with_capacity(large_capacity),
|
||||||
lg2_cell_size,
|
lg2_cell_size,
|
||||||
lg2_large_cell_size,
|
lg2_large_cell_size,
|
||||||
radius_cutoff,
|
radius_cutoff,
|
||||||
@ -43,8 +48,12 @@ impl SpatialGrid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> (usize, usize) {
|
||||||
|
(self.grid.len(), self.large_grid.len())
|
||||||
|
}
|
||||||
|
|
||||||
/// Add an entity at the provided 2d pos into the spatial grid
|
/// Add an entity at the provided 2d pos into the spatial grid
|
||||||
pub fn insert(&self, pos: Vec2<i32>, radius: u32, entity: specs::Entity) {
|
pub fn insert(&mut self, pos: Vec2<i32>, radius: u32, entity: specs::Entity) {
|
||||||
if radius <= self.radius_cutoff {
|
if radius <= self.radius_cutoff {
|
||||||
let cell = pos.map(|e| e >> self.lg2_cell_size);
|
let cell = pos.map(|e| e >> self.lg2_cell_size);
|
||||||
self.grid.entry(cell).or_default().push(entity);
|
self.grid.entry(cell).or_default().push(entity);
|
||||||
@ -59,14 +68,16 @@ impl SpatialGrid {
|
|||||||
//
|
//
|
||||||
// TODO: Verify that intrinsics lower intelligently to a priority update on CPUs (since
|
// TODO: Verify that intrinsics lower intelligently to a priority update on CPUs (since
|
||||||
// the intrinsic seems targeted at GPUs).
|
// the intrinsic seems targeted at GPUs).
|
||||||
self.largest_large_radius.fetch_max(radius, Ordering::Relaxed);
|
/* self.largest_large_radius.fetch_max(radius, Ordering::Relaxed); */
|
||||||
|
let largest_radius = self.largest_large_radius.get_mut();
|
||||||
|
*largest_radius = (*largest_radius).max(radius);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_read_only(self) -> SpatialGridRef {
|
pub fn into_read_only(self) -> SpatialGridRef {
|
||||||
SpatialGridInner {
|
SpatialGridInner {
|
||||||
grid: self.grid.into_read_only(),
|
grid: self.grid/*.into_read_only()*/,
|
||||||
large_grid: self.large_grid.into_read_only(),
|
large_grid: self.large_grid/*.into_read_only()*/,
|
||||||
lg2_cell_size: self.lg2_cell_size,
|
lg2_cell_size: self.lg2_cell_size,
|
||||||
lg2_large_cell_size: self.lg2_large_cell_size,
|
lg2_large_cell_size: self.lg2_large_cell_size,
|
||||||
radius_cutoff: self.radius_cutoff,
|
radius_cutoff: self.radius_cutoff,
|
||||||
@ -136,8 +147,8 @@ impl SpatialGridRef {
|
|||||||
|
|
||||||
pub fn into_inner(self) -> SpatialGrid {
|
pub fn into_inner(self) -> SpatialGrid {
|
||||||
SpatialGridInner {
|
SpatialGridInner {
|
||||||
grid: self.grid.into_inner(),
|
grid: self.grid/*.into_inner()*/,
|
||||||
large_grid: self.large_grid.into_inner(),
|
large_grid: self.large_grid/*.into_inner()*/,
|
||||||
lg2_cell_size: self.lg2_cell_size,
|
lg2_cell_size: self.lg2_cell_size,
|
||||||
lg2_large_cell_size: self.lg2_large_cell_size,
|
lg2_large_cell_size: self.lg2_large_cell_size,
|
||||||
radius_cutoff: self.radius_cutoff,
|
radius_cutoff: self.radius_cutoff,
|
||||||
|
@ -214,6 +214,7 @@ impl State {
|
|||||||
ecs.register::<comp::Admin>();
|
ecs.register::<comp::Admin>();
|
||||||
ecs.register::<comp::Waypoint>();
|
ecs.register::<comp::Waypoint>();
|
||||||
ecs.register::<comp::MapMarker>();
|
ecs.register::<comp::MapMarker>();
|
||||||
|
ecs.register::<comp::ProjectileOwned>();
|
||||||
ecs.register::<comp::Projectile>();
|
ecs.register::<comp::Projectile>();
|
||||||
ecs.register::<comp::Melee>();
|
ecs.register::<comp::Melee>();
|
||||||
ecs.register::<comp::ItemDrop>();
|
ecs.register::<comp::ItemDrop>();
|
||||||
|
@ -20,10 +20,9 @@ use common::{
|
|||||||
use common_base::prof_span;
|
use common_base::prof_span;
|
||||||
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rayon::iter::ParallelIterator;
|
|
||||||
use specs::{
|
use specs::{
|
||||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity, Join, ParJoin, Read,
|
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity, Join, Read, ReadExpect,
|
||||||
ReadExpect, ReadStorage, SystemData, World, WriteStorage,
|
ReadStorage, SystemData, World, WriteStorage,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -71,20 +70,17 @@ impl<'a> System<'a> for Sys {
|
|||||||
// removes burning, but campfires don't have healths/stats/energies/buffs, so
|
// removes burning, but campfires don't have healths/stats/energies/buffs, so
|
||||||
// this needs a separate loop.
|
// this needs a separate loop.
|
||||||
job.cpu_stats.measure(ParMode::Rayon);
|
job.cpu_stats.measure(ParMode::Rayon);
|
||||||
let to_put_out_campfires = (
|
let light_emitters_mask = light_emitters.mask().clone();
|
||||||
|
prof_span!(guard_, "buff campfire deactivate");
|
||||||
|
(
|
||||||
&read_data.entities,
|
&read_data.entities,
|
||||||
&bodies,
|
&mut bodies,
|
||||||
&read_data.physics_states,
|
&read_data.physics_states,
|
||||||
&light_emitters, //to improve iteration speed
|
light_emitters_mask, //to improve iteration speed
|
||||||
)
|
)
|
||||||
.par_join()
|
.join()
|
||||||
.map_init(
|
.filter(|(_, body, physics_state, _)| {
|
||||||
|| {
|
matches!(&**body, Body::Object(object::Body::CampfireLit))
|
||||||
prof_span!(guard, "buff campfire deactivate");
|
|
||||||
guard
|
|
||||||
},
|
|
||||||
|_guard, (entity, body, physics_state, _)| {
|
|
||||||
if matches!(*body, Body::Object(object::Body::CampfireLit))
|
|
||||||
&& matches!(
|
&& matches!(
|
||||||
physics_state.in_fluid,
|
physics_state.in_fluid,
|
||||||
Some(Fluid::Liquid {
|
Some(Fluid::Liquid {
|
||||||
@ -92,38 +88,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
..
|
..
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
{
|
|
||||||
Some(entity)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.fold(Vec::new, |mut to_put_out_campfires, put_out_campfire| {
|
|
||||||
put_out_campfire.map(|put| to_put_out_campfires.push(put));
|
|
||||||
to_put_out_campfires
|
|
||||||
})
|
})
|
||||||
.reduce(
|
.for_each(|(e, mut body, _, _)| {
|
||||||
Vec::new,
|
*body = Body::Object(object::Body::Campfire);
|
||||||
|mut to_put_out_campfires_a, mut to_put_out_campfires_b| {
|
|
||||||
to_put_out_campfires_a.append(&mut to_put_out_campfires_b);
|
|
||||||
to_put_out_campfires_a
|
|
||||||
},
|
|
||||||
);
|
|
||||||
job.cpu_stats.measure(ParMode::Single);
|
|
||||||
{
|
|
||||||
prof_span!(_guard, "write deferred campfire deletion");
|
|
||||||
// Assume that to_put_out_campfires is near to zero always, so this access isn't
|
|
||||||
// slower than parallel checking above
|
|
||||||
for e in to_put_out_campfires {
|
|
||||||
{
|
|
||||||
bodies
|
|
||||||
.get_mut(e)
|
|
||||||
.map(|mut body| *body = Body::Object(object::Body::Campfire));
|
|
||||||
light_emitters.remove(e);
|
light_emitters.remove(e);
|
||||||
}
|
});
|
||||||
}
|
drop(guard_);
|
||||||
}
|
|
||||||
|
|
||||||
for (entity, mut buff_comp, mut stat, health, energy, physics_state) in (
|
for (entity, mut buff_comp, mut stat, health, energy, physics_state) in (
|
||||||
&read_data.entities,
|
&read_data.entities,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#![feature(btree_drain_filter)]
|
#![feature(let_chains, btree_drain_filter)]
|
||||||
#![allow(clippy::option_map_unit_fn)]
|
#![allow(clippy::option_map_unit_fn)]
|
||||||
|
|
||||||
mod aura;
|
mod aura;
|
||||||
|
@ -12,6 +12,7 @@ use common::{
|
|||||||
mounting::Rider,
|
mounting::Rider,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::DeltaTime,
|
resources::DeltaTime,
|
||||||
|
slowjob::SlowJobPool,
|
||||||
states,
|
states,
|
||||||
terrain::{Block, BlockKind, TerrainGrid},
|
terrain::{Block, BlockKind, TerrainGrid},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
@ -107,6 +108,7 @@ pub struct PhysicsRead<'a> {
|
|||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
terrain: ReadExpect<'a, TerrainGrid>,
|
terrain: ReadExpect<'a, TerrainGrid>,
|
||||||
|
slowjob: ReadExpect<'a, SlowJobPool>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
event_bus: Read<'a, EventBus<ServerEvent>>,
|
event_bus: Read<'a, EventBus<ServerEvent>>,
|
||||||
scales: ReadStorage<'a, Scale>,
|
scales: ReadStorage<'a, Scale>,
|
||||||
@ -121,6 +123,7 @@ pub struct PhysicsRead<'a> {
|
|||||||
character_states: ReadStorage<'a, CharacterState>,
|
character_states: ReadStorage<'a, CharacterState>,
|
||||||
densities: ReadStorage<'a, Density>,
|
densities: ReadStorage<'a, Density>,
|
||||||
stats: ReadStorage<'a, Stats>,
|
stats: ReadStorage<'a, Stats>,
|
||||||
|
outcomes: Read<'a, EventBus<Outcome>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
@ -133,7 +136,6 @@ pub struct PhysicsWrite<'a> {
|
|||||||
pos_vel_ori_defers: WriteStorage<'a, PosVelOriDefer>,
|
pos_vel_ori_defers: WriteStorage<'a, PosVelOriDefer>,
|
||||||
orientations: WriteStorage<'a, Ori>,
|
orientations: WriteStorage<'a, Ori>,
|
||||||
previous_phys_cache: WriteStorage<'a, PreviousPhysCache>,
|
previous_phys_cache: WriteStorage<'a, PreviousPhysCache>,
|
||||||
outcomes: Read<'a, EventBus<Outcome>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
@ -142,46 +144,53 @@ pub struct PhysicsData<'a> {
|
|||||||
write: PhysicsWrite<'a>,
|
write: PhysicsWrite<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PhysicsData<'a> {
|
impl<'a> PhysicsRead<'a> {
|
||||||
/// Add/reset physics state components
|
/// Add/reset physics state components
|
||||||
fn reset(&mut self) {
|
fn reset(
|
||||||
|
&self,
|
||||||
|
positions: &WriteStorage<'a, Pos>,
|
||||||
|
velocities: &WriteStorage<'a, Vel>,
|
||||||
|
orientations: &WriteStorage<'a, Ori>,
|
||||||
|
physics_states: &mut WriteStorage<'a, PhysicsState>,
|
||||||
|
) {
|
||||||
span!(_guard, "Add/reset physics state components");
|
span!(_guard, "Add/reset physics state components");
|
||||||
(
|
(
|
||||||
&self.read.entities,
|
&self.entities,
|
||||||
self.read.colliders.mask(),
|
self.colliders.mask(),
|
||||||
self.write.positions.mask(),
|
positions.mask(),
|
||||||
self.write.velocities.mask(),
|
velocities.mask(),
|
||||||
self.write.orientations.mask(),
|
orientations.mask(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.for_each(|(entity, _, _, _, _)| {
|
.for_each(|(entity, _, _, _, _)| {
|
||||||
let _ = self
|
let _ = physics_states
|
||||||
.write
|
|
||||||
.physics_states
|
|
||||||
.entry(entity)
|
.entry(entity)
|
||||||
.map(|e| e.or_insert_with(Default::default));
|
.map(|e| e.or_insert_with(Default::default));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maintain_pushback_cache(&mut self) {
|
fn maintain_pushback_cache(
|
||||||
|
&self,
|
||||||
|
positions: &WriteStorage<'a, Pos>,
|
||||||
|
velocities: &WriteStorage<'a, Vel>,
|
||||||
|
orientations: &WriteStorage<'a, Ori>,
|
||||||
|
previous_phys_cache: &mut WriteStorage<'a, PreviousPhysCache>,
|
||||||
|
) {
|
||||||
span!(_guard, "Maintain pushback cache");
|
span!(_guard, "Maintain pushback cache");
|
||||||
// Add PreviousPhysCache for all relevant entities
|
// Add PreviousPhysCache for all relevant entities
|
||||||
(
|
(
|
||||||
&self.read.entities,
|
&self.entities,
|
||||||
self.read.colliders.mask(),
|
self.colliders.mask(),
|
||||||
self.write.velocities.mask(),
|
velocities.mask(),
|
||||||
self.write.positions.mask(),
|
positions.mask(),
|
||||||
!self.write.previous_phys_cache.mask(),
|
!previous_phys_cache.mask(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.map(|(e, _, _, _, _)| e)
|
.map(|(e, _, _, _, _)| e)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.for_each(|entity| {
|
.for_each(|entity| {
|
||||||
let _ = self
|
let _ = previous_phys_cache.insert(entity, PreviousPhysCache {
|
||||||
.write
|
|
||||||
.previous_phys_cache
|
|
||||||
.insert(entity, PreviousPhysCache {
|
|
||||||
velocity_dt: Vec3::zero(),
|
velocity_dt: Vec3::zero(),
|
||||||
center: Vec3::zero(),
|
center: Vec3::zero(),
|
||||||
collision_boundary: 0.0,
|
collision_boundary: 0.0,
|
||||||
@ -197,13 +206,13 @@ impl<'a> PhysicsData<'a> {
|
|||||||
// Update PreviousPhysCache
|
// Update PreviousPhysCache
|
||||||
(
|
(
|
||||||
/* &self.read.entities, */
|
/* &self.read.entities, */
|
||||||
&self.write.velocities,
|
velocities,
|
||||||
&self.write.positions,
|
positions,
|
||||||
&self.write.orientations,
|
orientations,
|
||||||
&mut self.write.previous_phys_cache,
|
previous_phys_cache,
|
||||||
&self.read.colliders,
|
&self.colliders,
|
||||||
self.read.scales.maybe(),
|
self.scales.maybe(),
|
||||||
self.read.char_states.maybe(),
|
self.char_states.maybe(),
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.for_each(
|
.for_each(
|
||||||
@ -214,7 +223,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let (z_min, z_max) = (z_min * scale, z_max * scale);
|
let (z_min, z_max) = (z_min * scale, z_max * scale);
|
||||||
let half_height = (z_max - z_min) / 2.0;
|
let half_height = (z_max - z_min) / 2.0;
|
||||||
|
|
||||||
phys_cache.velocity_dt = vel.0 * self.read.dt.0;
|
phys_cache.velocity_dt = vel.0 * self.dt.0;
|
||||||
let entity_center = position.0 + Vec3::new(0.0, 0.0, z_min + half_height);
|
let entity_center = position.0 + Vec3::new(0.0, 0.0, z_min + half_height);
|
||||||
let flat_radius = collider.bounding_radius() * scale;
|
let flat_radius = collider.bounding_radius() * scale;
|
||||||
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
||||||
@ -279,12 +288,14 @@ impl<'a> PhysicsData<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_spatial_grid(&mut self) -> SpatialGrid {
|
fn construct_spatial_grid(
|
||||||
|
&self,
|
||||||
|
positions: &WriteStorage<'a, Pos>,
|
||||||
|
velocities: &WriteStorage<'a, Vel>,
|
||||||
|
capacity: (usize, usize),
|
||||||
|
/* previous_phys_cache: &WriteStorage<'a, PreviousPhysCache>, */
|
||||||
|
) -> SpatialGrid {
|
||||||
span!(_guard, "Construct spatial grid");
|
span!(_guard, "Construct spatial grid");
|
||||||
let PhysicsData {
|
|
||||||
ref read,
|
|
||||||
ref write,
|
|
||||||
} = self;
|
|
||||||
// NOTE: i32 places certain constraints on how far out collision works
|
// NOTE: i32 places certain constraints on how far out collision works
|
||||||
// NOTE: uses the radius of the entity and their current position rather than
|
// NOTE: uses the radius of the entity and their current position rather than
|
||||||
// the radius of their bounding sphere for the current frame of movement
|
// the radius of their bounding sphere for the current frame of movement
|
||||||
@ -298,19 +309,24 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let lg2_cell_size = 5;
|
let lg2_cell_size = 5;
|
||||||
let lg2_large_cell_size = 6;
|
let lg2_large_cell_size = 6;
|
||||||
let radius_cutoff = 8;
|
let radius_cutoff = 8;
|
||||||
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
|
let mut spatial_grid =
|
||||||
|
SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff, capacity);
|
||||||
(
|
(
|
||||||
&read.entities,
|
&self.entities,
|
||||||
&write.positions,
|
positions,
|
||||||
&write.previous_phys_cache,
|
/* previous_phys_cache, */
|
||||||
write.velocities.mask(),
|
velocities.mask(),
|
||||||
!read.projectiles.mask(), /* Not needed because they are skipped in the inner loop
|
&self.colliders,
|
||||||
|
self.scales.maybe(),
|
||||||
|
!self.projectiles.mask(), /* Not needed because they are skipped in the inner loop
|
||||||
* below */
|
* below */
|
||||||
)
|
)
|
||||||
.par_join()
|
./*par_join*/join()
|
||||||
.for_each(|(entity, pos, phys_cache, _, _)| {
|
.for_each(|(entity, pos, /*phys_cache, */_, collider, scale, _)| {
|
||||||
|
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||||
|
let scaled_radius = collider.bounding_radius() * scale;
|
||||||
// Note: to not get too fine grained we use a 2D grid for now
|
// Note: to not get too fine grained we use a 2D grid for now
|
||||||
let radius_2d = phys_cache.scaled_radius.ceil() as u32;
|
let radius_2d = /*phys_cache.*/scaled_radius.ceil() as u32;
|
||||||
let pos_2d = pos.0.xy().map(|e| e as i32);
|
let pos_2d = pos.0.xy().map(|e| e as i32);
|
||||||
const POS_TRUNCATION_ERROR: u32 = 1;
|
const POS_TRUNCATION_ERROR: u32 = 1;
|
||||||
spatial_grid.insert(pos_2d, radius_2d + POS_TRUNCATION_ERROR, entity);
|
spatial_grid.insert(pos_2d, radius_2d + POS_TRUNCATION_ERROR, entity);
|
||||||
@ -319,30 +335,34 @@ impl<'a> PhysicsData<'a> {
|
|||||||
spatial_grid
|
spatial_grid
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_pushback(&mut self, job: &mut Job<Sys>, spatial_grid: &SpatialGridRef) {
|
fn apply_pushback(
|
||||||
|
&self,
|
||||||
|
job: &mut Job<Sys>,
|
||||||
|
spatial_grid: &SpatialGridRef,
|
||||||
|
physics_metrics: &mut WriteExpect<'a, PhysicsMetrics>,
|
||||||
|
physics_states: &mut WriteStorage<'a, PhysicsState>,
|
||||||
|
positions: &WriteStorage<'a, Pos>,
|
||||||
|
velocities: &mut WriteStorage<'a, Vel>,
|
||||||
|
previous_phys_cache: &WriteStorage<'a, PreviousPhysCache>,
|
||||||
|
) {
|
||||||
span!(_guard, "Apply pushback");
|
span!(_guard, "Apply pushback");
|
||||||
job.cpu_stats.measure(ParMode::Rayon);
|
job.cpu_stats.measure(ParMode::Rayon);
|
||||||
let PhysicsData {
|
|
||||||
ref read,
|
|
||||||
ref mut write,
|
|
||||||
} = self;
|
|
||||||
let (positions, previous_phys_cache) = (&write.positions, &write.previous_phys_cache);
|
|
||||||
let metrics = (
|
let metrics = (
|
||||||
&read.entities,
|
&self.entities,
|
||||||
positions,
|
positions,
|
||||||
&mut write.velocities,
|
velocities,
|
||||||
previous_phys_cache,
|
previous_phys_cache,
|
||||||
&read.masses,
|
&self.masses,
|
||||||
&read.colliders,
|
&self.colliders,
|
||||||
read.is_ridings.maybe(),
|
self.is_ridings.maybe(),
|
||||||
read.stickies.maybe(),
|
self.stickies.maybe(),
|
||||||
read.immovables.maybe(),
|
self.immovables.maybe(),
|
||||||
&mut write.physics_states,
|
physics_states,
|
||||||
// TODO: if we need to avoid collisions for other things consider
|
// TODO: if we need to avoid collisions for other things consider
|
||||||
// moving whether it should interact into the collider component
|
// moving whether it should interact into the collider component
|
||||||
// or into a separate component.
|
// or into a separate component.
|
||||||
read.projectiles.maybe(),
|
self.projectiles.maybe(),
|
||||||
read.char_states.maybe(),
|
self.char_states.maybe(),
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.map_init(
|
.map_init(
|
||||||
@ -397,11 +417,11 @@ impl<'a> PhysicsData<'a> {
|
|||||||
spatial_grid
|
spatial_grid
|
||||||
.in_circle_aabr(query_center, query_radius)
|
.in_circle_aabr(query_center, query_radius)
|
||||||
.filter_map(|entity| {
|
.filter_map(|entity| {
|
||||||
let uid = read.uids.get(entity)?;
|
let uid = self.uids.get(entity)?;
|
||||||
let pos = positions.get(entity)?;
|
let pos = positions.get(entity)?;
|
||||||
let previous_cache = previous_phys_cache.get(entity)?;
|
let previous_cache = previous_phys_cache.get(entity)?;
|
||||||
let mass = read.masses.get(entity)?;
|
let mass = self.masses.get(entity)?;
|
||||||
let collider = read.colliders.get(entity)?;
|
let collider = self.colliders.get(entity)?;
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
entity,
|
entity,
|
||||||
@ -410,8 +430,8 @@ impl<'a> PhysicsData<'a> {
|
|||||||
previous_cache,
|
previous_cache,
|
||||||
mass,
|
mass,
|
||||||
collider,
|
collider,
|
||||||
read.char_states.get(entity),
|
self.char_states.get(entity),
|
||||||
read.is_ridings.get(entity),
|
self.is_ridings.get(entity),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.for_each(
|
.for_each(
|
||||||
@ -498,7 +518,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Change velocity
|
// Change velocity
|
||||||
vel.0 += vel_delta * read.dt.0;
|
vel.0 += vel_delta * self.dt.0;
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
PhysicsMetrics {
|
PhysicsMetrics {
|
||||||
@ -513,18 +533,17 @@ impl<'a> PhysicsData<'a> {
|
|||||||
entity_entity_collisions: old.entity_entity_collisions
|
entity_entity_collisions: old.entity_entity_collisions
|
||||||
+ new.entity_entity_collisions,
|
+ new.entity_entity_collisions,
|
||||||
});
|
});
|
||||||
write.physics_metrics.entity_entity_collision_checks =
|
physics_metrics.entity_entity_collision_checks = metrics.entity_entity_collision_checks;
|
||||||
metrics.entity_entity_collision_checks;
|
physics_metrics.entity_entity_collisions = metrics.entity_entity_collisions;
|
||||||
write.physics_metrics.entity_entity_collisions = metrics.entity_entity_collisions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_voxel_collider_spatial_grid(&mut self) -> SpatialGrid {
|
fn construct_voxel_collider_spatial_grid(
|
||||||
|
&self,
|
||||||
|
positions: &WriteStorage<'a, Pos>,
|
||||||
|
orientations: &WriteStorage<'a, Ori>,
|
||||||
|
capacity: (usize, usize),
|
||||||
|
) -> SpatialGrid {
|
||||||
span!(_guard, "Construct voxel collider spatial grid");
|
span!(_guard, "Construct voxel collider spatial grid");
|
||||||
let PhysicsData {
|
|
||||||
ref read,
|
|
||||||
ref write,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
||||||
|
|
||||||
// NOTE: i32 places certain constraints on how far out collision works
|
// NOTE: i32 places certain constraints on how far out collision works
|
||||||
@ -536,15 +555,16 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let lg2_cell_size = 7; // 128
|
let lg2_cell_size = 7; // 128
|
||||||
let lg2_large_cell_size = 8; // 256
|
let lg2_large_cell_size = 8; // 256
|
||||||
let radius_cutoff = 64;
|
let radius_cutoff = 64;
|
||||||
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
|
let mut spatial_grid =
|
||||||
|
SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff, capacity);
|
||||||
// TODO: give voxel colliders their own component type
|
// TODO: give voxel colliders their own component type
|
||||||
(
|
(
|
||||||
&read.entities,
|
&self.entities,
|
||||||
&write.positions,
|
positions,
|
||||||
&read.colliders,
|
&self.colliders,
|
||||||
&write.orientations,
|
orientations,
|
||||||
)
|
)
|
||||||
.par_join()
|
./*par_join*/join()
|
||||||
.for_each(|(entity, pos, collider, ori)| {
|
.for_each(|(entity, pos, collider, ori)| {
|
||||||
let vol = match collider {
|
let vol = match collider {
|
||||||
Collider::Voxel { id } => voxel_colliders_manifest.colliders.get(id),
|
Collider::Voxel { id } => voxel_colliders_manifest.colliders.get(id),
|
||||||
@ -563,7 +583,9 @@ impl<'a> PhysicsData<'a> {
|
|||||||
|
|
||||||
spatial_grid
|
spatial_grid
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PhysicsData<'a> {
|
||||||
fn handle_movement_and_terrain(
|
fn handle_movement_and_terrain(
|
||||||
&mut self,
|
&mut self,
|
||||||
job: &mut Job<Sys>,
|
job: &mut Job<Sys>,
|
||||||
@ -707,7 +729,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
&mut write.previous_phys_cache,
|
&mut write.previous_phys_cache,
|
||||||
read.colliders.mask(),
|
read.colliders.mask(),
|
||||||
)
|
)
|
||||||
.par_join()
|
.join()
|
||||||
.for_each(|(pos, ori, previous_phys_cache, _)| {
|
.for_each(|(pos, ori, previous_phys_cache, _)| {
|
||||||
// Note: updating ori with the rest of the cache values above was attempted but
|
// Note: updating ori with the rest of the cache values above was attempted but
|
||||||
// it did not work (investigate root cause?)
|
// it did not work (investigate root cause?)
|
||||||
@ -746,6 +768,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
&mut write.pos_vel_ori_defers,
|
&mut write.pos_vel_ori_defers,
|
||||||
previous_phys_cache,
|
previous_phys_cache,
|
||||||
!&read.is_ridings,
|
!&read.is_ridings,
|
||||||
|
read.projectiles.maybe(),
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.filter(|tuple| tuple.3.is_voxel() == terrain_like_entities)
|
.filter(|tuple| tuple.3.is_voxel() == terrain_like_entities)
|
||||||
@ -769,6 +792,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
pos_vel_ori_defer,
|
pos_vel_ori_defer,
|
||||||
previous_cache,
|
previous_cache,
|
||||||
_,
|
_,
|
||||||
|
projectile,
|
||||||
)| {
|
)| {
|
||||||
let mut land_on_ground = None;
|
let mut land_on_ground = None;
|
||||||
let mut outcomes = Vec::new();
|
let mut outcomes = Vec::new();
|
||||||
@ -931,11 +955,9 @@ impl<'a> PhysicsData<'a> {
|
|||||||
|
|
||||||
// TODO: Not all projectiles should count as sticky!
|
// TODO: Not all projectiles should count as sticky!
|
||||||
if sticky.is_some() {
|
if sticky.is_some() {
|
||||||
if let Some((projectile, body)) = read
|
if let Some((projectile, body)) = projectile
|
||||||
.projectiles
|
|
||||||
.get(entity)
|
|
||||||
.filter(|_| vel.0.magnitude_squared() > 1.0 && block.is_some())
|
.filter(|_| vel.0.magnitude_squared() > 1.0 && block.is_some())
|
||||||
.zip(read.bodies.get(entity).copied())
|
.zip(body.copied())
|
||||||
{
|
{
|
||||||
outcomes.push(Outcome::ProjectileHit {
|
outcomes.push(Outcome::ProjectileHit {
|
||||||
pos: pos.0 + pos_delta * dist,
|
pos: pos.0 + pos_delta * dist,
|
||||||
@ -1212,6 +1234,11 @@ impl<'a> PhysicsData<'a> {
|
|||||||
}
|
}
|
||||||
if vel != old_vel {
|
if vel != old_vel {
|
||||||
pos_vel_ori_defer.vel = Some(vel);
|
pos_vel_ori_defer.vel = Some(vel);
|
||||||
|
// Moving this logic here instead of the projectile system allows it to
|
||||||
|
// avoid writing to ori.
|
||||||
|
if projectile.is_some() && let Some(dir) = Ori::from_unnormalized_vec(vel.0) {
|
||||||
|
ori = dir;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pos_vel_ori_defer.vel = None;
|
pos_vel_ori_defer.vel = None;
|
||||||
}
|
}
|
||||||
@ -1244,7 +1271,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
drop(guard);
|
drop(guard);
|
||||||
job.cpu_stats.measure(ParMode::Single);
|
job.cpu_stats.measure(ParMode::Single);
|
||||||
|
|
||||||
write.outcomes.emitter().emit_many(outcomes);
|
read.outcomes.emitter().emit_many(outcomes);
|
||||||
|
|
||||||
prof_span!(guard, "write deferred pos and vel");
|
prof_span!(guard, "write deferred pos and vel");
|
||||||
(
|
(
|
||||||
@ -1255,7 +1282,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
&mut write.pos_vel_ori_defers,
|
&mut write.pos_vel_ori_defers,
|
||||||
&read.colliders,
|
&read.colliders,
|
||||||
)
|
)
|
||||||
.par_join()
|
.join()
|
||||||
.filter(|tuple| tuple./*5*/4.is_voxel() == terrain_like_entities)
|
.filter(|tuple| tuple./*5*/4.is_voxel() == terrain_like_entities)
|
||||||
.for_each(|(/* _, */ pos, vel, ori, pos_vel_ori_defer, _)| {
|
.for_each(|(/* _, */ pos, vel, ori, pos_vel_ori_defer, _)| {
|
||||||
if let Some(new_pos) = pos_vel_ori_defer.pos.take() {
|
if let Some(new_pos) = pos_vel_ori_defer.pos.take() {
|
||||||
@ -1276,26 +1303,20 @@ impl<'a> PhysicsData<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_cached_spatial_grid(&mut self) {
|
fn update_cached_spatial_grid(&mut self, mut spatial_grid: SpatialGrid) {
|
||||||
span!(_guard, "Update cached spatial grid");
|
span!(_guard, "Update cached spatial grid");
|
||||||
let PhysicsData {
|
let PhysicsData {
|
||||||
ref read,
|
ref read,
|
||||||
ref mut write,
|
ref mut write,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Borrow checker dance, since transferring away from a read only view requires
|
|
||||||
// &mut self.
|
|
||||||
let mut spatial_grid = core::mem::take(&mut *write.cached_spatial_grid)
|
|
||||||
.0
|
|
||||||
.into_inner();
|
|
||||||
spatial_grid.clear();
|
|
||||||
(
|
(
|
||||||
&read.entities,
|
&read.entities,
|
||||||
&write.positions,
|
&write.positions,
|
||||||
read.scales.maybe(),
|
read.scales.maybe(),
|
||||||
read.colliders.maybe(),
|
read.colliders.maybe(),
|
||||||
)
|
)
|
||||||
.par_join()
|
.join()
|
||||||
.for_each(|(entity, pos, scale, collider)| {
|
.for_each(|(entity, pos, scale, collider)| {
|
||||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||||
let radius_2d =
|
let radius_2d =
|
||||||
@ -1317,33 +1338,112 @@ impl<'a> System<'a> for Sys {
|
|||||||
const PHASE: Phase = Phase::Create;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(job: &mut Job<Self>, mut physics_data: Self::SystemData) {
|
fn run(job: &mut Job<Self>, mut physics_data: Self::SystemData) {
|
||||||
physics_data.reset();
|
// Borrow checker dance, since transferring away from a read only view requires
|
||||||
|
// &mut self.
|
||||||
|
let mut cached_spatial_grid = core::mem::take(&mut *physics_data.write.cached_spatial_grid)
|
||||||
|
.0
|
||||||
|
.into_inner();
|
||||||
|
// Initialize grids to twice the cached grid's capacity, since entity
|
||||||
|
// distribution will rarely change much from tick to tick.
|
||||||
|
let grid_capacity = cached_spatial_grid.len();
|
||||||
|
let grid_capacity = (
|
||||||
|
grid_capacity.0.saturating_mul(2),
|
||||||
|
grid_capacity.1.saturating_mul(2),
|
||||||
|
);
|
||||||
|
rayon::join(
|
||||||
|
|| {
|
||||||
|
let PhysicsData {
|
||||||
|
ref read,
|
||||||
|
ref mut write,
|
||||||
|
} = physics_data;
|
||||||
|
|
||||||
|
let (spatial_grid, voxel_collider_spatial_grid) = rayon::join(
|
||||||
|
|| {
|
||||||
|
let (spatial_grid, ()) = rayon::join(
|
||||||
|
|| {
|
||||||
|
read.construct_spatial_grid(
|
||||||
|
&write.positions,
|
||||||
|
&write.velocities,
|
||||||
|
grid_capacity,
|
||||||
|
)
|
||||||
|
.into_read_only()
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
rayon::join(
|
||||||
|
|| {
|
||||||
|
read.reset(
|
||||||
|
&write.positions,
|
||||||
|
&write.velocities,
|
||||||
|
&write.orientations,
|
||||||
|
&mut write.physics_states,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
read.maintain_pushback_cache(
|
||||||
|
&write.positions,
|
||||||
|
&write.velocities,
|
||||||
|
&write.orientations,
|
||||||
|
&mut write.previous_phys_cache,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Apply pushback
|
// Apply pushback
|
||||||
//
|
//
|
||||||
// Note: We now do this first because we project velocity ahead. This is slighty
|
// Note: We now do this first because we project velocity ahead. This is
|
||||||
// imperfect and implies that we might get edge-cases where entities
|
// slighty imperfect and implies that we might get
|
||||||
// standing right next to the edge of a wall may get hit by projectiles
|
// edge-cases where entities standing right next to
|
||||||
|
// the edge of a wall may get hit by projectiles
|
||||||
// fired into the wall very close to them. However, this sort of thing is
|
// fired into the wall very close to them. However, this sort of thing is
|
||||||
// already possible with poorly-defined hitboxes anyway so it's not too
|
// already possible with poorly-defined hitboxes anyway so it's not too
|
||||||
// much of a concern.
|
// much of a concern.
|
||||||
//
|
//
|
||||||
// If this situation becomes a problem, this code should be integrated with the
|
// If this situation becomes a problem, this code should be integrated with
|
||||||
// terrain collision code below, although that's not trivial to do since
|
// the terrain collision code below, although that's
|
||||||
// it means the step needs to take into account the speeds of both
|
// not trivial to do since it means the step needs
|
||||||
// entities.
|
// to take into account the speeds of both entities.
|
||||||
physics_data.maintain_pushback_cache();
|
read.apply_pushback(
|
||||||
|
job,
|
||||||
|
&spatial_grid,
|
||||||
|
&mut write.physics_metrics,
|
||||||
|
&mut write.physics_states,
|
||||||
|
&write.positions,
|
||||||
|
&mut write.velocities,
|
||||||
|
&write.previous_phys_cache,
|
||||||
|
);
|
||||||
|
spatial_grid
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
read.construct_voxel_collider_spatial_grid(
|
||||||
|
&write.positions,
|
||||||
|
&write.orientations,
|
||||||
|
// Almost certainly overkill since most chunks won't contain a
|
||||||
|
// collider, but probably not worth altering.
|
||||||
|
grid_capacity,
|
||||||
|
)
|
||||||
|
.into_read_only()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let spatial_grid = physics_data.construct_spatial_grid().into_read_only();
|
|
||||||
physics_data.apply_pushback(job, &spatial_grid);
|
|
||||||
|
|
||||||
let voxel_collider_spatial_grid = physics_data
|
|
||||||
.construct_voxel_collider_spatial_grid()
|
|
||||||
.into_read_only();
|
|
||||||
physics_data.handle_movement_and_terrain(job, &voxel_collider_spatial_grid);
|
physics_data.handle_movement_and_terrain(job, &voxel_collider_spatial_grid);
|
||||||
|
|
||||||
|
physics_data.read.slowjob.spawn("CHUNK_DROP", move || {
|
||||||
|
drop(spatial_grid);
|
||||||
|
drop(voxel_collider_spatial_grid);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
cached_spatial_grid.clear();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Spatial grid used by other systems
|
// Spatial grid used by other systems
|
||||||
physics_data.update_cached_spatial_grid();
|
//
|
||||||
|
// TODO: Consider inserting only the difference so we can update in parallel,
|
||||||
|
// rather than clearing and reinserting everything.
|
||||||
|
physics_data.update_cached_spatial_grid(cached_spatial_grid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,20 +3,21 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
projectile, Alignment, Body, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
projectile, Alignment, Body, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
||||||
PhysicsState, Player, Pos, Projectile, Stats, Vel,
|
PhysicsState, Player, Pos, Projectile, ProjectileOwned, Stats, Vel,
|
||||||
},
|
},
|
||||||
event::{Emitter, EventBus, ServerEvent},
|
event::{Emitter, EventBus, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
util::Dir,
|
|
||||||
GroupTarget,
|
GroupTarget,
|
||||||
};
|
};
|
||||||
|
use common_base::prof_span;
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
use rayon::iter::ParallelIterator;
|
||||||
use specs::{
|
use specs::{
|
||||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity as EcsEntity, Join, Read,
|
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity as EcsEntity, Join, ParJoin,
|
||||||
ReadStorage, SystemData, World, WriteStorage,
|
Read, ReadStorage, SystemData, World, WriteStorage,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -25,6 +26,8 @@ use vek::*;
|
|||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
|
projectiles: ReadStorage<'a, Projectile>,
|
||||||
|
orientations: ReadStorage<'a, Ori>,
|
||||||
players: ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
uid_allocator: Read<'a, UidAllocator>,
|
uid_allocator: Read<'a, UidAllocator>,
|
||||||
@ -50,8 +53,7 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadData<'a>,
|
ReadData<'a>,
|
||||||
WriteStorage<'a, Ori>,
|
WriteStorage<'a, ProjectileOwned>,
|
||||||
WriteStorage<'a, Projectile>,
|
|
||||||
Read<'a, EventBus<Outcome>>,
|
Read<'a, EventBus<Outcome>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -59,23 +61,26 @@ impl<'a> System<'a> for Sys {
|
|||||||
const ORIGIN: Origin = Origin::Common;
|
const ORIGIN: Origin = Origin::Common;
|
||||||
const PHASE: Phase = Phase::Create;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(
|
fn run(_job: &mut Job<Self>, (read_data, mut projectiles, outcomes): Self::SystemData) {
|
||||||
_job: &mut Job<Self>,
|
|
||||||
(read_data, mut orientations, mut projectiles, outcomes): Self::SystemData,
|
|
||||||
) {
|
|
||||||
let mut server_emitter = read_data.server_bus.emitter();
|
|
||||||
let mut outcomes_emitter = outcomes.emitter();
|
|
||||||
|
|
||||||
// Attacks
|
// Attacks
|
||||||
'projectile_loop: for (entity, pos, physics, vel, mut projectile) in (
|
(
|
||||||
&read_data.entities,
|
&read_data.entities,
|
||||||
&read_data.positions,
|
&read_data.positions,
|
||||||
&read_data.physics_states,
|
&read_data.physics_states,
|
||||||
&read_data.velocities,
|
&read_data.velocities,
|
||||||
|
&read_data.projectiles,
|
||||||
|
// TODO: Investigate whether the `maybe` are actually necessary here.
|
||||||
|
(&read_data.orientations).maybe(),
|
||||||
|
(&read_data.bodies).maybe(),
|
||||||
&mut projectiles,
|
&mut projectiles,
|
||||||
)
|
)
|
||||||
.join()
|
.par_join()
|
||||||
{
|
.for_each_init(
|
||||||
|
|| {
|
||||||
|
prof_span!(guard, "projectile rayon job");
|
||||||
|
(read_data.server_bus.emitter(), outcomes.emitter(), guard)
|
||||||
|
},
|
||||||
|
|(server_emitter, outcomes_emitter, _guard), (entity, pos, physics, vel, projectile, ori, body, projectile_write)| {
|
||||||
let projectile_owner = projectile
|
let projectile_owner = projectile
|
||||||
.owner
|
.owner
|
||||||
.and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()));
|
.and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()));
|
||||||
@ -117,19 +122,21 @@ impl<'a> System<'a> for Sys {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let projectile = &mut *projectile;
|
let projectile_write = &mut *projectile_write;
|
||||||
|
|
||||||
let entity_of =
|
let entity_of =
|
||||||
|uid: Uid| read_data.uid_allocator.retrieve_entity_internal(uid.into());
|
|uid: Uid| read_data.uid_allocator.retrieve_entity_internal(uid.into());
|
||||||
for effect in projectile.hit_entity.drain(..) {
|
for effect in projectile_write.hit_entity.drain(..) {
|
||||||
let owner = projectile.owner.and_then(entity_of);
|
let owner = projectile.owner.and_then(entity_of);
|
||||||
let projectile_info = ProjectileInfo {
|
let projectile_info = ProjectileInfo {
|
||||||
entity,
|
entity,
|
||||||
effect,
|
effect,
|
||||||
owner_uid: projectile.owner,
|
owner_uid: projectile.owner,
|
||||||
owner,
|
owner,
|
||||||
ori: orientations.get(entity),
|
ori,
|
||||||
pos,
|
pos,
|
||||||
|
vel,
|
||||||
|
body,
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = entity_of(other);
|
let target = entity_of(other);
|
||||||
@ -137,7 +144,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
uid: other,
|
uid: other,
|
||||||
entity: target,
|
entity: target,
|
||||||
target_group,
|
target_group,
|
||||||
ori: target.and_then(|target| orientations.get(target)),
|
ori: target.and_then(|target| read_data.orientations.get(target)),
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch_hit(
|
dispatch_hit(
|
||||||
@ -145,19 +152,19 @@ impl<'a> System<'a> for Sys {
|
|||||||
projectile_target_info,
|
projectile_target_info,
|
||||||
&read_data,
|
&read_data,
|
||||||
&mut projectile_vanished,
|
&mut projectile_vanished,
|
||||||
&mut outcomes_emitter,
|
outcomes_emitter,
|
||||||
&mut server_emitter,
|
server_emitter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if projectile_vanished {
|
if projectile_vanished {
|
||||||
continue 'projectile_loop;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if physics.on_surface().is_some() {
|
if physics.on_surface().is_some() {
|
||||||
let projectile = &mut *projectile;
|
let projectile_write = &mut *projectile_write;
|
||||||
for effect in projectile.hit_solid.drain(..) {
|
for effect in projectile_write.hit_solid.drain(..) {
|
||||||
match effect {
|
match effect {
|
||||||
projectile::Effect::Explode(e) => {
|
projectile::Effect::Explode(e) => {
|
||||||
// We offset position a little back on the way,
|
// We offset position a little back on the way,
|
||||||
@ -167,8 +174,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
// TODO: orientation of fallen projectile is
|
// TODO: orientation of fallen projectile is
|
||||||
// fragile heuristic for direction, find more
|
// fragile heuristic for direction, find more
|
||||||
// robust method.
|
// robust method.
|
||||||
let projectile_direction = orientations
|
let projectile_direction = ori
|
||||||
.get(entity)
|
|
||||||
.map_or_else(Vec3::zero, |ori| ori.look_vec());
|
.map_or_else(Vec3::zero, |ori| ori.look_vec());
|
||||||
let offset = -0.2 * projectile_direction;
|
let offset = -0.2 * projectile_direction;
|
||||||
server_emitter.emit(ServerEvent::Explosion {
|
server_emitter.emit(ServerEvent::Explosion {
|
||||||
@ -193,22 +199,18 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if projectile_vanished {
|
if projectile_vanished {
|
||||||
continue 'projectile_loop;
|
return;
|
||||||
}
|
|
||||||
} else if let Some(ori) = orientations.get_mut(entity) {
|
|
||||||
if let Some(dir) = Dir::from_unnormalized(vel.0) {
|
|
||||||
*ori = dir.into();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if projectile.time_left == Duration::default() {
|
if projectile_write.time_left == Duration::default() {
|
||||||
server_emitter.emit(ServerEvent::Delete(entity));
|
server_emitter.emit(ServerEvent::Delete(entity));
|
||||||
}
|
}
|
||||||
projectile.time_left = projectile
|
projectile_write.time_left = projectile_write
|
||||||
.time_left
|
.time_left
|
||||||
.checked_sub(Duration::from_secs_f32(read_data.dt.0))
|
.checked_sub(Duration::from_secs_f32(read_data.dt.0))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +220,9 @@ struct ProjectileInfo<'a> {
|
|||||||
owner_uid: Option<Uid>,
|
owner_uid: Option<Uid>,
|
||||||
owner: Option<EcsEntity>,
|
owner: Option<EcsEntity>,
|
||||||
ori: Option<&'a Ori>,
|
ori: Option<&'a Ori>,
|
||||||
|
body: Option<&'a Body>,
|
||||||
pos: &'a Pos,
|
pos: &'a Pos,
|
||||||
|
vel: &'a Vel,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProjectileTargetInfo<'a> {
|
struct ProjectileTargetInfo<'a> {
|
||||||
@ -258,7 +262,6 @@ fn dispatch_hit(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let owner = projectile_info.owner;
|
let owner = projectile_info.owner;
|
||||||
let projectile_entity = projectile_info.entity;
|
|
||||||
|
|
||||||
let attacker_info =
|
let attacker_info =
|
||||||
owner
|
owner
|
||||||
@ -285,14 +288,11 @@ fn dispatch_hit(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Is it possible to have projectile without body??
|
// TODO: Is it possible to have projectile without body??
|
||||||
if let Some(&body) = read_data.bodies.get(projectile_entity) {
|
if let Some(&body) = projectile_info.body {
|
||||||
outcomes_emitter.emit(Outcome::ProjectileHit {
|
outcomes_emitter.emit(Outcome::ProjectileHit {
|
||||||
pos: target_pos,
|
pos: target_pos,
|
||||||
body,
|
body,
|
||||||
vel: read_data
|
vel: projectile_info.vel.0,
|
||||||
.velocities
|
|
||||||
.get(projectile_entity)
|
|
||||||
.map_or(Vec3::zero(), |v| v.0),
|
|
||||||
source: projectile_info.owner_uid,
|
source: projectile_info.owner_uid,
|
||||||
target: read_data.uids.get(target).copied(),
|
target: read_data.uids.get(target).copied(),
|
||||||
});
|
});
|
||||||
|
@ -8,8 +8,8 @@ use common::{
|
|||||||
beam,
|
beam,
|
||||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||||
shockwave, Agent, Alignment, Anchor, Body, Health, Inventory, ItemDrop, LightEmitter,
|
shockwave, Agent, Alignment, Anchor, Body, Health, Inventory, ItemDrop, LightEmitter,
|
||||||
Object, Ori, PidController, Poise, Pos, Projectile, Scale, SkillSet, Stats, Vel,
|
Object, Ori, PidController, Poise, Pos, Projectile, ProjectileOwned, Scale, SkillSet,
|
||||||
WaypointArea,
|
Stats, Vel, WaypointArea,
|
||||||
},
|
},
|
||||||
event::{EventBus, UpdateCharacterMetadata},
|
event::{EventBus, UpdateCharacterMetadata},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
@ -93,7 +93,7 @@ pub fn handle_create_npc(
|
|||||||
loot: LootSpec<String>,
|
loot: LootSpec<String>,
|
||||||
home_chunk: Option<Anchor>,
|
home_chunk: Option<Anchor>,
|
||||||
rtsim_entity: Option<RtSimEntity>,
|
rtsim_entity: Option<RtSimEntity>,
|
||||||
projectile: Option<Projectile>,
|
projectile: Option<(ProjectileOwned, Projectile)>,
|
||||||
) {
|
) {
|
||||||
let entity = server
|
let entity = server
|
||||||
.state
|
.state
|
||||||
@ -125,8 +125,8 @@ pub fn handle_create_npc(
|
|||||||
entity
|
entity
|
||||||
};
|
};
|
||||||
|
|
||||||
let entity = if let Some(projectile) = projectile {
|
let entity = if let Some((projectile_owned, projectile)) = projectile {
|
||||||
entity.with(projectile)
|
entity.with(projectile_owned).with(projectile)
|
||||||
} else {
|
} else {
|
||||||
entity
|
entity
|
||||||
};
|
};
|
||||||
@ -207,7 +207,7 @@ pub fn handle_shoot(
|
|||||||
dir: Dir,
|
dir: Dir,
|
||||||
body: Body,
|
body: Body,
|
||||||
light: Option<LightEmitter>,
|
light: Option<LightEmitter>,
|
||||||
projectile: Projectile,
|
projectile: (ProjectileOwned, Projectile),
|
||||||
speed: f32,
|
speed: f32,
|
||||||
object: Option<Object>,
|
object: Option<Object>,
|
||||||
) {
|
) {
|
||||||
|
@ -72,7 +72,7 @@ pub trait StateExt {
|
|||||||
pos: comp::Pos,
|
pos: comp::Pos,
|
||||||
vel: comp::Vel,
|
vel: comp::Vel,
|
||||||
body: comp::Body,
|
body: comp::Body,
|
||||||
projectile: comp::Projectile,
|
projectile: (comp::ProjectileOwned, comp::Projectile),
|
||||||
) -> EcsEntityBuilder;
|
) -> EcsEntityBuilder;
|
||||||
/// Build a shockwave entity
|
/// Build a shockwave entity
|
||||||
fn create_shockwave(
|
fn create_shockwave(
|
||||||
@ -343,7 +343,7 @@ impl StateExt for State {
|
|||||||
pos: comp::Pos,
|
pos: comp::Pos,
|
||||||
vel: comp::Vel,
|
vel: comp::Vel,
|
||||||
body: comp::Body,
|
body: comp::Body,
|
||||||
projectile: comp::Projectile,
|
(projectile_owned, projectile): (comp::ProjectileOwned, comp::Projectile),
|
||||||
) -> EcsEntityBuilder {
|
) -> EcsEntityBuilder {
|
||||||
let mut projectile_base = self
|
let mut projectile_base = self
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
@ -363,7 +363,10 @@ impl StateExt for State {
|
|||||||
projectile_base = projectile_base.with(body.collider())
|
projectile_base = projectile_base.with(body.collider())
|
||||||
}
|
}
|
||||||
|
|
||||||
projectile_base.with(projectile).with(body)
|
projectile_base
|
||||||
|
.with(projectile_owned)
|
||||||
|
.with(projectile)
|
||||||
|
.with(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_shockwave(
|
fn create_shockwave(
|
||||||
|
@ -71,7 +71,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
const ENABLE_RECURSIVE_FIREWORKS: bool = true;
|
const ENABLE_RECURSIVE_FIREWORKS: bool = true;
|
||||||
if ENABLE_RECURSIVE_FIREWORKS {
|
if ENABLE_RECURSIVE_FIREWORKS {
|
||||||
use common::{
|
use common::{
|
||||||
comp::{object, Body, LightEmitter, Projectile},
|
comp::{object, Body, LightEmitter, Projectile, ProjectileOwned},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@ -121,15 +121,19 @@ impl<'a> System<'a> for Sys {
|
|||||||
strength: 2.0,
|
strength: 2.0,
|
||||||
col: Rgb::new(1.0, 1.0, 0.0),
|
col: Rgb::new(1.0, 1.0, 0.0),
|
||||||
}),
|
}),
|
||||||
projectile: Projectile {
|
projectile: (
|
||||||
|
ProjectileOwned {
|
||||||
hit_solid: Vec::new(),
|
hit_solid: Vec::new(),
|
||||||
hit_entity: Vec::new(),
|
hit_entity: Vec::new(),
|
||||||
time_left: Duration::from_secs(60),
|
time_left: Duration::from_secs(60),
|
||||||
|
},
|
||||||
|
Projectile {
|
||||||
owner: *owner,
|
owner: *owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
is_sticky: true,
|
is_sticky: true,
|
||||||
is_point: true,
|
is_point: true,
|
||||||
},
|
},
|
||||||
|
),
|
||||||
speed,
|
speed,
|
||||||
object: Some(Object::Firework {
|
object: Some(Object::Firework {
|
||||||
owner: *owner,
|
owner: *owner,
|
||||||
|
Loading…
Reference in New Issue
Block a user