《当老温遭遇C#》之网络棋牌游戏系统架构篇【附演示+代码+全套图】~~极力推荐C#程序员阅读

老温评语:在我写这篇文章之前,我的心情比较沉重,不知道会不会影响我现在的写作情绪,之前由于我发表了几篇文章而大受欢迎同时也有网友对我进行炮轰,认为我是在炒作.我真的感到非常悲哀,我真的是撞猪上了;而且给Brain看到了顺便笑了我一把!苍天啊,上帝啊,佛祖啊.但话又说回来没有风雨怎么能见彩虹,一定要保持乐观精神,引用伟大领袖毛主席的话:一切反动派都是纸老虎!我相信只要用事实说话,那么是金子就一定会发光,黑暗是掩盖不了它的.也有些“高手”指明我的文章没有技术含量,的确我自认为相比其他而言是没有一点技术含量,难道我有在强奸ing你的眼睛吗,你为什么看在眼里都不舒服?是什么驱使你一颗丑陋的心在吞吐恶臭的气体而影响它人的健康呼吸与成长,您犯不着和我这无名小卒在呕气呀,根本不值得!每个人都有属于自己的一片天空,人不犯我我不犯人,人诺犯我,我必宰了你(男人要对自己狠点)

上次的文章<地图编辑器>篇受到了如此众多朋友的青睐,我在这儿有必要再作一次说明:该地图编辑器的确像某些人所说的没有任何技术含量,无非就是用GDI+结合一些矩阵知识及数学公式等进行地图几个逻辑坐标世界坐标和物理坐标的转换及图形计算罢了,我所做的就是把C++下面的工程转为C#工程,让其评测C#的力量,至于不能开源的原因也就在此,博客园的任何一个程序员都可以实现它,那篇我只是展示并不是一篇技术文献,有机会我将会重新组织一篇讲述如何实现它,以及其中的算法!我还是那句话:我没有在强奸ing你的眼睛和眼镜,你任为不适合你,完全可以不理会我,但是这并不代表其它人的想法呀~OK,止住~~多说有害,整就一个娘们!而现在面对博客园这样的巨大压力之下,关系到我在博客园的发展,我最后决定我还是有必要把我之前一直想要发表的文章继续发布下去,不能因为某些个人行为而阻碍了我们千千万万的求知者.最重要的是我们一起交流学习!如果您认为我是在作秀,或者作为我是在炒作那么我想我的全身每个器官都不欢迎您,博客园所有求知者也不欢迎您,就此打住,好吗?算我老温恳请您了!!.好了我尽在讲废话,走自己的路写自己的代码和大家一块学习.切入正点:这篇文章主要是和大家一起来谈谈如何用C#架构网络棋牌游戏系统,包括客户端、服务端、游戏逻辑判断、图形处理、棋牌游戏规则、传输安全等等问题,其中我还会和大家一起展示我的一个DEMO,老温我大约足足花了数十个通宵才搞定了该项目,使工程变牵成C#平台,再次体验我甜心(C#)的锐利.希望您阅读本文后可以进一步提高对C#的热情,用它来实现自己的梦想.知识是通用的,不管用什么语言最重要的是实现它的方法与手段!

撰写本文的目的:希望有更多的爱好者加入C#开发行列,更重要的是让已经把C#作为开发工具的人对C#进一步提升FANS热情包括我自己,不要质疑它的威力,同时也给博客园注入一股新的C#异形生命力!让我们一起扣开棋牌游戏之门,我在此顺便再次感谢dudu和博客园的朋友们能够支持我的文章,其实是大家共同学习罢了,忠心的祝福你们,天下最可敬的就是求知者和你们!.

