6

Scratch3技术分析之云变量 API(第7篇)

 2 years ago
source link: http://wwj718.github.io/post/%E5%B0%91%E5%84%BF%E7%BC%96%E7%A8%8B/scratch3-cloud-var/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

关于云变量

我们先来看下云变量的样子:

Scratch团队在FAQ里解释了

什么是云变量

云变量可以让作品里保存的数据与Scratch社区的其他人所共享。你可以利用云变量发布调查或其他作品,而社区中的其他人可以访问和修改这些数据。

谁能看见云变量中存放的数据?

当你运行一个用了云变量的作品时,你使用过程中产生的数据会存放在你的用户名下,其他人可以看到这些数据。

云变量可以存放什么类型的数据?

云变量只能存放数字。

我能使用云变量创建聊天室吗?

不可以。虽然在技术上是可能的,但Scratch网站不允许这样做。

谁可以改变云变量的内容?

只有你和项目的查看者才能将数据存储在项目的云变量中。 如果人们进入项目内部源码或重新混合(remix)你的代码,将会创建变量的副本,而不会影响或更改原始变量。

可以用云变量创作多人游戏吗?

由于网速和同步的问题,创作多人游戏比较困难。但仍然有一些Scratcher别出心载,使用云变量制作回合制游戏以及其他类型的游戏。

云变量会在后台产生日志。每个项目最多有10个云变量。

Scratch新用户可能无法使用云数据。Scratch团队不希望Scratch新手滥用云变量,因为它可能会给系统带来很大的负担。

云变量会自动更新,利用这个特性,可以建立联机游戏和聊天室,但官方不希望有高频交互,这将带来服务器压力。此外,实时性也不能保证,如果你希望建立实时的强连接,参考codelab-adapter的虫洞(wormhole)


说了半天,你可以看一个带有云变量的项目Google Chrome Dino Run 2 remix

解释完了云变量,我们进入技术分析部分,我们试图回答: 云变量是怎么实现的,并给出简单的实现例子。

和之前的分析一样,借助Chrome DevTools,我们来观察创建和使用云变量的过程中都发生了什么。

注意: 你的账号不能是新注册的,否则你没有创建云变量的权限。

创建我们的第一个云变量: test_cloud_var

来看看请求细节(Headers)

Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的。websocket中似乎没有处理用户凭证相关的问题。

接着看看Frames:

向上的箭头表示的数据流向为 client(scratch-gui)-> server 向下的箭头表示的数据流向为 server-> client

创建变量的过程非常简单:

  • 建立websocket通道
  • 在websocket通道中来回传送json。采用类似json-rpc的交互方式。

后端的云变量服务地址位于wss://clouddata.scratch.mit.edu/,通过这个线索,我们可以找到与云变量相关的前端源码, 源码非常简单。

值得注意的是,设置变量的过程,数据从前端发往服务端,服务端不做响应。

项目中存在云变量时,我们重新打开这个项目,默认将拉去云变量中存储的的值

删除云变量

前端源码中似乎没看到权限相关的部分。

明天有空做个实验,试试未登录状态是否能与云变量服务进行通信。

var ws = new WebSocket('wss://clouddata.scratch.mit.edu/');
ws.onopen = function(evt) { 
  var data = {"method":"handshake","user":"wwj718","project_id":"291228938"}
  ws.send(JSON.stringify(data)+"\n");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
};    

实验发现只有在登陆情况下,才能建立websocket连接。websocket在建立连接的时候会携带cookie,这就是实现用户身份验证的关键。

实现一个云变量服务

下边我们给出兼容Scratch开源前端的云变量后端实现。这个实现只作为原理展示,如果你要用于生产环境,需要做些调整。

该实现基于Python的websockets

import json
import asyncio
import websockets
# json-rpc

def handshake(req_json):
    # 返回项目中已有的云变量
    project_id = req_json.get("project_id")
    result = {"method":"set","project_id":project_id,"name":"☁ test","value":"0"}
    return result

def create(req_json):
    # {"method":"create","user":"wwj718","project_id":"291229535","name":"☁ test1","value":0}
    name = req_json.get("name")
    return {"method": "ack", "name": name, "reply": "OK"}

def set(req_json):
    # {"method":"set","user":"wwj718","project_id":"291229535","name":"☁ test1","value":"0"}
    return None

def delete(req_json):
    # {"method":"delete","user":"wwj718","project_id":"291229535","name":"☁ test1"}
    return None

method_map = {}
method_map["handshake"] = handshake
method_map["create"] = create
method_map["set"] = set
method_map["delete"] = delete

async def handle_cloud_data(websocket, path):
    async for message in websocket:
        # from IPython import embed;embed()
        # message结尾有换行符\n, 流
        req_json = json.loads(message.strip())
        print("request data:",req_json)
        method = req_json.get("method")
        handle = method_map.get(method)
        if handle:
            result = handle(req_json)
        await websocket.send(json.dumps(result))


asyncio.get_event_loop().run_until_complete(
    websockets.serve(handle_cloud_data, '0.0.0.0', 8765))
print("server is running")
asyncio.get_event_loop().run_forever()

测试代码与scratch-gui中的代码基本一致:

var ws = new WebSocket('ws://127.0.0.1:8765');
ws.onopen = function(evt) { 
  var data = {"method":"handshake","user":"wwj718","project_id":"291228938"}
  ws.send(JSON.stringify(data)+"\n");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
};    

// var data = {"method":"set","user":"wwj718","project_id":"291228938","name":"☁ test1","value":"1"}
// ws.send(JSON.stringify(data)+"\n")

如果你要用于生产环境,还需要处理cookie和多个客户端一同读写云变量的问题(考虑到读写频率,推荐使用redis, 如果你的后端也用python异步api实现,推荐使用aioredis)。

websocket测试工具

Socket Wrench

其他值得留意的问题

throttle 10:

Send a message to the cloud server at a rate of no more than 10 messages/sec

this.sendCloudData = throttle(this._sendCloudData, 100);


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK