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