72.网络游戏逆向分析与漏洞攻防-角色与怪物信息的更新-完善利用角色与怪物创建的功能

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

如果看不懂、不知道现在做的什么,那就跟着做完看效果

现在的代码都是依据数据包来写的,如果看不懂代码,就说明没看懂数据包

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

上一个内容:71.分析并利用角色与怪物创建的数据包

码云版本号:d426a018b0d6423d4e6b10f4a4e35280e4758c19

代码下载地址,在 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的分享

以 71.分析并利用角色与怪物创建的数据包 它的代码为基础进行修改

上一个内容把30数据包把框架代码写了一部分,本次接着继续完善

效果图:

下图中有7个怪物,也正常把30数据包解析出了7个

在做任务的时候走到下图位置找到了类型9的数据

下图已经根据30数据包把9号数据解析出来了,根据它英文的翻译,LastObject最后一个对象,猜测它是一个long long 类型是某个人的id,下图可以证明猜测是正确的,写的代码并没有导致游戏闪退而是正常解析出来了

GameOBJECT.h、GameOBJECT.cpp修改了,重新使用 GameAnly.cpp 代码生成一次

NetClient.cpp文件的修改:修改了 CreateAim函数、OnSvrObjectInit函数、OnSvrObjectInitEx函数

#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  WinSock->OnSend(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 WinSock->OnSend(&_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 WinSock->OnSend(&_buff.op, ilen);
}

bool NetClient::CreateRole(wchar_t* name, double sex, double camp, double face, 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(face);
    _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 WinSock->OnSend(&_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 WinSock->OnSend((char*)&_data, sizeof(_data));
}

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::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 WinSock->OnSend(&_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 WinSock->OnSend(&_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 WinSock->OnSend(&_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 WinSock->OnSend(&_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 WinSock->OnSend(&_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 WinSock->OnSend((char*)&_data, sizeof(DATA_DELROLE) - 1);
}

PAIM NetClient::GetAimById(long long lId)
{
    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();
}

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

void NetClient::Init(GameWinSock* _winSock)
{
    for (int i = 0; i < 0x100; i++) {
        SendProc[i] = &NetClient::DefaultProc;
        RecvProc[i] = &NetClient::DefaultProc;
    }
    this->WinSock = _winSock;
    // 注册登录数据包处理函数
    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::OnSverStruct;
    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;
}

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  WinSock->OnSend(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:
    // case SC_REONLINE:
    case SC_INIT_START:
    case SC_HAND:
    case SC_HAND_IN:
        return false;
    default:
        break;
    }
    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::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::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;
}


// 默认的数据处理函数
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;
    }


    /*
        Client->OnCreateRole(head, (PCREATE_ROLE_DATAS)codes) 数据包传给虚函数
        如果想对发送创建角色数据包做些什么直接继承NetClient重写OnCreateRole函数既可以了
    */
    return Client->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;
    }

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

}

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

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

    PDATADELROLE p = (PDATADELROLE)buff;
    return Client->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 Client->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);
        }
        Client->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_NOTICE_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) {
    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(&Player.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);
            head = (PNR_OBJINITEX)(buffStart - 8);
            iref++;
        }

        iObjectCount++;
    }

    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 Client->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
    /*
        Client->OnSendCustom((PNET_SEND_CHEAD)codes, buff, len); 数据包传给虚函数
        如果想对0A开头的据包做些什么直接继承NetClient重写OnSendCustom函数既可以了
    */
    return Client->OnScrStartCreateRole(_st[0], _txt);
}


GameAnly.cpp文件的修改:修改了 AnlyData函数、CreateObjectfiles函数

#include "pch.h"
#include "GameAnly.h"
#include <iostream>
#include <fstream>

