1
0
mirror of https://github.com/godotengine/godot.git synced 2025-12-05 17:15:09 +00:00

Don't free already freed scenes when changing SceneTree current scene

This commit is contained in:
kleonc
2025-03-18 22:31:37 +01:00
parent 28102e6682
commit f8d13c8a46
2 changed files with 48 additions and 24 deletions

View File

@@ -586,7 +586,7 @@ bool SceneTree::process(double p_time) {
MessageQueue::get_singleton()->flush(); //small little hack MessageQueue::get_singleton()->flush(); //small little hack
flush_transform_notifications(); //transforms after world update, to avoid unnecessary enter/exit notifications flush_transform_notifications(); //transforms after world update, to avoid unnecessary enter/exit notifications
if (unlikely(pending_new_scene)) { if (unlikely(pending_new_scene_id.is_valid())) {
_flush_scene_change(); _flush_scene_change();
} }
@@ -1508,17 +1508,33 @@ Node *SceneTree::get_current_scene() const {
} }
void SceneTree::_flush_scene_change() { void SceneTree::_flush_scene_change() {
if (prev_scene_id.is_valid()) {
// Might have already been freed externally.
Node *prev_scene = Object::cast_to<Node>(ObjectDB::get_instance(prev_scene_id));
if (prev_scene) { if (prev_scene) {
memdelete(prev_scene); memdelete(prev_scene);
prev_scene = nullptr;
} }
prev_scene_id = ObjectID();
}
DEV_ASSERT(pending_new_scene_id.is_valid());
Node *pending_new_scene = Object::cast_to<Node>(ObjectDB::get_instance(pending_new_scene_id));
if (pending_new_scene) {
// Ensure correct state before `add_child` (might enqueue subsequent scene change).
current_scene = pending_new_scene; current_scene = pending_new_scene;
pending_new_scene_id = ObjectID();
root->add_child(pending_new_scene); root->add_child(pending_new_scene);
pending_new_scene = nullptr;
// Update display for cursor instantly. // Update display for cursor instantly.
root->update_mouse_cursor_state(); root->update_mouse_cursor_state();
// Only on successful scene change.
emit_signal(SNAME("scene_changed")); emit_signal(SNAME("scene_changed"));
} else {
current_scene = nullptr;
pending_new_scene_id = ObjectID();
ERR_PRINT("Scene instance has been freed before becoming the current scene. No current scene is set.");
}
} }
Error SceneTree::change_scene_to_file(const String &p_path) { Error SceneTree::change_scene_to_file(const String &p_path) {
@@ -1538,21 +1554,23 @@ Error SceneTree::change_scene_to_packed(const Ref<PackedScene> &p_scene) {
ERR_FAIL_NULL_V(new_scene, ERR_CANT_CREATE); ERR_FAIL_NULL_V(new_scene, ERR_CANT_CREATE);
// If called again while a change is pending. // If called again while a change is pending.
if (pending_new_scene_id.is_valid()) {
Node *pending_new_scene = Object::cast_to<Node>(ObjectDB::get_instance(pending_new_scene_id));
if (pending_new_scene) { if (pending_new_scene) {
queue_delete(pending_new_scene); queue_delete(pending_new_scene);
pending_new_scene = nullptr; }
pending_new_scene_id = ObjectID();
} }
prev_scene = current_scene;
if (current_scene) { if (current_scene) {
prev_scene_id = current_scene->get_instance_id();
// Let as many side effects as possible happen or be queued now, // Let as many side effects as possible happen or be queued now,
// so they are run before the scene is actually deleted. // so they are run before the scene is actually deleted.
root->remove_child(current_scene); root->remove_child(current_scene);
} }
DEV_ASSERT(!current_scene); DEV_ASSERT(!current_scene);
pending_new_scene = new_scene; pending_new_scene_id = new_scene->get_instance_id();
return OK; return OK;
} }
@@ -2009,13 +2027,19 @@ SceneTree::SceneTree() {
} }
SceneTree::~SceneTree() { SceneTree::~SceneTree() {
if (prev_scene_id.is_valid()) {
Node *prev_scene = Object::cast_to<Node>(ObjectDB::get_instance(prev_scene_id));
if (prev_scene) { if (prev_scene) {
memdelete(prev_scene); memdelete(prev_scene);
prev_scene = nullptr;
} }
prev_scene_id = ObjectID();
}
if (pending_new_scene_id.is_valid()) {
Node *pending_new_scene = Object::cast_to<Node>(ObjectDB::get_instance(pending_new_scene_id));
if (pending_new_scene) { if (pending_new_scene) {
memdelete(pending_new_scene); memdelete(pending_new_scene);
pending_new_scene = nullptr; }
pending_new_scene_id = ObjectID();
} }
if (root) { if (root) {
root->_set_tree(nullptr); root->_set_tree(nullptr);

View File

@@ -187,8 +187,8 @@ private:
TypedArray<Node> _get_nodes_in_group(const StringName &p_group); TypedArray<Node> _get_nodes_in_group(const StringName &p_group);
Node *current_scene = nullptr; Node *current_scene = nullptr;
Node *prev_scene = nullptr; ObjectID prev_scene_id;
Node *pending_new_scene = nullptr; ObjectID pending_new_scene_id;
Color debug_collisions_color; Color debug_collisions_color;
Color debug_collision_contact_color; Color debug_collision_contact_color;