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 {
50                                    msg: message,
51                                    from_client: true,
52                                });
53                            },
54                            Err(error) => {
55                                client.send_fallible(ServerGeneral::ChatMsg(
56                                    ChatType::CommandError.into_msg(error),
57                                ));
58                            },
59                        }
60                    } else {
61                        error!("Could not send message. Missing player uid");
62                    }
63                } else {
64                    warn!("Received a chat message from an unregistered client");
65                }
66            },
67            ClientGeneral::Command(name, args) => {
68                if player.is_some() {
69                    emitters.emit(event::CommandEvent(entity, name, args));
70                }
71            },
72            ClientGeneral::Terminate => {
73                debug!(?entity, "Client send message to terminate session");
74                emitters.emit(event::ClientDisconnectEvent(
75                    entity,
76                    common::comp::DisconnectReason::ClientRequested,
77                ));
78            },
79            ClientGeneral::RequestPlugins(plugins) => {
80                tracing::info!("Plugin request {plugins:x?}, {}", player.is_some());
81                emitters.emit(event::RequestPluginsEvent { entity, plugins });
82            },
83            _ => {
84                debug!("Kicking possible misbehaving client due to invalid message request");
85                emitters.emit(event::ClientDisconnectEvent(
86                    entity,
87                    common::comp::DisconnectReason::NetworkError,
88                ));
89            },
90        }
91        Ok(())
92    }
93}
94
95/// This system will handle new messages from clients
96#[derive(Default)]
97pub struct Sys;
98impl<'a> System<'a> for Sys {
99    type SystemData = (
100        Entities<'a>,
101        Events<'a>,
102        Read<'a, ProgramTime>,
103        ReadStorage<'a, Uid>,
104        ReadStorage<'a, ChatMode>,
105        ReadStorage<'a, Player>,
106        ReadStorage<'a, Group>,
107        WriteStorage<'a, Client>,
108    );
109
110    const NAME: &'static str = "msg::general";
111    const ORIGIN: Origin = Origin::Server;
112    const PHASE: Phase = Phase::Create;
113
114    fn run(
115        _job: &mut Job<Self>,
116        (entities, events, program_time, uids, chat_modes, players, groups, mut clients): Self::SystemData,
117    ) {
118        (&entities, &mut clients, players.maybe())
119            .par_join()
120            .for_each_init(
121                || events.get_emitters(),
122                |emitters, (entity, client, player)| {
123                    let res = super::try_recv_all(client, 3, |client, msg| {
124                        Self::handle_general_msg(
125                            emitters,
126                            entity,
127                            client,
128                            player,
129                            &uids,
130                            &chat_modes,
131                            &groups,
132                            msg,
133                        )
134                    });
135
136                    if let Ok(1_u64..=u64::MAX) = res {
137                        // Update client ping.
138                        client.last_ping = program_time.0
139                    }
140                },
141            );
142    }
143}