#ifdef Anly
// 它会生成一个结构体,详情看效果图
void GameAnly::AnlyBuff(char* start, char* end, int MsgId, char index)
{
    CStringA txt;
    CStringA tmp;
    CString utmp;
    EnCode _coder;

    GBYTE* _bytecoder;
    GSHORT* _shortcoder;
    GINT* _intcoder;
    GFLOAT* _floatcoder;
    GDOUBLE* _doublecoder;
    GCHAR* _asccoder;
    GUTF16* _utfcoder;
    GINT64* _int64coder;



    while (start < end) {
        _coder.Init(start, index);
        CStringA _opname = data_desc[_coder.index][_coder.op].name;
        // _opname.MakeLower()是变为小写字母,会影响 _opname它的值
        // 所以又写了一边 data_desc[_coder.index][_coder.op].name
        tmp.Format("%s %s;//", data_desc[_coder.index][_coder.op].name, _opname.MakeLower().GetBuffer());
        txt = txt + tmp;
        if (_coder.index == 0) {
            switch (_coder.op)
            {
            case 1:
                _shortcoder = (GSHORT*)&_coder;
                tmp.Format("%d\r\n", _shortcoder->value());
                txt = txt + tmp;
                break;
            case 2:
                _intcoder = (GINT*)&_coder;
                tmp.Format("%d\r\n", _intcoder->value());
                txt = txt + tmp;
                break;
            case 4:
                _floatcoder = (GFLOAT*)&_coder;
                tmp.Format("%f\r\n", _floatcoder->value());
                txt = txt + tmp;
                break;
            case 6:
                _bytecoder = (GBYTE*)&_coder;
                tmp.Format("%d\r\n", _bytecoder->value());
                txt = txt + tmp;
                break;
            case 7:
                _utfcoder = (GUTF16*)&_coder;
                utmp.Format(L"[%s]\r\n", _utfcoder->value());
                tmp = utmp;
                txt = txt + tmp;
                break;
                // 5号之前分析的忘记截图了,现在找不到它的数据包了,如果后面再见到05的时候再详细补充说明
                // 之前的分析05就是double类型
            case 5:
                _doublecoder = (GDOUBLE*)&_coder;
                tmp.Format("%lf\r\n", _doublecoder->value());
                txt = txt + tmp;
                break;
            case 8:
            case 3:
                _int64coder = (GINT64*)&_coder;
                tmp.Format("%lld\r\n", _int64coder->value());
                txt = txt + tmp;
                break;
            default:
                break;
            }
        }

        if (_coder.index == 1) {
            switch (_coder.op)
            {
            case 1:
                _shortcoder = (GSHORT*)&_coder;
                tmp.Format("%d\r\n", _shortcoder->value());
                txt = txt + tmp;
                break;
            case 2:
                _intcoder = (GINT*)&_coder;
                tmp.Format("%d\r\n", _intcoder->value());
                txt = txt + tmp;
                break;
            case 4:
                _floatcoder = (GFLOAT*)&_coder;
                tmp.Format("%f\r\n", _floatcoder->value());
                txt = txt + tmp;
                break;
            case 6:
                _asccoder = (GCHAR*)&_coder;
                tmp.Format("%s\r\n", _asccoder->value());
                txt = txt + tmp;
                break;
            case 7:
                _utfcoder = (GUTF16*)&_coder;
                utmp.Format(L"[%s]\r\n", _utfcoder->value());
                tmp = utmp;
                txt = txt + tmp;
                break;
            case 5:
                _doublecoder = (GDOUBLE*)&_coder;
                tmp.Format("%lf\r\n", _doublecoder->value());
                txt = txt + tmp;
                break;
            case 8:
            case 3:
                _int64coder = (GINT64*)&_coder;
                tmp.Format("%lld\r\n", _int64coder->value());
                txt = txt + tmp;
                break;
            default:
                break;
            }
        }
    }
    anly->SendData(TTYPE::I_DIS, MsgId, txt.GetBuffer(), txt.GetAllocLength() + 1);
}
void GameAnly::AnlyData(char* start, char* end, int count, int MsgId, POBJ_DESC desc)
{
    int iProc = 0;

    int value;
    long long llvalue;
    float fvalue;
    double dbval;
    CStringA szTmp, _tmp, szTxt;
    CString wTmp;

    while ((iProc < count) && (start <end)) {
        short* index = (short*)start;
        int type = desc[index[0]].type;
        char* name = desc[index[0]].name;
   

    switch (type)
       {
       case 0:
           AfxMessageBox(L"0号信息!"); break;
       case 1:
           value = ToChar(start);
           szTmp.Format("%s = %d", name, value);
           break;
       case 2:
           value = ToShort(start);
           szTmp.Format("%s = %d", name, value);
           break;
       case 3:
           value = ToInt(start);
           szTmp.Format("%s = %d", name, value);
           break;
       case 4:
           llvalue = ToLLong(start);
           szTmp.Format("%s = %lld", name, llvalue);
           break;
       case 5:
           fvalue = ToFloat(start);
           szTmp.Format("%s = %f", name, fvalue);
           break;
       case 6:
           dbval = ToDouble(start);
           szTmp.Format("%s = %lf", name, dbval);
           break;
       case 7:
           _tmp = ToAscii(start);
           szTmp.Format("%s = %s", name, _tmp.GetBuffer());
           break;
       case 8:
           wTmp = ToUniode(start);
           _tmp = wTmp;
           szTmp.Format("%s = %s", name, _tmp.GetBuffer());
           break;
       case 9:
           llvalue = ToLLong(start);
           szTmp.Format("%s = %llX", name, llvalue);
           break;
       default:
           break;
       }
       szTxt = szTxt + szTmp + "\r\n";
       iProc++;
   }
   anly->SendData(TTYPE::I_DIS, MsgId, szTxt.GetBuffer(), szTxt.GetAllocLength() + 1);

    //CStringA tmpA;
    //CStringA szTxt, szTmp;
    //szTmp.Format("id:%lld\r\n", head->lId);
    //szTxt = szTxt + szTmp;
    //szTmp.Format("x:%f h:%f y:%f\r\n", head->x, head->h, head->y);
    //szTxt = szTxt + szTmp;
    //char* buffStart = (char*)head + sizeof(NR_OBNJECT_INIT) - 2;
    //int icount = head->icount;
    //int iProc = 0;

    //while (iProc < icount) {
    //    short* type = (short*)buffStart;
    //    char* _name = ObjectTable[type[0]].name;
    //    int _type = ObjectTable[type[0]].type;

    //    char* _byte;
    //    short* _short;
    //    int* _int;
    //    float* _float;
    //    long long* _llong;
    //    double* _double;
    //    int lenth;

    //    CString _txt;
    //    /*
    //        1B 00 type[0] buffStart + 2;
    //        0C 00 00 00 CA 4E 5A 66 53 62 01 80 4E 86 00 00
    //        1D 00 type[0]
    //        00 00 48 42 buffStart + 2;
    //        01 00 type[0]
    //        02 buffStart + 2;
    //        02 00 type[0]
    //        01 buffStart + 2;
    //        03 00 2E 00 00 00 67 75 69 5C 42 47 5F 74 65 61 6D 5C 54 65 61 6D 52 6F 6C 65 5C 54 65 61 6D 72 6F 6C 65 5F 7A 71 5F 68 75 6D 46 5F 30 30 31 2E 50 4E 47 00
    //        04 00 01 00 00 00
    //        05 00 01 00 00 00
    //        06 00 01 00 00 00
    //        07 00 01 00 00 00
    //        08 00 00 B1 9E 00
    //    */
    //    buffStart = buffStart + 2;

    //    switch (_type)
    //    {
    //    case 0:
    //        AfxMessageBox(L"0号信息!"); break;
    //    case 1:
    //        _byte = buffStart;
    //        szTmp.Format("%s = %d", _name, _byte[0]);
    //        buffStart = buffStart + 1;
    //        break;
    //    case 2:
    //        _short = (short*)buffStart;
    //        szTmp.Format("%s = %d", _name, _short[0]);
    //        buffStart = buffStart + 2;
    //        break;
    //    case 3:
    //        _int = (int*)buffStart;
    //        szTmp.Format("%s = %d", _name, _int[0]);
    //        buffStart = buffStart + 4;
    //        break;
    //    case 4:
    //        _llong = (long long*)buffStart;
    //        szTmp.Format("%s = %lld", _name, _llong[0]);
    //        buffStart = buffStart + 8;
    //        break;
    //    case 5:
    //        _float = (float*)buffStart;
    //        szTmp.Format("%s = %f", _name, _float[0]);
    //        buffStart = buffStart + 4;
    //        break;
    //    case 6:
    //        _double = (double*)buffStart;
    //        szTmp.Format("%s = %lf", _name, _double[0]);
    //        buffStart = buffStart + 8;
    //        break;
    //    case 7:
    //        _int = (int*)buffStart;
    //        lenth = _int[0];
    //        // szTmp = buffStart + 4;
    //        szTmp.Format("%s = %s", _name, buffStart + 4);
    //        buffStart = buffStart + 4 + lenth;
    //        break;
    //    case 8:
    //        _int = (int*)buffStart;
    //        lenth = _int[0];
    //        _txt = (wchar_t*)(buffStart + 4);
    //        tmpA = _txt;
    //        szTmp.Format("%s = %s", _name, tmpA);
    //        buffStart = buffStart + 4 + lenth;
    //        break;
    //    case 9:MessageBoxA(0, buffStart, buffStart, MB_OK); return true;
    //    default:
    //        break;
    //    }
    //    szTxt = szTxt + szTmp + "\r\n";
    //    iProc++;
    //}
    //anly->SendData(TTYPE::I_DIS, S_OBJECT_INIT, szTxt.GetBuffer(), szTxt.GetAllocLength() + 1);
}
void GameAnly::CreateObjectfiles(POBJ_DESC desc, int icount)
{
    /*char* _GameOBJECThpp = "F:\\代码存放地\\c\\titan\\tilib\\GameOBJECT.h";
    char* _GameOBJECTcpp = "F:\\代码存放地\\c\\titan\\tilib\\GameOBJECT.cpp";
    char* _GameOBJECTdef = "F:\\代码存放地\\c\\titan\\tilib\\GameOBJECTDef.h";*/
    char* _GameOBJECThpp = "D:\\代码存放地\\c\\titan\\tilib\\GameOBJECT.h";
    char* _GameOBJECTcpp = "D:\\代码存放地\\c\\titan\\tilib\\GameOBJECT.cpp";
    char* _GameOBJECTdef = "D:\\代码存放地\\c\\titan\\tilib\\GameOBJECTDef.h";
    
    std::ofstream ofs(_GameOBJECThpp); // 根据09数据包生成类的头文件
    std::ofstream ofCpp(_GameOBJECTcpp); // 根据09数据包生成类的cpp文件
    std::ofstream ofDef(_GameOBJECTdef);// 生成宏

    if (ofs.bad() || ofCpp.bad() || ofDef.bad()) {
        ofs.close();
        ofCpp.close();
        ofDef.close();
        return;
    }
    else
    {
        // 定义CPP文件头部
        ofCpp << "#include \"pch.h\"" << std::endl;
        ofCpp << "#include \"GameOBJECT.h\"" << std::endl;
        ofCpp << "#include \"GameOBJECTDef.h\"" << std::endl;

        ofCpp << "void GAMEOBJECT::UpdateData(char*& buffStart)" << std::endl;
        ofCpp << "{                                                              " << std::endl;
        ofCpp << "    short* id = (short*)buffStart;" << std::endl;
        ofCpp << "    Isfree = false;" << std::endl;
        ofCpp << "    /*                                                         " << std::endl;
        ofCpp << "        1B 00 buffStart                                        " << std::endl;
        ofCpp << "        0C 00 00 00 buffStart + 2                              " << std::endl;
        ofCpp << "        CA 4E 5A 66 53 62 01 80 4E 86 00 00 1D 00              " << std::endl;
        ofCpp << "    */                                                         " << std::endl;
        ofCpp << "    buffStart = buffStart + 2;" << std::endl;
        ofCpp << "    switch (id[0])" << std::endl;
        ofCpp << "    {" << std::endl;


        // 定义文件头部区域
        ofDef << "#pragma once" << std::endl;


        // 头部生成区域
        ofs << "#pragma once" << std::endl;
        ofs << "#define GASCII CStringA" << std::endl;
        ofs << "#define GUNICODE CString" << std::endl;
        ofs << "#define GOBJECT long long // LastObject" << std::endl;
        ofs << "typedef class GAMEOBJECT{" << std::endl;
        ofs << "public:" << std::endl;
        ofs << "    bool Isfree = false;" << std::endl;
        // 变量声明
        //  i = 1的原因是游戏的数据类型表里(接收的09数据包)第一个是NONE
        for (int i = 1; i < icount; i++)
        {
            char* valueName = desc[i].name;
            int valueType = desc[i].type;
            char* valueTypeName = data_desc[2][valueType].name;
            int valueSize = data_desc[2][valueType].lenth;

            ofs << "    " << valueTypeName << " " << valueName << ";" << std::endl;
            ofDef << "#define INDEX_" << valueName << " " << i << std::endl;

            ofCpp << "    case INDEX_" << valueName << ":" << std::endl;
            ofCpp << "      return Set" << valueName << "(buffStart);" << std::endl;

        }

        ofCpp << "    }" << std::endl;
        ofCpp << "}" << std::endl;
   
        // 函数声明
        ofs << "    void virtual UpdateData(char*& buffStart);" << std::endl;
        ofs << "protected:" << std::endl;

        for (int i = 1; i < icount; i++)
        {
            char* valueName = desc[i].name;
            int valueType = desc[i].type;
            char* valueTypeName = data_desc[2][valueType].name;
            int valueSize = data_desc[2][valueType].lenth;

            ofs << "    void virtual Set" << valueName << "(char*& buffStart);" << std::endl;

            ofCpp << "void GAMEOBJECT::Set" << valueName << "(char*& buffStart)" << std::endl;
            ofCpp << "{" << std::endl;

            if(valueType == 7){
                ofCpp << "   int* lenth = (int*)buffStart;" << std::endl;
                ofCpp << "   buffStart += 4;" << std::endl;
                ofCpp << "   " << valueName << " = (char*)buffStart; " << std::endl;
                ofCpp << "   buffStart += lenth[0];" << std::endl;
            }
            else if(valueType == 8) {
                ofCpp << "   int* lenth = (int*)buffStart;" << std::endl;
                ofCpp << "   buffStart += 4;" << std::endl;
                ofCpp << "   " << valueName << " = (wchar_t*)buffStart; " << std::endl;
                ofCpp << "   buffStart += lenth[0];" << std::endl;
            }
            else {
                ofCpp << "    " << valueTypeName << "* value = (" << valueTypeName << "*)buffStart;" << std::endl;
                ofCpp << "    buffStart += sizeof(" << valueTypeName << ");" << std::endl;
                ofCpp << "    " << valueName << " = value[0];" << std::endl;
            }
                  
            ofCpp << "}" << std::endl;
   
        }

        ofs << "}*PGAMEOBJ;" << std::endl;

    }

    ofs.close();
    ofCpp.close();
    ofDef.close();
}

