Fix sync PVP opponent syncing issue

Ripped out backfire from PlayLevelView so we have more control over
data synchronization.  These changes should wait properly for the
opponent data to load before blazing ahead, after a PlayLevelView
reload.

Fixes #1767
This commit is contained in:
Matt Lott 2014-11-23 17:15:59 -08:00
parent 0c8b5ec9a8
commit c203ff15a2
3 changed files with 202 additions and 186 deletions

View file

@ -24,6 +24,7 @@ module.exports = class MultiplayerView extends CocoView
constructor: (options) ->
super(options)
@level = options.level
@levelID = @level?.get 'slug'
@session = options.session
@listenTo @session, 'change:multiplayer', @updateLinkSection
@watchRealTimeSessions() if @level?.get('type') in ['hero-ladder']
@ -39,7 +40,7 @@ module.exports = class MultiplayerView extends CocoView
c.joinLink = "#{document.location.href.replace(/\?.*/, '').replace('#', '')}?session=#{@session.id}"
c.multiplayer = @session.get 'multiplayer'
c.team = @session.get 'team'
c.levelSlug = @level?.get 'slug'
c.levelSlug = @levelID
# For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet.
if @level?.get('type') in ['ladder', 'hero-ladder']
c.ladderGame = true
@ -71,7 +72,7 @@ module.exports = class MultiplayerView extends CocoView
e.target.select()
onGameSubmitted: (e) ->
ladderURL = "/play/ladder/#{@level.get('slug')}#my-matches"
ladderURL = "/play/ladder/#{@levelID}#my-matches"
Backbone.Mediator.publish 'router:navigate', route: ladderURL
updateLinkSection: ->
@ -104,13 +105,14 @@ module.exports = class MultiplayerView extends CocoView
# @realTimeSessionsPlayers - Collection of player lists for active real-time multiplayer sessions
# @realTimeSessions - Active real-time multiplayer sessions
# @currentRealTimeSession - Our current real-time multiplayer session
#
# TODO: Ditch backfire and just use Firebase directly. Easier to debug, richer APIs (E.g. presence stuff).
watchRealTimeSessions: ->
# Setup monitoring of real-time multiplayer level sessions
@realTimeSessionsPlayers = {}
# TODO: only request sessions for this level, !team, etc.
# TODO: move this to multiplayer_level_sessions/#{levelID}/
@realTimeSessions = new RealTimeCollection('multiplayer_level_sessions/')
@realTimeSessions = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}")
@realTimeSessions.on 'add', @onRealTimeSessionAdded
@realTimeSessions.each (rts) => @watchRealTimeSession rts
@ -120,9 +122,9 @@ module.exports = class MultiplayerView extends CocoView
# console.log 'MultiplayerView watchRealTimeSession', rts
# Setup monitoring of players for given session
# TODO: verify we need this
realTimeSession = new RealTimeModel('multiplayer_level_sessions/' + rts.id)
realTimeSession = new RealTimeModel("multiplayer_level_sessions/#{@levelID}/#{rts.id}")
realTimeSession.on 'change', @onRealTimeSessionChanged
@realTimeSessionsPlayers[rts.id] = new RealTimeCollection('multiplayer_level_sessions/' + rts.id + '/players')
@realTimeSessionsPlayers[rts.id] = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{rts.id}/players")
@realTimeSessionsPlayers[rts.id].on 'add', @onRealTimePlayerAdded
@findCurrentRealTimeSession rts
@ -133,7 +135,7 @@ module.exports = class MultiplayerView extends CocoView
@realTimeSessionsPlayers[rts.id].each (player) =>
if player.id is me.id and player.get('state') isnt 'left'
# console.log 'MultiplayerView found current real-time session', rts
@currentRealTimeSession = new RealTimeModel('multiplayer_level_sessions/' + rts.id)
@currentRealTimeSession = new RealTimeModel("multiplayer_level_sessions/#{@levelID}/#{rts.id}")
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
# TODO: Is this necessary? Shouldn't everyone already know we joined a game at this point?
@ -170,7 +172,7 @@ module.exports = class MultiplayerView extends CocoView
@currentRealTimeSession = @realTimeSessions.get(s.id)
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
# TODO: s.id === @currentRealTimeSession.id ?
players = new RealTimeCollection('multiplayer_level_sessions/' + @currentRealTimeSession.id + '/players')
players = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{@currentRealTimeSession.id}/players")
players.create
id: me.id
state: 'coding'

