1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
//! User information provided by Unofficial Extras.
use crate::util::{str_from_cstr, strip_account_prefix};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::{os::raw::c_char, slice};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "strum")]
use strum::{Display, EnumCount, EnumIter, IntoStaticStr, VariantNames};
/// Role of a user in the squad.
#[derive(
Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, IntoPrimitive, TryFromPrimitive,
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "strum",
derive(Display, EnumCount, EnumIter, IntoStaticStr, VariantNames)
)]
#[repr(u8)]
pub enum UserRole {
/// User is leader (commander tag).
SquadLeader = 0,
/// User is lieutenant.
Lieutenant = 1,
/// User is regular member.
Member = 2,
/// User is invited.
Invited = 3,
/// User has requested to join.
Applied = 4,
/// User has left.
None = 5,
/// Internal only.
Invalid = 6,
}
/// Information about a player related to the squad.
///
/// Strings are available for the duration of the call.
/// If you need it for longer than that, consider converting it to [`UserInfoOwned`].
///
/// ```no_run
/// # use arcdps::extras::{UserInfo, UserInfoOwned};
/// # let user: UserInfo = todo!();
/// let owned = user.to_owned();
/// let owned: UserInfoOwned = user.into();
/// ```
#[derive(Debug)]
#[repr(C)]
pub struct UserInfo {
/// Account name with leading `':'`.
account_name: *const c_char,
/// Unix timestamp when the user joined the squad.
///
/// `0` if time could not be determined.
pub join_time: u64,
/// Role in squad, or [`UserRole::None`] if the user was removed from the squad.
pub role: UserRole,
/// Subgroup the user is in.
///
/// `0` when no subgroup could be found, which is either the first subgroup or no subgroup.
pub subgroup: u8,
/// Whether this player is ready or not (in a squad ready check).
///
/// ### Remarks
/// `role` set to [`UserRole::SquadLeader`] and `ready_status == true` implies that a ready check was just started.
/// Similarly, `role` set to [`UserRole::SquadLeader`] and `ready_status == false` implies that a ready check either finished or was cancelled.
/// If everyone in the squad had an event sent with `ready_status == true` then that means that the ready check finished successfully.
/// After which there will be events sent for each user where their `ready_status == false`.
pub ready_status: bool,
/// Unused space.
pub _unused1: u8,
/// Unused space.
pub _unused2: u32,
}
impl UserInfo {
/// Returns the user account name without leading `':'`.
#[inline]
pub fn account_name(&self) -> Option<&str> {
unsafe { str_from_cstr(self.account_name).map(strip_account_prefix) }
}
/// Returns the raw pointer to the user account name.
#[inline]
pub fn account_name_ptr(&self) -> *const c_char {
self.account_name
}
/// Converts the [`UserInfo`] to the owned version [`UserInfoOwned`].
#[inline]
pub fn to_owned(self) -> UserInfoOwned {
self.into()
}
}
/// [`UserInfo`] with an owned [`String`] name.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct UserInfoOwned {
/// Account name, without leading ':'.
pub account_name: Option<String>,
/// Unix timestamp when the user joined the squad.
///
/// `0` if time could not be determined.
pub join_time: u64,
/// Role in squad, or [`UserRole::None`] if the user was removed from the squad.
pub role: UserRole,
/// Subgroup the user is in.
///
/// `0` when no subgroup could be found, which is either the first subgroup or no subgroup.
pub subgroup: u8,
/// Whether this player is ready or not (in a squad ready check).
///
/// ### Remarks
/// `role` set to [`UserRole::SquadLeader`] and `ready_status == true` implies that a ready check was just started.
/// Similarly, `role` set to [`UserRole::SquadLeader`] and `ready_status == false` implies that a ready check either finished or was cancelled.
/// If everyone in the squad had an event sent with `ready_status == true` then that means that the ready check finished successfully.
/// After which there will be events sent for each user where their `ready_status == false`.
pub ready_status: bool,
}
impl From<UserInfo> for UserInfoOwned {
#[inline]
fn from(user: UserInfo) -> Self {
Self {
account_name: user.account_name().map(|x| x.to_string()),
join_time: user.join_time,
role: user.role,
subgroup: user.subgroup,
ready_status: user.ready_status,
}
}
}
/// Iterator over changed users.
pub type UserInfoIter<'a> = slice::Iter<'a, UserInfo>;
/// Helper to generate an iterator over [`UserInfo`] structs.
#[inline]
pub unsafe fn to_user_info_iter<'a>(ptr: *const UserInfo, len: u64) -> UserInfoIter<'a> {
slice::from_raw_parts(ptr, len as usize).iter()
}