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

Detect external modification of scenes #31747

Merged
merged 2 commits into from
Feb 10, 2021
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
7 changes: 6 additions & 1 deletion core/config/project_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ Error ProjectSettings::_load_settings_text(const String &p_path) {
// If we're loading a project.godot from source code, we can operate some
// ProjectSettings conversions if need be.
_convert_to_last_version(config_version);
last_save_time = FileAccess::get_modified_time(get_resource_path().plus_file("project.godot"));
return OK;
} else if (err != OK) {
ERR_PRINT("Error parsing " + p_path + " at line " + itos(lines) + ": " + error_text + " File might be corrupted.");
Expand Down Expand Up @@ -676,7 +677,11 @@ void ProjectSettings::clear(const String &p_name) {
}

Error ProjectSettings::save() {
return save_custom(get_resource_path().plus_file("project.godot"));
Error error = save_custom(get_resource_path().plus_file("project.godot"));
if (error == OK) {
last_save_time = FileAccess::get_modified_time(get_resource_path().plus_file("project.godot"));
}
return error;
}

Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<String, List<String>> &props, const CustomMap &p_custom, const String &p_custom_features) {
Expand Down
4 changes: 3 additions & 1 deletion core/config/project_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class ProjectSettings : public Object {

int last_order = NO_BUILTIN_ORDER_BASE;
int last_builtin_order = 0;
uint64_t last_save_time = 0;

Map<StringName, VariantContainer> props;
String resource_path;
Map<StringName, PropertyInfo> custom_prop_info;
Expand Down Expand Up @@ -113,7 +115,6 @@ class ProjectSettings : public Object {

Error _setup(const String &p_path, const String &p_main_pack, bool p_upwards = false);

protected:
akien-mga marked this conversation as resolved.
Show resolved Hide resolved
static void _bind_methods();

public:
Expand Down Expand Up @@ -150,6 +151,7 @@ class ProjectSettings : public Object {
Error save();
void set_custom_property_info(const String &p_prop, const PropertyInfo &p_info);
const Map<StringName, PropertyInfo> &get_custom_property_info() const;
uint64_t get_last_saved_time() { return last_save_time; }

Vector<String> get_optimizer_presets() const;

Expand Down
20 changes: 20 additions & 0 deletions editor/editor_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ int EditorData::add_edited_scene(int p_at_pos) {
EditedScene es;
es.root = nullptr;
es.path = String();
es.file_modified_time = 0;
es.history_current = -1;
es.version = 0;
es.live_edit_root = NodePath(String("/root"));
Expand Down Expand Up @@ -666,6 +667,10 @@ void EditorData::set_edited_scene_root(Node *p_root) {
p_root->set_filename(edited_scene[current_edited_scene].path);
}
}

if (edited_scene[current_edited_scene].path != "") {
edited_scene.write[current_edited_scene].file_modified_time = FileAccess::get_modified_time(edited_scene[current_edited_scene].path);
}
}

int EditorData::get_edited_scene_count() const {
Expand Down Expand Up @@ -697,6 +702,21 @@ uint64_t EditorData::get_scene_version(int p_idx) const {
return edited_scene[p_idx].version;
}

void EditorData::set_scene_modified_time(int p_idx, uint64_t p_time) {
if (p_idx == -1) {
p_idx = current_edited_scene;
}

ERR_FAIL_INDEX(p_idx, edited_scene.size());

edited_scene.write[p_idx].file_modified_time = p_time;
}

uint64_t EditorData::get_scene_modified_time(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), 0);
return edited_scene[p_idx].file_modified_time;
}

String EditorData::get_scene_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), String());
if (!edited_scene[p_idx].root) {
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class EditorData {
struct EditedScene {
Node *root = nullptr;
String path;
uint64_t file_modified_time = 0;
Dictionary editor_states;
List<Node *> selection;
Vector<EditorHistory::History> history_stored;
Expand Down Expand Up @@ -191,6 +192,8 @@ class EditorData {
Ref<Script> get_scene_root_script(int p_idx) const;
void set_edited_scene_version(uint64_t version, int p_scene_idx = -1);
uint64_t get_scene_version(int p_idx) const;
void set_scene_modified_time(int p_idx, uint64_t p_time);
uint64_t get_scene_modified_time(int p_idx) const;
void clear_edited_scenes();
void set_edited_scene_live_edit_root(const NodePath &p_root);
NodePath get_edited_scene_live_edit_root();
Expand Down
107 changes: 105 additions & 2 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ void EditorNode::_notification(int p_what) {
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec")));

EditorFileSystem::get_singleton()->scan_changes();
_scan_external_changes();
} break;

case NOTIFICATION_APPLICATION_FOCUS_OUT: {
Expand Down Expand Up @@ -880,6 +881,81 @@ void EditorNode::_sources_changed(bool p_exist) {
}
}

void EditorNode::_scan_external_changes() {
disk_changed_list->clear();
TreeItem *r = disk_changed_list->create_item();
disk_changed_list->set_hide_root(true);
bool need_reload = false;

// Check if any edited scene has changed.

for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
if (editor_data.get_scene_path(i) == "") {
continue;
}

uint64_t last_date = editor_data.get_scene_modified_time(i);
uint64_t date = FileAccess::get_modified_time(editor_data.get_scene_path(i));

if (date > last_date) {
TreeItem *ti = disk_changed_list->create_item(r);
ti->set_text(0, editor_data.get_scene_path(i).get_file());
need_reload = true;
}
}

String project_settings_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("project.godot");
if (FileAccess::get_modified_time(project_settings_path) > ProjectSettings::get_singleton()->get_last_saved_time()) {
TreeItem *ti = disk_changed_list->create_item(r);
ti->set_text(0, "project.godot");
need_reload = true;
}

if (need_reload) {
disk_changed->call_deferred("popup_centered_ratio", 0.5);
}
}

void EditorNode::_resave_scenes(String p_str) {
save_all_scenes();
ProjectSettings::get_singleton()->save();
disk_changed->hide();
}

void EditorNode::_reload_modified_scenes() {
int current_idx = editor_data.get_edited_scene();

for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
if (editor_data.get_scene_path(i) == "") {
continue;
}

uint64_t last_date = editor_data.get_scene_modified_time(i);
uint64_t date = FileAccess::get_modified_time(editor_data.get_scene_path(i));

if (date > last_date) {
String filename = editor_data.get_scene_path(i);
editor_data.set_edited_scene(i);
_remove_edited_scene(false);

Error err = load_scene(filename, false, false, true, false, true);
if (err != OK) {
ERR_PRINT(vformat("Failed to load scene: %s", filename));
}
editor_data.move_edited_scene_to_index(i);
}
}

get_undo_redo()->clear_history(false);
set_current_scene(current_idx);
_update_scene_tabs();
disk_changed->hide();
}

void EditorNode::_reload_project_settings() {
ProjectSettings::get_singleton()->setup(ProjectSettings::get_singleton()->get_resource_path(), String(), true);
}

void EditorNode::_vp_resized() {
}

Expand Down Expand Up @@ -1505,6 +1581,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
} else {
editor_data.set_edited_scene_version(0, idx);
}
editor_data.set_scene_modified_time(idx, FileAccess::get_modified_time(p_file));

editor_folding.save_scene_folding(scene, p_file);

Expand Down Expand Up @@ -3322,7 +3399,7 @@ int EditorNode::new_scene() {
return idx;
}

Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_clear_errors, bool p_force_open_imported) {
Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_clear_errors, bool p_force_open_imported, bool p_silent_change_tab) {
if (!is_inside_tree()) {
defer_load_scene = p_scene;
return OK;
Expand Down Expand Up @@ -3362,8 +3439,10 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b

if (!editor_data.get_edited_scene_root() && editor_data.get_edited_scene_count() == 2) {
_remove_edited_scene();
} else {
} else if (!p_silent_change_tab) {
_scene_tab_changed(idx);
} else {
set_current_scene(idx);
}

dependency_errors.clear();
Expand Down Expand Up @@ -6599,6 +6678,30 @@ EditorNode::EditorNode() {
//plugin stuff

add_editor_plugin(memnew(DebuggerEditorPlugin(this, debug_menu)));

disk_changed = memnew(ConfirmationDialog);
{
VBoxContainer *vbc = memnew(VBoxContainer);
disk_changed->add_child(vbc);

Label *dl = memnew(Label);
dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?"));
vbc->add_child(dl);

disk_changed_list = memnew(Tree);
vbc->add_child(disk_changed_list);
disk_changed_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);

disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_modified_scenes));
disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_project_settings));
Comment on lines +6695 to +6696
Copy link
Member

