109.网络游戏逆向分析与漏洞攻防-装备系统数据分析-商店与捨取窗口数据的处理

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

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

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

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

上一个内容:108.利用数据包实现物品的使用丢弃交换

码云版本号:2d53d9eadcd3122e198f5305ffafdbf69556f84c

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

以 108.利用数据包实现物品的使用丢弃交换 它的代码为基础进行修改

本次来处理商店与捨取窗口的数据,这样以后打怪捡东西也可以了

进入游戏点击商店

打开商店发送的数据包是 552

然后是15数据包

剩下的都是18数据包,18数据包在处理背包数据时已经处理过了

然后再换一个商店(防具店),它出现了17的数据包

17数据包的拆分,它看着像是对15数据包的一个补充

商人坐标位置

通过观察打开商店它的数据包,发现并没有告诉客户端商店背包结构大小修改的东西,所以猜测它是每打开一次商店它会重新申请一次内存,这里在我们的代码中给它优化一下,不要每次都申请内存

捨取窗口

数据包 18 50

点击全部捨取的数据包

16数据包可能代表了关闭捨取窗口

有个王者之箱的物品,它也可以弹出捨取窗口

它的数据包,它的捨取看着有点不一样,这里先不管了

本次代码为了使用起来方便,修改了物品相关代码(物品获取、修改框架),没有做功能性的东西(只是简单的把商店、捨取窗口结构创建了出来,并且实现初始化,但是并没有进行测试)

NetClient.cpp文件的修改:修改了 GetGameOBJECT函数、ReSetGameOBJECT函数、ExChangeOBJECT函数、,新加 GetGameOBJECTUnion函数

