Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDScript: Hot-reload changed scripts only #86676

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
31 changes: 25 additions & 6 deletions core/debugger/remote_debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "core/debugger/engine_profiler.h"
#include "core/debugger/script_debugger.h"
#include "core/input/input.h"
#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
#include "core/os/os.h"

Expand Down Expand Up @@ -515,8 +516,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
_send_stack_vars(globals, globals_vals, 2);

} else if (command == "reload_scripts") {
script_paths_to_reload = data;
} else if (command == "reload_all_scripts") {
reload_all_scripts = true;

} else if (command == "breakpoint") {
ERR_FAIL_COND(data.size() < 3);
bool set = data[2];
Expand Down Expand Up @@ -591,19 +593,36 @@ void RemoteDebugger::poll_events(bool p_is_idle) {
}

// Reload scripts during idle poll only.
if (p_is_idle && reload_all_scripts) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->reload_all_scripts();
if (p_is_idle) {
if (reload_all_scripts) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->reload_all_scripts();
}
reload_all_scripts = false;
} else if (!script_paths_to_reload.is_empty()) {
Array scripts_to_reload;
for (int i = 0; i < script_paths_to_reload.size(); ++i) {
String path = script_paths_to_reload[i];
Error err = OK;
Ref<Script> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
ERR_CONTINUE_MSG(err != OK, vformat("Could not reload script '%s': %s", path, error_names[err]));
ERR_CONTINUE_MSG(script.is_null(), vformat("Could not reload script '%s': Not a script!", path, error_names[err]));
scripts_to_reload.push_back(script);
}
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->reload_scripts(scripts_to_reload, true);
}
}
reload_all_scripts = false;
script_paths_to_reload.clear();
}
}

Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
r_captured = true;
if (p_cmd == "reload_scripts") {
script_paths_to_reload = p_data;
} else if (p_cmd == "reload_all_scripts") {
reload_all_scripts = true;

} else if (p_cmd == "breakpoint") {
ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA);
bool set = p_data[2];
Expand Down
1 change: 1 addition & 0 deletions core/debugger/remote_debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class RemoteDebugger : public EngineDebugger {
int warn_count = 0;
int last_reset = 0;
bool reload_all_scripts = false;
Array script_paths_to_reload;

// Make handlers and send_message thread safe.
Mutex mutex;
Expand Down
1 change: 1 addition & 0 deletions core/object/script_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ class ScriptLanguage : public Object {
virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }

virtual void reload_all_scripts() = 0;
virtual void reload_scripts(const Array &p_scripts, bool p_soft_reload) = 0;
virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) = 0;
/* LOADER FUNCTIONS */

Expand Down
1 change: 1 addition & 0 deletions core/object/script_language_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ class ScriptLanguageExtension : public ScriptLanguage {
}

EXBIND0(reload_all_scripts)
EXBIND2(reload_scripts, const Array &, bool)
EXBIND2(reload_tool_script, const Ref<Script> &, bool)
/* LOADER FUNCTIONS */

Expand Down
10 changes: 8 additions & 2 deletions editor/debugger/editor_debugger_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,9 +593,15 @@ void EditorDebuggerNode::set_breakpoints(const String &p_path, Array p_lines) {
}
}

void EditorDebuggerNode::reload_scripts() {
void EditorDebuggerNode::reload_all_scripts() {
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
dbg->reload_scripts();
dbg->reload_all_scripts();
});
}

void EditorDebuggerNode::reload_scripts(const Vector<String> &p_script_paths) {
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
dbg->reload_scripts(p_script_paths);
});
}

Expand Down
3 changes: 2 additions & 1 deletion editor/debugger/editor_debugger_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ class EditorDebuggerNode : public MarginContainer {
bool is_skip_breakpoints() const;
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
void set_breakpoints(const String &p_path, Array p_lines);
void reload_scripts();
void reload_all_scripts();
void reload_scripts(const Vector<String> &p_script_paths);

// Remote inspector/edit.
void request_remote_tree();
Expand Down
8 changes: 6 additions & 2 deletions editor/debugger/script_editor_debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1518,8 +1518,12 @@ void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool
}
}

void ScriptEditorDebugger::reload_scripts() {
_put_msg("reload_scripts", Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
void ScriptEditorDebugger::reload_all_scripts() {
_put_msg("reload_all_scripts", Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
}

void ScriptEditorDebugger::reload_scripts(const Vector<String> &p_script_paths) {
_put_msg("reload_scripts", Variant(p_script_paths).operator Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
}

bool ScriptEditorDebugger::is_skip_breakpoints() {
Expand Down
3 changes: 2 additions & 1 deletion editor/debugger/script_editor_debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ class ScriptEditorDebugger : public MarginContainer {

void update_live_edit_root();

void reload_scripts();
void reload_all_scripts();
void reload_scripts(const Vector<String> &p_script_paths);

bool is_skip_breakpoints();

Expand Down
26 changes: 23 additions & 3 deletions editor/plugins/script_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,10 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
}

_update_script_names();
trigger_live_script_reload();
Ref<Script> scr = p_res;
if (scr.is_valid()) {
trigger_live_script_reload(scr->get_path());
}
}

void ScriptEditor::_scene_saved_callback(const String &p_path) {
Expand Down Expand Up @@ -1029,16 +1032,33 @@ void ScriptEditor::_scene_saved_callback(const String &p_path) {
}
}

void ScriptEditor::trigger_live_script_reload() {
void ScriptEditor::trigger_live_script_reload(const String &p_script_path) {
if (!script_paths_to_reload.has(p_script_path)) {
script_paths_to_reload.append(p_script_path);
}
if (!pending_auto_reload && auto_reload_running_scripts) {
call_deferred(SNAME("_live_auto_reload_running_scripts"));
pending_auto_reload = true;
}
}

void ScriptEditor::trigger_live_script_reload_all() {
if (!pending_auto_reload && auto_reload_running_scripts) {
call_deferred(SNAME("_live_auto_reload_running_scripts"));
pending_auto_reload = true;
reload_all_scripts = true;
}
}

void ScriptEditor::_live_auto_reload_running_scripts() {
pending_auto_reload = false;
EditorDebuggerNode::get_singleton()->reload_scripts();
if (reload_all_scripts) {
EditorDebuggerNode::get_singleton()->reload_all_scripts();
} else {
EditorDebuggerNode::get_singleton()->reload_scripts(script_paths_to_reload);
}
reload_all_scripts = false;
script_paths_to_reload.clear();
}

bool ScriptEditor::_test_script_times_on_disk(Ref<Resource> p_for_script) {
Expand Down
5 changes: 4 additions & 1 deletion editor/plugins/script_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ class ScriptEditor : public PanelContainer {

bool pending_auto_reload;
bool auto_reload_running_scripts;
bool reload_all_scripts = false;
Vector<String> script_paths_to_reload;
void _live_auto_reload_running_scripts();

void _update_selected_editor_menu();
Expand Down Expand Up @@ -542,7 +544,8 @@ class ScriptEditor : public PanelContainer {
void clear_docs_from_script(const Ref<Script> &p_script);
void update_docs_from_script(const Ref<Script> &p_script);

void trigger_live_script_reload();
void trigger_live_script_reload(const String &p_script_path);
void trigger_live_script_reload_all();

bool can_take_away_focus() const;

Expand Down
2 changes: 1 addition & 1 deletion editor/plugins/script_text_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,7 @@ void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_fo
scr->set_last_modified_time(rel_scr->get_last_modified_time());
scr->update_exports();

trigger_live_script_reload();
trigger_live_script_reload(scr->get_path());
}
}
}
Expand Down
29 changes: 14 additions & 15 deletions modules/gdscript/gdscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2373,14 +2373,13 @@ struct GDScriptDepSort {
void GDScriptLanguage::reload_all_scripts() {
#ifdef DEBUG_ENABLED
print_verbose("GDScript: Reloading all scripts");
List<Ref<GDScript>> scripts;
Array scripts;
{
MutexLock lock(this->mutex);

SelfList<GDScript> *elem = script_list.first();
while (elem) {
// Scripts will reload all subclasses, so only reload root scripts.
if (elem->self()->is_root_script() && elem->self()->get_path().is_resource_file()) {
if (elem->self()->get_path().is_resource_file()) {
print_verbose("GDScript: Found: " + elem->self()->get_path());
scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
}
Expand All @@ -2401,19 +2400,11 @@ void GDScriptLanguage::reload_all_scripts() {
#endif
}

//as scripts are going to be reloaded, must proceed without locking here

scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order

for (Ref<GDScript> &scr : scripts) {
print_verbose("GDScript: Reloading: " + scr->get_path());
scr->load_source_code(scr->get_path());
scr->reload(true);
}
reload_scripts(scripts, true);
#endif
}

void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload) {
#ifdef DEBUG_ENABLED

List<Ref<GDScript>> scripts;
Expand All @@ -2439,7 +2430,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order

for (Ref<GDScript> &scr : scripts) {
bool reload = scr == p_script || to_reload.has(scr->get_base());
bool reload = p_scripts.has(scr) || to_reload.has(scr->get_base());

if (!reload) {
continue;
Expand All @@ -2462,7 +2453,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
}
}

//same thing for placeholders
//same thing for placeholders
#ifdef TOOLS_ENABLED

while (scr->placeholders.size()) {
Expand Down Expand Up @@ -2490,6 +2481,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so

for (KeyValue<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
Ref<GDScript> scr = E.key;
print_verbose("GDScript: Reloading: " + scr->get_path());
scr->load_source_code(scr->get_path());
scr->reload(p_soft_reload);

//restore state if saved
Expand Down Expand Up @@ -2537,6 +2530,12 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
#endif
}

void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
Array scripts;
scripts.push_back(p_script);
reload_scripts(scripts, p_soft_reload);
}

void GDScriptLanguage::frame() {
calls = 0;

Expand Down
1 change: 1 addition & 0 deletions modules/gdscript/gdscript.h
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ class GDScriptLanguage : public ScriptLanguage {
virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) override;

virtual void reload_all_scripts() override;
virtual void reload_scripts(const Array &p_scripts, bool p_soft_reload) override;
virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;

virtual void frame() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ void GDScriptTextDocument::didSave(const Variant &p_param) {
scr->update_exports();
ScriptEditor::get_singleton()->reload_scripts(true);
ScriptEditor::get_singleton()->update_docs_from_script(scr);
ScriptEditor::get_singleton()->trigger_live_script_reload();
ScriptEditor::get_singleton()->trigger_live_script_reload(scr->get_path());
}
}

Expand Down
23 changes: 20 additions & 3 deletions modules/mono/csharp_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,11 +720,22 @@ void CSharpLanguage::reload_all_scripts() {
#endif
}

void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
(void)p_script; // UNUSED

void CSharpLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload) {
CRASH_COND(!Engine::get_singleton()->is_editor_hint());

bool has_csharp_script = false;
for (int i = 0; i < p_scripts.size(); ++i) {
Ref<CSharpScript> cs_script = p_scripts[i];
if (cs_script.is_valid()) {
has_csharp_script = true;
break;
}
}

if (!has_csharp_script) {
return;
}

#ifdef TOOLS_ENABLED
get_godotsharp_editor()->get_node(NodePath("HotReloadAssemblyWatcher"))->call("RestartTimer");
#endif
Expand All @@ -736,6 +747,12 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
#endif
}

void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
Array scripts;
scripts.push_back(p_script);
reload_scripts(scripts, p_soft_reload);
}

#ifdef GD_MONO_HOT_RELOAD
bool CSharpLanguage::is_assembly_reloading_needed() {
ERR_FAIL_NULL_V(gdmono, false);
Expand Down
1 change: 1 addition & 0 deletions modules/mono/csharp_script.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ class CSharpLanguage : public ScriptLanguage {
/* TODO? */ void get_public_annotations(List<MethodInfo> *p_annotations) const override {}

void reload_all_scripts() override;
void reload_scripts(const Array &p_scripts, bool p_soft_reload) override;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
void reload_scripts(const Array &p_scripts, bool p_soft_reload) override;
virtual void reload_scripts(const Array &p_scripts, bool p_soft_reload) override;

This isn't done consistently here but should be done here when adding new entries IMO, it's done on some methods already above

Copy link
Contributor

Choose a reason for hiding this comment

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

This would only matter if someone was to extend CSharpLanguage, which is highly unlikely. So I'd say this is a non-blocking issue.

Copy link
Member

@AThousandShips AThousandShips Jan 17, 2024

Choose a reason for hiding this comment

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

No I mean that we always use virtual and override as a matter of style regularity, unless it's marked final it doesn't matter for that sense

void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;

/* LOADER FUNCTIONS */
Expand Down
4 changes: 2 additions & 2 deletions modules/mono/editor/editor_internal_calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) {
}

void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
EditorDebuggerNode::get_singleton()->reload_scripts();
EditorDebuggerNode::get_singleton()->reload_all_scripts();
}

bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) {
Expand All @@ -175,7 +175,7 @@ void godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(Control *p_contr
void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
if (ed) {
ed->reload_scripts();
ed->reload_all_scripts();
}
}

Expand Down
Loading