Skip to main content

tentacli_traits/types/
update_fields.rs

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                    // TODO: rename this
97                    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}