欢迎光临
我们一直在努力

传奇源码分析-客户端(游戏逻辑处理源

购买童装可电话联系:13783019327 同微信 巩义本地可去实体店选购。

登录处理事件:

0.WinMain主函数调用g_xLoginProc.Load();加载图片等初始化,设置g_bProcState 的状态。

1.CLoginProcess::OnKeyDown-> m_xLogin.OnKeyDown->g_xClientSocket.OnLogin;

WSAAsyncSelect模型ID_SOCKCLIENT_EVENT_MSG,因此,(登录,角色选择,游戏逻辑处理)都回调g_xClientSocket.OnSocketMessage(wParam, lParam)进行处理。

OnSocketMessage函数中:FD_READ事件中:

2.g_bProcState判断当前状态,_GAME_PROC时,把GameGate的发送过来的消息压入PacketQ队列中,再进行处理。否则则调用OnMessageReceive(虚方法,根据g_bProcState状态,调用CloginProcess或者是CcharacterProcess的OnMessageReceive方法)。

3.CloginProcess:调用OnSocketMessageRecieve处理返回情况。如果服务器验证失败(SM_ID_NOTFOUND, SM_PASSWD_FAIL)消息,否则收到SM_PASSOK_SELECTSERVER消息(SelGate服务器列表消息)。m_Progress = PRG_SERVER_SELE;进行下一步选择SelGate服务器操作。

4. m_xSelectSrv.OnButtonDown->CselectSrv. OnButtonUp->

g_xClientSocket.OnSelectServer(CM_SELECTSERVER),得到真正的IP地址。调用OnSocketMessageRecieve处理返回的SM_SELECTSERVER_OK消息。并且断开与loginSrv服务器连接。 g_xClientSocket.DisconnectToServer();设置状态为PRG_TO_SELECT_CHR状态。

角色选择处理:

1. WinMain消息循环处理:g_xLoginProc.RenderScene(dwDelay)-> RenderScroll->

SetNextProc调用

g_xClientSocket.m_pxDefProc = g_xMainWnd.m_pxDefProcess = &g_xChrSelProc;

g_xChrSelProc.Load();

g_bProcState = _CHAR_SEL_PROC;

   2.g_xChrSelProc.Load();连接SelGate服务器(从LoginGate服务器得到IP地址)。

g_xClientSocket.OnQueryChar();查询用户角色信息,发送消息:CM_QUERYCHR,设置状态为_CHAR_SEL_PROC, m_Progress = PRG_CHAR_SELE; 在OnSocketMessageRecieve函数中接收到SelGate服务器发送的消息。

   3.点击ChrStart按钮:g_xChrSelProc.OnLButtonDown-> CSelectChr::OnButtonUp->

g_xClientSocket.OnSelChar->发送CM_SELCHR消息到SelGate服务器。

4.CClientSocket::OnSocketMessage->CCharacterProcess::OnMessageReceive

(SM_STARTPLAY) 接受到SelGate服务器发送的GameGate服务器IP地址,并断开与SelGate服务器的连接。m_xSelectChr.m_nRenderState = 2;

  

   5. WinMain消息循环处理:g_xLoginProc.RenderScene ->

m_xSelectChr.Render(nLoopTime);-> CSelectChr::Render(INT   nLoopTime)-> m_nRenderState = m_nRenderState + 10; 为12-> CCharacterProcess::RenderScene执行

m_Progress = PRG_SEL_TO_GAME;

    m_Progress = PRG_PLAY_GAME;                           

SetNextProc();

6.SetNextProc();执行: g_xGameProc.Load(); g_bProcState = _GAME_PROC;进行游戏状态。

游戏逻辑处理:

1.客户端处理:

CGameProcess::Load() 初始化游戏环境,加载地图等操作,调用ConnectToServer(m_pxDefProc->OnConnectToServer)连接到GameGate游戏网关服务器(DBSrv处理后经SelGate服务器返回的GameGate服务器IP地址)。

     CClientSocket->ConnectToServer调用connect时,由GameGate服务器发送GM_OPEN消息到GameSrv服务器。WSAAsyncSelect I/O模型回调函数 g_xClientSocket.OnSocketMessage。然后由m_pxDefProc->OnConnectToServer()调用CGameProcess::OnConnectToServer()函数,调用:g_xClientSocket.SendRunLogin。

2. GameGate服务器ServerWorkerThread处理:

GameGate服务器ServerWorkerThread收到消息,ThreadFuncForMsg处理数据,生成MsgHdr结构,并设置

MsgHdr.nCode    = 0xAA55AA55; //数据标志

MsgHdr.wIdent   = GM_DATA;    //数据类型

3. GameSrv服务器ServerWorkerThread线程处理

   GameSrv服务器ServerWorkerThread线程处理调用DoClientCertification设置用户信息,及USERMODE_LOGIN的状态。并且调用LoadPlayer(CUserInfo* pUserInfo)函数-> LoadHumanFromDB-> SendRDBSocket发送DB_LOADHUMANRCD请求,返回该玩家的所有数据信息。

4. 客户端登录验证(GameSrv服务器的线程ProcessLogin处理)

   用户的验证是由GameSrv服务器的线程ProcessLogin处理。g_xReadyUserInfoList2列表中搜索,判断用户是否已经登录,一旦登录就调用LoadPlayer(这里两个参数):

a. 设置玩家游戏状态。m_btCurrentMode状态为USERMODE_PLAYGAME

b. 加载物品,个人设置,魔法等。

c. pUserInfo->m_pxPlayerObject->Initialize();初始化用户信息,加载用户坐标,方向,地图。

   Initialize执行流程:

1)       AddProcess(this, RM_LOGON, 0, 0, 0, 0, NULL);加入登录消息。

