mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-30 10:56:53 -05:00
Tracking who is simulating matches so we can see patterns in ill-reported matches. Rejecting simulations from simulators with old versions of the Simulator code.
This commit is contained in:
parent
2efb6aafbc
commit
63fa2f86d4
6 changed files with 53 additions and 13 deletions
|
@ -5,9 +5,23 @@ GoalManager = require 'lib/world/GoalManager'
|
||||||
God = require 'lib/God'
|
God = require 'lib/God'
|
||||||
{createAetherOptions} = require 'lib/aether_utils'
|
{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
|
module.exports = class Simulator extends CocoClass
|
||||||
constructor: (@options) ->
|
constructor: (@options) ->
|
||||||
@options ?= {}
|
@options ?= {}
|
||||||
|
simulatorType = if @options.headlessClient then 'headless' else 'browser'
|
||||||
|
@simulator =
|
||||||
|
type: simulatorType
|
||||||
|
version: SIMULATOR_VERSION
|
||||||
|
info: simulatorInfo
|
||||||
_.extend @, Backbone.Events
|
_.extend @, Backbone.Events
|
||||||
@trigger 'statusUpdate', 'Starting simulation!'
|
@trigger 'statusUpdate', 'Starting simulation!'
|
||||||
@retryDelayInSeconds = 2
|
@retryDelayInSeconds = 2
|
||||||
|
@ -28,10 +42,17 @@ module.exports = class Simulator extends CocoClass
|
||||||
type: 'POST'
|
type: 'POST'
|
||||||
parse: true
|
parse: true
|
||||||
data:
|
data:
|
||||||
'humansGameID': humanGameID
|
humansGameID: humanGameID
|
||||||
'ogresGameID': ogresGameID
|
ogresGameID: ogresGameID
|
||||||
|
simulator: @simulator
|
||||||
error: (errorData) ->
|
error: (errorData) ->
|
||||||
console.warn "There was an error fetching two games! #{JSON.stringify 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) =>
|
success: (taskData) =>
|
||||||
return if @destroyed
|
return if @destroyed
|
||||||
unless taskData
|
unless taskData
|
||||||
|
@ -278,7 +299,6 @@ module.exports = class Simulator extends CocoClass
|
||||||
return if @destroyed
|
return if @destroyed
|
||||||
console.log "Task registration result: #{JSON.stringify result}"
|
console.log "Task registration result: #{JSON.stringify result}"
|
||||||
@trigger 'statusUpdate', 'Results were successfully sent back to server!'
|
@trigger 'statusUpdate', 'Results were successfully sent back to server!'
|
||||||
console.log 'Simulated by you:', @simulatedByYou
|
|
||||||
@simulatedByYou++
|
@simulatedByYou++
|
||||||
unless @options.headlessClient
|
unless @options.headlessClient
|
||||||
simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1
|
simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1
|
||||||
|
@ -307,6 +327,7 @@ module.exports = class Simulator extends CocoClass
|
||||||
originalSessionRank: -1
|
originalSessionRank: -1
|
||||||
calculationTime: 500
|
calculationTime: 500
|
||||||
sessions: []
|
sessions: []
|
||||||
|
simulator: @simulator
|
||||||
|
|
||||||
for session in @task.getSessions()
|
for session in @task.getSessions()
|
||||||
sessionResult =
|
sessionResult =
|
||||||
|
|
|
@ -285,6 +285,7 @@ _.extend LevelSessionSchema.properties,
|
||||||
codeLanguage:
|
codeLanguage:
|
||||||
type: ['string', 'null'] # 'null' in case an opponent session got corrupted, don't care much here
|
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'
|
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.extendBasicProperties LevelSessionSchema, 'level.session'
|
||||||
c.extendPermissionsProperties LevelSessionSchema, 'level.session'
|
c.extendPermissionsProperties LevelSessionSchema, 'level.session'
|
||||||
|
|
|
@ -31,7 +31,7 @@ div#columns.row
|
||||||
th(data-i18n="general.when") When
|
th(data-i18n="general.when") When
|
||||||
th
|
th
|
||||||
for match in team.matches
|
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
|
td.state-cell
|
||||||
if match.state === 'win'
|
if match.state === 'win'
|
||||||
span(data-i18n="general.win").win Win
|
span(data-i18n="general.win").win Win
|
||||||
|
|
|
@ -72,6 +72,7 @@ module.exports = class MyMatchesTabView extends CocoView
|
||||||
stale: match.date < submitDate
|
stale: match.date < submitDate
|
||||||
fresh: fresh
|
fresh: fresh
|
||||||
codeLanguage: match.codeLanguage
|
codeLanguage: match.codeLanguage
|
||||||
|
simulator: JSON.stringify(match.simulator)
|
||||||
}
|
}
|
||||||
|
|
||||||
for team in @teams
|
for team in @teams
|
||||||
|
|
|
@ -15,6 +15,8 @@ bayes = new (require 'bayesian-battle')()
|
||||||
scoringTaskQueue = undefined
|
scoringTaskQueue = undefined
|
||||||
scoringTaskTimeoutInSeconds = 600
|
scoringTaskTimeoutInSeconds = 600
|
||||||
|
|
||||||
|
SIMULATOR_VERSION = 1
|
||||||
|
|
||||||
module.exports.setup = (app) -> connectToScoringQueue()
|
module.exports.setup = (app) -> connectToScoringQueue()
|
||||||
|
|
||||||
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.')
|
#if userIsAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.')
|
||||||
humansGameID = req.body.humansGameID
|
humansGameID = req.body.humansGameID
|
||||||
ogresGameID = req.body.ogresGameID
|
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 = ['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']
|
ladderGameIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove']
|
||||||
levelID = _.sample ladderGameIDs
|
levelID = _.sample ladderGameIDs
|
||||||
|
@ -224,6 +227,8 @@ module.exports.getTwoGames = (req, res) ->
|
||||||
module.exports.recordTwoGames = (req, res) ->
|
module.exports.recordTwoGames = (req, res) ->
|
||||||
sessions = req.body.sessions
|
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
|
#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
|
yetiGuru = clientResponseObject: req.body, isRandomMatch: true
|
||||||
async.waterfall [
|
async.waterfall [
|
||||||
|
@ -442,7 +447,9 @@ getSessionInformation = (sessionIDString, callback) ->
|
||||||
callback null, session
|
callback null, session
|
||||||
|
|
||||||
module.exports.processTaskResult = (req, res) ->
|
module.exports.processTaskResult = (req, res) ->
|
||||||
|
return if simulatorIsTooOld req, res
|
||||||
originalSessionID = req.body?.originalSessionID
|
originalSessionID = req.body?.originalSessionID
|
||||||
|
req.body?.simulator?.user = '' + req.user?._id
|
||||||
yetiGuru = {}
|
yetiGuru = {}
|
||||||
try
|
try
|
||||||
async.waterfall [
|
async.waterfall [
|
||||||
|
@ -578,14 +585,14 @@ addMatchToSessions = (newScoreObject, callback) ->
|
||||||
matchObject.opponents = {}
|
matchObject.opponents = {}
|
||||||
for session in @clientResponseObject.sessions
|
for session in @clientResponseObject.sessions
|
||||||
sessionID = session.sessionID
|
sessionID = session.sessionID
|
||||||
matchObject.opponents[sessionID] = {}
|
matchObject.opponents[sessionID] = match = {}
|
||||||
matchObject.opponents[sessionID].sessionID = sessionID
|
match.sessionID = sessionID
|
||||||
matchObject.opponents[sessionID].userID = session.creator
|
match.userID = session.creator
|
||||||
matchObject.opponents[sessionID].name = session.name
|
match.name = session.name
|
||||||
matchObject.opponents[sessionID].totalScore = session.totalScore
|
match.totalScore = session.totalScore
|
||||||
matchObject.opponents[sessionID].metrics = {}
|
match.metrics = {}
|
||||||
matchObject.opponents[sessionID].metrics.rank = Number(newScoreObject[sessionID]?.gameRanking ? 0)
|
match.metrics.rank = Number(newScoreObject[sessionID]?.gameRanking ? 0)
|
||||||
matchObject.opponents[sessionID].codeLanguage = newScoreObject[sessionID].submittedCodeLanguage
|
match.codeLanguage = newScoreObject[sessionID].submittedCodeLanguage
|
||||||
|
|
||||||
#log.info "Match object computed, result: #{matchObject}"
|
#log.info "Match object computed, result: #{matchObject}"
|
||||||
#log.info 'Writing match object to database...'
|
#log.info 'Writing match object to database...'
|
||||||
|
@ -603,6 +610,7 @@ updateMatchesInSession = (matchObject, sessionID, callback) ->
|
||||||
opponentsArray = _.toArray opponentsClone
|
opponentsArray = _.toArray opponentsClone
|
||||||
currentMatchObject.opponents = opponentsArray
|
currentMatchObject.opponents = opponentsArray
|
||||||
currentMatchObject.codeLanguage = matchObject.opponents[opponentsArray[0].sessionID].codeLanguage
|
currentMatchObject.codeLanguage = matchObject.opponents[opponentsArray[0].sessionID].codeLanguage
|
||||||
|
currentMatchObject.simulator = @clientResponseObject.simulator
|
||||||
LevelSession.findOne {'_id': sessionID}, (err, session) ->
|
LevelSession.findOne {'_id': sessionID}, (err, session) ->
|
||||||
session = session.toObject()
|
session = session.toObject()
|
||||||
currentMatchObject.playtime = session.playtime ? 0
|
currentMatchObject.playtime = session.playtime ? 0
|
||||||
|
@ -785,3 +793,12 @@ retrieveOldSessionData = (sessionID, callback) ->
|
||||||
markSessionAsDoneRanking = (sessionID, cb) ->
|
markSessionAsDoneRanking = (sessionID, cb) ->
|
||||||
#console.log 'Marking session as done ranking...'
|
#console.log 'Marking session as done ranking...'
|
||||||
LevelSession.update {'_id': sessionID}, {'isRanking': false}, cb
|
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
|
||||||
|
|
|
@ -50,7 +50,7 @@ setupErrorMiddleware = (app) ->
|
||||||
res.status(err.status ? 500).send(error: "Something went wrong!")
|
res.status(err.status ? 500).send(error: "Something went wrong!")
|
||||||
message = "Express error: #{req.method} #{req.path}: #{err.message}"
|
message = "Express error: #{req.method} #{req.path}: #{err.message}"
|
||||||
log.error "#{message}, stack: #{err.stack}"
|
log.error "#{message}, stack: #{err.stack}"
|
||||||
hipchat.sendTowerHipChatMessage(message)
|
hipchat.sendHipChatMessage(message, ['tower'], {papertrail: true})
|
||||||
else
|
else
|
||||||
next(err)
|
next(err)
|
||||||
setupExpressMiddleware = (app) ->
|
setupExpressMiddleware = (app) ->
|
||||||
|
|
Loading…
Reference in a new issue