从零开始编写网络游戏


任何事情都需要一定的基础,没有坚实的地基,是不可能建立雄伟的大厦的。所以我在整个系列博文的最前面,把一些最基础的东西先整理出来,为后面的系统做铺垫。

本篇的内容,会介绍几个内容:单例,dll动态加载以及一些跨平台的处理。

1、单例:单例模式是一种使用广泛而又比较简单的设计模式,他的定义我就不多介绍了,大家上网一查就知道了,基本都能理解。在游戏开发中,会有很多单件,所以封装一个单例类供后面的开发使用。

本单例使用模板实现,代码如下:

  1. //singleton.h
  2. #ifndef_SINGLETON_H
  3. #define_SINGLETON_H
  4. namespaceBlaze
  5. {
  6. template<classT>
  7. classSingleton
  8. {
  9. public:
  10. staticT*instance()
  11. {
  12. if(!_instance)
  13. {
  14. _instance=newT;
  15. }
  16. return_instance;
  17. }
  18. protected:
  19. ///使用保护构造是为了用户不能在栈上声明一个实例
  20. Singleton(){}
  21. private:
  22. staticT*_instance;///实例静态指针
  23. };
  24. ///静态实例指针初始化
  25. template<classT>T*Singleton<T>::_instance=NULL;
  26. }//namespace
  27. #endif//_SINGLETON_H
//singleton.h
#ifndef _SINGLETON_H
#define _SINGLETON_H

namespace Blaze
{
	template<class T>
	class Singleton
	{
	public:
		static T* instance()
		{
			if (!_instance)
			{
				_instance = new T;
			}
			return _instance;
		}

	protected:
		/// 使用保护构造是为了用户不能在栈上声明一个实例
		Singleton() {}

	private:
		static T* _instance;		/// 实例静态指针
	};

	/// 静态实例指针初始化
	template <class T> T* Singleton<T>::_instance = NULL;

} // namespace

#endif //_SINGLETON_H
  1. //使用的时候只需要单件类继承此模板即可。
  2. classTest:publicSingleton<Test>{...};
//使用的时候只需要单件类继承此模板即可。
class Test : public Singleton<Test >{...}; 

2、dll(so)动态加载

在开发网络游戏的过程中,现在已经不是能够单打独斗的年代了,一款游戏基本上不可能有一个人完成,因此分模块开发成为了必然,各自开发相关的模块,然后组合到一起。dll就是分模块开发的产物之一,它的加载有动态和静态之分,各有优势,但是由于服务器程序是需要运行在多个平台,而他们又各自有各自的加载方法,为了方便使用,因此我们队加载dll进行了封装。

实现使用模板,非常简单,代码如下

  1. //lib_def.h
  2. #ifndef__LIB_DEF_H_
  3. #define__LIB_DEF_H_
  4. namespaceBlaze
  5. {
  6. //DLL对象创建辅助类
  7. template<constTCHAR*&szFileName>
  8. classDllApi
  9. {
  10. public:
  11. staticBOOLLoad()
  12. {
  13. if(!m_h)m_h=::LoadLibrary(szFileName);
  14. returnm_h!=NULL;
  15. }
  16. staticvoidUnload()
  17. {
  18. if(m_h)::FreeLibrary(m_h);
  19. m_h=NULL;
  20. }
  21. protected:
  22. staticHMODULEm_h;
  23. };
  24. template<constTCHAR*&szFileName>HMODULEDllApi<szFileName>::m_h;
  25. //库文件前后缀
  26. #ifdefWIN32
  27. #define__DLL_PREFIX_T("")
  28. #define__DLL_SUFFIX_T(".dll")
  29. #else
  30. #define__DLL_PREFIX_T("lib")
  31. #define__DLL_SUFFIX_T(".so")
  32. #endif
  33. //声明DLL文件名常量
  34. #defineDECLARE_DLL_FILE(module)extern"C"constTCHAR*module;
  35. //定义DLL文件名常量
  36. #if!defined(_LIB)&&!defined(_USE_STATIC_LIB)
  37. #defineDEFINE_DLL_FILE(module)extern"C"constTCHAR*module=_T("./")""__DLL_PREFIX""_T(#module)""__DLL_SUFFIX;
  38. #else
  39. #defineDEFINE_DLL_FILE(module)
  40. #endif
  41. }
  42. #endif