char GameAnly::ToChar(char*& start)
{
    char result = start[0];
    start = start + 3;
    return result;
}

short GameAnly::ToShort(char*& start)
{
    short* result = (short*)(start + 2);
    start = start + 2 + 2;
    return result[0];
}

int GameAnly::ToInt(char*& start)
{
    int* result = (int*)(start + 2);
    start = start + 2 + 4;
    return result[0];
}

float GameAnly::ToFloat(char*& start)
{
    float* result = (float*)(start + 2);
    start = start + 2 + 4;
    return result[0];
}

double GameAnly::ToDouble(char*& start)
{
    double* result = (double*)(start + 2);
    start = start + 2 + 8;
    return result[0];
}

long long GameAnly::ToLLong(char*& start)
{
    long long* result = (long long*)(start + 2);
    start = start + 2 + 8;
    return result[0];
}

char* GameAnly::ToAscii(char*& start)
{
    int* lenth = (int*)(start + 2);
    char* result = start + 2 + 4; // +4这个操作是跳过 lenth的值
    start = start + 2 + 4 + lenth[0];
    return result;
}

wchar_t* GameAnly::ToUniode(char*& start)
{
    int* lenth = (int*)(start + 2);
    wchar_t* result = (wchar_t*)(start + 2 + 4); // +4这个操作是跳过 lenth的值
    start = start + 2 + 4 + lenth[0];
    return result;
}
#endif // Anly

extern_all.cpp文件的修改:修改了 init_datadesc函数

/*
	此文件是用来存放全局变量、全局函数(通用函数)
*/
#include "pch.h"
#include "extern_all.h"
GameWinSock* WinSock = nullptr;
GameProc* PGameProc = nullptr;
NetClient* Client = nullptr;
#ifdef Anly
CAnly* anly = nullptr;
GameAnly* GameAnlyer = nullptr;
#endif

DATA_DESC data_desc[3][10]{};

void init_datadesc() {
	data_desc[0][0] = { "none", 0 };
	data_desc[0][1] = { "GSHORT", 2 };
	data_desc[0][2] = { "GINT", 4 };
	data_desc[0][3] = { "GINT64", 8 };
	data_desc[0][4] = { "GFLOAT", 4 };
	data_desc[0][5] = { "GDOUBLE", 8 };
	data_desc[0][6] = { "GBYTE", 1 };
	data_desc[0][7] = { "GUTF16", 4 };
	data_desc[0][8] = { "GINT64", 8 };

	data_desc[1][0] = { "none", 0 };
	data_desc[1][1] = { "GSHORT", 2 };
	data_desc[1][2] = { "GINT", 4 };
	data_desc[1][3] = { "GINT64", 8 };
	data_desc[1][4] = { "GFLOAT", 4 };
	data_desc[1][5] = { "GDOUBLE", 8 };
	data_desc[1][6] = { "GCHAR", 4 };
	data_desc[1][7] = { "GUTF16", 4 };
	data_desc[1][8] = { "GINT64", 8 };

	data_desc[2][0] = { "none", 0 };
	data_desc[2][1] = { "char", 1 };
	data_desc[2][2] = { "short", 2 };
	data_desc[2][3] = { "int", 4 };
	data_desc[2][4] = { "long long", 8 };
	data_desc[2][5] = { "float", 4 };
	data_desc[2][6] = { "double", 8 };
	data_desc[2][7] = { "GASCII", 4 };
	data_desc[2][8] = { "GUNICODE", 4 };
	data_desc[2][9] = { "GOBJECT", 8 }; // LastObject
}

