veloren_server/sys/msg/
general.rs

1use crate::client::Client;
2use common::{
3    comp::{ChatMode, ChatType, Content, Group, Player},
4    event::{self, EmitExt},
5    event_emitters,
6    resources::ProgramTime,
7    uid::Uid,
8};
9use common_ecs::{Job, Origin, Phase, System};
10use common_net::msg::{ClientGeneral, ServerGeneral};
11use rayon::prelude::*;
12use specs::{Entities, LendJoin, ParJoin, Read, ReadStorage, WriteStorage};
13use tracing::{debug, error, warn};
14
15event_emitters! {
16    struct Events[Emitters] {
17        command: event::CommandEvent,
18        client_disconnect: event::ClientDisconnectEvent,
19        chat: event::ChatEvent,
20        plugins: event::RequestPluginsEvent,
21    }
22}
23
24impl Sys {
25    fn handle_general_msg(
26        emitters: &mut Emitters,
27        entity: specs::Entity,
28        client: &Client,
29        player: Option<&Player>,
30        uids: &ReadStorage<'_, Uid>,
31        chat_modes: &ReadStorage<'_, ChatMode>,
32        groups: &ReadStorage<'_, Group>,
33        msg: ClientGeneral,
34    ) -> Result<(), crate::error::Error> {
35        match msg {
36            ClientGeneral::ChatMsg(message) => {
37                if !client.client_type.can_send_message() {
38                    client.send_fallible(ServerGeneral::ChatMsg(
39                        ChatType::CommandError
40                            .into_msg(Content::localized("command-cannot-send-message-hidden")),
41                    ));
42                } else if player.is_some() {
43                    if let Some(from) = uids.get(entity) {
44                        const CHAT_MODE_DEFAULT: &ChatMode = &ChatMode::default();
45                        let mode = chat_modes.get(entity).unwrap_or(CHAT_MODE_DEFAULT);
46                        // Try sending the chat message
47                        match mode.to_msg(*from, message, groups.get(entity).copied()) {
48                            Ok(message) => {
49                                emitters.emit(event::ChatEvent(message));
50                            },
51                            Err(error) => {
52                                client.send_fallible(ServerGeneral::ChatMsg(
53                                    ChatType::CommandError.into_msg(error),
54                                ));
55                            },
56                        }
57                    } else {
58                        error!("Could not send message. Missing player uid");
59                    }
60                } else {
61                    warn!("Received a chat message from an unregistered client");
62                }
63            },
64            ClientGeneral::Command(name, args) => {
65                if player.is_some() {
66                    emitters.emit(event::CommandEvent(entity, name, args));
67                }
68            },
69            ClientGeneral::Terminate => {
70                debug!(?entity, "Client send message to terminate session");
71                emitters.emit(event::ClientDisconnectEvent(
72                    entity,
73                    common::comp::DisconnectReason::ClientRequested,
74                ));
75            },
76            ClientGeneral::RequestPlugins(plugins) => {
77                tracing::info!("Plugin request {plugins:x?}, {}", player.is_some());
78                emitters.emit(event::RequestPluginsEvent { entity, plugins });
79            },
80            _ => {
81                debug!("Kicking possible misbehaving client due to invalid message request");
82                emitters.emit(event::ClientDisconnectEvent(
83                    entity,
84                    common::comp::DisconnectReason::NetworkError,
85                ));
86            },
87        }
88        Ok(())
89    }
90}
91
92/// This system will handle new messages from clients
93#[derive(Default)]
94pub struct Sys;
95impl<'a> System<'a> for Sys {
96    type SystemData = (
97        Entities<'a>,
98        Events<'a>,
99        Read<'a, ProgramTime>,
100        ReadStorage<'a, Uid>,
101        ReadStorage<'a, ChatMode>,
102        ReadStorage<'a, Player>,
103        ReadStorage<'a, Group>,
104        WriteStorage<'a, Client>,
105    );
106
107    const NAME: &'static str = "msg::general";
108    const ORIGIN: Origin = Origin::Server;
109    const PHASE: Phase = Phase::Create;
110
111    fn run(
112        _job: &mut Job<Self>,
113        (entities, events, program_time, uids, chat_modes, players, groups, mut clients): Self::SystemData,
114    ) {
115        (&entities, &mut clients, players.maybe())
116            .par_join()
117            .for_each_init(
118                || events.get_emitters(),
119                |emitters, (entity, client, player)| {
120                    let res = super::try_recv_all(client, 3, |client, msg| {
121                        Self::handle_general_msg(
122                            emitters,
123                            entity,
124                            client,
125                            player,
126                            &uids,
127                            &chat_modes,
128                            &groups,
129                            msg,
130                        )
131                    });
132
133                    if let Ok(1_u64..=u64::MAX) = res {
134                        // Update client ping.
135                        client.last_ping = program_time.0
136                    }
137                },
138            );
139    }
140}