传奇客户端(WindHorn/Mir2Ex工程)在进入_GAME_PROC状态后,所有游戏期逻辑都围绕一条主轴转:CClientSocket收GameGate转发包→PacketQ排队→CGameProcess::OnTimer轮询→ProcessPacket/ProcessDefaultPacket分发→本玩家Hero或m_xActorList中NPC/怪物/其他玩家对象消费。服务器端不是“直接改画面”,而是回送TDEFAULTMESSAGE+正文(加密体或非加密体),客户端按Recog标识路由,再驱动DX渲染与状态机。
一、网络入口:CClientSocket::OnSocketMessage(FD_READ)
WSAAsyncSelect模型下,GameGate推来的TCP数据在CClientSocket::OnSocketMessage的FD_READ分支被读入缓冲区,随即:
PacketQ.PushQ((BYTE*)pszPacket);
仅压队列,不在此处解包。真正消费在CGameProcess::Load()后由主循环OnTimer驱动:
OnMessageReceive();
ProcessPacket();
ProcessDefaultPacket();
_GAME_PROC状态下g_bProcState决定走CLoginProcess/CCharacterProcess/CGameProcess各自的OnMessageReceive虚实现。
二、OnMessageReceive与心跳
OnMessageReceive先做心跳判定:收到心跳包回'*'并回发心跳;随后调OnSocketMessageRecieve。包首字节决定分支:
-以'+'开头→OnProcPacketNotEncode(非编码明文指令:GOOD/FAIL/LNG/WID/FIR等)
•否则按_TDEFAULTMESSAGE结构解头(nRecog/wIdent/wParam/lParam/dwBodySize),进入游戏逻辑分发
三、ProcessPacket:Recog路由核心
ProcessPacket处理m_xWaitPacketQueue里的包,关键判断:
if(lpPacketMsg->stDefMsg.nRecog==m_xMyHero.m_dwIdentity){
//服务器发给“我”的本玩家消息
}else{
//其他玩家/怪物/NPC消息
//遍历m_xActorList,找不到则按stFeature.bGender建对象:
//_GENDER_MAN→CHero;_GENDER_WOMAN→CHero;_GENDER_NPC→CNPC;_GENDER_MON→CActor
pxActor->m_xPacketQueue.PushQ(lpPacketMsg);
}
本玩家重要消息(SM_ABILITY/SM_NEWMAP/SM_LOGON/SM_SENDUSEITEMS/SM_SENDMYMAGIC等)当场处理;其余压入m_xMyHero.m_xPacketQueue。Actor对象各自在UpdatePacketState()(CActor/CNPC/CHero均实现)里消费自身队列。
四、服务器端响应典型SM_*与客户端动作对照
•SM_NEWMAP/SM_MAPDESCRIPTION:加载地图块、设置场景光照(SM_DAYCHANGING控制昼夜雾效)
•SM_LOGON:本地创建Hero对象,回发CM_QUERYBAGITEMS拉背包
-SM_SENDNOTICE:系统公告框
•SM_ABILITY:写金钱、职业、基础属性(m_Ability)
•SM_SUBABILITY:技能等级、毒/恢复/速度参数
•SM_SENDUSEITEMS:穿戴装备清单刷新渲染
-SM_SENDMYMAGIC:魔法列表写UI
•SM_CHARSTATUSCHANGED:攻击力/状态位(中毒、麻痹、隐身)变更
•SM_TURN/SM_WALK/SM_RUN/SM_STRUCK/SM_MAGSTRUCK/SM_HIT/SM_DEATH:驱动Actor动作状态机
-SM_CLEAROBJECT/SM_DISAPPEAR:从m_xActorList移除对象,释放DX资源
五、走动指令闭环(CM_WALK→服务端判定→回流)
客户端:CGameProcess::OnLButtonDown→计算目标格→g_xClientSocket.Send(CM_WALK...)
GameSrv:ProcessUserHuman线程遍历UserInfoList→pUserInfo->Operate()→m_pxPlayerObject->Operate()→死亡判m_fIsDead回_MSG_FAIL,否则WalkTo()(WalkNextPos八向随机、CheckDoorEvent门状态、m_pMap->CanMove、MoveToMovingObject)→成功回_MSG_GOOD并AddRefMsg(RM_WALK)广播周围26×26格内单元格玩家
客户端回流处理:
•_MSG_FAIL→CGameProcess::OnProcPacketNotEncode→m_xMyHero.SetOldPosition()拉回移动前坐标
-_MSG_GOOD→m_bMotionLock=false,解除本地预测锁,正式播走路帧
•他人RM_WALK→对应CHero/CActorUpdatePacketState播位移
六、怪物/NPC/物品可见性回流
GameSrvCPlayerObject::SearchViewRange()算出m_xVisibleObjectList/m_xVisibleItemList/m_xVisibleEventList,对每项目AddProcess(RM_TURN/RM_ITEMSHOW/RM_SHOWEVENT),经GameGate下发SM_TURN/SM_ITEMSHOW。客户端首次见到时建对象并常驻ActorList,离开视距收SM_DISAPPEAR销毁。ProcessMonster线程独立跑AI:CMonsterObject::SearchViewRange()→Operate()过滤RM_DOOPENHEALTH/RM_STRUCK/RM_MAGSTRUCK事件下发。
七、战斗与拾取回流
客户端发CM_HIT/CM_SPELL/CM_PICKUP/CM_DROPITEM→GameSrvProcessUserMessage()路由到CPlayerObject战斗/施法/捡物分支→算命中、伤害、掉落→回SM_STRUCK/SM_MAGSTRUCK/SM_DROPITEM_SUCCESS/SM_PICKUP_FAIL等。客户端在Hero/Actor的UpdatePacketState里扣血、播受击、刷掉落物列表m_stMapItemList。
八、编码与分包结构
TDEFAULTMESSAGE头固定长度,后续Body走服务端Encode/客户端Decode(异或+查表+位移,LoginGate下发的dyncode参与密钥派生)。'+'前缀包跳过Decode直交OnProcPacketNotEncode,用于运动锁、坐标校正、简单确认,降低关键帧延迟。
九、对象生命周期与内存归属
•m_xMyHero:唯一本控角色,dwIdentity由SM_LOGON回包写入
•m_xActorList:CHero/CNPC/CActor混合链表,按Recog索引
-m_stMapItemList:地面物品/金币/装备,SM_ITEMSHOW增、SM_CLEARITEM删
•PacketQueue双层:全局PacketQ(CClientSocket收)→对象级m_xPacketQueue(分发后)→UpdatePacketState消费
-切场景/回城/掉线:清空ActorList、MapItemList、PacketQueue,重建Hero状态
十、服务端回流链路总览(组件级)
Client→GameGate(转发/心跳/加密)→GameSrv(ProcessUserHuman/ProcessMonster/UserInfo::Operate/CPlayerObject::Operate)→AddRefMsg按地图单元格广播→GameGate→ClientCGameProcess分发→Hero/Actor状态机→DX渲染
服务器端响应本质不是“推送画面”,而是回送带Recog的指令流;客户端所有位移、受击、装备、魔法、昼夜、可见性,都是对SM_/RM_消息的解释执行,Hero本地只做预测与回滚,权威状态始终在GameSrv的CPlayerObject与地图单元格里。
一、网络入口:CClientSocket::OnSocketMessage(FD_READ)
WSAAsyncSelect模型下,GameGate推来的TCP数据在CClientSocket::OnSocketMessage的FD_READ分支被读入缓冲区,随即:
PacketQ.PushQ((BYTE*)pszPacket);
仅压队列,不在此处解包。真正消费在CGameProcess::Load()后由主循环OnTimer驱动:
OnMessageReceive();
ProcessPacket();
ProcessDefaultPacket();
_GAME_PROC状态下g_bProcState决定走CLoginProcess/CCharacterProcess/CGameProcess各自的OnMessageReceive虚实现。
二、OnMessageReceive与心跳
OnMessageReceive先做心跳判定:收到心跳包回'*'并回发心跳;随后调OnSocketMessageRecieve。包首字节决定分支:
-以'+'开头→OnProcPacketNotEncode(非编码明文指令:GOOD/FAIL/LNG/WID/FIR等)
•否则按_TDEFAULTMESSAGE结构解头(nRecog/wIdent/wParam/lParam/dwBodySize),进入游戏逻辑分发
三、ProcessPacket:Recog路由核心
ProcessPacket处理m_xWaitPacketQueue里的包,关键判断:
if(lpPacketMsg->stDefMsg.nRecog==m_xMyHero.m_dwIdentity){
//服务器发给“我”的本玩家消息
}else{
//其他玩家/怪物/NPC消息
//遍历m_xActorList,找不到则按stFeature.bGender建对象:
//_GENDER_MAN→CHero;_GENDER_WOMAN→CHero;_GENDER_NPC→CNPC;_GENDER_MON→CActor
pxActor->m_xPacketQueue.PushQ(lpPacketMsg);
}
本玩家重要消息(SM_ABILITY/SM_NEWMAP/SM_LOGON/SM_SENDUSEITEMS/SM_SENDMYMAGIC等)当场处理;其余压入m_xMyHero.m_xPacketQueue。Actor对象各自在UpdatePacketState()(CActor/CNPC/CHero均实现)里消费自身队列。
四、服务器端响应典型SM_*与客户端动作对照
•SM_NEWMAP/SM_MAPDESCRIPTION:加载地图块、设置场景光照(SM_DAYCHANGING控制昼夜雾效)
•SM_LOGON:本地创建Hero对象,回发CM_QUERYBAGITEMS拉背包
-SM_SENDNOTICE:系统公告框
•SM_ABILITY:写金钱、职业、基础属性(m_Ability)
•SM_SUBABILITY:技能等级、毒/恢复/速度参数
•SM_SENDUSEITEMS:穿戴装备清单刷新渲染
-SM_SENDMYMAGIC:魔法列表写UI
•SM_CHARSTATUSCHANGED:攻击力/状态位(中毒、麻痹、隐身)变更
•SM_TURN/SM_WALK/SM_RUN/SM_STRUCK/SM_MAGSTRUCK/SM_HIT/SM_DEATH:驱动Actor动作状态机
-SM_CLEAROBJECT/SM_DISAPPEAR:从m_xActorList移除对象,释放DX资源
五、走动指令闭环(CM_WALK→服务端判定→回流)
客户端:CGameProcess::OnLButtonDown→计算目标格→g_xClientSocket.Send(CM_WALK...)
GameSrv:ProcessUserHuman线程遍历UserInfoList→pUserInfo->Operate()→m_pxPlayerObject->Operate()→死亡判m_fIsDead回_MSG_FAIL,否则WalkTo()(WalkNextPos八向随机、CheckDoorEvent门状态、m_pMap->CanMove、MoveToMovingObject)→成功回_MSG_GOOD并AddRefMsg(RM_WALK)广播周围26×26格内单元格玩家
客户端回流处理:
•_MSG_FAIL→CGameProcess::OnProcPacketNotEncode→m_xMyHero.SetOldPosition()拉回移动前坐标
-_MSG_GOOD→m_bMotionLock=false,解除本地预测锁,正式播走路帧
•他人RM_WALK→对应CHero/CActorUpdatePacketState播位移
六、怪物/NPC/物品可见性回流
GameSrvCPlayerObject::SearchViewRange()算出m_xVisibleObjectList/m_xVisibleItemList/m_xVisibleEventList,对每项目AddProcess(RM_TURN/RM_ITEMSHOW/RM_SHOWEVENT),经GameGate下发SM_TURN/SM_ITEMSHOW。客户端首次见到时建对象并常驻ActorList,离开视距收SM_DISAPPEAR销毁。ProcessMonster线程独立跑AI:CMonsterObject::SearchViewRange()→Operate()过滤RM_DOOPENHEALTH/RM_STRUCK/RM_MAGSTRUCK事件下发。
七、战斗与拾取回流
客户端发CM_HIT/CM_SPELL/CM_PICKUP/CM_DROPITEM→GameSrvProcessUserMessage()路由到CPlayerObject战斗/施法/捡物分支→算命中、伤害、掉落→回SM_STRUCK/SM_MAGSTRUCK/SM_DROPITEM_SUCCESS/SM_PICKUP_FAIL等。客户端在Hero/Actor的UpdatePacketState里扣血、播受击、刷掉落物列表m_stMapItemList。
八、编码与分包结构
TDEFAULTMESSAGE头固定长度,后续Body走服务端Encode/客户端Decode(异或+查表+位移,LoginGate下发的dyncode参与密钥派生)。'+'前缀包跳过Decode直交OnProcPacketNotEncode,用于运动锁、坐标校正、简单确认,降低关键帧延迟。
九、对象生命周期与内存归属
•m_xMyHero:唯一本控角色,dwIdentity由SM_LOGON回包写入
•m_xActorList:CHero/CNPC/CActor混合链表,按Recog索引
-m_stMapItemList:地面物品/金币/装备,SM_ITEMSHOW增、SM_CLEARITEM删
•PacketQueue双层:全局PacketQ(CClientSocket收)→对象级m_xPacketQueue(分发后)→UpdatePacketState消费
-切场景/回城/掉线:清空ActorList、MapItemList、PacketQueue,重建Hero状态
十、服务端回流链路总览(组件级)
Client→GameGate(转发/心跳/加密)→GameSrv(ProcessUserHuman/ProcessMonster/UserInfo::Operate/CPlayerObject::Operate)→AddRefMsg按地图单元格广播→GameGate→ClientCGameProcess分发→Hero/Actor状态机→DX渲染
服务器端响应本质不是“推送画面”,而是回送带Recog的指令流;客户端所有位移、受击、装备、魔法、昼夜、可见性,都是对SM_/RM_消息的解释执行,Hero本地只做预测与回滚,权威状态始终在GameSrv的CPlayerObject与地图单元格里。

