从零开始编写网络游戏--基础篇(1)

从零开始编写网络游戏--基础篇(1)

最近2周比较忙,没有抽出时间来写Blog,不过在这段时间里面把整个思路理了一遍,梳理了一下大纲,以后会多抽时间来写Blog。

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

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

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

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

//singleton.h

#ifndef _SINGLETON_H

#define _SINGLETON_H

namespace Blaze

{

template

class Singleton

{

public:

static T* instance()

{

if (!_instance)

{

_instance = new T;

}

return _instance;

}

protected:

/// 使用保护构造是为了用户不能在栈上声明一个实例

Singleton() {}

private:

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

};

/// 静态实例指针初始化

template T* Singleton::_instance = NULL;

} // namespace

#endif //_SINGLETON_H

//使用的时候只需要单件类继承此模板即可。

class Test : public Singleton{...};

2、dll(so)动态加载

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

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

// lib_def.h

#ifndef __LIB_DEF_H_

#define __LIB_DEF_H_

namespace Blaze

{

// DLL对象创建辅助类

template

class DllApi

{

public:

static BOOL Load()

{

if(!m_h) m_h = ::LoadLibrary(szFileName);

return m_h != NULL;

}

static void Unload()

{

if(m_h) ::FreeLibrary(m_h);

m_h = NULL;

}

protected:

static HMODULE m_h;

};

template HMODULE DllApi::m_h;

//库文件前后缀

#ifdef WIN32

#define __DLL_PREFIX _T("")

#define __DLL_SUFFIX _T(".dll")

#else

#define __DLL_PREFIX _T("lib")

#define __DLL_SUFFIX _T(".so")

#endif

// 声明DLL文件名常量

#define DECLARE_DLL_FILE(module) extern "C" const TCHAR* module;

// 定义DLL文件名常量

#if !defined(_LIB) && !defined(_USE_STATIC_LIB)

#define DEFINE_DLL_FILE(module) extern "C" const TCHAR* module = _T("./")""__DLL_PREFIX""_T(#module)""__DLL_SUFFIX;

#else

#define DEFINE_DLL_FILE(module)

#endif

}

#endif

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

#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

{

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的处理相当简单,只是定义一些简单的宏。

// gwindef.h : windows开发定义文件

#ifndef __G_WIN_DEF_H_

#define __G_WIN_DEF_H_

#include

#include

#include

#include

#include

#include

#define SYS_API WINAPI

#define STD_CALL __stdcall

#if !defined(_LIB)

#define EXPORT_API extern "C" _declspec(dllexport)

#else

#define EXPORT_API extern "C"

#endif

#if !defined(_LIB) && !defined(_USE_STATIC_LIB)

#define IMPORT_API extern "C" _declspec(dllimport)

#else

#define IMPORT_API extern "C"

#endif

#endif // ndef __G_WIN_DEF_H_

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

// glindef.h : linux开发定义文件

#ifndef __G_LIN_DEF_H_

#define __G_LIN_DEF_H_

//

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

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

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相关函数的名字。如果在开发中遇到了其他的情况,也可以加到此文件中,以方便应用开发。

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

从零开始编写网络游戏--基础篇 源码

相关推荐

家乐福和沃尔玛哪个好一些 沃尔玛和家乐福哪个便宜
beat365体育官网平台

家乐福和沃尔玛哪个好一些 沃尔玛和家乐福哪个便宜

📅 08-03 👁️ 2478
农民影视app安卓版v20236.4最新版
365bet手机网址是多少

农民影视app安卓版v20236.4最新版

📅 07-18 👁️ 2010
闯关东是哪年拍摄的
365bet手机网址是多少

闯关东是哪年拍摄的

📅 06-28 👁️ 2183
《祭妹文》的原文打印版、对照翻译袁枚
365bet足球现金网

《祭妹文》的原文打印版、对照翻译袁枚

📅 07-29 👁️ 5365