2023-07-21 09:50:06 +00:00
|
|
|
/**
|
|
|
|
* limbo_debugger_plugin.cpp
|
|
|
|
* =============================================================================
|
|
|
|
* Copyright 2021-2023 Serhii Snitsaruk
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style
|
|
|
|
* license that can be found in the LICENSE file or at
|
|
|
|
* https://opensource.org/licenses/MIT.
|
|
|
|
* =============================================================================
|
|
|
|
*/
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-04-16 09:30:31 +00:00
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
|
2023-04-13 07:29:45 +00:00
|
|
|
#include "limbo_debugger_plugin.h"
|
2023-07-20 16:35:36 +00:00
|
|
|
|
|
|
|
#include "limbo_debugger.h"
|
|
|
|
#include "modules/limboai/editor/debugger/behavior_tree_data.h"
|
|
|
|
#include "modules/limboai/editor/debugger/behavior_tree_view.h"
|
|
|
|
|
2023-04-13 07:29:45 +00:00
|
|
|
#include "core/debugger/engine_debugger.h"
|
|
|
|
#include "core/math/math_defs.h"
|
|
|
|
#include "core/object/callable_method_pointer.h"
|
|
|
|
#include "core/os/memory.h"
|
|
|
|
#include "core/string/print_string.h"
|
2023-04-16 08:34:13 +00:00
|
|
|
#include "core/string/ustring.h"
|
2023-04-13 07:29:45 +00:00
|
|
|
#include "core/variant/array.h"
|
|
|
|
#include "editor/editor_scale.h"
|
|
|
|
#include "editor/plugins/editor_debugger_plugin.h"
|
|
|
|
#include "scene/gui/box_container.h"
|
|
|
|
#include "scene/gui/control.h"
|
|
|
|
#include "scene/gui/item_list.h"
|
|
|
|
#include "scene/gui/label.h"
|
2023-04-15 07:01:37 +00:00
|
|
|
#include "scene/gui/line_edit.h"
|
2023-04-13 07:29:45 +00:00
|
|
|
#include "scene/gui/split_container.h"
|
2023-08-20 09:34:13 +00:00
|
|
|
#include "scene/gui/tab_container.h"
|
2023-04-13 07:29:45 +00:00
|
|
|
#include "scene/gui/texture_rect.h"
|
|
|
|
|
2023-08-20 09:34:13 +00:00
|
|
|
//**** LimboDebuggerTab
|
2023-04-13 07:29:45 +00:00
|
|
|
|
|
|
|
void LimboDebuggerTab::start_session() {
|
2023-04-15 07:01:37 +00:00
|
|
|
bt_player_list->clear();
|
2023-04-13 07:29:45 +00:00
|
|
|
bt_view->clear();
|
2023-04-15 07:01:37 +00:00
|
|
|
alert_box->hide();
|
|
|
|
info_message->set_text(TTR("Pick a player from the list to display behavior tree."));
|
|
|
|
info_message->show();
|
2023-04-13 07:29:45 +00:00
|
|
|
session->send_message("limboai:start_session", Array());
|
|
|
|
}
|
|
|
|
|
|
|
|
void LimboDebuggerTab::stop_session() {
|
2023-04-15 07:01:37 +00:00
|
|
|
bt_player_list->clear();
|
|
|
|
bt_view->clear();
|
|
|
|
alert_box->hide();
|
|
|
|
info_message->set_text(TTR("Run project to start debugging."));
|
|
|
|
info_message->show();
|
2023-04-13 07:29:45 +00:00
|
|
|
session->send_message("limboai:stop_session", Array());
|
|
|
|
}
|
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
void LimboDebuggerTab::update_active_bt_players(const Array &p_node_paths) {
|
|
|
|
active_bt_players.clear();
|
|
|
|
for (int i = 0; i < p_node_paths.size(); i++) {
|
|
|
|
active_bt_players.push_back(p_node_paths[i]);
|
|
|
|
}
|
|
|
|
_update_bt_player_list(active_bt_players, filter_players->get_text());
|
|
|
|
}
|
|
|
|
|
|
|
|
String LimboDebuggerTab::get_selected_bt_player() {
|
|
|
|
if (!bt_player_list->is_anything_selected()) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
return bt_player_list->get_item_text(bt_player_list->get_selected_items()[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LimboDebuggerTab::update_behavior_tree(const BehaviorTreeData &p_data) {
|
|
|
|
bt_view->update_tree(p_data);
|
|
|
|
info_message->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LimboDebuggerTab::_show_alert(const String &p_message) {
|
|
|
|
alert_message->set_text(p_message);
|
2023-04-15 08:53:57 +00:00
|
|
|
// alert_icon->set_texture(get_theme_icon(SNAME("NodeInfo"), SNAME("EditorIcons")));
|
|
|
|
alert_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
|
2023-04-15 07:01:37 +00:00
|
|
|
alert_box->set_visible(!p_message.is_empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
void LimboDebuggerTab::_update_bt_player_list(const List<String> &p_node_paths, const String &p_filter) {
|
2023-04-13 07:29:45 +00:00
|
|
|
// Remember selected item.
|
2023-04-15 07:01:37 +00:00
|
|
|
String selected_player = "";
|
|
|
|
if (bt_player_list->is_anything_selected()) {
|
|
|
|
selected_player = bt_player_list->get_item_text(bt_player_list->get_selected_items().get(0));
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
bt_player_list->clear();
|
2023-04-13 07:29:45 +00:00
|
|
|
int select_idx = -1;
|
2023-04-15 07:01:37 +00:00
|
|
|
bool selection_filtered_out = false;
|
|
|
|
for (const String &p : p_node_paths) {
|
|
|
|
if (p_filter.is_empty() || p.contains(p_filter)) {
|
|
|
|
int idx = bt_player_list->add_item(p);
|
|
|
|
// Make item text shortened from the left, e.g ".../Agent/BTPlayer".
|
|
|
|
bt_player_list->set_item_text_direction(idx, TEXT_DIRECTION_RTL);
|
|
|
|
if (p == selected_player) {
|
|
|
|
select_idx = idx;
|
|
|
|
}
|
|
|
|
} else if (p == selected_player) {
|
|
|
|
selection_filtered_out = true;
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore selected item.
|
|
|
|
if (select_idx > -1) {
|
2023-04-15 07:01:37 +00:00
|
|
|
bt_player_list->select(select_idx);
|
|
|
|
} else if (!selected_player.is_empty()) {
|
|
|
|
if (selection_filtered_out) {
|
|
|
|
session->send_message("limboai:untrack_bt_player", Array());
|
|
|
|
bt_view->clear();
|
|
|
|
_show_alert("");
|
|
|
|
} else {
|
|
|
|
_show_alert("BehaviorTree player is no longer present.");
|
|
|
|
}
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LimboDebuggerTab::_bt_selected(int p_idx) {
|
2023-04-15 07:01:37 +00:00
|
|
|
alert_box->hide();
|
2023-04-13 07:29:45 +00:00
|
|
|
bt_view->clear();
|
2023-04-15 07:01:37 +00:00
|
|
|
info_message->set_text(TTR("Waiting for behavior tree update."));
|
|
|
|
info_message->show();
|
|
|
|
NodePath path = bt_player_list->get_item_text(p_idx);
|
2023-07-20 18:10:02 +00:00
|
|
|
Array msg_data;
|
|
|
|
msg_data.push_back(path);
|
|
|
|
session->send_message("limboai:track_bt_player", msg_data);
|
2023-04-15 07:01:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LimboDebuggerTab::_filter_changed(String p_text) {
|
|
|
|
_update_bt_player_list(active_bt_players, p_text);
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|
|
|
|
|
2023-08-02 10:13:00 +00:00
|
|
|
void LimboDebuggerTab::_window_visibility_changed(bool p_visible) {
|
|
|
|
make_floating->set_visible(!p_visible);
|
|
|
|
}
|
|
|
|
|
|
|
|
LimboDebuggerTab::LimboDebuggerTab(Ref<EditorDebuggerSession> p_session, WindowWrapper *p_wrapper) {
|
2023-04-13 07:29:45 +00:00
|
|
|
session = p_session;
|
2023-08-02 10:13:00 +00:00
|
|
|
window_wrapper = p_wrapper;
|
|
|
|
|
|
|
|
root_vb = memnew(VBoxContainer);
|
|
|
|
add_child(root_vb);
|
|
|
|
|
|
|
|
toolbar = memnew(HBoxContainer);
|
|
|
|
root_vb->add_child(toolbar);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
|
|
|
hsc = memnew(HSplitContainer);
|
2023-08-02 10:13:00 +00:00
|
|
|
hsc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
|
|
|
hsc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
|
|
|
root_vb->add_child(hsc);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
VBoxContainer *list_box = memnew(VBoxContainer);
|
|
|
|
hsc->add_child(list_box);
|
|
|
|
|
2023-08-02 10:13:00 +00:00
|
|
|
if (p_wrapper->is_window_available()) {
|
|
|
|
make_floating = memnew(ScreenSelect);
|
|
|
|
make_floating->set_flat(true);
|
|
|
|
make_floating->set_h_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_END);
|
|
|
|
make_floating->set_tooltip_text(TTR("Make the LimboAI Debugger floating."));
|
|
|
|
make_floating->connect(SNAME("request_open_in_screen"), callable_mp(window_wrapper, &WindowWrapper::enable_window_on_screen).bind(true));
|
|
|
|
toolbar->add_child(make_floating);
|
|
|
|
p_wrapper->connect(SNAME("window_visibility_changed"), callable_mp(this, &LimboDebuggerTab::_window_visibility_changed));
|
|
|
|
}
|
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
filter_players = memnew(LineEdit);
|
|
|
|
filter_players->set_placeholder(TTR("Filter Players"));
|
|
|
|
filter_players->connect(SNAME("text_changed"), callable_mp(this, &LimboDebuggerTab::_filter_changed));
|
|
|
|
list_box->add_child(filter_players);
|
|
|
|
|
|
|
|
bt_player_list = memnew(ItemList);
|
|
|
|
bt_player_list->set_custom_minimum_size(Size2(240.0 * EDSCALE, 0.0));
|
|
|
|
bt_player_list->set_h_size_flags(SIZE_FILL);
|
|
|
|
bt_player_list->set_v_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
bt_player_list->connect(SNAME("item_selected"), callable_mp(this, &LimboDebuggerTab::_bt_selected));
|
|
|
|
list_box->add_child(bt_player_list);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
|
|
|
view_box = memnew(VBoxContainer);
|
2023-04-15 07:01:37 +00:00
|
|
|
hsc->add_child(view_box);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
bt_view = memnew(BehaviorTreeView);
|
|
|
|
bt_view->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
|
|
|
bt_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
|
|
|
view_box->add_child(bt_view);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
alert_box = memnew(HBoxContainer);
|
|
|
|
alert_box->hide();
|
|
|
|
view_box->add_child(alert_box);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
alert_icon = memnew(TextureRect);
|
|
|
|
alert_box->add_child(alert_icon);
|
|
|
|
alert_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
alert_message = memnew(Label);
|
|
|
|
alert_box->add_child(alert_message);
|
|
|
|
alert_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-04-15 07:01:37 +00:00
|
|
|
info_message = memnew(Label);
|
|
|
|
info_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
|
|
|
|
info_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
|
|
|
info_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
|
|
|
|
info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
|
|
|
|
info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
|
|
|
|
|
|
|
|
bt_view->add_child(info_message);
|
2023-04-13 07:29:45 +00:00
|
|
|
|
|
|
|
stop_session();
|
|
|
|
}
|
|
|
|
|
2023-08-20 09:34:13 +00:00
|
|
|
//**** LimboDebuggerPlugin
|
|
|
|
|
|
|
|
LimboDebuggerPlugin *LimboDebuggerPlugin::singleton = nullptr;
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-08-02 10:13:00 +00:00
|
|
|
void LimboDebuggerPlugin::_window_visibility_changed(bool p_visible) {
|
|
|
|
}
|
|
|
|
|
2023-04-13 07:29:45 +00:00
|
|
|
void LimboDebuggerPlugin::setup_session(int p_idx) {
|
|
|
|
Ref<EditorDebuggerSession> session = get_session(p_idx);
|
2023-08-02 10:13:00 +00:00
|
|
|
|
|
|
|
if (tab != nullptr) {
|
|
|
|
tab->queue_free();
|
|
|
|
window_wrapper->queue_free();
|
|
|
|
}
|
|
|
|
|
|
|
|
window_wrapper = memnew(WindowWrapper);
|
|
|
|
window_wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), TTR("LimboAI Debugger")));
|
|
|
|
window_wrapper->set_margins_enabled(true);
|
|
|
|
window_wrapper->set_name("LimboAI");
|
|
|
|
|
|
|
|
tab = memnew(LimboDebuggerTab(session, window_wrapper));
|
2023-04-13 07:29:45 +00:00
|
|
|
tab->set_name("LimboAI");
|
2023-08-02 10:13:00 +00:00
|
|
|
window_wrapper->set_wrapped_control(tab);
|
|
|
|
|
|
|
|
window_wrapper->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
|
|
|
window_wrapper->connect(SNAME("window_visibility_changed"), callable_mp(this, &LimboDebuggerPlugin::_window_visibility_changed));
|
|
|
|
|
2023-04-13 07:29:45 +00:00
|
|
|
session->connect(SNAME("started"), callable_mp(tab, &LimboDebuggerTab::start_session));
|
|
|
|
session->connect(SNAME("stopped"), callable_mp(tab, &LimboDebuggerTab::stop_session));
|
2023-08-02 10:13:00 +00:00
|
|
|
session->add_session_tab(window_wrapper);
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool LimboDebuggerPlugin::capture(const String &p_message, const Array &p_data, int p_session) {
|
|
|
|
bool captured = true;
|
2023-04-15 07:01:37 +00:00
|
|
|
if (p_message == "limboai:active_bt_players") {
|
|
|
|
tab->update_active_bt_players(p_data);
|
2023-04-13 07:29:45 +00:00
|
|
|
} else if (p_message == "limboai:bt_update") {
|
|
|
|
BehaviorTreeData data = BehaviorTreeData();
|
|
|
|
data.deserialize(p_data);
|
2023-04-15 07:01:37 +00:00
|
|
|
if (data.bt_player_path == tab->get_selected_bt_player()) {
|
|
|
|
tab->update_behavior_tree(data);
|
|
|
|
}
|
2023-04-13 07:29:45 +00:00
|
|
|
} else {
|
|
|
|
captured = false;
|
|
|
|
}
|
|
|
|
return captured;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LimboDebuggerPlugin::has_capture(const String &p_capture) const {
|
|
|
|
return p_capture == "limboai";
|
|
|
|
}
|
|
|
|
|
2023-08-20 09:34:13 +00:00
|
|
|
WindowWrapper *LimboDebuggerPlugin::get_session_tab() const {
|
|
|
|
return window_wrapper;
|
|
|
|
}
|
|
|
|
|
|
|
|
int LimboDebuggerPlugin::get_session_tab_index() const {
|
|
|
|
TabContainer *c = Object::cast_to<TabContainer>(window_wrapper->get_parent());
|
|
|
|
ERR_FAIL_COND_V(c == nullptr, -1);
|
|
|
|
return c->get_tab_idx_from_control(window_wrapper);
|
|
|
|
}
|
|
|
|
|
2023-04-13 07:29:45 +00:00
|
|
|
LimboDebuggerPlugin::LimboDebuggerPlugin() {
|
|
|
|
tab = nullptr;
|
2023-08-20 09:34:13 +00:00
|
|
|
singleton = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
LimboDebuggerPlugin::~LimboDebuggerPlugin() {
|
|
|
|
singleton = nullptr;
|
2023-04-15 07:01:37 +00:00
|
|
|
}
|
2023-04-16 09:30:31 +00:00
|
|
|
|
2023-08-02 10:13:00 +00:00
|
|
|
#endif // TOOLS_ENABLED
|