Fixed #1638: detect and automatically report unrecoverable world loading errors, plus adding better instructions for checking dev console. We can't recover yet, but having eyes on it will hopefully give us better visibility on this kind of error.

This commit is contained in:
Nick Winter 2015-04-25 17:29:02 -07:00
parent aeadb83975
commit 80af32180c
8 changed files with 46 additions and 7 deletions

View file

@ -1,6 +1,7 @@
module.exports.sendContactMessage = (contactMessageObject, modal) ->
modal.find('.sending-indicator').show()
modal?.find('.sending-indicator').show()
jqxhr = $.post '/contact', contactMessageObject, (response) ->
return unless modal
modal.find('.sending-indicator').hide()
modal.find('#contact-message').val('Thanks!')
_.delay ->

View file

@ -5,6 +5,7 @@
World = require 'lib/world/world'
CocoClass = require 'core/CocoClass'
GoalManager = require 'lib/world/GoalManager'
{sendContactMessage} = require 'core/contact'
module.exports = class Angel extends CocoClass
@nicks: ['Archer', 'Lana', 'Cyril', 'Pam', 'Cheryl', 'Woodhouse', 'Ray', 'Krieger']
@ -30,6 +31,7 @@ module.exports = class Angel extends CocoClass
@abortTimeoutDuration *= 10
@initialized = false
@running = false
@allLogs = []
@hireWorker()
@shared.angels.push @
@ -44,11 +46,12 @@ module.exports = class Angel extends CocoClass
# say: debugging stuff, usually off; log: important performance indicators, keep on
say: (args...) -> #@log args...
log: ->
# console.info.apply is undefined in IE9, CofeeScript splats invocation won't work.
# console.info.apply is undefined in IE9, CoffeeScript splats invocation won't work.
# http://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function
message = "|#{@shared.godNick}'s #{@nick}|"
message += " #{arg}" for arg in arguments
console.info message
@allLogs.push message
testWorker: =>
return if @destroyed
@ -86,7 +89,7 @@ module.exports = class Angel extends CocoClass
when 'non-user-code-problem'
Backbone.Mediator.publish 'god:non-user-code-problem', problem: event.data.problem
if @shared.firstWorld
@infinitelyLooped() # For now, this should do roughly the right thing if it happens during load.
@infinitelyLooped(false, true) # For now, this should do roughly the right thing if it happens during load.
else
@fireWorker()
@ -166,15 +169,26 @@ module.exports = class Angel extends CocoClass
@worker.postMessage func: 'finalizePreload'
@work.preload = false
infinitelyLooped: (escaped=false) =>
infinitelyLooped: (escaped=false, nonUserCodeProblem=false) =>
@say 'On infinitely looped! Aborting?', @aborting
return if @aborting
problem = type: 'runtime', level: 'error', id: 'runtime_InfiniteLoop', message: 'Code never finished. It\'s either really slow or has an infinite loop.'
problem.message = 'Escape pressed; code aborted.' if escaped
Backbone.Mediator.publish 'god:user-code-problem', problem: problem
Backbone.Mediator.publish 'god:infinite-loop', firstWorld: @shared.firstWorld
Backbone.Mediator.publish 'god:infinite-loop', firstWorld: @shared.firstWorld, nonUserCodeProblem: nonUserCodeProblem
@reportLoadError() if nonUserCodeProblem
@fireWorker()
reportLoadError: ->
context = email: me.get('email')
context.message = "Automatic Report - Unable to Load Level\nLogs:\n" + @allLogs.join('\n')
if $.browser
context.browser = "#{$.browser.platform} #{$.browser.name} #{$.browser.versionNumber}"
context.screenSize = "#{screen?.width ? $(window).width()} x #{screen?.height ? $(window).height()}"
context.subject = "Level Load Error: #{@work?.level?.name or 'Unknown Level'}"
context.levelSlug = @work?.level?.slug
sendContactMessage context unless me.isAdmin()
doWork: ->
return if @aborting
return @say 'Not initialized for work yet.' unless @initialized

View file

