为什么要从云变量过渡到 KV 数据库
Scratch 大多数游戏都是单机的,Scratch 官方提供了 云变量 功能作为联机作品的解决方案。共创世界 CCW 甚至在这个基础上开发了 云列表 功能。 但最近肝酱们发现 CCW 中调低了云变量和云列表的同步时间,导致了这两个功能无法使用。 最新的 Gandi IDE 也计划下线云变量和云列表功能。 就像 Scratch 官方的云变量需要先申请白名单,批准后才能使用, 这是因为云变量的底层设计逻辑是有错的。
本文会详细介绍一个云变量、 KV 数据库和 Simple MMO 中的三种数据通信的原理以及不同场景中应该如何选择。
原理
网络传输的具体原理不是本文的重点,有兴趣可以自行学习。 但网络传输无非关注以下几个方面:
- 【权限】谁拥有这个数据的写操作权限?谁有读的权限?
- 【负载】每次传输多少数据?
- 【时限】这些数据需要以多高的频率进行同步?
- 【持久化】这些数据是否需要长期保存?
云变量
云变量,像是一个公共空间,【权限】每个人都能读写,不会考虑谁拥有读取或者写入权限,因此防数据冲突必须靠开发者自己完成(例如建立多个云变量,每个连入房间的第一个人拥有写入权限);【负载】云变量由于无法做到差分传输(只传输改变的部分),每次发生了变化都会对所有人进行全量同步;【时限】如果是不停地写入操作,写入得越频繁,数据量就越大;【持久化】云变量的数据是会持久保存的,即便持久保存也不一定有用。
云列表,更是如此。 CCW 的服务器第一次因为云变量和云列表宕机,就是因为 yk1boy 的《金色回声》。 他使用了云列表来存储玩家的排行榜。 但由于《金色回声》太好玩了,玩的用户假如有 10w 个, 那此时云列表中会存入 10w 条数据,每个用户在玩的时候都需要下载这 10w 条数据。 即便增加了一条数据,云列表也会向服务器同步 100001 条数据。 这就相当于让服务器被 DDoS 攻击了(用巨量的流量让服务器无法正常服务用户)。
计算一下,如果每个人存储的数据有 1k, 10w 条数据就相当于 100M 的数据。而且每次更新哪怕是 1k,也需要传输 100M 数据。
KV 数据库
KV 是 Key Value 的简称。 KV 数据库指的就是存储这样一对一对的 key 和 value。 例如, 要建立一个变量名字为 a, 里面存储的内容为 123。 KV 数据库里就会存为 a = 123。 十分简单直观。
在【权限】方面, KV 数据库的权限控制就比云变量好了,每个项目可以有公共空间也可以有个人空间。 例如假设用户 Shawn 的 password = 123, 当 Arkos 在运行工程时读取 password 时,是永远无法获得其他用户的 password key 对应的 value 的。
在【负载】方面, KV 数据库可以通过隔离 Key 的形式来减小传输。 例如当 Shawn 更新 password 从 123 到 12345 时,实际网络传输的内容,只会是这个 key 对应的 12345 的传输量。
在【时限】方面, KV 数据库支持高频读写,并且做了缓存。也就是说,当数据没有更新时,反复读取是不需要有网络传输的。
在【持久化】 方面, KV 数据库和云变量一样,可以长期保存。
简单地说, 使用 KV 数据库,是完全可以替代云列表和云变量的。 不仅在传输负载方面优于云变量,在权限管理方面更是有突出优势。
MMO
Simple MMO 提供了通信的手段,可以保证多人之间的数据可以被快速同步。 和云变量、KV 数据库相比, MMO 更适合做实时通信类的游戏(例如吃鸡、狼人杀等)。
Simple MMO 的网络传输方式和云变量、 KV 数据库完全不同。 你可以想像 Simple MMO 像是一个水管(socket 连接),每个人都在向水管里读取或者写入数据。 水管只传输发生变化的部分,没有发生变化的部分不放入水管中。 它的最大优点就是:快!
在【权限】方面, MMO 是十分严格的,每个人只能修改自己的状态部分,对别人的内容是无法篡改的。 每个人和别人的通信除了状态同步外, 还可以通过广播消息的方式完成。 无论是实现多人实时聊天,还是游戏中额外的事件同步,都十分方便。(你可以想像是 Scratch 中用广播+接收广播的方式,不过,这个广播的事件消息会给所有连入房间的人发送)
在【负载】方面,MMO 使用的是差分传输,也就是说,只传输发生改变的部分。 例如一篇 3000 字的作文,如果只改变了中间的一个字, Simple MMO 只传输这变化的部分。 大大降低了浪费传输的部分,从而能实现高实时性的游戏。
在【时限】方面,因为负载比较低,所以实时性 MMO 是最高的。 目前 CCW 支持 60 fps,也就是说,最低网络延迟是 16 ms。
但在【持久化】上, MMO 的设计是不记录过程,也就是说,当连接中断后,数据即放弃。 若要存储游戏中的数据,需要使用 KV 数据库进行持久化。
三种技术对比如下
常见场景和编码建议
一、存储游戏的配置
很多游戏会根据用户的反馈调整游戏的难度,或者管理员(开发者,肝酱)会根据不同的时期,设定此时游戏的难度。 改变这些设置又不希望重新修改游戏的代码,此时可以使用 KV 数据库中的这两句代码:
写入:
读取:
假设,我们的游戏有三个配置需要存储,分别为 难度、抽卡概率和游戏季节。我们可以设计在用户打开游戏时,自动读取游戏配置并且设置到对应的变量上。 当管理员登录后,可以有界面分别设置这三个配置。 示例代码如下:
定义保存游戏配置的函数
结合 Kontakt API,判断是否为管理员(作者自己),如果是,就允许调用此方法对游戏进行设置:
读取配置
当用户打开游戏时,从云上读取配置,并且设置对应的变量:
二、存储当前用户在游戏中的虚拟币
此场景和上一个场景类似, 不同的时,每个用户会有自己权限下的 KV 数据。例如,我 Shawn,永远不能读取别人,例如 Arkos 的同一个 key 的数据。
为了实现这个功能,我们需要选择的积木是一样的。 不过,在选项中,要选择【当前用户】:
写入:
读取:
以存储当前用户的虚拟币为例,例如结合社区 Kontakt 为例,当用户投币 10 枚时,虚拟币加 1000。
我们可以先定义两个函数,分别是增加虚拟币和消费虚拟币。 流程都一样,首先是获取服务器上的当前虚拟币数存储到变量上,对变量进行增减的计算操作,再把计算结果存储回服务器上。
结合 Kontakt 使用时:
三、双人聊天室和实时对抗游戏
需要使用 Simple MMO 插件。
MMO 的使用是有一些难度的,因此家铭和 Arkos 一起在社区中开发了【模板】来帮助大家快速上手。模板中包含了 三个模块,分别是:匹配系统、消息模块和同步系统。 几乎可以制作所有类型的网游了。模板中结构清晰,直接拖入自己的工程就能使用,而且模板中的注释清晰,我就不在这里赘述了。
后记
有人会问,如果就是要用 Scratch 的云变量,能解决传输效率方面的问题么? 答案是 YES。
如果你去观摩了太空狼人杀 Among Us 等 Scratch 大作时你会发现,他们每次在更新云变量的时候会执行一堆类似这样构建数字串的代码(此代码出自于 Among Us),贴在后面了。
这样的构建,专业的术语叫做【编码】,对应的解开的过程叫做【解码】。 编解码可以简单地理解为开发者协定用一些简单的数字或字母代表不同的含义。编解码做得好的程序,传输效率可以很高,但这对开发者要求甚至比用 KV 数据库和 Simple MMO 更要高。 例如,看看下面这节代码你就 get 了。 如果能做到这样的水平, 使用 云变量当然也能做到不错的效果(但都永远无法和 Simple MMO 媲美,因为技术底层完全不一样了)。
Gandi IDE 和 CCW 在数据方面的计划
看了上面的这段代码后,你就知道,为了让普通人都能实现多人实时在线游戏、以及每个人都能简单易用的数据库,我们必须向前了。以下是我们对云数据方面的规划:
- Gandi IDE 下线云变量和云列表功能。 在教学场景中,依然可以使用这两个功能,但在社区中将不再提供;
- 对已经使用了云变量和云列表的项目,将不会受影响。 数据同步时间为 5 分钟/次;
- 《数据助手》 插件将会代替云变量和云列表;
- 实时性高的数据,将会用《Simple MMO》 提供;
在考虑中的,目前没有实施:
- 对于网络传输设计得比较很好的作品(例如 Among Us),申请下可开白名单
- 对 API 调用次数和网络流量进行统计,不同的等级肝酱可以用到不同等级的网络服务
特别鸣谢
以下肝酱和开发者在我们整个网络数据改造方面帮助了我们很多,特此鸣谢,排名不分先后:
Nick, yk1boy, 软萌软萌d皮卡丘, Oscar, Arkos, bob就是个grasspack, 家铭