标签:
http://www.yxrb.net/thread-11839-1-1.html
在程序设计领域,程序就是数据结构加算法的理论早已深入人心。在手机游戏的后台系统中,数据结构就是各式各样的struct或class,遍布于数据存储(DB)、内存(业务逻辑)、网络(C/S交互协议),算法就是各种基于数据的业务处理逻辑了。
手机游戏后台数据结构有一些特点:
以整形数据为主,字符串类型比例很少,绝大部分数据可以用整形(直接或间接)表示。
相对大型端游,数据结构相对简单,绝大多数可以通过2-3层结构来抽象。
基于以上特点,《天天酷跑》后台设计了一套基于属性系统的结构体标准化表示方法,用于尽量统一C/S协议、逻辑处理和后台数据存储中用到的各式数据结构。
常规结构体定义
常规的结构体定义过程,就是程序员把用到的各种成员很直观地定义在代码(或者xml之类的模板中)中。如下图所示:
图1的结构体定义简单、直接,但也有一些不太好的地方:
1、结构体看上去很类似,字段类型少量变化和变量名称的不同而已。定义时也是到处拷贝后稍作修改,就变成其他结构体的定义。
2、同一对象在程序中对应多个版本的结构定义,例如游戏中的“角色”可能在DB、逻辑、C/S协议中有多个版本的定义,在使用的过程中需要各种对接和转换。
3、结构体随着需求变化时,特别是在C/S协议中共用的结构体,需要不断升级协议的版本号,增加维护和兼容的成本。
如果把这些类似的结构体定义统一起来,施以标准化实施,并辅以统一的接口(算法),是否可以既避免上述的弊端,并能够获取到额外的收益呢?
本人在《天天酷跑》的后台开发过程中,进行了一些探索,并使用属性系统作为结构体标准化的载体来实现。
属性系统介绍
由于手机游戏后台中无法用整形(以及无法用整形类型间接)表示的数据占比非常之低,经过对比,选择uint32作为基础的数据类型。
属性系统架构上分为3个层次:
1、属性:Key/Value结构表示,Key是属性ID,Value是对象的值。用于表示对象的一个属性。
2、属性堆:多个属性组成一个堆,有一个堆ID。用于表示不同对象实例。
3、属性仓库:多个属性堆组合成一个属性仓库,用于表示对象的数组。
属性系统特点
属性系统对结构体(对象)的定义进行了标准化,有如下一些特点:
1、不支持字符型成员,需要单独定义并与属性系统进行关联使用。
2、uint32类型表示所有的成员类型,其他类型需要转换。例如浮点转定点,64位整形用2个32位表示等。
3、本质上是key/value的二维数组结构。
4、可以通过ID(属性堆ID和属性ID)来随机读写成员,提供脚本化访问成员的能力。
5、标准化后可以使用统一的接口操纵(读写、序列化/反序列化等)。
在项目用使用属性仓库来表示各种游戏对象,例如角色的数值(类型、等级等)用AttributeStruct表示;单个角色对象通过AttributePile表示,角色对象之间通过属性堆 ID区分;玩家身上的角色列表通过AttributeWarehouse表示。
属性仓库的读写有3种方式:
1、一维索引。索引到某个属性堆,通过属性ID遍历读写堆中的属性成员,或者,根据属性堆ID 遍历到某个属性堆,通过索引访问堆中的属性成员。
2、二维索引。通过堆索引和属性索引来读写属性成员。
3、遍历式。完全获取不到索引信息时,使用属性堆ID和属性ID遍历整个属性仓库来对属性成员,性能低但功能强大,可以通过配置2个ID的方式读写任何成员。
DB中的属性系统
属性系统在DB中的应用,主要是玩家信息表中用于表示各种游戏核心对象的属性仓库字段。如下图:
玩家的基本账户信息、角色、坐骑、宠物、精灵、背包等,在DB玩家信息表中的载体,都是属性仓库。它们之间的不同,只是二维数组的2个维度的大小不同而已。例如角色仓库的属性堆数量少,但单个角色的属性数量多。而宠物的属性堆数量非常多,但单个宠物的属性数量少。业务使用不同的维度大的属性仓库来表示不同对象,主要用于减少空间的浪费。
代码中的属性系统
属性系统结合手机游戏后台程序设计的特定,在代码层级上,通常分为三层封装:
1、API层:通过模板封装一些基本的属性系统操作接口和算法。
2、逻辑层:直接操作DB中的属性仓库结构,封装具体业务的各种逻辑。
3、业务层:封装逻辑层的接口,加上日志流水、属性变化通知、DB保存等完整功能,供其他业务系统直接调用,屏蔽细节实现。
三层架构的好处是结构更加清晰,接口功能独立并易于维护。另外中间的逻辑层由于只操作DB字段,可以跨进程代码共享,例如idipsvr拿到玩家的DB字段后,引用gamesvr的逻辑层代码构造管理器,进行操作而不需要知道具体的业务细节。
C/S协议中的属性系统
客户端实现一套属性系统的封装后,就可以与服务器统一使用属性系统来解析玩家的数据了。在C/S协议中,当客户端请求玩家数据时,服务器会下发整个经过TDR打包后的属性仓库的结构。客户端TDR解包后,即可交给属性系统封装进行解析,然后再交由各种业务逻辑自己使用。
有以下几个好处:
1、客户端和服务器共享同一套数据格式,便于理解和数据处理。
2、减少协议升级的次数。基本上需求的增加和修改,只需增加一个属性定义交由客户端逻辑解析即可,不需要在协议中增加字段来支持。
3、存储系统的数据与协议返回的玩家数据都使用属性系统表示时,处理比较简单,dump出来甚至直接返回给客户端即可解析。
下图是使用属性系统来定义协议的例子。结算协议包含了大量游戏内统计数据,用于单局结算、数据校验、奖励发放、反作弊等大量逻辑运算。
属性系统与名称服务
名称服务(Name Service)用于记录属性名称、属性ID和属性所属对象之间关系。使用之前需要进行属性信息注册,例如< X_ATTR,ATTR_GOLDNUM,“ATTR_GOLDNUM”>三元组就定义了玩家金币数这个属性,属性属于X_ATTR范围,属性ID为ATTR_GOLDNUM,属性名为“ATTR_GOLDNUM”。
注册属性信息到名称服务器后,就可以用配表的方式完成很多功能,当然前提是底层的命令行解析和相应的业务逻辑代码调用。
总结
通过属性系统,有条件性地标准化了前后台协议、后台业务逻辑和DB中数据存储的结构体表示,并通过一套封装的接口来读写结构体的成员,为开发的中后期带来非常多的便利,早期因为接口封装不完善、面向对象的思维方式等带来了一些沟通的成本。
通常标准化和灵活性是矛盾的双方,标准化之后就要求按照既定的标准(或者限制)来实施,在灵活性要求很高的场景(例如结构层级复杂、变化频繁的业务需求),会带来不便。但从《天天酷跑》后台的探索情况来看,属性系统非常适合在手机游戏中标准化表示各种结构体和对象。
标签:
原文地址:http://www.cnblogs.com/bambomtan/p/5122368.html