#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); // 可以把坐标设置成nanMoveStop(v.fNan, v.fNan, v.fNan, v.fNan); // 可以把坐标设置成nanMoveStop(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判断两个字符串是否相同// 比较时区分大小写,如果字符串相同返回0if (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 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::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::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 0x28:return &Skill;case 0x30: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;
}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::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::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::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::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  AnlyCString 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);
#endifreturn 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  Anlyanly->SendData(TTYPE::I_RECV, buff[0], buff, len);
#endifreturn (this->*RecvProc[buff[0]])(buff, len);
}bool NetClient::OnSendData(char*& buff, unsigned& len)
{
#ifdef  Anlyanly->SendData(TTYPE::I_SEND, buff[0], buff, len);
#endifreturn (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);
#endifreturn 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  Anlyanly->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 AnlyGameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);// 翻译解析约定数据
#endif // AnlyEnCode 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  Anlyif (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);
#endifreturn true;}#ifdef  AnlyGameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);
#endifint 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 AnlyGameAnlyer->AnlyBuff(buffStart, buff + len, buff[0]);
#endif // AnlyroleDatas = 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 // Anlyfor (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 AnlyGameAnlyer->CreateObjectfiles(ObjectTable, head->count);
#endif // Anlyreturn true;
}
bool NetClient::OnSverStruct(char*& buff, unsigned& len) {PNR_HEAD head = (PNR_HEAD)(buff - 1);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;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];}#ifdef AnlyGameAnlyer->CreateStructfile(StructTable, head->count);
#endif // Anlyreturn true;
}
bool NetClient::OnSvrObjectInit(char*& buff, unsigned& len)
{/*00 00 00 00 00 00 00 为了内存对齐补充了7个0,也就是 un1[6] 和 len28 opCD 48 00 01 62 A7 DE 04 PNR_OBJINIT:lIdC1 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  AnlyGameAnlyer->AnlyData(buffStart, buff + len, head->icount, S_OBJECT_INIT, ObjectTable);
#endifint 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  AnlyGameAnlyer->AnlyData(buffStart, buff + len, refCount, S_OBJECT_INITEX, ObjectTable);
#endifwhile (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 head21 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 StateCD 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  Anlyanly->SendData(TTYPE::I_DIS, S_UPDATECORD, txtA.GetBuffer(), txtA.GetAllocLength() + 1);
#endifhead = (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  AnlyGameAnlyer->AnlyData(buffStart, buff + len, refcount, S_UPDATEPRO, ObjectTable);
#endifif (head->MsgId ) {_object = GetGameOBJECT(head->Index[0], head->Index[1]);}else {_object = GetAimById(head->lId);}while ((iref < refcount)&&(_object)) {_object->UpdateData(buffStart);iref++;}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 objCount9B 49 00 01 D5 8C 98 05  GetAimById(head->lId);02 00 25 00 (char*)&head->itype18 00 00 00 29 00 BA 01 00 00 while (iobjCount < objCount) 里获取的是9B 49 00 01 D5 8C 98 0502 0025 00 18 00 00 0029 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 0025 00 (char*)&head->itype18 00 00 0029 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  AnlyGameAnlyer->AnlyData(buffStart, buff + len, refCount, S_UPDATEPROMU, ObjectTable);
#endifwhile (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;
}/*1F87 44 00 01 7F B0 D6 05FE AD DE C36C F5 46 4207 FD 36 C490 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  AnlyCStringA 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);
#endifPOBJECTBASE 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++;}return true;
}
bool NetClient::OnSvrGameBaseReset(char*& buff, unsigned& len)
{PNR_GAMEBASE_RESET head = nullptr;head = (PNR_GAMEBASE_RESET)(buff - &head->op);ReSetGameOBJECT(head->type, head->max);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;
}
/*OnSverrNotice函数处理的数据包格式如下1E 06 0006 11 00 00 00 70 6C 61 79 5F 70 6F 69 6E 74 5F 73 6F 75 6E 64 0006 01 00 00 00 0004 2C 92 87 C504 FA 03 BF 4204 33 14 BD 4502 00 00 00 001E 06 00 是 PNR_NOTICE_HEAD06 11 00 00 00 70 6C 61 79 5F 70 6F 69 6E 74 5F 73 6F 75 6E 64 00是一个EnCode06 01 00 00 00 00是一个EnCode04 2C 92 87 C5是一个EnCode04 FA 03 BF 42是一个EnCode04 33 14 BD 45是一个EnCode02 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  Anlyanly->SendData(TTYPE::I_DIS, S_NOTICE, "S_NOTICE 解码器内存不足", 24);
#endifreturn true;}#ifdef  AnlyGameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);
#endifint 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  AnlyCString 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文件的修改:新加 OBJECT_TYPE宏、OBJECT_UNION结构体、GetGameOBJECTUnion函数,修改了 Equip变量、MountBag变量、Item变量、ItemEx变量、Skill变量、Shop变量、Pickup变量,删除了多个变量,详情看码云日志

#pragma once
#include "NetClass.h"
#include "AIM.h"
#include "ITEM.h"
#include "SKILL.h"#define CAMP_NAME_QH "xuanrenQH"
#define CAMP_NAME_ZE "xuanrenZQ"#define MAX_AIM 0x200enum 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 3DPickup = 0x50 // 捨取,18 50 是打怪,怪死了之后掉落的捨取弹框
};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 3DOBJECT_UNION Pickup; // 捨取,18 50 是打怪,怪死了之后掉落的捨取弹框POBJECT_UNION GetGameOBJECTUnion(short MainIndex);// 获取一个物品或技能
protected:PSTRUCT_DESC StructTable = nullptr;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: // 消息处理函数-SENDbool OnClientlogin(char*& buff, unsigned& len); // 登录数据包的处理 I_LOGINbool OnClientStartCreateRole(char*& buff, unsigned& len);  // 申请进入创建角色界面 I_CREATEROLE_STARTbool OnClientDelRole(char*& buff, unsigned& len);bool OnClientSendCustom(char*& buff, unsigned& len);bool OnClientCreateRole(char*& buff, unsigned& len);bool OnClientSelectRole(char*& buff, unsigned& len);
protected: // 消息处理函数-RECVbool 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);
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);// 根据角色名字获取一个登录成功数据包(选择角色列表里的一个数据)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);};

CWndSkill.cpp文件的修改:修改了 OnBnClickedButton1函数(刷新技能信息按钮点击事件处理函数)

// CWndSkill.cpp: 实现文件
//#include "pch.h"
#include "CWndSkill.h"
#include "resource.h"
#include "CUI.h"
#include "extern_all.h"// CWndSkill 对话框IMPLEMENT_DYNAMIC(CWndSkill, CDialogEx)CWndSkill::CWndSkill(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_PAGE_4, pParent)
{}CWndSkill::~CWndSkill()
{
}void CWndSkill::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_LIST1, lstSkill);
}BOOL CWndSkill::OnInitDialog()
{CDialogEx::OnInitDialog();CUI* ui = (CUI*)Client;ui->SetListView(&lstSkill);lstSkill.InsertColumn(0, L"名称", 0, 100);lstSkill.InsertColumn(1, L"冷却时间", 0, 100);return TRUE;
}BEGIN_MESSAGE_MAP(CWndSkill, CDialogEx)ON_BN_CLICKED(IDC_BUTTON1, &CWndSkill::OnBnClickedButton1)
END_MESSAGE_MAP()// CWndSkill 消息处理程序void CWndSkill::OnBnClickedButton1()
{lstSkill.DeleteAllItems();float coolDown;int index = 0;CString txt;auto objectTable = Client->GetGameOBJECTUnion((short)OBJECT_TYPE::Skill);if (objectTable) {int index = 0;for (int i = 0; i < objectTable->Count; i++){if (!objectTable->skill[i]->Isfree) {coolDown = objectTable->skill[i]->CoolDownTime / 1000;lstSkill.InsertItem(index, objectTable->skill[i]->Name);txt.Format(L"%.2f秒", coolDown);lstSkill.SetItemText(index++, 1, txt);}}}
}

CWndItem.cpp文件的修改:新加 ShowItem函数,修改了 OnBnClickedButton1函数(装备按钮点击事件处理函数)、OnBnClickedButton4函数(物品按钮点击事件处理函数)、OnBnClickedButton5函数(坐骑按钮点击事件处理函数)、OnBnClickedButton6函数(扩展包按钮点击事件处理函数)

// CWndItem.cpp: 实现文件
//#include "pch.h"
#include "CWndItem.h"
#include "resource.h"
#include "extern_all.h"
#include "CUI.h"// CWndItem 对话框IMPLEMENT_DYNAMIC(CWndItem, CDialogEx)CWndItem::CWndItem(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_PAGE_3, pParent)
{}CWndItem::~CWndItem()
{
}void CWndItem::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_LIST1, lstItem);
}BOOL CWndItem::OnInitDialog()
{CDialogEx::OnInitDialog();CUI* ui = (CUI*)Client;ui->SetListView(&lstItem);lstItem.InsertColumn(0, L"名称",0, 100);lstItem.InsertColumn(1, L"数量",0, 100);return TRUE;
}BEGIN_MESSAGE_MAP(CWndItem, CDialogEx)ON_BN_CLICKED(IDC_BUTTON1, &CWndItem::OnBnClickedButton1)ON_BN_CLICKED(IDC_BUTTON4, &CWndItem::OnBnClickedButton4)ON_BN_CLICKED(IDC_BUTTON5, &CWndItem::OnBnClickedButton5)ON_BN_CLICKED(IDC_BUTTON6, &CWndItem::OnBnClickedButton6)
END_MESSAGE_MAP()// CWndItem 消息处理程序void CWndItem::OnBnClickedButton1()
{ShowItem((short)OBJECT_TYPE::Equip);
}void CWndItem::OnBnClickedButton4()
{ShowItem((short)OBJECT_TYPE::Item);
}void CWndItem::OnBnClickedButton5()
{ShowItem((short)OBJECT_TYPE::MountBag);
}void CWndItem::OnBnClickedButton6()
{// 测试召唤和解除坐骑/*Client->Mount(1);return;*/ShowItem((short)OBJECT_TYPE::ItemEx);
}void CWndItem::ShowItem(short index)
{lstItem.DeleteAllItems();CString txt;auto objectTable = Client->GetGameOBJECTUnion(index);if (objectTable) {int index = 0;for (int i = 0; i < objectTable->Count; i++){if (!objectTable->item[i]->Isfree) {lstItem.InsertItem(index, objectTable->item[i]->Name);txt.Format(L"%d/%d", objectTable->item[i]->Amount, objectTable->item[i]->MaxAmount);lstItem.SetItemText(index++, 1, txt);}}}
}

