mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 08:50:58 -05:00
Experimental support for preloading worlds when we would have autocast if manual cast is on.
This commit is contained in:
parent
630c3b7d57
commit
74ef9bc987
11 changed files with 62 additions and 35 deletions
|
@ -440,6 +440,10 @@ self.reportIn = function reportIn() {
|
|||
self.postMessage({type: 'report-in'});
|
||||
};
|
||||
|
||||
self.finalizePreload = function finalizePreload() {
|
||||
self.world.finalizePreload(self.onWorldLoaded);
|
||||
};
|
||||
|
||||
self.addEventListener('message', function(event) {
|
||||
self[event.data.func](event.data.args);
|
||||
});
|
||||
|
|
|
@ -112,6 +112,9 @@ module.exports = class Angel extends CocoClass
|
|||
_.remove @shared.busyAngels, @
|
||||
@doWork()
|
||||
|
||||
finalizePreload: ->
|
||||
@worker.postMessage func: 'finalizePreload'
|
||||
|
||||
infinitelyLooped: =>
|
||||
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."
|
||||
|
@ -123,12 +126,12 @@ module.exports = class Angel extends CocoClass
|
|||
return if @aborting
|
||||
return @say "Not initialized for work yet." unless @initialized
|
||||
if @shared.workQueue.length
|
||||
work = @shared.workQueue.shift()
|
||||
return _.defer @simulateSync, work if work.synchronous
|
||||
@work = @shared.workQueue.shift()
|
||||
return _.defer @simulateSync, @work if @work.synchronous
|
||||
@say "Running world..."
|
||||
@running = true
|
||||
@shared.busyAngels.push @
|
||||
@worker.postMessage func: 'runWorld', args: work
|
||||
@worker.postMessage func: 'runWorld', args: @work
|
||||
clearTimeout @purgatoryTimer
|
||||
@purgatoryTimer = setInterval @testWorker, @infiniteLoopIntervalDuration
|
||||
else
|
||||
|
@ -139,6 +142,7 @@ module.exports = class Angel extends CocoClass
|
|||
return unless @worker and @running
|
||||
@say "Aborting..."
|
||||
@running = false
|
||||
@work = null
|
||||
_.remove @shared.busyAngels, @
|
||||
@abortTimeout = _.delay @fireWorker, @abortTimeoutDuration
|
||||
@aborting = true
|
||||
|
|
|
@ -39,7 +39,7 @@ module.exports = class God extends CocoClass
|
|||
_.delay (=> new Angel @angelsShare unless @destroyed), 250 * i for i in [0 ... angelCount]
|
||||
|
||||
destroy: ->
|
||||
angel.destroy() for angel in @angelsShare.angels
|
||||
angel.destroy() for angel in @angelsShare.angels.slice()
|
||||
@angelsShare.goalManager?.destroy()
|
||||
@debugWorker?.terminate()
|
||||
@debugWorker?.removeEventListener 'message', @onDebugWorkerMessage
|
||||
|
@ -54,10 +54,24 @@ module.exports = class God extends CocoClass
|
|||
|
||||
createWorld: (spells, preload=false) ->
|
||||
console.log "#{@nick}: Let there be light upon #{@level.name}!"
|
||||
angel.abort() for angel in @angelsShare.busyAngels # We really only ever want one world calculated per God.
|
||||
userCodeMap = @getUserCodeMap spells
|
||||
|
||||
# We only want one world being simulated, so we abort other angels, unless we had one preloading this very code.
|
||||
hadPreloader = false
|
||||
for angel in @angelsShare.busyAngels
|
||||
isPreloading = angel.running and angel.work.preload and _.isEqual angel.work.userCodeMap, userCodeMap, (a, b) ->
|
||||
return a.raw is b.raw if a?.raw? and b?.raw?
|
||||
undefined # Let default equality test suffice.
|
||||
if not hadPreloader and isPreloading
|
||||
angel.finalizePreload()
|
||||
hadPreloader = true
|
||||
else
|
||||
angel.abort()
|
||||
return if hadPreloader
|
||||
|
||||
@angelsShare.workQueue = []
|
||||
@angelsShare.workQueue.push
|
||||
userCodeMap: @getUserCodeMap(spells)
|
||||
userCodeMap: userCodeMap
|
||||
level: @level
|
||||
goals: @angelsShare.goalManager?.getGoals()
|
||||
headless: @angelsShare.headless
|
||||
|
|
|
@ -14,9 +14,9 @@ module.exports = class CastingScreen extends CocoClass
|
|||
console.error @toString(), "needs a camera." unless @camera
|
||||
console.error @toString(), "needs a layer." unless @layer
|
||||
@build()
|
||||
|
||||
onCastingBegins: -> @show()
|
||||
onCastingEnds: -> @hide()
|
||||
|
||||
onCastingBegins: (e) -> @show() unless e.preload
|
||||
onCastingEnds: (e) -> @hide()
|
||||
|
||||
toString: -> "<CastingScreen>"
|
||||
|
||||
|
|
|
@ -218,7 +218,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
@world = @options.world = e.world
|
||||
@play()
|
||||
|
||||
onCastSpells: -> @stop()
|
||||
onCastSpells: (e) -> @stop() unless e.preload
|
||||
|
||||
play: ->
|
||||
sprite.play() for sprite in @spriteArray
|
||||
|
|
|
@ -331,7 +331,8 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
else
|
||||
performToggle()
|
||||
|
||||
onCastSpells: ->
|
||||
onCastSpells: (e) ->
|
||||
return if e.preload
|
||||
@casting = true
|
||||
@wasPlayingWhenCastingBegan = @playing
|
||||
Backbone.Mediator.publish 'level-set-playing', { playing: false }
|
||||
|
|
|
@ -119,6 +119,10 @@ module.exports = class World
|
|||
loadProgressCallback? 1
|
||||
loadedCallback()
|
||||
|
||||
finalizePreload: (loadedCallback) ->
|
||||
@preloading = false
|
||||
loadedCallback() if @ended
|
||||
|
||||
abort: ->
|
||||
@aborted = true
|
||||
|
||||
|
|
|
@ -183,7 +183,8 @@ module.exports = class PlaybackView extends View
|
|||
onEditEditorConfig: ->
|
||||
@openModalView(new EditorConfigModal())
|
||||
|
||||
onCastSpells: ->
|
||||
onCastSpells: (e) ->
|
||||
return if e.preload
|
||||
@casting = true
|
||||
@$progressScrubber.slider('disable', true)
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ module.exports = class CastButtonView extends View
|
|||
@updateCastButton()
|
||||
|
||||
onCastSpells: (e) ->
|
||||
return if e.preload
|
||||
@casting = true
|
||||
Backbone.Mediator.publish 'play-sound', trigger: 'cast', volume: 0.5
|
||||
@updateCastButton()
|
||||
|
|
|
@ -51,7 +51,7 @@ module.exports = class SpellView extends View
|
|||
@session = options.session
|
||||
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
|
||||
@spell = options.spell
|
||||
@problems = {}
|
||||
@problems = []
|
||||
@writable = false unless me.team in @spell.permissions.readwrite # TODO: make this do anything
|
||||
@highlightCurrentLine = _.throttle @highlightCurrentLine, 100
|
||||
|
||||
|
@ -289,30 +289,21 @@ module.exports = class SpellView extends View
|
|||
callback() for callback in onAnyChange # Then these
|
||||
@aceDoc.on 'change', @onCodeChangeMetaHandler
|
||||
|
||||
setRecompileNeeded: (needed=true) =>
|
||||
if needed
|
||||
@recompileNeeded = needed # and @recompileValid # todo, remove if not caring about validity
|
||||
else
|
||||
@recompileNeeded = false
|
||||
setRecompileNeeded: (@recompileNeeded) =>
|
||||
|
||||
onCursorActivity: =>
|
||||
@currentAutocastHandler?()
|
||||
|
||||
# Design for a simpler system?
|
||||
# * Turn off ACE's JSHint worker
|
||||
# * Keep Aether linting, debounced, on any significant change
|
||||
# - Don't send runtime errors from in-progress worlds
|
||||
# - All problems just vanish when you make any change to the code
|
||||
# * You wouldn't accept any Aether updates/runtime information/errors unless its code was current when you got it
|
||||
# * Store the last run Aether in each spellThang and use it whenever its code actually is current
|
||||
# This suffers from the problem that any whitespace/comment changes will lose your info, but what else
|
||||
# could you do other than somehow maintain a mapping from current to original code locations?
|
||||
# I guess you could use dynamic markers for problem ranges and keep annotations/alerts in when insignificant
|
||||
# * Store the last run Aether in each spellThang and use it whenever its code actually is current.
|
||||
# Use dynamic markers for problem ranges and keep annotations/alerts in when insignificant
|
||||
# changes happen, but always treat any change in the (trimmed) number of lines as a significant change.
|
||||
# Ooh, that's pretty nice. Gets you most of the way there and is simple.
|
||||
# - All problems have a master representation as a Problem, and we can easily generate all Problems from
|
||||
# any Aether instance. Then when we switch contexts in any way, we clear, recreate, and reapply the Problems.
|
||||
# * Problem alerts will have their own templated ProblemAlertViews
|
||||
# * Problem alerts have their own templated ProblemAlertViews.
|
||||
# * We'll only show the first problem alert, and it will always be at the bottom.
|
||||
# Annotations and problem ranges can show all, I guess.
|
||||
# * The editor will reserve space for one annotation as a codeless area.
|
||||
|
@ -366,6 +357,7 @@ module.exports = class SpellView extends View
|
|||
displayAether: (aether) ->
|
||||
@displayedAether = aether
|
||||
isCast = not _.isEmpty(aether.metrics) or _.some aether.problems.errors, {type: 'runtime'}
|
||||
problem.destroy() for problem in @problems # Just in case another problem was added since clearAetherDisplay() ran.
|
||||
@problems = []
|
||||
annotations = []
|
||||
seenProblemKeys = {}
|
||||
|
@ -393,7 +385,6 @@ module.exports = class SpellView extends View
|
|||
# But the error message display was delayed, so now trying:
|
||||
# - Go after specified delay if a) and not b) or c)
|
||||
guessWhetherFinished: (aether) ->
|
||||
#@recompileValid = not aether.getAllProblems().length
|
||||
valid = not aether.getAllProblems().length
|
||||
cursorPosition = @ace.getCursorPosition()
|
||||
currentLine = _.string.rtrim(@aceDoc.$lines[cursorPosition.row].replace(/[ \t]*\/\/[^"']*/g, '')) # trim // unless inside "
|
||||
|
@ -402,15 +393,22 @@ module.exports = class SpellView extends View
|
|||
#console.log "finished?", valid, endOfLine, beginningOfLine, cursorPosition, currentLine.length, aether, new Date() - 0, currentLine
|
||||
if valid and (endOfLine or beginningOfLine)
|
||||
if @autocastDelay > 60000
|
||||
null #@cast true
|
||||
@preload()
|
||||
else
|
||||
@recompile()
|
||||
#console.log "recompile now!"
|
||||
#else if not valid
|
||||
# # if this works, we can get rid of all @recompileValid logic
|
||||
# console.log "not valid, but so we'll wait to do it in", @autocastDelay + "ms"
|
||||
#else
|
||||
# console.log "valid but not at end of line; recompile in", @autocastDelay + "ms"
|
||||
|
||||
preload: ->
|
||||
# Send this code over to the God for preloading, but don't change the cast state.
|
||||
oldSource = @spell.source
|
||||
oldSpellThangAethers = {}
|
||||
for thangID, spellThang of @spell.thangs
|
||||
oldSpellThangAethers[thangID] = spellThang.aether.serialize() # Get raw, pure, and problems
|
||||
@spell.transpile @getSource()
|
||||
@cast true
|
||||
@spell.source = oldSource
|
||||
for thangID, spellThang of @spell.thangs
|
||||
for key, value of oldSpellThangAethers[thangID]
|
||||
spellThang.aether[key] = value
|
||||
|
||||
onSpellChanged: (e) ->
|
||||
@spellHasChanged = true
|
||||
|
|
|
@ -205,7 +205,7 @@ module.exports = class TomeView extends View
|
|||
|
||||
reloadAllCode: ->
|
||||
spell.view.reloadCode false for spellKey, spell of @spells when spell.team is me.team or (spell.team in ["common", "neutral", null])
|
||||
Backbone.Mediator.publish 'tome:cast-spells', spells: @spells
|
||||
Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: false
|
||||
|
||||
updateLanguageForAllSpells: ->
|
||||
spell.updateLanguageAether() for spellKey, spell of @spells
|
||||
|
|
Loading…
Reference in a new issue