2)       m_pMap->AddNewObject 地图中单元格(玩家列表)加入该游戏玩家。OS_MOVINGOBJECT玩家状态。

3)       AddRefMsg(RM_TURN 向周围玩家群发 RM_TURN消息。以玩家自己为中心,以24*24的区域里,向这个区域所属的块里的所有玩家列表发送消息)广播 AddProcess

4)       RecalcAbilitys 设置玩家的能力属性(攻击力(手,衣服),武器力量等)。

5)       循环处理本游戏玩家的附属物品,把这些物品的力量加到(手,衣服等)的攻击力量里。

6)       RM_CHARSTATUSCHANGED消息,通知玩家状态改变消息。

7)       AddProcess(this, RM_ABILITY, 0, 0, 0, 0, NULL); 等级

AddProcess(this, RM_SUBABILITY, 0, 0, 0, 0, NULL);

AddProcess(this, RM_DAYCHANGING, 0, 0, 0, 0, NULL); 校时

AddProcess(this, RM_SENDUSEITEMS, 0, 0, 0, 0, NULL); 装备

AddProcess(this, RM_SENDMYMAGIC, 0, 0, 0, 0, NULL); 魔法

          SysMsg(szMsg, 1) 攻击力

并把用户数据从g_xReadyUserInfoList2列表中删除。

   说明:

一旦通过验证,就从验证列表中该玩家,改变玩家状态,LoadPlayer加载用户资源(地图中加入用户信息,向用户24*24区域内的块内玩家发送上线消息GameSrv广播新玩家上线(坐标)的消息。向该新玩家发送玩家信息(等级,装备,魔法,攻击力等)。

5.接受登录成功后,接收GameSrv服务器发送的消息:

接收GameGate发送的消息:CClientSocket::OnSocketMessage的FD_READ事件中,PacketQ.PushQ((BYTE*)pszPacket);把接收到的消息,压入PacketQ队列中。处理PacketQ队列数据是由CGameProcess::Load()时调用OnTimer在CGameProcess::OnTimer中处理的,   

处理过程为:

OnMessageReceive;

ProcessPacket();

   ProcessDefaultPacket();

OnMessageReceive函数;

1.     判断是否收到心跳数据包,发送'*',发送心跳数据包。

2.     调用OnSocketMessageRecieve函数。这个函数里面详细处理了客户端的游戏执行逻辑。如果是‘+’开头(数据包)则调用OnProcPacketNotEncode处理这种类型数据包。否则得到_TDEFAULTMESSAGE数据包,进行游戏逻辑处理。

OnProcPacketNotEncode说明:

   收到GameSrv服务器的相应消息:

     "GOOD":可以执行动作。 m_bMotionLock为假。

    "FAIL":不允许执行动作。人物被拉回移动前位置。

    "LNG"

    "ULNG"

    "WID"

    "UWID"

    "FIR"

    "UFIR"

    "PWR"

3.     CGameProcess::OnSocketMessageRecieve(char *pszMsg)函数。处理游戏相关的消息。

    SM_SENDNOTICE: 服务器提示信息:

SM_NEWMAP: 用户登录后,服务器发送的初始化地图消息。

SM_LOGON: 用户登录消息(服务器处理后返回结果)。用户登录成功后,在本地创建游戏对象,并发送消息,请求返回用户物品清单(魔法,等级,物品等)。

SM_MAPDESCRIPTION: 得到服务器发送的地图的描述信息。

SM_ABILITY:服务器发送的本玩家金钱,职业信息。

SM_WINEXP

SM_SUBABILITY : 服务器发送的玩家技能(魔法,杀伤力,速度,毒药,中毒恢复,生命恢复,符咒恢复)

SM_ SM_SENDMYMAGIC: 用户魔法列表信息。

SM_MAGIC_LVEXP: 魔法等级列表。

    SM_BAGITEMS用户物品清单 (玩家CM_QUERYBAGITEMS消息)

SM_SENDUSEITEMS用户装备清单

SM_ADDITEM: 拣东西

SM_DELITEM: 丢弃物品。

等等。

4.     部分数据未处理,加入m_xWaitPacketQueue队列中由ProcessPacket处理。

    新登录游戏玩家:在OnSocketMessageRecieve函数中依次收到的消息为:

1. GameSrv 服务器ProcessLogin线程返回GameGate服务器后返回的:

   AddProcess(this, RM_LOGON, 0, 0, 0, 0, NULL);加入登录消息。

SM_NEWMAP, SM_LOGON, SM_USERNAME, SM_MAPDESCRIPTION消息

    AddProcess(this, RM_ABILITY, 0, 0, 0, 0, NULL); 等级

      SM_ABILITY

AddProcess(this, RM_SUBABILITY, 0, 0, 0, 0, NULL);

SM_SUBABILITY

AddProcess(this, RM_DAYCHANGING, 0, 0, 0, 0, NULL); 校时

SM_DAYCHANGING

AddProcess(this, RM_SENDUSEITEMS, 0, 0, 0, 0, NULL); 装备

SM_SENDUSEITEMS

AddProcess(this, RM_SENDMYMAGIC, 0, 0, 0, 0, NULL); 魔法

     SM_SENDMYMAGIC

    

     客户端收到消息后相应的处理:

SM_NEWMAP 接受地图消息 OnSvrMsgNewMap

     初始化玩家坐标,m_xMyHero.m_wPosX = ptdm->wParam;

                     m_xMyHero.m_wPosY = ptdm->wTag;

加载地图文件    m_xMap.LoadMapData(szMapName);

设置场景。 m_xLightFog.ChangeLightColor(dwFogColor);

SM_LOGON 返回登录消息 OnSvrMsgLogon

           m_xMyHero.Create初始化玩家信息(头发,武器,加载图片等),设置玩家

地图m_xMyHero.SetMapHandler(&m_xMap),创建用户魔法。加入m_xMagicList列表,pxMagic->CreateMagic, m_xMagicList.AddNode(pxMagic);并向服务器发送CM_QUERYBAGITEMS消息(用户物品清单,血,气,衣服,兵器等)。

SM_USERNAME           获取玩家的游戏角色名字。

SM_MAPDESCRIPTION     地图对应的名字。

SM_BAGITEMS           用户物品清单 (玩家CM_QUERYBAGITEMS消息)

SM_CHARSTATUSCHANGED   通知玩家状态改变消息(攻击力,状态)。

SM_ABILITY       玩家金钱,职业

SM_SUBABILITY    玩家技能(魔法,杀伤力,速度,毒药,中毒恢复,生命恢复,符

咒恢复)

SM_DAYCHANGING  返回游戏状态。(Day, Fog)让客户端随着服务器的时间,加载不同场景。

SM_SENDUSEITEMS 用户装备清单

SM_SENDMYMAGIC   用户魔法列表信息。

总结:

客户端连接到GameGate游戏网关服务器,并通过GameSrv服务器验证之后,就会收到GameSrv服务器发来的消息。主要是地图消息,登录消息,玩家的装备,技能,魔法,个人设置等等。GameSrv把地图分成若干块,把该玩家加入其中一块,并加入这一块的用户对象列表中,设置其状态为OS_MOVINGOBJECT。客户端加载地图,设置场景,设置自己的玩家状态(此时还没有怪物和其它玩家,所以玩家还需要接收其它游戏玩家和怪物的清单列表)。

. 接收怪物,商人,其它玩家的消息:

ProcessUserHuman:(其它玩家—服务器处理)

CPlayerObject->SearchViewRange();

CPlayerObject->Operate();

遍历UserInfoList列表,依次调用每个UserInfo的Operate来处理命令队列中的所有操作; pUserInfo->Operate()调用m_pxPlayerObject->Operate()调用。根据分发消息(RM_TURN)向客户端发送SM_TURN消息。GameSrv广播新玩家上线(坐标)的消息。向该新玩家发送玩家信息(等级,装备,魔法,攻击力等)。

玩家,移动对象:

1. 遍历m_xVisibleObjectList列表,所有(玩家,商人,怪物)发送调用AddProcess

(RM_TURN向周围玩家发送消息)。

地图:

2.遍历m_xVisibleItemList,发送AddProcess(this, RM_ITEMSHOW消息更新地图。

3.遍历m_xVisibleEventList,发送AddProcess(this, RM_SHOWEVENT

ProcessMonster线程(怪物—服务器处理)

GameSrv服务器在ProcessMonster线程:创建不同的CMonsterObject对象,并且加入xMonsterObjList列表和pMapCellInfo->m_xpObjectList列表中,然后再调用CMonsterObject::SearchViewRange()更新视线范围内目标,根据g_SearchTable计算出搜索坐标,转换为相应的地图单元格,遍历所有可移动生物,加入m_xVisibleObjectList列表,调用Operate;Operate遍历m_DelayProcessQ列表,过滤出RM_DOOPENHEALTH,RM_STRUCK和RM_MAGSTRUCK三个事件(恢复生命值,攻击,魔法攻击),并处理。

ProcessMerchants线程:(商人–服务器处理)

       1). 遍历g_pMerchantInfo结构(根据nNumOfMurchantInfo数量)。得到商人类型相关的地图,创建商人对象,设置不同的编号,坐标,头像及所属地图。在该地图中加入该商人,且在g_xMerchantObjList商人清单中加入该商人。

2). 遍历g_xMerchantObjList, SearchViewRange,对每个商人更新视线范围内目标

a). 遍历m_xVisibleObjectList,设置每个pVisibleObject->nVisibleFlag = 0;设置状态(删除)。

b). 搜索VisibleObjectList列表,(服务器启动时InitializingServer加载 searchTable.tbl),根据坐标,找到相应的地图单元格。然后遍历pMapCellInfo->m_xpObjectList列表,判断如果为OS_MOVINGOBJECT标志,调用UpdateVisibleObject函数,该函数遍历 m_xVisibleObjectList列表,如果找到该商人对象,则pVisibleObject->nVisibleFlag = 1;否则判断pNewVisibleObject对象,设置nVisibleFlag为2,设置对象为该商人实体,然后加入m_xVisibleObjectList列表中。

总结:循环列表,找出地图单元格中的所有玩家,把所有玩家(OS_MOVINGOBJECT)加入到m_xVisibleObjectList列表中。

c). 遍历m_xVisibleObjectList列表,(pVisibleObject->nVisibleFlag == 0)则删除该pVisibleObject对象。

d). RunRace调用AddRefMsg 向周围玩家发送SM_TURN和SM_HIT

客户端收到消息后相应的处理:

1.CGameProcess::OnSocketMessageRecieve加入m_xWaitPacketQueue队列

      遍历m_xVisibleObjectList队列中所有移动物体(角色):

        RM_DISAPPEAR   消失(SM_DISAPPEAR) ProcessDefaultPacket函数

       RM_DEATH       死亡(SM_NOWDEATH, SM_DEATH)

            CHero::OnDeath 其它玩家。

            CActor::OnDeath 怪物。

//g_xGameProc.m_xMagicList

        RM_TURN        移动

SM_TURN消息处理

     遍历m_xVisibleItemList队列中所有移动物体(地图):

        RM_ITEMHIDE    从m_stMapItemList列表中删除该移动对象

RM_ITEMSHOW   遍历m_stMapItemList,如果不存在,则创建一个GROUNDITEM结构,并加入m_stMapItemList列表中。

typedef struct tagGROUNDITEM

{

                     INT             nRecog;

                     SHORT           shTileX;

                     SHORT           shTileY;

                     WORD            wLooks;

                     CHAR            szItemName[40];

}GROUNDITEM, *LPGROUNDITEM;

     遍历m_xVisibleEventList队列中所有移动物体(事件):

         RM_HIDEEVENT  

RM_SHOWEVENT  

2. 部分数据未处理,加入m_xWaitPacketQueue队列中由ProcessPacket处理。

CClientSocket::OnSocketMessage的FD_READ事件中,PacketQ.PushQ把接收到的消息,压入PacketQ队列中。处理PacketQ队列数据是由CGameProcess::Load()时调用OnTimer在CGameProcess::OnTimer中处理的,处理过程为:

OnTimer -> ProcessPacket -> ProcessPacket处理m_xWaitPacketQueue队列消息(OnSocketMessageRecieve函数中未处理的消息)。

ProcessPacket 函数处理流程:

1. 处理本玩家(SM_NOWDEATH, SM_DEATH, SM_CHANGEMAP, SM_STRUCK)

