//! # Server thrust handling
//! The thrust solver runs on the client and sends a `ThrustSolution` message;
//! this file receives it and applies it to the physics simulation.
use crate::client::components::Me;
use crate::shared::ecs::{Part, Temperature};
use crate::shared::ecs::thruster::{Thruster, ThrusterOfPart};
use crate::prelude::*;
use crate::shared::attachment::Parts;
use crate::shared::thrust::ThrustSolution;
pub fn server_thrust_plugin(app: &mut App) {
app
.add_systems(Update, process_thrust_events)
.add_systems(FixedUpdate, apply_thrust_solutions);
}
fn process_thrust_events(
mut events: MessageReader<ThrustSolution>,
me_entity: Query<Entity, With<Me>>,
mut commands: Commands,
) {
for thrust_solution in events.read() {
let Ok(player_entity) = me_entity.single() else { return };
commands.entity(player_entity).insert(thrust_solution.clone());
}
}
/// Find all players, and apply their current `ThrustSolution`s
fn apply_thrust_solutions(
players: Query<(Entity, &ThrustSolution, Option<&Parts>)>,
thrusters: Query<(&Thruster, &ThrusterOfPart, &GlobalTransform)>,
mut parts: Query<(Forces, &mut Temperature), With<Part>>,
time: Res<Time>,
) {
for (player_entity, thrust_solution, maybe_parts) in players {
if !thrust_solution.converged {
debug!(?player_entity, "ignoring unconverged thrust solution");
}
if thrust_solution.thrusters_on.is_empty() { continue }
let attached_parts = if let Some(parts) = maybe_parts {
parts.as_slice()
} else {
&[]
};
for thruster_entity in &thrust_solution.thrusters_on {
let Ok((thruster_info, parent_part, thruster_transform)) = thrusters.get(*thruster_entity) else {
debug!(?thruster_entity, "couldn't find thruster to apply force");
continue
};
let parent_is_hearty = parent_part.0 == player_entity;
let parent_is_in_ship = attached_parts.contains(&parent_part.0);
if !(parent_is_hearty || parent_is_in_ship) {
debug!(?thruster_entity, "ignoring disallowed thruster action");
continue
}
let (mut part_forces, mut temperature) = parts.get_mut(parent_part.0).unwrap();
temperature.0 += thruster_info.heat_constant * (thruster_info.exhaust_temperature - temperature.0) * time.delta_secs() as f64;
part_forces.apply_force_at_point(
(thruster_transform.rotation() * thruster_info.thrust_vector.extend(0.0)).xy().into(),
thruster_transform.translation().xy().into(),
);
}
}
}