void InitClassProc(LPVOID proc_addr, unsigned value)
{
	unsigned* writer = (unsigned*)proc_addr;
	writer[0] = value;
}

新加AIM.cpp文件:

#include "pch.h"
#include "AIM.h"

// 复制游戏数据包到自己的内存中
void AIM::SetHeadDatas(void* buffStart)
{
	unsigned lStart = (unsigned)&lId;
	unsigned lEnd = (unsigned)&endclass;
	memcpy(&lId, buffStart, lEnd - lStart);
}

AIM.h文件的修改:新加 SetHeadDatas函数

#pragma once
#include "GameOBJECT.h"
typedef class AIM:public GAMEOBJECT {
public:
	long long lId;
	float x;
	float h;
	float y;
	float face;

	float tx;
	float th;
	float ty;
	float tface;

	int endclass;

	void virtual  SetHeadDatas(void* buffStart);
}*PAIM;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开源模型应用落地-LangChain高阶-Tools工具-集成agents(四)

一、前言 LangChain 的 tools 是一系列关键组件&#xff0c;它们提供了与外部世界进行交互的能力。通过适当的使用这些组件&#xff0c;可以简单实现如执行网络搜索以获取最新信息、调用特定的 API 来获取数据或执行特定的操作、与数据库进行交互以获取存储的信息等需求。 本章…

