From c5c831c2117b5702020413d2fb43eef8d8ee3cae Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 14 Jul 2016 10:26:09 -0700 Subject: [PATCH] Remove real-time multiplayer prototype code --- app/collections/RealTimeCollection.coffee | 6 - app/core/Router.coffee | 2 - app/core/initialize.coffee | 3 +- app/lib/LevelBus.coffee | 12 +- app/lib/surface/Surface.coffee | 9 +- app/lib/surface/WaitingScreen.coffee | 65 --- app/locale/en.coffee | 12 +- app/models/LevelSession.coffee | 2 - app/models/RealTimeModel.coffee | 6 - app/schemas/models/level_session.coffee | 2 - app/schemas/subscriptions/multiplayer.coffee | 21 - app/schemas/subscriptions/play.coffee | 2 - app/styles/play/level/control_bar.sass | 36 -- app/styles/play/menu/multiplayer-view.sass | 8 - app/templates/play/level/control_bar.jade | 23 +- app/templates/play/menu/multiplayer-view.jade | 90 ---- app/views/ladder/MainLadderView.coffee | 48 +-- app/views/play/SpectateView.coffee | 3 - app/views/play/level/ControlBarView.coffee | 84 ---- app/views/play/level/LevelChatView.coffee | 1 + app/views/play/level/LevelFlagsView.coffee | 38 +- app/views/play/level/LevelPlaybackView.coffee | 6 - app/views/play/level/PlayLevelView.coffee | 390 +----------------- .../play/level/tome/CastButtonView.coffee | 13 +- .../play/level/tome/ProblemAlertView.coffee | 1 - app/views/play/level/tome/SpellView.coffee | 44 +- app/views/play/menu/GameMenuModal.coffee | 6 +- app/views/play/menu/MultiplayerView.coffee | 229 ---------- server/models/LevelSession.coffee | 2 +- 29 files changed, 36 insertions(+), 1128 deletions(-) delete mode 100644 app/collections/RealTimeCollection.coffee delete mode 100644 app/lib/surface/WaitingScreen.coffee delete mode 100644 app/models/RealTimeModel.coffee delete mode 100644 app/schemas/subscriptions/multiplayer.coffee delete mode 100644 app/styles/play/menu/multiplayer-view.sass delete mode 100644 app/templates/play/menu/multiplayer-view.jade delete mode 100644 app/views/play/menu/MultiplayerView.coffee diff --git a/app/collections/RealTimeCollection.coffee b/app/collections/RealTimeCollection.coffee deleted file mode 100644 index a5af239ed..000000000 --- a/app/collections/RealTimeCollection.coffee +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = class RealTimeCollection 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() diff --git a/app/core/Router.coffee b/app/core/Router.coffee index 4890b9bb5..de3530738 100644 --- a/app/core/Router.coffee +++ b/app/core/Router.coffee @@ -125,8 +125,6 @@ module.exports = class CocoRouter extends Backbone.Router 'legal': go('LegalView') - 'multiplayer': go('MultiplayerView') - 'play(/)': go('play/CampaignView') # extra slash is to get Facebook app to work 'play/ladder/:levelID/:leagueType/:leagueID': go('ladder/LadderView') 'play/ladder/:levelID': go('ladder/LadderView') diff --git a/app/core/initialize.coffee b/app/core/initialize.coffee index 7acbc0cdc..b81841a6b 100644 --- a/app/core/initialize.coffee +++ b/app/core/initialize.coffee @@ -8,7 +8,6 @@ channelSchemas = 'errors': require 'schemas/subscriptions/errors' 'ipad': require 'schemas/subscriptions/ipad' 'misc': require 'schemas/subscriptions/misc' - 'multiplayer': require 'schemas/subscriptions/multiplayer' 'play': require 'schemas/subscriptions/play' 'surface': require 'schemas/subscriptions/surface' 'tome': require 'schemas/subscriptions/tome' @@ -165,5 +164,5 @@ window.onbeforeunload = (e) -> return leavingMessage else return - + $ -> init() diff --git a/app/lib/LevelBus.coffee b/app/lib/LevelBus.coffee index 741420cca..aab23bf16 100644 --- a/app/lib/LevelBus.coffee +++ b/app/lib/LevelBus.coffee @@ -41,7 +41,6 @@ module.exports = class LevelBus extends Bus @fireScriptsRef = @fireRef?.child('scripts') setSession: (@session) -> - @listenTo(@session, 'change:multiplayer', @onMultiplayerChanged) @timerIntervalID = setInterval(@incrementSessionPlaytime, 1000) onIdleChanged: (e) -> @@ -53,8 +52,7 @@ module.exports = class LevelBus extends Bus @session.set('playtime', (@session.get('playtime') ? 0) + 1) onPoint: -> - return true unless @session?.get('multiplayer') - super() + return true onMeSynced: => super() @@ -236,17 +234,11 @@ module.exports = class LevelBus extends Bus @changedSessionProperties.chat = true @saveSession() - onMultiplayerChanged: -> - @changedSessionProperties.multiplayer = true - @session.updatePermissions() - @changedSessionProperties.permissions = true - @saveSession() - # Debounced as saveSession reallySaveSession: -> return if _.isEmpty @changedSessionProperties # don't let peeking admins mess with the session accidentally - return unless @session.get('multiplayer') or @session.get('creator') is me.id + return unless @session.get('creator') is me.id return if @session.fake Backbone.Mediator.publish 'level:session-will-save', session: @session patch = {} diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index 01980a2d8..4f63e1ce3 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -10,7 +10,6 @@ Letterbox = require './Letterbox' Dimmer = require './Dimmer' CountdownScreen = require './CountdownScreen' PlaybackOverScreen = require './PlaybackOverScreen' -WaitingScreen = require './WaitingScreen' DebugDisplay = require './DebugDisplay' CoordinateDisplay = require './CoordinateDisplay' CoordinateGrid = require './CoordinateGrid' @@ -70,7 +69,6 @@ module.exports = Surface = class Surface extends CocoClass 'level:set-letterbox': 'onSetLetterbox' 'application:idle-changed': 'onIdleChanged' 'camera:zoom-updated': 'onZoomUpdated' - 'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting' 'playback:real-time-playback-started': 'onRealTimePlaybackStarted' 'playback:real-time-playback-ended': 'onRealTimePlaybackEnded' 'level:flag-color-selected': 'onFlagColorSelected' @@ -135,7 +133,6 @@ module.exports = Surface = class Surface extends CocoClass @countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer, showsCountdown: @world.showsCountdown @playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer, playerNames: @options.playerNames @normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen. - @waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer @initCoordinates() @webGLStage.enableMouseOver(10) @webGLStage.addEventListener 'stagemousemove', @onMouseMove @@ -570,7 +567,7 @@ module.exports = Surface = class Surface extends CocoClass scaleFactor = 1 if @options.stayVisible availableHeight = window.innerHeight - availableHeight -= $('.ad-container').outerHeight() + availableHeight -= $('.ad-container').outerHeight() availableHeight -= $('#game-area').outerHeight() - $('#canvas-wrapper').outerHeight() scaleFactor = availableHeight / newHeight if availableHeight < newHeight newWidth *= scaleFactor @@ -602,9 +599,6 @@ module.exports = Surface = class Surface extends CocoClass #- Real-time playback - onRealTimePlaybackWaiting: (e) -> - @onRealTimePlaybackStarted e - onRealTimePlaybackStarted: (e) -> return if @realTime @realTimeInputEvents.reset() @@ -741,7 +735,6 @@ module.exports = Surface = class Surface extends CocoClass @dimmer?.destroy() @countdownScreen?.destroy() @playbackOverScreen?.destroy() - @waitingScreen?.destroy() @coordinateDisplay?.destroy() @coordinateGrid?.destroy() @normalStage.clear() diff --git a/app/lib/surface/WaitingScreen.coffee b/app/lib/surface/WaitingScreen.coffee deleted file mode 100644 index 232ddb5f8..000000000 --- a/app/lib/surface/WaitingScreen.coffee +++ /dev/null @@ -1,65 +0,0 @@ -CocoClass = require 'core/CocoClass' -RealTimeCollection = require 'collections/RealTimeCollection' - -module.exports = class WaitingScreen extends CocoClass - subscriptions: - 'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting' - 'playback:real-time-playback-started': 'onRealTimePlaybackStarted' - 'playback:real-time-playback-ended': 'onRealTimePlaybackEnded' - 'real-time-multiplayer:player-status': 'onRealTimeMultiplayerPlayerStatus' - - constructor: (options) -> - super() - options ?= {} - @camera = options.camera - @layer = options.layer - @waitingText = options.text or 'Waiting...' - console.error @toString(), 'needs a camera.' unless @camera - console.error @toString(), 'needs a layer.' unless @layer - @build() - - onCastingBegins: (e) -> @show() unless e.preload - onCastingEnds: (e) -> @hide() - - toString: -> '' - - build: -> - @dimLayer = new createjs.Container() - @dimLayer.mouseEnabled = @dimLayer.mouseChildren = false - @dimLayer.addChild @dimScreen = new createjs.Shape() - @dimScreen.graphics.beginFill('rgba(0,0,0,0.5)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight - @dimLayer.alpha = 0 - @dimLayer.addChild @makeWaitingText() - - makeWaitingText: -> - size = Math.ceil @camera.canvasHeight / 8 - text = new createjs.Text @waitingText, "#{size}px Open Sans Condensed", '#F7B42C' - text.shadow = new createjs.Shadow '#000', Math.ceil(@camera.canvasHeight / 300), Math.ceil(@camera.canvasHeight / 300), Math.ceil(@camera.canvasHeight / 120) - text.textAlign = 'center' - text.textBaseline = 'middle' - text.x = @camera.canvasWidth / 2 - text.y = @camera.canvasHeight / 2 - @text = text - return text - - show: -> - return if @showing - @showing = true - @dimLayer.alpha = 0 - createjs.Tween.removeTweens @dimLayer - createjs.Tween.get(@dimLayer).to({alpha: 1}, 500) - @layer.addChild @dimLayer - - hide: -> - return unless @showing - @showing = false - createjs.Tween.removeTweens @dimLayer - createjs.Tween.get(@dimLayer).to({alpha: 0}, 500).call => @layer.removeChild @dimLayer unless @destroyed - - onRealTimeMultiplayerPlayerStatus: (e) -> @text.text = e.status - - onRealTimePlaybackWaiting: (e) -> @show() - - onRealTimePlaybackStarted: (e) -> @hide() - - onRealTimePlaybackEnded: (e) -> @hide() diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 143010bb6..04bd218f0 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -318,7 +318,7 @@ write_this_down: "Write this down:" start_playing: "Start Playing!" sso_connected: "Successfully connected with:" - + recover: recover_account_title: "Recover Account" send_password: "Send Recovery Password" @@ -1890,16 +1890,6 @@ merge_conflict_with: "MERGE CONFLICT WITH" no_changes: "No Changes" - multiplayer: - multiplayer_title: "Multiplayer Settings" # We'll be changing this around significantly soon. Until then, it's not important to translate. - multiplayer_toggle: "Enable multiplayer" - multiplayer_toggle_description: "Allow others to join your game." - multiplayer_link_description: "Give this link to anyone to have them join you." - multiplayer_hint_label: "Hint:" - multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." - multiplayer_coming_soon: "More multiplayer features to come!" - multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." - legal: page_title: "Legal" opensource_intro: "CodeCombat is completely open source." diff --git a/app/models/LevelSession.coffee b/app/models/LevelSession.coffee index 1d4334535..23b68851c 100644 --- a/app/models/LevelSession.coffee +++ b/app/models/LevelSession.coffee @@ -15,8 +15,6 @@ module.exports = class LevelSession extends CocoModel updatePermissions: -> permissions = @get 'permissions', true permissions = (p for p in permissions when p.target isnt 'public') - if @get('multiplayer') - permissions.push {target: 'public', access: 'write'} @set 'permissions', permissions getSourceFor: (spellKey) -> diff --git a/app/models/RealTimeModel.coffee b/app/models/RealTimeModel.coffee deleted file mode 100644 index 217c72f2e..000000000 --- a/app/models/RealTimeModel.coffee +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = class RealTimeModel extends Backbone.Firebase.Model - constructor: (savePath) -> - # TODO: Don't hard code this here - # TODO: Use prod path in prod - @firebase = 'https://codecombat.firebaseio.com/test/db/' + savePath - super() diff --git a/app/schemas/models/level_session.coffee b/app/schemas/models/level_session.coffee index 95be39816..2c7bbe1fe 100644 --- a/app/schemas/models/level_session.coffee +++ b/app/schemas/models/level_session.coffee @@ -37,8 +37,6 @@ _.extend LevelSessionSchema.properties, type: 'string' levelID: type: 'string' - multiplayer: - type: 'boolean' creator: c.objectId links: [ diff --git a/app/schemas/subscriptions/multiplayer.coffee b/app/schemas/subscriptions/multiplayer.coffee deleted file mode 100644 index fd88f449c..000000000 --- a/app/schemas/subscriptions/multiplayer.coffee +++ /dev/null @@ -1,21 +0,0 @@ -c = require 'schemas/schemas' - -module.exports = - 'real-time-multiplayer:created-game': c.object {title: 'Multiplayer created game', required: ['realTimeSessionID']}, - realTimeSessionID: {type: 'string'} - - 'real-time-multiplayer:joined-game': c.object {title: 'Multiplayer joined game', required: ['realTimeSessionID']}, - realTimeSessionID: {type: 'string'} - - 'real-time-multiplayer:left-game': c.object {title: 'Multiplayer left game'}, - userID: {type: 'string'} - - 'real-time-multiplayer:manual-cast': c.object {title: 'Multiplayer manual cast'} - - 'real-time-multiplayer:new-opponent-code': c.object {title: 'Multiplayer new opponent code', required: ['code', 'codeLanguage']}, - code: {type: 'object'} - codeLanguage: {type: 'string'} - team: {type: 'string'} - - 'real-time-multiplayer:player-status': c.object {title: 'Multiplayer player status', required: ['status']}, - status: {type: 'string'} diff --git a/app/schemas/subscriptions/play.coffee b/app/schemas/subscriptions/play.coffee index 1c4adc9b4..e03736613 100644 --- a/app/schemas/subscriptions/play.coffee +++ b/app/schemas/subscriptions/play.coffee @@ -96,8 +96,6 @@ module.exports = 'playback:stop-real-time-playback': c.object {} - 'playback:real-time-playback-waiting': c.object {} - 'playback:real-time-playback-started': c.object {} 'playback:real-time-playback-ended': c.object {} diff --git a/app/styles/play/level/control_bar.sass b/app/styles/play/level/control_bar.sass index a31844c8e..48f835534 100644 --- a/app/styles/play/level/control_bar.sass +++ b/app/styles/play/level/control_bar.sass @@ -124,37 +124,6 @@ @include rotate(-15deg) vertical-align: middle - .multiplayer-area-container - position: relative - width: 100% - height: 50px - pointer-events: none - - .multiplayer-area - min-width: 200px - max-width: 293px - height: 60px - margin: 0 auto - padding: 8px - border-style: solid - border-image: url(/images/level/control_bar_level_name_background.png) 30 fill round - border-width: 0 15px 15px 15px - text-align: center - position: absolute - left: 50% - cursor: pointer - pointer-events: all - @include translate(-50%, 0) - - .multiplayer-label - font-size: 12px - color: $control-yellow-highlight - margin-bottom: -5px - - .multiplayer-status - color: white - font-size: 18px - .buttons-area position: absolute right: 35px @@ -210,11 +179,6 @@ html.no-borderimage background: transparent url(/images/level/control_bar_level_name_background.png) background-size: contain background-repeat: no-repeat - #control-bar-view .multiplayer-area - border: 0 - background: transparent url(/images/level/control_bar_level_name_background.png) - background-size: contain - background-repeat: no-repeat body:not(.ipad) diff --git a/app/styles/play/menu/multiplayer-view.sass b/app/styles/play/menu/multiplayer-view.sass deleted file mode 100644 index c1bfbee23..000000000 --- a/app/styles/play/menu/multiplayer-view.sass +++ /dev/null @@ -1,8 +0,0 @@ -#multiplayer-view - textarea - width: 100% - box-sizing: border-box - padding: 5px - text-align: center - height: 30px - font-size: 11px diff --git a/app/templates/play/level/control_bar.jade b/app/templates/play/level/control_bar.jade index 639a389f8..2f6c56148 100644 --- a/app/templates/play/level/control_bar.jade +++ b/app/templates/play/level/control_bar.jade @@ -9,22 +9,13 @@ .glyphicon.glyphicon-play span(data-i18n=me.isSessionless() ? "nav.courses" : (ladderGame ? "general.ladder" : "nav.play")).home-text Levels -if isMultiplayerLevel && !observing - .multiplayer-area-container - .multiplayer-area - .multiplayer-label(data-i18n="play_level.control_bar_multiplayer") - if multiplayerStatus - .multiplayer-status= multiplayerStatus - else - .multiplayer-status(data-i18n="play_level.control_bar_join_game") -else - .level-name-area-container - .level-name-area - .level-label(data-i18n="play_level.level") - .level-name(title=difficultyTitle || "") - span #{view.levelNumber ? view.levelNumber + '. ' : ''}#{worldName.replace('Course: ', '')} - if levelDifficulty - sup.level-difficulty= levelDifficulty +.level-name-area-container + .level-name-area + .level-label(data-i18n="play_level.level") + .level-name(title=difficultyTitle || "") + span #{view.levelNumber ? view.levelNumber + '. ' : ''}#{worldName.replace('Course: ', '')} + if levelDifficulty + sup.level-difficulty= levelDifficulty .buttons-area diff --git a/app/templates/play/menu/multiplayer-view.jade b/app/templates/play/menu/multiplayer-view.jade deleted file mode 100644 index c45a1259c..000000000 --- a/app/templates/play/menu/multiplayer-view.jade +++ /dev/null @@ -1,90 +0,0 @@ -if !ladderGame - .form - .form-group.checkbox - label(for="multiplayer") - 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 realTimeSessions && realTimeSessionsPlayers - button#create-game-button Create Game - - hr - - div#created-multiplayer-session - h3 Your Game - if currentRealTimeSession - div - span(style="margin:10px")= currentRealTimeSession.get('levelID') - span(style="margin:10px")= currentRealTimeSession.get('creatorName') - span(style="margin:10px")= currentRealTimeSession.get('state') - span(style="margin:10px")= currentRealTimeSession.id - button#leave-game-button(data-item=item) Leave Game - div - - var players = realTimeSessionsPlayers[currentRealTimeSession.id] - if players - span(style="margin:10px") Players: - - for (var i=0; i < players.length; i++) { - span(style="margin:10px")= players.at(i).get('name') - span(style="margin:10px")= players.at(i).get('team') - span(style="margin:10px")= players.at(i).get('state') - - } - else - span No Players? - else - div Click something above to create a game. - - hr - - div#open-games - h3 Open Games - //- TODO: do not let you join ones with same-team opponent - - var noOpenGames = true - - for (var i=0; i < realTimeSessions.length; i++) { - if (currentRealTimeSession && realTimeSessions.at(i).id == currentRealTimeSession.id) - - continue - if levelID === realTimeSessions.at(i).get('levelID') && realTimeSessions.at(i).get('state') === 'creating' - - var id = realTimeSessions.at(i).get('id') - - var players = realTimeSessionsPlayers[id] - if players && players.length === 1 - - noOpenGames = false - - var creatorName = realTimeSessions.at(i).get('creatorName') - - var creator = realTimeSessions.at(i).get('creator') - - var state = realTimeSessions.at(i).get('state') - - var item = realTimeSessions.at(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: - span(style="margin:10px")= players.at(0).get('name') - span(style="margin:10px")= players.at(0).get('team') - span(style="margin:10px")= players.at(0).get('state') - - } - 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 diff --git a/app/views/ladder/MainLadderView.coffee b/app/views/ladder/MainLadderView.coffee index b4108ee42..171f8b3ee 100644 --- a/app/views/ladder/MainLadderView.coffee +++ b/app/views/ladder/MainLadderView.coffee @@ -21,7 +21,7 @@ module.exports = class MainLadderView extends RootView @campaigns = campaigns @sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', {cache: false}, 0).model - @listenToOnce @sessions, 'sync', @onSessionsLoaded + @listenToOnce @sessions, 'sync', @onSessionsLoaded @getLevelPlayCounts() @@ -94,52 +94,6 @@ heroArenas = [ } ] -oldArenas = [ - { - name: 'Criss-Cross' - difficulty: 5 - id: 'criss-cross' - image: '/file/db/level/5391f3d519dc22b8082159b2/banner2.png' - description: 'Participate in a bidding war with opponents to reach the other side!' - } - { - name: 'Greed' - difficulty: 4 - id: 'greed' - image: '/file/db/level/53558b5a9914f5a90d7ccddb/greed_banner.jpg' - description: 'Liked Dungeon Arena and Gold Rush? Put them together in this economic arena!' - } - { - name: 'Sky Span (Testing)' - difficulty: 3 - id: 'sky-span' - image: '/file/db/level/53c80fce0ddbef000084c667/sky-Span-banner.jpg' - description: 'Preview version of an upgraded Dungeon Arena. Help us with hero balance before release!' - } - { - name: 'Dungeon Arena' - difficulty: 3 - id: 'dungeon-arena' - image: '/file/db/level/53173f76c269d400000543c2/Level%20Banner%20Dungeon%20Arena.jpg' - description: 'Play head-to-head against fellow Wizards in a dungeon melee!' - } - { - name: 'Gold Rush' - difficulty: 3 - id: 'gold-rush' - image: '/file/db/level/533353722a61b7ca6832840c/Gold-Rush.png' - description: 'Prove you are better at collecting gold than your opponent!' - } - { - name: 'Brawlwood' - difficulty: 4 - id: 'brawlwood' - image: '/file/db/level/52d97ecd32362bc86e004e87/Level%20Banner%20Brawlwood.jpg' - description: 'Combat the armies of other Wizards in a strategic forest arena! (Fast computer required.)' - } -] - campaigns = [ {id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: heroArenas} - #{id: 'old_multiplayer', name: '(Deprecated) Old Multiplayer Arenas', description: 'Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas.', levels: oldArenas} ] diff --git a/app/views/play/SpectateView.coffee b/app/views/play/SpectateView.coffee index b9122bd59..f5a28fac8 100644 --- a/app/views/play/SpectateView.coffee +++ b/app/views/play/SpectateView.coffee @@ -144,9 +144,6 @@ module.exports = class SpectateLevelView extends RootView if c then myCode[thang][spell] = c else delete myCode[thang][spell] @session.set('code', myCode) - if @session.get('multiplayer') and @otherSession? - # For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet. - @session.set 'multiplayer', false onLevelStarted: (e) -> go = => diff --git a/app/views/play/level/ControlBarView.coffee b/app/views/play/level/ControlBarView.coffee index 000256999..895c6f0fc 100644 --- a/app/views/play/level/ControlBarView.coffee +++ b/app/views/play/level/ControlBarView.coffee @@ -7,17 +7,13 @@ Classroom = require 'models/Classroom' Course = require 'models/Course' CourseInstance = require 'models/CourseInstance' GameMenuModal = require 'views/play/menu/GameMenuModal' -RealTimeModel = require 'models/RealTimeModel' -RealTimeCollection = require 'collections/RealTimeCollection' LevelSetupManager = require 'lib/LevelSetupManager' -GameMenuModal = require 'views/play/menu/GameMenuModal' module.exports = class ControlBarView extends CocoView id: 'control-bar-view' template: template subscriptions: - 'bus:player-states-changed': 'onPlayerStatesChanged' 'level:disable-controls': 'onDisableControls' 'level:enable-controls': 'onEnableControls' 'ipad:memory-warning': 'onIPadMemoryWarning' @@ -28,7 +24,6 @@ module.exports = class ControlBarView extends CocoView 'click': -> Backbone.Mediator.publish 'tome:focus-editor', {} 'click .levels-link-area': 'onClickHome' 'click .home a': 'onClickHome' - 'click .multiplayer-area': 'onClickMultiplayer' 'click #control-bar-sign-up-button': 'onClickSignupButton' constructor: (options) -> @@ -64,9 +59,6 @@ module.exports = class ControlBarView extends CocoView @supermodel.trackRequest(@campaign.fetch()) ) super options - if @level.isType('hero-ladder', 'course-ladder') and me.isAdmin() - @isMultiplayerLevel = true - @multiplayerStatusManager = new MultiplayerStatusManager @levelID, @onMultiplayerStateChanged if @level.get 'replayable' @listenTo @session, 'change-difficulty', @onSessionDifficultyChanged @@ -79,25 +71,10 @@ module.exports = class ControlBarView extends CocoView setBus: (@bus) -> - onPlayerStatesChanged: (e) -> - # TODO: this doesn't fire any more. Replacement? - return unless @bus is e.bus - numPlayers = _.keys(e.players).length - return if numPlayers is @numPlayers - @numPlayers = numPlayers - text = 'Multiplayer' - text += " (#{numPlayers})" if numPlayers > 1 - $('#multiplayer-button', @$el).text(text) - - onMultiplayerStateChanged: => @render?() - getRenderData: (c={}) -> super c c.worldName = @worldName - c.multiplayerEnabled = @session.get('multiplayer') c.ladderGame = @level.isType('ladder', 'hero-ladder', 'course-ladder') - if c.isMultiplayerLevel = @isMultiplayerLevel - c.multiplayerStatus = @multiplayerStatusManager?.status if @level.get 'replayable' c.levelDifficulty = @session.get('state')?.difficulty ? 0 if @observing @@ -161,9 +138,6 @@ module.exports = class ControlBarView extends CocoView e.stopImmediatePropagation() Backbone.Mediator.publish 'router:navigate', route: @homeLink, viewClass: @homeViewClass, viewArgs: @homeViewArgs - onClickMultiplayer: (e) -> - @showGameMenuModal e, 'multiplayer' - onClickSignupButton: (e) -> window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Control Bar', level: @levelID @@ -184,62 +158,4 @@ module.exports = class ControlBarView extends CocoView destroy: -> @setupManager?.destroy() - @multiplayerStatusManager?.destroy() super() - -# MultiplayerStatusManager ###################################################### -# -# Manages the multiplayer status, and calls @statusChangedCallback when it changes. -# -# It monitors these: -# Real-time multiplayer players -# Internal multiplayer status -# -# Real-time state variables: -# @playersCollection - Real-time multiplayer players -# -# TODO: Not currently using player counts. Should remove if we keep simple design. -# -class MultiplayerStatusManager - - constructor: (@levelID, @statusChangedCallback) -> - @status = '' - # @players = {} - # @playersCollection = new RealTimeCollection('multiplayer_players/' + @levelID) - # @playersCollection.on 'add', @onPlayerAdded - # @playersCollection.each (player) => @onPlayerAdded player - Backbone.Mediator.subscribe 'real-time-multiplayer:player-status', @onMultiplayerPlayerStatus - - destroy: -> - Backbone.Mediator.unsubscribe 'real-time-multiplayer:player-status', @onMultiplayerPlayerStatus - # @playersCollection?.off 'add', @onPlayerAdded - # player.off 'change', @onPlayerChanged for id, player of @players - - onMultiplayerPlayerStatus: (e) => - @status = e.status - @statusChangedCallback() - - # onPlayerAdded: (player) => - # unless player.id is me.id - # @players[player.id] = new RealTimeModel('multiplayer_players/' + @levelID + '/' + player.id) - # @players[player.id].on 'change', @onPlayerChanged - # @countPlayers player - # - # onPlayerChanged: (player) => - # @countPlayers player - # - # countPlayers: (changedPlayer) => - # # TODO: save this stale hearbeat threshold setting somewhere - # staleHeartbeat = new Date() - # staleHeartbeat.setMinutes staleHeartbeat.getMinutes() - 3 - # @playerCount = 0 - # @playersCollectionAvailable = 0 - # @playersCollectionUnavailable = 0 - # @playersCollection.each (player) => - # # Assume changedPlayer is fresher than entry in @playersCollection collection - # player = changedPlayer if changedPlayer? and player.id is changedPlayer.id - # unless staleHeartbeat >= new Date(player.get('heartbeat')) - # @playerCount++ - # @playersCollectionAvailable++ if player.get('state') is 'available' - # @playersCollectionUnavailable++ if player.get('state') is 'unavailable' - # @statusChangedCallback() diff --git a/app/views/play/level/LevelChatView.coffee b/app/views/play/level/LevelChatView.coffee index 06c218294..122b15b7a 100644 --- a/app/views/play/level/LevelChatView.coffee +++ b/app/views/play/level/LevelChatView.coffee @@ -18,6 +18,7 @@ module.exports = class LevelChatView extends CocoView constructor: (options) -> @levelID = options.levelID @session = options.session + # TODO: we took out session.multiplayer, so this will not fire. If we want to resurrect it, we'll of course need a new way of activating chat. @listenTo(@session, 'change:multiplayer', @updateMultiplayerVisibility) @sessionID = options.sessionID @bus = LevelBus.get(@levelID, @sessionID) diff --git a/app/views/play/level/LevelFlagsView.coffee b/app/views/play/level/LevelFlagsView.coffee index 9bdb04c4a..83b2a71ce 100644 --- a/app/views/play/level/LevelFlagsView.coffee +++ b/app/views/play/level/LevelFlagsView.coffee @@ -1,9 +1,6 @@ CocoView = require 'views/core/CocoView' template = require 'templates/play/level/level-flags-view' {me} = require 'core/auth' -RealTimeCollection = require 'collections/RealTimeCollection' - -multiplayerFlagDelay = 0.5 # Long, static second delay for now; should be more than enough. module.exports = class LevelFlagsView extends CocoView id: 'level-flags-view' @@ -17,7 +14,6 @@ module.exports = class LevelFlagsView extends CocoView 'god:new-world-created': 'onNewWorld' 'god:streaming-world-updated': 'onNewWorld' 'surface:remove-flag': 'onRemoveFlag' - 'real-time-multiplayer:joined-game': 'onJoinedMultiplayerGame' events: 'click .green-flag': -> @onFlagSelected color: 'green', source: 'button' @@ -60,9 +56,8 @@ module.exports = class LevelFlagsView extends CocoView return unless @flagColor and @realTime @playSound 'menu-button-click' # TODO: different flag placement sound? pos = x: e.worldPos.x, y: e.worldPos.y - delay = if @realTimeFlags then multiplayerFlagDelay else 0 now = @world.dt * @world.frames.length - flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: now + delay, active: true, source: 'click' + flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: now, active: true, source: 'click' @flags[@flagColor] = flag @flagHistory.push flag @realTimeFlags?.create flag @@ -75,9 +70,8 @@ module.exports = class LevelFlagsView extends CocoView onRemoveFlag: (e) -> delete @flags[e.color] - delay = if @realTimeFlags then multiplayerFlagDelay else 0 now = @world.dt * @world.frames.length - flag = player: me.id, team: me.team, color: e.color, time: now + delay, active: false, source: 'click' + flag = player: me.id, team: me.team, color: e.color, time: now, active: false, source: 'click' @flagHistory.push flag Backbone.Mediator.publish 'level:flag-updated', flag #console.log e.color, 'deleted at time', flag.time @@ -85,31 +79,3 @@ module.exports = class LevelFlagsView extends CocoView onNewWorld: (event) -> return unless event.world.name is @world.name @world = @options.world = event.world - - onJoinedMultiplayerGame: (e) -> - @realTimeFlags = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{e.realTimeSessionID}/flagHistory") - @realTimeFlags.on 'add', @onRealTimeMultiplayerFlagAdded - @realTimeFlags.on 'remove', @onRealTimeMultiplayerFlagRemoved - - onLeftMultiplayerGame: (e) -> - if @realTimeFlags - @realTimeFlags.off 'add', @onRealTimeMultiplayerFlagAdded - @realTimeFlags.off 'remove', @onRealTimeMultiplayerFlagRemoved - @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') - #source: 'click'? e.get('source')? nothing? - @flagHistory.push flag - Backbone.Mediator.publish 'level:flag-updated', flag - - onRealTimeMultiplayerFlagRemoved: (e) => diff --git a/app/views/play/level/LevelPlaybackView.coffee b/app/views/play/level/LevelPlaybackView.coffee index 6547a683b..811d431a1 100644 --- a/app/views/play/level/LevelPlaybackView.coffee +++ b/app/views/play/level/LevelPlaybackView.coffee @@ -21,7 +21,6 @@ module.exports = class LevelPlaybackView extends CocoView 'tome:cast-spells': 'onTomeCast' 'playback:real-time-playback-ended': 'onRealTimePlaybackEnded' 'playback:stop-real-time-playback': 'onStopRealTimePlayback' - 'real-time-multiplayer:manual-cast': 'onRealTimeMultiplayerCast' events: 'click #music-button': 'onToggleMusic' @@ -110,11 +109,6 @@ module.exports = class LevelPlaybackView extends CocoView Backbone.Mediator.publish 'playback:real-time-playback-started', {} @playSound 'real-time-playback-start' - onRealTimeMultiplayerCast: (e) -> - @realTime = true - @togglePlaybackControls false - Backbone.Mediator.publish 'playback:real-time-playback-waiting', {} - onWindowResize: (s...) => @barWidth = $('.progress', @$el).width() diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index d0a4179f6..26ce79d54 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -73,13 +73,8 @@ module.exports = class PlayLevelView extends RootView 'level:loading-view-unveiling': 'onLoadingViewUnveiling' 'level:loading-view-unveiled': 'onLoadingViewUnveiled' 'level:session-loaded': 'onSessionLoaded' - 'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting' 'playback:real-time-playback-started': 'onRealTimePlaybackStarted' 'playback:real-time-playback-ended': 'onRealTimePlaybackEnded' - 'real-time-multiplayer:created-game': 'onRealTimeMultiplayerCreatedGame' - 'real-time-multiplayer:joined-game': 'onRealTimeMultiplayerJoinedGame' - 'real-time-multiplayer:left-game': 'onRealTimeMultiplayerLeftGame' - 'real-time-multiplayer:manual-cast': 'onRealTimeMultiplayerCast' 'ipad:memory-warning': 'onIPadMemoryWarning' 'store:item-purchased': 'onItemPurchased' @@ -192,8 +187,6 @@ module.exports = class PlayLevelView extends RootView @initGoalManager() @insertSubviews() @initVolume() - @listenTo(@session, 'change:multiplayer', @onMultiplayerChanged) - @register() @controlBar.setBus(@bus) @initScriptManager() @@ -239,9 +232,6 @@ module.exports = class PlayLevelView extends RootView myCode[thang] ?= {} if c then myCode[thang][spell] = c else delete myCode[thang][spell] @session.set('code', myCode) - if @session.get('multiplayer') and @otherSession? - # For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet. - @session.set 'multiplayer', false setupGod: -> @god.setLevel @level.serialize {@supermodel, @session, @otherSession, headless: false, sessionless: false} @@ -289,9 +279,7 @@ module.exports = class PlayLevelView extends RootView @bus = LevelBus.get(@levelID, @session.id) @bus.setSession(@session) @bus.setSpells @tome.spells - if @session.get('multiplayer') and not me.isAdmin() - @session.set 'multiplayer', false # Temp: multiplayer has bugged out some sessions, so ignoring it. - @bus.connect() if @session.get('multiplayer') + #@bus.connect() if @session.get('multiplayer') # TODO: session's multiplayer flag removed; connect bus another way if we care about it # Load Completed Setup ###################################################### @@ -315,8 +303,6 @@ module.exports = class PlayLevelView extends RootView @setupManager = new LevelSetupManager({supermodel: @supermodel, level: e.level, levelID: @levelID, parent: @, session: e.session, courseID: @courseID, courseInstanceID: @courseInstanceID}) @setupManager.open() - @onRealTimeMultiplayerLevelLoaded e.session if e.level.isType('hero-ladder', 'course-ladder') - onLoaded: -> _.defer => @onLevelLoaderLoaded() @@ -393,9 +379,6 @@ module.exports = class PlayLevelView extends RootView @removeSubView @loadingView @loadingView = null @playAmbientSound() - if @options.realTimeMultiplayerSessionID? - Backbone.Mediator.publish 'playback:real-time-playback-waiting', {} - @realTimeMultiplayerContinueGame @options.realTimeMultiplayerSessionID # TODO: Is it possible to create a Mongoose ObjectId for 'ls', instead of the string returned from get()? application.tracker?.trackEvent 'Started Level', category:'Play Level', levelID: @levelID, ls: @session?.get('_id') unless @observing $(window).trigger 'resize' @@ -584,13 +567,6 @@ module.exports = class PlayLevelView extends RootView onFocusDom: (e) -> $(e.selector).focus() - onMultiplayerChanged: (e) -> - if @session.get('multiplayer') - @bus.connect() - else - @bus.removeFirebaseData => - @bus.disconnect() - onContactClicked: (e) -> Backbone.Mediator.publish 'level:contact-button-pressed', {} @openModalView contactModal = new ContactModal levelID: @level.get('slug') or @level.id, courseID: @courseID, courseInstanceID: @courseInstanceID @@ -629,10 +605,6 @@ module.exports = class PlayLevelView extends RootView AudioPlayer.preloadSoundReference sound # Real-time playback - onRealTimePlaybackWaiting: (e) -> - @$el.addClass('real-time').focus() - @onWindowResize() - onRealTimePlaybackStarted: (e) -> @$el.addClass('real-time').focus() @onWindowResize() @@ -645,14 +617,12 @@ module.exports = class PlayLevelView extends RootView _.delay @onSubmissionComplete, 750 # Wait for transition to end. else @waitingForSubmissionComplete = true - @onRealTimeMultiplayerPlaybackEnded() onSubmissionComplete: => return if @destroyed Backbone.Mediator.publish 'level:set-time', ratio: 1 return if @level.hasLocalChanges() # Don't award achievements when beating level changed in level editor - # TODO: Show a victory dialog specific to hero-ladder level - if @goalManager.checkOverallStatus() is 'success' and not @options.realTimeMultiplayerSessionID? + if @goalManager.checkOverallStatus() is 'success' showModalFn = -> Backbone.Mediator.publish 'level:show-victory', showModal: true @session.recordScores @world.scores, @level if @level.get 'replayable' @@ -677,7 +647,6 @@ module.exports = class PlayLevelView extends RootView #@instance.save() unless @instance.loading delete window.nextURL console.profileEnd?() if PROFILE_ME - @onRealTimeMultiplayerLevelUnloaded() application.tracker?.disableInspectletJS() super() @@ -693,358 +662,3 @@ module.exports = class PlayLevelView extends RootView @setupManager?.destroy() @setupManager = new LevelSetupManager({supermodel: @supermodel, level: @level, levelID: @levelID, parent: @, session: @session, hadEverChosenHero: true}) @setupManager.open() - - # Start Real-time Multiplayer ###################################################### - # - # This view acts as a hub for the real-time multiplayer session for the current level. - # - # It performs these actions: - # Player heartbeat - # Publishes player status - # Updates real-time multiplayer session state - # Updates real-time multiplayer player state - # Cleans up old sessions (sets state to 'finished') - # Real-time multiplayer cast handshake - # Swap teams on game joined, if necessary - # Reload PlayLevelView on real-time submit, automatically continue game and real-time playback - # - # It monitors these: - # Real-time multiplayer sessions - # Current real-time multiplayer session - # Internal multiplayer create/joined/left events - # - # 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 - multiplayerFireHost: 'https://codecombat.firebaseio.com/test/db/' - - 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? - # 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() - @timerMultiplayerHeartbeatID = setInterval @onRealTimeMultiplayerHeartbeat, 60 * 1000 - @cleanupRealTimeSessions() - - cleanupRealTimeSessions: -> - # 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 - - # TODO: similar to game ending cleanup - 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: => - # console.log 'PlayLevelView onRealTimeMultiplayerHeartbeat', @realTimePlayerRef - @realTimePlayerRef.update 'heartbeat': new Date().toISOString() if @realTimePlayerRef? - - onRealTimeMultiplayerCreatedGame: (e) -> - # console.log 'PlayLevelView onRealTimeMultiplayerCreatedGame' - @joinRealTimeMultiplayerGame e - @realTimePlayerGameRef.update 'state': 'coding' - @realTimePlayerRef.update 'state': 'available' - Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Waiting for opponent..' - - 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: (snapshot) => - # console.log 'PlayLevelView onRealTimePlayerAdded', snapshot.val() - # Assume game is full, game on - 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: (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 = @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: "#{@realTimeOpponentData.name} waiting for your code" - - joinRealTimeMultiplayerGame: (e) -> - # console.log 'PlayLevelView joinRealTimeMultiplayerGame', e - unless @realTimeSessionRef? - @session.set('submittedCodeLanguage', @session.get('codeLanguage')) - @session.save() - - @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 @realTimeSessionRef.' - - onRealTimeMultiplayerJoinedGame: (e) -> - # console.log 'PlayLevelView onRealTimeMultiplayerJoinedGame', e - @joinRealTimeMultiplayerGame e - @realTimePlayerGameRef.update 'state': 'coding' - @realTimePlayerRef.update 'state': 'unavailable' - - onRealTimeMultiplayerLeftGame: (e) -> - # console.log 'PlayLevelView onRealTimeMultiplayerLeftGame', e - if e.userID? and e.userID is me.id - @realTimePlayerGameRef.update 'state': 'left' - @realTimeGameEnded() - - realTimeMultiplayerContinueGame: (realTimeSessionID) -> - # console.log 'PlayLevelView realTimeMultiplayerContinueGame', realTimeSessionID, me.id - Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: realTimeSessionID - - console.info 'Setting my game status to ready' - @realTimePlayerGameRef.update 'state': 'ready' - - if @realTimeOpponentData.state is 'ready' - @realTimeOpponentIsReady() - else - console.info 'Waiting for opponent to be ready' - @realTimeOpponentRef.on 'value', @realTimeOpponentMaybeReady - - realTimeOpponentMaybeReady: (snapshot) => - # console.log 'PlayLevelView realTimeOpponentMaybeReady' - if @realTimeOpponentData = snapshot.val() - if @realTimeOpponentData.state is 'ready' - @realTimeOpponentRef.off 'value', @realTimeOpponentMaybeReady - @realTimeOpponentIsReady() - - realTimeOpponentIsReady: => - console.info 'All real-time multiplayer players are ready!' - @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 @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', @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 @realTimeSessionData.creator - sessionState = @session.get('state') - if sessionState? - submissionCount = sessionState.submissionCount ? 0 - console.info 'Setting multiplayer submissionCount to', submissionCount - @realTimeSessionRef.update 'submissionCount': submissionCount - else - console.error 'Failed to read sessionState in onRealTimeMultiplayerCast' - - console.info 'Submitting my code' - permissions = @session.get 'permissions' ? [] - unless _.find(permissions, (p) -> p.target is 'public' and p.access is 'read') - permissions.push target:'public', access:'read' - @session.set 'permissions', permissions - @session.patch() - @realTimePlayerGameRef.update 'state': 'submitted' - - console.info 'Other player is', @realTimeOpponentData.state - if @realTimeOpponentData.state in ['submitted', 'ready'] - @realTimeOpponentSubmittedCode @realTimeOpponentData, @realTimePlayerGameData - else - # 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: -> - # 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', @realTimeSessionData.id, opponentPlayer.level_session - # Read submissionCount for joined real-time multiplayer session - if me.id isnt @realTimeSessionData.creator - sessionState = @session.get('state') ? {} - newSubmissionCount = @realTimeSessionData.submissionCount - if newSubmissionCount? - # TODO: This isn't always getting updated where the random seed generation uses it. - sessionState.submissionCount = parseInt newSubmissionCount - console.info 'Got multiplayer submissionCount', sessionState.submissionCount - @session.set 'state', sessionState - @session.patch() - - # Reload this level so the opponent session can easily be wired up - Backbone.Mediator.publish 'router:navigate', - route: "/play/level/#{@levelID}" - viewClass: PlayLevelView - 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 @realTimeOpponentData? - console.error 'Tried to switch teams without real-time multiplayer opponent data.' - return - unless @realTimeSessionData? - console.error 'Tried to switch teams without real-time multiplayer session data.' - return - return if me.id is @realTimeSessionData.creator - - oldTeam = @realTimeOpponentData.team - return unless oldTeam is @session.get('team') - - # Need to switch to other team - newTeam = if oldTeam is 'humans' then 'ogres' else 'humans' - console.info "Switching from team #{oldTeam} to #{newTeam}" - - # Move code from old team to new team - # Assumes teamSpells has matching spells for each team - # TODO: Similar to code in loadOpponentTeam, consolidate? - code = @session.get 'code' - teamSpells = @session.get 'teamSpells' - for oldSpellKey in teamSpells[oldTeam] - [oldThang, oldSpell] = oldSpellKey.split '/' - oldCode = code[oldThang]?[oldSpell] - continue unless oldCode? - # Move oldCode to new team under same spell - for newSpellKey in teamSpells[newTeam] - [newThang, newSpell] = newSpellKey.split '/' - if newSpell is oldSpell - # Found spell location under new team - # console.log "Swapping spell=#{oldSpell} from #{oldThang} to #{newThang}" - if code[newThang]?[oldSpell]? - # Option 1: have a new spell to swap - code[oldThang][oldSpell] = code[newThang][oldSpell] - else - # Option 2: no new spell to swap - delete code[oldThang][oldSpell] - code[newThang] = {} unless code[newThang]? - code[newThang][oldSpell] = oldCode - break - - @setTeam newTeam # Sets @session 'team' - sessionState = @session.get('state') - if sessionState? - # TODO: Don't hard code thangID - sessionState.selected = if newTeam is 'humans' then 'Hero Placeholder' else 'Hero Placeholder 1' - @session.set 'state', sessionState - @session.set 'code', code - @session.patch() - - if sessionState? - # TODO: Don't hardcode spellName - Backbone.Mediator.publish 'level:select-sprite', thangID: sessionState.selected, spellName: 'plan' - -# End Real-time Multiplayer ###################################################### diff --git a/app/views/play/level/tome/CastButtonView.coffee b/app/views/play/level/tome/CastButtonView.coffee index deade19b4..c40dec5a6 100644 --- a/app/views/play/level/tome/CastButtonView.coffee +++ b/app/views/play/level/tome/CastButtonView.coffee @@ -18,9 +18,6 @@ module.exports = class CastButtonView extends CocoView 'tome:cast-spells': 'onCastSpells' 'tome:manual-cast-denied': 'onManualCastDenied' 'god:new-world-created': 'onNewWorld' - 'real-time-multiplayer:created-game': 'onJoinedRealTimeMultiplayerGame' - 'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame' - 'real-time-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame' 'goal-manager:new-goal-states': 'onNewGoalStates' 'god:goals-calculated': 'onGoalsCalculated' 'playback:ended-changed': 'onPlaybackEndedChanged' @@ -71,9 +68,7 @@ module.exports = class CastButtonView extends CocoView Backbone.Mediator.publish 'tome:manual-cast', {} onCastRealTimeButtonClick: (e) -> - if @inRealTimeMultiplayerSession - Backbone.Mediator.publish 'real-time-multiplayer:manual-cast', {} - else if @options.level.get('replayable') and (timeUntilResubmit = @options.session.timeUntilResubmit()) > 0 + if @options.level.get('replayable') and (timeUntilResubmit = @options.session.timeUntilResubmit()) > 0 Backbone.Mediator.publish 'tome:manual-cast-denied', timeUntilResubmit: timeUntilResubmit else Backbone.Mediator.publish 'tome:manual-cast', {realTime: true} @@ -178,9 +173,3 @@ module.exports = class CastButtonView extends CocoView return unless placeholder.length @ladderSubmissionView = new LadderSubmissionView session: @options.session, level: @options.level, mirrorSession: @mirrorSession @insertSubView @ladderSubmissionView, placeholder - - onJoinedRealTimeMultiplayerGame: (e) -> - @inRealTimeMultiplayerSession = true - - onLeftRealTimeMultiplayerGame: (e) -> - @inRealTimeMultiplayerSession = false diff --git a/app/views/play/level/tome/ProblemAlertView.coffee b/app/views/play/level/tome/ProblemAlertView.coffee index c6508eb17..c9ffc1363 100644 --- a/app/views/play/level/tome/ProblemAlertView.coffee +++ b/app/views/play/level/tome/ProblemAlertView.coffee @@ -14,7 +14,6 @@ module.exports = class ProblemAlertView extends CocoView 'level:restart': 'onHideProblemAlert' 'tome:jiggle-problem-alert': 'onJiggleProblemAlert' 'tome:manual-cast': 'onHideProblemAlert' - 'real-time-multiplayer:manual-cast': 'onHideProblemAlert' events: 'click .close': 'onRemoveClicked' diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index 834757ac2..db465c974 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -61,7 +61,6 @@ module.exports = class SpellView extends CocoView @supermodel = options.supermodel @worker = options.worker @session = options.session - @listenTo(@session, 'change:multiplayer', @onMultiplayerChanged) @spell = options.spell @problems = [] @savedProblems = {} # Cache saved user code problems to prevent duplicates @@ -77,11 +76,7 @@ module.exports = class SpellView extends CocoView @fillACE() @createOnCodeChangeHandlers() @lockDefaultCode() - if @session.get('multiplayer') - @createFirepad() - else - # needs to happen after the code generating this view is complete - _.defer @onAllLoaded + _.defer @onAllLoaded # Needs to happen after the code generating this view is complete createACE: -> # Test themes and settings here: http://ace.ajax.org/build/kitchen-sink.html @@ -402,7 +397,6 @@ module.exports = class SpellView extends CocoView wrapper => orig.apply obj, args obj[method] - finishRange = (row, startRow, startColumn) => range = new Range startRow, startColumn, row, @aceSession.getLine(row).length - 1 range.start = @aceDoc.createAnchor range.start @@ -502,8 +496,6 @@ module.exports = class SpellView extends CocoView return unless @zatanna and @autocomplete @zatanna.addCodeCombatSnippets @options.level, @, e - - translateFindNearest: -> # If they have advanced glasses but are playing a level which assumes earlier glasses, we'll adjust the sample code to use the more advanced APIs instead. oldSource = @getSource() @@ -514,14 +506,9 @@ module.exports = class SpellView extends CocoView @updateACEText newSource _.delay (=> @recompile?()), 1000 - onMultiplayerChanged: -> - if @session.get('multiplayer') - @createFirepad() - else - @firepad?.dispose() - createFirepad: -> - # load from firebase or the original source if there's nothing there + # Currently not called; could be brought back for future multiplayer modes. + # Load from firebase or the original source if there's nothing there. return if @firepadLoading @eventsSuppressed = true @loaded = false @@ -532,19 +519,18 @@ module.exports = class SpellView extends CocoView @fireRef = new Firebase fireURL firepadOptions = userId: me.id @firepad = Firepad.fromACE @fireRef, @ace, firepadOptions - @firepad.on 'ready', @onFirepadLoaded @firepadLoading = true - - onFirepadLoaded: => - @firepadLoading = false - firepadSource = @ace.getValue() - if firepadSource - @spell.source = firepadSource - else - @ace.setValue @previousSource - @aceSession.setUndoManager(new UndoManager()) - @ace.clearSelection() - @onAllLoaded() + @firepad.on 'ready', => + return if @destroyed + @firepadLoading = false + firepadSource = @ace.getValue() + if firepadSource + @spell.source = firepadSource + else + @ace.setValue @previousSource + @aceSession.setUndoManager(new UndoManager()) + @ace.clearSelection() + @onAllLoaded() onAllLoaded: => @spell.transpile @spell.source @@ -573,7 +559,7 @@ module.exports = class SpellView extends CocoView @saveSpade() getSource: -> - @ace.getValue() # could also do @firepad.getText() + @ace.getValue() setThang: (thang) -> @focus() diff --git a/app/views/play/menu/GameMenuModal.coffee b/app/views/play/menu/GameMenuModal.coffee index efe19ec2f..b0489865c 100644 --- a/app/views/play/menu/GameMenuModal.coffee +++ b/app/views/play/menu/GameMenuModal.coffee @@ -5,7 +5,6 @@ submenuViews = [ require 'views/play/menu/SaveLoadView' require 'views/play/menu/OptionsView' require 'views/play/menu/GuideView' - require 'views/play/menu/MultiplayerView' ] module.exports = class GameMenuModal extends ModalView @@ -31,11 +30,10 @@ module.exports = class GameMenuModal extends ModalView getRenderData: (context={}) -> context = super(context) docs = @options.level.get('documentation') ? {} - submenus = ['guide', 'options', 'save-load', 'multiplayer'] + submenus = ['guide', 'options', 'save-load'] submenus = _.without submenus, 'options' if window.serverConfig.picoCTF submenus = _.without submenus, 'guide' unless docs.specificArticles?.length or docs.generalArticles?.length or window.serverConfig.picoCTF submenus = _.without submenus, 'save-load' unless me.isAdmin() or /https?:\/\/localhost/.test(window.location.href) - submenus = _.without submenus, 'multiplayer' unless me.isAdmin() or (@level?.isType('ladder', 'hero-ladder', 'course-ladder') and @level.get('slug') not in ['ace-of-coders', 'elemental-wars']) @includedSubmenus = submenus context.showTab = @options.showTab ? submenus[0] context.submenus = submenus @@ -43,7 +41,6 @@ module.exports = class GameMenuModal extends ModalView 'options': 'cog' 'guide': 'list' 'save-load': 'floppy-disk' - 'multiplayer': 'globe' context showsChooseHero: -> @@ -55,7 +52,6 @@ module.exports = class GameMenuModal extends ModalView super() @insertSubView new submenuView @options for submenuView in submenuViews firstView = switch @options.showTab - when 'multiplayer' then @subviews.multiplayer_view when 'guide' then @subviews.guide_view else if 'guide' in @includedSubmenus then @subviews.guide_view else @subviews.options_view diff --git a/app/views/play/menu/MultiplayerView.coffee b/app/views/play/menu/MultiplayerView.coffee deleted file mode 100644 index 4925744ce..000000000 --- a/app/views/play/menu/MultiplayerView.coffee +++ /dev/null @@ -1,229 +0,0 @@ -CocoView = require 'views/core/CocoView' -template = require 'templates/play/menu/multiplayer-view' -{me} = require 'core/auth' -ThangType = require 'models/ThangType' -LadderSubmissionView = require 'views/play/common/LadderSubmissionView' -RealTimeModel = require 'models/RealTimeModel' -RealTimeCollection = require 'collections/RealTimeCollection' - -module.exports = class MultiplayerView extends CocoView - id: 'multiplayer-view' - className: 'tab-pane' - template: template - - subscriptions: - 'ladder:game-submitted': 'onGameSubmitted' - - events: - 'click textarea': 'onClickLink' - 'change #multiplayer': 'updateLinkSection' - 'click #create-game-button': 'onCreateRealTimeGame' - 'click #join-game-button': 'onJoinRealTimeGame' - 'click #leave-game-button': 'onLeaveRealTimeGame' - - constructor: (options) -> - super(options) - @level = options.level - @levelID = @level?.get 'slug' - @session = options.session - @listenTo @session, 'change:multiplayer', @updateLinkSection - @watchRealTimeSessions() if @level?.isType('hero-ladder', 'course-ladder') and me.isAdmin() - - destroy: -> - @realTimeSessions?.off 'add', @onRealTimeSessionAdded - @currentRealTimeSession?.off 'change', @onCurrentRealTimeSessionChanged - collection.off() for id, collection of @realTimeSessionsPlayers - super() - - getRenderData: -> - c = super() - c.joinLink = "#{document.location.href.replace(/\?.*/, '').replace('#', '')}?session=#{@session.id}" - c.multiplayer = @session.get 'multiplayer' - c.team = @session.get 'team' - c.levelSlug = @levelID - # For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet. - if @level?.isType('ladder', 'hero-ladder', 'course-ladder') - c.ladderGame = true - c.readyToRank = @session?.readyToRank() - - # Real-time multiplayer stuff - if @level?.isType('hero-ladder', 'course-ladder') and me.isAdmin() - c.levelID = @session.get('levelID') - c.realTimeSessions = @realTimeSessions - c.currentRealTimeSession = @currentRealTimeSession if @currentRealTimeSession - c.realTimeSessionsPlayers = @realTimeSessionsPlayers if @realTimeSessionsPlayers - # console.log 'MultiplayerView getRenderData', c.levelID - # console.log 'realTimeSessions', c.realTimeSessions - # console.log c.realTimeSessions.at(c.realTimeSessions.length - 1).get('state') if c.realTimeSessions.length > 0 - # console.log 'currentRealTimeSession', c.currentRealTimeSession - # console.log 'realTimeSessionPlayers', c.realTimeSessionsPlayers - - c - - afterRender: -> - super() - @updateLinkSection() - @ladderSubmissionView = new LadderSubmissionView session: @session, level: @level - @insertSubView @ladderSubmissionView, @$el.find('.ladder-submission-view') - @$el.find('#created-multiplayer-session').toggle Boolean(@currentRealTimeSession?) - @$el.find('#create-game-button').toggle Boolean(not (@currentRealTimeSession?)) - - onClickLink: (e) -> - e.target.select() - - onGameSubmitted: (e) -> - # Preserve the supermodel as we navigate back to the ladder. - viewArgs = [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @levelID] - ladderURL = "/play/ladder/#{@levelID}" - if leagueID = @getQueryVariable 'league' - leagueType = if @level?.isType('course-ladder') then 'course' else 'clan' - viewArgs.push leagueType - viewArgs.push leagueID - ladderURL += "/#{leagueType}/#{leagueID}" - ladderURL += '#my-matches' - Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: 'views/ladder/LadderView', viewArgs: viewArgs - - updateLinkSection: -> - multiplayer = @$el.find('#multiplayer').prop('checked') - la = @$el.find('#link-area') - la.toggle if @level?.isType('ladder', 'hero-ladder', 'course-ladder') then false else Boolean(multiplayer) - true - - onHidden: -> - multiplayer = Boolean(@$el.find('#multiplayer').prop('checked')) - @session.set('multiplayer', multiplayer) - - # Real-time Multiplayer ###################################################### - # - # This view is responsible for joining and leaving real-time multiplayer games. - # - # It performs these actions: - # Display your current game (level, players) - # Display open games - # Create game button, if not in a game - # Join game button - # Leave game button, if in a game - # - # It monitors these: - # Real-time multiplayer sessions (for open games, player states) - # Current real-time multiplayer game session for changes - # Players for real-time multiplayer game session - # - # Real-time state variables: - # @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. - @realTimeSessions = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}") - @realTimeSessions.on 'add', @onRealTimeSessionAdded - @realTimeSessions.each (rts) => @watchRealTimeSession rts - - watchRealTimeSession: (rts) -> - return if rts.get('state') is 'finished' - return if rts.get('levelID') isnt @session.get('levelID') - # console.log 'MultiplayerView watchRealTimeSession', rts - # Setup monitoring of players for given session - # TODO: verify we need this - realTimeSession = new RealTimeModel("multiplayer_level_sessions/#{@levelID}/#{rts.id}") - realTimeSession.on 'change', @onRealTimeSessionChanged - @realTimeSessionsPlayers[rts.id] = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{rts.id}/players") - @realTimeSessionsPlayers[rts.id].on 'add', @onRealTimePlayerAdded - @findCurrentRealTimeSession rts - - findCurrentRealTimeSession: (rts) -> - # Look for our current real-time session (level, level state, member player) - return if @currentRealTimeSession or not @realTimeSessionsPlayers? - if rts.get('levelID') is @session.get('levelID') and rts.get('state') isnt 'finished' - @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/#{@levelID}/#{rts.id}") - @currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged - - # TODO: Is this necessary? Shouldn't everyone already know we joined a game at this point? - Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: @currentRealTimeSession.id - - onRealTimeSessionAdded: (rts) => - @watchRealTimeSession rts - @render() - - onRealTimeSessionChanged: (rts) => - # console.log 'MultiplayerView onRealTimeSessionChanged', rts.get('state') - # TODO: @realTimeSessions isn't updated before we call render() here - # TODO: so this game isn't updated in open games list - @render?() - - onCurrentRealTimeSessionChanged: (rts) => - # console.log 'MultiplayerView onCurrentRealTimeSessionChanged', rts - if rts.get('state') is 'finished' - @currentRealTimeSession.off 'change', @onCurrentRealTimeSessionChanged - @currentRealTimeSession = null - @render?() - - onRealTimePlayerAdded: (e) => - @render?() - - onCreateRealTimeGame: -> - @playSound 'menu-button-click' - s = @realTimeSessions.create { - creator: @session.get('creator') - creatorName: @session.get('creatorName') - levelID: @session.get('levelID') - created: (new Date()).toISOString() - state: 'creating' - } - @currentRealTimeSession = @realTimeSessions.get(s.id) - @currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged - # TODO: s.id === @currentRealTimeSession.id ? - players = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{@currentRealTimeSession.id}/players") - players.create - id: me.id - state: 'coding' - name: @session.get('creatorName') - team: @session.get('team') - level_session: @session.id - Backbone.Mediator.publish 'real-time-multiplayer:created-game', realTimeSessionID: @currentRealTimeSession.id - @render() - - onJoinRealTimeGame: (e) -> - return if @currentRealTimeSession - @playSound 'menu-button-click' - item = @$el.find(e.target).data('item') - @currentRealTimeSession = @realTimeSessions.get(item.id) - @currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged - if @realTimeSessionsPlayers[item.id] - - # TODO: SpellView updateTeam() should take care of this team swap update in the real-time multiplayer session - creatorID = @currentRealTimeSession.get('creator') - creator = @realTimeSessionsPlayers[item.id].get(creatorID) - creatorTeam = creator.get('team') - myTeam = @session.get('team') - if myTeam is creatorTeam - myTeam = if creatorTeam is 'humans' then 'ogres' else 'humans' - - @realTimeSessionsPlayers[item.id].create - id: me.id - state: 'coding' - name: me.get('name') - team: myTeam - level_session: @session.id - else - console.error 'MultiplayerView onJoinRealTimeGame did not have a players collection', @currentRealTimeSession - Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: @currentRealTimeSession.id - @render() - - onLeaveRealTimeGame: (e) -> - @playSound 'menu-button-click' - if @currentRealTimeSession - @currentRealTimeSession.off 'change', @onCurrentRealTimeSessionChanged - @currentRealTimeSession = null - Backbone.Mediator.publish 'real-time-multiplayer:left-game', userID: me.id - else - console.error "Tried to leave a game with no currentMultiplayerSession" - @render() diff --git a/server/models/LevelSession.coffee b/server/models/LevelSession.coffee index f61c9f4f8..b68263841 100644 --- a/server/models/LevelSession.coffee +++ b/server/models/LevelSession.coffee @@ -83,7 +83,7 @@ LevelSessionSchema.pre 'save', (next) -> next() LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed'] -LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state', +LevelSessionSchema.statics.editableProperties = ['players', 'code', 'codeLanguage', 'completed', 'state', 'levelName', 'creatorName', 'levelID', 'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime', 'heroConfig', 'team',