我觉得这次联机对战课程中,最难理解的部分就是Client端和后端WebSocket如何进行通信,以达到将一局对战中的信息传递到每个用户Client端中。
要想理解y总写的这部分代码,首先我们需要了解 关于WebSocket的两个事件。
this.mps.ws.onopen = function() {
outer.mps.send_create_player(outer.root.settings.username, outer.root.settings.photo);
};
this.ws.onmessage = function(e) {
let data = JSON.parse(e.data);
let uuid = data.uuid;
if (uuid === outer.uuid) return false;
let event = data.event;
if (event === "create_player") {
outer.receive_create_player(uuid, data.username, data.photo);
}
};
正如上述代码中编写的,是Client端给两个事件绑定响应函数,onopen事件是Client端成功与后端WebSocket建立起连接后,Client端所响应的事件,onmessage事件是后端WebSocket发消息给Client端后,Client端所响应的事件。
接着我们继续分析整个通信过程是如何进行的,通信最开始是this.mps = new MultiPlayerSocket(this);
,在MultiPlayerSocket类的构造函数中执行this.ws = new WebSocket("wss://app165.acapp.acwing.com.cn/wss/multiplayer/");
开始与后端WebSocket建立起连接,然后在后端WebSocket中执行
async def connect(self):
self.room_name = None
for i in range(1000):
name = "room-%d" % (i)
if not cache.has_key(name) or len(cache.get(name)) < settings.ROOM_CAPACITY:
self.room_name = name
break
if not self.room_name:
return
await self.accept()
if not cache.has_key(self.room_name):
cache.set(self.room_name, [], 3600) # 有效期1小时
for player in cache.get(self.room_name):
await self.send(text_data=json.dumps({
'event': "create_player",
'uuid': player['uuid'],
'username': player['username'],
'photo': player['photo'],
}))
await self.channel_layer.group_add(self.room_name, self.channel_name)
该函数首先创建or获取未满房间,然后将房间中的用户信息发送给Client端,接着把本次连接加入到以房间名创建的组中。执行结束后,Client端先执行成功建立连接事件的响应函数
send_create_player(username, photo) {
let outer = this;
this.ws.send(JSON.stringify({
'event': "create_player",
'uuid': outer.uuid,
'username': username,
'photo': photo,
}));
}
该函数向后端WebSocket发送本地用户的信息,后端接收到后,执行下述函数
async def receive(self, text_data):
data = json.loads(text_data)
event = data['event']
if event == "create_player":
await self.create_player(data)
通过判断事件类型,执行不同的函数,这里是执行create_player函数
async def create_player(self, data):
players = cache.get(self.room_name)
players.append({
'uuid': data['uuid'],
'username': data['username'],
'photo': data['photo']
})
cache.set(self.room_name, players, 3600) # 有效期1小时
await self.channel_layer.group_send(
self.room_name,
{
'type': "group_create_player",
'event': "create_player",
'uuid': data['uuid'],
'username': data['username'],
'photo': data['photo'],
}
)
create_player函数将本机Client用户加入房间,然后把用户信息等群发给在同组的所有WebSocket连接,然后同组的WebSocket连接执行group_create_player函数。
async def group_create_player(self, data):
await self.send(text_data=json.dumps(data))
该函数将信息传回给Client端,Client端执行onmessage事件响应函数,然后执行receive_create_player函数
receive_create_player(uuid, username, photo) {
let player = new Player(
this.playground,
this.playground.width / 2 / this.playground.scale,
0.5,
0.05,
"white",
0.15,
"enemy",
username,
photo,
);
player.uuid = uuid;
this.playground.players.push(player);
}
该函数在客户端本地画一个玩家,这样基本上就是一个与后端WebSocket进行通信的流程中,所会发生的一些事情了,剩下的将其余玩家画在本地也是通过onmessage事件响应函数所产生的。