好了,各位朋友坐好了,您到火星的旅程即将出发,请绑好安全带!!!!(~~~~星际之旅?老温到底要干啥……..

我为什么要撰写这篇文章:

我相信不仅仅是我,你也和我一样拥有同一个梦想,一个非常想实现它而又胆怯的想法,那就是想写一套网络棋牌游戏系统,对,就是它,用C#来实现!现在网络上多火爆呀而且对于这类项目一直是DELPHIC++的专属领域。因此你的人脑(不叫大脑,那是对动物的叫法)会有所顾忌,那么我在这儿可以很明确的告诉你我们用C#完全可以实现,要是实现不了你把我老温拉出去凌迟处死!!不信你往下看,一定会读到你在博客园中很难获取的知识,保证让你大开眼界,接下来我们会一起交流如何用C#处理这类系统,让你更加得到老板的赏识,更重要的是你已经学会如何开发这类项目了。在我写这篇文章之前我搜索了一些资料,关于这类系统的开发文章几乎少的令人抽涕,更不用说用C#来实现了,那么我写这篇文章将更加具有实际意义!希望也给刚刚入行的朋友作为一个参考对象,少走弯路; 下面我就抖胆献丑了,不管你干不干还是有没有兴趣你已经起程了~~~往火星飞啦!

什么是游戏?

其实,游戏这个词的历史远远比电子游戏程序要早得多,早在宋·《教战守》一文中,苏轼就有“游戏酒食”一词,本意为“嬉戏”的意思。而在电子游戏程序,没有出现时,游戏一词已经被人们广为使用了,象是“幼儿园老师:‘小朋友,今天我们来做个游戏,好不好?’”或是“我们来做个小小的智力游戏?”之类的。说白了就是玩,游戏就是玩,玩了玩了就上瘾了;我很不情愿的叫它Game

什么是网络棋牌游戏?

首先我们来谈谈传统的棋牌室,什么是棋牌室?我想大家都可以在自己所在的城市中看到某某棋牌室,从字面上就可以看出它是供人打牌、下棋娱乐的地方,那么它的结构是怎么样的呢?首先你要到一家棋牌室,然后它里面有好几个房间,有一楼前屋,后屋,二楼前屋后屋,你首先就是选择一个屋,因为不同的屋会玩不同的牌,比如一个屋专门用来打麻将的,一个屋专门用来下棋的,所以你要选择一个屋依自己今天想玩什么而决定。然后你进入这个屋后里面可能有好几桌,那么你就会选择一桌,或者在旁边观看! 这就是传统的棋牌室结构。OK,做稳了,我马上就要启动亚光速引擎了,飞入时空隧道…….网络中的棋牌系统与传统的棋牌室是很相似的,首先我们要有大厅就是一个总的棋牌室,然后大厅里有很多个房间,每个房间都是专供某种娱乐项目的,然后这个房间里会有很多桌子。了解了这种结构之后那么我们对这种系统体系就有了初步的认识。

接下来我们就一起层层划分网络棋牌游戏系统模块,逐一击迫,胜利就在眼前!

 

我们先从实质性的层次划分它,首先要有玩家,那么我们就设计玩家的一个结构,先打住~~STOP,亚光速引擎先停止!!! 其实我们所做的工作就是把传统的对象实体设计成计算机面向对象的实体,然后给这些实体一些操作方法,就这么简单,不要被那些软文给讲蒙了,就是这么一回事!啥单件模式,三层模式,观察者模式最终都离不开这个出发点,OK,继续启动~~ 首先我们肯定要有玩家对吧,没有玩家咱玩呀~那么我们就先设计一个玩家类,如下:

Class Struct 都可以,如果是Class最好加上抽象(abstract)声明至于为什么要加你问别人吧,我在使用C++的时候都被C#的完全面向对象所吸引,真是我的甜心! 然后我们给这个类赋一些属性,天呐,C#下面实现这些属性太棒了get set 以及那么成熟高级的特性(什么你不会设计这样一个类,那么就此打住先收藏本文学习这部分知识之后再来阅读)不像C++下面的那样古老级的做法,不过你可不要忘记哦一切源自C++的设计灵感才会有现在的C#~. 我称C++可是杀手锏~~~~


 

游戏玩家类

{

属性:

   玩家在系统的唯一标识

   玩家的帐户名

   玩家的昵称

   玩家的积分

   玩家的注册时间

   玩家的当前登录时间

   玩家的当前登录IP

   玩家的等级

   玩家的信誉度

   是男是女还是人妖

   是不是喜欢老温

   ……..

}

 这儿讲句题外话,你在设计属性时,对Set操作时一定要进行判断规则,这样才能从基层保证了数据是OK的,合法的,比如玩家的等级不可能小于0标识,最好做个判断,玩家的金币不可能超出多少钱做个判断,如果不符合就抛出异常,我好像从哪本EFFCT C++啥中看到的,英文单词肯定打错啦,管它个屁,学到的知识就是我的!

具体有哪些属性要看你自己的发挥了,要知道这一切都是由你来决定,没有人能够阻止你!切记,自由很重要,人类为了追求自由流了多少鲜血呀!我怎么竟讲蠢话。上面的这个类就是一个纯的玩家实体类,没有啥功能,就是一个概念级的东西描述符罢了。也就是一个玩家就有这些特性,够简单吧!好了,这是一个玩家,那么我要是有上百个玩家进行管理呢,怎么组织它们,瞧,数据结构发挥了它魅力的部分了,太帅了。 你可以自己写一个线性链表进行组织它们,一个类就代表一个玩家,一个个玩家链在一起就方便我们程序员管理它们了,再次感谢微软,感谢老盖,C#下面实现了这些数据结构类,什么你不知道,LIST类总知道吧,连排序方法都帮你搞定了。打个比方,我们要把用户一个个组织串起来,然后就依次对它取一个出来操作或遍历它或删除它等。也就是玩家退出大厅和登录大厅这样的情况下的操作罢了。

我们有了一个玩家类还不够,我们得给它生命,让他学会叫,学会叛逆才行。那么这一切工作就需要我们编写一个操作它的类,我就叫它逻辑类(生命类)吧,我又要告诉您,不要在乎别人怎么讲,游戏设计是很自由的,这个世界属于你自己的! 由这个逻辑类负责给这个玩家实体类产生生命的源泉! 嘿它是单性繁殖的。

在这儿我有必要提出另一种设计方法:我一开始还是比较倾向把这些实现逻辑部分的代码放进这个实体类本身,因为按照面向对象的原则性是这样做的,一只猫,一只狗都有自己的属性而且都有自己的方法,所以它是一体的!但是从另一层次讲可以把这些叫方法独立开来,让一只猫就是一只猫,需要叫,需要吃都由另外负责类处理!这一点我在WEB应用程序中一直这么做! 其实你在明白这些道理之后你就会对啥工厂模式啦,啥三层模式啦都明白啦,就是那个味,只是给它套上了一个专业术语,有些还是啥英文的,奶奶的咱们中国民族的文化可比你们强多了,啥哈日族哈美族哈韩族统统见鬼去吧~瞧你那孙样留着金毛~ 可惜我们现在所学的知识仍然完全是由老外控制的,哎~~讲这话我腰酸呀!

不好意思,我忘了启动星际之亚光速引擎了,HOHOHOHO………….狂飞向火星!

那么这个负责处理玩家的类需要哪些方法呢?

玩家需要登录就有一个 登录方法();玩家需要退出游戏 就需要一个 退出方法();哦我的天呐,有一次我在我的项目中偿试我的方法都用汉字来定义差点被团友给扔出去(你不会不知道C#是可以用汉字来命名的吧)。如果想要得到一位玩家资料就有一个获取方法();删除玩家方法();给玩家发送通知方法();让玩家强制下线方法();玩家注册方法() 等等;发挥你的想象力,但是一定要拥有一个原则,它是围绕玩家类来实现的。这一切都会在你设计你的项目时就已经很清晰的画出来了,要不然你就哭吧,痛快的哭吧!

好的,亲爱的旅客我们已经离开了地球,坐好了!我会开启双核亚光速引擎,速度提高双倍!尽可能快的到达火星; 听人家说美国的的火星探车程序用JAVA编写用来通讯的!

经过以上部分的阅读,我们已经初步有了一个概念,那么接下来就是很重要的部分,我们来分析一下这种游戏的总体架构模型,我认为很重要,不知道你认为重要不重要,重不重要得看了才知道哟!Let’s GO

我将这个系统划分为这几个重要组成部分:

1) 客户端大厅程序

2) 游戏服务器(集群)

