From 6f0ed9040a4b463b722694e325eb7390a2e854b5 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 17 Feb 2014 17:38:49 -0800 Subject: [PATCH] Fixes for dirt path, fast portraits, and slow transpilation. Starting to move Aether stuff into a Tome worker with Catiline. --- .../workers/catiline_worker_shim.js | 2 + .../javascripts/workers/worker_world.coffee | 140 ------------------ app/lib/LevelLoader.coffee | 5 +- app/lib/surface/CocoSprite.coffee | 2 +- app/lib/surface/SpriteBoss.coffee | 4 +- app/models/ThangType.coffee | 3 +- app/views/play/level/tome/spell.coffee | 20 ++- app/views/play/level/tome/tome_view.coffee | 24 ++- bower.json | 3 +- config.coffee | 4 +- 10 files changed, 51 insertions(+), 156 deletions(-) create mode 100644 app/assets/javascripts/workers/catiline_worker_shim.js delete mode 100644 app/assets/javascripts/workers/worker_world.coffee diff --git a/app/assets/javascripts/workers/catiline_worker_shim.js b/app/assets/javascripts/workers/catiline_worker_shim.js new file mode 100644 index 000000000..38fb64d2a --- /dev/null +++ b/app/assets/javascripts/workers/catiline_worker_shim.js @@ -0,0 +1,2 @@ +self._noTransferable = true; +self.onmessage = function(e) { eval(e.data); }; diff --git a/app/assets/javascripts/workers/worker_world.coffee b/app/assets/javascripts/workers/worker_world.coffee deleted file mode 100644 index a963cef7a..000000000 --- a/app/assets/javascripts/workers/worker_world.coffee +++ /dev/null @@ -1,140 +0,0 @@ - -throw "Attempt to load worker_world into main window instead of web worker." if not self.importScripts or not window? - -self.window = self -self.workerID = "Worker" - -self.logLimit = 200 -self.logsLogged = 0 -console = - log: -> - self.logsLogged += 1 - if self.logsLogged is self.logLimit - self.postMessage - type: 'console-log' - args: ["Log limit " + self.logLimit + " reached; shutting up."] - id: self.workerID - else if self.logsLogged < self.logLimit - args = [].slice.call arguments - for arg in args - if arg and arg.constructor and (arg.constructor.className is "Thang" or arg.isComponent) - arg = arg.toString() - - try - self.postMessage - type: 'console-log' - args: args - id: self.workerID - - catch error - self.postMessage - type: 'console-log' - args: ["Could not post log: " + args, error.toString(), error.stack, error.stackTrace] - id: self.workerID - - -console.error = console.info = console.log -self.console = console - -importScripts '/javascripts/world.js' - - -self.transferableSupported = transferableSupported = -> - try - ab = new ArrayBuffer 1 - worker.postMessage ab, [ab] - return ab.byteLength is 0 - catch error - return false - return false - - - World = self.require 'lib/world/world' - GoalManager = self.require 'lib/world/GoalManager' - - self.runWorld = runWorld = (args) -> - self.postedErrors = {} - self.t0 = new Date() - self.firstWorld = args.firstWorld - self.postedErrors = false - self.logsLogged = 0 - - try - self.world = new World args.worldName, args.userCodeMap - if args.level - self.world.loadFromLevel args.level, true - self.goalManager = new GoalManager self.world - self.goalManager.setGoals args.level?.goals or args.goals - self.goalManager.setCode args.userCodeMap - self.goalManager.worldGenerationWillBegin() - self.world.setGoalManager self.goalManager - catch error - self.onWorldError error - return - Math.random = self.world.rand.randf - self.world.loadFrames self.onWorldLoaded, self.onWorldError, self.onWorldLoadProgress - -self.onWorldLoaded = onWorldLoaded = () -> - self.goalManager.worldGenerationEnded() - t1 = new Date() - diff = t1 - self.t0 - transferableSupported = self.transferableSupported() - try - serialized = self.world.serialize() - catch error - console.log "World serialization error:", error.toString() + "\n" + error.stack or error.stackTrace - - - t2 = new Date() - - try - if transferableSupported - self.postMessage( - type: 'new-world' - serialized: serialized.serializedWorld - goalStates: self.goalManager.getGoalStates() - , serialized.transferableObjects) - else - self.postMessage - type: 'new-world' - serialized: serialized.serializedWorld - goalStates: self.goalManager.getGoalStates() - catch error - console.log "World delivery error:", error.toString + "\n" + error.stack or error.stackTrace - t3 = new Date() - console.log "And it was so: (" + (diff / self.world.totalFrames).toFixed(3) + "ms per frame,", self.world.totalFrames, "frames)\nSimulation :" - self.world = null - -self.onWorldError = onWorldError = (error) -> - if error instanceof Aether.problems.UserCodeProblem - unless self.postedErrors[error.key] - problem = error.serialize() - self.postMessage - type: 'user-code-problem' - problem: problem - self.postedErrors[error.key] = problem - - else - console.log "Non-UserCodeError:", error.toString() + "\n" + error.stack or error.stackTrace - return true - -self.onWorldLoadProgress = onWorldLoadProgress = (progress) -> - self.postMessage - type: 'world-load-progress-changed' - progress: progress - -self.abort = abort = -> - if self.world and self.world.name - console.log "About to abort:", self.world.name, typeof self.world.abort - self.world?.abort() - self.world = null - - self.postMessage - type: 'abort' - -self.reportIn = reportIn = -> - self.postMessage - type: 'reportIn' - -self.addEventListener 'message', (event) -> - self[event.data.func](event.data.args) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index aba6fae47..e655aa7b1 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -146,19 +146,22 @@ module.exports = class LevelLoader extends CocoClass colorConfigs = @world.getTeamColors() thangsProduced = {} - baseOptions = {resolutionFactor: 4, async: true} for thang in @world.thangs continue unless thang.spriteName thangType = thangTypes[thang.spriteName] options = thang.getSpriteOptions(colorConfigs) options.async = true + if thangType.get('kind') is 'Floor' + options.resolutionFactor = 2 thangsProduced[thang.spriteName] = true @buildSpriteSheet(thangType, options) for thangName, thangType of thangTypes continue if thangsProduced[thangName] thangType.spriteOptions = {resolutionFactor: 4, async: true} + if thangType.get('kind') is 'Floor' + thangType.spriteOptions.resolutionFactor = 2 @buildSpriteSheet(thangType, thangType.spriteOptions) buildSpriteSheet: (thangType, options) -> diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 6246619db..1232ffe48 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -87,7 +87,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass toString: -> "" buildSpriteSheet: -> - options = @thang?.getSpriteOptions?() or @options + options = _.extend @options, @thang?.getSpriteOptions?() ? {} options.colorConfig = @options.colorConfig if @options.colorConfig options.async = false @thangType.getSpriteSheet options diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee index cf242ffcc..e0aa01341 100644 --- a/app/lib/surface/SpriteBoss.coffee +++ b/app/lib/surface/SpriteBoss.coffee @@ -137,7 +137,9 @@ module.exports = class SpriteBoss extends CocoClass addThangToSprites: (thang, layer=null) -> return console.warn 'Tried to add Thang to the surface it already has:', thang.id if @sprites[thang.id] thangType = _.find @options.thangTypes, (m) -> m.get('name') is thang.spriteName - sprite = new CocoSprite thangType, @createSpriteOptions thang: thang + options = @createSpriteOptions thang: thang + options.resolutionFactor = if thangType.get('kind') is 'Floor' then 2 else 4 + sprite = new CocoSprite thangType, options @addSprite sprite, null, layer sprite.setDebug @debug sprite diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index d538ba4dd..544052316 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -180,13 +180,14 @@ module.exports = class ThangType extends CocoModel pt = @actions.portrait?.positions?.registration sprite.regX = pt?.x or 0 sprite.regY = pt?.y or 0 + sprite.framerate = @actions.portrait?.framerate ? 20 sprite.gotoAndStop 'portrait' stage.addChild(sprite) stage.update() stage.startTalking = -> sprite.gotoAndPlay 'portrait' return if @tick - @tick = => @update() + @tick = (e) => @update(e) createjs.Ticker.addEventListener 'tick', @tick stage.stopTalking = -> sprite.gotoAndStop 'portrait' diff --git a/app/views/play/level/tome/spell.coffee b/app/views/play/level/tome/spell.coffee index cee5cf382..12c3f7e41 100644 --- a/app/views/play/level/tome/spell.coffee +++ b/app/views/play/level/tome/spell.coffee @@ -14,6 +14,7 @@ module.exports = class Spell @supermodel = options.supermodel @skipFlow = options.skipFlow @skipProtectAPI = options.skipProtectAPI + @worker = options.worker p = options.programmableMethod @name = p.name @@ -29,12 +30,12 @@ module.exports = class Spell @team = @permissions.readwrite[0] ? "common" Backbone.Mediator.publish 'tome:spell-created', spell: @ - + destroy: -> @view.destroy() @tabView.destroy() @thangs = null - + @worker = null addThang: (thang) -> if @thangs[thang.id] @@ -59,10 +60,17 @@ module.exports = class Spell @source = source else source = @getSource() - spellThang.aether.transpile source for thangID, spellThang of @thangs - #for thangID, spellThang of @thangs - # console.log "aether transpiled", source, "to", spellThang.aether.pure - # break + [pure, problems] = [null, null] + for thangID, spellThang of @thangs + unless pure + pure = spellThang.aether.transpile source + problems = spellThang.aether.problems + #console.log "aether transpiled", source.length, "to", pure.length, "for", thangID, @spellKey + else + spellThang.aether.pure = pure + spellThang.aether.problems = problems + #console.log "aether reused transpilation for", thangID, @spellKey + null hasChanged: (newSource=null, currentSource=null) -> (newSource ? @originalSource) isnt (currentSource ? @source) diff --git a/app/views/play/level/tome/tome_view.coffee b/app/views/play/level/tome/tome_view.coffee index 621481d31..08b5ba470 100644 --- a/app/views/play/level/tome/tome_view.coffee +++ b/app/views/play/level/tome/tome_view.coffee @@ -36,6 +36,8 @@ ThangListView = require './thang_list_view' SpellPaletteView = require './spell_palette_view' CastButtonView = require './cast_button_view' +window.SHIM_WORKER_PATH = '/javascripts/workers/catiline_worker_shim.coffee' + module.exports = class TomeView extends View id: 'tome-view' template: template @@ -55,8 +57,8 @@ module.exports = class TomeView extends View afterRender: -> super() + @worker = @createWorker() programmableThangs = _.filter @options.thangs, 'isProgrammable' - if programmableThangs.length @createSpells programmableThangs, programmableThangs[0].world # Do before spellList, thangList, and castButton @spellList = @insertSubView new SpellListView spells: @spells, supermodel: @supermodel @@ -75,6 +77,21 @@ module.exports = class TomeView extends View @thangList.adjustThangs @spells, thangs @spellList.adjustSpells @spells + createWorker: -> + return + # In progress + worker = cw + initialize: (scope) -> + importScripts '/javascripts/lodash.min.js' + importScripts '/javascripts/aether.js' + console.log 'Tome worker initialized.' + doIt: (data, callback, scope) -> + console.log 'doing', what + a = new Aether() + callback 'good' + undefined + worker + generateTeamSpellMap: (spellObject) -> teamSpellMap = {} for spellName, spell of spellObject @@ -89,7 +106,6 @@ module.exports = class TomeView extends View return teamSpellMap - createSpells: (programmableThangs, world) -> pathPrefixComponents = ['play', 'level', @options.levelID, @options.session.id, 'code'] @spells ?= {} @@ -107,7 +123,7 @@ module.exports = class TomeView extends View unless method.cloneOf skipProtectAPI = true #@getQueryVariable("skip_protect_api") is "true" skipFlow = @getQueryVariable("skip_flow") is "true" or @options.levelID is 'project-dota' - spell = @spells[spellKey] = new Spell programmableMethod: method, spellKey: spellKey, pathComponents: pathPrefixComponents.concat(pathComponents), session: @options.session, supermodel: @supermodel, skipFlow: skipFlow, skipProtectAPI: skipProtectAPI + spell = @spells[spellKey] = new Spell programmableMethod: method, spellKey: spellKey, pathComponents: pathPrefixComponents.concat(pathComponents), session: @options.session, supermodel: @supermodel, skipFlow: skipFlow, skipProtectAPI: skipProtectAPI, worker: @worker for thangID, spellKeys of @thangSpells thang = world.getThangByID thangID if thang @@ -188,4 +204,6 @@ module.exports = class TomeView extends View destroy: -> spell.destroy() for spellKey, spell of @spells + @worker?._close() + @worker = null super() diff --git a/bower.json b/bower.json index be482292f..590d0e3ee 100644 --- a/bower.json +++ b/bower.json @@ -34,7 +34,8 @@ "moment": "~2.5.0", "aether": "~0.1.2", "underscore.string": "~2.3.3", - "firebase": "~1.0.2" + "firebase": "~1.0.2", + "catiline": "~2.9.3" }, "overrides": { "backbone": { diff --git a/config.coffee b/config.coffee index 3c4bb76d9..ad0b737c0 100644 --- a/config.coffee +++ b/config.coffee @@ -26,11 +26,11 @@ exports.config = )/// 'javascripts/app.js': /^app/ 'javascripts/vendor.js': ///^( - vendor[\/\\](?!(scripts[\/\\]Box2d|scripts[\/\\]box2d)) + vendor[\/\\](?!scripts[\/\\]Box2d) |bower_components )/// 'javascripts/vendor_with_box2d.js': ///^( - vendor[\/\\](?!scripts[\/\\]box2d) + vendor[\/\\] |bower_components # include box2dweb for profiling (and for IE9...) )/// 'test/javascripts/test.js': /^test[\/\\](?!vendor)/