2014-01-03 10:32:13 -08:00
Bus = require ' ./Bus '
{ me } = require ' lib/auth '
LevelSession = require ' models/LevelSession '
2014-08-08 13:08:13 +02:00
utils = require ' lib/utils '
2014-01-03 10:32:13 -08:00
module.exports = class LevelBus extends Bus
@get: (levelID, sessionID) ->
docName = " play/level/ #{ levelID } / #{ sessionID } "
return Bus . getFromCache ( docName ) or new LevelBus docName
subscriptions:
' self-wizard:target-changed ' : ' onSelfWizardTargetChanged '
2014-08-27 12:24:03 -07:00
' self-wizard:created ' : ' onSelfWizardCreated '
2014-01-03 10:32:13 -08:00
' tome:editing-began ' : ' onEditingBegan '
' tome:editing-ended ' : ' onEditingEnded '
' script:state-changed ' : ' onScriptStateChanged '
' script:ended ' : ' onScriptEnded '
' script:reset ' : ' onScriptReset '
' surface:frame-changed ' : ' onFrameChanged '
' surface:sprite-selected ' : ' onSpriteSelected '
2014-08-27 12:24:03 -07:00
' level:set-playing ' : ' onSetPlaying '
' level:show-victory ' : ' onVictory '
2014-01-03 10:32:13 -08:00
' tome:spell-changed ' : ' onSpellChanged '
2014-02-10 13:18:39 -08:00
' tome:spell-created ' : ' onSpellCreated '
2014-10-18 17:32:01 -07:00
' tome:cast-spells ' : ' onCastSpells '
2014-05-16 15:27:46 -07:00
' application:idle-changed ' : ' onIdleChanged '
2014-08-08 13:08:13 +02:00
' goal-manager:new-goal-states ' : ' onNewGoalStates '
2014-10-18 17:32:01 -07:00
' god:new-world-created ' : ' onNewWorldCreated '
2014-06-11 19:38:41 -07:00
2014-01-03 10:32:13 -08:00
constructor: ->
super ( arguments . . . )
@changedSessionProperties = { }
2014-10-10 19:20:00 -07:00
@saveSession = _ . debounce ( @ reallySaveSession , 1000 , { maxWait: 5000 } )
2014-05-16 15:27:46 -07:00
@playerIsIdle = false
2014-06-11 19:38:41 -07:00
2014-01-03 10:32:13 -08:00
init: ->
super ( )
@fireScriptsRef = @ fireRef ? . child ( ' scripts ' )
setSession: (@session) ->
2014-03-24 22:28:34 +05:30
@ listenTo ( @ session , ' change:multiplayer ' , @ onMultiplayerChanged )
2014-05-16 15:27:46 -07:00
@timerIntervalID = setInterval ( @ incrementSessionPlaytime , 1000 )
2014-06-11 19:38:41 -07:00
2014-05-16 15:27:46 -07:00
onIdleChanged: (e) ->
@playerIsIdle = e . idle
incrementSessionPlaytime: =>
if @ playerIsIdle then return
@changedSessionProperties.playtime = true
2014-10-07 10:06:41 -07:00
@ session . set ( ' playtime ' , ( @ session . get ( ' playtime ' ) ? 0 ) + 1 )
2014-06-11 19:38:41 -07:00
2014-01-03 10:32:13 -08:00
onPoint: ->
return true unless @ session ? . get ( ' multiplayer ' )
super ( )
2014-08-27 12:24:03 -07:00
onSelfWizardCreated: (e) ->
2014-09-28 14:00:48 -07:00
@selfWizardLank = e . sprite
2014-08-27 12:24:03 -07:00
onSelfWizardTargetChanged: (e) ->
2014-09-28 14:00:48 -07:00
@ wizardRef ? . child ( ' targetPos ' ) . set ( @ selfWizardLank ? . targetPos or null )
@ wizardRef ? . child ( ' targetSprite ' ) . set ( @ selfWizardLank ? . targetSprite ? . thang . id or null )
2014-01-03 10:32:13 -08:00
onMeSynced: =>
super ( )
@ wizardRef ? . child ( ' wizardColor1 ' ) . set ( me . get ( ' wizardColor1 ' ) or 0.0 )
join: ->
super ( )
@wizardRef = @ myConnection . child ( ' wizard ' )
2014-09-28 14:00:48 -07:00
@ wizardRef ? . child ( ' targetPos ' ) . set ( @ selfWizardLank ? . targetPos or null )
@ wizardRef ? . child ( ' targetSprite ' ) . set ( @ selfWizardLank ? . targetSprite ? . thang . id or null )
2014-01-03 10:32:13 -08:00
@ wizardRef ? . child ( ' wizardColor1 ' ) . set ( me . get ( ' wizardColor1 ' ) or 0.0 )
2014-02-22 12:01:05 -08:00
2014-01-03 10:32:13 -08:00
disconnect: ->
@ wizardRef ? . off ( )
@wizardRef = null
@ fireScriptsRef ? . off ( )
@fireScriptsRef = null
super ( )
2014-02-22 12:01:05 -08:00
2014-01-03 10:32:13 -08:00
removeFirebaseData: (callback) ->
return callback ? ( ) unless @ myConnection
@ myConnection . child ( ' connected ' )
@ fireRef . remove ( )
@ onDisconnect . cancel ( -> callback ? ( ) )
# UPDATING FIREBASE AND SESSION
onEditingBegan: -> @ wizardRef ? . child ( ' editing ' ) . set ( true )
onEditingEnded: -> @ wizardRef ? . child ( ' editing ' ) . set ( false )
2014-02-22 12:01:05 -08:00
2014-01-03 10:32:13 -08:00
# HACK: Backbone does not work with nested documents, but we want to
# patch only those props that have changed. Look into plugins to
# give Backbone support for nested docs and update the code here.
2014-02-22 12:01:05 -08:00
2014-01-03 10:32:13 -08:00
# TODO: The LevelBus doesn't need to be in charge of updating the
# LevelSession object. Either break this off into a separate class
# or have the LevelSession object listen for all these events itself.
2014-02-25 10:48:23 -08:00
setSpells: (spells) ->
@ onSpellCreated spell: spell for spellKey , spell of spells
2014-01-03 10:32:13 -08:00
onSpellChanged: (e) ->
return unless @ onPoint ( )
code = @ session . get ( ' code ' )
code ? = { }
parts = e . spell . spellKey . split ( ' / ' )
2014-02-10 13:18:39 -08:00
2014-01-03 10:32:13 -08:00
code [ parts [ 0 ] ] ? = { }
code [ parts [ 0 ] ] [ parts [ 1 ] ] = e . spell . getSource ( )
@changedSessionProperties.code = true
@ session . set ( { ' code ' : code } )
@ saveSession ( )
2014-02-10 13:18:39 -08:00
onSpellCreated: (e) ->
2014-02-10 16:18:39 -08:00
return unless @ onPoint ( )
2014-02-10 13:18:39 -08:00
spellTeam = e . spell . team
2014-02-25 10:48:23 -08:00
@ teamSpellMap ? = { }
2014-02-10 16:18:39 -08:00
@ teamSpellMap [ spellTeam ] ? = [ ]
2014-02-10 13:18:39 -08:00
2014-02-10 16:18:39 -08:00
unless e . spell . spellKey in @ teamSpellMap [ spellTeam ]
@ teamSpellMap [ spellTeam ] . push e . spell . spellKey
2014-02-10 16:31:21 -08:00
@changedSessionProperties.teamSpells = true
@ session . set ( { ' teamSpells ' : @ teamSpellMap } )
@ saveSession ( )
2014-08-26 18:11:35 -07:00
if spellTeam is me . team or ( e . spell . otherSession and spellTeam isnt e . spell . otherSession . get ( ' team ' ) )
# https://github.com/codecombat/codecombat/issues/81
2014-02-25 14:46:48 -08:00
@ onSpellChanged e # Save the new spell to the session, too.
2014-02-10 13:18:39 -08:00
2014-10-18 17:32:01 -07:00
onCastSpells: (e) ->
return unless @ onPoint ( ) and e . realTime
# We have incremented state.submissionCount and reset state.flagHistory.
@changedSessionProperties.state = true
@ saveSession ( )
onNewWorldCreated: (e) ->
return unless @ onPoint ( )
# Record the flag history.
state = @ session . get ( ' state ' )
return if _ . isEqual state . flagHistory , e . world . flagHistory
state.flagHistory = e . world . flagHistory
@changedSessionProperties.state = true
@ session . set ( ' state ' , state )
@ saveSession ( )
2014-01-03 10:32:13 -08:00
onScriptStateChanged: (e) ->
return unless @ onPoint ( )
@ fireScriptsRef ? . update ( e )
state = @ session . get ( ' state ' )
scripts = state . scripts
scripts.currentScript = e . currentScript
scripts.currentScriptOffset = e . currentScriptOffset
@changedSessionProperties.state = true
@ session . set ( ' state ' , state )
@ saveSession ( )
onScriptEnded: (e) ->
return unless @ onPoint ( )
state = @ session . get ( ' state ' )
scripts = state . scripts
scripts . ended ? = { }
return if scripts . ended [ e . scriptID ] ?
index = _ . keys ( scripts . ended ) . length + 1
@ fireScriptsRef ? . child ( ' ended ' ) . child ( e . scriptID ) . set ( index )
scripts . ended [ e . scriptID ] = index
@ session . set ( ' state ' , state )
@changedSessionProperties.state = true
@ saveSession ( )
onScriptReset: ->
return unless @ onPoint ( )
@ fireScriptsRef ? . set ( { } )
state = @ session . get ( ' state ' )
state.scripts = { }
state.complete = false
@ session . set ( ' state ' , state )
@changedSessionProperties.state = true
@ saveSession ( )
onFrameChanged: (e) ->
return unless @ onPoint ( )
state = @ session . get ( ' state ' )
state.frame = e . frame
@ session . set ( ' state ' , state )
@changedSessionProperties.state = true
@ saveSession ( )
onSpriteSelected: (e) ->
return unless @ onPoint ( )
state = @ session . get ( ' state ' )
state.selected = e . thang ? . id or null
@ session . set ( ' state ' , state )
@changedSessionProperties.state = true
@ saveSession ( )
onSetPlaying: (e) ->
return unless @ onPoint ( )
state = @ session . get ( ' state ' )
state.playing = e . playing
@ session . set ( ' state ' , state )
@changedSessionProperties.state = true
@ saveSession ( )
onVictory: ->
return unless @ onPoint ( )
state = @ session . get ( ' state ' )
state.complete = true
@ session . set ( ' state ' , state )
@changedSessionProperties.state = true
2014-10-10 19:20:00 -07:00
@ reallySaveSession ( ) # Make sure it saves right away; don't debounce it.
2014-01-03 10:32:13 -08:00
2014-08-08 13:08:13 +02:00
onNewGoalStates: ({goalStates})->
state = @ session . get ' state '
2014-08-23 13:54:52 -07:00
unless utils . kindaEqual state . goalStates , goalStates # Only save when goals really change
# TODO: this log doesn't capture when null-status goals are being set during world streaming. Where can they be coming from?
2014-08-22 21:35:08 -07:00
return console . error ( " Somehow trying to save null goal states! " , goalStates ) if _ . find ( goalStates , (gs) -> not gs . status )
2014-08-08 13:08:13 +02:00
state.goalStates = goalStates
@ session . set ' state ' , state
@changedSessionProperties.state = true
@ saveSession ( )
2014-01-03 10:32:13 -08:00
onPlayerJoined: (snapshot) =>
super ( arguments . . . )
return unless @ onPoint ( )
players = @ session . get ( ' players ' )
players ? = { }
player = snapshot . val ( )
return if players [ player . id ] ?
players [ player . id ] = { }
@ session . set ( ' players ' , players )
@changedSessionProperties.players = true
@ saveSession ( )
onChatAdded: (snapshot) =>
super ( arguments . . . )
chat = @ session . get ( ' chat ' )
chat ? = [ ]
message = snapshot . val ( )
return if message . system
chat . push ( message )
chat = chat [ chat . length - 50 . . . ] if chat . length > 50
@ session . set ( ' chat ' , chat )
@changedSessionProperties.chat = true
@ saveSession ( )
2014-02-11 15:38:36 -08:00
onMultiplayerChanged: ->
2014-01-03 10:32:13 -08:00
@changedSessionProperties.multiplayer = true
@ session . updatePermissions ( )
@changedSessionProperties.permissions = true
@ saveSession ( )
2014-10-10 19:20:00 -07:00
# Debounced as saveSession
reallySaveSession: ->
2014-01-03 10:32:13 -08:00
return if _ . isEmpty @ changedSessionProperties
2014-06-11 19:38:41 -07:00
# don't let peeking admins mess with the session accidentally
2014-01-03 10:32:13 -08:00
return unless @ session . get ( ' multiplayer ' ) or @ session . get ( ' creator ' ) is me . id
Backbone . Mediator . publish ' level:session-will-save ' , session: @ session
patch = { }
patch [ prop ] = @ session . get ( prop ) for prop of @ changedSessionProperties
@changedSessionProperties = { }
2014-02-22 12:01:05 -08:00
2014-01-03 10:32:13 -08:00
# since updates are coming fast and loose for session objects
# don't let what the server returns overwrite changes since the save began
2014-07-01 10:16:26 +08:00
tempSession = new LevelSession _id: @ session . id
2014-01-03 10:32:13 -08:00
tempSession . save ( patch , { patch: true } )
2014-02-11 15:38:36 -08:00
destroy: ->
2014-05-16 15:27:46 -07:00
clearInterval ( @ timerIntervalID )
2014-02-14 10:57:47 -08:00
super ( )