最近下到了一份mangos0.9版本的源码,打算用心读一读,也算是学习学习。
当然这也是本人第一次接触游戏服务器源码,估计会遇到很多很多的问题,这篇文章也是本人的第一篇博客,来csdn那么多年了,终于打算开始写点东西了,接下来的一段时间,想自己研究研究这份代码,随手写点东西也算是做了笔记了。
mangos不是一个魔兽私服模拟器,它是一个开源的自由软件项目,是用c++和C#编程语言,实现的一个支持大型多人在线角色扮演游戏服务器的程序框架,在这个框架下,它理论上应该支持任何客户端的网络游戏,由于现在很多人使用魔兽世界来对它进行测试,所以针对魔兽世界的脚本和数据库文件比较完善,很多人就利用这个开源项目来实现魔兽私服。mangos的技术细节上是这样的,核心部分是个和特定游戏没有关系的核心框架程序,主要是进行进程调度,创造世界,建立心跳机制,处理网络接入等。数据库也是使用的开源数据库软件MySQL,编译器使用的是GCC。至于游戏内容数据库,游戏人物,时间,世界脚本,都是由这个核心程序所支持的扩展脚本来实现,所以有一些独立出来的项目专门模拟魔兽世界来开发支持mangos的核心程序。现在mangos的核心程序已经放到著名的协同开发网站sourceforge上开发了,使用的版本控制工具是subversion,(目前已经迁移到GIT)。大家都可以到github下载最新的源代码程序。(该段转自百度百科)
我下载的mangos0.9版本在csdn资源中就有,源代码在mangos/src目录中,该目录中包括6个文件夹,和makefile.in 和makefile.am文件,这两个文件不用说的,产生makefile文件用的。 关键在所包含的六个文件夹 bindings framework game mangosd realmd shared 。
其中bindings文件夹中包含脚本文件,应该是对脚本进行绑定的。
framework文件夹中包括一些游戏框架,其中包括网络框架,游戏系统框架,工具,平台等内容。
game文件夹中应该是游戏的文件,包括世界系统,战斗系统,游戏事件,游戏场景等的实现。
mangosd文件夹中是mangosd的主程序,包括程序的入口等。
realmd 文件夹中是游戏区域信息,包括RealmList等内容。
shared文件夹中 应该是公用的函数和库,database的内容包含在其中。
以上内容只是自己的摸索,不保准的,呵呵。
另外分析一下整体结构:
mangos使用了4个外部工具库,分别是:
跨平台的网络通讯框架The ADAPTIVE Communication Environment (ACE)
压缩库zlib
Socket通信库 C++ Sockets Library 和 C++的并行编程模板库Threading Building Blocks (tbb 和 tbbmalloc)
mangos可以分为以下几个工程模块:
工程mangosd
mangos是世界服务器的管理器,负责初始化工作和启动世界服务器各层的线程,这些工作主要是由类Master来实现。
类Master执行包括连接数据库,载入世界模型,启动世界更新线程,启动命令行线程,启动RA线程等操作。
mangosd共有13个线程:
一个World线程(逻辑层)
三个DB线程(数据层)
一个CLI线程(输入层),运行时候会生成一个WorldDatabase线程
一个RA线程(管理层)
一个freeze catcher 线程(可选)
工程g3dlite:游戏逻辑层的底层库
工程framework:系统框架
工程realm :负责登陆和选择游戏服务器,进行负载均衡用到了C++ Sockets Library进行登录处理,采用select I/O模型实现了Wow, Mangos登录时的SRP6认证客户端作为它的client连接到realm server认证和选择了mangos server就断开 而mangos server和realm server则不进行连接,只是通过数据库交互数据:mangos server把自己的状态和拥有的角色数放入库中realm server会读取数据库中的这些信息来获知mangos server的状态 数据库realm的realmlist表保存了realm的列表。
工程game
game:是Mangos的核心代码,网络层和逻辑层代码(采用了ACE反应器(Reactor)模式)
mangos的执行模型如下:
一、线程分布:
1、主线程 main---- Master::Run() ,主要功能:初始化world、创建子线程、回收资源
2、WorldRunnable -------GS主线程
3、CliRunnable -----后台调试线程
4、RARunnable -------事件处理和分发线程
5、MaNGOSsoapRunnable---协议
6、FreezeDetectorRunnable
7、线程池 Master::Run----WorldSocketMgr::StartNetwork---WorldSocketMgr::StartReactiveIO ---ReactorRunnable
二、事件分发和处理
WorldRunnable::run---World:update----World:UpdateSessions---WorldSession::Update(一个socket内所有事件)---各种各样的handler
基本框架:ACE的Reactor机制(ACE_TP_Reactor)
三、WorldRunnable 主要功能
WorldRunnable ----World 定时器任务+网络事件(session中的)+异步IO回调+任务系统调度+cli
以上部分内容参考网络上一些资料
按照逻辑,先从mangosd开始读起, 因为目前我也不是很清楚这份代码的架构,也处于摸索阶段,先从main开始入手,也算是摸着石头过河了。
// 此处略去了版权声明,开源软件尊重版权
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "Config/ConfigEnv.h"
#include "Log.h"
#include "Master.h"
#include "SystemConfig.h"
// 用于系统的判断,用于兼容Windows系统,mangosd原本是linux下的开源软件
#ifdef WIN32
#include "ServiceWin32.h"
char serviceName[] = "mangosd";
char serviceLongName[] = "MaNGOS world service";
char serviceDescription[] = "Massive Network Game Object Server";
/*
* -1 - not in service mode
* 0 - stopped
* 1 - running
* 2 - paused
*/
int m_ServiceStatus = -1;
#endif
//选择数据库的宏定义,一般选择MySql数据库 ,其中DatabaseMySql类定义在shared文件夹中,数据库类作为公共文件
#ifdef DO_POSTGRESQL
DatabasePostgre WorldDatabase; ///< Accessor to the world database
DatabasePostgre CharacterDatabase; ///< Accessor to the character database
DatabasePostgre loginDatabase; ///< Accessor to the realm/login database
#else
DatabaseMysql WorldDatabase; ///< Accessor to the world database
DatabaseMysql CharacterDatabase; ///< Accessor to the character database
DatabaseMysql loginDatabase; ///< Accessor to the realm/login database
#endif
uint32 realmID; /// 使用一个全局变量记录游戏区域的id,因为游戏区id唯一性,一旦选择应该保持全局不变
/// 从终端输出一些版本Server运行信息
void usage(const char *prog)
{
sLog.outString("Usage: \n %s []\n"
" -c config_file use config_file as configuration file\n\r"
#ifdef WIN32
" Running as service functions:\n\r"
" --service run as service\n\r"
" -s install install service\n\r"
" -s uninstall uninstall service\n\r"
#endif
,prog);
}
/// 主函数,登录mangos的入口
extern int main(int argc, char **argv)
{
// 创建一个mangos内存管理 并对该内存管理类进行初始化
// 该内存管理类模版在framework文件中的policies中进行声明,使用MaNGOS命名空间,负责什么以后再看了。。。
MaNGOS::Singleton::Instance();
///读取Mangosd的配置文件 其中 宏_MANGOSD_CONFIG为"mangosd.conf" 该配置文件就在mangosd文件夹中
char const* cfg_file = _MANGOSD_CONFIG;
int c=1;
//下面执行的是一些附带命令,在启动时作为附带命令来指定文件和输出相关信息 ( 附带命令是随便解释的呵呵 )
while( c < argc )
{
if( strcmp(argv[c],"-c") == 0)
{ // "-c"应该是配置文件的命令,后面附带配置文件
if( ++c >= argc )
{
// 后文不带参数,报错
sLog.outError("Runtime-Error: -c option requires an input argument");
usage(argv[0]);
return 1;
}
else
cfg_file = argv[c]; // 配置文件
}
// 以下宏定义范围内为Windows下的文件配置信息也是用来配置configure文件的, 此处略过
#ifdef WIN32
//Services//
if( strcmp(argv[c],"-s") == 0)
{
if( ++c >= argc )
{
sLog.outError("Runtime-Error: -s option requires an input argument");
usage(argv[0]);
return 1;
}
if( strcmp(argv[c],"install") == 0)
{
if (WinServiceInstall())
sLog.outString("Installing service");
return 1;
}
else if( strcmp(argv[c],"uninstall") == 0)
{
if(WinServiceUninstall())
sLog.outString("Uninstalling service");
return 1;
}
else
{
sLog.outError("Runtime-Error: unsupported option %s",argv[c]);
usage(argv[0]);
return 1;
}
}
if( strcmp(argv[c],"--service") == 0)
{
WinServiceRun();
}
#endif
++c;
}
// 读取配置文件出错
if (!sConfig.SetSource(cfg_file))
{
sLog.outError("Could not find configuration file %s.", cfg_file);
return 1;
}
// 成功读取配置文件,输出信息
sLog.outString("Using configuration file %s.", cfg_file);
// 其实服务器主程序应该是在这里运行的,这个sMaster对象这样定义着
// #define sMaster MaNGOS::Singleton::Instance()
// 其实还是作为Singleton的模版传进去进行初始化后得到的,这里的Singleton是不是设计模式中的单例模式呢??我也不清 // 楚 呵呵
// 而模版类MaNGOS::Singleton 自我感觉应该是非常关键的一点,以后会详细分析其实现和设计
sMaster.Run(); // 运行Master对象 ,至此,Mangos开始运行。
return 0;
}
其实 mangos中的main()多是一些配置文件信息,进行数据库,内存管理,配置文件的初始化等工作,然后执行sMaster开始真正运行系统。
下一次顺着这条路 从sMaster开始接着向下分析。
实验室关门了。。