From 4c19f80227b06db2ed97f63afb2c30d302e83b2c Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Sun, 30 Jun 2024 01:59:34 -0400 Subject: [PATCH] GH-285 Add EditorDebugger support (Requires Godot 4.3) --- src/editor/editor_cache.cpp | 133 ++++++++ src/editor/editor_cache.h | 85 +++++ src/editor/editor_panel.cpp | 78 +++++ src/editor/editor_panel.h | 14 + src/editor/editor_viewport.cpp | 31 ++ src/editor/editor_viewport.h | 16 + src/editor/graph/graph_node.cpp | 114 ++++++- src/editor/graph/graph_node.h | 13 + .../orchestrator_editor_debugger_plugin.cpp | 97 ++++++ .../orchestrator_editor_debugger_plugin.h | 73 ++++ .../plugins/orchestrator_editor_plugin.cpp | 28 ++ .../plugins/orchestrator_editor_plugin.h | 11 + src/editor/register_editor_types.cpp | 4 + src/script/instances/node_instance.h | 1 + src/script/instances/script_instance.cpp | 2 +- src/script/instances/script_instance.h | 3 + src/script/language.cpp | 311 +++++++++++++++++- src/script/language.h | 39 +++ src/script/node.cpp | 14 + src/script/node.h | 26 ++ src/script/script.cpp | 4 +- src/script/vm/execution_context.h | 8 + src/script/vm/script_state.cpp | 1 + src/script/vm/script_state.h | 1 + src/script/vm/script_vm.cpp | 73 +++- src/script/vm/script_vm.h | 5 +- 26 files changed, 1168 insertions(+), 17 deletions(-) create mode 100644 src/editor/editor_cache.cpp create mode 100644 src/editor/editor_cache.h create mode 100644 src/editor/plugins/orchestrator_editor_debugger_plugin.cpp create mode 100644 src/editor/plugins/orchestrator_editor_debugger_plugin.h diff --git a/src/editor/editor_cache.cpp b/src/editor/editor_cache.cpp new file mode 100644 index 00000000..b3b44b7e --- /dev/null +++ b/src/editor/editor_cache.cpp @@ -0,0 +1,133 @@ +// This file is part of the Godot Orchestrator project. +// +// Copyright (c) 2023-present Vahera Studios LLC and its contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "editor/editor_cache.h" +#include "plugins/orchestrator_editor_debugger_plugin.h" +#include "plugins/orchestrator_editor_plugin.h" + +#include + +#if GODOT_VERSION >= 0x040300 +PackedInt64Array OrchestratorEditorCache::_get_breakpoints_for_path(const String& p_path, bool p_disabled) const +{ + return _cache->get_value(p_path, p_disabled ? "disabled_breakpoints" : "breakpoints", PackedInt64Array()); +} +#endif + +Error OrchestratorEditorCache::load() +{ + const EditorInterface* ei = OrchestratorPlugin::get_singleton()->get_editor_interface(); + + _cache = Ref(memnew(ConfigFile)); + + Error result = _cache->load(ei->get_editor_paths()->get_project_settings_dir().path_join(CACHE_FILE)); + if (result != OK) + return result; + + #if GODOT_VERSION >= 0x040300 + if (OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton()) + { + for (const String& section : _cache->get_sections()) + { + if (_cache->has_section_key(section, "breakpoints")) + { + PackedInt64Array breakpoints = _get_breakpoints_for_path(section, false); + for (int i = 0; i < breakpoints.size(); i++) + debugger->set_breakpoint(section, breakpoints[i], true); + } + if (_cache->has_section_key(section, "disabled_breakpoints")) + { + PackedInt64Array disabled_breakpoints = _get_breakpoints_for_path(section, true); + for (int i = 0; i < disabled_breakpoints.size(); i++) + debugger->set_breakpoint(section, disabled_breakpoints[i], true); + } + } + } + #endif + + return OK; +} + +Error OrchestratorEditorCache::save() +{ + if (_cache.is_valid()) + { + const EditorInterface* ei = OrchestratorPlugin::get_singleton()->get_editor_interface(); + return _cache->save(ei->get_editor_paths()->get_project_settings_dir().path_join(CACHE_FILE)); + } + + return ERR_FILE_CANT_WRITE; +} + +#if GODOT_VERSION >= 0x040300 +void OrchestratorEditorCache::clear_all_breakpoints() +{ + for (const String& section : _cache->get_sections()) + { + PackedInt64Array breakpoints = _get_breakpoints_for_path(section, false); + if (!breakpoints.is_empty()) + _cache->set_value(section, "breakpoints", Variant()); + + PackedInt64Array disabled_breakpoints = _get_breakpoints_for_path(section, true); + if (!disabled_breakpoints.is_empty()) + _cache->set_value(section, "disabled_breakpoints", Variant()); + } +} + +bool OrchestratorEditorCache::is_node_breakpoint(const String& p_path, int p_node_id) const +{ + return _get_breakpoints_for_path(p_path, false).has(p_node_id); +} + +bool OrchestratorEditorCache::is_node_disabled_breakpoint(const String& p_path, int p_node_id) const +{ + return _get_breakpoints_for_path(p_path, true).has(p_node_id); +} + +void OrchestratorEditorCache::set_breakpoint(const String& p_path, int p_node_id, bool p_enabled) +{ + PackedInt64Array breakpoints = _get_breakpoints_for_path(p_path, false); + if (p_enabled && !breakpoints.has(p_node_id)) + { + breakpoints.push_back(p_node_id); + _cache->set_value(p_path, "breakpoints", breakpoints); + } + else if (!p_enabled && breakpoints.has(p_node_id)) + { + breakpoints.remove_at(breakpoints.find(p_node_id)); + _cache->set_value(p_path, "breakpoints", breakpoints); + } +} + +void OrchestratorEditorCache::set_disabled_breakpoint(const String& p_path, int p_node_id, bool p_remove) +{ + PackedInt64Array breakpoints = _get_breakpoints_for_path(p_path, true); + if (p_remove && breakpoints.has(p_node_id)) + { + breakpoints.remove_at(breakpoints.find(p_node_id)); + _cache->set_value(p_path, "disabled_breakpoints", breakpoints); + } + else if (!p_remove && !breakpoints.has(p_node_id)) + { + breakpoints.push_back(p_node_id); + _cache->set_value(p_path, "disabled_breakpoints", breakpoints); + } +} +#endif + +void OrchestratorEditorCache::_bind_methods() +{ +} \ No newline at end of file diff --git a/src/editor/editor_cache.h b/src/editor/editor_cache.h new file mode 100644 index 00000000..d36d482b --- /dev/null +++ b/src/editor/editor_cache.h @@ -0,0 +1,85 @@ +// This file is part of the Godot Orchestrator project. +// +// Copyright (c) 2023-present Vahera Studios LLC and its contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef ORCHESTRATOR_EDITOR_CACHE_H +#define ORCHESTRATOR_EDITOR_CACHE_H + +#include "common/version.h" + +#include +#include + +using namespace godot; + +/// A simple cache that maintains editor state details for Orchestrator. +class OrchestratorEditorCache : public RefCounted +{ + GDCLASS(OrchestratorEditorCache, RefCounted); + static void _bind_methods(); + +protected: + const String CACHE_FILE = "orchestrator_editor_cache.cfg"; //! The cache file name + Ref _cache; //! Cache file + + #if GODOT_VERSION >= 0x040300 + /// Get breakpoints for the specified path + /// @param p_path the path + /// @param p_disabled when true, returns disabled breakpoints, false returns enabled breakpoints + /// @return array of breakpoints + PackedInt64Array _get_breakpoints_for_path(const String& p_path, bool p_disabled) const; + #endif + +public: + /// Loads the script editor cache from disk. + /// @return the error code based on the load operation + Error load(); + + /// Saves the script editor cache to disk. + /// @return the error code based on the save operation + Error save(); + + #if GODOT_VERSION >= 0x040300 + /// Clears all breakpoints + void clear_all_breakpoints(); + + /// Check whether the node in a script is a breakpoint + /// @param p_path the script path + /// @param p_node_id the node id + /// @return true if the node is a breakpoint, false otherwise + bool is_node_breakpoint(const String& p_path, int p_node_id) const; + + /// Check whether the node in a script is a breakpoint and is disabled + /// @param p_path the script path + /// @param p_node_id the node id + /// @return true if the node is a breakpoint that is disabled, false otherwise + bool is_node_disabled_breakpoint(const String& p_path, int p_node_id) const; + + /// Set whether a breakpoint is enabled + /// @param p_path the script path + /// @param p_node_id the node id + /// @param p_enabled whether the breakpoint is enabled or disabled + void set_breakpoint(const String& p_path, int p_node_id, bool p_enabled); + + /// Set the breakpoint as disabled + /// @param p_path the script path + /// @param p_node_id the node id + /// @param p_remove whether to remove the disabled entry + void set_disabled_breakpoint(const String& p_path, int p_node_id, bool p_remove = false); + #endif + +}; + +#endif // ORCHESTRATOR_EDITOR_CACHE_H \ No newline at end of file diff --git a/src/editor/editor_panel.cpp b/src/editor/editor_panel.cpp index fbc31570..4735026b 100644 --- a/src/editor/editor_panel.cpp +++ b/src/editor/editor_panel.cpp @@ -157,10 +157,31 @@ void OrchestratorEditorPanel::_update_scene_tab_signals(bool p_connect) if (p_connect) { OCONNECT(scene_tabs, "tab_changed", callable_mp(this, &OrchestratorEditorPanel::_scene_tab_changed)); + + #if GODOT_VERSION >= 0x040300 + OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton(); + if (debugger) + { + OCONNECT(debugger, "goto_script_line", callable_mp(this, &OrchestratorEditorPanel::_goto_script_line)); + OCONNECT(debugger, "breakpoints_cleared_in_tree", callable_mp(this, &OrchestratorEditorPanel::_clear_all_breakpoints)); + OCONNECT(debugger, "breakpoint_set_in_tree", callable_mp(this, &OrchestratorEditorPanel::_set_breakpoint)); + } + #endif + return; } ODISCONNECT(scene_tabs, "tab_changed", callable_mp(this, &OrchestratorEditorPanel::_scene_tab_changed)); + + #if GODOT_VERSION >= 0x040300 + OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton(); + if (debugger) + { + ODISCONNECT(debugger, "goto_script_line", callable_mp(this, &OrchestratorEditorPanel::_goto_script_line)); + ODISCONNECT(debugger, "breakpoints_cleared_in_tree", callable_mp(this, &OrchestratorEditorPanel::_clear_all_breakpoints)); + ODISCONNECT(debugger, "breakpoint_set_in_tree", callable_mp(this, &OrchestratorEditorPanel::_set_breakpoint)); + } + #endif } void OrchestratorEditorPanel::_update_file_system_dock_signals(bool p_connect) @@ -649,6 +670,53 @@ void OrchestratorEditorPanel::_folder_removed(const String& p_folder_name) } } +#if GODOT_VERSION >= 0x040300 +void OrchestratorEditorPanel::_goto_script_line(const Ref