游戏服务器引擎开发笔记之三——游戏服务器引擎和逻辑的功能的相互调用(二)

来源:互联网 发布:云计算 中国科学院 编辑:程序博客网 时间:2024/04/29 03:36

其实很早就想将代码贴出来,一直都在想服务器的讲解可能比较枯燥。

有时间的话,也可以先看看设计模式之类的,比如一些通用的Factory模式,Adapter模式,了解了解。

其他的也不多说。先贴代码,做做简单的说明。

上一章讲述了IModule和IModuleFactory的实现,这里就不多讲。现在有一个关键的地方,就是我们在实现相应逻辑功能的时候,不可能只写一个逻辑功能模块。这时候我们就需要使用相应的ModuleFactory来创建对象实例。

IModuleFactory重写一遍:

class IModuleFactory{public:IModuleFactory(IModuleFactory * next){m_pNext = next;m_pModule = 0;};virtual ~IModuleFactory(){if (m_pModule != 0){m_pModule->Shutdown();delete m_pModule;}};virtual const char * Name() = 0;virtual IModule * Create() = 0;IModuleFactory * Next() { return m_pNext; }IModule * GetModule() { return m_pModule; }bool Created() { return m_pModule != 0; }protected:IModuleFactory *m_pNext;IModule *m_pModule;};

通过链表的形式,只需要得到第一个ModuleFactory,我们就能得到所有的ModuleFactory,然后创建出所有Module的实例了。

下面使用宏来替换相应的ModuleFactory:

// 这里需要关注一下##和#的用法#define CREATE_MODULE(a) \class a##ModuleFactory: public IModuleFactory \{\public: \a##ModuleFactory(IModuleFactory * & p): IModuleFactory(p) { p = this; } \virtual ~##a##ModuleFactory() {} \virtual IModule * Create() { if (m_pModule == 0) {m_pModule = new a;} return m_pModule; } \virtual const char * Name() { return #a; } \}; \a##ModuleFactory a##ModuleFactory(g_pModuleFactory);

在此说明一下,#把宏参数变为一个字符串,用##把两个宏参数贴合在一起。具体的用法各位可以上百度或者写个小程序测试一下。

为了好看和代码编写简单,我们将DLL的函数导出也用宏定义一下:

#define BEGIN_MODULES \extern "C" __declspec(dllexport)\IModuleFactory * g_pModuleFactory = 0;\extern "C" __declspec(dllexport)\IModuleFactory * __cdecl GetModuleFactory()\{\return g_pModuleFactory;\}


上面就是逻辑功能DLL的基础功能了,下面我们再来说说引擎冲的基础功能。

首先我们需要一个class来管理游戏引擎的操作,我们将它命名为GameWorld:

class CGameWorld{public:CGameWorld(const char *path);virtual ~CGameWorld();public:CKernel* GetKernel(){return m_pKernel;}CModuleSet* GetModuleSet(){return m_pModuleSet;}public:bool Begin();bool Shutdown();private:CKernel* m_pKernel;CModuleSet* m_pModuleSet;private:char m_path[MAX_PATH];};
它里面有两个成员,Kernel就是我所定义的引擎指针,ModuleSet是负责管理逻辑模块的功能。

我们先看ModuleSet:

class CModuleSet{public:CModuleSet();CModuleSet(const CModuleSet &other);virtual ~CModuleSet();public://初始化bool Initialize(CKernel * pKernel);//查找模块IModule * FindModule(const char * module);//释放模块bool Shutdown();//////////////////////////////////////////////////////////////////////////public://加载模块bool Create(const char * module);protected:typedef map<string, IModule*> MapModule;private:MapModule m_mapModule;typedef IModuleFactory * (*GetModuleFactory)(); //宏定义函数指针类型 };
看到后面有一个typedef了吗,GetModuleFactory是不是很熟悉,在哪见过呢?
IModuleFactory * __cdecl GetModuleFactory()\{\return g_pModuleFactory;\}
就是它了,我们能通过它,得到函数地址,然后就能够得到ModuleFactory的指针,再然后就能创建Module的实例。

一切就是这么简单,但是没办法,还是需要一步步的讲解,首先是ModuleSet的构造和析构函数:

CModuleSet::CModuleSet(){}CModuleSet::CModuleSet(const CModuleSet &other){ m_mapModule = other.m_mapModule;}CModuleSet::~CModuleSet(){}
这个有点浪费篇幅了,我也觉得,下面就是过去指针创建实例的地方了:

//加载模块bool CModuleSet::Create(const char * module){HINSTANCE hDll = NULL; //DLL句柄 hDll = LoadLibrary(module); if (hDll == NULL) {return false;}GetModuleFactory modulefactory; //函数指针 modulefactory = (GetModuleFactory)GetProcAddress(hDll, "GetModuleFactory"); IModuleFactory * pCreator = (*modulefactory)();if (pCreator == NULL) { FreeLibrary(hDll);} //保存Moduledo {IModule * p = pCreator->Create();string name = pCreator->Name();m_mapModule.insert(make_pair(name, p));pCreator = pCreator->Next();} while (pCreator != NULL);return true;}
关于应用程序调用DLL的地方,有不熟悉的,可以去找找相关资料。
主要是3个函数LoadLibrary、FreeLibrary、GetProcAddress。
Module的初始化和释放:

//初始化bool CModuleSet::Initialize(CKernel * pKernel){for (MapModule::iterator it = m_mapModule.begin();it != m_mapModule.end();++it){if (!it->second->Initialize((IKernel*)pKernel)){return false;}}return true;}//释放模块bool CModuleSet::Shutdown(){for (MapModule::iterator it = m_mapModule.begin();it != m_mapModule.end();++it){if (!it->second->Shutdown()){return false;}}return true;}
查找模块:
//查找模块IModule * CModuleSet::FindModule(const char * module){MapModule::iterator it = m_mapModule.find(module);if (it != m_mapModule.end()){return it->second;}return NULL;}
下面我们讲讲提供给逻辑层调用的引擎接口:

class CKernel : IKernel{public:CKernel();virtual ~CKernel();public:// 显示virtual void Print(const char * info);// 获取其他逻辑模块地址virtual IModule *GetModule(const char * name);};
这里面我只写了2个接口,其中一个Print是为了抛砖引玉,能实现它,自然也就能实现其他的接口。

另外一个是获得其他逻辑模块的地址,这样的话我们就能够实现引擎和逻辑、逻辑和逻辑之间的相会调用了。

下面的是实现代码:

extern CGameWorld *g_pGameWorld;CKernel::CKernel() {}CKernel::~CKernel(){}// void CKernel::Print(const char * info){printf("%s", info);}// IModule *CKernel::GetModule(const char * name){return g_pGameWorld->GetModuleSet()->FindModule(name);}
到此,GameWorld的两个成员就都实现完了,我们再来看看GameWorld的实现。

先是构造和析构:

CGameWorld::CGameWorld(const char *path){m_pKernel= NULL;m_pModuleSet= NULL;strcpy(m_path, path);}CGameWorld::~CGameWorld(){delete m_pKernel;delete m_pModuleSet;}
Begin和Shutdown的实现:

bool CGameWorld::Begin(){m_pKernel = new CKernel();m_pModuleSet = new CModuleSet();// 加载dll,这里只加载了一个,抛砖引玉只用// 主要讲解并不在这里,有兴趣的可以加载多个dllchar dll[MAX_PATH] = {0};if (!m_path || strlen(m_path) == 0){return false;}size_t index = strlen(m_path) - 1;while (index >= 0){if (m_path[index] == '\\'){break;}index--;}strncpy(dll, m_path, index + 1);strcat(dll, "Logic.dll");m_pModuleSet->Create(dll);// 断点跟进就能看到如何相互调用,包括引擎和逻辑间调用,逻辑和逻辑间调用m_pModuleSet->Initialize(m_pKernel);return true;}bool CGameWorld::Shutdown(){m_pModuleSet->Shutdown();return true;}

我们再来看看main函数:

CGameWorld * g_pGameWorld = 0;int main(int argc, char* argv[]){g_pGameWorld = new CGameWorld(argv[0]);g_pGameWorld->Begin();// 这里可以使用scanf或其他通过读字符串的操作,通过输入命令来break,随意发挥while (true){Sleep(1000);}g_pGameWorld->Shutdown();delete g_pGameWorld;return 0;}
引擎的代码就到这里了,逻辑DLL的代码可以通过下面的链接下载之后看一下。只是实现了Print和GetModule的调用。

在vs2005下编译并且调试通过。

说完这一章,其实应该明白了逻辑功能个引擎之间的调用,后面的只要讲解都会是关于引擎的实现,有可能讲解的篇幅太长,一个功能分为几个篇章讲解。代码并不太会频繁提供源码下载,主要是一些想法的东西和文章中将代码贴出。不过在一个完整的功能讲完之后都会有编译通过的的源码提供。


本章源码下载地址:

http://download.csdn.net/detail/abc2258/5455659

原创粉丝点击