3) WEB游戏站点

4) 辅助GM工具

(本来我得画一幅图的,可惜PS不好,又没有漂亮的PS MM帮我!只能用文字组织,累就累点呗~)

我们逐一讲解,先看最简单的部分就是WEB游戏站点,既然我们选择了C#还干嘛要用PHPJSP呢,可惜人家PHPFREE的呀,羡慕ing,不过我用盗版,嘿嘿!等我做大了还怕出不起这几个版权费用嘛,最贵的可是SQL系统哦,好几万啊,所有的预算加起来要十几万啊才一个解决方案! WEB游戏站点我用ASP.NET实现,我们新建一个解决方案,再建立一个网站项目,我想做网站任何人都比我来的强,这个网站就是给用户查询积分,在线交流,新闻发布,用户注册,修改密码,互动娱乐等等作为一个推广的平台!这中间没啥好讲的,唯一能够打交道的就是数据库,因为你和游戏系统 交互纽带就是通过数据库。既然讲到数据库,那么我们就顺便讲一下如何设计数据表结构吧,这中间我不想用更多的废话讲,很简单,把你的策划与需求分析好,转成数据表,规划好你的数据完整约束,建立好你的索引,命名好你的规范,合理设计好你的用户表等等;其实前面所讲到的玩家类就是一个数据表的体现。博客园的人谁不会呀!

