Added debugger plugin support
Changes: * EngineDebugger is exposed to gdscript. Game side of communication can be implemented through it. * EditorDebuggerPlugin is added which handles the editor side of communication.
This commit is contained in:
parent
a609b30ddb
commit
ea2eaf8c27
|
@ -31,6 +31,7 @@
|
||||||
#include "core_bind.h"
|
#include "core_bind.h"
|
||||||
|
|
||||||
#include "core/crypto/crypto_core.h"
|
#include "core/crypto/crypto_core.h"
|
||||||
|
#include "core/debugger/engine_debugger.h"
|
||||||
#include "core/io/file_access_compressed.h"
|
#include "core/io/file_access_compressed.h"
|
||||||
#include "core/io/file_access_encrypted.h"
|
#include "core/io/file_access_encrypted.h"
|
||||||
#include "core/io/json.h"
|
#include "core/io/json.h"
|
||||||
|
@ -2467,3 +2468,154 @@ Ref<JSONParseResult> _JSON::parse(const String &p_json) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_JSON *_JSON::singleton = nullptr;
|
_JSON *_JSON::singleton = nullptr;
|
||||||
|
|
||||||
|
////// _EngineDebugger //////
|
||||||
|
|
||||||
|
void _EngineDebugger::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("is_active"), &_EngineDebugger::is_active);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &_EngineDebugger::register_profiler);
|
||||||
|
ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &_EngineDebugger::unregister_profiler);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_profiling", "name"), &_EngineDebugger::is_profiling);
|
||||||
|
ClassDB::bind_method(D_METHOD("has_profiler", "name"), &_EngineDebugger::has_profiler);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("profiler_add_frame_data", "name", "data"), &_EngineDebugger::profiler_add_frame_data);
|
||||||
|
ClassDB::bind_method(D_METHOD("profiler_enable", "name", "enable", "arguments"), &_EngineDebugger::profiler_enable, DEFVAL(Array()));
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("register_message_capture", "name", "callable"), &_EngineDebugger::register_message_capture);
|
||||||
|
ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &_EngineDebugger::unregister_message_capture);
|
||||||
|
ClassDB::bind_method(D_METHOD("has_capture", "name"), &_EngineDebugger::has_capture);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &_EngineDebugger::send_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _EngineDebugger::is_active() {
|
||||||
|
return EngineDebugger::is_active();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) {
|
||||||
|
ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler already registered: " + p_name);
|
||||||
|
profilers.insert(p_name, ProfilerCallable(p_toggle, p_add, p_tick));
|
||||||
|
ProfilerCallable &p = profilers[p_name];
|
||||||
|
EngineDebugger::Profiler profiler(
|
||||||
|
&p,
|
||||||
|
&_EngineDebugger::call_toggle,
|
||||||
|
&_EngineDebugger::call_add,
|
||||||
|
&_EngineDebugger::call_tick);
|
||||||
|
EngineDebugger::register_profiler(p_name, profiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::unregister_profiler(const StringName &p_name) {
|
||||||
|
ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name);
|
||||||
|
EngineDebugger::unregister_profiler(p_name);
|
||||||
|
profilers.erase(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _EngineDebugger::_EngineDebugger::is_profiling(const StringName &p_name) {
|
||||||
|
return EngineDebugger::is_profiling(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _EngineDebugger::has_profiler(const StringName &p_name) {
|
||||||
|
return EngineDebugger::has_profiler(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) {
|
||||||
|
EngineDebugger::profiler_add_frame_data(p_name, p_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) {
|
||||||
|
if (EngineDebugger::get_singleton()) {
|
||||||
|
EngineDebugger::get_singleton()->profiler_enable(p_name, p_enabled, p_opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) {
|
||||||
|
ERR_FAIL_COND_MSG(captures.has(p_name) || has_capture(p_name), "Capture already registered: " + p_name);
|
||||||
|
captures.insert(p_name, p_callable);
|
||||||
|
Callable &c = captures[p_name];
|
||||||
|
EngineDebugger::Capture capture(&c, &_EngineDebugger::call_capture);
|
||||||
|
EngineDebugger::register_message_capture(p_name, capture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::unregister_message_capture(const StringName &p_name) {
|
||||||
|
ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name);
|
||||||
|
EngineDebugger::unregister_message_capture(p_name);
|
||||||
|
captures.erase(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _EngineDebugger::has_capture(const StringName &p_name) {
|
||||||
|
return EngineDebugger::has_capture(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::send_message(const String &p_msg, const Array &p_data) {
|
||||||
|
ERR_FAIL_COND_MSG(!EngineDebugger::is_active(), "Can't send message. No active debugger");
|
||||||
|
EngineDebugger::get_singleton()->send_message(p_msg, p_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_opts) {
|
||||||
|
Callable &toggle = ((ProfilerCallable *)p_user)->callable_toggle;
|
||||||
|
if (toggle.is_null()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Variant enable = p_enable, opts = p_opts;
|
||||||
|
const Variant *args[2] = { &enable, &opts };
|
||||||
|
Variant retval;
|
||||||
|
Callable::CallError err;
|
||||||
|
toggle.call(args, 2, retval, err);
|
||||||
|
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'toggle' to callable: " + Variant::get_callable_error_text(toggle, args, 2, err));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::call_add(void *p_user, const Array &p_data) {
|
||||||
|
Callable &add = ((ProfilerCallable *)p_user)->callable_add;
|
||||||
|
if (add.is_null()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Variant data = p_data;
|
||||||
|
const Variant *args[1] = { &data };
|
||||||
|
Variant retval;
|
||||||
|
Callable::CallError err;
|
||||||
|
add.call(args, 1, retval, err);
|
||||||
|
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'add' to callable: " + Variant::get_callable_error_text(add, args, 1, err));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _EngineDebugger::call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
|
||||||
|
Callable &tick = ((ProfilerCallable *)p_user)->callable_tick;
|
||||||
|
if (tick.is_null()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Variant frame_time = p_frame_time, idle_time = p_idle_time, physics_time = p_physics_time, physics_frame_time = p_physics_frame_time;
|
||||||
|
const Variant *args[4] = { &frame_time, &idle_time, &physics_time, &physics_frame_time };
|
||||||
|
Variant retval;
|
||||||
|
Callable::CallError err;
|
||||||
|
tick.call(args, 4, retval, err);
|
||||||
|
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'tick' to callable: " + Variant::get_callable_error_text(tick, args, 4, err));
|
||||||
|
}
|
||||||
|
|
||||||
|
Error _EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
|
||||||
|
Callable &capture = *(Callable *)p_user;
|
||||||
|
if (capture.is_null()) {
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
Variant cmd = p_cmd, data = p_data;
|
||||||
|
const Variant *args[2] = { &cmd, &data };
|
||||||
|
Variant retval;
|
||||||
|
Callable::CallError err;
|
||||||
|
capture.call(args, 2, retval, err);
|
||||||
|
ERR_FAIL_COND_V_MSG(err.error != Callable::CallError::CALL_OK, FAILED, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(capture, args, 2, err));
|
||||||
|
ERR_FAIL_COND_V_MSG(retval.get_type() != Variant::BOOL, FAILED, "Error calling 'capture' to callable: " + String(capture) + ". Return type is not bool.");
|
||||||
|
r_captured = retval;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
_EngineDebugger::~_EngineDebugger() {
|
||||||
|
for (Map<StringName, Callable>::Element *E = captures.front(); E; E = E->next()) {
|
||||||
|
EngineDebugger::unregister_message_capture(E->key());
|
||||||
|
}
|
||||||
|
captures.clear();
|
||||||
|
for (Map<StringName, ProfilerCallable>::Element *E = profilers.front(); E; E = E->next()) {
|
||||||
|
EngineDebugger::unregister_profiler(E->key());
|
||||||
|
}
|
||||||
|
profilers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
_EngineDebugger *_EngineDebugger::singleton = nullptr;
|
||||||
|
|
|
@ -715,4 +715,58 @@ public:
|
||||||
_JSON() { singleton = this; }
|
_JSON() { singleton = this; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class _EngineDebugger : public Object {
|
||||||
|
GDCLASS(_EngineDebugger, Object);
|
||||||
|
|
||||||
|
class ProfilerCallable {
|
||||||
|
friend class _EngineDebugger;
|
||||||
|
|
||||||
|
Callable callable_toggle;
|
||||||
|
Callable callable_add;
|
||||||
|
Callable callable_tick;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ProfilerCallable() {}
|
||||||
|
|
||||||
|
ProfilerCallable(const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) {
|
||||||
|
callable_toggle = p_toggle;
|
||||||
|
callable_add = p_add;
|
||||||
|
callable_tick = p_tick;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<StringName, Callable> captures;
|
||||||
|
Map<StringName, ProfilerCallable> profilers;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
static _EngineDebugger *singleton;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static _EngineDebugger *get_singleton() { return singleton; }
|
||||||
|
|
||||||
|
bool is_active();
|
||||||
|
|
||||||
|
void register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick);
|
||||||
|
void unregister_profiler(const StringName &p_name);
|
||||||
|
bool is_profiling(const StringName &p_name);
|
||||||
|
bool has_profiler(const StringName &p_name);
|
||||||
|
void profiler_add_frame_data(const StringName &p_name, const Array &p_data);
|
||||||
|
void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array());
|
||||||
|
|
||||||
|
void register_message_capture(const StringName &p_name, const Callable &p_callable);
|
||||||
|
void unregister_message_capture(const StringName &p_name);
|
||||||
|
bool has_capture(const StringName &p_name);
|
||||||
|
|
||||||
|
void send_message(const String &p_msg, const Array &p_data);
|
||||||
|
|
||||||
|
static void call_toggle(void *p_user, bool p_enable, const Array &p_opts);
|
||||||
|
static void call_add(void *p_user, const Array &p_data);
|
||||||
|
static void call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
|
||||||
|
static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);
|
||||||
|
|
||||||
|
_EngineDebugger() { singleton = this; }
|
||||||
|
~_EngineDebugger();
|
||||||
|
};
|
||||||
|
|
||||||
#endif // CORE_BIND_H
|
#endif // CORE_BIND_H
|
||||||
|
|
|
@ -86,6 +86,7 @@ static _Engine *_engine = nullptr;
|
||||||
static _ClassDB *_classdb = nullptr;
|
static _ClassDB *_classdb = nullptr;
|
||||||
static _Marshalls *_marshalls = nullptr;
|
static _Marshalls *_marshalls = nullptr;
|
||||||
static _JSON *_json = nullptr;
|
static _JSON *_json = nullptr;
|
||||||
|
static _EngineDebugger *_engine_debugger = nullptr;
|
||||||
|
|
||||||
static IP *ip = nullptr;
|
static IP *ip = nullptr;
|
||||||
|
|
||||||
|
@ -227,6 +228,7 @@ void register_core_types() {
|
||||||
_classdb = memnew(_ClassDB);
|
_classdb = memnew(_ClassDB);
|
||||||
_marshalls = memnew(_Marshalls);
|
_marshalls = memnew(_Marshalls);
|
||||||
_json = memnew(_JSON);
|
_json = memnew(_JSON);
|
||||||
|
_engine_debugger = memnew(_EngineDebugger);
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_core_settings() {
|
void register_core_settings() {
|
||||||
|
@ -256,6 +258,7 @@ void register_core_singletons() {
|
||||||
ClassDB::register_class<InputMap>();
|
ClassDB::register_class<InputMap>();
|
||||||
ClassDB::register_class<_JSON>();
|
ClassDB::register_class<_JSON>();
|
||||||
ClassDB::register_class<Expression>();
|
ClassDB::register_class<Expression>();
|
||||||
|
ClassDB::register_class<_EngineDebugger>();
|
||||||
|
|
||||||
Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton()));
|
Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton()));
|
||||||
Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton()));
|
Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton()));
|
||||||
|
@ -271,6 +274,7 @@ void register_core_singletons() {
|
||||||
Engine::get_singleton()->add_singleton(Engine::Singleton("Input", Input::get_singleton()));
|
Engine::get_singleton()->add_singleton(Engine::Singleton("Input", Input::get_singleton()));
|
||||||
Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton()));
|
Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton()));
|
||||||
Engine::get_singleton()->add_singleton(Engine::Singleton("JSON", _JSON::get_singleton()));
|
Engine::get_singleton()->add_singleton(Engine::Singleton("JSON", _JSON::get_singleton()));
|
||||||
|
Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", _EngineDebugger::get_singleton()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregister_core_types() {
|
void unregister_core_types() {
|
||||||
|
@ -281,6 +285,7 @@ void unregister_core_types() {
|
||||||
memdelete(_classdb);
|
memdelete(_classdb);
|
||||||
memdelete(_marshalls);
|
memdelete(_marshalls);
|
||||||
memdelete(_json);
|
memdelete(_json);
|
||||||
|
memdelete(_engine_debugger);
|
||||||
|
|
||||||
memdelete(_geometry_2d);
|
memdelete(_geometry_2d);
|
||||||
memdelete(_geometry_3d);
|
memdelete(_geometry_3d);
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
<member name="Engine" type="Engine" setter="" getter="">
|
<member name="Engine" type="Engine" setter="" getter="">
|
||||||
The [Engine] singleton.
|
The [Engine] singleton.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="EngineDebugger" type="EngineDebugger" setter="" getter="">
|
||||||
|
The [EngineDebugger] singleton.
|
||||||
|
</member>
|
||||||
<member name="Geometry2D" type="Geometry2D" setter="" getter="">
|
<member name="Geometry2D" type="Geometry2D" setter="" getter="">
|
||||||
The [Geometry2D] singleton.
|
The [Geometry2D] singleton.
|
||||||
</member>
|
</member>
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<class name="EditorDebuggerPlugin" inherits="Control" version="4.0">
|
||||||
|
<brief_description>
|
||||||
|
A base class to implement debugger plugins.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
All debugger plugin scripts must extend [EditorDebuggerPlugin]. It provides functions related to editor side of debugger.
|
||||||
|
You don't need to instantiate this class. That is handled by the debugger itself. [Control] nodes can be added as child nodes to provide a GUI front-end for the plugin.
|
||||||
|
Do not queue_free/reparent it's instance otherwise the instance becomes unusable.
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="has_capture">
|
||||||
|
<return type="bool">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if a message capture with given name is present otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_breaked">
|
||||||
|
<return type="bool">
|
||||||
|
</return>
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the game is in break state otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_debuggable">
|
||||||
|
<return type="bool">
|
||||||
|
</return>
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the game can be debugged otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_session_active">
|
||||||
|
<return type="bool">
|
||||||
|
</return>
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if there is an instance of the game running with the attached debugger otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="register_message_capture">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<argument index="1" name="callable" type="Callable">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Registers a message capture with given [code]name[/code]. If [code]name[/code] is "my_message" then messages starting with "my_message:" will be called with the given callable.
|
||||||
|
Callable must accept a message string and a data array as argument. If the message and data are valid then callable must return [code]true[/code] otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="send_message">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="message" type="String">
|
||||||
|
</argument>
|
||||||
|
<argument index="1" name="data" type="Array">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Sends a message with given [code]message[/code] and [code]data[/code] array.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="unregister_message_capture">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Unregisters the message capture with given name.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
<signals>
|
||||||
|
<signal name="breaked">
|
||||||
|
<argument index="0" name="can_debug" type="bool">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Emitted when the game enters a break state.
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
|
<signal name="continued">
|
||||||
|
<description>
|
||||||
|
Emitted when the game exists a break state.
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
|
<signal name="started">
|
||||||
|
<description>
|
||||||
|
Emitted when the debugging starts.
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
|
<signal name="stopped">
|
||||||
|
<description>
|
||||||
|
Emitted when the debugging stops.
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
|
</signals>
|
||||||
|
<constants>
|
||||||
|
</constants>
|
||||||
|
</class>
|
|
@ -76,6 +76,15 @@
|
||||||
During run-time, this will be a simple object with a script so this function does not need to be called then.
|
During run-time, this will be a simple object with a script so this function does not need to be called then.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="add_debugger_plugin">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="script" type="Script">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Adds a [Script] as debugger plugin to the Debugger. The script must extend [EditorDebuggerPlugin].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="add_export_plugin">
|
<method name="add_export_plugin">
|
||||||
<return type="void">
|
<return type="void">
|
||||||
</return>
|
</return>
|
||||||
|
@ -424,6 +433,15 @@
|
||||||
Removes a custom type added by [method add_custom_type].
|
Removes a custom type added by [method add_custom_type].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="remove_debugger_plugin">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="script" type="Script">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Removes the debugger plugin with given script fromm the Debugger.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="remove_export_plugin">
|
<method name="remove_export_plugin">
|
||||||
<return type="void">
|
<return type="void">
|
||||||
</return>
|
</return>
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<class name="EngineDebugger" inherits="Object" version="4.0">
|
||||||
|
<brief_description>
|
||||||
|
Exposes the internal debugger.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
[EngineDebugger] handles the communication between the editor and the running game. It is active in the running game. Messages can be sent/received through it. It also manages the profilers.
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="has_capture">
|
||||||
|
<return type="bool">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if a capture with the given name is present otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="has_profiler">
|
||||||
|
<return type="bool">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if a profiler with the given name is present otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_active">
|
||||||
|
<return type="bool">
|
||||||
|
</return>
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the debugger is active otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_profiling">
|
||||||
|
<return type="bool">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if a profiler with the given name is present and active otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="profiler_add_frame_data">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<argument index="1" name="data" type="Array">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Calls the [code]add[/code] callable of the profiler with given [code]name[/code] and [code]data[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="profiler_enable">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<argument index="1" name="enable" type="bool">
|
||||||
|
</argument>
|
||||||
|
<argument index="2" name="arguments" type="Array" default="[ ]">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Calls the [code]toggle[/code] callable of the profiler with given [code]name[/code] and [code]arguments[/code]. Enables/Disables the same profiler depending on [code]enable[/code] argument.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="register_message_capture">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<argument index="1" name="callable" type="Callable">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Registers a message capture with given [code]name[/code]. If [code]name[/code] is "my_message" then messages starting with "my_message:" will be called with the given callable.
|
||||||
|
Callable must accept a message string and a data array as argument. If the message and data are valid then callable must return [code]true[/code] otherwise [code]false[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="register_profiler">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<argument index="1" name="toggle" type="Callable">
|
||||||
|
</argument>
|
||||||
|
<argument index="2" name="add" type="Callable">
|
||||||
|
</argument>
|
||||||
|
<argument index="3" name="tick" type="Callable">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Registers a profiler with the given [code]name[/code].
|
||||||
|
[code]toggle[/code] callable is called when the profiler is enabled/disabled. It must take an argument array as an argument.
|
||||||
|
[code]add[/code] callable is called when data is added to profiler using [method EngineDebugger.profiler_add_frame_data]. It must take a data array as argument.
|
||||||
|
[code]tick[/code] callable is called at every active profiler iteration. It must take frame time, idle time, physics time, and physics idle time as arguments.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="send_message">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="message" type="String">
|
||||||
|
</argument>
|
||||||
|
<argument index="1" name="data" type="Array">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Sends a message with given [code]message[/code] and [code]data[/code] array.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="unregister_message_capture">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Unregisters the message capture with given [code]name[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="unregister_profiler">
|
||||||
|
<return type="void">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="name" type="StringName">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Unregisters a profiler with given [code]name[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
<constants>
|
||||||
|
</constants>
|
||||||
|
</class>
|
|
@ -34,6 +34,7 @@
|
||||||
#include "editor/debugger/script_editor_debugger.h"
|
#include "editor/debugger/script_editor_debugger.h"
|
||||||
#include "editor/editor_log.h"
|
#include "editor/editor_log.h"
|
||||||
#include "editor/editor_node.h"
|
#include "editor/editor_node.h"
|
||||||
|
#include "editor/plugins/editor_debugger_plugin.h"
|
||||||
#include "editor/plugins/script_editor_plugin.h"
|
#include "editor/plugins/script_editor_plugin.h"
|
||||||
#include "scene/gui/menu_button.h"
|
#include "scene/gui/menu_button.h"
|
||||||
#include "scene/gui/tab_container.h"
|
#include "scene/gui/tab_container.h"
|
||||||
|
@ -114,6 +115,12 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
|
||||||
tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("DebuggerPanel", "EditorStyles"));
|
tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("DebuggerPanel", "EditorStyles"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!debugger_plugins.empty()) {
|
||||||
|
for (Set<Ref<Script>>::Element *i = debugger_plugins.front(); i; i = i->next()) {
|
||||||
|
node->add_debugger_plugin(i->get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,3 +625,23 @@ void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const No
|
||||||
dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos);
|
dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerNode::add_debugger_plugin(const Ref<Script> &p_script) {
|
||||||
|
ERR_FAIL_COND_MSG(debugger_plugins.has(p_script), "Debugger plugin already exists.");
|
||||||
|
ERR_FAIL_COND_MSG(p_script.is_null(), "Debugger plugin script is null");
|
||||||
|
ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) == "", "Debugger plugin script has error.");
|
||||||
|
ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) != "EditorDebuggerPlugin", "Base type of debugger plugin is not 'EditorDebuggerPlugin'.");
|
||||||
|
ERR_FAIL_COND_MSG(!p_script->is_tool(), "Debugger plugin script is not in tool mode.");
|
||||||
|
debugger_plugins.insert(p_script);
|
||||||
|
for (int i = 0; get_debugger(i); i++) {
|
||||||
|
get_debugger(i)->add_debugger_plugin(p_script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerNode::remove_debugger_plugin(const Ref<Script> &p_script) {
|
||||||
|
ERR_FAIL_COND_MSG(!debugger_plugins.has(p_script), "Debugger plugin doesn't exists.");
|
||||||
|
debugger_plugins.erase(p_script);
|
||||||
|
for (int i = 0; get_debugger(i); i++) {
|
||||||
|
get_debugger(i)->remove_debugger_plugin(p_script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -103,6 +103,8 @@ private:
|
||||||
CameraOverride camera_override = OVERRIDE_NONE;
|
CameraOverride camera_override = OVERRIDE_NONE;
|
||||||
Map<Breakpoint, bool> breakpoints;
|
Map<Breakpoint, bool> breakpoints;
|
||||||
|
|
||||||
|
Set<Ref<Script>> debugger_plugins;
|
||||||
|
|
||||||
ScriptEditorDebugger *_add_debugger();
|
ScriptEditorDebugger *_add_debugger();
|
||||||
EditorDebuggerRemoteObject *get_inspected_remote_object();
|
EditorDebuggerRemoteObject *get_inspected_remote_object();
|
||||||
|
|
||||||
|
@ -186,5 +188,8 @@ public:
|
||||||
Error start(const String &p_protocol = "tcp://");
|
Error start(const String &p_protocol = "tcp://");
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
void add_debugger_plugin(const Ref<Script> &p_script);
|
||||||
|
void remove_debugger_plugin(const Ref<Script> &p_script);
|
||||||
};
|
};
|
||||||
#endif // EDITOR_DEBUGGER_NODE_H
|
#endif // EDITOR_DEBUGGER_NODE_H
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "editor/editor_scale.h"
|
#include "editor/editor_scale.h"
|
||||||
#include "editor/editor_settings.h"
|
#include "editor/editor_settings.h"
|
||||||
#include "editor/plugins/canvas_item_editor_plugin.h"
|
#include "editor/plugins/canvas_item_editor_plugin.h"
|
||||||
|
#include "editor/plugins/editor_debugger_plugin.h"
|
||||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||||
#include "editor/property_editor.h"
|
#include "editor/property_editor.h"
|
||||||
#include "main/performance.h"
|
#include "main/performance.h"
|
||||||
|
@ -701,7 +702,28 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
||||||
performance_profiler->update_monitors(monitors);
|
performance_profiler->update_monitors(monitors);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
WARN_PRINT("unknown message " + p_msg);
|
int colon_index = p_msg.find_char(':');
|
||||||
|
ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");
|
||||||
|
|
||||||
|
bool parsed = false;
|
||||||
|
const String cap = p_msg.substr(0, colon_index);
|
||||||
|
Map<StringName, Callable>::Element *element = captures.find(cap);
|
||||||
|
if (element) {
|
||||||
|
Callable &c = element->value();
|
||||||
|
ERR_FAIL_COND_MSG(c.is_null(), "Invalid callable registered: " + cap);
|
||||||
|
Variant cmd = p_msg.substr(colon_index + 1), data = p_data;
|
||||||
|
const Variant *args[2] = { &cmd, &data };
|
||||||
|
Variant retval;
|
||||||
|
Callable::CallError err;
|
||||||
|
c.call(args, 2, retval, err);
|
||||||
|
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(c, args, 2, err));
|
||||||
|
ERR_FAIL_COND_MSG(retval.get_type() != Variant::BOOL, "Error calling 'capture' to callable: " + String(c) + ". Return type is not bool.");
|
||||||
|
parsed = retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parsed) {
|
||||||
|
WARN_PRINT("unknown message " + p_msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -847,6 +869,7 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
|
||||||
tabs->set_current_tab(0);
|
tabs->set_current_tab(0);
|
||||||
_set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);
|
_set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);
|
||||||
_update_buttons_state();
|
_update_buttons_state();
|
||||||
|
emit_signal("started");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEditorDebugger::_update_buttons_state() {
|
void ScriptEditorDebugger::_update_buttons_state() {
|
||||||
|
@ -1395,6 +1418,7 @@ void ScriptEditorDebugger::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("request_remote_object", "id"), &ScriptEditorDebugger::request_remote_object);
|
ClassDB::bind_method(D_METHOD("request_remote_object", "id"), &ScriptEditorDebugger::request_remote_object);
|
||||||
ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value"), &ScriptEditorDebugger::update_remote_object);
|
ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value"), &ScriptEditorDebugger::update_remote_object);
|
||||||
|
|
||||||
|
ADD_SIGNAL(MethodInfo("started"));
|
||||||
ADD_SIGNAL(MethodInfo("stopped"));
|
ADD_SIGNAL(MethodInfo("stopped"));
|
||||||
ADD_SIGNAL(MethodInfo("stop_requested"));
|
ADD_SIGNAL(MethodInfo("stop_requested"));
|
||||||
ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame")));
|
ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame")));
|
||||||
|
@ -1408,6 +1432,43 @@ void ScriptEditorDebugger::_bind_methods() {
|
||||||
ADD_SIGNAL(MethodInfo("remote_tree_updated"));
|
ADD_SIGNAL(MethodInfo("remote_tree_updated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptEditorDebugger::add_debugger_plugin(const Ref<Script> &p_script) {
|
||||||
|
if (!debugger_plugins.has(p_script)) {
|
||||||
|
EditorDebuggerPlugin *plugin = memnew(EditorDebuggerPlugin());
|
||||||
|
plugin->attach_debugger(this);
|
||||||
|
plugin->set_script(p_script);
|
||||||
|
tabs->add_child(plugin);
|
||||||
|
debugger_plugins.insert(p_script, plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEditorDebugger::remove_debugger_plugin(const Ref<Script> &p_script) {
|
||||||
|
if (debugger_plugins.has(p_script)) {
|
||||||
|
tabs->remove_child(debugger_plugins[p_script]);
|
||||||
|
debugger_plugins[p_script]->detach_debugger(false);
|
||||||
|
memdelete(debugger_plugins[p_script]);
|
||||||
|
debugger_plugins.erase(p_script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args) {
|
||||||
|
_put_msg(p_message, p_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEditorDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) {
|
||||||
|
ERR_FAIL_COND_MSG(has_capture(p_name), "Capture already registered: " + p_name);
|
||||||
|
captures.insert(p_name, p_callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEditorDebugger::unregister_message_capture(const StringName &p_name) {
|
||||||
|
ERR_FAIL_COND_MSG(!has_capture(p_name), "Capture not registered: " + p_name);
|
||||||
|
captures.erase(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptEditorDebugger::has_capture(const StringName &p_name) {
|
||||||
|
return captures.has(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
||||||
editor = p_editor;
|
editor = p_editor;
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ class EditorVisualProfiler;
|
||||||
class EditorNetworkProfiler;
|
class EditorNetworkProfiler;
|
||||||
class EditorPerformanceProfiler;
|
class EditorPerformanceProfiler;
|
||||||
class SceneDebuggerTree;
|
class SceneDebuggerTree;
|
||||||
|
class EditorDebuggerPlugin;
|
||||||
|
|
||||||
class ScriptEditorDebugger : public MarginContainer {
|
class ScriptEditorDebugger : public MarginContainer {
|
||||||
GDCLASS(ScriptEditorDebugger, MarginContainer);
|
GDCLASS(ScriptEditorDebugger, MarginContainer);
|
||||||
|
@ -146,6 +147,10 @@ private:
|
||||||
|
|
||||||
EditorDebuggerNode::CameraOverride camera_override;
|
EditorDebuggerNode::CameraOverride camera_override;
|
||||||
|
|
||||||
|
Map<Ref<Script>, EditorDebuggerPlugin *> debugger_plugins;
|
||||||
|
|
||||||
|
Map<StringName, Callable> captures;
|
||||||
|
|
||||||
void _stack_dump_frame_selected();
|
void _stack_dump_frame_selected();
|
||||||
|
|
||||||
void _file_selected(const String &p_file);
|
void _file_selected(const String &p_file);
|
||||||
|
@ -253,6 +258,16 @@ public:
|
||||||
bool is_skip_breakpoints();
|
bool is_skip_breakpoints();
|
||||||
|
|
||||||
virtual Size2 get_minimum_size() const override;
|
virtual Size2 get_minimum_size() const override;
|
||||||
|
|
||||||
|
void add_debugger_plugin(const Ref<Script> &p_script);
|
||||||
|
void remove_debugger_plugin(const Ref<Script> &p_script);
|
||||||
|
|
||||||
|
void send_message(const String &p_message, const Array &p_args);
|
||||||
|
|
||||||
|
void register_message_capture(const StringName &p_name, const Callable &p_callable);
|
||||||
|
void unregister_message_capture(const StringName &p_name);
|
||||||
|
bool has_capture(const StringName &p_name);
|
||||||
|
|
||||||
ScriptEditorDebugger(EditorNode *p_editor = nullptr);
|
ScriptEditorDebugger(EditorNode *p_editor = nullptr);
|
||||||
~ScriptEditorDebugger();
|
~ScriptEditorDebugger();
|
||||||
};
|
};
|
||||||
|
|
|
@ -123,6 +123,7 @@
|
||||||
#include "editor/plugins/cpu_particles_3d_editor_plugin.h"
|
#include "editor/plugins/cpu_particles_3d_editor_plugin.h"
|
||||||
#include "editor/plugins/curve_editor_plugin.h"
|
#include "editor/plugins/curve_editor_plugin.h"
|
||||||
#include "editor/plugins/debugger_editor_plugin.h"
|
#include "editor/plugins/debugger_editor_plugin.h"
|
||||||
|
#include "editor/plugins/editor_debugger_plugin.h"
|
||||||
#include "editor/plugins/editor_preview_plugins.h"
|
#include "editor/plugins/editor_preview_plugins.h"
|
||||||
#include "editor/plugins/gi_probe_editor_plugin.h"
|
#include "editor/plugins/gi_probe_editor_plugin.h"
|
||||||
#include "editor/plugins/gpu_particles_2d_editor_plugin.h"
|
#include "editor/plugins/gpu_particles_2d_editor_plugin.h"
|
||||||
|
@ -3622,6 +3623,7 @@ void EditorNode::register_editor_types() {
|
||||||
// FIXME: Is this stuff obsolete, or should it be ported to new APIs?
|
// FIXME: Is this stuff obsolete, or should it be ported to new APIs?
|
||||||
ClassDB::register_class<EditorScenePostImport>();
|
ClassDB::register_class<EditorScenePostImport>();
|
||||||
//ClassDB::register_type<EditorImportExport>();
|
//ClassDB::register_type<EditorImportExport>();
|
||||||
|
ClassDB::register_class<EditorDebuggerPlugin>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorNode::unregister_editor_types() {
|
void EditorNode::unregister_editor_types() {
|
||||||
|
|
|
@ -811,6 +811,14 @@ ScriptCreateDialog *EditorPlugin::get_script_create_dialog() {
|
||||||
return EditorNode::get_singleton()->get_script_create_dialog();
|
return EditorNode::get_singleton()->get_script_create_dialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorPlugin::add_debugger_plugin(const Ref<Script> &p_script) {
|
||||||
|
EditorDebuggerNode::get_singleton()->add_debugger_plugin(p_script);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorPlugin::remove_debugger_plugin(const Ref<Script> &p_script) {
|
||||||
|
EditorDebuggerNode::get_singleton()->remove_debugger_plugin(p_script);
|
||||||
|
}
|
||||||
|
|
||||||
void EditorPlugin::_bind_methods() {
|
void EditorPlugin::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("add_control_to_container", "container", "control"), &EditorPlugin::add_control_to_container);
|
ClassDB::bind_method(D_METHOD("add_control_to_container", "container", "control"), &EditorPlugin::add_control_to_container);
|
||||||
ClassDB::bind_method(D_METHOD("add_control_to_bottom_panel", "control", "title"), &EditorPlugin::add_control_to_bottom_panel);
|
ClassDB::bind_method(D_METHOD("add_control_to_bottom_panel", "control", "title"), &EditorPlugin::add_control_to_bottom_panel);
|
||||||
|
@ -851,6 +859,8 @@ void EditorPlugin::_bind_methods() {
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface);
|
ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface);
|
||||||
ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
|
ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
|
||||||
|
ClassDB::bind_method(D_METHOD("add_debugger_plugin", "script"), &EditorPlugin::add_debugger_plugin);
|
||||||
|
ClassDB::bind_method(D_METHOD("remove_debugger_plugin", "script"), &EditorPlugin::remove_debugger_plugin);
|
||||||
|
|
||||||
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
|
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
|
||||||
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
|
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include "core/io/config_file.h"
|
#include "core/io/config_file.h"
|
||||||
#include "core/undo_redo.h"
|
#include "core/undo_redo.h"
|
||||||
|
#include "editor/debugger/editor_debugger_node.h"
|
||||||
#include "editor/editor_inspector.h"
|
#include "editor/editor_inspector.h"
|
||||||
#include "editor/editor_translation_parser.h"
|
#include "editor/editor_translation_parser.h"
|
||||||
#include "editor/import/editor_import_plugin.h"
|
#include "editor/import/editor_import_plugin.h"
|
||||||
|
@ -249,6 +250,9 @@ public:
|
||||||
void add_autoload_singleton(const String &p_name, const String &p_path);
|
void add_autoload_singleton(const String &p_name, const String &p_path);
|
||||||
void remove_autoload_singleton(const String &p_name);
|
void remove_autoload_singleton(const String &p_name);
|
||||||
|
|
||||||
|
void add_debugger_plugin(const Ref<Script> &p_script);
|
||||||
|
void remove_debugger_plugin(const Ref<Script> &p_script);
|
||||||
|
|
||||||
void enable_plugin();
|
void enable_plugin();
|
||||||
void disable_plugin();
|
void disable_plugin();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* editor_debugger_plugin.cpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#include "editor_debugger_plugin.h"
|
||||||
|
|
||||||
|
#include "editor/debugger/script_editor_debugger.h"
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::_breaked(bool p_really_did, bool p_can_debug) {
|
||||||
|
if (p_really_did) {
|
||||||
|
emit_signal("breaked", p_can_debug);
|
||||||
|
} else {
|
||||||
|
emit_signal("continued");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::_started() {
|
||||||
|
emit_signal("started");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::_stopped() {
|
||||||
|
emit_signal("stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &EditorDebuggerPlugin::send_message);
|
||||||
|
ClassDB::bind_method(D_METHOD("register_message_capture", "name", "callable"), &EditorDebuggerPlugin::register_message_capture);
|
||||||
|
ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &EditorDebuggerPlugin::unregister_message_capture);
|
||||||
|
ClassDB::bind_method(D_METHOD("has_capture", "name"), &EditorDebuggerPlugin::has_capture);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_breaked"), &EditorDebuggerPlugin::is_breaked);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_debuggable"), &EditorDebuggerPlugin::is_debuggable);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_session_active"), &EditorDebuggerPlugin::is_session_active);
|
||||||
|
|
||||||
|
ADD_SIGNAL(MethodInfo("started"));
|
||||||
|
ADD_SIGNAL(MethodInfo("stopped"));
|
||||||
|
ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "can_debug")));
|
||||||
|
ADD_SIGNAL(MethodInfo("continued"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::attach_debugger(ScriptEditorDebugger *p_debugger) {
|
||||||
|
debugger = p_debugger;
|
||||||
|
if (debugger) {
|
||||||
|
debugger->connect("started", callable_mp(this, &EditorDebuggerPlugin::_started));
|
||||||
|
debugger->connect("stopped", callable_mp(this, &EditorDebuggerPlugin::_stopped));
|
||||||
|
debugger->connect("breaked", callable_mp(this, &EditorDebuggerPlugin::_breaked));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::detach_debugger(bool p_call_debugger) {
|
||||||
|
if (debugger) {
|
||||||
|
debugger->disconnect("started", callable_mp(this, &EditorDebuggerPlugin::_started));
|
||||||
|
debugger->disconnect("stopped", callable_mp(this, &EditorDebuggerPlugin::_stopped));
|
||||||
|
debugger->disconnect("breaked", callable_mp(this, &EditorDebuggerPlugin::_breaked));
|
||||||
|
if (p_call_debugger && get_script_instance()) {
|
||||||
|
debugger->remove_debugger_plugin(get_script_instance()->get_script());
|
||||||
|
}
|
||||||
|
debugger = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::send_message(const String &p_message, const Array &p_args) {
|
||||||
|
ERR_FAIL_COND_MSG(!debugger, "Plugin is not attached to debugger");
|
||||||
|
debugger->send_message(p_message, p_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::register_message_capture(const StringName &p_name, const Callable &p_callable) {
|
||||||
|
ERR_FAIL_COND_MSG(!debugger, "Plugin is not attached to debugger");
|
||||||
|
debugger->register_message_capture(p_name, p_callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorDebuggerPlugin::unregister_message_capture(const StringName &p_name) {
|
||||||
|
ERR_FAIL_COND_MSG(!debugger, "Plugin is not attached to debugger");
|
||||||
|
debugger->unregister_message_capture(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorDebuggerPlugin::has_capture(const StringName &p_name) {
|
||||||
|
ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger");
|
||||||
|
return debugger->has_capture(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorDebuggerPlugin::is_breaked() {
|
||||||
|
ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger");
|
||||||
|
return debugger->is_breaked();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorDebuggerPlugin::is_debuggable() {
|
||||||
|
ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger");
|
||||||
|
return debugger->is_debuggable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorDebuggerPlugin::is_session_active() {
|
||||||
|
ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger");
|
||||||
|
return debugger->is_session_active();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorDebuggerPlugin::~EditorDebuggerPlugin() {
|
||||||
|
detach_debugger(true);
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* editor_debugger_plugin.h */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#ifndef EDITOR_DEBUGGER_PLUGIN_H
|
||||||
|
#define EDITOR_DEBUGGER_PLUGIN_H
|
||||||
|
|
||||||
|
#include "scene/gui/control.h"
|
||||||
|
|
||||||
|
class ScriptEditorDebugger;
|
||||||
|
|
||||||
|
class EditorDebuggerPlugin : public Control {
|
||||||
|
GDCLASS(EditorDebuggerPlugin, Control);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScriptEditorDebugger *debugger;
|
||||||
|
|
||||||
|
void _breaked(bool p_really_did, bool p_can_debug);
|
||||||
|
void _started();
|
||||||
|
void _stopped();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void attach_debugger(ScriptEditorDebugger *p_debugger);
|
||||||
|
void detach_debugger(bool p_call_debugger);
|
||||||
|
void send_message(const String &p_message, const Array &p_args);
|
||||||
|
void register_message_capture(const StringName &p_name, const Callable &p_callable);
|
||||||
|
void unregister_message_capture(const StringName &p_name);
|
||||||
|
bool has_capture(const StringName &p_name);
|
||||||
|
bool is_breaked();
|
||||||
|
bool is_debuggable();
|
||||||
|
bool is_session_active();
|
||||||
|
~EditorDebuggerPlugin();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EDITOR_DEBUGGER_PLUGIN_H
|
Loading…
Reference in New Issue