总结我所了解的网络游戏知识5

quanwuling 2008-05-14 03:59:41
4。网络游戏的一大乐趣是可以积累。所谓积累,最终反映在程序上就是一个可以长久保留的数据,存在 文件中或者数据库中。为此,数据的存储也是网络游戏一个很重要的部分。 国内最早的网络游戏《万王之王》,其核心来自于以前的文字MUD,由MUDOS和脚本组成。其玩家数据的存储是用文件形式的,并以首字母为索引分目录。 现今的网络游戏基本上都使用了数据库。因为数据库管理大量规则数据的优势明显,结构化查询语言(SQL)提供丰富的查询功能,完善的数据库系统产品提供简便而完备的维护功能。如果直接使用文件,这些功能都要自己实现,有些类似于重复发明轮子。 《快乐西游》的数据库我选择的是MySQL,主要是基于几点:免费、效率高、功能简单够用。严格来讲MySQL不能算是数据库,因为其没有提供事务功 能,只能算是一个提供SQL的文件系统。(新版本的MySQL好像已经实现了事务功能)功能简单可能也正是它高效率的原因。因为它提供了我所需要的所有功能,够用就好。 在大学毕业之前曾帮一家公司做过MIS(管理信息系统),用过Oraclehe和MS SQL Server。也正是那个时候学到的SQL,学到的数据库的一些基本知识。当时已经有很好的图形工具来建立数据库和表结构,十分方便直观,但是指导我的一个长者坚持让我用脚本来完成这些工作。我很不解,放着这么好的可视化工具不用,非要自己去抠一些规则细节来写脚本是何苦?得到的回答是:你以后就明白其中的好处了。是的,很快我就明白了数据库脚本的好处。 移植、迁移、重建时脚本是如此的容易,而图形化的工具则要重复劳动并且无法确保自己不遗漏东西。当然,现在的图形化工具都提供导出脚本的功能了,但是我依然改不了自己写脚本的习惯。 旧事不提了,言归正传。网络游戏的数据相对于大型企业的数据来讲简直就是小儿科,表结构简单,需要的查询功能也少,但是有一个重要的要求就是查询频度高并且要求查询效率高,这些特点也是我选择MySQL的重要原因。 《快乐西游》的数据库接口可以分成两个部分,一个是数据库访问接口,一个是程序的查询管理。前者我采用的MySQL的C语言API实现,封装为一个类, 其核心就是一个ExecuteSQL函数,其实也是对MySQL提供的C语言接口的一些简单的封装,没有太多需要细说的地方。查询管理我也封装了一个名为QueryMgr的类。 因为查询数据的函数调用不可能即刻返回,主线程又不可能在这里等着(否则什么事都不要做了:),所以需要有个查询管理器来管理这些查询及其返回的结果。 查询管理器有个有个后台线程来等待查询的结果,并将结果还给发起查询请求的主线程,示例代码如下: class Query //查询管理器与主线程交换信息的数据结构或者说通信协议 { public: int nQueryID;//查询ID,用来标志不同的查询 void *pData;//查询附带的数据 void *pTage;//查询附加标志,查询发起者在查询返回时需要的一些发起时的数据区分。 int nResult;//查询结果 } class QueryMgr { public: int Initialize();//初始化一些东西,并启动后台线程,等待在查询队列上 void AddQuery(Query* pQuery);//向查询队列中加入一个查询 Query* GetQuery();//从查询队列中取出一个等待完成的查询 Query* GetQueryFinished();//从已经完成的查询队列中取出查询,供主线程调用 void FinishQuery(Query* pQuery);//查询完成,加入到完成队列 void ThreadProc()//线程函数 { while ( bWorking ) { Query* pQuery = GetQuery(); if( pQuery != NULL) { pQuery->nResult = ProcessQuery(pQuery); FinishQuery(pQuery); } } } int ProcessQuery(Query* pQuery)//处理查询,也就是执行相关的查询语句并得到结果 { switch(pQuery->nQueryID) { case .... //执行查询并记录结果 break; } } protected: ThreadSafeQueue queue_; ThreadSafeQueue queueFinished_; } 主线程的OnUpdate()中加入如下一段代码 Query* pQueryFinished(NULL); while( pQueryFinished= QueryMgr->GetQueryFinished() ) != NULL ) { switch ( pQueryFinished->nQueryID) { ... } } 大致情况如上。这样做的好处是简单明了,想做什么查询只需要调用QueryMgr->AddQuery()即可,然后定时去调用QueryMgr->GetQueryFinished()得到结果处理之。如果需要,可以把用法改的更漂亮一点,把这些switch-case都用回调函数的方式替代。 为Query类添加一个Exec(CallbackFunction func)函数,一个SetQueryMgr(QueryMgr*)函数(如果只 使用一个QueryMgr则可以把QueryMgr做成单体,这样SetQueryMgr()函数都不需要了)。Exec()函数调用QueryMgr的AddQuery()把自己和自己的回调函数加入到对列中(QueryMgr也需要做相应的修改支持回调函数功能),当完成查询之后调用该回调函数执行后续的功能。 《快乐西游》中的GameServer直接使用我上面提供的接口访问数据库,因为有多个GameServer可能同时访问同一个数据库,这中间就需要有 个同步问题:即数据更新后可能在没有被写入数据库之前,数据库又被读取了。同步的问题在测试之初并没有解决,所以当时曾出现过因此引起物品复制问题(同 步实现之后就不再有此引起的复制问题,更多的物品复制问题源自于程序逻辑漏洞而非数据库接口问题)。 为了解决数据库同步问题,曾想到过一个方案:在GameServer和Database之间添加一个中间层。所有的数据访问由这个中间层来执行,GameServer只和这个中间层来交换数据,就算数据的修改没有及时提交给数据库,但是中间层内的数据已经被更新。其实这种方式也是有同步问题的,只是不容易显现而已。其实此方案最大的好处是能统一组织数据,提供高效率的数据访问,减少数据库读写次数。

