« 一些工作进展 | 返回首页 | 开发笔记(21) : 无锁消息队列 »

开发笔记(20) : 交易系统

物品交易和掉落系统不是一期里程碑的内容,手头事情不多,就还是先做设计了。

物品掉落和交易,本质上是一件事情。区别在于玩家之间的物品交换于玩家和系统之间的物品交换。这部分的设计,我在去年初曾经写过一篇 blog 总结过。

这次重新设计实现,做了少量的改动。主要是一些简化。设计原则是,交易服务器独立,简洁,健壮,容易回溯交易历史做数据恢复和分析。

交易服务,我认为是一个后备系统。在数据不出问题时,不大用理会它的存在。它仅仅是用来保证物品不被复制,确定每件物品可以有唯一的所有人。在出现争议的时候,可以作为仲裁机构。

在游戏服务中,玩家或其他实体储存的物品,同样会保存在玩家数据中,利用共享内存读写 。 每件物品记录在对应的包裹格类,直接记录物品名称或指代名称的 id 。即,同样的物品用同样的标识。之外,再额外记录一个(或若干个)物品唯一 id (用 64bit 或 128bit ) 。这样做,避免在访问物品的功能时,额外去查一张表。

交易认证服务,其实只关心一件事情。即,某个物品 id 归属于谁。它不关心物品 id 代表的是什么含义。当然,可以有另一个数据库记录这个关系。在物品的所有者改变时,交易服务来保证其原子性。除了玩家间的转换,还有大量的和系统间的交换。掉落物品其实是从系统改为玩家所有,而玩家销毁物品则是把所有者改为系统所有。


我把交易系统所操作对象分为三类,Entity 表示所有者,Goods 表示物品,Currency 表示货币。货币是唯一不用唯一 ID 来记录的,它只记录数量。游戏中可能有多种不同的货币,所以需要额外记录一个货币的类型。

每笔交易都是由若干交易品构成的,每件交易品是 GoodsID 或 CurrenyType + CurrenyAmount ,为了让系统可以检查出交易品的合法性,我们需要同时绑定物品所有人,或物品所有人名下的货币总量。大体上,每次交易,对于一个交易方需要提供的信息是:

EntityID (所有人标识) Balances (所有人货币账户余额) { GoodsID (物品标识) | Currency (支出货币金额) }

展示我只打算支持两方交易,所以单条交易请求,只需要提供两个交易方的支出就可以完成交换了。

交易系统会校对双方的账户余额是否正确,交易交易品的所有人是否正确。如果有一项不正确,则取消交易,并返回错误的交易品是什么(或是正确的账户余额)。

其它协议大体上没有改变,和以前写的那篇类似。只是我增加了创建 Entity 时可以初始化账号余额的参数,用起来更方便一点。初始化物品也可以设置所有者,而不是默认给系统所有,进行一次交易才能赋予玩家。


系统不会只有一个 ID ,在掉落系统中,每张地图每个副本都有独立的 EntityID 。我们会在副本开始初始化好左右的可掉落物品(对于开放地图,会略微复杂一点,由某种和时间以及玩家数量有关的量来定期初始化)。货币也是定额配给在地图的 Entity 账户里的。和玩家账户不同,系统账户允许为负值,但是当系统账户为负时,会提醒维护人员。这样,我们比较好控制副本的货币产出。

销毁物品会把物品所有者更改为一个回收站名下,而不会从数据库中删除。但在实现上,或做一些特别优化,把这部分冷数据(正常逻辑不再访问的到的数据)移动到分离的数据库中。


这个系统接口简洁,需求单一,需要考虑的是在海量数据时足够稳定高效。所以对热数据(在线玩家的物品)需要做一些 cache 优化。对于每件物品都有一个唯一 ID ,可以预想到整个游戏世界中的对象可以轻易超过 10 亿这个数量级。因为日后还有合并服务器的需求,务必要保证所有服务器间也不可以有重复 id 的物品。这个数量非常之大,在实现时要从多方面考虑性能优化问题。

数据库的选择上,我认为是一个比较次要的问题。交给最终实现的人来选就好了。其实需要维护的数据仅仅是物品ID 到所有人 ID 的映射关系,使用 SQL 数据库也好,NoSQL 数据库也好,都没有特别的必要性。晓靖同学自告奋勇想做这套系统,他决定使用 Redis 来做数据储存,我觉得没有太大问题。

在数据容灾问题上,我们打算把交易请求通过独立服务来逐条记录 log 。保证使用 log 就可以完全恢复出数据库中的数据来。靠加强 Log 系统来保证数据不易损坏。

Comments

掉落物品其实是从系统改为玩家所有,而玩家销毁物品则是把所有者改为系统所有。

前面那句明白,后面那句为什么不是从数据库中删除这个数据?或者是为了将来可以有类似删除恢复的功能?

玩游戏的时候没想过这些简单的操作实现起来还真麻烦。。

你好,我不是做游戏的,但是看你的文章很开阔我的思路,应该可以应用在更广阔的空间。
关于你说的“掉落物品”,我理解是玩家的东西掉了,物品所有者应该是从玩家变为系统吧。但是你说“掉落物品其实是从系统改为玩家所有,而玩家销毁物品则是把所有者改为系统所有。”
是不是我理解错误呢?这里的掉落物品难道是说“玩家从地上捡了一个物品”?

您好,设置系统也有货币帐户的概念来控制游戏世界内的货币平衡的想法挺不错,
“销毁物品会把物品所有者更改为一个回收站名下,而不会从数据库中删除。但在实现上,或做一些特别优化,把这部分冷数据(正常逻辑不再访问的到的数据)移动到分离的数据库中。”这样实现有什么好处?为什么直接删除,再需要的时候动态生成物品呢