CWndItem.h文件的修改:新加 ShowItem函数

#pragma once
#include "afxdialogex.h"// CWndItem 对话框class CWndItem : public CDialogEx
{DECLARE_DYNAMIC(CWndItem)public:CWndItem(CWnd* pParent = nullptr);   // 标准构造函数virtual ~CWndItem();// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_PAGE_3 };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持virtual BOOL OnInitDialog();DECLARE_MESSAGE_MAP()
public:CListCtrl lstItem;afx_msg void OnBnClickedButton1();afx_msg void OnBnClickedButton4();afx_msg void OnBnClickedButton5();afx_msg void OnBnClickedButton6();void ShowItem(short index);
};

 

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

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

相关文章

【优选算法】详解target类求和问题(附总结)

目录 1.两数求和 题目&#xff1a; 算法思路&#xff1a; 代码&#xff1a; 2.&#xff01;&#xff01;&#xff01;三数之和 题目 算法思路&#xff1a; 代码&#xff1a; 3.四数字和 题目&#xff1a; 算法思路&#xff1a; 代码&#xff1a; 总结&易错点&…

用人工智能写2024年高考作文

目录 用人工智能写2024年高考作文 引用 一、2024年 新课标I卷 作文真题 AI写作范文 二、2024年 全国甲卷 作文真题 AI写作范文 三、2024年 新课标II卷 作文真题 AI写作范文 四、2024年 北京卷 作文真题一 AI写作范文 作文真题二 AI写作范文 作文真题三 AI写作…

