azalea_client/plugins/chat/
mod.rs1pub mod handler;
4
5use std::sync::Arc;
6
7use azalea_chat::FormattedText;
8use azalea_protocol::packets::game::{
9 c_disguised_chat::ClientboundDisguisedChat, c_player_chat::ClientboundPlayerChat,
10 c_system_chat::ClientboundSystemChat,
11};
12use bevy_app::{App, Plugin, Update};
13use bevy_ecs::prelude::*;
14use handler::{SendChatKindEvent, handle_send_chat_kind_event};
15use uuid::Uuid;
16
17use crate::client::Client;
18
19pub struct ChatPlugin;
20impl Plugin for ChatPlugin {
21 fn build(&self, app: &mut App) {
22 app.add_event::<SendChatEvent>()
23 .add_event::<SendChatKindEvent>()
24 .add_event::<ChatReceivedEvent>()
25 .add_systems(
26 Update,
27 (handle_send_chat_event, handle_send_chat_kind_event).chain(),
28 );
29 }
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub enum ChatPacket {
35 System(Arc<ClientboundSystemChat>),
36 Player(Arc<ClientboundPlayerChat>),
37 Disguised(Arc<ClientboundDisguisedChat>),
38}
39
40macro_rules! regex {
41 ($re:literal $(,)?) => {{
42 static RE: std::sync::LazyLock<regex::Regex> =
43 std::sync::LazyLock::new(|| regex::Regex::new($re).unwrap());
44 &RE
45 }};
46}
47
48impl ChatPacket {
49 pub fn message(&self) -> FormattedText {
51 match self {
52 ChatPacket::System(p) => p.content.clone(),
53 ChatPacket::Player(p) => p.message(),
54 ChatPacket::Disguised(p) => p.message(),
55 }
56 }
57
58 pub fn split_sender_and_content(&self) -> (Option<String>, String) {
63 match self {
64 ChatPacket::System(p) => {
65 let message = p.content.to_string();
66 if p.overlay {
68 return (None, message);
69 }
70 if let Some(m) = regex!("^<([a-zA-Z_0-9]{1,16})> (.+)$").captures(&message) {
73 return (Some(m[1].to_string()), m[2].to_string());
74 }
75
76 (None, message)
77 }
78 ChatPacket::Player(p) => (
79 Some(p.chat_type.name.to_string()),
82 p.body.content.clone(),
83 ),
84 ChatPacket::Disguised(p) => (
85 Some(p.chat_type.name.to_string()),
88 p.message.to_string(),
89 ),
90 }
91 }
92
93 pub fn sender(&self) -> Option<String> {
97 self.split_sender_and_content().0
98 }
99
100 pub fn sender_uuid(&self) -> Option<Uuid> {
104 match self {
105 ChatPacket::System(_) => None,
106 ChatPacket::Player(m) => Some(m.sender),
107 ChatPacket::Disguised(_) => None,
108 }
109 }
110
111 pub fn content(&self) -> String {
115 self.split_sender_and_content().1
116 }
117
118 pub fn new(message: &str) -> Self {
121 ChatPacket::System(Arc::new(ClientboundSystemChat {
122 content: FormattedText::from(message),
123 overlay: false,
124 }))
125 }
126
127 pub fn is_whisper(&self) -> bool {
131 match self.message() {
132 FormattedText::Text(_) => false,
133 FormattedText::Translatable(t) => t.key == "commands.message.display.incoming",
134 }
135 }
136}
137
138impl Client {
139 pub fn send_chat_packet(&self, message: &str) {
145 self.ecs.lock().send_event(SendChatKindEvent {
146 entity: self.entity,
147 content: message.to_string(),
148 kind: ChatKind::Message,
149 });
150 }
151
152 pub fn send_command_packet(&self, command: &str) {
158 self.ecs.lock().send_event(SendChatKindEvent {
159 entity: self.entity,
160 content: command.to_string(),
161 kind: ChatKind::Command,
162 });
163 }
164
165 pub fn chat(&self, content: &str) {
175 self.ecs.lock().send_event(SendChatEvent {
176 entity: self.entity,
177 content: content.to_string(),
178 });
179 }
180}
181
182#[derive(Event, Debug, Clone)]
184pub struct ChatReceivedEvent {
185 pub entity: Entity,
186 pub packet: ChatPacket,
187}
188
189#[derive(Event)]
191pub struct SendChatEvent {
192 pub entity: Entity,
193 pub content: String,
194}
195
196pub fn handle_send_chat_event(
197 mut events: EventReader<SendChatEvent>,
198 mut send_chat_kind_events: EventWriter<SendChatKindEvent>,
199) {
200 for event in events.read() {
201 if event.content.starts_with('/') {
202 send_chat_kind_events.write(SendChatKindEvent {
203 entity: event.entity,
204 content: event.content[1..].to_string(),
205 kind: ChatKind::Command,
206 });
207 } else {
208 send_chat_kind_events.write(SendChatKindEvent {
209 entity: event.entity,
210 content: event.content.clone(),
211 kind: ChatKind::Message,
212 });
213 }
214 }
215}
216
217pub enum ChatKind {
219 Message,
220 Command,
221}
222
223