a.如果接收到消息是SM_NOWDEATH或SM_DEATH 则加入m_xPriorPacketQueue队列。

b. 如果接收到消息是SM_CHANGEMAP则调用LoadMapChanged,设置场景。

c. SM_STRUCK 处理受攻击(本玩家,或者其它的玩家,NPC等)。

2. 其它消息:m_xMyHero.StruckMsgReassign();

                m_xMyHero.m_xPacketQueue.PushQ((BYTE*)lpPacketMsg);

判断服务器发送来的消息ID是否相同。m_xMyHero.m_dwIdentity在登录成功的时

候由服务器发送的用户消息获取的。

if ( lpPacketMsg->stDefMsg.nRecog == m_xMyHero.m_dwIdentity )

如果是服务器端游戏玩家自己发送的消息,则处理自己的消息。否则如果是其它玩家(怪物)发送的消息,遍历m_xActorList列表, 判断该对象是否存在,如果该不存在,则根据stFeature.bGender的类型

_GENDER_MAN: 创建一个CHero对象,加入到m_xActorList列表中。

_GENDER_WOMAN

_GENDER_NPC: 创建一个CNPC对象,加入到m_xActorList列表中。

_GENDER_MON: 创建一个CActor对象,加入到m_xActorList列表中。

然后pxActor->m_xPacketQueue.PushQ 然后把消息压入该对象的xPacketQueue列表中。

    总结:ProcessPacket处理 CClientSocket类接受的消息(m_xWaitPacketQueue),判断是否是服务器发送给自己的消息,处理一些发送给自己的重要消息,其它消息处理则加入m_xMyHero.m_xPacketQueue队列中,然后再遍历m_xActorList队列,判断如果服务器端发来的消息里的玩家(NPC,怪物),在m_xActorList队列中找不到,就判断一个加入m_xActorList列表中,并且把该消息压入pxActor->m_xPacketQueue交给该NPC去处理该事件。

而xPacketQueue队列的消息分别由该对象的UpdatePacketState处理,如下:

BOOL CActor::UpdatePacketState() ,BOOL CNPC::UpdatePacketState()

BOOL CHero::UpdatePacketState()

ProcessDefaultPacket函数:

    处理CGameProcess::OnSocketMessageRecieve 中 SM_CLEAROBJECT消息:

处理(SM_DISAPPEAR,SM_CLEAROBJECT)消息。

   遍历m_xWaitDefaultPacketQueue消息列表

SM_DISAPPEAR和SM_CLEAROBJECT:

          遍历m_xActorList列表,清除pxActor->m_xPacketQueue队列内所有消息。

m_xActorList.DeleteCurrentNodeEx();从对列中删除该对象。

CHero* pxHero = (CHero*)pxActor; delete((CHero*)pxHero);销毁该玩家。

游戏循环处理: CGameProcess::RenderScene(INT nLoopTime)函数:

主要流程如下:

    wMoveTime += nLoopTime; 判断wMoveTime>100时,bIsMoveTime置为真。

1.m_xMyHero.UpdateMotionState(nLoopTime, bIsMoveTime);处理本玩家消息。

     a. UpdatePacketState函数:

           遍历m_xPriorPacketQueue队列,如果有SM_NOWDEATH或SM_DEATH消息,则优先处理。

           处理m_xPacketQueue队列中消息。

              SM_STRUCK:

              SM_RUSH

              SM_BACKSTEP

              SM_FEATURECHANGED:

              SM_OPENHEALTH:        

SM_CLOSEHEALTH:       

SM_CHANGELIGHT:       

SM_USERNAME:          

SM_CHANGENAMECOLOR:

              SM_CHARSTATUSCHANGE:  

SM_MAGICFIRE:         

SM_HEALTHSPELLCHANGED:

2.CheckMappedData函数:遍历m_xActorList列表分别调用

         CActor::UpdateMotionState(INT nLoopTime, BOOL bIsMoveTime)

CNPC::UpdateMotionState(INT nLoopTime, BOOL bIsMoveTime)

CMyHero::UpdateMotionState(INT nLoopTime, BOOL bIsMoveTime)

     处理自己消息。

CHero::UpdatePacketState()

