~starkingdoms/starkingdoms

ref: 92b84cca379c046bc49547f68fede1599c9ac3b9 starkingdoms/crates/unified/src/server/player/thrust.rs -rw-r--r-- 4.0 KiB
92b84cca — core fix: dragging 16 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! # Server thrust handling
//! The bulk of the actual thrust work is done in the thrust solver on the client.
//! It sends us it's `ThrustSolution` when it's done; in this file we process it
//! and apply it to the physics simulation.

use crate::attachment::Parts;
use crate::ecs::Part;
use crate::ecs::thruster::{PartThrusters, Thruster, ThrusterOfPart};
use crate::prelude::*;
use crate::server::ConnectedNetworkEntity;
use crate::thrust::ThrustSolution;

pub fn server_thrust_plugin(app: &mut App) {
    app
        .add_systems(Update, process_thrust_events)
        .add_systems(Update, apply_thrust_solutions);
}

/// Handle new `ThrustSolution`s from clients as they come in
fn process_thrust_events(
    mut events: MessageReader<FromClient<ThrustSolution>>,

    clients: Query<&ConnectedNetworkEntity>,
    q_ls_me: Query<Entity, With<crate::ecs::Me>>,
    mut commands: Commands
) {
    // For each event from a client...
    for FromClient {
        client_id,
        message: thrust_solution,
    } in events.read()
    {
        // Find the hearty entity of the player...
        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()
        };

        // and apply the new thrust solution
        commands.entity(*player_hearty_entity).insert(thrust_solution.clone());
        trace!("installed thrust solution {:?}", thrust_solution);
    }
}

/// 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, With<Part>>,
) {
    //gizmos.arrow_2d(
    //             thruster.2.translation().xy(),
    //             thruster.2.translation().xy() + thruster.2.rotation().mul_vec3(thruster.0.thrust_vector.extend(0.0)).xy(),
    //             color
    //         );

    // Iterate through all players with a ThrustSolution
    for (player_entity, thrust_solution, maybe_parts) in players {
        // If their reported thrust solution didn't converge, do nothing
        if !thrust_solution.converged {
            debug!(?player_entity, "ignoring unconverged thrust solution");
        }
        // If it's empty, exit early (no reason to waste time)
        if thrust_solution.thrusters_on.is_empty() { continue }

        // Collect a list of all parts in the player's ship, to validate
        // the thrusters the player sends
        let attached_parts = if let Some(parts) = maybe_parts {
            parts.as_slice()
        } else {
            &[]
        };

        // Go over each active thruster in the thrust solution...
        for thruster_entity in &thrust_solution.thrusters_on {
            // ...load it's data...
            let Ok((thruster_info, parent_part, thruster_transform)) = thrusters.get(*thruster_entity) else {
                debug!(?thruster_entity, "couldn't find thruster to apply force");
                continue
            };

            // ...verify this user is allowed to control this thruster...
            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
            } // not permitted

            // great, it's valid; apply the force
            let mut part_forces = parts.get_mut(parent_part.0).unwrap();
            part_forces.apply_force_at_point(
                (thruster_transform.rotation() * thruster_info.thrust_vector.extend(0.0)).xy(),
                thruster_transform.translation().xy()
            );
        }
    }
}