codecombat/app/assets/javascripts/workers/worker_world.js

154 lines
5.4 KiB
JavaScript

// 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.";
// 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'});