case SM_SITDOWN:

         case SM_BUTCH:    

         case SM_FEATURECHANGED:   

         case SM_CHARSTATUSCHANGE:

         case SM_OPENHEALTH:           

         case SM_CLOSEHEALTH:      

         case SM_CHANGELIGHT:      

         case SM_USERNAME:         

         case SM_CHANGENAMECOLOR:  

         case SM_HEALTHSPELLCHANGED:

         case SM_RUSH:             

         case SM_BACKSTEP:         

         case SM_NOWDEATH:

         case SM_DEATH:            

         case SM_WALK:             

         case SM_RUN:              

         case SM_TURN:             

         case SM_STRUCK:           

         case SM_HIT:

         case SM_FIREHIT:

         case SM_LONGHIT:

         case SM_POWERHIT:

         case SM_WIDEHIT:          

         case SM_MAGICFIRE:    

     case SM_SPELL:    

                           

CNPC::UpdatePacketState()

     case SM_OPENHEALTH:           

     case SM_CLOSEHEALTH:  

     case SM_CHANGELIGHT:      

     case SM_USERNAME:         

     case SM_CHANGENAMECOLOR:  

     case SM_HEALTHSPELLCHANGED:   

     case SM_TURN:             

     case SM_HIT:

     CActor::UpdatePacketState()

        case SM_DEATH:     SetMotionFrame(_MT_MON_DIE, bDir);

        case SM_WALK:      SetMotionFrame(_MT_MON_WALK, bDir);

case SM_TURN:      SetMotionFrame(_MT_MON_STAND, bDir);

case SM_DIGUP:     SetMotionFrame(_MT_MON_APPEAR, bDir);

case SM_DIGDOWN:   SetMotionFrame(_MT_MON_APPEAR, bDir);

        case SM_FEATURECHANGED:       

       case SM_OPENHEALTH:           

        case SM_CLOSEHEALTH:      

        case SM_CHANGELIGHT:      

        case SM_CHANGENAMECOLOR:  

        case SM_USERNAME:         

        case SM_HEALTHSPELLCHANGED:   

        case SM_BACKSTEP:      SetMotionFrame(_MT_MON_WALK, bDir);

        case SM_STRUCK:            SetMotionFrame(_MT_MON_HITTED, m_bCurrDir);

        case SM_HIT:           SetMotionFrame(_MT_MON_ATTACK_A, bDir);

        case SM_FLYAXE:               

        case SM_LIGHTING:         

        case SM_SKELETON:

     收到多个NPC,玩家发送的SM_TURN消息:由下面对象调用处理:

CHero::OnTurn

CNPC::OnTurn

CActor::OnTurn

根据服务器发送的消息,(创建一个虚拟玩家NPC,怪物,在客户端),根据参数,初始化该对象设置(方向,坐标,名字,等级等)。在后面的处理中绘制该对象到UI界面中(移动对象的UI界面处理。)

       

         SetMotionFrame(_MT_MON_STAND, bDir); m_bCurrMtn := _MT_MON_STAND

       m_dwFstFrame , m_dwEndFrame , m_wDelay 第一帧,最后一帧,延迟时间。

   3. AutoTargeting 自动搜索目标(NPC,怪物,玩家等)

  

   4. RenderObject补偿对象时间

  

   5. RenderMapTileGrid

        m_xMagicList,处理玩家魔法后,UI界面的处理。

6.       m_xSnow, m_xRain, m_xFlyingTail, m_xSmoke, m_xLightFog设置场景UI界面处理。

  7. m_xMyHero.ShowMessage(nLoopTime); 显示用户(UI处理)

m_xMyHero.DrawHPBar(); 显示用户HP值。

  遍历m_xActorList,处理所有NPC的UI界面重绘

   pxHero->ShowMessage(nLoopTime);

pxHero->DrawHPBar();

8. DropItemShow下拉显示。

9. 判断m_pxMouseTargetActor(玩家查看其它玩家,NPC,怪物时)

   g_xClientSocket.SendQueryName向服务器提交查询信息。

m_pxMouseOldTargetActor = m_pxMouseTargetActor; 保存该对象

    m_pxMouseTargetActor->DrawName(); 重绘对象名字(UI界面显示)

下面分析一下用户登录之后的流程:

从前面的分析中可以看到,该用户玩家登录成功之后,得到了服务器发送来的各种消息。处理也比较复杂,同时有一定的优先级处理。并且根据用户登录后的XY坐标,向用户发送来了服务器XY坐标为中心附近单元格中的所有玩家(NPC,怪物)的SM_TURN消息。

客户端根据数据包的标志,创建这些NPC,设置属性,并且把它们加入m_xActorList对列中。最后在UI界面上绘制这些对象。

现在假设玩家开始操作游戏:

传奇的客户端源代码工程WindHorn

一、CWHApp派生CWHWindow和CWHDXGraphicWindow。

二、CWHDefProcess派生出CloginProcess、CcharacterProcess、CgameProcess

