From 37dc65d4e0b37a01d5993c01579aeb79af68c229 Mon Sep 17 00:00:00 2001 From: deardrops Date: Sun, 7 May 2017 23:23:08 +0800 Subject: [PATCH 1/4] build new reconnect method 1. check if clientId in battle 2. if in battle, send `reconnectContent` to client 3. after client rebuild battle, update `oldClient`'s socket. --- .gitignore | 4 ++- Client.js | 16 ++++++++- Game.js | 10 ++++++ RoomManager.js | 88 ++++++++++++++++++++++++++++++++++++++++++++++---- test.js | 3 +- webxoss-client | 2 +- 6 files changed, 112 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index bc67cdb..1f7b4c7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ node_modules trash .DS_Store *.sublime-* -yarn.lock \ No newline at end of file +yarn.lock +.idea/ +.vscode/ diff --git a/Client.js b/Client.js index bbac066..86c8c97 100644 --- a/Client.js +++ b/Client.js @@ -1,12 +1,26 @@ 'use strict'; +// return hex string of 256bit number +function getRandomString() { + if (global.window) { + var array = new Uint32Array(8); + window.crypto.getRandomValues(array); + return array.reduce(function(str, num){ + return str+num.toString(16) + }, '') + } else { + var crypto = require('crypto'); + return crypto.randomBytes(32).toString('hex'); + } +} + function Client (manager,socket) { this.manager = manager; this.socket = socket; this.room = null; this.cfg = null; this.nickname = ''; - this.id = Math.random(); + this.id = getRandomString(); // 长度在128左右 // this.ip = ''; this.onSocketUpdate = null; diff --git a/Game.js b/Game.js index 98b2747..a08e742 100644 --- a/Game.js +++ b/Game.js @@ -1124,4 +1124,14 @@ Game.prototype.getLiveMessagePacks = function () { return this.hostPlayer.messagePacks; }; +Game.prototype.getMessagePacks = function(target) { + if (target === 'host') { + return this.hostPlayer.messagePacks; + } else if (target === 'guest') { + return this.guestPlayer.messagePacks; + } else { + return null; + } +}; + global.Game = Game; \ No newline at end of file diff --git a/RoomManager.js b/RoomManager.js index de6eee2..a49d4d2 100644 --- a/RoomManager.js +++ b/RoomManager.js @@ -22,14 +22,14 @@ function RoomManager (cfg) { setInterval(this.cleanUp.bind(this),60*1000); } -RoomManager.prototype.createClient = function (socket,id) { +RoomManager.prototype.createClient = function (socket,id,reconnect) { if (this.clients.length >= this.MAX_CLIENTS) { socket.disconnect(); return; } var client; - if (id) { + if (id && reconnect) { var room; for (var i = 0; i < this.rooms.length; i++) { room = this.rooms[i]; @@ -54,6 +54,14 @@ RoomManager.prototype.createClient = function (socket,id) { socket.emit('game reconnect failed'); } } + if (id && !reconnect) { + // 掉线并刷新了浏览器的用户, + // 服务器发送重建对战需要的数据, + this.sendReconnectContent(socket,id); + // 客户端重建现场后再进行updateSocket + socket.on('updateSocket',this.updateSocket.bind(this,socket,id)) + return; + } if (!client) { client = new Client(this,socket); } @@ -63,7 +71,14 @@ RoomManager.prototype.createClient = function (socket,id) { // this.maxClientsCount = this.clients.length; // console.log(new Date().toISOString().replace('T',' ').substr(0,19)+' Max clients count: %s',this.clients.length); // } + + this.bindSocketEvent(socket, client); + // socket.on('reloadCardInfo',this.reloadCardInfo.bind(this)); + socket.emit('version',this.VERSION); + this.updateRoomList(); +}; +RoomManager.prototype.bindSocketEvent = function (socket, client) { socket.on('error',this.handleError.bind(this,client)); socket.on('disconnect',this.disconnect.bind(this,client)); socket.on('createRoom',this.createRoom.bind(this,client)); @@ -83,12 +98,73 @@ RoomManager.prototype.createClient = function (socket,id) { socket.on('surrender',client.surrender.bind(client)); socket.on('drop',client.drop.bind(client)); socket.on('tick',client.tick.bind(client)); +} - // socket.on('reloadCardInfo',this.reloadCardInfo.bind(this)); +RoomManager.prototype.checkInRoom = function (id) { + var room; + for (var i = 0; i < this.rooms.length; i++) { + room = this.rooms[i]; + if (room.host.id === id) { + return true; + } else if (room.guest && room.guest.id === id) { + return true; + } + } + return false; +} - socket.emit('version',this.VERSION); - this.updateRoomList(); -}; +RoomManager.prototype.getRoomByClientId = function (id) { + var room; + for (var i = 0; i < this.rooms.length; i++) { + room = this.rooms[i]; + if (room.host.id === id) { + return room; + } else if (room.guest && room.guest.id === id) { + return room; + } + } + return null; +} + +RoomManager.prototype.sendReconnectContent = function (socket, id) { + // 发送游戏录像,等待客户端恢复对战 + var room; + var position; + if (this.checkInRoom(id)) { + room = this.getRoomByClientId(id); + if (room.guest.id === id) { + position = 'guest'; + } else { + position = 'host'; + } + socket.emit('reconnectContent', room.game.getMessagePacks(position)); + } else { + // TODO: 要求客户端重新建立socket连接(重新发送空的ClinetId) + socket.emit('game reconnect failed'); + } +} + +RoomManager.prototype.updateSocket = function (socket, id) { + // 获知客户端恢复对战后,尝试updateSocket和聊天socket + var room; + var oldClient; + if (this.checkInRoom(id)) { + room = this.getRoomByClientId(id); + if (room.guest.id === id) { + room.guest.updateSocket(socket); + this.bindSocketEvent(socket, room.guest); + } else { + room.host.updateSocket(socket); + this.bindSocketEvent(socket, room.host); + } + room.reconnecting = false; + room.emit('opponent reconnect'); + + } else { + // TODO: 要求客户端重新建立socket连接(重新发送空的ClinetId) + client.emit('game reconnect failed'); + } +} RoomManager.prototype.handleError = function (client,err) { console.error(err); diff --git a/test.js b/test.js index a409a07..1ea352b 100644 --- a/test.js +++ b/test.js @@ -157,8 +157,7 @@ io.on('connect',function (socket) { var query = require('url').parse(req.url,true).query; // console.log('connect: %s, count: %s',req.connection.remoteAddress,getSocketCount()); // console.log(query.clientId); - roomManager.createClient(socket,+query.clientId); - + roomManager.createClient(socket,query.clientId,+query.reconnect); // test // socket.on('force disconnect',function () { // socket.disconnect(); diff --git a/webxoss-client b/webxoss-client index 6d358bd..8af436d 160000 --- a/webxoss-client +++ b/webxoss-client @@ -1 +1 @@ -Subproject commit 6d358bdb59d985d7482cd6fba12c8935dab9090d +Subproject commit 8af436d6f8744693337be56f0468195093b367c0 From 52fff2d9e3e2091522c7f8df071a8685e5e40de4 Mon Sep 17 00:00:00 2001 From: deardrops Date: Mon, 8 May 2017 16:09:28 +0800 Subject: [PATCH 2/4] fixed length of random string --- Client.js | 16 +--------------- test.js | 18 +++++++++++++++++- webxoss-client | 2 +- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Client.js b/Client.js index 86c8c97..99b5928 100644 --- a/Client.js +++ b/Client.js @@ -1,26 +1,12 @@ 'use strict'; -// return hex string of 256bit number -function getRandomString() { - if (global.window) { - var array = new Uint32Array(8); - window.crypto.getRandomValues(array); - return array.reduce(function(str, num){ - return str+num.toString(16) - }, '') - } else { - var crypto = require('crypto'); - return crypto.randomBytes(32).toString('hex'); - } -} - function Client (manager,socket) { this.manager = manager; this.socket = socket; this.room = null; this.cfg = null; this.nickname = ''; - this.id = getRandomString(); // 长度在128左右 + this.id = getRandomString(); // 长度为64 // this.ip = ''; this.onSocketUpdate = null; diff --git a/test.js b/test.js index 1ea352b..989c1ac 100644 --- a/test.js +++ b/test.js @@ -1,5 +1,21 @@ 'use strict'; +// return hex string of a random 256bit number +global.getRandomString = function () { + if (global.window) { + var array = new Uint32Array(8); + window.crypto.getRandomValues(array); + return array.reduce(function(str, num){ + console.log(num.toString(16).length) + return str+('0000000' + num.toString(16)).slice(-8); + }, '') + } else { + var crypto = require('crypto'); + var numString = crypto.randomBytes(32).toString('hex'); + return ('0'.repeat(63) + numString).slice(-64); + } +} + if (!global.window) { global.Random = require("random-js"); require("./util.js"); @@ -157,7 +173,7 @@ io.on('connect',function (socket) { var query = require('url').parse(req.url,true).query; // console.log('connect: %s, count: %s',req.connection.remoteAddress,getSocketCount()); // console.log(query.clientId); - roomManager.createClient(socket,query.clientId,+query.reconnect); + roomManager.createClient(socket,query.clientId,query.reconnect); // test // socket.on('force disconnect',function () { // socket.disconnect(); diff --git a/webxoss-client b/webxoss-client index 8af436d..668c751 160000 --- a/webxoss-client +++ b/webxoss-client @@ -1 +1 @@ -Subproject commit 8af436d6f8744693337be56f0468195093b367c0 +Subproject commit 668c7519e30b91b805e1ee75a94edbd0590aaa4e From 07034c27f8f94c2191c3b08d9a90eb4ce75fd17e Mon Sep 17 00:00:00 2001 From: deardrops Date: Tue, 9 May 2017 13:00:03 +0800 Subject: [PATCH 3/4] refresh browser when reconnect fail --- webxoss-client | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webxoss-client b/webxoss-client index 668c751..99561c7 160000 --- a/webxoss-client +++ b/webxoss-client @@ -1 +1 @@ -Subproject commit 668c7519e30b91b805e1ee75a94edbd0590aaa4e +Subproject commit 99561c70e6ba5a0de2c3ca54330b9b86d3c0a061 From 6064ef372a38f290825c591f9e1acfe053dacbe0 Mon Sep 17 00:00:00 2001 From: deardrops Date: Tue, 9 May 2017 20:34:17 +0800 Subject: [PATCH 4/4] fix some bugs after reconnection --- Client.js | 2 +- RoomManager.js | 70 ++++++++++++++++++++++++-------------------------- webxoss-client | 2 +- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Client.js b/Client.js index 99b5928..8bef945 100644 --- a/Client.js +++ b/Client.js @@ -6,7 +6,7 @@ function Client (manager,socket) { this.room = null; this.cfg = null; this.nickname = ''; - this.id = getRandomString(); // 长度为64 + this.id = getRandomString(); // length: 64 // this.ip = ''; this.onSocketUpdate = null; diff --git a/RoomManager.js b/RoomManager.js index a49d4d2..0cf2159 100644 --- a/RoomManager.js +++ b/RoomManager.js @@ -29,24 +29,18 @@ RoomManager.prototype.createClient = function (socket,id,reconnect) { } var client; + var room; if (id && reconnect) { - var room; - for (var i = 0; i < this.rooms.length; i++) { - room = this.rooms[i]; - // if (!room.reconnecting) continue; // 服务器可能还不知道掉线,所以注释掉. - // console.log('host:%s\nguest:%s\nyou:%s',room.host.id,room.guest.id,id); + // 在游戏中掉线的用户,updateSocket后即可恢复连接 + if (this.checkInRoom(id)) { + room = this.getRoomByClientId(id); if (room.host.id === id) { client = room.host; client.updateSocket(socket); - break; - } - if (room.guest && room.guest.id === id) { + } else { client = room.guest; client.updateSocket(socket); - break; } - } - if (client) { room.reconnecting = false; socket.emit('game reconnect'); room.emit('opponent reconnect'); @@ -54,14 +48,16 @@ RoomManager.prototype.createClient = function (socket,id,reconnect) { socket.emit('game reconnect failed'); } } + if (id && !reconnect) { - // 掉线并刷新了浏览器的用户, - // 服务器发送重建对战需要的数据, + // 掉线并刷新了浏览器的用户,不建立Client对象 + // 首先,服务器发送重建对战需要的数据 this.sendReconnectContent(socket,id); - // 客户端重建现场后再进行updateSocket - socket.on('updateSocket',this.updateSocket.bind(this,socket,id)) + // 等待客户端重建现场,进行updateSocket + socket.on('updateSocket',this.updateSocket.bind(this,socket,id)); return; } + if (!client) { client = new Client(this,socket); } @@ -78,6 +74,7 @@ RoomManager.prototype.createClient = function (socket,id,reconnect) { socket.emit('version',this.VERSION); this.updateRoomList(); }; + RoomManager.prototype.bindSocketEvent = function (socket, client) { socket.on('error',this.handleError.bind(this,client)); socket.on('disconnect',this.disconnect.bind(this,client)); @@ -104,9 +101,7 @@ RoomManager.prototype.checkInRoom = function (id) { var room; for (var i = 0; i < this.rooms.length; i++) { room = this.rooms[i]; - if (room.host.id === id) { - return true; - } else if (room.guest && room.guest.id === id) { + if ((room.host.id === id) || (room.guest && room.guest.id === id)) { return true; } } @@ -117,9 +112,7 @@ RoomManager.prototype.getRoomByClientId = function (id) { var room; for (var i = 0; i < this.rooms.length; i++) { room = this.rooms[i]; - if (room.host.id === id) { - return room; - } else if (room.guest && room.guest.id === id) { + if ((room.host.id === id) || (room.guest.id === id)) { return room; } } @@ -127,41 +120,46 @@ RoomManager.prototype.getRoomByClientId = function (id) { } RoomManager.prototype.sendReconnectContent = function (socket, id) { - // 发送游戏录像,等待客户端恢复对战 + // 发送该玩家的游戏录像,等待客户端恢复对战 var room; var position; if (this.checkInRoom(id)) { room = this.getRoomByClientId(id); - if (room.guest.id === id) { - position = 'guest'; - } else { + if (room.host.id === id) { position = 'host'; + } else { + position = 'guest'; } - socket.emit('reconnectContent', room.game.getMessagePacks(position)); + socket.emit('reconnectContent', { + 'position': position, + 'messagePacks': room.game.getMessagePacks(position) + }); } else { - // TODO: 要求客户端重新建立socket连接(重新发送空的ClinetId) socket.emit('game reconnect failed'); } } RoomManager.prototype.updateSocket = function (socket, id) { - // 获知客户端恢复对战后,尝试updateSocket和聊天socket + // 获知客户端恢复对战现场后,updateSocket var room; - var oldClient; + var client; if (this.checkInRoom(id)) { room = this.getRoomByClientId(id); - if (room.guest.id === id) { - room.guest.updateSocket(socket); - this.bindSocketEvent(socket, room.guest); + if (room.host.id === id) { + client = room.host; } else { - room.host.updateSocket(socket); - this.bindSocketEvent(socket, room.host); + client = room.guest; } + client.updateSocket(socket); + this.bindSocketEvent(socket,client); + this.clients.push(client); + + client.emit('tock'); + client.emit('version',this.VERSION); + room.reconnecting = false; room.emit('opponent reconnect'); - } else { - // TODO: 要求客户端重新建立socket连接(重新发送空的ClinetId) client.emit('game reconnect failed'); } } diff --git a/webxoss-client b/webxoss-client index 99561c7..09e0115 160000 --- a/webxoss-client +++ b/webxoss-client @@ -1 +1 @@ -Subproject commit 99561c70e6ba5a0de2c3ca54330b9b86d3c0a061 +Subproject commit 09e011562ace5b5944d386e207dfc439c1220f1e