php 混合xml js,html 代码报错 ,结束标签关闭, short_open_tag 的作用,php关闭文件结束判断

结束标签关闭, short_open_tag 的作用&#xff0c;php关闭文件结束判断 有时候我们我们会将php&#xff0c;xml&#xff0c;js&#xff0c;html 混合编写 php文件只要开始标签而不要结尾标签? 混合代码看代码 直接运行 yntax error, unexpected version (T_STRING) in php…

验证码识别接口、多种样式验证码识别接口、中英文验证码识别接口

验证码识别接口、多种样式验证码识别接口、中英文验证码识别接口 本文提供一个基于OCR和机器学习的验证码识别接口&#xff0c;能够识别较复杂的中文、英文验证码&#xff0c;在OCR的基础上针对验证码进行算法优化。本接口是收费的&#xff08;最低0.5分1次调用&#xff0c;试…

C语言详解(文件操作)2

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

CSS id选择器

目录 任务描述 相关知识 id选择器 id选择器语法 类选择器与id选择器的区别 编程要求 任务描述 在本关中&#xff0c;你将通过id选择器的方式完成页面菜单栏样式布局&#xff0c;栏目导航等任务。 完成任务之后&#xff0c;基本页面效果如下&#xff1a; 动态效果如下&am…

IDEA:配置Golang的开发环境

1、下载&安装 进入GO的官网下载对应的GO 我们可以下载安装版&#xff0c;不过本人习惯下载解压版&#xff0c;这个因个人而异 2、配置环境变量 GOBIN : %GOROOT%\bin GOPATH : D:\MyGo 工作区间 GOROOT : D:\Program Files\Go GOJDK地址PATH: %GOBIN% ; %GOROOT%\bin ; …

生成性人工智能模型:行业和当局的机会和风险

1 目标受众和本文档目的 BSI&#xff08;德国联邦信息安全办公室&#xff09;通过本出版物面向考虑在其工作流程中使用生成性AI模型的公司和当局&#xff0c;以提高对这些模型的基本安全意识&#xff0c;并促进它们的安全使用。为此&#xff0c;除了机会外&#xff0c;它还突出…

Spring Boot整合Redis实现发布/订阅功能

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

Kimichat使用案例012:用Kimichat拆解雷军在小米汽车SU7发布会上的演讲技巧

文章目录 一、介绍二、输入内容三、输出内容四、继续追问五、继续回答六、讲解对比七、对比回答相似之处:不同之处:八、职场人士如何借鉴九、借鉴内容一、介绍 小米SU7发布会可以说是非常成功。雷军的演讲技巧是发布会成功的重要因素之一,很值得借鉴学习。 可以借助Kimichat…

git服务器gitblit安装