MATLAB - 自定义惯性矩阵

系列文章目录 前言 一、关键惯性约定 Simscape 多体软件在惯性定义中采用了一系列约定。请注意这些约定&#xff0c;因为如果手动进行惯性计算&#xff0c;这些约定可能会影响计算结果。如果您的惯性数据来自 CAD 应用程序或其他第三方软件&#xff0c;这些约定还可能影响到您需…

TranslatePress Pro插件下载:一键国际化,让您的网站走向世界

在全球化的今天&#xff0c;一个多语言的网站是连接不同文化和市场的桥梁。TranslatePress Pro插件&#xff0c;作为一款专为WordPress用户设计的多语言解决方案&#xff0c;以其简便的操作和强大的功能&#xff0c;帮助您的网站跨越语言障碍&#xff0c;吸引全球用户。 [Tran…

vector 的模拟实现

目录 1. vector 的核心框架 2. size 和 capacity 以及 empty 3. reserve 和 push_back 4. insert 5. erase 6. copy constructor 6.1. 第一个版本 6.2. 第二个版本 6.3. 第三个版本 7. operator 7.1. 第一个版本 7.2. 第二个版本 7.3. 第三个版本 8. constructor…

用自然语言即可完全控制用户界面;无需调整的文本至图片生成的ID定制方法;OpenAI构建应用指南

