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

游戏编程系列[2]--游戏编程中RPC与OpLog协议的结合--序的更多相关文章

  1. WCF编程系列(六)以编程方式配置终结点

    WCF编程系列(六)以编程方式配置终结点   示例一中我们的宿主程序非常简单:只是简单的实例化了一个ServiceHost对象,然后调用open方法来启动服务.而关于终结点的配置我们都是通过配置文件来 ...

  2. Java并发编程系列-(1) 并发编程基础

    1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运 ...

  3. java并发编程系列原理篇--JDK中的通信工具类Semaphore

    前言 java多线程之间进行通信时,JDK主要提供了以下几种通信工具类.主要有Semaphore.CountDownLatch.CyclicBarrier.exchanger.Phaser这几个通讯类 ...

  4. 学习ASP.NET Core Blazor编程系列十——路由(中)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

  5. 游戏编程系列[1]--游戏编程中RPC协议的使用

    RPC(Remote Procedure Call Protocol)--远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC协议假定某些传输协议的存在 ...

  6. 游戏编程系列[1]--游戏编程中RPC协议的使用[3]--体验

    运行环境,客户端一般编译为.Net 3.5 Unity兼容,服务端因为用了一些库,所以一般为4.0 或往上.同一份代码,建立拥有2个项目.客户端引用: WindNet.Client服务端引用: OpL ...

  7. 游戏编程系列[1]--游戏编程中RPC协议的使用[2]--Aop PostSharp篇

    上一篇我们使用了一个通用JSON协议约定来进行达到远程调用的目的.但是从实现上,我们需要不断的在所有的方法上添加拦截,并且判断拦截,然后执行,这就达到了一个比较繁琐的目的. 之前我们尝试过使用代码生成 ...

  8. python编程系列---Pycharm快捷键(更新中....)

    以下是我常用到的Pycharm快捷键(还有很多,只是我暂时用的最多的就这些): 在开发过程中,经常使用一些快捷键会大大提高开发效率,不要因为看这多而不用,常用的就那些,用得多就都记住了,脱离鼠标,逼格 ...

  9. netty系列之:在netty中使用native传输协议

    目录 简介 native传输协议的依赖 netty本地传输协议的使用 总结 简介 对于IO来说,除了传统的block IO,使用最多的就是NIO了,通常我们在netty程序中最常用到的就是NIO,比如 ...

随机推荐

  1. 微信小程序开发心得

    微信小程序也已出来有一段时间了,最近写了几款微信小程序项目,今天来说说感受. 首先开发一款微信小程序,最主要的就是针对于公司来运营的,因为,在申请appid(微信小程序ID号)时候,需要填写相关的公司 ...

  2. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  3. setTimeout 的黑魔法

    setTimeout,前端工程师必定会打交道的一个函数.它看上去非常的简单,朴实.有着一个很不平凡的名字--定时器.让年少的我天真的以为自己可以操纵未来.却不知朴实之中隐含着惊天大密.我还记得我第一次 ...

  4. ajax

    常见的HTTP状态码状态码:200 请求成功.一般用于GET和POST方法 OK301 资源移动.所请求资源移动到新的URL,浏览器自动跳转到新的URL Moved Permanently304 未修 ...

  5. Matlab 高斯_拉普拉斯滤波器处理医学图像

    前言:本程序是我去年实现论文算法时所做.主要功能为标记切割肝脏区域.时间有点久,很多细节已经模糊加上代码做了很多注释,因此在博客中不再详述. NOTE: 程序分几大段功能模块,仔细阅读,对解决医学图像 ...

  6. Xamarin+Prism开发详解一:PCL跨平台类库与Profile的关系

    在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]中提到过以下错误,不知道大伙还记得不: 无法安装程序包"Microsoft.Identity.Client 1.0. ...

  7. Postman接口调试神器-Chrome浏览器插件

    首先大家可以去这个地址下载 Postman_v4.1.3 这个版本,我用的就是这个版本 http://chromecj.com/web-development/2014-09/60/download. ...

  8. jenkins无法重启tomcat的原因

    在使用Hudson的执行sh脚本的时候,如果sh脚本是一个后台进程,如 Tomcat 这样的服务.如果使用Hudson的默认配置,会发现这些sh 进程有启动的过程,但是不会常驻后台,看Hudson 输 ...

  9. SQL 约束

    先用设计器创建约束.再用代码创建约束.数据库约束是为了保证数据的完整性(正确性)而实现的一套机制见文件Employee.sql非空约束(选择复选框)主键约束(PK) primary key const ...

  10. 虚拟机VMware12.05下安装Ubuntu16.04几个关键地方

    在踩了自己按照网上的教程安装Ubuntu之后,仍然踩了不少坑,鼓捣了一段时间,才达到自己想要的界面.   下面就来说说,大家可能也会遇到的情况:   1.安装ISO镜像时候,路径直接选择 你从Ubun ...