diff options
Diffstat (limited to 'platform/web')
20 files changed, 219 insertions, 251 deletions
diff --git a/platform/web/SCsub b/platform/web/SCsub index 78d7ce4074..e8d0181ede 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -6,7 +6,7 @@ web_files = [ "audio_driver_web.cpp", "display_server_web.cpp", "http_client_web.cpp", - "javascript_singleton.cpp", + "javascript_bridge_singleton.cpp", "web_main.cpp", "os_web.cpp", "api/web_tools_editor_plugin.cpp", @@ -35,18 +35,17 @@ for ext in env["JS_EXTERNS"]: sys_env["ENV"]["EMCC_CLOSURE_ARGS"] += " --externs " + ext.abspath build = [] -if env["gdnative_enabled"]: - build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] - if env["threads_enabled"]: - build_targets.append("#bin/godot${PROGSUFFIX}.worker.js") +build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm", "#bin/godot${PROGSUFFIX}.worker.js"] +if env["dlink_enabled"]: # Reset libraries. The main runtime will only link emscripten libraries, not godot ones. sys_env["LIBS"] = [] # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. sys_env.Append(LIBS=["idbfs.js"]) # Configure it as a main module (dynamic linking support). + sys_env["CCFLAGS"].remove("SIDE_MODULE=2") + sys_env["LINKFLAGS"].remove("SIDE_MODULE=2") sys_env.Append(CCFLAGS=["-s", "MAIN_MODULE=1"]) sys_env.Append(LINKFLAGS=["-s", "MAIN_MODULE=1"]) - sys_env.Append(CCFLAGS=["-s", "EXPORT_ALL=1"]) sys_env.Append(LINKFLAGS=["-s", "EXPORT_ALL=1"]) sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"]) # Force exporting the standard library (printf, malloc, etc.) @@ -55,16 +54,9 @@ if env["gdnative_enabled"]: sys = sys_env.Program(build_targets, ["web_runtime.cpp"]) # The side library, containing all Godot code. - wasm_env = env.Clone() - wasm_env.Append(CPPDEFINES=["WASM_GDNATIVE"]) # So that OS knows it can run GDNative libraries. - wasm_env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"]) - wasm_env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"]) - wasm = wasm_env.add_program("#bin/godot.side${PROGSUFFIX}.wasm", web_files) + wasm = env.add_program("#bin/godot.side${PROGSUFFIX}.wasm", web_files) build = sys + [wasm[0]] else: - build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] - if env["threads_enabled"]: - build_targets.append("#bin/godot${PROGSUFFIX}.worker.js") # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. sys_env.Append(LIBS=["idbfs.js"]) build = sys_env.Program(build_targets, web_files + ["web_runtime.cpp"]) @@ -88,6 +80,8 @@ wrap_list = [ ] js_wrapped = env.Textfile("#bin/godot", [env.File(f) for f in wrap_list], TEXTFILESUFFIX="${PROGSUFFIX}.wrapped.js") -# Extra will be the thread worker, or the GDNative side, or None -extra = build[2:] if len(build) > 2 else None -env.CreateTemplateZip(js_wrapped, build[1], extra) +# 0 - unwrapped js file (use wrapped one instead) +# 1 - wasm file +# 2 - worker file +# 3 - wasm side (when dlink is enabled). +env.CreateTemplateZip(js_wrapped, build[1], build[2], build[3] if len(build) > 3 else None) diff --git a/platform/web/api/api.cpp b/platform/web/api/api.cpp index a724b0456d..e637f2aef2 100644 --- a/platform/web/api/api.cpp +++ b/platform/web/api/api.cpp @@ -30,66 +30,66 @@ #include "api.h" #include "core/config/engine.h" -#include "javascript_singleton.h" +#include "javascript_bridge_singleton.h" #include "web_tools_editor_plugin.h" -static JavaScript *javascript_singleton; +static JavaScriptBridge *javascript_bridge_singleton; void register_web_api() { WebToolsEditorPlugin::initialize(); GDREGISTER_ABSTRACT_CLASS(JavaScriptObject); - GDREGISTER_ABSTRACT_CLASS(JavaScript); - javascript_singleton = memnew(JavaScript); - Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_singleton)); + GDREGISTER_ABSTRACT_CLASS(JavaScriptBridge); + javascript_bridge_singleton = memnew(JavaScriptBridge); + Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScriptBridge", javascript_bridge_singleton)); } void unregister_web_api() { - memdelete(javascript_singleton); + memdelete(javascript_bridge_singleton); } -JavaScript *JavaScript::singleton = nullptr; +JavaScriptBridge *JavaScriptBridge::singleton = nullptr; -JavaScript *JavaScript::get_singleton() { +JavaScriptBridge *JavaScriptBridge::get_singleton() { return singleton; } -JavaScript::JavaScript() { - ERR_FAIL_COND_MSG(singleton != nullptr, "JavaScript singleton already exist."); +JavaScriptBridge::JavaScriptBridge() { + ERR_FAIL_COND_MSG(singleton != nullptr, "JavaScriptBridge singleton already exist."); singleton = this; } -JavaScript::~JavaScript() {} +JavaScriptBridge::~JavaScriptBridge() {} -void JavaScript::_bind_methods() { - ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScript::eval, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_interface", "interface"), &JavaScript::get_interface); - ClassDB::bind_method(D_METHOD("create_callback", "callable"), &JavaScript::create_callback); +void JavaScriptBridge::_bind_methods() { + ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScriptBridge::eval, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_interface", "interface"), &JavaScriptBridge::get_interface); + ClassDB::bind_method(D_METHOD("create_callback", "callable"), &JavaScriptBridge::create_callback); { MethodInfo mi; mi.name = "create_object"; mi.arguments.push_back(PropertyInfo(Variant::STRING, "object")); - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "create_object", &JavaScript::_create_object_bind, mi); + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "create_object", &JavaScriptBridge::_create_object_bind, mi); } - ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScript::download_buffer, DEFVAL("application/octet-stream")); - ClassDB::bind_method(D_METHOD("pwa_needs_update"), &JavaScript::pwa_needs_update); - ClassDB::bind_method(D_METHOD("pwa_update"), &JavaScript::pwa_update); + ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScriptBridge::download_buffer, DEFVAL("application/octet-stream")); + ClassDB::bind_method(D_METHOD("pwa_needs_update"), &JavaScriptBridge::pwa_needs_update); + ClassDB::bind_method(D_METHOD("pwa_update"), &JavaScriptBridge::pwa_update); ADD_SIGNAL(MethodInfo("pwa_update_available")); } #if !defined(WEB_ENABLED) || !defined(JAVASCRIPT_EVAL_ENABLED) -Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { +Variant JavaScriptBridge::eval(const String &p_code, bool p_use_global_exec_context) { return Variant(); } -Ref<JavaScriptObject> JavaScript::get_interface(const String &p_interface) { +Ref<JavaScriptObject> JavaScriptBridge::get_interface(const String &p_interface) { return Ref<JavaScriptObject>(); } -Ref<JavaScriptObject> JavaScript::create_callback(const Callable &p_callable) { +Ref<JavaScriptObject> JavaScriptBridge::create_callback(const Callable &p_callable) { return Ref<JavaScriptObject>(); } -Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +Variant JavaScriptBridge::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 0; @@ -105,12 +105,12 @@ Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, } #endif #if !defined(WEB_ENABLED) -bool JavaScript::pwa_needs_update() const { +bool JavaScriptBridge::pwa_needs_update() const { return false; } -Error JavaScript::pwa_update() { +Error JavaScriptBridge::pwa_update() { return ERR_UNAVAILABLE; } -void JavaScript::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) { +void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) { } #endif diff --git a/platform/web/api/javascript_singleton.h b/platform/web/api/javascript_bridge_singleton.h index e93b0a18a1..1e7b5a1699 100644 --- a/platform/web/api/javascript_singleton.h +++ b/platform/web/api/javascript_bridge_singleton.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* javascript_singleton.h */ +/* javascript_bridge_singleton.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef JAVASCRIPT_SINGLETON_H -#define JAVASCRIPT_SINGLETON_H +#ifndef JAVASCRIPT_BRIDGE_SINGLETON_H +#define JAVASCRIPT_BRIDGE_SINGLETON_H #include "core/object/class_db.h" #include "core/object/ref_counted.h" @@ -44,11 +44,11 @@ protected: virtual void _get_property_list(List<PropertyInfo> *p_list) const {} }; -class JavaScript : public Object { +class JavaScriptBridge : public Object { private: - GDCLASS(JavaScript, Object); + GDCLASS(JavaScriptBridge, Object); - static JavaScript *singleton; + static JavaScriptBridge *singleton; protected: static void _bind_methods(); @@ -62,9 +62,9 @@ public: bool pwa_needs_update() const; Error pwa_update(); - static JavaScript *get_singleton(); - JavaScript(); - ~JavaScript(); + static JavaScriptBridge *get_singleton(); + JavaScriptBridge(); + ~JavaScriptBridge(); }; -#endif // JAVASCRIPT_SINGLETON_H +#endif // JAVASCRIPT_BRIDGE_SINGLETON_H diff --git a/platform/web/api/web_tools_editor_plugin.cpp b/platform/web/api/web_tools_editor_plugin.cpp index 6f684a73c2..46fcb2d452 100644 --- a/platform/web/api/web_tools_editor_plugin.cpp +++ b/platform/web/api/web_tools_editor_plugin.cpp @@ -76,7 +76,7 @@ void WebToolsEditorPlugin::_download_zip(Variant p_v) { const String datetime_safe = Time::get_singleton()->get_datetime_string_from_system(false, true).replace(" ", "_"); const String output_name = OS::get_singleton()->get_safe_dir_name(vformat("%s_%s.zip")); - const String output_path = String("/tmp").plus_file(output_name); + const String output_path = String("/tmp").path_join(output_name); zipFile zip = zipOpen2(output_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io); const String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/"; @@ -131,7 +131,7 @@ void WebToolsEditorPlugin::_zip_recursive(String p_path, String p_base_path, zip String cur = dir->get_next(); String project_data_dir_name = ProjectSettings::get_singleton()->get_project_data_dir_name(); while (!cur.is_empty()) { - String cs = p_path.plus_file(cur); + String cs = p_path.path_join(cur); if (cur == "." || cur == ".." || cur == project_data_dir_name) { // Skip } else if (dir->current_is_dir()) { diff --git a/platform/web/detect.py b/platform/web/detect.py index ae0ff2c4ea..08f964db92 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -31,15 +31,15 @@ def get_opts(): return [ ("initial_memory", "Initial WASM memory (in MiB)", 32), BoolVariable("use_assertions", "Use Emscripten runtime assertions", False), - BoolVariable("use_thinlto", "Use ThinLTO", False), BoolVariable("use_ubsan", "Use Emscripten undefined behavior sanitizer (UBSAN)", False), BoolVariable("use_asan", "Use Emscripten address sanitizer (ASAN)", False), BoolVariable("use_lsan", "Use Emscripten leak sanitizer (LSAN)", False), BoolVariable("use_safe_heap", "Use Emscripten SAFE_HEAP sanitizer", False), # eval() can be a security concern, so it can be disabled. BoolVariable("javascript_eval", "Enable JavaScript eval interface", True), - BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", True), - BoolVariable("gdnative_enabled", "Enable WebAssembly GDNative support (produces bigger binaries)", False), + BoolVariable( + "dlink_enabled", "Enable WebAssembly dynamic linking (GDExtension support). Produces bigger binaries", False + ), BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False), ] @@ -50,6 +50,13 @@ def get_flags(): ("tools", False), ("builtin_pcre2_with_jit", False), ("vulkan", False), + # Use -Os to prioritize optimizing for reduced file size. This is + # particularly valuable for the web platform because it directly + # decreases download time. + # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about + # 100 KiB over -Os, which does not justify the negative impact on + # run-time performance. + ("optimize", "size"), ] @@ -71,15 +78,12 @@ def configure(env): ## Build type if env["target"].startswith("release"): - # Use -Os to prioritize optimizing for reduced file size. This is - # particularly valuable for the web platform because it directly - # decreases download time. - # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about - # 100 KiB over -Os, which does not justify the negative impact on - # run-time performance. - if env["optimize"] != "none": + if env["optimize"] == "size": env.Append(CCFLAGS=["-Os"]) env.Append(LINKFLAGS=["-Os"]) + elif env["optimize"] == "speed": + env.Append(CCFLAGS=["-O3"]) + env.Append(LINKFLAGS=["-O3"]) if env["target"] == "release_debug": # Retain function names for backtraces at the cost of file size. @@ -93,21 +97,11 @@ def configure(env): env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"]) if env["tools"]: - if not env["threads_enabled"]: - print('Note: Forcing "threads_enabled=yes" as it is required for the web editor.') - env["threads_enabled"] = "yes" if env["initial_memory"] < 64: print('Note: Forcing "initial_memory=64" as it is required for the web editor.') env["initial_memory"] = 64 - env.Append(CCFLAGS=["-frtti"]) - elif env["builtin_icu"]: - env.Append(CCFLAGS=["-fno-exceptions", "-frtti"]) else: - # Disable exceptions and rtti on non-tools (template) builds - # These flags help keep the file size down. - env.Append(CCFLAGS=["-fno-exceptions", "-fno-rtti"]) - # Don't use dynamic_cast, necessary with no-rtti. - env.Append(CPPDEFINES=["NO_SAFE_CAST"]) + env.Append(CPPFLAGS=["-fno-exceptions"]) env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]]) @@ -115,12 +109,13 @@ def configure(env): env["ENV"] = os.environ # LTO - if env["use_thinlto"]: - env.Append(CCFLAGS=["-flto=thin"]) - env.Append(LINKFLAGS=["-flto=thin"]) - elif env["use_lto"]: - env.Append(CCFLAGS=["-flto=full"]) - env.Append(LINKFLAGS=["-flto=full"]) + if env["lto"] != "none": + if env["lto"] == "thin": + env.Append(CCFLAGS=["-flto=thin"]) + env.Append(LINKFLAGS=["-flto=thin"]) + else: + env.Append(CCFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-flto"]) # Sanitizers if env["use_ubsan"]: @@ -171,9 +166,9 @@ def configure(env): env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix") env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}" - # All intermediate files are just LLVM bitcode. + # All intermediate files are just object files. env["OBJPREFIX"] = "" - env["OBJSUFFIX"] = ".bc" + env["OBJSUFFIX"] = ".o" env["PROGPREFIX"] = "" # Program() output consists of multiple files, so specify suffixes manually at builder. env["PROGSUFFIX"] = "" @@ -196,31 +191,22 @@ def configure(env): env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"]) # Thread support (via SharedArrayBuffer). - if env["threads_enabled"]: - env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) - env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) - env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) - env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) - env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) - env.extra_suffix = ".threads" + env.extra_suffix - else: - env.Append(CPPDEFINES=["NO_THREADS"]) + env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) + env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) + env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) - if env["gdnative_enabled"]: + if env["dlink_enabled"]: cc_version = get_compiler_version(env) cc_semver = (int(cc_version["major"]), int(cc_version["minor"]), int(cc_version["patch"])) - if cc_semver < (2, 0, 10): - print("GDNative support requires emscripten >= 2.0.10, detected: %s.%s.%s" % cc_semver) + if cc_semver < (3, 1, 14): + print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) sys.exit(255) - if env["threads_enabled"] and cc_semver < (3, 1, 14): - print("Threads and GDNative requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) - sys.exit(255) - env.Append(CCFLAGS=["-s", "RELOCATABLE=1"]) - env.Append(LINKFLAGS=["-s", "RELOCATABLE=1"]) - # Weak symbols are broken upstream: https://github.com/emscripten-core/emscripten/issues/12819 - env.Append(CPPDEFINES=["ZSTD_HAVE_WEAK_SYMBOLS=0"]) - env.extra_suffix = ".gdnative" + env.extra_suffix + env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"]) + env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"]) + env.extra_suffix = ".dlink" + env.extra_suffix # Reduce code size by generating less support code (e.g. skip NodeJS support). env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"]) @@ -241,3 +227,7 @@ def configure(env): # Add code that allow exiting runtime. env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"]) + + # This workaround creates a closure that prevents the garbage collector from freeing the WebGL context. + # We also only use WebGL2, and changing context version is not widely supported anyway. + env.Append(LINKFLAGS=["-s", "GL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0"]) diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index b36f9d14a4..f6a61b18e4 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -764,10 +764,10 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode if (wants_webgl2 && !webgl2_init_failed) { EmscriptenWebGLContextAttributes attributes; emscripten_webgl_init_context_attributes(&attributes); - //attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed"); - attributes.alpha = true; + attributes.alpha = OS::get_singleton()->is_layered_allowed(); attributes.antialias = false; attributes.majorVersion = 2; + attributes.explicitSwapControl = true; webgl_ctx = emscripten_webgl_create_context(canvas_id, &attributes); if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) { @@ -997,7 +997,7 @@ void DisplayServerWeb::window_set_mode(WindowMode p_mode, WindowID p_window) { } break; case WINDOW_MODE_MAXIMIZED: case WINDOW_MODE_MINIMIZED: - WARN_PRINT("WindowMode MAXIMIZED and MINIMIZED are not supported in Web platform."); + // WindowMode MAXIMIZED and MINIMIZED are not supported in Web platform. break; default: break; diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py index b7b1026ef7..6045bc6fbd 100644 --- a/platform/web/emscripten_helpers.py +++ b/platform/web/emscripten_helpers.py @@ -37,26 +37,25 @@ def create_engine_file(env, target, source, externs): return env.Textfile(target, [env.File(s) for s in source]) -def create_template_zip(env, js, wasm, extra): +def create_template_zip(env, js, wasm, worker, side): binary_name = "godot.tools" if env["tools"] else "godot" zip_dir = env.Dir("#bin/.web_zip") in_files = [ js, wasm, + worker, "#platform/web/js/libs/audio.worklet.js", ] out_files = [ zip_dir.File(binary_name + ".js"), zip_dir.File(binary_name + ".wasm"), + zip_dir.File(binary_name + ".worker.js"), zip_dir.File(binary_name + ".audio.worklet.js"), ] - # GDNative/Threads specific - if env["gdnative_enabled"]: - in_files.append(extra.pop()) # Runtime + # Dynamic linking (extensions) specific. + if env["dlink_enabled"]: + in_files.append(side) # Side wasm (contains the actual Godot code). out_files.append(zip_dir.File(binary_name + ".side.wasm")) - if env["threads_enabled"]: - in_files.append(extra.pop()) # Worker - out_files.append(zip_dir.File(binary_name + ".worker.js")) service_worker = "#misc/dist/html/service-worker.js" if env["tools"]: diff --git a/platform/web/export/editor_http_server.h b/platform/web/export/editor_http_server.h index 1e50e60a28..fa0010ec8d 100644 --- a/platform/web/export/editor_http_server.h +++ b/platform/web/export/editor_http_server.h @@ -32,7 +32,7 @@ #define WEB_EDITOR_HTTP_SERVER_H #include "core/io/image_loader.h" -#include "core/io/stream_peer_ssl.h" +#include "core/io/stream_peer_tls.h" #include "core/io/tcp_server.h" #include "core/io/zip_io.h" #include "editor/editor_paths.h" @@ -42,18 +42,18 @@ private: Ref<TCPServer> server; HashMap<String, String> mimes; Ref<StreamPeerTCP> tcp; - Ref<StreamPeerSSL> ssl; + Ref<StreamPeerTLS> tls; Ref<StreamPeer> peer; Ref<CryptoKey> key; Ref<X509Certificate> cert; - bool use_ssl = false; + bool use_tls = false; uint64_t time = 0; uint8_t req_buf[4096]; int req_pos = 0; void _clear_client() { peer = Ref<StreamPeer>(); - ssl = Ref<StreamPeerSSL>(); + tls = Ref<StreamPeerTLS>(); tcp = Ref<StreamPeerTCP>(); memset(req_buf, 0, sizeof(req_buf)); time = 0; @@ -62,8 +62,8 @@ private: void _set_internal_certs(Ref<Crypto> p_crypto) { const String cache_path = EditorPaths::get_singleton()->get_cache_dir(); - const String key_path = cache_path.plus_file("html5_server.key"); - const String crt_path = cache_path.plus_file("html5_server.crt"); + const String key_path = cache_path.path_join("html5_server.key"); + const String crt_path = cache_path.path_join("html5_server.crt"); bool regen = !FileAccess::exists(key_path) || !FileAccess::exists(crt_path); if (!regen) { key = Ref<CryptoKey>(CryptoKey::create()); @@ -98,19 +98,19 @@ public: _clear_client(); } - Error listen(int p_port, IPAddress p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) { - use_ssl = p_use_ssl; - if (use_ssl) { + Error listen(int p_port, IPAddress p_address, bool p_use_tls, String p_tls_key, String p_tls_cert) { + use_tls = p_use_tls; + if (use_tls) { Ref<Crypto> crypto = Crypto::create(); if (crypto.is_null()) { return ERR_UNAVAILABLE; } - if (!p_ssl_key.is_empty() && !p_ssl_cert.is_empty()) { + if (!p_tls_key.is_empty() && !p_tls_cert.is_empty()) { key = Ref<CryptoKey>(CryptoKey::create()); - Error err = key->load(p_ssl_key); + Error err = key->load(p_tls_key); ERR_FAIL_COND_V(err != OK, err); cert = Ref<X509Certificate>(X509Certificate::create()); - err = cert->load(p_ssl_cert); + err = cert->load(p_tls_cert); ERR_FAIL_COND_V(err != OK, err); } else { _set_internal_certs(crypto); @@ -139,8 +139,8 @@ public: const String req_file = path.get_file(); const String req_ext = path.get_extension(); - const String cache_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); - const String filepath = cache_path.plus_file(req_file); + const String cache_path = EditorPaths::get_singleton()->get_cache_dir().path_join("web"); + const String filepath = cache_path.path_join(req_file); if (!mimes.has(req_ext) || !FileAccess::exists(filepath)) { String s = "HTTP/1.1 404 Not Found\r\n"; @@ -201,22 +201,22 @@ public: return; } - if (use_ssl) { - if (ssl.is_null()) { - ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); - peer = ssl; - ssl->set_blocking_handshake_enabled(false); - if (ssl->accept_stream(tcp, key, cert) != OK) { + if (use_tls) { + if (tls.is_null()) { + tls = Ref<StreamPeerTLS>(StreamPeerTLS::create()); + peer = tls; + tls->set_blocking_handshake_enabled(false); + if (tls->accept_stream(tcp, key, cert) != OK) { _clear_client(); return; } } - ssl->poll(); - if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) { + tls->poll(); + if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) { // Still handshaking, keep waiting. return; } - if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { + if (tls->get_status() != StreamPeerTLS::STATUS_CONNECTED) { _clear_client(); return; } diff --git a/platform/web/export/export.cpp b/platform/web/export/export.cpp index 3d40f2c10d..4b4e8b2705 100644 --- a/platform/web/export/export.cpp +++ b/platform/web/export/export.cpp @@ -34,14 +34,16 @@ #include "export_plugin.h" void register_web_exporter() { +#ifndef ANDROID_ENABLED EDITOR_DEF("export/web/http_host", "localhost"); EDITOR_DEF("export/web/http_port", 8060); - EDITOR_DEF("export/web/use_ssl", false); - EDITOR_DEF("export/web/ssl_key", ""); - EDITOR_DEF("export/web/ssl_certificate", ""); + EDITOR_DEF("export/web/use_tls", false); + EDITOR_DEF("export/web/tls_key", ""); + EDITOR_DEF("export/web/tls_certificate", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "export/web/http_port", PROPERTY_HINT_RANGE, "1,65535,1")); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/ssl_key", PROPERTY_HINT_GLOBAL_FILE, "*.key")); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/ssl_certificate", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem")); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/tls_key", PROPERTY_HINT_GLOBAL_FILE, "*.key")); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/tls_certificate", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem")); +#endif Ref<EditorExportPlatformWeb> platform; platform.instantiate(); diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 996d1db2e7..306453c1eb 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -75,7 +75,7 @@ Error EditorExportPlatformWeb::_extract_template(const String &p_template, const unzCloseCurrentFile(pkg); //write - String dst = p_dir.plus_file(file.replace("godot", p_name)); + String dst = p_dir.path_join(file.replace("godot", p_name)); Ref<FileAccess> f = FileAccess::open(dst, FileAccess::WRITE); if (f.is_null()) { add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not write file: \"%s\"."), dst)); @@ -162,7 +162,7 @@ void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<Edito Error EditorExportPlatformWeb::_add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr) { const String name = p_path.get_file().get_basename(); const String icon_name = vformat("%s.%dx%d.png", name, p_size, p_size); - const String icon_dest = p_path.get_base_dir().plus_file(icon_name); + const String icon_dest = p_path.get_base_dir().path_join(icon_name); Ref<Image> icon; if (!p_icon.is_empty()) { @@ -201,7 +201,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese // Service worker const String dir = p_path.get_base_dir(); const String name = p_path.get_file().get_basename(); - const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + bool extensions = (bool)p_preset->get("variant/extensions_support"); HashMap<String, String> replaces; replaces["@GODOT_VERSION@"] = String::num_int64(OS::get_singleton()->get_unix_time()) + "|" + String::num_int64(OS::get_singleton()->get_ticks_usec()); replaces["@GODOT_NAME@"] = proj_name.substr(0, 16); @@ -216,17 +216,15 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese cache_files.push_back(name + ".icon.png"); cache_files.push_back(name + ".apple-touch-icon.png"); } - if (mode & EXPORT_MODE_THREADS) { - cache_files.push_back(name + ".worker.js"); - cache_files.push_back(name + ".audio.worklet.js"); - } + cache_files.push_back(name + ".worker.js"); + cache_files.push_back(name + ".audio.worklet.js"); replaces["@GODOT_CACHE@"] = Variant(cache_files).to_json_string(); // Heavy files that are cached on demand. Array opt_cache_files; opt_cache_files.push_back(name + ".wasm"); opt_cache_files.push_back(name + ".pck"); - if (mode & EXPORT_MODE_GDNATIVE) { + if (extensions) { opt_cache_files.push_back(name + ".side.wasm"); for (int i = 0; i < p_shared_objects.size(); i++) { opt_cache_files.push_back(p_shared_objects[i].path.get_file()); @@ -234,7 +232,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese } replaces["@GODOT_OPT_CACHE@"] = Variant(opt_cache_files).to_json_string(); - const String sw_path = dir.plus_file(name + ".service.worker.js"); + const String sw_path = dir.path_join(name + ".service.worker.js"); Vector<uint8_t> sw; { Ref<FileAccess> f = FileAccess::open(sw_path, FileAccess::READ); @@ -246,7 +244,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese f->get_buffer(sw.ptrw(), sw.size()); } _replace_strings(replaces, sw); - Error err = _write_or_error(sw.ptr(), sw.size(), dir.plus_file(name + ".service.worker.js")); + Error err = _write_or_error(sw.ptr(), sw.size(), dir.path_join(name + ".service.worker.js")); if (err != OK) { return err; } @@ -255,7 +253,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese const String offline_page = p_preset->get("progressive_web_app/offline_page"); if (!offline_page.is_empty()) { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - const String offline_dest = dir.plus_file(name + ".offline.html"); + const String offline_dest = dir.path_join(name + ".offline.html"); err = da->copy(ProjectSettings::get_singleton()->globalize_path(offline_page), offline_dest); if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("PWA"), vformat(TTR("Could not read file: \"%s\"."), offline_dest)); @@ -295,7 +293,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese manifest["icons"] = icons_arr; CharString cs = Variant(manifest).to_json_string().utf8(); - err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.plus_file(name + ".manifest.json")); + err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.path_join(name + ".manifest.json")); if (err != OK) { return err; } @@ -317,20 +315,14 @@ void EditorExportPlatformWeb::get_preset_features(const Ref<EditorExportPreset> r_features->push_back("etc2"); } } - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - if (mode & EXPORT_MODE_THREADS) { - r_features->push_back("threads"); - } - if (mode & EXPORT_MODE_GDNATIVE) { - r_features->push_back("wasm32"); - } + r_features->push_back("wasm32"); } void EditorExportPlatformWeb::get_export_options(List<ExportOption> *r_options) { r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type. + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/extensions_support"), false)); // Export type. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer @@ -363,22 +355,13 @@ Ref<Texture2D> EditorExportPlatformWeb::get_logo() const { } bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { -#ifndef DEV_ENABLED - // We don't provide export templates for the Web platform currently as there - // is no suitable renderer to use with them. So we forbid exporting and tell - // users why. This is skipped in DEV_ENABLED so that contributors can still test - // the pipeline once we start having WebGL or WebGPU support. - r_error = "The Web platform is currently not supported in Godot 4.0, as there is no suitable renderer for it.\n"; - return false; -#endif - String err; bool valid = false; - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + bool extensions = (bool)p_preset->get("variant/extensions_support"); // Look for export templates (first official, and if defined custom templates). - bool dvalid = exists_export_template(_get_template_name(mode, true), &err); - bool rvalid = exists_export_template(_get_template_name(mode, false), &err); + bool dvalid = exists_export_template(_get_template_name(extensions, true), &err); + bool rvalid = exists_export_template(_get_template_name(extensions, false), &err); if (p_preset->get("custom_template/debug") != "") { dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); @@ -404,15 +387,6 @@ bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExp } bool EditorExportPlatformWeb::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { -#ifndef DEV_ENABLED - // We don't provide export templates for the Web platform currently as there - // is no suitable renderer to use with them. So we forbid exporting and tell - // users why. This is skipped in DEV_ENABLED so that contributors can still test - // the pipeline once we start having WebGL or WebGPU support. - r_error = "The Web platform is currently not supported in Godot 4.0, as there is no suitable renderer for it.\n"; - return false; -#endif - String err; bool valid = true; @@ -456,8 +430,8 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p String template_path = p_debug ? custom_debug : custom_release; template_path = template_path.strip_edges(); if (template_path.is_empty()) { - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - template_path = find_export_template(_get_template_name(mode, p_debug)); + bool extensions = (bool)p_preset->get("variant/extensions_support"); + template_path = find_export_template(_get_template_name(extensions, p_debug)); } if (!DirAccess::exists(base_dir)) { @@ -481,7 +455,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < shared_objects.size(); i++) { - String dst = base_dir.plus_file(shared_objects[i].path.get_file()); + String dst = base_dir.path_join(shared_objects[i].path.get_file()); error = da->copy(shared_objects[i].path, dst); if (error != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not write file: \"%s\"."), shared_objects[i].path.get_file())); @@ -601,7 +575,7 @@ Error EditorExportPlatformWeb::run(const Ref<EditorExportPreset> &p_preset, int return OK; } - const String dest = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); + const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("web"); Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (!da->dir_exists(dest)) { Error err = da->make_dir_recursive(dest); @@ -611,7 +585,7 @@ Error EditorExportPlatformWeb::run(const Ref<EditorExportPreset> &p_preset, int } } - const String basepath = dest.plus_file("tmp_js_export"); + const String basepath = dest.path_join("tmp_js_export"); Error err = export_project(p_preset, true, basepath + ".html", p_debug_flags); if (err != OK) { // Export generates several files, clean them up on failure. @@ -641,23 +615,23 @@ Error EditorExportPlatformWeb::run(const Ref<EditorExportPreset> &p_preset, int } ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'."); - const bool use_ssl = EDITOR_GET("export/web/use_ssl"); - const String ssl_key = EDITOR_GET("export/web/ssl_key"); - const String ssl_cert = EDITOR_GET("export/web/ssl_certificate"); + const bool use_tls = EDITOR_GET("export/web/use_tls"); + const String tls_key = EDITOR_GET("export/web/tls_key"); + const String tls_cert = EDITOR_GET("export/web/tls_certificate"); // Restart server. { MutexLock lock(server_lock); server->stop(); - err = server->listen(bind_port, bind_ip, use_ssl, ssl_key, ssl_cert); + err = server->listen(bind_port, bind_ip, use_tls, tls_key, tls_cert); } if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), vformat(TTR("Error starting HTTP server: %d."), err)); return err; } - OS::get_singleton()->shell_open(String((use_ssl ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html")); + OS::get_singleton()->shell_open(String((use_tls ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html")); // FIXME: Find out how to clean up export files after running the successfully // exported game. Might not be trivial. return OK; diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h index 9c1bf2a5ef..f11e38df09 100644 --- a/platform/web/export/export_plugin.h +++ b/platform/web/export/export_plugin.h @@ -33,7 +33,7 @@ #include "core/config/project_settings.h" #include "core/io/image_loader.h" -#include "core/io/stream_peer_ssl.h" +#include "core/io/stream_peer_tls.h" #include "core/io/tcp_server.h" #include "core/io/zip_io.h" #include "editor/editor_node.h" @@ -57,20 +57,10 @@ class EditorExportPlatformWeb : public EditorExportPlatform { Mutex server_lock; Thread server_thread; - enum ExportMode { - EXPORT_MODE_NORMAL = 0, - EXPORT_MODE_THREADS = 1, - EXPORT_MODE_GDNATIVE = 2, - EXPORT_MODE_THREADS_GDNATIVE = 3, - }; - - String _get_template_name(ExportMode p_mode, bool p_debug) const { - String name = "webassembly"; - if (p_mode & EXPORT_MODE_GDNATIVE) { - name += "_gdnative"; - } - if (p_mode & EXPORT_MODE_THREADS) { - name += "_threads"; + String _get_template_name(bool p_extension, bool p_debug) const { + String name = "web"; + if (p_extension) { + name += "_dlink"; } if (p_debug) { name += "_debug.zip"; diff --git a/platform/web/http_client_web.cpp b/platform/web/http_client_web.cpp index bfdea95f4a..d045275826 100644 --- a/platform/web/http_client_web.cpp +++ b/platform/web/http_client_web.cpp @@ -37,14 +37,14 @@ void HTTPClientWeb::_parse_headers(int p_len, const char **p_headers, void *p_re } } -Error HTTPClientWeb::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) { +Error HTTPClientWeb::connect_to_host(const String &p_host, int p_port, bool p_tls, bool p_verify_host) { close(); - if (p_ssl && !p_verify_host) { + if (p_tls && !p_verify_host) { WARN_PRINT("Disabling HTTPClientWeb's host verification is not supported for the Web platform, host will be verified"); } port = p_port; - use_tls = p_ssl; + use_tls = p_tls; host = p_host; diff --git a/platform/web/http_client_web.h b/platform/web/http_client_web.h index ff776d72af..5059b4693e 100644 --- a/platform/web/http_client_web.h +++ b/platform/web/http_client_web.h @@ -86,7 +86,7 @@ public: Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override; - Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) override; + Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) override; void set_connection(const Ref<StreamPeer> &p_connection) override; Ref<StreamPeer> get_connection() const override; void close() override; diff --git a/platform/web/javascript_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp index 36ab4db452..69cd0cece1 100644 --- a/platform/web/javascript_singleton.cpp +++ b/platform/web/javascript_bridge_singleton.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* javascript_singleton.cpp */ +/* javascript_bridge_singleton.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "api/javascript_singleton.h" +#include "api/javascript_bridge_singleton.h" #include "emscripten.h" #include "os_web.h" @@ -62,7 +62,7 @@ extern int godot_js_wrapper_create_object(const char *p_method, void **p_args, i class JavaScriptObjectImpl : public JavaScriptObject { private: - friend class JavaScript; + friend class JavaScriptBridge; int _js_id = 0; Callable _callable; @@ -272,20 +272,20 @@ void JavaScriptObjectImpl::_callback(void *p_ref, int p_args_id, int p_argc) { } } -Ref<JavaScriptObject> JavaScript::create_callback(const Callable &p_callable) { +Ref<JavaScriptObject> JavaScriptBridge::create_callback(const Callable &p_callable) { Ref<JavaScriptObjectImpl> out = memnew(JavaScriptObjectImpl); out->_callable = p_callable; out->_js_id = godot_js_wrapper_create_cb(out.ptr(), JavaScriptObjectImpl::_callback); return out; } -Ref<JavaScriptObject> JavaScript::get_interface(const String &p_interface) { +Ref<JavaScriptObject> JavaScriptBridge::get_interface(const String &p_interface) { int js_id = godot_js_wrapper_interface_get(p_interface.utf8().get_data()); ERR_FAIL_COND_V_MSG(!js_id, Ref<JavaScriptObject>(), "No interface '" + p_interface + "' registered."); return Ref<JavaScriptObject>(memnew(JavaScriptObjectImpl(js_id))); } -Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +Variant JavaScriptBridge::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 0; @@ -328,7 +328,7 @@ void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_le return arr->ptrw(); } -Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { +Variant JavaScriptBridge::eval(const String &p_code, bool p_use_global_exec_context) { union js_eval_ret js_data; PackedByteArray arr; VectorWriteProxy<uint8_t> arr_write; @@ -354,13 +354,13 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { } #endif // JAVASCRIPT_EVAL_ENABLED -void JavaScript::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) { +void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) { godot_js_os_download_buffer(p_arr.ptr(), p_arr.size(), p_name.utf8().get_data(), p_mime.utf8().get_data()); } -bool JavaScript::pwa_needs_update() const { +bool JavaScriptBridge::pwa_needs_update() const { return OS_Web::get_singleton()->pwa_needs_update(); } -Error JavaScript::pwa_update() { +Error JavaScriptBridge::pwa_update() { return OS_Web::get_singleton()->pwa_update(); } diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js index 9c4b6c2012..41be7b2512 100644 --- a/platform/web/js/engine/config.js +++ b/platform/web/js/engine/config.js @@ -317,7 +317,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- if (!(this.canvas instanceof HTMLCanvasElement)) { const nodes = document.getElementsByTagName('canvas'); if (nodes.length && nodes[0] instanceof HTMLCanvasElement) { - this.canvas = nodes[0]; + const first = nodes[0]; + this.canvas = /** @type {!HTMLCanvasElement} */ (first); } if (!this.canvas) { throw new Error('No canvas found in page'); diff --git a/platform/web/js/libs/audio.worklet.js b/platform/web/js/libs/audio.worklet.js index ea4d8cb221..daf5c9ef12 100644 --- a/platform/web/js/libs/audio.worklet.js +++ b/platform/web/js/libs/audio.worklet.js @@ -133,6 +133,8 @@ class GodotProcessor extends AudioWorkletProcessor { this.running = false; this.output = null; this.input = null; + this.lock = null; + this.notifier = null; } else if (p_cmd === 'start_nothreads') { this.output = new RingBuffer(p_data[0], p_data[0].length, false); } else if (p_cmd === 'chunk') { diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js index 756c1ac595..68e100cca0 100644 --- a/platform/web/js/libs/library_godot_audio.js +++ b/platform/web/js/libs/library_godot_audio.js @@ -339,16 +339,21 @@ const GodotAudioWorklet = { if (GodotAudioWorklet.promise === null) { return; } - GodotAudioWorklet.promise.then(function () { + const p = GodotAudioWorklet.promise; + p.then(function () { GodotAudioWorklet.worklet.port.postMessage({ 'cmd': 'stop', 'data': null, }); GodotAudioWorklet.worklet.disconnect(); + GodotAudioWorklet.worklet.port.onmessage = null; GodotAudioWorklet.worklet = null; GodotAudioWorklet.promise = null; resolve(); - }).catch(function (err) { /* aborted? */ }); + }).catch(function (err) { + // Aborted? + GodotRuntime.error(err); + }); }); }, }, diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js index 377eec3234..ce64fb98c0 100644 --- a/platform/web/js/libs/library_godot_os.js +++ b/platform/web/js/libs/library_godot_os.js @@ -106,12 +106,14 @@ autoAddDeps(GodotConfig, '$GodotConfig'); mergeInto(LibraryManager.library, GodotConfig); const GodotFS = { - $GodotFS__deps: ['$ERRNO_CODES', '$FS', '$IDBFS', '$GodotRuntime'], + $GodotFS__deps: ['$FS', '$IDBFS', '$GodotRuntime'], $GodotFS__postset: [ 'Module["initFS"] = GodotFS.init;', 'Module["copyToFS"] = GodotFS.copy_to_fs;', ].join(''), $GodotFS: { + // ERRNO_CODES works every odd version of emscripten, but this will break too eventually. + ENOENT: 44, _idbfs: false, _syncing: false, _mount_points: [], @@ -138,8 +140,9 @@ const GodotFS = { try { FS.stat(dir); } catch (e) { - if (e.errno !== ERRNO_CODES.ENOENT) { - throw e; + if (e.errno !== GodotFS.ENOENT) { + // Let mkdirTree throw in case, we cannot trust the above check. + GodotRuntime.error(e); } FS.mkdirTree(dir); } @@ -208,8 +211,9 @@ const GodotFS = { try { FS.stat(dir); } catch (e) { - if (e.errno !== ERRNO_CODES.ENOENT) { - throw e; + if (e.errno !== GodotFS.ENOENT) { + // Let mkdirTree throw in case, we cannot trust the above check. + GodotRuntime.error(e); } FS.mkdirTree(dir); } diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index 461dc71119..ebe56924df 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -45,7 +45,7 @@ #include <emscripten.h> #include <stdlib.h> -#include "api/javascript_singleton.h" +#include "api/javascript_bridge_singleton.h" #include "godot_js.h" void OS_Web::alert(const String &p_alert, const String &p_title) { @@ -140,26 +140,9 @@ int OS_Web::get_processor_count() const { } bool OS_Web::_check_internal_feature_support(const String &p_feature) { - if (p_feature == "html5" || p_feature == "web") { - return true; - } - -#ifdef JAVASCRIPT_EVAL_ENABLED if (p_feature == "web") { return true; } -#endif -#ifndef NO_THREADS - if (p_feature == "threads") { - return true; - } -#endif -#if WASM_GDNATIVE - if (p_feature == "wasm32") { - return true; - } -#endif - return false; } @@ -216,8 +199,8 @@ void OS_Web::update_pwa_state_callback() { if (OS_Web::get_singleton()) { OS_Web::get_singleton()->pwa_is_waiting = true; } - if (JavaScript::get_singleton()) { - JavaScript::get_singleton()->emit_signal("pwa_update_available"); + if (JavaScriptBridge::get_singleton()) { + JavaScriptBridge::get_singleton()->emit_signal("pwa_update_available"); } } diff --git a/platform/web/web_main.cpp b/platform/web/web_main.cpp index 0f4411727a..287fe48c4d 100644 --- a/platform/web/web_main.cpp +++ b/platform/web/web_main.cpp @@ -55,6 +55,18 @@ void cleanup_after_sync() { emscripten_set_main_loop(exit_callback, -1, false); } +void early_cleanup() { + emscripten_cancel_main_loop(); // After this, we can exit! + int exit_code = OS_Web::get_singleton()->get_exit_code(); + memdelete(os); + os = nullptr; + emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing. +} + +void early_cleanup_sync() { + emscripten_set_main_loop(early_cleanup, -1, false); +} + void main_loop_callback() { uint64_t current_ticks = os->get_ticks_usec(); @@ -87,7 +99,19 @@ extern EMSCRIPTEN_KEEPALIVE int godot_web_main(int argc, char *argv[]) { // We must override main when testing is enabled TEST_MAIN_OVERRIDE - Main::setup(argv[0], argc - 1, &argv[1]); + Error err = Main::setup(argv[0], argc - 1, &argv[1]); + + // Proper shutdown in case of setup failure. + if (err != OK) { + int exit_code = (int)err; + if (err == ERR_HELP) { + exit_code = 0; // Called with --help. + } + os->set_exit_code(exit_code); + // Will only exit after sync. + godot_js_os_finish_async(early_cleanup_sync); + return exit_code; + } // Ease up compatibility. ResourceLoader::set_abort_on_missing_resources(false); |