差异更新

#include <iostream>
//下面说的是在服务器差异脏标记,不管在想客户端还是在数据库中经常用到的一种方法
//由于其实直接拿为进行操作,所以速度是非常快的,也许刚开始不太好理解,但只要熟悉之后就非常好用
//对于包而言如果Update包只改变一项你把整个结构体都发过去会大大增加网络带快,现在基本都走差异更新
enum UpdateAtt
{
    eUpdate1,
    eUpdate2,
    eUpdate3,
    eUpdate4,
    eUpdate5,
    eUpdate6,
    eUpdate7,
    eUpdateMax
};
#define Update_Att_Max_Num ((eUpdateMax >> 3) + 1)

/**
 *@brief 脏标记类,关键理解此类
 */
struct UserAttrFlag
{
    bool IsUpdated(int nAttr) const
    {
        if(m_Flags[nAttr/8] & (1<<(nAttr%8)))
            return true;
        return false;
    }
    void SetBitFlag(int nAttr)
    {
        m_Flags[nAttr/8] |= (1<<(nAttr%8));
    }
    void ClearBitFlag()
    {
        memset(m_Flags, 0, Update_Att_Max_Num);
    }
    char m_Flags[Update_Att_Max_Num];
};

struct UserReg
{
    unsigned char    m_AttrOffset[eUpdateMax];        ///< 角色属性偏移量,距离首地址的偏移位置
    unsigned char    m_AttrSize[eUpdateMax];            ///< 角色属性大小
};

struct Update
{
private://需要将此类的所有数据成员设置成私有,针对没个成员提供set/get接口
    int nUpdate1;
    int nUpdate2;
    int nUpdate3;
    int nUpdate4;
    int nUpdate5;
    int nUpdate6;
    int nUpdate7;
public:
    void SetUpdate1(int nData)
    {
        if(nUpdate1 != nData)
        {
            nUpdate1 = nData;
            SetDBEnumAttrDirty(eUpdate1);
        }
    }
    void SetUpdate7(int nData)
    {
        if(nUpdate7 != nData)
        {
            nUpdate7 = nData;
            SetDBEnumAttrDirty(eUpdate7);
        }
    }
    int GetUpdate1(){return nUpdate1;}
    int GetUpdate7(){return nUpdate7;}
    Update()
    {
        RegisterAttributes();
        memset(&m_attrFlag, 0, sizeof(m_attrFlag));
    }
    void RegisterAttributes();//注册标记

    void _RegAttr(UpdateAtt eAtt, int nOffset, int nSize)
    {
        /// 角色属性偏移量,距离首地址的偏移位置,之前看到天龙代码直接存的是变量的地址,这种只适合单线程使用,多线程会发生内存拷贝
        /// 所以用偏移量更合适
        m_attReg.m_AttrOffset[eAtt] = nOffset;
        m_attReg.m_AttrSize[eAtt] = nSize;
    }
    void SetDBEnumAttrDirty(UpdateAtt eUpdateAttr)
    {
        m_attrFlag.SetBitFlag(eUpdateAttr);
    }
    UserAttrFlag m_attrFlag;//属相脏标记
    UserReg         m_attReg;//属相注册
public:
    const UserAttrFlag* GetAttrFlag() const  {return &m_attrFlag;} 
    const UserReg*  GetAttrReg() const {return &m_attReg;}
};

void Update::RegisterAttributes()
{
    #define REG_DB_ATTR(type,var) _RegAttr(type, ((int)&(var) - (int)this), sizeof(var));
    REG_DB_ATTR(eUpdate1, nUpdate1)
    REG_DB_ATTR(eUpdate2, nUpdate2)
    REG_DB_ATTR(eUpdate3, nUpdate3)
    REG_DB_ATTR(eUpdate4, nUpdate4)
    REG_DB_ATTR(eUpdate5, nUpdate5)
    REG_DB_ATTR(eUpdate6, nUpdate6)
    REG_DB_ATTR(eUpdate7, nUpdate7)
}

int main()
{
    Update update;
    update.SetUpdate1(12);
    update.SetUpdate7(120);
    char* buff = new char[1024];//拼buff
    //拼包操作
    int nOffset = 0;
    for (int i = 0; i < eUpdateMax; i++)
    {
        if (update.GetAttrFlag()->IsUpdated(i))//判断其是否为脏
        {
            memcpy(buff + nOffset, &i, sizeof(int));
            nOffset += sizeof(int);
            memcpy(buff + nOffset, &update + update.GetAttrReg()->m_AttrOffset[i], update.GetAttrReg()->m_AttrSize[i]);
            nOffset += update.GetAttrReg()->m_AttrSize[i];
        }
    }
    getchar();
    return 0;
}

//现在简单解释一下脏标记的处理
/*
#define Update_Att_Max_Num ((eUpdateMax >> 3) + 1)
定义的宏,根据此宏就知道定义多大的char数组,他是吧将8个枚举作为一个char,而一个char正好是8位,+1是为了不足8位,也为其分配一个char

void SetBitFlag(int nAttr)
{
    m_Flags[nAttr/8] |= (1<<(nAttr%8));
}
m_Flags[nAttr/8]是定位其在那个char上,如0,肯定是char[0],如果是9就是char[1]
(1<<(nAttr%8))表示其在某个char的哪个位上,如9,(1<<(nAttr%8))就是第二个char[1]&10,这样就正确的设置到对应的位上了
其实将char[..]看出1011111这样位,而此种方法方法就是能更方便的操作,c++没有对应的位的类型,stl里面好像有个bitset,但复杂类型在某些场合不适用的
|=或操作正好将其设置成1

bool IsUpdated(int nAttr) const
{
if(m_Flags[nAttr/8] & (1<<(nAttr%8)))
    return true;
return false;
}
上面那个理解了,这个就好理解了
m_Flags[nAttr/8]也是定位哪个char,(1<<(nAttr%8)))也是对应char那个位置
&操作获取对应为是0,还是1

对于其他的大小和偏移量,为了打包更好获取其地址和大小,更快的打包而设置的,好理解

*/

/*
额外补充
对于DBSvr怎么解析,是根绝实现定义好的function,然后根据类型直接调用,那个类型里面关于数据类型都是写死的,个人觉得不是太好
客户端我问了,他是根绝枚举,硬猜是哪个包,已醉
*/

 

posted @ 2015-02-06 22:38  zzyoucan  阅读(372)  评论(0编辑  收藏  举报