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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//! Friend related packets. \[0x18\]
use super::{HelperReadWrite, PacketReadWrite};
use std::time::Duration;

// ----------------------------------------------------------------
// Friend packets
// ----------------------------------------------------------------

/// (0x18, 0x14) Friend List Request.
///
/// (C -> S) Sent when a client wants a friend list.
///
/// Respond with: [`crate::protocol::Packet::FriendList`]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[derive(Debug, Clone, Default, PartialEq, PacketReadWrite)]
#[Id(0x18, 0x14)]
pub struct FriendListRequestPacket {
    pub unk: u32,
}

/// (0x18, 0x15) Friend List.
///
/// (S -> C) Sent in response to a request.
///
/// Response to: [`crate::protocol::Packet::FriendListRequest`]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[derive(Debug, Clone, Default, PartialEq, PacketReadWrite)]
#[Id(0x18, 0x15)]
#[Flags(Flags::PACKED)]
#[Magic(0x2E1E, 0x63)]
pub struct FriendListPacket {
    pub unk1: u32,
    pub unk2: u16,
    pub unk3: u16,
    /// Players friends.
    pub friends: Vec<FriendListEntry>,
    /// Player nickname.
    pub nickname: String,
}

/// (0x18, 0x18) Send Friend Request.
///
/// (C -> S) Sent when a client has sent a friend request to another player.
///
/// Respond with: [`crate::protocol::Packet::AddedRequest`]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[derive(Debug, Clone, Default, PartialEq, PacketReadWrite)]
#[Id(0x18, 0x18)]
#[Flags(Flags::PACKED)]
#[Magic(0xBF57, 0x44)]
pub struct SendFriendRequestPacket {
    /// Target player ID.
    pub id: u32,
    /// Request message.
    #[Seek(4)]
    pub msg: String,
}

/// (0x18, 0x1A) Friend Request Sent.
///
/// (S -> C) Sent in response to a request.
///
/// Response to: [`crate::protocol::Packet::SendFriendRequest`]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[derive(Debug, Clone, Default, PartialEq, PacketReadWrite)]
#[Id(0x18, 0x1A)]
pub struct AddedRequestPacket {
    pub unk1: u32,
    /// Sender player ID.
    pub sender_id: u32,
    /// Target player ID.
    #[Seek(4)]
    pub target_id: u32,
    /// Sender player nickname.
    #[Seek(4)]
    #[FixedLen(0x22)]
    pub sender_nickname: String,
    /// Target player nickname.
    #[FixedLen(0x22)]
    pub target_nickname: String,
    /// Request message.
    #[FixedLen(0x80)]
    pub msg: String,
    /// Request send timestamp.
    pub send_time: Duration,
    #[Seek(0x88)]
    pub unk2: u8,
    pub unk3: u8,
    #[SeekAfter(0x91)]
    pub unk4: u8,
}

// ----------------------------------------------------------------
// Additional structs
// ----------------------------------------------------------------

/// Friend entry in friend list.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
#[derive(Debug, Clone, Default, PartialEq, HelperReadWrite)]
pub struct FriendListEntry {
    /// Player ID.
    pub id: u32,
    /// Player nickname.
    #[Seek(4)]
    #[FixedLen(0x20)]
    pub nickname: String,
    /// Player character name (if logged in).
    #[Seek(4)]
    #[FixedLen(0x10)]
    pub char_name: String,
    /// Friend flags.
    #[Seek(4)]
    pub flags: FriendFlags,
    pub unk2: u8,
    pub unk3: u8,
    pub unk4: u8,
    #[Seek(4)]
    pub unk5: u32,
    /// Player current block ID.
    #[Seek(8)]
    pub blockid: u32,
    /// Player current location.
    pub location: FriendLocation,
    pub unk6: u16,
    pub unk7: u32,
    /// Player alliance name.
    #[FixedLen(0x18)]
    pub alliance_name: String,
    #[Seek(0x8)]
    pub unk8: Duration,
    pub unk9: Duration,
    #[Seek(0x38)]
    pub unk10: u32,
    #[Seek(4)]
    pub unk11: u8,
    pub unk12: u8,
    #[Seek(2)]
    #[SeekAfter(4)]
    pub unk13: Duration,
}

bitflags::bitflags! {
    /// Friend flags.
    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
    #[cfg_attr(feature = "serde", serde(default))]
    #[derive(Debug, Copy, Clone, Default, PartialEq, HelperReadWrite)]
    #[BitFlags(u8)]
    pub struct FriendFlags: u8 {
        /// Is the friend online.
        const IS_ONLINE = 1 << 0;
        /// Are login notifications enabled for this friend.
        const LOGIN_NOTIF = 1 << 3;
        /// Did the player not log in for a while.
        const NO_RECENT_LOGINS = 1 << 5;

        const _ = !0;
    }
}

/// Friend map location.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
#[derive(Debug, Copy, Clone, Default, PartialEq, HelperReadWrite)]
pub enum FriendLocation {
    /// Player is in the lobby.
    #[default]
    Lobby,
    /// Player is in the quest map.
    Quest,
    /// Player is in the personal quarters.
    PersonalQ,
    /// Player is in the alliance quarters.
    AllianceQ,
    /// Player is in the casino.
    Casino,
    /// Player is in the challenger lobby.
    ChallengerLobby,
    /// Player is in the ARKS bridge.
    Bridge,
    /// Player is in the cafe.
    FrancasCafe,
    /// Player is in the battle lobby.
    BattleLobby,

    #[Read_default]
    Unknown = 0xFFFF,
}