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

热血传奇RunGate源代码分析笔记:网关Socket模型与封包转发实录

热度:
RunGate在传奇服务端体系里对应游戏运行网关(常见目录MirServer\RunGate\RunGate.exe,对外端口7200,对内连M2Server的5000通信口),职责是承接已进入游戏阶段的客户端TCP连接,把操作指令(移动、攻击、拾取、交易、技能)转发给M2,再把M2回包编码后送回客户端;账号验证走LoginGate(7000→LoginSvr5500),角色创建选择走SelGate/SelChrGate(7100→DBSvr5100),RunGate只管“进图以后”的业务流,不参与账号生命周期。

源码形态分两支:早期官方泄露版是Delphi(Pascal)写的RunGate.exe,单元文件含Main.pas、GateShare.pas、HUtil32.pas、IPaddrFilter.pas、OnLineHum.pas、GameSpeed.pas;现代重构版(OpenMir2、mirbeta)是C#的GameGate工程,目录src/GameGate/GameGate.csproj,配置appsettings.json,监听7000/7100/7200视分支命名而定。

Delphi原版核心结构:三个Socket对象——ClientSocket(监昕客户端7200)、M2Socket(连M2Server内部口)、ConsoleSocket(连控制台/管理器发保活包,手工独立启动时可无,纯为管理端探活)。实际业务只靠前两个,典型生产者-消费者模型:客户端收包→校验→入ReciveMsgList;Timer定时从列表取消息→封TMsgHeader→发M2;M2回包→按Socket索引查会话→编码下行。

会话管理不在堆上动态new,而是编译期常量定长栈数组TSessionArr[MAXSESSION],结构体含dwConnTime、nPacketSize、dwLastMoveTick、dwLastCommTick、nSocket、nUserIdx等;新连接进来扫未用槽位初始化,断开清槽。定长数组换来O(1)索引但上限硬顶,超员直接拒连,这是老网关并发上不去的根因。

封包头TMsgHeader(packedrecord,Delphi里packed防对齐填充):

dwCode:LongWord;//标识码如$AA55或引擎约定魔法数
nSocket:Integer;//M2侧回包定位客户端用的网关内Socket句柄
wGSocketIdx:Word;//网关会话索引
wIdent:Word;//消息类型CM_MOVE/CM_ATTACK/SM_UPDATE等
wUserListIndex:Integer;
nLength:Integer;//后续body长度

客户端上行原包经Gate加头变内部格式发给M2;M2下行包Gate按wGSocketIdx/nSocket反查会话,剥头,做EnDecode(XOR/表置换,老版解密实际落在M2,Gate只做透传与简单校验,这是后来被诟病的一点)后写回客户端。

主循环不是独立线程跑recv/send,而是靠TTimer周期触发(如50ms一次)扫ReciveMsgList与SendBuf,配合WSAEventSelect或select模型收发包。优点是无锁简单,缺点是Timer精度与界面消息队列绑定,客户端量大时延迟抖动明显;社区改法一般是拆IOCP/独立收发线程+无锁环形队列替代Timer。

M2侧对应结构(C++版框架参考):GameServer维护m_RunGateMap<SOCKETCRunGate*>,RunGate连上来accept后newCRunGate接管;CRunGate内维护m_CPlayerMap<SOCKETCPlayer*>与m_strRecvBuf,ProcessData()从缓冲切完整包→ParsePkg()→CPacketParser::ProcessPkgFromClient(pPlayerdatalen)落到游戏逻辑;客户端上线RunGate发通知建CPlayer,断线发通知删对象,M2不直接持客户端TCP。

配置文件RunGate.ini/MirGate.ini关键字段:ServerAddr(M2IP,本地127.0.0.1)、ServerPort(内部通信口5000)、GatePort(对外7200)、MaxSession、BlockIPList.txt(黑名单)、WordFilter.txt(聊天过滤)、KeepAliveTick(保活间隔)。C#版改appsettings.json的UpstreamGameSvr、ListenPort、AntiSpeed、PacketLimit。

辅助模块:IPaddrFilter.pas做IP黑名单/白名单与连接频率限制;GameSpeed.pas统计客户端移动间隔判定变速器;OnLineHum.pas维护在线人数与会话列表供控制台读;GateShare.pas放全局常量、TMsgHeader定义、EnDecode表、日志宏。EDoceTool是运行期日志落地,MakeKey生成通信密钥,OL验证器做在线合法性探活。

启动顺序硬约束:DBServer→LoginSvr→M2Server(Mir200)→LoginGate→SelGate→RunGate,最后起登录器;Gate连不上上游会打日志并周期重连,不会自退。

数据流一句话串:客户端TCP7200→RunGate.ClientSocket→校验/入队→TMsgHeader封装→M2Socket→M2Server解头跑逻辑→回包带nSocket/wGSocketIdx→RunGate反查会话→编码下行→客户端。登录前链:Client→7000LoginGate→5500LoginSvr→7100SelGate→5100DBSvr→下发角色列表→穴后由M2授权RunGate建会话→切到7200主通信。

读源码建议顺序:GateShare.pas(结构定义)→Main.pas(Socket事件、Timer)→Session数组管理→ReciveMsgList/SendList→TMsgHeader封装拆解→M2Socket发送→Console保活。Delphi版变量名保留dw/n/w匈牙利前缀,C#版看ChannelMessageHandler.cs与GateConnection.cs对照看最快。
[顶部]