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
use common::{comp::Player, util::GIT_DATE_TIMESTAMP};
use common_ecs::{Origin, Phase, System};
use lazy_static::lazy_static;
use specs::{Join, Read, ReadStorage};
use tracing::warn;
use veloren_query_server::proto::ServerInfo;

use crate::{client::Client, Settings, Tick};

// Update the server stats every 60 ticks
const INFO_SEND_INTERVAL: u64 = 60;

lazy_static! {
    pub static ref GIT_HASH: u32 =
        u32::from_str_radix(&common::util::GIT_HASH[..8], 16).expect("Invalid git hash");
}

#[derive(Default)]
pub struct Sys;

impl<'a> System<'a> for Sys {
    type SystemData = (
        Read<'a, Tick>,
        Read<'a, Settings>,
        Option<Read<'a, tokio::sync::watch::Sender<ServerInfo>>>,
        ReadStorage<'a, Player>,
        ReadStorage<'a, Client>,
    );

    const NAME: &'static str = "server_info";
    const ORIGIN: Origin = Origin::Server;
    const PHASE: Phase = Phase::Create;

    fn run(
        _job: &mut common_ecs::Job<Self>,
        (tick, settings, sender, players, clients): Self::SystemData,
    ) {
        if let Some(sender) = sender.as_ref()
            && tick.0 % INFO_SEND_INTERVAL == 0
        {
            let count = (&players, &clients)
                .join()
                // Hide silent spectators from the player count
                .filter(|(_, client)| client.client_type.emit_login_events())
                .count()
                .try_into()
                .unwrap_or(u16::MAX);
            if let Err(e) = sender.send(ServerInfo {
                git_hash: *GIT_HASH,
                git_timestamp: *GIT_DATE_TIMESTAMP,
                players_count: count,
                player_cap: settings.max_players,
                battlemode: settings.gameplay.battle_mode.into(),
            }) {
                warn!(?e, "Failed to send server info to the query server");
            }
        }
    }
}