@ -291,6 +291,11 @@
time_current: "Now:"
time_total: "Max:"
time_goto: "Go to:"
non_user_code_problem_title: "Unable to Load Level"
infinite_loop_title: "Infinite Loop Detected"
infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know."
check_dev_console: "You can also open the developer console to see what might be going wrong."
check_dev_console_link: "(instructions)"
infinite_loop_try_again: "Try Again"
infinite_loop_reset_level: "Reset Level"
infinite_loop_comment_out: "Comment Out My Code"

View file

@ -35,6 +35,7 @@ module.exports =
'god:infinite-loop': c.object {required: ['firstWorld']},
firstWorld: {type: 'boolean'}
nonUserCodeProblem: {type: 'boolean'}
'god:new-world-created': worldUpdatedEventSchema

View file

@ -1,12 +1,19 @@
extends /templates/core/modal-base
block modal-header-content
h3(data-i18n="play_level.infinite_loop_title") Infinite Loop Detected
if nonUserCodeProblem
h3(data-i18n="play_level.non_user_code_problem_title") Unable to Load Level
else
h3(data-i18n="play_level.infinite_loop_title") Infinite Loop Detected
block modal-body-content
.modal-body
p(data-i18n="play_level.infinite_loop_explanation") The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know.
p
span.spr(data-i18n="play_level.check_dev_console") You can also open the developer console to see what might be going wrong.
a(href="http://webmasters.stackexchange.com/questions/8525/how-to-open-the-javascript-console-in-different-browsers/77337#77337", data-i18n="play_level.check_dev_console_instructions", target="_blank") (instructions)
block modal-footer-content
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="play_level.infinite_loop_try_again").btn#restart-level-infinite-loop-retry-button Try Again
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="play_level.infinite_loop_reset_level").btn.btn-danger#restart-level-infinite-loop-confirm-button Reset Level

View file

@ -455,7 +455,7 @@ module.exports = class PlayLevelView extends RootView
onInfiniteLoop: (e) ->
return unless e.firstWorld
@openModalView new InfiniteLoopModal()
@openModalView new InfiniteLoopModal nonUserCodeProblem: e.nonUserCodeProblem
application.tracker?.trackEvent 'Saw Initial Infinite Loop', category: 'Play Level', level: @level.get('name'), label: @level.get('name') unless @observing
onHighlightDOM: (e) -> @highlightElement e.selector, delay: e.delay, sides: e.sides, offset: e.offset, rotation: e.rotation

View file

@ -9,3 +9,8 @@ module.exports = class InfiniteLoopModal extends ModalView
'click #restart-level-infinite-loop-retry-button': -> Backbone.Mediator.publish 'tome:cast-spell', {}
'click #restart-level-infinite-loop-confirm-button': -> Backbone.Mediator.publish 'level:restart', {}
'click #restart-level-infinite-loop-comment-button': -> Backbone.Mediator.publish 'tome:comment-my-code', {}
getRenderData: ->
c = super()
c.nonUserCodeProblem = @options.nonUserCodeProblem
c

View file

@ -5,6 +5,7 @@ sendwithus = require '../sendwithus'
async = require 'async'
LevelSession = require '../levels/sessions/LevelSession'
moment = require 'moment'
hipchat = require '../hipchat'
module.exports.setup = (app) ->
app.post '/contact', (req, res) ->
@ -65,6 +66,11 @@ createMailContext = (req, done) ->
context.email_data.content += "\n<img src='#{req.body.screenshotURL}' />"
done context
if /Level Load Error/.test context.email_data.subject
message = "#{user.get('name') or user.get('email')} saw #{context.email_data.subject} <a href=\"http://direct.codecombat.com/editor/level/#{req.body.levelSlug}\">(level editor)</a>"
hipchat.sendHipChatMessage message, ['tower'], color: 'red'
fetchRecentSessions = (user, context, callback) ->
query = creator: user.get('_id') + ''
projection = levelID: 1, levelName: 1, changed: 1, team: 1, codeLanguage: 1, 'state.complete': 1, playtime: 1