本例中使用了LoadLibrary,是windows的实现方法,在后面平台相关处理中,我会将linux的函数封装一下,和windows同名。此模板使用方法很简单:

  1. #ifdefined(_LIB)||defined(_USE_STATIC_LIB)//静态库版本
  2. #ifndef_LUA_ENGINE_API
  3. #define_LUA_ENGINE_APIIMPORT_API
  4. #pragmacomment(lib,MAKE_LIB_NAME(LuaEngine))
  5. #endif
  6. _LUA_ENGINE_APIILuaEngine*GlobalLuaEngine();
  7. #else
  8. DECLARE_DLL_FILE(LuaEngine);
  9. classGlobalLuaEngine:publicDllApi<LuaEngine>
  10. {
  11. typedefILuaEngine*(*CREATE_PROC)();
  12. ILuaEngine*m_p;
  13. public:
  14. GlobalLuaEngine():m_p(NULL)
  15. {
  16. Load();
  17. staticCREATE_PROCfunc;
  18. if(func==NULL)func=(CREATE_PROC)::GetProcAddress(m_h,"GlobalLuaEngine");
  19. if(func!=NULL)m_p=func();
  20. }
  21. operatorILuaEngine*(){returnm_p;}
  22. ILuaEngine*operator->(){returnm_p;}
  23. };
  24. #endif
#if defined(_LIB) || defined(_USE_STATIC_LIB)	// 静态库版本
	#ifndef _LUA_ENGINE_API
		#define _LUA_ENGINE_API IMPORT_API
		#pragma comment(lib,  MAKE_LIB_NAME(LuaEngine))
	#endif

	_LUA_ENGINE_API ILuaEngine* GlobalLuaEngine();
#else
	DECLARE_DLL_FILE(LuaEngine);
	class GlobalLuaEngine : public DllApi<LuaEngine>
	{
	typedef ILuaEngine* (*CREATE_PROC)();
	ILuaEngine* m_p;
	public:
		GlobalLuaEngine() : m_p(NULL)
		{
			Load();
			static CREATE_PROC func;
			if(func == NULL) func = (CREATE_PROC)::GetProcAddress(m_h, "GlobalLuaEngine");
			if(func != NULL) m_p = func();
		}
		operator ILuaEngine* (){ return m_p; }
		ILuaEngine* operator ->(){ return m_p; }
	};
#endif

如上面代码所示,LuaEngine是一个dll,我们在加载它的时候,使用了一个额外的类,在他的构造函数里面加载了共享库。而且在应用级上也与平台无关。

3、跨平台的若干处理

windows的处理相当简单,只是定义一些简单的宏。

  1. //gwindef.h:windows开发定义文件
  2. #ifndef__G_WIN_DEF_H_
  3. #define__G_WIN_DEF_H_
  4. #include<windows.h>
  5. #include<process.h>
  6. #include<tchar.h>
  7. #include<unknwn.h>
  8. #include<stdio.h>
  9. #include<stdlib.h>
  10. #defineSYS_APIWINAPI
  11. #defineSTD_CALL__stdcall
  12. #if!defined(_LIB)
  13. #defineEXPORT_APIextern"C"_declspec(dllexport)
  14. #else
  15. #defineEXPORT_APIextern"C"
  16. #endif
  17. #if!defined(_LIB)&&!defined(_USE_STATIC_LIB)
  18. #defineIMPORT_APIextern"C"_declspec(dllimport)
  19. #else
  20. #defineIMPORT_APIextern"C"
  21. #endif
  22. #endif//ndef__G_WIN_DEF_H_

而为了开发的时候去除平台无关性,在linux的开发中,我们需要做一些包装,使其在开发过程中和window代码一致。

  1. //glindef.h:linux开发定义文件
  2. #ifndef__G_LIN_DEF_H_
  3. #define__G_LIN_DEF_H_
  4. //
  5. #include<stdlib.h>
  6. #include<stdio.h>
  7. #include<string.h>
  8. #include<wchar.h>
  9. #include<unistd.h>
  10. #include<pthread.h>
  11. #include<semaphore.h>
  12. #include<errno.h>
  13. #include<sys/times.h>
  14. #include<time.h>
  15. #include<dlfcn.h>
  16. #include<sys/types.h>
  17. #include<linux/unistd.h>
  18. inline_syscall0(pid_t,gettid)/*Usingsyscall(2)maybepreferable;seeintro(2)*/
  19. #ifdefUNICODE
  20. #define_T(str)L##str
  21. #else
  22. #define_T(str)str
  23. #endif
  24. #defineTRUE1
  25. #defineFALSE0
  26. #defineMAX_PATH256
  27. #defineSYS_API
  28. #defineSTD_CALL
  29. #defineEXPORT_APIextern"C"
  30. #defineIMPORT_APIextern"C"
  31. ///HRESULT常量定义
  32. typedeflongHRESULT;
  33. enumHResult
  34. {
  35. S_OK=((HRESULT)0x00000000),/**<成功,值为0*/
  36. S_FALSE=((HRESULT)0x00000001),/**<成功,但值为1*/
  37. E_FAIL=_HRESULT_TYPEDEF_(0x80004005),/**<未定义错误*/
  38. E_NOTIMPL=_HRESULT_TYPEDEF_(0x80004001),/**<接口未实现*/
  39. E_OUTOFMEMORY=_HRESULT_TYPEDEF_(0x8007000E),/**<内存不足*/
  40. E_INVALIDARG=_HRESULT_TYPEDEF_(0x80070057),/**<无效参数*/
  41. E_NOINTERFACE=_HRESULT_TYPEDEF_(0x80004002),/**<接口不存在*/
  42. E_POINTER=_HRESULT_TYPEDEF_(0x80004003),/**<无效指针*/
  43. E_HANDLE=_HRESULT_TYPEDEF_(0x80070006),/**<无效句柄*/
  44. E_ABORT=_HRESULT_TYPEDEF_(0x80004004),/**<操作被取消*/
  45. E_ACCESSDENIED=_HRESULT_TYPEDEF_(0x80070005),/**<访问拒绝*/
  46. E_PENDING=_HRESULT_TYPEDEF_(0x8000000A),/**<操作被挂起*/
  47. E_UNEXPECTED=_HRESULT_TYPEDEF_(0x8000FFFF)/**<未预料的错误*/
  48. };
  49. ///判定HRESULT值是否为成功值
  50. #defineSUCCEEDED(Status)((HRESULT)(Status)>=0)
  51. ///判定HRESULT值是否为失败值
  52. #defineFAILED(Status)((HRESULT)(Status)<0)
  53. ///GUID类型定义
  54. /**
  55. 要定义GUID常量请使用GUID专门的生成工具(比如VS携带的guidgen.exe程序)来生成,
  56. 以确保其唯一性。
  57. 接口ID(IID),类ID(CLSID)均为GUID的别名*/
  58. structGUID
  59. {
  60. unsignedlongData1;
  61. unsignedshortData2;
  62. unsignedshortData3;
  63. unsignedcharData4[8];
  64. };
  65. typedefGUIDIID;
  66. typedefGUIDCLSID;
  67. #defineREFGUIDconstGUID&
  68. #defineREFIIDconstIID&
  69. #defineREFCLSIDconstCLSID&
  70. ///判断两个GUID是否相等(内联版)
  71. inlineBOOLInlineIsEqualGUID(REFGUIDrguid1,REFGUIDrguid2)
  72. {
  73. return((long*)&rguid1)[0]==((long*)&rguid2)[0]&&
  74. ((long*)&rguid1)[1]==((long*)&rguid2)[1]&&
  75. ((long*)&rguid1)[2]==((long*)&rguid2)[2]&&
  76. ((long*)&rguid1)[3]==((long*)&rguid2)[3];
  77. }
  78. ///判断两个GUID是否相等
  79. inlineBOOLIsEqualGUID(REFGUIDrguid1,REFGUIDrguid2)
  80. {
  81. return!memcmp(&rguid1,&rguid2,sizeof(GUID));
  82. }
  83. #defineCopyMemory(dest,src,len)memcpy((dest),(src),(len))
  84. #defineZeroMemory(dest,len)memset((dest),0,(len))
  85. #defineFillMemory(dest,len,value)memset((dest),value,(len))
  86. #defineGetCurrentThreadIdgettid
  87. #defineOutputDebugString(str)tprintf(_T("%s"),str)
  88. #defineLoadLibrary(file)dlopen(file,RTLD_NOW)
  89. #defineFreeLibrarydlclose
  90. #defineGetProcAddressdlsym
  91. inlineintGetLastError()
  92. {
  93. returnerrno;
  94. }
  95. inlineDWORDGetTickCount()
  96. {
  97. staticintclkTck=0;
  98. if(clkTck==0)clkTck=1000/::sysconf(_SC_CLK_TCK);
  99. return(DWORD)::times(NULL)*clkTck;//不能溢出
  100. }
  101. inlinevoidSleep(DWORDms)
  102. {
  103. structtimespecreq,rem;
  104. req.tv_sec=ms/1000;req.tv_nsec=(ms%1000)*1000000;
  105. while(::nanosleep(&req,&rem)&&::GetLastError()==EINTR)req=rem;
  106. }
  107. inlinelongInterlockedIncrement(longvolatile*v)
  108. {
  109. longsrc=1;
  110. /*Modern486+processor*/
  111. __asm____volatile__(
  112. "lockxaddl%0,%1;"
  113. :"=r"(src),"=m"(*v)
  114. :"0"(src));
  115. returnsrc+1;
  116. }
  117. inlinelongInterlockedDecrement(longvolatile*v)
  118. {
  119. longsrc=-1;
  120. /*Modern486+processor*/
  121. __asm____volatile__(
  122. "lockxaddl%0,%1;"
  123. :"=r"(src),"=m"(*v)
  124. :"0"(src));
  125. returnsrc-1;
  126. }
  127. #definestricmpstrcasecmp
  128. #include<ctype.h>
  129. inlinevoidstrupr(char*s)
  130. {
  131. while(*s){
  132. *s=toupper((unsignedchar)*s);
  133. s++;
  134. }
  135. }
  136. inlinevoidstrlwr(char*s)
  137. {
  138. while(*s){
  139. *s=tolower((unsignedchar)*s);
  140. s++;
  141. }
  142. }
  143. #endif//ndef__G_LIN_DEF_H_
