Silverlight MMORPG WebGame游戏设计(六)-----Server和Client的婚后协议[附上完整15M游戏DEMO]

        上回说到Server少爷和Client小姐好不容易踏入婚姻的殿堂,洞房花烛之夜,Client小姐却要Server少爷签下婚后协议。Server一脸不快:“都一家人还签什么协议啊?”Client道:“你们男人啊,就是花心,不看紧点,不知道跑那野去了。为了以后我们能琴瑟相合,还是签了协议的好。”Server呵呵一笑:“好老婆,那就签吧,你开心的时候,我就陪你开心;你不开心的时候,我会哄你开心...”一番话哄得Client心花怒放,看着Server写完“奴隶宣言”,拿到手里仔细查看,却见Server写到:

       

  public enum WifeCmd
  {
         哄老婆开心,
         上交工资,
         陪老婆逛街,
         带老婆去兜风
         ......
  }

 

 Client看到这里,笑颜如花,连enum都用上了,真是个好夫君,这美满的婚姻也真是来之不易。

 

         -----------------------------------------------分割线,以上Server和Client的爱情剧完美剧终---------------------------------

          前五章,我们用讲故事的方式把Server端和Client的通讯机制讲解了下。此后我们要在此基础上开始我们的Web传奇的开发之路,让我们感谢Server和Client的精彩演出。

          每一个玩过传奇的人都记得那古朴的登录画面,当我们输入账号和密码,点击登录,随着铿锵的击鼓声,一扇石门徐徐而开,一个游戏的世界在向我们召唤,让我们赶快开启我们的传奇之旅吧!!

          游戏的开始,我们需要注册账号。这账号数据放在那里呢?放客户端,显然不可能,用户数据太不安全了。只好放服务端了,再说用户账号数据可是很重要,那些网游公司为此争得头破血流。经典的案例那是网易从九城夺取“魔兽世界”的代理权,期间坎坎坷坷,勾心斗角,俨然一部商业大片。还是“魔兽世界”的开发公司暴雪大神有远见,所有的用户数据都是属于暴雪的,这500W用户账号是属于暴雪公司的,数据库里的这些数据早就在停服前被暴雪的技术人员备份走了。

          所以说我们做Web传奇也要有远见,这个账号信息呢,还是归我williams所有吧,我把它们都储存在Sql Server2005数据库里呢。

          一张简单的账号表如下:

             

账号信息表 Users

字段名称

数据类型

含义

约束

UserID

int

 

 

UEmail

Nvarchar(32)

 

 

Upwd

Nvarchar(16)

 

 

UaddTime

DateTime

 

 

Ubalance

int

用户账号金额,用来消费

 

UloginTime

DateTime

 

 

UloginIP

Nvarchar(32)

 

 

Ustate

int

账号状态

 

          

           看到这里,你们会说这咋个和做网站的差不多呀?反正也不用忽悠大家,WebGame上的注册和网站注册有啥区别呢?本质上没什么区别,不就是把用户输入数据写到数据库里。

           用asp.net做过网站的都知道,做一个注册页面很简单,双击按钮写事件,调用BLL层的方法,执行insert语句就OK。但是在silverlight里,客户端不能直接和数据库打交道,怎么办呢?

           如果你看了前五章,你很容易想到客户端可以把用户数据放到byte[]数组里,封装到一个Message里发给服务端就可以了。服务端收到这个Message,判定后再调用BLL层的方法执行Insert语句也就可以了。和asp.net网站不同的是,在silverlight WebGame里数据是从Silverlight客户端发送到服务端,而在网站里数据是通过表单提交到Web服务端,asp.nett网站表单提交数据的过程作一个asp.net程序员可以不关心是怎么传递的,但是对于silverlight客户端来说,由于选用了原生态的Socket的通讯方式,我们得了解清楚些。

           看到这里,你可能豁然开朗,前五章讲的那肉麻的爱情故事就是为了方便的传递数据啊。哎,谁让我们这些asp.net程序员比较少接触到Socket通讯方式呢,我也是花了一周才看个明白点。

          罗嗦了这么多,让我们开始设计界面吧,我这个人很喜欢怀旧的,也没有美术功底,就把传奇里的界面搬来了,我在这里特此声明:本教程只供学习研究,所有素材来自网络,请勿跨省,勿发律师函。

        

        注册界面:

        

 

我们先来完成注册界面的功能,玩一个游戏,如果不注册个账号,是没办法登陆进去的,玩过游戏的都知道。

 

 void btnReg_Click(object sender, RoutedEventArgs e)
   {
            Users user 
= new Users { UserName = this.txtUserName.Text, Upwd =GameCMDFormat.HashFormat(this.txtPwd.Password), Uemail = this.txtUserName.Text, Ustate = 1 };
            MessagePool.AddSendMessage(GameCMDFormat.CreateNewUser(user));
//把发送的消息放到消息队列里
     }

 

 ①这里我们用一个User对象来保存了用户注册数据,这样才显得OO一些。

 ②这里我们写了一个GameCMDFormat类来产生Message对象

 

 

   /// <summary>
        
