芒果魔兽 属性系统 掩码更新算法

掩码更新算法的 意义在于 有更新的数据 才会跟数据库 或者 客户端同步 没有更新的数据会跳过 不同步 这样节约了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()函数了

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值