Commit ee99cd42 by Fabio Alessandrelli

[HTML5] Locale, input fix, context, exit.

Add missing semicolumns in engine.js Add optional extra args to JS Engine.startGame Remove loader.js, explicit noExitRuntime. Also add onExit callback (undocumented in emscripten)
parent 6a0473bc
......@@ -32,7 +32,6 @@ env.Depends(build, js_modules)
engine = [
"engine/preloader.js",
"engine/loader.js",
"engine/utils.js",
"engine/engine.js",
]
......
......@@ -164,3 +164,6 @@ def configure(env):
# callMain for manual start, FS for preloading.
env.Append(LINKFLAGS=["-s", 'EXTRA_EXPORTED_RUNTIME_METHODS=["callMain", "FS"]'])
# Add code that allow exiting runtime.
env.Append(LINKFLAGS=['-s', 'EXIT_RUNTIME=1'])
Function('return this')()['Engine'] = (function() {
var unloadAfterInit = true;
var canvas = null;
var resizeCanvasOnStart = false;
var customLocale = 'en_US';
var wasmExt = '.wasm';
var preloader = new Preloader();
var loader = new Loader();
var rtenv = null;
var executableName = '';
var wasmExt = '.wasm';
var unloadAfterInit = true;
var loadPath = '';
var loadPromise = null;
var initPromise = null;
......@@ -33,31 +25,42 @@ Function('return this')()['Engine'] = (function() {
};
/** @constructor */
function Engine() {};
function Engine() {
this.canvas = null;
this.executableName = '';
this.rtenv = null;
this.customLocale = null;
this.resizeCanvasOnStart = false;
this.onExit = null;
};
Engine.prototype.init = /** @param {string=} basePath */ function(basePath) {
if (initPromise) {
return initPromise;
}
if (!loadPromise) {
if (loadPromise == null) {
if (!basePath) {
initPromise = Promise.reject(new Error("A base path must be provided when calling `init` and the engine is not loaded."));
return initPromise;
}
load(basePath);
}
var config = {}
var config = {};
if (typeof stdout === 'function')
config.print = stdout;
if (typeof stderr === 'function')
config.printErr = stderr;
initPromise = loader.init(loadPromise, loadPath, config).then(function() {
return new Promise(function(resolve, reject) {
rtenv = loader.env;
var me = this;
initPromise = new Promise(function(resolve, reject) {
config['locateFile'] = Utils.createLocateRewrite(loadPath);
config['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
Godot(config).then(function(module) {
me.rtenv = module;
if (unloadAfterInit) {
loadPromise = null;
unload();
}
resolve();
config = null;
});
});
return initPromise;
......@@ -76,33 +79,71 @@ Function('return this')()['Engine'] = (function() {
args.push(arguments[i]);
}
var me = this;
return new Promise(function(resolve, reject) {
return me.init().then(function() {
if (!(canvas instanceof HTMLCanvasElement)) {
canvas = Utils.findCanvas();
}
rtenv['locale'] = customLocale;
rtenv['canvas'] = canvas;
rtenv['thisProgram'] = executableName;
rtenv['resizeCanvasOnStart'] = resizeCanvasOnStart;
loader.start(preloader.preloadedFiles, args).then(function() {
loader = null;
initPromise = null;
resolve();
return me.init().then(function() {
if (!me.rtenv) {
reject(new Error('The engine must be initialized before it can be started'));
}
if (!(me.canvas instanceof HTMLCanvasElement)) {
me.canvas = Utils.findCanvas();
}
// Canvas can grab focus on click, or key events won't work.
if (me.canvas.tabIndex < 0) {
me.canvas.tabIndex = 0;
}
// Disable right-click context menu.
me.canvas.addEventListener('contextmenu', function(ev) {
ev.preventDefault();
}, false);
// Until context restoration is implemented warn the user of context loss.
me.canvas.addEventListener('webglcontextlost', function(ev) {
alert("WebGL context lost, please reload the page");
ev.preventDefault();
}, false);
// Browser locale, or custom one if defined.
var locale = me.customLocale;
if (!locale) {
locale = navigator.languages ? navigator.languages[0] : navigator.language;
locale = locale.split('.')[0];
}
me.rtenv['locale'] = locale;
me.rtenv['canvas'] = me.canvas;
me.rtenv['thisProgram'] = me.executableName;
me.rtenv['resizeCanvasOnStart'] = me.resizeCanvasOnStart;
me.rtenv['noExitRuntime'] = true;
me.rtenv['onExit'] = function(code) {
if (me.onExit)
me.onExit(code);
me.rtenv = null;
}
return new Promise(function(resolve, reject) {
preloader.preloadedFiles.forEach(function(file) {
Utils.copyToFS(me.rtenv['FS'], file.path, file.buffer);
});
preloader.preloadedFiles.length = 0; // Clear memory
me.rtenv['callMain'](args);
initPromise = null;
resolve();
});
});
};
Engine.prototype.startGame = function(execName, mainPack) {
Engine.prototype.startGame = function(execName, mainPack, extraArgs) {
// Start and init with execName as loadPath if not inited.
executableName = execName;
this.executableName = execName;
var me = this;
return Promise.all([
this.init(execName),
this.preloadFile(mainPack, mainPack)
]).then(function() {
return me.start('--main-pack', mainPack);
var args = ['--main-pack', mainPack];
if (extraArgs)
args = args.concat(extraArgs);
return me.start.apply(me, args);
});
};
......@@ -118,67 +159,78 @@ Function('return this')()['Engine'] = (function() {
};
Engine.prototype.setCanvas = function(canvasElem) {
canvas = canvasElem;
this.canvas = canvasElem;
};
Engine.prototype.setCanvasResizedOnStart = function(enabled) {
resizeCanvasOnStart = enabled;
this.resizeCanvasOnStart = enabled;
};
Engine.prototype.setLocale = function(locale) {
customLocale = locale;
this.customLocale = locale;
};
Engine.prototype.setExecutableName = function(newName) {
executableName = newName;
this.executableName = newName;
};
Engine.prototype.setProgressFunc = function(func) {
progressFunc = func;
}
};
Engine.prototype.setStdoutFunc = function(func) {
var print = function(text) {
if (arguments.length > 1) {
text = Array.prototype.slice.call(arguments).join(" ");
}
func(text);
};
if (rtenv)
rtenv.print = print;
if (this.rtenv)
this.rtenv.print = print;
stdout = print;
};
Engine.prototype.setStderrFunc = function(func) {
var printErr = function(text) {
if (arguments.length > 1)
text = Array.prototype.slice.call(arguments).join(" ");
func(text);
};
if (rtenv)
rtenv.printErr = printErr;
if (this.rtenv)
this.rtenv.printErr = printErr;
stderr = printErr;
};
Engine.prototype.setOnExit = function(onExit) {
this.onExit = onExit;
}
Engine.prototype.copyToFS = function(path, buffer) {
if (this.rtenv == null) {
throw new Error("Engine must be inited before copying files");
}
Utils.copyToFS(this.rtenv['FS'], path, buffer);
}
// Closure compiler exported engine methods.
/** @export */
Engine['isWebGLAvailable'] = Utils.isWebGLAvailable;
Engine['load'] = load;
Engine['unload'] = unload;
Engine.prototype['init'] = Engine.prototype.init
Engine.prototype['preloadFile'] = Engine.prototype.preloadFile
Engine.prototype['start'] = Engine.prototype.start
Engine.prototype['startGame'] = Engine.prototype.startGame
Engine.prototype['setWebAssemblyFilenameExtension'] = Engine.prototype.setWebAssemblyFilenameExtension
Engine.prototype['setUnloadAfterInit'] = Engine.prototype.setUnloadAfterInit
Engine.prototype['setCanvas'] = Engine.prototype.setCanvas
Engine.prototype['setCanvasResizedOnStart'] = Engine.prototype.setCanvasResizedOnStart
Engine.prototype['setLocale'] = Engine.prototype.setLocale
Engine.prototype['setExecutableName'] = Engine.prototype.setExecutableName
Engine.prototype['setProgressFunc'] = Engine.prototype.setProgressFunc
Engine.prototype['setStdoutFunc'] = Engine.prototype.setStdoutFunc
Engine.prototype['setStderrFunc'] = Engine.prototype.setStderrFunc
Engine.prototype['init'] = Engine.prototype.init;
Engine.prototype['preloadFile'] = Engine.prototype.preloadFile;
Engine.prototype['start'] = Engine.prototype.start;
Engine.prototype['startGame'] = Engine.prototype.startGame;
Engine.prototype['setWebAssemblyFilenameExtension'] = Engine.prototype.setWebAssemblyFilenameExtension;
Engine.prototype['setUnloadAfterInit'] = Engine.prototype.setUnloadAfterInit;
Engine.prototype['setCanvas'] = Engine.prototype.setCanvas;
Engine.prototype['setCanvasResizedOnStart'] = Engine.prototype.setCanvasResizedOnStart;
Engine.prototype['setLocale'] = Engine.prototype.setLocale;
Engine.prototype['setExecutableName'] = Engine.prototype.setExecutableName;
Engine.prototype['setProgressFunc'] = Engine.prototype.setProgressFunc;
Engine.prototype['setStdoutFunc'] = Engine.prototype.setStdoutFunc;
Engine.prototype['setStderrFunc'] = Engine.prototype.setStderrFunc;
Engine.prototype['setOnExit'] = Engine.prototype.setOnExit;
Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
return Engine;
})();
var Loader = /** @constructor */ function() {
this.env = null;
this.init = function(loadPromise, basePath, config) {
var me = this;
return new Promise(function(resolve, reject) {
var cfg = config || {};
cfg['locateFile'] = Utils.createLocateRewrite(basePath);
cfg['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
loadPromise = null;
Godot(cfg).then(function(module) {
me.env = module;
resolve();
});
});
}
this.start = function(preloadedFiles, args) {
var me = this;
return new Promise(function(resolve, reject) {
if (!me.env) {
reject(new Error('The engine must be initialized before it can be started'));
}
preloadedFiles.forEach(function(file) {
Utils.copyToFS(me.env['FS'], file.path, file.buffer);
});
preloadedFiles.length = 0; // Clear memory
me.env['callMain'](args);
resolve();
});
}
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment