veloren/server/src/sys/wiring.rs
2022-06-29 20:28:09 +00:00

142 lines
5.3 KiB
Rust

use crate::wiring::{Circuit, WiringElement};
use common::{
comp::{LightEmitter, PhysicsState, Pos},
event::{EventBus, ServerEvent},
resources::EntitiesDiedLastTick,
};
use common_ecs::{Job, Origin, Phase, System};
use common_state::BlockChange;
use hashbrown::HashMap;
use specs::{
join::Join, shred::ResourceId, Entities, Entity, Read, ReadStorage, SystemData, World, Write,
WriteStorage,
};
#[derive(SystemData)]
pub struct ReadData<'a> {
entities: Entities<'a>,
circuits: ReadStorage<'a, Circuit>,
pos: ReadStorage<'a, Pos>,
physics_states: ReadStorage<'a, PhysicsState>,
entities_died_last_tick: Read<'a, EntitiesDiedLastTick>,
}
/// This system is responsible for handling wiring (signals and wiring systems)
#[derive(Default)]
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
ReadData<'a>,
Read<'a, EventBus<ServerEvent>>,
WriteStorage<'a, WiringElement>,
WriteStorage<'a, LightEmitter>, // maybe
Write<'a, BlockChange>,
);
const NAME: &'static str = "wiring";
const ORIGIN: Origin = Origin::Server;
const PHASE: Phase = Phase::Create;
fn run(
_job: &mut Job<Self>,
(read_data, event_bus, mut wiring_elements, mut light_emitters, mut block_change): Self::SystemData,
) {
let mut server_emitter = event_bus.emitter();
// Compute the output for each wiring element by computing
// the output for each `OutputFormula` and store each value per output per
// entity.
let computed_outputs: HashMap<Entity, HashMap<String, f32>> = (
&read_data.entities,
&wiring_elements,
read_data.physics_states.maybe(),
read_data.pos.maybe(),
)
.join()
.map(|(entity, wiring_element, physics_state, pos)| {
(
entity,
wiring_element
.outputs
.iter()
.map(|(key, output_formula)| {
(
// Output name/key
key.to_string(),
// Output value
output_formula.compute_output(
&wiring_element.inputs,
physics_state,
&read_data.entities_died_last_tick.0,
pos,
),
)
})
.collect::<HashMap<_, _>>(),
)
})
.collect();
// Pass new outputs as inputs for the next tick to the proper elements in the
// circuit.
(read_data.circuits)
.join()
.flat_map(|circuit| circuit.wires.iter())
.for_each(|wire| {
// The current output values becomes input values for the next tick
let input_value = computed_outputs
.get(&wire.input.entity)
.and_then(|e| e.get(&wire.input.name))
.unwrap_or(&0.0);
// Push the current output value into the inputs for the proper element to be
// used next tick.
if let Some(wiring_element) = wiring_elements.get_mut(wire.output.entity) {
wiring_element
.inputs
.insert(wire.output.name.clone(), *input_value);
}
});
// Use inputs to dispatch actions and apply effects
(
&read_data.entities,
&mut wiring_elements,
read_data.physics_states.maybe(),
(&mut light_emitters).maybe(),
read_data.pos.maybe(),
)
.join()
.for_each(
|(entity, wiring_element, physics_state, mut light_emitter, pos)| {
wiring_element
.actions
.iter()
.filter(|wiring_action| {
// Filter out any wiring actions with a total output less than the
// threshold
wiring_action.formula.compute_output(
&wiring_element.inputs,
physics_state,
&read_data.entities_died_last_tick.0,
pos,
) >= wiring_action.threshold
})
.for_each(|wiring_action| {
// Apply world effects of each wiring action
wiring_action.apply_effects(
entity,
&wiring_element.inputs,
physics_state,
&read_data.entities_died_last_tick.0,
&mut server_emitter,
pos,
&mut block_change,
light_emitter.as_deref_mut(),
);
})
},
)
}
}