2014-06-30 22:16:26 -04:00
mongoose = require ' mongoose '
2016-04-06 13:56:06 -04:00
plugins = require ' ../plugins/plugins '
AchievablePlugin = require ' ../plugins/achievements '
jsonschema = require ' ../../app/schemas/models/level_session '
2014-06-24 09:28:18 -04:00
log = require ' winston '
2016-04-06 13:56:06 -04:00
config = require ' ../../server_config '
2014-01-03 13:32:13 -05:00
LevelSessionSchema = new mongoose . Schema ( {
created:
type: Date
' default ' : Date . now
2015-03-21 21:49:32 -04:00
} , { strict: false , read : config . mongo . readpref } )
2015-01-27 13:02:47 -05:00
LevelSessionSchema . index ( { creator: 1 } )
LevelSessionSchema . index ( { level: 1 } )
LevelSessionSchema . index ( { levelID: 1 } )
LevelSessionSchema . index ( { ' level.majorVersion ' : 1 } )
LevelSessionSchema . index ( { ' level.original ' : 1 } , { name: ' Level Original ' } )
LevelSessionSchema . index ( { ' level.original ' : 1 , ' level.majorVersion ' : 1 , ' creator ' : 1 , ' team ' : 1 } )
2015-08-15 15:44:19 -04:00
LevelSessionSchema . index ( { creator: 1 , level: 1 } ) # Looks like the ones operating on level as two separate fields might not be working, and sometimes this query uses the "level" index instead of the "creator" index.
2015-01-27 13:02:47 -05:00
LevelSessionSchema . index ( { playtime: 1 } , { name: ' Playtime ' } )
LevelSessionSchema . index ( { submitted: 1 } , { sparse: true } )
LevelSessionSchema . index ( { team: 1 } , { sparse: true } )
LevelSessionSchema . index ( { totalScore: 1 } , { sparse: true } )
LevelSessionSchema . index ( { user: 1 , changed: - 1 } , { name: ' last played index ' , sparse: true } )
2015-11-14 19:47:55 -05:00
LevelSessionSchema . index ( { levelID: 1 , changed: - 1 } , { name: ' last played by level index ' , sparse: true } ) # Needed for getRecentSessions for CampaignLevelView
2015-01-31 00:36:36 -05:00
LevelSessionSchema . index ( { ' level.original ' : 1 , ' state.topScores.type ' : 1 , ' state.topScores.date ' : - 1 , ' state.topScores.score ' : - 1 } , { name: ' top scores index ' , sparse: true } )
2015-08-15 09:45:38 -04:00
LevelSessionSchema . index ( { submitted: 1 , team: 1 , level: 1 , totalScore: - 1 } , { name: ' rank counting index ' , sparse: true } )
#LevelSessionSchema.index({level: 1, 'leagues.leagueID': 1, submitted: 1, team: 1, totalScore: -1}, {name: 'league rank counting index', sparse: true}) # needed for league leaderboards?
LevelSessionSchema . index ( { levelID: 1 , submitted: 1 , team: 1 } , { name: ' get all scores index ' , sparse: true } )
#LevelSessionSchema.index({levelID: 1, 'leagues.leagueID': 1, submitted: 1, team: 1}, {name: 'league get all scores index', sparse: true}) # needed for league histograms?
2015-04-12 14:34:19 -04:00
LevelSessionSchema . index ( { submitted: 1 , team: 1 , levelID: 1 , submitDate: - 1 } , { name: ' matchmaking index ' , sparse: true } )
LevelSessionSchema . index ( { submitted: 1 , team: 1 , levelID: 1 , randomSimulationIndex: - 1 } , { name: ' matchmaking random index ' , sparse: true } )
2015-08-15 08:38:47 -04:00
LevelSessionSchema . index ( { ' leagues.leagueID ' : 1 , submitted: 1 , levelID: 1 , team: 1 , randomSimulationIndex: - 1 } , { name: ' league-based matchmaking random index ' , sparse: true } ) # Really need MongoDB 3.2 for partial indexes for this and several others: https://jira.mongodb.org/browse/SERVER-785
2015-01-27 13:02:47 -05:00
2014-01-03 13:32:13 -05:00
LevelSessionSchema . plugin ( plugins . PermissionsPlugin )
2014-05-13 16:46:56 -04:00
LevelSessionSchema . plugin ( AchievablePlugin )
2014-01-03 13:32:13 -05:00
2014-06-24 09:28:18 -04:00
LevelSessionSchema . post ' init ' , (doc) ->
2015-02-17 23:51:22 -05:00
unless doc . previousStateInfo
doc.previousStateInfo =
' state.complete ' : doc . get ' state.complete '
playtime: doc . get ' playtime '
2014-06-24 09:28:18 -04:00
2014-01-03 13:32:13 -05:00
LevelSessionSchema . pre ' save ' , (next) ->
2016-04-06 13:56:06 -04:00
User = require ' ./User ' # Avoid mutual inclusion cycles
Level = require ' ./Level '
2016-04-19 16:44:48 -04:00
now = new Date ( )
@ set ( ' changed ' , now )
2014-06-24 09:28:18 -04:00
id = @ get ( ' id ' )
2015-02-18 11:39:29 -05:00
initd = @ previousStateInfo ?
2014-12-04 15:57:57 -05:00
levelID = @ get ( ' levelID ' )
userID = @ get ( ' creator ' )
activeUserEvent = null
2014-06-24 09:28:18 -04:00
2014-12-04 15:57:57 -05:00
# Newly completed level
2015-02-18 11:39:29 -05:00
if not ( initd and @ previousStateInfo [ ' state.complete ' ] ) and @ get ( ' state.complete ' )
2016-04-19 16:44:48 -04:00
@ set ( ' dateFirstCompleted ' , now )
2015-11-04 16:42:01 -05:00
Level . findOne ( { slug: levelID } ) . select ( ' concepts -_id ' ) . lean ( ) . exec (err, level) ->
2014-06-24 09:28:18 -04:00
log . error err if err ?
2015-11-04 16:42:01 -05:00
update = $inc: { ' stats.gamesCompleted ' : 1 }
for concept in level ? . concepts ? [ ]
update . $inc [ " stats.concepts. #{ concept } " ] = 1
2015-12-09 15:08:16 -05:00
User . findByIdAndUpdate userID , update , { new : true } , (err, user) ->
2015-11-04 16:42:01 -05:00
log . error err if err ?
oldCopy = user . toObject ( )
oldCopy.stats = _ . clone oldCopy . stats
- - oldCopy . stats . gamesCompleted
oldCopy . stats . concepts ? = { }
for concept in level ? . concepts ? [ ]
- - oldCopy . stats . concepts [ concept ]
User . schema . statics . createNewEarnedAchievements user , oldCopy
2014-12-04 15:57:57 -05:00
activeUserEvent = " level-completed/ #{ levelID } "
# Spent at least 30s playing this level
2015-02-18 11:39:29 -05:00
if not initd and @ get ( ' playtime ' ) >= 30 or initd and ( @ get ( ' playtime ' ) - @ previousStateInfo [ ' playtime ' ] >= 30 )
2014-12-04 15:57:57 -05:00
activeUserEvent = " level-playtime/ #{ levelID } "
2014-06-24 09:28:18 -04:00
2014-12-04 15:57:57 -05:00
if activeUserEvent ?
User . saveActiveUser userID , activeUserEvent , next
else
next ( )
2014-01-03 13:32:13 -05:00
2014-07-22 14:07:00 -04:00
LevelSessionSchema.statics.privateProperties = [ ' code ' , ' submittedCode ' , ' unsubscribed ' ]
2016-07-14 13:26:09 -04:00
LevelSessionSchema.statics.editableProperties = [ ' players ' , ' code ' , ' codeLanguage ' , ' completed ' , ' state ' ,
2016-07-08 20:04:24 -04:00
' levelName ' , ' creatorName ' , ' levelID ' ,
2014-12-19 20:27:58 -05:00
' chat ' , ' teamSpells ' , ' submitted ' , ' submittedCodeLanguage ' ,
2016-07-08 20:04:24 -04:00
' unsubscribed ' , ' playtime ' , ' heroConfig ' , ' team ' ,
2015-01-30 17:45:34 -05:00
' browser ' ]
2014-07-22 14:07:00 -04:00
LevelSessionSchema.statics.jsonSchema = jsonschema
2016-05-09 18:16:54 -04:00
LevelSessionSchema . set ( ' toObject ' , {
transform: (doc, ret, options) ->
req = options . req
return ret unless req # TODO: Make deleting properties the default, but the consequences are far reaching
2016-07-08 20:04:24 -04:00
2016-05-09 18:16:54 -04:00
submittedCode = doc . get ( ' submittedCode ' )
unless req . user ? . isAdmin ( ) or req . user ? . id is doc . get ( ' creator ' ) or ( ' employer ' in ( req . user ? . get ( ' permissions ' ) ? [ ] ) ) or not doc . get ( ' submittedCode ' ) # TODO: only allow leaderboard access to non-top-5 solutions
ret = _ . omit ret , LevelSession . privateProperties
if req . query . interpret
plan = submittedCode [ if doc . get ( ' team ' ) is ' humans ' then ' hero-placeholder ' else ' hero-placeholder-1 ' ] ? . plan ? ' '
plan = LZString . compressToUTF16 plan
ret.interpret = plan
ret.code = submittedCode
return ret
} )
2016-07-11 21:02:58 -04:00
if config . mongo . level_session_replica_string ?
2016-07-11 19:40:05 -04:00
levelSessionMongo = mongoose . createConnection ( )
2016-07-11 21:02:58 -04:00
levelSessionMongo . open config . mongo . level_session_replica_string , (error) ->
2016-07-11 19:40:05 -04:00
if error
2016-07-14 11:58:43 -04:00
log . error " Couldn ' t connect to session mongo! " , error
2016-07-11 19:40:05 -04:00
else
2016-07-11 21:02:58 -04:00
log . info " Connected to seperate level session server with string " , config . mongo . level_session_replica_string
2016-07-11 19:40:05 -04:00
else
levelSessionMongo = mongoose
LevelSession = levelSessionMongo . model ( ' level.session ' , LevelSessionSchema , ' level.sessions ' )
2016-07-11 21:02:58 -04:00
if config . mongo . level_session_aux_replica_string ?
2016-07-11 19:40:05 -04:00
auxLevelSessionMongo = mongoose . createConnection ( )
2016-07-11 21:02:58 -04:00
auxLevelSessionMongo . open config . mongo . level_session_aux_replica_string , (error) ->
2016-07-11 19:40:05 -04:00
if error
2016-07-14 11:58:43 -04:00
log . error " Couldn ' t connect to AUX session mongo! " , error
2016-07-11 19:40:05 -04:00
else
2016-07-11 21:02:58 -04:00
log . info " Connected to seperate level AUX session server with string " , config . mongo . level_session_aux_replica_string
2016-07-11 19:40:05 -04:00
auxLevelSession = auxLevelSessionMongo . model ( ' level.session ' , LevelSessionSchema , ' level.sessions ' )
LevelSessionSchema . post ' save ' , (d) ->
return unless d instanceof LevelSession
o = d . toObject { transform: ( (x, r) -> r ) , virtuals: false }
auxLevelSession . collection . save o , { w : 1 } , (err, v) ->
2016-07-11 21:02:58 -04:00
log . error err . stack if err
2016-07-11 19:40:05 -04:00
module.exports = LevelSession