游戏编程系列[2]--游戏编程中RPC与OpLog协议的结合--序


在系列[1]中,我们展示了RPC调用协议的定义以及演示,通过方法定义以及协议约定,进行了协议约定以及调用过程的约定。
然而,实际上在游戏中,调用过程之后,需要传输相对多的数据给服务端。

常用场景,客户端使用金币购买一把木剑。

一般情况下我们会这么约定:

              /// <summary>
            /// 购买的返回消息
            /// </summary>
            public class ByItemReturn
            {
                /// <summary>
                /// 购买的物品ID
                /// </summary>
                public int ItemId { get; set; }

                /// <summary>
                /// 购买的物品数量
                /// </summary>
                public  int Count { get; set; }

                /// <summary>
                /// 玩家剩余金钱/或减少金钱
                /// </summary>
                public int NowGold { get; set; }


            }

  

方法定义和实现:

	
            /// <summary>
            /// 购买物品
            /// </summary>
            /// <param name="itemId">物品id</param>
            /// <returns></returns>
            public static ByItemReturn BuyItemByGold(int itemId)
            {
                //伪代码
                //获取玩家信息
                //查找购买价格
                //检查玩家金币
                //玩家金币减少
                //获取玩家背包
                //添加背包物品
                return  new ByItemReturn()
                {
                    Count = 1,
                    Code = 0,
                    ItemId = 100,
                    NowGold = 900
                };
            }

  

客户端调用:

 

            /// <summary>
            /// 客户端方法
            /// </summary>
            /// <param name="itemId"></param>
            public static void DoBuyItemByGold(int itemId)
            {
                //执行调用
                BuyItemByGold(itemId, (r) =>
                {
                    //显示购买成功
                    //把金币付给本地数据
                    //刷新本地金币
                });
            }

  


等等,假如我们修改了需求,我们允许拿钻石购买木剑。


修改代码:

            /// <summary>
            /// 购买的返回消息
            /// </summary>
            public class ByItemReturn2
            {
                /// <summary>
                /// 购买的物品ID
                /// </summary>
                public int ItemId { get; set; }

                /// <summary>
                /// 购买的物品数量
                /// </summary>
                public int Count { get; set; }

                /// <summary>
                /// 玩家剩余金钱/或减少金钱
                /// </summary>
                public int NowGold { get; set; }

                /// <summary>
                /// 玩家剩余钻石
                /// </summary>
                public int NowDiamond { get; set; }


            }


            /// <summary>
            /// 购买物品
            /// </summary>
            /// <param name="itemId">物品id</param>
            /// <returns></returns>
            public static ByItemReturn2 BuyItemByGold2(int itemId,bool diamond=false, Action<ByItemReturn2> callback = null)
            {
                //伪代码
                //获取玩家信息
                //查找购买价格
                //检查玩家金币
                //玩家金币减少
                //检查玩家钻石
                //玩家钻石减少
                //获取玩家背包
                //添加背包物品
                return new ByItemReturn2()
                {
                    Count = 1,
                    Code = 0,
                    ItemId = 100,
                    NowGold = 900,
                    NowDiamond = 100
                };
            }

            /// <summary>
            /// 客户端方法
            /// </summary>
            /// <param name="itemId"></param>
            public static void DoBuyItemByGold2(int itemId, bool diamond = false)
            {
                //执行调用
                BuyItemByGold2(itemId,diamond, (r) =>
                {
                    //显示购买成功
                    //把金币付给本地数据
                    //刷新本地金币
                    //刷新本地钻石
                });
            }

  


假设我们有个通用协议,能描述所有资源的修改,就能轻松的搞定这个问题了。
假如我们代码变成这样:

	            /// <summary>
            /// 购买的返回消息
            /// </summary>
            public class ByItemReturn
            {
                /// <summary>
                /// 购买的物品ID
                /// </summary>
                public int ItemId { get; set; }

                /// <summary>
                /// 购买的物品数量
                /// </summary>
                public  int Count { get; set; }

                /// <summary>
                /// 修改信息
                /// </summary>
                public object ChangeMessage { get; set; }
            }


            /// <summary>
            /// 购买物品
            /// </summary>
            /// <param name="itemId">物品id</param>
            /// <returns></returns>
            public static ByItemReturn BuyItemByGold(int itemId,Action<ByItemReturn> callback=null)
            {
                //伪代码
                //获取玩家信息
                //查找购买价格
                //检查玩家金币
                //玩家金币减少
                //获取玩家背包
                //添加背包物品
                return  new ByItemReturn()
                {
                    Count = 1,
                    ItemId = 100,
                    ChangeMessage = GetChangeMessage()
                };
            }


            public static object GetChangeMessage()
            {
                return null;
            }
            public static object SetChangeMessage(object message)
            {
                return null;
            }

            /// <summary>
            /// 客户端方法
            /// </summary>
            /// <param name="itemId"></param>
            public static void DoBuyItemByGold(int itemId)
            {
                //执行调用
                BuyItemByGold(itemId, (r) =>
                {
                    //显示购买成功
                    //设置修改数据
                    SetChangeMessage(r.ChangeMessage);
                    //刷新显示面板
                });
            }

  

