拿到传奇3源码第一眼看到的东西决定了你能不能往下走。整套代码分两块,一块是服务端,一块是客户端,外加地图编辑器、图片编辑器、脚本编辑器、数据转换器这些外围工具。服务端不是单个程序,是一组程序在跑,每个程序管不同的事情。
**服务器集群架构:Gate、Game、DB三层**
传奇3的服务器设计成集群模式,由Gate服务器、Game服务器、DB服务器组成,数量不固定,可以按需增减。Gate服务器需要公网IP,其他服务器全部跑在内网,外面的人打不到Game和DB,等于天然多了一道屏障。
Gate服务器做的事情不止是转发数据包,还负责加解密和身份确认。客户端发过来的数据先到Gate,解密完了再转给Game服务器。Game服务器往外发的数据也经过Gate加密之后才出去。这种做法的好处是Game服务器不用暴露在公网上,只管跑游戏逻辑,网络层面的脏活累活全让Gate扛了。
Login服务器管两件事:一是把玩家分配到不同的Game服务器,负载均衡的逻辑在这里;二是维护整个游戏世界的服务器列表。玩家瑶务器的时候看到的那个列表,就是Login服务器从数据库里拉出来的。每个Game服务器只负责特定地图上的业务,玩家从一个地图切到另一个地图,实际上是从一个Game服务器切到了另一个Game服务器。
数据流向是这样的:客户端发消息给Gate,Gate解密后转发给Game,Game处理完需要读写数据库的交给DB服务器,DB服务器把结果返回给Game,Game再经过Gate把响应回给客户端。整个过程DB服务器和Game服务器之间通过IOCP完成端口通讯,多个线程同时处理网络请求,不会互相堵住。
**网络层实现:IOCP和多线程模型**
源码里网络通讯这部分用的是IO完成端口,这是Windows平台下处理高并发连接的主流方案。原始代码里网络通讯这块有缺陷,有人接手之后重写了服务器间的通讯模块,用IOCP重新实现了一遍,性能明显提上去了。
多线程共享资源这块原始代码也没处理好,不同线程同时读写同一块内存区域,不加锁或者锁粒度太大都会出问题。接手的人把多线程的共享问题整个梳理了一遍,该加临界区的地方加临界区,该用互斥量的地方用互斥量,Release版程序里嵌入了自诊断代码,出了错能在没有调试环境的情况下定位问题。
LoginServer源码里能看到具体的线程分工:一个AcceptThread线程接收新客户端连接,多个ServerWorkerThread线程处理IOCP上的数据收发,还有个LoadAccountRecords线程专门从数据库拉服务列表。不同线程处理不同类型的消息包,比如心跳包走一个分支,登录验证走另一个分支,用户上线消息走单独的分支。
**怪物AI算法:8万个怪压到60%CPU**
怪物AI这块的算法有人重写过。原始版本的怪物AI效率不行,怪物一多服务器就扛不住。改进之后的算法让怪物的行动效率大幅提升,测试数据是在P43.0GHz单核、1GB内存的机器上,加载了500个地图,怪物数量跑到8万个,CPU使用率只有60%。
算法改进的核心思路是把怪物分组处理,同一区域的怪物共享计算资源,而不是每只怪物独立跑完整的AI逻辑链。怪物状态机的判定条件也做了精简,该合并的判定合并掉,能走位运算的不用乘除。8万个怪物同时在线,每个怪物的移动、攻击、仇恨判定都在可控的延迟内完成,玩家操作时的反馈不会明显滞后。
**脚本解释器:让普通玩家也能写脚本**
脚本解释器这部分改动的思路很直接:原来的解释器算法有漏洞,刷钱刷装备的外挂就是钻了解释器的空子。重写之后的解释器结构更简单清晰,有人甚至说"不懂程序的玩家也能自己编写脚本",说明脚本语法的可读性和容错性都做了改进。
脚本解释器在源码里是独立模块,Game服务器调用它来解析NPC对话、任务条件、物品合成规则这些逻辑。脚本不编译成二进制,而是运行时逐行解释执行,好处是改脚本不用重启服务器,坏处是解释器本身要吃一部分CPU。重写后的解释器在效率上做了平衡,解释执行的速度比原来版本快了不少。
**客户端图形引擎和地图系统**
客户端的图形引擎是一个完整的2D图形函数库,封装了DirectX的底层调用,开发者调用引擎接口贴文字、贴图片到屏幕上就行,不用直接跟DirectX打交道。瓦片地图、角色、魔法特效、物品图标,都通过这套引擎渲染。
地图系统比看上去复杂。地图文件里不只存了瓦片数据,还记录了事件触发点、光照信息、动画帧序列。玩家走到某个坐标点触发NPC对话或者进入新地图,就是地图文件里的事件数据在起作用。地图编辑器可以修改这些数据,不需要动代码。
图片资源文件WIX/WIL的格式和传奇2不一样。传奇3的WIX文件头是56个字节,传奇2是另外一种结构,直接用传奇2的读冉式读传奇3的图片会读出一堆乱码。WIX文件里存的是图片数量和每张图片在WIL文件中的位置偏移,客户端启动时先读WIX拿到索引,再从WIL里按偏移加载具体的图片数据。
**登录模块的代码结构**
客户端的入口从WinMain开始,初始化之后进入登录流程,对应的类是CLoginProcess,继承自CWHDefProcess。CLoginProcess里包含了登录界面的渲染、按钮事件处理、鼠标键盘响应、服务器列表获取、账号密码验证这些功能。
登录界面渲染分成几个状态:Intro开场、Scene场景、Scroll滚动公告、NewAccount注册新账号、Patch更新、Password密码找回。每个状态有独立的Render函数,界面切换的时候调用对应的渲染函数。用户点击登录按钮之后,客户端通过Socket连到LoginServer,LoginServer验证账号密码之后返回服务器列表,玩家选完服务器之后客户端断开LoginServer的连接,转去连GateServer。
Socket通讯这块用的是异步模式,客户端发消息出去之后不等返回就继续跑界面渲染,返回数据到了之后通过消息回调函数处理。OnMessageReceive函数是接收消息的统一入口,根据消息类型走不同的处理分支。
**服务器集群架构:Gate、Game、DB三层**
传奇3的服务器设计成集群模式,由Gate服务器、Game服务器、DB服务器组成,数量不固定,可以按需增减。Gate服务器需要公网IP,其他服务器全部跑在内网,外面的人打不到Game和DB,等于天然多了一道屏障。
Gate服务器做的事情不止是转发数据包,还负责加解密和身份确认。客户端发过来的数据先到Gate,解密完了再转给Game服务器。Game服务器往外发的数据也经过Gate加密之后才出去。这种做法的好处是Game服务器不用暴露在公网上,只管跑游戏逻辑,网络层面的脏活累活全让Gate扛了。
Login服务器管两件事:一是把玩家分配到不同的Game服务器,负载均衡的逻辑在这里;二是维护整个游戏世界的服务器列表。玩家瑶务器的时候看到的那个列表,就是Login服务器从数据库里拉出来的。每个Game服务器只负责特定地图上的业务,玩家从一个地图切到另一个地图,实际上是从一个Game服务器切到了另一个Game服务器。
数据流向是这样的:客户端发消息给Gate,Gate解密后转发给Game,Game处理完需要读写数据库的交给DB服务器,DB服务器把结果返回给Game,Game再经过Gate把响应回给客户端。整个过程DB服务器和Game服务器之间通过IOCP完成端口通讯,多个线程同时处理网络请求,不会互相堵住。
**网络层实现:IOCP和多线程模型**
源码里网络通讯这部分用的是IO完成端口,这是Windows平台下处理高并发连接的主流方案。原始代码里网络通讯这块有缺陷,有人接手之后重写了服务器间的通讯模块,用IOCP重新实现了一遍,性能明显提上去了。
多线程共享资源这块原始代码也没处理好,不同线程同时读写同一块内存区域,不加锁或者锁粒度太大都会出问题。接手的人把多线程的共享问题整个梳理了一遍,该加临界区的地方加临界区,该用互斥量的地方用互斥量,Release版程序里嵌入了自诊断代码,出了错能在没有调试环境的情况下定位问题。
LoginServer源码里能看到具体的线程分工:一个AcceptThread线程接收新客户端连接,多个ServerWorkerThread线程处理IOCP上的数据收发,还有个LoadAccountRecords线程专门从数据库拉服务列表。不同线程处理不同类型的消息包,比如心跳包走一个分支,登录验证走另一个分支,用户上线消息走单独的分支。
**怪物AI算法:8万个怪压到60%CPU**
怪物AI这块的算法有人重写过。原始版本的怪物AI效率不行,怪物一多服务器就扛不住。改进之后的算法让怪物的行动效率大幅提升,测试数据是在P43.0GHz单核、1GB内存的机器上,加载了500个地图,怪物数量跑到8万个,CPU使用率只有60%。
算法改进的核心思路是把怪物分组处理,同一区域的怪物共享计算资源,而不是每只怪物独立跑完整的AI逻辑链。怪物状态机的判定条件也做了精简,该合并的判定合并掉,能走位运算的不用乘除。8万个怪物同时在线,每个怪物的移动、攻击、仇恨判定都在可控的延迟内完成,玩家操作时的反馈不会明显滞后。
**脚本解释器:让普通玩家也能写脚本**
脚本解释器这部分改动的思路很直接:原来的解释器算法有漏洞,刷钱刷装备的外挂就是钻了解释器的空子。重写之后的解释器结构更简单清晰,有人甚至说"不懂程序的玩家也能自己编写脚本",说明脚本语法的可读性和容错性都做了改进。
脚本解释器在源码里是独立模块,Game服务器调用它来解析NPC对话、任务条件、物品合成规则这些逻辑。脚本不编译成二进制,而是运行时逐行解释执行,好处是改脚本不用重启服务器,坏处是解释器本身要吃一部分CPU。重写后的解释器在效率上做了平衡,解释执行的速度比原来版本快了不少。
**客户端图形引擎和地图系统**
客户端的图形引擎是一个完整的2D图形函数库,封装了DirectX的底层调用,开发者调用引擎接口贴文字、贴图片到屏幕上就行,不用直接跟DirectX打交道。瓦片地图、角色、魔法特效、物品图标,都通过这套引擎渲染。
地图系统比看上去复杂。地图文件里不只存了瓦片数据,还记录了事件触发点、光照信息、动画帧序列。玩家走到某个坐标点触发NPC对话或者进入新地图,就是地图文件里的事件数据在起作用。地图编辑器可以修改这些数据,不需要动代码。
图片资源文件WIX/WIL的格式和传奇2不一样。传奇3的WIX文件头是56个字节,传奇2是另外一种结构,直接用传奇2的读冉式读传奇3的图片会读出一堆乱码。WIX文件里存的是图片数量和每张图片在WIL文件中的位置偏移,客户端启动时先读WIX拿到索引,再从WIL里按偏移加载具体的图片数据。
**登录模块的代码结构**
客户端的入口从WinMain开始,初始化之后进入登录流程,对应的类是CLoginProcess,继承自CWHDefProcess。CLoginProcess里包含了登录界面的渲染、按钮事件处理、鼠标键盘响应、服务器列表获取、账号密码验证这些功能。
登录界面渲染分成几个状态:Intro开场、Scene场景、Scroll滚动公告、NewAccount注册新账号、Patch更新、Password密码找回。每个状态有独立的Render函数,界面切换的时候调用对应的渲染函数。用户点击登录按钮之后,客户端通过Socket连到LoginServer,LoginServer验证账号密码之后返回服务器列表,玩家选完服务器之后客户端断开LoginServer的连接,转去连GateServer。
Socket通讯这块用的是异步模式,客户端发消息出去之后不等返回就继续跑界面渲染,返回数据到了之后通过消息回调函数处理。OnMessageReceive函数是接收消息的统一入口,根据消息类型走不同的处理分支。