1、下载 Gitblit 2、下载完后解压&#xff1a; 3、配制&#xff1a; 保存&#xff0c;退出编辑。 4、运行cmd&#xff0c;启用gitblit。 5、根据运行后的提示&#xff0c;也就是我们之间设置的port9990打开&#xff1a; 输入admin,admin就可以登录&#xff0c;这个账号密码&a…

[大模型]GLM4-9B-chat Lora 微调

本节我们简要介绍如何基于 transformers、peft 等框架&#xff0c;对 LLaMA3-8B-Instruct 模型进行 Lora 微调。Lora 是一种高效微调方法&#xff0c;深入了解其原理可参见博客&#xff1a;知乎|深入浅出 Lora。 这个教程会在同目录下给大家提供一个 nodebook 文件&#xff0c…

IP协议报文格式

IP协议报文格式 一: 报头格式1.1 : 4位版本1.2 : 4位首部长度1.3 : 8位服务类型 :1.4 : 16位总长度(字节数)1.5 : 8位生存时间(TTL)1.6 : 8 位协议1.7 : 32 位源IP / 32 位目的IP 一: 报头格式 1.1 : 4位版本 现在使用的也就只有IPv4,IPv6 1.2 : 4位首部长度 以 4字节为单位…

Xilinx(AMD) vivado对FPGA网表文件进行功能仿真的方法

1 概述 在FPGA开发中很多商用IP核出于知识产权保护的目的&#xff0c;不提供源代码&#xff0c;而是提供综合后的FPGA网表。由于没有源代码&#xff0c;也无法对网表文件直接进行仿真的操作来验证功能&#xff0c;此时需要独立的仿真模型文件。 本文介绍在Xilinx(AMD) vivado软…

【解决问题】QApplication: No such file or directory,C++ 使用Qt或项目未正确加载Cmake报错

运行环境&#xff1a; Clion编译&#xff0c;构建C工程项目报错QApplication: No such file or directory 问题描述 QApplication: No such file or directory 引用的#include <QApplication>飘红 解决方案 1、Qt没有安装正确&#xff0c;请使用对应版本的Qt。或编译…

激光点云配准算法——Cofinet / GeoTransforme / MAC

激光点云配准算法——Cofinet / GeoTransformer / MAC GeoTransformer MAC是当前最SOTA的点云匹配算法&#xff0c;在之前我用总结过视觉特征匹配的相关算法 视觉SLAM总结——SuperPoint / SuperGlue 本篇博客对Cofinet、GeoTransformer、MAC三篇论文进行简单总结 1. Cofine…

Nginx之正向代理配置示例和说明

一、NGINX正向代理功能简介 Nginx的正向代理功能允许局域网中的客户端通过代理服务器访问Internet资源。具体来说&#xff0c;Nginx作为一种流行的Web服务器和反向代理服务器&#xff0c;在正向代理方面的应用也相当实用。以下是其正向代理功能的几个关键点&#xff1a; 访问外…

使用手机做PC机摄像头

准备工作&#xff1a; 带摄像头的安卓手机一部模拟相机软件&#xff1a;Iriun 、DroidCam 、IP摄像头pythonopencv 一、Iriun 1、分别在PC和手机上安装 2、手机和PC在同一个局域网 3、分别打开PC和手机端软件&#xff0c;电脑端就可以使用手机相机 ​ 二、 DroidCam 1、…

开发小Tips:切换淘宝,腾讯,官方,yarn,cnpm镜像源,nrm包管理工具的具体使用方式(方便切换镜像源)

由于开发中经常要下载一些软件或者依赖&#xff0c;且大多数的官方源的服务器都在国外&#xff0c;网速比较慢&#xff0c;国内为了方便&#xff0c;国内一些大厂就建立一些镜像&#xff0c;加快下载速度。 1.各大镜像源的切换&#xff1a; 切换淘宝镜像源&#xff1a; npm …

数据挖掘丨轻松应用RapidMiner机器学习内置数据分析案例模板详解(上篇)

RapidMiner 案例模板 RapidMiner 机器学习平台提供了一个可视化的操作界面&#xff0c;允许用户通过拖放的方式构建数据分析流程。 RapidMiner目前内置了 13 种案例模板&#xff0c;这些模板是预定义的数据分析流程&#xff0c;可以帮助用户快速启动和执行常见的数据分析任务。…