View file

@ -33,6 +33,7 @@ module.exports = class LevelFlagsView extends CocoView
constructor: (options) ->
super options
@levelID = options.levelID
@world = options.world
onRealTimePlaybackStarted: (e) ->
@ -84,7 +85,7 @@ module.exports = class LevelFlagsView extends CocoView
@world = @options.world = event.world
onJoinedMultiplayerGame: (e) ->
@realTimeFlags = new RealTimeCollection('multiplayer_level_sessions/' + e.realTimeSessionID + '/flagHistory')
@realTimeFlags = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{e.realTimeSessionID}/flagHistory")
@realTimeFlags.on 'add', @onRealTimeMultiplayerFlagAdded
@realTimeFlags.on 'remove', @onRealTimeMultiplayerFlagRemoved

View file

@ -19,8 +19,6 @@ LevelComponent = require 'models/LevelComponent'
Article = require 'models/Article'
Camera = require 'lib/surface/Camera'
AudioPlayer = require 'lib/AudioPlayer'
RealTimeModel = require 'models/RealTimeModel'
RealTimeCollection = require 'collections/RealTimeCollection'
# subviews
LevelLoadingView = require './LevelLoadingView'
@ -252,7 +250,7 @@ module.exports = class PlayLevelView extends RootView
@insertSubView @tome = new TomeView levelID: @levelID, session: @session, otherSession: @otherSession, thangs: @world.thangs, supermodel: @supermodel, level: @level
@insertSubView new LevelPlaybackView session: @session, levelID: @levelID, level: @level
@insertSubView new GoalsView {}
@insertSubView new LevelFlagsView world: @world if @$el.hasClass 'flags'
@insertSubView new LevelFlagsView levelID: @levelID, world: @world if @$el.hasClass 'flags'
@insertSubView new GoldView {}
@insertSubView new HUDView {level: @level}
@insertSubView new LevelDialogueView {level: @level}
@ -290,7 +288,7 @@ module.exports = class PlayLevelView extends RootView
@setupManager = new LevelSetupManager({supermodel: @supermodel, levelID: @levelID, parent: @, session: @session})
@setupManager.open()
@onRealTimeMultiplayerLevelLoaded() if e.level.get('type') in ['hero-ladder']
@onRealTimeMultiplayerLevelLoaded e.session if e.level.get('type') in ['hero-ladder']
onLoaded: ->
_.defer => @onLevelLoaderLoaded()
@ -608,147 +606,167 @@ module.exports = class PlayLevelView extends RootView
# Current real-time multiplayer session
# Internal multiplayer create/joined/left events
#
# Real-time state variables:
# @realTimePlayerStatus - User's real-time multiplayer state for this level
# @realTimePlayerGameStatus - User's state for current real-time multiplayer game session
# @realTimeSession - Current real-time multiplayer game session
# @realTimeOpponent - Current real-time multiplayer opponent
# @realTimePlayers - Real-time players for current real-time multiplayer game session
# @realTimeSessionCollection - Collection of all real-time multiplayer sessions
# Real-time state variables.
# Each Ref is Firebase reference, and may have a matching Data suffixed variable with the latest data received.
# @realTimePlayerRef - User's real-time multiplayer player for this level
# @realTimePlayerGameRef - User's current real-time multiplayer player game session
# @realTimeSessionRef - Current real-time multiplayer game session
# @realTimeOpponentRef - Current real-time multiplayer opponent
# @realTimePlayersRef - Real-time players for current real-time multiplayer game session
# @options.realTimeMultiplayerSessionID - Need to continue an existing real-time multiplayer session
#
# TODO: Move this code to it's own file, or possibly the LevelBus
# TODO: Save settings somewhere reasonable
# TODO: Ditch backfire and just use Firebase directly. Easier to debug, richer APIs (E.g. presence stuff).
multiplayerFireHost: 'https://codecombat.firebaseio.com/test/db/'
onRealTimeMultiplayerLevelLoaded: ->
return if @realTimePlayerStatus?
onRealTimeMultiplayerLevelLoaded: (session) ->
# console.log 'PlayLevelView onRealTimeMultiplayerLevelLoaded'
return if @realTimePlayerRef?
return if me.get('anonymous')
@realTimePlayerRef = new Firebase "#{@multiplayerFireHost}multiplayer_players/#{@levelID}/#{me.id}"
unless @options.realTimeMultiplayerSessionID?
players = new RealTimeCollection('multiplayer_players/' + @levelID)
players.create
id: me.id
name: me.get('name')
# TODO: Wait for name instead of using 'Anon', or try and update it later?
name = me.get('name') ? session.get('creatorName') ? 'Anon'
@realTimePlayerRef.set
id: me.id # TODO: is this redundant info necessary?
name: name
state: 'playing'
created: new Date().toISOString()
heartbeat: new Date().toISOString()
@realTimePlayerStatus = new RealTimeModel('multiplayer_players/' + @levelID + '/' + me.id)
@timerMultiplayerHeartbeatID = setInterval @onRealTimeMultiplayerHeartbeat, 60 * 1000
@cleanupRealTimeSessions()
cleanupRealTimeSessions: ->
@realTimeSessionCollection = new RealTimeCollection 'multiplayer_level_sessions'
@realTimeSessionCollection.on 'add', @cleanupRealTimeSession
@realTimeSessionCollection.each @cleanupRealTimeSession
cleanupRealTimeSession: (session) =>
return if @options.realTimeMultiplayerSessionID? and @options.realTimeMultiplayerSessionID is session.id
if session.get('state') isnt 'finished'
players = new RealTimeCollection 'multiplayer_level_sessions/' + session.id + '/players'
players.each (player) =>
if player.id is me.id
p = new RealTimeModel 'multiplayer_level_sessions/' + session.id + '/players/' + me.id
console.info 'Cleaning up previous real-time multiplayer session', session.id
p.set 'state', 'left'
session.set 'state', 'finished'
# console.log 'PlayLevelView cleanupRealTimeSessions'
# TODO: Reduce this call, possibly by username and dates
realTimeSessionCollection = new Firebase "#{@multiplayerFireHost}multiplayer_level_sessions/#{@levelID}"
realTimeSessionCollection.once 'value', (collectionSnapshot) =>
for multiplayerSessionID, multiplayerSession of collectionSnapshot.val()
continue if @options.realTimeMultiplayerSessionID? and @options.realTimeMultiplayerSessionID is multiplayerSessionID
continue unless multiplayerSession.state isnt 'finished'
player = realTimeSessionCollection.child "#{multiplayerSession.id}/players/#{me.id}"
player.once 'value', (playerSnapshot) =>
if playerSnapshot.val()
console.info 'Cleaning up previous real-time multiplayer session', multiplayerSessionID
player.update 'state': 'left'
multiplayerSessionRef = realTimeSessionCollection.child "#{multiplayerSessionID}"
multiplayerSessionRef.update 'state': 'finished'
onRealTimeMultiplayerLevelUnloaded: ->
# console.log 'PlayLevelView onRealTimeMultiplayerLevelUnloaded'
if @timerMultiplayerHeartbeatID?
clearInterval @timerMultiplayerHeartbeatID
@timerMultiplayerHeartbeatID = null
if @realTimeSessionCollection?
@realTimeSessionCollection.off 'add', @cleanupRealTimeSession
@realTimeSessionCollection = null
# TODO: similar to game ending cleanup
if @realTimeOpponent?
@realTimeOpponent.off 'change', @onRealTimeOpponentChanged
@realTimeOpponent = null
if @realTimePlayers?
@realTimePlayers.off 'add', @onRealTimePlayerAdded
@realTimePlayers = null
if @realTimeSession?
@realTimeSession.off 'change', @onRealTimeSessionChanged
@realTimeSession = null
if @realTimePlayerGameStatus?
@realTimePlayerGameStatus = null
if @realTimePlayerStatus?
@realTimePlayerStatus = null
if @realTimeOpponentRef?
@realTimeOpponentRef.off 'value', @onRealTimeOpponentChanged
@realTimeOpponentRef = null
if @realTimePlayersRef?
@realTimePlayersRef.off 'child_added', @onRealTimePlayerAdded
@realTimePlayersRef = null
if @realTimeSessionRef?
@realTimeSessionRef.off 'value', @onRealTimeSessionChanged
@realTimeSessionRef = null
if @realTimePlayerGameRef?
@realTimePlayerGameRef = null
if @realTimePlayerRef?
@realTimePlayerRef = null
onRealTimeMultiplayerHeartbeat: =>
@realTimePlayerStatus.set 'heartbeat', new Date().toISOString() if @realTimePlayerStatus
# console.log 'PlayLevelView onRealTimeMultiplayerHeartbeat', @realTimePlayerRef
@realTimePlayerRef.update 'heartbeat': new Date().toISOString() if @realTimePlayerRef?
onRealTimeMultiplayerCreatedGame: (e) ->
# console.log 'PlayLevelView onRealTimeMultiplayerCreatedGame'
@joinRealTimeMultiplayerGame e
@realTimePlayerGameStatus.set 'state', 'coding'
@realTimePlayerStatus.set 'state', 'available'
@realTimePlayerGameRef.update 'state': 'coding'
@realTimePlayerRef.update 'state': 'available'
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Waiting for opponent..'
onRealTimeSessionChanged: (e) =>
# console.log 'PlayLevelView onRealTimeSessionChanged', e
if e.get('state') is 'finished'
onRealTimeSessionChanged: (snapshot) =>
# console.log 'PlayLevelView onRealTimeSessionChanged', snapshot.val()
@realTimeSessionData = snapshot.val()
if @realTimeSessionData?.state is 'finished'
@realTimeGameEnded()
Backbone.Mediator.publish 'real-time-multiplayer:left-game', {}
onRealTimePlayerAdded: (e) =>
# console.log 'PlayLevelView onRealTimePlayerAdded', e
onRealTimePlayerAdded: (snapshot) =>
# console.log 'PlayLevelView onRealTimePlayerAdded', snapshot.val()
# Assume game is full, game on
if @realTimeSession.get('state') is 'creating'
@realTimeSession.set 'state', 'coding'
@realTimePlayerStatus.set 'state', 'unavailable'
@realTimeOpponent = new RealTimeModel('multiplayer_level_sessions/' + @realTimeSession.id + '/players/' + e.id)
@realTimeOpponent.on 'change', @onRealTimeOpponentChanged
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Playing against ' + e.get('name')
else
console.error 'PlayLevelView onRealTimePlayerAdded session in unexpected state', @realTimeSession.get('state')
data = snapshot.val()
if data? and data.id isnt me.id
@realTimeOpponentData = data
console.log 'PlayLevelView onRealTimePlayerAdded opponent', @realTimeOpponentData, @realTimePlayersData
@realTimePlayersData[@realTimeOpponentData.id] = @realTimeOpponentData
if @realTimeSessionData?.state is 'creating'
@realTimeSessionRef.update 'state': 'coding'
@realTimePlayerRef.update 'state': 'unavailable'
@realTimeOpponentRef = @realTimeSessionRef.child "players/#{@realTimeOpponentData.id}"
@realTimeOpponentRef.on 'value', @onRealTimeOpponentChanged
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "Playing against #{@realTimeOpponentData.name}"
onRealTimeOpponentChanged: (e) =>
# console.log 'PlayLevelView onRealTimeOpponentChanged', e
switch @realTimeOpponent.get('state')
onRealTimeOpponentChanged: (snapshot) =>
# console.log 'PlayLevelView onRealTimeOpponentChanged', snapshot.val()
@realTimeOpponentData = snapshot.val()
switch @realTimeOpponentData?.state
when 'left'
console.info 'Real-time multiplayer opponent left the game'
opponentID = @realTimeOpponent.id
opponentID = @realTimeOpponentData.id
@realTimeGameEnded()
Backbone.Mediator.publish 'real-time-multiplayer:left-game', userID: opponentID
when 'submitted'
# TODO: What should this message say?
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: @realTimeOpponent.get('name') + ' waiting for your code'
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "#{@realTimeOpponentData.name} waiting for your code"
joinRealTimeMultiplayerGame: (e) ->
unless @realTimeSession?
# TODO: Necessary for real-time multiplayer sessions?
# console.log 'PlayLevelView joinRealTimeMultiplayerGame', e
unless @realTimeSessionRef?
@session.set('submittedCodeLanguage', @session.get('codeLanguage'))
@session.save()
@realTimeSession = new RealTimeModel 'multiplayer_level_sessions/' + e.realTimeSessionID
@realTimeSession.on 'change', @onRealTimeSessionChanged
@realTimePlayers = new RealTimeCollection 'multiplayer_level_sessions/' + e.realTimeSessionID + '/players'
@realTimePlayers.on 'add', @onRealTimePlayerAdded
@realTimePlayerGameStatus = new RealTimeModel 'multiplayer_level_sessions/' + e.realTimeSessionID + '/players/' + me.id
@realTimeSessionRef = new Firebase "#{@multiplayerFireHost}multiplayer_level_sessions/#{@levelID}/#{e.realTimeSessionID}"
@realTimePlayersRef = @realTimeSessionRef.child 'players'
# Look for opponent
@realTimeSessionRef.once 'value', (multiplayerSessionSnapshot) =>
if @realTimeSessionData = multiplayerSessionSnapshot.val()
@realTimePlayersRef.once 'value', (playsSnapshot) =>
if @realTimePlayersData = playsSnapshot.val()
for id, player of @realTimePlayersData
if id isnt me.id
@realTimeOpponentRef = @realTimeSessionRef.child "players/#{id}"
@realTimeOpponentRef.once 'value', (opponentSnapshot) =>
if @realTimeOpponentData = opponentSnapshot.val()
@updateTeam()
else
console.error 'Could not lookup multiplayer opponent data.'
@realTimeOpponentRef.on 'value', @onRealTimeOpponentChanged
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Playing against ' + player.name
else
console.error 'Could not lookup multiplayer session players data.'
# TODO: need child_removed too?
@realTimePlayersRef.on 'child_added', @onRealTimePlayerAdded
else
console.error 'Could not lookup multiplayer session data.'
@realTimeSessionRef.on 'value', @onRealTimeSessionChanged
@realTimePlayerGameRef = @realTimeSessionRef.child "players/#{me.id}"
# TODO: Follow up in MultiplayerView to see if double joins can be avoided
# else
# console.error 'Joining real-time multiplayer game with an existing @realTimeSession.'
# console.error 'Joining real-time multiplayer game with an existing @realTimeSessionRef.'
onRealTimeMultiplayerJoinedGame: (e) ->
# console.log 'PlayLevelView onRealTimeMultiplayerJoinedGame', e
console.log 'PlayLevelView onRealTimeMultiplayerJoinedGame', e
@joinRealTimeMultiplayerGame e
@realTimePlayerGameStatus.set 'state', 'coding'
@realTimePlayerStatus.set 'state', 'unavailable'
unless @realTimeOpponent?
for id, player of @realTimeSession.get('players')
if id isnt me.id
@realTimeOpponent = new RealTimeModel 'multiplayer_level_sessions/' + e.realTimeSessionID + '/players/' + id
@realTimeOpponent.on 'change', @onRealTimeOpponentChanged
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Playing against ' + player.name
unless @realTimeOpponent?
console.error 'Did not find an oppoonent in onRealTimeMultiplayerJoinedGame.'
@updateTeam()
@realTimePlayerGameRef.update 'state': 'coding'
@realTimePlayerRef.update 'state': 'unavailable'
onRealTimeMultiplayerLeftGame: (e) ->
# console.log 'PlayLevelView onRealTimeMultiplayerLeftGame', e
if e.userID? and e.userID is me.id
@realTimePlayerGameStatus.set 'state', 'left'
@realTimePlayerGameRef.update 'state': 'left'
@realTimeGameEnded()
realTimeMultiplayerContinueGame: (realTimeSessionID) ->
@ -756,117 +774,112 @@ module.exports = class PlayLevelView extends RootView
Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: realTimeSessionID
console.info 'Setting my game status to ready'
@realTimePlayerGameStatus.set 'state', 'ready'
@realTimePlayerGameRef.update 'state': 'ready'
if @realTimeOpponent.get('state') is 'ready'
if @realTimeOpponentData.state is 'ready'
@realTimeOpponentIsReady()
else
console.info 'Waiting for opponent to be ready'
@realTimeOpponent.on 'change', @realTimeOpponentMaybeReady
@realTimeOpponentRef.on 'value', @realTimeOpponentMaybeReady
realTimeOpponentMaybeReady: =>
realTimeOpponentMaybeReady: (snapshot) =>
# console.log 'PlayLevelView realTimeOpponentMaybeReady'
if @realTimeOpponent.get('state') is 'ready'
@realTimeOpponent.off 'change', @realTimeOpponentMaybeReady
@realTimeOpponentIsReady()
if @realTimeOpponentData = snapshot.val()
if @realTimeOpponentData.state is 'ready'
@realTimeOpponentRef.off 'value', @realTimeOpponentMaybeReady
@realTimeOpponentIsReady()
realTimeOpponentIsReady: =>
console.info 'All real-time multiplayer players are ready!'
@realTimeSession.set 'state', 'running'
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Battling ' + @realTimeOpponent.get('name')
@realTimeSessionRef.update 'state': 'running'
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Battling ' + @realTimeOpponentData.name
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
realTimeGameEnded: ->
if @realTimeOpponent?
@realTimeOpponent.off 'change', @onRealTimeOpponentChanged
@realTimeOpponent = null
if @realTimePlayers?
@realTimePlayers.off 'add', @onRealTimePlayerAdded
@realTimePlayers = null
if @realTimeSession?
@realTimeSession.off 'change', @onRealTimeSessionChanged
@realTimeSession.set 'state', 'finished'
@realTimeSession = null
if @realTimePlayerGameStatus?
@realTimePlayerGameStatus = null
if @realTimePlayerStatus?
@realTimePlayerStatus.set 'state', 'playing'
if @realTimeOpponentRef?
@realTimeOpponentRef.off 'value', @onRealTimeOpponentChanged
@realTimeOpponentRef = null
if @realTimePlayersRef?
@realTimePlayersRef.off 'child_added', @onRealTimePlayerAdded
@realTimePlayersRef = null
if @realTimeSessionRef?
@realTimeSessionRef.off 'value', @onRealTimeSessionChanged
@realTimeSessionRef.update 'state': 'finished'
@realTimeSessionRef = null
if @realTimePlayerGameRef?
@realTimePlayerGameRef = null
if @realTimePlayerRef?
@realTimePlayerRef.update 'state': 'playing'
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: ''
onRealTimeMultiplayerCast: (e) ->
# console.log 'PlayLevelView onRealTimeMultiplayerCast', e
unless @realTimeSession
console.error 'onRealTimeMultiplayerCast without a multiplayerSession'
# console.log 'PlayLevelView onRealTimeMultiplayerCast', @realTimeSessionData, @realTimePlayersData
unless @realTimeSessionRef?
console.error 'Real-time multiplayer cast without multiplayer session.'
return
unless @realTimeSessionData?
console.error 'Real-time multiplayer cast without multiplayer data.'
return
unless @realTimePlayersData?
console.error 'Real-time multiplayer cast without multiplayer players data.'
return
# Set submissionCount for created real-time multiplayer session
if me.id is @realTimeSession.get('creator')
if me.id is @realTimeSessionData.creator
sessionState = @session.get('state')
if sessionState?
submissionCount = sessionState.submissionCount
console.info 'Setting multiplayer submissionCount to', submissionCount
@realTimeSession.set 'submissionCount', submissionCount
@realTimeSessionRef.update 'submissionCount': submissionCount
else
console.error 'Failed to read sessionState in onRealTimeMultiplayerCast'
players = new RealTimeCollection('multiplayer_level_sessions/' + @realTimeSession.id + '/players')
myPlayer = opponentPlayer = null
players.each (player) ->
if player.id is me.id
myPlayer = player
else
opponentPlayer = player
if myPlayer
console.info 'Submitting my code'
# Transpile code
# Copied from scripts/transpile.coffee
# TODO: Should this live somewhere else?
transpiledCode = {}
for thang, spells of @session.get('code')
transpiledCode[thang] = {}
for spellID, spell of spells
spellName = thang + '/' + spellID
continue if @session.get('teamSpells') and not (spellName in @session.get('teamSpells')[@session.get('team')])
# console.log "PlayLevelView Transpiling spell #{spellName}"
aetherOptions = createAetherOptions functionName: spellID, codeLanguage: @session.get('submittedCodeLanguage')
aether = new Aether aetherOptions
transpiledCode[thang][spellID] = aether.transpile spell
# console.log "PlayLevelView transpiled code", transpiledCode
@session.set 'transpiledCode', transpiledCode
@session.patch()
myPlayer.set 'state', 'submitted'
console.info 'Submitting my code'
# Transpiling code copied from scripts/transpile.coffee
# TODO: Should this live somewhere else?
transpiledCode = {}
for thang, spells of @session.get('code')
transpiledCode[thang] = {}
for spellID, spell of spells
spellName = thang + '/' + spellID
continue if @session.get('teamSpells') and not (spellName in @session.get('teamSpells')[@session.get('team')])
# console.log "PlayLevelView Transpiling spell #{spellName}"
aetherOptions = createAetherOptions functionName: spellID, codeLanguage: @session.get('submittedCodeLanguage'), includeFlow: true
aether = new Aether aetherOptions
transpiledCode[thang][spellID] = aether.transpile spell
# console.log "PlayLevelView transpiled code", transpiledCode
@session.set 'transpiledCode', transpiledCode
@session.patch()
@realTimePlayerGameRef.update 'state': 'submitted'
console.info 'Other player is', @realTimeOpponentData.state
if @realTimeOpponentData.state in ['submitted', 'ready']
@realTimeOpponentSubmittedCode @realTimeOpponentData, @realTimePlayerGameData
else
console.error 'Did not find my player in onRealTimeMultiplayerCast'
if opponentPlayer
# TODO: Shouldn't need nested opponentPlayer change listeners here
state = opponentPlayer.get('state')
console.info 'Other player is', state
if state in ['submitted', 'ready']
@realTimeOpponentSubmittedCode opponentPlayer, myPlayer
else
# Wait for opponent to submit their code
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Waiting for code from ' + @realTimeOpponent.get('name')
opponentPlayer.on 'change', (e) =>
state = opponentPlayer.get('state')
if state in ['submitted', 'ready']
@realTimeOpponentSubmittedCode opponentPlayer, myPlayer
opponentPlayer.off 'change'
else
console.error 'Did not find opponent player in onRealTimeMultiplayerCast'
# Wait for opponent to submit their code
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "Waiting for code from #{@realTimeOpponentData.name}"
@realTimeOpponentRef.on 'value', @realTimeOpponentMaybeSubmitted
realTimeOpponentMaybeSubmitted: (snapshot) =>
if @realTimeOpponentData = snapshot.val()
if @realTimeOpponentData.state in ['submitted', 'ready']
@realTimeOpponentRef.off 'value', @realTimeOpponentMaybeSubmitted
@realTimeOpponentSubmittedCode @realTimeOpponentData, @realTimePlayerGameData
onRealTimeMultiplayerPlaybackEnded: ->
if @realTimeSession?
@realTimeSession.set 'state', 'coding'
@realTimePlayers.each (player) -> player.set 'state', 'coding' if player.id is me.id
if @realTimeOpponent?
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Playing against ' + @realTimeOpponent.get('name')
# console.log 'PlayLevelView onRealTimeMultiplayerPlaybackEnded'
if @realTimeSessionRef?
@realTimeSessionRef.update 'state': 'coding'
@realTimePlayerGameRef.update 'state': 'coding'
if @realTimeOpponentData?
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "Playing against #{@realTimeOpponentData.name}"
realTimeOpponentSubmittedCode: (opponentPlayer, myPlayer) =>
# console.log 'PlayLevelView realTimeOpponentSubmittedCode', @realTimeSession.id, opponentPlayer.get('level_session')
# console.log 'PlayLevelView realTimeOpponentSubmittedCode', @realTimeSessionData.id, opponentPlayer.level_session
# Read submissionCount for joined real-time multiplayer session
if me.id isnt @realTimeSession.get('creator')
if me.id isnt @realTimeSessionData.creator
sessionState = @session.get('state') ? {}
newSubmissionCount = @realTimeSession.get 'submissionCount'
newSubmissionCount = @realTimeSessionData.submissionCount
if newSubmissionCount?
# TODO: This isn't always getting updated where the random seed generation uses it.
sessionState.submissionCount = parseInt newSubmissionCount
@ -878,21 +891,21 @@ module.exports = class PlayLevelView extends RootView
Backbone.Mediator.publish 'router:navigate',
route: "/play/level/#{@levelID}"
viewClass: PlayLevelView
viewArgs: [{supermodel: @supermodel, autoUnveil: true, realTimeMultiplayerSessionID: @realTimeSession.id, opponent: opponentPlayer.get('level_session'), team: @team}, @levelID]
viewArgs: [{supermodel: @supermodel, autoUnveil: true, realTimeMultiplayerSessionID: @realTimeSessionData.id, opponent: opponentPlayer.level_session, team: @team}, @levelID]
updateTeam: ->
# If not creator, and same team as creator, then switch teams
# TODO: Assumes there are only 'humans' and 'ogres'
unless @realTimeOpponent?
console.error 'Tried to switch teams without a real-time opponent.'
unless @realTimeOpponentData?
console.error 'Tried to switch teams without real-time multiplayer opponent data.'
return
unless @realTimeSession?
console.error 'Tried to switch teams without a real-time session.'
unless @realTimeSessionData?
console.error 'Tried to switch teams without real-time multiplayer session data.'
return
return if me.id is @realTimeSession.get('creator')
return if me.id is @realTimeSessionData.creator
oldTeam = @realTimeOpponent.get('team')
oldTeam = @realTimeOpponentData.team
return unless oldTeam is @session.get('team')
# Need to switch to other team