当前位置 : 145z游戏站 | 热血传奇 | 技术教程 | 

C++传奇服务端MMO架构拆解:GameSrv与DBSrv网关线程模型及核心模块实战

热度:
传奇(LegendofMir2/M2系)C++服务端不是单体程序,而是按“接入—认证—逻辑—持久化”拆成的多进程协同体:LoginGate→LoginSrv→SelGate→GameGate→GameSrv(M2Server)↔DBSrv↔数据库。各进程通过TCP/命名管道/内部消息协议通信,CM_(客户端指令)、SM_(服务端下发)、RM_(内部引用消息)、GM_(网关消息)、DB_/DBR_(库消息)五类标识符串起整条链路。下面按进程边界、线程模型、核心类、地图广播、战斗与脚本、启动顺序逐层拆。

一、进程边界与职责切分
1.LoginGate(端口7000):客户端首个入口,收账号包做XOR+位移解密、版本过滤、连接数限制,转发LoginSrv,不直接碰库。
2.LoginSrv:验ID.DB/SQL账号密码、签发session、返回服务器列表,无状态可横向扩。
3.SelGate(端口7100):角色列表、建/删角,从DBSrv拉_THUMANRCD快照序列化回客户端。
4.GameGate(RunGate):游戏期长连接代理,收CM_WALK/CM_HIT/CM_SPELL/CM_SAY,转发GameSrv;回包压对应CUserInfo发送缓冲,处理GM_*前缀。
5.GameSrv(M2Server/GameSvr):世界逻辑内核,加载MapInfo/MonGen/Envir、跑CPlayerObject/CMonsterObject/CNPCObject/CItemObject、战斗公式、技能、NPC脚本解析、地图单元格广播。
6.DBSrv:持久层代理,ODBC/SQL连接池,处理DB_LOADHUMANRCD/DB_SAVEHUMANRCD/物品与行会写入,强制异步+内存缓存,不阻塞GameSrv主循环。

二、线程模型(GameSrv内部分工)
GameSrv主循环按“流水线”拆线程,避免单循环串行卡死:
•ProcessLogin线程:新连接建CUserInfo+CPlayerObject,发RM_LOGON→SM_NEWMAP/SM_LOGON/SM_ABILITY/SM_SENDUSEITEMS/SM_SENDMYMAGIC回流客户端。

-ProcessUserHuman线程:遍历全局UserInfoList,调pUserInfo->Operate()→m_pxPlayerObject->Operate(),消费命令队列(移动/攻击/施法/拾取/交易),算完调pGateInfo->xSend()发回GameGate;SO_SNDBUF置0,WSASend阻塞式顺序发,避免乱序。
-ProcessMonster线程:创建CMonsterObject入xMonsterObjList与pMapCellInfo->m_xpObjectList;SearchViewRange()用g_SearchTable算视距格→填m_xVisibleObjectList→Operate()过滤RM_DOOPENHEALTH/RM_STRUCK/RM_MAGSTRUCK事件。
-ProcessMerchants/ProcessNPC线程:遍历g_pMerchantInfo,按地图建商人对象,跑Market_Def脚本段。
•网络IO线程:早期WSAAsyncSelect,后期IOCP(AcceptEx+WSASend+完成端口),LoginGate/GameGate同构。

-同步原语:CIntLock封装临界区,CWHQueue/CDBManager/CGlobalUserList/CUserInfo/CPlayerObject均从其派生,Lock()/Unlock()控共享数据;地图单元格对象列表、可见性列表、发送队列全走这层。

三、核心类与数据结构(GameSrv/目录对照)
•CUserInfo:会话载体,嵌CGateInfo(sock、xSendBuffQ、GateIdx)、m_pxPlayerObject指针、命令队列。

-CPlayerObject(ObjectPlayer.cpp):角色实体,管移动WalkTo/CanMove/MoveToMovingObject、战斗AttackTarget、魔法UseMagic、背包Inventory、装备UseItems、技能MagicList、属性Ability/SubAbility。
-CMonsterObject(ObjectMonster.cpp):Race行为机、AI状态(巡逻/追击/攻击/召唤/复活)、SearchViewRange、DropLoot。
•CNPCObject/CMerchantObject:脚本绑定NPC,解析#IF/#ACT/#ELSE、CALLQuestDiary段。

-CMapCellInfo/TMapCell:地图格子,存可行走/遮挡、m_xpObjectList(本格内玩家/怪/NPC/物品指针)、可见性推导源。
•_THUMANRCD/_TUSERITEMRCD/_THUMANMAGICRCD:角色、物品、技能落库结构体,DBSrv序列化读写。