✨ 1: PyWinAssistant 用自然语言即可完全控制用户界面 PyWinAssistant是一个突破性的项目&#xff0c;它基于2023年12月31日发布的技术&#xff0c;代表了首个大型行为模型、开源Windows 10/11人工智能框架。这个框架的主要亮点在于它能够通过利用思维可视化&#xff08;Vis…

Java复习第十九天学习笔记(Cookie、Session登录),附有道云笔记链接

【有道云笔记】十九 4.7 Cookie、Session登录 https://note.youdao.com/s/VwpxfEim 一、会话技术简介 生活中会话 我&#xff1a; 小张&#xff0c;你会跳小苹果码&#xff1f; 小张&#xff1a; 会&#xff0c;怎么了&#xff1f; 我&#xff1a; 公司年会上要表演节目&a…

HTML_CSS学习:常用文本属性

一、文本颜色 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>文本颜色</title><style>div{font-size: 90px;}.atguigu1{color: #238c20;}.atguigu2{color: rgb(2…

AI文章框架分析

大家在文章写作的时候结构难免会有点凌乱&#xff0c;但是自己可能无法发现问题所在&#xff0c;那么有没有一款工具可以帮你自动分析你写的文章框架存在的问题&#xff0c;然后并给你详细的分析报告呢&#xff1f;今天给大家介绍一下文件框架分析助手&#xff01; 使用说明 打…

jQuery Moblie 笔记14 开发跨平台移动设备网页

相关内容&#xff1a;jQuery Moblie基础、操作、移动设备仿真器、jQuery Moblie网页实例、jQuery Moblie的UI组件、…… jQuery推出了一套新的函数库jQuery Mobile&#xff0c;目的是希望能够统一当前移动设备的用户界面(UI)。 移动设备开发应用程序目前大致分为两种&#xff…

大数据分析入门之10分钟掌握GROUP BY语法

前言 书接上回大数据分析入门10分钟快速了解SQL。 本篇将会进一步介绍group by语法。 基本语法 SELECT column_name, aggregate_function(column_name) FROM table_name GROUP BY column_name HAVING condition假设我们有students表&#xff0c;其中有id,grade_number,class…

vue快速入门(五十一)历史模式

注释很详细&#xff0c;直接上代码 上一篇 新增内容 历史模式配置方法 默认哈希模式&#xff0c;历史模式与哈希模式在表层的区别是是否有/#/ 其他差异暂不深究 源码 //导入所需模块 import Vue from "vue"; import VueRouter from "vue-router"; import m…

全方位解析Node.js:从模块系统、文件操作、事件循环、异步编程、性能优化、网络编程等高级开发到后端服务架构最佳实践以及Serverless服务部署指南

Node.js是一种基于Chrome V8引擎的JavaScript运行环境&#xff0c;专为构建高性能、可扩展的网络应用而设计。其重要性在于革新了后端开发&#xff0c;通过非阻塞I/O和事件驱动模型&#xff0c;实现了轻量级、高并发处理能力。Node.js的模块化体系和活跃的npm生态极大加速了开发…

Centos 7.9 配置VNCServer实现远程vnc连接

文章目录 1、Centos安装图形界面1.1、安装X Windows System图形界面1.2、安装GNOME图形界面 2、VNC SERVER配置2.1、VNC SERVER安装2.2、VNC SERVER配置1&#xff09;创建vnc配置文件2&#xff09;修改配置文件内容3&#xff09;完整配置文件参考 2.3、设置vnc密码2.4、配置防火…

C++基础——输入输出(文件)

一、标准输入输出流 C 的输入输出是程序与用户或外部设备&#xff08;如文件、网络等&#xff09;之间交换信息的过程。 C 提供了丰富的标准库来支持这种交互&#xff0c;主要通过流的概念来实现。 流&#xff1a;抽象概念&#xff0c;表示一连串的数据&#xff08;字节或字…

c语言从入门到函数速成(2)

温馨提醒&#xff1a;本篇文章适合人群&#xff1a;刚学c又感觉那个地方不怎么懂的同学以及以及学了一些因为自身原因停学一段时间后又继续学​​​c的学 好&#xff0c;正片开始&#xff01; 数组 概念&#xff1a;数组中存放的是1个或者多个数据&#xff0c;但是数组元素个…

频率和转速转换功能块(CODESYS ST源代码)

1、转速和频率转换功能块 转速和频率转换功能块(CODESYS ST源代码)-CSDN博客文章浏览阅读10次。1、转速/频率常用转换关系转速/频率/线速度/角速度计算FC_200 plc计算角速度-CSDN博客文章浏览阅读3.2k次。https://rxxw-control.blog.csdn.net/article/details/138438864 1、转…

企业计算机服务器中了rmallox勒索病毒怎么处理,rmallox勒索病毒处理建议

在网络技术不断发展的时代&#xff0c;网络在企业中的应用广泛&#xff0c;可以为企业带来更多的便利&#xff0c;大大提升了企业的生产效率&#xff0c;但网络作为虚拟世界&#xff0c;在为企业提供便利的同时&#xff0c;也为企业数据安全带来严重威胁。近期&#xff0c;云天…

C++入门系列-基于范围的for循环(C++11)和指针空值nullptr(C++11)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 基于范围的for循环 范围for的语法 在C98中如果要遍历一个数组&#xff0c;可以按照以下方式进行&#xff1a; void TestFor() {int array[] { 1,2,3,4,5 };for (int i 1; i …

VmWare 虚拟机没有网络解决办法

由于最近需要&#xff0c;装了个VM虚拟机&#xff0c;但是突然发现本机有网络&#xff0c;虚拟机却没有网络&#xff0c;更换了虚拟机的网络设置&#xff0c;都尝试过了 都不管用&#xff0c; 最后尝试了这种方法完美解决 还原网络默认设置 首先还原虚拟网络编辑器设置 启动V…

力扣---二叉树的锯齿形层序遍历

给你二叉树的根节点 root &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往右&#xff0c;再从右往左进行下一层遍历&#xff0c;以此类推&#xff0c;层与层之间交替进行&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,…
最新文章