客户端WinMain调用CWHDXGraphicWindow g_xMainWnd;创建一个窗口。

客户端CWHDXGraphicWindow在自己的Create函数中调用了CWHWindow的Create来创建窗口,然后再调用自己的CreateDXG()来初始化DirectX。

消息循环:

因此,当客户端鼠标单击的时候,先调用CWHWindow窗口的回调函数WndProc,即:    g_pWHApp->MainWndProc g_pWHApp定义为:static CWHApp* g_pWHApp = NULL;在CWHApp

构造函数中赋值为:g_pWHApp = this;

g_pWHApp->MainWndProc便调用了CWHApp::MainWndProc,这是一个虚函数,实际上则是调用它的派生类CWHDXGraphicWindow::MainWndProc
   if ( m_pxDefProcess )
        return m_pxDefProcess->DefMainWndProc(hWnd, uMsg, wParam, lParam);
根据g_xMainWnd.m_pxDefProcess和全局变量g_bProcState标记当前的处理状态。调用

     CLoginProcess->DefMainWndProc     

CCharacterProcess->DefMainWndProc

CGameProcess->DefMainWndProc      

当用户进行游戏之后,点击鼠标左键,来处理玩家走动的动作:

客户端执行流程:(玩家走动)

CGameProcess::OnLButtonDown(WPARAM wParam, LPARAM lParam)函数:该函数的处理流程:

1. g_xClientSocket.SendNoticeOK();如果点中CnoticeBox则m_xNotice.OnButtonDown

      if m_xMsgBtn.OnLButtonDown则调用g_xClientSocket.SendNoticeOK()方法,发送还CM_LOGINNOTICEOK消息。

2.m_pxSavedTargetActor = NULL;设置为空。CInterface::OnLButtonDown函数会判断

   鼠标点击的位置(CmirMsgBox, CscrlBar,CgameBtn,GetWindowInMousePos)

     a. g_xClientSocket.SendItemIndex(CM_DROPITEM 丢弃物品)

游戏服务器执行流程m_pxPlayerObject->Operate()调用

           m_pUserInfo->UserDropGenItem    

        m_pUserInfo->UserDropItem     删除普通物品。

           SM_DROPITEM_SUCCESS             返回删除成功命令

SM_DROPITEM_FAIL                返回删除失败命令

     b. 遍历m_stMapItemList列表(存储玩家,怪物,NPC), g_xClientSocket.SendPickUp 发送CM_PICKUP命令。

         游戏服务器:m_pxPlayerObject->Operate()调用 PickUp(捡东西)消息处理:

m_pMap->GetItem(m_nCurrX, m_nCurrY) 返回地图里的物体(草药,物品,金子等)

          1.memcmp(pMapItem->szName, g_szGoldName 如果是黄金:

                 m_pMap->RemoveObject从地图中移走该的品。

if (m_pUserInfo->IncGold(pMapItem->nCount))增加用户的金钱(向周转玩家发送RM_ITEMHIDE 消息,隐藏该物体,GoldChanged(),改变玩家的金钱。否则,把黄金返回地图中。

2.m_pUserInfo->IsEnoughBag()

                如果玩家的还可以随身带装备(空间)。m_pMap->RemoveObject从地图中移走该的品。UpdateItemToDB,更新用户信息到数据库。(向周转玩家发送RM_ITEMHIDE 消息,隐藏该物体,SendAddItem(lptItemRcd)向本玩家发送捡到东西的消息。m_pUserInfo->m_lpTItemRcd.AddNewNode并把该物品加入自己的列表中。

     c. if m_pxMouseTargetActor g_xClientSocket.SendNPCClick发送CM_CLICKNPC命令。

客户端RenderScene调用m_pxMouseTargetActor = NULL;

CheckMappedData(nLoopTime, bIsMoveTime)处理,如果鼠标在某个移动对象的区域内就会设置 m_pxMouseTargetActor为该对象。

            如果是NPC:

if ( m_pxMouseTargetActor->m_stFeature.bGender == _GENDER_NPC )

        g_xClientSocket.SendNPCClick(m_pxMouseTargetActor->m_dwIdentity);

            CM_CLICKNPC消息:

            否则:

m_xMyHero.OnLButtonDown

     d. 否则m_xMyHero.OnLButtonDown

先判断m_xPacketQueue是否有数据,有则先处理。返回。

判断m_pxMap->GetNextTileCanMove 根据坐标,判断地图上该点属性是否可以移动到该位置:

        可移动时:

          人:SetMotionState(_MT_WALK

          骑马:SetMotionState(_MT_HORSEWALK

        不可移动时:

          人:SetMotionState(_MT_STAND, bDir);

          骑马:SetMotionState(_MT_HORSESTAND, bDir);

        SetMotionState函数:

            判断循环遍历目标点的周围八个坐标,如果发现是一扇门,则向服务器发送打开这扇门的命令。g_xClientSocket.SendOpenDoor,否则则发送CM_WALK命令到服务器。

            m_bMotionLock = m_bInputLock = TRUE; 设置游戏状态

            m_wOldPosX = m_wPosX;                保存玩家X点

            m_wOldPosY = m_wPosY;               保存玩家Y点

            m_bOldDir = m_bCurrDir;             保存玩家方向

然后调用SetMotionFrame设置m_bCurrMtn = _MT_WALK,方向等游戏状态。

        设置m_bMoveSpeed = _SPEED_WALK(移动速度1)。m_pxMap->ScrollMap设置地图的偏移位置(m_shViewOffsetX, m_shViewOffsetY)。然后滚动地图,重绘玩家由CGameProcess::RenderScene CGameProcess::RenderObject->DrawActor重绘。

传奇源码分析-客户端(游戏逻辑处理源分析五 服务器端响应)

2008-10-02 15:09

游戏服务器执行流程:(玩家走动)

   GameSrv服务器ProcessUserHuman线程处理玩家消息:

遍历UserInfoList列表,依次调用每个UserInfo的Operate来处理命令队列中的所有操作;   pUserInfo->Operate()调用m_pxPlayerObject->Operate()调用。

判断玩家if   (!m_fIsDead),如果已死,则发送_MSG_FAIL消息。我们在前面看到过,该消息是被优先处理的。否则则调用WalkTo,并发送_MSG_GOOD消息给客户端。

WalkTo函数的流程:

1) WalkNextPos 根据随机值产生,八个方向的坐标位置。

2) WalkXY怪物走动到一个坐标值中。

CheckDoorEvent根据pMapCellInfo->m_sLightNEvent返回四种状态。

a) 要移动的位置是一扇门 _DOOR_OPEN

b) 不是一扇门 _DOOR_NOT

c) 是一扇门不可以打开返回 _DOOR_MAPMOVE_BACK或_DOOR_MAPMOVE_FRONT玩家前/后移动

3) 如果_DOOR_OPEN则发送SM_DOOROPEN消息给周围玩家。

4) m_pMap->CanMove如果可以移动,则MoveToMovingObject从当前点移动到另一点。并发送AddRefMsg(RM_WALK)给周围玩家。

    AddRefMsg函数,我们在后面的服务器代码里分析过:它会根据X,Y坐标,在以自己坐标为中心周围26*26区域里面,按地图单元格的划分,遍历所有单元格,再遍历所有单元格内的玩家列表,广播发送RM_WALK消息。

客户端执行流程:(反馈服务器端本玩家走动)

1. 服务器如果发送_MSG_FAIL 由客户端CGameProcess::OnProcPacketNotEncode处理。

     m_xMyHero.SetOldPosition();

人: SetMotionFrame(_MT_STAND

              AdjustMyPostion(); 重绘地图

              m_bMotionLock = m_bInputLock = FALSE;

     骑马:SetMotionFrame(_MT_HORSESTAND

               AdjustMyPostion(); 重绘地图

               m_bMotionLock = m_bInputLock = FALSE;

     2. 服务器如果发送_MSG_GOOD, 由客户端CGameProcess::OnProcPacketNotEncode处理。m_xMyHero.m_bMotionLock   = FALSE;

其它客户端执行流程:(反馈服务器端其它玩家)

1.其它玩家:

    人: SetMotionFrame(_MT_WALK, bDir);

    骑马:SetMotionFrame(_MT_HORSEWALK, bDir);

    m_bMoveSpeed = _SPEED_WALK;

    SetMoving(); 设置m_shShiftPixelX,   m_shShiftPixelY坐标。

2.NPC,怪物:

SetMotionFrame(_MT_MON_WALK,   bDir);

    m_bMoveSpeed = _SPEED_WALK;

    SetMoving(); 设置m_shShiftPixelX,   m_shShiftPixelY坐标。

CGameProcess::RenderObject->DrawActor(m_shShiftPixelX, m_shShiftPixelY)重绘发消息的玩家,NPC怪物位置。

 

未经允许不得转载:随缘小居 » 传奇源码分析-客户端(游戏逻辑处理源
购买童装可电话联系:13783019327 同微信 巩义本地可去实体店选购。
5

评论 抢沙发

评论前必须登录!

 

不跟随,有视角焦点的博客

联系我们联系我们