2014-05-05 20:37:14 -04:00
# function to use inside a webworker.
# This function needs to run inside an environment that has a 'self'.
# This specific worker is targeted towards the node.js headless_client environment.
JASON = require ' jason '
fs = require ' fs '
2014-05-10 21:52:55 -04:00
GLOBAL.Aether = Aether = require ' aether '
GLOBAL._ = _ = require ' lodash '
2014-05-05 20:37:14 -04:00
betterConsole = () ->
self.logLimit = 200 ;
self.logsLogged = 0 ;
self.transferableSupported = () -> true
self.console = log: ->
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 )
i = 0
while i < args . length
args [ i ] = args [ i ] . toString ( ) if args [ i ] . constructor . className is " Thang " or args [ i ] . isComponent if args [ i ] and args [ i ] . constructor
++ i
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
# so that we don't crash when debugging statements happen
2014-05-10 21:24:50 -04:00
self.console.error = self.console.warn = self.console.info = self.console.debug = self . console . log
2014-05-05 20:37:14 -04:00
GLOBAL.console = console = self . console
self . console
work = () ->
console . log " starting... "
console.log = ->
2014-05-10 21:24:50 -04:00
World = self . require ( ' lib/world/world ' )
GoalManager = self . require ( ' lib/world/GoalManager ' )
Aether . addGlobal ( ' Vector ' , require ( ' lib/world/vector ' ) )
Aether . addGlobal ( ' _ ' , _ )
2014-05-05 20:37:14 -04:00
self.cleanUp = ->
self.world = null
self.goalManager = null
self.postedErrors = { }
self.t0 = null
self.logsLogged = 0
self.runWorld = (args) ->
console . log " Running world inside worker. "
self.postedErrors = { }
self.t0 = new Date ( )
self.postedErrors = false
self.logsLogged = 0
try
2014-05-10 21:24:50 -04:00
self.world = new World ( args . userCodeMap )
2014-05-15 17:54:31 -04:00
self.world.levelSessionIDs = args . levelSessionIDs
2014-05-05 20:37:14 -04:00
self . world . loadFromLevel args . level , true if args . level
2014-05-10 21:24:50 -04:00
self.world.headless = args . headless
2014-05-05 20:37:14 -04:00
self.goalManager = new GoalManager ( self . world )
self . goalManager . setGoals args . goals
self . goalManager . setCode args . userCodeMap
self . goalManager . worldGenerationWillBegin ( )
self . world . setGoalManager self . goalManager
catch error
console . log " There has been an error inside the worker. "
self . onWorldError error
return
Math . random = self . world . rand . randf # so user code is predictable
console . log " Loading frames. "
self . postMessage type: " start-load-frames "
self . world . loadFrames self . onWorldLoaded , self . onWorldError , self . onWorldLoadProgress , true
self.onWorldLoaded = onWorldLoaded = ->
self . goalManager . worldGenerationEnded ( )
2014-05-10 21:24:50 -04:00
goalStates = self . goalManager . getGoalStates ( )
self . postMessage type: " end-load-frames " , goalStates: goalStates
2014-05-05 20:37:14 -04:00
t1 = new Date ( )
diff = t1 - self . t0
2014-05-10 21:24:50 -04:00
if ( self . world . headless )
return console . log ( " Headless simulation completed in #{ diff } ms. " ) ;
2014-05-05 20:37:14 -04:00
transferableSupported = self . transferableSupported ( )
try
2014-05-10 21:24:50 -04:00
serialized = serializedWorld: self . world . serialize ( )
2014-05-05 20:37:14 -04:00
transferableSupported = false
catch error
console . log " World serialization error: " , error . toString ( ) + " \n " + error . stack or error . stackTrace
t2 = new Date ( )
# console.log("About to transfer", serialized.serializedWorld.trackedPropertiesPerThangValues, serialized.transferableObjects);
try
2014-05-10 21:24:50 -04:00
message =
type: " new-world "
serialized: serialized . serializedWorld
goalStates: goalStates
2014-05-05 20:37:14 -04:00
if transferableSupported
2014-05-10 21:24:50 -04:00
self . postMessage message , serialized . transferableObjects
2014-05-05 20:37:14 -04:00
else
2014-05-10 21:24:50 -04:00
self . postMessage message
2014-05-05 20:37:14 -04:00
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) \n Simulation : " , diff + " ms \n Serialization: " , ( t2 - t1 ) + " ms \n Delivery : " , ( t3 - t2 ) + " ms "
self . cleanUp ( )
self.onWorldError = onWorldError = (error) ->
self . postMessage type: " end-load-frames "
if error instanceof Aether . problems . UserCodeProblem
#console.log "Aether userCodeProblem occured."
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
self . cleanUp ( )
self.onWorldLoadProgress = onWorldLoadProgress = (progress) ->
#console.log "Worker onWorldLoadProgress"
self . postMessage
type: " world-load-progress-changed "
progress: progress
self.abort = abort = ->
#console.log "Abort called for worker."
2014-05-10 21:24:50 -04:00
if self . world
2014-05-05 20:37:14 -04:00
#console.log "About to abort:", self.world.name, typeof self.world.abort
2014-05-10 21:24:50 -04:00
self . world . abort ( )
2014-05-05 20:37:14 -04:00
self.world = null
self . postMessage type: " abort "
self . cleanUp ( )
self.reportIn = reportIn = ->
console . log " Reporting in. "
2014-05-10 21:24:50 -04:00
self . postMessage type: " report-in "
2014-05-05 20:37:14 -04:00
self . addEventListener " message " , (event) ->
#console.log JSON.stringify event
self [ event . data . func ] event . data . args
self . postMessage type: " worker-initialized "
2014-05-10 21:52:55 -04:00
worldCode = fs . readFileSync " ./public/javascripts/world.js " , ' utf8 '
lodashCode = fs . readFileSync " ./public/javascripts/lodash.js " , ' utf8 '
aetherCode = fs . readFileSync " ./public/javascripts/aether.js " , ' utf8 '
2014-05-05 20:37:14 -04:00
#window.BOX2D_ENABLED = true;
newConsole = " newConsole = #{ } JASON.stringify newConsole}() " ;
ret = """
GLOBAL = root = window = self ;
GLOBAL.window = window ;
self.workerID = " Worker " ;
console = #{JASON.stringify betterConsole}();
try {
/ / the world javascript file
2014-05-10 21:52:55 -04:00
#{worldCode};
#{lodashCode};
#{aetherCode};
2014-05-05 20:37:14 -04:00
/ / Don ' t let user generated code access stuff from our file system!
self.importScripts = importScripts = null ;
self.native_fs_ = native_fs_ = null ;
/ / the actual function
#{JASON.stringify work}();
} catch ( error ) {
self . postMessage ( { " type " : " console-log " , args: [ " An unhandled error occured: " , error . toString ( ) , error . stack ] , id: - 1 } ) ;
}
"""
#console = #{JASON.stringify createConsole}();
#
# console.error = console.info = console.log;
#self.console = console;
#GLOBAL.console = console;
module.exports = new Function ( ret )