// There's no reason that this file is in JavaScript instead of CoffeeScript. // We should convert it and update the brunch config. // If we wanted to be more robust, we could use this: https://github.com/padolsey/operative/blob/master/src/operative.js if(typeof window !== 'undefined' || !self.importScripts) throw "Attempt to load worker_world into main window instead of web worker."; // Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind // This is here for running simuations in enviroments lacking function.bind (PhantomJS mostly) if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new TypeError("Function.prototype.bind (Shim) - target is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; } // assign global window so that Brunch's require (in world.js) can go into it self.window = self; self.workerID = "Worker"; self.logLimit = 200; self.logsLogged = 0; var console = { log: function() { if(self.logsLogged++ == 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(var i = 0; i < args.length; ++i) { if(args[i] && args[i].constructor) { if(args[i].constructor.className === "Thang" || args[i].isComponent) args[i] = args[i].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}); } } }}; // so that we don't crash when debugging statements happen console.error = console.info = console.log; self.console = console; importScripts('/javascripts/world.js'); // We could do way more from this: http://stackoverflow.com/questions/10653809/making-webworkers-a-safe-environment Object.defineProperty(self, "XMLHttpRequest", { get: function() { throw new Error("Access to XMLHttpRequest is forbidden."); }, configurable: false }); self.transferableSupported = function transferableSupported() { // Not in IE, even in IE 11 try { var ab = new ArrayBuffer(1); worker.postMessage(ab, [ab]); return ab.byteLength == 0; } catch(error) { return false; } return false; } var World = self.require('lib/world/world'); var GoalManager = self.require('lib/world/GoalManager'); self.runWorld = function 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.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; // so user code is predictable self.world.loadFrames(self.onWorldLoaded, self.onWorldError, self.onWorldLoadProgress); }; self.onWorldLoaded = function onWorldLoaded() { self.goalManager.worldGenerationEnded(); var t1 = new Date(); var diff = t1 - self.t0; var transferableSupported = self.transferableSupported(); try { var serialized = self.world.serialize(); } catch(error) { console.log("World serialization error:", error.toString() + "\n" + error.stack || error.stackTrace); } var t2 = new Date(); //console.log("About to transfer", serialized.serializedWorld.trackedPropertiesPerThangValues, serialized.transferableObjects); 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 || error.stackTrace); } 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 = null; }; self.onWorldError = function onWorldError(error) { if(error instanceof Aether.problems.UserCodeProblem) { if(!self.postedErrors[error.key]) { var 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 || error.stackTrace); } /* We don't actually have the recoverable property any more; hmm if(!self.firstWorld && !error.recoverable) { self.abort(); return false; } */ return true; }; self.onWorldLoadProgress = function onWorldLoadProgress(progress) { self.postMessage({type: 'world-load-progress-changed', progress: progress}); }; self.abort = function abort() { if(self.world && self.world.name) { console.log("About to abort:", self.world.name, typeof self.world.abort); if(typeof self.world !== "undefined") self.world.abort(); self.world = null; } self.postMessage({type: 'abort'}); }; self.reportIn = function reportIn() { self.postMessage({type: 'reportIn'}); } self.addEventListener('message', function(event) { self[event.data.func](event.data.args); }); self.postMessage({type: 'worker-initialized'});