技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 算法 --> 网络游戏物品校验系统的设计

网络游戏物品校验系统的设计

浏览:875次  出处信息

网络游戏若要有支持一个稳固的经济系统,服务器底层必须有一个可靠的数据服务。要设计出精简的数据协议可不容易。它需要保证在发生异常(可能是硬件异常,也可能是软件异常)时,不会出现物品/货币丢失,复制的问题。

使用带事务的数据库是一个不错的选择,但对程序员的要求较高。程序员需要仔细考虑每个数据操作如何编写事务才是安全的,还需要考虑数据操作的异步性。这对需求变化迅速,量又比较大的游戏,做的好真的是很困难。

我思考了很久,几经易稿,大约得到了这么一个东西:

数据存储和合法性校验应该分开,独立为不同的服务,这样才容易做的稳固。也就是说,数据服务不必做到完全的完备,简单的去读写修改数据即可。这部分,我倾向于用简单的 key/value 方式储存数据到数据库,可以自己实现,也可以用 redis 这样的现成产品。不必使用事务机制。

但是我们应该提供一个强的校验系统,所有的虚拟物品发放、转移,都应该经过这个校验系统。一切操作都需要经过事后的核对。由此系统来修正数据异常。


简单来说,我们需要保证的是游戏世界中的每件物品都有唯一的拥有者,如果不被玩家拥有,则被系统所有。虚拟货币也是如此。应该避免物品的蒸发(平白无故丢失)或是被复制。

物品和所有者的关系是简单的,单层的,不必实现多层次拥有的树状关系。每个玩家以及可以拥有货币和物品的 Entity 都有独立的帐号(用一个 64bit ID 表示)。而每件物品 Goods ,都有其唯一的 ID (64bit 整数),以及唯一的拥有者。

Entity 和 Goods 是独立正交的两个概念,一个 Entity 不可能是一个 Goods ,反之亦然。Goods 对 Entity 是一个 n:1 的关系。

这样看来,这个数据校验系统的 API 设计就可以比较简洁了。

本来,Entity 和 Goods 可以有相同的 id ,因为它们相互不影响(类型不同)。但为了实现方便,我们让 Entity 和 Goods 公用一个 id 空间,不会使用相同的 id 。

ApplyID(number)

申请一段 ID 备用。这是一个有返回值的 API 。多在服务启动时调用。数据服务返回 number 个空闲 ID 。这样,请求者之后,可以任意使用这段 ID 中的某一个,而不用担心和其他人冲突。一旦申请的 ID 接近枯竭,则可以提前申请下一批。0~1023 为保留 id ,通常 0 表示系统。

CreateEntity(id)

创建一个 entity ,赋予它一个空闲 id 。如果 id 是正确的通过 ApplyID 得到,这个 API 通常不会调用失败。

CreateGoods(id)

创建一个 Goods ,赋予它一个空闲 id 。它的所有者,默认为 0 ,即系统。

ExchangeGoods( { entity1 , funds , goods[] } , ... )

这个 API 接收若干组数据,每组数据包含 entity 的 id ,货币 funds 数量,以及它可以获得的 goods

这个 API 用于在几组物品以及货币的所有者间做交换。所有的 goods 的所有者必须存在于传入的列表内。 所有的 funds 总数必须为 0 。例如:

玩家 player1 用 1000 块钱,交换 player2 的编号为 12345 的物品,系统抽取 player1 的 10 块钱税。则可以表达为 ExcahngeGoods ( { player1 , -1010 , [12345] } , { player2 , 1000 , [] } , { 0 , 10 } ) 即,每组数据表达了某个 Entity 在这次交易中将获得什么。

系统发放(凋落)物品则可以先用 CreateGoods 创建出来,再用 ExchangeGoods 来发放。

VerifyGoods( entity , goods [] )

这个 API 用于校验 entity 所拥有的物品的合法性。API 传入他所拥有物品 id 列表 goods[]。校验服务校验完毕后,将告诉调用者,entity 拥有的物品是否有缺失,或是否有冗余。一般说来,再服务正常的情况下,这个校验是多余的。所以可以在服务维护时,离线跑一次。也可以在玩家上线时(或定期)做一次校对。

QueryGoods( entity )

可以获取 entity 所拥有的所有物品列表以及货币数量。这个 API 仅供调试使用。功能上和 VerifyGoods 有所重复。

如果一个玩家拥有的东西过多,可以考虑把仓库和背包分离成两个 Entity ,这样可以减轻 VerifyGoods 的负担(如果需要定期去做的话)


数据校验服务和数据存储服务是独立的,所以数据储存服务中还是记录有玩家对物品的拥有关系。游戏逻辑不应该依赖数据校验服务提供的物品列表,它只是用来保证游戏内的交易系统、怪物凋落系统都是正常工作的。并可以在异常发生时(硬件异常或软件异常),提供一份数据来修复。


1 月 13 日 补充:

关于一个玩家拥有多件相同物品的优化。

其实大多数物品并不具备唯一性,比如血瓶,材料等等。每个玩家都可能拥有多件。这种东西界于货币和特有物品之间。如果每个都为其分配一个 uuid ,可能会浪费大量的储存空间。

解决方法有三:

  1. 对于这个数据校验服务,不记录这些无关紧要的物品。

  2. 当同一件物品达到一定数量,以一定数量和系统兑换大面额 ID 。比如 10 个 id 兑换 1 个表示 10 个数量的 id 。这个方法不用为校验服务增加新协议,只需要在使用方约定即可。需要特殊处理的只是把系统 id 0 特殊对待,因其只换出不换入,就不需要把换掉的 id 进入数据库,写一下 log 即可。

  3. 保留的 1~1023 号 id 做特殊用途(当然从协议上说,不限于某一段 id ,每个 id 都可以允许多份,但不利于做一个简洁健壮的实现),每个表示特定物品。每个 entity 可以拥有这些小 id 多件。在校验服务的实现里,可以优化储存,保存 id 和数量即可。这个方法不需要修改接口 API ,但需要为实现做一定的优化。

QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1