122.网络游戏逆向分析与漏洞攻防-邮件系统数据分析-邮件物品箱的管理

news2024/12/23 10:26:45

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

如果看不懂、不知道现在做的什么,那就跟着做完看效果,代码看不懂是正常的,只要会抄就行,抄着抄着就能懂了

内容参考于:易道云信息技术研究院

上一个内容:121.邮件读取与发送界面设计

码云版本号:9135d5bd5933fd46b983b6d022f9491bcc9dc9d8

代码下载地址,在 titan 目录下,文件名为:titan-邮件物品箱的管理.zip

链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg

提取码:q9n5

--来自百度网盘超级会员V4的分享

HOOK引擎,文件名为:黑兔sdk升级版.zip

链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw

提取码:78h8

--来自百度网盘超级会员V4的分享

以 121.邮件读取与发送界面设计 它的代码为基础进行修改

效果图:

收取邮件正常

物品显示

上一个内容里发现邮件里有物品,然后这个物品处理起来还是比较麻烦,上一个内容中读取邮件的窗口使用的是模态窗口,这就导致打开我们的邮件窗口之后,游戏会卡死,游戏卡死数据包就处理不了,然后读取物品就有问题,所以第一窗口就不能用模态窗口

邮件窗口控件修改属性

物品输入框禁用

退信的数据包

退信

退信数据包350 11

邮件窗口的界面

NetClient.cpp文件的修改:新加 OnSvrGameBaseed函数,修改了 OnSvrUpdateProperty函数、OnSvrGameBase函数

#include "pch.h"
#include "NetClient.h"
#include "extern_all.h"

bool NetClient::login(const char* Id, const char* Pass)
{
    
  const int bufflen = sizeof(DATA_LOGIN) + 1;
  char buff[bufflen];
  DATA_LOGIN data;
  // 有些操作系统这样写会报错,因为内存不对齐,现在Windows下没事
  //PDATALOGIN _data = (PDATALOGIN)(buff + 1);
  // 这样写就能解决内存对齐问题
  PDATALOGIN _data =&data;
  int len = strlen(Id);
  memcpy(_data->Id, Id, len);
  len = strlen(Pass);
  memcpy(_data->Pass, Pass, len);
  memcpy(buff+1, _data, sizeof(DATA_LOGIN));
  buff[0] = I_LOGIN;
  return  NetSend(buff, sizeof(buff));
  
}

bool NetClient::DelRole(const wchar_t* rolename)
{
    PROLEDATA _role = GetRoleByName(rolename);
    if (_role == nullptr) {
        return false;
    }
    else {
        return DelRole(rolename, _role->name.lenth);
    }
    return false;
}

bool NetClient::StartCreateRole()
{
    NET_CREATEROLE_START _data;
    return NetSend(&_data.op, _data.len);
}

bool NetClient::SelectCamp(const char* _campname)
{
    NET_SEND_BUFF _buff;
    NET_SEND_CHOOSECAMP _data;
    _data.opcode.Set(SC_CHOOSECAMP);
    _data.camps.Set(_campname);
    /* 
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

//性别 0 男 1 女
//
//阵营 1 艾森赫特 2 格兰蒂尔
//
//种族 1 布冯特人 3 格洛玛人 4 尤恩图人 6 喀什人
//
//职业 1 仲裁者 3秘法师 6 猎魔人 8 元素法师
//
//脸型 0 1 2 3
bool NetClient::CreateRole(wchar_t* name, double sex, double camp, double race, double occu, const char* photo, const char* infos, const char* txt, double faceShape)
{
    // rolecount > 4说明角色的数量够了
    if (rolecount > 4)return false;
    int index = 0;
    bool roleindex[5]{true, true, true, true, true };
    for (int i = 0; i < rolecount; i++) {
        roleindex[roles[i].index] = false;
    }
   
    for (int i = 0; i < 5; i++)
    {
        if (roleindex[i]) {
            index = i;
            break;
        }
    }

    // wchar_t _name[] = L"am52111";
    NS_CREATEROLE_HEAD_BUFF _buff;
    CREATE_ROLE_DATAS _data;
    _data.sex.Set(sex);
    _data.camp.Set(camp);
    _data.face.Set(race);
    _data.occu.Set(occu);
    _data.faceSahpe.Set(faceShape);
    //_data.Photo.Set("gui\BG_team\TeamRole\Teamrole_zq_humF_001.PNG");
    _data.Photo.Set(photo);
    //_data.Infos.Set("Face,0;Hat,0;Eyes,0;Beard,0;Ears,0;Tail,0;Finger,0;Cloth,0;Pants,0;Gloves,0;Shoes,0;Trait,0;HairColor,0;SkinColor,0;SkinMtl,0;Tattoo,0;TattooColor,16777215;");
    _data.Infos.Set(infos);
    _data.Txt.Set(txt);
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;
    _buff.index = index;
    int lenth = wcslen(name) + 1;
    lenth = lenth * 2;
    memcpy(_buff.name, name, lenth);
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEHD_CREATEROLE_HEAD) - 3;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::SelectRole(const wchar_t* rolename)
{
    PROLEDATA roles = GetRoleByName(rolename);
    if (roles == nullptr)return false;
    NS_SELECTROLE _data;
    memcpy(_data.buff, roles->name.value(), roles->name.lenth);
    
    return NetSend((char*)&_data, sizeof(_data));
}

bool NetClient::Fall()
{
    NET_SEND_BUFF _buff;
    FALL_DATA_STOP _data2;
    _data2.opcode.Set(SC_FALL_HEADER);
    _data2.Mode.Set(0x2);
    _data2.StartH.Set(Player.h);
    _data2.NextH.Set(Player.h - 12);
    _data2.EndH.Set(Player.h - 120);
    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data2) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data2.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);

}

void NetClient::Teleport(float x, float h, float y, float face)
{
    // 修正坐标
     //PAIM aim = GetAimByName(L"r");
     //if (aim == nullptr) {
     //	return;
     //}

     // 目的地坐标
     //float decX = aim->x;
     //float decH = aim->h;
     //float decY = aim->y;
 /*   float decX = 3843.776123f;
    float decH = 11.731983f;
    float decY = -2005.533813f;
    float face = 0.0f;*/

    union {
        unsigned nt = 0xFFC00000;
        float fNan;
    }v;
    /*
        这里的参数虽然都是无效值,但不见得就非要全部是无效值
        可能只设置x坐标就可以,可能值设置y坐标就可以,可能要设置x、y坐标就可以。。。
        现在全部设置成无效值了,如果游戏服务端有这个漏洞,我们角色的坐标就会
        全部设置成无效值,然后按照逻辑来讲下一次设置坐标的函数可以任意修改
        然后还可能有的游戏设置NaN不成功,这种的可以多试几个修改坐标或者其它数据的函数
        如果还都不行那就没办法了
    */
    //MoveWJump(v.fNan, v.fNan, v.fNan, v.fNan, v.fNan, v.fNan, v.fNan); // 可以把坐标设置成nan
    //MoveWalk(v.fNan, v.fNan, v.fNan, v.fNan, v.fNan, v.fNan, v.fNan); // 可以把坐标设置成nan
    MoveStop(v.fNan, v.fNan, v.fNan, v.fNan); // 可以把坐标设置成nan
    MoveStop(x, h, y, face);
    // 利用修正坐标数据包瞬移
    SetCoord(Player.lId, x, h, y, face);
}

bool NetClient::MoveStop(float x, float h, float y, float face)
{
    NET_SEND_BUFF _buff;
    MOVE_DATA_STOP _data;
    _data.opcode.Set(SC_MOVE_HEADER);
    _data.Mode.Set(0x0);
    _data.Count.Set(4);
    _data.x.Set(x);
    _data.h.Set(h);
    _data.y.Set(y);
    _data.face.Set(face);
    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::MoveJump(float x, float h, float y, float face, float oldy, float xNext, float yNext)
{
    NET_SEND_BUFF _buff;
    MOVE_DATA_JUMP _data;
    _data.opcode.Set(SC_MOVE_HEADER);
    _data.Mode.Set(0x2);
    _data.Count.Set(9);
    _data.x.Set(x);
    _data.h.Set(h);
    _data.y.Set(y);

    _data.xNext.Set(xNext);
    _data.yNext.Set(yNext);
    _data.MoveSpeed.Set(Player.MoveSpeed);
    _data.JumpSpeed.Set(Player.JumpSpeed);
    _data.GSpeed.Set(0.0f);
    _data.face.Set(face);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::MoveWalk(float x, float h, float y, float face, float oldy, float xNext, float yNext)
{
    NET_SEND_BUFF _buff;
    MOVE_DATA_WALK _data;
    _data.opcode.Set(SC_MOVE_HEADER);
    _data.Mode.Set(0x1);
    _data.Count.Set(8);
    _data.x.Set(x);
    _data.h.Set(h);
    _data.y.Set(y);

    _data.xNext.Set(xNext);
    _data.yNext.Set(yNext);
    _data.MoveSpeed.Set(Player.MoveSpeed);
    _data.GSpeed.Set(0.0f);
    _data.face.Set(face);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::MoveWJump(float x, float h, float y, float face, float oldy, float xNext, float yNext)
{
    NET_SEND_BUFF _buff;
    MOVE_DATA_WJUMP _data;
    _data.opcode.Set(SC_MOVE_HEADER);
    _data.Mode.Set(0x3);
    _data.Count.Set(7);
    _data.x.Set(x);
    _data.h.Set(h);
    _data.y.Set(y);

    _data.xNext.Set(xNext);
    _data.yNext.Set(yNext);
    _data.MoveSpeed.Set(Player.MoveSpeed);
    _data.face.Set(face);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::UseSkill(const char* _skillName, float x, float h, float y, float face, float xTarget, float hTarget, float yTarget, int rInt1, int rInt2, int rInt3)
{
    NET_SEND_BUFF _buff;
    USESKILL_DATA _data;
    _data.opcode.Set(SC_USESKILL);
    _data.skillName.Set(_skillName);
    _data.x.Set(x);
    _data.h.Set(h);
    _data.y.Set(y);
    _data.xTarget.Set(xTarget);
    _data.hTarget.Set(hTarget);
    _data.yTarget.Set(yTarget);
    _data.un0.Set(rInt1);
    _data.un1.Set(rInt2);
    _data.un2.Set(rInt3);
    //_data.face.Set(face);
    _data.face.Set(0);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::UseItem(short BagIndex, short Index)
{
    NET_SEND_BUFF _buff;
    ITEM_USE_DATA _data;
    _data.opcode.Set(SC_ITEM_USE);
    _data.MainIndex.Set(BagIndex);
    _data.Index.Set(Index);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::ExChangeItem(short BagIndex, short Index, short tBagindex, short tIndex, bool IsEmpty)
{
    NET_SEND_BUFF _buff;
    ITEM_EXCHANGE_DATA _data;

    if(IsEmpty)
        _data.opcode.Set(SC_ITEM_EXCHANGE);
    else
        _data.opcode.Set(SC_ITEM_EXCHANGEEX);

    _data.MainIndex.Set(BagIndex);
    _data.Index.Set(Index);
    _data.TargetId.Set(tBagindex);
    _data.TargetIndex.Set(tIndex);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::DropItem(short BagIndex, short Index, short Amount)
{
    NET_SEND_BUFF _buff;
    ITEM_DROP_DATA _data;
    _data.opcode.Set(SC_ITEM_DROP);
    _data.MainIndex.Set(BagIndex);
    _data.Index.Set(Index);
    _data.Amount.Set(Amount);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::SplitItem(short BagIndex, short Index, short tBagindex, short tIndex, short Amount)
{
    NET_SEND_BUFF _buff;
    ITEM_SPLIT_DATA _data;
    _data.opcode.Set(SC_ITEM_SPLIT);

    _data.MainIndex.Set(BagIndex);
    _data.Index.Set(Index);
    _data.TargetId.Set(tBagindex);
    _data.TargetIndex.Set(tIndex);
    _data.TargetAmount.Set(Amount);
    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::Mount(short Index)
{
    NET_SEND_BUFF _buff;
    MOUNT_DATA _data;
    _data.opcode.Set(SC_MOUNT);
    _data.Index.Set(Index);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

PROLEDATA NetClient::GetRoleByName(const wchar_t* rolename)
{
    //PROLEDATA result = nullptr;
    for (int i = 0; i < rolecount; i++)
    {
        // StrCmpW判断两个字符串是否相同
        // 比较时区分大小写,如果字符串相同返回0
        if (StrCmpW(roles[i].name.value(), rolename) == 0) {
            return &roles[i];
        }

    }
    return nullptr;
}

bool NetClient::PickItem(short id, short Index)
{
    NET_SEND_BUFF _buff;
    ITEM_PICK_DATA _data;
    _data.opcode.Set(id);
    _data.Index.Set(Index);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;


    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::Talk(wchar_t* txt, int PdId, double un)
{
    NET_SEND_BUFF _buff;
    CHAT_PUBLIC _data;
    _data.opcode.Set(SC_CHAT);
    _data.ChartId.Set(PdId);
    _data.txt.Set(txt);
    _data.un.Set(un);
    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::TalkTo(wchar_t* name, wchar_t* txt, double un)
{
    NET_SEND_BUFF _buff;
    CHAT_PRIVATE _data;
    _data.opcode.Set(SC_CHAT);
    _data.ChartId.Set(3);
    _data.txt.Set(txt);
    _data.name.Set(name);
    _data.un.Set(un);
    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::HeartBeep()
{
    NET_SEND_BUFF _buff;
    HEART_BEEP _data;
    _data.opcode.Set(SC_BEEP);
    _data.tick.Set(3);
    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::BuyItem(int shopPage, int shopIndex, int itemIndex, int Amount)
{
    NET_SEND_BUFF _buff;
    ITEM_BUY_DATA _data;
    const char* shopId;
    auto _object = Client->GetGameOBJECTUnion((short)OBJECT_TYPE::Shop);
    if ((_object) && (_object->item)) {
        shopId = _object->item[0]->ShopID.GetBuffer();
    }
    else return false;

    _data.opcode.Set(SC_ITEM_SHOP);
    _data.opMode.Set(SC_ITEM_SHOP_BUY);
    _data.ShopId.Set(shopId);
    _data.ShopPage.Set(shopPage);
    _data.ShopIndex.Set(shopIndex);
    _data.Index.Set(itemIndex);
    _data.Amount.Set(Amount);
    _data.un1.Set(0);
    _data.un2.Set(0);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::HeartLoop()
{
    NET_SEND_BUFF _buff;
    HEART_LOOP _data;
    _data.opcode.Set(SC_LOOP);
    _data.tick.Set(GetTickCount64());
    _data.txt.Set("");
    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::Backtoroles()
{
    // 返回角色
    NET_SEND_BUFF _buff;
    NSR_CHEAD _data;
    _data.opcode.Set(SC_REONLINE);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::SoldItem(int MainIndex, int Index, int Amount)
{
    NET_SEND_BUFF _buff;
    ITEM_SOLD_DATA _data;

    const char* shopId;
    auto _object = GetGameOBJECTUnion((short)OBJECT_TYPE::Shop);
    if ((_object) && (_object->item)) {
        shopId = _object->item[0]->ShopID.GetBuffer();
    }
    else return false;

    _data.opcode.Set(SC_ITEM_SHOP);
    _data.opMode.Set(SC_ITEM_SHOP_SOLD);
    _data.un1.Set(0);
    _data.ShopId.Set(shopId);
    _data.MainIndex.Set(MainIndex);
    _data.Index.Set(Index);
    _data.Amount.Set(Amount);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::DelRole(const wchar_t* rolename, unsigned _len)
{
    DATA_DELROLE _data;
    _data.op = 0x06;
    _data.len = _len;
    memcpy(_data.buff, rolename, _len);
    return NetSend((char*)&_data, sizeof(DATA_DELROLE) - 1);
}

PAIM NetClient::GetAimById(long long lId)
{
    if (Player.lId == 0)return &Player;// 我们玩家的数据未初始化其它的也不能初始化
    if (Player.lId == lId)return &Player;
    for (int i = 0; i < MAX_AIM; i++)
    {
        if ((Aimlst[i] != nullptr) && (Aimlst[i]->lId == lId)) {
            return Aimlst[i];
        }
    }
    return CreateAim(lId);
}

PAIM NetClient::CreateAim(long long lId)
{
    for (int i = 0; i < MAX_AIM; i++)
    {
        if (Aimlst[i] == nullptr) {
            Aimlst[i] = new AIM();
            Aimlst[i]->lId = lId;
            return Aimlst[i];
        }
        else if (Aimlst[i]->Isfree) {
            Aimlst[i]->lId = lId;
            return Aimlst[i];
        }
    }
    return nullptr;
}

void NetClient::RemoveAimById(long long lId)
{
    for (int i = 0; i < MAX_AIM; i++)
    {
        if ((Aimlst[i] != nullptr) && (Aimlst[i]->lId == lId)) {
            // CString _txt;
            // _txt.Format(L"附近的 %s 消失", Aimlst[i]->Name);
            // AfxMessageBox(_txt);
            Aimlst[i]->Release();
        }
    }
}

POBJECT_UNION NetClient::GetGameOBJECTUnion(short MainIndex)
{
    switch (MainIndex)
    {
    case 1:
        return &Equip;
    case 2:
        return &Item;
    case 3:
        return &ItemEx;
    case 8:
        return &MountBag;
    case (short)OBJECT_TYPE::MailRecv:
        return &MailRecv;
    case (short)OBJECT_TYPE::MailSend:
        return &MailSend;
    case 0x28:
        return &Skill;
    case 0x3D:
        return &Shop;
    case 0x50:
        return &Pickup;
    }
    return nullptr;
}

POBJECTBASE NetClient::GetGameOBJECT(short MainIndex, short Index)
{
    POBJECT_UNION _objectTable = GetGameOBJECTUnion(MainIndex);

    if (_objectTable) {
        return _objectTable->item[Index];
    }
    return nullptr;
}

POBJECTBASE NetClient::ReSetGameOBJECT(short MainIndex, short max)
{
    max = max + 1;
    POBJECT_UNION _objectTable = GetGameOBJECTUnion(MainIndex);

    if (_objectTable) {
        _objectTable->Count = max;
        if (_objectTable->item) {
            if (max > _objectTable->MaxCount) {
                if (MainIndex == 0x28) {
                    delete[] _objectTable->skill;
                    delete[] _objectTable->skillAry;
                }
                else {
                    delete[] _objectTable->item;
                    delete[] _objectTable->itemAry;
                }
            }
            else {
              
                for (int i = 0; i < _objectTable->MaxCount; i++)
                {
                    _objectTable->item[i]->Isfree = true;
                }
                return  _objectTable->item[0];
            }
        }else   _objectTable->MaxCount = max;

        if (MainIndex == 0x28) {
            _objectTable->skill = new PSKILL[max];
            _objectTable->skillAry = new SKILL[max];
            for (int i = 0; i < max; i++) _objectTable->skill[i] = &_objectTable->skillAry[i];
        }
        else {
            _objectTable->item = new PITEM[max];
            _objectTable->itemAry = new ITEM[max];
            for (int i = 0; i < max; i++) _objectTable->item[i] = &_objectTable->itemAry[i];
        }

        return  _objectTable->item[0];
    }

    return nullptr;
}

void NetClient::ExChangeOBJECT(short MainIndex, short IndexFrom, short IndexTo)
{
    POBJECT_UNION _objectTable = GetGameOBJECTUnion(MainIndex);
    if (_objectTable) {
        POBJECTBASE* _base = _objectTable->item;
        POBJECTBASE _tmp = _base[IndexFrom];
        _base[IndexFrom] = _base[IndexTo];
        _base[IndexTo] = _tmp;
    }
}

void NetClient::DestoryOBJECT(short MainIndex, short Index)
{
    POBJECTBASE base = GetGameOBJECT(MainIndex, Index);
    if (base)base->Isfree = true;
}

PAIM NetClient::GetAimByName(const wchar_t* name)
{
    for (int i = 0; i < MAX_AIM; i++)
    {
        if ((Aimlst[i] != nullptr) && (!Aimlst[i]->Isfree) && (Aimlst[i]->Name == name)) {
            return Aimlst[i];
        }
    }
    return nullptr;
}

// x,y是玩家的坐标,targetX,targetY是目标的坐标
float NetClient::GetFace(float x, float y, float targetX, float targetY)
{
    // 计算差值
    x = targetX - x;
    y = targetY - y;
    double pi = 3.14159265358979323846;

    double p = atan2(x, y); // atan2是计算三角形弧度atan2函数返回值-pi ~ pi,负的3.1415926...到正的3.1415926...
    
    // 如果x是负数atan2函数返回值必定是在三四象限里,也就是一个负π
    if (x < 0) {
        p = pi * 2 + p;
    }
    return p;
}

void NetClient::FaceTo(const wchar_t* name)
{
    PAIM _aim = GetAimByName(name);
    if (_aim) {
        float _face = GetFace(Player.x, Player.y, _aim->x, _aim->y);
        MoveStop(Player.x, Player.h, Player.y, _face);
    }
}

void NetClient::Init(PNetOperation _send, PNetOperation _recv)
{
    NetSend = _send;
    NetRecv = _recv;

    for (int i = 0; i < 0x100; i++) {
        SendProc[i] = &NetClient::DefaultProc;
        RecvProc[i] = &NetClient::DefaultProc;
    }
    // 注册登录数据包处理函数
    SendProc[I_LOGIN] = &NetClient::OnClientlogin;
    SendProc[I_CREATEROLE_START] = &NetClient::OnClientStartCreateRole;
    SendProc[I_DELROLE] = &NetClient::OnClientDelRole;
    SendProc[I_SEND_CUSTOM] = &NetClient::OnClientSendCustom;
    SendProc[I_CREATEROLE] = &NetClient::OnClientCreateRole;
    SendProc[I_SELECT_ROLE] = &NetClient::OnClientSelectRole;
    // 注册数据登录失败数据包处理函数
    RecvProc[S_TIPS] = &NetClient::OnSvrTips;
    RecvProc[S_LOGINOK] = &NetClient::OnSvrloginOk;
    RecvProc[S_CREATEROLE_START] = &NetClient::OnSvrStartCreateRole;
    RecvProc[S_NOTICE] = &NetClient::OnSverNotice;
    RecvProc[S_NOTICE_COM] = &NetClient::OnSverNotice;
    RecvProc[S_OBJECT] = &NetClient::OnSverObject;
    RecvProc[S_STRUCT] = &NetClient::OnSverStruct;
    RecvProc[S_OBJECT_INIT] = &NetClient::OnSvrObjectInit;
    RecvProc[S_OBJECT_INITEX] = &NetClient::OnSvrObjectInitEx;
    RecvProc[S_OBJECT_INITEX_UCOM] = &NetClient::OnSvrObjectInitEx;
    RecvProc[S_UPDATECORD] = &NetClient::OnSvrUpdateCord;
    RecvProc[S_UPDATEPRO] = &NetClient::OnSvrUpdateProperty;
    RecvProc[S_UPDATEPROMU] = &NetClient::OnSvrUpdatePropertyMu;
    RecvProc[S_UPDATEPROMU_COM] = &NetClient::OnSvrUpdatePropertyMu;
    RecvProc[S_OBJECT_REMOVE] = &NetClient::OnSvrRemoveObjectMu;
    RecvProc[S_UPDATECORDEX] = &NetClient::OnSvrUpdateCordEx;
    RecvProc[S_GAMEBASE] = &NetClient::OnSvrGameBase;
    RecvProc[S_GAMEBASE_RESET] = &NetClient::OnSvrGameBaseReset;
    RecvProc[S_GAMEBASE_EXCHANGE] = &NetClient::OnSvrGameBaseExChange;
    RecvProc[S_GAMEBASE_DROP] = &NetClient::OnSvrGameBaseDestroy;
    RecvProc[S_GAMEBASE_SET] = &NetClient::OnSvrGameBaseSet;
    RecvProc[S_STRUCT_INIT] = &NetClient::OnSvrStructInit;
    RecvProc[S_STRUCT_INITEX] = &NetClient::OnSvrStructInit;
    RecvProc[S_STRUCT_CLEAR] = &NetClient::OnSvrStructClear;
    RecvProc[S_STRUCT_DELETE] = &NetClient::OnSvrStructDelete;
    RecvProc[S_STRUCT_UPDATE] = &NetClient::OnSvrStructUpdate;
}

bool NetClient::SetCoord(long long lId, float x, float h, float y, float face)
{
    NR_UPDATECOORD head;
    head.lId = lId;
    head.x = x;
    head.h = h;
    head.y = y;
    head.face = face;
    return NetRecv(&head.op, head.len);
}

bool NetClient::SetProperty(long long lId, int ProType, void* value)
{
    NR_OBJECT_UPDATEPRO head;
    head.lId = lId;
    head.itype = ProType;

    int valueType = ObjectTable[ProType].type;
    int valueSize = data_desc[2][valueType].lenth;
    int bufflen = 14;
    switch (valueType)
    {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 9:
            memcpy(head.buff, value, valueSize);
            bufflen = bufflen + valueSize;
            break;
        case 7:
            head.lenth = strlen((char*)value) + 1;
            memcpy(head.buffEnd, value, head.lenth);
            bufflen = bufflen + 4 + head.lenth;
            break;
        case 8:
            head.lenth = wcslen((wchar_t*)value) + 1;
            head.lenth = head.lenth + 2;
            memcpy(head.buffEnd, value, head.lenth);
            bufflen = bufflen + 4 + head.lenth;
            break;
    default:
        break;
    }

    return NetRecv(&head.op, bufflen);
}

bool NetClient::OnDelRole(wchar_t* rolename, unsigned _len)
{
    // AfxMessageBox(rolename);
    return true;
}

void NetClient::Onlogin(const char* Id, const char* Pass)
{
    
    /*
    const int bufflen = sizeof(DATA_LOGIN) + 1;
    char buff[bufflen];
    DATA_LOGIN data;
    // 有些操作系统这样写会报错,因为内存不对齐,现在Windows下没事
    //PDATALOGIN _data = (PDATALOGIN)(buff + 1);
    // 这样写就能解决内存对齐问题
    PDATALOGIN _data =&data;
    int len = strlen(Id);
    memcpy(_data->Id, Id, len);
    len = strlen(Pass);
    memcpy(_data->Pass, Pass, len);
    memcpy(buff+1, _data, sizeof(DATA_LOGIN));
    buff[0] = I_LOGIN;
    return  NetSend(buff, sizeof(buff));
    */
}

bool NetClient::OnStartCreateRole(int code)
{
    return true;
}

bool NetClient::OnCreateRole(PNS_CREATEROLE _header, PCREATE_ROLE_DATAS _body)
{
    return true;
}

bool NetClient::OnSendCustom(PNET_SEND_CHEAD _coder, char*& buffer, unsigned& len)
{
    switch (_coder->opcode.value())
    {
    case SC_CHOOSECAMP:
        return OnChooseCamp((PNS_CHOOSECAMP)_coder);
    case SC_CHAT:
        return OnChat((PCHAT_DATA)_coder);
    case SC_BEEP:
        return OnHeartBeep((PHEART_BEEP)_coder);
    case SC_LOOP:
        return OnHeartLoop((PHEART_LOOP)_coder);
    case SC_INITED:
        return OnInited();
     case SC_REONLINE:
    //case SC_INIT_START:
    //case SC_HAND:
    //case SC_HAND_IN:
    //    return false;
    case SC_MOVE_HEADER:
        return OnMove((PMOVE_DATA)_coder);
    case SC_FALL_HEADER:
        return OnFall((PFALL_DATA_START)_coder);
    case SC_INWATER:
      return false;
    case SC_USESKILL:
        return OnUseSkill((PUSESKILL)_coder);
    default:
        return true;
    }
    return true;
}

bool NetClient::OnSelectRole(wchar_t* rolename)
{
    //AfxMessageBox(rolename);
    return true;
}

bool NetClient::OnChooseCamp(PNS_CHOOSECAMP _coder)
{
    PNS_CHOOSECAMP _p = (PNS_CHOOSECAMP)_coder;
   
    return true;
}

bool NetClient::OnChat(PCHAT_DATA _coder)
{
    switch (_coder->ChartId)
    {
    case 3:// 私聊
        return OnChatPrivate((PCHAT_PRV)_coder);
    case 1:// 附近频道
    case 2:// 区域频道
    case 6:// 公会频道
    case 9:// 阵营频道
    case 21:// 喊话频道
        return OnChatPublic((PCHAT_PUB)_coder);
        break;
    }
    return true;
}

bool NetClient::OnChatPublic(PCHAT_PUB _coder)
{
    return true;
}

bool NetClient::OnChatPrivate(PCHAT_PRV _coder)
{
    return true;
}

bool NetClient::OnHeartBeep(PHEART_BEEP _coder)
{
    return true; // 返回false会拦截81心跳包不给服务端发送
}

bool NetClient::OnHeartLoop(PHEART_LOOP _coder)
{
    return true; // 返回false会拦截SC_LOOP心跳包不给服务端发送
}

bool NetClient::MailBox(int optype)
{
    NET_SEND_BUFF _buff;
    MAIL_OPERAION _data;

    _data.opcode.Set(SC_MAIL);
    _data.optype.Set(optype);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::OnMove(PMOVE_DATA _coder)
{
    switch (_coder->Mode)
    {
        case 0:
            return OnMoveStop((PMOVE_DATA_STOP)_coder);
        case 1:
            return OnMoveWalk((PMOVE_DATA_WALK)_coder);
        case 2:
            return OnMoveJump((PMOVE_DATA_JUMP)_coder);
        case 3:
            return OnMoveWJump((PMOVE_DATA_WJUMP)_coder);
    }
    return false;
}

bool NetClient::OpenMailBox()
{
    return MailBox(SC_MAIL_OPENBOX);
}

bool NetClient::CloseMailBox()
{
    bool ret;
    ret = MailBox(SC_MAIL_CLOSEMAIL);
    return ret && MailBox(SC_MAIL_CLOSEBOX);
}

bool NetClient::Mail(const char* guid, int optype)
{
    NET_SEND_BUFF _buff;
    MAIL_OPERAION_RD _data;

    _data.opcode.Set(SC_MAIL);
    _data.optype.Set(optype);
    _data.Guid.Set(guid);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

// 移动中处理函数
bool NetClient::OnMoveWalk(PMOVE_DATA_WALK _coder)
{
    /*float* MoveSpeed = (float*)_coder->MoveSpeed.oldPointer;
    MoveSpeed[0] = 5.0f;*/
    /*if (HideMode) {
        float* f = (float*)_coder->h.oldPointer;
        f[0] = f[0] + 5;
    }*/
    return true;
}

// 停止移动处理函数
bool NetClient::OnMoveStop(PMOVE_DATA_STOP _coder)
{
  /*  if (HideMode) {
        float* f = (float*)_coder->h.oldPointer;
        f[0] = f[0] + 5;
    }*/
    return true;
}

// 跳跃处理函数
bool NetClient::OnMoveJump(PMOVE_DATA_JUMP _coder)
{
    //float* MoveSpeed = (float*)_coder->MoveSpeed.oldPointer;
    //MoveSpeed[0] = 5.0f;
    //if(HideMode) {
    //    float* f = (float*)_coder->h.oldPointer;
    //    f[0] = f[0] + 5;
    //}
    return true;
}

bool NetClient::MailEx(const char* guid, int optype, int opother)
{
    NET_SEND_BUFF _buff;
    MAIL_OPERAION_GET _data;

    _data.opcode.Set(SC_MAIL);
    _data.optype.Set(optype);
    _data.Guid.Set(guid);
    _data.opother.Set(opother);


    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::GetMail(const char* guid)
{
    bool ret;
    ret = MailEx(guid);
    return ret && Mail(guid, SC_MAIL_GETSC);
}

// 移动时跳跃
bool NetClient::OnMoveWJump(PMOVE_DATA_WJUMP _coder)
{
   /* float* MoveSpeed = (float*)_coder->MoveSpeed.oldPointer;
    MoveSpeed[0] = 5.0f;*/
    //if(HideMode) {
    //    float* f = (float*)_coder->h.oldPointer;
    //    f[0] = f[0] + 5;
    //}
    return true;
}

bool NetClient::OnFall(PFALL_DATA_START _coder)
{
    return true;
}

bool NetClient::PostMail(int MailType, wchar_t* Target, wchar_t* Title, wchar_t* Txt, long long Money, BOOL Urgent)
{
    NET_SEND_BUFF _buff;
    MAIL_OPERAION_POST _data;

    _data.opcode.Set(SC_MAIL);
    _data.optype.Set(SC_MAIL_POST);
    _data.Target.Set(Target);
    _data.Title.Set(Title);
    _data.Txt.Set(Txt);
    _data.Money.Set(Money);
    _data.Urgent.Set(Urgent);

    /*
        sizeof(_data) / sizeof(EnCode)的原因
        NET_SEND_CHOOSECAMP结构体里面,没别 东西
        全是 EnCode 这个结构
    */
    short count = sizeof(_data) / sizeof(EnCode);
    _buff.count = count;

    /*
        CodeMe函数给 _buff.buff里写数据参数的数据
        也就是给0A开头数据包里写,数据参数个数后面的内容
        然后返回值是写了多长的数据
        也就是给0A开头数据包里的数据参数个数后面的数据写了多长
    */
    int ilen = _data.CodeMe(count, _buff.buff);
    ilen = ilen + sizeof(NET_SEND_HEAD) - 1;
    return NetSend(&_buff.op, ilen);
}

bool NetClient::OnSvrChat(PCHAT_PRV _coder)
{
    //AfxMessageBox(_coder->name);
    //AfxMessageBox(_coder->txt);
    //switch (_coder->ChartId)
    //{
    //case 3:// 私聊
    //    return OnChatPrivate((PCHAT_PRV)_coder);
    //case 1:// 附近频道
    //case 2:// 区域频道
    //case 6:// 公会频道
    //case 9:// 阵营频道
    //case 21:// 喊话频道
    //    return OnChatPublic((PCHAT_PUB)_coder);
    //    break;
    //}
    return true;
}

bool NetClient::OnUseSkill(PUSESKILL _coder)
{
    // 无法x坐标无效,会无法释放技能
    /*float* f = (float*)_coder->x.oldPointer;
    f[0] = 0;*/
    return true;
}

bool NetClient::OnInited()
{

    return true;
}

bool NetClient::Tips(int code)
{
#ifdef  Anly
    CString txt;
    if (code == 51001) {
        txt = L"登陆失败,易道云通行证不存在!";
    }else if (code == 51002) {
        txt = L"登录失败,密码错误!";
    }else if (code == 21101) {
        txt = L"人物重名!";
    }else if (code == 21109) {
        txt = L"名字过长或包含非法字符!";
    }
    else {
        txt.Format(L"未知登录错误:%d", code);
    }


    anly->SendData(TTYPE::I_LOG, 0, txt.GetBuffer(), (txt.GetLength() + 1)*2);
#endif
    return true;
}

void NetClient::loginok(ROLE_DATA* _roles, int count)
{
    logined = true;
    if(roles) delete[] roles;
    roles = _roles;
    rolecount = count;
}

bool NetClient::OnScrStartCreateRole(short code, wchar_t* _txt)
{
    return true;
}

bool NetClient::OnSvrNotice(PNET_SEND_CHEAD _coder, int count, char*& buffer, unsigned& len)
{
    if (_coder->msgHeader == "chat") {
        return OnSvrChat((PCHAT_PRV)_coder);
    }

    return true;
}

bool NetClient::OnRecvData(char*& buff, unsigned& len)
{
#ifdef  Anly
	anly->SendData(TTYPE::I_RECV, buff[0], buff, len);
#endif
    return (this->*RecvProc[buff[0]])(buff, len);
}

bool NetClient::OnSendData(char*& buff, unsigned& len)
{
#ifdef  Anly
	anly->SendData(TTYPE::I_SEND, buff[0], buff, len);
#endif
    return (this->*SendProc[buff[0]])(buff, len);
}

bool NetClient::OnConnect(char*& ip, unsigned& port)
{
#ifdef  Anly
    // 长度24的原因,它是宽字节要,一个文字要2个字节,一共是10个文字加上结尾的0是11个
    // 所以 11 乘以2,然后再加2 
    anly->SendData(TTYPE::I_LOG, 0, L"服务器正在连接。。。", 24);
#endif
    return true;
}

void NetClient::OnSvrGameBaseed(short structIndex, short index)
{
    
}


// 默认的数据处理函数
bool NetClient::DefaultProc(char*&, unsigned&)
{
    return true;
}

// 复制过来的内容
bool NetClient::OnClientlogin(char*& buff, unsigned& len)
{
    PDATALOGIN _data = (PDATALOGIN)(buff + 1);
    char* _id = _data->Id;
    _data = (PDATALOGIN)(buff + 1 + _data->lenId - 0x10);
    char* _pass = _data->Pass;

    Onlogin(_id, _pass);

    /* 修改账号密码
    len = sizeof(DATA_LOGIN) + 1;
    buff = new char[len];
    DATA_LOGIN data;
    PDATALOGIN _data = &data;
    buff[0] = 0x2;

    CStringA _id = "";// 补充账号
    CStringA _pass = "";// 补充密码
    memcpy(_data->Id, _id.GetBuffer(), _id.GetLength());
    memcpy(_data->Pass, _pass.GetBuffer(), _pass.GetLength());
    memcpy(buff + 1, _data, len - 1);
    */
    /* 监控登录数据
    PDATALOGIN _data = (PDATALOGIN)buff;
    CStringA _id = _data->Id;
    _data = (PDATALOGIN)(buff + _data->lenId - 0x10);
    CStringA _pass = _data->Pass;
    CStringA _tmp;
    // 请求登录 账号[% s]密码[% s] 这个内容别人在逆向的时候就会看到
    // 所以这种东西需要自己搞个编码来代替它

     _tmp.Format("请求登录 账号[%s]密码[%s]", _id, _pass);
#ifdef  Anly
    anly->SendData(TTYPE::I_DIS, 1, _tmp.GetBuffer(), _tmp.GetAllocLength());
#endif
    */

    /*
        返回false,游戏无法发送数据包
        原因看调用此此函数的位置 OnSend 函数(if (SendDealProc[buff[0]]((buff + 1), len - 1)))
    */
    return true;
}

bool NetClient::OnClientStartCreateRole(char*& buff, unsigned& len)
{
    // 申请进入创建角色界面
    int* code = (int*)&buff[1];
    return OnStartCreateRole(code[0]);
}

bool NetClient::OnClientCreateRole(char*& buff, unsigned& len) {
    PNS_CREATEROLE head = (PNS_CREATEROLE)(buff - 3);
    int icount = head->count;
    if (icount < 1)return true;
    char* buffStart = (char*)head + sizeof(NET_SEHD_CREATEROLE_HEAD);
#ifdef Anly
    GameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);// 翻译解析约定数据
#endif // Anly

    EnCode codes[sizeof(CREATE_ROLE_DATAS) / sizeof(EnCode)]{};
    int stDecode = 0;
    while (stDecode < icount) {
        codes[stDecode++] = buffStart;
    }


    /*
        OnCreateRole(head, (PCREATE_ROLE_DATAS)codes) 数据包传给虚函数
        如果想对发送创建角色数据包做些什么直接继承NetClient重写OnCreateRole函数既可以了
    */
    return OnCreateRole(head, (PCREATE_ROLE_DATAS)codes);// 返回false屏蔽05开头的数据包,也就是创建角色发送的数据包
}

bool NetClient::OnClientSendCustom(char*& buff, unsigned& len) {
    PNET_SEND_HEAD head = (PNET_SEND_HEAD)(buff - 1);
    int icount = head->count;
    if (icount < 1)return true;
    char* buffStart = (char*)head + sizeof(NET_SEND_HEAD);
    if (buffStart[0] != 0x02) {

#ifdef  Anly
        if (icount < MAX_SEND_COUNT)
            anly->SendData(TTYPE::I_DIS, I_SEND_CUSTOM, "SEND_CUSTOM MAX_SEND_COUNT 内存解码器空间不足", 46);

        anly->SendData(TTYPE::I_DIS, I_SEND_CUSTOM, "SEND_CUSTOM 发现异常数据", 25);
#endif
        return true;
    }

#ifdef  Anly
    GameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);
#endif

    int stDecode = 0;
    EnCode codes[MAX_SEND_COUNT]{};
    while (stDecode < icount) {
        codes[stDecode++] = buffStart;
    }

    /*
        OnSendCustom((PNET_SEND_CHEAD)codes, buff, len); 数据包传给虚函数
        如果想对发送数据的0A开头的据包做些什么直接继承NetClient重写OnSendCustom函数既可以了
    */
    return OnSendCustom((PNET_SEND_CHEAD)codes, buff, len);

}

bool NetClient::OnClientSelectRole(char*& buff, unsigned& len) {
    PNS_SELECTROLE p = (PNS_SELECTROLE)buff;
    return OnSelectRole((wchar_t*)(p->buff));
}

bool NetClient::OnClientDelRole(char*& buff, unsigned& len) {

    PDATADELROLE p = (PDATADELROLE)buff;
    return OnDelRole((wchar_t*)(p->buff), p->len);



    // 返回值改为false将拦截发送的删除角色数据包
    // 详情看注册 OnDelRole 函数的位置,Init函数
    // return true;
}

// 接收数据截取区

bool NetClient::OnSvrTips(char*& buff, unsigned& len) {
    int* code = (int*)&buff[1];
    return Tips(code[0]);
}

bool NetClient::OnSvrloginOk(char*& buff, unsigned& len) {

    PDATALOGINOK _p = (PDATALOGINOK)&buff[1];
    ROLE_DATA* roleDatas = nullptr;
    if (_p->RoleCount > 0) {
        char* buffStart = buff + 1 + sizeof(DATA_LOGIN_OK);

#ifdef Anly
        GameAnlyer->AnlyBuff(buffStart, buff + len, buff[0]);
#endif // Anly

        roleDatas = new ROLE_DATA[_p->RoleCount];
        for (int i = 0; i < _p->RoleCount; i++)
        {
            roleDatas[i].byte.Init(buffStart, 0);
            roleDatas[i].index.Init(buffStart, 0);
            roleDatas[i].un1.Init(buffStart, 0);
            roleDatas[i].name.Init(buffStart, 0);
            roleDatas[i].infos.Init(buffStart, 0);
            roleDatas[i].un2.Init(buffStart, 0);
            roleDatas[i].un3.Init(buffStart, 0);
        }
        loginok(roleDatas, _p->RoleCount);
    }
    return true;
}

bool NetClient::OnSverObject(char*& buff, unsigned& len) {
    PNR_HEAD head = (PNR_HEAD)(buff - 1);
    //head->count;

    if (ObjectTable) {
        delete[] ObjectTable;
    }

    if (ObjectTxt) {
        delete[] ObjectTxt;
    }

    ObjectTable = new OBJECT_DESC[head->count];
    ObjectTxt = new char[len];
    memcpy(ObjectTxt, buff, len);// 这里怕 buff 的内容被游戏释放掉,后面我们用的时候没法用,所以把buff的内容复制到我们的变量里
    char* buffStart = ObjectTxt + sizeof(NR_HEAD)-1;
//#ifdef Anly
//    CStringA szTxtA;
//    CStringA szTmp;
//#endif // Anly
//#ifdef Anly
//    szTmp.Format("[%X]%s:%d\r\n", i, ObjectTable[i].name, ObjectTable[i].type);
//    szTxtA += szTmp;
//#endif // Anly
//#ifdef  Anly
//    anly->SendData(TTYPE::I_DIS, S_OBJECT, szTxtA.GetBuffer(), szTxtA.GetAllocLength() + 1);
//#endif // Anly
    for (int i = 0; i < head->count; i++)
    {
        ObjectTable[i].name = buffStart;
        buffStart = buffStart + strlen(ObjectTable[i].name) + 1;
        ObjectTable[i].type = buffStart[0];
        buffStart++;
    }

#ifdef Anly
    GameAnlyer->CreateObjectfiles(ObjectTable, head->count);
#endif // Anly



    return true;
}
bool NetClient::OnSverStruct(char*& buff, unsigned& len) {
    PNR_HEAD head = (PNR_HEAD)(buff - 1);
    
    MaxStruct = head->count;

    if (StructTable) {
        delete[] StructTable;
    }

    if (StructTxt) {
        delete[] StructTxt;
    }

    StructTable = new STRUCT_DESC[head->count];
    StructTxt = new char[len];
    memcpy(StructTxt, buff, len);
    char* buffStart = StructTxt + sizeof(NR_HEAD) - 1;

    unsigned MaxOffSet = 0;

    for (int i = 0; i < head->count; i++)
    {
        StructTable[i].name = buffStart;
        buffStart = buffStart + strlen(StructTable[i].name) + 1;
        short* icount = (short*)buffStart;
        StructTable[i].count = icount[0];
        buffStart = buffStart + 2;
        StructTable[i].buff = buffStart;
        buffStart = buffStart + icount[0];
        MaxOffSet += StructTable[i].count;
    }
    if (StructOffSet) delete[] StructOffSet;
    StructOffSet = new short[MaxOffSet];

#ifdef Anly
    GameAnlyer->CreateStructfile(StructTable, head->count);
#endif // Anly

    // 初始化偏移表
    InitStruct(StructTable, head->count, StructOffSet);

    return true;
}
bool NetClient::OnSvrObjectInit(char*& buff, unsigned& len)
{
    /*
        00 00 00 00 00 00 00 为了内存对齐补充了7个0,也就是 un1[6] 和 len
        28 op
        CD 48 00 01 62 A7 DE 04 PNR_OBJINIT:lId
        C1 AA FB C3 PNR_OBJINIT:x;
        3D FF 22 41 PNR_OBJINIT:h;
        D7 0B 4A 44 PNR_OBJINIT:y;
        52 B8 06 40 PNR_OBJINIT:face;
        C1 AA FB C3 PNR_OBJINIT:tx;
        3D FF 22 41 PNR_OBJINIT:th;
        D7 0B 4A 44 PNR_OBJINIT:ty;
        52 B8 06 40 PNR_OBJINIT:tface;
        00 00 00 00 PNR_OBJINIT:un2[0]
        00 00 00 00 PNR_OBJINIT:un2[1]
        00 00 00 00 PNR_OBJINIT:un2[2]
        00 00 00 00 PNR_OBJINIT:un2[3]
        00 00 00 00 PNR_OBJINIT:un2[4]
        61 00       PNR_OBJINIT:icount;

        
        1B 00 (char*)head + sizeof(NR_OBNJECT_INIT) - 2;也就是指向到了 PNR_OBJINIT:type 这个位置
        0C 00 00 00 CA 4E 5A 66 53 62 01 80 4E 86 00 00 1D 00
    */
    // 初始化对象
    PNR_OBJINIT head = (PNR_OBJINIT)(buff - 7);
    char* buffStart = (char*)head + sizeof(NR_OBNJECT_INIT) - 2;

    //int nStart = (int)&Player.lId;
    //int nEnd = (int)&Player.endclass;
    //memcpy(&Player.lId, &head->lId, nEnd - nStart);
    Player.SetHeadDatas(&head->lId);
#ifdef  Anly
    GameAnlyer->AnlyData(buffStart, buff + len, head->icount, S_OBJECT_INIT, ObjectTable);
#endif

    int iProc = 0;
    while (iProc < head->icount)
    {
        Player.UpdateData(buffStart);
        iProc++;
    }

    return true;
}
bool NetClient::OnSvrObjectInitEx(char*& buff, unsigned& len)
{
    // 初始化对象
    PNR_OBJINITEX head = (PNR_OBJINITEX)(buff - 5);
    char* buffStart;

    /*int nStart = (int)&Player.lId;
    int nEnd = (int)&Player.endclass;
    memcpy(&Player.lId, &head->lId, nEnd - nStart);*/

    int iObjectCount = 0;
    short objCount = head->obj_count; // 可以理解为一共有多少个接收的28数据
    while (iObjectCount < objCount) {
        PAIM _paim = GetAimById(head->lId);
        _paim->SetHeadDatas(&head->lId);
       
        buffStart = (char*)head + sizeof(NR_OBNJECT_INITEX) - 2;
        short refCount = head->icount; // 接收的28数据里的数据参数个数
        short iref = 0;
#ifdef  Anly
        GameAnlyer->AnlyData(buffStart, buff + len, refCount, S_OBJECT_INITEX, ObjectTable);
#endif
        while (iref < refCount) {
            _paim->UpdateData(buffStart);
            iref++;
        }
        head = (PNR_OBJINITEX)(buffStart - 8);

        iObjectCount++;
    }

    return true;
}
bool NetClient::OnSvrUpdateCord(char*& buff, unsigned& len)
{
    /*
    00 00 00 00 00 head
    21 02 00 
    CD 14 00 01 CD 14 00 00 第一个坐标数据
    6D BF 54 43 
    A6 FA C7 3F 
    8C 52 A9 C1 
    CB 30 06 40 
    00 00 00 00 
    DB 0F C9 41 
    00 00 00 00 
    00 00 00 00 
    01 00 00 00 State

    CD 14 00 01 CD 14 00 00 第二个坐标数据
    61 41 5B 43 
    A6 FA C7 3F 
    C5 8A C7 C1 
    CB 30 06 40 
    9A 99 99 3E 
    DB 0F C9 40 
    00 00 00 00 
    00 00 00 00 
    01 00 00 00 
    
    */
    // 初始化对象
    PNR_OBJINITEX head = (PNR_OBJINITEX)(buff - 5);

    /*int nStart = (int)&Player.lId;
    int nEnd = (int)&Player.endclass;
    memcpy(&Player.lId, &head->lId, nEnd - nStart);*/

    int iObjectCount = 0;
    short objCount = head->obj_count; // 可以理解为一共有多少个接收的28数据
    while (iObjectCount < objCount) {
        PAIM _paim = GetAimById(head->lId);
        _paim->SetHeadDatas(&head->lId);
        CStringA txtA;
        txtA.Format("x:%f h:%f y:%f", head->x, head->h, head->y);
#ifdef  Anly
        anly->SendData(TTYPE::I_DIS, S_UPDATECORD, txtA.GetBuffer(), txtA.GetAllocLength() + 1);
#endif
        head = (PNR_OBJINITEX)((char*)&head->State - 4);
        iObjectCount++;
    }
    return true;
}
bool NetClient::OnSvrUpdateProperty(char*& buff, unsigned& len)
{
    // 初始化对象
    PNR_OBJECT_UP head = (PNR_OBJECT_UP)(buff - 6);
    PGAMEOBJ _object = nullptr;

    char* buffStart = (char*)&head->itype;
    short refcount = head->icount;
    int iref = 0;
#ifdef  Anly
    GameAnlyer->AnlyData(buffStart, buff + len, refcount, S_UPDATEPRO, ObjectTable);
#endif
    if (head->MsgId ) {
        _object = GetGameOBJECT(head->Index[0], head->Index[1]);
    }
    else {

        _object = GetAimById(head->lId);
      
    }

    while ((iref < refcount)&&(_object)) {
     
        _object->UpdateData(buffStart);
        iref++;
    }
    if (head->Index[0] == (short)OBJECT_TYPE::MailRecv) {
        POBJECTBASE object = GetGameOBJECT(head->Index[0], head->Index[1]);
        // object->UpdateData(buffStart);
        OnSvrGameBaseed(object->MainIndex, object->Index);
    }
    return true;
}
bool NetClient::OnSvrUpdatePropertyMu(char*& buff, unsigned& len)
{
    // 初始化对象
    PNR_OBJECT_UPMU head = (PNR_OBJECT_UPMU)(buff - 5);

    int iobjCount = 0;
    short objCount = head->objCount; // 可以理解为一共有多少个接收的28数据
    char* buffStart;
    /*
      数据包是下方的样子,
        2D 
        01 00 objCount
        9B 49 00 01 D5 8C 98 05  GetAimById(head->lId);
        02 00 
        25 00 (char*)&head->itype
        18 00 00 00 
        29 00 BA 01 00 00 
        
        while (iobjCount < objCount) 里获取的是
        9B 49 00 01 D5 8C 98 05
        02 00
        25 00 18 00 00 00
        29 00 BA 01 00 00 这一块数据

         while (iref++ < refCount) 里获取的是
         25 00 18 00 00 00

        (PNR_OBJECT_UPMU)(buffStart - 8); 移动到下一个
        9B 49 00 01 D5 8C 98 05  GetAimById(head->lId);
        02 00
        25 00 (char*)&head->itype
        18 00 00 00
        29 00 BA 01 00 00 这个数据

    */
    while (iobjCount < objCount) {
        PAIM _paim = GetAimById(head->lId);
        buffStart = (char*)&head->itype;

        short refCount = head->icount; // 接收的28数据里的数据参数个数
        short iref = 0;

#ifdef  Anly
        GameAnlyer->AnlyData(buffStart, buff + len, refCount, S_UPDATEPROMU, ObjectTable);
#endif

        while (iref++ < refCount) {
            _paim->UpdateData(buffStart);
        }
        head = (PNR_OBJECT_UPMU)(buffStart - 8);
        iobjCount++;
    }

    return true;
}
bool NetClient::OnSvrRemoveObjectMu(char*& buff, unsigned& len)
{
    // 初始化对象
    PNR_OBJECT_REMOVEMU head = nullptr;
    head = (PNR_OBJECT_REMOVEMU)(buff - &head->op);

    int iobjCount = head->objCount;
    
    for (int i = 0; i < iobjCount; i++)
    {
        RemoveAimById(head->lId[i]);
    }

    return true;
}

/*
    1F
    87 44 00 01 7F B0 D6 05
    FE AD DE C3
    6C F5 46 42
    07 FD 36 C4
    90 D9 9C 40
*/
bool NetClient::OnSvrUpdateCordEx(char*& buff, unsigned& len)
{
    // 初始化对象
    PNR_UPDATECOORD head = nullptr;
    head = (PNR_UPDATECOORD)(buff - &head->op);
    PAIM aim = GetAimById(head->lId);

    if (aim) {
        return aim->SetHeadCord(&head->lId);
    }

    return true;
}
bool NetClient::OnSvrGameBase(char*& buff, unsigned& len)
{
    // 初始化对象
    PNR_GAMEBASE head = nullptr; 
    head = (PNR_GAMEBASE)(buff - &head->op);
    char* buffStart = (char*)head + sizeof(NR_GAMEBASE);

    //int nStart = (int)&Player.lId;
    //int nEnd = (int)&Player.endclass;
    //memcpy(&Player.lId, &head->lId, nEnd - nStart);
    //Player.SetHeadDatas(&head->lId);
#ifdef  Anly
    CStringA szTxt;
    szTxt.Format("%X %X", head->type, head->index);
    anly->SendData(TTYPE::I_DIS, S_GAMEBASE, szTxt.GetBuffer(), szTxt.GetAllocLength() + 1);
    GameAnlyer->AnlyData(buffStart, buff + len, head->count, S_GAMEBASE, ObjectTable);
#endif
    POBJECTBASE object = GetGameOBJECT(head->type, head->index);
    if (object == nullptr)return true;

    object->MainIndex = head->type;
    object->Index = head->index;

    int iProc = 0;
    while (iProc < head->count)
    {
        object->UpdateData(buffStart);
        iProc++;
    }
    OnSvrGameBaseed(object->MainIndex, object->Index);
    return true;
}
bool NetClient::OnSvrGameBaseReset(char*& buff, unsigned& len)
{
    PNR_GAMEBASE_RESET head = nullptr;
    head = (PNR_GAMEBASE_RESET)(buff - &head->op);
    POBJECTBASE object = ReSetGameOBJECT(head->type, head->max);
    if (object) {
        char* buffStart = (char*)head + sizeof(NR_GAMEBASE);
        int iProc = 0;
        while (iProc < head->count)
        {
            object->UpdateData(buffStart);
            iProc++;
        }

    }
    return true;
}
bool NetClient::OnSvrGameBaseExChange(char*& buff, unsigned& len)
{
    PNR_GAMEBASE_EXCHANGE head = nullptr;
    head = (PNR_GAMEBASE_EXCHANGE)(buff - &head->op);
    ExChangeOBJECT(head->MainIndex, head->IndexFrom, head->IndexTo);
    return true;
}
bool NetClient::OnSvrGameBaseDestroy(char*& buff, unsigned& len)
{
    PNR_GAMEBASE_DROP head = nullptr;
    head = (PNR_GAMEBASE_DROP)(buff - &head->op);
    DestoryOBJECT(head->MainIndex, head->Index);
    return true;
}
bool NetClient::OnSvrGameBaseSet(char*& buff, unsigned& len)
{
    PNR_GAMEBASE_SET head = nullptr;
    head = (PNR_GAMEBASE_SET)(buff - &head->op);
    POBJECTBASE object = ReSetGameOBJECT(head->MainIndex, 0);
    if (object) {
        char* buffStart = head->buff;
        int iProc = 0;
        while (iProc < head->count)
        {
            object->UpdateData(buffStart);
            iProc++;
        }

    }
    return true;
}
bool NetClient::OnSvrStructInit(char*& buff, unsigned& len)
{
    PNR_STRUCT_INIT head = nullptr;
    head = (PNR_STRUCT_INIT)(buff - &head->op);

#ifdef Anly
    GameAnlyer->AnlyStruct(head, buff + len, S_STRUCT_INIT, StructTable);
#endif

    if (head->optype) {
        return true;
    }
    else {
        PAIM aim = GetAimById(head->lId);
        if (aim->CreateStruct(head->StructIndex, head->Start, head->Count)) {
            char* buffStart = head->buff;
            int iProc = 0;
            while (iProc < head->Count)
            {
                UpdateStruct(aim, head->StructIndex, head->Start + iProc, buffStart);
                iProc++;
            }
        }else return true;
    }

}
bool NetClient::UpdateStruct(PAIM aim, short structIndex, short index, char*& buff)
{
    void* base = aim->GetStruct(structIndex, index);
    if (!base) return false;
    short icount = StructTable[structIndex].count;
    
    short iProc = 0;
    while (iProc < icount) {
        short type = StructTable[structIndex].buff[iProc];

        switch (type)
        {
            case 1:
                UpdateStructByte(base, aim, structIndex, iProc, buff);
                break;
            case 2:
                UpdateStructShort(base, aim, structIndex, iProc, buff);
                break;
            case 3:
                UpdateStructInt(base, aim, structIndex, iProc, buff);
                break;
            case 5:
                UpdateStructfloat(base, aim, structIndex, iProc, buff);
                break;
            case 6:
                UpdateStructDouble(base, aim, structIndex, iProc, buff);
                break;
            case 7:
                UpdateStructAscii(base, aim, structIndex, iProc, buff);
                break;
            case 8:
                UpdateStructUnicode(base, aim, structIndex, iProc, buff);
                break;
            case 4:
            case 9:
                UpdateStructInt64(base, aim, structIndex, iProc, buff);
                break;
            default:
                return false;
        }

        iProc++;
    }
    return true;
}
bool NetClient::OnSvrStructClear(char*& buff, unsigned& len)
{
    PNR_STRUCT_INIT head = nullptr;
    head = (PNR_STRUCT_INIT)(buff - &head->op);
    if (head->optype) {
        return true;
    }
    else {
        PAIM aim = GetAimById(head->lId);
        aim->DeleteStruct(head->StructIndex);
    }
    return true;
}
bool NetClient::OnSvrStructDelete(char*& buff, unsigned& len)
{
    PNR_STRUCT_INIT head = nullptr;
    head = (PNR_STRUCT_INIT)(buff - &head->op);
    if (head->optype) {
        return true;
    }
    else {
        PAIM aim = GetAimById(head->lId);
        aim->DeleteStruct(head->StructIndex, head->DelIndex);
    }
    return true;
}
bool NetClient::OnSvrStructUpdate(char*& buff, unsigned& len)
{
    PNR_STRUCT_UPDATE head = nullptr;
    head = (PNR_STRUCT_UPDATE)(buff - &head->op);
    if (head->optype) {
        return true;
    }
    else {
#ifdef Anly
        GameAnlyer->AnlyStruct(head, buff + len, S_STRUCT_UPDATE, StructTable);
#endif
        PAIM aim = GetAimById(head->lId);
        char* buffStart = head->buff;
        UpdateStruct(aim, head->StructIndex, buffStart, head->Count);
    }
    return true;
}
bool NetClient::UpdateStructByte(void* base, PAIM aim, short structIndex, short index, char*& buff)
{
    char* writer = (char*)((char*)base + StructTable[structIndex].offset[index]);
    writer[0] = buff[0];
    buff = buff + sizeof(char);
    return true;
}
bool NetClient::UpdateStructShort(void* base, PAIM aim, short structIndex, short index, char*& buff)
{
    short* writer = (short*)((char*)base + StructTable[structIndex].offset[index]);
    short* wtstream = (short*)buff;
    writer[0] = wtstream[0];
    buff = buff + sizeof(short);
    return true;
}
bool NetClient::UpdateStructInt(void* base, PAIM aim, short structIndex, short index, char*& buff)
{
    int* writer = (int*)((char*)base + StructTable[structIndex].offset[index]);
    int* wtstream = (int*)buff;
    writer[0] = wtstream[0];
    buff = buff + sizeof(int);
    return true;
}
bool NetClient::UpdateStructInt64(void* base, PAIM aim, short structIndex, short index, char*& buff)
{
    long long* writer = (long long*)((char*)base + StructTable[structIndex].offset[index]);
    long long* wtstream = (long long*)buff;
    writer[0] = wtstream[0];
    buff = buff + sizeof(long long);
    return true;
}
bool NetClient::UpdateStructfloat(void* base, PAIM aim, short structIndex, short index, char*& buff)
{
    float* writer = (float*)((char*)base + StructTable[structIndex].offset[index]);
    float* wtstream = (float*)buff;
    writer[0] = wtstream[0];
    buff = buff + sizeof(float);
    return true;
}
bool NetClient::UpdateStruct(PAIM aim, short structIndex, char*& buff, short count)
{
    short iProc = 0;
    while (iProc < count) {
        short* pindex = (short*)buff;
        buff = buff + 2;
        char* sIndex = buff;
        void* base = aim->GetStruct(structIndex, pindex[0]);
        if (!base)return true;
        short type = StructTable[structIndex].buff[sIndex[0]];
        buff = buff + 1;
        switch (type)
        {
        case 1:
            UpdateStructByte(base, aim, structIndex, sIndex[0], buff);
            break;
        case 2:
            UpdateStructShort(base, aim, structIndex, sIndex[0], buff);
            break;
        case 3:
            UpdateStructInt(base, aim, structIndex, sIndex[0], buff);
            break;
        case 5:
            UpdateStructfloat(base, aim, structIndex, sIndex[0], buff);
            break;
        case 6:
            UpdateStructDouble(base, aim, structIndex, sIndex[0], buff);
            break;
        case 7:
            UpdateStructAscii(base, aim, structIndex, sIndex[0], buff);
            break;
        case 8:
            UpdateStructUnicode(base, aim, structIndex, sIndex[0], buff);
            break;
        case 4:
        case 9:
            UpdateStructInt64(base, aim, structIndex, sIndex[0], buff);
            break;
        default:
            return false;
        }
        iProc++;
    }
    return true;
}
bool NetClient::UpdateStructDouble(void* base, PAIM aim, short structIndex, short index, char*& buff)
{
    double* writer = (double*)((char*)base + StructTable[structIndex].offset[index]);
    double* wtstream = (double*)buff;
    writer[0] = wtstream[0];
    buff = buff + sizeof(double);
    return true;
}
bool NetClient::UpdateStructAscii(void* base, PAIM aim, short structIndex, short index, char*& buff)
{
    GASCII* writer = (GASCII*)((char*)base + StructTable[structIndex].offset[index]);
    int* Plen = (int*)buff;// 如果是字符串,第一个字节是一个int类型,用来表示字符串的长度
    buff = buff + 4;
    writer[0] = buff;
    buff = buff + Plen[0];
    return true;
}
bool NetClient::UpdateStructUnicode(void* base, PAIM aim, short structIndex, short index, char*& buff)
{
    GUNICODE* writer = (GUNICODE*)((char*)base + StructTable[structIndex].offset[index]);
    int* Plen = (int*)buff;// 如果是字符串,第一个字节是一个int类型,用来表示字符串的长度
    buff = buff + 4;
    writer[0] = (wchar_t*)buff;// 如果是宽字节需要强制转换,不然会乱码
    buff = buff + Plen[0];
    //CString txt;
    //txt.Format(L"%s %d", writer[0], StructTable[structIndex].offset[index]);
    //AfxMessageBox(txt);
    return true;
}
/*
 OnSverrNotice函数处理的数据包格式如下
    1E 06 00
    06 11 00 00 00 70 6C 61 79 5F 70 6F 69 6E 74 5F 73 6F 75 6E 64 00
    06 01 00 00 00 00
    04 2C 92 87 C5
    04 FA 03 BF 42
    04 33 14 BD 45
    02 00 00 00 00
    1E 06 00 是 PNR_NOTICE_HEAD
    06 11 00 00 00 70 6C 61 79 5F 70 6F 69 6E 74 5F 73 6F 75 6E 64 00是一个EnCode
    06 01 00 00 00 00是一个EnCode
    04 2C 92 87 C5是一个EnCode
    04 FA 03 BF 42是一个EnCode
    04 33 14 BD 45是一个EnCode
    02 00 00 00 00是一个EnCode
*/
bool NetClient::OnSverNotice(char*& buff, unsigned& len) {
    PNR_NOTICE_HEAD head = (PNR_NOTICE_HEAD)(buff - 1);
    int icount = head->count;
    char* buffStart = (char*)head + sizeof(NR_NOTICE_HEAD);
    if (icount < 1) {
        return true;
    }
    if (icount > MAX_RECV_COUNT) {
#ifdef  Anly
        anly->SendData(TTYPE::I_DIS, S_NOTICE, "S_NOTICE 解码器内存不足", 24);
#endif
        return true;
    }

#ifdef  Anly
    GameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);
#endif

    int stDecode = 0;
    EnCode codes[MAX_RECV_COUNT]{};
    while (stDecode < icount) {
        codes[stDecode++] = buffStart;
    }
    return OnSvrNotice((PNET_SEND_CHEAD)codes, icount, buff, len);
}

bool NetClient::OnSvrStartCreateRole(char*& buff, unsigned& len) {
    short* _st = (short*)&buff[1];
    wchar_t* _txt = (wchar_t*)&buff[3];
#ifdef  Anly
    CString txt;
    CStringA txtA;
    txt.Format(L"code:%d\r\n%s", _st[0], _txt);
    txtA = txt;
    //AfxMessageBox(txtA);
    anly->SendData(TTYPE::I_DIS, S_CREATEROLE_START, txtA.GetBuffer(), txt.GetAllocLength() + 1);
#endif
    /*
        OnSendCustom((PNET_SEND_CHEAD)codes, buff, len); 数据包传给虚函数
        如果想对0A开头的据包做些什么直接继承NetClient重写OnSendCustom函数既可以了
    */
    return OnScrStartCreateRole(_st[0], _txt);
}


NetClient.h文件的修改:新加 OnSvrGameBaseed函数

#pragma once
#include "NetClass.h"
#include "AIM.h"
#include "ITEM.h"
#include "SKILL.h"
#include "GameSTRUCTRENAME.h"

#define CAMP_NAME_QH "xuanrenQH"
#define CAMP_NAME_ZE "xuanrenZQ"

#define MAX_AIM 0x200

enum class OBJECT_TYPE {
	Equip = 0x01, // 装备栏,18 01 是装备,用于列表交换
	MountBag = 0x08, // 坐骑栏,18 08 是坐骑背包,用于列表交换
	Item = 0x02, // 物品栏,18 02 是角色背包,用于列表交换
	ItemEx = 0x03, // 扩展栏,18 03 是扩展背包,用于列表交换
	Skill = 0x28,  // 技能栏,18 28 是技能信息,用于列表交换
	Shop = 0x3D, // 商店,18 3D
	Pickup = 0x50, // 捨取,18 50 是打怪,怪死了之后掉落的捨取弹框
	MailRecv = 0x24, // 邮件中的物品
	MailSend = 0x25
};

typedef struct OBJECT_UNION {
	union {
		PITEM* item = nullptr;
		PSKILL* skill;
	};

	/*
		short EquipMax = 0; // 装备栏数据个数
		short MountMax = 0; // 坐骑栏数据个数
		short ItemMax = 0; // 物品栏数据个数
		short ItemExMax = 0; // 扩展栏数据个数
		short SkillMax = 0; // 技能栏数据个数
	*/
	short Count = 0;
	short MaxCount = 0; // 装备栏数据个数,坐骑栏数据个数,物品栏数据个数,扩展栏数据个数,技能栏数据个数
	
	/*
		PITEM EquipAry = nullptr; // 装备栏,18 01 是装备,用于存放真正的数据
		PITEM MountBagAry = nullptr; // 坐骑栏,18 08 是坐骑背包,用于存放真正的数据
		PITEM ItemBagAry = nullptr; // 物品栏,18 02 是角色背包,用于存放真正的数据
		PITEM ItemExAry = nullptr; // 扩展栏,18 03 是扩展背包,用于存放真正的数据
		PSKILL SkillAry = nullptr;  // 技能栏,18 28 是技能信息,用于存放真正的数据
	*/
	union {
		PSKILL skillAry = nullptr;
		PITEM itemAry;
		void* Pointer;
	};
}*POBJECT_UNION;

typedef bool (*PNetOperation)(char* buff, unsigned len);

class NetClient // 监视客户端每一个操作
{
	typedef bool (NetClient::* DATAPROC)(char*&, unsigned&);
public:
	AIM Player; // 玩家角色
	/* 
		怪物列表,最好排序,使用lId排序,然后使用二分查找实现快速查询
		结构用链表或者数组都可以
		怪物或附近玩家这种东西不可能会很多
		如果太多电脑处理不过来,所以数组设置为0x100大小差不多够用
	*/
	PAIM Aimlst[MAX_AIM]{};
	// 用来控制飞天
	bool HideMode;
	/*
		18 01 是装备
		18 02 是角色背包
		18 03 是扩展背包
		18 08 是坐骑背包
		18 31 是攻击技能
		18 28 是移动类型技能
		18 3D 商店数据
		18 50 捨取窗口数据
	*/
	OBJECT_UNION Equip; // 装备栏,18 01 是装备,用于列表交换
	OBJECT_UNION MountBag; // 坐骑栏,18 08 是坐骑背包,用于列表交换
	OBJECT_UNION Item; // 物品栏,18 02 是角色背包,用于列表交换
	OBJECT_UNION ItemEx; // 扩展栏,18 03 是扩展背包,用于列表交换
	OBJECT_UNION Skill;  // 技能栏,18 28 是技能信息,用于列表交换
	OBJECT_UNION Shop; // 商店,18 3D
	OBJECT_UNION Pickup; // 捨取,18 50 是打怪,怪死了之后掉落的捨取弹框
	OBJECT_UNION MailRecv;
	OBJECT_UNION MailSend;

	POBJECT_UNION GetGameOBJECTUnion(short MainIndex);// 获取一个物品或技能
protected:
	PSTRUCT_DESC StructTable = nullptr;
	short* StructOffSet = nullptr; // 偏移表
	short MaxStruct = 0; // 结构体数量

	char* StructTxt = nullptr;
	POBJ_DESC ObjectTable = nullptr;// 游戏的数据类型表
	char* ObjectTxt = nullptr;
	DATAPROC SendProc[0x100];
	DATAPROC RecvProc[0x100];
	bool DefaultProc(char*&, unsigned&);
	PNetOperation NetSend = nullptr;
	PNetOperation NetRecv = nullptr;

protected: // 消息处理函数-SEND
	bool OnClientlogin(char*& buff, unsigned& len); // 登录数据包的处理 I_LOGIN
	bool OnClientStartCreateRole(char*& buff, unsigned& len);  // 申请进入创建角色界面 I_CREATEROLE_START
	bool OnClientDelRole(char*& buff, unsigned& len);
	bool OnClientSendCustom(char*& buff, unsigned& len);
	bool OnClientCreateRole(char*& buff, unsigned& len);
	bool OnClientSelectRole(char*& buff, unsigned& len);
protected: // 消息处理函数-RECV
	bool OnSvrTips(char*& buff, unsigned& len);
	bool OnSvrloginOk(char*& buff, unsigned& len);
	bool OnSvrStartCreateRole(char*& buff, unsigned& len);
	bool OnSverNotice(char*& buff, unsigned& len);
	bool OnSverObject(char*& buff, unsigned& len);
	bool OnSverStruct(char*& buff, unsigned& len);
	bool OnSvrObjectInit(char*& buff, unsigned& len);
	bool OnSvrObjectInitEx(char*& buff, unsigned& len);
	bool OnSvrUpdateCord(char*& buff, unsigned& len);
	bool OnSvrUpdateProperty(char*& buff, unsigned& len);
	bool OnSvrUpdatePropertyMu(char*& buff, unsigned& len);
	bool OnSvrRemoveObjectMu(char*& buff, unsigned& len);
	bool OnSvrUpdateCordEx(char*& buff, unsigned& len);
	bool OnSvrGameBase(char*& buff, unsigned& len);
	bool OnSvrGameBaseReset(char*& buff, unsigned& len);
	bool OnSvrGameBaseExChange(char*& buff, unsigned& len);
	bool OnSvrGameBaseDestroy(char*& buff, unsigned& len);
	bool OnSvrGameBaseSet(char*& buff, unsigned& len);
	bool OnSvrStructInit(char*& buff, unsigned& len); // 结构体数据初始化
	bool OnSvrStructClear(char*& buff, unsigned& len); // 结构体数据重置
	bool OnSvrStructDelete(char*& buff, unsigned& len); // 结构体数据删除
	bool OnSvrStructUpdate(char*& buff, unsigned& len); // 结构体数据更新
	

	bool UpdateStruct(PAIM aim, short structIndex, char*& buff, short count); // 结构体数据更新
	bool UpdateStruct(PAIM aim, short structIndex, short index,char*& buff); // 结构体数据更新
	bool virtual UpdateStructByte(void* base, PAIM aim, short structIndex, short index,char*& buff);
	bool virtual UpdateStructShort(void* base, PAIM aim, short structIndex, short index,char*& buff);
	bool virtual UpdateStructInt(void* base, PAIM aim, short structIndex, short index,char*& buff);
	bool virtual UpdateStructInt64(void* base, PAIM aim, short structIndex, short index,char*& buff);
	bool virtual UpdateStructfloat(void* base, PAIM aim, short structIndex, short index,char*& buff);
	bool virtual UpdateStructDouble(void* base, PAIM aim, short structIndex, short index,char*& buff);
	bool virtual UpdateStructAscii(void* base, PAIM aim, short structIndex, short index,char*& buff);
	bool virtual UpdateStructUnicode(void* base, PAIM aim, short structIndex, short index,char*& buff);
public:
	PROLEDATA roles;
	unsigned rolecount;
	bool logined = false;
private:

	bool DelRole(const wchar_t* rolename, unsigned _len);
	PAIM GetAimById(long long lId);
	PAIM CreateAim(long long lId);
	void RemoveAimById(long long lId);
	POBJECTBASE GetGameOBJECT(short MainIndex, short Index);// 获取一个物品或技能
	POBJECTBASE ReSetGameOBJECT(short MainIndex, short max);
	// 交换物品
	void ExChangeOBJECT(short MainIndex, short IndexFrom, short IndexTo);
	// 删除物品
	void DestoryOBJECT(short MainIndex, short Index);
public:
	// 通过角色名获取附近角色的信息
	PAIM GetAimByName(const wchar_t* name);
	// 获取面向
	float GetFace(float x, float y, float targetX, float targetY);
	// 通过附近角色名获取它的信息,然后通过它的信息里的坐标,再通过我们角色坐标计算面向,效果就是面向 GetAimByName(name); 这个角色
	void FaceTo(const wchar_t* name);
public:
	void virtual Init(PNetOperation _send, PNetOperation _recv);
	// 模拟接收的数据包
	bool SetCoord(long long lId, float x, float h, float y, float face);
	bool SetProperty(long long lId, int ProType, void* value);
	/*
		模拟登陆的方法
		Id是账号
		Pass是密码
		它要基于发送的方法实现,因为我们没有连接socket的操作
	*/
	bool login(const char* Id, const char* Pass);
	bool DelRole(const wchar_t* rolename);
	bool StartCreateRole();// 用于创建角色
	bool SelectCamp(const char* _campname);// 选择阵营
	/*
		性别 0 男 1 女

		阵营 1 艾森赫特 2 格兰蒂尔

		种族 1 布冯特人 3 格洛玛人 4 尤恩图人 6 喀什人

		职业 1 仲裁者 3秘法师 6 猎魔人 8 元素法师

		脸型 0 1 2 3
	*/
	bool CreateRole(wchar_t* name,double sex, double camp, double face, double occu, const char* photo, const char*infos, const char* txt, double faceShape);// 角色创建
	// 选择角色并且登录进游戏
	bool SelectRole(const wchar_t* rolename);
	// 坠落
	bool Fall();
	// 瞬移
	void Teleport(float x, float h, float y, float face);
	// 模拟停止
	bool MoveStop(float x, float h, float y, float face);
	// 原地跳
	bool MoveJump(float x, float h, float y, float face, float oldy, float xNext, float yNext);
	// 移动开始
	bool MoveWalk(float x, float h, float y, float face, float oldy, float xNext, float yNext);
	// 应该是移动时跳跃
	bool MoveWJump(float x, float h, float y, float face, float oldy, float xNext, float yNext);
	// 使用技能
	bool UseSkill(const char* _skillName, float x, float h, float y, float face, float xTarget = 0, float hTarget = 0, float yTarget = 0, int rInt1 = 0, int rInt2 = 0, int rInt3 = 0);
	// 使用物品
	bool UseItem(short BagIndex,short Index);
	// 交换物品
	bool ExChangeItem(short BagIndex,short Index, short tBagindex, short tIndex, bool IsEmpty = true);
	// 丢弃物品
	bool DropItem(short BagIndex,short Index, short Amount);
	// 拆分物品
	bool SplitItem(short BagIndex, short Index, short tBagindex, short tIndex, short Amount);
	// 召唤或解除坐骑
	bool Mount(short Index);
	// 拾取物品
	bool PickItem(short id, short Index);
	// 商店购买物品
	bool BuyItem(int shopPage, int shopIndex, int itemIndex, int Amount);
	// 卖物品给商店
	bool SoldItem(int MainIndex, int Index, int Amount);
	// 操作邮箱
	bool MailBox(int optype);
	// 打开邮箱窗口
	bool OpenMailBox();
	// 关闭邮箱窗口
	bool CloseMailBox();
	// 删除邮件
	bool Mail(const char* guid, int optype = SC_MAIL_READ);
	// 接收附件
	bool MailEx(const char* guid, int optype = SC_MAIL_GET, int opother = -1);
	// 接收附件
	bool GetMail(const char* guid);
	// 发送邮件
	bool PostMail(int MailType, wchar_t* Target, wchar_t* Title, wchar_t* Txt, long long Money, BOOL Urgent);
	// 根据角色名字获取一个登录成功数据包(选择角色列表里的一个数据)
	PROLEDATA GetRoleByName(const wchar_t* rolename);
	bool Talk(wchar_t* txt, int PdId = 1, double un = 0.0);
	bool TalkTo(wchar_t* name, wchar_t* txt, double un = 0.0);
	bool HeartBeep();// 心跳数据包(5秒)
	bool HeartLoop();// 延迟心跳数据包(20秒)
	bool Backtoroles(); // 返回到选择角色界面
public:
	// 用于拦截游戏删除角色功能
	bool virtual OnDelRole(wchar_t* rolename, unsigned _len);
	// 用于拦截游戏登录功能
	void virtual Onlogin(const char* Id, const char*Pass);
	// 用于拦截游戏创建角色功能
	bool virtual OnStartCreateRole(int code);
	// 拦截创建角色
	bool virtual OnCreateRole(PNS_CREATEROLE _header, PCREATE_ROLE_DATAS _body);
	// opcode意思是操作码,count意思是数量,buffStart意思是解码的内容开始,buffend意思是解码的内容结束,buffer是原始的数据,len是原始数据的长度
	// char& buffer, int& len这俩参数带&的原因是,在 OnSendCustom 里进行修改之后,通过&的方式传递回去
	bool virtual OnSendCustom(PNET_SEND_CHEAD _coder, char*& buffer, unsigned& len);// 拦截0xA开头发送的数据包
	bool virtual OnSelectRole(wchar_t* rolename);
public:
	// 针对SendCustom的单独处理
	bool virtual OnChooseCamp(PNS_CHOOSECAMP _coder);
	bool virtual OnChat(PCHAT_DATA _coder);
	bool virtual OnChatPublic(PCHAT_PUB _coder);
	bool virtual OnChatPrivate(PCHAT_PRV _coder);
	bool virtual OnHeartBeep(PHEART_BEEP _coder);
	bool virtual OnHeartLoop(PHEART_LOOP _coder);
	// 分发移动处理函数
	bool virtual OnMove(PMOVE_DATA _coder);
	// 移动中处理函数
	bool virtual OnMoveWalk(PMOVE_DATA_WALK _coder);
	// 停止移动处理函数
	bool virtual OnMoveStop(PMOVE_DATA_STOP _coder);
	// 跳跃处理函数
	bool virtual OnMoveJump(PMOVE_DATA_JUMP _coder);
	// 移动时跳跃
	bool virtual OnMoveWJump(PMOVE_DATA_WJUMP _coder);
	//  分发坠落函数
	bool virtual OnFall(PFALL_DATA_START _coder);
	// 针对Notice的单独处理
	bool virtual OnSvrChat(PCHAT_PRV _coder);
	bool virtual OnUseSkill(PUSESKILL _coder);
	bool virtual OnInited(); // 发送完了964数据包,表示角色上线

public:
	// 处理失败,参数是错误码
	bool virtual Tips(int code);
	void virtual loginok(ROLE_DATA* _roles, int count);
	bool virtual OnScrStartCreateRole(short code,wchar_t* _txt);
	bool virtual OnSvrNotice(PNET_SEND_CHEAD _coder, int count, char*& buffer, unsigned& len);
public:
	bool virtual OnRecvData(char*& buff, unsigned& len);
	bool virtual OnSendData(char*& buff, unsigned& len);
	bool virtual OnConnect(char*& ip, unsigned& port);
public:
	// 18、10数据包扩展
	void virtual OnSvrGameBaseed(short structIndex, short index);

};


NetClass.h文件的修改:修改了 SC_MAIL_CLOSEBOX宏、SC_MAIL_CLOSEMAIL宏,新加 SC_MAIL_DENY宏

#pragma once
#include "EnCode.h"

// 数据包头
// 发送
#define I_LOGIN 0x2 // 登录
#define I_CREATEROLE_START 0x3 // 创建角色发送数据包的头
#define I_SELECT_ROLE 0x04 // 选择角色进入游戏的数据包头
#define I_CREATEROLE 0x05 // 创建角色给服务端发送的数据包头
#define I_DELROLE 0x6 // 删除角色发送数据包的头
#define I_SEND_CUSTOM 0x0A // 进入游戏之后大部分数据包的头

// 接收
#define S_TIPS 0x3 // 服务端返回错误的数据包头
#define S_LOGINOK 0x4 // 登录成功的返回数据包的头
// 创建角色服务端反馈的数据(发送03 01 00 00 00这个数据包服务端返回的数据包头)
#define S_CREATEROLE_START 0x05
#define S_OBJECT 0x9 // 数据类型表
#define S_STRUCT 0xA // 里面有玩家角色坐标信息,进入游戏时它有数据结构描述的信息
#define S_UPDATEPRO 0x10 // 更新角色怪物信息
#define S_STRUCT_INITEX 0x11 // 结构体数据创建
#define S_STRUCT_DELETE 0x12 // 删除结构体
#define S_STRUCT_UPDATE 0x13 // 更新结构体
#define S_STRUCT_CLEAR 0x14 // 重置结构
#define S_GAMEBASE_RESET 0x15 // 通知更新数据
#define S_GAMEBASE_SET 0x17 // 初步猜测是 通知更新数据0x15的补充
#define S_GAMEBASE 0x18 // 杂项数据包,里面有技能的信息、装备的信息 还有未知的信息
#define S_GAMEBASE_DROP 0x19 // 删除物品
#define S_NOTICE 0x1E // 里面有聊天数据
#define S_UPDATECORDEX 0x1F // 坐标修正
#define S_UPDATECORD 0x21 // 坐标更新
#define S_NOTICE_COM 0x27 // 里面有聊天数据
#define S_OBJECT_INIT 0x28 // 里面有角色基础信息
#define S_STRUCT_INIT 0x29 // 邮箱初始化或者说是初始化邮件,结构体数据创建
#define S_GAMEBASE_EXCHANGE 0x2B // 物品交换
#define S_UPDATEPROMU 0x2D // 更新信息
#define S_UPDATEPROMU_COM 0x2E // 更新信息
#define S_OBJECT_INITEX_UCOM 0x2F // 里面有附近NPC、玩家初始化信息,与0x30数据包一样只是它未压缩
#define S_OBJECT_INITEX 0x30 // 里面有附近NPC、玩家初始化信息
#define S_OBJECT_REMOVE 0x31 // 附近角色或怪物消失

/*
	最多的数据项, 0A开头的数据包里有数据参数个数,这个宏就是用来设定
	数据参数个数最大数量是多少,0A开头的数据包目前见过最大
	数据参数个数是十几个,这里写20应该完全够用,如果不够用
	到时候再增加
*/
#define MAX_SEND_COUNT 100
#define MAX_RECV_COUNT 100
// 发送数据操作码定义
#define SC_CHOOSECAMP 591 // 阵营选择操作码
#define SC_CHAT 1
#define SC_BEEP 81
#define SC_LOOP 1015
// 游戏客户端处理完进入游戏按钮触发的数据包之后,发送的请求
#define SC_INITED 964
#define SC_REONLINE 860
#define SC_INIT_START 661
#define SC_HAND 962
#define SC_HAND_IN 102

#define SC_MOVE_HEADER 15 // 移动
#define SC_FALL_HEADER 890 // 坠入摔血
#define SC_INWATER 478 // 只要发了这个数据包就说明在水下了
#define SC_USESKILL 211 // 使用技能

#define SC_ITEM_USE 31 // 使用物品发送的操作码
#define SC_ITEM_EXCHANGE 30 // 交换物品发送的操作码-物品与空位置交换
#define SC_ITEM_EXCHANGEEX 40 // 交换物品发送的操作码-物品与物品交换
#define SC_ITEM_DROP 34 // 丢弃物品发送的操作码
#define SC_ITEM_SPLIT 33 // 拆分物品发送的操作码
#define SC_MOUNT 243 // 召唤或解除坐骑

#define SC_ITEM_PICK_START 380 // 拾取物品,后发送
#define SC_ITEM_PICK_END 1008 // 拾取物品,优先发送
#define SC_ITEM_SHOP 70 // 买/卖商店物品的数据头
#define SC_ITEM_SHOP_BUY 20 // 买商店物品操作码
#define SC_ITEM_SHOP_SOLD 21 // 卖物品给商店操作码

#define SC_MAIL 350 // 数据包操作码
#define SC_MAIL_OPENBOX 0 // 打开邮箱窗口
#define SC_MAIL_POST 2 // 发送邮件
#define SC_MAIL_READ 3 // 读取邮件
#define SC_MAIL_GETSC 4 // 收取附件
#define SC_MAIL_DELETE 5 // 删除邮件
#define SC_MAIL_CLOSEBOX 6 // 关闭邮箱
#define SC_MAIL_CLOSEMAIL  7 // 关闭打开邮件窗口
#define SC_MAIL_GET 8 // 收取附件
#define SC_MAIL_DELETE_ITEM 9 // 删除未领取附件的邮件
#define SC_MAIL_PAY 10 // 给邮件付费
#define SC_MAIL_DENY 11 // 退信

/*
	00 00 00 00 00 00 内存对齐补的0
	13 00 数据包头
	4D 49 00 01 6C 3D 4E 0A 接收者的id
	48 00 数据类型编号
	0F 00 数据个数
	04 00 00 04 00 00 00 72 00 00 00 发邮件的角色名
	04 00 01 21 00 00 00 35 38 41 39 34 42 43 30 43 44 39 42 34 38 42 34 39 36 33 46 46 37 38 39 46 46 30 42 30 41 35 37 00 校验码
	04 00 02 01
	04 00 03 00 8D 27 00 00 00 00 00
	04 00 04 01
	04 00 05 01
	04 00 06 10 00 00 00 71 00 31 00 71 00 31 00 71 00 31 00 31 00 00 00 邮件主题
	04 00 07 1C 00 00 00 1C 00 71 00 32 00 71 00 32 00 71 00 32 00 71 00 32 00 71 00 32 00 71 00 1D 00 00 00 邮件内容
	04 00 08 02 00 00 00 30 00
	04 00 09 42 00 00 00 3C 50 6F 73 74 20 67 6F 6C 64 3D 22 30 22 20 74 72 61 64 65 3D 22 30 22 20 74 69 6D 65 3D 22 32 30 32 34 2D 36 2D 31 32 20 32 30 3A 34 34 3A 35 30 22 20 75 72 67 65 6E 74 3D 22 31 22 20 2F 3E 0A 00 xml的东西里面有时间、是否加急、金币、物品的信息
	04 00 0A 13 00 00 00 32 30 32 34 2D 36 2D 31 32 20 32 30 3A 34 34 3A 35 30 00
	04 00 0B 00
	04 00 0C 21 00 00 00 42 37 30 45 33 41 35 46 41 36 30 41 34 37 42 30 39 38 32 46 43 46 37 35 37 31 44 39 32 34 32 46 00 校验码
	04 00 0D 02 00 00 00 30 00
	04 00 0E 01
*/

typedef struct NR_STRUCT_UPDATE {
	char un[6];
	char op = 0x29;
	char optype;
	union {
		long long lId;
		unsigned Index[2];
	};
	short StructIndex;
	short Count;
	char buff[2];
}*PNR_STRUCT_UPDATE;

/*
	29 00
	9A 49 00 01 B1 5C 8C 0A
	48 00 表示48号结构体
	00 00 从什么位置开始修改
	04 00 修改几个

	11 00
	4D 49 00 01 6C 3D 4E 0A
	48 00
	04 00 从什么位置开始
	01 00 更新几个数据

	14 00 数据包头
	C3 45 00 01 4F 32 E7 0A 应该是人物id
	08 00 结构体序号

	12 00 数据包头
	AF 49 00 01 DA 03 50 0A 应该是人物id
	48 00 结构体序号
	01 00 删除的邮件序号

*/

typedef struct NR_STRUCT_INIT {
	char un[6];
	char op = 0x29;
	char optype;
	union {
		long long lId;
		unsigned Index[2];
	};
	short StructIndex;
	union {
		short Start;
		short DelIndex;
	};
	short Count;
	char buff[2];
}*PNR_STRUCT_INIT;

/*
	17 op
	3D 00 背包编码(商店也是属于背包的一种) MainIndex
	01 00 参数个数 count
	FB 01 buff[2]
	0F 00 00 00 53 68 6F 70 5F 79 73 5F 71 68 5F 30 30 31 00 参数
*/
typedef struct NR_GAMEBASE_SET {
	char un;
	char op;
	short MainIndex;
	unsigned short count;
	char buff[2];

}*PNR_GAMEBASE_SET;

/*
	交换物品的数据包
    00 为了内存对齐补个0
	2B 
	02 00 
	04 00 
	06 00

	丢弃接收的数据包
	00 为了内存对齐补个0
	19
	02 00
	04 00
*/
typedef struct NR_GAMEBASE_EXCHANGE {
	char un;
	char op = S_GAMEBASE_EXCHANGE;
	short MainIndex;
	union {
		short IndexFrom;
		short Index;
	};
	short IndexTo;
}NR_GAMEBASE_DROP, *PNR_GAMEBASE_DROP, *PNR_GAMEBASE_EXCHANGE;

/*
 	00 为了内存对齐补个0
	15 
	40 00 
	0E 00 
	01 00 
	FA 01 0E 00 00 00 
 ------------------------------------------
	00 为了内存对齐补个0
	18
	28 00
	13 00
	05 00
	BC 01
	E8 03 00 00 61
	01 12 00 00 00 53 6B 69 6C 6C 5F 47 6C 31 32 34 30 5F 30 31 5F 31 00
	F8 01 01 00
	07 00 01 00 00 00
	B6 00 20 4E 00 00
*/
typedef struct NR_GAMEBASE {// 15与18数据包的头部,当前用于技能与装备的结构
	char un;
	char op;
	short type;
	union 
	{
		short index;
		short max;
	};
	short count;
}NR_GAMEBASE_RESET, *PNR_GAMEBASE, *PNR_GAMEBASE_RESET;
/*
	1F
	87 44 00 01 7F B0 D6 05
	FE AD DE C3
	6C F5 46 42
	07 FD 36 C4
	90 D9 9C 40
*/
typedef struct NR_UPDATECOORD {
	char un[6];
	char len = sizeof(NR_UPDATECOORD) - 7;
	char op = S_UPDATECORDEX;
	long long lId;
	float x;
	float h;
	float y;
	float face;
}*PNR_UPDATECOORD;

/*
	31 
	01 00 个数
	9B 49 00 01 D5 8C 98 05 id
	内存对齐
	00 00 00 00 00 31 01 00 // 补五个0与id的8字节对齐
	9B 49 00 01 D5 8C 98 05 
*/
typedef struct NR_OBJECT_REMOVEMU {
	char un[4];
	char len;
	char op = 0x31;
	short objCount = 0;
	long long lId[2];// 这里是一个指针,[]里写什么都行
}*PNR_OBJECT_REMOVEMU;
/*
	角色怪物数据更新结构 2D和2E
	00 00 00 00 00 2D 01 00
	9B 49 00 01 D5 8C 98 05
	02 00
	25 00 18 00 00 00
	29 00 BA 01 00 00
*/
typedef struct NR_OBJECT_UPDATEPROMU {
	char un1[4]; // 为了向id数据的8字节对齐
	char len;
	char op = 0x2D;
	short objCount = 0;
	long long lId;
	short icount;
	short itype;
}NR_OBJECT_UPMU, * PNR_OBJECT_UPMU;

/*角色怪物基本信息更新 接收的10数据包结构体*/
/*
	00 00 为了内存对齐补零
	10 00
	78 48 00 01 FC 61 79 05
	01 00
	29 00 65 09 00 00
*/
typedef struct NR_OBJECT_UPDATEPRO {
	char un1[5]; // 为了向id数据的8字节对齐
	char len;
	char op = 0x10;
	char MsgId = 0x00;
	union
	{
		long long lId;
		unsigned Index[2];
	};
	short icount = 1;
	short itype;
	union
	{
		char buff[0x04];
		int lenth;
	};
	char buffEnd[0x196];
}NR_OBJECT_UP, *PNR_OBJECT_UP;
/*
  角色怪物初始化结构
	00 00 00 00 00 30 0B 00
	82 0B 00 01 82 0B 00 00
	69 59 F8 C3
	67 49 FF 41
	FE 55 9C 42
	C6 70 BA 40
	A8 8F 07 C4
	06 52 58 41
	3B D0 2A 43
	C6 70 BA 40
	00 00 20 41
	00 00 00 00
	00 00 00 00
	00 00 00 00
	04 00 00 00
	11 00
*/

typedef struct NR_OBNJECT_INITEX {
	char un1[4];
	char len = sizeof(NR_OBNJECT_INITEX) - 7;
	char op = 0x30;
	short obj_count;
	long long lId;
	float x;
	float h;
	float y;
	float face;
	float tx;
	float th;
	float ty;
	float tface;
	int State;
	float un2[4]{};
	short icount;
	short type;
}*PNR_OBJINITEX;

// 角色初始化数据
/*
	28  op
	E6 44 00 01 39 0A 63 04 lId
	6F 3A 07 C4 x
	49 C8 81 41 h
	46 1E 40 44 y
	BC 74 03 40 face
	6F 3A 07 C4 tx
	49 C8 81 41 th
	46 1E 40 44 ty
	BC 74 03 40 tface
	00 00 00 00 un[0]
	00 00 00 00 un[1]
	00 00 00 00 un[2]
	00 00 00 00 un[3]
	00 00 00 00 un[4]
	5E 00 icount

	多出来的type是为了内存对齐才写的
*/
typedef struct NR_OBNJECT_INIT {
	char un1[6];
	char len = sizeof(NR_OBNJECT_INIT) - 9;
	char op = 0x28;
	long long lId;
	float x;
	float h;
	float y;
	float face;
	float tx;
	float th;
	float ty;
	float tface;

	float un2[5]{};
	short icount;
	short type;
}*PNR_OBJINIT;
// 保存结构数据描述信息的结构体
typedef struct STRUCT_DESC {
	char* name;
	int count;// 为了内存对齐这里使用int类型
	char* buff;
	short* offset;
}*PSTRUCT_DESC;
// 保存对象数据描述信息的结构体(游戏数据类型表)
typedef struct OBJECT_DESC {
	char* name;
	int type;
}*POBJ_DESC;

// 数据头结构体 数据结构约定
// 也就是接收到1E包的头部
typedef struct NR_NOTICE_HEAD {
	char un;
	char op;
	short count;
}NR_HEAD, *PNR_HEAD, *PNR_NOTICE_HEAD;

// 选择角色头部
typedef struct NET_SEND_SELECTROLE {
	char op = 0x04;
	char buff[83]{};
}NS_SELECTROLE, * PNS_SELECTROLE;

// 创建角色数据头部
typedef struct NET_SEHD_CREATEROLE_HEAD {
	/*
		char un[0x2];
		char len = sizeof(NET_SEHD_CREATEROLE_HEAD) - 0x03;
		这俩代码是为了解决内存对齐的问题,创建角色给服务端发送的
		05开头的数据包,把05变成00 00 00 05
	*/
	char un[0x2];
	char len = sizeof(NET_SEHD_CREATEROLE_HEAD) - 0x03;
	char op = 0x05;
	int index = 0x0; // 选择角色列表索引
	/*
		宽字节是2字节0x10是16两个是32,正好满足
		给服务端发送的创建角色数据包,也就是05开头的数据包
	*/
	wchar_t name[0x20];
	short unst = 0x00;
	short count = 0x08;
}*PNS_CREATEROLE;

// 基类
typedef class NET_SEND_BASE {
public:
	// 实现编码操作,模拟游戏数据包给服务端发送数据,这个模拟的数据包通过调用 CodeMe函数得到
	unsigned CodeMe(int size, char* buffer);
}NSR_BASE, *PNET_SEND_BASE, * PNSR_BASE;

typedef struct NS_CREATEROLE_HEAD_BUFF :public NET_SEHD_CREATEROLE_HEAD {
	char buff[0x300]{};
}*PNS_CREATEROLEBUFF;

// 解析约定结构体
// 创建角色数据结构体
typedef class CREATE_ROLE_DATAS:public NET_SEND_BASE {
public:
	GDOUBLE sex;//1.000000 性别 0 男 1 女
	GDOUBLE camp;//1.000000 阵营 1 艾森赫特 2 格兰蒂尔
	GDOUBLE face;//1.000000 种族 1 布冯特人 3 格洛玛人 4 尤恩图人 6 喀什人
	GDOUBLE occu;//3.000000 职业 1 仲裁者 3秘法师 6 猎魔人 8 元素法师
	GCHAR Photo;//gui\BG_team\TeamRole\Teamrole_zq_humF_001.PNG
	GCHAR Infos;//Face,0;Hat,0;Eyes,0;Beard,0;Ears,0;Tail,0;Finger,0;Cloth,0;Pants,0;Gloves,0;Shoes,0;Trait,0;HairColor,0;SkinColor,0;SkinMtl,0;Tattoo,0;TattooColor,16777215;
	GCHAR Txt;// 细节编辑捏脸
	GDOUBLE faceSahpe;//0.000000 脸型 0 1 2 3
}* PCREATE_ROLE_DATAS;


// 发送数据标准头 0xA数据包
typedef struct NET_SEND_HEAD {
	// 为了内存对齐,当前结构不可能超过255,所以这样写是没问题的
	char len = sizeof(NET_SEND_HEAD) - 1;
	char op = 0x0A;
	short unst = 0;
	int unnt[4]{};
	short unst1 = 0;
	short count = 0;
}*PNET_SEND_HEAD;
// 开始创建角色的数据结构
// 申请创建角色结构体
typedef struct NET_CREATEROLE_START {
	char un[0x02];// 用来做内存对齐
	char len = sizeof(NET_CREATEROLE_START) - 3;// 用来做内存对齐
	char op = 0x03;
	int code = 0x01;
}*PNET_CREATEROLE_START;

// 删除角色数据结构
typedef struct DATA_DELROLE {
	char op;
	char buff[0x1F];
	int len;
	int un[8] = { 0x01 , 0x03 };

}*PDATADELROLE;

/*
	数据包还原结构体要注意内存对齐,如果数据不满4字节,它字段会补齐
	比如结构体里有一个char变量,它是1字节,在内存里它可能会为了内存对齐
	让它变成4字节,所以这要注意
*/
// 登录数据
typedef struct DATA_LOGIN {
	int op = 0x0300;
	char buff[0x10]{};
	int lenId = 0x10;
	/*
		这个是登录的账号,它可能会变成0x20或更长,现在默认让它0x10
		读的时候以长度为准就好了
	*/
	char Id[0x10]{};
	int lenPass = 0x10;
	/*
		这个是登录的密码,它可能会变成0x20或更长,现在默认让它0x10
		读的时候以长度为准就好了
	*/
	char Pass[0x10]{};
	int lenCode = 0x10;
	char Code[0x10]{};
	int eop = 0x01;
}*PDATALOGIN;
typedef struct DATA_LOGIN_OK {// 登录成功数据包头
	int un[8] = { 0, 0, 0x76B, 0x0C, 0x1E,0, 0, 0 };
	int index = 0;
	int RoleCount = 0;
}*PDATALOGINOK;

// 发送数据包含内存空间,也就是数据参数,数据参数的意思看44文章里的内容
typedef struct NET_SEND_BUFF:public NET_SEND_HEAD {
	char buff[0x1000]{};
}*PNET_SEND_BUFF;

typedef class  NET_SEND_CHEAD:public NET_SEND_BASE {
public:
	union {
		GCHAR msgHeader;
		GINT opcode;
	};
public:
	NET_SEND_CHEAD() {};
	~NET_SEND_CHEAD() {};
}NSR_CHEAD, *PNSR_CHEAD, *PNET_SEND_CHEAD;

// 选择角色
typedef class NET_SEND_CHOOSECAMP :public NET_SEND_CHEAD {
public:
	GCHAR camps;
}*PNS_CHOOSECAMP;

// 心跳包数据结构
typedef class HEART_BEEP :public NET_SEND_CHEAD {
public:
	// 20秒一次
	GINT tick;//3
}*PHEART_BEEP;

typedef class HEART_LOOP :public NET_SEND_CHEAD {
public:
	// 5秒一次
	GINT64 tick;//9320695
	GCHAR txt;//

}*PHEART_LOOP;

typedef struct ONLINE_HAND: public NSR_CHEAD{
	GINT step; // 2
	GINT state; // 1
}*PONLINE_HAND;

typedef struct ONLINE_INIT : public NSR_CHEAD {
	GINT state;//0
	GDOUBLE un;//180000000.000000
}*PONLINE_INIT;
// 登陆后角色数据区
// 聊天数据包的数据结构
typedef class CHAT_DATA :public NET_SEND_CHEAD {
public:
	GINT ChartId;//1
}*PCHAT_DATA;
typedef class CHAT_PUBLIC :public CHAT_DATA {
public:
	GUTF16 txt;//[222222222222222222222222222222222222222222<img src="Face37"  valign="bottom"  only="line"/>]
	GDOUBLE un;//0.000000
}*PCHAT_PUB;
typedef class CHAT_PRIVATE :public CHAT_DATA {
public:
	GUTF16 txt;//[222222222222222222222222222222222222222222<img src="Face37"  valign="bottom"  only="line"/>]
	GUTF16 name;//[皮革兔]
	GDOUBLE un;//0.000000
}*PCHAT_PRV;


// 登陆后角色数据包
typedef class ROLE_DATA {// 登录成功数据包
public:
	GBYTE byte;
	GINT index;
	GINT un1;
	GUTF16 name;
	GUTF16 infos;
	GINT un2;
	GINT64 un3;
}*PROLEDATA;

typedef class MOVE_DATA :public NET_SEND_CHEAD {
public:
	GINT Mode; // 0停止 1移动中 2跳跃
	GINT Count; // 带入场景这里就是一共有几个与坐标相关的数据
}*PMOVE_DATA;

// 停止移动
typedef class MOVE_DATA_STOP :public MOVE_DATA {
public:
	GFLOAT x;//-584.845459
	GFLOAT h;//65.004677
	GFLOAT y;//708.458740 
	GFLOAT face;//4.912942
}*PMOVE_DATA_STOP;

// 移动
typedef class MOVE_DATA_WALK :public MOVE_DATA {
public:
	GFLOAT x;//-584.845459 当前坐标 x
	GFLOAT h;//65.004677 当前坐标 h
	GFLOAT y;//708.458740 当前坐标 y
	GFLOAT xNext;//-596.604919 预计移动到的坐标
	GFLOAT yNext;//710.849304 预计移动到的坐标
	GFLOAT MoveSpeed;//5.000000 移动速度
	GFLOAT GSpeed;//0.000000 根据跳跃的数据包猜测它是重力加速(也就是坠落的速度)
	GFLOAT face;//4.912942 面向
}*PMOVE_DATA_WALK;

// 跳跃或往前跳(前后左右跳)
typedef class MOVE_DATA_JUMP:public MOVE_DATA {
public:
	GFLOAT x;//-599.624939
	GFLOAT h;//65.004105
	GFLOAT y;//711.464600
	GFLOAT xNext;//-599.624939
	GFLOAT yNext;//711.464600
	GFLOAT MoveSpeed;//5.000000
	GFLOAT JumpSpeed;//4.500000
	GFLOAT GSpeed;//9.800000 重力加速(也就是坠落的速度)
	GFLOAT face;//4.912942
}*PMOVE_DATA_JUMP;

// 移动时跳跃
typedef class MOVE_DATA_WJUMP :public MOVE_DATA {
public:
	GFLOAT x;//-394.622162
	GFLOAT h;//8.719633
	GFLOAT y;//912.655151
	GFLOAT xNext;//-400.995270
	GFLOAT yNext;//902.487366
	GFLOAT MoveSpeed;//50.000000
	GFLOAT face;//3.701483
}*PMOVE_DATA_WJUMP;

// 开始坠落
typedef class FALL_DATA_START :public NET_SEND_CHEAD {
public:
	GINT Mode;//890
	GINT StartH;//1
	GFLOAT NextH;//58.223557
	GINT EndH;//0

}*PFALL_DATA_START;

// 坠落结束
typedef class FALL_DATA_STOP :public NET_SEND_CHEAD {
public:
	GINT Mode;//2
	GFLOAT StartH;//58.223557
	GFLOAT NextH;//48.128914
	GFLOAT EndH;//24.581387
}*PFALL_DATA_STOP;

// 释放技能结构
typedef class USESKILL_DATA :public NET_SEND_CHEAD {
public:
	//GINT gint;//211
	GCHAR skillName;//Skill_In1025_01_1
	GFLOAT x;//-522.842712,猜测它是使用技能的坐标
	GFLOAT h;//8.122560,猜测它是使用技能的坐标
	GFLOAT y;//866.518250,猜测它是使用技能的坐标
	GFLOAT face;//5.872537 面向
	GFLOAT xTarget;//0.000000 释放技能的目标位置,也就是技能释放到哪
	GFLOAT hTarget;//0.000000 释放技能的目标位置,也就是技能释放到哪
	GFLOAT yTarget;//0.000000 释放技能的目标位置,也就是技能释放到哪
	GINT un0;//0
	GINT un1;//0
	GINT un2;//1
}*PUSESKILL;

/*
	装备替换
	GINT gint;//40
	GINT gint;//2
	GINT gint;//1
	GINT gint;//1
	GINT gint;//3
	装备装备
	(注意这个不是替换装备,这是比如帽子这个装备,然后当前身上并没有装备帽子,帽子在背包中,这时装备帽子就会是下方的数据包)
	GINT gint;//30
	GINT gint;//2
	GINT gint;//20
	GINT gint;//1
	GINT gint;//1
	物品数量拆分
	GINT gint;//33
	GINT gint;//2
	GINT gint;//3
	GINT gint;//2
	GINT gint;//18
	GINT gint;//3
	物品合并
	GINT gint;//40
	GINT gint;//2
	GINT gint;//18
	GINT gint;//2
	GINT gint;//3
	物品交换:物品与物品交换
	GINT gint;//40
	GINT gint;//2
	GINT gint;//6
	GINT gint;//2
	GINT gint;//5
	物品交换:物品与空位置交换
	GINT gint;//30
	GINT gint;//2
	GINT gint;//5
	GINT gint;//2
	GINT gint;//4
	使用物品
	GINT gint;//31 使用物品的操作码
	GINT gint;//2 背包编号
	GINT gint;//15 物品在背包中的位置
	丢弃物品
	GINT gint;//34 丢弃物品的操作码
	GINT gint;//2 背包编号
	GINT gint;//1 物品在背包中的位置
	GINT gint;//3 数量
*/
// 召唤或解除坐骑
typedef class MOUNT_DATA :public NET_SEND_CHEAD {
public:
	GINT Index;
}ITEM_PICK_DATA,*PMOUNT_DATA, *PMOUNT_DATA;
// 使用物品发送的数据包的结构
typedef class ITEMOP_DATA :public NET_SEND_CHEAD {// 物品丢弃、使用、拆分、合并交换相关数据包的结构
public:
	GINT MainIndex; // 背包编码
	GINT Index; // 物品所在背包的位置
}ITEM_USE_DATA, *PITEMOP_DATA, *PITEM_USE_DATA;
// 丢掉物品发送的数据包的结构
typedef class ITEM_DROP_DATA :public ITEMOP_DATA {
public:
	GINT Amount; // 物品的数量
}*PITEM_DROP_DATA;
// 交换物品
typedef class ITEM_EXCHANGE_DATA :public ITEMOP_DATA {
public:
	GINT TargetId; // 背包编号,在这里的意思是交换到什么背包里
	GINT TargetIndex; // 物品所在背包的位置,也就是物品在 TargetId 里什么位置
}*PITEM_EXCHANGE_DATA;
// 拆分物品
typedef class ITEM_SPLIT_DATA :public ITEMOP_DATA {
public:
	GINT TargetId; // 背包编号,在这里的意思是拆分到什么背包里
	GINT TargetIndex; // 物品所在背包的位置,也就是物品在 TargetId 里什么位置
	GINT TargetAmount; // 拆分的数量
}*PITEM_SPLIT_DATA;

/*
	GINT gint;//70 数据头
	GINT gint;//20 操作码
	GCHAR gchar;//Shop_ys_qh_001 商店id
	GINT gint;//1 商店物品页码
	GINT gint;//1
	GINT gint;//4 物品所在商店位置
	GINT gint;//1 数量
	GINT gint;//0
	GINT gint;//0
*/
typedef class ITEM_BUY_DATA : public NET_SEND_CHEAD {
public:
	GINT opMode;//20 操作码
	GCHAR ShopId;//Shop_ys_qh_001 商店id
	GINT ShopPage;//1 商店物品页码
	GINT ShopIndex;//1
	GINT Index;//4 物品所在商店位置
	GINT Amount;//1 数量
	GINT un1;//0
	GINT un2;//0
}*PITEM_BUY_DATA;

/*
	GINT gint;//70
	GINT gint;//21
	GINT gint;//0
	GCHAR gchar;//Shop_ZH_005
	GINT gint;//2
	GINT gint;//1
	GINT gint;//30
*/
typedef class ITEM_SOLD_DATA : public NET_SEND_CHEAD {
public:
	GINT opMode;//21
	GINT un1;//0
	GCHAR ShopId;//Shop_ZH_005
	GINT MainIndex;//2
	GINT Index;//1
	GINT Amount;//30
}*PITEM_SOLD_DATA;

/*
  打开邮箱窗口
	GINT gint;//350
	GINT gint;//3
	GCHAR gchar;//2C3B97C1C6E542AD90E540E6A4C8754C
  删除邮件
	GINT gint;//350
	GINT gint;//5
	GCHAR gchar;//B70E3A5FA60A47B0982FCF7571D9242F
*/
typedef class MAIL_OPERAION : public NET_SEND_CHEAD {
public:
	GINT optype;//3
	// GCHAR gchar;//2C3B97C1C6E542AD90E540E6A4C8754C
}*PMAIL_OPERAION;

// 邮件的读取删除
typedef class MAIL_OPERAION_RD : public MAIL_OPERAION {
public:
	GCHAR Guid;//2C3B97C1C6E542AD90E540E6A4C8754C
}*PMAIL_OPERAION_RD;

/*
  附件收取
	GINT gint;//350
	GINT gint;//8
	GCHAR gchar;//6AB6F03436154524A73CFF5C12D6691C
	GINT gint;//-1
*/
typedef class MAIL_OPERAION_GET : public MAIL_OPERAION_RD {
public:
	GINT opother;//-1
}*PMAIL_OPERAION_GET;

/*
  发送邮件
	GINT gint;//350
	GINT gint;//2
	GINT gint;//1
	GUTF16 gutf16;//[r]
	GUTF16 gutf16;//[1111111111111111111]
	GUTF16 gutf16;//[333333333333333333333333333]
	GINT64 gint64;//0
	GINT gint;//1
*/
typedef class MAIL_OPERAION_POST : public MAIL_OPERAION {
public:
	GINT MailType;//1
	GUTF16 Target;//[r]
	GUTF16 Title;//[1111111111111111111]
	GUTF16 Txt;//[333333333333333333333333333]
	GINT64 Money;//0 金钱
	GINT Urgent;//1
}*PMAIL_OPERAION_POST;







CWndMailBox.cpp文件的修改:新加 ReadMode函数、PostMode函数、OnBnClickedCancel函数、OnBnClickedOk函数

// CWndMailBox.cpp: 实现文件
//

#include "pch.h"
#include "CWndMailBox.h"
#include "afxdialogex.h"
#include "resource.h"
#include "extern_all.h"

// CWndMailBox 对话框

IMPLEMENT_DYNAMIC(CWndMailBox, CDialogEx)

CWndMailBox::CWndMailBox(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_EMIAL, pParent)
	, target(_T(""))
	, title(_T(""))
	, txt(_T(""))
	, Money(0)
	, txtItem(_T(""))
	, B_Urgent(FALSE)
	, B_Pay(FALSE)
{

}

CWndMailBox::~CWndMailBox()
{
}

void CWndMailBox::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, target);
	DDX_Text(pDX, IDC_EDIT4, title);
	DDX_Text(pDX, IDC_EDIT5, txt);
	DDX_Text(pDX, IDC_EDIT7, Money);
	DDX_Text(pDX, IDC_EDIT6, txtItem);
	DDX_Control(pDX, IDC_COMBO1, cmbBag);
	DDX_Check(pDX, IDC_CHECK3, B_Urgent);
	DDX_Check(pDX, IDC_CHECK2, B_Pay);
	DDX_Control(pDX, IDOK, btnSure);
	DDX_Control(pDX, IDCANCEL2, btnDel);
}


BEGIN_MESSAGE_MAP(CWndMailBox, CDialogEx)
	ON_BN_CLICKED(IDCANCEL, &CWndMailBox::OnBnClickedCancel)
	ON_BN_CLICKED(IDOK, &CWndMailBox::OnBnClickedOk)
END_MESSAGE_MAP()


// CWndMailBox 消息处理程序



void CWndMailBox::ReadMode()
{
	if (B_Pay) {
		btnSure.SetWindowTextW(L"付费");
		btnDel.SetWindowTextW(L"拒收");
	}
	else {
		btnSure.SetWindowTextW(L"收件");
		btnDel.SetWindowTextW(L"删除");
	}
}

void CWndMailBox::PostMode()
{
}


void CWndMailBox::OnBnClickedCancel()
{
	Client->MailBox(SC_MAIL_CLOSEMAIL);
	CDialogEx::OnCancel();
}


void CWndMailBox::OnBnClickedOk()
{
	if (B_Pay) {
		Client->Mail(Guid, SC_MAIL_PAY);
	}
	else {
		Client->MailEx(Guid);
	}

	CDialogEx::OnOK();
}

CWndMailBox.h文件的修改:新加 Guid变量、btnSure变量、btnDel变量、ReadMode函数、PostMode函数、OnBnClickedCancel函数(退出按钮点击事件)、OnBnClickedOk函数(付费/收取附件按钮点击事件)、,删除 OnActivate函数

#pragma once
#include "afxdialogex.h"

// CWndMailBox 对话框

class CWndMailBox : public CDialogEx
{
	DECLARE_DYNAMIC(CWndMailBox)

public:
	CWndMailBox(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CWndMailBox();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_EMIAL };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	CString target;// 目标名称
	CString title;// 邮件标题
	CString txt;// 邮件内容
	long long Money;// 泰坦(或者称为 金钱)
	CString txtItem;// 物品
	CComboBox cmbBag;// 包裹
	BOOL B_Urgent;// 加急邮件
	BOOL B_Pay;// 付费邮件

	CStringA Guid;

	afx_msg void OnBnClickedCancel();
	CButton btnSure;
	// 删除按钮
	CButton btnDel;

	void ReadMode();
	void PostMode();
	afx_msg void OnBnClickedOk();
};

CUI.cpp文件的修改:重写 OnSvrGameBaseed函数

// CUI.cpp: 实现文件
//

#include "pch.h"
#include "htdMfcDll.h"
#include "CUI.h"
#include "afxdialogex.h"
#include "extern_all.h"
// CUI 对话框

IMPLEMENT_DYNAMIC(CUI, CDialogEx)

CUI::CUI(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_MAIN, pParent)
	, txtDetails(_T(""))
	, txtChat(_T(""))
	, txtChatName(_T(""))
{

}

CUI::~CUI()
{
}

void CUI::SetListView(CListCtrl* lst)
{
	auto lStyle = GetWindowLongPtr(lst->m_hWnd, GWL_STYLE); // 获取窗口样式
	lStyle |= LVS_REPORT; // 设置为报表模式
	SetWindowLongPtr(lst->m_hWnd, GWL_STYLE, lStyle);// 给窗口设置样式
	auto dStyle = lst->GetExtendedStyle(); // 获取扩展样式
	dStyle |= LVS_EX_FULLROWSELECT; // 设置选择时选择一行
	dStyle |= LVS_EX_GRIDLINES; // 画网格线
	lst->SetExtendedStyle(dStyle); // 设置扩展样式
}

void CUI::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_TAB1, mTab);
	DDX_Control(pDX, IDC_LIST1, lstlog);
	DDX_Text(pDX, IDC_EDIT1, txtDetails);
	DDX_Text(pDX, IDC_EDIT3, txtChat);
	DDX_Text(pDX, IDC_EDIT2, txtChatName);
	DDX_Control(pDX, IDC_COMBO2, cmbChat);
}

BOOL CUI::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	
	SetListView(&lstlog);

	InstallPage(new CUIWnd_0(this), IDD_PAGE_0, L"角色", TRUE);
	InstallPage(new CUIWnd_1(this), IDD_PAGE_1, L"周围");
	InstallPage(new CWndSet(), IDD_PAGE_2, L"设置");
	InstallPage(new CWndItem(), IDD_PAGE_3, L"装备");
	InstallPage(new CWndSkill(), IDD_PAGE_4, L"技能");
	InstallPage(new CWndMail(), IDD_DIALOG2, L"邮件");

	lstlog.InsertColumn(0, L"消息", 0, 70);
	lstlog.InsertColumn(1, L"内容", 0, 700);
	lstlog.InsertColumn(2, L"时间", 0, 200);

	//PageINJ.Init(wAppPath);
	//PageRAN.SetAppPath(wAppPath);

	for (CString & txt : ChatPdName)
	{
		cmbChat.AddString(txt);
	}
	cmbChat.SetCurSel(0);
	return TRUE;
}

bool CUI::InstallPage(CDialogEx* wnd, int IDD_WND, CString&& _Name, BOOL IsShow)
{

	if (CurPage >= (sizeof(WNDS) / sizeof(CDialogEx*))) return false;
	Pages[CurPage] = wnd;
	Pages[CurPage]->Create(IDD_WND, this);
	//Pages[CurPage]->SetParent(this);
	Pages[CurPage]->ShowWindow(IsShow);

	CRect rect;
	mTab.GetClientRect(&rect);
	rect.top += 32;
	rect.left += 5;
	rect.bottom -= 4;
	rect.right -= 5;
	Pages[CurPage]->MoveWindow(&rect);
	mTab.InsertItem(CurPage, _Name);

	CurPage++;
	return true;
}

BEGIN_MESSAGE_MAP(CUI, CDialogEx)
	ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &CUI::OnTcnSelchangeTab1)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_BUTTON1, &CUI::OnBnClickedButton1)
END_MESSAGE_MAP()


// CUI 消息处理程序


void CUI::OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{
	// TODO: 在此添加控件通知处理程序代码
	*pResult = 0;
	int n = mTab.GetCurSel();
	if (lastPage != n) LeavePage(n);
	InPage(n);
	for (int i = 0; i < CurPage; i++)
	{
		
		lastPage = n;
		Pages[i]->ShowWindow(i == n);
	}
}

PTextManger CUI::getTxtManger()
{
	if (!txtManger)txtManger = new TextManger("F:\\语言包.txt");
	return txtManger;
}

void CUI::loginok(ROLE_DATA* _roles, int count)
{
	PushLog(LOGTYPE::SYS, L"账号登录成功");
	NetClient::loginok(_roles, count);
	wnds.wndRole->lstRole.DeleteAllItems();
	CString txtInfos;
	for (int i = 0; i < count; i++)
	{
		txtInfos = _roles[i].infos.value();
		txtInfos = txtInfos + L";";
		CString txtIndex, txtSex, txtMap, txtJob, txtCamp, txtRace, txtLv;
		CStringA stxtMap, stxtJob, stxtCamp, stxtRace;
		txtSex = ReadValue(txtInfos, L"1,", L";");
		txtMap = ReadValue(txtInfos, L"56,", L";");
		txtJob = L"job_" + ReadValue(txtInfos, L"37,", L";");
		txtCamp = L"camp_" + ReadValue(txtInfos, L"35,", L";");
		txtRace = L"race_" + ReadValue(txtInfos, L"36,", L";");
		txtLv = ReadValue(txtInfos, L"38,", L";");
		txtIndex.Format(L"%d", _roles[i].index.value());

		stxtMap = txtMap;
		stxtJob = txtJob;
		stxtCamp = txtCamp;
		stxtRace = txtRace;
		txtSex = SexName[txtSex == L"1"];
		wnds.wndRole->lstRole.InsertItem(0, txtIndex);
		wnds.wndRole->lstRole.SetItemText(0, 1, _roles[i].name);
		wnds.wndRole->lstRole.SetItemText(0, 2, txtLv);
		wnds.wndRole->lstRole.SetItemText(0, 3, txtSex);
		wnds.wndRole->lstRole.SetItemText(0, 4, getTxtManger()->ReadTextById(stxtCamp.GetBuffer()));
		wnds.wndRole->lstRole.SetItemText(0, 5, getTxtManger()->ReadTextById(stxtMap.GetBuffer()));
		wnds.wndRole->lstRole.SetItemText(0, 6, getTxtManger()->ReadTextById(stxtRace.GetBuffer()));
		wnds.wndRole->lstRole.SetItemText(0, 7, getTxtManger()->ReadTextById(stxtJob.GetBuffer()));
	}
}

bool CUI::Tips(int code)
{
	CString logName;
	logName.Format(L"服务器提示:%d", code);
	PushLog(LOGTYPE::TIPS, logName.GetBuffer());
	// NetClient::Tips(code);
	return true;
}

bool CUI::OnInited()
{
	SetTimer(0x10001, 50, NULL);
	return true;
}

bool CUI::OnSvrChat(PCHAT_PRV _coder)
{
	
	CString txt;
	CString txtPd;
	CString txtName = _coder->name;
	CString txtInfo = _coder->txt;
	txt.Format(L"%d", _coder->ChartId.value());
	AfxMessageBox(txt);

	switch (_coder->ChartId)
	{
	case 1:// 附近频道
		txtPd = L"附近";
		break;
	case 2:// 区域频道
		txtPd = L"区域";
		break;
	case 3:// 私聊
		txtPd = L"接收的私聊";
		// return OnChatPrivate((PCHAT_PRV)_coder);
		break;
	case 6:// 公会频道
		txtPd = L"公会";
		break;
	case 9:// 阵营频道
		txtPd = L"阵营";
		break;
	case 21:// 喊话频道
		txtPd = L"喊话";
	    // return OnChatPublic((PCHAT_PUB)_coder);
	    break;
	case 103:// 喊话频道
		txtPd = L"发送的私信";
	    // return OnChatPublic((PCHAT_PUB)_coder);
	    break;
	}

	txt.Format(L"[%s][%s][%s]", txtPd, txtInfo, txtName);
	PushLog(LOGTYPE::CHAT, txt.GetBuffer());
	return true;
}

bool CUI::OnFall(PFALL_DATA_START _coder)
{
	NetClient::OnFall(_coder);

	return !wnds.wndSet->setValue[(unsigned)SETNAME::NeverFall];
}

bool CUI::OnMoveWalk(PMOVE_DATA_WALK _coder)
{
	if (wnds.wndSet->setValue[(unsigned)SETNAME::AddSpeed])
	{
		float* MoveSpeed = (float*)_coder->MoveSpeed.oldPointer;
		MoveSpeed[0] = wnds.wndSet->OldSpeed;
	}

	return true;
}

bool CUI::OnMoveJump(PMOVE_DATA_JUMP _coder)
{
	if (wnds.wndSet->setValue[(unsigned)SETNAME::AddSpeed])
	{
		float* MoveSpeed = (float*)_coder->MoveSpeed.oldPointer;
		MoveSpeed[0] = wnds.wndSet->OldSpeed;
	}

	return true;
}

bool CUI::OnMoveWJump(PMOVE_DATA_WJUMP _coder)
{
	if (wnds.wndSet->setValue[(unsigned)SETNAME::AddSpeed])
	{
		float* MoveSpeed = (float*)_coder->MoveSpeed.oldPointer;
		MoveSpeed[0] = wnds.wndSet->OldSpeed;
	}

	return true;
}

void CUI::OnSvrGameBaseed(short structIndex, short index)
{
	if (structIndex == (short)OBJECT_TYPE::MailRecv) {
		if (index > 0) {
			CString tmp;
			tmp.Format(L"%d(%d个)", MailRecv.item[index]->Name, MailRecv.item[index]->Amount);
			wnds.wndMail->wndMailBox.txtItem.Format(L"%s(%d个)", MailRecv.item[index]->Name, MailRecv.item[index]->Amount);
			wnds.wndMail->wndMailBox.UpdateData(FALSE);
		}
	}
}

CString CUI::ReadValue(CString& txt, wchar_t* key, wchar_t* endStr)
{
	CString result = L"";
	int iStart = txt.Find(key);
	if (iStart > -1) {
		iStart = iStart + wcslen(key);
		int iend = txt.Find(endStr, iStart);
		if (iend > -1)result = txt.Mid(iStart, iend - iStart);
	}
	return result;
}

void CUI::PushLog(LOGTYPE type, wchar_t* txt)
{
	struct tm newtiem {};
	time_t t;
	time(&t);
	localtime_s(&newtiem, &t); // 获取时间
	CString logName;
	logName.Format(L"%.4d-%.2d-%.2d %.2d:%.2d:%.2d", newtiem.tm_year + 1900, newtiem.tm_mon + 1, newtiem.tm_mday, newtiem.tm_hour, newtiem.tm_min, newtiem.tm_sec);

	lstlog.InsertItem(0, MsgName[(int)type]);
	lstlog.SetItemText(0, 1, txt);
	lstlog.SetItemText(0, 2, logName);
}


void CUI::OnTimer(UINT_PTR nIDEvent)
{
	// wnds.wndAIM->UI();
	CStringA tmp;
	tmp.Format("%d", Player.CurArea);
	txtDetails.Format(L"lv.%d.%s 生命值[%d/%d] 经验值[%d/%d] 当前所在场景[%s](%.2f|%.2f|%.2f)", Player.Level, Player.Name,  Player.HP, Player.MaxHP + Player.MaxHPAdd, Player.PlayerExp, Player.PlayerUpgradeExp, getTxtManger()->ReadTextById(tmp.GetBuffer()), Player.x, Player.h, Player.y);
	SetDlgItemText(IDC_EDIT1, txtDetails.GetBuffer());
	// UpdateData(FALSE);
	__super::OnTimer(nIDEvent);
}


void CUI::OnBnClickedButton1()
{
	int nsel = cmbChat.GetCurSel();
		UpdateData(TRUE);
	if (nsel > -1) {
		if (ChatPdId[nsel] != -1) {
			Talk(txtChat.GetBuffer(), ChatPdId[nsel]);
		}
		else {
			TalkTo(txtChatName.GetBuffer(), txtChat.GetBuffer());
		}
		txtChat = L"";
	}
		SetDlgItemText(IDC_EDIT3, txtChat.GetBuffer());

}

void CUI::LeavePage(int id)
{
	if (id != 5) {
		CloseMailBox();
	}
}

void CUI::InPage(int id)// 打开
{
	if (id == 5) {
		OpenMailBox();
	}
}

CUI.h文件的修改:重写 OnSvrGameBaseed函数

#pragma once
#include "afxdialogex.h"
#include "NetClient.h"
#include "TextManger.h"
//增加页面头文件
#include "CUIWnd_0.h"
#include "CUIWnd_1.h"
#include "CWndSet.h"
#include "CWndItem.h"
#include "CWndSkill.h"
#include "CWndMail.h"

//游戏辅助UI类
// CUI 对话框

enum class LOGTYPE {
	TIPS = 0,
	SYS = 1,
	CHAT = 2,
	MAX
};

typedef struct WNDS {
	CUIWnd_0* wndRole;
	CUIWnd_1* wndAIM;
	CWndSet* wndSet;
	CWndItem* wndItem;
	CWndSkill* wndSkill;
	CWndMail* wndMail;
}*PWNDS;

// #define MAX_PAGE_MAIN 3

// 这里用了多重继承,这回有一个问题,函数名一样的会发生冲突
// 所以在继承的时候要注意函数名
class CUI : public CDialogEx,public NetClient
{
	DECLARE_DYNAMIC(CUI)

public:
	CUI(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CUI();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_MAIN };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
	union {
		CDialogEx* Pages[sizeof(WNDS)/sizeof(CDialogEx*)];
		WNDS wnds;
	};
	
	short      CurPage = 0;
public:
	CTabCtrl mTab;
	virtual BOOL OnInitDialog();
	bool    InstallPage(CDialogEx* wnd, int IDD_WND, CString&& _Name, BOOL IsShow=FALSE);
	afx_msg void OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult);
public:
	void SetListView(CListCtrl* lst);
	PTextManger getTxtManger();
	CListCtrl lstlog;
protected:
	CString MsgName[(unsigned int)LOGTYPE::MAX]{
		L"错误",
		L"系统",
		L"聊天"
	};

	CString SexName[2]{
		L"男",
		L"女"
	};

	CString ChatPdName[6]{
		L"附近",
		L"区域",
		L"公会",
		L"阵营",
		L"喊话",
		L"私信"
	};

	int ChatPdId[6]{
		1, 2, 6, 9, 21, -1
	};

	void PushLog(LOGTYPE type, wchar_t* txt);
protected:
	void virtual loginok(ROLE_DATA* _roles, int count);
	bool virtual Tips(int code);
	bool virtual OnInited();
	bool virtual OnSvrChat(PCHAT_PRV _coder);
	//  分发坠落函数
	bool virtual OnFall(PFALL_DATA_START _coder);
	// 移动中处理函数
	bool virtual OnMoveWalk(PMOVE_DATA_WALK _coder);
	// 跳跃处理函数
	bool virtual OnMoveJump(PMOVE_DATA_JUMP _coder);
	// 移动时跳跃
	bool virtual OnMoveWJump(PMOVE_DATA_WJUMP _coder);
	// 用于拦截18 24,为了用于得到邮件里的物品写的
	void virtual OnSvrGameBaseed(short structIndex, short index);
	// 解析角色的信息,性别、种族、阵营等
	CString ReadValue(CString&txt, wchar_t* key, wchar_t* endStr);
public:
	CString txtDetails;
	afx_msg void OnTimer(UINT_PTR nIDEvent);
	CString txtChat;
	CString txtChatName;
	afx_msg void OnBnClickedButton1();
	CComboBox cmbChat;

	int lastPage = -1;
	void LeavePage(int id); // 发送关闭邮箱数据包
	void InPage(int id); // 发送打开邮箱数据包
};


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1858121.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习300问】130、什么是Seq2Seq?又叫编码器(Encoder)和解码器(Decoder)。

Seq2Seq&#xff0c;全称为Sequence to Sequence&#xff0c;是一种用于处理序列数据的神经网络模型&#xff0c;特别适用于如机器翻译、语音识别、聊天机器人等需要将一个序列转换为另一个序列的任务。这种模型由两部分核心组件构成&#xff1a;编码器&#xff08;Encoder&…

智源更新大模型排行榜:豆包大模型“客观评测”排名国产第一

6月中旬&#xff0c;智源研究院旗下的 FlagEval 大模型评测平台发布最新榜单&#xff1a;在有标准答案的“客观评测”中&#xff0c;GPT-4 以76.11分在闭源大模型中排名第一&#xff1b;Doubao-Pro&#xff08;豆包大模型&#xff09;以75.96分排名第二&#xff0c;同时也是得分…

lvs集群 Keepalived

Keepalived高可用集群 Keepalived概述 功能 LVS规则管理LVS集群真实服务器状态监测管理VIP Keepalived实现web高可用 安装keepalived软件 在webservers上配置 启动服务 webservers systemctl start keepalived.service ip a s | grep 192.168 #web1主机绑定vip 测试…

《互联网政务应用安全管理规定》深度解读

《互联网政务应用安全管理规定》的出台&#xff0c;对互联网政务应用的安全提出了一系列具体要求。 2024年5月15日&#xff0c;中央网信办、中央编办、工业和信息化部、公安部等四部门联合公布《互联网政务应用安全管理规定》&#xff08;以下称《规定》&#xff09;&#xff…

Windows资源管理器down了,怎么解

ctrlshiftesc 打开任务管理器 文件 运行新任务 输入 Explorer.exe 资源管理器重启 问题解决 桌面也回来了

Java数据结构4-链表

1. ArrayList的缺陷 由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后搬移&#xff0c;时间复杂度为O(n)&#xff0c;效率比较低&#xff0c;因此ArrayList不适合做任意位置插入和删除比较多的场景…

使用 Reqable 在 MuMu 模拟器进行App抓包(https)

1、为什么要抓包&#xff1f; 用开发手机应用时&#xff0c;查看接口数据不能像在浏览器中可以直接通过network查看&#xff0c;只能借助抓包工具来抓包&#xff0c;还有一些线上应用我们也只能通过抓包来排查具体的问题。 2、抓包工具 实现抓包&#xff0c;需要一个抓包工具…

鸿蒙NEXT,保障亿万中国老百姓数据安全的操作系统

吉祥学安全知识星球&#x1f517;除了包含技术干货&#xff1a;Java代码审计、web安全、应急响应等&#xff0c;还包含了安全中常见的售前护网案例、售前方案、ppt等&#xff0c;同时也有面向学生的网络安全面试、护网面试等。 上周华为发布了最新的鸿蒙NEXT操作系统&#xff0…

用Vue3打造一个交互式营养追踪仪表盘

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 Vue.js 构建营养仪表盘卡片 应用场景介绍 营养仪表盘卡片是一个可视化工具&#xff0c;用于跟踪个人的营养摄入情况。它通常包含以下信息&#xff1a; 卡路里摄入目标和进度营养成分&#xff08;如脂肪…

SwiftUI 6.0(iOS 18/macOS 15)关于颜色 Color 的新玩法

概览 WWDC 2024 重装升级的 SwiftUI 6.0 让 Apple 不同平台&#xff08;iOS 18/macOS 15&#xff09;显得愈发的冰壶玉衡、美轮美奂。 之前梦寐以求的颜色混合功能在 WWDC 24 里终于美梦成真啦&#xff01; 在本篇博文中&#xff0c;您将学到如下内容&#xff1a; 概览1. 梦想…

【CentOS 7】深入指南:使用LVM和扩展文件系统增加root分区存储容量

【CentOS 7】深入指南&#xff1a;使用LVM和扩展文件系统增加root分区存储容量 大家好 我是寸铁&#x1f44a; 【CentOS 7】深入指南&#xff1a;使用LVM和扩展文件系统增加root分区存储容量 ✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 在运行CentOS 7服务器或虚拟机时&a…

每日一题——Python代码实现PAT甲级1006 Sign In and Sign Out(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 代码点评 时间复杂度分析 空间复杂度分析 我要更强 优化建议 优化后的…

【总结】在SpringBoot项目中如何动态切换数据源、数据库?(可直接CV)

注意&#xff1a;文章若有错误的地方&#xff0c;欢迎评论区里面指正 &#x1f36d; 前言 本文参考若依源码&#xff0c;介绍了如何在SpringBoot项目中使用AOP和自定义注解实现MySQL主从数据库的动态切换&#xff0c;当从库故障时&#xff0c;能自动切换到主库&#xff0c;确…

国标GB28181视频汇聚平台EasyCVR设备展示数量和显示条数不符的原因排查与解决

国标GB28181/GA/T1400协议/安防综合管理系统EasyCVR视频汇聚平台能在复杂的网络环境中&#xff0c;将前端设备统一集中接入与汇聚管理。智慧安防/视频存储/视频监控/视频汇聚EasyCVR平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级…

API低代码平台介绍6-数据库记录删除功能

数据库记录删除功能 在前续文章中我们介绍了如何插入和修改数据库记录&#xff0c;本篇文章会沿用之前的测试数据&#xff0c;介绍如何使用ADI平台定义一个删除目标数据库记录的接口&#xff0c;包括 单主键单表删除、复合主键单表删除、多表删除&#xff08;整合前两者&#x…

aws的eks(k8s)ingress+elb部署实践

eks&#xff08;k8s&#xff09;版本1.29 ingress 版本1.10.0 负载均衡elb 1. 创建Ingress-Nginx服务 部署项目地址【点我跳转】推荐自定义部署 可绑定acm证书什么的自己属性 这里就是aws上面Certificate Manager产品上面创建证书 导入 创建都行 对应集群版本推荐阵列GitH…

YOLOv8 目标检测程序,依赖的库最少,使用onnxruntime推理

YOLOv8 目标检测程序&#xff0c;依赖的库最少&#xff0c;使用onnxruntime推理 flyfish 为了方便理解&#xff0c;加入了注释 """ YOLOv8 目标检测程序 Author: flyfish Date: Description: 该程序使用ONNX运行时进行YOLOv8模型的目标检测。它对输入图像进行…

尴尬时刻:如何在忘记名字时巧妙应对

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

代理IP知识:导致代理IP访问超时的原因有哪些?

很多用户在使用代理IP进行网络访问时&#xff0c;可能会遇到代理IP超时的情况&#xff0c;也就是代理IP的延迟过高。代理IP延迟过高会影响用户的网络体验和数据获取效率。因此&#xff0c;了解代理IP延迟过高的原因很重要。以下是导致代理IP延迟过高的一些常见原因&#xff1a;…

美容美发店营销版微信小程序源码

打造线上生意新篇章 一、引言&#xff1a;微信小程序&#xff0c;开启美容美发行业新纪元 在数字化时代&#xff0c;微信小程序以其便捷、高效的特点&#xff0c;成为了美容美发行业营销的新宠。本文将带您深入了解美容美发营销微信小程序&#xff0c;探讨其独特优势及如何助…