[HTML5] EditorRunNative works with GDNative.

This "breaks" our loading bar logic (libraries are not counted).
Fixing it is non trivial and probably deserves investigating a different
strategy.
This commit is contained in:
Fabio Alessandrelli 2020-12-05 00:37:41 +01:00
parent ca34b5e57a
commit 611c4998e8
6 changed files with 65 additions and 17 deletions

View File

@ -145,6 +145,7 @@ $GODOT_HEAD_INCLUDE
const EXECUTABLE_NAME = '$GODOT_BASENAME';
const MAIN_PACK = '$GODOT_BASENAME.pck';
const EXTRA_ARGS = JSON.parse('$GODOT_ARGS');
const GDNATIVE_LIBS = [$GODOT_GDNATIVE_LIBS];
const INDETERMINATE_STATUS_STEP_MS = 100;
const FULL_WINDOW = $GODOT_FULL_WINDOW;
@ -263,6 +264,7 @@ $GODOT_HEAD_INCLUDE
} else {
setStatusMode('indeterminate');
engine.setCanvas(canvas);
engine.setGDNativeLibraries(GDNATIVE_LIBS);
engine.startGame(EXECUTABLE_NAME, MAIN_PACK, EXTRA_ARGS).then(() => {
setStatusMode('hidden');
initializing = false;

View File

@ -85,36 +85,44 @@ public:
// Wrong protocol
ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version.");
String filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_js_export");
const String cache_path = EditorSettings::get_singleton()->get_cache_dir();
const String basereq = "/tmp_js_export";
String ctype = "";
String filepath;
String ctype;
if (req[1] == basereq + ".html") {
filepath += ".html";
filepath = cache_path.plus_file(req[1].get_file());
ctype = "text/html";
} else if (req[1] == basereq + ".js") {
filepath += ".js";
filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/javascript";
} else if (req[1] == basereq + ".audio.worklet.js") {
filepath += ".audio.worklet.js";
filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/javascript";
} else if (req[1] == basereq + ".worker.js") {
filepath += ".worker.js";
filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/javascript";
} else if (req[1] == basereq + ".pck") {
filepath += ".pck";
filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/octet-stream";
} else if (req[1] == basereq + ".png" || req[1] == "/favicon.png") {
// Also allow serving the generated favicon for a smoother loading experience.
if (req[1] == "/favicon.png") {
filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png");
} else {
filepath += ".png";
filepath = basereq + ".png";
}
ctype = "image/png";
} else if (req[1] == basereq + ".wasm") {
filepath += ".wasm";
} else if (req[1] == basereq + ".side.wasm") {
filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/wasm";
} else {
} else if (req[1] == basereq + ".wasm") {
filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/wasm";
} else if (req[1].ends_with(".wasm")) {
filepath = cache_path.plus_file(req[1].get_file()); // TODO dangerous?
ctype = "application/wasm";
}
if (filepath.empty() || !FileAccess::exists(filepath)) {
String s = "HTTP/1.1 404 Not Found\r\n";
s += "Connection: Close\r\n";
s += "\r\n";
@ -205,7 +213,7 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform {
Ref<ImageTexture> stop_icon;
int menu_options;
void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags);
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);
private:
Ref<EditorHTTPServer> server;
@ -250,7 +258,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) {
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) {
String str_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size());
String str_export;
Vector<String> lines = str_template.split("\n");
@ -258,6 +266,10 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
String flags_json;
gen_export_flags(flags, p_flags);
flags_json = JSON::print(flags);
String libs;
for (int i = 0; i < p_shared_objects.size(); i++) {
libs += "\"" + p_shared_objects[i].path.get_file() + "\",";
}
for (int i = 0; i < lines.size(); i++) {
String current_line = lines[i];
@ -265,6 +277,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
current_line = current_line.replace("$GODOT_PROJECT_NAME", ProjectSettings::get_singleton()->get_setting("application/config/name"));
current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include"));
current_line = current_line.replace("$GODOT_FULL_WINDOW", p_preset->get("html/full_window_size") ? "true" : "false");
current_line = current_line.replace("$GODOT_GDNATIVE_LIBS", libs);
current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false");
current_line = current_line.replace("$GODOT_ARGS", flags_json);
str_export += current_line + "\n";
@ -393,12 +406,24 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
return ERR_FILE_NOT_FOUND;
}
Vector<SharedObject> shared_objects;
String pck_path = p_path.get_basename() + ".pck";
Error error = save_pack(p_preset, pck_path);
Error error = save_pack(p_preset, pck_path, &shared_objects);
if (error != OK) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path);
return error;
}
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
String dst = p_path.get_base_dir().plus_file(shared_objects[i].path.get_file());
error = da->copy(shared_objects[i].path, dst);
if (error != OK) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + shared_objects[i].path.get_file());
memdelete(da);
return error;
}
}
memdelete(da);
FileAccess *src_f = nullptr;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
@ -437,14 +462,18 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
if (!custom_html.empty()) {
continue;
}
_fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug, p_flags);
_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") {
file = p_path.get_file().get_basename() + ".js";
} else if (file == "godot.worker.js") {
file = p_path.get_file().get_basename() + ".worker.js";
} else if (file == "godot.side.wasm") {
file = p_path.get_file().get_basename() + ".side.wasm";
} else if (file == "godot.audio.worklet.js") {
file = p_path.get_file().get_basename() + ".audio.worklet.js";
@ -475,7 +504,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
buf.resize(f->get_len());
f->get_buffer(buf.ptrw(), buf.size());
memdelete(f);
_fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags);
_fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
f = FileAccess::open(p_path, FileAccess::WRITE);
if (!f) {
@ -577,6 +606,7 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese
DirAccess::remove_file_or_error(basepath + ".audio.worklet.js");
DirAccess::remove_file_or_error(basepath + ".pck");
DirAccess::remove_file_or_error(basepath + ".png");
DirAccess::remove_file_or_error(basepath + ".side.wasm");
DirAccess::remove_file_or_error(basepath + ".wasm");
DirAccess::remove_file_or_error(EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png"));
return err;

View File

@ -1 +1 @@
Module['dynamicLibraries'] = [Module['thisProgram'] + '.side.wasm'];
Module['dynamicLibraries'] = [Module['thisProgram'] + '.side.wasm'].concat(Module['dynamicLibraries'] ? Module['dynamicLibraries'] : []);

View File

@ -34,6 +34,7 @@ const Engine = (function () {
this.onExecute = null;
this.onExit = null;
this.persistentPaths = ['/userfs'];
this.gdnativeLibs = [];
}
Engine.prototype.init = /** @param {string=} basePath */ function (basePath) {
@ -61,6 +62,7 @@ const Engine = (function () {
// Emscripten configuration.
config['thisProgram'] = me.executableName;
config['noExitRuntime'] = true;
config['dynamicLibraries'] = me.gdnativeLibs;
Godot(config).then(function (module) {
module['initFS'](me.persistentPaths).then(function (fs_err) {
me.rtenv = module;
@ -249,6 +251,10 @@ const Engine = (function () {
this.persistentPaths = persistentPaths;
};
Engine.prototype.setGDNativeLibraries = function (gdnativeLibs) {
this.gdnativeLibs = gdnativeLibs;
};
Engine.prototype.requestQuit = function () {
if (this.rtenv) {
this.rtenv['request_quit']();
@ -277,6 +283,7 @@ const Engine = (function () {
Engine.prototype['setOnExit'] = Engine.prototype.setOnExit;
Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
Engine.prototype['setPersistentPaths'] = Engine.prototype.setPersistentPaths;
Engine.prototype['setGDNativeLibraries'] = Engine.prototype.setGDNativeLibraries;
Engine.prototype['requestQuit'] = Engine.prototype.requestQuit;
return Engine;
}());

View File

@ -43,6 +43,7 @@
#include "modules/websocket/remote_debugger_peer_websocket.h"
#endif
#include <dlfcn.h>
#include <emscripten.h>
#include <stdlib.h>
@ -187,6 +188,13 @@ bool OS_JavaScript::is_userfs_persistent() const {
return idb_available;
}
Error OS_JavaScript::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
String path = p_path.get_file();
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ". Error: " + dlerror());
return OK;
}
OS_JavaScript *OS_JavaScript::get_singleton() {
return static_cast<OS_JavaScript *>(OS::get_singleton());
}

View File

@ -87,6 +87,7 @@ public:
String get_user_data_dir() const override;
bool is_userfs_persistent() const override;
Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) override;
void resume_audio();