initial commit of poker game
This commit is contained in:
388
game-server/app/services/botService.js
Normal file
388
game-server/app/services/botService.js
Normal file
@@ -0,0 +1,388 @@
|
||||
var BOT_CONFIG = require('../../config/bots.json');
|
||||
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||
var UserStore = require('../../app/persistence/users');
|
||||
Hand = require('hoyle').Hand;
|
||||
|
||||
|
||||
var BotService = function(app){
|
||||
this.app = app;
|
||||
this.channelService = app.get('channelService');
|
||||
this.tableService = app.get('tableService');
|
||||
this.tableInstance = {};
|
||||
this.tableInstanceActive = {};
|
||||
this.bots = {};
|
||||
this.config = BOT_CONFIG.config;
|
||||
};
|
||||
|
||||
module.exports = BotService;
|
||||
|
||||
BotService.prototype.start = function(cb){
|
||||
var me = this;
|
||||
if(!me.config.enabled){
|
||||
return cb();
|
||||
}
|
||||
me.registerBots(BOT_CONFIG.bots, function(){
|
||||
logger.info('all bots registered');
|
||||
me.checkAvailability();
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
BotService.prototype.registerBots = function(bots, cb){
|
||||
var me = this, i = 0;
|
||||
if(!bots.length){
|
||||
cb();
|
||||
}
|
||||
function createIfNotExist(){
|
||||
bots[i].chips = 100000;
|
||||
UserStore.create(bots[i], function(e, user){
|
||||
me.bots[user.id] = user;
|
||||
me.bots[user.id].available = true;
|
||||
if(++i == bots.length){
|
||||
return cb();
|
||||
}
|
||||
createIfNotExist();
|
||||
});
|
||||
}
|
||||
createIfNotExist();
|
||||
};
|
||||
|
||||
BotService.prototype.checkAvailability = function(){
|
||||
var me = this;
|
||||
setInterval(function(){
|
||||
var bot = me.getAvailableBot();
|
||||
var table = me.getAvailableTable();
|
||||
if(bot && table && (!me.config.minBots || me.config.minBots > me.getActiveBots()) && !me.config.banBots){
|
||||
me.joinGame(bot, table);
|
||||
}
|
||||
}, (1000 * getRandomInt(me.config.joinInterval.min, (me.config.joinInterval.max))));
|
||||
};
|
||||
|
||||
BotService.prototype.getById = function(id){
|
||||
return this.bots[id];
|
||||
};
|
||||
|
||||
BotService.prototype.getAvailableBot = function(){
|
||||
var bot;
|
||||
for(var i in this.bots){
|
||||
if(this.bots[i].available){
|
||||
bot = this.bots[i];
|
||||
}
|
||||
}
|
||||
return bot;
|
||||
};
|
||||
|
||||
BotService.prototype.getActiveBots = function(){
|
||||
var ctr = 0;
|
||||
for(var i in this.bots){
|
||||
if(!this.bots[i].available){
|
||||
ctr += 1;
|
||||
}
|
||||
}
|
||||
return ctr;
|
||||
};
|
||||
|
||||
BotService.prototype.getAvailableTable = function(){
|
||||
var table;
|
||||
for(var i in this.tableService.tables){
|
||||
if(!this.tableInstanceActive[i] && (this.tableService.tables[i].state == 'JOIN' && this.tableService.tables[i].table.playersToAdd.length < this.tableService.tables[i].table.maxPlayers) ||
|
||||
(this.tableService.tables[i].state == 'IN_PROGRESS' && (this.tableService.tables[i].table.playersToAdd + this.tableService.tables[i].table.players.length) < this.tableService.tables[i].table.maxPlayers)){
|
||||
table = this.tableService.tables[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return table;
|
||||
};
|
||||
|
||||
BotService.prototype.joinGame = function(bot, table){
|
||||
var me = this;
|
||||
me.tableService.addPlayer(table.id, bot.id, me.config.buyIn || 1000, function(e){
|
||||
if(e){
|
||||
return logger.error('bot error joining game', e);
|
||||
}
|
||||
bot.available = false;
|
||||
bot.games = getRandomInt(me.config.gamesToPlay.min, me.config.gamesToPlay.max);
|
||||
bot.tid = table.id;
|
||||
logger.debug('bot '+bot.username+' joining table '+table.id+' for '+bot.games+' games');
|
||||
me.tableInstance[table.id] = me.tableInstance[table.id] || 0;
|
||||
me.tableInstance[table.id] += 1;
|
||||
me.listen(table.id);
|
||||
});
|
||||
};
|
||||
|
||||
BotService.prototype.startGame = function(table, tid){
|
||||
var me = this;
|
||||
var interval = setInterval(function(){
|
||||
if(table.state == 'IN_PROGRESS'){
|
||||
clearInterval(interval);
|
||||
}
|
||||
if(table.table.playersToAdd.length >= (me.config.minPlayers ? me.config.minPlayers : 0) && !me.config.banBots){
|
||||
table.tableService.startGame(tid, function(e){
|
||||
if(e){
|
||||
return logger.debug('cant start game yet', e);
|
||||
}
|
||||
clearInterval(interval);
|
||||
});
|
||||
}
|
||||
}, (1000 * getRandomInt(7, 20)));
|
||||
}
|
||||
|
||||
BotService.prototype.leaveGame = function(tid, uid, cb){
|
||||
var me = this;
|
||||
me.tableService.removeMember(tid, uid, function(){
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
BotService.prototype.listen = function(tid){
|
||||
var me = this;
|
||||
if(me.tableInstance[tid] > 1){
|
||||
return false;
|
||||
}
|
||||
logger.debug('initializing listeners for table '+tid);
|
||||
var table = me.tableService.getTable(tid);
|
||||
var playerJoinedListener = function(){
|
||||
logger.debug('playerJoined');
|
||||
me.startGame(table, tid);
|
||||
};
|
||||
var newRoundListener = function(){
|
||||
logger.debug('newRound');
|
||||
me.moveIfTurn(table);
|
||||
};
|
||||
var turnStartListener = function(){
|
||||
logger.debug('turnStart');
|
||||
me.moveIfTurn(table);
|
||||
};
|
||||
var gameInitListener = function(){
|
||||
logger.debug('gameInit');
|
||||
setTimeout(function(){
|
||||
me.removeAllBots(tid);
|
||||
}, 300);
|
||||
};
|
||||
table.table.eventEmitter.on('playerJoined', playerJoinedListener);
|
||||
table.table.eventEmitter.on('newRound', newRoundListener);
|
||||
table.table.eventEmitter.on('turnStart', turnStartListener);
|
||||
table.table.eventEmitter.on('gameInit', gameInitListener);
|
||||
};
|
||||
|
||||
BotService.prototype.moveIfTurn = function(table){
|
||||
var me = this, pid;
|
||||
if(typeof table.table.currentPlayer === 'number' && table.table.players[table.table.currentPlayer])
|
||||
pid = table.table.players[table.table.currentPlayer].id;
|
||||
if(this.bots[pid]){
|
||||
logger.debug('starting move: '+this.bots[pid].username);
|
||||
table.table.stopTimer();
|
||||
setTimeout(function(){
|
||||
if(!table.table || !table.table.game) return false;
|
||||
var board = table.table.game.board || [];
|
||||
if(board.length < 3){
|
||||
table.table.players[table.table.currentPlayer].Call();
|
||||
}else{
|
||||
performMove(table);
|
||||
}
|
||||
logger.debug('completed move.');
|
||||
table.tableService.handleGameState(table.id, function(e){
|
||||
if(e){
|
||||
logger.error(e);
|
||||
}
|
||||
});
|
||||
}, (getRandomInt(me.config.actionInterval.min, me.config.actionInterval.max) * 1000));
|
||||
}
|
||||
};
|
||||
|
||||
function performMove(table){
|
||||
var currentPlayer = table.table.players[table.table.currentPlayer];
|
||||
var myBet = table.table.game.bets[table.table.currentPlayer];
|
||||
var myHand = Hand.make(getHand(table.table.game.board.concat(table.table.players[table.table.currentPlayer].cards)));
|
||||
var maxBet = myBet;
|
||||
var maxRank = myHand.rank;
|
||||
var winningPlayer = currentPlayer.playerName;
|
||||
var hands = [];
|
||||
for(var i=0;i<table.table.players.length;++i){
|
||||
var bet = table.table.game.bets[i];
|
||||
var hand = Hand.make(getHand(table.table.game.board.concat(table.table.players[i].cards)));
|
||||
if(bet > maxBet) maxBet = bet;
|
||||
if(hand.rank > maxRank){
|
||||
maxRank = hand.rank;
|
||||
winningPlayer = table.table.players[i].playerName;
|
||||
}
|
||||
hands.push(hand);
|
||||
}
|
||||
logger.debug('has best hand: '+ winningPlayer);
|
||||
var isWinner = false;
|
||||
var winners = Hand.pickWinners(hands);
|
||||
var diff = maxBet - myBet;
|
||||
for(var i=0;i<winners.length;++i){
|
||||
if(winners[i] === myHand) isWinner = true;
|
||||
}
|
||||
if(myHand.rank < maxRank){
|
||||
if(myBet >= maxBet){
|
||||
if(getRandomInt(1, 51) > 47){
|
||||
currentPlayer.AllIn();
|
||||
}else if(getRandomInt(1, 10) > 7){
|
||||
currentPlayer.Bet(getRandomInt(2, 53));
|
||||
}else{
|
||||
currentPlayer.Call();
|
||||
}
|
||||
}else if(myBet < maxBet){
|
||||
if(diff > getRandomInt(4, 61)){
|
||||
if(getRandomInt(1, 73) > 71){
|
||||
currentPlayer.AllIn();
|
||||
}else if(getRandomInt(1, 10) > 8){
|
||||
currentPlayer.Bet(getRandomInt(2, 36));
|
||||
}else if(getRandomInt(1, 10) > 2){
|
||||
currentPlayer.Fold();
|
||||
}else{
|
||||
currentPlayer.Call();
|
||||
}
|
||||
}else{
|
||||
if(getRandomInt(1, 73) > 70){
|
||||
currentPlayer.AllIn();
|
||||
}else if(getRandomInt(1, 10) > 7){
|
||||
currentPlayer.Bet(getRandomInt(2, 43));
|
||||
}else if(diff > getRandomInt(1, 7) && getRandomInt(1, 10) > 6){
|
||||
currentPlayer.Fold();
|
||||
}else{
|
||||
currentPlayer.Call();
|
||||
}
|
||||
}
|
||||
}else{
|
||||
currentPlayer.Call();
|
||||
}
|
||||
}else{
|
||||
if(myBet >= maxBet){
|
||||
if(getRandomInt(1, 10) == 10){
|
||||
currentPlayer.AllIn();
|
||||
}else if(getRandomInt(1, 10) > 4){
|
||||
currentPlayer.Bet(getRandomInt(1, 271));
|
||||
}else{
|
||||
currentPlayer.Call();
|
||||
}
|
||||
}else if(myBet < maxBet){
|
||||
if(getRandomInt(1, 10) == 10){
|
||||
currentPlayer.AllIn();
|
||||
}else if(getRandomInt(1, 10) > 4){
|
||||
currentPlayer.Bet(getRandomInt(2, 189));
|
||||
}else if(diff > getRandomInt(71, 104) && getRandomInt(1, 10) > 8){
|
||||
currentPlayer.Fold();
|
||||
}else{
|
||||
currentPlayer.Call();
|
||||
}
|
||||
}else{
|
||||
currentPlayer.Call();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getHand(hand){
|
||||
for(var j=0;j<hand.length;++j){
|
||||
hand[j] = hand[j].split('');
|
||||
hand[j] = hand[j][0]+hand[j][1].toLowerCase();
|
||||
}
|
||||
return hand;
|
||||
}
|
||||
|
||||
function getRandomInt(min, max){
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
BotService.prototype.removeAllBots = function(tid, banBots){
|
||||
var me = this, botAry = [];
|
||||
me.config.banBots = banBots;
|
||||
var table = me.tableService.getTable(tid);
|
||||
for(var id in me.bots){
|
||||
if(me.bots[id].tid === tid)
|
||||
botAry.push(id);
|
||||
}
|
||||
if(me.config.validateChips){
|
||||
me.tableInstanceActive[tid] = true;
|
||||
}
|
||||
me.removeBot(botAry, 0, tid, banBots, function(){
|
||||
if(me.tableInstance[tid] === 0){
|
||||
logger.debug('removing listeners for table '+tid);
|
||||
table.table.eventEmitter.removeAllListeners('playerJoined');
|
||||
table.table.eventEmitter.removeAllListeners('newRound');
|
||||
table.table.eventEmitter.removeAllListeners('turnStart');
|
||||
table.table.eventEmitter.removeAllListeners('gameInit');
|
||||
}
|
||||
if(me.config.validateChips){
|
||||
me.validateTotalChips(function(e){
|
||||
if(!e){
|
||||
me.tableInstanceActive[tid] = false;
|
||||
me.startGame(table, tid);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
me.startGame(table, tid);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
BotService.prototype.removeBot = function(botAry, i, tid, banBots, cb){
|
||||
var me = this;
|
||||
if(i === botAry.length){
|
||||
return cb();
|
||||
}
|
||||
var bid = botAry[i];
|
||||
if(me.bots[bid].tid === tid){
|
||||
me.bots[bid].games -= 1;
|
||||
var user = me.tableService.getPlayerJSON(tid, bid, 'players') || me.tableService.getPlayerJSON(tid, bid, 'playersToAdd') || me.tableService.getPlayerJSON(tid, bid, 'previousPlayers');
|
||||
console.log(me.tableService.getTable(tid));
|
||||
console.log(tid, bid, user);
|
||||
if(me.bots[bid].games === 0 || (user && user.chips === 0) || banBots === true){
|
||||
logger.debug('bot '+me.bots[bid].username+' ('+me.bots[bid].id+') left game '+tid+' with '+((user && user.chips) ? user.chips : 0)+' chips', user);
|
||||
me.leaveGame(tid, bid, function(){
|
||||
me.bots[bid].available = true;
|
||||
delete me.bots[bid].tid;
|
||||
me.tableInstance[tid] -= 1;
|
||||
me.removeBot(botAry, (i+1), tid, banBots, cb);
|
||||
});
|
||||
}else{
|
||||
me.removeBot(botAry, (i+1), tid, banBots, cb);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// validate chips per all bots
|
||||
BotService.prototype.validateTotalChips = function(cb){
|
||||
var total = 0;
|
||||
var ids = [];
|
||||
for(var i in this.bots){
|
||||
ids.push(this.bots[i].id);
|
||||
}
|
||||
UserStore.getByIds(ids, function(e, users){
|
||||
for(var user in users){
|
||||
total += users[user].chips;
|
||||
}
|
||||
var correctTotal = (ids.length * 100000);
|
||||
if(total != correctTotal){
|
||||
logger.error('INCORRECT TOTAL CHIPS! '+correctTotal+' - '+total+' = '+(correctTotal - total));
|
||||
return cb('incorrect-total');
|
||||
}
|
||||
logger.debug('correct total chips '+total+'/'+correctTotal);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
// validate chips per table
|
||||
BotService.prototype.validateChips = function(table, cb){
|
||||
var me = this;
|
||||
var total = 0;
|
||||
var ids = [];
|
||||
for(var i=0;i<table.table.playersToAdd.length;++i){
|
||||
ids.push(table.table.playersToAdd[i].id);
|
||||
}
|
||||
UserStore.getByIds(ids, function(e, users){
|
||||
for(var user in users){
|
||||
total += users[user].chips;
|
||||
}
|
||||
total += (ids.length * me.config.buyIn);
|
||||
var correctTotal = (ids.length * 100000);
|
||||
if(total != correctTotal){
|
||||
logger.error('INCORRECT GAME CHIPS! '+correctTotal+' - '+total+' = '+(correctTotal - total));
|
||||
return cb('incorrect-total');
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
240
game-server/app/services/chatService.js
Normal file
240
game-server/app/services/chatService.js
Normal file
@@ -0,0 +1,240 @@
|
||||
var UserStore = require('../persistence/users');
|
||||
var dispatcher = require('../util/dispatcher');
|
||||
|
||||
var ChatService = function(app) {
|
||||
this.app = app;
|
||||
this.uidMap = {};
|
||||
this.nameMap = {};
|
||||
this.channelMap = {};
|
||||
};
|
||||
|
||||
module.exports = ChatService;
|
||||
|
||||
/**
|
||||
* Add player into a channel
|
||||
*
|
||||
* @param {String} uid user id
|
||||
* @param {String} cid channel id
|
||||
* @return {Number} see code.js
|
||||
*/
|
||||
ChatService.prototype.addToChannel = function(uid, cid, cb){
|
||||
var me = this;
|
||||
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||
if(e){
|
||||
return cb(e);
|
||||
}
|
||||
var sid = getSidByUid(uid, me.app);
|
||||
if(!sid){
|
||||
return cb('invalid-connector-server');
|
||||
}
|
||||
if(checkDuplicate(me, uid, cid)){
|
||||
return cb();
|
||||
}
|
||||
var channel = me.app.get('channelService').getChannel(cid, true);
|
||||
if(!channel){
|
||||
return cb('invalid-channel');
|
||||
}
|
||||
channel.add(uid, sid);
|
||||
addRecord(me, uid, user.username, sid, cid);
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add player record
|
||||
*
|
||||
* @param {String} uid user id
|
||||
*/
|
||||
ChatService.prototype.add = function(uid, cb){
|
||||
var me = this;
|
||||
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||
if(e){
|
||||
return cb(e);
|
||||
}
|
||||
var sid = getSidByUid(uid, me.app);
|
||||
if(!sid){
|
||||
return cb('invalid-connector-server');
|
||||
}
|
||||
addRecord(me, uid, user.username, sid);
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* User leaves the channel
|
||||
*
|
||||
* @param {String} uid user id
|
||||
* @param {String} cid channel id
|
||||
*/
|
||||
ChatService.prototype.leave = function(uid, cid) {
|
||||
var record = this.uidMap[uid];
|
||||
var channel = this.app.get('channelService').getChannel(cid, true);
|
||||
if(channel && record) {
|
||||
channel.leave(uid, record.sid);
|
||||
}
|
||||
removeRecord(this, uid, cid);
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnect user from all channels in chat service.
|
||||
* This operation would remove the user from all channels and
|
||||
* clear all the records of the user.
|
||||
*
|
||||
* @param {String} uid user id
|
||||
*/
|
||||
ChatService.prototype.disconnect = function(uid){
|
||||
var cids = this.channelMap[uid];
|
||||
var record = this.uidMap[uid];
|
||||
if(cids && record){
|
||||
// remove user from channels
|
||||
var channel;
|
||||
for(var name in cids){
|
||||
channel = this.app.get('channelService').getChannel(name);
|
||||
if(channel){
|
||||
channel.leave(uid, record.sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
clearRecords(this, uid);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get friend list
|
||||
* @param {string} uid user id
|
||||
* @param {Function} cb callback function
|
||||
*/
|
||||
ChatService.prototype.getFriendList = function(uid, cb){
|
||||
var me = this;
|
||||
UserStore.getByAttr('id', uid, {
|
||||
getFullEntity : true
|
||||
}, function(e, user){
|
||||
if(e){
|
||||
return cb(e);
|
||||
}
|
||||
user.friends = user.friends || [];
|
||||
for(var i=0;i<user.friends.length;++i){
|
||||
user.friends[i].online = me.uidMap[user.friends[i].id] ? true : false;
|
||||
}
|
||||
cb(null, user.friends);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Push message by the specified channel
|
||||
*
|
||||
* @param {String} cid channel id
|
||||
* @param {Object} msg message json object
|
||||
* @param {Function} cb callback function
|
||||
*/
|
||||
ChatService.prototype.pushByChannel = function(cid, msg, cb){
|
||||
var channel = this.app.get('channelService').getChannel(cid);
|
||||
if(!channel){
|
||||
cb(new Error('channel ' + cid + ' doses not exist'));
|
||||
return;
|
||||
}
|
||||
channel.pushMessage('onChat', msg, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Push message to the specified player
|
||||
*
|
||||
* @param {String} username player's role name
|
||||
* @param {Object} msg message json object
|
||||
* @param {Function} cb callback
|
||||
*/
|
||||
ChatService.prototype.pushByPlayerName = function(username, msg, cb){
|
||||
var record = this.nameMap[username];
|
||||
if(!record){
|
||||
cb('user-not-online');
|
||||
return;
|
||||
}
|
||||
this.app.get('channelService').pushMessageByUids('onUserChat', msg, [{uid: record.uid, sid: record.sid}], cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Push message to the specified player
|
||||
*
|
||||
* @param {String} uid player's user id
|
||||
* @param {Object} msg message json object
|
||||
* @param {Function} cb callback
|
||||
*/
|
||||
ChatService.prototype.pushByPlayerId = function(uid, msg, cb){
|
||||
var record = this.uidMap[uid];
|
||||
if(!record){
|
||||
cb('user-not-online');
|
||||
return;
|
||||
}
|
||||
this.app.get('channelService').pushMessageByUids('onUserChat', msg, [{uid: record.uid, sid: record.sid}], cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the user is already in the channel
|
||||
*/
|
||||
var checkDuplicate = function(service, uid, cid) {
|
||||
return !!service.channelMap[uid] && !!service.channelMap[uid][cid];
|
||||
};
|
||||
|
||||
/**
|
||||
* Add records for the specified user
|
||||
*/
|
||||
var addRecord = function(service, uid, name, sid, cid){
|
||||
var record = {uid: uid, name: name, sid: sid};
|
||||
service.uidMap[uid] = record;
|
||||
service.nameMap[name] = record;
|
||||
var item = service.channelMap[uid];
|
||||
if(!item){
|
||||
item = service.channelMap[uid] = {};
|
||||
}
|
||||
if(cid){
|
||||
item[cid] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove records for the specified user and channel pair
|
||||
*/
|
||||
var removeRecord = function(service, uid, cid) {
|
||||
delete service.channelMap[uid][cid];
|
||||
// if(objLen(service.channelMap[uid])){
|
||||
// return;
|
||||
// }
|
||||
// // if user not in any channel then clear his records
|
||||
// clearRecords(service, uid);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear all records of the user
|
||||
*/
|
||||
var clearRecords = function(service, uid) {
|
||||
delete service.channelMap[uid];
|
||||
var record = service.uidMap[uid];
|
||||
if(!record) {
|
||||
return;
|
||||
}
|
||||
delete service.uidMap[uid];
|
||||
delete service.nameMap[record.name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the connector server id associated with the uid
|
||||
*/
|
||||
var getSidByUid = function(uid, app) {
|
||||
var connector = dispatcher.dispatch(uid, app.getServersByType('connector'));
|
||||
if(connector) {
|
||||
return connector.id;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
function objLen(obj){
|
||||
if(!obj) {
|
||||
return 0;
|
||||
}
|
||||
var size = 0;
|
||||
for(var f in obj) {
|
||||
if(obj.hasOwnProperty(f)) {
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
64
game-server/app/services/stateService.js
Normal file
64
game-server/app/services/stateService.js
Normal file
@@ -0,0 +1,64 @@
|
||||
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||
var REDIS_CONFIG = require('../../config/redis.json');
|
||||
var redis = require('redis');
|
||||
|
||||
/**
|
||||
* Maintain a persistent store .
|
||||
*
|
||||
* StateService is created by stateComponent.
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
*/
|
||||
module.exports = StateService = function(){
|
||||
this.prefix = 'POKER:';
|
||||
};
|
||||
|
||||
StateService.prototype.start = function(cb){
|
||||
this.redis.createClient(REDIS_CONFIG.port, REDIS_CONFIG.host, REDIS_CONFIG.opts);
|
||||
this.redis.on('error', function(e){
|
||||
logger.error('redis error', e.stack);
|
||||
cb('connection-error');
|
||||
});
|
||||
this.redis.once('ready', function(){
|
||||
logger.info('redis initialized!');
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
StateService.prototype.stop = function(cb){
|
||||
if(this.redis){
|
||||
logger.info('redis stopped');
|
||||
this.redis.end();
|
||||
this.redis = null;
|
||||
}
|
||||
cb();
|
||||
};
|
||||
|
||||
StateService.prototype.push = function(uid, sid ,cb){
|
||||
this.redis.sadd(genKey(this, uid), sid, function(err){
|
||||
invokeCallback(cb, err);
|
||||
});
|
||||
};
|
||||
|
||||
StateService.prototype.remove = function(uid, sid, cb){
|
||||
this.redis.srem(genKey(this, uid), sid, function(err){
|
||||
invokeCallback(cb, err);
|
||||
});
|
||||
};
|
||||
|
||||
StateService.prototype.getSidsByUid = function(uid, cb){
|
||||
this.redis.smembers(genKey(this, uid), function(err, list){
|
||||
invokeCallback(cb, err, list);
|
||||
});
|
||||
};
|
||||
|
||||
var genKey = function(me, uid){
|
||||
return me.prefix + ':' + uid;
|
||||
};
|
||||
|
||||
var invokeCallback = function(cb){
|
||||
if(!!cb && typeof cb === 'function'){
|
||||
cb.apply(null, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
};
|
677
game-server/app/services/tableService.js
Normal file
677
game-server/app/services/tableService.js
Normal file
@@ -0,0 +1,677 @@
|
||||
var events = require('events');
|
||||
var uuid = require('node-uuid');
|
||||
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||
var TableStore = require('../../app/persistence/tables');
|
||||
var UserStore = require('../../app/persistence/users');
|
||||
var dispatcher = require('../util/dispatcher');
|
||||
var Table = require('../game/table');
|
||||
|
||||
/**
|
||||
* Create and maintain table tables.
|
||||
*
|
||||
* TableService is created by tableComponent.
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
*/
|
||||
var TableService = function(app, opts){
|
||||
opts = opts || {};
|
||||
this.app = app;
|
||||
this.tables = {};
|
||||
this.prefix = opts.prefix;
|
||||
this.store = opts.store;
|
||||
this.stateService = this.app.get('stateService');
|
||||
};
|
||||
|
||||
module.exports = TableService;
|
||||
|
||||
TableService.prototype.start = function(cb){
|
||||
cb();
|
||||
};
|
||||
|
||||
TableService.prototype.stop = function(force, cb){
|
||||
cb();
|
||||
};
|
||||
|
||||
TableService.prototype.getTable = function(tid){
|
||||
return this.tables[tid];
|
||||
};
|
||||
|
||||
TableService.prototype.getTables = function(){
|
||||
var tables = {
|
||||
tables : [],
|
||||
totalMembers : 0,
|
||||
totalPlayers : 0
|
||||
};
|
||||
for(var i in this.tables){
|
||||
var table = this.tables[i];
|
||||
var members = table.table.members.length;
|
||||
var players = (table.table.players.length - table.table.playersToRemove.length);
|
||||
tables.totalMembers += members;
|
||||
tables.totalPlayers += players;
|
||||
tables.tables.push({
|
||||
id : table.id,
|
||||
smallBlind : table.table.smallBlind,
|
||||
bigBlind : table.table.bigBlind,
|
||||
minBuyIn : table.table.minBuyIn,
|
||||
maxBuyIn : table.table.maxBuyIn,
|
||||
minPlayers : table.table.minPlayers,
|
||||
maxPlayers : table.table.maxPlayers,
|
||||
gameMode : table.table.gameMode,
|
||||
players : players,
|
||||
members : members
|
||||
});
|
||||
}
|
||||
return tables;
|
||||
};
|
||||
|
||||
TableService.prototype.createTable = function(uid, obj, cb){
|
||||
if(!obj || (obj && (
|
||||
isNaN(obj.smallBlind) ||
|
||||
isNaN(obj.bigBlind) ||
|
||||
isNaN(obj.minBuyIn) ||
|
||||
isNaN(obj.maxBuyIn) ||
|
||||
isNaN(obj.minPlayers) ||
|
||||
isNaN(obj.maxPlayers) ||
|
||||
obj.minPlayers < 2 ||
|
||||
obj.minPlayers > 10 ||
|
||||
obj.maxPlayers < 2 ||
|
||||
obj.maxPlayers > 10
|
||||
))){
|
||||
return cb('invalid-table-rules');
|
||||
}
|
||||
var tid = uuid.v1();
|
||||
this.tables[tid] = {};
|
||||
this.tables[tid].id = tid;
|
||||
this.tables[tid].creator = uid;
|
||||
this.tables[tid].state = 'JOIN';
|
||||
this.tables[tid].tableService = this;
|
||||
obj.smallBlind = Math.round(parseInt(obj.smallBlind));
|
||||
obj.bigBlind = Math.round(parseInt(obj.bigBlind));
|
||||
obj.minBuyIn = Math.round(parseInt(obj.minBuyIn));
|
||||
obj.maxBuyIn = Math.round(parseInt(obj.maxBuyIn));
|
||||
obj.minPlayers = Math.round(parseInt(obj.minPlayers));
|
||||
obj.maxPlayers = Math.round(parseInt(obj.maxPlayers));
|
||||
obj.gameMode = (obj.gameMode == 'normal' || obj.gameMode == 'fast') ? obj.gameMode : 'normal';
|
||||
this.tables[tid].table = new Table(obj.smallBlind, obj.bigBlind, obj.minPlayers, obj.maxPlayers, obj.minBuyIn, obj.maxBuyIn, obj.gameMode, this.tables[tid]);
|
||||
// automatically join created table
|
||||
// session.set('tid', table.id);
|
||||
// var tid = session.get('tid');
|
||||
// me.app.rpc.chat.chatRemote.add(session, session.uid, tid, function(e, users){
|
||||
// if(e){
|
||||
// next(500, {
|
||||
// code : 200,
|
||||
// error : e
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// var channelService = me.app.get('channelService');
|
||||
// var channel = channelService.getChannel(tid, true);
|
||||
// channel.pushMessage({
|
||||
// route : 'onTableEvent',
|
||||
// msg : tableService.getTableJSON(tid, session.uid)
|
||||
// });
|
||||
// channel.pushMessage({
|
||||
// route : 'onUpdateUsers',
|
||||
// users : users
|
||||
// });
|
||||
// tableService.broadcastGameState(tid);
|
||||
// next(null, {
|
||||
// code : 200,
|
||||
// route : msg.route
|
||||
// });
|
||||
// });
|
||||
cb(null, this.tables[tid]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add member to the table
|
||||
*
|
||||
* @param {Object} tid id of an existing table
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.addMember = function(tid, uid, cb){
|
||||
var me = this;
|
||||
var channelService = me.app.get('channelService');
|
||||
var table = this.tables[tid];
|
||||
if(!table){
|
||||
cb('table-not-found');
|
||||
return;
|
||||
}
|
||||
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||
if(!user){
|
||||
cb(e);
|
||||
}
|
||||
var sid = getSidByUid(uid, me.app);
|
||||
if(!sid){
|
||||
return cb('invalid-connector-server');
|
||||
}
|
||||
// TODO: reduce payload by handling based on game state
|
||||
var channel = channelService.getChannel(tid, true);
|
||||
channel.add(uid, sid);
|
||||
channelService.pushMessageByUids({
|
||||
route : 'onTableEvent',
|
||||
msg : me.getTableJSON(tid, uid)
|
||||
}, [{
|
||||
uid : uid,
|
||||
sid : channel.getMember(uid)['sid']
|
||||
}], function(){
|
||||
logger.debug('initiated player '+uid+' into table '+tid+' with state '+table.state);
|
||||
table.table.members.push(user);
|
||||
channel.pushMessage({
|
||||
route : 'onUpdateUsers',
|
||||
members : table.table.members
|
||||
});
|
||||
cb();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the connector server id associated with the uid
|
||||
*/
|
||||
var getSidByUid = function(uid, app){
|
||||
var connector = dispatcher.dispatch(uid, app.getServersByType('connector'));
|
||||
if(connector){
|
||||
return connector.id;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove member from the table
|
||||
*
|
||||
* @param {Object} tid id of an existing table
|
||||
* @param {string} uid userId to remove from the table
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.removeMember = function(tid, uid, cb){
|
||||
var me = this;
|
||||
if(!me.tables[tid]){
|
||||
var e = 'table-not-found';
|
||||
logger.error('error removing player '+uid+' from table '+tid, e);
|
||||
cb(e);
|
||||
return;
|
||||
}
|
||||
var channelService = me.app.get('channelService');
|
||||
var channel = channelService.getChannel(tid, false);
|
||||
if(channel && channel.getMember(uid)){
|
||||
channel.leave(uid, channel.getMember(uid)['sid']);
|
||||
}
|
||||
var user = me.getPlayerJSON(tid, uid, 'players') || me.getPlayerJSON(tid, uid, 'playersToAdd') || me.getPlayerJSON(tid, uid, 'previousPlayers');
|
||||
if(user){
|
||||
console.log('adding '+user.chips+' to player '+user.id);
|
||||
me.updatePlayerInfo(uid, {
|
||||
chips : user.chips
|
||||
}, function(e, updatedUser){
|
||||
if(e){
|
||||
logger.error('error removing player '+uid+' from table ', e);
|
||||
}else{
|
||||
logger.debug('removed player '+uid+' from table '+tid);
|
||||
}
|
||||
me.tables[tid].table.removePlayer(uid);
|
||||
me.pushPlayerInfo(tid, uid, updatedUser);
|
||||
me.handleGameState(tid, cb);
|
||||
});
|
||||
}else{
|
||||
me.tables[tid].table.removePlayer(uid);
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update player information
|
||||
*
|
||||
* @param {string} uid id of a user to update
|
||||
* @param {object} obj updated player information
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.updatePlayerInfo = function(uid, obj, cb){
|
||||
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||
if(e){
|
||||
return cb(e);
|
||||
}
|
||||
if(!user){
|
||||
return cb('user-not-found');
|
||||
}
|
||||
var userObj = {
|
||||
id : user.id
|
||||
};
|
||||
if(obj.chips && typeof obj.chips === 'number' && obj.chips != 0){
|
||||
userObj.chips = Math.round(user.chips + Math.round(obj.chips))
|
||||
}
|
||||
if(obj.wins){
|
||||
userObj.wins = Math.round(user.wins + Math.round(obj.wins))
|
||||
}
|
||||
if(obj.wonAmount && obj.wonAmount > user.largestWin){
|
||||
userObj.largestWin = obj.wonAmount;
|
||||
}
|
||||
UserStore.set(userObj, function(e, updatedUser){
|
||||
if(e){
|
||||
cb(e);
|
||||
return;
|
||||
}
|
||||
cb(null, updatedUser);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
TableService.prototype.getTableJSON = function(tid, uid){
|
||||
if(!this.tables[tid]){
|
||||
return;
|
||||
}
|
||||
var table = this.tables[tid];
|
||||
return {
|
||||
state : table.state,
|
||||
id : (table.table && table.table.game && table.table.game.id ? table.table.game.id : undefined),
|
||||
tid : tid,
|
||||
creator : table.creator,
|
||||
smallBlind : table.table.smallBlind,
|
||||
bigBlind : table.table.bigBlind,
|
||||
minPlayers : table.table.minPlayers,
|
||||
maxPlayers : table.table.maxPlayers,
|
||||
minBuyIn : table.table.minBuyIn,
|
||||
maxBuyIn : table.table.maxBuyIn,
|
||||
gameMode : table.table.gameMode,
|
||||
players : this.getPlayersJSON(tid, 'players', uid),
|
||||
playersToRemove : this.getPlayersJSON(tid, 'playersToRemove', uid),
|
||||
playersToAdd : this.getPlayersJSON(tid, 'playersToAdd', uid),
|
||||
gameWinners : this.getPlayersJSON(tid, 'gameWinners', uid),
|
||||
actions : table.table.actions,
|
||||
game : stripDeck(table.table.game, ['deck', 'id']),
|
||||
board : (table.table.game && table.table.game.board) ? table.table.game.board : [],
|
||||
currentPlayer : table.table.currentPlayer
|
||||
};
|
||||
};
|
||||
|
||||
function stripDeck(obj, props){
|
||||
var out = {};
|
||||
for(var key in obj){
|
||||
if(props.indexOf(key) == -1){
|
||||
out[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
TableService.prototype.getPlayerIndex = function(tid, uid, type){
|
||||
var match;
|
||||
if(!this.tables[tid]){
|
||||
return;
|
||||
}
|
||||
for(var i=0;i<this.tables[tid].table[type ? type : 'players'].length;++i){
|
||||
if(uid == this.tables[tid].table[type ? type : 'players'][i].id){
|
||||
match = i;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
};
|
||||
|
||||
TableService.prototype.getPlayerJSON = function(tid, uid, type, requestUid){
|
||||
if(!this.tables[tid]){
|
||||
return;
|
||||
}
|
||||
var playerIndex = this.getPlayerIndex(tid, uid, type);
|
||||
var player = this.tables[tid].table[type ? type : 'players'][playerIndex];
|
||||
return player ? {
|
||||
playerName : player.playerName,
|
||||
id : player.id,
|
||||
chips : player.chips,
|
||||
folded : player.folded,
|
||||
allIn : player.allIn,
|
||||
talked : player.talked,
|
||||
amount : player.amount,
|
||||
cards : (typeof requestUid === 'undefined' || player.id == requestUid) ? player.cards : undefined,
|
||||
} : undefined;
|
||||
};
|
||||
|
||||
TableService.prototype.getPlayersJSON = function(tid, type, requestUid){
|
||||
var players = [];
|
||||
if(!this.tables[tid]){
|
||||
return;
|
||||
}
|
||||
for(var i=0;i<this.tables[tid].table[type ? type : 'players'].length;++i){
|
||||
players.push(this.getPlayerJSON(tid, this.tables[tid].table[type ? type : 'players'][i].id, type, requestUid));
|
||||
}
|
||||
return players;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a player to the game
|
||||
*
|
||||
* @param {Object} tid id of an existing table
|
||||
* @param {string} uid userId to add to the table
|
||||
* @param {number} buyIn amount to buy in
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.addPlayer = function(tid, uid, buyIn, cb){
|
||||
var me = this;
|
||||
if(!this.tables[tid]){
|
||||
return cb('table-not-found');
|
||||
}
|
||||
var table = this.tables[tid].table;
|
||||
if(me.getPlayerIndex(tid, uid, 'playersToAdd')){
|
||||
return cb('already-joined');
|
||||
}
|
||||
buyIn = parseInt(buyIn);
|
||||
if(isNaN(buyIn) || buyIn < table.minBuyIn || buyIn > table.maxBuyIn){
|
||||
cb('invalid-buyin');
|
||||
return;
|
||||
}
|
||||
buyIn = Math.round(buyIn);
|
||||
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||
if(e){
|
||||
cb(e);
|
||||
return;
|
||||
}
|
||||
if(Math.round(user.chips) < table.minBuyIn){
|
||||
cb('below-minimum-buyin');
|
||||
return;
|
||||
}
|
||||
if(Math.round(user.chips) < buyIn){
|
||||
cb('not-enough-chips');
|
||||
return;
|
||||
}
|
||||
var chips = Math.round(user.chips - buyIn);
|
||||
UserStore.set({
|
||||
id : user.id,
|
||||
chips : chips
|
||||
}, function(e, updatedUser){
|
||||
if(e){
|
||||
cb(e);
|
||||
return;
|
||||
}
|
||||
table.eventEmitter.emit('playerJoined');
|
||||
var mIndex = me.getPlayerIndex(tid, updatedUser.id, 'members');
|
||||
if(typeof mIndex === 'number'){
|
||||
table.members[mIndex].chips = chips;
|
||||
}
|
||||
table.AddPlayer(updatedUser.username, buyIn, uid);
|
||||
me.pushPlayerInfo(tid, user.id, updatedUser);
|
||||
me.app.get('channelService').getChannel(tid, true).pushMessage({
|
||||
route : 'onUpdateUsers',
|
||||
members : table.members
|
||||
});
|
||||
me.app.get('channelService').getChannel(tid, true).pushMessage({
|
||||
route : 'onTableJoin',
|
||||
msg : me.getPlayerJSON(tid, uid, 'playersToAdd') || me.getPlayerJSON(tid, uid)
|
||||
});
|
||||
cb();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Push detailed user information to a user
|
||||
*
|
||||
* @param {Object} tid id of an existing table
|
||||
* @param {string} uid userId to add to the table
|
||||
* @param {object} info player information
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.pushPlayerInfo = function(tid, uid, info){
|
||||
var channelService = this.app.get('channelService');
|
||||
var channel = channelService.getChannel(tid, false);
|
||||
if(!channel || !channel.getMember(uid)) return;
|
||||
channelService.pushMessageByUids({
|
||||
route : 'onUpdateMyself',
|
||||
user : info
|
||||
}, [{
|
||||
uid : uid,
|
||||
sid : channel.getMember(uid)['sid']
|
||||
}], function(e){
|
||||
if(e){
|
||||
logger.error('unable to push player info ', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Start the game
|
||||
*
|
||||
* @param {Object} tid id of an existing table
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.startGame = function(tid, cb){
|
||||
var table = this.tables[tid];
|
||||
if(!table){
|
||||
return cb('table-not-found');
|
||||
}
|
||||
if(table.state != 'JOIN'){
|
||||
return cb('table-not-ready');
|
||||
}
|
||||
if(table.table.active){
|
||||
return cb('table-still-active');
|
||||
}
|
||||
if(table.table.playersToAdd.length < table.table.minPlayers){
|
||||
return cb('not-enough-players');
|
||||
}
|
||||
if(table.table.playersToAdd.length > table.table.maxPlayers){
|
||||
return cb('too-many-players');
|
||||
}
|
||||
// remove chips from user for buy in
|
||||
table.table.StartGame();
|
||||
this.app.get('channelService').getChannel(tid, true).pushMessage({
|
||||
route : 'onUpdateUsers',
|
||||
members : table.table.members
|
||||
});
|
||||
this.broadcastGameState(tid);
|
||||
cb();
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform a game action
|
||||
*
|
||||
* @param {string} tid table id
|
||||
* @param {string} uid userId to add to the table
|
||||
* @param {object} action an object containing the action type and optionally the amount of chips
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.performAction = function(tid, uid, action, cb){
|
||||
var me = this;
|
||||
var table = this.tables[tid];
|
||||
if(!table){
|
||||
return cb('table-not-found');
|
||||
}
|
||||
if(table.state != 'IN_PROGRESS'){
|
||||
return cb('game-not-ready');
|
||||
}
|
||||
if(me.getPlayerIndex(tid, uid) != table.table.currentPlayer){
|
||||
return cb('not-your-turn');
|
||||
}
|
||||
if(me.getPlayerJSON(tid, uid).folded == true){
|
||||
return cb('already-folded');
|
||||
}
|
||||
if(action.action == 'bet' && isNaN(action.amt)){
|
||||
return cb('invalid-bet-amt');
|
||||
}
|
||||
// perform action
|
||||
if(action.action == 'call'){
|
||||
table.table.players[table.table.currentPlayer].Call();
|
||||
}else if(action.action == 'bet'){
|
||||
table.table.players[table.table.currentPlayer].Bet(parseInt(action.amt));
|
||||
}else if(action.action == 'check'){
|
||||
table.table.players[table.table.currentPlayer].Check();
|
||||
}else if(action.action == 'allin'){
|
||||
table.table.players[table.table.currentPlayer].AllIn();
|
||||
}else if(action.action == 'fold'){
|
||||
table.table.players[table.table.currentPlayer].Fold();
|
||||
}else{
|
||||
return cb('invalid-action');
|
||||
}
|
||||
table.table.stopTimer();
|
||||
logger.debug('player '+uid+' executed action '+action.action+' on table '+tid+' with state '+table.state);
|
||||
me.handleGameState(tid, function(e){
|
||||
if(e){
|
||||
return cb(e);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* End game and broadcast result to clients
|
||||
*
|
||||
* @param {string} tid table id
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.endGame = function(tid, cb){
|
||||
var me = this;
|
||||
if(!me.tables[tid]){
|
||||
cb('table-not-found');
|
||||
return;
|
||||
}
|
||||
var table = me.tables[tid];
|
||||
if(table.table.game.roundName != 'GameEnd'){
|
||||
cb('not-game-end');
|
||||
return;
|
||||
}
|
||||
table.table.active = false;
|
||||
table.table.stopTimer();
|
||||
me.saveResults(tid, function(e){
|
||||
if(e){
|
||||
cb(e);
|
||||
return;
|
||||
}
|
||||
var channelService = me.app.get('channelService');
|
||||
channelService.getChannel(tid, false).pushMessage({
|
||||
route : 'onUpdateUsers',
|
||||
members : table.table.members
|
||||
});
|
||||
table.table.initNewGame();
|
||||
me.broadcastGameState(tid);
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Store table results to persistence
|
||||
*
|
||||
* @param {string} tid id of the table
|
||||
* @param {string} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.saveResults = function(tid, cb){
|
||||
var me = this;
|
||||
if(!this.tables[tid]){
|
||||
cb('table-not-found');
|
||||
}
|
||||
var table = this.tables[tid];
|
||||
TableStore.getByAttr('id', table.table.game.id, function(e, foundTable){
|
||||
if(foundTable){
|
||||
cb('game-already-exists');
|
||||
return;
|
||||
}
|
||||
TableStore.create(me.getTableJSON(tid), function(e, newTable){
|
||||
if(e){
|
||||
cb(e);
|
||||
return;
|
||||
}
|
||||
var i = 0;
|
||||
function saveWinner(){
|
||||
me.updatePlayerInfo(table.table.gameWinners[i].id, {
|
||||
wins : 1,
|
||||
wonAmount : table.table.gameWinners[i].amount
|
||||
}, function(){
|
||||
if(++i === table.table.gameWinners.length){
|
||||
cb();
|
||||
}else{
|
||||
saveWinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
if(table.table.gameWinners.length){
|
||||
saveWinner();
|
||||
}else{
|
||||
return cb();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle end of game or broadcast game state to users
|
||||
*
|
||||
* @param {string} tid id of the table
|
||||
* @param {function} cb callback
|
||||
*
|
||||
*/
|
||||
TableService.prototype.handleGameState = function(tid, cb){
|
||||
var me = this;
|
||||
var table = me.tables[tid];
|
||||
if(table.table && table.table.game && table.table.game.roundName == 'GameEnd' && table.state == 'IN_PROGRESS' && table.table.active){
|
||||
me.endGame(tid, cb);
|
||||
}else{
|
||||
me.app.get('channelService').getChannel(tid, true).pushMessage({
|
||||
route : 'onUpdateUsers',
|
||||
members : table.table.members
|
||||
});
|
||||
me.broadcastGameState(tid);
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Broadcast game state by iteratively pushing game details to clients
|
||||
*
|
||||
* @param {string} tid id
|
||||
*
|
||||
*/
|
||||
TableService.prototype.broadcastGameState = function(tid){
|
||||
var i = 0;
|
||||
var me = this;
|
||||
var channelService = me.app.get('channelService');
|
||||
var channel = channelService.getChannel(tid, false);
|
||||
function broadcast(){
|
||||
if(i == me.tables[tid].table.members.length){
|
||||
if(me.tables[tid].state == 'IN_PROGRESS' && me.tables[tid].table.active){
|
||||
me.tables[tid].table.startTimer();
|
||||
}
|
||||
return;
|
||||
}
|
||||
var uid = me.tables[tid].table.members[i].id;
|
||||
if(channel.getMember(uid)){
|
||||
channelService.pushMessageByUids({
|
||||
route : 'onTableEvent',
|
||||
msg : me.getTableJSON(tid, uid)
|
||||
}, [{
|
||||
uid : uid,
|
||||
sid : channel.getMember(uid)['sid']
|
||||
}], function(){
|
||||
++i;
|
||||
broadcast();
|
||||
});
|
||||
}else{
|
||||
++i;
|
||||
broadcast();
|
||||
}
|
||||
}
|
||||
broadcast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles an array
|
||||
*
|
||||
* @param {array} ary an array
|
||||
*
|
||||
*/
|
||||
TableService.prototype.shuffle = function(ary){
|
||||
var currentIndex = ary.length, temporaryValue, randomIndex;
|
||||
while(0 !== currentIndex){
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex -= 1;
|
||||
temporaryValue = ary[currentIndex];
|
||||
ary[currentIndex] = ary[randomIndex];
|
||||
ary[randomIndex] = temporaryValue;
|
||||
}
|
||||
return ary;
|
||||
};
|
||||
|
Reference in New Issue
Block a user