mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-24 19:59:53 -04:00
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:
parent
0c8b5ec9a8
commit
c203ff15a2
3 changed files with 202 additions and 186 deletions
|
@ -24,6 +24,7 @@ module.exports = class MultiplayerView extends CocoView
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super(options)
|
super(options)
|
||||||
@level = options.level
|
@level = options.level
|
||||||
|
@levelID = @level?.get 'slug'
|
||||||
@session = options.session
|
@session = options.session
|
||||||
@listenTo @session, 'change:multiplayer', @updateLinkSection
|
@listenTo @session, 'change:multiplayer', @updateLinkSection
|
||||||
@watchRealTimeSessions() if @level?.get('type') in ['hero-ladder']
|
@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.joinLink = "#{document.location.href.replace(/\?.*/, '').replace('#', '')}?session=#{@session.id}"
|
||||||
c.multiplayer = @session.get 'multiplayer'
|
c.multiplayer = @session.get 'multiplayer'
|
||||||
c.team = @session.get 'team'
|
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.
|
# For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet.
|
||||||
if @level?.get('type') in ['ladder', 'hero-ladder']
|
if @level?.get('type') in ['ladder', 'hero-ladder']
|
||||||
c.ladderGame = true
|
c.ladderGame = true
|
||||||
|
@ -71,7 +72,7 @@ module.exports = class MultiplayerView extends CocoView
|
||||||
e.target.select()
|
e.target.select()
|
||||||
|
|
||||||
onGameSubmitted: (e) ->
|
onGameSubmitted: (e) ->
|
||||||
ladderURL = "/play/ladder/#{@level.get('slug')}#my-matches"
|
ladderURL = "/play/ladder/#{@levelID}#my-matches"
|
||||||
Backbone.Mediator.publish 'router:navigate', route: ladderURL
|
Backbone.Mediator.publish 'router:navigate', route: ladderURL
|
||||||
|
|
||||||
updateLinkSection: ->
|
updateLinkSection: ->
|
||||||
|
@ -104,13 +105,14 @@ module.exports = class MultiplayerView extends CocoView
|
||||||
# @realTimeSessionsPlayers - Collection of player lists for active real-time multiplayer sessions
|
# @realTimeSessionsPlayers - Collection of player lists for active real-time multiplayer sessions
|
||||||
# @realTimeSessions - Active real-time multiplayer sessions
|
# @realTimeSessions - Active real-time multiplayer sessions
|
||||||
# @currentRealTimeSession - Our current real-time multiplayer session
|
# @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: ->
|
watchRealTimeSessions: ->
|
||||||
# Setup monitoring of real-time multiplayer level sessions
|
# Setup monitoring of real-time multiplayer level sessions
|
||||||
@realTimeSessionsPlayers = {}
|
@realTimeSessionsPlayers = {}
|
||||||
# TODO: only request sessions for this level, !team, etc.
|
# TODO: only request sessions for this level, !team, etc.
|
||||||
# TODO: move this to multiplayer_level_sessions/#{levelID}/
|
@realTimeSessions = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}")
|
||||||
@realTimeSessions = new RealTimeCollection('multiplayer_level_sessions/')
|
|
||||||
@realTimeSessions.on 'add', @onRealTimeSessionAdded
|
@realTimeSessions.on 'add', @onRealTimeSessionAdded
|
||||||
@realTimeSessions.each (rts) => @watchRealTimeSession rts
|
@realTimeSessions.each (rts) => @watchRealTimeSession rts
|
||||||
|
|
||||||
|
@ -120,9 +122,9 @@ module.exports = class MultiplayerView extends CocoView
|
||||||
# console.log 'MultiplayerView watchRealTimeSession', rts
|
# console.log 'MultiplayerView watchRealTimeSession', rts
|
||||||
# Setup monitoring of players for given session
|
# Setup monitoring of players for given session
|
||||||
# TODO: verify we need this
|
# 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
|
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
|
@realTimeSessionsPlayers[rts.id].on 'add', @onRealTimePlayerAdded
|
||||||
@findCurrentRealTimeSession rts
|
@findCurrentRealTimeSession rts
|
||||||
|
|
||||||
|
@ -133,7 +135,7 @@ module.exports = class MultiplayerView extends CocoView
|
||||||
@realTimeSessionsPlayers[rts.id].each (player) =>
|
@realTimeSessionsPlayers[rts.id].each (player) =>
|
||||||
if player.id is me.id and player.get('state') isnt 'left'
|
if player.id is me.id and player.get('state') isnt 'left'
|
||||||
# console.log 'MultiplayerView found current real-time session', rts
|
# 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
|
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
|
||||||
|
|
||||||
# TODO: Is this necessary? Shouldn't everyone already know we joined a game at this point?
|
# 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 = @realTimeSessions.get(s.id)
|
||||||
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
|
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
|
||||||
# TODO: s.id === @currentRealTimeSession.id ?
|
# 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
|
players.create
|
||||||
id: me.id
|
id: me.id
|
||||||
state: 'coding'
|
state: 'coding'
|
||||||
|
|
|
@ -33,6 +33,7 @@ module.exports = class LevelFlagsView extends CocoView
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
@levelID = options.levelID
|
||||||
@world = options.world
|
@world = options.world
|
||||||
|
|
||||||
onRealTimePlaybackStarted: (e) ->
|
onRealTimePlaybackStarted: (e) ->
|
||||||
|
@ -84,7 +85,7 @@ module.exports = class LevelFlagsView extends CocoView
|
||||||
@world = @options.world = event.world
|
@world = @options.world = event.world
|
||||||
|
|
||||||
onJoinedMultiplayerGame: (e) ->
|
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 'add', @onRealTimeMultiplayerFlagAdded
|
||||||
@realTimeFlags.on 'remove', @onRealTimeMultiplayerFlagRemoved
|
@realTimeFlags.on 'remove', @onRealTimeMultiplayerFlagRemoved
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,6 @@ LevelComponent = require 'models/LevelComponent'
|
||||||
Article = require 'models/Article'
|
Article = require 'models/Article'
|
||||||
Camera = require 'lib/surface/Camera'
|
Camera = require 'lib/surface/Camera'
|
||||||
AudioPlayer = require 'lib/AudioPlayer'
|
AudioPlayer = require 'lib/AudioPlayer'
|
||||||
RealTimeModel = require 'models/RealTimeModel'
|
|
||||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
|
||||||
|
|
||||||
# subviews
|
# subviews
|
||||||
LevelLoadingView = require './LevelLoadingView'
|
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 @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 LevelPlaybackView session: @session, levelID: @levelID, level: @level
|
||||||
@insertSubView new GoalsView {}
|
@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 GoldView {}
|
||||||
@insertSubView new HUDView {level: @level}
|
@insertSubView new HUDView {level: @level}
|
||||||
@insertSubView new LevelDialogueView {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 = new LevelSetupManager({supermodel: @supermodel, levelID: @levelID, parent: @, session: @session})
|
||||||
@setupManager.open()
|
@setupManager.open()
|
||||||
|
|
||||||
@onRealTimeMultiplayerLevelLoaded() if e.level.get('type') in ['hero-ladder']
|
@onRealTimeMultiplayerLevelLoaded e.session if e.level.get('type') in ['hero-ladder']
|
||||||
|
|
||||||
onLoaded: ->
|
onLoaded: ->
|
||||||
_.defer => @onLevelLoaderLoaded()
|
_.defer => @onLevelLoaderLoaded()
|
||||||
|
@ -608,147 +606,167 @@ module.exports = class PlayLevelView extends RootView
|
||||||
# Current real-time multiplayer session
|
# Current real-time multiplayer session
|
||||||
# Internal multiplayer create/joined/left events
|
# Internal multiplayer create/joined/left events
|
||||||
#
|
#
|
||||||
# Real-time state variables:
|
# Real-time state variables.
|
||||||
# @realTimePlayerStatus - User's real-time multiplayer state for this level
|
# Each Ref is Firebase reference, and may have a matching Data suffixed variable with the latest data received.
|
||||||
# @realTimePlayerGameStatus - User's state for current real-time multiplayer game session
|
# @realTimePlayerRef - User's real-time multiplayer player for this level
|
||||||
# @realTimeSession - Current real-time multiplayer game session
|
# @realTimePlayerGameRef - User's current real-time multiplayer player game session
|
||||||
# @realTimeOpponent - Current real-time multiplayer opponent
|
# @realTimeSessionRef - Current real-time multiplayer game session
|
||||||
# @realTimePlayers - Real-time players for current real-time multiplayer game session
|
# @realTimeOpponentRef - Current real-time multiplayer opponent
|
||||||
# @realTimeSessionCollection - Collection of all real-time multiplayer sessions
|
# @realTimePlayersRef - Real-time players for current real-time multiplayer game session
|
||||||
# @options.realTimeMultiplayerSessionID - Need to continue an existing real-time multiplayer 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: Move this code to it's own file, or possibly the LevelBus
|
||||||
# TODO: Save settings somewhere reasonable
|
# 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: ->
|
onRealTimeMultiplayerLevelLoaded: (session) ->
|
||||||
return if @realTimePlayerStatus?
|
# console.log 'PlayLevelView onRealTimeMultiplayerLevelLoaded'
|
||||||
|
return if @realTimePlayerRef?
|
||||||
return if me.get('anonymous')
|
return if me.get('anonymous')
|
||||||
|
@realTimePlayerRef = new Firebase "#{@multiplayerFireHost}multiplayer_players/#{@levelID}/#{me.id}"
|
||||||
unless @options.realTimeMultiplayerSessionID?
|
unless @options.realTimeMultiplayerSessionID?
|
||||||
players = new RealTimeCollection('multiplayer_players/' + @levelID)
|
# TODO: Wait for name instead of using 'Anon', or try and update it later?
|
||||||
players.create
|
name = me.get('name') ? session.get('creatorName') ? 'Anon'
|
||||||
id: me.id
|
@realTimePlayerRef.set
|
||||||
name: me.get('name')
|
id: me.id # TODO: is this redundant info necessary?
|
||||||
|
name: name
|
||||||
state: 'playing'
|
state: 'playing'
|
||||||
created: new Date().toISOString()
|
created: new Date().toISOString()
|
||||||
heartbeat: new Date().toISOString()
|
heartbeat: new Date().toISOString()
|
||||||
@realTimePlayerStatus = new RealTimeModel('multiplayer_players/' + @levelID + '/' + me.id)
|
|
||||||
@timerMultiplayerHeartbeatID = setInterval @onRealTimeMultiplayerHeartbeat, 60 * 1000
|
@timerMultiplayerHeartbeatID = setInterval @onRealTimeMultiplayerHeartbeat, 60 * 1000
|
||||||
@cleanupRealTimeSessions()
|
@cleanupRealTimeSessions()
|
||||||
|
|
||||||
cleanupRealTimeSessions: ->
|
cleanupRealTimeSessions: ->
|
||||||
@realTimeSessionCollection = new RealTimeCollection 'multiplayer_level_sessions'
|
# console.log 'PlayLevelView cleanupRealTimeSessions'
|
||||||
@realTimeSessionCollection.on 'add', @cleanupRealTimeSession
|
# TODO: Reduce this call, possibly by username and dates
|
||||||
@realTimeSessionCollection.each @cleanupRealTimeSession
|
realTimeSessionCollection = new Firebase "#{@multiplayerFireHost}multiplayer_level_sessions/#{@levelID}"
|
||||||
|
realTimeSessionCollection.once 'value', (collectionSnapshot) =>
|
||||||
cleanupRealTimeSession: (session) =>
|
for multiplayerSessionID, multiplayerSession of collectionSnapshot.val()
|
||||||
return if @options.realTimeMultiplayerSessionID? and @options.realTimeMultiplayerSessionID is session.id
|
continue if @options.realTimeMultiplayerSessionID? and @options.realTimeMultiplayerSessionID is multiplayerSessionID
|
||||||
if session.get('state') isnt 'finished'
|
continue unless multiplayerSession.state isnt 'finished'
|
||||||
players = new RealTimeCollection 'multiplayer_level_sessions/' + session.id + '/players'
|
player = realTimeSessionCollection.child "#{multiplayerSession.id}/players/#{me.id}"
|
||||||
players.each (player) =>
|
player.once 'value', (playerSnapshot) =>
|
||||||
if player.id is me.id
|
if playerSnapshot.val()
|
||||||
p = new RealTimeModel 'multiplayer_level_sessions/' + session.id + '/players/' + me.id
|
console.info 'Cleaning up previous real-time multiplayer session', multiplayerSessionID
|
||||||
console.info 'Cleaning up previous real-time multiplayer session', session.id
|
player.update 'state': 'left'
|
||||||
p.set 'state', 'left'
|
multiplayerSessionRef = realTimeSessionCollection.child "#{multiplayerSessionID}"
|
||||||
session.set 'state', 'finished'
|
multiplayerSessionRef.update 'state': 'finished'
|
||||||
|
|
||||||
onRealTimeMultiplayerLevelUnloaded: ->
|
onRealTimeMultiplayerLevelUnloaded: ->
|
||||||
# console.log 'PlayLevelView onRealTimeMultiplayerLevelUnloaded'
|
# console.log 'PlayLevelView onRealTimeMultiplayerLevelUnloaded'
|
||||||
if @timerMultiplayerHeartbeatID?
|
if @timerMultiplayerHeartbeatID?
|
||||||
clearInterval @timerMultiplayerHeartbeatID
|
clearInterval @timerMultiplayerHeartbeatID
|
||||||
@timerMultiplayerHeartbeatID = null
|
@timerMultiplayerHeartbeatID = null
|
||||||
if @realTimeSessionCollection?
|
|
||||||
@realTimeSessionCollection.off 'add', @cleanupRealTimeSession
|
|
||||||
@realTimeSessionCollection = null
|
|
||||||
|
|
||||||
# TODO: similar to game ending cleanup
|
# TODO: similar to game ending cleanup
|
||||||
if @realTimeOpponent?
|
if @realTimeOpponentRef?
|
||||||
@realTimeOpponent.off 'change', @onRealTimeOpponentChanged
|
@realTimeOpponentRef.off 'value', @onRealTimeOpponentChanged
|
||||||
@realTimeOpponent = null
|
@realTimeOpponentRef = null
|
||||||
if @realTimePlayers?
|
if @realTimePlayersRef?
|
||||||
@realTimePlayers.off 'add', @onRealTimePlayerAdded
|
@realTimePlayersRef.off 'child_added', @onRealTimePlayerAdded
|
||||||
@realTimePlayers = null
|
@realTimePlayersRef = null
|
||||||
if @realTimeSession?
|
if @realTimeSessionRef?
|
||||||
@realTimeSession.off 'change', @onRealTimeSessionChanged
|
@realTimeSessionRef.off 'value', @onRealTimeSessionChanged
|
||||||
@realTimeSession = null
|
@realTimeSessionRef = null
|
||||||
if @realTimePlayerGameStatus?
|
if @realTimePlayerGameRef?
|
||||||
@realTimePlayerGameStatus = null
|
@realTimePlayerGameRef = null
|
||||||
if @realTimePlayerStatus?
|
if @realTimePlayerRef?
|
||||||
@realTimePlayerStatus = null
|
@realTimePlayerRef = null
|
||||||
|
|
||||||
onRealTimeMultiplayerHeartbeat: =>
|
onRealTimeMultiplayerHeartbeat: =>
|
||||||
@realTimePlayerStatus.set 'heartbeat', new Date().toISOString() if @realTimePlayerStatus
|
# console.log 'PlayLevelView onRealTimeMultiplayerHeartbeat', @realTimePlayerRef
|
||||||
|
@realTimePlayerRef.update 'heartbeat': new Date().toISOString() if @realTimePlayerRef?
|
||||||
|
|
||||||
onRealTimeMultiplayerCreatedGame: (e) ->
|
onRealTimeMultiplayerCreatedGame: (e) ->
|
||||||
|
# console.log 'PlayLevelView onRealTimeMultiplayerCreatedGame'
|
||||||
@joinRealTimeMultiplayerGame e
|
@joinRealTimeMultiplayerGame e
|
||||||
@realTimePlayerGameStatus.set 'state', 'coding'
|
@realTimePlayerGameRef.update 'state': 'coding'
|
||||||
@realTimePlayerStatus.set 'state', 'available'
|
@realTimePlayerRef.update 'state': 'available'
|
||||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Waiting for opponent..'
|
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Waiting for opponent..'
|
||||||
|
|
||||||
onRealTimeSessionChanged: (e) =>
|
onRealTimeSessionChanged: (snapshot) =>
|
||||||
# console.log 'PlayLevelView onRealTimeSessionChanged', e
|
# console.log 'PlayLevelView onRealTimeSessionChanged', snapshot.val()
|
||||||
if e.get('state') is 'finished'
|
@realTimeSessionData = snapshot.val()
|
||||||
|
if @realTimeSessionData?.state is 'finished'
|
||||||
@realTimeGameEnded()
|
@realTimeGameEnded()
|
||||||
Backbone.Mediator.publish 'real-time-multiplayer:left-game', {}
|
Backbone.Mediator.publish 'real-time-multiplayer:left-game', {}
|
||||||
|
|
||||||
onRealTimePlayerAdded: (e) =>
|
onRealTimePlayerAdded: (snapshot) =>
|
||||||
# console.log 'PlayLevelView onRealTimePlayerAdded', e
|
# console.log 'PlayLevelView onRealTimePlayerAdded', snapshot.val()
|
||||||
# Assume game is full, game on
|
# Assume game is full, game on
|
||||||
if @realTimeSession.get('state') is 'creating'
|
data = snapshot.val()
|
||||||
@realTimeSession.set 'state', 'coding'
|
if data? and data.id isnt me.id
|
||||||
@realTimePlayerStatus.set 'state', 'unavailable'
|
@realTimeOpponentData = data
|
||||||
@realTimeOpponent = new RealTimeModel('multiplayer_level_sessions/' + @realTimeSession.id + '/players/' + e.id)
|
console.log 'PlayLevelView onRealTimePlayerAdded opponent', @realTimeOpponentData, @realTimePlayersData
|
||||||
@realTimeOpponent.on 'change', @onRealTimeOpponentChanged
|
@realTimePlayersData[@realTimeOpponentData.id] = @realTimeOpponentData
|
||||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Playing against ' + e.get('name')
|
if @realTimeSessionData?.state is 'creating'
|
||||||
else
|
@realTimeSessionRef.update 'state': 'coding'
|
||||||
console.error 'PlayLevelView onRealTimePlayerAdded session in unexpected state', @realTimeSession.get('state')
|
@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) =>
|
onRealTimeOpponentChanged: (snapshot) =>
|
||||||
# console.log 'PlayLevelView onRealTimeOpponentChanged', e
|
# console.log 'PlayLevelView onRealTimeOpponentChanged', snapshot.val()
|
||||||
switch @realTimeOpponent.get('state')
|
@realTimeOpponentData = snapshot.val()
|
||||||
|
switch @realTimeOpponentData?.state
|
||||||
when 'left'
|
when 'left'
|
||||||
console.info 'Real-time multiplayer opponent left the game'
|
console.info 'Real-time multiplayer opponent left the game'
|
||||||
opponentID = @realTimeOpponent.id
|
opponentID = @realTimeOpponentData.id
|
||||||
@realTimeGameEnded()
|
@realTimeGameEnded()
|
||||||
Backbone.Mediator.publish 'real-time-multiplayer:left-game', userID: opponentID
|
Backbone.Mediator.publish 'real-time-multiplayer:left-game', userID: opponentID
|
||||||
when 'submitted'
|
when 'submitted'
|
||||||
# TODO: What should this message say?
|
# 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) ->
|
joinRealTimeMultiplayerGame: (e) ->
|
||||||
unless @realTimeSession?
|
# console.log 'PlayLevelView joinRealTimeMultiplayerGame', e
|
||||||
# TODO: Necessary for real-time multiplayer sessions?
|
unless @realTimeSessionRef?
|
||||||
@session.set('submittedCodeLanguage', @session.get('codeLanguage'))
|
@session.set('submittedCodeLanguage', @session.get('codeLanguage'))
|
||||||
@session.save()
|
@session.save()
|
||||||
|
|
||||||
@realTimeSession = new RealTimeModel 'multiplayer_level_sessions/' + e.realTimeSessionID
|
@realTimeSessionRef = new Firebase "#{@multiplayerFireHost}multiplayer_level_sessions/#{@levelID}/#{e.realTimeSessionID}"
|
||||||
@realTimeSession.on 'change', @onRealTimeSessionChanged
|
@realTimePlayersRef = @realTimeSessionRef.child 'players'
|
||||||
@realTimePlayers = new RealTimeCollection 'multiplayer_level_sessions/' + e.realTimeSessionID + '/players'
|
|
||||||
@realTimePlayers.on 'add', @onRealTimePlayerAdded
|
# Look for opponent
|
||||||
@realTimePlayerGameStatus = new RealTimeModel 'multiplayer_level_sessions/' + e.realTimeSessionID + '/players/' + me.id
|
@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
|
# TODO: Follow up in MultiplayerView to see if double joins can be avoided
|
||||||
# else
|
# 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) ->
|
onRealTimeMultiplayerJoinedGame: (e) ->
|
||||||
# console.log 'PlayLevelView onRealTimeMultiplayerJoinedGame', e
|
console.log 'PlayLevelView onRealTimeMultiplayerJoinedGame', e
|
||||||
@joinRealTimeMultiplayerGame e
|
@joinRealTimeMultiplayerGame e
|
||||||
@realTimePlayerGameStatus.set 'state', 'coding'
|
@realTimePlayerGameRef.update 'state': 'coding'
|
||||||
@realTimePlayerStatus.set 'state', 'unavailable'
|
@realTimePlayerRef.update '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()
|
|
||||||
|
|
||||||
onRealTimeMultiplayerLeftGame: (e) ->
|
onRealTimeMultiplayerLeftGame: (e) ->
|
||||||
# console.log 'PlayLevelView onRealTimeMultiplayerLeftGame', e
|
# console.log 'PlayLevelView onRealTimeMultiplayerLeftGame', e
|
||||||
if e.userID? and e.userID is me.id
|
if e.userID? and e.userID is me.id
|
||||||
@realTimePlayerGameStatus.set 'state', 'left'
|
@realTimePlayerGameRef.update 'state': 'left'
|
||||||
@realTimeGameEnded()
|
@realTimeGameEnded()
|
||||||
|
|
||||||
realTimeMultiplayerContinueGame: (realTimeSessionID) ->
|
realTimeMultiplayerContinueGame: (realTimeSessionID) ->
|
||||||
|
@ -756,117 +774,112 @@ module.exports = class PlayLevelView extends RootView
|
||||||
Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: realTimeSessionID
|
Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: realTimeSessionID
|
||||||
|
|
||||||
console.info 'Setting my game status to ready'
|
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()
|
@realTimeOpponentIsReady()
|
||||||
else
|
else
|
||||||
console.info 'Waiting for opponent to be ready'
|
console.info 'Waiting for opponent to be ready'
|
||||||
@realTimeOpponent.on 'change', @realTimeOpponentMaybeReady
|
@realTimeOpponentRef.on 'value', @realTimeOpponentMaybeReady
|
||||||
|
|
||||||
realTimeOpponentMaybeReady: =>
|
realTimeOpponentMaybeReady: (snapshot) =>
|
||||||
# console.log 'PlayLevelView realTimeOpponentMaybeReady'
|
# console.log 'PlayLevelView realTimeOpponentMaybeReady'
|
||||||
if @realTimeOpponent.get('state') is 'ready'
|
if @realTimeOpponentData = snapshot.val()
|
||||||
@realTimeOpponent.off 'change', @realTimeOpponentMaybeReady
|
if @realTimeOpponentData.state is 'ready'
|
||||||
@realTimeOpponentIsReady()
|
@realTimeOpponentRef.off 'value', @realTimeOpponentMaybeReady
|
||||||
|
@realTimeOpponentIsReady()
|
||||||
|
|
||||||
realTimeOpponentIsReady: =>
|
realTimeOpponentIsReady: =>
|
||||||
console.info 'All real-time multiplayer players are ready!'
|
console.info 'All real-time multiplayer players are ready!'
|
||||||
@realTimeSession.set 'state', 'running'
|
@realTimeSessionRef.update 'state': 'running'
|
||||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Battling ' + @realTimeOpponent.get('name')
|
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Battling ' + @realTimeOpponentData.name
|
||||||
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
||||||
|
|
||||||
realTimeGameEnded: ->
|
realTimeGameEnded: ->
|
||||||
if @realTimeOpponent?
|
if @realTimeOpponentRef?
|
||||||
@realTimeOpponent.off 'change', @onRealTimeOpponentChanged
|
@realTimeOpponentRef.off 'value', @onRealTimeOpponentChanged
|
||||||
@realTimeOpponent = null
|
@realTimeOpponentRef = null
|
||||||
if @realTimePlayers?
|
if @realTimePlayersRef?
|
||||||
@realTimePlayers.off 'add', @onRealTimePlayerAdded
|
@realTimePlayersRef.off 'child_added', @onRealTimePlayerAdded
|
||||||
@realTimePlayers = null
|
@realTimePlayersRef = null
|
||||||
if @realTimeSession?
|
if @realTimeSessionRef?
|
||||||
@realTimeSession.off 'change', @onRealTimeSessionChanged
|
@realTimeSessionRef.off 'value', @onRealTimeSessionChanged
|
||||||
@realTimeSession.set 'state', 'finished'
|
@realTimeSessionRef.update 'state': 'finished'
|
||||||
@realTimeSession = null
|
@realTimeSessionRef = null
|
||||||
if @realTimePlayerGameStatus?
|
if @realTimePlayerGameRef?
|
||||||
@realTimePlayerGameStatus = null
|
@realTimePlayerGameRef = null
|
||||||
if @realTimePlayerStatus?
|
if @realTimePlayerRef?
|
||||||
@realTimePlayerStatus.set 'state', 'playing'
|
@realTimePlayerRef.update 'state': 'playing'
|
||||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: ''
|
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: ''
|
||||||
|
|
||||||
onRealTimeMultiplayerCast: (e) ->
|
onRealTimeMultiplayerCast: (e) ->
|
||||||
# console.log 'PlayLevelView onRealTimeMultiplayerCast', e
|
# console.log 'PlayLevelView onRealTimeMultiplayerCast', @realTimeSessionData, @realTimePlayersData
|
||||||
unless @realTimeSession
|
unless @realTimeSessionRef?
|
||||||
console.error 'onRealTimeMultiplayerCast without a multiplayerSession'
|
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
|
return
|
||||||
|
|
||||||
# Set submissionCount for created real-time multiplayer session
|
# 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')
|
sessionState = @session.get('state')
|
||||||
if sessionState?
|
if sessionState?
|
||||||
submissionCount = sessionState.submissionCount
|
submissionCount = sessionState.submissionCount
|
||||||
console.info 'Setting multiplayer submissionCount to', submissionCount
|
console.info 'Setting multiplayer submissionCount to', submissionCount
|
||||||
@realTimeSession.set 'submissionCount', submissionCount
|
@realTimeSessionRef.update 'submissionCount': submissionCount
|
||||||
else
|
else
|
||||||
console.error 'Failed to read sessionState in onRealTimeMultiplayerCast'
|
console.error 'Failed to read sessionState in onRealTimeMultiplayerCast'
|
||||||
|
|
||||||
players = new RealTimeCollection('multiplayer_level_sessions/' + @realTimeSession.id + '/players')
|
console.info 'Submitting my code'
|
||||||
myPlayer = opponentPlayer = null
|
# Transpiling code copied from scripts/transpile.coffee
|
||||||
players.each (player) ->
|
# TODO: Should this live somewhere else?
|
||||||
if player.id is me.id
|
transpiledCode = {}
|
||||||
myPlayer = player
|
for thang, spells of @session.get('code')
|
||||||
else
|
transpiledCode[thang] = {}
|
||||||
opponentPlayer = player
|
for spellID, spell of spells
|
||||||
if myPlayer
|
spellName = thang + '/' + spellID
|
||||||
console.info 'Submitting my code'
|
continue if @session.get('teamSpells') and not (spellName in @session.get('teamSpells')[@session.get('team')])
|
||||||
# Transpile code
|
# console.log "PlayLevelView Transpiling spell #{spellName}"
|
||||||
# Copied from scripts/transpile.coffee
|
aetherOptions = createAetherOptions functionName: spellID, codeLanguage: @session.get('submittedCodeLanguage'), includeFlow: true
|
||||||
# TODO: Should this live somewhere else?
|
aether = new Aether aetherOptions
|
||||||
transpiledCode = {}
|
transpiledCode[thang][spellID] = aether.transpile spell
|
||||||
for thang, spells of @session.get('code')
|
# console.log "PlayLevelView transpiled code", transpiledCode
|
||||||
transpiledCode[thang] = {}
|
@session.set 'transpiledCode', transpiledCode
|
||||||
for spellID, spell of spells
|
@session.patch()
|
||||||
spellName = thang + '/' + spellID
|
@realTimePlayerGameRef.update 'state': 'submitted'
|
||||||
continue if @session.get('teamSpells') and not (spellName in @session.get('teamSpells')[@session.get('team')])
|
|
||||||
# console.log "PlayLevelView Transpiling spell #{spellName}"
|
console.info 'Other player is', @realTimeOpponentData.state
|
||||||
aetherOptions = createAetherOptions functionName: spellID, codeLanguage: @session.get('submittedCodeLanguage')
|
if @realTimeOpponentData.state in ['submitted', 'ready']
|
||||||
aether = new Aether aetherOptions
|
@realTimeOpponentSubmittedCode @realTimeOpponentData, @realTimePlayerGameData
|
||||||
transpiledCode[thang][spellID] = aether.transpile spell
|
|
||||||
# console.log "PlayLevelView transpiled code", transpiledCode
|
|
||||||
@session.set 'transpiledCode', transpiledCode
|
|
||||||
@session.patch()
|
|
||||||
myPlayer.set 'state', 'submitted'
|
|
||||||
else
|
else
|
||||||
console.error 'Did not find my player in onRealTimeMultiplayerCast'
|
# Wait for opponent to submit their code
|
||||||
if opponentPlayer
|
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "Waiting for code from #{@realTimeOpponentData.name}"
|
||||||
# TODO: Shouldn't need nested opponentPlayer change listeners here
|
@realTimeOpponentRef.on 'value', @realTimeOpponentMaybeSubmitted
|
||||||
state = opponentPlayer.get('state')
|
|
||||||
console.info 'Other player is', state
|
realTimeOpponentMaybeSubmitted: (snapshot) =>
|
||||||
if state in ['submitted', 'ready']
|
if @realTimeOpponentData = snapshot.val()
|
||||||
@realTimeOpponentSubmittedCode opponentPlayer, myPlayer
|
if @realTimeOpponentData.state in ['submitted', 'ready']
|
||||||
else
|
@realTimeOpponentRef.off 'value', @realTimeOpponentMaybeSubmitted
|
||||||
# Wait for opponent to submit their code
|
@realTimeOpponentSubmittedCode @realTimeOpponentData, @realTimePlayerGameData
|
||||||
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'
|
|
||||||
|
|
||||||
onRealTimeMultiplayerPlaybackEnded: ->
|
onRealTimeMultiplayerPlaybackEnded: ->
|
||||||
if @realTimeSession?
|
# console.log 'PlayLevelView onRealTimeMultiplayerPlaybackEnded'
|
||||||
@realTimeSession.set 'state', 'coding'
|
if @realTimeSessionRef?
|
||||||
@realTimePlayers.each (player) -> player.set 'state', 'coding' if player.id is me.id
|
@realTimeSessionRef.update 'state': 'coding'
|
||||||
if @realTimeOpponent?
|
@realTimePlayerGameRef.update 'state': 'coding'
|
||||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Playing against ' + @realTimeOpponent.get('name')
|
if @realTimeOpponentData?
|
||||||
|
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "Playing against #{@realTimeOpponentData.name}"
|
||||||
|
|
||||||
realTimeOpponentSubmittedCode: (opponentPlayer, myPlayer) =>
|
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
|
# 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') ? {}
|
sessionState = @session.get('state') ? {}
|
||||||
newSubmissionCount = @realTimeSession.get 'submissionCount'
|
newSubmissionCount = @realTimeSessionData.submissionCount
|
||||||
if newSubmissionCount?
|
if newSubmissionCount?
|
||||||
# TODO: This isn't always getting updated where the random seed generation uses it.
|
# TODO: This isn't always getting updated where the random seed generation uses it.
|
||||||
sessionState.submissionCount = parseInt newSubmissionCount
|
sessionState.submissionCount = parseInt newSubmissionCount
|
||||||
|
@ -878,21 +891,21 @@ module.exports = class PlayLevelView extends RootView
|
||||||
Backbone.Mediator.publish 'router:navigate',
|
Backbone.Mediator.publish 'router:navigate',
|
||||||
route: "/play/level/#{@levelID}"
|
route: "/play/level/#{@levelID}"
|
||||||
viewClass: PlayLevelView
|
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: ->
|
updateTeam: ->
|
||||||
# If not creator, and same team as creator, then switch teams
|
# If not creator, and same team as creator, then switch teams
|
||||||
# TODO: Assumes there are only 'humans' and 'ogres'
|
# TODO: Assumes there are only 'humans' and 'ogres'
|
||||||
|
|
||||||
unless @realTimeOpponent?
|
unless @realTimeOpponentData?
|
||||||
console.error 'Tried to switch teams without a real-time opponent.'
|
console.error 'Tried to switch teams without real-time multiplayer opponent data.'
|
||||||
return
|
return
|
||||||
unless @realTimeSession?
|
unless @realTimeSessionData?
|
||||||
console.error 'Tried to switch teams without a real-time session.'
|
console.error 'Tried to switch teams without real-time multiplayer session data.'
|
||||||
return
|
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')
|
return unless oldTeam is @session.get('team')
|
||||||
|
|
||||||
# Need to switch to other team
|
# Need to switch to other team
|
||||||
|
|
Loading…
Add table
Reference in a new issue