Merge pull request #13044 from eska014/enginejs
Change HTML5 start-up API
This commit is contained in:
commit
f0795ae2fe
5 changed files with 137 additions and 94 deletions
25
misc/dist/html/default.html
vendored
25
misc/dist/html/default.html
vendored
|
@ -225,7 +225,7 @@ $GODOT_HEAD_INCLUDE
|
||||||
<script type="text/javascript" src="$GODOT_BASENAME.js"></script>
|
<script type="text/javascript" src="$GODOT_BASENAME.js"></script>
|
||||||
<script type="text/javascript">//<![CDATA[
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
|
||||||
var game = new Engine;
|
var engine = new Engine;
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ $GODOT_HEAD_INCLUDE
|
||||||
var indeterminiateStatusAnimationId = 0;
|
var indeterminiateStatusAnimationId = 0;
|
||||||
|
|
||||||
setStatusMode('indeterminate');
|
setStatusMode('indeterminate');
|
||||||
game.setCanvas(canvas);
|
engine.setCanvas(canvas);
|
||||||
|
|
||||||
function setStatusMode(mode) {
|
function setStatusMode(mode) {
|
||||||
|
|
||||||
|
@ -300,7 +300,7 @@ $GODOT_HEAD_INCLUDE
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
game.setProgressFunc((current, total) => {
|
engine.setProgressFunc((current, total) => {
|
||||||
|
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
statusProgressInner.style.width = current/total * 100 + '%';
|
statusProgressInner.style.width = current/total * 100 + '%';
|
||||||
|
@ -330,10 +330,6 @@ $GODOT_HEAD_INCLUDE
|
||||||
outputRoot.style.display = 'block';
|
outputRoot.style.display = 'block';
|
||||||
|
|
||||||
function print(text) {
|
function print(text) {
|
||||||
if (arguments.length > 1) {
|
|
||||||
text = Array.prototype.slice.call(arguments).join(" ");
|
|
||||||
}
|
|
||||||
if (text.length <= 0) return;
|
|
||||||
while (outputScroll.childElementCount >= OUTPUT_MSG_COUNT_MAX) {
|
while (outputScroll.childElementCount >= OUTPUT_MSG_COUNT_MAX) {
|
||||||
outputScroll.firstChild.remove();
|
outputScroll.firstChild.remove();
|
||||||
}
|
}
|
||||||
|
@ -354,26 +350,31 @@ $GODOT_HEAD_INCLUDE
|
||||||
};
|
};
|
||||||
|
|
||||||
function printError(text) {
|
function printError(text) {
|
||||||
print('**ERROR**' + ":", text);
|
if (!text.startsWith('**ERROR**: ')) {
|
||||||
|
text = '**ERROR**: ' + text;
|
||||||
|
}
|
||||||
|
print(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.setStdoutFunc(text => {
|
engine.setStdoutFunc(text => {
|
||||||
print(text);
|
print(text);
|
||||||
console.log(text);
|
console.log(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
game.setStderrFunc(text => {
|
engine.setStderrFunc(text => {
|
||||||
printError(text);
|
printError(text);
|
||||||
console.warn(text);
|
console.warn(text);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
game.start(BASENAME + '.pck').then(() => {
|
engine.startGame(BASENAME + '.pck').then(() => {
|
||||||
setStatusMode('hidden');
|
setStatusMode('hidden');
|
||||||
initializing = false;
|
initializing = false;
|
||||||
}, err => {
|
}, err => {
|
||||||
if (DEBUG_ENABLED)
|
if (DEBUG_ENABLED) {
|
||||||
printError(err.message);
|
printError(err.message);
|
||||||
|
console.warn(err);
|
||||||
|
}
|
||||||
setStatusNotice(err.message);
|
setStatusNotice(err.message);
|
||||||
setStatusMode('notice');
|
setStatusMode('notice');
|
||||||
initializing = false;
|
initializing = false;
|
||||||
|
|
|
@ -102,14 +102,13 @@ def configure(env):
|
||||||
|
|
||||||
## Link flags
|
## Link flags
|
||||||
|
|
||||||
env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"'])
|
|
||||||
env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
|
|
||||||
|
|
||||||
env.Append(LINKFLAGS=['-s', 'BINARYEN=1'])
|
env.Append(LINKFLAGS=['-s', 'BINARYEN=1'])
|
||||||
# In contrast to asm.js, enabling memory growth on WebAssembly has no
|
|
||||||
# major performance impact, and causes only a negligible increase in
|
|
||||||
# memory size.
|
|
||||||
env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1'])
|
env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1'])
|
||||||
|
env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
|
||||||
|
env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"'])
|
||||||
|
|
||||||
|
env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0'])
|
||||||
|
env.Append(LINKFLAGS=['-s', 'NO_EXIT_RUNTIME=1'])
|
||||||
|
|
||||||
# TODO: Move that to opus module's config
|
# TODO: Move that to opus module's config
|
||||||
if 'module_opus_enabled' in env and env['module_opus_enabled']:
|
if 'module_opus_enabled' in env and env['module_opus_enabled']:
|
||||||
|
|
|
@ -31,82 +31,101 @@
|
||||||
|
|
||||||
this.rtenv = null;
|
this.rtenv = null;
|
||||||
|
|
||||||
var gameInitPromise = null;
|
var initPromise = null;
|
||||||
var unloadAfterInit = true;
|
var unloadAfterInit = true;
|
||||||
|
|
||||||
|
var preloadedFiles = [];
|
||||||
|
|
||||||
|
var resizeCanvasOnStart = true;
|
||||||
var progressFunc = null;
|
var progressFunc = null;
|
||||||
var pckProgressTracker = {};
|
var preloadProgressTracker = {};
|
||||||
var lastProgress = { loaded: 0, total: 0 };
|
var lastProgress = { loaded: 0, total: 0 };
|
||||||
|
|
||||||
var canvas = null;
|
var canvas = null;
|
||||||
|
var executableName = null;
|
||||||
|
var locale = null;
|
||||||
var stdout = null;
|
var stdout = null;
|
||||||
var stderr = null;
|
var stderr = null;
|
||||||
|
|
||||||
this.initGame = function(mainPack) {
|
this.init = function(newBasePath) {
|
||||||
|
|
||||||
if (!gameInitPromise) {
|
if (!initPromise) {
|
||||||
|
initPromise = Engine.load(newBasePath).then(
|
||||||
if (mainPack === undefined) {
|
|
||||||
if (basePath !== null) {
|
|
||||||
mainPack = basePath + '.pck';
|
|
||||||
} else {
|
|
||||||
return Promise.reject(new Error("No main pack to load specified"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (basePath === null)
|
|
||||||
basePath = getBasePath(mainPack);
|
|
||||||
|
|
||||||
gameInitPromise = Engine.initEngine().then(
|
|
||||||
instantiate.bind(this)
|
instantiate.bind(this)
|
||||||
);
|
);
|
||||||
var gameLoadPromise = loadPromise(mainPack, pckProgressTracker).then(function(xhr) { return xhr.response; });
|
|
||||||
gameInitPromise = Promise.all([gameLoadPromise, gameInitPromise]).then(function(values) {
|
|
||||||
// resolve with pck
|
|
||||||
return new Uint8Array(values[0]);
|
|
||||||
});
|
|
||||||
if (unloadAfterInit)
|
|
||||||
gameInitPromise.then(Engine.unloadEngine);
|
|
||||||
requestAnimationFrame(animateProgress);
|
requestAnimationFrame(animateProgress);
|
||||||
|
if (unloadAfterInit)
|
||||||
|
initPromise.then(Engine.unloadEngine);
|
||||||
}
|
}
|
||||||
return gameInitPromise;
|
return initPromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
function instantiate(initializer) {
|
function instantiate(wasmBuf) {
|
||||||
|
|
||||||
var rtenvOpts = {
|
var rtenvProps = {
|
||||||
noInitialRun: true,
|
|
||||||
thisProgram: getBaseName(basePath),
|
|
||||||
engine: this,
|
engine: this,
|
||||||
|
ENV: {},
|
||||||
};
|
};
|
||||||
if (typeof stdout === 'function')
|
if (typeof stdout === 'function')
|
||||||
rtenvOpts.print = stdout;
|
rtenvProps.print = stdout;
|
||||||
if (typeof stderr === 'function')
|
if (typeof stderr === 'function')
|
||||||
rtenvOpts.printErr = stderr;
|
rtenvProps.printErr = stderr;
|
||||||
if (typeof WebAssembly === 'object' && initializer instanceof ArrayBuffer) {
|
rtenvProps.instantiateWasm = function(imports, onSuccess) {
|
||||||
rtenvOpts.instantiateWasm = function(imports, onSuccess) {
|
WebAssembly.instantiate(wasmBuf, imports).then(function(result) {
|
||||||
WebAssembly.instantiate(initializer, imports).then(function(result) {
|
onSuccess(result.instance);
|
||||||
onSuccess(result.instance);
|
});
|
||||||
});
|
return {};
|
||||||
return {};
|
};
|
||||||
};
|
|
||||||
} else {
|
|
||||||
throw new Error("Invalid initializer");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
rtenvOpts.onRuntimeInitialized = resolve;
|
rtenvProps.onRuntimeInitialized = resolve;
|
||||||
rtenvOpts.onAbort = reject;
|
rtenvProps.onAbort = reject;
|
||||||
rtenvOpts.engine.rtenv = Engine.RuntimeEnvironment(rtenvOpts);
|
rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.start = function(mainPack) {
|
this.preloadFile = function(pathOrBuffer, bufferFilename) {
|
||||||
|
|
||||||
return this.initGame(mainPack).then(synchronousStart.bind(this));
|
if (pathOrBuffer instanceof ArrayBuffer) {
|
||||||
|
pathOrBuffer = new Uint8Array(pathOrBuffer);
|
||||||
|
} else if (ArrayBuffer.isView(pathOrBuffer)) {
|
||||||
|
pathOrBuffer = new Uint8Array(pathOrBuffer.buffer);
|
||||||
|
}
|
||||||
|
if (pathOrBuffer instanceof Uint8Array) {
|
||||||
|
preloadedFiles.push({
|
||||||
|
name: bufferFilename,
|
||||||
|
buffer: pathOrBuffer
|
||||||
|
});
|
||||||
|
return Promise.resolve();
|
||||||
|
} else if (typeof pathOrBuffer === 'string') {
|
||||||
|
return loadPromise(pathOrBuffer, preloadProgressTracker).then(function(xhr) {
|
||||||
|
preloadedFiles.push({
|
||||||
|
name: pathOrBuffer,
|
||||||
|
buffer: xhr.response
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw Promise.reject("Invalid object for preloading");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function synchronousStart(pckView) {
|
this.start = function() {
|
||||||
// TODO don't expect canvas when runninng as cli tool
|
|
||||||
|
return this.init().then(
|
||||||
|
Function.prototype.apply.bind(synchronousStart, this, arguments)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.startGame = function(mainPack) {
|
||||||
|
|
||||||
|
executableName = getBaseName(mainPack);
|
||||||
|
return Promise.all([this.init(getBasePath(mainPack)), this.preloadFile(mainPack)]).then(
|
||||||
|
Function.prototype.apply.bind(synchronousStart, this, [])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function synchronousStart() {
|
||||||
|
|
||||||
if (canvas instanceof HTMLCanvasElement) {
|
if (canvas instanceof HTMLCanvasElement) {
|
||||||
this.rtenv.canvas = canvas;
|
this.rtenv.canvas = canvas;
|
||||||
} else {
|
} else {
|
||||||
|
@ -141,15 +160,33 @@
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
this.rtenv.FS.createDataFile('/', this.rtenv.thisProgram + '.pck', pckView, true, true, true);
|
if (locale) {
|
||||||
gameInitPromise = null;
|
this.rtenv.locale = locale;
|
||||||
this.rtenv.callMain();
|
} else {
|
||||||
|
this.rtenv.locale = navigator.languages ? navigator.languages[0] : navigator.language;
|
||||||
|
}
|
||||||
|
this.rtenv.locale = this.rtenv.locale.split('.')[0];
|
||||||
|
this.rtenv.resizeCanvasOnStart = resizeCanvasOnStart;
|
||||||
|
|
||||||
|
this.rtenv.thisProgram = executableName || getBaseName(basePath);
|
||||||
|
|
||||||
|
preloadedFiles.forEach(function(file) {
|
||||||
|
this.rtenv.FS.createDataFile('/', file.name, new Uint8Array(file.buffer), true, true, true);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
preloadedFiles = null;
|
||||||
|
initPromise = null;
|
||||||
|
this.rtenv.callMain(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setProgressFunc = function(func) {
|
this.setProgressFunc = function(func) {
|
||||||
progressFunc = func;
|
progressFunc = func;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.setResizeCanvasOnStart = function(enabled) {
|
||||||
|
resizeCanvasOnStart = enabled;
|
||||||
|
};
|
||||||
|
|
||||||
function animateProgress() {
|
function animateProgress() {
|
||||||
|
|
||||||
var loaded = 0;
|
var loaded = 0;
|
||||||
|
@ -157,7 +194,7 @@
|
||||||
var totalIsValid = true;
|
var totalIsValid = true;
|
||||||
var progressIsFinal = true;
|
var progressIsFinal = true;
|
||||||
|
|
||||||
[loadingFiles, pckProgressTracker].forEach(function(tracker) {
|
[loadingFiles, preloadProgressTracker].forEach(function(tracker) {
|
||||||
Object.keys(tracker).forEach(function(file) {
|
Object.keys(tracker).forEach(function(file) {
|
||||||
if (!tracker[file].final)
|
if (!tracker[file].final)
|
||||||
progressIsFinal = false;
|
progressIsFinal = false;
|
||||||
|
@ -184,10 +221,20 @@
|
||||||
canvas = elem;
|
canvas = elem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.setExecutableName = function(newName) {
|
||||||
|
|
||||||
|
executableName = newName;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setLocale = function(newLocale) {
|
||||||
|
|
||||||
|
locale = newLocale;
|
||||||
|
};
|
||||||
|
|
||||||
this.setUnloadAfterInit = function(enabled) {
|
this.setUnloadAfterInit = function(enabled) {
|
||||||
|
|
||||||
if (enabled && !unloadAfterInit && gameInitPromise) {
|
if (enabled && !unloadAfterInit && initPromise) {
|
||||||
gameInitPromise.then(Engine.unloadEngine);
|
initPromise.then(Engine.unloadEngine);
|
||||||
}
|
}
|
||||||
unloadAfterInit = enabled;
|
unloadAfterInit = enabled;
|
||||||
};
|
};
|
||||||
|
@ -222,7 +269,7 @@
|
||||||
|
|
||||||
Engine.RuntimeEnvironment = engine.RuntimeEnvironment;
|
Engine.RuntimeEnvironment = engine.RuntimeEnvironment;
|
||||||
|
|
||||||
Engine.initEngine = function(newBasePath) {
|
Engine.load = function(newBasePath) {
|
||||||
|
|
||||||
if (newBasePath !== undefined) basePath = getBasePath(newBasePath);
|
if (newBasePath !== undefined) basePath = getBasePath(newBasePath);
|
||||||
if (engineLoadPromise === null) {
|
if (engineLoadPromise === null) {
|
||||||
|
@ -240,7 +287,7 @@
|
||||||
return engineLoadPromise;
|
return engineLoadPromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
Engine.unloadEngine = function() {
|
Engine.unload = function() {
|
||||||
engineLoadPromise = null;
|
engineLoadPromise = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -259,7 +306,7 @@
|
||||||
if (!file.endsWith('.js')) {
|
if (!file.endsWith('.js')) {
|
||||||
xhr.responseType = 'arraybuffer';
|
xhr.responseType = 'arraybuffer';
|
||||||
}
|
}
|
||||||
['loadstart', 'progress', 'load', 'error', 'timeout', 'abort'].forEach(function(ev) {
|
['loadstart', 'progress', 'load', 'error', 'abort'].forEach(function(ev) {
|
||||||
xhr.addEventListener(ev, onXHREvent.bind(xhr, resolve, reject, file, tracker));
|
xhr.addEventListener(ev, onXHREvent.bind(xhr, resolve, reject, file, tracker));
|
||||||
});
|
});
|
||||||
xhr.send();
|
xhr.send();
|
||||||
|
@ -274,7 +321,7 @@
|
||||||
this.abort();
|
this.abort();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
loadXHR(resolve, reject, file);
|
setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,12 +348,11 @@
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'error':
|
case 'error':
|
||||||
case 'timeout':
|
|
||||||
if (++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) {
|
if (++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) {
|
||||||
tracker[file].final = true;
|
tracker[file].final = true;
|
||||||
reject(new Error("Failed loading file '" + file + "'"));
|
reject(new Error("Failed loading file '" + file + "'"));
|
||||||
} else {
|
} else {
|
||||||
loadXHR(resolve, reject, file);
|
setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,6 @@ int main(int argc, char *argv[]) {
|
||||||
// run the 'main_after_fs_sync' function
|
// run the 'main_after_fs_sync' function
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
EM_ASM(
|
EM_ASM(
|
||||||
Module.noExitRuntime = true;
|
|
||||||
FS.mkdir('/userfs');
|
FS.mkdir('/userfs');
|
||||||
FS.mount(IDBFS, {}, '/userfs');
|
FS.mount(IDBFS, {}, '/userfs');
|
||||||
FS.syncfs(true, function(err) {
|
FS.syncfs(true, function(err) {
|
||||||
|
|
|
@ -438,25 +438,23 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i
|
||||||
video_mode = p_desired;
|
video_mode = p_desired;
|
||||||
// can't fulfil fullscreen request due to browser security
|
// can't fulfil fullscreen request due to browser security
|
||||||
video_mode.fullscreen = false;
|
video_mode.fullscreen = false;
|
||||||
set_window_size(Size2(p_desired.width, p_desired.height));
|
/* clang-format off */
|
||||||
|
bool resize_canvas_on_start = EM_ASM_INT_V(
|
||||||
|
return Module.resizeCanvasOnStart;
|
||||||
|
);
|
||||||
|
/* clang-format on */
|
||||||
|
if (resize_canvas_on_start) {
|
||||||
|
set_window_size(Size2(video_mode.width, video_mode.height));
|
||||||
|
} else {
|
||||||
|
Size2 canvas_size = get_window_size();
|
||||||
|
video_mode.width = canvas_size.width;
|
||||||
|
video_mode.height = canvas_size.height;
|
||||||
|
}
|
||||||
|
|
||||||
// find locale, emscripten only sets "C"
|
|
||||||
char locale_ptr[16];
|
char locale_ptr[16];
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
EM_ASM_({
|
EM_ASM_ARGS({
|
||||||
var locale = "";
|
stringToUTF8(Module.locale, $0, 16);
|
||||||
if (Module.locale) {
|
|
||||||
// best case: server-side script reads Accept-Language early and
|
|
||||||
// defines the locale to be read here
|
|
||||||
locale = Module.locale;
|
|
||||||
} else {
|
|
||||||
// no luck, use what the JS engine can tell us
|
|
||||||
// if this turns out not compatible enough, add tests for
|
|
||||||
// browserLanguage, systemLanguage and userLanguage
|
|
||||||
locale = navigator.languages ? navigator.languages[0] : navigator.language;
|
|
||||||
}
|
|
||||||
locale = locale.split('.')[0];
|
|
||||||
stringToUTF8(locale, $0, 16);
|
|
||||||
}, locale_ptr);
|
}, locale_ptr);
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
setenv("LANG", locale_ptr, true);
|
setenv("LANG", locale_ptr, true);
|
||||||
|
|
Loading…
Add table
Reference in a new issue