From 4b88296265486668c341f434a69dfd040800ca36 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 22 May 2014 11:47:38 -0700 Subject: [PATCH 1/6] Set up the supermodel to clear out jqxhrs once they're done, since they take a lot of memory. --- app/models/SuperModel.coffee | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index ace9f1629..90118faa5 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -167,10 +167,12 @@ module.exports = class SuperModel extends Backbone.Model return unless @resources[r.rid] @num += r.value _.defer @updateProgress + r.clean() onResourceFailed: (source) -> return unless @resources[r.rid] @trigger('failed', source) + r.clean() updateProgress: => # Because this is _.defer'd, this might end up getting called after @@ -221,6 +223,10 @@ class Resource extends Backbone.Model markLoading: -> @isLoaded = @isFailed = false @isLoading = true + + clean: -> + # request objects get rather large. Clean them up after the request is finished. + @jqxhr = null load: -> @ @@ -243,6 +249,9 @@ class ModelResource extends Resource @listenToOnce @model, 'sync', -> @markLoaded() @listenToOnce @model, 'error', -> @markFailed() + clean: -> + @jqxhr = null + @model.jqxhr = null class RequestResource extends Resource From 4c7d7fce035f6a088e2ae30b41abafb58b6137f7 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 22 May 2014 12:05:30 -0700 Subject: [PATCH 2/6] Fixed some memory leaks. --- app/lib/surface/CocoSprite.coffee | 8 ++++---- app/lib/surface/SpriteBoss.coffee | 1 + app/models/ThangType.coffee | 7 +++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 75c99088f..7d1a63091 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -302,7 +302,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass [@imageObject.x, @imageObject.y] = [sup.x, sup.y] @lastPos = p1.copy?() or _.clone(p1) @hasMoved = true - + updateBaseScale: -> scale = 1 scale = @thangType.get('scale') or 1 if @isRaster @@ -331,9 +331,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @imageObject.scaleY *= @thangType.get('scale') ? 1 [@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height] return - + scaleX = scaleY = 1 - + if @thangType.get('name') in ['Arrow', 'Spear'] # Scales the arrow so it appears longer when flying parallel to horizon. # To do that, we convert angle to [0, 90] (mirroring half-planes twice), then make linear function out of it: @@ -791,4 +791,4 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass p.removeChild @healthBar if p = @healthBar?.parent @imageObject?.off 'animationend', @playNextAction clearInterval @effectInterval if @effectInterval - super() \ No newline at end of file + super() diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee index 617232af7..a0fe3c490 100644 --- a/app/lib/surface/SpriteBoss.coffee +++ b/app/lib/surface/SpriteBoss.coffee @@ -161,6 +161,7 @@ module.exports = class SpriteBoss extends CocoClass thang = sprite.thang delete @sprites[sprite.thang.id] @spriteArray.splice @spriteArray.indexOf(sprite), 1 + @stopListening sprite sprite.destroy() sprite.thang = thang # Keep around so that we know which thang the destroyed thang was for diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index cd3b70c0c..e1ccf1054 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -30,7 +30,7 @@ module.exports = class ThangType extends CocoModel isFullyLoaded: -> # TODO: Come up with a better way to identify when the model doesn't have everything needed to build the sprite. ie when it's a projection without all the required data. return @get('actions') or @get('raster') # needs one of these two things - + getActions: -> return {} unless @isFullyLoaded() return @actions or @buildActions() @@ -62,7 +62,9 @@ module.exports = class ThangType extends CocoModel @options = @fillOptions options key = @spriteSheetKey(@options) if ss = @spriteSheets[key] then return ss - return key if @building[key] + if @building[key] + @options = null + return key @t0 = new Date().getTime() @initBuild(options) @addGeneralFrames() unless @options.portraitOnly @@ -159,6 +161,7 @@ module.exports = class ThangType extends CocoModel @spriteSheets[key] = spriteSheet delete @building[key] @builder = null + @options = null spriteSheet onBuildSpriteSheetComplete: (e, data) -> From 681adc1dcd914409fca78b926ecf0b23d2ed02c9 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 22 May 2014 13:37:08 -0700 Subject: [PATCH 3/6] Got rid of a log. --- app/lib/world/GoalManager.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/app/lib/world/GoalManager.coffee b/app/lib/world/GoalManager.coffee index 82b6b58d2..b729ece4b 100644 --- a/app/lib/world/GoalManager.coffee +++ b/app/lib/world/GoalManager.coffee @@ -110,7 +110,6 @@ module.exports = class GoalManager extends CocoClass goals: @goals overallStatus: overallStatus timedOut: @world.totalFrames is @world.maxTotalFrames - console.log 'timed out', @world.totalFrames is @world.maxTotalFrames, @world.totalFrames, @world.maxTotalFrames, @world.frames.length Backbone.Mediator.publish('goal-manager:new-goal-states', event) checkOverallStatus: (ignoreIncomplete=false) -> From 5a407492dbf969a17929c698c390ce1ac50eea39 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 22 May 2014 13:48:48 -0700 Subject: [PATCH 4/6] Got rid of a bunch of magic numbers in mark scaling. Now assumed all target marks have a width of 10m by default. --- app/lib/surface/CocoSprite.coffee | 5 ----- app/lib/surface/Mark.coffee | 24 ++++++++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 7d1a63091..999b2b325 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -629,11 +629,6 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @addMark 'debug', @options.floatingLayer if debug @marks.debug?.toggle debug - getAverageDimension: -> - bounds = @imageObject.getBounds() - averageDimension = (bounds.height + bounds.width) / 2 - Math.min(80, averageDimension) - addLabel: (name, style) -> @labels[name] ?= new Label sprite: @, camera: @options.camera, layer: @options.textLayer, style: style @labels[name] diff --git a/app/lib/surface/Mark.coffee b/app/lib/surface/Mark.coffee index 486556fa9..d37b57b59 100644 --- a/app/lib/surface/Mark.coffee +++ b/app/lib/surface/Mark.coffee @@ -235,7 +235,7 @@ module.exports = class Mark extends CocoClass if @name is 'debug' or (@name is 'shadow' and @sprite.thang?.shape in ["rectangle", "box"]) @mark.rotation = @sprite.thang.rotation * 180 / Math.PI - updateScale: (log) -> + updateScale: -> if @name is 'bounds' and (@sprite.thang.width isnt @lastWidth or @sprite.thang.height isnt @lastHeight) oldMark = @mark @buildBounds() @@ -247,16 +247,20 @@ module.exports = class Mark extends CocoClass @markSprite.scaleFactor = 1.2 @markSprite.updateScale() return unless @name in ["selection", "target", "repair", "highlight"] + + # scale these marks to 10m (100px). Adjust based on sprite size. + factor = 0.3 # default size: 3m width, most commonly for target when pointing to a location + if @sprite?.imageObject - size = @sprite.getAverageDimension() - size += 60 if @name is 'selection' - size += 60 if @name is 'repair' - size *= @sprite.scaleFactor - scale = size / {selection: 128, target: 128, repair: 320, highlight: 160}[@name] - scale /= 3 - if @sprite?.thang.spriteName.search(/(dungeon|indoor).wall/i) isnt -1 - scale *= 2 - @mark.scaleX = @mark.scaleY = Math.min 1, scale + width = @sprite.imageObject.getBounds()?.width or 0 + width /= @sprite.options.resolutionFactor + # all targets should be set to have a width of 100px, and then be scaled accordingly + factor = width / 100 # normalize + factor *= 1.1 # add margin + factor = Math.max(factor, 0.3) # lower bound + @mark.scaleX *= factor + @mark.scaleY *= factor + if @name in ['selection', 'target', 'repair'] @mark.scaleY *= @camera.y2x # code applies perspective From bd575ad667589f20396168ad81cc047afbb6869e Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 22 May 2014 14:16:39 -0700 Subject: [PATCH 5/6] Fixed the placement of wall sprites in the level editor to wait until the sprite sheet is finished being built. --- app/lib/surface/CocoSprite.coffee | 3 ++- app/lib/surface/SpriteBoss.coffee | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 999b2b325..1278cf826 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -293,7 +293,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass p1.z += bobOffset x: p1.x, y: p1.y, z: if @thang.isLand then 0 else p1.z - @thang.depth / 2 - updatePosition: -> + updatePosition: (log) -> + return if @stillLoading return unless @thang?.pos and @options.camera? wop = @getWorldPosition() [p0, p1] = [@lastPos, @thang.pos] diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee index a0fe3c490..52bd11f45 100644 --- a/app/lib/surface/SpriteBoss.coffee +++ b/app/lib/surface/SpriteBoss.coffee @@ -201,8 +201,7 @@ module.exports = class SpriteBoss extends CocoClass cache: (update=false) -> return if @cached and not update wallSprites = (sprite for sprite in @spriteArray when sprite.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1) - unless _.all (s.thangType.isFullyLoaded() for s in wallSprites) - return + return if _.any (s.stillLoading for s in wallSprites) walls = (sprite.thang for sprite in wallSprites) @world.calculateBounds() wallGrid = new Grid walls, @world.size()... From 26d2816071a59296d247149e6c46f2f66baa6be3 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 22 May 2014 19:05:05 -0700 Subject: [PATCH 6/6] Fixed some more memory leaks. --- app/assets/javascripts/workers/worker_world.js | 3 +++ app/lib/world/thang.coffee | 8 +++++++- app/lib/world/world.coffee | 8 ++++++++ bower.json | 2 +- package.json | 2 +- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/workers/worker_world.js b/app/assets/javascripts/workers/worker_world.js index 3da92969c..9b6264173 100644 --- a/app/assets/javascripts/workers/worker_world.js +++ b/app/assets/javascripts/workers/worker_world.js @@ -315,6 +315,7 @@ self.onDebugWorldProgress = function onDebugWorldProgress(progress) { self.debugAbort = function () { if(self.debugWorld) { self.debugWorld.abort(); + self.debugWorld.destroy(); self.debugWorld = null; } self.postMessage({type: 'debug-abort'}); @@ -379,6 +380,7 @@ self.onWorldLoaded = function onWorldLoaded() { var t3 = new Date(); console.log("And it was so: (" + (diff / self.world.totalFrames).toFixed(3) + "ms per frame,", self.world.totalFrames, "frames)\nSimulation :", diff + "ms \nSerialization:", (t2 - t1) + "ms\nDelivery :", (t3 - t2) + "ms"); self.world.goalManager.destroy(); + self.world.destroy(); self.world = null; }; @@ -410,6 +412,7 @@ self.abort = function abort() { if(self.world) { self.world.abort(); self.world.goalManager.destroy(); + self.world.destroy(); self.world = null; } self.postMessage({type: 'abort'}); diff --git a/app/lib/world/thang.coffee b/app/lib/world/thang.coffee index ffb938387..248f30e60 100644 --- a/app/lib/world/thang.coffee +++ b/app/lib/world/thang.coffee @@ -31,6 +31,12 @@ module.exports = class Thang @addTrackedProperties ['exists', 'boolean'] # TODO: move into Systems/Components, too? #console.log "Generated #{@toString()}." + destroy: -> + # Just trying to destroy __aetherAPIClone, but might as well nuke everything just in case + @[key] = undefined for key of @ + @destroyed = true + @destroy = -> + updateRegistration: -> system.register @ for system in @world.systems @@ -40,7 +46,7 @@ module.exports = class Thang getGoalState: (goalID) -> @world.getGoalState goalID - + setGoalState: (goalID, status) -> @world.setGoalState goalID, status diff --git a/app/lib/world/world.coffee b/app/lib/world/world.coffee index acdc62048..fa075995f 100644 --- a/app/lib/world/world.coffee +++ b/app/lib/world/world.coffee @@ -33,6 +33,13 @@ module.exports = class World @rand = new Rand 0 # Existence System may change this seed @frames = [new WorldFrame(@, 0)] + destroy: -> + @goalManager?.destroy() + thang.destroy() for thang in @thangs + @[key] = undefined for key of @ + @destroyed = true + @destroy = -> + getFrame: (frameIndex) -> # Optimize it a bit--assume we have all if @ended and are at the previous frame otherwise frames = @frames @@ -109,6 +116,7 @@ module.exports = class World console.log(' Loaded', i, 'of', @totalFrames, "(+" + (t2 - @t0).toFixed(0) + "ms)") @t0 = t2 continueFn = => + return if @destroyed if loadUntilFrame @loadFrames(loadedCallback,errorCallback,loadProgressCallback, skipDeferredLoading, loadUntilFrame) else diff --git a/bower.json b/bower.json index 0c8b9ba8a..d56b2d70d 100644 --- a/bower.json +++ b/bower.json @@ -32,7 +32,7 @@ "firepad": "~0.1.2", "marked": "~0.3.0", "moment": "~2.5.0", - "aether": "~0.2.0", + "aether": "~0.2.8", "underscore.string": "~2.3.3", "firebase": "~1.0.2", "catiline": "~2.9.3", diff --git a/package.json b/package.json index 0725978ee..3986248e1 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "redis": "", "webworker-threads": "~0.4.11", "node-gyp": "~0.13.0", - "aether": "~0.2.3", + "aether": "~0.2.8", "JASON": "~0.1.3", "JQDeferred": "~2.1.0" },