Fixed a ton of memory leaks, but not all of them.
This commit is contained in:
parent
b696a3f188
commit
6af2d34f59
10 changed files with 56 additions and 17 deletions
|
@ -20,7 +20,7 @@ elementAcceptsKeystrokes = (el) ->
|
||||||
# not radio, checkbox, range, or color
|
# not radio, checkbox, range, or color
|
||||||
return (tag is 'textarea' or (tag is 'input' and type in textInputTypes) or el.contentEditable in ["", "true"]) and not (el.readOnly or el.disabled)
|
return (tag is 'textarea' or (tag is 'input' and type in textInputTypes) or el.contentEditable in ["", "true"]) and not (el.readOnly or el.disabled)
|
||||||
|
|
||||||
COMMON_FILES = ['/images/modal_background.png', '/images/level/code_palette_background.png']
|
COMMON_FILES = ['/images/pages/base/modal_background.png', '/images/level/code_palette_background.png']
|
||||||
preload = (arrayOfImages) ->
|
preload = (arrayOfImages) ->
|
||||||
$(arrayOfImages).each ->
|
$(arrayOfImages).each ->
|
||||||
$('<img/>')[0].src = @
|
$('<img/>')[0].src = @
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
## Uncomment to imitate IE9 (and in world_utils.coffee)
|
## Uncomment to imitate IE9 (and in world_utils.coffee)
|
||||||
#window.Worker = null
|
#window.Worker = null
|
||||||
#window.Float32Array = null
|
#window.Float32Array = null
|
||||||
# (except that we won't have included vendor_with_box2d.js, so Collision won't run, things won't move, etc.)
|
# Also uncomment vendor_with_box2d.js in index.html if you want Collision to run and things to move.
|
||||||
|
|
||||||
module.exports = class God
|
module.exports = class God
|
||||||
@ids: ['Athena', 'Baldr', 'Crom', 'Dagr', 'Eris', 'Freyja', 'Great Gish', 'Hades', 'Ishtar', 'Janus', 'Khronos', 'Loki', 'Marduk', 'Negafook', 'Odin', 'Poseidon', 'Quetzalcoatl', 'Ra', 'Shiva', 'Thor', 'Umvelinqangi', 'Týr', 'Vishnu', 'Wepwawet', 'Xipe Totec', 'Yahweh', 'Zeus', '上帝', 'Tiamat', '盘古', 'Phoebe', 'Artemis', 'Osiris', "嫦娥", 'Anhur', 'Teshub', 'Enlil', 'Perkele', 'Aether', 'Chaos', 'Hera', 'Iris', 'Theia', 'Uranus', 'Stribog', 'Sabazios', 'Izanagi', 'Ao', 'Tāwhirimātea', 'Tengri', 'Inmar', 'Torngarsuk', 'Centzonhuitznahua', 'Hunab Ku', 'Apollo', 'Helios', 'Thoth', 'Hyperion', 'Alectrona', 'Eos', 'Mitra', 'Saranyu', 'Freyr', 'Koyash', 'Atropos', 'Clotho', 'Lachesis', 'Tyche', 'Skuld', 'Urðr', 'Verðandi', 'Camaxtli', 'Huhetotl', 'Set', 'Anu', 'Allah', 'Anshar', 'Hermes', 'Lugh', 'Brigit', 'Manannan Mac Lir', 'Persephone', 'Mercury', 'Venus', 'Mars', 'Azrael', 'He-Man', 'Anansi', 'Issek', 'Mog', 'Kos', 'Amaterasu Omikami', 'Raijin', 'Susanowo', 'Blind Io', 'The Lady', 'Offler', 'Ptah', 'Anubis', 'Ereshkigal', 'Nergal', 'Thanatos', 'Macaria', 'Angelos', 'Erebus', 'Hecate', 'Hel', 'Orcus', 'Ishtar-Deela Nakh', 'Prometheus', 'Hephaestos', 'Sekhmet', 'Ares', 'Enyo', 'Otrera', 'Pele', 'Hadúr', 'Hachiman', 'Dayisun Tngri', 'Ullr', 'Lua', 'Minerva']
|
@ids: ['Athena', 'Baldr', 'Crom', 'Dagr', 'Eris', 'Freyja', 'Great Gish', 'Hades', 'Ishtar', 'Janus', 'Khronos', 'Loki', 'Marduk', 'Negafook', 'Odin', 'Poseidon', 'Quetzalcoatl', 'Ra', 'Shiva', 'Thor', 'Umvelinqangi', 'Týr', 'Vishnu', 'Wepwawet', 'Xipe Totec', 'Yahweh', 'Zeus', '上帝', 'Tiamat', '盘古', 'Phoebe', 'Artemis', 'Osiris', "嫦娥", 'Anhur', 'Teshub', 'Enlil', 'Perkele', 'Aether', 'Chaos', 'Hera', 'Iris', 'Theia', 'Uranus', 'Stribog', 'Sabazios', 'Izanagi', 'Ao', 'Tāwhirimātea', 'Tengri', 'Inmar', 'Torngarsuk', 'Centzonhuitznahua', 'Hunab Ku', 'Apollo', 'Helios', 'Thoth', 'Hyperion', 'Alectrona', 'Eos', 'Mitra', 'Saranyu', 'Freyr', 'Koyash', 'Atropos', 'Clotho', 'Lachesis', 'Tyche', 'Skuld', 'Urðr', 'Verðandi', 'Camaxtli', 'Huhetotl', 'Set', 'Anu', 'Allah', 'Anshar', 'Hermes', 'Lugh', 'Brigit', 'Manannan Mac Lir', 'Persephone', 'Mercury', 'Venus', 'Mars', 'Azrael', 'He-Man', 'Anansi', 'Issek', 'Mog', 'Kos', 'Amaterasu Omikami', 'Raijin', 'Susanowo', 'Blind Io', 'The Lady', 'Offler', 'Ptah', 'Anubis', 'Ereshkigal', 'Nergal', 'Thanatos', 'Macaria', 'Angelos', 'Erebus', 'Hecate', 'Hel', 'Orcus', 'Ishtar-Deela Nakh', 'Prometheus', 'Hephaestos', 'Sekhmet', 'Ares', 'Enyo', 'Otrera', 'Pele', 'Hadúr', 'Hachiman', 'Dayisun Tngri', 'Ullr', 'Lua', 'Minerva']
|
||||||
|
@ -25,17 +25,22 @@ module.exports = class God
|
||||||
@createWorld()
|
@createWorld()
|
||||||
|
|
||||||
getAngel: ->
|
getAngel: ->
|
||||||
|
freeAngel = null
|
||||||
for angel in @angels
|
for angel in @angels
|
||||||
return angel.enslave() unless angel.busy
|
if angel.busy
|
||||||
|
angel.abort()
|
||||||
|
else
|
||||||
|
freeAngel ?= angel
|
||||||
|
return freeAngel.enslave() if freeAngel
|
||||||
maxedOut = @angels.length is @maxAngels
|
maxedOut = @angels.length is @maxAngels
|
||||||
if not maxedOut
|
if not maxedOut
|
||||||
angel = new Angel @
|
angel = new Angel @
|
||||||
@angels.push angel
|
@angels.push angel
|
||||||
return angel.enslave()
|
return angel.enslave()
|
||||||
oldestAngel = {started: new Date(2099, 1, 1)}
|
#oldestAngel = {started: new Date(2099, 1, 1)}
|
||||||
for angel in @angels
|
#for angel in @angels
|
||||||
oldestAngel = angel if angel.started < oldestAngel.started
|
# oldestAngel = angel if angel.started < oldestAngel.started
|
||||||
oldestAngel.abort()
|
#oldestAngel.abort()
|
||||||
null
|
null
|
||||||
|
|
||||||
angelInfinitelyLooped: (angel) ->
|
angelInfinitelyLooped: (angel) ->
|
||||||
|
@ -65,6 +70,13 @@ module.exports = class God
|
||||||
else
|
else
|
||||||
@worldWaiting = true
|
@worldWaiting = true
|
||||||
return
|
return
|
||||||
|
console.log "about to post message", @getUserCodeMap(), @level, @firstWorld, @goalManager?.getGoals(), JSON.stringify({
|
||||||
|
worldName: @world.name
|
||||||
|
userCodeMap: @getUserCodeMap()
|
||||||
|
level: @level
|
||||||
|
firstWorld: @firstWorld
|
||||||
|
goals: @goalManager?.getGoals()
|
||||||
|
}).length
|
||||||
angel.worker.postMessage {func: 'runWorld', args: {
|
angel.worker.postMessage {func: 'runWorld', args: {
|
||||||
worldName: @world.name
|
worldName: @world.name
|
||||||
userCodeMap: @getUserCodeMap()
|
userCodeMap: @getUserCodeMap()
|
||||||
|
@ -91,8 +103,11 @@ module.exports = class God
|
||||||
Backbone.Mediator.publish('god:new-world-created', world: @world, firstWorld: @firstWorld, errorCount: errorCount, goalStates: @latestGoalStates)
|
Backbone.Mediator.publish('god:new-world-created', world: @world, firstWorld: @firstWorld, errorCount: errorCount, goalStates: @latestGoalStates)
|
||||||
for scriptNote in @world.scriptNotes
|
for scriptNote in @world.scriptNotes
|
||||||
Backbone.Mediator.publish scriptNote.channel, scriptNote.event
|
Backbone.Mediator.publish scriptNote.channel, scriptNote.event
|
||||||
|
@goalManager?.world = newWorld
|
||||||
@firstWorld = false
|
@firstWorld = false
|
||||||
@testWorld = null
|
@testWorld = null
|
||||||
|
unless _.find @angels, 'busy'
|
||||||
|
@spells = null # Don't hold onto old spells; memory leaks
|
||||||
|
|
||||||
getUserCodeMap: ->
|
getUserCodeMap: ->
|
||||||
userCodeMap = {}
|
userCodeMap = {}
|
||||||
|
@ -171,6 +186,7 @@ class Angel
|
||||||
@busy = true
|
@busy = true
|
||||||
@started = new Date()
|
@started = new Date()
|
||||||
@purgatoryTimer = setInterval @testWorker, @infiniteLoopIntervalDuration
|
@purgatoryTimer = setInterval @testWorker, @infiniteLoopIntervalDuration
|
||||||
|
@spawnWorker() unless @worker
|
||||||
@
|
@
|
||||||
|
|
||||||
free: ->
|
free: ->
|
||||||
|
@ -178,6 +194,8 @@ class Angel
|
||||||
@started = null
|
@started = null
|
||||||
clearInterval @purgatoryTimer
|
clearInterval @purgatoryTimer
|
||||||
@purgatoryTimer = null
|
@purgatoryTimer = null
|
||||||
|
@worker.terminate()
|
||||||
|
@worker = null
|
||||||
@
|
@
|
||||||
|
|
||||||
abort: ->
|
abort: ->
|
||||||
|
@ -186,8 +204,8 @@ class Angel
|
||||||
|
|
||||||
terminate: =>
|
terminate: =>
|
||||||
@worker.terminate()
|
@worker.terminate()
|
||||||
|
@worker = null
|
||||||
return if @dead
|
return if @dead
|
||||||
@spawnWorker()
|
|
||||||
@free()
|
@free()
|
||||||
@god.angelAborted @
|
@god.angelAborted @
|
||||||
|
|
||||||
|
@ -219,7 +237,9 @@ class Angel
|
||||||
clearTimeout @abortTimeout
|
clearTimeout @abortTimeout
|
||||||
@free()
|
@free()
|
||||||
@god.angelAborted @
|
@god.angelAborted @
|
||||||
@worker.terminate() if @god.dead
|
if @god.dead
|
||||||
|
@worker.terminate()
|
||||||
|
@worker = null
|
||||||
when 'reportIn'
|
when 'reportIn'
|
||||||
clearTimeout @condemnTimeout
|
clearTimeout @condemnTimeout
|
||||||
else
|
else
|
||||||
|
|
|
@ -116,7 +116,8 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
# World init
|
# World init
|
||||||
|
|
||||||
initWorld: ->
|
initWorld: ->
|
||||||
return if @world
|
return if @initialized
|
||||||
|
@initialized = true
|
||||||
@world = new World @level.get('name')
|
@world = new World @level.get('name')
|
||||||
serializedLevel = @level.serialize(@supermodel)
|
serializedLevel = @level.serialize(@supermodel)
|
||||||
@world.loadFromLevel serializedLevel, false
|
@world.loadFromLevel serializedLevel, false
|
||||||
|
@ -203,9 +204,9 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
notifyProgress: ->
|
notifyProgress: ->
|
||||||
Backbone.Mediator.publish 'level-loader:progress-changed', progress: @progress()
|
Backbone.Mediator.publish 'level-loader:progress-changed', progress: @progress()
|
||||||
@initWorld() if @allDone()
|
@initWorld() if @allDone()
|
||||||
# @trigger 'ready-to-init-world' if @allDone()
|
|
||||||
@trigger 'loaded-all' if @progress() is 1
|
@trigger 'loaded-all' if @progress() is 1
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
|
@world = null # don't hold onto garbage
|
||||||
@supermodel.off 'loaded-one', @onSupermodelLoadedOne
|
@supermodel.off 'loaded-one', @onSupermodelLoadedOne
|
||||||
super()
|
super()
|
||||||
|
|
|
@ -19,6 +19,7 @@ module.exports = class SpriteBoss extends CocoClass
|
||||||
'level-suppress-selection-sounds': 'onSuppressSelectionSounds'
|
'level-suppress-selection-sounds': 'onSuppressSelectionSounds'
|
||||||
'level-lock-select': 'onSetLockSelect'
|
'level-lock-select': 'onSetLockSelect'
|
||||||
'level:restarted': 'onLevelRestarted'
|
'level:restarted': 'onLevelRestarted'
|
||||||
|
'god:new-world-created': 'onNewWorld'
|
||||||
|
|
||||||
constructor: (@options) ->
|
constructor: (@options) ->
|
||||||
super()
|
super()
|
||||||
|
@ -87,7 +88,7 @@ module.exports = class SpriteBoss extends CocoClass
|
||||||
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: @thangTypeFor("Selection")
|
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: @thangTypeFor("Selection")
|
||||||
|
|
||||||
createSpriteOptions: (options) ->
|
createSpriteOptions: (options) ->
|
||||||
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible, world: @world
|
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible
|
||||||
|
|
||||||
createIndieSprites: (indieSprites, withWizards) ->
|
createIndieSprites: (indieSprites, withWizards) ->
|
||||||
unless @indieSprites
|
unless @indieSprites
|
||||||
|
@ -195,6 +196,9 @@ module.exports = class SpriteBoss extends CocoClass
|
||||||
|
|
||||||
spriteFor: (thangID) -> @sprites[thangID]
|
spriteFor: (thangID) -> @sprites[thangID]
|
||||||
|
|
||||||
|
onNewWorld: (e) ->
|
||||||
|
@world = @options.world = e.world
|
||||||
|
|
||||||
# Selection
|
# Selection
|
||||||
|
|
||||||
onSuppressSelectionSounds: (e) -> @suppressSelectionSounds = e.suppress
|
onSuppressSelectionSounds: (e) -> @suppressSelectionSounds = e.suppress
|
||||||
|
|
|
@ -90,6 +90,7 @@ module.exports = class GoalManager extends CocoClass
|
||||||
# passes the word along
|
# passes the word along
|
||||||
onNewWorldCreated: (e) =>
|
onNewWorldCreated: (e) =>
|
||||||
@updateGoalStates(e.goalStates) if e.goalStates?
|
@updateGoalStates(e.goalStates) if e.goalStates?
|
||||||
|
@world = e.world
|
||||||
|
|
||||||
updateGoalStates: (newGoalStates) ->
|
updateGoalStates: (newGoalStates) ->
|
||||||
for goalID, goalState of newGoalStates
|
for goalID, goalState of newGoalStates
|
||||||
|
|
|
@ -78,7 +78,7 @@ module.exports = class World
|
||||||
(@runtimeErrors ?= []).push error
|
(@runtimeErrors ?= []).push error
|
||||||
(@unhandledRuntimeErrors ?= []).push error
|
(@unhandledRuntimeErrors ?= []).push error
|
||||||
|
|
||||||
loadFrames: (loadedCallback, errorCallback, loadProgressCallback) =>
|
loadFrames: (loadedCallback, errorCallback, loadProgressCallback) ->
|
||||||
return if @aborted
|
return if @aborted
|
||||||
unless @thangs.length
|
unless @thangs.length
|
||||||
console.log "Warning: loadFrames called on empty World (no thangs)."
|
console.log "Warning: loadFrames called on empty World (no thangs)."
|
||||||
|
|
|
@ -28,7 +28,10 @@ module.exports = class Spell
|
||||||
@tabView.render()
|
@tabView.render()
|
||||||
|
|
||||||
addThang: (thang) ->
|
addThang: (thang) ->
|
||||||
@thangs[thang.id] ?= {thang: thang, aether: @createAether(thang), castAether: null}
|
if @thangs[thang.id]
|
||||||
|
@thangs[thang.id].thang = thang
|
||||||
|
else
|
||||||
|
@thangs[thang.id] = {thang: thang, aether: @createAether(thang), castAether: null}
|
||||||
|
|
||||||
removeThangID: (thangID) ->
|
removeThangID: (thangID) ->
|
||||||
delete @thangs[thangID]
|
delete @thangs[thangID]
|
||||||
|
|
|
@ -134,3 +134,7 @@ module.exports = class ThangListEntryView extends View
|
||||||
return unless currentThang = e.world.thangMap[@thang.id]
|
return unless currentThang = e.world.thangMap[@thang.id]
|
||||||
@$el.toggle Boolean(currentThang.exists)
|
@$el.toggle Boolean(currentThang.exists)
|
||||||
@$el.toggleClass 'dead', currentThang.health <= 0 if currentThang.exists
|
@$el.toggleClass 'dead', currentThang.health <= 0 if currentThang.exists
|
||||||
|
|
||||||
|
destroy: ->
|
||||||
|
super()
|
||||||
|
@avatar.destroy()
|
||||||
|
|
|
@ -65,6 +65,7 @@ module.exports = class TomeView extends View
|
||||||
else
|
else
|
||||||
@cast()
|
@cast()
|
||||||
console.warn "Warning: There are no Programmable Thangs in this level, which makes it unplayable."
|
console.warn "Warning: There are no Programmable Thangs in this level, which makes it unplayable."
|
||||||
|
delete @options.thangs
|
||||||
|
|
||||||
onNewWorld: (e) ->
|
onNewWorld: (e) ->
|
||||||
thangs = _.filter e.world.thangs, 'isSelectable'
|
thangs = _.filter e.world.thangs, 'isSelectable'
|
||||||
|
@ -95,7 +96,7 @@ module.exports = class TomeView extends View
|
||||||
@spells[spellKey].addThang thang for spellKey in spellKeys
|
@spells[spellKey].addThang thang for spellKey in spellKeys
|
||||||
else
|
else
|
||||||
delete @thangSpells[thangID]
|
delete @thangSpells[thangID]
|
||||||
@spells[spellKey].removeThangID thangID for spellKey in spellKeys
|
spell.removeThangID thangID for spell in @spells
|
||||||
null
|
null
|
||||||
|
|
||||||
onSpellLoaded: (e) ->
|
onSpellLoaded: (e) ->
|
||||||
|
|
|
@ -50,6 +50,7 @@ module.exports = class PlayLevelView extends View
|
||||||
'level-focus-dom': 'onFocusDom'
|
'level-focus-dom': 'onFocusDom'
|
||||||
'level-disable-controls': 'onDisableControls'
|
'level-disable-controls': 'onDisableControls'
|
||||||
'level-enable-controls': 'onEnableControls'
|
'level-enable-controls': 'onEnableControls'
|
||||||
|
'god:new-world-created': 'onNewWorld'
|
||||||
'god:infinite-loop': 'onInfiniteLoop'
|
'god:infinite-loop': 'onInfiniteLoop'
|
||||||
'bus:connected': 'onBusConnected'
|
'bus:connected': 'onBusConnected'
|
||||||
'level-reload-from-data': 'onLevelReloadFromData'
|
'level-reload-from-data': 'onLevelReloadFromData'
|
||||||
|
@ -102,7 +103,6 @@ module.exports = class PlayLevelView extends View
|
||||||
|
|
||||||
load: ->
|
load: ->
|
||||||
@levelLoader = new LevelLoader(@levelID, @supermodel, @sessionID)
|
@levelLoader = new LevelLoader(@levelID, @supermodel, @sessionID)
|
||||||
@levelLoader.once 'ready-to-init-world', @onReadyToInitWorld
|
|
||||||
@levelLoader.once 'loaded-all', @onLevelLoaderLoaded
|
@levelLoader.once 'loaded-all', @onLevelLoaderLoaded
|
||||||
|
|
||||||
getRenderData: ->
|
getRenderData: ->
|
||||||
|
@ -120,6 +120,8 @@ module.exports = class PlayLevelView extends View
|
||||||
@session = @levelLoader.session
|
@session = @levelLoader.session
|
||||||
@level = @levelLoader.level
|
@level = @levelLoader.level
|
||||||
@world = @levelLoader.world
|
@world = @levelLoader.world
|
||||||
|
@levelLoader.destroy()
|
||||||
|
@levelLoader = null
|
||||||
@loadingScreen.destroy()
|
@loadingScreen.destroy()
|
||||||
@setTeam @world.teamForPlayer 1 # We don't know which player we are; this will go away--temp TODO
|
@setTeam @world.teamForPlayer 1 # We don't know which player we are; this will go away--temp TODO
|
||||||
@initSurface()
|
@initSurface()
|
||||||
|
@ -206,6 +208,9 @@ module.exports = class PlayLevelView extends View
|
||||||
$('#level-done-button', @$el).hide()
|
$('#level-done-button', @$el).hide()
|
||||||
window.tracker?.trackEvent 'Confirmed Restart', level: @world.name, label: @world.name
|
window.tracker?.trackEvent 'Confirmed Restart', level: @world.name, label: @world.name
|
||||||
|
|
||||||
|
onNewWorld: (e) ->
|
||||||
|
@world = e.world
|
||||||
|
|
||||||
onInfiniteLoop: (e) ->
|
onInfiniteLoop: (e) ->
|
||||||
return unless e.firstWorld
|
return unless e.firstWorld
|
||||||
@openModalView new InfiniteLoopModal()
|
@openModalView new InfiniteLoopModal()
|
||||||
|
@ -378,7 +383,7 @@ module.exports = class PlayLevelView extends View
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
super()
|
super()
|
||||||
@levelLoader.destroy()
|
@levelLoader?.destroy()
|
||||||
@surface?.destroy()
|
@surface?.destroy()
|
||||||
@god?.destroy()
|
@god?.destroy()
|
||||||
@goalManager?.destroy()
|
@goalManager?.destroy()
|
||||||
|
|
Reference in a new issue