|  | 
提醒:若下载的软件是收费的"请不要付款",可能是骗子,请立即联系本站举报,执意要付款被骗后本站概不负责。(任何交易请走第三方中介,请勿直接付款交易以免被骗!切记).
  
| 做的是任务挂,用LUA做功能的,挂中提供接口函数。 由于内挂大部分是HOOK游戏处理封包位置来做相应功能的,所以转脱机算是很方便。因为LUA脚本来做功能的,其实脱机也就是把接口写好就行了。
 
 其实脱机可以抛弃不少封包处理,有很多不明白的封包,只要不和封号有关,不和功能需求有关,直接PASS。
 
 
 先说点大局, 脱机要做单文档,也就是象游戏一样,一个程序一个客户端,方便管理,也方便写作,为什么呢,全局变量可以随便扔。-
 
 
 游戏登陆部分,一般是一个帐号验证服务器,一个游戏服务器,然后靠KEY来相互关联,我的逻辑块是:
 
 UINT CGameRobotDlg::MainThreadCyc(LPVOID pVoid)
 {//这是一个线程,外部还有个定时器再不停的检测,如果该线程退出了,再继续启动
 //这样做可以让帐号退出或者什么意外的,再继续登陆注意上
 //注意CreateEvent所创建的句柄,可以控制连接的断开
 if (g_pMainDlg->m_bThreaFlag)//线程还在进行中
 return 1;
 
 //获得启动信息
 CGameRobotDlg* pThis = (CGameRobotDlg*)pVoid;
 int nSel = pThis->m_cServerList.GetCurSel();//选择的服务器
 if (nSel == LB_ERR)
 return 1;
 g_pMainDlg->m_bThreaFlag = TRUE;
 
 strcpy(pThis->m_LoginConnectIP, g_ServerList[nSel].Address);
 if (szLineServer[0] == 0)
 strcpy(szLineServer, g_ServerList[nSel].Title);
 pThis->GetDlgItem(IDC_EDIT_USER)->GetWindowText(pThis->m_LoginName, 0x20);
 pThis->GetDlgItem(IDC_EDIT_PASS)->GetWindowText(pThis->m_LoginPass, 0x30);
 pThis->GetDlgItem(IDC_Btn_BeginGame)->EnableWindow(FALSE);
 MD5_CTX md5;
 BYTE outmd5[16] = {0};
 md5.MD5Update((BYTE*)pThis->m_LoginPass, strlen(pThis->m_LoginPass));
 md5.MD5Final(outmd5);
 char temp[10];
 ZeroMemory(pThis->m_LoginPass, 0x30);
 ZeroMemory(pThis->m_SelectRole, 0x20);
 for (int i=0; i<16; i++)
 {
 wsprintf(temp, "%02X", outmd5);
 strcat(pThis->m_LoginPass, temp);
 }
 //////////////////////////////////////////////////////////////////////////
 //登陆部分
 pThis->m_hLoginTimeHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
 
 pThis->m_GamePacket.m_bIsLoginGetKey = FALSE;
 
 pThis->m_GamePacket.m_bIsGameGetKey = FALSE;
 pThis->m_LoginSocket.UnInit();
 
 if (pThis->m_LoginSocket.Init(ProcessLoginRecvData, pThis->m_LoginConnectIP, GAME_CONNECT_PORT))
 {//连接登陆服务器
 pThis->m_bCheckLogin = TRUE;
 pThis->m_2CheckLogin = 0;
 pThis->KillTimer(CGameRobotDlg::B_CHECK_FADAI);//检测发呆状态,包括想象得到的无响应状态
 pThis->SetTimer(CGameRobotDlg::B_CHECK_FADAI, 1000, NULL);
 pThis->KillTimer(CGameRobotDlg::B_CHECK_LOGIN);//有时候登陆没有返回角色信息
 pThis->SetTimer(CGameRobotDlg::B_CHECK_LOGIN, 1000, NULL);
 
 pThis->m_GamePacket.InitGame();
 ZeroMemory(pThis->m_GamePacket.m_RoleName, 30);
 ::WaitForSingleObject(pThis->m_hLoginTimeHandle, INFINITE);
 g_pMainDlg->AddInfo(C_RED, "登陆连接已经断开!");
 pThis->m_LoginSocket.UnInit();
 
 if (g_AccountUseing)
 {//帐号使用中,等待10秒
 ResetEvent(pThis->m_hLoginTimeHandle);
 ::WaitForSingleObject(pThis->m_hLoginTimeHandle, 10*1000);
 }
 }
 else
 {
 pThis->AddInfo(C_RED, "连接登陆服务器失败!");
 pThis->m_bCheckLogin = FALSE;
 if (pThis->m_3CheckLogin++ > 20)//有时候怎么连都连不上的
 {//网络环境问题的话,关闭游戏
 CloseHandle(pThis->m_hLoginTimeHandle);
 pThis->m_hLoginTimeHandle = NULL;
 g_pMainDlg->m_bThreaFlag = TRUE;
 g_pMainDlg->KillTimer(CGameRobotDlg::BEGIN_LOOP);
 g_pMainDlg->CloseGame();
 return 2;
 }
 }
 
 CloseHandle(pThis->m_hLoginTimeHandle);
 pThis->m_hLoginTimeHandle = NULL;
 
 //////////////////////////////////////////////////////////////////////////
 //游戏部分
 if (pThis->m_enumState == CGameRobotDlg::R_OK)
 {
 int nIndex = 0;
 pThis->m_hGameTimeHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
 
 pThis->m_2CheckGame = 0;
 pThis->KillTimer(CGameRobotDlg::B_CHECK_GAME);//又是些检测
 pThis->SetTimer(CGameRobotDlg::B_CHECK_GAME, 1000, NULL);
 do {
 pThis->m_bChangeMap = FALSE;
 pThis->m_GameSocket.UnInit();
 g_pMainDlg->AddInfo(C_GREEN, "正在连接游戏服务器中...");
 if ((pThis->m_ConnectPort != 0) && pThis->m_GameSocket.Init(ProcessGameRecvData, pThis->m_ConnectIP, pThis->m_ConnectPort))
 {
 nIndex = 0;
 g_pMainDlg->AddInfo(C_GREEN, "连接游戏服务器成功,进入游戏循环");
 ::WaitForSingleObject(pThis->m_hGameTimeHandle, INFINITE);
 ResetEvent(pThis->m_hGameTimeHandle);
 g_pMainDlg->AddInfo(C_RED, "游戏连接已经断开!");
 pThis->m_GameSocket.UnInit();
 }
 else
 {
 pThis->AddInfo(C_RED, "连接游戏服务器失败!");
 if (pThis->m_ConnectIP[0] != 0)
 pThis->m_bChangeMap = TRUE;
 if (nIndex++ > 5) {
 pThis->m_bChangeMap = FALSE;
 }
 }
 } while(pThis->m_bChangeMap);//m_bChangeMap 是切换地图的标志,切换地图情况下需要再次进行网络连接
 
 pThis->m_2CheckGame = 0;
 pThis->KillTimer(CGameRobotDlg::B_CHECK_GAME);
 
 CloseHandle(pThis->m_hGameTimeHandle);
 pThis->m_hGameTimeHandle = NULL;
 //////////////////////////////////////////////////////////////////////////
 pThis->GetDlgItem(IDC_Btn_BeginGame)->EnableWindow(TRUE);
 pThis->m_bThreaFlag = FALSE;
 return 0;
 }
 else if (pThis->m_enumState == CGameRobotDlg::R_PASS_GAME
 || pThis->m_enumState == CGameRobotDlg::R_LOGIN_RETURN)
 {
 pThis->GetDlgItem(IDC_Btn_BeginGame)->EnableWindow(TRUE);
 pThis->m_bThreaFlag = FALSE;
 }
 return 1;
 }
 
 
 void __stdcall ProcessLoginRecvData(char *pData, DWORD DataLength)
 {//处理登陆连接
 if (g_pMainDlg == NULL || DataLength == 0)
 {
 g_pMainDlg->SetMainState(TRUE, CGameRobotDlg::R_LOGIN_RETURN);
 return;
 }
 if (g_pMainDlg->m_GamePacket.m_bIsLoginGetKey == FALSE)
 {//获得登陆KEY
 if (*(PBYTE)pData == 0x20)
 {
 g_pMainDlg->m_GamePacket.GetLoginKey(pData, DataLength);
 g_pMainDlg->m_GamePacket.m_bIsLoginGetKey = TRUE;//标志
 return;
 }
 }
 g_pMainDlg->m_GamePacket.LoginDeCrypt(pData, DataLength);//解密
 switch(*(PBYTE)pData) {
 case 1: break;
 //这里依据封包命令字做相应处理
 default:
 g_pMainDlg->AddInfo(C_RED, "缺省封包: %X == %X", *(PWORD)(pData), DataLength);
 break;
 }
 }
 
 void __stdcall ProcessGameRecvData(char *pData, DWORD DataLength)
 {//处理游戏连接
 if (g_pMainDlg == NULL || DataLength == 0)
 {
 g_AttackObjectID = 0;    //攻击对象去掉
 g_pMainDlg->m_bStartAttack = FALSE;//停止攻击
 bDoScript_State = FALSE;//把脚本停掉
 
 g_pMainDlg->AddInfo(C_RED, "无封包了.");
 g_pMainDlg->SetMainState(FALSE, CGameRobotDlg::R_OK);
 return;
 }
 g_pMainDlg->m_2CheckGame = 0;
 if (g_pMainDlg->m_GamePacket.m_bIsGameGetKey == FALSE)
 {
 if (*(PBYTE)pData == 0x20)
 {
 //换游戏加密KEY了
 g_pMainDlg->m_GamePacket.GetGameKey(pData, DataLength);
 g_pMainDlg->m_GamePacket.m_bIsGameGetKey = TRUE;//标志
 g_pMainDlg->m_GamePacket.m_bCanLoop = FALSE;
 bD0Flag = FALSE;
 return;
 }
 }
 g_pMainDlg->m_GamePacket.LoginDeCrypt(pData, DataLength);//解密
 ProcessGameRecvPack(pData, DataLength);//具体的封包处理了
 }
 
 给出些数据结构,可以从中看出,脱机中想做什么功能,只要相关数据就行了,有些不必了解
 typedef struct  {//自己的装备
 DWORD    ItemUseKeyID;
 BYTE    at_pos;    //0 是身上, 2是包袱
 BYTE    x;
 BYTE    y;
 BYTE    CurCount; //数量
 BYTE    IsBind;    //是否绑定
 BYTE    Needlevel;//装备需求, 穿装备的功能需要
 char    name[30];
 BYTE    type1;    //物品的分类
 WORD    type2;
 WORD    type3;
 BYTE    type4;
 }TMyItemData, *PTMyItemData;
 
 
 typedef struct  {//玩家,怪物,NPC
 
 DWORD    ID;
 char    name[22];
 DWORD    x;
 DWORD    y;
 int        type;    //3是NPC
 DWORD    CurHP;
 DWORD    MaxHP;
 DWORD    CurMP;
 DWORD    MaxMP;
 BYTE    MenPai;
 BYTE    IsCaptainFlag;    //是否是队长
 BOOL    IsDead;        //是否死亡
 DWORD    GroupID;    //供组队的ID
 char    npcClassName[50];
 }Object, *PObject;
 map<DWORD, Object>    m_AroundObject;//周围对象(NPC,怪物,玩家)
 ★在这里注意下说一下★, 我们的对象链表,当他们死亡时,最好不要做删除链表操作,只要将他们的ID设置为0就好了。
 一些游戏在刷怪时,包括NPC,他们的ID是比较固定的
 
 
 struct GroupItem {//地面上物品
 DWORD    ID;
 char    name[22];
 DWORD    x;
 DWORD    y;
 };
 
 struct MemberInfo {//大小0x54
 DWORD    MemberID;    //成员ID,注意这个ID不是角色ID
 char    name[0x20];//+34成员的名称
 };
 typedef struct  {
 DWORD    GroupID;    //队伍ID
 MemberInfo pMI[6];//最多有6个成员, 其中第一个是队长
 }TGroupMgr, *PTGroupMgr;
 
 就这些差不多了,技能好像处理不是很多,就是需要个技能ID就差不多了
 case 0xD9://已经学习的技能
 //R(0x0015): D9 14 00 00 00 5B 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00
 //                          =========== =========== 技能ID以及已经学习的等级
 {//extern map<DWORD, int> g_AlreadyStudySkill
 DWORD skillid = *(PDWORD)(GameCurPack+5);
 int skilllevel= *(PDWORD)(GameCurPack+5+4);
 g_AlreadyStudySkill[skillid] = skilllevel;
 }
 break;
 
 
 
 联系我时,请说是在 挂海论坛 上看到的,谢谢!
 | 
 上一篇:写点东西,菜鸟也玩保护... 下一篇:一个剥离GPK版的DragonNest.exe 
免责声明:
1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关。一切关于该内容及资源商业行为与www.52ghai.com无关。 
2、本站提供的一切资源内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。 
3、本站信息来自第三方用户,非本站自制,版权归原作者享有,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。 
4、如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵犯你版权的,请邮件与我们联系删除(邮箱:xhzlw@foxmail.com),本站将立即改正。 
 |