Skip to content

Commit

Permalink
WIP JS: first message received from JS displayed on Godot
Browse files Browse the repository at this point in the history
  • Loading branch information
Lecrapouille committed Dec 9, 2024
1 parent fe423c5 commit f8a21d9
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 99 deletions.
18 changes: 8 additions & 10 deletions addons/gdcef/demos/JS/Control.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

# ==============================================================================
Expand Down Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions addons/gdcef/demos/JS/qq.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">

<head>
<meta charset="UTF-8">
<title>Character Management UI</title>
</head>

<body>
<button onclick="envoyerAGodot()">Envoyer à Godot</button>

<script>
function envoyerAGodot() {
godot.callGodotMethod("maMethodeGodot", "Hello from JS!");
}
</script>
</body>

</html>
68 changes: 49 additions & 19 deletions addons/gdcef/gdcef/src/gdbrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
// SOFTWARE.
//*****************************************************************************

//------------------------------------------------------------------------------
#include "gdbrowser.hpp"
#include "godot_js_binder.hpp"
#include "helper_config.hpp"
#include "helper_files.hpp"

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message)
{
BROWSER_DEBUG("Received message " << message->GetName().ToString());
if (message->GetName() != CALL_GODOT_METHOD)
{
BROWSER_DEBUG("Not method " << CALL_GODOT_METHOD);
return false;
}

CefRefPtr<CefListValue> 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;
}
25 changes: 24 additions & 1 deletion addons/gdcef/gdcef/src/gdbrowser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override;
CefRefPtr<CefProcessMessage> message) override
{
return m_owner.onProcessMessageReceived(
browser, frame, source_process, message);
}

private: // CefRenderHandler interfaces

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -775,6 +787,14 @@ class GDBrowserView: public godot::Node
CefRefPtr<CefDownloadItem> download_item,
CefRefPtr<CefDownloadItemCallback> callback);

// -------------------------------------------------------------------------
//! \brief Called when a message is received from a different process.
// -------------------------------------------------------------------------
bool onProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message);

private:

//! \brief CEF interface implementation
Expand Down Expand Up @@ -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<std::string, godot::Callable> m_js_bindings;
};

#if !defined(_WIN32)
Expand Down
57 changes: 0 additions & 57 deletions addons/gdcef/gdcef/src/gdcef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -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::Node>(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<CefBrowser> /*browser*/)
{
Expand Down
7 changes: 0 additions & 7 deletions addons/gdcef/gdcef/src/gdcef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
// -------------------------------------------------------------------------
Expand Down
7 changes: 2 additions & 5 deletions addons/gdcef/gdcef/src/godot_js_binder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@

#include "godot_js_binder.hpp"

static CefRefPtr<CefV8Value> GodotToV8(const godot::Variant& godot_value);
static godot::Variant V8ToGodot(CefRefPtr<CefV8Value> v8_value);

//------------------------------------------------------------------------------
bool GodotMethodInvoker::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
Expand All @@ -51,7 +48,7 @@ bool GodotMethodInvoker::Execute(const CefString& name,
}

//------------------------------------------------------------------------------
static CefRefPtr<CefV8Value> GodotToV8(const godot::Variant& godot_value)
CefRefPtr<CefV8Value> GodotToV8(const godot::Variant& godot_value)
{
switch (godot_value.get_type())
{
Expand Down Expand Up @@ -105,7 +102,7 @@ static CefRefPtr<CefV8Value> GodotToV8(const godot::Variant& godot_value)
}

//------------------------------------------------------------------------------
static godot::Variant V8ToGodot(CefRefPtr<CefV8Value> v8_value)
godot::Variant V8ToGodot(CefRefPtr<CefV8Value> v8_value)
{
if (!v8_value.get())
{
Expand Down
10 changes: 10 additions & 0 deletions addons/gdcef/gdcef/src/godot_js_binder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@
// Chromium Embedded Framework
#include "cef_v8.h"

// -----------------------------------------------------------------------------
//! \brief Convert a Godot variant to a V8 value
// -----------------------------------------------------------------------------
CefRefPtr<CefV8Value> GodotToV8(const godot::Variant& godot_value);

// -----------------------------------------------------------------------------
//! \brief Convert a V8 value to a Godot variant
// -----------------------------------------------------------------------------
godot::Variant V8ToGodot(CefRefPtr<CefV8Value> v8_value);

// ****************************************************************************
//! \class GodotMethodInvoker
//! \brief Class to handle binding between JavaScript and GDScript methods
Expand Down
Loading

0 comments on commit f8a21d9

Please sign in to comment.