veloren_server/sys/
wiring.rs

1use crate::wiring::{Circuit, WiringElement};
2use common::{
3    comp::{LightEmitter, PhysicsState, Pos},
4    event, event_emitters,
5    resources::EntitiesDiedLastTick,
6};
7use common_ecs::{Job, Origin, Phase, System};
8use common_state::BlockChange;
9use hashbrown::HashMap;
10use specs::{
11    Entities, Entity, Join, LendJoin, Read, ReadStorage, SystemData, Write, WriteStorage, shred,
12};
13
14#[derive(SystemData)]
15pub struct ReadData<'a> {
16    entities: Entities<'a>,
17    circuits: ReadStorage<'a, Circuit>,
18    pos: ReadStorage<'a, Pos>,
19    physics_states: ReadStorage<'a, PhysicsState>,
20    entities_died_last_tick: Read<'a, EntitiesDiedLastTick>,
21}
22
23event_emitters! {
24    struct Events[Emitters] {
25        shoot: event::ShootEvent,
26    }
27}
28
29/// This system is responsible for handling wiring (signals and wiring systems)
30#[derive(Default)]
31pub struct Sys;
32impl<'a> System<'a> for Sys {
33    type SystemData = (
34        ReadData<'a>,
35        Events<'a>,
36        WriteStorage<'a, WiringElement>,
37        WriteStorage<'a, LightEmitter>, // maybe
38        Write<'a, BlockChange>,
39    );
40
41    const NAME: &'static str = "wiring";
42    const ORIGIN: Origin = Origin::Server;
43    const PHASE: Phase = Phase::Create;
44
45    fn run(
46        _job: &mut Job<Self>,
47        (read_data, events, mut wiring_elements, mut light_emitters, mut block_change): Self::SystemData,
48    ) {
49        let mut emitters = events.get_emitters();
50
51        // Compute the output for each wiring element by computing
52        // the output for each `OutputFormula` and store each value per output per
53        // entity.
54        let computed_outputs: HashMap<Entity, HashMap<String, f32>> = (
55            &read_data.entities,
56            &wiring_elements,
57            read_data.physics_states.maybe(),
58            read_data.pos.maybe(),
59        )
60            .join()
61            .map(|(entity, wiring_element, physics_state, pos)| {
62                (
63                    entity,
64                    wiring_element
65                        .outputs
66                        .iter()
67                        .map(|(key, output_formula)| {
68                            (
69                                // Output name/key
70                                key.to_string(),
71                                // Output value
72                                output_formula.compute_output(
73                                    &wiring_element.inputs,
74                                    physics_state,
75                                    &read_data.entities_died_last_tick.0,
76                                    pos,
77                                ),
78                            )
79                        })
80                        .collect::<HashMap<_, _>>(),
81                )
82            })
83            .collect();
84
85        // Pass new outputs as inputs for the next tick to the proper elements in the
86        // circuit.
87        (read_data.circuits)
88            .join()
89            .flat_map(|circuit| circuit.wires.iter())
90            .for_each(|wire| {
91                // The current output values becomes input values for the next tick
92                let input_value = computed_outputs
93                    .get(&wire.input.entity)
94                    .and_then(|e| e.get(&wire.input.name))
95                    .unwrap_or(&0.0);
96
97                // Push the current output value into the inputs for the proper element to be
98                // used next tick.
99                if let Some(wiring_element) = wiring_elements.get_mut(wire.output.entity) {
100                    wiring_element
101                        .inputs
102                        .insert(wire.output.name.clone(), *input_value);
103                }
104            });
105
106        // Use inputs to dispatch actions and apply effects
107        (
108            &read_data.entities,
109            &mut wiring_elements,
110            read_data.physics_states.maybe(),
111            (&mut light_emitters).maybe(),
112            read_data.pos.maybe(),
113        )
114            .lend_join()
115            .for_each(
116                |(entity, wiring_element, physics_state, mut light_emitter, pos)| {
117                    wiring_element
118                        .actions
119                        .iter()
120                        .filter(|wiring_action| {
121                            // Filter out any wiring actions with a total output less than the
122                            // threshold
123                            wiring_action.formula.compute_output(
124                                &wiring_element.inputs,
125                                physics_state,
126                                &read_data.entities_died_last_tick.0,
127                                pos,
128                            ) >= wiring_action.threshold
129                        })
130                        .for_each(|wiring_action| {
131                            // Apply world effects of each wiring action
132                            wiring_action.apply_effects(
133                                entity,
134                                &wiring_element.inputs,
135                                physics_state,
136                                &read_data.entities_died_last_tick.0,
137                                &mut emitters,
138                                pos,
139                                &mut block_change,
140                                light_emitter.as_deref_mut(),
141                            );
142                        })
143                },
144            )
145    }
146}