OK,我们在双核亚光速的旅途中已经建立好了游戏站点,玩家可以申请帐号,登录管理中心等等操作!让地球的玩家通过WAP连接进来吧,什么这种无线技术延时……….你现在很振奋吧,我们的棋牌游戏站点架设完毕了!一切看上去都是那么的真实美妙,只有自己知道我们还没有做出棋牌游戏来呢?,不急,往下看~~~~ 飞往火星的风景真美,一片漆黑!哇还有月兔呢?(有时候我感觉自己挺傻的)

伟大的C#,我们在做游戏站点的时候已经拥有了sqlhelper类,通常为了能够给游戏中使用将它单独划入一个公用类库中,我想大家也是这么做的。 一家的东西就是好,通用性强呀!

这儿我将这个数据帮助类已经写好了,下载即可,只有几个方法很简单也够全面,几乎我可以用这几个方法操作所有的需求,学习过那个叫什么Pet什么的工程就清楚了。什么你看不懂这段话吗? SqlHelper类你不知道吗,网上一大把呀,你做Asp.net不用它吗? 这位旅客请下船,什么下船还要拿走SqlHelper类? 那就看完这篇文章再下吧,下载地址在下面呢?

接着我们继续来观看辅助GM工具的设计,其实这类工具未必要做成WINFORM形式的,我们完全有必要集成在游戏站点的后台中,依你的情况与心情决定吧!它主要负责的就是给一些用户增加积分,删除一些用户,注册一些用户,禁用一些用户,配置一些游戏参数,积分双倍,活动定义,游戏公告,游戏通知,广告通知等等操作!这其中涉及到的一些操作你可能不是十分完全明白,不知道怎么和游戏结合,OK,你只要把你的操作保存到DB中即可,我不是说过了吗DB就是纽带,如果你是单独用WINFORM操作也可以把它保存到INI文件或XML文件中,注意这可是配置服务参数,不要搞进客户端哦! 

对以上的言论我想我有必要和你做一个了段,该交钱的就得交钱(呵呵):我们已经完成了数据表的设计,游戏站点的设计!辅助工具的编写,接下来就是我们的最后冲刺部分,启动四核亚光速引擎全面提速~~~~~~~~~~~~~~CN国际空间站看不到了~~~远离了我的地球.

 