// glindef.h : linux开发定义文件
#ifndef __G_LIN_DEF_H_
#define __G_LIN_DEF_H_
// 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <sys/times.h>
#include <time.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <linux/unistd.h>

inline _syscall0(pid_t, gettid)        /* Using syscall(2) may be preferable; see intro(2) */

#ifdef UNICODE
#define _T(str)		L##str
#else
#define _T(str)		str
#endif

#define TRUE		1
#define FALSE		0

#define MAX_PATH		256

#define SYS_API
#define STD_CALL
#define EXPORT_API		extern "C"
#define IMPORT_API		extern "C"

/// HRESULT 常量定义
typedef long		HRESULT;
enum HResult
{
	S_OK = ((HRESULT)0x00000000),			/**< 成功,值为0 */
	S_FALSE = ((HRESULT)0x00000001),			/**< 成功,但值为1 */
	E_FAIL = _HRESULT_TYPEDEF_(0x80004005),		/**< 未定义错误 */
	E_NOTIMPL = _HRESULT_TYPEDEF_(0x80004001),		/**< 接口未实现 */
	E_OUTOFMEMORY = _HRESULT_TYPEDEF_(0x8007000E),	/**< 内存不足 */
	E_INVALIDARG = _HRESULT_TYPEDEF_(0x80070057),		/**< 无效参数 */
	E_NOINTERFACE = _HRESULT_TYPEDEF_(0x80004002),	/**< 接口不存在 */
	E_POINTER = _HRESULT_TYPEDEF_(0x80004003),		/**< 无效指针 */
	E_HANDLE = _HRESULT_TYPEDEF_(0x80070006),		/**< 无效句柄 */
	E_ABORT = _HRESULT_TYPEDEF_(0x80004004),		/**< 操作被取消 */
	E_ACCESSDENIED = _HRESULT_TYPEDEF_(0x80070005),	/**< 访问拒绝 */
	E_PENDING = _HRESULT_TYPEDEF_(0x8000000A),		/**< 操作被挂起 */
	E_UNEXPECTED = _HRESULT_TYPEDEF_(0x8000FFFF)		/**< 未预料的错误 */
};

