掩码更新算法的 意义在于 有更新的数据 才会跟数据库 或者 客户端同步 没有更新的数据会跳过 不同步 这样节约了IO传输的字节数 减少带宽压力 数据库压力 还有根据类型掩码 做到同步具体类型数据给玩家 文章中有不懂得 可以加Q群397926909
1 掩码类代码:
#include "Util/Errors.h"
class UpdateMask
{
public:
UpdateMask() : mHasData(false), mCount(0), mBlocks(0), mUpdateMask(nullptr) { }
UpdateMask(const UpdateMask& mask) : mUpdateMask(nullptr) { *this = mask; }
~UpdateMask()
{
delete[] mUpdateMask;
}
void SetBit(uint32 index)
{
((uint8*)mUpdateMask)[ index >> 3 ] |= 1 << (index & 0x7);
mHasData = true;
}
void UnsetBit(uint32 index)
{
((uint8*)mUpdateMask)[ index >> 3 ] &= (0xff ^ (1 << (index & 0x7)));
}
bool GetBit(uint32 index) const
{
return (((uint8*)mUpdateMask)[ index >> 3 ] & (1 << (index & 0x7))) != 0;
}
uint32 GetBlockCount() const { return mBlocks; }
uint32 GetLength() const { return mBlocks << 2; }
uint32 GetCount() const { return mCount; }
uint8* GetMask() const { return (uint8*)mUpdateMask; }
bool HasData() const { return mHasData; }
void SetCount(uint32 valuesCount)
{
delete[] mUpdateMask;
mCount = valuesCount;
mBlocks = (valuesCount + 31) / 32;
mUpdateMask = new uint32[mBlocks];
memset(mUpdateMask, 0, mBlocks << 2);
}
void Clear()
{
if (mUpdateMask)
memset(mUpdateMask, 0, mBlocks << 2);
mHasData = false;
}
UpdateMask& operator = (const UpdateMask& mask)
{
SetCount(mask.mCount);
memcpy(mUpdateMask, mask.mUpdateMask, mBlocks << 2);
return *this;
}
void operator &= (const UpdateMask& mask)
{
MANGOS_ASSERT(mask.mCount <= mCount);
for (uint32 i = 0; i < mBlocks; ++i)
mUpdateMask[i] &= mask.mUpdateMask[i];
}
void operator |= (const UpdateMask& mask)
{
MANGOS_ASSERT(mask.mCount <= mCount);
for (uint32 i = 0; i < mBlocks; ++i)
mUpdateMask[i] |= mask.mUpdateMask[i];
}
UpdateMask operator & (const UpdateMask& mask) const
{
MANGOS_ASSERT(mask.mCount <= mCount);
UpdateMask newmask = *this;
newmask &= mask;
return newmask;
}
UpdateMask operator | (const UpdateMask& mask) const
{
MANGOS_ASSERT(mask.mCount <= mCount);
UpdateMask newmask = *this;
newmask |= mask;
return newmask;
}
private:
bool mHasData;
uint32 mCount;
uint32 mBlocks;
uint32* mUpdateMask;
};
上面代码需要注意的是SetCount 类 这个类会根据你的valuesCount 生成一个mBlocks 大小的uint32字节数组对象 我们就可以操作 mUpdateMask 数组对象的每个bit 位保存 对应数据是否更新过
2.UpdateFileds.h 这个头文件作用是生成一些操作方法 还有枚举定义每个bit保存的数据
enum EObjectFields
{
OBJECT_FIELD_GUID = 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
OBJECT_FIELD_TYPE = 0x0002, // Size: 1, Type: INT, Flags: PUBLIC
OBJECT_FIELD_ENTRY = 0x0003, // Size: 1, Type: INT, Flags: PUBLIC
OBJECT_FIELD_SCALE_X = 0x0004, // Size: 1, Type: FLOAT, Flags: PUBLIC
OBJECT_FIELD_PADDING = 0x0005, // Size: 1, Type: INT, Flags: NONE
OBJECT_END = 0x0006,
};
//物品类的操作bit枚举
enum EItemFields
{
ITEM_FIELD_OWNER = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
ITEM_FIELD_CONTAINED = OBJECT_END + 0x0002, // Size: 2, Type: LONG, Flags: PUBLIC
ITEM_FIELD_CREATOR = OBJECT_END + 0x0004, // Size: 2, Type: LONG, Flags: PUBLIC
ITEM_FIELD_GIFTCREATOR = OBJECT_END + 0x0006, // Size: 2, Type: LONG, Flags: PUBLIC
ITEM_FIELD_STACK_COUNT = OBJECT_END + 0x0008, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2
ITEM_FIELD_DURATION = OBJECT_END + 0x0009, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2
ITEM_FIELD_SPELL_CHARGES = OBJECT_END + 0x000A, // Size: 5, Type: INT, Flags: OWNER_ONLY, UNK2
ITEM_FIELD_FLAGS = OBJECT_END + 0x000F, // Size: 1, Type: INT, Flags: PUBLIC
ITEM_FIELD_ENCHANTMENT_1_1 = OBJECT_END + 0x0010, // Size: 33, Type: INT, Flags: PUBLIC
ITEM_FIELD_PROPERTY_SEED = OBJECT_END + 0x0031, // Size: 1, Type: INT, Flags: PUBLIC
ITEM_FIELD_RANDOM_PROPERTIES_ID = OBJECT_END + 0x0032, // Size: 1, Type: INT, Flags: PUBLIC
ITEM_FIELD_ITEM_TEXT_ID = OBJECT_END + 0x0033, // Size: 1, Type: INT, Flags: OWNER_ONLY
ITEM_FIELD_DURABILITY = OBJECT_END + 0x0034, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2
ITEM_FIELD_MAXDURABILITY = OBJECT_END + 0x0035, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2
ITEM_END = OBJECT_END + 0x0036,
};
enum EContainerFields
{
//省略 就是一些枚举结构 跟上面EItemFields类似
};
enum EUnitFields
{
//省略 就是一些枚举结构 跟上面EItemFields类似
};
enum EGameObjectFields
{
//省略 就是一些枚举结构 跟上面EItemFields类似
};
enum ECorpseFields
{
//省略 就是一些枚举结构 跟上面EItemFields类似
};
//操作结构数值的类型
enum UpdateFieldValueTypes
{
UF_TYPE_NONE = 0,
UF_TYPE_INT = 1,
UF_TYPE_TWO_SHORT = 2,
UF_TYPE_FLOAT = 3,
UF_TYPE_GUID = 4,
UF_TYPE_BYTES = 5,
UF_TYPE_BYTES2 = 6
};
//操作对象掩码格式
enum UpdateFieldFlags : uint16
{
UF_FLAG_NONE = 0x000, // not a real field
UF_FLAG_PUBLIC = 0x001, // visible to everyone
UF_FLAG_PRIVATE = 0x002, // only visible to self
UF_FLAG_OWNER_ONLY = 0x004, // only visible to owner
UF_FLAG_UNK1 = 0x008, // meaning unknown, not used in vanilla
UF_FLAG_UNK2 = 0x010, // meaning unknown, only seen on items together with OWNER_ONLY
UF_FLAG_SPECIAL_INFO = 0x020, // only visible to caster of SPELL_AURA_EMPATHY (Beast Lore)
UF_FLAG_GROUP_ONLY = 0x040, // only visible to raid group members
UF_FLAG_UNK5 = 0x080, // meaning unknown, not used in vanilla
UF_FLAG_DYNAMIC = 0x100, // visible to everyone, but different values can be sent to different observers
};
//对应的操作对象
struct UpdateFieldData
{
UpdateFieldData() = default;
constexpr UpdateFieldData(uint8 objectTypeMask_, const char* name_, uint16 offset_, uint16 size_, UpdateFieldValueTypes valueType_, uint16 flags_) :
objectTypeMask(objectTypeMask_), name(name_), offset(offset_), size(size_), valueType(valueType_), flags(flags_) {}
uint8 objectTypeMask = 0;
const char* name = "";
uint16 offset = 0;
uint16 size = 0;
UpdateFieldValueTypes valueType = UF_TYPE_NONE;
uint16 flags = UF_FLAG_NONE;
};
//调用函数
namespace UpdateFields
{
uint16 const* GetUpdateFieldFlagsArray(uint8 objectTypeId);
UpdateFieldData const* GetUpdateFieldDataByName(char const* name);
UpdateFieldData const* GetUpdateFieldDataByTypeMaskAndOffset(uint8 objectTypeMask, uint16 offset);
};
上面就是一个枚举结构 外加一部分的操作函数 UpdateFieldFlags 结构体代表设置不同掩码类型
3.UpdateFileds.cpp文件 根据上面的枚举 已经UpdateFieldData生成了一个数组 数组长度为399 类容是UpdateFieldData 代码太长 省略一部分
static std::array<UpdateFieldData, 399> const g_updateFieldsData =
{{
// enum EObjectFields
{ TYPEMASK_OBJECT , "OBJECT_FIELD_GUID" , 0x0 , 2 , UF_TYPE_GUID , UF_FLAG_PUBLIC },
{ TYPEMASK_OBJECT , "OBJECT_FIELD_TYPE" , 0x2 , 1 , UF_TYPE_INT , UF_FLAG_PUBLIC },
{ TYPEMASK_OBJECT , "OBJECT_FIELD_ENTRY" , 0x3 , 1 , UF_TYPE_INT , UF_FLAG_PUBLIC },
{ TYPEMASK_OBJECT , "OBJECT_FIELD_SCALE_X" , 0x4 , 1 , UF_TYPE_FLOAT , UF_FLAG_PUBLIC },
{ TYPEMASK_OBJECT , "OBJECT_FIELD_PADDING" , 0x5 , 1 , UF_TYPE_INT , UF_FLAG_NONE },
{ TYPEMASK_OBJECT , "OBJECT_END" , 0x6 , 0 , UF_TYPE_NONE , UF_FLAG_NONE }
}}
下面这个方法 生成了不同数据对应的掩码 并且由g_updateFieldsData数组 初始化了掩码的flag
template<std::size_t SIZE>
static std::array<uint16, SIZE> SetupUpdateFieldFlagsArray(uint8 objectTypeMask)
{
std::array<uint16, SIZE> flagsArray;
for (auto const& itr : g_updateFieldsData)
{
if ((itr.objectTypeMask & objectTypeMask) == 0)
continue;
for (uint16 i = itr.offset; i < itr.offset + itr.size; i++)
{
flagsArray.at(i) = itr.flags;
}
}
return flagsArray;
}
static std::array<uint16, CONTAINER_END> const g_containerUpdateFieldFlags = SetupUpdateFieldFlagsArray<CONTAINER_END>(TYPEMASK_OBJECT | TYPEMASK_ITEM | TYPEMASK_CONTAINER);
static std::array<uint16, PLAYER_END> const g_playerUpdateFieldFlags = SetupUpdateFieldFlagsArray<PLAYER_END>(TYPEMASK_OBJECT | TYPEMASK_UNIT | TYPEMASK_PLAYER);
static std::array<uint16, GAMEOBJECT_END> const g_gameObjectUpdateFieldFlags = SetupUpdateFieldFlagsArray<GAMEOBJECT_END>(TYPEMASK_OBJECT | TYPEMASK_GAMEOBJECT);
static std::array<uint16, DYNAMICOBJECT_END> const g_dynamicObjectUpdateFieldFlags = SetupUpdateFieldFlagsArray<DYNAMICOBJECT_END>(TYPEMASK_OBJECT | TYPEMASK_DYNAMICOBJECT);
static std::array<uint16, CORPSE_END> const g_corpseUpdateFieldFlags = SetupUpdateFieldFlagsArray<CORPSE_END>(TYPEMASK_OBJECT | TYPEMASK_CORPSE);
下面代码是根据不同的函数调用 获取对应的数组数据 返回
uint16 const* UpdateFields::GetUpdateFieldFlagsArray(uint8 objectTypeId)
{
switch (objectTypeId)
{
case TYPEID_ITEM:
case TYPEID_CONTAINER:
{
return g_containerUpdateFieldFlags.data();
}
case TYPEID_UNIT:
case TYPEID_PLAYER:
{
return g_playerUpdateFieldFlags.data();
}
case TYPEID_GAMEOBJECT:
{
return g_gameObjectUpdateFieldFlags.data();
}
case TYPEID_DYNAMICOBJECT:
{
return g_dynamicObjectUpdateFieldFlags.data();
}
case TYPEID_CORPSE:
{
return g_corpseUpdateFieldFlags.data();
}
}
sLog.outError("Unhandled object type id (%hhu) in GetUpdateFieldFlagsArray!", objectTypeId);
return 0;
}
UpdateFieldData const* UpdateFields::GetUpdateFieldDataByName(char const* name)
{
for (const auto& itr : g_updateFieldsData)
{
if (strcmp(itr.name, name) == 0)
return &itr;
}
return nullptr;
}
UpdateFieldData const* UpdateFields::GetUpdateFieldDataByTypeMaskAndOffset(uint8 objectTypeMask, uint16 offset)
{
for (auto const& itr : g_updateFieldsData)
{
if ((itr.objectTypeMask & objectTypeMask) == 0)
continue;
if (offset == itr.offset || (offset > itr.offset && offset < (itr.offset + itr.size)))
return &itr;
}
return nullptr;
}
4.updateData 文件 这个文件实现的功能是把数据通过zlib压缩 发送给需要同步的玩家
#ifndef __UPDATEDATA_H
#define __UPDATEDATA_H
#include "Util/ByteBuffer.h"
#include "Entities/ObjectGuid.h"
class WorldPacket;
class WorldSession;
enum ObjectUpdateType
{
UPDATETYPE_VALUES = 0,
UPDATETYPE_MOVEMENT = 1,
UPDATETYPE_CREATE_OBJECT = 2,
UPDATETYPE_CREATE_OBJECT2 = 3,
UPDATETYPE_OUT_OF_RANGE_OBJECTS = 4,
UPDATETYPE_NEAR_OBJECTS = 5
};
enum ObjectUpdateFlags
{
UPDATEFLAG_NONE = 0x0000,
UPDATEFLAG_SELF = 0x0001,
UPDATEFLAG_TRANSPORT = 0x0002,
UPDATEFLAG_HAS_ATTACKING_TARGET = 0x0004,
UPDATEFLAG_LOWGUID = 0x0008,
UPDATEFLAG_HIGHGUID = 0x0010,
UPDATEFLAG_LIVING = 0x0020,
UPDATEFLAG_HAS_POSITION = 0x0040,
};
struct BufferPair
{
ByteBuffer m_buffer;
uint32 m_blockCount;
};
class UpdateData
{
public:
UpdateData();
void AddOutOfRangeGUID(GuidSet& guids);
void AddOutOfRangeGUID(ObjectGuid const& guid);
void AddUpdateBlock(const ByteBuffer& block);
WorldPacket BuildPacket(size_t index, bool hasTransport = false); // Copy Elision is a thing
bool HasData() const { return m_data[0].m_buffer.size() > 0 || !m_outOfRangeGUIDs.empty(); }
size_t GetPacketCount() const { return m_data.size(); }
void Clear();
GuidSet const& GetOutOfRangeGUIDs() const { return m_outOfRangeGUIDs; }
void SendData(WorldSession& session);
protected:
GuidSet m_outOfRangeGUIDs;
std::vector<BufferPair> m_data;
uint32 m_currentIndex;
static void Compress(void* dst, uint32* dst_size, void* src, int src_size);
};
#include <zlib.h>
#include "Common.h"
#include "Entities/UpdateData.h"
#include "Util/ByteBuffer.h"
#include "Server/WorldPacket.h"
#include "Log.h"
#include "Server/Opcodes.h"
#include "World/World.h"
#include "Entities/ObjectGuid.h"
#include "Server/WorldSession.h"
UpdateData::UpdateData() : m_data(1, {ByteBuffer(0), 0}), m_currentIndex(0)
{
}
void UpdateData::AddOutOfRangeGUID(GuidSet& guids)
{
m_outOfRangeGUIDs.insert(guids.begin(), guids.end());
}
void UpdateData::AddOutOfRangeGUID(ObjectGuid const& guid)
{
m_outOfRangeGUIDs.insert(guid);
}
void UpdateData::AddUpdateBlock(const ByteBuffer& block)
{
const size_t existing = (128 + (9 * m_outOfRangeGUIDs.size()) + m_data[m_currentIndex].m_buffer.size());
if ((existing + block.size()) < MAX_NETCLIENT_PACKET_SIZE)
{
m_data[m_currentIndex].m_buffer.append(block);
++m_data[m_currentIndex].m_blockCount;
}
else
{
++m_currentIndex;
m_data.emplace_back();
m_data[m_currentIndex].m_buffer.append(block);
m_data[m_currentIndex].m_blockCount = 1;
}
}
void UpdateData::Compress(void* dst, uint32* dst_size, void* src, int src_size)
{
z_stream c_stream;
c_stream.zalloc = (alloc_func)nullptr;
c_stream.zfree = (free_func)nullptr;
c_stream.opaque = (voidpf)nullptr;
// default Z_BEST_SPEED (1)
int z_res = deflateInit(&c_stream, sWorld.getConfig(CONFIG_UINT32_COMPRESSION));
if (z_res != Z_OK)
{
sLog.outError("Can't compress update packet (zlib: deflateInit) Error code: %i (%s)", z_res, zError(z_res));
*dst_size = 0;
return;
}
c_stream.next_out = (Bytef*)dst;
c_stream.avail_out = *dst_size;
c_stream.next_in = (Bytef*)src;
c_stream.avail_in = (uInt)src_size;
z_res = deflate(&c_stream, Z_NO_FLUSH);
if (z_res != Z_OK)
{
sLog.outError("Can't compress update packet (zlib: deflate) Error code: %i (%s)", z_res, zError(z_res));
*dst_size = 0;
return;
}
if (c_stream.avail_in != 0)
{
sLog.outError("Can't compress update packet (zlib: deflate not greedy)");
*dst_size = 0;
return;
}
z_res = deflate(&c_stream, Z_FINISH);
if (z_res != Z_STREAM_END)
{
sLog.outError("Can't compress update packet (zlib: deflate should report Z_STREAM_END instead %i (%s)", z_res, zError(z_res));
*dst_size = 0;
return;
}
z_res = deflateEnd(&c_stream);
if (z_res != Z_OK)
{
sLog.outError("Can't compress update packet (zlib: deflateEnd) Error code: %i (%s)", z_res, zError(z_res));
*dst_size = 0;
return;
}
*dst_size = c_stream.total_out;
}
WorldPacket UpdateData::BuildPacket(size_t index, bool hasTransport)
{
WorldPacket packet;
MANGOS_ASSERT(packet.empty()); // shouldn't happen
ByteBuffer buf(4 + 1 + (m_outOfRangeGUIDs.empty() ? 0 : 1 + 4 + 9 * m_outOfRangeGUIDs.size()) + m_data[index].m_buffer.wpos());
buf << (uint32)(!m_outOfRangeGUIDs.empty() ? m_data[index].m_blockCount + 1 : m_data[index].m_blockCount);
buf << (uint8)(hasTransport ? 1 : 0);
if (!m_outOfRangeGUIDs.empty())
{
buf << (uint8) UPDATETYPE_OUT_OF_RANGE_OBJECTS;
buf << (uint32) m_outOfRangeGUIDs.size();
for (auto m_outOfRangeGUID : m_outOfRangeGUIDs)
buf << m_outOfRangeGUID.WriteAsPacked();
}
buf.append(m_data[index].m_buffer);
size_t pSize = buf.wpos(); // use real used data size
if (pSize > 100) // compress large packets
{
uint32 destsize = compressBound(pSize);
packet.resize(destsize + sizeof(uint32));
packet.put<uint32>(0, pSize);
Compress(const_cast<uint8*>(packet.contents()) + sizeof(uint32), &destsize, (void*)buf.contents(), pSize);
if (destsize == 0)
return packet;
packet.resize(destsize + sizeof(uint32));
packet.SetOpcode(SMSG_COMPRESSED_UPDATE_OBJECT);
}
else // send small packets without compression
{
packet.append(buf);
packet.SetOpcode(SMSG_UPDATE_OBJECT);
}
return packet;
}
void UpdateData::Clear()
{
m_data.clear();
m_outOfRangeGUIDs.clear();
}
void UpdateData::SendData(WorldSession& session)
{
for (size_t i = 0; i < GetPacketCount(); ++i)
{
WorldPacket packet = BuildPacket(i);
session.SendPacket(packet);
}
}
上面代码就实现了掩码记录当前位置是否更新过 使用的方法如下
void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const
{
if (!target)
return;
uint8 updatetype = UPDATETYPE_CREATE_OBJECT;
uint8 updateFlags = m_updateFlag;
/** lower flag1 **/
if (target == this) // building packet for yourself
updateFlags |= UPDATEFLAG_SELF;
if (m_itsNewObject)
{
switch (GetObjectGuid().GetHigh())
{
case HighGuid::HIGHGUID_DYNAMICOBJECT:
case HighGuid::HIGHGUID_CORPSE:
case HighGuid::HIGHGUID_PLAYER:
case HighGuid::HIGHGUID_UNIT:
case HighGuid::HIGHGUID_GAMEOBJECT:
case HighGuid::HIGHGUID_TRANSPORT:
updatetype = UPDATETYPE_CREATE_OBJECT2;
break;
default:
break;
}
}
if (isType(TYPEMASK_UNIT))
{
if (static_cast<Unit const*>(this)->GetVictim() && static_cast<Unit const*>(this)->hasUnitState(UNIT_STAT_MELEE_ATTACKING))
updateFlags |= UPDATEFLAG_HAS_ATTACKING_TARGET;
}
// DEBUG_LOG("BuildCreateUpdate: update-type: %u, object-type: %u got updateFlags: %X", updatetype, m_objectTypeId, updateFlags);
ByteBuffer buf(500);
buf << uint8(updatetype);
buf << GetPackGUID();
buf << uint8(m_objectTypeId);
BuildMovementUpdate(&buf, updateFlags);
UpdateMask updateMask;
updateMask.SetCount(m_valuesCount);
_SetCreateBits(updateMask, target);
BuildValuesUpdate(updatetype, &buf, &updateMask, target);
data->AddUpdateBlock(buf);
}
void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* updateMask, Player* target) const
{
if (!target)
return;
bool IsActivateToQuest = false;
bool IsPerCasterAuraState = false;
if (updatetype == UPDATETYPE_CREATE_OBJECT || updatetype == UPDATETYPE_CREATE_OBJECT2)
{
if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
{
if (((GameObject*)this)->ActivateToQuest(target) || target->IsGameMaster())
IsActivateToQuest = true;
updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
}
else if (isType(TYPEMASK_UNIT))
{
if (((Unit*)this)->HasAuraState(AURA_STATE_CONFLAGRATE))
{
IsPerCasterAuraState = true;
updateMask->SetBit(UNIT_FIELD_AURASTATE);
}
}
}
else // case UPDATETYPE_VALUES
{
if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
{
if (((GameObject*)this)->ActivateToQuest(target) || target->IsGameMaster())
IsActivateToQuest = true;
updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS);
}
else if (isType(TYPEMASK_UNIT))
{
if (((Unit*)this)->HasAuraState(AURA_STATE_CONFLAGRATE))
{
IsPerCasterAuraState = true;
updateMask->SetBit(UNIT_FIELD_AURASTATE);
}
}
}
MANGOS_ASSERT(updateMask && updateMask->GetCount() == m_valuesCount);
*data << (uint8)updateMask->GetBlockCount();
data->append(updateMask->GetMask(), updateMask->GetLength());
// 2 specialized loops for speed optimization in non-unit case
if (isType(TYPEMASK_UNIT)) // unit (creature/player) case
{
for (uint16 index = 0; index < m_valuesCount; ++index)
{
if (updateMask->GetBit(index))
{
if (index == UNIT_NPC_FLAGS)
{
uint32 appendValue = m_uint32Values[index];
if (GetTypeId() == TYPEID_UNIT)
{
if (appendValue & UNIT_NPC_FLAG_TRAINER)
{
if (!((Creature*)this)->IsTrainerOf(target, false))
appendValue &= ~(UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS | UNIT_NPC_FLAG_TRAINER_PROFESSION);
}
if (appendValue & UNIT_NPC_FLAG_STABLEMASTER)
{
if (target->getClass() != CLASS_HUNTER)
appendValue &= ~UNIT_NPC_FLAG_STABLEMASTER;
}
if (appendValue & UNIT_NPC_FLAG_FLIGHTMASTER)
{
QuestRelationsMapBounds bounds = sObjectMgr.GetCreatureQuestRelationsMapBounds(((Creature*)this)->GetEntry());
for (QuestRelationsMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
{
Quest const* pQuest = sObjectMgr.GetQuestTemplate(itr->second);
if (target->CanSeeStartQuest(pQuest))
{
appendValue &= ~UNIT_NPC_FLAG_FLIGHTMASTER;
break;
}
}
bounds = sObjectMgr.GetCreatureQuestInvolvedRelationsMapBounds(((Creature*)this)->GetEntry());
for (QuestRelationsMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
{
Quest const* pQuest = sObjectMgr.GetQuestTemplate(itr->second);
if (target->CanRewardQuest(pQuest, false))
{
appendValue &= ~UNIT_NPC_FLAG_FLIGHTMASTER;
break;
}
}
}
}
*data << uint32(appendValue);
}
else if (index == UNIT_FIELD_AURASTATE)
{
if (IsPerCasterAuraState)
{
// IsPerCasterAuraState set if related pet caster aura state set already
if (((Unit*)this)->HasAuraStateForCaster(AURA_STATE_CONFLAGRATE, target->GetObjectGuid()))
*data << m_uint32Values[index];
else
*data << (m_uint32Values[index] & ~(1 << (AURA_STATE_CONFLAGRATE - 1)));
}
else
*data << m_uint32Values[index];
}
// FIXME: Some values at server stored in float format but must be sent to client in uint32 format
else if (index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME)
{
// convert from float to uint32 and send
*data << uint32(m_floatValues[index] < 0 ? 0 : m_floatValues[index]);
}
// there are some float values which may be negative or can't get negative due to other checks
else if ((index >= UNIT_FIELD_NEGSTAT0 && index <= UNIT_FIELD_NEGSTAT4) ||
(index >= UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE && index <= (UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + 6)) ||
(index >= UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE && index <= (UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + 6)) ||
(index >= UNIT_FIELD_POSSTAT0 && index <= UNIT_FIELD_POSSTAT4))
{
*data << uint32(m_floatValues[index]);
}
else if (index == UNIT_FIELD_HEALTH || index == UNIT_FIELD_MAXHEALTH)
{
uint32 value = m_uint32Values[index];
// Fog of War: replace absolute health values with percentages for non-allied units according to settings
if (!static_cast<const Unit*>(this)->IsFogOfWarVisibleHealth(target) &&
!target->CanSeeSpecialInfoOf(static_cast<const Unit*>(this)))
{
switch (index)
{
case UNIT_FIELD_HEALTH: value = uint32(ceil((100.0 * value) / m_uint32Values[UNIT_FIELD_MAXHEALTH])); break;
case UNIT_FIELD_MAXHEALTH: value = 100; break;
}
}
*data << value;
}
else if (index == UNIT_FIELD_FLAGS)
{
uint32 value = m_uint32Values[index];
// For gamemasters in GM mode:
if (target->IsGameMaster())
{
// Gamemasters should be always able to select units - remove not selectable flag:
value &= ~UNIT_FLAG_UNINTERACTIBLE;
}
// Client bug workaround: Fix for missing chat channels when resuming taxi flight on login
// Client does not send any chat joining attempts by itself when taxi flag is on
if (target == this && (value & UNIT_FLAG_TAXI_FLIGHT))
{
if (sWorld.getConfig(CONFIG_BOOL_TAXI_FLIGHT_CHAT_FIX))
if (WorldSession* session = static_cast<Player const*>(this)->GetSession())
if (!session->IsInitialZoneUpdated())
value &= ~UNIT_FLAG_TAXI_FLIGHT;
}
*data << value;
}
// Hide lootable animation for unallowed players
// Handle tapped flag
// Hide special-info for non empathy-casters,
else if (index == UNIT_DYNAMIC_FLAGS)
{
uint32 dynflagsValue = m_uint32Values[index];
// Checking SPELL_AURA_EMPATHY and caster
if (dynflagsValue & UNIT_DYNFLAG_SPECIALINFO && static_cast<const Unit*>(this)->IsAlive())
{
bool bIsEmpathy = false;
bool bIsCaster = false;
Unit::AuraList const& mAuraEmpathy = static_cast<const Unit*>(this)->GetAurasByType(SPELL_AURA_EMPATHY);
for (Unit::AuraList::const_iterator itr = mAuraEmpathy.begin(); !bIsCaster && itr != mAuraEmpathy.end(); ++itr)
{
bIsEmpathy = true; // Empathy by aura set
if ((*itr)->GetCasterGuid() == target->GetObjectGuid())
bIsCaster = true; // target is the caster of an empathy aura
}
if (bIsEmpathy && !bIsCaster) // Empathy by aura, but target is not the caster
dynflagsValue &= ~UNIT_DYNFLAG_SPECIALINFO;
}
// Hide lootable animation for unallowed players
// Handle tapped flag
if (GetTypeId() == TYPEID_UNIT)
{
Creature* creature = (Creature*)this;
bool setTapFlags = false;
if (creature->IsAlive())
{
// creature is alive so, not lootable
dynflagsValue = dynflagsValue & ~UNIT_DYNFLAG_LOOTABLE;
if (creature->IsInCombat())
{
// as creature is in combat we have to manage tap flags
setTapFlags = true;
}
else
{
// creature is not in combat so its not tapped
dynflagsValue = dynflagsValue & ~UNIT_DYNFLAG_TAPPED;
//sLog.outString(">> %s is not in combat so not tapped by %s", this->GetGuidStr().c_str(), target->GetGuidStr().c_str());
}
}
else
{
// check m_loot flag
if (creature->m_loot && creature->m_loot->CanLoot(target))
{
// creature is dead and this player can loot it
dynflagsValue = dynflagsValue | UNIT_DYNFLAG_LOOTABLE;
//sLog.outString(">> %s is lootable for %s", this->GetGuidStr().c_str(), target->GetGuidStr().c_str());
}
else
{
// creature is dead but this player cannot loot it
dynflagsValue = dynflagsValue & ~UNIT_DYNFLAG_LOOTABLE;
//sLog.outString(">> %s is not lootable for %s", this->GetGuidStr().c_str(), target->GetGuidStr().c_str());
}
// as creature is died we have to manage tap flags
setTapFlags = true;
}
// check tap flags
if (setTapFlags)
{
if (creature->IsTappedBy(target))
{
// creature is in combat or died and tapped by this player
dynflagsValue = dynflagsValue & ~UNIT_DYNFLAG_TAPPED;
//sLog.outString(">> %s is tapped by %s", this->GetGuidStr().c_str(), target->GetGuidStr().c_str());
}
else
{
// creature is in combat or died but not tapped by this player
dynflagsValue = dynflagsValue | UNIT_DYNFLAG_TAPPED;
//sLog.outString(">> %s is not tapped by %s", this->GetGuidStr().c_str(), target->GetGuidStr().c_str());
}
}
}
if (GetTypeId() == TYPEID_UNIT || GetTypeId() == TYPEID_PLAYER)
{
Unit const* unit = static_cast<const Unit*>(this); // hunters mark effects should only be visible to owners and not all players
if (!unit->HasAuraTypeWithCaster(SPELL_AURA_MOD_STALKED, target->GetObjectGuid()))
dynflagsValue &= ~UNIT_DYNFLAG_TRACK_UNIT;
}
*data << dynflagsValue;
}
else if (index == UNIT_FIELD_FACTIONTEMPLATE)
{
uint32 value = m_uint32Values[index];
// [XFACTION]: Alter faction if detected crossfaction group interaction when updating faction field:
if (this != target && GetTypeId() == TYPEID_PLAYER)
{
Player const* thisPlayer = static_cast<Player const*>(this);
if (sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_GROUP) && target->IsInGroup(thisPlayer))
{
const uint32 targetTeam = target->GetTeam();
if (thisPlayer->GetTeam() != targetTeam && value == Player::getFactionForRace(thisPlayer->getRace()))
{
switch (targetTeam)
{
case ALLIANCE: value = 1054; break; // "Alliance Generic"
case HORDE: value = 1495; break; // "Horde Generic"
}
}
}
}
*data << value;
}
else // Unhandled index, just send
{
// send in current format (float as float, uint32 as uint32)
*data << m_uint32Values[index];
}
}
}
}
else if (isType(TYPEMASK_CORPSE)) // corpse case
{
for (uint16 index = 0; index < m_valuesCount; ++index)
{
if (updateMask->GetBit(index))
{
if (index == CORPSE_FIELD_BYTES_1)
{
uint32 value = m_uint32Values[index];
// [XFACTION]: Alter race field if detected crossfaction group interaction:
if (sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_GROUP))
{
Corpse const* thisCorpse = static_cast<Corpse const*>(this);
ObjectGuid const& ownerGuid = thisCorpse->GetOwnerGuid();
Group const* targetGroup = target->GetGroup();
if (ownerGuid != target->GetObjectGuid() && targetGroup && targetGroup->IsMember(ownerGuid))
{
const uint8 targetRace = target->getRace();
if (Player::TeamForRace(thisCorpse->getRace()) != Player::TeamForRace(targetRace))
value = ((value &~ uint32(0xFF << 8)) | (uint32(targetRace) << 8));
}
}
*data << value;
}
else
*data << m_uint32Values[index]; // other cases
}
}
}
else if (isType(TYPEMASK_GAMEOBJECT)) // gameobject case
{
for (uint16 index = 0; index < m_valuesCount; ++index)
{
if (updateMask->GetBit(index))
{
// send in current format (float as float, uint32 as uint32)
if (index == GAMEOBJECT_DYN_FLAGS)
{
// GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY can have lo flag = 2
// most likely related to "can enter map" and then should be 0 if can not enter
if (IsActivateToQuest)
{
GameObject const* gameObject = static_cast<GameObject const*>(this);
switch (((GameObject*)this)->GetGoType())
{
case GAMEOBJECT_TYPE_QUESTGIVER:
*data << uint16(GO_DYNFLAG_LO_ACTIVATE);
*data << uint16(0);
break;
case GAMEOBJECT_TYPE_CHEST:
if (gameObject->GetLootState() == GO_READY || gameObject->GetLootState() == GO_ACTIVATED)
*data << uint16(GO_DYNFLAG_LO_ACTIVATE | GO_DYNFLAG_LO_SPARKLE);
else
*data << uint16(0);
*data << uint16(0);
break;
case GAMEOBJECT_TYPE_GENERIC:
case GAMEOBJECT_TYPE_SPELL_FOCUS:
case GAMEOBJECT_TYPE_GOOBER:
*data << uint16(GO_DYNFLAG_LO_ACTIVATE | GO_DYNFLAG_LO_SPARKLE);
*data << uint16(0);
break;
default:
*data << uint32(0); // unknown, not happen.
break;
}
}
else
*data << uint32(0); // disable quest object
}
else
*data << m_uint32Values[index]; // other cases
}
}
}
else // other objects case (no special index checks)
{
for (uint16 index = 0; index < m_valuesCount; ++index)
{
if (updateMask->GetBit(index))
{
// send in current format (float as float, uint32 as uint32)
*data << m_uint32Values[index];
}
}
}
}
void Object::_SetCreateBits(UpdateMask& updateMask, Player* target) const
{
uint16 const* flags = nullptr;
uint16 visibleFlag = GetUpdateFieldFlagsForTarget(target, flags);
MANGOS_ASSERT(flags);
for (uint16 index = 0; index < m_valuesCount; ++index)
if (GetUInt32Value(index) != 0 && (flags[index] & visibleFlag))
updateMask.SetBit(index);
}
uint16 Object::GetUpdateFieldFlagsForTarget(Player const* target, uint16 const*& flags) const
{
flags = UpdateFields::GetUpdateFieldFlagsArray(GetTypeId());
uint16 visibleFlag = UF_FLAG_PUBLIC | UF_FLAG_DYNAMIC;
if (target == this)
visibleFlag |= UF_FLAG_PRIVATE;
switch (GetTypeId())
{
case TYPEID_ITEM:
case TYPEID_CONTAINER:
if (static_cast<Item const*>(this)->GetOwnerGuid() == target->GetObjectGuid())
visibleFlag |= UF_FLAG_OWNER_ONLY | UF_FLAG_UNK2;
break;
case TYPEID_UNIT:
case TYPEID_PLAYER:
{
if (static_cast<Unit const*>(this)->GetMasterGuid() == target->GetObjectGuid())
visibleFlag |= UF_FLAG_OWNER_ONLY;
if (HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_SPECIALINFO))
if (target->CanSeeSpecialInfoOf(static_cast<Unit const*>(this)))
visibleFlag |= UF_FLAG_SPECIAL_INFO;
if (static_cast<Unit const*>(this)->IsInGroup(target, false, false))
visibleFlag |= UF_FLAG_GROUP_ONLY;
break;
}
case TYPEID_GAMEOBJECT:
if (static_cast<GameObject const*>(this)->GetOwnerGuid() == target->GetObjectGuid())
visibleFlag |= UF_FLAG_OWNER_ONLY;
break;
case TYPEID_DYNAMICOBJECT:
if (static_cast<DynamicObject const*>(this)->GetCasterGuid() == target->GetObjectGuid())
visibleFlag |= UF_FLAG_OWNER_ONLY;
break;
case TYPEID_CORPSE:
if (static_cast<Corpse const*>(this)->GetOwnerGuid() == target->GetObjectGuid())
visibleFlag |= UF_FLAG_OWNER_ONLY;
break;
case TYPEID_OBJECT:
break;
}
return visibleFlag;
}
上面代码完成了设置可视代码 然后根据可视掩码设置需要更新字段的bit位 最后根据具体的类型填充数据 填充完毕后 发给对应的目标 上面掩码算法针对的是广播类型更新 下面掩码算法就是玩家等实体属性改变掩码算法
1. 先在基类object.h 的object类创立掩码数据指针联合体 以及设置了一个是否更新过的掩码
union
{
int32* m_int32Values;
uint32* m_uint32Values;
float* m_floatValues;
};
std::vector<bool> m_changedValues;
2 创建的时候调用_InitValues()
void Object::_Create(uint32 dbGuid, uint32 guidlow, uint32 entry, HighGuid guidhigh)
{
if (!m_uint32Values)
_InitValues();
ObjectGuid guid = ObjectGuid(guidhigh, entry, guidlow);
m_dbGuid = dbGuid;
SetGuidValue(OBJECT_FIELD_GUID, guid);
SetUInt32Value(OBJECT_FIELD_TYPE, m_objectType);
m_PackGUID.Set(guid);
}
void Object::_InitValues()
{
m_uint32Values = new uint32[ m_valuesCount ];
memset(m_uint32Values, 0, m_valuesCount * sizeof(uint32));
m_changedValues.resize(m_valuesCount, false);
m_objectUpdated = false;
}
上面就创建好了针对每个object对象的值及设置了m_changedValues的初始值 下面代码就是设置这个值以及获取 其中m_valuesCount的值也是根据updatefields里面的枚举值设定的
例如下面的player_end的枚举值
Player::Player(WorldSession* session): Unit(), m_taxiTracker(*this), m_mover(this), m_camera(this), m_reputationMgr(this), m_launched(false)
{
m_valuesCount = PLAYER_END;
}
void Object::SetInt32Value(uint16 index, int32 value)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (m_int32Values[index] != value)
{
m_int32Values[index] = value;
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::SetUInt32Value(uint16 index, uint32 value)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (m_uint32Values[index] != value)
{
m_uint32Values[index] = value;
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::UpdateUInt32Value(uint16 index, uint32 value)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
m_uint32Values[index] = value;
m_changedValues[index] = true;
}
void Object::SetUInt64Value(uint16 index, const uint64& value)
{
MANGOS_ASSERT(index + 1 < m_valuesCount || PrintIndexError(index, true));
if (*((uint64*) & (m_uint32Values[index])) != value)
{
m_uint32Values[index] = *((uint32*)&value);
m_uint32Values[index + 1] = *(((uint32*)&value) + 1);
m_changedValues[index] = true;
m_changedValues[index + 1] = true;
MarkForClientUpdate();
}
}
void Object::SetFloatValue(uint16 index, float value)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (m_floatValues[index] != value)
{
m_floatValues[index] = value;
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::SetByteValue(uint16 index, uint8 offset, uint8 value)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (offset > 4)
{
sLog.outError("Object::SetByteValue: wrong offset %u", offset);
return;
}
if (uint8(m_uint32Values[index] >> (offset * 8)) != value)
{
m_uint32Values[index] &= ~uint32(uint32(0xFF) << (offset * 8));
m_uint32Values[index] |= uint32(uint32(value) << (offset * 8));
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::SetUInt16Value(uint16 index, uint8 offset, uint16 value)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (offset > 2)
{
sLog.outError("Object::SetUInt16Value: wrong offset %u", offset);
return;
}
if (uint16(m_uint32Values[index] >> (offset * 16)) != value)
{
m_uint32Values[index] &= ~uint32(uint32(0xFFFF) << (offset * 16));
m_uint32Values[index] |= uint32(uint32(value) << (offset * 16));
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::SetStatFloatValue(uint16 index, float value)
{
if (value < 0)
value = 0.0f;
SetFloatValue(index, value);
}
void Object::SetStatInt32Value(uint16 index, int32 value)
{
if (value < 0)
value = 0;
SetUInt32Value(index, uint32(value));
}
void Object::ApplyModUInt32Value(uint16 index, int32 val, bool apply)
{
int32 cur = GetUInt32Value(index);
cur += (apply ? val : -val);
if (cur < 0)
cur = 0;
SetUInt32Value(index, cur);
}
void Object::ApplyModInt32Value(uint16 index, int32 val, bool apply)
{
int32 cur = GetInt32Value(index);
cur += (apply ? val : -val);
SetInt32Value(index, cur);
}
void Object::ApplyModSignedFloatValue(uint16 index, float val, bool apply)
{
float cur = GetFloatValue(index);
cur += (apply ? val : -val);
SetFloatValue(index, cur);
}
void Object::ApplyModPositiveFloatValue(uint16 index, float val, bool apply)
{
float cur = GetFloatValue(index);
cur += (apply ? val : -val);
if (cur < 0)
cur = 0;
SetFloatValue(index, cur);
}
void Object::SetFlag(uint16 index, uint32 newFlag)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
uint32 oldval = m_uint32Values[index];
uint32 newval = oldval | newFlag;
if (oldval != newval)
{
m_uint32Values[index] = newval;
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::RemoveFlag(uint16 index, uint32 oldFlag)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
uint32 oldval = m_uint32Values[index];
uint32 newval = oldval & ~oldFlag;
if (oldval != newval)
{
m_uint32Values[index] = newval;
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::SetByteFlag(uint16 index, uint8 offset, uint8 newFlag)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (offset > 4)
{
sLog.outError("Object::SetByteFlag: wrong offset %u", offset);
return;
}
if (!(uint8(m_uint32Values[index] >> (offset * 8)) & newFlag))
{
m_uint32Values[index] |= uint32(uint32(newFlag) << (offset * 8));
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::RemoveByteFlag(uint16 index, uint8 offset, uint8 oldFlag)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (offset > 4)
{
sLog.outError("Object::RemoveByteFlag: wrong offset %u", offset);
return;
}
if (uint8(m_uint32Values[index] >> (offset * 8)) & oldFlag)
{
m_uint32Values[index] &= ~uint32(uint32(oldFlag) << (offset * 8));
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::SetShortFlag(uint16 index, bool highpart, uint16 newFlag)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (!(uint16(m_uint32Values[index] >> (highpart ? 16 : 0)) & newFlag))
{
m_uint32Values[index] |= uint32(uint32(newFlag) << (highpart ? 16 : 0));
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::RemoveShortFlag(uint16 index, bool highpart, uint16 oldFlag)
{
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
if (uint16(m_uint32Values[index] >> (highpart ? 16 : 0)) & oldFlag)
{
m_uint32Values[index] &= ~uint32(uint32(oldFlag) << (highpart ? 16 : 0));
m_changedValues[index] = true;
MarkForClientUpdate();
}
}
void Object::MarkForClientUpdate()
{
if (m_inWorld)
{
if (!m_objectUpdated)
{
AddToClientUpdateList();
m_objectUpdated = true;
}
}
}
void WorldObject::AddToClientUpdateList()
{
GetMap()->AddUpdateObject(this);
}
void AddUpdateObject(Object* obj)
{
i_objectsToClientUpdate.insert(obj);
}
上面代码最后的m_objectUpdated =true ;这个修改后 就会添加到客户端需要更新的列表里面去
地图的update()函数下一帧更新会调用
// Send world objects and item update field changes
SendObjectUpdates();
void Map::SendObjectUpdates()
{
UpdateDataMapType update_players;
while (!i_objectsToClientUpdate.empty())
{
Object* obj = *i_objectsToClientUpdate.begin();
i_objectsToClientUpdate.erase(i_objectsToClientUpdate.begin());
obj->BuildUpdateData(update_players);
}
for (auto& update_player : update_players)
{
for (size_t i = 0; i < update_player.second.GetPacketCount(); ++i)
{
WorldPacket packet = update_player.second.BuildPacket(i);
update_player.first->GetSession()->SendPacket(packet);
}
}
}
玩家是属于WorldObject 类 然后重写了WorldObject的BuildUpdateData方法 下面是整个函数添加需要更新字段的流程 WorldObjectChangeAccumulator结构体的用法不清楚的 可以去看我同步数据那篇文章
void WorldObject::BuildUpdateData(UpdateDataMapType& update_players)
{
WorldObjectChangeAccumulator notifier(*this, update_players);
Cell::VisitWorldObjects(this, notifier, GetVisibilityData().GetVisibilityDistance());
ClearUpdateMask(false);
}
struct WorldObjectChangeAccumulator
{
UpdateDataMapType& i_updateDatas;
WorldObject& i_object;
WorldObjectChangeAccumulator(WorldObject& obj, UpdateDataMapType& d) : i_updateDatas(d), i_object(obj)
{
//处理玩家摄像机离自己很远的情况下 没有收到数据更新
if (i_object.isType(TYPEMASK_PLAYER))
i_object.BuildUpdateDataForPlayer((Player*)&i_object, i_updateDatas);
}
void Visit(CameraMapType& m)
{
for (auto& iter : m)
{
Player* owner = iter.getSource()->GetOwner();
if (owner != &i_object && owner->HasAtClient(&i_object))
i_object.BuildUpdateDataForPlayer(owner, i_updateDatas);
}
}
template<class SKIP> void Visit(GridRefManager<SKIP>&) {}
};
void Object::BuildUpdateDataForPlayer(Player* pl, UpdateDataMapType& update_players) const
{
UpdateDataMapType::iterator iter = update_players.find(pl);
if (iter == update_players.end())
{
std::pair<UpdateDataMapType::iterator, bool> p = update_players.insert(UpdateDataMapType::value_type(pl, UpdateData()));
MANGOS_ASSERT(p.second);
iter = p.first;
}
BuildValuesUpdateBlockForPlayer(iter->second, iter->first);
}
void Object::BuildValuesUpdateBlockForPlayer(UpdateData& data, Player* target) const
{
UpdateMask updateMask;
updateMask.SetCount(m_valuesCount);
_SetUpdateBits(updateMask, target);
if (updateMask.HasData())
BuildValuesUpdateBlockForPlayer(data, updateMask, target);
}
void Object::_SetUpdateBits(UpdateMask& updateMask, Player* target) const
{
uint16 const* flags = nullptr;
uint16 visibleFlag = GetUpdateFieldFlagsForTarget(target, flags);
MANGOS_ASSERT(flags);
for (uint16 index = 0; index < m_valuesCount; ++index)
if (m_changedValues[index] && (flags[index] & visibleFlag))
updateMask.SetBit(index);
}
最后这里 if (m_changedValues[index] && (flags[index] & visibleFlag)) 就根据m_changedValues 的值是否更新过 然后设置更新掩码 这里设置好后 就走上面BuildValuesUpdat()函数了