|
@@ -0,0 +1,459 @@
|
|
|
+const { tunnel } = require('../qcloud')
|
|
|
+const { mysql } = require('../qcloud')
|
|
|
+
|
|
|
+const option = {
|
|
|
+ MAX_SCORE_GAP: 10000,//匹配的玩家最大分差不能超过10000分
|
|
|
+ MATCH_SPEED: 3000,//匹配的频率:每3秒遍历匹配一次
|
|
|
+ QUESTION_NUMBER: 5,//答题数5个
|
|
|
+ SEND_QUESTIONS_DELAY: 3500,//匹配完成后,间隔3.5S后开始向前端发题
|
|
|
+ SEND_QUESTION_TIME: 16000,//发题的频率:每16秒发送一题
|
|
|
+ PING_PONG_TIME: 6000,//PING-PONG响应的PING发送频率
|
|
|
+ PING_PONG_OUT_TIME: 20000,//PING-PONG响应超时时间
|
|
|
+ MAX_NUMBER_TUNNEL_RESEND: 3,//每个信道允许出现无效信道时重新发送的次数
|
|
|
+}
|
|
|
+const players = {} //用户信息存储对象:openId为key
|
|
|
+const rooms = {}//房间存储对象:房间名为key
|
|
|
+const fightingRecord = {}//每局比赛战绩存储对象:房间名为key,包含:openId_winner,openId_loser,score_winner,score_loser
|
|
|
+const match = {//匹配对象:包含数据和函数
|
|
|
+ queueData: [],
|
|
|
+ init() {
|
|
|
+ let finished = true
|
|
|
+ function match_succ(player1,player2){
|
|
|
+ //创建房间
|
|
|
+ match.createRoom(player1.openId, player2.openId)
|
|
|
+ //队列中删除匹配好的2个玩家
|
|
|
+ tools.deleteQueueOpenId(player1.openId)
|
|
|
+ tools.deleteQueueOpenId(player2.openId)
|
|
|
+ }
|
|
|
+ const loopMatch = setInterval(() => {
|
|
|
+ if (finished) {
|
|
|
+ finished = false
|
|
|
+ for (let index1 = 0; index1 < this.queueData.length; index1++) {
|
|
|
+ let player1 = players[this.queueData[index1]]
|
|
|
+ //排位赛匹配
|
|
|
+ if (player1.friendsFightingRoom === undefined) {
|
|
|
+ for (let index2 = index1; index2 < this.queueData.length; index2++) {
|
|
|
+ let player2 = players[this.queueData[index2]]
|
|
|
+ if (player2.friendsFightingRoom === undefined && player2.sortId === player1.sortId && Math.abs(player2.score - player1.score) < option.MAX_SCORE_GAP && player2.openId !== player1.openId) {
|
|
|
+ match_succ(player1,player2)
|
|
|
+ //结束该player1的匹配
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //好友匹配
|
|
|
+ if (player1.friendsFightingRoom !== undefined && player1.friendsFightingRoom !== null) {
|
|
|
+ for (let index2 = index1; index2 < this.queueData.length; index2++) {
|
|
|
+ let player2 = players[this.queueData[index2]]
|
|
|
+ if (player2.friendsFightingRoom !== undefined && player2.friendsFightingRoom !== null && player2.friendsFightingRoom === player1.friendsFightingRoom && player2.openId !== player1.openId) {
|
|
|
+ this.match_succ(player1,player2)
|
|
|
+ //结束该player1的匹配
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ finished = true
|
|
|
+ }
|
|
|
+ }, option.MATCH_SPEED)
|
|
|
+ },
|
|
|
+ createRoom(openId1, openId2) {
|
|
|
+ let roomName = new Date().getTime().toString() + parseInt(Math.random() * 10000000)//创建时间+随机数
|
|
|
+ rooms[roomName] = {
|
|
|
+ roomName,
|
|
|
+ player1: openId1,
|
|
|
+ player2: openId2,
|
|
|
+ library: null,
|
|
|
+ responseNumber: 0,//收到的响应次数
|
|
|
+ finished: false,//是否完成了答题
|
|
|
+ }
|
|
|
+ players[openId1].roomName = roomName
|
|
|
+ players[openId2].roomName = roomName
|
|
|
+ console.info('创建后的总房间和玩家为:', rooms, players)
|
|
|
+ //library,默认包含5道题目
|
|
|
+ mysql('question_detail').where((players[openId1].sortId == 1) ? {} : { sort_id: players[openId1].sortId }).select('*').orderByRaw('RAND()').limit(option.QUESTION_NUMBER).then(res => {
|
|
|
+ rooms[roomName].library = res //将查询到的题目存到房间的题库library中
|
|
|
+ //房间创建完成后通知前端匹配完成
|
|
|
+ tools.broadcast([players[openId1].tunnelId, players[openId2].tunnelId], 'matchNotice', {
|
|
|
+ 'player1': {
|
|
|
+ openId: openId1,
|
|
|
+ nickName: players[openId1].nickName,
|
|
|
+ avatarUrl: players[openId1].avatarUrl,
|
|
|
+ roomName,
|
|
|
+ },
|
|
|
+ 'player2': {
|
|
|
+ openId: openId2,
|
|
|
+ nickName: players[openId2].nickName,
|
|
|
+ avatarUrl: players[openId2].avatarUrl,
|
|
|
+ roomName,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ //向客户端发题
|
|
|
+ tools.sendQuestionMain(roomName)
|
|
|
+ },error=>{
|
|
|
+ console.log(error)
|
|
|
+ })
|
|
|
+ },
|
|
|
+}
|
|
|
+match.init()
|
|
|
+const tools = {//工具对象,包含常用数据和函数
|
|
|
+ data: {
|
|
|
+ timerSendQuestion: [],//每个房间的发题定时器
|
|
|
+ numberTunnelResend: [],//key为信道ID,记录每个信道重复发送的次数
|
|
|
+ },
|
|
|
+ //广播到指定信道
|
|
|
+ broadcast(tunnelIdsArray, type, content) {
|
|
|
+ tunnel.broadcast(tunnelIdsArray, type, content)
|
|
|
+ .then(result => {
|
|
|
+ const invalidTunnelIds = result.data && result.data.invalidTunnelIds || []
|
|
|
+ if (invalidTunnelIds.length) {
|
|
|
+ console.error('======检测到无效的信道IDs======', invalidTunnelIds)
|
|
|
+ invalidTunnelIds.forEach(tunnelId => {
|
|
|
+ let number = this.data.numberTunnelResend[tunnelId] ? this.data.numberTunnelResend[tunnelId] : 0
|
|
|
+ if (number < option.MAX_NUMBER_TUNNEL_RESEND) {//当发送次数不大于规定次数时,可以进行重发
|
|
|
+ let timer = setTimeout(() => {//1.5S后重发一次数据
|
|
|
+ this.data.numberTunnelResend[tunnelId] = ++number
|
|
|
+ tools.broadcast([tunnelId], type, content)
|
|
|
+ clearTimeout(timer)
|
|
|
+ }, 2000)
|
|
|
+ } else {
|
|
|
+ console.log('当前的numberTunnelResend长度为:', Object.keys(this.data.numberTunnelResend).length)
|
|
|
+ if (Object.keys(this.data.numberTunnelResend).length > 20) { //当累积未清理掉的无效信道达到20个时,清空一次数组
|
|
|
+ this.data.numberTunnelResend = []
|
|
|
+ console.info('清空后的numberTunnelResend为', this.data.numberTunnelResend)
|
|
|
+ } else {
|
|
|
+ this.clsTimeout(tunnelId, this.data.numberTunnelResend)
|
|
|
+ console.info('删除后的numberTunnelResend为', this.data.numberTunnelResend)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, error => {
|
|
|
+ console.log(error)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ //关闭指定信道
|
|
|
+ closeTunnel(tunnelId) {
|
|
|
+ console.info('开始关闭信道:' + tunnelId)
|
|
|
+ tunnel.closeTunnel(tunnelId)
|
|
|
+ let openId = this.getPlayersOpenId(tunnelId)
|
|
|
+ if (players[openId]) {
|
|
|
+ if (players[openId].roomName) {
|
|
|
+ if (rooms[players[openId].roomName]) {
|
|
|
+ if (!rooms[players[openId].roomName].finished) {//如果用户存在房号,则说明是战斗状态断线,视为逃跑
|
|
|
+ tools.runAway(openId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (players[openId]) {
|
|
|
+ if (players[openId].roomName) {
|
|
|
+ this.clsTimeout(players[openId].roomName, tools.data.timerSendQuestion)//清除发题定时器
|
|
|
+ delete rooms[players[openId].roomName]//删除房间
|
|
|
+ }
|
|
|
+ this.deleteQueueOpenId(openId)//删除匹配队列中的openId
|
|
|
+ delete players[openId]//删除玩家信息
|
|
|
+ console.info('信道关闭后的队列、玩家、房间和发题定时器为:', match.queueData, players, rooms, tools.data.timerSendQuestion)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ //根据信道ID获取openId
|
|
|
+ getPlayersOpenId(tunnelId) {
|
|
|
+ for (let index in players) {
|
|
|
+ if (players[index].tunnelId === tunnelId) {
|
|
|
+ return index
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null
|
|
|
+ },
|
|
|
+ //删除匹配队列中的指定openId
|
|
|
+ deleteQueueOpenId(openId) {
|
|
|
+ let index = match.queueData.indexOf(openId)
|
|
|
+ if (~index) {
|
|
|
+ match.queueData.splice(index, 1)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ //清除数组中延时定时器
|
|
|
+ clsTimeout(index, arr) {
|
|
|
+ clearTimeout(arr[index])
|
|
|
+ delete arr[index]
|
|
|
+ },
|
|
|
+ //主控发题函数
|
|
|
+ sendQuestionMain(roomName) {
|
|
|
+ let sendQuestionsDelay = setTimeout(() => {
|
|
|
+ this.sendQuestion(roomName)
|
|
|
+ clearTimeout(sendQuestionsDelay)
|
|
|
+ }, option.SEND_QUESTIONS_DELAY)
|
|
|
+ },
|
|
|
+ //发题函数
|
|
|
+ sendQuestion(roomName) {
|
|
|
+ try {
|
|
|
+ let openId1 = rooms[roomName].player1
|
|
|
+ let openId2 = rooms[roomName].player2
|
|
|
+ this.clsTimeout(roomName, this.data.timerSendQuestion)
|
|
|
+ tools.broadcast([players[openId1].tunnelId, players[openId2].tunnelId], 'sendQuestion', {
|
|
|
+ question: rooms[roomName].library[0] ? rooms[roomName].library[0] : {},
|
|
|
+ //choice = []//[openID:'',userChoose: '',//用户选择了第几个答案 answerColor: '',//用户是否答对 scoreMyself: 0,//用户总得分]
|
|
|
+ choicePlayer1: players[openId1].choice,
|
|
|
+ choicePlayer2: players[openId2].choice
|
|
|
+ })
|
|
|
+ console.info('已经向客户端发送一题')
|
|
|
+ this.data.timerSendQuestion[roomName] = setTimeout(() => {
|
|
|
+ this.sendQuestion(roomName)
|
|
|
+ }, option.SEND_QUESTION_TIME)
|
|
|
+ //当发送完{}时,清除掉定时器
|
|
|
+ if (rooms[roomName].library[0] ? false : true) {
|
|
|
+ this.clsTimeout(roomName, this.data.timerSendQuestion)
|
|
|
+ }
|
|
|
+ rooms[roomName].library.shift() //发送一个,原始题库就删除一个
|
|
|
+ rooms[roomName].responseNumber = 0 //初始化房间响应次数
|
|
|
+ players[openId1].choice[1] = ''//初始化用户选择状态:第几个答案
|
|
|
+ players[openId1].choice[2] = ''//初始化用户选择状态:是否答对
|
|
|
+ players[openId2].choice[1] = ''//初始化用户选择状态:第几个答案
|
|
|
+ players[openId2].choice[2] = ''//初始化用户选择状态:是否答对
|
|
|
+ } catch (error) {
|
|
|
+ console.error('错误:' + error)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ //更新得分
|
|
|
+ updateScore(openId, fightingResult) {
|
|
|
+ mysql('cSessionInfo').where({ open_id: openId }).select('score').then(res => {//获取原始得分
|
|
|
+ let score = res[0].score
|
|
|
+ if (fightingResult === 1) {
|
|
|
+ score = score + 10
|
|
|
+ } else if (fightingResult === 0) {
|
|
|
+ score = score - 10
|
|
|
+ if (score < 0) {
|
|
|
+ score = 0
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mysql('cSessionInfo').where({ open_id: openId }).update('score', score).then(res => {
|
|
|
+ console.info(openId + '得分已更新:' + score)
|
|
|
+ }, error => {
|
|
|
+ //201803010019:此处添加数据库操作失败报错
|
|
|
+ console.log(error)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ //逃跑处理函数:
|
|
|
+ runAway(openId) {
|
|
|
+ //获取逃跑者和胜利者的openId
|
|
|
+ console.info('开始执行逃跑函数')
|
|
|
+ let openIdFail = openId, openIdWin
|
|
|
+ let room = rooms[players[openIdFail].roomName]
|
|
|
+ if (openIdFail === room.player1) {
|
|
|
+ openIdWin = room.player2
|
|
|
+ } else {
|
|
|
+ openIdWin = room.player1
|
|
|
+ }
|
|
|
+ //更新得分
|
|
|
+ this.updateScore(openIdWin, 1)
|
|
|
+ this.updateScore(openIdFail, 0)
|
|
|
+ //存储比赛结果
|
|
|
+ this.storeFightingRecord(openIdWin, 1, true)
|
|
|
+ this.storeFightingRecord(openIdFail, 0, true)
|
|
|
+ //通知赢家对方已逃跑
|
|
|
+ if (players[openId]) {
|
|
|
+ if (players[openId].roomName) {
|
|
|
+ rooms[players[openId].roomName].finished = true//先改变房间状态,再关闭,避免被认为逃跑行为
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.broadcast([players[openIdWin].tunnelId], 'runawayNotice', {
|
|
|
+ message: '对手已逃跑'
|
|
|
+ })
|
|
|
+ //有时出现无效信道ID,导致onclose无法删除用户信息,这里手工补充删除
|
|
|
+ delete players[openId]//删除玩家信息
|
|
|
+ },
|
|
|
+ //保存战绩函数:
|
|
|
+ //fightingResult: 0:表示输,1:表示赢,2:表示平手
|
|
|
+ //runAway:true/false
|
|
|
+ async storeFightingRecord(openId, fightingResult, runAway = false) {
|
|
|
+ let roomName = players[openId].roomName
|
|
|
+ try {
|
|
|
+ if (fightingRecord[roomName] ? false : true) {
|
|
|
+ fightingRecord[roomName] = {
|
|
|
+ openId_winner: '',
|
|
|
+ openId_loser: '',
|
|
|
+ score_winner: 0,
|
|
|
+ score_loser: 0,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let myRecord = fightingRecord[roomName]
|
|
|
+ //获取双方比赛数据
|
|
|
+ if (fightingResult == 0) {//0为输
|
|
|
+ myRecord.openId_loser = openId
|
|
|
+ myRecord.score_loser = players[openId].choice[3]
|
|
|
+ } else {
|
|
|
+ myRecord.openId_winner = openId
|
|
|
+ myRecord.score_winner = players[openId].choice[3]
|
|
|
+ }
|
|
|
+ //当获取到双方的数据时,开始存储到数据库
|
|
|
+ if (myRecord.openId_winner && myRecord.openId_loser) {
|
|
|
+ let room_name = roomName,
|
|
|
+ run_away = runAway,
|
|
|
+ open_id_winner = myRecord.openId_winner,
|
|
|
+ open_id_loser = myRecord.openId_loser,
|
|
|
+ score_winner = myRecord.score_winner,
|
|
|
+ score_loser = myRecord.score_loser
|
|
|
+ delete fightingRecord[room_name]
|
|
|
+ try{
|
|
|
+ await mysql('fighting_record').insert({ id: null, room_name, run_away, open_id_winner, open_id_loser, score_winner, score_loser, time: null })
|
|
|
+ }catch(error){
|
|
|
+ console.log(error)
|
|
|
+ }
|
|
|
+ console.log('清空后的fightingRecord为', fightingRecord)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 实现 onConnect 方法
|
|
|
+ * 在客户端成功连接 WebSocket 信道服务之后会调用该方法,
|
|
|
+ */
|
|
|
+function onConnect(tunnelId) {
|
|
|
+ tools.broadcast([tunnelId], 'tunnelIdReplaced', {
|
|
|
+ newTunnelId: tunnelId
|
|
|
+ })
|
|
|
+ //PING-PONG机制:发送PING
|
|
|
+ clearTimeout(players[tools.getPlayersOpenId(tunnelId)].timer)
|
|
|
+ tools.broadcast([tunnelId], 'PING', {})
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 实现 onClose 方法
|
|
|
+ * 客户端关闭 WebSocket 信道或者被信道服务器判断为已断开后,
|
|
|
+ * 会调用该方法,此时可以进行清理及通知操作
|
|
|
+ */
|
|
|
+function onClose(tunnelId) {
|
|
|
+ console.info('onClose监听到信道' + tunnelId + '关闭')
|
|
|
+ tools.closeTunnel(tunnelId)
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 实现 onMessage 方法
|
|
|
+ * 客户端推送消息到 WebSocket 信道服务器上后,会调用该方法,此时可以处理信道的消息。
|
|
|
+ */
|
|
|
+function onMessage(tunnelId, type, content) {
|
|
|
+ console.info('onMessage监听到新消息:', { tunnelId, type, content })
|
|
|
+ // if (!(tunnelId in players)) {
|
|
|
+ // tools.closeTunnel(tunnelId)
|
|
|
+ // }
|
|
|
+ switch (type) {
|
|
|
+ case 'PONG': //PING-PONG机制:监听PONG
|
|
|
+ if (tunnelId) {
|
|
|
+ let openId = content.openId
|
|
|
+ clearTimeout(players[openId].timer)//清除掉定时器
|
|
|
+
|
|
|
+ let timer = setTimeout(() => {
|
|
|
+ if (players[openId]) {
|
|
|
+ //再次设置一个定时器
|
|
|
+ players[openId].timer = setTimeout(() => {//ping-pong机制:监听客户端是否离线
|
|
|
+ console.log('开始执行PING-PONG定时器函数')
|
|
|
+ tools.closeTunnel(tunnelId)//如果离线,则清空用户信息
|
|
|
+ }, option.PING_PONG_OUT_TIME)
|
|
|
+ //再次发送一个PING
|
|
|
+ tools.broadcast([tunnelId], 'PING', {})
|
|
|
+ console.log(tunnelId + '发送一个PING')
|
|
|
+ clearTimeout(timer)
|
|
|
+ }
|
|
|
+ }, option.PING_PONG_TIME)
|
|
|
+ }
|
|
|
+ break
|
|
|
+
|
|
|
+ case 'updateMatchInfo':
|
|
|
+ if (tunnelId) {
|
|
|
+ let openId = content.openId
|
|
|
+ players[openId].sortId = content.sortId
|
|
|
+ players[openId].friendsFightingRoom = content.friendsFightingRoom
|
|
|
+ console.info('更新用户匹配条件信息:', players[openId])
|
|
|
+ }
|
|
|
+ break
|
|
|
+
|
|
|
+ case 'answer':
|
|
|
+ if (tunnelId) {
|
|
|
+ tools.broadcast([tunnelId], 'getAnswer', {})//通知前端,后台已收到选项
|
|
|
+ console.info('收到了' + players[content.choice.openId].nickName + '(' + tunnelId + ')的信息')
|
|
|
+ let roomName = content.roomName
|
|
|
+ let openId = content.choice.openId
|
|
|
+ rooms[roomName].responseNumber = rooms[roomName].responseNumber + 1//房间获得了1次响应
|
|
|
+ players[openId].choice[1] = content.choice.userChoose
|
|
|
+ players[openId].choice[2] = content.choice.answerColor
|
|
|
+ players[openId].choice[3] = content.choice.scoreMyself
|
|
|
+ if (rooms[roomName].responseNumber === 2) {
|
|
|
+ //当两位玩家都完成答题时,立刻向客户端发送下一题
|
|
|
+ tools.sendQuestion(roomName)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break
|
|
|
+
|
|
|
+ case 'fightingResult': //用户答完题监听
|
|
|
+ if (tunnelId) {
|
|
|
+ console.info('进入fightingResult阶段')
|
|
|
+ const fightingResult = content.fightingResult
|
|
|
+ const openId = content.openId
|
|
|
+ tools.updateScore(openId, fightingResult)//更新分数
|
|
|
+ tools.storeFightingRecord(openId, fightingResult) //存储比赛详情数据
|
|
|
+ if (rooms[players[openId].roomName]) {
|
|
|
+ rooms[players[openId].roomName].finished = true//先改变房间状态,再关闭,避免被认为逃跑行为
|
|
|
+ }
|
|
|
+ tools.closeTunnel(tunnelId)//关闭信道连接
|
|
|
+ console.info('战斗完成后的队列、玩家、房间和发题定时器为:', match.queueData, players, rooms, tools.data.timerSendQuestion)
|
|
|
+ }
|
|
|
+ break
|
|
|
+
|
|
|
+ default:
|
|
|
+ break
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+module.exports = {
|
|
|
+ get: async ctx => {//响应用户开始进行websocket连接,信道服务器连接成功后通知客户端
|
|
|
+ //data:{tunnel:{tunnelId:xxx,connectUrl:xxx},userinfo:{openId:xxx.nickName:xxx,...}}
|
|
|
+ let data = await tunnel.getTunnelUrl(ctx.req)//当用户发起信道请求的时候,会得到信道信息和用户信息
|
|
|
+ let userinfo = data.userinfo
|
|
|
+ let openId = userinfo.openId
|
|
|
+ if (openId in players) {//如果已经存在openId,则说明只需更新信道ID
|
|
|
+ players[openId].tunnelId = data.tunnel.tunnelId
|
|
|
+ console.info('信道变化后的队列、玩家、房间和发题定时器为:', match.queueData, players, rooms, tools.data.timerSendQuestion)
|
|
|
+ } else {
|
|
|
+ let score = await mysql('cSessionInfo').where({ open_id: data.userinfo.openId }).select('score')//[{score:4324}]
|
|
|
+ userinfo.score = score[0].score //number,在用户信息中加入得分
|
|
|
+ userinfo.tunnelId = data.tunnel.tunnelId //在用户信息中加入tunnel_id属性
|
|
|
+ userinfo.matchTime = new Date().getTime() //在用户信息中加入匹配的时间,如:1513670126897
|
|
|
+ userinfo.roomName = null//在用户信息中加入当前战斗状态,默认为null,否则为对战房间号
|
|
|
+ userinfo.friendsFightingRoom = null//初始值为null,匹配前会复制赋值为undefined或一个数字判断是排位赛还是好友匹配
|
|
|
+ userinfo.sortId = null
|
|
|
+ userinfo.choice = [openId, '', '', 0]//[openID:'',user_choose: '',//用户选择了第几个答案 answer_color: '',//用户是否答对 score_myself: '',//用户总得分]
|
|
|
+ userinfo.timer = setTimeout(() => {//ping-pong机制:监听客户端是否离线
|
|
|
+ //tools.closeTunnel(data.tunnel.tunnelId)//如果离线,则清空用户信息//暂时先取消此处定时器
|
|
|
+ }, option.PING_PONG_OUT_TIME)
|
|
|
+ players[openId] = userinfo //将此用户作为合法用户,并将openId和用户信息(不包含得分,状态等数据)关联起来
|
|
|
+ console.info('新信道加入后的队列、玩家、房间和发题定时器为:', match.queueData, players, rooms, tools.data.timerSendQuestion)
|
|
|
+
|
|
|
+ //在匹配队列中压入一个openId,外套一个队列限制函数
|
|
|
+ match.queueData.push(openId)
|
|
|
+
|
|
|
+ }
|
|
|
+ ctx.state.data = data.tunnel //返回信道信息给用户
|
|
|
+ },
|
|
|
+
|
|
|
+ post: async ctx => {//用来处理信道传递过来的消息
|
|
|
+ const packet = await tunnel.onTunnelMessage(ctx.request.body) //onTunnelMessage:当用户消息发送到信道上时,使用该函数处理信道的消息
|
|
|
+ switch (packet.type) {
|
|
|
+ case 'connect': //用户开始进行websocket连接,信道服务器连接成功后通知服务端
|
|
|
+ onConnect(packet.tunnelId)
|
|
|
+ break
|
|
|
+ case 'message':
|
|
|
+ onMessage(packet.tunnelId, packet.content.messageType, packet.content.messageContent)
|
|
|
+ break
|
|
|
+ case 'close':
|
|
|
+ onClose(packet.tunnelId)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|