mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 19:42:43 +00:00
[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:
parent
ca34b5e57a
commit
611c4998e8
2
misc/dist/html/full-size.html
vendored
2
misc/dist/html/full-size.html
vendored
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1 +1 @@
|
||||
Module['dynamicLibraries'] = [Module['thisProgram'] + '.side.wasm'];
|
||||
Module['dynamicLibraries'] = [Module['thisProgram'] + '.side.wasm'].concat(Module['dynamicLibraries'] ? Module['dynamicLibraries'] : []);
|
||||
|
@ -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;
|
||||
}());
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user