veloren_server/sys/msg/
ping.rs

1use crate::{Settings, client::Client};
2use common::{
3    event::{ClientDisconnectEvent, EventBus},
4    resources::ProgramTime,
5};
6use common_ecs::{Job, Origin, Phase, System};
7use common_net::msg::PingMsg;
8use rayon::prelude::*;
9use specs::{Entities, ParJoin, Read, WriteStorage};
10use tracing::{debug, info};
11
12impl Sys {
13    fn handle_ping_msg(client: &Client, msg: PingMsg) -> Result<(), crate::error::Error> {
14        match msg {
15            PingMsg::Ping => client.send(PingMsg::Pong)?,
16            PingMsg::Pong => {},
17        }
18        Ok(())
19    }
20}
21
22/// This system will handle new messages from clients
23#[derive(Default)]
24pub struct Sys;
25impl<'a> System<'a> for Sys {
26    type SystemData = (
27        Entities<'a>,
28        Read<'a, EventBus<ClientDisconnectEvent>>,
29        Read<'a, ProgramTime>,
30        WriteStorage<'a, Client>,
31        Read<'a, Settings>,
32    );
33
34    const NAME: &'static str = "msg::ping";
35    const ORIGIN: Origin = Origin::Server;
36    const PHASE: Phase = Phase::Create;
37
38    fn run(
39        _job: &mut Job<Self>,
40        (entities, client_disconnect, program_time, mut clients, settings): Self::SystemData,
41    ) {
42        (&entities, &mut clients).par_join().for_each_init(
43            || client_disconnect.emitter(),
44            |client_disconnect_emitter, (entity, client)| {
45                let res = super::try_recv_all(client, 4, Self::handle_ping_msg);
46
47                match res {
48                    Err(e) => {
49                        debug!(?entity, ?e, "network error with client, disconnecting");
50                        client_disconnect_emitter.emit(ClientDisconnectEvent(
51                            entity,
52                            common::comp::DisconnectReason::NetworkError,
53                        ));
54                    },
55                    Ok(1_u64..=u64::MAX) => {
56                        // Update client ping.
57                        client.last_ping = program_time.0
58                    },
59                    Ok(0) => {
60                        let last_ping: f64 = client.last_ping;
61                        if program_time.0 - last_ping > settings.client_timeout.as_secs() as f64
62                        // Timeout
63                        {
64                            info!(?entity, "timeout error with client, disconnecting");
65                            client_disconnect_emitter.emit(ClientDisconnectEvent(
66                                entity,
67                                common::comp::DisconnectReason::Timeout,
68                            ));
69                        } else if program_time.0 - last_ping
70                            > settings.client_timeout.as_secs() as f64 * 0.5
71                        {
72                            // Try pinging the client if the timeout is nearing.
73                            client.send_fallible(PingMsg::Ping);
74                        }
75                    },
76                }
77            },
78        );
79    }
80}