Files
benilla-octo/crates/benilla-protocol/src/guid.rs
T
SamandClaude Opus 5.5 0668465bf3 comments: protocol, the crate root, guids and the action-bar to channel messages
1,031 comment lines become 320; the code is unchanged. A guid test
called transport 176495 the Menethil-Theramore ferry. The world
database names it the Grom'Gol-Undercity zeppelin.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 20:03:55 +07:00

163 lines
5.4 KiB
Rust

//! The 1.12.1 guid bit layout (vmangos `ObjectGuid.h`): `[high:16][entry:24][counter:24]` for
//! the families that carry a template entry, `[high:16][counter:48]` (32 bits used) for the rest.
/// `HIGHGUID_PLAYER`.
pub const HIGH_PLAYER: u16 = 0x0000;
/// `HIGHGUID_ITEM`, which is also `HIGHGUID_CONTAINER`: a bag is an item.
pub const HIGH_ITEM: u16 = 0x4000;
/// `HIGHGUID_GAMEOBJECT`.
pub const HIGH_GAMEOBJECT: u16 = 0xF110;
/// `HIGHGUID_UNIT`: a spawned creature.
pub const HIGH_UNIT: u16 = 0xF130;
/// `HIGHGUID_PET`.
pub const HIGH_PET: u16 = 0xF140;
/// `HIGHGUID_MO_TRANSPORT` (`ObjectGuid.h:77`): a boat or zeppelin, gameobject type 15.
pub const HIGH_MO_TRANSPORT: u16 = 0x1FC0;
/// `HIGHGUID_TRANSPORT` (`ObjectGuid.h:72`): an elevator, gameobject type 11.
pub const HIGH_TRANSPORT: u16 = 0xF120;
/// The high 16 bits, the object-family tag (`GetHigh`).
pub fn high(guid: u64) -> u16 {
((guid >> 48) & 0xFFFF) as u16
}
/// A non-zero player-character guid.
pub fn is_player(guid: u64) -> bool {
guid != 0 && high(guid) == HIGH_PLAYER
}
/// A creature or pet guid (`IsCreatureOrPet`); only a creature's carries a template entry.
pub fn is_creature_or_pet(guid: u64) -> bool {
matches!(high(guid), HIGH_UNIT | HIGH_PET)
}
/// A pet guid (`IsPet`), whose middle field is a pet number, not a template entry.
pub fn is_pet(guid: u64) -> bool {
high(guid) == HIGH_PET
}
/// The pet number in bits 24-47, fed in as `_Create`'s entry (`Objects/Pet.cpp:2250`): the key
/// of `CMSG_PET_NAME_QUERY`, answered only when it matches `CharmInfo::GetPetNumber()`.
pub fn pet_number(guid: u64) -> Option<u32> {
is_pet(guid).then_some(((guid >> 24) & 0xFF_FFFF) as u32)
}
/// A non-zero item or container guid (`IsItem`); the entry is only in `OBJECT_FIELD_ENTRY`.
pub fn is_item(guid: u64) -> bool {
guid != 0 && high(guid) == HIGH_ITEM
}
/// Either transport family: a boat or zeppelin, or an elevator.
pub fn is_transport(guid: u64) -> bool {
matches!(high(guid), HIGH_MO_TRANSPORT | HIGH_TRANSPORT)
}
pub fn is_gameobject(guid: u64) -> bool {
high(guid) == HIGH_GAMEOBJECT
}
/// The template entry: bits 24-47 for creatures, gameobjects and elevators (`HasEntry`,
/// `ObjectGuid.h:223-240`; `GameObject.cpp:207`), the full low 32 bits for boats and zeppelins,
/// whose template entry is passed as the counter (`Transport.cpp:65`, `ObjectGuid.h:123`). `None`
/// for pets although `HasEntry` is true: their slot holds the pet number.
pub fn entry(guid: u64) -> Option<u32> {
match high(guid) {
HIGH_MO_TRANSPORT => Some((guid & 0xFFFF_FFFF) as u32),
HIGH_UNIT | HIGH_GAMEOBJECT | HIGH_TRANSPORT => Some(((guid >> 24) & 0xFF_FFFF) as u32),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Composes a guid as the vmangos ctor does (`ObjectGuid.h:123`).
fn compose(high: u16, entry: u32, counter: u32) -> u64 {
u64::from(counter) | (u64::from(entry) << 24) | (u64::from(high) << 48)
}
#[test]
fn creature_guid_decodes_high_and_entry() {
let g = compose(HIGH_UNIT, 69, 12345);
assert_eq!(high(g), HIGH_UNIT);
assert!(is_creature_or_pet(g));
assert!(!is_player(g));
assert_eq!(entry(g), Some(69));
}
#[test]
fn player_guid_has_no_entry() {
let g = compose(HIGH_PLAYER, 0, 7);
assert!(is_player(g));
assert!(!is_creature_or_pet(g));
assert_eq!(entry(g), None);
}
#[test]
fn zero_guid_is_nothing() {
assert!(!is_player(0));
assert!(!is_creature_or_pet(0));
assert!(!is_item(0));
assert_eq!(entry(0), None);
}
#[test]
fn item_guid_is_item_and_has_no_entry() {
let g = compose(HIGH_ITEM, 0, 42);
assert!(is_item(g));
assert!(!is_player(g));
assert_eq!(
entry(g),
None,
"an item's entry is a descriptor field, not guid bits"
);
}
#[test]
fn entry_masks_to_24_bits() {
let g = compose(HIGH_UNIT, 0xFF_FFFF, 0xFF_FFFF);
assert_eq!(entry(g), Some(0xFF_FFFF));
}
#[test]
fn a_pet_carries_a_pet_number_not_a_template_entry() {
let g = compose(HIGH_PET, 137, 4242);
assert!(is_pet(g));
assert!(is_creature_or_pet(g));
assert_eq!(pet_number(g), Some(137));
assert_eq!(entry(g), None, "a pet's slot is not a template entry");
assert_eq!(pet_number(compose(HIGH_UNIT, 137, 4242)), None);
}
#[test]
fn elevator_guid_decodes_high_and_entry() {
let g = compose(HIGH_TRANSPORT, 900, 4242);
assert_eq!(high(g), HIGH_TRANSPORT);
assert!(is_transport(g));
assert_eq!(entry(g), Some(900));
}
#[test]
fn mo_transport_guid_decodes_high_and_entry() {
// The Grom'Gol-Undercity zeppelin's template entry, which the ctor takes as the counter.
let g = compose(HIGH_MO_TRANSPORT, 0, 176_495);
assert_eq!(high(g), HIGH_MO_TRANSPORT);
assert!(is_transport(g));
assert_eq!(
entry(g),
Some(176_495),
"the template entry rides the full low 32 bits, not bits 24-47"
);
// The 24-bit entry slot does not hold it: the two layouts really differ.
assert_ne!(((g >> 24) & 0xFF_FFFF) as u32, 176_495);
}
#[test]
fn is_transport_excludes_other_families() {
assert!(!is_transport(compose(HIGH_UNIT, 69, 1)));
assert!(!is_transport(compose(HIGH_GAMEOBJECT, 1, 1)));
assert!(!is_transport(0));
}
}