1use std::io::{Read, Write};
2
3use crate::Guid;
4use crate::wrath::{
5 DungeonDifficulty, GroupListMember, GroupLootSetting, ItemQuality, RaidDifficulty,
6};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
31pub struct SMSG_GROUP_LIST {
32 pub group_type: u8,
33 pub group_id: u8,
34 pub flags: u8,
36 pub roles: u8,
37 pub group: Guid,
38 pub counter: u32,
40 pub members: Vec<GroupListMember>,
41 pub leader: Guid,
42 pub group_not_empty: Option<SMSG_GROUP_LIST_group_not_empty>,
43}
44
45impl crate::private::Sealed for SMSG_GROUP_LIST {}
46impl SMSG_GROUP_LIST {
47 fn read_inner(mut r: &mut &[u8], body_size: u32) -> Result<Self, crate::errors::ParseErrorKind> {
48 if !(28..=16777215).contains(&body_size) {
49 return Err(crate::errors::ParseErrorKind::InvalidSize);
50 }
51
52 let group_type = crate::util::read_u8_le(&mut r)?;
54
55 let group_id = crate::util::read_u8_le(&mut r)?;
57
58 let flags = crate::util::read_u8_le(&mut r)?;
60
61 let roles = crate::util::read_u8_le(&mut r)?;
63
64 let group = crate::util::read_guid(&mut r)?;
66
67 let counter = crate::util::read_u32_le(&mut r)?;
69
70 let amount_of_members = crate::util::read_u32_le(&mut r)?;
72
73 let members = {
75 let mut members = Vec::with_capacity(amount_of_members as usize);
76
77 let allocation_size = u64::from(amount_of_members) * 13;
78 if allocation_size > crate::errors::MAX_ALLOCATION_SIZE_WRATH {
79 return Err(crate::errors::ParseErrorKind::AllocationTooLargeError(allocation_size));
80 }
81
82 for _ in 0..amount_of_members {
83 members.push(GroupListMember::read(&mut r)?);
84 }
85 members
86 };
87
88 let leader = crate::util::read_guid(&mut r)?;
90
91 let current_size = {
93 1 + 1 + 1 + 1 + 8 + 4 + 4 + members.iter().fold(0, |acc, x| acc + x.size()) + 8 };
103 let group_not_empty = if current_size < body_size as usize {
104 let loot_setting = crate::util::read_u8_le(&mut r)?.try_into()?;
106
107 let master_loot = crate::util::read_guid(&mut r)?;
109
110 let loot_threshold = crate::util::read_u8_le(&mut r)?.try_into()?;
112
113 let difficulty = crate::util::read_u8_le(&mut r)?.try_into()?;
115
116 let raid_difficulty = crate::util::read_u8_le(&mut r)?.try_into()?;
118
119 let heroic = crate::util::read_bool_u8(&mut r)?;
121
122 Some(SMSG_GROUP_LIST_group_not_empty {
123 loot_setting,
124 master_loot,
125 loot_threshold,
126 difficulty,
127 raid_difficulty,
128 heroic,
129 })
130 } else {
131 None
132 };
133
134 Ok(Self {
135 group_type,
136 group_id,
137 flags,
138 roles,
139 group,
140 counter,
141 members,
142 leader,
143 group_not_empty,
144 })
145 }
146
147}
148
149impl crate::Message for SMSG_GROUP_LIST {
150 const OPCODE: u32 = 0x007d;
151
152 #[cfg(feature = "print-testcase")]
153 fn message_name(&self) -> &'static str {
154 "SMSG_GROUP_LIST"
155 }
156
157 #[cfg(feature = "print-testcase")]
158 fn to_test_case_string(&self) -> Option<String> {
159 use std::fmt::Write;
160 use crate::traits::Message;
161
162 let mut s = String::new();
163
164 writeln!(s, "test SMSG_GROUP_LIST {{").unwrap();
165 writeln!(s, " group_type = {};", self.group_type).unwrap();
167 writeln!(s, " group_id = {};", self.group_id).unwrap();
168 writeln!(s, " flags = {};", self.flags).unwrap();
169 writeln!(s, " roles = {};", self.roles).unwrap();
170 writeln!(s, " group = {};", self.group.guid()).unwrap();
171 writeln!(s, " counter = {};", self.counter).unwrap();
172 writeln!(s, " amount_of_members = {};", self.members.len()).unwrap();
173 writeln!(s, " members = [").unwrap();
174 for v in self.members.as_slice() {
175 writeln!(s, " {{").unwrap();
176 writeln!(s, " name = \"{}\";", v.name).unwrap();
178 writeln!(s, " guid = {};", v.guid.guid()).unwrap();
179 writeln!(s, " is_online = {};", if v.is_online { "TRUE" } else { "FALSE" }).unwrap();
180 writeln!(s, " group_id = {};", v.group_id).unwrap();
181 writeln!(s, " flags = {};", v.flags).unwrap();
182 writeln!(s, " lfg_roles = {};", v.lfg_roles).unwrap();
183
184 writeln!(s, " }},").unwrap();
185 }
186 writeln!(s, " ];").unwrap();
187 writeln!(s, " leader = {};", self.leader.guid()).unwrap();
188 if let Some(group_not_empty) = &self.group_not_empty {
189 writeln!(s, " loot_setting = {};", group_not_empty.loot_setting.as_test_case_value()).unwrap();
190 writeln!(s, " master_loot = {};", group_not_empty.master_loot.guid()).unwrap();
191 writeln!(s, " loot_threshold = {};", group_not_empty.loot_threshold.as_test_case_value()).unwrap();
192 writeln!(s, " difficulty = {};", group_not_empty.difficulty.as_test_case_value()).unwrap();
193 writeln!(s, " raid_difficulty = {};", group_not_empty.raid_difficulty.as_test_case_value()).unwrap();
194 writeln!(s, " heroic = {};", if group_not_empty.heroic { "TRUE" } else { "FALSE" }).unwrap();
195 }
196
197 writeln!(s, "}} [").unwrap();
198
199 let [a, b] = (u16::try_from(self.size() + 2).unwrap()).to_be_bytes();
200 writeln!(s, " {a:#04X}, {b:#04X}, /* size */").unwrap();
201 let [a, b] = 125_u16.to_le_bytes();
202 writeln!(s, " {a:#04X}, {b:#04X}, /* opcode */").unwrap();
203 let mut bytes: Vec<u8> = Vec::new();
204 self.write_into_vec(&mut bytes).unwrap();
205 let mut bytes = bytes.into_iter();
206
207 crate::util::write_bytes(&mut s, &mut bytes, 1, "group_type", " ");
208 crate::util::write_bytes(&mut s, &mut bytes, 1, "group_id", " ");
209 crate::util::write_bytes(&mut s, &mut bytes, 1, "flags", " ");
210 crate::util::write_bytes(&mut s, &mut bytes, 1, "roles", " ");
211 crate::util::write_bytes(&mut s, &mut bytes, 8, "group", " ");
212 crate::util::write_bytes(&mut s, &mut bytes, 4, "counter", " ");
213 crate::util::write_bytes(&mut s, &mut bytes, 4, "amount_of_members", " ");
214 if !self.members.is_empty() {
215 writeln!(s, " /* members: GroupListMember[amount_of_members] start */").unwrap();
216 for (i, v) in self.members.iter().enumerate() {
217 writeln!(s, " /* members: GroupListMember[amount_of_members] {i} start */").unwrap();
218 crate::util::write_bytes(&mut s, &mut bytes, v.name.len() + 1, "name", " ");
219 crate::util::write_bytes(&mut s, &mut bytes, 8, "guid", " ");
220 crate::util::write_bytes(&mut s, &mut bytes, 1, "is_online", " ");
221 crate::util::write_bytes(&mut s, &mut bytes, 1, "group_id", " ");
222 crate::util::write_bytes(&mut s, &mut bytes, 1, "flags", " ");
223 crate::util::write_bytes(&mut s, &mut bytes, 1, "lfg_roles", " ");
224 writeln!(s, " /* members: GroupListMember[amount_of_members] {i} end */").unwrap();
225 }
226 writeln!(s, " /* members: GroupListMember[amount_of_members] end */").unwrap();
227 }
228 crate::util::write_bytes(&mut s, &mut bytes, 8, "leader", " ");
229 if let Some(group_not_empty) = &self.group_not_empty {
230 crate::util::write_bytes(&mut s, &mut bytes, 1, "loot_setting", " ");
231 crate::util::write_bytes(&mut s, &mut bytes, 8, "master_loot", " ");
232 crate::util::write_bytes(&mut s, &mut bytes, 1, "loot_threshold", " ");
233 crate::util::write_bytes(&mut s, &mut bytes, 1, "difficulty", " ");
234 crate::util::write_bytes(&mut s, &mut bytes, 1, "raid_difficulty", " ");
235 crate::util::write_bytes(&mut s, &mut bytes, 1, "heroic", " ");
236 }
237
238
239 writeln!(s, "] {{").unwrap();
240 writeln!(s, " versions = \"{}\";", std::env::var("WOWM_TEST_CASE_WORLD_VERSION").unwrap_or("3.3.5".to_string())).unwrap();
241 writeln!(s, "}}\n").unwrap();
242
243 Some(s)
244 }
245
246 fn size_without_header(&self) -> u32 {
247 self.size() as u32
248 }
249
250 fn write_into_vec(&self, mut w: impl Write) -> Result<(), std::io::Error> {
251 w.write_all(&self.group_type.to_le_bytes())?;
253
254 w.write_all(&self.group_id.to_le_bytes())?;
256
257 w.write_all(&self.flags.to_le_bytes())?;
259
260 w.write_all(&self.roles.to_le_bytes())?;
262
263 w.write_all(&self.group.guid().to_le_bytes())?;
265
266 w.write_all(&self.counter.to_le_bytes())?;
268
269 w.write_all(&(self.members.len() as u32).to_le_bytes())?;
271
272 for i in self.members.iter() {
274 i.write_into_vec(&mut w)?;
275 }
276
277 w.write_all(&self.leader.guid().to_le_bytes())?;
279
280 if let Some(v) = &self.group_not_empty {
282 w.write_all(&(v.loot_setting.as_int().to_le_bytes()))?;
284
285 w.write_all(&v.master_loot.guid().to_le_bytes())?;
287
288 w.write_all(&(v.loot_threshold.as_int().to_le_bytes()))?;
290
291 w.write_all(&(v.difficulty.as_int().to_le_bytes()))?;
293
294 w.write_all(&(v.raid_difficulty.as_int().to_le_bytes()))?;
296
297 w.write_all(u8::from(v.heroic).to_le_bytes().as_slice())?;
299
300 }
301
302 Ok(())
303 }
304
305 fn read_body<S: crate::private::Sealed>(r: &mut &[u8], body_size: u32) -> Result<Self, crate::errors::ParseError> {
306 Self::read_inner(r, body_size).map_err(|a| crate::errors::ParseError::new(125, "SMSG_GROUP_LIST", body_size, a))
307 }
308
309}
310
311#[cfg(feature = "wrath")]
312impl crate::wrath::ServerMessage for SMSG_GROUP_LIST {}
313
314impl SMSG_GROUP_LIST {
315 pub(crate) fn size(&self) -> usize {
316 1 + 1 + 1 + 1 + 8 + 4 + 4 + self.members.iter().fold(0, |acc, x| acc + x.size()) + 8 + if let Some(group_not_empty) = &self.group_not_empty {
326 1 + 8 + 1 + 1 + 1 + 1 } else {
333 0
334 }
335 }
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
339pub struct SMSG_GROUP_LIST_group_not_empty {
340 pub loot_setting: GroupLootSetting,
341 pub master_loot: Guid,
342 pub loot_threshold: ItemQuality,
343 pub difficulty: DungeonDifficulty,
344 pub raid_difficulty: RaidDifficulty,
345 pub heroic: bool,
346}
347