我们现在开始全面讲解游戏服务器方面的知识

哥们要是不耽误您抽烟的时间请先麻烦看一下我之前发表的那篇《当C#遭遇老温》之网络通讯编程篇 地址为:http://www.cnblogs.com/wenweifeng/archive/2008/07/03/1234783.html

该篇有很多网友提出了正确的方法,在此感谢!请你吃饭好吗~~

游戏服务器是非常重要的组成部分,游戏的性能以及安全和稳定全靠它来支撑,所以设计一个合理的游戏服务器是非常重要的~这是一个很严肃的问题,因为太阳风暴就要来临了,搞不好咱们全玩了,回不了地球了~~很严肃,注意看!

在这类设计中,通常会使用C/S的应用程序模型。客户端不会处理逻辑判断,仅仅是一个收发器,如果你把重要的逻辑判断放在客户端处理你就完了,那还有什么信任可言呀,客户随时可以搞个外挂模拟提交~~隔位刺杀,极速外挂!!全乱套了。客户端负责与玩家的交互,接收玩家的操作事件,然后发送给服务端,服务端负责处理判断,然后再发给客户端,客户端收到服务器的消息后再转成UI展现出来,就是这么一个流程,如果想要让通信安全把这些数据包经过一层加密或数字签名~其实所有的游戏都是这么做的,客户端就是存储着些声音图形等界面性的东西。

好了,我花了数十个通宵才完成的一个例子,现在先展示它,一层层剖析,可能我这不是最好的方案,但是它代表了我一家的风范,你有权拒绝也有权接受,老温我是讲人权的呀~~

看下图:


 

由一个DEMO解决方案组成,下面有ChessChessServerCommonDataServerGameClientHallServerLoginServerOnlyServer八大部分组成。其中Chess是一个棋类游戏的实现,也就是房间,ChessServer是负责这个棋类游戏的服务端逻辑判断部分,Common是存放公存类库的,DataServer是专门用来处理数据库层的服务,GameClient是游戏的客户端是一个Windows应用程序,HallServer是一个负责处理大厅事务请求的服务器服务,LoginServer是负责处理用户登录的服务器服务,OnlyServer是仅给服务器应用程序提供的类库,为了安全起见不能将它与Common弄在一起,要不然发布的时候客户端中也会有这类服务逻辑处理代码,那样就很危险了!

好了介绍完了这个工程下面我就开始讲解这些服务器之间是如何协同工作的,以及为什么要采用这种设计模式?

之所以采用这种设计模式是有很肯定的原因的,废话,要不然这么做干啥!首先做为客户端它不可能知道有这么多服务器的存在,也就是客户端它只要知道我登录到哪里,也就是用户要知道我要登录到哪个服务器呀,用户需要选择一个最近的服务器,看下图:




   

我做的这个客户端是一个典型的休闲棋牌游戏客户端,其中分二层第一层就是浮在上面的登录层,必须要进行登录后才可以进入第二层;其中登录层是可以托动的,实现了不规则窗体透明效果,这些在C# Windows开发下还是比较方便实现的!如下图:

我做的这个客户端是一个典型的休闲棋牌游戏客户端,其中分二层第一层就是浮在上面的登录层,必须要进行登录后才可以进入第二层;其中登录层是可以托动的,实现了不规则窗体透明效果,这些在C# Windows开发下还是比较方便实现的!如下图:


 

大家看到首先会让用户选择服务器,不同的服务器根据用户距离而决定,其次就是这中间还会加上一层智能判断,来协调哪个登录服务器是爆满的哪个登录服务器是空闲的,这个方法利用计数器就可以实现,其中大家可以看到这些登录按钮都是双个图片进行变色的鼠标移上去就会发生变换。上面灰色部分是准备嵌套一个公告网页的,这样就可以动态告知用户系统举行了哪些活动等;还可以收入广告费用~~其它系统都不是这样设计的吗