原来我想问的是有没有人有兴趣花一百万支持我免费公开它?

哦,不过其实也没关系,其实如果没人买得话,过半年一年我会以十元一份的价格公开的:D,只是现在还在完善工具以便到时能把它说清楚~

最主要的原因还是我太累了,缺钱,就是缺钱~,我前面提的问题,第三个已经完美解决了,第二个基本完美解决,第一个基本解决,但是以我现在的资源,要把他们转化为财富,还需要好多年的时间。我还有,我的解法对于当前的机器来说是非实时的,所以游戏业暂时不太用得上。

再不挣到钱,媳妇可能等不了我了,加上之前研究太累,急需休息,所以急缺钱~

@jichong
你既然已经把这个世界所有的问题都抽象为简单、有序和可控了,还要钱干吗?

哦,不是,我是想问云风有没有兴趣花一百万购买~

问题我已经做出来了,有没有人有兴趣花一百万购买的啊?

所以嘛,唯一有意义的问题只有一个
计冲公开课第一集: 为什么五次多项式如此重要? 答:我们身处的世界在表象上纷繁复杂,存不存在一种简单统一的方法去描述和理解、求解这个世界的现象和问题? => 如果这个方法存在,那么应该存在一种方法去通解世间的一切方程或方程组 => 如果通解一切方程或方程组的方法存在,那么五次多项式方程的通解便应该存在。 这就是为什么五次多项式方程如此重要,因为只有它可被通解,世界才有可能走向简单、有序和可控。

嗯嗯,挑战云风,你弄的这些东西真的有意义么?
游戏制作到最后归根结底是3维世界的模拟。那么程序的工作实际上是和三维方程组打交道。
对于3维方程组而言
f0(x, y, z) = 0
f1(x, y, z) = 0
f2(x, y, z) = 0
如果我们有10类不同的1维方程,就可以产生1000种不同的三维方程组合,如果没有通解方案,而使用人力去解决的话,假设我们能够一周解决一个,需要1000周/人,也就是将近20年/人,然后我们招聘10个人,花费两年时间,经过无数的加班、通宵,最终终于把这些问题都解决了,然后老板或策划跑过来告诉我们他昨天做梦梦到一类全新的方程,然后10类变成11类,问题的规模变成1331种,需要的时间就变成27.7年/人。而且这一切都还是纯乐观的,现实中你不可能1周解决一个问题,也不可能找到10个能力又强又听话的解题者。

怎么不考虑 整个玩家的物品拼接成json字符串,这样就能保证一个玩家的所有物品在数据库表中只有一条数据。

我是看您写的书的时候看到这个链接地址的,相信以后会经常来的。

回复13楼,程序实现和游戏策划是不同吧。我觉得先有强大的技术做支持,游戏才有成功的基点

玩家之间的物品交换与玩家和系统之间的物品交换如果放到一起设计的话感觉需要考虑的问题反而更多,我们只是把向玩家身上添加道具的部分作为一组操作,而道具来源则分别处理,这样逻辑拆分开,对于其他人来说也更好理解。

事实上我觉得吧,游戏成功或者不成功有很多因素,程序只占很小但也必不可少的部分,期望云大侠在钻研技术之余多多考虑一下游戏玩家需求和当前时代期待的游戏方式方面的东西,毕竟,游戏程序写的再好再高效,引擎再成熟,玩家并不能体会得那么深刻,甚至完全体会不到,他们更注重的是游戏玩法上的亮点和游戏乐趣的发掘,当然,老玩家还有各种游戏情结,这些的基础是程序,但也是你程序很难给予的,中国游戏行业病态的症结不在技术上,像我这种千千万万中的小虾米程序员没有能力改变这样的现状,但是你可以尝试,相信你一直立志于为中国游戏行业发展,可是你好像弄错了方向,plz, make change happen!

大侠,请教一个菜鸟问题,关于技能系统的
主流管理方式是将技能的各种属性以buff为单元放到一个"list"里然后while处理,关于这个"list",既要考虑到 遍历的效率 ,也要考虑到 删除node的效率 ,还有 查找的效率 ,(内存消耗无视),stl无论是list,hash还是map,都似乎很难很好同时兼顾这几点,我想知道在大侠你的项目里是怎么选择的,麻烦详细讲解一下,3Q

技术很强悍,值得学习和借签的文章

这是很基本的概念,正常都是这样做的,至少我开发的交易系统也是这概念,这文章较适合新手...

所以对热数据(在线玩家的物品)需要做一些 cache 优化。

用redis还需要再加cache么?

交易的时候是先把内存中的数据删除,再向交易服务器发请求还是先发请求等交易服务器返回再删呢?
如果是前者交易服务器不可用的时候怎么办?
如果是后者A把物品b交易给C,A和C都点了确认,这个时候向交易服务器发送请求,在交易服务器修改完A的存储向游戏服务器返回的过程中A下线,是否会覆盖交易服务器的修改呢?

为什么要用“共享内存读写 ”呢,现在内存数据库那么多。。。也可异步。。

恕直言:水平很高,格局很低。

为啥要设计道具ID系统?不设计ID系统又会怎样呢?

经常来这看。作者的文笔不错,技术更好,这样的分享很有价值

和楼上一样,我也是过来膜拜的

不得不承认,很多人们心理上认为的很不起眼的idea,牛B的程序员能用这些很不起眼的零碎东西去完善自己的游戏世界,甚至能形成一种被其他人模仿的风格
而与牛b无关的程序员似乎永远不太愿意注意这样的问题
膜拜了

Post a comment

非这个主题相关的留言请到:留言本