mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 00:40:56 -05:00
Real-time multiplayer initial commit
Simple matchmaking, synchronous multiplayer PVP, flags! Rough matchmaking is under the game menu multiplayer tab, for ladder games only. After creating a 2-person game there, you can exit that modal and real-time cast to play against each other. If you’re the first person to cast, you’ll sit at the real-time level playback view waiting until the other player casts. When they do, you both should start the real-time playback (and start placing flags like crazy people). If in a multiplayer session, the real-time simulation runs the players’ code against each other. Your multiplayer opponent’s name should be up near the level name. Multiplayer sessions are stored completely in Firebase for now, and removed if both players leave the game. There’s plenty of bugs, synchronization issues, and minimal polish to add before we push it to master.
This commit is contained in:
parent
a4b2333fd3
commit
68cca74b43
11 changed files with 358 additions and 10 deletions
6
app/collections/RealTimeCollection.coffee
Normal file
6
app/collections/RealTimeCollection.coffee
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = class FlagCollection extends Backbone.Firebase.Collection
|
||||
constructor: (savePath) ->
|
||||
# TODO: Don't hard code this here
|
||||
# TODO: Use prod path in prod
|
||||
@firebase = 'https://codecombat.firebaseio.com/test/db/' + savePath
|
||||
super()
|
|
@ -5,24 +5,85 @@ if !ladderGame
|
|||
input#multiplayer(name="multiplayer", type="checkbox", checked=multiplayer)
|
||||
span(data-i18n="multiplayer.multiplayer_toggle") Enable multiplayer
|
||||
span.help-block(data-i18n="multiplayer.multiplayer_toggle_description") Allow others to join your game.
|
||||
|
||||
|
||||
hr
|
||||
|
||||
div#link-area
|
||||
p(data-i18n="multiplayer.multiplayer_link_description") Give this link to anyone to have them join you.
|
||||
|
||||
|
||||
textarea.well#multiplayer-join-link(readonly=true)= joinLink
|
||||
|
||||
|
||||
p
|
||||
strong(data-i18n="multiplayer.multiplayer_hint_label") Hint:
|
||||
span(data-i18n="multiplayer.multiplayer_hint") Click the link to select all, then press ⌘-C or Ctrl-C to copy the link.
|
||||
|
||||
|
||||
p(data-i18n="multiplayer.multiplayer_coming_soon") More multiplayer features to come!
|
||||
|
||||
if ladderGame
|
||||
if me.get('anonymous')
|
||||
p(data-i18n="multiplayer.multiplayer_sign_in_leaderboard") Sign in or create an account and get your solution on the leaderboard.
|
||||
else if readyToRank
|
||||
button#create-game-button Create Game
|
||||
|
||||
hr
|
||||
|
||||
div#created-multiplayer-session
|
||||
h3 Your Game
|
||||
if currentMultiplayerSession
|
||||
div
|
||||
span(style="margin:10px")= currentMultiplayerSession.get('levelID')
|
||||
span(style="margin:10px")= currentMultiplayerSession.get('creatorName')
|
||||
span(style="margin:10px")= currentMultiplayerSession.get('state')
|
||||
span(style="margin:10px")= currentMultiplayerSession.id
|
||||
button#leave-game-button(data-item=item) Leave Game
|
||||
div
|
||||
- var players = playersCollections[currentMultiplayerSession.id]
|
||||
span(style="margin:10px") Players:
|
||||
- for (var i=0; i < players.length; i++) {
|
||||
- var name = players.at(i).get('name')
|
||||
- var team = players.at(i).get('team')
|
||||
span(style="margin:10px")= name
|
||||
span(style="margin:10px")= team
|
||||
- }
|
||||
else
|
||||
div Click something above to create a game.
|
||||
|
||||
hr
|
||||
|
||||
div#open-games
|
||||
h3 Open Games
|
||||
- var noOpenGames = true
|
||||
if multiplayerSessions
|
||||
- for (var i=0; i < multiplayerSessions.length; i++) {
|
||||
if levelID === multiplayerSessions[i].get('levelID') && multiplayerSessions[i].get('state') === 'creating'
|
||||
- var id = multiplayerSessions[i].get('id')
|
||||
- var players = playersCollections[id]
|
||||
if players && players.length < 2
|
||||
- noOpenGames = false
|
||||
- var creatorName = multiplayerSessions[i].get('creatorName')
|
||||
- var creator = multiplayerSessions[i].get('creator')
|
||||
- var state = multiplayerSessions[i].get('state')
|
||||
- var item = multiplayerSessions[i]
|
||||
div
|
||||
button#join-game-button(data-item=item) Join Game
|
||||
span(style="margin:10px")= levelID
|
||||
span(style="margin:10px")= creatorName
|
||||
span(style="margin:10px")= state
|
||||
span(style="margin:10px")= id
|
||||
div
|
||||
span(style="margin:10px") Players:
|
||||
- for (var j=0; j < players.length; j++) {
|
||||
- var name = players.at(j).get('name')
|
||||
- var team = players.at(j).get('team')
|
||||
span(style="margin:10px")= name
|
||||
span(style="margin:10px")= team
|
||||
- }
|
||||
- }
|
||||
if noOpenGames
|
||||
div No games available.
|
||||
|
||||
hr
|
||||
|
||||
.ladder-submission-view
|
||||
else
|
||||
a.btn.btn-primary(href="/play/ladder/#{levelSlug}#my-matches", data-i18n="multiplayer.victory_go_ladder") Return to Ladder
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
h4.home
|
||||
|
||||
a(href=homeLink || "/")
|
||||
|
||||
a(href=homeLink || "/")
|
||||
i.icon-home.icon-white
|
||||
span(data-i18n="play_level.home") Home
|
||||
|
||||
h4.title
|
||||
| #{worldName}
|
||||
| -
|
||||
| -
|
||||
a(href=editorLink, data-i18n="nav.editor", title="Open " + worldName + " in the Level Editor") Editor
|
||||
if multiplayerSession
|
||||
- found = false
|
||||
- for (var i=0; i < multiplayerPlayers.length; i++) {
|
||||
if (multiplayerPlayers.at(i).id !== meID)
|
||||
| - vs #{multiplayerPlayers.at(i).get('name')}
|
||||
- found = true
|
||||
- break
|
||||
- }
|
||||
if !found
|
||||
| - waiting...
|
||||
|
||||
|
||||
|
||||
button.btn.btn-xs.btn-warning.banner#stop-real-time-playback-button(title="Stop real-time playback", data-i18n="play_level.stop") Stop
|
||||
|
|
|
@ -3,6 +3,7 @@ template = require 'templates/game-menu/multiplayer-view'
|
|||
{me} = require 'lib/auth'
|
||||
ThangType = require 'models/ThangType'
|
||||
LadderSubmissionView = require 'views/play/common/LadderSubmissionView'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
|
||||
module.exports = class MultiplayerView extends CocoView
|
||||
id: 'multiplayer-view'
|
||||
|
@ -15,6 +16,9 @@ module.exports = class MultiplayerView extends CocoView
|
|||
events:
|
||||
'click textarea': 'onClickLink'
|
||||
'change #multiplayer': 'updateLinkSection'
|
||||
'click #create-game-button': 'onCreateGame'
|
||||
'click #join-game-button': 'onJoinGame'
|
||||
'click #leave-game-button': 'onLeaveGame'
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
|
@ -23,6 +27,20 @@ module.exports = class MultiplayerView extends CocoView
|
|||
@playableTeams = options.playableTeams
|
||||
@listenTo @session, 'change:multiplayer', @updateLinkSection
|
||||
|
||||
# TODO: only request sessions for this level, !team, etc.
|
||||
# TODO: don't hard code this path all over the place
|
||||
@multiplayerSessions = new RealTimeCollection('multiplayer_level_sessions/')
|
||||
@multiplayerSessions.on 'add', @onMultiplayerSessionAdded
|
||||
@multiplayerSessions.on 'remove', @onMultiplayerSessionRemoved
|
||||
@playersCollections = {}
|
||||
|
||||
destroy: ->
|
||||
@multiplayerSessions?.off()
|
||||
@currentMultiplayerSession?.off()
|
||||
for id in @playersCollections
|
||||
@playersCollections[id].off()
|
||||
super()
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.joinLink = "#{document.location.href.replace(/\?.*/, '').replace('#', '')}?session=#{@session.id}"
|
||||
|
@ -34,6 +52,11 @@ module.exports = class MultiplayerView extends CocoView
|
|||
if @level?.get('type') is 'ladder'
|
||||
c.ladderGame = true
|
||||
c.readyToRank = @session?.readyToRank()
|
||||
|
||||
c.levelID = @session.get('levelID')
|
||||
c.multiplayerSessions = @multiplayerSessions.models
|
||||
c.currentMultiplayerSession = @currentMultiplayerSession if @currentMultiplayerSession
|
||||
c.playersCollections = @playersCollections if @playersCollections
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
|
@ -41,6 +64,8 @@ module.exports = class MultiplayerView extends CocoView
|
|||
@updateLinkSection()
|
||||
@ladderSubmissionView = new LadderSubmissionView session: @session, level: @level
|
||||
@insertSubView @ladderSubmissionView, @$el.find('.ladder-submission-view')
|
||||
@$el.find('#created-multiplayer-session').toggle Boolean(@currentMultiplayerSession?)
|
||||
@$el.find('#create-game-button').toggle Boolean(!(@currentMultiplayerSession?))
|
||||
|
||||
onClickLink: (e) ->
|
||||
e.target.select()
|
||||
|
@ -52,9 +77,96 @@ module.exports = class MultiplayerView extends CocoView
|
|||
updateLinkSection: ->
|
||||
multiplayer = @$el.find('#multiplayer').prop('checked')
|
||||
la = @$el.find('#link-area')
|
||||
la.toggle Boolean(multiplayer)
|
||||
la.toggle if @level?.get('type') is 'ladder' then false else Boolean(multiplayer)
|
||||
true
|
||||
|
||||
onHidden: ->
|
||||
multiplayer = Boolean(@$el.find('#multiplayer').prop('checked'))
|
||||
@session.set('multiplayer', multiplayer)
|
||||
|
||||
|
||||
# TODO: shouldn't have to open MultiplayerView to read existing multiplayerSession?
|
||||
# TODO: No current game shown when: this view closed, opponent leaves your game, this view opened
|
||||
|
||||
onMultiplayerSessionAdded: (e) =>
|
||||
# TODO: double check these players events are needed on top of onMultiplayerSessionChanged
|
||||
@playersCollections[e.id] = new RealTimeCollection('multiplayer_level_sessions/' + e.id + '/players')
|
||||
@playersCollections[e.id].on 'add', @onPlayerAdded
|
||||
@playersCollections[e.id].on 'remove', @onPlayerRemoved
|
||||
# Check if we've already joined this multiplayer session
|
||||
if not @currentMultiplayerSession and e.get('levelID') == @session.get('levelID')
|
||||
for i in [0...@playersCollections[e.id].length]
|
||||
player = @playersCollections[e.id].at(i)
|
||||
if player.get('id') is me.id and player.get('team') is @session.get('team')
|
||||
@currentMultiplayerSession = e
|
||||
@currentMultiplayerSession.on 'change', @onMultiplayerSessionChanged
|
||||
Backbone.Mediator.publish 'realtime-multiplayer:joined-game', @currentMultiplayerSession
|
||||
break
|
||||
@render()
|
||||
|
||||
onMultiplayerSessionRemoved: (e) =>
|
||||
@playersCollections[e.id].off()
|
||||
delete @playersCollections[e.id]
|
||||
@render()
|
||||
|
||||
onMultiplayerSessionChanged: (e) =>
|
||||
@render()
|
||||
|
||||
onPlayerAdded: (e) =>
|
||||
# TODO: listeners not being unhooked, this should not be called if no @render.
|
||||
@render() if @render
|
||||
|
||||
onPlayerRemoved: (e) =>
|
||||
# TODO: listeners not being unhooked, this should not be called if no @render.
|
||||
@render() if @render
|
||||
|
||||
onCreateGame: ->
|
||||
s = @multiplayerSessions.create {
|
||||
creator: @session.get('creator')
|
||||
creatorName: @session.get('creatorName')
|
||||
levelID: @session.get('levelID')
|
||||
created: Date.now()
|
||||
state: 'creating'
|
||||
}
|
||||
@currentMultiplayerSession = @multiplayerSessions.get(s.id)
|
||||
@currentMultiplayerSession.on 'change', @onMultiplayerSessionChanged
|
||||
players = new RealTimeCollection('multiplayer_level_sessions/' + @currentMultiplayerSession.id + '/players')
|
||||
players.create {id: me.id, name: @session.get('creatorName'), team: @session.get('team')}
|
||||
Backbone.Mediator.publish 'realtime-multiplayer:joined-game', @currentMultiplayerSession
|
||||
@render()
|
||||
|
||||
onJoinGame: (e) ->
|
||||
return if @currentMultiplayerSession
|
||||
item = @$el.find(e.target).data('item')
|
||||
@currentMultiplayerSession = @multiplayerSessions.get(item.id)
|
||||
@currentMultiplayerSession.on 'change', @onMultiplayerSessionChanged
|
||||
if @playersCollections[item.id]
|
||||
@playersCollections[item.id].create {id: me.id, name: @session.get('creatorName'), team: @session.get('team')}
|
||||
else
|
||||
console.error 'onJoinGame did not have a players collection', @currentMultiplayerSession
|
||||
Backbone.Mediator.publish 'realtime-multiplayer:joined-game', @currentMultiplayerSession
|
||||
if @playersCollections[item.id]?.length is 2
|
||||
@currentMultiplayerSession.set 'state', 'coding'
|
||||
# TODO: close multiplayer view?
|
||||
@render()
|
||||
|
||||
onLeaveGame: (e) ->
|
||||
# TODO: This doesn't update open games or current game
|
||||
if @currentMultiplayerSession
|
||||
players = @playersCollections[@currentMultiplayerSession.id]
|
||||
for i in [0...players.length]
|
||||
player = players.at(i)
|
||||
if player.get('id') is me.id
|
||||
players.remove(player)
|
||||
# NOTE: remove(@something) doesn't stick locally, only remotely
|
||||
cms = @currentMultiplayerSession
|
||||
@currentMultiplayerSession.off()
|
||||
@currentMultiplayerSession = null
|
||||
if players.length is 0
|
||||
@multiplayerSessions.remove(cms)
|
||||
break
|
||||
console.error "Tried to leave a game we hadn't joined!" if @currentMultiplayerSession
|
||||
Backbone.Mediator.publish 'realtime-multiplayer:left-game'
|
||||
else
|
||||
console.error "Tried to leave a game with no currentMultiplayerSession"
|
||||
@render()
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
CocoView = require 'views/kinds/CocoView'
|
||||
template = require 'templates/play/level/control_bar'
|
||||
{me} = require 'lib/auth'
|
||||
|
||||
LevelGuideModal = require './modal/LevelGuideModal'
|
||||
GameMenuModal = require 'views/game-menu/GameMenuModal'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
|
||||
module.exports = class ControlBarView extends CocoView
|
||||
id: 'control-bar-view'
|
||||
|
@ -10,6 +12,8 @@ module.exports = class ControlBarView extends CocoView
|
|||
|
||||
subscriptions:
|
||||
'bus:player-states-changed': 'onPlayerStatesChanged'
|
||||
'realtime-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame'
|
||||
'realtime-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame'
|
||||
|
||||
events:
|
||||
'click #docs-button': ->
|
||||
|
@ -55,6 +59,9 @@ module.exports = class ControlBarView extends CocoView
|
|||
else
|
||||
c.homeLink = '/'
|
||||
c.editorLink = "/editor/level/#{@level.get('slug')}"
|
||||
c.multiplayerSession = @multiplayerSession if @multiplayerSession
|
||||
c.multiplayerPlayers = @multiplayerPlayers if @multiplayerPlayers
|
||||
c.meID = me.id
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
|
@ -77,3 +84,22 @@ module.exports = class ControlBarView extends CocoView
|
|||
|
||||
showGameMenuModal: ->
|
||||
@openModalView new GameMenuModal level: @level, session: @session, playableTeams: @playableTeams
|
||||
|
||||
onJoinedRealTimeMultiplayerGame: (item) ->
|
||||
@multiplayerSession = item
|
||||
@multiplayerPlayers = new RealTimeCollection('multiplayer_level_sessions/' + item.id + '/players')
|
||||
@multiplayerPlayers.on 'add', @onRealTimeMultiplayerPlayerAdded
|
||||
@multiplayerPlayers.on 'remove', @onRealTimeMultiplayerPlayerRemoved
|
||||
@render()
|
||||
|
||||
onLeftRealTimeMultiplayerGame: ->
|
||||
@multiplayerSession = null
|
||||
@multiplayerPlayers.off()
|
||||
@multiplayerPlayers = null
|
||||
@render()
|
||||
|
||||
onRealTimeMultiplayerPlayerAdded: (e) =>
|
||||
@render()
|
||||
|
||||
onRealTimeMultiplayerPlayerRemoved: (e) =>
|
||||
@render()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
CocoView = require 'views/kinds/CocoView'
|
||||
template = require 'templates/play/level/level-flags-view'
|
||||
{me} = require 'lib/auth'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
|
||||
module.exports = class LevelFlagsView extends CocoView
|
||||
id: 'level-flags-view'
|
||||
|
@ -13,6 +14,8 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
'god:new-world-created': 'onNewWorld'
|
||||
'god:streaming-world-updated': 'onNewWorld'
|
||||
'surface:remove-flag': 'onRemoveFlag'
|
||||
'realtime-multiplayer:joined-game': 'onJoinedMultiplayerGame'
|
||||
'realtime-multiplayer:left-game': 'onLeftMultiplayerGame'
|
||||
|
||||
events:
|
||||
'click .green-flag': -> @onFlagSelected color: 'green', source: 'button'
|
||||
|
@ -40,6 +43,7 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
@onFlagSelected color: null
|
||||
@realTime = false
|
||||
@$el.hide()
|
||||
@multiplayerSession?.set 'state', 'coding'
|
||||
|
||||
onFlagSelected: (e) ->
|
||||
return unless @realTime
|
||||
|
@ -55,6 +59,7 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: @world.dt * @world.frames.length, active: true
|
||||
@flags[@flagColor] = flag
|
||||
@flagHistory.push flag
|
||||
@realTimeFlags?.create flag
|
||||
Backbone.Mediator.publish 'level:flag-updated', flag
|
||||
#console.log 'trying to place flag at', @world.age, 'and think it will happen by', flag.time
|
||||
|
||||
|
@ -72,3 +77,30 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
onNewWorld: (event) ->
|
||||
return unless event.world.name is @world.name
|
||||
@world = @options.world = event.world
|
||||
|
||||
onJoinedMultiplayerGame: (item) ->
|
||||
@realTimeFlags = new RealTimeCollection('multiplayer_level_sessions/' + item.id + '/flagHistory')
|
||||
@realTimeFlags.on 'add', @onRealTimeMultiplayerFlagAdded
|
||||
|
||||
onLeftMultiplayerGame: () ->
|
||||
@multiplayerState = null
|
||||
if @multiplayerSession
|
||||
@multiplayerSession.off()
|
||||
@multiplayerSession = null
|
||||
if @realTimeFlags
|
||||
@realTimeFlags.off()
|
||||
@realTimeFlags = null
|
||||
|
||||
onRealTimeMultiplayerFlagAdded: (e) =>
|
||||
if e.get('player') != me.id
|
||||
# TODO: what is @flags used for?
|
||||
# Build local flag from Backbone.Model flag
|
||||
flag =
|
||||
player: e.get('player')
|
||||
team: e.get('team')
|
||||
color: e.get('color')
|
||||
pos: e.get('pos')
|
||||
time: e.get('time')
|
||||
active: e.get('active')
|
||||
@flagHistory.push flag
|
||||
Backbone.Mediator.publish 'level:flag-updated', flag
|
||||
|
|
|
@ -25,6 +25,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
'tome:cast-spells': 'onTomeCast'
|
||||
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||
'playback:stop-real-time-playback': 'onStopRealTimePlayback'
|
||||
'realtime-multiplayer:manual-cast': 'onRealTimeMultiplayerCast'
|
||||
|
||||
events:
|
||||
'click #debug-toggle': 'onToggleDebug'
|
||||
|
@ -161,6 +162,9 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
|
||||
onTomeCast: (e) ->
|
||||
return unless e.realTime
|
||||
@onRealTimeMultiplayerCast e
|
||||
|
||||
onRealTimeMultiplayerCast: (e) ->
|
||||
@realTime = true
|
||||
@togglePlaybackControls false
|
||||
Backbone.Mediator.publish 'playback:real-time-playback-started', {}
|
||||
|
|
|
@ -20,6 +20,7 @@ LevelComponent = require 'models/LevelComponent'
|
|||
Article = require 'models/Article'
|
||||
Camera = require 'lib/surface/Camera'
|
||||
AudioPlayer = require 'lib/AudioPlayer'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
|
||||
# subviews
|
||||
LevelLoadingView = require './LevelLoadingView'
|
||||
|
@ -65,6 +66,9 @@ module.exports = class PlayLevelView extends RootView
|
|||
'level:loading-view-unveiled': 'onLoadingViewUnveiled'
|
||||
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
|
||||
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||
'realtime-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame'
|
||||
'realtime-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame'
|
||||
'realtime-multiplayer:manual-cast': 'onRealTimeMultiplayerCast'
|
||||
|
||||
events:
|
||||
'click #level-done-button': 'onDonePressed'
|
||||
|
@ -543,3 +547,61 @@ module.exports = class PlayLevelView extends RootView
|
|||
delete window.nextLevelURL
|
||||
console.profileEnd?() if PROFILE_ME
|
||||
super()
|
||||
|
||||
# Real-time Multiplayer ######################################################
|
||||
|
||||
onJoinedRealTimeMultiplayerGame: (item) ->
|
||||
@multiplayerSession = item
|
||||
|
||||
onLeftRealTimeMultiplayerGame: () ->
|
||||
if @multiplayerSession
|
||||
@multiplayerSession.off()
|
||||
@multiplayerSession = null
|
||||
|
||||
onRealTimeMultiplayerCast: (e) ->
|
||||
unless @multiplayerSession
|
||||
console.error 'onRealTimeMultiplayerCast without a multiplayerSession'
|
||||
return
|
||||
players = new RealTimeCollection('multiplayer_level_sessions/' + @multiplayerSession.id + '/players')
|
||||
myPlayer = opponentPlayer = null
|
||||
for i in [0...players.length]
|
||||
player = players.at(i)
|
||||
if player.get('id') is me.id
|
||||
myPlayer = player
|
||||
else
|
||||
opponentPlayer = player
|
||||
if myPlayer
|
||||
console.info 'Submitting my code'
|
||||
myPlayer.set 'code', @session.get('code')
|
||||
myPlayer.set 'codeLanguage', @session.get('codeLanguage')
|
||||
myPlayer.set 'state', 'submitted'
|
||||
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']
|
||||
@onOpponentSubmitted(opponentPlayer, myPlayer)
|
||||
else
|
||||
# Wait for opponent to submit their code
|
||||
opponentPlayer.on 'change', (e) =>
|
||||
state = opponentPlayer.get('state')
|
||||
if state in ['submitted', 'ready']
|
||||
@onOpponentSubmitted(opponentPlayer, myPlayer)
|
||||
|
||||
onOpponentSubmitted: (opponentPlayer, myPlayer) =>
|
||||
# Save opponent's code
|
||||
Backbone.Mediator.publish 'realtime-multiplayer:new-opponent-code', {codeLanguage: opponentPlayer.get('codeLanguage'), code: opponentPlayer.get('code')}
|
||||
# I'm ready to rumble
|
||||
myPlayer.set 'state', 'ready'
|
||||
if opponentPlayer.get('state') is 'ready'
|
||||
console.info 'All real-time multiplayer players are ready!'
|
||||
@multiplayerSession.set 'state', 'running'
|
||||
else
|
||||
# Wait for opponent to be ready
|
||||
opponentPlayer.on 'change', (e) =>
|
||||
if opponentPlayer.get('state') is 'ready'
|
||||
opponentPlayer.off()
|
||||
console.info 'All real-time multiplayer players are ready!'
|
||||
@multiplayerSession.set 'state', 'running'
|
||||
|
|
|
@ -15,6 +15,8 @@ module.exports = class CastButtonView extends CocoView
|
|||
'tome:cast-spells': 'onCastSpells'
|
||||
'god:world-load-progress-changed': 'onWorldLoadProgressChanged'
|
||||
'god:new-world-created': 'onNewWorld'
|
||||
'realtime-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame'
|
||||
'realtime-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
|
@ -48,7 +50,16 @@ module.exports = class CastButtonView extends CocoView
|
|||
Backbone.Mediator.publish 'tome:manual-cast', {}
|
||||
|
||||
onCastRealTimeButtonClick: (e) ->
|
||||
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
||||
if @multiplayerSession
|
||||
Backbone.Mediator.publish 'realtime-multiplayer:manual-cast', {}
|
||||
# Wait for multiplayer session to be up and running
|
||||
@multiplayerSession.on 'change', (e) =>
|
||||
if @multiplayerSession.get('state') is 'running'
|
||||
# Real-time multiplayer session is ready to go, so resume normal cast
|
||||
@multiplayerSession.off()
|
||||
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
||||
else
|
||||
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
||||
|
||||
onCastOptionsClick: (e) =>
|
||||
Backbone.Mediator.publish 'tome:focus-editor', {}
|
||||
|
@ -106,3 +117,11 @@ module.exports = class CastButtonView extends CocoView
|
|||
spell.view?.setAutocastDelay delay for spellKey, spell of @spells
|
||||
@castOptions.find('a').each ->
|
||||
$(@).toggleClass('selected', parseInt($(@).attr('data-delay')) is delay)
|
||||
|
||||
onJoinedRealTimeMultiplayerGame: (item) ->
|
||||
@multiplayerSession = item
|
||||
|
||||
onLeftRealTimeMultiplayerGame: () ->
|
||||
if @multiplayerSession
|
||||
@multiplayerSession.off()
|
||||
@multiplayerSession = null
|
||||
|
|
|
@ -47,6 +47,7 @@ module.exports = class Spell
|
|||
@tabView.render()
|
||||
@team = @permissions.readwrite[0] ? 'common'
|
||||
Backbone.Mediator.publish 'tome:spell-created', spell: @
|
||||
Backbone.Mediator.subscribe 'realtime-multiplayer:new-opponent-code', @onNewOpponentCode
|
||||
|
||||
destroy: ->
|
||||
@view?.destroy()
|
||||
|
@ -180,3 +181,13 @@ module.exports = class Spell
|
|||
# Players without permissions can't view the raw code.
|
||||
return true if @session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions'))
|
||||
false
|
||||
|
||||
onNewOpponentCode: (e) =>
|
||||
return unless @spellKey
|
||||
if e.codeLanguage and e.code
|
||||
spellkeyComponents = @spellKey.split '/'
|
||||
if e.code[spellkeyComponents[0]]?[spellkeyComponents[1]]
|
||||
@source = e.code[spellkeyComponents[0]][spellkeyComponents[1]]
|
||||
@updateLanguageAether e.codeLanguage
|
||||
else
|
||||
console.error 'Spell onNewOpponentCode did not recieve code', e
|
||||
|
|
|
@ -45,12 +45,16 @@
|
|||
"validated-backbone-mediator": "~0.1.3",
|
||||
"jquery.browser": "~0.0.6",
|
||||
"zatanna": "~0.0.6",
|
||||
"modernizr": "~2.8.3"
|
||||
"modernizr": "~2.8.3",
|
||||
"backfire": "~0.3.0"
|
||||
},
|
||||
"overrides": {
|
||||
"backbone": {
|
||||
"main": "backbone.js"
|
||||
},
|
||||
"backfire": {
|
||||
"main": "backbone-firebase.min.js"
|
||||
},
|
||||
"lodash": {
|
||||
"main": "dist/lodash.js"
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue