diff --git a/app/lib/simulator/Simulator.coffee b/app/lib/simulator/Simulator.coffee index 9805f38bc..2ec95bfef 100644 --- a/app/lib/simulator/Simulator.coffee +++ b/app/lib/simulator/Simulator.coffee @@ -5,9 +5,23 @@ GoalManager = require 'lib/world/GoalManager' God = require 'lib/God' {createAetherOptions} = require 'lib/aether_utils' +SIMULATOR_VERSION = 1 + +simulatorInfo = {} +if $.browser + simulatorInfo['desktop'] = $.browser.desktop if $.browser.desktop + simulatorInfo['name'] = $.browser.name if $.browser.name + simulatorInfo['platform'] = $.browser.platform if $.browser.platform + simulatorInfo['version'] = $.browser.versionNumber if $.browser.versionNumber + module.exports = class Simulator extends CocoClass constructor: (@options) -> @options ?= {} + simulatorType = if @options.headlessClient then 'headless' else 'browser' + @simulator = + type: simulatorType + version: SIMULATOR_VERSION + info: simulatorInfo _.extend @, Backbone.Events @trigger 'statusUpdate', 'Starting simulation!' @retryDelayInSeconds = 2 @@ -28,10 +42,17 @@ module.exports = class Simulator extends CocoClass type: 'POST' parse: true data: - 'humansGameID': humanGameID - 'ogresGameID': ogresGameID + humansGameID: humanGameID + ogresGameID: ogresGameID + simulator: @simulator error: (errorData) -> console.warn "There was an error fetching two games! #{JSON.stringify errorData}" + if errorData?.responseText?.indexOf("Old simulator") isnt -1 + noty { + text: errorData.responseText + layout: 'center' + type: 'error' + } success: (taskData) => return if @destroyed unless taskData @@ -278,7 +299,6 @@ module.exports = class Simulator extends CocoClass return if @destroyed console.log "Task registration result: #{JSON.stringify result}" @trigger 'statusUpdate', 'Results were successfully sent back to server!' - console.log 'Simulated by you:', @simulatedByYou @simulatedByYou++ unless @options.headlessClient simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1 @@ -307,6 +327,7 @@ module.exports = class Simulator extends CocoClass originalSessionRank: -1 calculationTime: 500 sessions: [] + simulator: @simulator for session in @task.getSessions() sessionResult = diff --git a/app/schemas/models/level_session.coffee b/app/schemas/models/level_session.coffee index 6942b8f6b..41f585f43 100644 --- a/app/schemas/models/level_session.coffee +++ b/app/schemas/models/level_session.coffee @@ -285,6 +285,7 @@ _.extend LevelSessionSchema.properties, codeLanguage: type: ['string', 'null'] # 'null' in case an opponent session got corrupted, don't care much here description: 'What submittedCodeLanguage the opponent used during the match' + simulator: {type: 'object', description: 'Holds info on who simulated the match, and with what tools.'} c.extendBasicProperties LevelSessionSchema, 'level.session' c.extendPermissionsProperties LevelSessionSchema, 'level.session' diff --git a/app/templates/play/ladder/my_matches_tab.jade b/app/templates/play/ladder/my_matches_tab.jade index bf4d9e856..5c1a12543 100644 --- a/app/templates/play/ladder/my_matches_tab.jade +++ b/app/templates/play/ladder/my_matches_tab.jade @@ -31,7 +31,7 @@ div#columns.row th(data-i18n="general.when") When th for match in team.matches - tr(class=(match.stale ? "stale " : "") + (match.fresh ? "fresh " : "") + match.state) + tr(class=(match.stale ? "stale " : "") + (match.fresh ? "fresh " : "") + match.state, title=match.simulator) td.state-cell if match.state === 'win' span(data-i18n="general.win").win Win diff --git a/app/views/ladder/MyMatchesTabView.coffee b/app/views/ladder/MyMatchesTabView.coffee index eebc92ab8..ecc2370e0 100644 --- a/app/views/ladder/MyMatchesTabView.coffee +++ b/app/views/ladder/MyMatchesTabView.coffee @@ -72,6 +72,7 @@ module.exports = class MyMatchesTabView extends CocoView stale: match.date < submitDate fresh: fresh codeLanguage: match.codeLanguage + simulator: JSON.stringify(match.simulator) } for team in @teams diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee index f97403e1d..74f822243 100644 --- a/server/queues/scoring.coffee +++ b/server/queues/scoring.coffee @@ -15,6 +15,8 @@ bayes = new (require 'bayesian-battle')() scoringTaskQueue = undefined scoringTaskTimeoutInSeconds = 600 +SIMULATOR_VERSION = 1 + module.exports.setup = (app) -> connectToScoringQueue() connectToScoringQueue = -> @@ -124,6 +126,7 @@ module.exports.getTwoGames = (req, res) -> #if userIsAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.') humansGameID = req.body.humansGameID ogresGameID = req.body.ogresGameID + return if simulatorIsTooOld req, res #ladderGameIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span'] # Let's not give any extra simulations to old ladders. ladderGameIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove'] levelID = _.sample ladderGameIDs @@ -224,6 +227,8 @@ module.exports.getTwoGames = (req, res) -> module.exports.recordTwoGames = (req, res) -> sessions = req.body.sessions #console.log 'Recording non-chained result of', sessions?[0]?.name, sessions[0]?.metrics?.rank, 'and', sessions?[1]?.name, sessions?[1]?.metrics?.rank + return if simulatorIsTooOld req, res + req.body?.simulator?.user = '' + req.user?._id yetiGuru = clientResponseObject: req.body, isRandomMatch: true async.waterfall [ @@ -442,7 +447,9 @@ getSessionInformation = (sessionIDString, callback) -> callback null, session module.exports.processTaskResult = (req, res) -> + return if simulatorIsTooOld req, res originalSessionID = req.body?.originalSessionID + req.body?.simulator?.user = '' + req.user?._id yetiGuru = {} try async.waterfall [ @@ -578,14 +585,14 @@ addMatchToSessions = (newScoreObject, callback) -> matchObject.opponents = {} for session in @clientResponseObject.sessions sessionID = session.sessionID - matchObject.opponents[sessionID] = {} - matchObject.opponents[sessionID].sessionID = sessionID - matchObject.opponents[sessionID].userID = session.creator - matchObject.opponents[sessionID].name = session.name - matchObject.opponents[sessionID].totalScore = session.totalScore - matchObject.opponents[sessionID].metrics = {} - matchObject.opponents[sessionID].metrics.rank = Number(newScoreObject[sessionID]?.gameRanking ? 0) - matchObject.opponents[sessionID].codeLanguage = newScoreObject[sessionID].submittedCodeLanguage + matchObject.opponents[sessionID] = match = {} + match.sessionID = sessionID + match.userID = session.creator + match.name = session.name + match.totalScore = session.totalScore + match.metrics = {} + match.metrics.rank = Number(newScoreObject[sessionID]?.gameRanking ? 0) + match.codeLanguage = newScoreObject[sessionID].submittedCodeLanguage #log.info "Match object computed, result: #{matchObject}" #log.info 'Writing match object to database...' @@ -603,6 +610,7 @@ updateMatchesInSession = (matchObject, sessionID, callback) -> opponentsArray = _.toArray opponentsClone currentMatchObject.opponents = opponentsArray currentMatchObject.codeLanguage = matchObject.opponents[opponentsArray[0].sessionID].codeLanguage + currentMatchObject.simulator = @clientResponseObject.simulator LevelSession.findOne {'_id': sessionID}, (err, session) -> session = session.toObject() currentMatchObject.playtime = session.playtime ? 0 @@ -785,3 +793,12 @@ retrieveOldSessionData = (sessionID, callback) -> markSessionAsDoneRanking = (sessionID, cb) -> #console.log 'Marking session as done ranking...' LevelSession.update {'_id': sessionID}, {'isRanking': false}, cb + +simulatorIsTooOld = (req, res) -> + clientSimulator = req.body.simulator + return false if clientSimulator?.version >= SIMULATOR_VERSION + message = "Old simulator version #{clientSimulator?.version}, need to clear cache and get version #{SIMULATOR_VERSION}." + log.debug "400: #{message}" + res.send 400, message + res.end() + true diff --git a/server_setup.coffee b/server_setup.coffee index df81d50a0..ab99f4a04 100644 --- a/server_setup.coffee +++ b/server_setup.coffee @@ -50,7 +50,7 @@ setupErrorMiddleware = (app) -> res.status(err.status ? 500).send(error: "Something went wrong!") message = "Express error: #{req.method} #{req.path}: #{err.message}" log.error "#{message}, stack: #{err.stack}" - hipchat.sendTowerHipChatMessage(message) + hipchat.sendHipChatMessage(message, ['tower'], {papertrail: true}) else next(err) setupExpressMiddleware = (app) ->