mirror of
https://github.com/godotengine/godot
synced 2024-10-14 14:52:41 +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 EXECUTABLE_NAME = '$GODOT_BASENAME';
|
||||||
const MAIN_PACK = '$GODOT_BASENAME.pck';
|
const MAIN_PACK = '$GODOT_BASENAME.pck';
|
||||||
const EXTRA_ARGS = JSON.parse('$GODOT_ARGS');
|
const EXTRA_ARGS = JSON.parse('$GODOT_ARGS');
|
||||||
|
const GDNATIVE_LIBS = [$GODOT_GDNATIVE_LIBS];
|
||||||
const INDETERMINATE_STATUS_STEP_MS = 100;
|
const INDETERMINATE_STATUS_STEP_MS = 100;
|
||||||
const FULL_WINDOW = $GODOT_FULL_WINDOW;
|
const FULL_WINDOW = $GODOT_FULL_WINDOW;
|
||||||
|
|
||||||
|
@ -263,6 +264,7 @@ $GODOT_HEAD_INCLUDE
|
||||||
} else {
|
} else {
|
||||||
setStatusMode('indeterminate');
|
setStatusMode('indeterminate');
|
||||||
engine.setCanvas(canvas);
|
engine.setCanvas(canvas);
|
||||||
|
engine.setGDNativeLibraries(GDNATIVE_LIBS);
|
||||||
engine.startGame(EXECUTABLE_NAME, MAIN_PACK, EXTRA_ARGS).then(() => {
|
engine.startGame(EXECUTABLE_NAME, MAIN_PACK, EXTRA_ARGS).then(() => {
|
||||||
setStatusMode('hidden');
|
setStatusMode('hidden');
|
||||||
initializing = false;
|
initializing = false;
|
||||||
|
|
|
@ -85,36 +85,44 @@ public:
|
||||||
// Wrong protocol
|
// Wrong protocol
|
||||||
ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version.");
|
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";
|
const String basereq = "/tmp_js_export";
|
||||||
String ctype = "";
|
String filepath;
|
||||||
|
String ctype;
|
||||||
if (req[1] == basereq + ".html") {
|
if (req[1] == basereq + ".html") {
|
||||||
filepath += ".html";
|
filepath = cache_path.plus_file(req[1].get_file());
|
||||||
ctype = "text/html";
|
ctype = "text/html";
|
||||||
} else if (req[1] == basereq + ".js") {
|
} else if (req[1] == basereq + ".js") {
|
||||||
filepath += ".js";
|
filepath = cache_path.plus_file(req[1].get_file());
|
||||||
ctype = "application/javascript";
|
ctype = "application/javascript";
|
||||||
} else if (req[1] == basereq + ".audio.worklet.js") {
|
} else if (req[1] == basereq + ".audio.worklet.js") {
|
||||||
filepath += ".audio.worklet.js";
|
filepath = cache_path.plus_file(req[1].get_file());
|
||||||
ctype = "application/javascript";
|
ctype = "application/javascript";
|
||||||
} else if (req[1] == basereq + ".worker.js") {
|
} else if (req[1] == basereq + ".worker.js") {
|
||||||
filepath += ".worker.js";
|
filepath = cache_path.plus_file(req[1].get_file());
|
||||||
ctype = "application/javascript";
|
ctype = "application/javascript";
|
||||||
} else if (req[1] == basereq + ".pck") {
|
} else if (req[1] == basereq + ".pck") {
|
||||||
filepath += ".pck";
|
filepath = cache_path.plus_file(req[1].get_file());
|
||||||
ctype = "application/octet-stream";
|
ctype = "application/octet-stream";
|
||||||
} else if (req[1] == basereq + ".png" || req[1] == "/favicon.png") {
|
} else if (req[1] == basereq + ".png" || req[1] == "/favicon.png") {
|
||||||
// Also allow serving the generated favicon for a smoother loading experience.
|
// Also allow serving the generated favicon for a smoother loading experience.
|
||||||
if (req[1] == "/favicon.png") {
|
if (req[1] == "/favicon.png") {
|
||||||
filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png");
|
filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png");
|
||||||
} else {
|
} else {
|
||||||
filepath += ".png";
|
filepath = basereq + ".png";
|
||||||
}
|
}
|
||||||
ctype = "image/png";
|
ctype = "image/png";
|
||||||
} else if (req[1] == basereq + ".wasm") {
|
} else if (req[1] == basereq + ".side.wasm") {
|
||||||
filepath += ".wasm";
|
filepath = cache_path.plus_file(req[1].get_file());
|
||||||
ctype = "application/wasm";
|
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";
|
String s = "HTTP/1.1 404 Not Found\r\n";
|
||||||
s += "Connection: Close\r\n";
|
s += "Connection: Close\r\n";
|
||||||
s += "\r\n";
|
s += "\r\n";
|
||||||
|
@ -205,7 +213,7 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform {
|
||||||
Ref<ImageTexture> stop_icon;
|
Ref<ImageTexture> stop_icon;
|
||||||
int menu_options;
|
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:
|
private:
|
||||||
Ref<EditorHTTPServer> server;
|
Ref<EditorHTTPServer> server;
|
||||||
|
@ -250,7 +258,7 @@ public:
|
||||||
~EditorExportPlatformJavaScript();
|
~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_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size());
|
||||||
String str_export;
|
String str_export;
|
||||||
Vector<String> lines = str_template.split("\n");
|
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;
|
String flags_json;
|
||||||
gen_export_flags(flags, p_flags);
|
gen_export_flags(flags, p_flags);
|
||||||
flags_json = JSON::print(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++) {
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
String current_line = lines[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_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_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_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_DEBUG_ENABLED", p_debug ? "true" : "false");
|
||||||
current_line = current_line.replace("$GODOT_ARGS", flags_json);
|
current_line = current_line.replace("$GODOT_ARGS", flags_json);
|
||||||
str_export += current_line + "\n";
|
str_export += current_line + "\n";
|
||||||
|
@ -393,12 +406,24 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
|
||||||
return ERR_FILE_NOT_FOUND;
|
return ERR_FILE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<SharedObject> shared_objects;
|
||||||
String pck_path = p_path.get_basename() + ".pck";
|
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) {
|
if (error != OK) {
|
||||||
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path);
|
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path);
|
||||||
return error;
|
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;
|
FileAccess *src_f = nullptr;
|
||||||
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
|
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()) {
|
if (!custom_html.empty()) {
|
||||||
continue;
|
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();
|
file = p_path.get_file();
|
||||||
|
|
||||||
} else if (file == "godot.js") {
|
} else if (file == "godot.js") {
|
||||||
file = p_path.get_file().get_basename() + ".js";
|
file = p_path.get_file().get_basename() + ".js";
|
||||||
|
|
||||||
} else if (file == "godot.worker.js") {
|
} else if (file == "godot.worker.js") {
|
||||||
file = p_path.get_file().get_basename() + ".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") {
|
} else if (file == "godot.audio.worklet.js") {
|
||||||
file = p_path.get_file().get_basename() + ".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());
|
buf.resize(f->get_len());
|
||||||
f->get_buffer(buf.ptrw(), buf.size());
|
f->get_buffer(buf.ptrw(), buf.size());
|
||||||
memdelete(f);
|
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);
|
f = FileAccess::open(p_path, FileAccess::WRITE);
|
||||||
if (!f) {
|
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 + ".audio.worklet.js");
|
||||||
DirAccess::remove_file_or_error(basepath + ".pck");
|
DirAccess::remove_file_or_error(basepath + ".pck");
|
||||||
DirAccess::remove_file_or_error(basepath + ".png");
|
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(basepath + ".wasm");
|
||||||
DirAccess::remove_file_or_error(EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png"));
|
DirAccess::remove_file_or_error(EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png"));
|
||||||
return err;
|
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.onExecute = null;
|
||||||
this.onExit = null;
|
this.onExit = null;
|
||||||
this.persistentPaths = ['/userfs'];
|
this.persistentPaths = ['/userfs'];
|
||||||
|
this.gdnativeLibs = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine.prototype.init = /** @param {string=} basePath */ function (basePath) {
|
Engine.prototype.init = /** @param {string=} basePath */ function (basePath) {
|
||||||
|
@ -61,6 +62,7 @@ const Engine = (function () {
|
||||||
// Emscripten configuration.
|
// Emscripten configuration.
|
||||||
config['thisProgram'] = me.executableName;
|
config['thisProgram'] = me.executableName;
|
||||||
config['noExitRuntime'] = true;
|
config['noExitRuntime'] = true;
|
||||||
|
config['dynamicLibraries'] = me.gdnativeLibs;
|
||||||
Godot(config).then(function (module) {
|
Godot(config).then(function (module) {
|
||||||
module['initFS'](me.persistentPaths).then(function (fs_err) {
|
module['initFS'](me.persistentPaths).then(function (fs_err) {
|
||||||
me.rtenv = module;
|
me.rtenv = module;
|
||||||
|
@ -249,6 +251,10 @@ const Engine = (function () {
|
||||||
this.persistentPaths = persistentPaths;
|
this.persistentPaths = persistentPaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Engine.prototype.setGDNativeLibraries = function (gdnativeLibs) {
|
||||||
|
this.gdnativeLibs = gdnativeLibs;
|
||||||
|
};
|
||||||
|
|
||||||
Engine.prototype.requestQuit = function () {
|
Engine.prototype.requestQuit = function () {
|
||||||
if (this.rtenv) {
|
if (this.rtenv) {
|
||||||
this.rtenv['request_quit']();
|
this.rtenv['request_quit']();
|
||||||
|
@ -277,6 +283,7 @@ const Engine = (function () {
|
||||||
Engine.prototype['setOnExit'] = Engine.prototype.setOnExit;
|
Engine.prototype['setOnExit'] = Engine.prototype.setOnExit;
|
||||||
Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
|
Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
|
||||||
Engine.prototype['setPersistentPaths'] = Engine.prototype.setPersistentPaths;
|
Engine.prototype['setPersistentPaths'] = Engine.prototype.setPersistentPaths;
|
||||||
|
Engine.prototype['setGDNativeLibraries'] = Engine.prototype.setGDNativeLibraries;
|
||||||
Engine.prototype['requestQuit'] = Engine.prototype.requestQuit;
|
Engine.prototype['requestQuit'] = Engine.prototype.requestQuit;
|
||||||
return Engine;
|
return Engine;
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "modules/websocket/remote_debugger_peer_websocket.h"
|
#include "modules/websocket/remote_debugger_peer_websocket.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -187,6 +188,13 @@ bool OS_JavaScript::is_userfs_persistent() const {
|
||||||
return idb_available;
|
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() {
|
OS_JavaScript *OS_JavaScript::get_singleton() {
|
||||||
return static_cast<OS_JavaScript *>(OS::get_singleton());
|
return static_cast<OS_JavaScript *>(OS::get_singleton());
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ public:
|
||||||
String get_user_data_dir() const override;
|
String get_user_data_dir() const override;
|
||||||
|
|
||||||
bool is_userfs_persistent() 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();
|
void resume_audio();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue