Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/object/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
#include "core/string/translation_server.h"
#include "core/variant/typed_array.h"

// Static member initialization for signal emission callback
Object::SignalEmissionCallback Object::signal_emission_callback = nullptr;
bool Object::signal_emission_callback_enabled = false;

#ifdef DEBUG_ENABLED

struct _ObjectDebugLock {
Expand Down Expand Up @@ -1237,6 +1241,12 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
return ERR_UNAVAILABLE;
}

// STEP 1: Signal emission hook - Call the registered callback if enabled
// This allows editor tools like the Signal Viewer to track signal emissions globally
if (signal_emission_callback_enabled && signal_emission_callback) {
signal_emission_callback(this, p_name, p_args, p_argcount);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (s->slot_map.size() > MAX_SLOTS_ON_STACK) {
slot_callables = (Callable *)memalloc(sizeof(Callable) * s->slot_map.size());
slot_flags = (uint32_t *)memalloc(sizeof(uint32_t) * s->slot_map.size());
Expand Down
23 changes: 23 additions & 0 deletions core/object/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,29 @@ class Object {
public:
static constexpr bool _class_is_enabled = true;

// Signal tracking callback for editor tools (e.g., Signal Viewer)
// This callback is invoked whenever a signal is emitted, allowing tools to track signal activity
// The callback is only active when explicitly registered to minimize performance impact
typedef void (*SignalEmissionCallback)(Object *p_emitter, const StringName &p_signal, const Variant **p_args, int p_argcount);

private:
static SignalEmissionCallback signal_emission_callback;
static bool signal_emission_callback_enabled;

public:
static void set_signal_emission_callback(SignalEmissionCallback p_callback) {
signal_emission_callback = p_callback;
signal_emission_callback_enabled = (p_callback != nullptr);
}

static SignalEmissionCallback get_signal_emission_callback() {
return signal_emission_callback;
}

static bool is_signal_emission_callback_enabled() {
return signal_emission_callback_enabled;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

void notify_property_list_changed();

static void *get_class_ptr_static() {
Expand Down
63 changes: 63 additions & 0 deletions editor/debugger/script_editor_debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "editor/debugger/editor_visual_profiler.h"
#include "editor/docks/filesystem_dock.h"
#include "editor/docks/inspector_dock.h"
#include "editor/docks/signalize_dock.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
Expand Down Expand Up @@ -489,6 +490,52 @@
can_request_idle_draw = true;
}

void ScriptEditorDebugger::_msg_signal_viewer_signal_emitted(uint64_t p_thread_id, const Array &p_data) {
// Forward signal emission data to SignalizeDock
// Data format: [emitter_id, node_name, node_class, signal_name, count, connections_array]

if (p_data.size() < 6) {
print_line("[ScriptEditorDebugger] WARNING: Invalid signal_viewer data, expected 6 elements");
return;
}

// Extract the data
ObjectID emitter_id = p_data[0];
String node_name = p_data[1];
String node_class = p_data[2];
String signal_name = p_data[3];
int count = p_data[4];
Array connections = p_data[5];

Check warning on line 508 in editor/debugger/script_editor_debugger.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor)

declaration of 'connections' hides class member

Check failure on line 508 in editor/debugger/script_editor_debugger.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor)

the following warning is treated as an error

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Align signal_viewer payload schema to avoid dropping emissions.

_msg_signal_viewer_signal_emitted() requires 6 elements (including count), but the runtime sender currently builds a 5‑element payload (no count). This makes the handler return early and the dock never updates. Consider supporting both formats or update the sender.

🛠️ Backward‑compatible handler fix
-	if (p_data.size() < 6) {
-		print_line("[ScriptEditorDebugger] WARNING: Invalid signal_viewer data, expected 6 elements");
+	if (p_data.size() < 5) {
+		print_line("[ScriptEditorDebugger] WARNING: Invalid signal_viewer data, expected 5+ elements");
 		return;
 	}
 
 	// Extract the data
 	ObjectID emitter_id = p_data[0];
 	String node_name = p_data[1];
 	String node_class = p_data[2];
 	String signal_name = p_data[3];
-	int count = p_data[4];
-	Array connections = p_data[5];
+	int count = 1;
+	Array connections;
+	if (p_data.size() == 5) {
+		connections = p_data[4];
+	} else {
+		count = p_data[4];
+		connections = p_data[5];
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void ScriptEditorDebugger::_msg_signal_viewer_signal_emitted(uint64_t p_thread_id, const Array &p_data) {
// Forward signal emission data to SignalizeDock
// Data format: [emitter_id, node_name, node_class, signal_name, count, connections_array]
if (p_data.size() < 6) {
print_line("[ScriptEditorDebugger] WARNING: Invalid signal_viewer data, expected 6 elements");
return;
}
// Extract the data
ObjectID emitter_id = p_data[0];
String node_name = p_data[1];
String node_class = p_data[2];
String signal_name = p_data[3];
int count = p_data[4];
Array connections = p_data[5];
void ScriptEditorDebugger::_msg_signal_viewer_signal_emitted(uint64_t p_thread_id, const Array &p_data) {
// Forward signal emission data to SignalizeDock
// Data format: [emitter_id, node_name, node_class, signal_name, count, connections_array]
if (p_data.size() < 5) {
print_line("[ScriptEditorDebugger] WARNING: Invalid signal_viewer data, expected 5+ elements");
return;
}
// Extract the data
ObjectID emitter_id = p_data[0];
String node_name = p_data[1];
String node_class = p_data[2];
String signal_name = p_data[3];
int count = 1;
Array connections;
if (p_data.size() == 5) {
connections = p_data[4];
} else {
count = p_data[4];
connections = p_data[5];
}
🤖 Prompt for AI Agents
In `@editor/debugger/script_editor_debugger.cpp` around lines 493 - 508, Update
_msg_signal_viewer_signal_emitted to accept both 5- and 6-element payloads:
detect p_data.size() == 5 and map elements accordingly (emitter_id = p_data[0],
node_name = p_data[1], node_class = p_data[2], signal_name = p_data[3],
connections = p_data[4]) and set count to connections.size(); otherwise keep the
existing 6-element mapping (using count = p_data[4] and connections =
p_data[5]); ensure you validate types when extracting ObjectID/String/Array and
only early-return on sizes < 5 so backward-compatible payloads aren't dropped.


// Get SignalizeDock singleton and update it
SignalizeDock *signal_viewer = SignalizeDock::get_singleton();
if (signal_viewer) {
signal_viewer->_on_runtime_signal_emitted(emitter_id, node_name, node_class, signal_name, count, connections);
} else {
print_line("[ScriptEditorDebugger] WARNING: No SignalizeDock singleton");
}
}

void ScriptEditorDebugger::_msg_signal_viewer_node_signal_data(uint64_t p_thread_id, const Array &p_data) {
// Forward node signal data response to SignalizeDock
// Data format: [node_id, node_name, node_class, [signals_data]]

if (p_data.is_empty()) {
print_line("[ScriptEditorDebugger] WARNING: Invalid node_signal_data, empty array");
return;
}

print_line(vformat("[ScriptEditorDebugger] Received node signal data with %d elements", p_data.size()));

// Get SignalizeDock singleton and forward the data
SignalizeDock *signal_viewer = SignalizeDock::get_singleton();
if (signal_viewer) {
signal_viewer->_on_node_signal_data_received(p_data);
} else {
print_line("[ScriptEditorDebugger] WARNING: No SignalizeDock singleton for node_signal_data");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}
}

void ScriptEditorDebugger::_msg_stack_dump(uint64_t p_thread_id, const Array &p_data) {
DebuggerMarshalls::ScriptStackDump stack;
stack.deserialize(p_data);
Expand Down Expand Up @@ -942,14 +989,28 @@
void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data) {
emit_signal(SNAME("debug_data"), p_msg, p_data);

// DEBUG: Log signal_viewer messages
if (p_msg.contains("signal_viewer")) {
print_line(vformat("[ScriptEditorDebugger] Received message: '%s'", p_msg));
}

ParseMessageFunc *fn_ptr = parse_message_handlers.getptr(p_msg);
if (fn_ptr) {
if (p_msg.contains("signal_viewer")) {
print_line(vformat("[ScriptEditorDebugger] Found handler for: '%s'", p_msg));
}
(this->**fn_ptr)(p_thread_id, p_data);
} else {
if (p_msg.contains("signal_viewer")) {
print_line(vformat("[ScriptEditorDebugger] No handler found for: '%s', trying plugins_capture", p_msg));
}
int colon_index = p_msg.find_char(':');
ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");

bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data);
if (p_msg.contains("signal_viewer")) {
print_line(vformat("[ScriptEditorDebugger] plugins_capture returned: %s", parsed ? "TRUE" : "FALSE"));
}
if (!parsed) {
WARN_PRINT("Unknown message: " + p_msg);
}
Expand All @@ -970,6 +1031,8 @@
#endif // DISABLE_DEPRECATED
parse_message_handlers["servers:memory_usage"] = &ScriptEditorDebugger::_msg_servers_memory_usage;
parse_message_handlers["servers:drawn"] = &ScriptEditorDebugger::_msg_servers_drawn;
parse_message_handlers["signal_viewer:signal_emitted"] = &ScriptEditorDebugger::_msg_signal_viewer_signal_emitted;
parse_message_handlers["signal_viewer:node_signal_data"] = &ScriptEditorDebugger::_msg_signal_viewer_node_signal_data;
parse_message_handlers["stack_dump"] = &ScriptEditorDebugger::_msg_stack_dump;
parse_message_handlers["stack_frame_vars"] = &ScriptEditorDebugger::_msg_stack_frame_vars;
parse_message_handlers["stack_frame_var"] = &ScriptEditorDebugger::_msg_stack_frame_var;
Expand Down
7 changes: 6 additions & 1 deletion editor/debugger/script_editor_debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ class ScriptEditorDebugger : public MarginContainer {
#endif // DISABLE_DEPRECATED
void _msg_servers_memory_usage(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_drawn(uint64_t p_thread_id, const Array &p_data);
void _msg_signal_viewer_signal_emitted(uint64_t p_thread_id, const Array &p_data);
void _msg_signal_viewer_node_signal_data(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_dump(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_frame_vars(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_frame_var(uint64_t p_thread_id, const Array &p_data);
Expand Down Expand Up @@ -275,7 +277,6 @@ class ScriptEditorDebugger : public MarginContainer {
void _item_menu_id_pressed(int p_option);
void _tab_changed(int p_tab);

void _put_msg(const String &p_message, const Array &p_data, uint64_t p_thread_id = Thread::MAIN_ID);
void _export_csv();

void _clear_execution();
Expand Down Expand Up @@ -305,8 +306,12 @@ class ScriptEditorDebugger : public MarginContainer {

void clear_inspector(bool p_send_msg = true);

// Send message to game process - made public for SignalizeDock
void _put_msg(const String &p_message, const Array &p_data, uint64_t p_thread_id = Thread::MAIN_ID);

// Needed by _live_edit_set, buttons state.
void set_editor_remote_tree(const Tree *p_tree) { editor_remote_tree = p_tree; }
const Tree *get_editor_remote_tree() const { return editor_remote_tree; }

void request_remote_tree();
const SceneDebuggerTree *get_remote_tree();
Expand Down
Loading
Loading