zoukankan      html  css  js  c++  java
  • 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画面:

              

     

  • 相关阅读:
    Codeforces Round #372 (Div. 1) B. Complete The Graph (枚举+最短路)
    HDU6430 Problem E. TeaTree【dsu on tree】
    HDU4358 Boring counting【dsu on tree】
    HDU6191 Query on A Tre【dsu on tree + 01字典树】
    2019 ICPC Asia Yinchuan Regional
    广义后缀自动机 例题
    2017-2018 ACM-ICPC Northern Eurasia (Northeastern European Regional) Contest (NEERC 17)
    BZOJ3238 [Ahoi2013]差异 【SAM or SA】
    HDU4622 Reincarnation【SAM】
    BZOJ1396 识别子串【SAM+SegmentTree】
  • 原文地址:https://www.cnblogs.com/wangergo/p/1730858.html
Copyright © 2011-2022 走看看