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

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

有时间的话,也可以先看看设计模式之类的,比如一些通用的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);
	} 

	//保存Module
	do 
	{
		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,这里只加载了一个,抛砖引玉只用
	// 主要讲解并不在这里,有兴趣的可以加载多个dll
	char 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值