免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
如果看不懂、不知道现在做的什么,那就跟着做完看效果,代码看不懂是正常的,只要会抄就行,抄着抄着就能懂了
内容参考于:易道云信息技术研究院
上一个内容:97.通过逆向分析确认角色信息
码云版本号:8d10ae3508841d3161f046cb569425c298614354
代码下载地址,在 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的分享
以 97.通过逆向分析确认角色信息 它的代码为基础进行修改
效果图:
上一个内容里实现了,角色列表、选择角色列表里的某个角色进入游戏、选择角色列表里的某个角色删除角色,本次就要实现创建角色的功能
首先把创建角色的数据包给拦截掉,不让它给服务端发送,然后方便我们找创建角色时需要的数据包,从而做界面
![]()
然后代码重新编译之后进入游戏,下图是创建默认样子角色的数据包
![]()
修改脸型之后的数据包,然后发现face变成1了如下图红框
![]()
然后上图有两处变成了1,但下图红框位置也变成了1,但是不知道它是什么意思,现在先把它当成脸型
![]()
然后每个都如上图的操作,每点一次就创建一次角色,看它的数据包,写这句话的原因是,每个都搞截一次图没必要浪费空间,也不好观看,然后游戏有多个阵营、职业,每个职业都有按照上方的操作找一遍,看看不同的职业、阵营创建角色(也要分男、女)的数据包的数据有没有不同,把不同的记下来,写我们创建角色的界面时要用,下方就直接上通过一个一个创建角色分析好的数据的答案了(创建角色界面的代码)
![]()
首先插入一个Dialog
![]()
窗口的属性
![]()
控件
![]()
下拉框的属性,四个下拉框属性都是一样的
![]()
下拉框要注意,如下图,鼠标单击倒着的三角形,然后再点击拖动黑色的方框,让下拉框变长,然后其它的控件没有什么特殊的,这里就没有截图,然后由于创建脸型那些东西没什么用,这里就没搞
![]()
然后添加一个类,添加类的时候,可能会报 无法注册类、COM组件的调用返回了错误HRESULT E_FAIL这样的东西,这时把项目放到一个全英文路径下就会好了
![]()
或者用安装程序修复一下,就可以正常创建类了
![]()
或者如下图随便添加一个类
然后再添加类
新加类
![]()
然后给输入框添加变量
![]()
![]()
阵营下拉框添加变量
![]()
![]()
种族下拉框添加变量
![]()
职业下拉框添加变量
![]()
性别下拉框添加变量
![]()
然后给阵营下拉框添加一个值变化的事件
![]()
![]()
种族也给添加一个值变化的事件
![]()
新加CWndCreateRole.h文件
#pragma once
#include "afxdialogex.h"
#include "TextManger.h"
// CWndCreateRole 对话框
class CWndCreateRole : public CDialogEx
{
DECLARE_DYNAMIC(CWndCreateRole)
public:
CWndCreateRole(CWnd* pParent = nullptr); // 标准构造函数
virtual ~CWndCreateRole();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_DIALOG1 };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
DECLARE_MESSAGE_MAP()
virtual BOOL OnInitDialog();
public:
PTextManger text;
CString txtName;
CComboBox cmbCamp;
CComboBox cmbRace;
CComboBox cmbJob;
CComboBox cmbSex;
afx_msg void OnCbnSelchangeCombo1();
int iRace;// = wndCreateRole.cmbRace.GetCurSel();
int iCamp;// = wndCreateRole.cmbCamp.GetCurSel();
int iJob;// = wndCreateRole.cmbJob.GetCurSel();
int iSex;// = wndCreateRole.cmbSex.GetCurSel();
/*
性别 0 男 1 女
阵营 1 艾森赫特 2 格兰蒂尔
种族 1 布冯特人 3 格洛玛人 4 尤恩图人 6 喀什人
职业 1 仲裁者 3秘法师 6 猎魔人 8 元素法师
脸型 0 1 2 3
*/
CString sex[2]{
L"男", L"女"
};
CString camp[2]{
L"1", L"2"
};
CString race[2][2]{
{
L"1", L"3"
},
{
L"4", L"6"
}
};
CString job[2][2]{
{
L"1", L"3"
},
{
L"6", L"8"
}
};
afx_msg void OnCbnSelchangeCombo4();
afx_msg void OnBnClickedOk();
};
新加CWndCreateRole.cpp文件
// CWndCreateRole.cpp: 实现文件
//
#include "pch.h"
#include "CWndCreateRole.h"
#include "Resource.h"
//#include "afxdialogex.h"
// CWndCreateRole 对话框
IMPLEMENT_DYNAMIC(CWndCreateRole, CDialogEx)
CWndCreateRole::CWndCreateRole(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DIALOG1, pParent)
, txtName(_T(""))
{
}
CWndCreateRole::~CWndCreateRole()
{
}
void CWndCreateRole::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, txtName);
DDX_Control(pDX, IDC_COMBO1, cmbCamp);
DDX_Control(pDX, IDC_COMBO4, cmbRace);
DDX_Control(pDX, IDC_COMBO2, cmbJob);
DDX_Control(pDX, IDC_COMBO3, cmbSex);
}
BOOL CWndCreateRole::OnInitDialog()
{
CDialogEx::OnInitDialog();
for (CString& txt : camp)
{
CStringA scamp;
scamp = txt;
scamp = "camp_" + scamp;
// 读取阵营的中文名,然后放到阵营下拉框里
cmbCamp.AddString(text->ReadTextById(scamp));
}
for (CString& txt : sex)
{
// 给性别下拉框添加值
cmbSex.AddString(txt);
}
// 下拉框默认选中下标0位置的值,也就是第一个值
cmbCamp.SetCurSel(0);
cmbSex.SetCurSel(0);
OnCbnSelchangeCombo1();
return TRUE;
}
BEGIN_MESSAGE_MAP(CWndCreateRole, CDialogEx)
ON_CBN_SELCHANGE(IDC_COMBO1, &CWndCreateRole::OnCbnSelchangeCombo1)
ON_CBN_SELCHANGE(IDC_COMBO4, &CWndCreateRole::OnCbnSelchangeCombo4)
ON_BN_CLICKED(IDOK, &CWndCreateRole::OnBnClickedOk)
END_MESSAGE_MAP()
// CWndCreateRole 消息处理程序
void CWndCreateRole::OnCbnSelchangeCombo1()
{
int nsel = cmbCamp.GetCurSel();
if (nsel > -1) {
cmbRace.ResetContent();
cmbJob.ResetContent();
for (CString& txt : race[nsel])
{
CStringA scamp;
scamp = txt;
scamp = "race_" + scamp;
// 读取种族的中文名,然后放到种族下拉框里
cmbRace.AddString(text->ReadTextById(scamp));
}
cmbRace.SetCurSel(0);
OnCbnSelchangeCombo4();
}
}
void CWndCreateRole::OnCbnSelchangeCombo4()
{
int nsel = cmbCamp.GetCurSel();
if (nsel > -1) {
cmbJob.ResetContent();
for (CString& txt : job[nsel])
{
CStringA scamp;
scamp = txt;
scamp = "job_" + scamp;
// 读取职业的中文名,然后放到职业下拉框里
cmbJob.AddString(text->ReadTextById(scamp));
if ((nsel == 0) && (cmbRace.GetCurSel() == 1)) {
return;
}
}
cmbJob.SetCurSel(0);
}
}
void CWndCreateRole::OnBnClickedOk()
{
UpdateData(TRUE);
iRace = cmbRace.GetCurSel();
iCamp = cmbCamp.GetCurSel();
iJob = cmbJob.GetCurSel();
iSex = cmbSex.GetCurSel();
CDialogEx::OnOK();
}
NetClient.cpp文件的修改:修改了 CreateRole函数(修改了一个变量名)
#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);
}
//性别 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 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));
}
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 WinSock->OnSend(&_buff.op, ilen);
}
void NetClient::Teleport(float x, float h, float y, float face)
{
// 修正坐标
//PAIM aim = Client->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不成功,这种的可以多试几个修改坐标或者其它数据的函数
如果还都不行那就没办法了
*/
//Client->MoveWJump(v.fNan, v.fNan, v.fNan, v.fNan, v.fNan, v.fNan, v.fNan); // 可以把坐标设置成nan
//Client->MoveWalk(v.fNan, v.fNan, v.fNan, v.fNan, v.fNan, v.fNan, v.fNan); // 可以把坐标设置成nan
Client->MoveStop(v.fNan, v.fNan, v.fNan, v.fNan); // 可以把坐标设置成nan
Client->MoveStop(x, h, y, face);
// 利用修正坐标数据包瞬移
Client->SetCoord(Client->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 WinSock->OnSend(&_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 WinSock->OnSend(&_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 WinSock->OnSend(&_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 WinSock->OnSend(&_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 WinSock->OnSend(&_buff.op, ilen);
}
PROLEDATA NetClient::GetRoleByName(const wchar_t* rolename)
{
//PROLEDATA result = nullptr;
for (int i = 0; i < rolecount; i++)
{
// StrCmpW判断两个字符串是否相同
// 比较时区分大小写,如果字符串相同返回0
if (StrCmpW(roles[i].name.value(), rolename) == 0) {
return &roles[i];
}
}
return nullptr;
}
bool NetClient::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::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();
}
}
}
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(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::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;
}
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 WinSock->Recv(&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 WinSock->Recv(&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 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;
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::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 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_HEAD)-1;
//#ifdef Anly
// CStringA szTxtA;
// CStringA szTmp;
//#endif // Anly
//#ifdef Anly
// szTmp.Format("[%X]%s:%d\r\n", i, ObjectTable[i].name, ObjectTable[i].type);
// szTxtA += szTmp;
//#endif // Anly
//#ifdef Anly
// anly->SendData(TTYPE::I_DIS, S_OBJECT, szTxtA.GetBuffer(), szTxtA.GetAllocLength() + 1);
//#endif // Anly
for (int i = 0; i < head->count; i++)
{
ObjectTable[i].name = buffStart;
buffStart = buffStart + strlen(ObjectTable[i].name) + 1;
ObjectTable[i].type = buffStart[0];
buffStart++;
}
#ifdef Anly
GameAnlyer->CreateObjectfiles(ObjectTable, head->count);
#endif // Anly
return true;
}
bool NetClient::OnSverStruct(char*& buff, unsigned& len) {
PNR_HEAD head = (PNR_HEAD)(buff - 1);
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 Anly
GameAnlyer->CreateStructfile(StructTable, head->count);
#endif // Anly
return true;
}
bool NetClient::OnSvrObjectInit(char*& buff, unsigned& len)
{
/*
00 00 00 00 00 00 00 为了内存对齐补充了7个0,也就是 un1[6] 和 len
28 op
CD 48 00 01 62 A7 DE 04 PNR_OBJINIT:lId
C1 AA FB C3 PNR_OBJINIT:x;
3D FF 22 41 PNR_OBJINIT:h;
D7 0B 4A 44 PNR_OBJINIT:y;
52 B8 06 40 PNR_OBJINIT:face;
C1 AA FB C3 PNR_OBJINIT:tx;
3D FF 22 41 PNR_OBJINIT:th;
D7 0B 4A 44 PNR_OBJINIT:ty;
52 B8 06 40 PNR_OBJINIT:tface;
00 00 00 00 PNR_OBJINIT:un2[0]
00 00 00 00 PNR_OBJINIT:un2[1]
00 00 00 00 PNR_OBJINIT:un2[2]
00 00 00 00 PNR_OBJINIT:un2[3]
00 00 00 00 PNR_OBJINIT:un2[4]
61 00 PNR_OBJINIT:icount;
1B 00 (char*)head + sizeof(NR_OBNJECT_INIT) - 2;也就是指向到了 PNR_OBJINIT:type 这个位置
0C 00 00 00 CA 4E 5A 66 53 62 01 80 4E 86 00 00 1D 00
*/
// 初始化对象
PNR_OBJINIT head = (PNR_OBJINIT)(buff - 7);
char* buffStart = (char*)head + sizeof(NR_OBNJECT_INIT) - 2;
//int nStart = (int)&Player.lId;
//int nEnd = (int)&Player.endclass;
//memcpy(&Player.lId, &head->lId, nEnd - nStart);
Player.SetHeadDatas(&head->lId);
#ifdef Anly
GameAnlyer->AnlyData(buffStart, buff + len, head->icount, S_OBJECT_INIT, ObjectTable);
#endif
int iProc = 0;
while (iProc < head->icount)
{
Player.UpdateData(buffStart);
iProc++;
}
return true;
}
bool NetClient::OnSvrObjectInitEx(char*& buff, unsigned& len)
{
// 初始化对象
PNR_OBJINITEX head = (PNR_OBJINITEX)(buff - 5);
char* buffStart;
/*int nStart = (int)&Player.lId;
int nEnd = (int)&Player.endclass;
memcpy(&Player.lId, &head->lId, nEnd - nStart);*/
int iObjectCount = 0;
short objCount = head->obj_count; // 可以理解为一共有多少个接收的28数据
while (iObjectCount < objCount) {
PAIM _paim = GetAimById(head->lId);
_paim->SetHeadDatas(&head->lId);
buffStart = (char*)head + sizeof(NR_OBNJECT_INITEX) - 2;
short refCount = head->icount; // 接收的28数据里的数据参数个数
short iref = 0;
#ifdef Anly
GameAnlyer->AnlyData(buffStart, buff + len, refCount, S_OBJECT_INITEX, ObjectTable);
#endif
while (iref < refCount) {
_paim->UpdateData(buffStart);
iref++;
}
head = (PNR_OBJINITEX)(buffStart - 8);
iObjectCount++;
}
return true;
}
bool NetClient::OnSvrUpdateCord(char*& buff, unsigned& len)
{
/*
00 00 00 00 00 head
21 02 00
CD 14 00 01 CD 14 00 00 第一个坐标数据
6D BF 54 43
A6 FA C7 3F
8C 52 A9 C1
CB 30 06 40
00 00 00 00
DB 0F C9 41
00 00 00 00
00 00 00 00
01 00 00 00 State
CD 14 00 01 CD 14 00 00 第二个坐标数据
61 41 5B 43
A6 FA C7 3F
C5 8A C7 C1
CB 30 06 40
9A 99 99 3E
DB 0F C9 40
00 00 00 00
00 00 00 00
01 00 00 00
*/
// 初始化对象
PNR_OBJINITEX head = (PNR_OBJINITEX)(buff - 5);
/*int nStart = (int)&Player.lId;
int nEnd = (int)&Player.endclass;
memcpy(&Player.lId, &head->lId, nEnd - nStart);*/
int iObjectCount = 0;
short objCount = head->obj_count; // 可以理解为一共有多少个接收的28数据
while (iObjectCount < objCount) {
PAIM _paim = GetAimById(head->lId);
_paim->SetHeadDatas(&head->lId);
CStringA txtA;
txtA.Format("x:%f h:%f y:%f", head->x, head->h, head->y);
#ifdef Anly
anly->SendData(TTYPE::I_DIS, S_UPDATECORD, txtA.GetBuffer(), txtA.GetAllocLength() + 1);
#endif
head = (PNR_OBJINITEX)((char*)&head->State - 4);
iObjectCount++;
}
return true;
}
bool NetClient::OnSvrUpdateProperty(char*& buff, unsigned& len)
{
// 初始化对象
PNR_OBJECT_UP head = (PNR_OBJECT_UP)(buff - 6);
PAIM _paim = GetAimById(head->lId);
int iref = 0;
short refcount = head->icount;
#ifdef Anly
char* buffStart = (char*)&head->itype;
GameAnlyer->AnlyData(buffStart, buff + len, refcount, S_UPDATEPRO, ObjectTable);
#endif
while (iref < refcount) {
_paim->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 objCount
9B 49 00 01 D5 8C 98 05 GetAimById(head->lId);
02 00
25 00 (char*)&head->itype
18 00 00 00
29 00 BA 01 00 00
while (iobjCount < objCount) 里获取的是
9B 49 00 01 D5 8C 98 05
02 00
25 00 18 00 00 00
29 00 BA 01 00 00 这一块数据
while (iref++ < refCount) 里获取的是
25 00 18 00 00 00
(PNR_OBJECT_UPMU)(buffStart - 8); 移动到下一个
9B 49 00 01 D5 8C 98 05 GetAimById(head->lId);
02 00
25 00 (char*)&head->itype
18 00 00 00
29 00 BA 01 00 00 这个数据
*/
while (iobjCount < objCount) {
PAIM _paim = GetAimById(head->lId);
buffStart = (char*)&head->itype;
short refCount = head->icount; // 接收的28数据里的数据参数个数
short iref = 0;
#ifdef Anly
GameAnlyer->AnlyData(buffStart, buff + len, refCount, S_UPDATEPROMU, ObjectTable);
#endif
while (iref++ < refCount) {
_paim->UpdateData(buffStart);
}
head = (PNR_OBJECT_UPMU)(buffStart - 8);
iobjCount++;
}
return true;
}
bool NetClient::OnSvrRemoveObjectMu(char*& buff, unsigned& len)
{
// 初始化对象
PNR_OBJECT_REMOVEMU head = nullptr;
head = (PNR_OBJECT_REMOVEMU)(buff - &head->op);
int iobjCount = head->objCount;
for (int i = 0; i < iobjCount; i++)
{
RemoveAimById(head->lId[i]);
}
return true;
}
/*
1F
87 44 00 01 7F B0 D6 05
FE AD DE C3
6C F5 46 42
07 FD 36 C4
90 D9 9C 40
*/
bool NetClient::OnSvrUpdateCordEx(char*& buff, unsigned& len)
{
// 初始化对象
PNR_UPDATECOORD head = nullptr;
head = (PNR_UPDATECOORD)(buff - &head->op);
PAIM aim = GetAimById(head->lId);
if (aim) {
return aim->SetHeadCord(&head->lId);
}
return true;
}
bool NetClient::OnSvrGameBase(char*& buff, unsigned& len)
{
// 初始化对象
PNR_GAMEBASE head = nullptr;
head = (PNR_GAMEBASE)(buff - &head->op);
char* buffStart = (char*)head + sizeof(NR_GAMEBASE);
//int nStart = (int)&Player.lId;
//int nEnd = (int)&Player.endclass;
//memcpy(&Player.lId, &head->lId, nEnd - nStart);
//Player.SetHeadDatas(&head->lId);
#ifdef Anly
CStringA szTxt;
szTxt.Format("%X %X", head->type, head->index);
anly->SendData(TTYPE::I_DIS, S_GAMEBASE, szTxt.GetBuffer(), szTxt.GetAllocLength() + 1);
GameAnlyer->AnlyData(buffStart, buff + len, head->count, S_GAMEBASE, ObjectTable);
#endif
/* int iProc = 0;
while (iProc < head->icount)
{
Player.UpdateData(buffStart);
iProc++;
}*/
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);
}
NetClient.h文件的修改:修改 roles变量为所有人都可以访问的公共访问权限、rolecount变量为所有人都可以访问的公共访问权限、logined变量为所有人都可以访问的公共访问权限
#pragma once
#include "NetClass.h"
#include "GameWinSock.h"
#include "AIM.h"
#define CAMP_NAME_QH "xuanrenQH"
#define CAMP_NAME_ZE "xuanrenZQ"
#define MAX_AIM 0x200
class NetClient // 监视客户端每一个操作
{
typedef bool (NetClient::* DATAPROC)(char*&, unsigned&);
public:
AIM Player; // 玩家角色
/*
怪物列表,最好排序,使用lId排序,然后使用二分查找实现快速查询
结构用链表或者数组都可以
怪物或附近玩家这种东西不可能会很多
如果太多电脑处理不过来,所以数组设置为0x100大小差不多够用
*/
PAIM Aimlst[MAX_AIM]{};
// 用来控制飞天
bool HideMode;
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&);
protected: // 消息处理函数-SEND
bool OnClientlogin(char*& buff, unsigned& len); // 登录数据包的处理 I_LOGIN
bool OnClientStartCreateRole(char*& buff, unsigned& len); // 申请进入创建角色界面 I_CREATEROLE_START
bool OnClientDelRole(char*& buff, unsigned& len);
bool OnClientSendCustom(char*& buff, unsigned& len);
bool OnClientCreateRole(char*& buff, unsigned& len);
bool OnClientSelectRole(char*& buff, unsigned& len);
protected: // 消息处理函数-RECV
bool OnSvrTips(char*& buff, unsigned& len);
bool OnSvrloginOk(char*& buff, unsigned& len);
bool OnSvrStartCreateRole(char*& buff, unsigned& len);
bool OnSverNotice(char*& buff, unsigned& len);
bool OnSverObject(char*& buff, unsigned& len);
bool OnSverStruct(char*& buff, unsigned& len);
bool OnSvrObjectInit(char*& buff, unsigned& len);
bool OnSvrObjectInitEx(char*& buff, unsigned& len);
bool OnSvrUpdateCord(char*& buff, unsigned& len);
bool OnSvrUpdateProperty(char*& buff, unsigned& len);
bool OnSvrUpdatePropertyMu(char*& buff, unsigned& len);
bool OnSvrRemoveObjectMu(char*& buff, unsigned& len);
bool OnSvrUpdateCordEx(char*& buff, unsigned& len);
bool OnSvrGameBase(char*& buff, unsigned& len);
public:
PROLEDATA roles;
unsigned rolecount;
bool logined = false;
private:
GameWinSock* WinSock;
bool DelRole(const wchar_t* rolename, unsigned _len);
PAIM GetAimById(long long lId);
PAIM CreateAim();
void RemoveAimById(long long lId);
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(GameWinSock * _winSock);
// 模拟接收的数据包
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);
// 根据角色名字获取一个登录成功数据包(选择角色列表里的一个数据)
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);
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);
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);
};
CUIWnd_0.cpp文件的修改:修改了 OnBnClickedButton1函数(创建角色按钮点击事件处理函数)
// CUIWnd_0.cpp: 实现文件
//
#include "pch.h"
#include "htdMfcDll.h"
#include "CUIWnd_0.h"
#include "afxdialogex.h"
#include "extern_all.h"
#include "GameOBJECTDef.h"
// CUIWnd_0 对话框
float _xNext = 0.0f;
float _yNext = 0.0f;
void _stdcall loops(HWND, UINT, UINT_PTR, DWORD) {
PAIM aim = Client->GetAimByName(L"r");
if (aim == nullptr) {
return;
}
//AfxMessageBox(aim->Name);
float xDis = fabs(Client->Player.x - aim->x);
float hDis = fabs(Client->Player.h - aim->h);
float yDis = fabs(Client->Player.y - aim->y);
float xNext, yNext, thisx, thisy;
int v[2]{ -1, 1 };
if (xDis > 12) {
// 强制修改角色x坐标的值
thisx = Client->Player.x;
Client->Player.x = Client->Player.x + 6 * v[Client->Player.x < aim->x];
xNext = Client->Player.x;
}
else {
xNext = aim->x;
}
if (yDis > 12) {
// 强制修改角色y坐标的值
thisy = Client->Player.y;
Client->Player.y = Client->Player.y + 6 * v[Client->Player.y < aim->y];
yNext = Client->Player.y;
}else{
yNext = aim->y;
}
if ((xDis < 2)&&(hDis < 2)&&(yDis < 2)) {
Client->MoveStop(aim->x, aim->h, aim->y, 0.0f);
}
else {
Client->MoveWalk(Client->Player.x,aim->h, Client->Player.x, 0.0f, 0.0f, xNext, yNext);
}
}
IMPLEMENT_DYNAMIC(CUIWnd_0, CDialogEx)
CUIWnd_0::CUIWnd_0(CUI* _netUI, CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_PAGE_0, pParent)
{
netUI = _netUI;
}
CUIWnd_0::~CUIWnd_0()
{
}
void CUIWnd_0::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST2, lstRole);
}
BOOL CUIWnd_0::OnInitDialog()
{
CDialogEx::OnInitDialog();
netUI->SetListView(&lstRole);
lstRole.InsertColumn(0, L"序号", 0, 50);
lstRole.InsertColumn(1, L"名字", 0, 160);
lstRole.InsertColumn(2, L"等级", 0, 50);
lstRole.InsertColumn(3, L"性别", 0, 40);
lstRole.InsertColumn(4, L"阵营", 0, 130);
lstRole.InsertColumn(5, L"场景", 0, 130);
lstRole.InsertColumn(6, L"种族", 0, 130);
lstRole.InsertColumn(7, L"职业", 0, 130);
return TRUE;
}
BEGIN_MESSAGE_MAP(CUIWnd_0, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON1, &CUIWnd_0::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &CUIWnd_0::OnBnClickedButton2)
ON_BN_CLICKED(IDC_BUTTON3, &CUIWnd_0::OnBnClickedButton3)
END_MESSAGE_MAP()
// CUIWnd_0 消息处理程序
void CUIWnd_0::OnBnClickedButton1()
{
if (netUI->rolecount > 4) return;// 这里说明角色创建满了
wndCreateRole.text = netUI->getTxtManger();
if (wndCreateRole.DoModal() == IDOK) {
double camp[2]{ 1.0, 2.0 };
double race[2][2]{
{1.0, 3.0},
{4.0, 6.0}
};
double job[2][2]{
{1, 3},
{6, 8}
};
CStringA campTxt[2]{
"zq", "qh"
};
CStringA sexTxt[2]{
"M", "F"
};
CStringA raceTxt[2][2]{
{"hum", "dwa"},
{"hum", "kosh"}
};
// gui\BG_team\TeamRole\Teamrole_zq_humF_001.PNG
CStringA photo = "gui\\BG_team\\TeamRole\\Teamrole_${camp}_${race}${sex}_001.PNG";
CStringA infos = "Face,1;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;";
CStringA details = "";
int iRace = wndCreateRole.iRace;
int iCamp = wndCreateRole.iCamp;
int iJob = wndCreateRole.iJob;
int iSex = wndCreateRole.iSex;
photo.Replace("${camp}", campTxt[iCamp]);
photo.Replace("${race}", raceTxt[iCamp][iRace]);
photo.Replace("${sex}", sexTxt[iSex]);
netUI->CreateRole(
wndCreateRole.txtName.GetBuffer(),
iSex,
camp[iCamp],
race[iCamp][iRace],
job[iCamp][iJob],
photo.GetBuffer(),
infos.GetBuffer(),
details.GetBuffer(),
0.0
);
}
// 释放技能
//float x = Client->Player.x;
//float h = Client->Player.h;
//float y = Client->Player.y;
//Client->UseSkill("Skill_Mg2692_01_1", x, h, y, Client->Player.face);
// 发送坠落数据包
//Client->Fall();
设置移动速度
// float Speed = 30.0;
// Client->SetProperty(Client->Player.lId, INDEX_MoveSpeed, &Speed);
/*
// 修改血量
int HP = 10000000;
Client->SetProperty(Client->Player.lId, INDEX_HP, &HP);*/
/*CString txt;
txt.Format(L"通过AIM类获取角色信息 名字:%s,x坐标:%f", Client->Player.Name, Client->Player.x);
AfxMessageBox(txt);*/
// Client->HeartBeep();
// Client->TalkTo(L"r", L"打架吗?");
// Client->Talk(L"[欢迎来到麟科思]");
// Client->SelectRole(L"今晚打老虎");
/*Client->CreateRole(L"am4", 1.0, 2.0, 4.0, 8.0, "gui\BG_team\TeamRole\Teamrole_zq_humF_001.PNG",
"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;",
"", 0.0);*/
//Client->SelectCamp("xuanrenZQ");
//Client->StartCreateRole();
//Client->DelRole(L"ranzhi11111");
/*
char buff[] = {
0xA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x4, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 00 ,0x00,
0x00, 0x07, 0x0E, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x31, 0x00, 0x32 ,0x00,
0x33, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
WinSock->OnSend(buff, sizeof(buff));
*/
/*char buff[] = {
0x27, 0x46, 0x92, 0x02, 0x00, 0x00, 0x89, 0x02, 0x00, 0x00, 0x06, 0x00, 0x06, 0x05,
0x00, 0x00, 0x00, 0x63, 0x68, 0x61, 0x74, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x07,
0x0A, 0x00, 0x00, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x39, 0x00, 0x00, 0x00,
0x07, 0x5A, 0x02, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31,
0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32,
0x00, 0x32, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33,
0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35,
0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34,
0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36,
0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36,
0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36,
0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x37,
0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00,
0x38, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00
};
WinSock->OnRecv(buff, sizeof(buff));*/
}
//TextManger txtMangerMain;
void CUIWnd_0::OnBnClickedButton2()
{
int index = lstRole.GetSelectionMark();
if (index > -1) {
CString txtName = lstRole.GetItemText(index, 1);
netUI->DelRole(txtName.GetBuffer());
}
// 根据语言表获取文字
//CString txt = txtManger->ReadTextById("npc_GRZJ_0001");// PGameProc->GetTextName("npc_GRZJ_0001");
//AfxMessageBox(txt);
//txtManger->CreateTextDat("F:\\语言包.txt");
// 读取导出到本地的语言包
//txtMangerMain.LoadTextData("F:\\语言包.txt");
// CString txt = txtMangerMain.ReadTextById("npc_GRZJ_0001");// npc_GRZJ_0001没有哈希冲突
/*
Hui_event_3_1有哈希冲突,通过打印 textDatas 变量的内存地址
去x96db找的(第一个就冲突了,ui_event_3_1是第一个哈希表里的链表的最后一个)
如果第一个没有冲突,那就找一个 next 不是 -1 的,不是-1说明是链表,然后找链表最后一个就可以了
*/
/*CString txt = txtMangerMain.ReadTextById("ui_event_3_1");
AfxMessageBox(txt);
txt = txtMangerMain.ReadTextById("desc_Item_A550001_01_2");
AfxMessageBox(txt);*/
// 瞬移
//float decX = 3843.776123f;
//float decH = 11.731983f;
//float decY = -2005.533813f;
//float face = 0.0f;
//Client->Teleport(decX, decH, decY, face);
//PAIM aim = Client->GetAimByName(L"r");
//if (aim == nullptr) {
// return;
//}
//Client->Teleport(aim->x, aim->h, aim->y, aim->face);
// 修正坐标
/*PAIM aim = Client->GetAimByName(L"r");
if (aim == nullptr) {
return;
}
Client->SetCoord(Client->Player.lId, aim->x, aim->h, aim->y, aim->face);*/
// 面向
// Client->FaceTo(L"r");
// 飞天
// Client->HideMode = true;
// 遁地
//Client->MoveStop(Client->Player.x, Client->Player.h - 5, Client->Player.y, 0.0f);
// 跟随
// ::SetTimer(m_hWnd, 0x1000, 2000, loops);
//
// Client->Backtoroles();
}
// 进入游戏按钮点击事件处理函数
void CUIWnd_0::OnBnClickedButton3()
{
int index = lstRole.GetSelectionMark();
if (index > -1) {
CString txtName = lstRole.GetItemText(index, 1);
netUI->SelectRole(txtName.GetBuffer());
}
}
CUIWnd_0.h文件的修改:新加 wndCreateRole变量
#pragma once
#include "afxdialogex.h"
#include "CWndCreateRole.h"
class CUI;// 如果引入CUI.h文件,会冲突,所以这里自己搞一个CUI类型
class CWndCreateRole;
// CUIWnd_0 对话框
class CUIWnd_0 : public CDialogEx
{
DECLARE_DYNAMIC(CUIWnd_0)
public:
CUIWnd_0(CUI* _netUI, CWnd* pParent = nullptr); // 标准构造函数
virtual ~CUIWnd_0();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_PAGE_0 };
#endif
private:
CUI* netUI;
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
virtual BOOL OnInitDialog();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedButton1();
afx_msg void OnBnClickedButton2();
CListCtrl lstRole;
afx_msg void OnBnClickedButton3();
CWndCreateRole wndCreateRole;
};