至少,看起来代码变短了。
遇到之前的情况,协议不用修改,如果有多个类似的接口,相对修改也是减少了。
基本修改逻辑,调整接口,调整调用。 那么也就重新完成需求了。

都写个GetChangeMessage SetChangeMessage 多麻烦,我们拿AOP搞定吧 会怎么样。
假设我们的所有callback之前都有个SetChangeMessage
所有的接口返回之前都会自动调用下GetChangeMessage
看看新代码

 

    /// <summary>
            /// 购买的返回消息
            /// </summary>
            public class ByItemReturn
            {
                /// <summary>
                /// 购买的物品ID
                /// </summary>
                public int ItemId { get; set; }

                /// <summary>
                /// 购买的物品数量
                /// </summary>
                public  int Count { get; set; }

                /// <summary>
                /// 修改信息
                /// </summary>
                public object ChangeMessage { get; set; }
            }


            /// <summary>
            /// 购买物品
            /// </summary>
            /// <param name="itemId">物品id</param>
            /// <returns></returns>
            public static ByItemReturn BuyItemByGold(int itemId,Action<ByItemReturn> callback=null)
            {
                //伪代码
                //获取玩家信息
                //查找购买价格
                //检查玩家金币
                //玩家金币减少
                //获取玩家背包
                //添加背包物品
                return  new ByItemReturn()
                {
                    Count = 1,
                    ItemId = 100,
                };
            }



            /// <summary>
            /// 客户端方法
            /// </summary>
            /// <param name="itemId"></param>
            public static void DoBuyItemByGold(int itemId)
            {
                //执行调用
                BuyItemByGold(itemId, (r) =>
                {
                    //显示购买成功
                    //刷新显示面板
                });
            }

  

似乎好像更好了。
那么问题来了,这样的一个想法不错,怎么实现呢?
我怎么知道哪个数据是否修改?
回头看看系列文章1的响应定义: 差异数据 就是所谓的 ChangeMessage

   /// <summary>
    /// 返回数据
    /// </summary>  
    public partial class ResponseObj 
    {
        /// <summary>
        /// 返回数据对象
        /// </summary>
        public object result { get; set; }

        /// <summary>
        /// 是否出错信息
        /// 默认0 无出错信息
        /// </summary>
        public int error { get; set; }

        /// <summary>
        /// 请求序号
        /// </summary>
        public int cid { get; set; }

        /// <summary>
        /// 差异数据
        /// </summary>
        public OpChangeItem[] opstr{ get; set; }

    }

  

协议部分后续如果有同学感兴趣,到时候在开一篇,现在可以暂且无视。
首先,我们的假定:
1.我们总是有办法监测到是否有一个字段被修改了。
2.我们总是可以知道,通过修改的值设置到具体某个实例里面的某个字段

也就是可以得到这样的一个类似描述。


首先客户端和服务端都有一份这个数据:

{
  "钻石": 100,
  "金币": 100,
  "背包": [
    {
      "ID": 101,
      "耐久": 10
    }
  ]
}

  

我们有个方法,叫木剑耐久降低1

通过以上的假设,我们可以得到一句修改描述

ID为101的木剑中的耐久变成9.

那么我们可以把这句话还原,找到101这把木剑,然后把耐久改成9.

{
  "钻石": 100,
  "金币": 100,
  "背包": [
    {
      "ID": 101,
      "耐久": 10 -> 9
    }
  ]
}

  

客户端,的数据就和服务端是一样的,然后再执行回调方法,看看我们得到了什么。
我们直接可以从本地数据来获得这些信息,而不用服务端传递给客户端。

然后这个事情就变得更简单了。
重复上面的例子。

 

            /// <summary>
            /// 购买物品
            /// </summary>
            /// <param name="itemId">物品id</param>
            /// <returns>返回物品ID</returns>
            public static int BuyItemByGold(int itemId,Action<ByItemReturn> callback=null)
            {
                //伪代码
                //获取玩家信息
                //查找购买价格
                //检查玩家金币
                //玩家金币减少
                //获取玩家背包
                //添加背包物品
                return  newId;
            }



            /// <summary>
            /// 客户端方法
            /// </summary>
            /// <param name="itemId"></param>
            public static void DoBuyItemByGold(int itemId)
            {
                //执行调用
                BuyItemByGold(itemId, (r) =>
                {
                    //显示购买成功
                    //从背包中查找那个ID=r的对象
					//展示这个道具
					//刷新显示面板
                });
            }

  

似乎好像更精简了,有没有。而实际上你可能获取的数量不一定为1,所以返回值还是跑不掉。
但是在很多时候确实可以达到这样的效果。
等等,是不是漏了点什么,如果这样可以达到开发的目的,我还要服务端干嘛?

posted @ 2017-01-06 15:23  乐逐风  阅读(164)  评论(1编辑  收藏  举报