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.
2015-03-11 13:14:33 -04:00
JASON = require ' JASON '
2014-05-05 20:37:14 -04:00
fs = require ' fs '
2014-05-10 21:52:55 -04:00
GLOBAL.Aether = Aether = require ' aether '
GLOBAL._ = _ = require ' lodash '
2015-04-06 19:59:44 -04:00
GLOBAL.CoffeeScript = require ' coffee-script '
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
2014-06-30 22:16:26 -04:00
type: ' console-log '
args: [ ' Log limit ' + self . logLimit + ' reached; shutting up. ' ]
2014-05-05 20:37:14 -04:00
id: self . workerID
else if self . logsLogged < self . logLimit
args = [ ] . slice . call ( arguments )
i = 0
while i < args . length
2014-06-30 22:16:26 -04:00
args [ i ] = args [ i ] . toString ( ) if args [ i ] . constructor . className is ' Thang ' or args [ i ] . isComponent if args [ i ] and args [ i ] . constructor
2014-05-05 20:37:14 -04:00
++ i
try
self . postMessage
2014-06-30 22:16:26 -04:00
type: ' console-log '
2014-05-05 20:37:14 -04:00
args: args
id: self . workerID
catch error
self . postMessage
2014-06-30 22:16:26 -04:00
type: ' console-log '
2014-05-05 20:37:14 -04:00
args: [
2014-06-30 22:16:26 -04:00
' Could not post log: ' + args
2014-05-05 20:37:14 -04:00
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 = () ->
2014-06-30 22:16:26 -04:00
console . log ' starting... '
2014-05-05 20:37:14 -04:00
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) ->
2014-06-30 22:16:26 -04:00
console . log ' Running world inside worker. '
2014-05-05 20:37:14 -04:00
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-10-18 20:32:01 -04:00
self.world.submissionCount = args . submissionCount
self.world.flagHistory = args . flagHistory
2015-01-05 13:44:17 -05:00
self.world.difficulty = args . difficulty
2014-06-30 22:16:26 -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
2014-06-30 22:16:26 -04:00
console . log ' There has been an error inside the worker. '
2014-05-05 20:37:14 -04:00
self . onWorldError error
return
Math . random = self . world . rand . randf # so user code is predictable
2014-06-30 22:16:26 -04:00
Aether . replaceBuiltin ( ' Math ' , Math )
2014-07-13 21:53:01 -04:00
replacedLoDash = _ . runInContext ( self )
_ [ key ] = replacedLoDash [ key ] for key , val of replacedLoDash
2014-06-30 22:16:26 -04:00
console . log ' Loading frames. '
2014-05-05 20:37:14 -04:00
2014-06-30 22:16:26 -04:00
self . postMessage type: ' start-load-frames '
2014-05-05 20:37:14 -04:00
2015-02-12 19:06:27 -05:00
self . world . loadFrames self . onWorldLoaded , self . onWorldError , self . onWorldLoadProgress , null , true
2014-05-05 20:37:14 -04:00
self.onWorldLoaded = onWorldLoaded = ->
self . goalManager . worldGenerationEnded ( )
2014-05-10 21:24:50 -04:00
goalStates = self . goalManager . getGoalStates ( )
2015-02-12 19:06:27 -05:00
self . postMessage type: ' end-load-frames ' , goalStates: goalStates , overallStatus: goalManager . checkOverallStatus ( )
2014-05-10 21:24:50 -04:00
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
2014-06-30 22:16:26 -04:00
console . log ' World serialization error: ' , error . toString ( ) + " \n " + error . stack or error . stackTrace
2014-05-05 20:37:14 -04:00
t2 = new Date ( )
2014-06-30 22:16:26 -04:00
# console.log('About to transfer', serialized.serializedWorld.trackedPropertiesPerThangValues, serialized.transferableObjects);
2014-05-05 20:37:14 -04:00
try
2014-05-10 21:24:50 -04:00
message =
2014-06-30 22:16:26 -04:00
type: ' new-world '
2014-05-10 21:24:50 -04:00
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
2014-06-30 22:16:26 -04:00
console . log ' World delivery error: ' , error . toString ( ) + " \n " + error . stack or error . stackTrace
2014-05-05 20:37:14 -04:00
t3 = new Date ( )
2014-06-30 22:16:26 -04:00
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 '
2014-05-05 20:37:14 -04:00
self . cleanUp ( )
self.onWorldError = onWorldError = (error) ->
2014-06-05 17:06:28 -04:00
if error . isUserCodeProblem
errorKey = error . userInfo . key
if not errorKey or not self . postedErrors [ errorKey ]
2014-05-05 20:37:14 -04:00
self . postMessage
2014-06-30 22:16:26 -04:00
type: ' user-code-problem '
2014-06-05 17:06:28 -04:00
problem: error
self . postedErrors [ errorKey ] = error
2014-05-05 20:37:14 -04:00
else
2014-06-30 22:16:26 -04:00
console . log ' Non-UserCodeError: ' , error . toString ( ) + " \n " + error . stack or error . stackTrace
2015-02-12 20:07:00 -05:00
self . postMessage type: ' non-user-code-problem ' , problem: { message: error . toString ( ) }
2014-06-05 17:06:28 -04:00
self . cleanUp ( )
2015-02-12 20:07:00 -05:00
return false
2014-06-05 17:06:28 -04:00
return true
2014-06-19 11:07:30 -04:00
2014-05-05 20:37:14 -04:00
self.onWorldLoadProgress = onWorldLoadProgress = (progress) ->
2014-06-30 22:16:26 -04:00
#console.log 'Worker onWorldLoadProgress'
2014-05-05 20:37:14 -04:00
self . postMessage
2014-06-30 22:16:26 -04:00
type: ' world-load-progress-changed '
2014-05-05 20:37:14 -04:00
progress: progress
self.abort = abort = ->
2014-06-30 22:16:26 -04:00
#console.log 'Abort called for worker.'
2014-05-10 21:24:50 -04:00
if self . world
2014-06-30 22:16:26 -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
2014-06-30 22:16:26 -04:00
self . postMessage type: ' abort '
2014-05-05 20:37:14 -04:00
self . cleanUp ( )
self.reportIn = reportIn = ->
2014-06-30 22:16:26 -04:00
console . log ' Reporting in. '
self . postMessage type: ' report-in '
2014-05-05 20:37:14 -04:00
2014-06-30 22:16:26 -04:00
self . addEventListener ' message ' , (event) ->
2014-05-05 20:37:14 -04:00
#console.log JSON.stringify event
self [ event . data . func ] event . data . args
2014-06-30 22:16:26 -04:00
self . postMessage type: ' worker-initialized '
2014-05-05 20:37:14 -04:00
2015-02-12 19:06:27 -05:00
codeFileContents = [ ]
for codeFile in [
' lodash.js '
2015-02-12 20:07:00 -05:00
' world.js '
2015-02-12 19:06:27 -05:00
' aether.js '
' app/vendor/aether-clojure.js '
' app/vendor/aether-coffeescript.js '
' app/vendor/aether-io.js '
' app/vendor/aether-javascript.js '
' app/vendor/aether-lua.js '
' app/vendor/aether-python.js '
]
codeFileContents . push fs . readFileSync ( " ./public/javascripts/ #{ codeFile } " , ' 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 ;
2014-06-30 22:16:26 -04:00
self.workerID = ' Worker ' ;
2014-05-05 20:37:14 -04:00
console = #{JASON.stringify betterConsole}();
try {
/ / the world javascript file
2015-02-12 19:06:27 -05:00
#{codeFileContents.join(';\n ')};
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}();
2014-06-30 22:16:26 -04:00
} catch ( error ) {
self . postMessage ( { ' type ' : ' console-log ' , args: [ ' An unhandled error occured: ' , error . toString ( ) , error . stack ] , id: - 1 } ) ;
2014-05-05 20:37:14 -04:00
}
"""
#console = #{JASON.stringify createConsole}();
#
# console.error = console.info = console.log;
#self.console = console;
#GLOBAL.console = console;
module.exports = new Function ( ret )