azalea_client/plugins/chat/
handler.rs

1use std::time::{SystemTime, UNIX_EPOCH};
2
3use azalea_protocol::packets::{
4    Packet,
5    game::{ServerboundChat, ServerboundChatCommand, s_chat::LastSeenMessagesUpdate},
6};
7use bevy_ecs::prelude::*;
8
9use super::ChatKind;
10use crate::packet::game::SendPacketEvent;
11
12/// Send a chat packet to the server of a specific kind (chat message or
13/// command). Usually you just want [`SendChatEvent`] instead.
14///
15/// Usually setting the kind to `Message` will make it send a chat message even
16/// if it starts with a slash, but some server implementations will always do a
17/// command if it starts with a slash.
18///
19/// If you're wondering why this isn't two separate events, it's so ordering is
20/// preserved if multiple chat messages and commands are sent at the same time.
21///
22/// [`SendChatEvent`]: super::SendChatEvent
23#[derive(Event)]
24pub struct SendChatKindEvent {
25    pub entity: Entity,
26    pub content: String,
27    pub kind: ChatKind,
28}
29
30pub fn handle_send_chat_kind_event(
31    mut events: EventReader<SendChatKindEvent>,
32    mut commands: Commands,
33) {
34    for event in events.read() {
35        let content = event
36            .content
37            .chars()
38            .filter(|c| !matches!(c, '\x00'..='\x1F' | '\x7F' | 'ยง'))
39            .take(256)
40            .collect::<String>();
41        let packet = match event.kind {
42            ChatKind::Message => ServerboundChat {
43                message: content,
44                timestamp: SystemTime::now()
45                    .duration_since(UNIX_EPOCH)
46                    .expect("Time shouldn't be before epoch")
47                    .as_millis()
48                    .try_into()
49                    .expect("Instant should fit into a u64"),
50                salt: azalea_crypto::make_salt(),
51                signature: None,
52                last_seen_messages: LastSeenMessagesUpdate::default(),
53            }
54            .into_variant(),
55            ChatKind::Command => {
56                // TODO: chat signing
57                ServerboundChatCommand { command: content }.into_variant()
58            }
59        };
60
61        commands.trigger(SendPacketEvent::new(event.entity, packet));
62    }
63}