arcdps\extras\message/squad.rs
1use crate::{strip_account_prefix, util::str_from_cstr_len};
2use bitflags::bitflags;
3use chrono::{DateTime, FixedOffset};
4use std::ffi::c_char;
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9#[cfg(feature = "strum")]
10use strum::{Display, EnumCount, EnumIter, IntoStaticStr, VariantNames};
11
12/// A squad/party chat message.
13///
14/// Strings are available for the duration of the call.
15/// If you need it for longer than that, consider converting it to [`SquadMessageOwned`].
16///
17/// ```no_run
18/// # use arcdps::extras::{SquadMessage, SquadMessageOwned};
19/// # let message: &SquadMessage = todo!();
20/// let owned = message.to_owned();
21/// let owned: SquadMessageOwned = message.into();
22/// ```
23#[derive(Debug, Clone)]
24#[repr(C)]
25pub struct SquadMessage {
26 /// A unique identifier for the channel this chat message was sent over.
27 ///
28 /// Can be used to, for example, differentiate between squad messages sent to different squads.
29 pub channel_id: u32,
30
31 /// Whether the message is sent in a party or a squad.
32 ///
33 /// Note that messages sent to the party chat while in a squad will have the type [`ChannelType::Squad`].
34 pub channel_type: ChannelType,
35
36 /// The subgroup the message was sent to, or `0` if it was sent to the entire squad.
37 pub subgroup: u8,
38
39 /// The lowest bit of this field will be set to `1` if the message is a broadcast, and `0` if it is not a broadcast.
40 /// The upper bits of this field may be used in a later version and **must not** be interpreted.
41 flags: u8,
42
43 /// Unused padding.
44 _unused1: u8,
45
46 /// Null terminated iso8601 formatted string denoting when this message was
47 /// received by the server, e.g. `"2022-07-09T11:45:24.888Z"`.
48 /// This is the "absolute ordering" for chat messages,
49 /// however the time can potentially differ several seconds between the client and server because of latency and clock skew.
50 ///
51 /// The string is only valid for the duration of the call.
52 timestamp: *const c_char,
53 timestamp_length: u64,
54
55 /// Null terminated account name of the player that sent the message, including leading ':'.
56 ///
57 /// The string is only valid for the duration of the call.
58 account_name: *const c_char,
59 account_name_length: u64,
60
61 /// Null terminated character name of the player that sent the message.
62 ///
63 /// The string is only valid for the duration of the call.
64 character_name: *const c_char,
65 character_name_length: u64,
66
67 /// Null terminated string containing the content of the message that was sent.
68 ///
69 /// The string is only valid for the duration of the call.
70 text: *const c_char,
71 text_length: u64,
72}
73
74impl SquadMessage {
75 /// Converts the squad message to its owned counterpart.
76 #[inline]
77 pub fn to_owned(&self) -> SquadMessageOwned {
78 self.into()
79 }
80
81 /// Returns the raw message flags.
82 #[inline]
83 pub fn flags_raw(&self) -> u8 {
84 self.flags
85 }
86
87 /// Returns the message flags.
88 #[inline]
89 pub fn flags(&self) -> SquadMessageFlags {
90 SquadMessageFlags::from_bits_truncate(self.flags)
91 }
92
93 /// Returns the message flags.
94 #[inline]
95 pub fn is_broadcast(&self) -> bool {
96 self.flags().contains(SquadMessageFlags::IS_BROADCAST)
97 }
98
99 /// Returns the timestamp as string.
100 #[inline]
101 pub fn timestamp_str(&self) -> &str {
102 unsafe { str_from_cstr_len(self.timestamp, self.timestamp_length) }
103 }
104
105 /// Returns the timestamp string as raw pointer.
106 #[inline]
107 pub fn timestamp_ptr(&self) -> *const c_char {
108 self.timestamp
109 }
110
111 /// Returns the timestamp string length.
112 #[inline]
113 pub fn timestamp_len(&self) -> usize {
114 self.timestamp_length as _
115 }
116
117 /// Returns the timestamp when the message was received.
118 ///
119 /// This is the "absolute ordering" for chat messages,
120 /// however the time can potentially differ several seconds between the client and server because of latency and clock skew.
121 #[inline]
122 pub fn timestamp(&self) -> Option<DateTime<FixedOffset>> {
123 DateTime::parse_from_rfc3339(self.timestamp_str()).ok()
124 }
125
126 /// Returns the account name of the player that sent the message.
127 #[inline]
128 pub fn account_name(&self) -> &str {
129 let account_name =
130 unsafe { str_from_cstr_len(self.account_name, self.account_name_length) };
131 strip_account_prefix(account_name)
132 }
133
134 /// Returns the account name as raw pointer.
135 #[inline]
136 pub fn account_name_ptr(&self) -> *const c_char {
137 self.account_name
138 }
139
140 /// Returns the account name length.
141 #[inline]
142 pub fn account_name_len(&self) -> usize {
143 self.account_name_length as _
144 }
145
146 /// Returns the character name of the player that sent the message.
147 #[inline]
148 pub fn character_name(&self) -> &str {
149 unsafe { str_from_cstr_len(self.character_name, self.character_name_length) }
150 }
151
152 /// Returns the character name as raw pointer.
153 #[inline]
154 pub fn character_name_ptr(&self) -> *const c_char {
155 self.character_name
156 }
157
158 /// Returns the account name length.
159 #[inline]
160 pub fn character_name_len(&self) -> usize {
161 self.character_name_length as _
162 }
163
164 /// Returns the text content of the message.
165 #[inline]
166 pub fn text(&self) -> &str {
167 unsafe { str_from_cstr_len(self.text, self.text_length) }
168 }
169
170 /// Returns the text as raw pointer.
171 #[inline]
172 pub fn text_ptr(&self) -> *const c_char {
173 self.text
174 }
175
176 /// Returns the account name length.
177 #[inline]
178 pub fn text_len(&self) -> usize {
179 self.text_length as _
180 }
181}
182
183/// [`SquadMessage`] with owned [`String`] fields.
184#[derive(Debug, Clone)]
185#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
186pub struct SquadMessageOwned {
187 /// A unique identifier for the channel this chat message was sent over.
188 ///
189 /// Can be used to, for example, differentiate between squad messages sent to different squads.
190 pub channel_id: u32,
191
192 /// Whether the message is sent in a party or a squad.
193 ///
194 /// Note that messages sent to the party chat while in a squad will have the type [`ChannelType::Squad`].
195 pub channel_type: ChannelType,
196
197 /// The subgroup the message was sent to, or `0` if it was sent to the entire squad.
198 pub subgroup: u8,
199
200 /// Whether the message is a broadcast.
201 pub flags: SquadMessageFlags,
202
203 /// Timestamp when the message was received.
204 ///
205 /// This is the "absolute ordering" for chat messages,
206 /// however the time can potentially differ several seconds between the client and server because of latency and clock skew.
207 pub timestamp: Option<DateTime<FixedOffset>>,
208
209 /// Account name of the player that sent the message.
210 pub account_name: String,
211
212 /// Character name of the player that sent the message.
213 pub character_name: String,
214
215 /// Content of the message.
216 pub text: String,
217}
218
219impl From<SquadMessage> for SquadMessageOwned {
220 #[inline]
221 fn from(msg: SquadMessage) -> Self {
222 (&msg).into()
223 }
224}
225
226impl From<&SquadMessage> for SquadMessageOwned {
227 #[inline]
228 fn from(msg: &SquadMessage) -> Self {
229 Self {
230 channel_id: msg.channel_id,
231 channel_type: msg.channel_type,
232 subgroup: msg.subgroup,
233 flags: SquadMessageFlags::from_bits_truncate(msg.flags),
234 timestamp: msg.timestamp(),
235 account_name: msg.account_name().to_owned(),
236 character_name: msg.character_name().to_owned(),
237 text: msg.text().to_owned(),
238 }
239 }
240}
241
242bitflags! {
243 /// Squad message flags.
244 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
245 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
246 pub struct SquadMessageFlags : u8 {
247 /// Message is a broadcast.
248 const IS_BROADCAST = 1;
249 }
250}
251
252/// Type of message channel.
253#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
254#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
255#[cfg_attr(
256 feature = "strum",
257 derive(Display, EnumCount, EnumIter, IntoStaticStr, VariantNames)
258)]
259#[repr(u8)]
260pub enum ChannelType {
261 Party = 0,
262 Squad = 1,
263 Reserved = 2,
264 Invalid = 3,
265}