mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 09:35:39 -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'
|
||||
{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 =
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) ->
|
||||
|
|
Loading…
Reference in a new issue