1use core::slice::Iter;
2use std::collections::BTreeMap;
3use std::ops::Range;
4
5use anyhow::anyhow;
6use serde::Serialize;
7
8#[macro_export]
9macro_rules! fields {
10 (
11 $(#[$enum_attr:meta])*
12 pub enum $enum_name:ident {
13 $(
14 $(#[$variant_attr:meta])*
15 $field_type:ident $(( $($custom_types:ident),+ ))?
16 $([$len:expr])? $variant:ident = $start_index:expr$(,)?
17 )*
18 }
19 ) => {
20 $(#[$enum_attr])*
21 #[derive(Serialize, Ord, PartialOrd, Eq, PartialEq, Clone, Debug)]
22 #[allow(dead_code)]
23 pub enum $enum_name {
24 $(
25 $(#[$variant_attr])*
26 #[doc = concat!(
27 stringify!(FieldValue::$field_type ),
28 $(
29 "Array(", stringify!($len), ")"
30 )?
31 )]
32 $variant,
33 )*
34 }
35
36 #[allow(dead_code)]
37 impl $enum_name {
38 pub fn get_field_name(&self) -> String {
39 match self {
40 $(
41 Self::$variant => format!("{}::{}",
42 stringify!($enum_name),
43 tentacli_utils::camel_to_upper_snake_case(
44 stringify!($variant)
45 ).to_uppercase()
46 ),
47 )*
48 }
49 }
50
51 pub fn get_limit() -> u32 {
52 let mut max_value: u32 = 0;
53 $(
54 max_value = max_value.max($start_index);
55 )*
56 max_value.wrapping_add(1)
57 }
58
59 pub fn read_from(
60 buffer: Vec<u32>,
61 update_mask: &mut Vec<bool>,
62 ) -> anyhow::Result<BTreeMap<Self, FieldValue>> {
63 let mut fields = BTreeMap::new();
64
65 let indices_to_update: Vec<u32> = update_mask.iter()
66 .enumerate()
67 .filter_map(|(index, &value)| if value { Some(index as u32) } else { None })
68 .collect();
69
70 let mut buffer_iter = buffer.iter();
71
72 $(
73 #[allow(unused_assignments)]
74 #[allow(unused_mut)]
75 let mut size: u32 = 1;
76 $(
77 size = $len;
78 )?
79
80 let field_type = stringify!($field_type);
81
82 let types = match field_type {
83 "Custom" => {
84 #[allow(unused_mut)]
85 let mut types = Vec::new();
86 $(
87 $(
88 types.push(stringify!($custom_types).to_string());
89 )*
90 )?
91 types
92 }
93 _ => vec![field_type.to_string()],
94 };
95
96 let offset = types.iter().fold(0, |sum, field_type| {
98 sum + match field_type.as_str() {
99 "Long" => 2,
100 _ => 1
101 }
102 });
103
104 let range: Range<u32> = $start_index..($start_index + size * offset);
105
106 let field_indices = indices_to_update.iter()
107 .filter(|&index| range.contains(index))
108 .cloned()
109 .collect::<Vec<u32>>();
110
111 if !field_indices.is_empty() {
112 let field_value = Self::read_value(field_type, types, &mut buffer_iter, field_indices, range)?;
113
114 if let Some(variant) = Self::get_variant_by_index($start_index) {
115 fields.insert(variant, field_value);
116 }
117 }
118 )*
119
120 Ok(fields)
121 }
122
123 fn read_value(
124 field_type: &str,
125 types: Vec<String>,
126 buffer_iter: &mut Iter<u32>,
127 field_indices: Vec<u32>,
128 range: Range<u32>
129 ) -> anyhow::Result<FieldValue> {
130 let value = match field_type {
131 "Long" => {
132 let mut values: Vec<u32> = vec![];
133
134 for i in range {
135 if !field_indices.contains(&i) {
136 values.push(0);
137 continue;
138 }
139
140 values.push(*buffer_iter.next()
141 .ok_or(anyhow!("Cannot read item(u32 of u64)"))? as u32);
142 }
143
144 let mut values_u64 = vec![];
145 for i in (0..values.len()).step_by(2) {
146 let low = values[i] as u64;
147 let high = values[i + 1] as u64;
148 values_u64.push(low | (high << 32))
149 }
150
151 if values.len() > 2 {
152 FieldValue::LongArray(values_u64)
153 } else {
154 FieldValue::Long(values_u64[0])
155 }
156 }
157 "Integer" => {
158 let mut values = vec![];
159 for i in range {
160 if !field_indices.contains(&i) {
161 values.push(0);
162 continue;
163 }
164
165 values.push(*buffer_iter.next()
166 .ok_or(anyhow!("Cannot read item(i32)"))? as i32);
167 }
168
169 if values.len() > 1 {
170 FieldValue::IntegerArray(values)
171 } else {
172 FieldValue::Integer(values[0])
173 }
174 }
175 "Bytes" => {
176 let mut values = vec![];
177
178 for i in range {
179 if !field_indices.contains(&i) {
180 values.push(0);
181 continue;
182 }
183
184 values.push(*buffer_iter.next()
185 .ok_or(anyhow!("Cannot read item(bytes)"))?);
186 }
187
188 if values.len() > 1 {
189 FieldValue::BytesArray(values)
190 } else {
191 FieldValue::Bytes(values[0])
192 }
193 }
194 "Float" => {
195 let mut values = vec![];
196
197 for i in range {
198 if !field_indices.contains(&i) {
199 values.push(0.);
200 continue;
201 }
202
203 let value = *buffer_iter.next()
204 .ok_or(anyhow!("Cannot read item(f32)"))?;
205 values.push(f32::from_bits(value));
206 }
207
208 if values.len() > 1 {
209 FieldValue::FloatArray(values)
210 } else {
211 FieldValue::Float(values[0])
212 }
213 }
214 "TwoShorts" => {
215 let mut values = vec![];
216
217 for i in range {
218 if !field_indices.contains(&i) {
219 values.push((0, 0));
220 continue;
221 }
222
223 let value = *buffer_iter.next()
224 .ok_or(anyhow!("Cannot read item(two shorts)"))?;
225 let first = (value & 0xFFFF) as i16;
226 let second = (value >> 16) as i16;
227
228 values.push((first, second));
229 }
230
231 if values.len() > 1 {
232 FieldValue::TwoShortsArray(values)
233 } else {
234 FieldValue::TwoShorts(values[0])
235 }
236 }
237 "Custom" => {
238 let mut values = vec![];
239 let mut cycle_iter = types.iter().cycle();
240 let mut sub_values = vec![];
241
242 for i in range {
243 let field_type = cycle_iter.next()
244 .ok_or(anyhow!("Cannot read field type for custom"))?;
245
246 if !field_indices.contains(&i) {
247 sub_values.push(FieldValue::None);
248 } else {
249 let value = Self::read_value(
250 field_type, vec![field_type.to_string()], buffer_iter, vec![i], i..i+1,
251 )?;
252 sub_values.push(value);
253 }
254
255 if sub_values.len() == types.len() {
256 values.push(sub_values.clone());
257 sub_values.clear();
258 }
259 }
260
261 if values.len() > 1 {
262 FieldValue::CustomArray(values)
263 } else {
264 FieldValue::Custom(values[0].clone())
265 }
266 }
267 _ => FieldValue::None
268 };
269
270 Ok(value)
271 }
272
273 fn get_name_by_index(index: u32) -> Option<String> {
274 match index {
275 $(
276 $start_index => Some(stringify!($variant).to_string()),
277 )*
278 _ => None,
279 }
280 }
281
282 fn get_variant_by_index(index: u32) -> Option<Self> {
283 match index {
284 $(
285 $start_index => Some(Self::$variant),
286 )*
287 _ => None,
288 }
289 }
290
291 pub fn get_index(variant: &$enum_name) -> u32 {
292 match variant {
293 $(
294 Self::$variant => $start_index,
295 )*
296 }
297 }
298 }
299 };
300}
301
302fields! {
303 pub enum ObjectField {
304 Long Guid = 0,
305 Integer Type = 2,
306 Integer Entry = 3,
307 Float ScaleX = 4
308 }
309}
310
311fields! {
312 pub enum UnitField {
313 Long Charm = 6,
314 Long Summon = 8,
315 Long Critter = 10,
316 Long CharmedBy = 12,
317 Long SummonedBy = 14,
318 Long CreatedBy = 16,
319 Long Target = 18,
320 Long ChannelObject = 20,
321 Integer ChannelSpell = 22,
322 Bytes Bytes0 = 23,
323 Integer Health = 24,
324 Integer[7] Powers = 25,
325 Integer MaxHealth = 32,
326 Integer[7] MaxPowers = 33,
327 Float[7] PowerRegenFlatModifier = 40,
328 Float[7] PowerRegenInterruptedFlatModifier = 47,
329 Integer Level = 54,
330 Integer FactionTemplate = 55,
331 Integer[3] VirtualItemSlotId = 56,
332 Integer Flags = 59,
333 Integer Flags2 = 60,
334 Integer AuraState = 61,
335 Integer[2] BaseAttackTime = 62,
336 Integer RangedAttackTime = 64,
337 Float BoundingRadius = 65,
338 Float CombatReach = 66,
339 Integer DisplayId = 67,
340 Integer NativeDisplayId = 68,
341 Integer MountDisplayId = 69,
342 Float MinDamage = 70,
343 Float MaxDamage = 71,
344 Float MinOffhandDamage = 72,
345 Float MaxOffhandDamage = 73,
346 Bytes Bytes1 = 74,
347 Integer PetNumber = 75,
348 Integer PetNameTimestamp = 76,
349 Integer PetExperience = 77,
350 Integer PetNextLevelExp = 78,
351 Integer DynamicFlags = 79,
352 Float ModCastSpeed = 80,
353 Integer CreatedBySpell = 81,
354 Integer NpcFlags = 82,
355 Integer NpcEmoteState = 83,
356 Integer[5] Stats = 84,
357 Integer[5] PosStats = 89,
358 Integer[5] NegStats = 94,
359 Integer[7] Resistances = 99,
360 Integer[7] ResistanceBuffModsPositive = 106,
361 Integer[7] ResistanceBuffModsNegative = 113,
362 Integer BaseMana = 120,
363 Integer BaseHealth = 121,
364 Bytes Bytes2 = 122,
365 Integer AttackPower = 123,
366 TwoShorts AttackPowerMods = 124,
367 Float AttackPowerMultiplier = 125,
368 Integer RangedAttackPower = 126,
369 TwoShorts RangedAttackPowerMods = 127,
370 Float RangedAttackPowerMultiplier = 128,
371 Float MinRangedDamage = 129,
372 Float MaxRangedDamage = 130,
373 Integer[7] PowerCostModifier = 131,
374 Float[7] PowerCostMultiplier = 138,
375 Float MaxHealthModifier = 145,
376 Float HoverHeight = 146
377 }
378}
379
380fields! {
381 pub enum PlayerField {
382 Long DuelArbiter = 148,
383 Integer Flags = 150,
384 Integer GuildId = 151,
385 Integer GuildRank = 152,
386 Bytes[3] Bytes = 153,
387 Integer DuelTeam = 156,
388 Integer GuildTimestamp = 157,
389 Custom (Integer, Integer, TwoShorts, TwoShorts, Integer)[25] QuestLog = 158,
390 Custom (Integer, TwoShorts)[12] VisibleItems = 283,
391 Integer ChosenTitle = 321,
392 Integer FakeInebriation = 322,
393 Integer Pad0 = 323,
394 Long[23] InvSlot = 324,
395 Long[16] PackSlot = 370,
396 Long[28] BankSlot = 402,
397 Long[7] BankBagSlot = 458,
398 Long[12] VendorBuybackSlot = 472,
399 Long[32] KeyringSlot = 496,
400 Long[32] CurrencyTokenSlot = 560,
401 Long Farsight = 624,
402 Long[3] KnownTitles = 626,
403 Long KnownCurrencies = 632,
404 Integer Xp = 634,
405 Integer NextLevelXp = 635,
406 TwoShorts[384] SkillInfo = 636,
407 Integer[2] CharacterPoints = 1020,
408 Integer TrackCreatures = 1022,
409 Integer TrackResources = 1023,
410 Float BlockPercentage = 1024,
411 Float DodgePercentage = 1025,
412 Float ParryPercentage = 1026,
413 Integer Expertise = 1027,
414 Integer OffhandExpertise = 1028,
415 Float CritPercentage = 1029,
416 Float RangedCritPercentage = 1030,
417 Float OffhandCritPercentage = 1031,
418 Float[7] SpellCritPercentage = 1032,
419 Integer ShieldBlock = 1039,
420 Float ShieldBlockCritPercentage = 1040,
421 Bytes[128] ExploredZones = 1041,
422 Integer RestStateExperience = 1169,
423 Integer Coinage = 1170,
424 Integer[7] ModDamageDonePos = 1171,
425 Integer[7] ModDamageDoneNeg = 1178,
426 Integer[7] ModDamageDonePct = 1185,
427 Integer ModHealingDonePos = 1192,
428 Float ModHealingPct = 1193,
429 Float ModHealingDonePct = 1194,
430 Integer ModTargetResistance = 1195,
431 Integer ModTargetPhysicalResistance = 1196,
432 Bytes FieldBytes = 1197,
433 Long AmmoId = 1198,
434 Integer SelfResSpell = 1199,
435 Integer PvpMedals = 1200,
436 Integer[12] BuybackPrice = 1201,
437 Integer[12] BuybackTimestamp = 1213,
438 TwoShorts Kills = 1225,
439 Integer TodayContribution = 1226,
440 Integer YesterdayContribution = 1227,
441 Integer LifetimeHonorableKills = 1228,
442 Integer FieldBytes2 = 1229,
443 Integer WatchedFactionIndex = 1230,
444 Integer[25] CombatRating = 1231,
445 Integer[21] ArenaTeamInfo1 = 1256,
446 Integer HonorCurrency = 1277,
447 Integer ArenaCurrency = 1278,
448 Integer MaxLevel = 1279,
449 Integer[25] DailyQuests = 1280,
450 Float[4] RuneRegen = 1305,
451 Integer[3] NoReagentCost = 1309,
452 Integer[6] GlyphSlots = 1312,
453 Integer[6] Glyphs = 1318,
454 Integer GlyphsEnabled = 1324,
455 Integer PetSpellPower = 1325,
456 }
457}
458
459fields! {
460 pub enum ItemField {
461 Long Owner = 6,
462 Long Contained = 8,
463 Long Creator = 10,
464 Long Giftcreator = 12,
465 Integer StackCount = 14,
466 Integer Duration = 15,
467 Integer[5] SpellCharges = 16,
468 Integer Flags = 21,
469 Custom (Integer, Integer, TwoShorts)[12] Enchantment = 22,
470 Integer PropertySeed = 58,
471 Integer RandomPropertiesId = 59,
472 Integer Durability = 60,
473 Integer MaxDurability = 61,
474 Integer CreatePlayedTime = 62,
475 }
476}
477
478fields! {
479 pub enum ContainerField {
480 Integer NumSlots = 64,
481 Bytes AlignPad = 65,
482 Long[36] Slot = 66,
483 }
484}
485
486fields! {
487 pub enum GameObjectField {
488 Long CreatedBy = 6,
489 Integer DisplayId = 8,
490 Integer Flags = 9,
491 Float[4] ParentRotation = 10,
492 TwoShorts Dynamic = 14,
493 Integer Faction = 15,
494 Integer Level = 16,
495 Bytes Bytes1 = 17,
496 }
497}
498
499fields! {
500 pub enum DynamicObjectField {
501 Long Caster = 6,
502 Bytes Bytes = 8,
503 Integer SpellId = 9,
504 Float Radius = 10,
505 Integer CastTime = 11,
506 }
507}
508
509fields! {
510 pub enum CorpseField {
511 Long Owner = 6,
512 Long Party = 8,
513 Integer DisplayId = 10,
514 Integer[19] Item = 11,
515 Bytes Bytes1 = 36,
516 Bytes Bytes2 = 37,
517 Integer Guild = 38,
518 Integer Flags = 39,
519 Integer DynamicFlags = 40,
520 }
521}
522
523#[derive(Serialize, Debug, Clone, PartialEq)]
524#[serde(untagged)]
525pub enum FieldValue {
526 Integer(i32),
527 IntegerArray(Vec<i32>),
528 Long(u64),
529 LongArray(Vec<u64>),
530 Float(f32),
531 FloatArray(Vec<f32>),
532 Bytes(u32),
533 BytesArray(Vec<u32>),
534 TwoShorts((i16, i16)),
535 TwoShortsArray(Vec<(i16, i16)>),
536 Custom(Vec<FieldValue>),
537 CustomArray(Vec<Vec<FieldValue>>),
538 None,
539}