diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index e0ba72d32a1..726f7caf4a2 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -242,7 +242,7 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform { return name; } - void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects); + void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes); static void _server_thread_poll(void *data); @@ -280,7 +280,7 @@ public: ~EditorExportPlatformJavaScript(); }; -void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects) { +void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) { String str_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size()); String str_export; @@ -300,6 +300,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re config["gdnativeLibs"] = libs; config["executable"] = p_name; config["args"] = args; + config["fileSizes"] = p_file_sizes; const String str_config = JSON::print(config); for (int i = 0; i < lines.size(); i++) { @@ -481,6 +482,8 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese return ERR_FILE_CORRUPT; } + Vector<uint8_t> html; + Dictionary file_sizes; do { //get filename unz_file_info info; @@ -489,6 +492,16 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese String file = fname; + // HTML is handled later + if (file == "godot.html") { + if (custom_html.empty()) { + html.resize(info.uncompressed_size); + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, html.ptrw(), html.size()); + unzCloseCurrentFile(pkg); + } + continue; + } Vector<uint8_t> data; data.resize(info.uncompressed_size); @@ -499,15 +512,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese //write - if (file == "godot.html") { - - if (!custom_html.empty()) { - continue; - } - _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects); - file = p_path.get_file(); - - } else if (file == "godot.js") { + if (file == "godot.js") { file = p_path.get_file().get_basename() + ".js"; } else if (file == "godot.worker.js") { @@ -525,6 +530,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese } else if (file == "godot.wasm") { file = p_path.get_file().get_basename() + ".wasm"; + file_sizes[file.get_file()] = (uint64_t)info.uncompressed_size; } String dst = p_path.get_base_dir().plus_file(file); @@ -547,19 +553,26 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese EditorNode::get_singleton()->show_warning(TTR("Could not read custom HTML shell:") + "\n" + custom_html); return ERR_FILE_CANT_READ; } - Vector<uint8_t> buf; - buf.resize(f->get_len()); - f->get_buffer(buf.ptrw(), buf.size()); + html.resize(f->get_len()); + f->get_buffer(html.ptrw(), html.size()); memdelete(f); - _fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects); - + } + { + FileAccess *f = FileAccess::open(pck_path, FileAccess::READ); + if (f) { + file_sizes[pck_path.get_file()] = (uint64_t)f->get_len(); + memdelete(f); + f = NULL; + } + _fix_html(html, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects, file_sizes); f = FileAccess::open(p_path, FileAccess::WRITE); if (!f) { EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path); return ERR_FILE_CANT_WRITE; } - f->store_buffer(buf.ptr(), buf.size()); + f->store_buffer(html.ptr(), html.size()); memdelete(f); + html.resize(0); } Ref<Image> splash; diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js index 8c2636c7aba..82ff273ecf2 100644 --- a/platform/javascript/js/engine/config.js +++ b/platform/javascript/js/engine/config.js @@ -100,6 +100,11 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- * @type {Array.<string>} */ gdnativeLibs: [], + /** + * @ignore + * @type {Array.<string>} + */ + fileSizes: [], /** * A callback function for handling Godot's ``OS.execute`` calls. * @@ -219,6 +224,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy); this.persistentPaths = parse('persistentPaths', this.persistentPaths); this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs); + this.fileSizes = parse('fileSizes', this.fileSizes); this.args = parse('args', this.args); this.onExecute = parse('onExecute', this.onExecute); this.onExit = parse('onExit', this.onExit); diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js index e2d27c4c023..19a0552c8cc 100644 --- a/platform/javascript/js/engine/engine.js +++ b/platform/javascript/js/engine/engine.js @@ -35,14 +35,15 @@ const Engine = (function () { * Load the engine from the specified base path. * * @param {string} basePath Base path of the engine to load. + * @param {number=} [size=0] The file size if known. * @returns {Promise} A Promise that resolves once the engine is loaded. * * @function Engine.load */ - Engine.load = function (basePath) { + Engine.load = function (basePath, size) { if (loadPromise == null) { loadPath = basePath; - loadPromise = preloader.loadPromise(`${loadPath}.wasm`, true); + loadPromise = preloader.loadPromise(`${loadPath}.wasm`, size, true); requestAnimationFrame(preloader.animateProgress); } return loadPromise; @@ -96,7 +97,7 @@ const Engine = (function () { initPromise = Promise.reject(new Error('A base path must be provided when calling `init` and the engine is not loaded.')); return initPromise; } - Engine.load(basePath); + Engine.load(basePath, this.config.fileSizes[`${basePath}.wasm`]); } const me = this; function doInit(promise) { @@ -137,7 +138,7 @@ const Engine = (function () { * @returns {Promise} A Promise that resolves once the file is loaded. */ preloadFile: function (file, path) { - return preloader.preload(file, path); + return preloader.preload(file, path, this.config.fileSizes[file]); }, /** diff --git a/platform/javascript/js/engine/preloader.js b/platform/javascript/js/engine/preloader.js index 45bdcf26d4f..3535fdb361b 100644 --- a/platform/javascript/js/engine/preloader.js +++ b/platform/javascript/js/engine/preloader.js @@ -43,9 +43,9 @@ const Preloader = /** @constructor */ function () { // eslint-disable-line no-un }), { headers: response.headers }); } - function loadFetch(file, tracker, raw) { + function loadFetch(file, tracker, fileSize, raw) { tracker[file] = { - total: 0, + total: fileSize || 0, loaded: 0, done: false, }; @@ -117,16 +117,16 @@ const Preloader = /** @constructor */ function () { // eslint-disable-line no-un progressFunc = callback; }; - this.loadPromise = function (file, raw = false) { - return retry(loadFetch.bind(null, file, loadingFiles, raw), DOWNLOAD_ATTEMPTS_MAX); + this.loadPromise = function (file, fileSize, raw = false) { + return retry(loadFetch.bind(null, file, loadingFiles, fileSize, raw), DOWNLOAD_ATTEMPTS_MAX); }; this.preloadedFiles = []; - this.preload = function (pathOrBuffer, destPath) { + this.preload = function (pathOrBuffer, destPath, fileSize) { let buffer = null; if (typeof pathOrBuffer === 'string') { const me = this; - return this.loadPromise(pathOrBuffer).then(function (buf) { + return this.loadPromise(pathOrBuffer, fileSize).then(function (buf) { me.preloadedFiles.push({ path: destPath || pathOrBuffer, buffer: buf,