From 6de4db94b165e9d18c471cc99c28ff35753347e8 Mon Sep 17 00:00:00 2001 From: core Date: Sat, 22 Nov 2025 22:24:27 -0500 Subject: [PATCH] feat: document for tm --- .../assets/config/parts/housing.part.toml | 2 +- crates/unified/src/client/ship/thrusters.rs | 2 +- crates/unified/src/ecs.rs | 8 +-- crates/unified/src/server/mod.rs | 2 + crates/unified/src/server/player.rs | 1 + crates/unified/src/server/player/thrust.rs | 68 +++++++++++++++++++ 6 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 crates/unified/src/server/player/thrust.rs diff --git a/crates/unified/assets/config/parts/housing.part.toml b/crates/unified/assets/config/parts/housing.part.toml index 3a3c943933fa99e5fc93b2c0256a60418986232a..20b93b52128227d2a7c8cbf097c3110a76eaff1f 100644 --- a/crates/unified/assets/config/parts/housing.part.toml +++ b/crates/unified/assets/config/parts/housing.part.toml @@ -10,7 +10,7 @@ mass = 50 [[thruster]] id = "primary" -thrust_vector = [ 0.0, 50.0 ] +thrust_vector = [ 0.0, -50.0 ] [[joint]] id = "Top" diff --git a/crates/unified/src/client/ship/thrusters.rs b/crates/unified/src/client/ship/thrusters.rs index f0892cd9d6d4bd41afd78a8b133bd62ac640e7c5..5c116b2ab977b71b5f37d8fc651bf3b4506ee8a6 100644 --- a/crates/unified/src/client/ship/thrusters.rs +++ b/crates/unified/src/client/ship/thrusters.rs @@ -143,7 +143,7 @@ fn solve_thrust( let thruster_torque = relative_translation.extend(0.0).cross(thruster_vector.extend(0.0)).z; - // magically assemble the vector! + // magically assemble the worldspace vector! for the solver (not shipspace) let target_vector = thruster_vector.extend(thruster_torque); all_thrusters.push((thruster_id, target_vector)); diff --git a/crates/unified/src/ecs.rs b/crates/unified/src/ecs.rs index fffffa662ea4af938646fced8d8c6b8800f11f8a..018ab4a5543776a672b592748cdd21cafba139ed 100644 --- a/crates/unified/src/ecs.rs +++ b/crates/unified/src/ecs.rs @@ -7,6 +7,7 @@ use crate::prelude::*; use bevy_replicon::prelude::Replicated; use serde::{Deserialize, Serialize}; use avian2d::prelude::*; +use crate::thrust::ThrustSolution; #[derive(Component)] pub struct MainCamera; @@ -23,12 +24,7 @@ pub struct FuelText; pub struct PowerText; #[derive(Debug, Deserialize, Message, Serialize)] -pub enum ThrustEvent { - Up(bool), - Down(bool), - Left(bool), - Right(bool), -} +pub struct ThrustEvent(pub ThrustSolution); #[derive(Component, Serialize, Deserialize, Debug)] #[require( diff --git a/crates/unified/src/server/mod.rs b/crates/unified/src/server/mod.rs index 827e622ee6ee3ee68a1d49596caf13a596e430ec..20fa6b208ecffdd4505fd0cb23f52f4ba3d16313 100644 --- a/crates/unified/src/server/mod.rs +++ b/crates/unified/src/server/mod.rs @@ -21,6 +21,7 @@ use aeronet_websocket::server::WebSocketServer; use crate::prelude::*; use bevy_replicon::prelude::Replicated; use std::net::SocketAddr; +use crate::server::player::thrust::server_thrust_plugin; pub struct ServerPlugin { pub bind: SocketAddr, @@ -49,6 +50,7 @@ impl Plugin for ServerPlugin { .add_plugins(player_management_plugin) .add_plugins(spawn_parts_plugin) .add_plugins(part_management_plugin) + .add_plugins(server_thrust_plugin) .configure_sets(Update, WorldUpdateSet.before(PlayerInputSet)); //.configure_sets(Update, PlayerInputSet.before(PhysicsSet::SyncBackend)); } diff --git a/crates/unified/src/server/player.rs b/crates/unified/src/server/player.rs index 8b59753dd4b1bfe178add11ee3a7b7d57fb05b35..6b5f492f4c44d31edd9068595b0300fad4f457d5 100644 --- a/crates/unified/src/server/player.rs +++ b/crates/unified/src/server/player.rs @@ -1,4 +1,5 @@ pub mod join; +pub mod thrust; use crate::attachment::{Joint, JointOf, Joints, PartInShip, Peer, SnapOf, SnapOfJoint}; use crate::ecs::{DragRequestEvent, Part, Player, PlayerStorage}; diff --git a/crates/unified/src/server/player/thrust.rs b/crates/unified/src/server/player/thrust.rs new file mode 100644 index 0000000000000000000000000000000000000000..d375d37d279da620e58249fe64d48043354b0431 --- /dev/null +++ b/crates/unified/src/server/player/thrust.rs @@ -0,0 +1,68 @@ +use crate::prelude::*; +use crate::ecs::ThrustEvent; +use crate::server::ConnectedNetworkEntity; + +pub fn server_thrust_plugin(app: &mut App) { + app + .add_systems(Update, process_thrust_events); +} + +pub fn process_thrust_events( + mut events: MessageReader>, + + clients: Query<&ConnectedNetworkEntity>, + q_ls_me: Query>, +) { + for FromClient { + client_id, + message: event, + } in events.read() + { + let player_hearty_entity = match client_id { + ClientId::Client(client_entity) => { + let ConnectedNetworkEntity { + game_entity: player_hearty_entity, + } = clients.get(*client_entity).unwrap(); + player_hearty_entity + }, + ClientId::Server => &q_ls_me.iter().next().unwrap() + }; + let thrust_solution = &event.0; + if !thrust_solution.converged { return }; + /* TODO: @tm85: have fun! + TODO: The ThrustSolution contains a set of thrusters that should be on. + TODO: All other thrusters should be off. + TODO: If a thruster should be on, apply it's force vector at it's point + TODO: (RigidBodyForces::apply_force_at_point, + TODO: note this is worldspace!! [GlobalTransform is your friend]) + + TODO: Note that this is only an event handler, and will only run + TODO: when the client sends us a NEW thrust solution. + + TODO: You probably want to add a Component to the player entity (player_hearty_entity, + TODO: it's also the hearty part entity) that stores the current ThrustSolution + TODO: and applies it every tick, since avian's ExternalForces only apply for a single + TODO: physics tick and must be applied every tick to be continuous. + TODO: Hint: you probably want a mut commands: Commands in this system, + TODO: and then insert a component with commands.entity(hearty).insert(YourNewComponent) + + TODO: To do this, create a new system (e.g. apply_thrust) or something, and apply thrust + TODO: there, every tick (Update). Register it with the plugin above. + + TODO: I'll leave you to that. Have fun! ping me if you've got questions, I'll be up + TODO: a while so can help you decipher the attachment system if need be. + + TODO: Overall, your goal is: + TODO: - find all parts in the ship (hint: query for Parts on the ship (hearty)) + TODO: - for each part, find all thrusters (hint: query for PartThrusters on the part) + TODO: - for each thruster, if it's on (check if it's in the player's current thrust soln) + TODO: - apply a force at the correct point + TODO: Much of the same logic is done in the client counterpart, to calculate effective + TODO: force for the solver. Take a look at client::ship::thrusters. + TODO: Note that relationship components (eg Parts, PartThrusters) wont exist if there are + TODO: no children - eg hearty won't have Parts if nothing is attached, a part won't have + TODO: PartThrusters if it has no thrusters. + TODO: Handle this with Option<&Component> in the query + */ + } +} \ No newline at end of file