From 7d646a7ed2d9bdc1a24930b00d8448a6900fe2d4 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Tue, 10 Jan 2023 16:23:21 +0100 Subject: [PATCH] Core/Creatures: Split creature_addon and creature_template_addon's byte columns into seperate ones * Instead of dumping raw byte values into these fields, we now only allow setting values which should be accessible to database devs. The remaining byte values should be handled by core internals and spells (pet talents and shapeshifting for example) --- .../world/4.3.4/2023_01_10_01_world.sql | 34 ++++++++ .../game/Entities/Creature/Creature.cpp | 85 ++++++++---------- .../game/Entities/Creature/CreatureData.h | 7 +- src/server/game/Entities/Unit/UnitDefines.h | 22 +++-- src/server/game/Globals/ObjectMgr.cpp | 86 ++++++++++++++----- 5 files changed, 152 insertions(+), 82 deletions(-) create mode 100644 sql/updates/world/4.3.4/2023_01_10_01_world.sql diff --git a/sql/updates/world/4.3.4/2023_01_10_01_world.sql b/sql/updates/world/4.3.4/2023_01_10_01_world.sql new file mode 100644 index 0000000000..7cb80c1cde --- /dev/null +++ b/sql/updates/world/4.3.4/2023_01_10_01_world.sql @@ -0,0 +1,34 @@ +ALTER TABLE `creature_template_addon` + ADD COLUMN `StandState` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `mount`, + ADD COLUMN `AnimTier` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `StandState`, + ADD COLUMN `VisFlags` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `AnimTier`, + ADD COLUMN `SheathState` TINYINT UNSIGNED DEFAULT 1 NOT NULL AFTER `VisFlags`, + ADD COLUMN `PvPFlags` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `SheathState`; + +UPDATE `creature_template_addon` SET `StandState`=`bytes1` & 0xFF; +UPDATE `creature_template_addon` SET `AnimTier`=(`bytes1` >> 24) & 0xFF; +UPDATE `creature_template_addon` SET `VisFlags`=(`bytes1` >> 16) & 0xFF; +UPDATE `creature_template_addon` SET `SheathState`=`bytes2` & 0xFF; +UPDATE `creature_template_addon` SET `PvPFlags`=(`bytes2` >> 8) & 0xFF; + +ALTER TABLE `creature_addon` + ADD COLUMN `StandState` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `mount`, + ADD COLUMN `AnimTier` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `StandState`, + ADD COLUMN `VisFlags` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `AnimTier`, + ADD COLUMN `SheathState` TINYINT UNSIGNED DEFAULT 1 NOT NULL AFTER `VisFlags`, + ADD COLUMN `PvPFlags` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `SheathState`; + +UPDATE `creature_addon` SET `StandState`=`bytes1` & 0xFF; +UPDATE `creature_addon` SET `AnimTier`=(`bytes1` >> 24) & 0xFF; +UPDATE `creature_addon` SET `VisFlags`=(`bytes1` >> 16) & 0xFF; +UPDATE `creature_addon` SET `SheathState`=`bytes2` & 0xFF; +UPDATE `creature_addon` SET `PvPFlags`=(`bytes2` >> 8) & 0xFF; + +-- Conversion done, drop old columns +ALTER TABLE `creature_template_addon` + DROP COLUMN `bytes1`, + DROP COLUMN `bytes2`; + +ALTER TABLE `creature_addon` + DROP COLUMN `bytes1`, + DROP COLUMN `bytes2`; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index e6c5d6fb6d..55384e9226 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2532,72 +2532,55 @@ CreatureAddon const* Creature::GetCreatureAddon() const //creature_addon table bool Creature::LoadCreaturesAddon() { - CreatureAddon const* cainfo = GetCreatureAddon(); - if (!cainfo) + CreatureAddon const* creatureAddon = GetCreatureAddon(); + if (!creatureAddon) return false; - if (cainfo->mount != 0) - Mount(cainfo->mount); + if (creatureAddon->mount != 0) + Mount(creatureAddon->mount); - if (cainfo->bytes1 != 0) - { - // 0 StandState - // 1 FreeTalentPoints Pet only, so always 0 for default creature - // 2 StandFlags - // 3 StandMiscFlags + // UNIT_FIELD_BYTES_1 values + SetStandState(UnitStandStateType(creatureAddon->standState)); + SetAnimationTier(AnimationTier(creatureAddon->animTier)); + SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_VIS_FLAG, creatureAddon->visFlags); - SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, uint8(cainfo->bytes1 & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_PET_TALENTS, uint8((cainfo->bytes1 >> 8) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_PET_TALENTS, 0); - SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_VIS_FLAG, uint8((cainfo->bytes1 >> 16) & 0xFF)); + //! Suspected correlation between UNIT_FIELD_BYTES_1, offset 3, value 0x2: + //! If no inhabittype_fly (if no MovementFlag_DisableGravity or MovementFlag_CanFly flag found in sniffs) + //! Check using InhabitType as movement flags are assigned dynamically + //! basing on whether the creature is in air or not + //! Set MovementFlag_Hover. Otherwise do nothing. + if (CanHover()) + AddUnitMovementFlag(MOVEMENTFLAG_HOVER); - SetAnimationTier(static_cast((cainfo->bytes1 >> 24) & 0xFF)); + // UNIT_FIELD_BYTES_2 values + SetSheath(SheathState(creatureAddon->sheathState)); + SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, creatureAddon->pvpFlags); - //! Suspected correlation between UNIT_FIELD_BYTES_1, offset 3, value 0x2: - //! If no inhabittype_fly (if no MovementFlag_DisableGravity or MovementFlag_CanFly flag found in sniffs) - //! Check using InhabitType as movement flags are assigned dynamically - //! basing on whether the creature is in air or not - //! Set MovementFlag_Hover. Otherwise do nothing. - if (CanHover()) - AddUnitMovementFlag(MOVEMENTFLAG_HOVER); - } + // These fields must only be handled by core internals and must not be modified via scripts/DB data + SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, 0); + SetShapeshiftForm(FORM_NONE); - if (cainfo->bytes2 != 0) - { - // 0 SheathState - // 1 PvpFlags - // 2 PetFlags Pet only, so always 0 for default creature - // 3 ShapeshiftForm Must be determined/set by shapeshift spell/aura + if (creatureAddon->emote != 0) + SetUInt32Value(UNIT_NPC_EMOTESTATE, creatureAddon->emote); - SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHEATH_STATE, uint8(cainfo->bytes2 & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, uint8((cainfo->bytes2 >> 8) & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, uint8((cainfo->bytes2 >> 16) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, 0); - //SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHAPESHIFT_FORM, uint8((cainfo->bytes2 >> 24) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHAPESHIFT_FORM, 0); - } - - if (cainfo->emote != 0) - SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote); - - SetAIAnimKitId(cainfo->aiAnimKit); - SetMovementAnimKitId(cainfo->movementAnimKit); - SetMeleeAnimKitId(cainfo->meleeAnimKit); + SetAIAnimKitId(creatureAddon->aiAnimKit); + SetMovementAnimKitId(creatureAddon->movementAnimKit); + SetMeleeAnimKitId(creatureAddon->meleeAnimKit); // Check if visibility distance different - if (cainfo->visibilityDistanceType != VisibilityDistanceType::Normal) - SetVisibilityDistanceOverride(cainfo->visibilityDistanceType); + if (creatureAddon->visibilityDistanceType != VisibilityDistanceType::Normal) + SetVisibilityDistanceOverride(creatureAddon->visibilityDistanceType); // Load pathing data - if (cainfo->waypointPathId) - _waypointPathId = cainfo->waypointPathId; + if (creatureAddon->waypointPathId) + _waypointPathId = creatureAddon->waypointPathId; - if (cainfo->cyclicSplinePathId) - _cyclicSplinePathId = cainfo->cyclicSplinePathId; + if (creatureAddon->cyclicSplinePathId) + _cyclicSplinePathId = creatureAddon->cyclicSplinePathId; - if (!cainfo->auras.empty()) + if (!creatureAddon->auras.empty()) { - for (std::vector::const_iterator itr = cainfo->auras.begin(); itr != cainfo->auras.end(); ++itr) + for (std::vector::const_iterator itr = creatureAddon->auras.begin(); itr != creatureAddon->auras.end(); ++itr) { SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(*itr); if (!AdditionalSpellInfo) diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 5d37cedc81..5193189630 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -336,8 +336,11 @@ struct CreatureAddon uint32 waypointPathId; uint32 cyclicSplinePathId; uint32 mount; - uint32 bytes1; - uint32 bytes2; + uint8 standState; + uint8 animTier; + uint8 sheathState; + uint8 pvpFlags; + uint8 visFlags; uint32 emote; uint16 aiAnimKit; uint16 movementAnimKit; diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index bc27785a79..2a6c4871bd 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -39,7 +39,9 @@ enum UnitStandStateType : uint8 UNIT_STAND_STATE_SIT_HIGH_CHAIR = 6, UNIT_STAND_STATE_DEAD = 7, UNIT_STAND_STATE_KNEEL = 8, - UNIT_STAND_STATE_SUBMERGED = 9 + UNIT_STAND_STATE_SUBMERGED = 9, + + MAX_UNIT_STAND_STATE }; // byte flag value (UNIT_FIELD_BYTES_1, 2) @@ -80,11 +82,13 @@ enum UnitBytes2Offsets : uint8 // UNIT_FIELD_BYTES_1 (UNIT_BYTES_1_OFFSET_ANIM_TIER) enum class AnimationTier : uint8 { - Ground = 0, - Swim = 1, - Hover = 2, - Fly = 3, - Submerged = 4, + Ground = 0, // plays ground tier animations + Swim = 1, // falls back to ground tier animations, not handled by the client, should never appear in sniffs, will prevent tier change animations from playing correctly if used + Hover = 2, // plays flying tier animations or falls back to ground tier animations, automatically enables hover clientside when entering visibility with this value + Fly = 3, // plays flying tier animations + Submerged = 4, + + Max }; // low byte (0 from 0..3) of UNIT_FIELD_BYTES_2 @@ -92,10 +96,10 @@ enum SheathState : uint8 { SHEATH_STATE_UNARMED = 0, // non prepared weapon SHEATH_STATE_MELEE = 1, // prepared melee weapon - SHEATH_STATE_RANGED = 2 // prepared ranged weapon -}; + SHEATH_STATE_RANGED = 2, // prepared ranged weapon -#define MAX_SHEATH_STATE 3 + MAX_SHEATH_STATE +}; // byte (1 from 0..3) of UNIT_FIELD_BYTES_2 enum UnitPVPStateFlags : uint8 diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 7b9af159d4..0d39b2c3d4 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -564,8 +564,8 @@ void ObjectMgr::LoadCreatureTemplateAddons() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 10 11 - QueryResult result = WorldDatabase.Query("SELECT entry, waypointPathId, cyclicSplinePathId, mount, bytes1, bytes2, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon"); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + QueryResult result = WorldDatabase.Query("SELECT entry, waypointPathId, cyclicSplinePathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon"); if (!result) { @@ -591,15 +591,18 @@ void ObjectMgr::LoadCreatureTemplateAddons() creatureAddon.waypointPathId = fields[1].GetUInt32(); creatureAddon.cyclicSplinePathId = fields[2].GetUInt32(); creatureAddon.mount = fields[3].GetUInt32(); - creatureAddon.bytes1 = fields[4].GetUInt32(); - creatureAddon.bytes2 = fields[5].GetUInt32(); - creatureAddon.emote = fields[6].GetUInt32(); - creatureAddon.aiAnimKit = fields[7].GetUInt16(); - creatureAddon.movementAnimKit = fields[8].GetUInt16(); - creatureAddon.meleeAnimKit = fields[9].GetUInt16(); - creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[10].GetUInt8()); + creatureAddon.standState = fields[4].GetUInt8(); + creatureAddon.animTier = fields[5].GetUInt8(); + creatureAddon.visFlags = fields[6].GetUInt8(); + creatureAddon.sheathState = fields[7].GetUInt8(); + creatureAddon.pvpFlags = fields[8].GetUInt8(); + creatureAddon.emote = fields[9].GetUInt32(); + creatureAddon.aiAnimKit = fields[10].GetUInt16(); + creatureAddon.movementAnimKit = fields[11].GetUInt16(); + creatureAddon.meleeAnimKit = fields[12].GetUInt16(); + creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[13].GetUInt8()); - Tokenizer tokens(fields[11].GetString(), ' '); + Tokenizer tokens(fields[14].GetString(), ' '); uint8 i = 0; creatureAddon.auras.resize(tokens.size()); for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) @@ -632,6 +635,26 @@ void ObjectMgr::LoadCreatureTemplateAddons() } } + if (creatureAddon.standState >= MAX_UNIT_STAND_STATE) + { + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid unit stand state (%u) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.standState); + creatureAddon.standState = 0; + } + + if (AnimationTier(creatureAddon.animTier) >= AnimationTier::Max) + { + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid animation tier (%u) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.animTier); + creatureAddon.animTier = 0; + } + + if (creatureAddon.sheathState >= MAX_SHEATH_STATE) + { + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid sheath state (%u) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.sheathState); + creatureAddon.sheathState = 0; + } + + // PvPFlags don't need any checking for the time being since they cover the entire range of a byte + if (!sEmotesStore.LookupEntry(creatureAddon.emote)) { TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid emote (%u) defined in `creature_template_addon`.", entry, creatureAddon.emote); @@ -1148,8 +1171,8 @@ void ObjectMgr::LoadCreatureAddons() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 10 11 - QueryResult result = WorldDatabase.Query("SELECT guid, waypointPathId, cyclicSplinePathId, mount, bytes1, bytes2, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon"); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + QueryResult result = WorldDatabase.Query("SELECT guid, waypointPathId, cyclicSplinePathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon"); if (!result) { @@ -1203,15 +1226,18 @@ void ObjectMgr::LoadCreatureAddons() } creatureAddon.mount = fields[3].GetUInt32(); - creatureAddon.bytes1 = fields[4].GetUInt32(); - creatureAddon.bytes2 = fields[5].GetUInt32(); - creatureAddon.emote = fields[6].GetUInt32(); - creatureAddon.aiAnimKit = fields[7].GetUInt16(); - creatureAddon.movementAnimKit = fields[8].GetUInt16(); - creatureAddon.meleeAnimKit = fields[9].GetUInt16(); - creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[10].GetUInt8()); + creatureAddon.standState = fields[4].GetUInt8(); + creatureAddon.animTier = fields[5].GetUInt8(); + creatureAddon.visFlags = fields[6].GetUInt8(); + creatureAddon.sheathState = fields[7].GetUInt8(); + creatureAddon.pvpFlags = fields[8].GetUInt8(); + creatureAddon.emote = fields[9].GetUInt32(); + creatureAddon.aiAnimKit = fields[10].GetUInt16(); + creatureAddon.movementAnimKit = fields[11].GetUInt16(); + creatureAddon.meleeAnimKit = fields[12].GetUInt16(); + creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[13].GetUInt8()); - Tokenizer tokens(fields[11].GetString(), ' '); + Tokenizer tokens(fields[14].GetString(), ' '); uint8 i = 0; creatureAddon.auras.resize(tokens.size()); for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) @@ -1244,6 +1270,26 @@ void ObjectMgr::LoadCreatureAddons() } } + if (creatureAddon.standState >= MAX_UNIT_STAND_STATE) + { + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid unit stand state (%u) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.standState); + creatureAddon.standState = 0; + } + + if (AnimationTier(creatureAddon.animTier) >= AnimationTier::Max) + { + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid animation tier (%u) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.animTier); + creatureAddon.animTier = 0; + } + + if (creatureAddon.sheathState >= MAX_SHEATH_STATE) + { + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid sheath state (%u) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.sheathState); + creatureAddon.sheathState = 0; + } + + // PvPFlags don't need any checking for the time being since they cover the entire range of a byte + if (!sEmotesStore.LookupEntry(creatureAddon.emote)) { TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid emote (%u) defined in `creature_addon`.", guid, creatureAddon.emote);