C++版本的传奇服务端源码呈现典型的多层网关转发架构。项目整体分为LoginGate、LoginSvr、SelGate、GameGate、GameSvr和DBSvr六个独立服务模块。各模块之间通过Windows消息机制和自定义协议包完成通信。LoginGate监听7000端口接收客户端登录请求,LoginSvr负责账号验证并与DBSvr交互完成数据库查询。SelGate处理角色选择阶段的数据流转,GameGate承载游戏过程中的数据包转发。DBSvr独立运作处理所有数据库读写请求,与GameSvr之间没有直接调用关系,所有数据交互通过消息队列中转。
核心数据结构TDefaultMessage定义所有游戏协议的基础格式。该结构包含wIdent消息编号、wParam和wParam2作为参数位、dwUser和dwUser2作为用户数据。每个数据包以uint16类型的高位和低位标识消息类型,不同wIdent对应不同的业务逻辑分支。消息编号通过系统消息宏定义,例如SM_NEWMAP表示地图加载,SM_LOGON表示登录成功响应。
网络层的IO模型采用IOCP完成端口。关键函数CreateIoCompletionPort将套接字与完成端口绑定,GetQueuedCompletionStatus在多个工作线程中阻塞等待完成事件。WSARecv和WSASend作为异步函数调用后立即返回,实际IO操作由内核线程池处理。处理完成后完成事件推入端口队列,工作线程取出并调用相应回调。每个客户端连接关联一个PER_SOCKET_CONTEXT结构,存储接收缓冲区、当前解析状态、待发送队列等上下文数据。
游戏主循环设计为固定时间片轮询。GameSvr的ProcessMain函数每帧执行Tick,计算当前时间与上一帧的时间差作为步长参数传入各处逻辑。ProcessUserHuman遍历在线玩家列表,对每个玩家调用Operate方法处理输入队列中的操作指令。ProcessMonster遍历怪物列表执行AI状态机更新,ProcessMerchants处理NPC逻辑。所有Update完成后调用SendAll发送累积的响应数据包。
实体管理器采用动态数组结合空闲链表的方式。玩家对象在登录时分配索引,登出时回收索引加入空闲链表。对象池预分配固定大小,避免运行时频繁new和delete。每个实体对象通过unique_id进行跨模块引用。属性系统使用int数组存储基础属性和临时加成,每种属性类型通过枚举值索引访问,属性变更时触发CheckState函数重新计算衍生值。
地图系统划分为固定尺寸的格子网格。每个格子持有该位置上的动态实体链表和静态阻挡标志。坐标移动请求先经过CanMove函数检测目标格子的阻挡层,再遍历实体链表检查是否与其他玩家或怪物发生碰撞。视野维护采用九宫格机制,每次位置更新后计算周围的9个格子,对比新旧可见列表生成进入视野和离开视野的事件。视野范围内的实体以ID数组形式打包下发。
脚本引擎通过封装C函数指针实现自定义逻辑。NPC脚本和任务脚本以文本形式存放在Envir目录。服务启动时加载所有脚本文件并解析成指令树。玩家触发对话时根据文件名和节点名查找对应指令段,执行Check系列条件判断,条件满足则执行Give等操作。自定义函数使用ScriptFunction宏注册,引擎上下文保存函数名与函数指针的映射表,脚本中使用CallFunc调用。
数据库访问通过ODBC接口连接MySQL或SQLite。DBSvr内部维护线程池执行异步查询,每个查询请求封装为DBJob结构,包含SQL语句、回调函数指针和用户数据。查询结果通过窗口消息返回GameSvr,主线程在消息循环中接收并派发给对应的玩家对象。角色存档操作每5分钟触发一次批量写入,将内存中脏数据标记的玩家对象逐条同步到数据库。
核心数据结构TDefaultMessage定义所有游戏协议的基础格式。该结构包含wIdent消息编号、wParam和wParam2作为参数位、dwUser和dwUser2作为用户数据。每个数据包以uint16类型的高位和低位标识消息类型,不同wIdent对应不同的业务逻辑分支。消息编号通过系统消息宏定义,例如SM_NEWMAP表示地图加载,SM_LOGON表示登录成功响应。
网络层的IO模型采用IOCP完成端口。关键函数CreateIoCompletionPort将套接字与完成端口绑定,GetQueuedCompletionStatus在多个工作线程中阻塞等待完成事件。WSARecv和WSASend作为异步函数调用后立即返回,实际IO操作由内核线程池处理。处理完成后完成事件推入端口队列,工作线程取出并调用相应回调。每个客户端连接关联一个PER_SOCKET_CONTEXT结构,存储接收缓冲区、当前解析状态、待发送队列等上下文数据。
游戏主循环设计为固定时间片轮询。GameSvr的ProcessMain函数每帧执行Tick,计算当前时间与上一帧的时间差作为步长参数传入各处逻辑。ProcessUserHuman遍历在线玩家列表,对每个玩家调用Operate方法处理输入队列中的操作指令。ProcessMonster遍历怪物列表执行AI状态机更新,ProcessMerchants处理NPC逻辑。所有Update完成后调用SendAll发送累积的响应数据包。
实体管理器采用动态数组结合空闲链表的方式。玩家对象在登录时分配索引,登出时回收索引加入空闲链表。对象池预分配固定大小,避免运行时频繁new和delete。每个实体对象通过unique_id进行跨模块引用。属性系统使用int数组存储基础属性和临时加成,每种属性类型通过枚举值索引访问,属性变更时触发CheckState函数重新计算衍生值。
地图系统划分为固定尺寸的格子网格。每个格子持有该位置上的动态实体链表和静态阻挡标志。坐标移动请求先经过CanMove函数检测目标格子的阻挡层,再遍历实体链表检查是否与其他玩家或怪物发生碰撞。视野维护采用九宫格机制,每次位置更新后计算周围的9个格子,对比新旧可见列表生成进入视野和离开视野的事件。视野范围内的实体以ID数组形式打包下发。
脚本引擎通过封装C函数指针实现自定义逻辑。NPC脚本和任务脚本以文本形式存放在Envir目录。服务启动时加载所有脚本文件并解析成指令树。玩家触发对话时根据文件名和节点名查找对应指令段,执行Check系列条件判断,条件满足则执行Give等操作。自定义函数使用ScriptFunction宏注册,引擎上下文保存函数名与函数指针的映射表,脚本中使用CallFunc调用。
数据库访问通过ODBC接口连接MySQL或SQLite。DBSvr内部维护线程池执行异步查询,每个查询请求封装为DBJob结构,包含SQL语句、回调函数指针和用户数据。查询结果通过窗口消息返回GameSvr,主线程在消息循环中接收并派发给对应的玩家对象。角色存档操作每5分钟触发一次批量写入,将内存中脏数据标记的玩家对象逐条同步到数据库。

