Django Socket机制¶
后端¶
websocket模块引入
from channels.generic.websocket import AsyncWebsocketConsumer
import json
from django.conf import settings
from django.core.cache import cache
# 函数编辑
class MultiPlayer(AsyncWebsocketConsumer):
# 创建连接
async def connect(self):
await self.accept()
print('accept')
# 断开连接 如果需要维护在线人数时 此函数不靠谱
async def disconnect(self, close_code):
print('disconnect')
await self.channel_layer.group_discard(self.room_name, self.channel_name)
# 以下群发事件包括创建角色、移动、发射炮弹、被攻击、技能闪现、聊天窗同步
async def create_player(self, data):
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
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)
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_send_event",
'event': "create_player",
'uuid': data['uuid'],
'username': data['username'],
'photo': data['photo'],
}
)
async def move_to(self, data):
await self.channel_layer.group_send(
self.room_name,
{
'type': "group_send_event",
'event': "move_to",
'uuid': data['uuid'],
'tx': data['tx'],
'ty': data['ty'],
}
)
async def shoot_fireball(self, data):
await self.channel_layer.group_send(
self.room_name,
{
'type': "group_send_event",
'event': "shoot_fireball",
'uuid': data['uuid'],
'tx': data['tx'],
'ty': data['ty'],
'ball_uuid': data['ball_uuid'],
}
)
async def attack(self, data):
await self.channel_layer.group_send(
self.room_name,
{
'type': "group_send_event",
'event': "attack",
'uuid': data['uuid'],
'attackee_uuid': data['attackee_uuid'],
'x': data['x'],
'y': data['y'],
'angle': data['angle'],
'damage': data['damage'],
'ball_uuid': data['ball_uuid'],
}
)
async def blink(self, data):
await self.channel_layer.group_send(
self.room_name,
{
'type': "group_send_event",
'event': "blink",
'uuid': data['uuid'],
'tx': data['tx'],
'ty': data['ty'],
}
)
async def message(self, data):
await self.channel_layer.group_send(
self.room_name,
{
'type': "group_send_event",
'event': "message",
'uuid': data['uuid'],
'username': data['username'],
'text': data['text'],
}
)
# 此类名需要与上面type名相同, 群发事件到前端每个窗口
async def group_send_event(self, data):
await self.send(text_data=json.dumps(data))
# 接受前端向后端发送的请求,然后定向到相关函数
async def receive(self, text_data):
data = json.loads(text_data)
event = data['event']
if event == "create_player":
await self.create_player(data)
elif event == "move_to":
await self.move_to(data)
elif event == "shoot_fireball":
await self.shoot_fireball(data)
elif event == "attack":
await self.attack(data)
elif event == "blink":
await self.blink(data)
elif event == "message":
await self.message(data)
前端¶
利用js的interface WebSocket接口,实现发送与接受消息
class MultiPlayerSocket {
constructor(playground) {
this.playground = playground;
this.ws = new WebSocket("wss://app5806.acapp.acwing.com.cn/wss/multiplayer/");
this.start();
}
start() {
this.receive();
}
// 接受后端的请求, 定向到相关事件
receive() {
let outer = this;
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);
}
else if (event === "move_to") {
outer.receive_move_to(uuid, data.tx, data.ty);
}
else if (event === "shoot_fireball") {
outer.receive_shoot_fireball(uuid, data.tx, data.ty, data.ball_uuid);
}
else if (event === "attack") {
outer.receive_attack(uuid, data.attackee_uuid, data.x, data.y, data.angle, data.damage, data.ball_uuid)
}
else if (event === "blink") {
outer.receive_blink(uuid, data.tx, data.ty);
}
else if (event === "message") {
outer.receive_message(uuid, data.username, data.text);
}
};
}
send_create_player(username, photo) {
let outer = this;
/* 向后端发送消息 */
this.ws.send(JSON.stringify({
'event': "create_player",
'uuid': outer.uuid,
'username': username,
'photo': photo,
}));
}
receive_create_player(uuid, username, photo) {
let player = new Player(
this.playground,
this.playground.width / 2 / this.playground.scale,
this.playground.height / 2 / this.playground.scale,
this.playground.height * 0.05 / this.playground.scale,
this.playground.get_random_color(),
this.playground.height * 0.3 / this.playground.scale,
"enemy",
username,
photo,
);
player.uuid = uuid;
this.playground.players.push(player);
}
get_player(uuid) {
let players = this.playground.players;
for (let i = 0; i < players.length; i++) {
let player = players[i];
if (player.uuid === uuid)
return player;
}
return null;
}
send_move_to(tx, ty) {
let outer = this;
/* 向后端发送消息 */
this.ws.send(JSON.stringify({
'event': "move_to",
'uuid': outer.uuid,
'tx': tx,
'ty': ty,
}));
}
receive_move_to(uuid, tx, ty) {
let player = this.get_player(uuid);
if (player) {
player.move_to(tx, ty);
}
}
send_shoot_fireball(tx, ty, ball_uuid) {
let outer = this;
/* 向后端发送消息 */
this.ws.send(JSON.stringify({
'event': "shoot_fireball",
'uuid': outer.uuid,
'tx': tx,
'ty': ty,
'ball_uuid': ball_uuid,
}));
}
receive_shoot_fireball(uuid, tx, ty, ball_uuid) {
let player = this.get_player(uuid);
if (player) {
let fireball = player.shoot_fireball(tx, ty);
fireball.uuid = ball_uuid;
}
}
send_attack(attackee_uuid, x, y, angle, damage, ball_uuid) {
let outer = this;
/* 向后端发送消息 */
this.ws.send(JSON.stringify({
'event': "attack",
'uuid': outer.uuid,
'attackee_uuid': attackee_uuid,
'x': x,
'y': y,
'angle': angle,
'damage': damage,
'ball_uuid': ball_uuid,
}));
}
receive_attack(uuid, attackee_uuid, x, y, angle, damage, ball_uuid) {
let attacker = this.get_player(uuid);
let attackee = this.get_player(attackee_uuid);
if (attacker && attackee) {
attackee.receive_attack(x, y, angle, damage, ball_uuid, attacker);
}
}
send_blink(tx, ty) {
let outer = this;
this.ws.send(JSON.stringify({
'event': "blink",
'uuid': outer.uuid,
'tx': tx,
'ty': ty,
}));
}
receive_blink(uuid, tx, ty) {
let player = this.get_player(uuid);
if(player) {
player.blink(tx, ty);
}
}
send_message(username, text) {
let outer = this;
this.ws.send(JSON.stringify({
'event': "message",
'uuid': outer.uuid,
'username': username,
'text': text,
}));
}
receive_message(uuid, username, text) {
this.playground.chat_field.add_message(username, text);
}
}