veloren_server/sys/
waypoint.rs

1use std::sync::Arc;
2
3use crate::client::Client;
4use common::{
5    comp::{PhysicsState, Player, Pos, Vel, Waypoint, WaypointArea},
6    resources::Time,
7};
8use common_ecs::{Job, Origin, Phase, System};
9use common_net::msg::{Notification, ServerGeneral};
10use specs::{Entities, Join, LendJoin, Read, ReadExpect, ReadStorage, WriteStorage};
11use world::{IndexOwned, World};
12
13/// Cooldown time (in seconds) for "Waypoint Saved" notifications
14const NOTIFY_TIME: f64 = 10.0;
15
16/// This system updates player waypoints
17/// TODO: Make this faster by only considering local waypoints
18#[derive(Default)]
19pub struct Sys;
20impl<'a> System<'a> for Sys {
21    type SystemData = (
22        Entities<'a>,
23        ReadStorage<'a, Pos>,
24        ReadStorage<'a, Player>,
25        ReadStorage<'a, WaypointArea>,
26        WriteStorage<'a, Waypoint>,
27        ReadStorage<'a, Client>,
28        Read<'a, Time>,
29        ReadStorage<'a, PhysicsState>,
30        ReadStorage<'a, Vel>,
31        ReadExpect<'a, Arc<World>>,
32        ReadExpect<'a, IndexOwned>,
33    );
34
35    const NAME: &'static str = "waypoint";
36    const ORIGIN: Origin = Origin::Server;
37    const PHASE: Phase = Phase::Create;
38
39    fn run(
40        _job: &mut Job<Self>,
41        (
42            entities,
43            positions,
44            players,
45            waypoint_areas,
46            mut waypoints,
47            clients,
48            time,
49            physics_states,
50            velocities,
51            world,
52            index,
53        ): Self::SystemData,
54    ) {
55        for (entity, player_pos, _, client, physics, velocity) in (
56            &entities,
57            &positions,
58            &players,
59            &clients,
60            physics_states.maybe(),
61            &velocities,
62        )
63            .join()
64        {
65            if physics.is_none_or(|ps| ps.on_ground.is_some()) && velocity.0.z >= 0.0 {
66                for (waypoint_pos, waypoint_area) in (&positions, &waypoint_areas).join() {
67                    if player_pos.0.distance_squared(waypoint_pos.0)
68                        < waypoint_area.radius().powi(2)
69                    {
70                        if let Ok(wp_old) =
71                            waypoints.insert(entity, Waypoint::new(player_pos.0, *time))
72                        {
73                            if wp_old.is_none_or(|w| w.elapsed(*time) > NOTIFY_TIME) {
74                                let location_name = world.get_location_name(
75                                    index.as_index_ref(),
76                                    player_pos.0.xy().as_::<i32>(),
77                                );
78
79                                if let Some(location_name) = location_name {
80                                    client.send_fallible(ServerGeneral::Notification(
81                                        Notification::WaypointSaved { location_name },
82                                    ));
83                                }
84                            }
85                        }
86                    }
87                }
88            }
89        }
90    }
91}