Choose a reason for hiding this comment

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

You're connecting two callables to the same signal, bug?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, this is intended. I made reloading scenes and project settings into separate methods.

disk_changed->get_ok_button()->set_text(TTR("Reload"));

disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_scenes));
}

gui_base->add_child(disk_changed);

add_editor_plugin(memnew(AnimationPlayerEditorPlugin(this)));
add_editor_plugin(memnew(CanvasItemEditorPlugin(this)));
add_editor_plugin(memnew(Node3DEditorPlugin(this)));
Expand Down
9 changes: 8 additions & 1 deletion editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@ class EditorNode : public Node {
Label *version_label;
Button *bottom_panel_raise;

Tree *disk_changed_list;
ConfirmationDialog *disk_changed;

void _bottom_panel_raise_toggled(bool);

EditorInterface *editor_interface;
Expand Down Expand Up @@ -641,6 +644,10 @@ class EditorNode : public Node {
static void _resource_loaded(RES p_resource, const String &p_path);

void _resources_changed(const Vector<String> &p_resources);
void _scan_external_changes();
void _reload_modified_scenes();
void _reload_project_settings();
void _resave_scenes(String p_str);

void _feature_profile_changed();
bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class);
Expand Down Expand Up @@ -741,7 +748,7 @@ class EditorNode : public Node {
void fix_dependencies(const String &p_for_file);
void clear_scene() { _cleanup_scene(); }
int new_scene();
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false);
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false);
Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false);

bool is_scene_open(const String &p_path);
Expand Down