/// 判定 HRESULT 值是否为成功值
#define SUCCEEDED(Status)	((HRESULT)(Status) >= 0)
/// 判定 HRESULT 值是否为失败值
#define FAILED(Status)		((HRESULT)(Status) < 0)

/// GUID 类型定义
/**
要定义 GUID 常量请使用 GUID 专门的生成工具(比如 VS 携带的 guidgen.exe 程序)来生成,
以确保其唯一性。
接口 ID(IID), 类 ID(CLSID)均为 GUID 的别名*/
struct GUID
{
    unsigned long  Data1;
    unsigned short Data2;
    unsigned short Data3;
    unsigned char  Data4[8];
};

typedef GUID IID;
typedef GUID CLSID;
#define REFGUID const GUID&
#define REFIID const IID&
#define REFCLSID const CLSID&

/// 判断两个 GUID 是否相等(内联版)
inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2)
{
	return ((long*)&rguid1)[0] == ((long*)&rguid2)[0] && 
		((long*)&rguid1)[1] == ((long*)&rguid2)[1] &&
		((long*)&rguid1)[2] == ((long*)&rguid2)[2] &&
		((long*)&rguid1)[3] == ((long*)&rguid2)[3];
}

/// 判断两个 GUID 是否相等
inline BOOL IsEqualGUID(REFGUID rguid1, REFGUID rguid2)
{
    return !memcmp(&rguid1, &rguid2, sizeof(GUID));
}

#define CopyMemory(dest, src, len)		memcpy((dest), (src),(len))
#define ZeroMemory(dest, len)		memset((dest), 0, (len))
#define FillMemory(dest, len, value)		memset((dest), value, (len))
#define GetCurrentThreadId			gettid
#define OutputDebugString(str)		tprintf(_T("%s"), str)
#define LoadLibrary(file)			dlopen(file, RTLD_NOW)
#define FreeLibrary			dlclose
#define GetProcAddress			dlsym

inline int GetLastError()
{
	return errno;
}

inline DWORD GetTickCount()
{
	static int clkTck = 0;
	if(clkTck == 0) clkTck = 1000 / ::sysconf(_SC_CLK_TCK);
	return (DWORD)::times(NULL) * clkTck;	// 不能溢出
}

inline void Sleep(DWORD ms)
{
	struct timespec req, rem;
	req.tv_sec = ms / 1000; req.tv_nsec = (ms % 1000) * 1000000;
	while(::nanosleep(&req, &rem) && ::GetLastError() == EINTR) req = rem;
}

inline long InterlockedIncrement(long volatile* v)
{
	long src = 1;
	/* Modern 486+ processor */
	__asm__ __volatile__(
		"lock xaddl %0, %1;"
		:"=r"(src), "=m"(*v)
		:"0"(src));
	return src + 1;
}

inline long InterlockedDecrement(long volatile* v)
{
	long src = -1;
	/* Modern 486+ processor */
	__asm__ __volatile__(
		"lock xaddl %0, %1;"
		:"=r"(src), "=m"(*v)
		:"0"(src));
	return src - 1;
}

#define stricmp						strcasecmp

#include <ctype.h>
inline void strupr(char *s)
{
	while (*s) {
		*s = toupper((unsigned char) *s);
		s++;
	}
}

inline void strlwr(char *s)
{
	while (*s) {
		*s = tolower((unsigned char) *s);
		s++;
	}
}

#endif // ndef __G_LIN_DEF_H_

代码都比较简单,我也不对这些做详细的解析,功能就是对一些常用函数改装成windows相关函数的名字。如果在开发中遇到了其他的情况,也可以加到此文件中,以方便应用开发。

大家可能会觉得在这里看代码比较别扭,我把代码上传到了空间,大家可以去下载。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值