-TDEFAULTMESSAGE:包头(nRecog/wIdent/wParam/lParam/dwBodySize),客户端与服务端统一解包结构。

四、地图单元格+视距广播(MMO同步核心)
传奇不用全图广播,用“地图号+单元格+视距半径”裁剪:
1.角色移动完,CPlayerObject::SearchViewRange()按g_SearchTable算以自身为中心的26×26(或配置半径)格矩形;
2.把范围内CMapCellInfo->m_xpObjectList里的玩家/NPC/怪物指针填入m_xVisibleObjectList;地面物品填m_xVisibleItemList;事件填m_xVisibleEventList;
3.状态变更(RM_TURN/RM_WALK/RM_HIT/RM_STRUCK/RM_ITEMSHOW/RM_SHOWEVENT)调AddProcess()→仅向视距内对象所属CUserInfo的xSendBuffQ推SM_*;
4.离开视距收SM_CLEAROBJECT/SM_DISAPPEAR,进视距收SM_TURN+属性包;
5.切图(SM_NEWMAP)清空三张可见列表,重载Map\*.map块数据,重算Light/Fog。
这套机制让单GameSrv承载千人同服仍可控,沙巴克攻城靠GameGate多开分线+皇宫独立单元格压缩广播域。

五、战斗与消息流转实例(CM_WALK→广播)
客户端发CM_WALK(方向)→GameGate→GameSrvProcessUserHuman扔列→CPlayerObject::Operate()判m_fIsDead/CheckDoorEvent/m_pMap->CanMove/MoveToMovingObject→成功发RM_WALK给周围单元格→AddProcess转SM_WALK下发;失败回CM_FAIL(客户端OnProcPacketNotEncode拉回坐标)。
攻击链:CM_HIT/CM_SPELL→Operate()算命中(HITvsAC/MAC)、伤害(DC/MC/SC±随机±技能系数)、毒/麻痹/暴击→RM_STRUCK广播SM_STRUCK→目标HP≤0发RM_DEATH→SM_DEATH+掉落实例进m_xVisibleItemList+DBSrv异步存盘。

六、脚本系统挂载点(逻辑热更层)
GameSrv内嵌脚本解析器,NPC对话/任务/商城/攻城不写死C++:
•Merchant.txt注册NPC→Market_Def\名字-地图号.txt对话树

-QFunction-0.txt(QF)挂物品使用/死亡/登录/打怪钩子
•QuestDiary\状态机脚本,NPC内#CALL[\QuestDiary\xxx.txt]@段触发

-Robot_def\AutoRunRobot.txt定时线程驱动喊话/刷怪/攻城
脚本解析器把文本指令编译成内部事件塞回CPlayerObject::Operate队列,实现不改C++热更玩法。

七、DBSrv持久化与IPC
DBSrv启动InitInstance里GetDBManager()->Init(LogCbdbNameuserpwd)建ODBC/SQL长连;收GameSrv的DB_LOADHUMANRCD/DB_SAVEHUMANRCD/DB_ADDITEM等→拼预编译SQL→回DBR_*结果包;写操作入内存队列异步刷盘,防金币/装备双花靠事务+GUID唯一标识。

八、标准启动顺序与排错
DBSrv→LoginSrv→GameSrv(M2Server)→LoginGate→SelGate→GameGate;任意前置没起,后续进程连不上配置端口直接退。
常见卡点:
•卡登录:LoginGate端口被占/ID.DB路径错/LoginSrv未起

•选角空:SelGate未起或DBSrv未回DBR_LOADHUMANRCD

-进图瞬断:Map\*.map缺文件/MiniMap.txt错行/Envir\!Setup.txt路径中文
•同屏卡:GameGate单线,千人攻城需多RunGate+地图分块

-存盘丢:DBSrvODBC断连未重连,Hum.DB锁文件残留

C++传奇服务端的架构本质是把“网络IO、账号认证、世界模拟、数据持久化”拆成独立进程,用CM/SM/RM/GM/DB五套消息标识做弱耦合强契约通信;GameSrv内再以ProcessUserHuman/ProcessMonster/ProcessMerchant多线程流水线+CMapCellInfo视距广播撑起MMO实时性,CPlayerObject与脚本系统把数值和玩法留在可热更层,C++只保底层确定性与吞吐。
[顶部]