module.exports = Eventer.extend({ init: function(task, worker) { this.worker = worker; this.task = task; this.ID = task.ID; this.updating = {}; this.started = false; var self = this; this.done = _.after(3,function() { self.emit('loaded.all', {updating: self.updating}); if(_(self.updating).size() > 0){ self.updated = _.after(_(self.updating).size(), function() { self.calculateRatings(); }); } }); }, startLoading: function() { this.started = true; this.getPlayerInfo(); this.getPlayerStats(); this.getPlayerTanks(); }, setPlayerInfo: function(player) { if(!player){ player = this.worker.Players.new({id: this.ID}); } this.player = player; this.emit('loaded.info', player.getData()); this.done(); }, getPlayerInfo: function() { var self = this; this.worker.Players.find(this.ID, function(err, player){ self.setPlayerInfo(player); }); }, setPlayerStats: function(stats) { var self = this; if(!stats){ stats = this.worker.PlayerStats.new({player_id: this.ID}); } this.stats = stats; if(stats.needsUpdate()){ this.updating.stats = true; this.updating.info = true; this.emit('update.info', function(event, data) { self.updateStats(data); }); } this.emit('loaded.stats', stats.getData()); this.done(); }, getPlayerStats: function() { var self = this; this.worker.PlayerStats.findByPlayerID(this.ID, function(err, stats){ self.setPlayerStats(stats); }); }, setPlayerTanks: function(tanks) { var self = this; if(!tanks){ tanks = this.worker.PlayerTankCollections.new({p: this.ID}); } this.tanks = tanks; if(tanks.needsUpdate()){ this.updating.tanks = true; this.emit('update.tanks', function(event, data) { self.updateTanks(data); }); } this.emit('loaded.tanks', tanks.getData()); this.done(); }, getPlayerTanks: function() { var self = this; this.worker.PlayerTankCollections.findByPlayerID(this.ID, function(err, tanks){ self.setPlayerTanks(tanks); }); }, updateTanks: function(data) { var self = this; this.tanks.update(data, function() { self.emit('updated.tanks', self.tanks.getData()); self.updated(); }); }, updateStats: function(data) { var self = this; this.player.update(data, function() { self.emit('updated.info', self.player.getData()); self.updated(); }); this.stats.update(data.statistics, function() { self.emit('updated.stats', self.stats.getData()); self.updated(); }); }, calculateRatings: function() { var self = this; this.stats.average_tier = this.tanks.getAverageTier(); this.stats.expected_values = this.tanks.getExpectedValues(); this.stats.calcWN7(); this.stats.calcEFR(); this.stats.calcWN8(); this.stats.sc3 = this.stats.wn7/1500*this.tanks.base_score; this.stats.saveAll(function() { self.emit('updated.ratings', self.stats.getData()); self.emit('updated.all'); }); } });
module.exports = Eventer.extend({ init: function(options){ this.tasks = {}; this.options = options; }, setModels: function(models) { this.models = models; _.each(this.models, function(model, name) { this[name] = model; },this); }, setTask: function(task, callback) { var self = this; var taskID = _.keys(task)[0]; task = task[taskID]; if(!task){ console.log('Set task, but it is undefined:',taskID); return; } this.Clans.inRegion(task.region,{order: 'id',limit: [task.skip, task.limit]}, function(err, clans) { var goodClans = []; _.each(clans, function(clan){ if(clan.status > -1){ goodClans.push(clan); } }); var count = goodClans.length; var done = 0; _.each(goodClans, function(clan){ self.once('clans.'+clan.id+'.updated', function(){ done++; if(done == count){ self.emit('finish-task', taskID, {count: done}); } }); }); if(done == count){ self.emit('finish-task', taskID, {count: done}); self.emit('ready', self.options); return; } var newTask = { ID: taskID, region: Regions.TranslatedRegion[task.region], skip: task.skip/task.limit, clans: _(goodClans).map(function(clan){return clan.id;}) }; callback(newTask); newTask.clans = goodClans; self.tasks[taskID] = newTask; }); }, getUnfinishedTasks: function() { return _(this.tasks).keys(); }, processTask: function (ID, data){ if(!this.tasks[ID]){ console.log('No such task:',ID); return; } var clans = this.tasks[ID].clans; delete this.tasks[ID]; var IDs = _.pluck(clans,'id'); var self = this; this.Players.where(['clan_id IN ?', IDs], function(err, players) { var clanPlayers = {}; _.each(players, function(player){ if(!clanPlayers[player.clan_id]){ clanPlayers[player.clan_id] = []; } clanPlayers[player.clan_id].push(player); }); _.each(clans, function(clan){ clan.members = clanPlayers[clan.id] || []; clan.on('*',function(){ var args = _.toArray(arguments); var event = args.shift(); args.unshift('clans.'+clan.id+'.'+event); self.emit.apply(self, args); }); clan.update(data[clan.id]); }); }); } });
module.exports = Eventer.extend({ init: function(app) { this.app = app; this.toDoQueue = []; this.pendingQueue = {}; this.pendingID = 0; this.clansPerItem = 30; this.queueTreshold = 50; this.totalCount = 0; this.doneCount = 0; this.finishedClans = 0; this.finishedTasks = 0; this.errorTasks = 0; this.speed = 0; this.fillingQueue = false; this.start = new Date(); this.regionStats = {}; _.each(Regions.supportedRegions, function(region) { this.regionStats[region] = { pending: 0, errors: [] }; }, this); this.recentTasks = []; }, setModels: function(models){ this.models = models; _.each(this.models, function(model, name) { this[name] = model; },this); }, fillQueue: function() { if(!this.models || this.fillingQueue){ return; } var self = this; this.fillingQueue = true; var tempQueues = []; var buildQueue = _.after(Regions.supportedRegions.length, function(){ var queue = []; tempQueues.sort(function(a, b) { return a.length - b.length; }); _.each(tempQueues, function(q) { if(q){ queue = queue.length > 0 ? self.mixArrays(q, queue) : q; } }); self.toDoQueue.push.apply(self.toDoQueue, queue); self.fillingQueue = false; self.totalCount = self.toDoQueue.length; self.doneCount = 0; self.cycleStart = new Date(); self.emit('filled'); self.emit('update', self.getCurrentStatus(), true); }); _.each(Regions.supportedRegions, function(region) { this.Clans.countInRegion(region, function(err, count) { tempQueues[region] = []; for(var i = 0; i < count/self.clansPerItem; i++){ tempQueues[region].push({ skip: i*self.clansPerItem, limit: self.clansPerItem, region: region, retryCount: 0 }); } buildQueue(); }); }, this); }, mixArrays: function (a1, a2){ var result = []; var length = a2.length; for(var i = 0; i < length; i++){ var ratio = a1.length/a2.length; result.push(a2.shift()); for(var j = 0; j < Math.round(ratio); j++){ result.push(a1.shift()); } } return result; }, getCurrentStatus: function() { return { totalCount: this.totalCount, doneCount: this.doneCount, pending: _.size(this.pendingQueue), finishedClans: this.finishedClans, finishedTasks: this.finishedTasks, errorTasks: this.errorTasks, speed: this.speed, start: this.start, cycleStart: this.start } }, tooManyErrors: function(i) { var task = this.toDoQueue[i]; var errors = this.regionStats[task.region].errors; if(errors.length == 0){ return false; } var duration = (new Date()).getTime() - _(errors).first().getTime(); return errors.length/duration*1000 > 0.25; }, notInRegion: function(i, region) { if(region === undefined){ return false; } var task = this.toDoQueue[i]; return region != task.region; }, getFromQueue: function(callback, workerID, options) { var self = this; if(this.toDoQueue.length == 0){ this.once('filled', function() { self.getFromQueue(callback, workerID, options); }); }else{ var ret = {}; var ID = this.pendingID++; setTimeout(function(){ if(self.pendingQueue[ID]){ console.log('Task timeout:', ID); self.reportFail(ID); } },60000); var i = 0; while(i<this.toDoQueue.length-1 && (this.tooManyErrors(i) || this.notInRegion(i, options.region))){i++;} var elem = this.toDoQueue.splice(i,1)[0]; this.pendingQueue[ID] = elem; if(i == this.toDoQueue.length-1 && !this.fillingQueue){ this.fillQueue(); } ret[ID] = elem; this.emit('update', this.getCurrentStatus(), true); callback(ret); } if(this.toDoQueue.length < this.queueTreshold && !this.fillingQueue){ this.fillQueue(); } _.each(Regions.supportedRegions, function(region) { while(this.regionStats[region].errors.length > 0 && (new Date()).getTime() - this.regionStats[region].errors[0].getTime() > 20*1000 ){ this.regionStats[region].errors.shift(); } }, this); }, confirmSuccess: function(ID, data) { delete this.pendingQueue[ID]; this.doneCount++; this.finishedTasks++; if(data){ this.finishedClans += data.count; } this.calcSpeed(data.count); this.emit('update', this.getCurrentStatus(), true); }, calcSpeed: function(count) { if(count){ this.recentTasks.push({ count: count, time: new Date() }); } while(this.recentTasks.length > 0 && (new Date()).getTime() - this.recentTasks[0].time.getTime() > 5*1000 ){ this.recentTasks.shift(); } if(this.recentTasks.length > 1){ var sum = _.chain(this.recentTasks).pluck('count').reduce(function(memo, num){ return memo + num; }, 0).value(); var duration = _(this.recentTasks).last().time.getTime() - _(this.recentTasks).first().time.getTime(); this.speed = Math.round(sum/duration*1000*100)/100; } }, reportFail: function(ID) { if(!this.pendingQueue[ID]){ return; } this.errorTasks++; if(this.pendingQueue[ID].limit == 1){ this.pendingQueue[ID].retryCount++; if(this.pendingQueue[ID].retryCount < 3){ this.toDoQueue.unshift(this.pendingQueue[ID]); }else{ console.log('Too many retries:',this.pendingQueue[ID]); } } else { this.splitTask(this.pendingQueue[ID]); } var region = this.pendingQueue[ID].region; delete this.pendingQueue[ID]; this.regionStats[region].errors.push(new Date()); this.calcSpeed(); this.emit('update', this.getCurrentStatus(), true); }, splitTask: function(task) { var task1 = {skip: task.skip, limit: Math.round(task.limit/2), region: task.region, retryCount: 0}; var task2 = {skip: task.skip+task1.limit, limit: task.limit-task1.limit, region: task.region, retryCount: 0}; //console.log('Split',task,'into',task1,task2); this.toDoQueue.unshift(task1); this.toDoQueue.unshift(task2); this.totalCount++; } });
module.exports = Eventer.extend({ init: function(ID, manager, controller) { this.ID = ID; this.controller = controller; this.manager = manager; this.done = false; this.loaded = false; this.lastAccessed = new Date(); this.maxAge = Config.caches.Clan.maxAge; this.IDs = []; this.caches = {}; var self = this; this.controller.Players.inClan(ID, function(err, players) { _(players).each(function(player) { self.IDs.push(player.id); var playerCache = self.controller.getCache('player.'+player.id); if(!playerCache){ playerCache = new PlayerCache(player.id, self.manager); self.controller.setCache('player.'+player.id, playerCache); } self.caches[player.id] = playerCache; }); self.setCacheCallbacks(); }); this.interval = setInterval(function(){ _(self.caches).each(function(cache) { cache.lastAccessed = new Date(); }); },1000); }, onClose: function() { if(this.done){ clearInterval(this.interval); } return this.done; }, setCacheCallbacks: function() { var self = this; _(this.caches).each(function(cache) { if(!cache.done){ cache.once('done', function() { if(self.getProgress() >= 100){ self.emit('done'); self.done = true; } }); } }); var counter = 0; _(this.caches).each(function(cache) { if(!cache.loaded){ counter++; cache.once('loaded', function() { if(--counter <= 0){ self.emit('loaded'); self.loaded = true; } }); } }); }, getProgress: function() { var count = 0; var progress = 0; _(this.caches).each(function(cache) { count += 1; progress += cache.progress; }); return progress/count; }, getData: function(options) { this.lastAccessed = new Date(); options = options || {}; return { progress: this.getProgress(), players: _.object(_(this.caches).keys(), _(this.caches).map(function(cache) { return cache.getData(options.players); })) }; } });