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 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#[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 client.last_ping = program_time.0
139 }
140 },
141 );
142 }
143}