From f8a21d9e8b1282cb7ea39a5dd967b3693fc081aa Mon Sep 17 00:00:00 2001 From: Quentin Quadrat Date: Mon, 9 Dec 2024 01:27:29 +0100 Subject: [PATCH] WIP JS: first message received from JS displayed on Godot --- addons/gdcef/demos/JS/Control.gd | 18 +++-- addons/gdcef/demos/JS/qq.html | 19 ++++++ addons/gdcef/gdcef/src/gdbrowser.cpp | 68 +++++++++++++------ addons/gdcef/gdcef/src/gdbrowser.hpp | 25 ++++++- addons/gdcef/gdcef/src/gdcef.cpp | 57 ---------------- addons/gdcef/gdcef/src/gdcef.hpp | 7 -- addons/gdcef/gdcef/src/godot_js_binder.cpp | 7 +- addons/gdcef/gdcef/src/godot_js_binder.hpp | 10 +++ .../render_process/src/render_process.cpp | 6 ++ 9 files changed, 118 insertions(+), 99 deletions(-) create mode 100644 addons/gdcef/demos/JS/qq.html diff --git a/addons/gdcef/demos/JS/Control.gd b/addons/gdcef/demos/JS/Control.gd index 35465f8..72812d8 100644 --- a/addons/gdcef/demos/JS/Control.gd +++ b/addons/gdcef/demos/JS/Control.gd @@ -22,14 +22,10 @@ const BROWSER_NAME = "player_stats" # ============================================================================== func _ready(): initialize_cef() -# expose_methods() pass -# ============================================================================== -# Expose methods to JavaScript via CEF -# ============================================================================== -#func expose_methods(): -# pass +func maMethodeGodot(message): + print("Message reçu depuis JavaScript : ", message) # ============================================================================== # Change character's weapon @@ -95,12 +91,14 @@ func get_character_state() -> Dictionary: # ============================================================================== # CEF Callback when a page has ended to load with success. +# TODO on page_unload ? # ============================================================================== func _on_page_loaded(browser): print("The browser " + browser.name + " has loaded " + browser.get_url()) - $CEF.register_method(self, browser, "change_weapon") - $CEF.register_method(self, browser, "set_character_name") - $CEF.register_method(self, browser, "modify_xp") + browser.register_method(Callable(self, "change_weapon")) + browser.register_method(Callable(self, "set_character_name")) + browser.register_method(Callable(self, "modify_xp")) + browser.register_method(Callable(self, "maMethodeGodot")) pass # ============================================================================== @@ -144,7 +142,7 @@ func initialize_cef(): # Load the HTML file containing the JavaScript code # ============================================================================== func _load_html_file(): - var file = FileAccess.open("res://character-management-ui.html", FileAccess.READ) + var file = FileAccess.open("/home/qq/MyGitHub/gdcef/addons/gdcef/demos/JS/qq.html", FileAccess.READ) var content = file.get_as_text() file.close() return content diff --git a/addons/gdcef/demos/JS/qq.html b/addons/gdcef/demos/JS/qq.html new file mode 100644 index 0000000..436de1f --- /dev/null +++ b/addons/gdcef/demos/JS/qq.html @@ -0,0 +1,19 @@ + + + + + + Character Management UI + + + + + + + + + \ No newline at end of file diff --git a/addons/gdcef/gdcef/src/gdbrowser.cpp b/addons/gdcef/gdcef/src/gdbrowser.cpp index 9b999ec..76687ac 100644 --- a/addons/gdcef/gdcef/src/gdbrowser.cpp +++ b/addons/gdcef/gdcef/src/gdbrowser.cpp @@ -23,8 +23,8 @@ // SOFTWARE. //***************************************************************************** -//------------------------------------------------------------------------------ #include "gdbrowser.hpp" +#include "godot_js_binder.hpp" #include "helper_config.hpp" #include "helper_files.hpp" @@ -153,6 +153,8 @@ void GDBrowserView::_bind_methods() &GDBrowserView::getAudioStreamer); ClassDB::bind_method(D_METHOD("get_pixel_color", "x", "y"), &GDBrowserView::getPixelColor); + ClassDB::bind_method(D_METHOD("register_method"), + &GDBrowserView::registerGodotMethod); // Signals ADD_SIGNAL(MethodInfo("on_download_updated", @@ -893,53 +895,81 @@ void GDBrowserView::onDownloadUpdated( } //------------------------------------------------------------------------------ -bool GDBrowserView::Impl::OnProcessMessageReceived( +void GDBrowserView::registerGodotMethod(const godot::Callable& callable) +{ + godot::String method_name = callable.get_method(); + + BROWSER_DEBUG("Registering gdscript method " + << method_name.utf8().get_data()); + if (!callable.is_valid()) + { + BROWSER_ERROR("Invalid callable provided"); + return; + } + + std::string key = method_name.utf8().get_data(); + m_js_bindings[key] = callable; +} + +//------------------------------------------------------------------------------ +bool GDBrowserView::onProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message) { + BROWSER_DEBUG("Received message " << message->GetName().ToString()); if (message->GetName() != CALL_GODOT_METHOD) { + BROWSER_DEBUG("Not method " << CALL_GODOT_METHOD); return false; } - CefRefPtr args = message->GetArgumentList(); - if (args->GetSize() < 1) + if (message->GetArgumentList()->GetSize() < 1) { + BROWSER_ERROR("Expected method name as first argument"); return false; } - // Get method name from first argument - CefString method_name = args->GetString(0); + // Create the callable key + std::string key = message->GetArgumentList()->GetString(0).ToString(); + + // Does not exist ? + auto callable = m_js_bindings[key]; + if (!callable.is_valid()) + { + BROWSER_ERROR("Callable not found for method " << key); + return false; + } - // Convert CEF arguments to Godot arguments - godot::Array godot_args; - for (size_t i = 1; i < args->GetSize(); ++i) + // Convert the message arguments to a Godot Array + godot::Array args; + auto message_args = message->GetArgumentList(); + for (size_t i = 1; i < message_args->GetSize(); ++i) { - switch (args->GetType(i)) + switch (message_args->GetType(i)) { case VTYPE_BOOL: - godot_args.push_back(args->GetBool(i)); + args.push_back(message_args->GetBool(i)); break; case VTYPE_INT: - godot_args.push_back(args->GetInt(i)); + args.push_back(message_args->GetInt(i)); break; case VTYPE_DOUBLE: - godot_args.push_back(args->GetDouble(i)); + args.push_back(message_args->GetDouble(i)); break; case VTYPE_STRING: - godot_args.push_back( - godot::String(args->GetString(i).ToString().c_str())); + args.push_back(godot::String( + message_args->GetString(i).ToString().c_str())); break; default: // For unsupported types, pass as string - godot_args.push_back( - godot::String(args->GetString(i).ToString().c_str())); + args.push_back(godot::String( + message_args->GetString(i).ToString().c_str())); } } - // Call the Godot method - m_owner.call_deferred(method_name.ToString().c_str(), godot_args); + // Call the function + callable.callv(args); return true; } \ No newline at end of file diff --git a/addons/gdcef/gdcef/src/gdbrowser.hpp b/addons/gdcef/gdcef/src/gdbrowser.hpp index 3bfbc8d..8f66ec2 100644 --- a/addons/gdcef/gdcef/src/gdbrowser.hpp +++ b/addons/gdcef/gdcef/src/gdbrowser.hpp @@ -189,11 +189,18 @@ class GDBrowserView: public godot::Node return this; } + // --------------------------------------------------------------------- + //! \brief Called when a message is received from a different process. + // --------------------------------------------------------------------- virtual bool OnProcessMessageReceived(CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, - CefRefPtr message) override; + CefRefPtr message) override + { + return m_owner.onProcessMessageReceived( + browser, frame, source_process, message); + } private: // CefRenderHandler interfaces @@ -674,6 +681,11 @@ class GDBrowserView: public godot::Node // ------------------------------------------------------------------------- godot::Color getPixelColor(int x, int y) const; + // ------------------------------------------------------------------------- + //! \brief Register a Godot method in the JavaScript context + // ------------------------------------------------------------------------- + void registerGodotMethod(const godot::Callable& callable); + private: void resize_(int width, int height); @@ -775,6 +787,14 @@ class GDBrowserView: public godot::Node CefRefPtr download_item, CefRefPtr callback); + // ------------------------------------------------------------------------- + //! \brief Called when a message is received from a different process. + // ------------------------------------------------------------------------- + bool onProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message); + private: //! \brief CEF interface implementation @@ -824,6 +844,9 @@ class GDBrowserView: public godot::Node //! \brief Download folder (configured from Browser config) fs::path m_download_folder; + + //! \brief + std::unordered_map m_js_bindings; }; #if !defined(_WIN32) diff --git a/addons/gdcef/gdcef/src/gdcef.cpp b/addons/gdcef/gdcef/src/gdcef.cpp index 1ba1e8d..572cad3 100644 --- a/addons/gdcef/gdcef/src/gdcef.cpp +++ b/addons/gdcef/gdcef/src/gdcef.cpp @@ -130,8 +130,6 @@ void GDCef::_bind_methods() ClassDB::bind_method(D_METHOD("shutdown"), &GDCef::shutdown); ClassDB::bind_method(D_METHOD("is_alive"), &GDCef::isAlive); ClassDB::bind_method(D_METHOD("get_error"), &GDCef::getError); - ClassDB::bind_method(D_METHOD("register_method"), - &GDCef::registerGodotMethod); } //------------------------------------------------------------------------------ @@ -584,61 +582,6 @@ GDBrowserView* GDCef::createBrowser(godot::String const& url, return browser; } -//------------------------------------------------------------------------------ -//! \brief Method to register Godot methods in the JavaScript context -//! \param [in] godot_object The Godot object containing the method to register -//! \param [in] method_name The name of the method to register -//------------------------------------------------------------------------------ -void GDCef::registerGodotMethod(godot::Object* godot_object, - GDBrowserView* browser, - godot::String const& method_name) -{ - if (godot_object == nullptr) - { - GDCEF_ERROR("Invalid Godot object passed to registerGodotMethod"); - return; - } - - if (browser == nullptr) - { - GDCEF_ERROR("Invalid browser passed to registerGodotMethod"); - return; - } - - // Check that the method exists in the Godot object. - if (!godot_object->has_method(method_name)) - { - GDCEF_ERROR("Method " << method_name.utf8().get_data() - << " does not exist in the Godot object"); - return; - } - - // Handle private GDScript methods (starting with "_"). - // Remove the "_" initial for the JavaScript method name - godot::String js_method_name = method_name; - if (method_name.begins_with("_")) - { - js_method_name = method_name.substr(1); - } - - // Inject the JavaScript code to create the function - std::string js_code = - "window.godot." + std::string(js_method_name.utf8().get_data()) + - " = function() { return window.godot.callGodotMethod('" + - std::string(js_method_name.utf8().get_data()) + "', ...arguments); };"; - - // Get the active browser from the caller - godot::Node* caller = godot::Object::cast_to(godot_object); - if (caller == nullptr) - { - GDCEF_ERROR("Caller object is not a Node"); - return; - } - - // Find the browser in the caller's children - browser->executeJavaScript(js_code.c_str()); -} - //------------------------------------------------------------------------------ void GDCef::Impl::OnAfterCreated(CefRefPtr /*browser*/) { diff --git a/addons/gdcef/gdcef/src/gdcef.hpp b/addons/gdcef/gdcef/src/gdcef.hpp index 6e3cf6a..a6ec768 100644 --- a/addons/gdcef/gdcef/src/gdcef.hpp +++ b/addons/gdcef/gdcef/src/gdcef.hpp @@ -108,13 +108,6 @@ class GDCef: public godot::Node // ------------------------------------------------------------------------- bool isAlive(); - // ------------------------------------------------------------------------- - //! \brief Register a Godot method in the JavaScript context - // ------------------------------------------------------------------------- - void registerGodotMethod(godot::Object* godot_object, - GDBrowserView* browser, - const godot::String& method_name); - // ------------------------------------------------------------------------- //! \brief Return the latest error. // ------------------------------------------------------------------------- diff --git a/addons/gdcef/gdcef/src/godot_js_binder.cpp b/addons/gdcef/gdcef/src/godot_js_binder.cpp index 5f7900e..ed8f9ce 100644 --- a/addons/gdcef/gdcef/src/godot_js_binder.cpp +++ b/addons/gdcef/gdcef/src/godot_js_binder.cpp @@ -25,9 +25,6 @@ #include "godot_js_binder.hpp" -static CefRefPtr GodotToV8(const godot::Variant& godot_value); -static godot::Variant V8ToGodot(CefRefPtr v8_value); - //------------------------------------------------------------------------------ bool GodotMethodInvoker::Execute(const CefString& name, CefRefPtr object, @@ -51,7 +48,7 @@ bool GodotMethodInvoker::Execute(const CefString& name, } //------------------------------------------------------------------------------ -static CefRefPtr GodotToV8(const godot::Variant& godot_value) +CefRefPtr GodotToV8(const godot::Variant& godot_value) { switch (godot_value.get_type()) { @@ -105,7 +102,7 @@ static CefRefPtr GodotToV8(const godot::Variant& godot_value) } //------------------------------------------------------------------------------ -static godot::Variant V8ToGodot(CefRefPtr v8_value) +godot::Variant V8ToGodot(CefRefPtr v8_value) { if (!v8_value.get()) { diff --git a/addons/gdcef/gdcef/src/godot_js_binder.hpp b/addons/gdcef/gdcef/src/godot_js_binder.hpp index 5e9ada4..419753b 100644 --- a/addons/gdcef/gdcef/src/godot_js_binder.hpp +++ b/addons/gdcef/gdcef/src/godot_js_binder.hpp @@ -34,6 +34,16 @@ // Chromium Embedded Framework #include "cef_v8.h" +// ----------------------------------------------------------------------------- +//! \brief Convert a Godot variant to a V8 value +// ----------------------------------------------------------------------------- +CefRefPtr GodotToV8(const godot::Variant& godot_value); + +// ----------------------------------------------------------------------------- +//! \brief Convert a V8 value to a Godot variant +// ----------------------------------------------------------------------------- +godot::Variant V8ToGodot(CefRefPtr v8_value); + // **************************************************************************** //! \class GodotMethodInvoker //! \brief Class to handle binding between JavaScript and GDScript methods diff --git a/addons/gdcef/render_process/src/render_process.cpp b/addons/gdcef/render_process/src/render_process.cpp index 54a9c3a..f82ef4b 100644 --- a/addons/gdcef/render_process/src/render_process.cpp +++ b/addons/gdcef/render_process/src/render_process.cpp @@ -54,9 +54,13 @@ bool GodotMethodHandler::Execute(const CefString& name, CefRefPtr& retval, CefString& exception) { + DEBUG_RENDER_PROCESS(name.ToString()); + // Function does not exist. if (name != CALL_GODOT_METHOD) { + exception = "Function does not exist"; + DEBUG_RENDER_PROCESS(exception.ToString()); return false; } @@ -64,6 +68,7 @@ bool GodotMethodHandler::Execute(const CefString& name, if (m_browser == nullptr) { exception = "Browser pointer at NULL"; + DEBUG_RENDER_PROCESS(exception.ToString()); return true; } @@ -71,6 +76,7 @@ bool GodotMethodHandler::Execute(const CefString& name, if (arguments.size() < 1 || !arguments[0]->IsString()) { exception = "First argument must be the method name"; + DEBUG_RENDER_PROCESS(exception.ToString()); return false; }