From b509916edf860b6ccaf62d3592980c8965983b77 Mon Sep 17 00:00:00 2001 From: Zerthox Date: Mon, 20 Jan 2025 21:55:01 +0100 Subject: [PATCH] Adjust message callback types --- arcdps/Cargo.toml | 1 + arcdps/src/extras/callbacks.rs | 8 +- arcdps/src/extras/message.rs | 343 ----------------------------- arcdps/src/extras/message/mod.rs | 64 ++++++ arcdps/src/extras/message/npc.rs | 103 +++++++++ arcdps/src/extras/message/squad.rs | 265 ++++++++++++++++++++++ arcdps_codegen/src/extras.rs | 7 +- arcdps_example_plugin/src/lib.rs | 16 +- evtc/src/agent/realtime.rs | 13 +- 9 files changed, 458 insertions(+), 362 deletions(-) delete mode 100644 arcdps/src/extras/message.rs create mode 100644 arcdps/src/extras/message/mod.rs create mode 100644 arcdps/src/extras/message/npc.rs create mode 100644 arcdps/src/extras/message/squad.rs diff --git a/arcdps/Cargo.toml b/arcdps/Cargo.toml index e5bf79f264..82b9c2b2da 100644 --- a/arcdps/Cargo.toml +++ b/arcdps/Cargo.toml @@ -12,6 +12,7 @@ license = "MIT" [dependencies] arcdps_codegen = { path = "../arcdps_codegen" } arcdps-imgui = { version = "0.8.0", features = ["tables-api"] } +bitflags = "2.8.0" chrono = { version = "0.4.24", optional = true } evtc = { path = "../evtc", features = ["realtime"]} log = { version = "0.4.17", features = ["std"], optional = true } diff --git a/arcdps/src/extras/callbacks.rs b/arcdps/src/extras/callbacks.rs index 0c7364f613..c25f23f3e1 100644 --- a/arcdps/src/extras/callbacks.rs +++ b/arcdps/src/extras/callbacks.rs @@ -2,7 +2,7 @@ use super::{ keybinds::{KeybindChange, RawKeybindChange}, - message::{Message, RawMessage, RawMessageType, RawSquadMessage, SquadMessage}, + message::{Message, MessageType, RawMessage, SquadMessage}, user::{UserInfo, UserInfoIter}, ExtrasAddonInfo, ExtrasSubscriberInfo, RawExtrasAddonInfo, }; @@ -16,7 +16,7 @@ pub type ExtrasLanguageChangedCallback = fn(language: Language); pub type ExtrasKeybindChangedCallback = fn(changed: KeybindChange); -pub type ExtrasSquadChatMessageCallback = fn(message: SquadMessage); +pub type ExtrasSquadChatMessageCallback = fn(message: &SquadMessage); pub type ExtrasChatMessageCallback = fn(message: Message); @@ -34,8 +34,8 @@ abi! { pub type RawExtrasKeybindChangedCallback = unsafe extern fn(changed: RawKeybindChange); pub type RawExtrasSquadChatMessageCallback = - unsafe extern fn(message: *const RawSquadMessage); + unsafe extern fn(message: *const SquadMessage); pub type RawExtrasChatMessageCallback = - unsafe extern fn(message_type: RawMessageType, message: RawMessage); + unsafe extern fn(message_type: MessageType, message: RawMessage); } diff --git a/arcdps/src/extras/message.rs b/arcdps/src/extras/message.rs deleted file mode 100644 index 476e83ae07..0000000000 --- a/arcdps/src/extras/message.rs +++ /dev/null @@ -1,343 +0,0 @@ -//! Message information provided by Unofficial Extras. - -use crate::{strip_account_prefix, util::str_from_cstr_len}; -use chrono::{DateTime, FixedOffset}; -use std::os::raw::c_char; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "strum")] -use strum::{Display, EnumCount, EnumIter, IntoStaticStr, VariantNames}; - -/// A squad/party chat message. -/// -/// Strings are available for the duration of the call. -/// If you need it for longer than that, consider converting it to [`SquadMessageOwned`]. -/// -/// ```no_run -/// # use arcdps::extras::{SquadMessage, SquadMessageOwned}; -/// # let message: SquadMessage = todo!(); -/// let owned = message.to_owned(); -/// let owned: SquadMessageOwned = message.into(); -/// ``` -#[derive(Debug, Clone)] -pub struct SquadMessage<'a> { - /// A unique identifier for the channel this chat message was sent over. - /// - /// Can be used to for example differentiate between squad messages sent to different squads. - pub channel_id: u32, - - /// Whether the message is sent in a party or a squad. - /// - /// Note that messages sent to the party chat while in a squad will have the type [`ChannelType::Squad`]. - pub channel_type: ChannelType, - - /// The subgroup the message was sent to, or `0` if it was sent to the entire squad. - pub subgroup: u8, - - /// Whether the message is a broadcast. - pub is_broadcast: bool, - - /// Timestamp when the message was received. - /// - /// This is the "absolute ordering" for chat messages, - /// however the time can potentially differ several seconds between the client and server because of latency and clock skew. - pub timestamp: DateTime, - - /// Account name of the player that sent the message. - pub account_name: &'a str, - - /// Character name of the player that sent the message. - pub character_name: &'a str, - - /// Content of the message. - pub text: &'a str, -} - -impl SquadMessage<'_> { - /// Converts the message to its owned counterpart. - #[inline] - pub fn to_owned(self) -> SquadMessageOwned { - self.into() - } -} - -impl<'a> From<&'a RawSquadMessage> for SquadMessage<'a> { - fn from(raw: &RawSquadMessage) -> Self { - let timestamp = unsafe { str_from_cstr_len(raw.timestamp, raw.timestamp_length) }; - let timestamp = - DateTime::parse_from_rfc3339(timestamp).expect("failed to parse message timestamp"); - - let account_name = unsafe { str_from_cstr_len(raw.account_name, raw.account_name_length) }; - let character_name = - unsafe { str_from_cstr_len(raw.character_name, raw.character_name_length) }; - let text = unsafe { str_from_cstr_len(raw.text, raw.text_length) }; - - let is_broadcast = (raw.is_broadcast & 0x01) != 0; - - Self { - channel_id: raw.channel_id, - channel_type: raw.channel_type, - subgroup: raw.subgroup, - is_broadcast, - timestamp, - account_name: strip_account_prefix(account_name), - character_name, - text, - } - } -} - -/// [`SquadMessage`] with owned [`String`] fields. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct SquadMessageOwned { - /// A unique identifier for the channel this chat message was sent over. - /// - /// Can be used to, for example, differentiate between squad messages sent to different squads. - pub channel_id: u32, - - /// Whether the message is sent in a party or a squad. - /// - /// Note that messages sent to the party chat while in a squad will have the type [`ChannelType::Squad`]. - pub channel_type: ChannelType, - - /// The subgroup the message was sent to, or `0` if it was sent to the entire squad. - pub subgroup: u8, - - /// Whether the message is a broadcast. - pub is_broadcast: bool, - - /// Timestamp when the message was received. - /// - /// This is the "absolute ordering" for chat messages, - /// however the time can potentially differ several seconds between the client and server because of latency and clock skew. - pub timestamp: DateTime, - - /// Account name of the player that sent the message. - pub account_name: String, - - /// Character name of the player that sent the message. - pub character_name: String, - - /// Content of the message. - pub text: String, -} - -impl From> for SquadMessageOwned { - #[inline] - fn from(msg: SquadMessage<'_>) -> Self { - Self { - channel_id: msg.channel_id, - channel_type: msg.channel_type, - subgroup: msg.subgroup, - is_broadcast: msg.is_broadcast, - timestamp: msg.timestamp, - account_name: msg.account_name.to_string(), - character_name: msg.character_name.to_string(), - text: msg.text.to_string(), - } - } -} - -/// Raw chat message information. -#[derive(Debug, Clone)] -#[repr(C)] -pub struct RawSquadMessage { - /// A unique identifier for the channel this chat message was sent over. - /// - /// Can be used to, for example, differentiate between squad messages sent to different squads. - pub channel_id: u32, - - /// Whether the message is sent in a party or a squad. - /// - /// Note that messages sent to the party chat while in a squad will have the type [`ChannelType::Squad`]. - pub channel_type: ChannelType, - - /// The subgroup the message was sent to, or `0` if it was sent to the entire squad. - pub subgroup: u8, - - /// 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. - /// The upper bits of this field may be used in a later version and **must not** be interpreted. - pub is_broadcast: u8, - - /// Unused padding. - pub _unused1: u8, - - /// Null terminated iso8601 formatted string denoting when this message was - /// received by the server, e.g. `"2022-07-09T11:45:24.888Z"`. - /// This is the "absolute ordering" for chat messages, - /// however the time can potentially differ several seconds between the client and server because of latency and clock skew. - /// - /// The string is only valid for the duration of the call. - pub timestamp: *const c_char, - pub timestamp_length: u64, - - /// Null terminated account name of the player that sent the message, including leading ':'. - /// - /// The string is only valid for the duration of the call. - pub account_name: *const c_char, - pub account_name_length: u64, - - /// Null terminated character name of the player that sent the message. - /// - /// The string is only valid for the duration of the call. - pub character_name: *const c_char, - pub character_name_length: u64, - - /// Null terminated string containing the content of the message that was sent. - /// - /// The string is only valid for the duration of the call. - pub text: *const c_char, - pub text_length: u64, -} - -/// Type of message channel. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "strum", - derive(Display, EnumCount, EnumIter, IntoStaticStr, VariantNames) -)] -#[repr(u8)] -pub enum ChannelType { - Party = 0, - Squad = 1, - Reserved = 2, - Invalid = 3, -} - -/// An NPC chat message. -/// -/// Strings are available for the duration of the call. -/// If you need it for longer than that, consider converting it to [`NpcMessageOwned`]. -/// -/// ```no_run -/// # use arcdps::extras::{NpcMessage, NpcMessageOwned}; -/// # let message: NpcMessage = todo!(); -/// let owned = message.to_owned(); -/// let owned: NpcMessageOwned = message.into(); -/// ``` -#[derive(Debug, Clone)] -pub struct NpcMessage<'a> { - /// Character name of the NPC that sent the message. - pub character_name: &'a str, - - /// Content of the message. - pub text: &'a str, -} - -impl NpcMessage<'_> { - /// Converts the message to its owned counterpart. - #[inline] - pub fn to_owned(self) -> NpcMessageOwned { - self.into() - } -} - -impl<'a> From<&'a RawNpcMessage> for NpcMessage<'a> { - #[inline] - fn from(raw: &RawNpcMessage) -> Self { - let character_name = - unsafe { str_from_cstr_len(raw.character_name, raw.character_name_length) }; - let text = unsafe { str_from_cstr_len(raw.text, raw.text_length) }; - - Self { - character_name, - text, - } - } -} - -/// [`NpcMessage`] with owned [`String`] fields. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct NpcMessageOwned { - /// Character name of the NPC that sent the message. - pub character_name: String, - - /// Content of the message. - pub text: String, -} - -impl From> for NpcMessageOwned { - #[inline] - fn from(msg: NpcMessage<'_>) -> Self { - Self { - character_name: msg.character_name.to_string(), - text: msg.text.to_string(), - } - } -} - -/// Raw NPC chat message information. -#[derive(Debug, Clone)] -#[repr(C)] -pub struct RawNpcMessage { - /// Null terminated character name of the NPC that sent the message. - /// - /// The string is only valid for the duration of the call. - pub character_name: *const c_char, - pub character_name_length: u64, - - /// Null terminated string containing the content of the message that was sent. - /// - /// The string is only valid for the duration of the call. - pub text: *const c_char, - pub text_length: u64, -} - -/// A chat message. -#[derive(Clone, Copy)] -#[repr(C)] -pub union RawMessage { - squad: *const RawSquadMessage, - npc: *const RawNpcMessage, -} - -/// A chat message. -#[derive(Debug, Clone)] -pub enum Message<'a> { - Squad(SquadMessage<'a>), - Npc(NpcMessage<'a>), -} - -impl Message<'_> { - /// Creates a new message from [`RawMessageType`] and [`RawMessage`]. - #[inline] - pub unsafe fn new(message_type: RawMessageType, message: RawMessage) -> Self { - match message_type { - RawMessageType::Squad => Self::Squad( - message - .squad - .as_ref() - .expect("invalid unofficial extras squad message info") - .into(), - ), - RawMessageType::Npc => Self::Npc( - message - .npc - .as_ref() - .expect("invalid unofficial extras npc message info") - .into(), - ), - } - } -} - -/// Type of message. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "strum", - derive(Display, EnumCount, EnumIter, IntoStaticStr, VariantNames) -)] -#[repr(C)] -pub enum RawMessageType { - /// Party or squad chat message. - Squad = 0, - - /// NPC message (selectable in ingame-chat as "NPC"). - Npc = 1, -} diff --git a/arcdps/src/extras/message/mod.rs b/arcdps/src/extras/message/mod.rs new file mode 100644 index 0000000000..dbd8818caf --- /dev/null +++ b/arcdps/src/extras/message/mod.rs @@ -0,0 +1,64 @@ +//! Message information provided by Unofficial Extras. + +mod npc; +mod squad; + +pub use self::{npc::*, squad::*}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "strum")] +use strum::{Display, EnumCount, EnumIter, IntoStaticStr, VariantNames}; + +/// A chat message. +#[derive(Clone, Copy)] +#[repr(C)] +pub union RawMessage { + squad: *const SquadMessage, + npc: *const NpcMessage, +} + +/// A chat message. +#[derive(Debug, Clone)] +pub enum Message<'a> { + Squad(&'a SquadMessage), + Npc(&'a NpcMessage), +} + +impl Message<'_> { + /// Creates a new message from [`RawMessageType`] and [`RawMessage`]. + #[inline] + pub unsafe fn new(message_type: MessageType, message: RawMessage) -> Self { + match message_type { + MessageType::Squad => Self::Squad( + message + .squad + .as_ref() + .expect("invalid unofficial extras squad message info"), + ), + MessageType::Npc => Self::Npc( + message + .npc + .as_ref() + .expect("invalid unofficial extras npc message info"), + ), + } + } +} + +/// Type of message. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "strum", + derive(Display, EnumCount, EnumIter, IntoStaticStr, VariantNames) +)] +#[repr(C)] +pub enum MessageType { + /// Party or squad chat message. + Squad = 0, + + /// NPC message (selectable in ingame-chat as "NPC"). + Npc = 1, +} diff --git a/arcdps/src/extras/message/npc.rs b/arcdps/src/extras/message/npc.rs new file mode 100644 index 0000000000..d8894436d0 --- /dev/null +++ b/arcdps/src/extras/message/npc.rs @@ -0,0 +1,103 @@ +use crate::util::str_from_cstr_len; +use std::ffi::c_char; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// An NPC chat message. +/// +/// Strings are available for the duration of the call. +/// If you need it for longer than that, consider converting it to [`NpcMessageOwned`]. +/// +/// ```no_run +/// # use arcdps::extras::{NpcMessage, NpcMessageOwned}; +/// # let message: &NpcMessage = todo!(); +/// let owned = message.to_owned(); +/// let owned: NpcMessageOwned = message.into(); +/// ``` +#[derive(Debug, Clone)] +pub struct NpcMessage { + /// Null terminated character name of the NPC that sent the message. + /// + /// The string is only valid for the duration of the call. + character_name: *const c_char, + character_name_length: u64, + + /// Null terminated string containing the content of the message that was sent. + /// + /// The string is only valid for the duration of the call. + text: *const c_char, + text_length: u64, +} + +impl NpcMessage { + /// Converts the message to its owned counterpart. + #[inline] + pub fn to_owned(&self) -> NpcMessageOwned { + self.into() + } + + /// Returns the character name of the player that sent the message. + #[inline] + pub fn character_name(&self) -> &str { + unsafe { str_from_cstr_len(self.character_name, self.character_name_length) } + } + + /// Returns the character name as raw pointer. + #[inline] + pub fn character_name_ptr(&self) -> *const c_char { + self.character_name + } + + /// Returns the account name length. + #[inline] + pub fn character_name_len(&self) -> usize { + self.character_name_length as _ + } + + /// Returns the text content of the message. + #[inline] + pub fn text(&self) -> &str { + unsafe { str_from_cstr_len(self.text, self.text_length) } + } + + /// Returns the text as raw pointer. + #[inline] + pub fn text_ptr(&self) -> *const c_char { + self.text + } + + /// Returns the account name length. + #[inline] + pub fn text_len(&self) -> usize { + self.text_length as _ + } +} + +/// [`NpcMessage`] with owned [`String`] fields. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct NpcMessageOwned { + /// Character name of the NPC that sent the message. + pub character_name: String, + + /// Content of the message. + pub text: String, +} + +impl From for NpcMessageOwned { + #[inline] + fn from(msg: NpcMessage) -> Self { + (&msg).into() + } +} + +impl From<&NpcMessage> for NpcMessageOwned { + #[inline] + fn from(msg: &NpcMessage) -> Self { + Self { + character_name: msg.character_name().to_owned(), + text: msg.text().to_owned(), + } + } +} diff --git a/arcdps/src/extras/message/squad.rs b/arcdps/src/extras/message/squad.rs new file mode 100644 index 0000000000..3f1c00d374 --- /dev/null +++ b/arcdps/src/extras/message/squad.rs @@ -0,0 +1,265 @@ +use crate::{strip_account_prefix, util::str_from_cstr_len}; +use bitflags::bitflags; +use chrono::{DateTime, FixedOffset}; +use std::ffi::c_char; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "strum")] +use strum::{Display, EnumCount, EnumIter, IntoStaticStr, VariantNames}; + +/// A squad/party chat message. +/// +/// Strings are available for the duration of the call. +/// If you need it for longer than that, consider converting it to [`SquadMessageOwned`]. +/// +/// ```no_run +/// # use arcdps::extras::{SquadMessage, SquadMessageOwned}; +/// # let message: &SquadMessage = todo!(); +/// let owned = message.to_owned(); +/// let owned: SquadMessageOwned = message.into(); +/// ``` +#[derive(Debug, Clone)] +#[repr(C)] +pub struct SquadMessage { + /// A unique identifier for the channel this chat message was sent over. + /// + /// Can be used to, for example, differentiate between squad messages sent to different squads. + pub channel_id: u32, + + /// Whether the message is sent in a party or a squad. + /// + /// Note that messages sent to the party chat while in a squad will have the type [`ChannelType::Squad`]. + pub channel_type: ChannelType, + + /// The subgroup the message was sent to, or `0` if it was sent to the entire squad. + pub subgroup: u8, + + /// 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. + /// The upper bits of this field may be used in a later version and **must not** be interpreted. + flags: u8, + + /// Unused padding. + _unused1: u8, + + /// Null terminated iso8601 formatted string denoting when this message was + /// received by the server, e.g. `"2022-07-09T11:45:24.888Z"`. + /// This is the "absolute ordering" for chat messages, + /// however the time can potentially differ several seconds between the client and server because of latency and clock skew. + /// + /// The string is only valid for the duration of the call. + timestamp: *const c_char, + timestamp_length: u64, + + /// Null terminated account name of the player that sent the message, including leading ':'. + /// + /// The string is only valid for the duration of the call. + account_name: *const c_char, + account_name_length: u64, + + /// Null terminated character name of the player that sent the message. + /// + /// The string is only valid for the duration of the call. + character_name: *const c_char, + character_name_length: u64, + + /// Null terminated string containing the content of the message that was sent. + /// + /// The string is only valid for the duration of the call. + text: *const c_char, + text_length: u64, +} + +impl SquadMessage { + /// Converts the squad message to its owned counterpart. + #[inline] + pub fn to_owned(&self) -> SquadMessageOwned { + self.into() + } + + /// Returns the raw message flags. + #[inline] + pub fn flags_raw(&self) -> u8 { + self.flags + } + + /// Returns the message flags. + #[inline] + pub fn flags(&self) -> SquadMessageFlags { + SquadMessageFlags::from_bits_truncate(self.flags) + } + + /// Returns the message flags. + #[inline] + pub fn is_broadcast(&self) -> bool { + self.flags().contains(SquadMessageFlags::IS_BROADCAST) + } + + /// Returns the timestamp as string. + #[inline] + pub fn timestamp_str(&self) -> &str { + unsafe { str_from_cstr_len(self.timestamp, self.timestamp_length) } + } + + /// Returns the timestamp string as raw pointer. + #[inline] + pub fn timestamp_ptr(&self) -> *const c_char { + self.timestamp + } + + /// Returns the timestamp string length. + #[inline] + pub fn timestamp_len(&self) -> usize { + self.timestamp_length as _ + } + + /// Returns the timestamp when the message was received. + /// + /// This is the "absolute ordering" for chat messages, + /// however the time can potentially differ several seconds between the client and server because of latency and clock skew. + #[inline] + pub fn timestamp(&self) -> Option> { + DateTime::parse_from_rfc3339(self.timestamp_str()).ok() + } + + /// Returns the account name of the player that sent the message. + #[inline] + pub fn account_name(&self) -> &str { + let account_name = + unsafe { str_from_cstr_len(self.account_name, self.account_name_length) }; + strip_account_prefix(account_name) + } + + /// Returns the account name as raw pointer. + #[inline] + pub fn account_name_ptr(&self) -> *const c_char { + self.account_name + } + + /// Returns the account name length. + #[inline] + pub fn account_name_len(&self) -> usize { + self.account_name_length as _ + } + + /// Returns the character name of the player that sent the message. + #[inline] + pub fn character_name(&self) -> &str { + unsafe { str_from_cstr_len(self.character_name, self.character_name_length) } + } + + /// Returns the character name as raw pointer. + #[inline] + pub fn character_name_ptr(&self) -> *const c_char { + self.character_name + } + + /// Returns the account name length. + #[inline] + pub fn character_name_len(&self) -> usize { + self.character_name_length as _ + } + + /// Returns the text content of the message. + #[inline] + pub fn text(&self) -> &str { + unsafe { str_from_cstr_len(self.text, self.text_length) } + } + + /// Returns the text as raw pointer. + #[inline] + pub fn text_ptr(&self) -> *const c_char { + self.text + } + + /// Returns the account name length. + #[inline] + pub fn text_len(&self) -> usize { + self.text_length as _ + } +} + +/// [`SquadMessage`] with owned [`String`] fields. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SquadMessageOwned { + /// A unique identifier for the channel this chat message was sent over. + /// + /// Can be used to, for example, differentiate between squad messages sent to different squads. + pub channel_id: u32, + + /// Whether the message is sent in a party or a squad. + /// + /// Note that messages sent to the party chat while in a squad will have the type [`ChannelType::Squad`]. + pub channel_type: ChannelType, + + /// The subgroup the message was sent to, or `0` if it was sent to the entire squad. + pub subgroup: u8, + + /// Whether the message is a broadcast. + pub flags: SquadMessageFlags, + + /// Timestamp when the message was received. + /// + /// This is the "absolute ordering" for chat messages, + /// however the time can potentially differ several seconds between the client and server because of latency and clock skew. + pub timestamp: Option>, + + /// Account name of the player that sent the message. + pub account_name: String, + + /// Character name of the player that sent the message. + pub character_name: String, + + /// Content of the message. + pub text: String, +} + +impl From for SquadMessageOwned { + #[inline] + fn from(msg: SquadMessage) -> Self { + (&msg).into() + } +} + +impl From<&SquadMessage> for SquadMessageOwned { + #[inline] + fn from(msg: &SquadMessage) -> Self { + Self { + channel_id: msg.channel_id, + channel_type: msg.channel_type, + subgroup: msg.subgroup, + flags: SquadMessageFlags::from_bits_truncate(msg.flags), + timestamp: msg.timestamp(), + account_name: msg.account_name().to_owned(), + character_name: msg.character_name().to_owned(), + text: msg.text().to_owned(), + } + } +} + +bitflags! { + /// Squad message flags. + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct SquadMessageFlags : u8 { + /// Message is a broadcast. + const IS_BROADCAST = 1; + } +} + +/// Type of message channel. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "strum", + derive(Display, EnumCount, EnumIter, IntoStaticStr, VariantNames) +)] +#[repr(u8)] +pub enum ChannelType { + Party = 0, + Squad = 1, + Reserved = 2, + Invalid = 3, +} diff --git a/arcdps_codegen/src/extras.rs b/arcdps_codegen/src/extras.rs index 02009f862a..4e6a07ddb4 100644 --- a/arcdps_codegen/src/extras.rs +++ b/arcdps_codegen/src/extras.rs @@ -203,10 +203,9 @@ impl ExtrasGen { quote_spanned! {span=> const __EXTRAS_SQUAD_CHAT_MESSAGE: ::arcdps::extras::callbacks::ExtrasSquadChatMessageCallback = #safe; - unsafe extern #C_ABI fn #name(message: *const ::arcdps::extras::message::RawSquadMessage) { + unsafe extern #C_ABI fn #name(message: *const ::arcdps::extras::message::SquadMessage) { let message = message.as_ref() - .expect("unofficial extras did not provide message info in chat message callback") - .into(); + .expect("unofficial extras did not provide message info in chat message callback"); self::__EXTRAS_SQUAD_CHAT_MESSAGE(message) } } @@ -224,7 +223,7 @@ impl ExtrasGen { quote_spanned! {span=> const __EXTRAS_CHAT_MESSAGE: ::arcdps::extras::callbacks::ExtrasChatMessageCallback = #safe; - unsafe extern #C_ABI fn #name(message_type: ::arcdps::extras::message::RawMessageType, message: ::arcdps::extras::message::RawMessage) { + unsafe extern #C_ABI fn #name(message_type: ::arcdps::extras::message::MessageType, message: ::arcdps::extras::message::RawMessage) { let message = ::arcdps::extras::message::Message::new(message_type, message); self::__EXTRAS_CHAT_MESSAGE(message) } diff --git a/arcdps_example_plugin/src/lib.rs b/arcdps_example_plugin/src/lib.rs index c24b6dedcc..d77db9e5ec 100644 --- a/arcdps_example_plugin/src/lib.rs +++ b/arcdps_example_plugin/src/lib.rs @@ -165,13 +165,13 @@ fn extras_keybind_changed(changed: KeybindChange) { } /// Unofficial extras squad chat message. -fn extras_squad_chat_message(message: SquadMessage) { - if message.is_broadcast { - log::info!("broadcast from {}", message.account_name); +fn extras_squad_chat_message(message: &SquadMessage) { + if message.is_broadcast() { + log::info!("broadcast from {}", message.account_name()); } else { log::info!( "message from {} in {:?}", - message.account_name, + message.account_name(), message.channel_type ) } @@ -181,18 +181,18 @@ fn extras_squad_chat_message(message: SquadMessage) { fn extras_chat_message(message: Message) { match message { Message::Squad(message) => { - if message.is_broadcast { - log::info!("broadcast from {}", message.account_name); + if message.is_broadcast() { + log::info!("broadcast from {}", message.account_name()); } else { log::info!( "message from {} in {:?}", - message.account_name, + message.account_name(), message.channel_type ) } } Message::Npc(message) => { - log::info!("message from NPC {}", message.character_name) + log::info!("message from NPC {}", message.character_name()) } } } diff --git a/evtc/src/agent/realtime.rs b/evtc/src/agent/realtime.rs index 1741e0e065..cf54566c0b 100644 --- a/evtc/src/agent/realtime.rs +++ b/evtc/src/agent/realtime.rs @@ -13,8 +13,8 @@ use serde::{Deserialize, Serialize}; /// If you need it for longer than its lifetime, consider converting it to [`AgentOwned`]. /// /// ```no_run -/// # use arcdps::{Agent, AgentOwned}; -/// # let agent: arcdps::Agent = todo!(); +/// # use evtc::agent::realtime::{Agent, AgentOwned}; +/// # let agent: &Agent = todo!(); /// let owned = agent.to_owned(); /// let owned: AgentOwned = agent.into(); /// ``` @@ -58,7 +58,7 @@ impl Agent { /// Converts the [`Agent`] to the owned version [`AgentOwned`]. #[inline] - pub fn to_owned(self) -> AgentOwned { + pub fn to_owned(&self) -> AgentOwned { self.into() } @@ -95,6 +95,13 @@ pub struct AgentOwned { impl From for AgentOwned { #[inline] fn from(agent: Agent) -> Self { + (&agent).into() + } +} + +impl From<&Agent> for AgentOwned { + #[inline] + fn from(agent: &Agent) -> Self { Self { name: agent.name().map(|string| string.to_owned()), id: agent.id,