81.网络游戏逆向分析与漏洞攻防-移动系统分析-飞天遁地的实现与面向计算

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

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

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

上一个内容:80.利用数据包尝试穿墙客

码云版本号:7fb0ea367d8569cb4ddbb6b5bf202e0be056a0ab

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

以 80.利用数据包尝试穿墙客 它的代码为基础进行修改

在观察它移动数据包的时候会发现15 0 4这个数据包,它的高度是可以修改的,但是不知道它修改的值有多大,所以尝试在它原有的数据上进行加或减操作

然后发现,服务器不愿意,服务器直接给我发了一个1F数据包重置了我们的坐标

现在的代码是一次加50

改成减少的跳,变成一次加5

然后就成功了

还能继续往上升

所以不能一次加的很多,可以一次加的少点,一步一步往上升

然后遁地加5改成减5

可以正常遁地

遁地的距离长了,我们的客户端也看不到角色了

但是有一个问题飞天的时候,只要一移动方向或者手动移动了一次,它就会回到地面,这个问题要通过修改数据包来解决,经过测试发现走路很卡顿,猜测加的高度太狠的原因

由加10改成了加5,然后走路就不卡顿了

效果图

然后到这飞天遁地穿墙这些功能都有了,接下来有一个面向需要计算

面向范围之前猜测是0-6.3,那这个东西是个什么来头呢?所有的面向它解决的是,如下图我们角色的坐标

然后下图绿色的是目标的坐标,那么我们要怎么计算这个面向?

它一般反映的是,下图蓝色部分的角度,这个角度用tan(正弦)、sin(正切)、cos(余弦)这三个任意一个都可以计算,这是数学里的三角函数

也就是通过使用下图两个粉色框所示的边长,然后要该怎么使用?

怎么计算看下图粉色的,也就是x1-x,y1-y,然后用减完得到的坐标,这里使用tan(正切)来计算角度,然后首先目标x坐标减我们的x坐标,目标y坐标减我们的y坐标,然后减完之后就会得到新的x,y然后拿着这两个边的比(两个边的比指的是一个角度,也就是做tab(正切)),然后在对这个角度做反三角函数,然后就能得到面向了

然后用角度求出来的结果,它的取值范围正常来讲应该是3.6或者3.6的倍数,只有这样才是正常的,因为角度是一度一度的一共有360个度,然后还有一种是弧度,弧度也就是求弧长,它这个跟π圆周率有关,弧度求出来特征表现就是3.14(π)或3.14(π)的倍数,然后游戏里的坐标它也正好是6.几,所以它是用π来求的,之前猜测面向的取值范围是0-6.3这是错的,它实际上是3.14 + 3.14 = 6.28,也就是它实际上是0-6.28这样的一个范围,然后计算弧长它本质上与角度计算是一个东西,然后如下图,绿色的是目标坐标,红色的是我们的坐标,然后c++里有一个atan2函数把x1-x=nx,y1-y=ny,把这个nx,ny传给atan2函数就能得到了

atan2函数它的结果,也就是0-π-负π-0,带入数字就是 0-3.14-负3.14-0,atan2函数返回的值就是0-3.14-负3.14-0这之间的一个数,游戏中是0-π-π加一点-2π,带入数字0-3.14-3.15-6.28-0游戏中是这样的

然后转换成游戏中的方式是2π-p,这个p指的是atan2函数返回值,这是三四象限的计算方式,一二象限的值直接是个正数直接就是atan2函数返回值就可以了

如果上面的计算方式看不懂没关系,因为这是三角形相关的数学题,不懂就记住下方代码,下方代码返回值就是面向的值,

// 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...
 if (x < 0) {// 如果x是负数atan2函数返回值必定是在三四象限里,也就是一个负π
     p = pi * 2 + p;
 }
 return p;
}

下图是通过必应搜索 atan2函数 关键字找到的

CUIWnd_0.cpp文件的修改:修改了 OnBnClickedButton2函数、loops函数

// 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(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PAGE_0, pParent)
{

}

CUIWnd_0::~CUIWnd_0()
{
}

void CUIWnd_0::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CUIWnd_0, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON1, &CUIWnd_0::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON2, &CUIWnd_0::OnBnClickedButton2)
END_MESSAGE_MAP()


// CUIWnd_0 消息处理程序


void CUIWnd_0::OnBnClickedButton1()
{
	// 发送坠落数据包
	Client->Fall();
	
	 设置移动速度
	//float Speed = 50.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));*/
}


void CUIWnd_0::OnBnClickedButton2()
{
	// 面向
	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();
}

NetClient.cpp文件的修改:修改了 MoveWalk函数、OnMoveStop函数、OnMoveJump函数、OnMoveJump函数、OnMoveWJump函数,新加GetFace函数、FaceTo函数

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

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

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

bool NetClient::StartCreateRole()
{
    NET_CREATEROLE_START _data;
    return WinSock->OnSend(&_data.op, _data.len);
}

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

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

bool NetClient::CreateRole(wchar_t* name, double sex, double camp, double face, double occu, const char* photo, const char* infos, const char* txt, double faceShape)
{
    // rolecount > 4说明角色的数量够了
    if (rolecount > 4)return false;
    int index = 0;
    bool roleindex[5]{true, true, true, true, true };
    for (int i = 0; i < rolecount; i++) {
        roleindex[roles[i].index] = false;
    }
   
    for (int i = 0; i < 5; i++)
    {
        if (roleindex[i]) {
            index = i;
            break;
        }
    }

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

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

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);

}

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::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);
}

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...
    
    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::OnSverStruct;
    RecvProc[S_CREATEROLE_START] = &NetClient::OnSvrStartCreateRole;
    RecvProc[S_NOTICE] = &NetClient::OnSverNotice;
    RecvProc[S_NOTICE_COM] = &NetClient::OnSverNotice;
    RecvProc[S_OBJECT] = &NetClient::OnSverObject;
    RecvProc[S_STRUCT] = &NetClient::OnSverStruct;
    RecvProc[S_OBJECT_INIT] = &NetClient::OnSvrObjectInit;
    RecvProc[S_OBJECT_INITEX] = &NetClient::OnSvrObjectInitEx;
    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;
}

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


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

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

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

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

    return true;
}

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

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

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


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

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

    Onlogin(_id, _pass);

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

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

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

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

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

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

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


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

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

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

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

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

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

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

}

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

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

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



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

// 接收数据截取区

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

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

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

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

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

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

    if (ObjectTable) {
        delete[] ObjectTable;
    }

    if (ObjectTxt) {
        delete[] ObjectTxt;
    }

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

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



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

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

    //int nStart = (int)&Player.lId;
    //int nEnd = (int)&Player.endclass;
    //memcpy(&Player.lId, &head->lId, nEnd - nStart);
    Player.SetHeadDatas(&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;
}
/*
 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文件的修改:新加 HideMode变量(用来控制飞天)、GetFace函数、FaceTo函数,修改了 MoveWalk函数

#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:
	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);
private:
	GameWinSock* WinSock;
	PROLEDATA roles;
	unsigned rolecount;
	bool logined = false;

	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 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);// 选择阵营
	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();
	// 模拟停止
	bool MoveStop(float x, float h, float y, float face);
	// 移动开始
	bool MoveWalk(float x, float h, float y, float face, float oldy, float xNext, float yNext);
	// 根据角色名字获取一个登录成功数据包(选择角色列表里的一个数据)
	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);

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);

};



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

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

相关文章

服装定制|基于SSM+vue的服装定制系统的设计与实现(源码+数据库+文档)

服装定制系统 目录 基于SSM&#xff0b;vue的服装定制系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3用户后台管理模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xf…

【CTF Web】QSNCTF 文章管理系统 Writeup(SQL注入+Linux命令+RCE)

文章管理系统 题目描述 这是我们的文章管理系统&#xff0c;快来看看有什么漏洞可以拿到FLAG吧&#xff1f;注意&#xff1a;可能有个假FLAG哦 解法 SQL 注入。 ?id1 or 11 --取得假 flag。 爆库名。 ?id1 union select 1,group_concat(schema_name) from information_sch…

【机器学习】集成学习在信用评分领域实例

集成学习在信用评分领域的应用与实践 一、引言二、集成学习的概念与原理三、集成学习在信用评分中的应用实例四、总结与展望 一、引言 在当今金融数字化快速发展的时代&#xff0c;信用评分成为银行、金融机构等评估个人或企业信用风险的重要工具。然而&#xff0c;单一的信用评…

飞天使-k8s知识点31-rancher的正确打开方式

文章目录 安装之前优化一下内核参数以及系统内核版本 rancher安装主要是使用以下命令nginx的配置为解决办法 安装之前优化一下内核参数以及系统内核版本 内核版本 4.17 cat > /etc/modules-load.d/iptables.conf <<EOF ip_tables iptable_filter EOF 然后重启服务器…

STM32快速入门(定时器之输出PWM波形)

STM32快速入门&#xff08;定时器之输出PWM波形&#xff09; 前言 本节主要讲解STM32利用通用定时器&#xff0c;利用CCR和CNT寄存器&#xff0c;输出指定占空比和频率的PWM波形。其功能的应用有&#xff1a;实现LED呼吸灯的效果、控制步进电机、控制直流电机转速等。 导航 …

基于FPGA的NC图像质量评估verilog实现,包含testbench和MATLAB辅助验证程序

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 vivado2019.2和matlab2022a测试&#xff0c;结果如下&#xff1a; 2.算法运行软件版本 vivado2019.2 matlab2022a 3.部分核心程序 timescale …

解决vue3项目打包后部署后某些静态资源图片不加载问题

目录 问题 原因 解决方案 问题 开发完项目打包并部署 然后访问时发现导航栏背景图片没加载 打开浏览器控制台发现这张图片报错404 原因 可能是因为在部署后的服务器环境中对中文文件名的支持不完善。服务器在解析 URL 时可能无法正确识别或编码中文字符&#xff0c;导致无…

高精度原理介绍及代码实现

目录 高精度 引入 使用场景 实现原理 高精度加法 数据存储 加法实现 总代码 高精度减法 与加法的不同点&#xff1a; 总代码 高精度乘法 总代码 高精度除法 总结 总注意点 减法注意点 高精度 引入 所谓高精度并不是很高级难懂的东西&#xff0c;只是对传统的…

Kubernetes 核心概念

kubernetes的对象 Kubernetes 包含多种类型的资源对象&#xff1a;Pod、Label、Service、Replication Controller 等。 所有的资源对象都可以通过 Kubernetes 提供的 kubectl 工具进行增、删、改、查等操作&#xff0c;并将其保存在 etcd 中持久化存储。 Kubernets其实是一个…

【JAVA进阶篇教学】第十三篇:Java中volatile关键字讲解

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第十三篇&#xff1a;volatile关键字讲解。 在 Java 中&#xff0c;volatile关键字是一种轻量级的同步机制&#xff0c;用于确保变量的可见性和禁止指令重排序。本文将详细解释volatile关键字的工作原理、可见性保证以及…

对称加密介绍

一、什么是对称加密 对称密钥算法(Symmetric-key algorithm)&#xff0c;又称为对称加密、私钥加密、共享密钥加密&#xff0c;是密码学中的一类加密算法。 对称加密的特点是&#xff0c;在加密和解密时使用相同的密钥&#xff0c;或是使用两个可以简单地相互推算的密钥。 这…

Zynq开发-使用PYNQ快速入门摄像头MIPI驱动(OV5640)-overlay设计

目录 1. 简介 2. Overlay 设计 2.1 总体设计 2.2 MIPI 子系统 2.3 去马赛克 2.4 AXI_IIC 3. 注意事项 4. 总结 1. 简介 本文是关于《Zynq开发-使用PYNQ快速入门摄像头MIPI驱动(OV5640)-CSDN博客》的博客文章&#xff0c;涉及了overlay设计的Vivado工程细节。在使用PYN…

iZotope RX 11 for Mac 激活版:让您的音频焕发生机!

在追求音频完美的道路上&#xff0c;iZotope RX 11 for Mac是您的得力助手。它凭借先进的音频修复技术和丰富的音频增强工具&#xff0c;让您的音频作品焕发出前所未有的生机与活力。无论您是专业的音频工程师&#xff0c;还是业余的音乐爱好者&#xff0c;都能在这款工具中找到…

【Spring】验证 @ServerEndpoint 的类成员变量线程安全

文章目录 前言猜想来源验证方法Controller 的情况ServerEndpoint 的情况 后记 前言 最近有 websocket 的需求。探索 ServerEndpoint 的类成员变量特点。 这里类比 Controller 讨论 ServerEndpoint 类成员变量是否线程安全。 猜想来源 网上的教程大多数都这么展示程序&#…

OBS插件--音频采集

音频采集 音频采集是一款 源 插件,类似于OBS的win-capture/game-capture&#xff0c;允许从特定应用程序捕获音频&#xff0c;而不是捕获整个系统的音频。避免了因为特定音频的采集而需要引入第三方软件&#xff0c;而且时延也非常低。 下面截图演示下操作步骤&#xff1a; 首…

HCIP-Datacom-ARST自选题库_06_排障【28道题】

一、单选题 1.如果面对复杂的网络故障&#xff0c;并经过评估认为短时间内无法完成排障&#xff0c;而此时用户又急需恢复网络的可用性&#xff0c;那么正确的做法是? 告诉用户这是不可能实现的 不通知客户的情况下&#xff0c;直接搭建替代的网络环境 始终尝试排除故障&a…

前端AJAX与后台交互技术知识点及案例(续2)

以下笔记均为学习哔站黑马程序员AJAX视频所得&#xff01;&#xff01;&#xff01; AJAX作用&#xff1a;浏览器和服务器之间通信&#xff0c;动态数据交互 axios函数 先引入axios库&#xff0c;可在bootcdn中寻找相关js文件或者对应的script标签 axios({url:http://hmajax…

最大子矩阵:前缀和、动态规划

最近在学习动态规划&#xff0c;在牛客上刷题时碰到了这一题。其实最初的想法是暴力和前缀和&#xff0c;但是时间复杂度极高&#xff0c;需要套4层循环。后来去网上搜了一下相关的题解和做法&#xff0c;进而了解到了前缀和&#xff0b;线性动态规划的做法。但是在成功做出这题…

【爬虫基础1.1课】——requests模块上

目录索引 requests模块的作用&#xff1a;实例引入&#xff1a; 特殊情况&#xff1a;锦囊1&#xff1a;锦囊2: 这一个栏目&#xff0c;我会给出我从零开始学习爬虫的全过程。感兴趣的小伙伴可以关注一波&#xff0c;用于复习和新学都是不错的选择。 那么废话不多说&#xff0c…

接搭建仿美团、代付系统源码搭建教程

最近很多粉丝催更、分享一下地球号&#xff1a;xiaobao0214520(WX) 现在大家都很流行搞网恋&#xff0c;我们搭建一个跟美团相似的系统 然后开发一个好友代付&#xff0c;我们在点单的时候转发链接让网恋对象付钱 若只是单点外卖的话&#xff0c;能榨出的油水还是太少。 所以…