forked from mirrors/webxoss-core
609 lines
No EOL
15 KiB
JavaScript
609 lines
No EOL
15 KiB
JavaScript
'use strict';
|
|
|
|
function RoomManager (cfg) {
|
|
this.VERSION = 68;
|
|
|
|
this.MAX_ROOMS = cfg.MAX_ROOMS;
|
|
this.MAX_CLIENTS = cfg.MAX_CLIENTS;
|
|
this.MAX_ROOM_NAME_LENGTH = cfg.MAX_ROOM_NAME_LENGTH;
|
|
this.MAX_NICKNAME_LENGTH = cfg.MAX_NICKNAME_LENGTH;
|
|
this.MAX_PASSWORD_LENGTH = cfg.MAX_PASSWORD_LENGTH;
|
|
|
|
this.clients = [];
|
|
this.rooms = [];
|
|
this.roomMap = {};
|
|
|
|
this.replayList = [];
|
|
this.MAX_REPLAY_LENGTH = 20;
|
|
|
|
this.maxClientsCount = 0;
|
|
this.maxGamesCount = 0;
|
|
|
|
setInterval(this.cleanUp.bind(this),60*1000);
|
|
}
|
|
|
|
RoomManager.prototype.createClient = function (socket,id) {
|
|
if (this.clients.length >= this.MAX_CLIENTS) {
|
|
socket.disconnect();
|
|
return;
|
|
}
|
|
|
|
var client;
|
|
if (id) {
|
|
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);
|
|
if (room.host.id === id) {
|
|
client = room.host;
|
|
client.updateSocket(socket);
|
|
break;
|
|
}
|
|
if (room.guest && room.guest.id === id) {
|
|
client = room.guest;
|
|
client.updateSocket(socket);
|
|
break;
|
|
}
|
|
}
|
|
if (client) {
|
|
room.reconnecting = false;
|
|
socket.emit('game reconnect');
|
|
room.emit('opponent reconnect');
|
|
} else {
|
|
socket.emit('game reconnect failed');
|
|
}
|
|
}
|
|
if (!client) {
|
|
client = new Client(this,socket);
|
|
}
|
|
this.clients.push(client);
|
|
|
|
// if (this.clients.length > this.maxClientsCount) {
|
|
// this.maxClientsCount = this.clients.length;
|
|
// console.log(new Date().toISOString().replace('T',' ').substr(0,19)+' Max clients count: %s',this.clients.length);
|
|
// }
|
|
|
|
socket.on('error',this.handleError.bind(this,client));
|
|
socket.on('disconnect',this.disconnect.bind(this,client));
|
|
socket.on('createRoom',this.createRoom.bind(this,client));
|
|
socket.on('joinRoom',this.joinRoom.bind(this,client));
|
|
socket.on('leaveRoom',this.leaveRoom.bind(this,client));
|
|
socket.on('lockSpec',this.lockSpec.bind(this,client));
|
|
socket.on('unlockSpec',this.unlockSpec.bind(this,client));
|
|
socket.on('changePosition',this.changePosition.bind(this,client));
|
|
socket.on('getReplayList',this.getReplayList.bind(this,client));
|
|
socket.on('getReplayContent',this.getReplayContent.bind(this,client));
|
|
socket.on('watchLive',this.watchLive.bind(this,client));
|
|
socket.on('renameRoom',this.renameRoom.bind(this,client));
|
|
|
|
socket.on('ready',client.ready.bind(client));
|
|
socket.on('unready',client.unready.bind(client));
|
|
socket.on('startGame',client.startGame.bind(client));
|
|
socket.on('chat',client.chat.bind(client));
|
|
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));
|
|
|
|
socket.emit('version',this.VERSION);
|
|
this.updateRoomList();
|
|
};
|
|
|
|
RoomManager.prototype.handleError = function (client,err) {
|
|
console.error(err);
|
|
console.error(err.stack);
|
|
// console.trace();
|
|
var errMsg = 'SERVER_ERROR';
|
|
var room = client.room;
|
|
if (room) {
|
|
// if (room.host) {
|
|
// room.host.socket.emit('error message',errMsg);
|
|
// }
|
|
// if (room.guest) {
|
|
// room.guest.socket.emit('error message',errMsg);
|
|
// }
|
|
room.emit('error message',errMsg);
|
|
this.removeRoom(client.room);
|
|
}
|
|
};
|
|
|
|
RoomManager.prototype.disconnect = function (client) {
|
|
removeFromArr(client,this.clients);
|
|
|
|
var room = client.room;
|
|
if (!room) return;
|
|
// --- 重新连接开始 ---
|
|
if (room.game && !room.reconnecting) {
|
|
if ((client === room.host) || (client === room.guest)) {
|
|
room.reconnecting = true;
|
|
// !here
|
|
// room.emit('wait for reconnect');
|
|
room.getAllClients().forEach(function (c) {
|
|
if (c === client) return;
|
|
c.emit('wait for reconnect');
|
|
},this);
|
|
return;
|
|
}
|
|
}
|
|
// --- 重新连接结束 ---
|
|
room.reconnecting = false;
|
|
if (client === room.host) {
|
|
// 主机掉线
|
|
room.emit('host disconnected');
|
|
this.removeRoom(room);
|
|
} else if (client === room.guest) {
|
|
// 客机掉线
|
|
if (room.game) {
|
|
room.emit('guest disconnected');
|
|
this.removeRoom(room);
|
|
} else {
|
|
room.guest = null;
|
|
room.update();
|
|
this.updateRoomList();
|
|
}
|
|
} else {
|
|
// 观众掉线
|
|
room.removeSpectator(client);
|
|
room.update();
|
|
this.updateRoomList();
|
|
}
|
|
};
|
|
|
|
RoomManager.prototype.removeRoom = function (room) {
|
|
if (!room) return;
|
|
if (room.game) room.game.destroy();
|
|
room.getAllClients().forEach(function (client) {
|
|
client.reset();
|
|
},this);
|
|
|
|
delete this.roomMap[room.name];
|
|
removeFromArr(room,this.rooms);
|
|
|
|
this.updateRoomList();
|
|
};
|
|
|
|
RoomManager.prototype.updateRoomList = function () {
|
|
this.clients.forEach(function (client) {
|
|
var list = [];
|
|
this.rooms.forEach(function (room) {
|
|
var flag = (room.live && room.game && !this.checkLiveIP(client,room)) ||
|
|
(!room.game && !room.isFull());
|
|
if (flag) {
|
|
list.push(room.toInfo());
|
|
}
|
|
},this);
|
|
if (client.room) return;
|
|
client.socket.emit('update room list',list);
|
|
client.socket.emit('update online counter',this.clients.length);
|
|
},this);
|
|
};
|
|
|
|
RoomManager.prototype.checkRoomName = function (roomName) {
|
|
// TODO: 过滤敏感字词
|
|
if (!isStr(roomName) || !roomName || roomName.length > this.MAX_ROOM_NAME_LENGTH) {
|
|
return 'INVALID_ROOM_NAME';
|
|
}
|
|
};
|
|
|
|
RoomManager.prototype.checkNickname = function (nickname) {
|
|
// TODO: 过滤敏感字词
|
|
if (!isStr(nickname) || !nickname || nickname.length > this.MAX_NICKNAME_LENGTH) {
|
|
return 'INVALID_NICKNAME';
|
|
}
|
|
};
|
|
|
|
RoomManager.prototype.checkPassword = function (password) {
|
|
if (!isStr(password) || password.length > this.MAX_PASSWORD_LENGTH) {
|
|
return 'INVALID_PASSWORD';
|
|
}
|
|
};
|
|
|
|
RoomManager.prototype.checkClientInRoom = function (client) {
|
|
if (client.room) return 'ALREADY_IN_A_ROOM';
|
|
};
|
|
|
|
RoomManager.prototype.checkLiveIP = function (client,room) {
|
|
if (!client.socket.handshake) return '';
|
|
if (!room.guest) return '';
|
|
var address = client.socket.handshake.address;
|
|
var guestAddress = room.guest.socket.handshake.address;
|
|
if (address === guestAddress) return 'IP_BANNED';
|
|
};
|
|
|
|
RoomManager.prototype.renameRoom = function (client,cfg) {
|
|
var errMsg;
|
|
if (!isObj(cfg) || !isStr(cfg.roomName)) {
|
|
errMsg = 'INVALID_CONFIG';
|
|
}
|
|
var oldRoomName = client.room.name;
|
|
var newRoomName = cfg.roomName;
|
|
if (!errMsg) {
|
|
errMsg = this.checkRoomName(newRoomName);
|
|
}
|
|
var room;
|
|
if (!errMsg) {
|
|
room = this.roomMap[oldRoomName];
|
|
if (!room) {
|
|
errMsg = 'ROOM_DOES_NOT_EXIST';
|
|
} else if (client.getPosition() !== 'host') {
|
|
errMsg = 'YOU_ARE_NOT_ROOM_HOST';
|
|
}
|
|
}
|
|
if (errMsg) {
|
|
client.socket.emit('error message',errMsg);
|
|
return;
|
|
}
|
|
|
|
room.name = newRoomName;
|
|
renameProperty(this.roomMap, oldRoomName, newRoomName);
|
|
room.update();
|
|
this.updateRoomList();
|
|
};
|
|
|
|
RoomManager.prototype.createRoom = function (client,cfg) {
|
|
var errMsg;
|
|
if (!isObj(cfg) || !isStr(cfg.roomName) || !isStr(cfg.nickname)) {
|
|
errMsg = 'INVALID_CONFIG';
|
|
}
|
|
var roomName = cfg.roomName;
|
|
var nickname = cfg.nickname;
|
|
var password = cfg.password;
|
|
if (!errMsg) {
|
|
errMsg =
|
|
this.checkRoomName(roomName) ||
|
|
this.checkNickname(nickname) ||
|
|
this.checkPassword(password) ||
|
|
this.checkClientInRoom(client);
|
|
}
|
|
if (!errMsg) {
|
|
if (roomName in this.roomMap) {
|
|
errMsg = 'ROOM_ALREADY_EXISTS';
|
|
} else if (this.rooms.length >= this.MAX_ROOMS) {
|
|
errMsg = 'MAX_ROOMS';
|
|
}
|
|
}
|
|
|
|
if (errMsg) {
|
|
client.socket.emit('error message',errMsg);
|
|
return;
|
|
}
|
|
|
|
// console.log('%s creates room: %s',client.socket.id,roomName);
|
|
if (password) {
|
|
console.log('nickname: %s, roomName: %s, password: %s',nickname,roomName,password);
|
|
}
|
|
// 双向绑定
|
|
var room = new Room(roomName,client,password,!!cfg.mayusRoom);
|
|
client.room = room;
|
|
this.roomMap[roomName] = room;
|
|
this.rooms.push(room);
|
|
client.nickname = nickname;
|
|
|
|
// client.socket.emit('host room',{
|
|
// roomName: room.name,
|
|
// host: nickname,
|
|
// guest: ''
|
|
// });
|
|
room.update();
|
|
this.updateRoomList();
|
|
};
|
|
|
|
RoomManager.prototype.joinRoom = function (client,cfg) {
|
|
var errMsg;
|
|
if (!isObj(cfg) || !isStr(cfg.roomName) || !isStr(cfg.nickname)) {
|
|
errMsg = 'INVALID_CONFIG';
|
|
}
|
|
var roomName = cfg.roomName;
|
|
var nickname = cfg.nickname;
|
|
var password = cfg.password;
|
|
if (!errMsg) {
|
|
errMsg =
|
|
this.checkRoomName(roomName) ||
|
|
this.checkNickname(nickname) ||
|
|
this.checkPassword(password) ||
|
|
this.checkClientInRoom(client);
|
|
}
|
|
var room;
|
|
if (!errMsg) {
|
|
room = this.roomMap[roomName];
|
|
if (!room) {
|
|
errMsg = 'ROOM_DOES_NOT_EXIST';
|
|
} else if (room.game) {
|
|
errMsg = 'GAME_ALREADY_STARTED';
|
|
} else if (room.isFull()) {
|
|
errMsg = 'ROOM_IS_FULL';
|
|
}
|
|
}
|
|
|
|
if (errMsg) {
|
|
client.socket.emit('error message',errMsg);
|
|
return;
|
|
}
|
|
|
|
if (room.password && (password !== room.password)) {
|
|
client.socket.emit('wrong password');
|
|
return;
|
|
}
|
|
|
|
client.nickname = nickname;
|
|
client.room = room;
|
|
|
|
if (!room.guest) {
|
|
room.guest = client;
|
|
} else if (!room.isHostSpectatorsFull()) {
|
|
room.pushHostSpectator(client);
|
|
} else {
|
|
room.pushGuestSpectator(client);
|
|
}
|
|
room.update();
|
|
this.updateRoomList();
|
|
};
|
|
|
|
// RoomManager.prototype.toGuest = function (client,room) {
|
|
// room.removeClient(client);
|
|
// room.guest = client;
|
|
// room.update();
|
|
// };
|
|
|
|
// RoomManager.prototype.toHostSpectator = function (client,room,i) {
|
|
// room.removeClient(client);
|
|
// room.setHostSpectator(client,i);
|
|
// room.update();
|
|
// };
|
|
|
|
// RoomManager.prototype.toGuestSpectator = function (client,room,i) {
|
|
// room.removeClient(client);
|
|
// room.setGuestSpectator(client,i);
|
|
// room.update();
|
|
// };
|
|
|
|
RoomManager.prototype.leaveRoom = function (client) {
|
|
var errMsg;
|
|
if (!client.room) {
|
|
errMsg = 'YOU_ARE_NOT_IN_ANY_ROOM';
|
|
} else if (client.room.game && ((client === client.room.host) || (client === client.room.guest))) {
|
|
errMsg = 'GAME_ALREADY_STARTED';
|
|
}
|
|
|
|
if (errMsg) {
|
|
client.socket.emit('error message',errMsg);
|
|
return;
|
|
}
|
|
|
|
var room = client.room;
|
|
if (client === room.host) {
|
|
room.emit('host left');
|
|
this.removeRoom(room);
|
|
} else {
|
|
room.removeClient(client);
|
|
client.reset();
|
|
room.update();
|
|
}
|
|
|
|
// var host = client.room.host;
|
|
// var guest = client.room.guest;
|
|
// if (client === host) {
|
|
// if (guest) {
|
|
// guest.socket.emit('host left');
|
|
// }
|
|
// this.removeRoom(client.room);
|
|
// } else {
|
|
// host.socket.emit('guest left');
|
|
// client.room.guest = null;
|
|
// client.reset();
|
|
// }
|
|
this.updateRoomList();
|
|
};
|
|
|
|
function checkSpectatorIndex (i) {
|
|
if (!((i >= 0) && (i < 5))) {
|
|
return 'INVALID_INDEX';
|
|
}
|
|
}
|
|
|
|
RoomManager.prototype.lockSpec = function (client,i) {
|
|
i >>>= 0;
|
|
var errMsg;
|
|
var room = client.room;
|
|
if (!room) {
|
|
errMsg = 'YOU_ARE_NOT_IN_ANY_ROOM';
|
|
} else if (client.room.game) {
|
|
errMsg = 'GAME_ALREADY_STARTED';
|
|
} else if ((client !== room.host) && (client !== room.guest)) {
|
|
errMsg = 'NO_PERMISSION';
|
|
} else {
|
|
errMsg = checkSpectatorIndex(i);
|
|
}
|
|
|
|
if (errMsg) {
|
|
client.socket.emit('error message',errMsg);
|
|
return;
|
|
}
|
|
|
|
if (client === room.host) {
|
|
room.lockHostSpec(i);
|
|
} else {
|
|
room.lockGuestSpec(i);
|
|
}
|
|
room.update();
|
|
this.updateRoomList();
|
|
};
|
|
|
|
RoomManager.prototype.unlockSpec = function (client,i) {
|
|
i >>>= 0;
|
|
var errMsg;
|
|
var room = client.room;
|
|
if (!room) {
|
|
errMsg = 'YOU_ARE_NOT_IN_ANY_ROOM';
|
|
} else if (room.game) {
|
|
errMsg = 'GAME_ALREADY_STARTED';
|
|
} else if ((client !== room.host) && (client !== room.guest)) {
|
|
errMsg = 'NO_PERMISSION';
|
|
} else {
|
|
errMsg = checkSpectatorIndex(i);
|
|
}
|
|
|
|
if (errMsg) {
|
|
client.socket.emit('error message',errMsg);
|
|
return;
|
|
}
|
|
|
|
if (client === room.host) {
|
|
room.unlockHostSpec(i);
|
|
} else {
|
|
room.unlockGuestSpec(i);
|
|
}
|
|
room.update();
|
|
this.updateRoomList();
|
|
};
|
|
|
|
RoomManager.prototype.changePosition = function (client,cfg) {
|
|
var errMsg;
|
|
var room = client.room;
|
|
if (!room) {
|
|
errMsg = 'YOU_ARE_NOT_IN_ANY_ROOM';
|
|
} else if (room.game) {
|
|
errMsg = 'GAME_ALREADY_STARTED';
|
|
} else if (client === room.host) {
|
|
errMsg = 'NO_PERMISSION';
|
|
} else if (!isObj(cfg)) {
|
|
errMsg = 'INVALID_CONFIG';
|
|
}
|
|
|
|
if (errMsg) {
|
|
client.socket.emit('error message',errMsg);
|
|
return;
|
|
}
|
|
|
|
if (cfg.position === 'guest') {
|
|
if (room.guest) return room.update();
|
|
room.removeClient(client);
|
|
room.guest = client;
|
|
return room.update();
|
|
}
|
|
|
|
var i = cfg.i >>> 0;
|
|
if (checkSpectatorIndex(i)) return;
|
|
if (cfg.position === 'host-spectator') {
|
|
room.setHostSpectator(client,i);
|
|
} else if (cfg.position === 'guest-spectator') {
|
|
room.setGuestSpectator(client,i);
|
|
}
|
|
room.update();
|
|
};
|
|
|
|
RoomManager.prototype.gameover = function (room,replay) {
|
|
// console.log('%s gameovered',room.name);
|
|
if (room) {
|
|
room.gameover();
|
|
}
|
|
if (replay) {
|
|
this.pushReplay(replay);
|
|
}
|
|
this.updateRoomList();
|
|
};
|
|
|
|
RoomManager.prototype.pushReplay = function (replay) {
|
|
// if (replay.messagePacks.length < 20) return;
|
|
replay.id = Math.random();
|
|
this.replayList.unshift(replay);
|
|
if (this.replayList.length > this.MAX_REPLAY_LENGTH) {
|
|
this.replayList.pop();
|
|
}
|
|
};
|
|
|
|
RoomManager.prototype.getReplayList = function (client) {
|
|
var list = this.replayList.map(function (replay) {
|
|
return {
|
|
id: replay.id,
|
|
win: replay.win,
|
|
surrender: replay.surrender,
|
|
selfLrig: replay.selfLrig,
|
|
opponentLrig: replay.opponentLrig
|
|
};
|
|
},this);
|
|
client.socket.emit('replayList',list);
|
|
};
|
|
|
|
RoomManager.prototype.getReplayContent = function (client,id) {
|
|
if (!isNum(id)) return;
|
|
for (var i = 0; i < this.replayList.length; i++) {
|
|
var replay = this.replayList[i];
|
|
if (replay.id === id) {
|
|
client.socket.emit('replayContent',{
|
|
clientVersion: this.version,
|
|
win: replay.win,
|
|
surrender: replay.surrender,
|
|
messagePacks: replay.messagePacks
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
client.socket.emit('replayContent',null);
|
|
};
|
|
|
|
RoomManager.prototype.watchLive = function (client,cfg) {
|
|
var errMsg;
|
|
if (!isObj(cfg) || !isStr(cfg.roomName)) {
|
|
errMsg = 'INVALID_CONFIG';
|
|
}
|
|
var roomName = cfg.roomName;
|
|
if (!errMsg) {
|
|
errMsg = this.checkClientInRoom(client);
|
|
}
|
|
var room;
|
|
if (!errMsg) {
|
|
room = this.roomMap[roomName];
|
|
if (!room) {
|
|
errMsg = 'ROOM_DOES_NOT_EXIST';
|
|
} else if (!room.live) {
|
|
errMsg = 'NOT_IN_LIVE_MODE';
|
|
} else if (!room.game) {
|
|
errMsg = 'GAME_NOT_IN_PROGRESS';
|
|
} else {
|
|
errMsg = this.checkLiveIP(client,room);
|
|
}
|
|
}
|
|
|
|
if (errMsg) {
|
|
client.socket.emit('error message',errMsg);
|
|
return;
|
|
}
|
|
|
|
client.room = room;
|
|
room.pushLiveSpectator(client);
|
|
client.emit('liveData',room.game.getLiveMessagePacks());
|
|
};
|
|
|
|
RoomManager.prototype.cleanUp = function () {
|
|
var clients = this.clients.slice();
|
|
clients.forEach(function (client) {
|
|
var socket = client.socket;
|
|
if (socket.disconnected) {
|
|
this.disconnect(client);
|
|
console.error('cleanUp');
|
|
}
|
|
},this);
|
|
var rooms = this.rooms.slice();
|
|
var now = Date.now();
|
|
rooms.forEach(function (room) {
|
|
var flag =
|
|
((now - room.activateTime) >= 3*60*60*1000) ||
|
|
(!room.reconnecting && room.host && room.host.socket.disconnected) ||
|
|
(!room.reconnecting && room.guest && room.guest.socket.disconnected);
|
|
if (!flag) return;
|
|
console.log('clean up: ' + room.name);
|
|
this.removeRoom(room);
|
|
},this);
|
|
};
|
|
|
|
// RoomManager.prototype.reloadCardInfo = function (password) {
|
|
// if (password !== 'WEBXOSS') return;
|
|
// var path = require('path');
|
|
// var filePath = './CardInfo.js';
|
|
// delete require.cache[path.resolve(filePath)];
|
|
// require(filePath);
|
|
// };
|
|
|
|
global.RoomManager = RoomManager; |