• 游戏编程系列[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,所以返回值还是跑不掉。
    但是在很多时候确实可以达到这样的效果。
    等等,是不是漏了点什么,如果这样可以达到开发的目的,我还要服务端干嘛?

  • 相关阅读:
    B树、B-树、B+树、B*树
    CentOS 7 源码编译安装PostgreSQL 9.5
    phpstorm里面无法配置deployment?
    Php7安装pdo_pgsql,pgsql扩展
    [xDebug] PhpStorm Xdebug远程调试环境搭建
    [xDebug]Xdebug和Sublime调试PHP代码
    [xDebug] 服务器端的配置参数
    [xDebug] php下的Debug的调试
    【转】漫画:什么是协程?
    【转】一个由正则表达式引发的血案----贪婪模式的回溯
  • 原文地址:https://www.cnblogs.com/icesun963/p/6256453.html
Copyright © 2020-2023  润新知