~starkingdoms/starkingdoms

ref: aa9bcf7200f6a8ce6ed78b392414cbffad896788 starkingdoms/crates/unified/src/server/part.rs -rw-r--r-- 6.8 KiB
aa9bcf72 — ghostly_zsh feat: starguide dragging 18 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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
use crate::attachment::{Joint, JointId, JointOf, Joints, Peer, SnapOf, SnapOfJoint};
use crate::config::part::{JointConfig, PartConfig};
use crate::ecs::{Part, PartHandle};
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use crate::ecs::thruster::{PartThrusters, Thruster, ThrusterBundle, ThrusterId, ThrusterOfPart};

pub fn part_management_plugin(app: &mut App) {
    app.add_systems(PreUpdate, (handle_ready_parts, handle_part_reloading));
}

#[derive(Bundle)]
pub struct SpawnPartBundle {
    pub req: SpawnPartRequest,
    pub transform: Transform,
}

#[derive(Component)]
#[require(Replicated)]
pub struct SpawnPartRequest(pub Handle<PartConfig>);

// wait for parts assets to be ready, then spawn the full part
fn handle_ready_parts(
    loading_parts: Query<(Entity, &SpawnPartRequest)>,
    mut commands: Commands,
    assets: Res<Assets<PartConfig>>,
) {
    for (entity, loading_part) in &loading_parts {
        if let Some(strong_config) = assets.get(&loading_part.0) {
            // config is strong; spawn 'er in!
            commands
                .entity(entity)
                .insert(calculate_bundle(strong_config, &loading_part.0))
                .remove::<SpawnPartRequest>();
            spawn_joints(strong_config, entity, commands.reborrow());
            spawn_thrusters(strong_config, entity, commands.reborrow());
        }
    }
}

fn handle_part_reloading(
    existing_parts: Query<(Entity, &PartHandle, &Joints, &PartThrusters)>,
    joints: Query<(&mut Joint, Option<&Peer>, Entity)>,
    snaps: Query<(Entity, &SnapOfJoint)>,
    assets: Res<Assets<PartConfig>>,
    mut asset_events: MessageReader<AssetEvent<PartConfig>>,
    mut commands: Commands,
) {
    for event in asset_events.read() {
        if let AssetEvent::Modified { id } = event {
            let config = assets.get(*id).unwrap();
            for existing_part in existing_parts.iter() {
                if existing_part.1.0.id() == *id {
                    commands
                        .entity(existing_part.0)
                        .insert(calculate_bundle(config, &existing_part.1.0));

                    // vaporize all thrusters, then respawn
                    for thruster in &**existing_part.3 {
                        commands.entity(*thruster).despawn();
                    }
                    spawn_thrusters(config, existing_part.0, commands.reborrow());
                    
                    // update all joints
                    let mut used_joints = vec![];
                    for joint_id in &**existing_part.2 {
                        // find in config
                        let Ok((joint, peer, _)) = joints.get(*joint_id) else {
                            continue;
                        };
                        let joint_cfg = config.joints.iter().find(|u| {
                            joint.id == JointId::from_part_and_joint_id(&config.part.name, &u.id)
                        });
                        let Some(joint_cfg) = joint_cfg else {
                            if let Some(peer_id) = peer
                                && let Ok(peer) = joints.get(peer_id.peer_joint_entity_id) {
                                    commands.entity(peer.2).remove::<Peer>();
                                }
                            commands.entity(*joint_id).despawn();
                            for snap in &snaps {
                                if snap.1.0 == *joint_id {
                                    commands.entity(snap.0).despawn();
                                }
                            }
                            continue;
                        };
                        used_joints.push(joint.id.clone());
                        commands.entity(*joint_id).insert(spawn_joint_bundle(
                            joint_cfg,
                            config,
                            &existing_part.0,
                        ));
                        // annihilate all snaps then respawn
                        for snap in &snaps {
                            if snap.1.0 == *joint_id {
                                commands.entity(snap.0).despawn();
                            }
                        }
                        commands.spawn(spawn_snap_bundle(joint_cfg, &existing_part.0, joint_id));
                    }
                    for joint in &config.joints {
                        let id = JointId::from_part_and_joint_id(&config.part.name, &joint.id);
                        if used_joints.contains(&id) {
                            continue;
                        }
                        let joint_id = commands
                            .spawn(spawn_joint_bundle(joint, config, &existing_part.0))
                            .id();
                        commands.spawn(spawn_snap_bundle(joint, &existing_part.0, &joint_id));
                    }
                }
            }
        }
    }
}

fn calculate_bundle(config: &PartConfig, handle: &Handle<PartConfig>) -> impl Bundle {
    let part = Part {
        strong_config: config.clone(),
    };
    let part_handle = PartHandle(handle.clone());
    let collider = Collider::rectangle(config.physics.width, config.physics.height);
    let mass = Mass(config.physics.mass);

    (
        part,
        part_handle,
        collider,
        mass,
    )
}
fn spawn_joint_bundle(joint: &JointConfig, part: &PartConfig, parent: &Entity) -> impl Bundle {
    let j_comp = Joint {
        id: JointId::from_part_and_joint_id(part.part.name.clone(), joint.id.clone()),
        transform: joint.target.into(),
    };
    let joint_transform: Transform = j_comp.transform;
    let joint_of = JointOf(*parent);

    (j_comp, joint_transform, joint_of, Replicated)
}
fn spawn_snap_bundle(joint: &JointConfig, parent: &Entity, p_joint: &Entity) -> impl Bundle {
    let snap_transform: Transform = joint.snap.into();
    let snap_for = SnapOf(*parent);
    let snap_of = SnapOfJoint(*p_joint);

    (snap_transform, snap_for, snap_of, Replicated)
}
fn spawn_joints(config: &PartConfig, parent: Entity, mut commands: Commands) {
    for joint in &config.joints {
        let joint_id = commands
            .spawn(spawn_joint_bundle(joint, config, &parent))
            .id();
        commands.spawn(spawn_snap_bundle(joint, &parent, &joint_id));
    }
}

fn spawn_thrusters(config: &PartConfig, part: Entity, mut commands: Commands) {
    for thruster in &config.thrusters {
        commands
            .spawn(ThrusterBundle {
                thruster: Thruster {
                    id: ThrusterId::from_part_and_thruster_id(&config.part.name, &thruster.id),
                    thrust_vector: thruster.thrust_vector,
                },
                transform: Transform::from_translation(Vec3::new(thruster.apply_force_at_local.x, thruster.apply_force_at_local.y, 0.0)),
                child_of: ChildOf(part),
                thruster_of_part: ThrusterOfPart(part),
            });
    }
}