/// 新建账号命令
        
/// </summary>
        
/// <param name="user"></param>
        
/// <returns></returns>
        internal static SocketClient.Message CreateNewUser(Users user)
        {
            
return new SocketClient.Message(Convert.ToByte(GameCmdEnums.CreateNewUser), 1, System.Text.UTF8Encoding.UTF8.GetBytes(SerializeUtil.Encoder<Users>(user)));
        }

 

 

        让我重点来介绍下这个GameCMDFormat类,以后我们的命令基本上都是这个类帮我们产生的。

        在构造返回的Message对象时,第一个参数是class参数,我们用来存放命令的类型,这里我们用到的就是enum,这就是我们的server少爷和client小姐签婚后协议用到的。女人都喜欢男人专一点,enum值是唯一的,刚好满足女人的要求。当然这协议嘛,男方女方手里都有一份才算有效。所以server端有这样的enum,client端也会有这样的enum

        

 /// <summary>
    
/// 客户端的请求,或服务器发给客户端的命令
    
/// </summary>
    public enum GameCmdEnums
    {
        
/// <summary>
        
/// 注册新账号
        
/// </summary>
        CreateNewUser,
    }
     
/// <summary>
    
/// 服务器执行结果
    
/// </summary>
    public enum GameCmdResult
    {
       CreateUserTrue,
       CreateUserFalse, 
    }

 

 

       这协议一式两份,童叟无欺,大家都看得明白,无可非议,客户端端告诉服务端: CreateNewUser,服务端拿来相同的enum一比对,哦,原来是要我去建一个新账号,那好我就把新用户数据写入到数据库。写完了,服务端发送一个Message给客户端,这个Message带的命令是CreateUserTrue,客户端拿来enum一看,哦,账号建立成功了。

  

 

        ③这里我们用了SerializeUtil.Encoder<Users>(user)方法,这里用的是json序列化,为什么要用json序列化呢?你想啊,一个User对象,我怎么好放到byte[]数组里,这个比较难,不太好描述。如果你想想我们在.net里的序列化,我们可以把一个对象变成xml数据或者二进制流。

       但是很多平常在.net framework里使用的序列化和反序列化帮助类(如System.Runtime.Serialization命名空间下的许多类和方法)在SilverLight下都不可使用了。谁让silverlight是个精简框架呢。

       查下资料发现

       Silverlight可以用序列化,但是不能直接使用,必须通过服务来调用,比如WCF和Web Service。

       Silverlight 内置支持WCF的 DataContractSerializerDataContractJsonSerializer 两种序列化方式,也可以用System.Xml.Serialization 里的  XmlSerializer

       一般的过程是,通过服务提供序列化的对象,然后Silverlight调用服务获得这些对象。

       请参考:Silverlight中的序列化

 

       不过我们这里用的是socket,以上的方式不适合了,还有XML序列化的结果比json的数据量要大不少,为了减少通讯量,在描述同样数据的情况下我选择了json序列化,我在网上我找到了一个.net下的 json序列化组件:

       Json.NET.

      使用了里面的两个方法:

 

    /// <summary>
    
/// 对象序列化和反序列化,Json序列化
    
/// </summary>
    public class SerializeUtil
    {
     
        
/// <summary>
        
/// 序列化
        
/// </summary>
        
/// <typeparam name="T"></typeparam>
        
/// <param name="data"></param>
        
/// <returns></returns>
        public static string Encoder<T>(T data)
        {   
            
return JsonConvert.SerializeObject(data);
        }
        
/// <summary>
        
/// 反序列化
        
/// </summary>
        
/// <typeparam name="T"></typeparam>
        
/// <param name="data"></param>
        
/// <returns></returns>
        public static T Decoder<T>(string data)
        {
            
return  JsonConvert.DeserializeObject<T>(data);
        } 
    }

 

 

  这样我们很方便把一个对象放变成string,再把string用 System.Text.UTF8Encoding.UTF8.GetBytes()方法变成byte[]存放到一个Message对象就可以了。

       ④我们用到了自己写的消息队列类MessagePool类里的AddSendMessage方法。

          至于为什么要用队列来储存发送的消息,这是由于MMorpg客户端和服务端交换数据很频繁,大量的数据如果拥塞在一起发送就不太好,你说我们中国人就是多,春节买火车票如果我们不排队,都挤到售票窗口去抢,那不乱了套么?

          在MessagePool类里,我用了多线程发送,这里就涉及到了队列queue<T>的线程不安全问题,如何保证线程安全,就要用到了lock锁定,至于是否会影响效率,JeffreyZhao在一篇文章里说,也许lock的影响微乎其微,我没有做实验来判定,有兴趣的同学检验下。

          可能对于一些.net初学者来说在多个线程中保证queue<T>的线程安全不太好理解,那就多查查资料吧,我也是查资料,花了些时间写好的。

         当然,客户端发送数据的同时,也是要接受数据的,所以MessagePool类里有发送队列也有接受队列。

         对于MessagePool类我这里就不多讲了,留着专门一节再讲消息队列。我们本节的重点是要完成注册的过程。

 

      以上四点只是为了说明我们要把一个Message对象组装起来放到消息队列里中间的过程也不简单,涉及到队列,序列化,线程安全,做游戏不容易,反正我觉得比做网站要难一些。

 

      好了,我们折腾了这么久,那么服务端接收到这个客户端发送的这个消息后会这么做呢?

 

   /// <summary>
        
/// 执行客户端发送来的请求
        
/// </summary>
        
/// <param name="cmdstr"></param>
       internal static Message ExcuteCmd(int type, int flag, string cmdstr,System.Net.Sockets.Socket clientSocket)
       {
           GameServerCmd cmd 
= new GameServerCmd();
           GameCmdEnums t 
= (GameCmdEnums)Enum.Parse(typeof(GameCmdEnums), type.ToString());
           
byte[] content = UTF8Encoding.UTF8.GetBytes(".");
           
switch (t)
           {

               
case GameCmdEnums.CreateNewUser:
                   {
                       
int userID = 0;
                       
if (cmd.CreateNewUser(cmdstr))//执行注册命令
                       {
                           
return new Message((byte)GameCmdResult.CreateUserTrue, (byte)userID, content, clientSocket);
                       }
                       
else
                       {
                           
return new Message((byte)GameCmdResult.CreateUserFalse, (byte)userID, content, clientSocket);
                       }
                   }

               }

 

GameServerCmd 这是个命令执行类,它调用了BLL层里的方法。

 

  /// <summary>
        
/// 注册新账号
        
/// </summary>
        
/// <param name="cmdstr"></param>
        
/// <returns></returns>
        internal bool CreateNewUser(string cmdstr)
        {
            Users user 
= SerializeUtil.Decoder<Users>(cmdstr);
            
if (UsersCtrlBase.Instance().CreateNewUser(user))
            {
                
return true;
            }
            
else
            {
                
return false;
            }
        }

 

        写入账号信息到数据库后,然后发送一个注册成功的Message对象,我们也把它装到服务端的消息队列里等待发送给客户端,在服务端同理我们也有接收队列和发送队列,服务端接收和发送的队列是客户端的N倍,你可以近似估计N为客户端的个数,如果一个服1000人,那么就至少是1000倍于客户端。不开多线程,服务端估计是不能快速处理完这些数据。写到这里我想到,现在服务端早就是多核了,是不是要在服务端要到多核编程呢?把服务端多核优势发挥出来。这就留给大家去实现吧,我也是要花些时间研究下多核编程的。

           不小心写了这么多,我喜欢写游戏,去年我下班后吃完饭就写Web传奇,中间遇到不少的困难,一路摸索,是梦想支撑着我,前方困难重重,我们只有披荆斩棘,努力前行。

           我说过这篇文章里,我要把Demo提供给大家,由于持续花了近3个月的业余时间,写的代码有点多,从创建人物到下载数据,保存数据,合成地图,人物,怪物,再到即时的PK,中途还写WPF的地图编辑器,一下子把这些东西都发出来,估计看得也是头痛。我就一步步把这些代码精简出来吧。

           下载文件列表:

           1.数据库设计文档           2.json.net组件

           3.MyMirWebGameDemo1

           4.MyMirWebGameDB.

           5.怪物数据包   (重要:请放在MyMirWebGame.Web项目的clientbin里的Data文件夹下)

          有兄弟反应MyMirWebGameDemo1无法下载,我做了分包文件。

           MyMirWebGameVS2010D.part1         

           MyMirWebGameVS2010D.part2

           MyMirWebGameVS2010D.part3

           MyMirWebGameVS2010D.part4

        

Demo框架机构图:

                 

Demo运行提示:

                 1.附加MyMirGameDB数据库

                 2.修改MyMirGameServer里的app.config里的connectstring数据库连接字符串

                 3.启动MyMirGameServer项目里bin/debug里的MyMirGameServer.exe程序

                 4.在IIS里hostMyMirWebGame.web站点。

                 5.账号 w,密码1;账号xiangwei,密码:1,当然也可以注册账号

                 6.游戏只实现了男战士职业,只完成了物理PK过程

                 7.只做了人物之间的PK互动,人与怪物间的PK互动还没写完。

Demo的客户端已经升级到SL4,建议使用vs2010,并下载 Silverlight 4 Tools RC2 for Visual Studio 2010.

Demo测试环境:win7+ms sqlserver2005+silverlight4;

                    局域网测试通过,感谢以前在武汉创美的同事们。

                    互联网测试通过,感谢"深蓝WPF/Silverlight(群号:73068105)"群里的兄弟们。

申明:Web传奇客户端代码是在“深蓝色右手”silverlight 游戏引擎基础上开发做了30%左右的变动,特此申明。

备注:代码文件已经上传,附上PK画面:

          

 

posted @ 2010-05-09 01:14  王传炜  阅读(4332)  评论(34编辑  收藏  举报