HO 我的天呐我输入的帐号非法,呵呵。其实这个弹出框就是一个窗体,我封装了一个MessageBox窗体类,专门用它来处理与用话对话返回确定,取消还有其它种类的返回值!
再来看一张我点击了关闭后弹出的对话框:


 

OK,到目前为止我们已经知道了用户第一步将要做的是什么,要么去注册,要么登录,你不登录后面的大厅就是无法使用的!能与用户交互的就是一个登录服务器,登录服务器判断用户的帐号和密码是否正确,正确将返回正确的数据通知客户端程序验证成功,及其它协调的服务器,在在线列表中保存住该玩家的状态! 然后下一步工作登录服务器就完成了它的使命了,断开与客户的线程链接交由HallServer服务器处理,然后由HallServer与客户端进行一次链接,HallServer并返回所有的游戏数据列表给客户端,并且我这儿设计了30分钟更新一次玩家的数据列表,因为要反应出哪些房间是多少人,是否爆满等情况! 注意:这儿客户与LoginServer是进行短链接的线程交互所以LoginServer可以很轻松的支持更多的玩家通信,还有一点我需要指明,在线程操作中线程的创建与切换是很耗资源的。那么玩家要是断线了或者不小心死机了怎么判断它呢? 其实这很好办,我这儿在客户端实现了一个定时器,它起到了“心跳包”的作用,定时向HallServer发送心跳数据,说我在线,我在线。该服务器收到后,嗯,这丫的确在线,如果在一二分钟内没有收到,该丫已经死了~~哈哈,这儿的时间完全由你自己来掌握,一切要把握好!关系到性能哦~

OK,上面我已经比较清晰的描述了客户端在登录之前和登录时的那一个过程服务器是如何处理的,那么假如我这儿假设用户已经通过验证,就是输入了正确的帐号和密码会发生什么情况? 首先HallServer将最新的房间列表发送给客户端,客户端此时已经能够操作大厅了,并且与HallServer保持着密切的交互关系,什么房间列表你不知道如何用数据结构描述它,天呐,就一个树形结构呗,对!就是树形结构,不过要你自己组织好哦,C# windows默认的树形可能不够适应你哦。得自己专门花点心思处理一下!也是很Easy的事情啦。关于对客户端的界面处理最好还是请一个美工朋友帮你做,我花了不少心思在这上面,不过美工朋友们可能不知道,你要切成不同等块的才行,因为程序中要考虑不同浏览器的分辨率,我现在用的是19寸分辨自然很大,所以要考虑百分比的概念,就像做网页中的居中显示还是按百分比来显示,不过这儿要难的多了!!

讲了上面这么多你已经知道客户端是怎么第一时间与开放的服务器通信的,但是你还不知道服务器之间是如何通信的,也就是对用户而言看不到的那层服务器集群是如何通信的下面我就来继续解说!

看下图,首先需要启动的第一个服务器是数据管理服务器





 

看界面就已经很明白它要完成哪些功能了,下面我再看一下代码:

        private TcpListener myListener;

        private IPAddress localAddress;

        private Service service;

        private UserOperat pubuserop = new UserOperat(System.Configuration.ConfigurationManager.AppSettings["Sqlconnstring"].ToString());//单独线程用于处理用户在线;

        private System.Collections.Generic.List<User> userList = new List<User>();

以上代码就是一些声前其中我们把各个服务器也视为User,之所以这儿用的是单线程是有原因的,因为总共服务器才几个,我这个数据管理器只为这几个服务器服务的,没必要用多线程,至于那几个服务器放在哪里我不管,管它放宇宙的哪个角落,只要能访问的到我就可以了。

我们继续看初始化部分的代码:

Code

 

这中间不过就是做了一些界面初始的显示和取一些IP等数据罢了,service = new Service(listBox1);你可能感到奇怪这个是干什么的,这个类就是负责专门用来把处理的结果显示到界面上的,其中用到了委托,因为C#中线程是不允许直接访问控件的!

我们再来看看其它代码,好长呀。我这儿就再贴出一个核心的部分:

Code