diff --git a/.cargo/config b/.cargo/config index 686f74a4b0..f85f7ca37c 100644 --- a/.cargo/config +++ b/.cargo/config @@ -10,6 +10,7 @@ csv-import = "run --manifest-path common/Cargo.toml --features=bin_csv --bin csv test-server = "run --bin veloren-server-cli --no-default-features" tracy-server = "-Zunstable-options run --bin veloren-server-cli --no-default-features --features tracy,simd --profile no_overflow" tracy-world-server = "-Zunstable-options run --bin veloren-server-cli --features tracy,simd --profile no_overflow" +tracy-world-server-releasedebuginfo = "-Zunstable-options run --bin veloren-server-cli --features tracy,simd --profile releasedebuginfo" test-voxygen = "run --bin veloren-voxygen --no-default-features --features simd,egui-ui" tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,simd,egui-ui --profile no_overflow" server = "run --bin veloren-server-cli" diff --git a/.gitignore b/.gitignore index 5621834e78..0c3b49c28a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,13 @@ maps screenshots todo.txt userdata + +# Profiling and traces heaptrack.* +cargo-timing* +flamegraph.* +perf.* +wgpu-trace/ # allow asset hud settings !assets/voxygen/i18n/*/hud/settings.ron diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 1feb6ed264..d34b4624e3 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -15,7 +15,7 @@ use crate::{ use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage, VecStorage}; use specs_idvs::IdvStorage; -use std::collections::{BTreeMap, VecDeque}; +use std::collections::BTreeMap; use strum_macros::Display; use vek::*; @@ -31,8 +31,21 @@ pub struct StateUpdate { pub should_strafe: bool, pub queued_inputs: BTreeMap, pub removed_inputs: Vec, - pub local_events: VecDeque, - pub server_events: VecDeque, +} + +pub struct OutputEvents<'a> { + local: &'a mut Vec, + server: &'a mut Vec, +} + +impl<'a> OutputEvents<'a> { + pub fn new(local: &'a mut Vec, server: &'a mut Vec) -> Self { + Self { local, server } + } + + pub fn emit_local(&mut self, event: LocalEvent) { self.local.push(event); } + + pub fn emit_server(&mut self, event: ServerEvent) { self.server.push(event); } } impl From<&JoinData<'_>> for StateUpdate { @@ -48,8 +61,6 @@ impl From<&JoinData<'_>> for StateUpdate { character: data.character.clone(), queued_inputs: BTreeMap::new(), removed_inputs: Vec::new(), - local_events: VecDeque::new(), - server_events: VecDeque::new(), } } } @@ -245,81 +256,96 @@ impl CharacterState { std::mem::discriminant(self) == std::mem::discriminant(other) } - pub fn behavior(&self, j: &JoinData) -> StateUpdate { + pub fn behavior(&self, j: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { match &self { - CharacterState::Idle => states::idle::Data.behavior(j), - CharacterState::Talk => states::talk::Data.behavior(j), - CharacterState::Climb(data) => data.behavior(j), - CharacterState::Glide(data) => data.behavior(j), - CharacterState::GlideWield(data) => data.behavior(j), - CharacterState::Stunned(data) => data.behavior(j), - CharacterState::Sit => states::sit::Data::behavior(&states::sit::Data, j), - CharacterState::Dance => states::dance::Data::behavior(&states::dance::Data, j), - CharacterState::Sneak => states::sneak::Data::behavior(&states::sneak::Data, j), - CharacterState::BasicBlock(data) => data.behavior(j), - CharacterState::Roll(data) => data.behavior(j), - CharacterState::Wielding => states::wielding::Data.behavior(j), - CharacterState::Equipping(data) => data.behavior(j), - CharacterState::ComboMelee(data) => data.behavior(j), - CharacterState::BasicMelee(data) => data.behavior(j), - CharacterState::BasicRanged(data) => data.behavior(j), - CharacterState::Boost(data) => data.behavior(j), - CharacterState::DashMelee(data) => data.behavior(j), - CharacterState::LeapMelee(data) => data.behavior(j), - CharacterState::SpinMelee(data) => data.behavior(j), - CharacterState::ChargedMelee(data) => data.behavior(j), - CharacterState::ChargedRanged(data) => data.behavior(j), - CharacterState::RepeaterRanged(data) => data.behavior(j), - CharacterState::Shockwave(data) => data.behavior(j), - CharacterState::BasicBeam(data) => data.behavior(j), - CharacterState::BasicAura(data) => data.behavior(j), - CharacterState::Blink(data) => data.behavior(j), - CharacterState::BasicSummon(data) => data.behavior(j), - CharacterState::SelfBuff(data) => data.behavior(j), - CharacterState::SpriteSummon(data) => data.behavior(j), - CharacterState::UseItem(data) => data.behavior(j), - CharacterState::SpriteInteract(data) => data.behavior(j), + CharacterState::Idle => states::idle::Data.behavior(j, output_events), + CharacterState::Talk => states::talk::Data.behavior(j, output_events), + CharacterState::Climb(data) => data.behavior(j, output_events), + CharacterState::Glide(data) => data.behavior(j, output_events), + CharacterState::GlideWield(data) => data.behavior(j, output_events), + CharacterState::Stunned(data) => data.behavior(j, output_events), + CharacterState::Sit => { + states::sit::Data::behavior(&states::sit::Data, j, output_events) + }, + CharacterState::Dance => { + states::dance::Data::behavior(&states::dance::Data, j, output_events) + }, + CharacterState::Sneak => { + states::sneak::Data::behavior(&states::sneak::Data, j, output_events) + }, + CharacterState::BasicBlock(data) => data.behavior(j, output_events), + CharacterState::Roll(data) => data.behavior(j, output_events), + CharacterState::Wielding => states::wielding::Data.behavior(j, output_events), + CharacterState::Equipping(data) => data.behavior(j, output_events), + CharacterState::ComboMelee(data) => data.behavior(j, output_events), + CharacterState::BasicMelee(data) => data.behavior(j, output_events), + CharacterState::BasicRanged(data) => data.behavior(j, output_events), + CharacterState::Boost(data) => data.behavior(j, output_events), + CharacterState::DashMelee(data) => data.behavior(j, output_events), + CharacterState::LeapMelee(data) => data.behavior(j, output_events), + CharacterState::SpinMelee(data) => data.behavior(j, output_events), + CharacterState::ChargedMelee(data) => data.behavior(j, output_events), + CharacterState::ChargedRanged(data) => data.behavior(j, output_events), + CharacterState::RepeaterRanged(data) => data.behavior(j, output_events), + CharacterState::Shockwave(data) => data.behavior(j, output_events), + CharacterState::BasicBeam(data) => data.behavior(j, output_events), + CharacterState::BasicAura(data) => data.behavior(j, output_events), + CharacterState::Blink(data) => data.behavior(j, output_events), + CharacterState::BasicSummon(data) => data.behavior(j, output_events), + CharacterState::SelfBuff(data) => data.behavior(j, output_events), + CharacterState::SpriteSummon(data) => data.behavior(j, output_events), + CharacterState::UseItem(data) => data.behavior(j, output_events), + CharacterState::SpriteInteract(data) => data.behavior(j, output_events), } } - pub fn handle_event(&self, j: &JoinData, action: ControlAction) -> StateUpdate { + pub fn handle_event( + &self, + j: &JoinData, + output_events: &mut OutputEvents, + action: ControlAction, + ) -> StateUpdate { match &self { - CharacterState::Idle => states::idle::Data.handle_event(j, action), - CharacterState::Talk => states::talk::Data.handle_event(j, action), - CharacterState::Climb(data) => data.handle_event(j, action), - CharacterState::Glide(data) => data.handle_event(j, action), - CharacterState::GlideWield(data) => data.handle_event(j, action), - CharacterState::Stunned(data) => data.handle_event(j, action), - CharacterState::Sit => states::sit::Data::handle_event(&states::sit::Data, j, action), + CharacterState::Idle => states::idle::Data.handle_event(j, output_events, action), + CharacterState::Talk => states::talk::Data.handle_event(j, output_events, action), + CharacterState::Climb(data) => data.handle_event(j, output_events, action), + CharacterState::Glide(data) => data.handle_event(j, output_events, action), + CharacterState::GlideWield(data) => data.handle_event(j, output_events, action), + CharacterState::Stunned(data) => data.handle_event(j, output_events, action), + CharacterState::Sit => { + states::sit::Data::handle_event(&states::sit::Data, j, output_events, action) + }, CharacterState::Dance => { - states::dance::Data::handle_event(&states::dance::Data, j, action) + states::dance::Data::handle_event(&states::dance::Data, j, output_events, action) }, CharacterState::Sneak => { - states::sneak::Data::handle_event(&states::sneak::Data, j, action) + states::sneak::Data::handle_event(&states::sneak::Data, j, output_events, action) }, - CharacterState::BasicBlock(data) => data.handle_event(j, action), - CharacterState::Roll(data) => data.handle_event(j, action), - CharacterState::Wielding => states::wielding::Data.handle_event(j, action), - CharacterState::Equipping(data) => data.handle_event(j, action), - CharacterState::ComboMelee(data) => data.handle_event(j, action), - CharacterState::BasicMelee(data) => data.handle_event(j, action), - CharacterState::BasicRanged(data) => data.handle_event(j, action), - CharacterState::Boost(data) => data.handle_event(j, action), - CharacterState::DashMelee(data) => data.handle_event(j, action), - CharacterState::LeapMelee(data) => data.handle_event(j, action), - CharacterState::SpinMelee(data) => data.handle_event(j, action), - CharacterState::ChargedMelee(data) => data.handle_event(j, action), - CharacterState::ChargedRanged(data) => data.handle_event(j, action), - CharacterState::RepeaterRanged(data) => data.handle_event(j, action), - CharacterState::Shockwave(data) => data.handle_event(j, action), - CharacterState::BasicBeam(data) => data.handle_event(j, action), - CharacterState::BasicAura(data) => data.handle_event(j, action), - CharacterState::Blink(data) => data.handle_event(j, action), - CharacterState::BasicSummon(data) => data.handle_event(j, action), - CharacterState::SelfBuff(data) => data.handle_event(j, action), - CharacterState::SpriteSummon(data) => data.handle_event(j, action), - CharacterState::UseItem(data) => data.handle_event(j, action), - CharacterState::SpriteInteract(data) => data.handle_event(j, action), + CharacterState::BasicBlock(data) => data.handle_event(j, output_events, action), + CharacterState::Roll(data) => data.handle_event(j, output_events, action), + CharacterState::Wielding => { + states::wielding::Data.handle_event(j, output_events, action) + }, + CharacterState::Equipping(data) => data.handle_event(j, output_events, action), + CharacterState::ComboMelee(data) => data.handle_event(j, output_events, action), + CharacterState::BasicMelee(data) => data.handle_event(j, output_events, action), + CharacterState::BasicRanged(data) => data.handle_event(j, output_events, action), + CharacterState::Boost(data) => data.handle_event(j, output_events, action), + CharacterState::DashMelee(data) => data.handle_event(j, output_events, action), + CharacterState::LeapMelee(data) => data.handle_event(j, output_events, action), + CharacterState::SpinMelee(data) => data.handle_event(j, output_events, action), + CharacterState::ChargedMelee(data) => data.handle_event(j, output_events, action), + CharacterState::ChargedRanged(data) => data.handle_event(j, output_events, action), + CharacterState::RepeaterRanged(data) => data.handle_event(j, output_events, action), + CharacterState::Shockwave(data) => data.handle_event(j, output_events, action), + CharacterState::BasicBeam(data) => data.handle_event(j, output_events, action), + CharacterState::BasicAura(data) => data.handle_event(j, output_events, action), + CharacterState::Blink(data) => data.handle_event(j, output_events, action), + CharacterState::BasicSummon(data) => data.handle_event(j, output_events, action), + CharacterState::SelfBuff(data) => data.handle_event(j, output_events, action), + CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action), + CharacterState::UseItem(data) => data.handle_event(j, output_events, action), + CharacterState::SpriteInteract(data) => data.handle_event(j, output_events, action), } } } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 12575c6d71..677b5c17e2 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -8,7 +8,7 @@ pub mod anchor; #[cfg(not(target_arch = "wasm32"))] pub mod body; pub mod buff; #[cfg(not(target_arch = "wasm32"))] -mod character_state; +pub mod character_state; #[cfg(not(target_arch = "wasm32"))] pub mod chat; #[cfg(not(target_arch = "wasm32"))] pub mod combo; pub mod compass; diff --git a/common/src/comp/ori.rs b/common/src/comp/ori.rs index 5531c94829..5c04d27c23 100644 --- a/common/src/comp/ori.rs +++ b/common/src/comp/ori.rs @@ -1,8 +1,8 @@ use crate::util::{Dir, Plane, Projection}; +use core::f32::consts::{FRAC_PI_2, PI, TAU}; use serde::{Deserialize, Serialize}; use specs::Component; use specs_idvs::IdvStorage; -use std::f32::consts::PI; use vek::{Quaternion, Vec2, Vec3}; // Orientation @@ -149,25 +149,59 @@ impl Ori { } pub fn to_horizontal(self) -> Self { - let fw = self.look_dir(); - Dir::from_unnormalized(fw.xy().into()) - .or_else(|| { - // if look_dir is straight down, pitch up, or if straight up, pitch down - Dir::from_unnormalized( - if fw.dot(Vec3::unit_z()) < 0.0 { - self.up() - } else { - self.down() - } - .xy() - .into(), - ) - }) - .map(|dir| dir.into()) - .expect( - "If the horizontal component of a Dir can not be normalized, the horizontal \ - component of a Dir perpendicular to it must be", - ) + // We don't use Self::look_dir to avoid the extra normalization step within + // Dir's Quaternion Mul impl + let fw = self.to_quat() * Dir::default().to_vec(); + // Check that dir is not straight up/down + // Uses a multiple of EPSILON to be safe + // We can just check z since beyond floating point errors `fw` should be + // normalized + if 1.0 - fw.z.abs() > f32::EPSILON * 4.0 { + // We know direction lies in the xy plane so we only need to compute a rotation + // about the z-axis + let Vec2 { x, y } = fw.xy().normalized(); + // Negate x and swap coords since we want to compute the angle from y+ + let quat = rotation_2d(Vec2::new(y, -x), Vec3::unit_z()); + + Self(quat) + } else { + // if the direction is straight down, pitch up, or if straight up, pitch down + if fw.z < 0.0 { + self.pitched_up(FRAC_PI_2) + } else { + self.pitched_down(FRAC_PI_2) + } + // TODO: test this alternative for speed and correctness compared to + // current impl + // + // removes a branch + // + // use core::f32::consts::FRAC_1_SQRT_2; + // let cos = FRAC_1_SQRT_2; + // let sin = -FRAC_1_SQRT_2 * fw.z.signum(); + // let axis = Vec3::unit_x(); + // let scalar = cos; + // let vector = sin * axis; + // Self((self.0 * Quaternion::from_scalar_and_vec3((scalar, + // vector))).normalized()) + } + } + + /// Find the angle between two `Ori`s + /// + /// NOTE: This finds the angle of the quaternion between the two `Ori`s + /// which can involve rolling and thus can be larger than simply the + /// angle between vectors at the start and end points. + /// + /// Returns angle in radians + pub fn angle_between(self, other: Self) -> f32 { + // Compute quaternion from one ori to the other + // https://www.mathworks.com/matlabcentral/answers/476474-how-to-find-the-angle-between-two-quaternions#answer_387973 + let between = self.to_quat().conjugate() * other.to_quat(); + // Then compute it's angle + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/ + let angle = 2.0 * between.w.acos(); + if angle < PI { angle } else { TAU - angle } } pub fn pitched_up(self, angle_radians: f32) -> Self { @@ -239,38 +273,68 @@ impl Ori { /// let ang2 = pd_rr.up().angle_between(zenith); /// assert!((ang1 - ang2).abs() <= std::f32::EPSILON); /// ``` - pub fn uprighted(self) -> Self { - let fw = self.look_dir(); - match Dir::up().projected(&Plane::from(fw)) { - Some(dir_p) => { - let up = self.up(); - let go_right_s = fw.cross(*up).dot(*dir_p).signum(); - self.rolled_right(up.angle_between(*dir_p) * go_right_s) - }, - None => self, - } - } + pub fn uprighted(self) -> Self { self.look_dir().into() } fn is_normalized(&self) -> bool { self.0.into_vec4().is_normalized() } } +/// Produce a quaternion from an axis to rotate about and a 2D point on the unit +/// circle to rotate to +/// +/// NOTE: the provided axis and 2D vector must be normalized +fn rotation_2d(Vec2 { x, y }: Vec2, axis: Vec3) -> Quaternion { + // Skip needing the angle for quaternion construction by computing cos/sin + // directly from the normalized x value + // + // scalar = cos(theta / 2) + // vector = axis * sin(theta / 2) + // + // cos(a / 2) = +/- ((1 + cos(a)) / 2)^0.5 + // sin(a / 2) = +/- ((1 - cos(a)) / 2)^0.5 + // + // scalar = +/- sqrt((1 + cos(a)) / 2) + // vector = vec3(0, 0, 1) * +/- sqrt((1 - cos(a)) / 2) + // + // cos(a) = x / |xy| => x (when normalized) + let scalar = ((1.0 + x) / 2.0).sqrt() * y.signum(); + let vector = axis * ((1.0 - x) / 2.0).sqrt(); + + // This is normalized by our construction above + Quaternion::from_scalar_and_vec3((scalar, vector)) +} + impl From for Ori { fn from(dir: Dir) -> Self { - let from = Dir::default(); - let q = Quaternion::::rotation_from_to_3d(*from, *dir).normalized(); + // Check that dir is not straight up/down + // Uses a multiple of EPSILON to be safe + let quat = if 1.0 - dir.z.abs() > f32::EPSILON * 4.0 { + // Compute rotation that will give an "upright" orientation (no + // rolling): + let xy_len = dir.xy().magnitude(); + let xy_norm = dir.xy() / xy_len; + // Rotation to get to this projected point from the default direction of y+ + // Negate x and swap coords since we want to compute the angle from y+ + let yaw = rotation_2d(Vec2::new(xy_norm.y, -xy_norm.x), Vec3::unit_z()); + // Rotation to then rotate up/down to the match the input direction + // In this rotated space the xy_len becomes the distance along the x axis + // And since we rotated around the z-axis the z value is unchanged + let pitch = rotation_2d(Vec2::new(xy_len, dir.z), Vec3::unit_x()); - Self(q).uprighted() + (yaw * pitch).normalized() + } else { + // Nothing in particular can be considered upright if facing up or down + // so we just produce a quaternion that will rotate to that direction + // (once again rotating from y+) + let pitch = PI / 2.0 * dir.z.signum(); + Quaternion::rotation_x(pitch) + }; + + Self(quat) } } impl From> for Ori { - fn from(dir: Vec3) -> Self { - let dir = Dir::from_unnormalized(dir).unwrap_or_default(); - let from = Dir::default(); - let q = Quaternion::::rotation_from_to_3d(*from, *dir).normalized(); - - Self(q).uprighted() - } + fn from(dir: Vec3) -> Self { Dir::from_unnormalized(dir).unwrap_or_default().into() } } impl From> for Ori { @@ -354,32 +418,83 @@ impl Component for Ori { mod tests { use super::*; + // Helper method to produce Dirs at different angles to test + fn dirs() -> impl Iterator { + let angles = 32; + (0..angles).flat_map(move |i| { + let theta = PI * 2.0 * (i as f32) / (angles as f32); + + let v = Vec3::unit_y(); + let q = Quaternion::rotation_x(theta); + let dir_1 = Dir::new(q * v); + + let v = Vec3::unit_z(); + let q = Quaternion::rotation_y(theta); + let dir_2 = Dir::new(q * v); + + let v = Vec3::unit_x(); + let q = Quaternion::rotation_z(theta); + let dir_3 = Dir::new(q * v); + + [dir_1, dir_2, dir_3] + }) + } + + #[test] + fn to_horizontal() { + let to_horizontal = |dir: Dir| { + let ori = Ori::from(dir); + + let horizontal = ori.to_horizontal(); + + approx::assert_relative_eq!(horizontal.look_dir().xy().magnitude(), 1.0); + approx::assert_relative_eq!(horizontal.look_dir().z, 0.0); + // Check correctness by comparing with Dir::to_horizontal + if let Some(dir_h) = ori.look_dir().to_horizontal() { + let quat_correct = Quaternion::::rotation_from_to_3d(Dir::default(), dir_h); + #[rustfmt::skip] + assert!( + dir_h + .map2(*horizontal.look_dir(), |d, o| approx::relative_eq!(d, o, epsilon = f32::EPSILON * 4.0)) + .reduce_and(), + "\n\ + Original: {:?}\n\ + Dir::to_horizontal: {:?}\n\ + Ori::to_horizontal(as dir): {:?}\n\ + Ori::to_horizontal(as quat): {:?}\n\ + Correct quaternion {:?}", + ori.look_dir(), + dir_h, + horizontal.look_dir(), + horizontal, + quat_correct, + ); + } + }; + + dirs().for_each(to_horizontal); + } + #[test] fn from_to_dir() { let from_to = |dir: Dir| { let ori = Ori::from(dir); - approx::assert_relative_eq!(ori.look_dir().dot(*dir), 1.0); + assert!(ori.is_normalized(), "ori {:?}\ndir {:?}", ori, dir); + assert!( + approx::relative_eq!(ori.look_dir().dot(*dir), 1.0), + "Ori::from(dir).look_dir() != dir\ndir: {:?}\nOri::from(dir).look_dir(): {:?}", + dir, + ori.look_dir(), + ); approx::assert_relative_eq!((ori.to_quat() * Dir::default()).dot(*dir), 1.0); }; - let angles = 32; - for i in 0..angles { - let theta = PI * 2. * (i as f32) / (angles as f32); - let v = Vec3::unit_y(); - let q = Quaternion::rotation_x(theta); - from_to(Dir::new(q * v)); - let v = Vec3::unit_z(); - let q = Quaternion::rotation_y(theta); - from_to(Dir::new(q * v)); - let v = Vec3::unit_x(); - let q = Quaternion::rotation_z(theta); - from_to(Dir::new(q * v)); - } + dirs().for_each(from_to); } #[test] - fn dirs() { + fn orthogonal_dirs() { let ori = Ori::default(); let def = Dir::default(); for dir in &[ori.up(), ori.down(), ori.left(), ori.right()] { diff --git a/common/src/event.rs b/common/src/event.rs index 4c7d1bc896..0c551ed39d 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -242,6 +242,9 @@ impl<'a, E> Emitter<'a, E> { pub fn emit(&mut self, event: E) { self.events.push_back(event); } pub fn append(&mut self, other: &mut VecDeque) { self.events.append(other) } + + // TODO: allow just emitting the whole vec of events at once? without copying + pub fn append_vec(&mut self, vec: Vec) { self.events.extend(vec) } } impl<'a, E> Drop for Emitter<'a, E> { diff --git a/common/src/lib.rs b/common/src/lib.rs index a337c57b88..01cd3e8dde 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -10,7 +10,8 @@ label_break_value, option_zip, trait_alias, - type_alias_impl_trait + type_alias_impl_trait, + extend_one )] /// Re-exported crates diff --git a/common/src/states/basic_aura.rs b/common/src/states/basic_aura.rs index 0c2f084c67..2ee79957b7 100644 --- a/common/src/states/basic_aura.rs +++ b/common/src/states/basic_aura.rs @@ -2,6 +2,7 @@ use crate::{ combat::GroupTarget, comp::{ aura::{AuraBuffConstructor, AuraChange, AuraKind, AuraTarget, Specifier}, + character_state::OutputEvents, CharacterState, StateUpdate, }, event::ServerEvent, @@ -52,12 +53,12 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.8); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { StageSection::Buildup => { @@ -89,12 +90,12 @@ impl CharacterBehavior for Data { 1.0 + (self.static_data.combo_at_cast.max(1) as f32).log(2.0); }, } - update.server_events.push_front(ServerEvent::ComboChange { + output_events.emit_server(ServerEvent::ComboChange { entity: data.entity, change: -(self.static_data.combo_at_cast as i32), }); } - update.server_events.push_front(ServerEvent::Aura { + output_events.emit_server(ServerEvent::Aura { entity: data.entity, aura_change: AuraChange::Add(aura), }); diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index deda921aa3..ffc7b4ad2e 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -3,7 +3,10 @@ use crate::{ Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, }, - comp::{beam, body::biped_large, Body, CharacterState, Ori, Pos, StateUpdate}, + comp::{ + beam, body::biped_large, character_state::OutputEvents, Body, CharacterState, Ori, Pos, + StateUpdate, + }, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -60,14 +63,14 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); let ori_rate = self.static_data.ori_rate; handle_orientation(data, &mut update, ori_rate, None); handle_move(data, &mut update, 0.4); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { StageSection::Buildup => { @@ -171,7 +174,7 @@ impl CharacterBehavior for Data { let pos = Pos(data.pos.0 + body_offsets); // Create beam segment - update.server_events.push_front(ServerEvent::BeamSegment { + output_events.emit_server(ServerEvent::BeamSegment { properties, pos, ori: beam_ori, diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index 273f9d8ab3..f0719ba15e 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{CharacterState, InputKind, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, InputKind, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; use serde::{Deserialize, Serialize}; @@ -35,7 +35,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 982a93f5d4..314bd4e1f7 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -3,7 +3,7 @@ use crate::{ Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, Knockback, }, - comp::{tool::ToolKind, CharacterState, Melee, StateUpdate}, + comp::{character_state::OutputEvents, tool::ToolKind, CharacterState, Melee, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -53,12 +53,12 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.7); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { StageSection::Buildup => { @@ -161,7 +161,7 @@ impl CharacterBehavior for Data { } else { // Done if input_is_pressed(data, self.static_data.ability_info.input) { - reset_state(self, data, &mut update); + reset_state(self, data, output_events, &mut update); } else { update.character = CharacterState::Wielding; } @@ -184,6 +184,16 @@ impl CharacterBehavior for Data { } } -fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) { - handle_input(join, update, data.static_data.ability_info.input); +fn reset_state( + data: &Data, + join: &JoinData, + output_events: &mut OutputEvents, + update: &mut StateUpdate, +) { + handle_input( + join, + output_events, + update, + data.static_data.ability_info.input, + ); } diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 2b487ba2ef..2e936d74f3 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -1,5 +1,8 @@ use crate::{ - comp::{Body, CharacterState, LightEmitter, Pos, ProjectileConstructor, StateUpdate}, + comp::{ + character_state::OutputEvents, Body, CharacterState, LightEmitter, Pos, + ProjectileConstructor, StateUpdate, + }, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -45,12 +48,12 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.3); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { StageSection::Buildup => { @@ -96,7 +99,7 @@ impl CharacterBehavior for Data { })) .unwrap_or(data.inputs.look_dir); // Tells server to create and shoot the projectile - update.server_events.push_front(ServerEvent::Shoot { + output_events.emit_server(ServerEvent::Shoot { entity: data.entity, pos, dir, @@ -121,7 +124,7 @@ impl CharacterBehavior for Data { } else { // Done if input_is_pressed(data, self.static_data.ability_info.input) { - reset_state(self, data, &mut update); + reset_state(self, data, output_events, &mut update); } else { update.character = CharacterState::Wielding; } @@ -142,6 +145,16 @@ impl CharacterBehavior for Data { } } -fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) { - handle_input(join, update, data.static_data.ability_info.input); +fn reset_state( + data: &Data, + join: &JoinData, + output_events: &mut OutputEvents, + update: &mut StateUpdate, +) { + handle_input( + join, + output_events, + update, + data.static_data.ability_info.input, + ); } diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 9ad22acf23..cec7765b68 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -1,6 +1,7 @@ use crate::{ comp::{ self, + character_state::OutputEvents, inventory::loadout_builder::{self, LoadoutBuilder}, Behavior, BehaviorCapability, CharacterState, Projectile, StateUpdate, }, @@ -54,7 +55,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); match self.stage_section { @@ -167,7 +168,7 @@ impl CharacterBehavior for Data { }); // Send server event to create npc - update.server_events.push_front(ServerEvent::CreateNpc { + output_events.emit_server(ServerEvent::CreateNpc { pos: comp::Pos(collision_vector - Vec3::unit_z() * obstacle_z), stats, skill_set, @@ -193,7 +194,7 @@ impl CharacterBehavior for Data { }); // Send local event used for frontend shenanigans - update.local_events.push_front(LocalEvent::CreateOutcome( + output_events.emit_local(LocalEvent::CreateOutcome( Outcome::SummonedCreature { pos: data.pos.0, body, diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index b54ff433e6..d98f1a0b84 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -1,8 +1,9 @@ use crate::{ comp::{ - self, item::MaterialStatManifest, Beam, Body, CharacterState, Combo, ControlAction, - Controller, ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory, - InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, SkillSet, StateUpdate, Stats, Vel, + self, character_state::OutputEvents, item::MaterialStatManifest, Beam, Body, + CharacterState, Combo, ControlAction, Controller, ControllerInputs, Density, Energy, + Health, InputAttr, InputKind, Inventory, InventoryAction, Mass, Melee, Ori, PhysicsState, + Pos, SkillSet, StateUpdate, Stats, Vel, }, resources::DeltaTime, terrain::TerrainGrid, @@ -12,20 +13,47 @@ use specs::{storage::FlaggedAccessMut, Entity, LazyUpdate}; use vek::*; pub trait CharacterBehavior { - fn behavior(&self, data: &JoinData) -> StateUpdate; + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate; // Impl these to provide behavior for these inputs - fn swap_equipped_weapons(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } - fn manipulate_loadout(&self, data: &JoinData, _inv_action: InventoryAction) -> StateUpdate { + fn swap_equipped_weapons( + &self, + data: &JoinData, + _output_events: &mut OutputEvents, + ) -> StateUpdate { + StateUpdate::from(data) + } + fn manipulate_loadout( + &self, + data: &JoinData, + _output_events: &mut OutputEvents, + _inv_action: InventoryAction, + ) -> StateUpdate { + StateUpdate::from(data) + } + fn wield(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { + StateUpdate::from(data) + } + fn glide_wield(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { + StateUpdate::from(data) + } + fn unwield(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { + StateUpdate::from(data) + } + fn sit(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { + StateUpdate::from(data) + } + fn dance(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { + StateUpdate::from(data) + } + fn sneak(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { + StateUpdate::from(data) + } + fn stand(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { + StateUpdate::from(data) + } + fn talk(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { StateUpdate::from(data) } - fn wield(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } - fn glide_wield(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } - fn unwield(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } - fn sit(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } - fn dance(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } - fn sneak(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } - fn stand(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } - fn talk(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) } fn start_input( &self, data: &JoinData, @@ -45,18 +73,25 @@ pub trait CharacterBehavior { update.removed_inputs.push(input); update } - fn handle_event(&self, data: &JoinData, event: ControlAction) -> StateUpdate { + fn handle_event( + &self, + data: &JoinData, + output_events: &mut OutputEvents, + event: ControlAction, + ) -> StateUpdate { match event { - ControlAction::SwapEquippedWeapons => self.swap_equipped_weapons(data), - ControlAction::InventoryAction(inv_action) => self.manipulate_loadout(data, inv_action), - ControlAction::Wield => self.wield(data), - ControlAction::GlideWield => self.glide_wield(data), - ControlAction::Unwield => self.unwield(data), - ControlAction::Sit => self.sit(data), - ControlAction::Dance => self.dance(data), - ControlAction::Sneak => self.sneak(data), - ControlAction::Stand => self.stand(data), - ControlAction::Talk => self.talk(data), + ControlAction::SwapEquippedWeapons => self.swap_equipped_weapons(data, output_events), + ControlAction::InventoryAction(inv_action) => { + self.manipulate_loadout(data, output_events, inv_action) + }, + ControlAction::Wield => self.wield(data, output_events), + ControlAction::GlideWield => self.glide_wield(data, output_events), + ControlAction::Unwield => self.unwield(data, output_events), + ControlAction::Sit => self.sit(data, output_events), + ControlAction::Dance => self.dance(data, output_events), + ControlAction::Sneak => self.sneak(data, output_events), + ControlAction::Stand => self.stand(data, output_events), + ControlAction::Talk => self.talk(data, output_events), ControlAction::StartInput { input, target_entity, diff --git a/common/src/states/blink.rs b/common/src/states/blink.rs index fae3bbef76..24623e0673 100644 --- a/common/src/states/blink.rs +++ b/common/src/states/blink.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, StateUpdate}, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -34,7 +34,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); @@ -52,7 +52,7 @@ impl CharacterBehavior for Data { // provided if let Some(input_attr) = self.static_data.ability_info.input_attr { if let Some(target) = input_attr.target_entity { - update.server_events.push_front(ServerEvent::TeleportTo { + output_events.emit_server(ServerEvent::TeleportTo { entity: data.entity, target, max_range: Some(self.static_data.max_range), diff --git a/common/src/states/boost.rs b/common/src/states/boost.rs index 9323cc2f3a..4e1ca96fac 100644 --- a/common/src/states/boost.rs +++ b/common/src/states/boost.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -28,7 +28,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_move(data, &mut update, 1.0); @@ -47,7 +47,7 @@ impl CharacterBehavior for Data { } else { // Done if input_is_pressed(data, self.static_data.ability_info.input) { - reset_state(self, data, &mut update); + reset_state(self, data, output_events, &mut update); } else { update.vel.0 = update.vel.0.try_normalized().unwrap_or_default() * update @@ -63,6 +63,16 @@ impl CharacterBehavior for Data { } } -fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) { - handle_input(join, update, data.static_data.ability_info.input); +fn reset_state( + data: &Data, + join: &JoinData, + output_events: &mut OutputEvents, + update: &mut StateUpdate, +) { + handle_input( + join, + output_events, + update, + data.static_data.ability_info.input, + ); } diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 5d3ca259e4..e4bc23107f 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -1,6 +1,6 @@ use crate::{ combat::{Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement}, - comp::{tool::ToolKind, CharacterState, Melee, StateUpdate}, + comp::{character_state::OutputEvents, tool::ToolKind, CharacterState, Melee, StateUpdate}, event::LocalEvent, outcome::Outcome, states::{ @@ -69,12 +69,12 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.7); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { StageSection::Charge => { @@ -190,13 +190,11 @@ impl CharacterBehavior for Data { if let Some(FrontendSpecifier::GroundCleave) = self.static_data.specifier { // Send local event used for frontend shenanigans - update.local_events.push_front(LocalEvent::CreateOutcome( - Outcome::GroundSlam { - pos: data.pos.0 - + *data.ori.look_dir() - * (data.body.max_radius() + self.static_data.range), - }, - )); + output_events.emit_local(LocalEvent::CreateOutcome(Outcome::GroundSlam { + pos: data.pos.0 + + *data.ori.look_dir() + * (data.body.max_radius() + self.static_data.range), + })); } } else if self.timer < self.static_data.swing_duration { // Swings diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index cbe3dfc482..9f5ca30b82 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -1,6 +1,7 @@ use crate::{ comp::{ - projectile::ProjectileConstructor, Body, CharacterState, LightEmitter, Pos, StateUpdate, + character_state::OutputEvents, projectile::ProjectileConstructor, Body, CharacterState, + LightEmitter, Pos, StateUpdate, }, event::ServerEvent, states::{ @@ -70,12 +71,12 @@ impl Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, self.static_data.move_speed); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { StageSection::Buildup => { @@ -118,7 +119,7 @@ impl CharacterBehavior for Data { crit_mult, buff_strength, ); - update.server_events.push_front(ServerEvent::Shoot { + output_events.emit_server(ServerEvent::Shoot { entity: data.entity, pos, dir: data.inputs.look_dir, diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 0ccd3bd106..4857e40242 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -1,5 +1,6 @@ use crate::{ comp::{ + character_state::OutputEvents, skills::{ClimbSkill::*, Skill, SKILL_MODIFIERS}, CharacterState, Climb, InputKind, Ori, StateUpdate, }, @@ -54,7 +55,7 @@ impl Default for Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // If no wall is in front of character or we stopped climbing; @@ -72,7 +73,7 @@ impl CharacterBehavior for Data { // How strong the climb boost is relative to a normal jump const CLIMB_BOOST_JUMP_FACTOR: f32 = 0.5; // They've climbed atop something, give them a boost - update.local_events.push_front(LocalEvent::Jump( + output_events.emit_local(LocalEvent::Jump( data.entity, CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0, )); diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index df55b12d89..17efb030b6 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -1,6 +1,7 @@ use crate::{ combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement}, comp::{ + character_state::OutputEvents, tool::{Stats, ToolKind}, CharacterState, Melee, StateUpdate, }, @@ -151,7 +152,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_move(data, &mut update, 0.4); @@ -340,7 +341,7 @@ impl CharacterBehavior for Data { } else { // Done if input_is_pressed(data, self.static_data.ability_info.input) { - reset_state(self, data, &mut update); + reset_state(self, data, output_events, &mut update); } else { update.character = CharacterState::Wielding; } @@ -363,8 +364,18 @@ impl CharacterBehavior for Data { } } -fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) { - handle_input(join, update, data.static_data.ability_info.input); +fn reset_state( + data: &Data, + join: &JoinData, + output_events: &mut OutputEvents, + update: &mut StateUpdate, +) { + handle_input( + join, + output_events, + update, + data.static_data.ability_info.input, + ); if let CharacterState::ComboMelee(c) = &mut update.character { c.stage = (data.stage % data.static_data.num_stages) + 1; diff --git a/common/src/states/dance.rs b/common/src/states/dance.rs index 8a13b84d10..ccaf6a4c39 100644 --- a/common/src/states/dance.rs +++ b/common/src/states/dance.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{CharacterState, InventoryAction, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; use serde::{Deserialize, Serialize}; @@ -9,11 +9,11 @@ use serde::{Deserialize, Serialize}; pub struct Data; impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_wield(data, &mut update); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); // Try to Fall/Stand up/Move if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { @@ -23,25 +23,30 @@ impl CharacterBehavior for Data { update } - fn manipulate_loadout(&self, data: &JoinData, inv_action: InventoryAction) -> StateUpdate { + fn manipulate_loadout( + &self, + data: &JoinData, + output_events: &mut OutputEvents, + inv_action: InventoryAction, + ) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_manipulate_loadout(data, &mut update, inv_action); + handle_manipulate_loadout(data, output_events, &mut update, inv_action); update } - fn wield(&self, data: &JoinData) -> StateUpdate { + fn wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_wield(data, &mut update); update } - fn sit(&self, data: &JoinData) -> StateUpdate { + fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sit(data, &mut update); update } - fn stand(&self, data: &JoinData) -> StateUpdate { + fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // Try to Fall/Stand up/Move update.character = CharacterState::Idle; diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index efe2ddcfdc..d009305cb8 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -1,6 +1,6 @@ use crate::{ combat::{Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement}, - comp::{tool::ToolKind, CharacterState, Melee, StateUpdate}, + comp::{character_state::OutputEvents, tool::ToolKind, CharacterState, Melee, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -74,7 +74,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_move(data, &mut update, 0.1); diff --git a/common/src/states/equipping.rs b/common/src/states/equipping.rs index 609acf48ae..78061755c6 100644 --- a/common/src/states/equipping.rs +++ b/common/src/states/equipping.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; use serde::{Deserialize, Serialize}; @@ -23,12 +23,12 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); if self.timer < self.static_data.buildup_duration { // Draw weapon diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 399110dc3c..1c66e519cf 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -1,8 +1,8 @@ use super::utils::handle_climb; use crate::{ comp::{ - fluid_dynamics::angle_of_attack, inventory::slot::EquipSlot, CharacterState, Ori, - StateUpdate, Vel, + character_state::OutputEvents, fluid_dynamics::angle_of_attack, inventory::slot::EquipSlot, + CharacterState, Ori, StateUpdate, Vel, }, states::{ behavior::{CharacterBehavior, JoinData}, @@ -73,7 +73,7 @@ impl Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // If player is on ground, end glide @@ -199,7 +199,7 @@ impl CharacterBehavior for Data { update } - fn unwield(&self, data: &JoinData) -> StateUpdate { + fn unwield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); update.character = CharacterState::Idle; update diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 88da23275b..961c9bb2aa 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -1,6 +1,9 @@ use super::utils::*; use crate::{ - comp::{slot::EquipSlot, CharacterState, InventoryAction, Ori, StateUpdate}, + comp::{ + character_state::OutputEvents, slot::EquipSlot, CharacterState, InventoryAction, Ori, + StateUpdate, + }, states::{ behavior::{CharacterBehavior, JoinData}, glide, @@ -32,12 +35,12 @@ impl From<&JoinData<'_>> for Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); handle_dodge_input(data, &mut update); handle_wield(data, &mut update); @@ -76,31 +79,36 @@ impl CharacterBehavior for Data { update } - fn manipulate_loadout(&self, data: &JoinData, inv_action: InventoryAction) -> StateUpdate { + fn manipulate_loadout( + &self, + data: &JoinData, + output_events: &mut OutputEvents, + inv_action: InventoryAction, + ) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_manipulate_loadout(data, &mut update, inv_action); + handle_manipulate_loadout(data, output_events, &mut update, inv_action); update } - fn unwield(&self, data: &JoinData) -> StateUpdate { + fn unwield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); update.character = CharacterState::Idle; update } - fn sit(&self, data: &JoinData) -> StateUpdate { + fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sit(data, &mut update); update } - fn dance(&self, data: &JoinData) -> StateUpdate { + fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_dance(data, &mut update); update } - fn sneak(&self, data: &JoinData) -> StateUpdate { + fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sneak(data, &mut update); update diff --git a/common/src/states/idle.rs b/common/src/states/idle.rs index d1f02390b1..43b95c996c 100644 --- a/common/src/states/idle.rs +++ b/common/src/states/idle.rs @@ -1,18 +1,18 @@ use super::utils::*; use crate::{ - comp::{InventoryAction, StateUpdate}, + comp::{character_state::OutputEvents, InventoryAction, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; pub struct Data; impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); handle_wield(data, &mut update); handle_climb(data, &mut update); handle_dodge_input(data, &mut update); @@ -20,49 +20,54 @@ impl CharacterBehavior for Data { update } - fn swap_equipped_weapons(&self, data: &JoinData) -> StateUpdate { + fn swap_equipped_weapons(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_swap_equipped_weapons(data, &mut update); update } - fn manipulate_loadout(&self, data: &JoinData, inv_action: InventoryAction) -> StateUpdate { + fn manipulate_loadout( + &self, + data: &JoinData, + output_events: &mut OutputEvents, + inv_action: InventoryAction, + ) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_manipulate_loadout(data, &mut update, inv_action); + handle_manipulate_loadout(data, output_events, &mut update, inv_action); update } - fn wield(&self, data: &JoinData) -> StateUpdate { + fn wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_wield(data, &mut update); update } - fn glide_wield(&self, data: &JoinData) -> StateUpdate { + fn glide_wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_glide_wield(data, &mut update); update } - fn sit(&self, data: &JoinData) -> StateUpdate { + fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sit(data, &mut update); update } - fn dance(&self, data: &JoinData) -> StateUpdate { + fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_dance(data, &mut update); update } - fn sneak(&self, data: &JoinData) -> StateUpdate { + fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sneak(data, &mut update); update } - fn talk(&self, data: &JoinData) -> StateUpdate { + fn talk(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_talk(data, &mut update); update diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index bc9f09be6b..2c0b773cb2 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -1,6 +1,6 @@ use crate::{ combat::{Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement}, - comp::{tool::ToolKind, CharacterState, Melee, StateUpdate}, + comp::{character_state::OutputEvents, tool::ToolKind, CharacterState, Melee, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, @@ -57,12 +57,12 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.3); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { // Delay before leaping into the air diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index 8e498d3ecb..180599a00f 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -1,5 +1,8 @@ use crate::{ - comp::{Body, CharacterState, LightEmitter, Pos, ProjectileConstructor, StateUpdate}, + comp::{ + character_state::OutputEvents, Body, CharacterState, LightEmitter, Pos, + ProjectileConstructor, StateUpdate, + }, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -49,7 +52,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.3); @@ -97,7 +100,7 @@ impl CharacterBehavior for Data { crit_mult, buff_strength, ); - update.server_events.push_front(ServerEvent::Shoot { + output_events.emit_server(ServerEvent::Shoot { entity: data.entity, pos, dir: data.inputs.look_dir, @@ -109,7 +112,7 @@ impl CharacterBehavior for Data { }); // Removes energy from character when arrow is fired - update.server_events.push_front(ServerEvent::EnergyChange { + output_events.emit_server(ServerEvent::EnergyChange { entity: data.entity, change: -self.static_data.energy_cost, }); diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index d91e0526f9..d87d599a41 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -1,6 +1,7 @@ use crate::{ comp::{ buff::{BuffChange, BuffKind}, + character_state::OutputEvents, CharacterState, InputKind, StateUpdate, }, event::ServerEvent, @@ -47,7 +48,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // You should not be able to strafe while rolling @@ -67,7 +68,7 @@ impl CharacterBehavior for Data { }); } else { // Remove burning effect if active - update.server_events.push_front(ServerEvent::Buff { + output_events.emit_server(ServerEvent::Buff { entity: data.entity, buff_change: BuffChange::RemoveByKind(BuffKind::Burning), }); @@ -109,7 +110,7 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 1.0); // Allows for jumps to interrupt recovery in roll if self.timer < self.static_data.recover_duration - && !handle_jump(data, &mut update, 1.5) + && !handle_jump(data, output_events, &mut update, 1.5) { // Recover update.character = CharacterState::Roll(Data { @@ -120,7 +121,7 @@ impl CharacterBehavior for Data { // Done if let Some((input, stage)) = self.was_combo { if input_is_pressed(data, input) { - handle_input(data, &mut update, input); + handle_input(data, output_events, &mut update, input); // If other states are introduced that progress through stages, add them // here if let CharacterState::ComboMelee(c) = &mut update.character { diff --git a/common/src/states/self_buff.rs b/common/src/states/self_buff.rs index 6f60e283d7..b8a3bd1d4a 100644 --- a/common/src/states/self_buff.rs +++ b/common/src/states/self_buff.rs @@ -1,6 +1,7 @@ use crate::{ comp::{ buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource}, + character_state::OutputEvents, CharacterState, StateUpdate, }, event::ServerEvent, @@ -43,11 +44,11 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_move(data, &mut update, 0.8); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { StageSection::Buildup => { @@ -68,7 +69,7 @@ impl CharacterBehavior for Data { Vec::new(), BuffSource::Character { by: *data.uid }, ); - update.server_events.push_front(ServerEvent::Buff { + output_events.emit_server(ServerEvent::Buff { entity: data.entity, buff_change: BuffChange::Add(buff), }); diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index 70d3eee546..d7ba8a66b2 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -3,7 +3,7 @@ use crate::{ Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, Knockback, }, - comp::{shockwave, CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, shockwave, CharacterState, StateUpdate}, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -62,7 +62,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); @@ -117,7 +117,7 @@ impl CharacterBehavior for Data { owner: Some(*data.uid), specifier: self.static_data.specifier, }; - update.server_events.push_front(ServerEvent::Shockwave { + output_events.emit_server(ServerEvent::Shockwave { properties, pos: *data.pos, ori: *data.ori, diff --git a/common/src/states/sit.rs b/common/src/states/sit.rs index afafb3e743..cb8183f196 100644 --- a/common/src/states/sit.rs +++ b/common/src/states/sit.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{CharacterState, InventoryAction, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; use serde::{Deserialize, Serialize}; @@ -9,11 +9,11 @@ use serde::{Deserialize, Serialize}; pub struct Data; impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_wield(data, &mut update); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); // Try to Fall/Stand up/Move if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { @@ -23,25 +23,30 @@ impl CharacterBehavior for Data { update } - fn manipulate_loadout(&self, data: &JoinData, inv_action: InventoryAction) -> StateUpdate { + fn manipulate_loadout( + &self, + data: &JoinData, + output_events: &mut OutputEvents, + inv_action: InventoryAction, + ) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_manipulate_loadout(data, &mut update, inv_action); + handle_manipulate_loadout(data, output_events, &mut update, inv_action); update } - fn wield(&self, data: &JoinData) -> StateUpdate { + fn wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_wield(data, &mut update); update } - fn dance(&self, data: &JoinData) -> StateUpdate { + fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_dance(data, &mut update); update } - fn stand(&self, data: &JoinData) -> StateUpdate { + fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // Try to Fall/Stand up/Move update.character = CharacterState::Idle; diff --git a/common/src/states/sneak.rs b/common/src/states/sneak.rs index 537c7a9836..d92f40f9fa 100644 --- a/common/src/states/sneak.rs +++ b/common/src/states/sneak.rs @@ -1,18 +1,18 @@ use super::utils::*; use crate::{ - comp::{CharacterState, InventoryAction, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; pub struct Data; impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.4); - handle_jump(data, &mut update, 1.0); + handle_jump(data, output_events, &mut update, 1.0); handle_wield(data, &mut update); handle_climb(data, &mut update); handle_dodge_input(data, &mut update); @@ -25,43 +25,48 @@ impl CharacterBehavior for Data { update } - fn swap_equipped_weapons(&self, data: &JoinData) -> StateUpdate { + fn swap_equipped_weapons(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_swap_equipped_weapons(data, &mut update); update } - fn manipulate_loadout(&self, data: &JoinData, inv_action: InventoryAction) -> StateUpdate { + fn manipulate_loadout( + &self, + data: &JoinData, + output_events: &mut OutputEvents, + inv_action: InventoryAction, + ) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_manipulate_loadout(data, &mut update, inv_action); + handle_manipulate_loadout(data, output_events, &mut update, inv_action); update } - fn wield(&self, data: &JoinData) -> StateUpdate { + fn wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_wield(data, &mut update); update } - fn glide_wield(&self, data: &JoinData) -> StateUpdate { + fn glide_wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_glide_wield(data, &mut update); update } - fn sit(&self, data: &JoinData) -> StateUpdate { + fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sit(data, &mut update); update } - fn dance(&self, data: &JoinData) -> StateUpdate { + fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_dance(data, &mut update); update } - fn stand(&self, data: &JoinData) -> StateUpdate { + fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); update.character = CharacterState::Idle; update diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 5baf929409..5ec0e2c88d 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -3,7 +3,7 @@ use crate::{ Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, Knockback, }, - comp::{tool::ToolKind, CharacterState, Melee, StateUpdate}, + comp::{character_state::OutputEvents, tool::ToolKind, CharacterState, Melee, StateUpdate}, consts::GRAVITY, states::{ behavior::{CharacterBehavior, JoinData}, @@ -71,7 +71,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); match self.static_data.movement_behavior { diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index e34b34cb04..e825d519b8 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{CharacterState, InventoryManip, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, InventoryManip, StateUpdate}, event::ServerEvent, states::behavior::{CharacterBehavior, JoinData}, terrain::SpriteKind, @@ -41,7 +41,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); let ori_dir = Dir::from_unnormalized(Vec3::from( @@ -93,9 +93,7 @@ impl CharacterBehavior for Data { } else { // Create inventory manipulation event let inv_manip = InventoryManip::Collect(self.static_data.sprite_pos); - update - .server_events - .push_front(ServerEvent::InventoryManip(data.entity, inv_manip)); + output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip)); // Done if self.static_data.was_wielded { update.character = CharacterState::Wielding; diff --git a/common/src/states/sprite_summon.rs b/common/src/states/sprite_summon.rs index df354f1013..2a81fe8f44 100644 --- a/common/src/states/sprite_summon.rs +++ b/common/src/states/sprite_summon.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, StateUpdate}, event::ServerEvent, spiral::Spiral2d, states::{ @@ -47,7 +47,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); match self.stage_section { @@ -119,7 +119,7 @@ impl CharacterBehavior for Data { Vec3::new(sprite_pos.x as i32, sprite_pos.y as i32, z); // Send server event to create sprite - update.server_events.push_front(ServerEvent::CreateSprite { + output_events.emit_server(ServerEvent::CreateSprite { pos: sprite_pos, sprite: self.static_data.sprite, }); diff --git a/common/src/states/stunned.rs b/common/src/states/stunned.rs index 97bd5e7937..0641ce0ad4 100644 --- a/common/src/states/stunned.rs +++ b/common/src/states/stunned.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{CharacterState, PoiseState, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, PoiseState, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; use serde::{Deserialize, Serialize}; @@ -33,7 +33,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); diff --git a/common/src/states/talk.rs b/common/src/states/talk.rs index 59abfc8e69..b069e2dfd8 100644 --- a/common/src/states/talk.rs +++ b/common/src/states/talk.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{CharacterState, InventoryAction, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; use serde::{Deserialize, Serialize}; @@ -11,7 +11,7 @@ const TURN_RATE: f32 = 40.0; pub struct Data; impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_wield(data, &mut update); @@ -20,33 +20,38 @@ impl CharacterBehavior for Data { update } - fn manipulate_loadout(&self, data: &JoinData, inv_action: InventoryAction) -> StateUpdate { + fn manipulate_loadout( + &self, + data: &JoinData, + output_events: &mut OutputEvents, + inv_action: InventoryAction, + ) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_manipulate_loadout(data, &mut update, inv_action); + handle_manipulate_loadout(data, output_events, &mut update, inv_action); update } - fn wield(&self, data: &JoinData) -> StateUpdate { + fn wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_wield(data, &mut update); update } - fn sit(&self, data: &JoinData) -> StateUpdate { + fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); update.character = CharacterState::Idle; attempt_sit(data, &mut update); update } - fn dance(&self, data: &JoinData) -> StateUpdate { + fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); update.character = CharacterState::Idle; attempt_dance(data, &mut update); update } - fn stand(&self, data: &JoinData) -> StateUpdate { + fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // Try to Fall/Stand up/Move update.character = CharacterState::Idle; diff --git a/common/src/states/use_item.rs b/common/src/states/use_item.rs index 10d490eec2..db56d7f6a5 100644 --- a/common/src/states/use_item.rs +++ b/common/src/states/use_item.rs @@ -2,6 +2,7 @@ use super::utils::*; use crate::{ comp::{ buff::{BuffChange, BuffKind}, + character_state::OutputEvents, inventory::{ item::{ConsumableKind, ItemKind}, slot::{InvSlotId, Slot}, @@ -47,7 +48,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); match self.static_data.item_kind { @@ -86,7 +87,7 @@ impl CharacterBehavior for Data { }); if let UsePoint::BuildupUse = use_point { // Create inventory manipulation event - use_item(data, &mut update, self); + use_item(data, output_events, self); } } }, @@ -107,7 +108,7 @@ impl CharacterBehavior for Data { }); if let UsePoint::UseRecover = use_point { // Create inventory manipulation event - use_item(data, &mut update, self); + use_item(data, output_events, self); } } }, @@ -141,11 +142,11 @@ impl CharacterBehavior for Data { if matches!(update.character, CharacterState::Roll(_)) { // Remove potion/saturation effect if left the use item state early by rolling - update.server_events.push_front(ServerEvent::Buff { + output_events.emit_server(ServerEvent::Buff { entity: data.entity, buff_change: BuffChange::RemoveByKind(BuffKind::Potion), }); - update.server_events.push_front(ServerEvent::Buff { + output_events.emit_server(ServerEvent::Buff { entity: data.entity, buff_change: BuffChange::RemoveByKind(BuffKind::Saturation), }); @@ -201,7 +202,7 @@ enum UsePoint { UseRecover, } -fn use_item(data: &JoinData, update: &mut StateUpdate, state: &Data) { +fn use_item(data: &JoinData, output_events: &mut OutputEvents, state: &Data) { // Check if the same item is in the slot let item_is_same = data .inventory @@ -212,8 +213,6 @@ fn use_item(data: &JoinData, update: &mut StateUpdate, state: &Data) { if item_is_same { // Create inventory manipulation event let inv_manip = InventoryManip::Use(Slot::Inventory(state.static_data.inv_slot)); - update - .server_events - .push_front(ServerEvent::InventoryManip(data.entity, inv_manip)); + output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip)); } } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 3b554b4f07..6bfe1a8885 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -3,6 +3,7 @@ use crate::{ combat, comp::{ biped_large, biped_small, + character_state::OutputEvents, inventory::slot::{EquipSlot, Slot}, item::{Hands, ItemKind, Tool, ToolKind}, quadruped_low, quadruped_medium, quadruped_small, @@ -390,21 +391,25 @@ pub fn handle_orientation( // Direction is set to the override if one is provided, else if entity is // strafing or attacking the horiontal component of the look direction is used, // else the current horizontal movement direction is used - let dir = if let Some(dir_override) = dir_override { - dir_override + let target_ori = if let Some(dir_override) = dir_override { + dir_override.into() } else if is_strafing(data, update) || update.character.is_attack() { - data.inputs.look_dir.to_horizontal().unwrap_or_default() + data.inputs + .look_dir + .to_horizontal() + .unwrap_or_default() + .into() } else { Dir::from_unnormalized(data.inputs.move_dir.into()) - .unwrap_or_else(|| data.ori.to_horizontal().look_dir()) + .map_or_else(|| data.ori.to_horizontal(), |dir| dir.into()) }; let rate = { - let angle = update.ori.look_dir().angle_between(*dir); + let angle = update.ori.angle_between(target_ori); data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle }; update.ori = update .ori - .slerped_towards(dir.into(), (data.dt.0 * rate).min(1.0)); + .slerped_towards(target_ori, (data.dt.0 * rate).min(1.0)); } /// Updates components to move player as if theyre swimming @@ -627,6 +632,7 @@ pub fn attempt_swap_equipped_weapons(data: &JoinData<'_>, update: &mut StateUpda /// Handles inventory manipulations that affect the loadout pub fn handle_manipulate_loadout( data: &JoinData<'_>, + output_events: &mut OutputEvents, update: &mut StateUpdate, inv_action: InventoryAction, ) { @@ -659,9 +665,8 @@ pub fn handle_manipulate_loadout( }); } else { // Else emit inventory action instantnaneously - update - .server_events - .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); + output_events + .emit_server(ServerEvent::InventoryManip(data.entity, inv_action.into())); } }, InventoryAction::Collect(sprite_pos) => { @@ -768,9 +773,7 @@ pub fn handle_manipulate_loadout( }, _ => { // Else just do event instantaneously - update - .server_events - .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); + output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_action.into())); }, } } @@ -793,12 +796,18 @@ pub fn attempt_glide_wield(data: &JoinData<'_>, update: &mut StateUpdate) { } /// Checks that player can jump and sends jump event if so -pub fn handle_jump(data: &JoinData<'_>, update: &mut StateUpdate, strength: f32) -> bool { +pub fn handle_jump( + data: &JoinData<'_>, + output_events: &mut OutputEvents, + // TODO: remove? + _update: &mut StateUpdate, + strength: f32, +) -> bool { (input_is_pressed(data, InputKind::Jump) && data.physics.on_ground.is_some()) .then(|| data.body.jump_impulse()) .flatten() .map(|impulse| { - update.local_events.push_front(LocalEvent::Jump( + output_events.emit_local(LocalEvent::Jump( data.entity, strength * impulse / data.mass.0 * data.stats.move_speed_modifier, )); @@ -876,24 +885,33 @@ pub fn handle_ability_input(data: &JoinData<'_>, update: &mut StateUpdate) { } } -pub fn handle_input(data: &JoinData<'_>, update: &mut StateUpdate, input: InputKind) { +pub fn handle_input( + data: &JoinData<'_>, + output_events: &mut OutputEvents, + update: &mut StateUpdate, + input: InputKind, +) { match input { InputKind::Primary | InputKind::Secondary | InputKind::Ability(_) => { handle_ability(data, update, input) }, InputKind::Roll => handle_dodge_input(data, update), InputKind::Jump => { - handle_jump(data, update, 1.0); + handle_jump(data, output_events, update, 1.0); }, InputKind::Block => handle_block_input(data, update), InputKind::Fly => {}, } } -pub fn attempt_input(data: &JoinData<'_>, update: &mut StateUpdate) { +pub fn attempt_input( + data: &JoinData<'_>, + output_events: &mut OutputEvents, + update: &mut StateUpdate, +) { // TODO: look into using first() when it becomes stable if let Some(input) = data.controller.queued_inputs.keys().next() { - handle_input(data, update, *input); + handle_input(data, output_events, update, *input); } } diff --git a/common/src/states/wielding.rs b/common/src/states/wielding.rs index 68c90aa4fd..7c7fd20993 100644 --- a/common/src/states/wielding.rs +++ b/common/src/states/wielding.rs @@ -1,6 +1,7 @@ use super::utils::*; use crate::{ comp::{ + character_state::OutputEvents, slot::{EquipSlot, Slot}, CharacterState, InventoryAction, StateUpdate, }, @@ -10,24 +11,29 @@ use crate::{ pub struct Data; impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); handle_climb(data, &mut update); - attempt_input(data, &mut update); + attempt_input(data, output_events, &mut update); update } - fn swap_equipped_weapons(&self, data: &JoinData) -> StateUpdate { + fn swap_equipped_weapons(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_swap_equipped_weapons(data, &mut update); update } - fn manipulate_loadout(&self, data: &JoinData, inv_action: InventoryAction) -> StateUpdate { + fn manipulate_loadout( + &self, + data: &JoinData, + output_events: &mut OutputEvents, + inv_action: InventoryAction, + ) -> StateUpdate { let mut update = StateUpdate::from(data); match inv_action { InventoryAction::Drop(EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand) @@ -40,35 +46,35 @@ impl CharacterBehavior for Data { }, _ => (), } - handle_manipulate_loadout(data, &mut update, inv_action); + handle_manipulate_loadout(data, output_events, &mut update, inv_action); update } - fn glide_wield(&self, data: &JoinData) -> StateUpdate { + fn glide_wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_glide_wield(data, &mut update); update } - fn unwield(&self, data: &JoinData) -> StateUpdate { + fn unwield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); update.character = CharacterState::Idle; update } - fn sit(&self, data: &JoinData) -> StateUpdate { + fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sit(data, &mut update); update } - fn dance(&self, data: &JoinData) -> StateUpdate { + fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_dance(data, &mut update); update } - fn sneak(&self, data: &JoinData) -> StateUpdate { + fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sneak(data, &mut update); update diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index f43fc53a05..7f0db06778 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -5,11 +5,12 @@ use specs::{ use common::{ comp::{ - self, inventory::item::MaterialStatManifest, Beam, Body, CharacterState, Combo, Controller, - Density, Energy, Health, Inventory, InventoryManip, Mass, Melee, Mounting, Ori, - PhysicsState, Poise, PoiseState, Pos, SkillSet, StateUpdate, Stats, Vel, + self, character_state::OutputEvents, inventory::item::MaterialStatManifest, Beam, Body, + CharacterState, Combo, Controller, Density, Energy, Health, Inventory, InventoryManip, + Mass, Melee, Mounting, Ori, PhysicsState, Poise, PoiseState, Pos, SkillSet, StateUpdate, + Stats, Vel, }, - event::{Emitter, EventBus, LocalEvent, ServerEvent}, + event::{EventBus, LocalEvent, ServerEvent}, outcome::Outcome, resources::DeltaTime, states::behavior::{JoinData, JoinStruct}, @@ -86,6 +87,10 @@ impl<'a> System<'a> for Sys { let mut server_emitter = read_data.server_bus.emitter(); let mut local_emitter = read_data.local_bus.emitter(); + let mut local_events = Vec::new(); + let mut server_events = Vec::new(); + let mut output_events = OutputEvents::new(&mut local_events, &mut server_events); + for ( entity, uid, @@ -257,13 +262,8 @@ impl<'a> System<'a> for Sys { &read_data.dt, &read_data.msm, ); - let state_update = j.character.handle_event(&j, action); - Self::publish_state_update( - &mut join_struct, - state_update, - &mut local_emitter, - &mut server_emitter, - ); + let state_update = j.character.handle_event(&j, &mut output_events, action); + Self::publish_state_update(&mut join_struct, state_update, &mut output_events); } // Mounted occurs after control actions have been handled @@ -283,14 +283,12 @@ impl<'a> System<'a> for Sys { &read_data.msm, ); - let state_update = j.character.behavior(&j); - Self::publish_state_update( - &mut join_struct, - state_update, - &mut local_emitter, - &mut server_emitter, - ); + let state_update = j.character.behavior(&j, &mut output_events); + Self::publish_state_update(&mut join_struct, state_update, &mut output_events); } + + local_emitter.append_vec(local_events); + server_emitter.append_vec(server_events); } } @@ -298,12 +296,8 @@ impl Sys { fn publish_state_update( join: &mut JoinStruct, mut state_update: StateUpdate, - local_emitter: &mut Emitter, - server_emitter: &mut Emitter, + output_events: &mut OutputEvents, ) { - local_emitter.append(&mut state_update.local_events); - server_emitter.append(&mut state_update.server_events); - // TODO: if checking equality is expensive use optional field in StateUpdate if *join.char_state != state_update.character { *join.char_state = state_update.character @@ -320,7 +314,7 @@ impl Sys { join.controller.queued_inputs.remove(&input); } if state_update.swap_equipped_weapons { - server_emitter.emit(ServerEvent::InventoryManip( + output_events.emit_server(ServerEvent::InventoryManip( join.entity, InventoryManip::SwapEquippedWeapons, ));