...全文
518 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
wildtech 2008-06-02
  • 打赏
  • 举报
回复
看不懂!
Learn-anything 2008-06-02
  • 打赏
  • 举报
回复
nybeyond 2008-06-01
  • 打赏
  • 举报
回复
MARK
wosisunli 2008-05-29
  • 打赏
  • 举报
回复
dd
wosilaoda 2008-05-29
  • 打赏
  • 举报
回复
ding
uaix2008 2008-05-29
  • 打赏
  • 举报
回复
go go go 啊累啊累啊累
super163 2008-05-29
  • 打赏
  • 举报
回复
no problem come here!!!
quanwuling 2008-05-29
  • 打赏
  • 举报
回复
if you give me enough money
super163 2008-05-29
  • 打赏
  • 举报
回复
来微软吧小伙子。
Spritemylike 2008-05-29
  • 打赏
  • 举报
回复
ding
phpkill 2008-05-29
  • 打赏
  • 举报
回复
jf
miniskirt 2008-05-29
  • 打赏
  • 举报
回复
jf
ilikeCocaCola 2008-05-29
  • 打赏
  • 举报
回复
jf
givemewater 2008-05-29
  • 打赏
  • 举报
回复
fengsengt 2008-05-29
  • 打赏
  • 举报
回复
我用的腿把这篇帖子踢上去。
bbsinvincibility 2008-05-29
  • 打赏
  • 举报
回复
牛人的帖,不顶不行啊。
back80 2008-05-29
  • 打赏
  • 举报
回复
不错不错,楼主发了很多篇关于这个的,我都看了,很受启发。
chuchuzinnia 2008-05-29
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 yatobiaf 的回复:]
排个版吧,mark
[/Quote]
quanwuling 2008-05-19
  • 打赏
  • 举报
回复
不好意思,没怎么整理,好像这里面没有编辑功能。下次吧。。。。
yatobiaf 2008-05-19
  • 打赏
  • 举报
回复
排个版吧,mark
加载更多回复(2)

8,304

社区成员

发帖
与我相关
我的任务
社区描述
游戏开发相关内容讨论专区
社区管理员
  • 游戏开发
  • 呆呆敲代码的小Y
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