Skip to content

Commit 8f1d481

Browse files
committed
Merge pull request godotengine#104335 from kleonc/scene_change_fix_freeing_freed_prev_scene
Don't free already freed scenes when changing `SceneTree` current scene
2 parents c94d783 + f8d13c8 commit 8f1d481

File tree

2 files changed

+48
-24
lines changed

2 files changed

+48
-24
lines changed

scene/main/scene_tree.cpp

+46-22
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ bool SceneTree::process(double p_time) {
586586
MessageQueue::get_singleton()->flush(); //small little hack
587587
flush_transform_notifications(); //transforms after world update, to avoid unnecessary enter/exit notifications
588588

589-
if (unlikely(pending_new_scene)) {
589+
if (unlikely(pending_new_scene_id.is_valid())) {
590590
_flush_scene_change();
591591
}
592592

@@ -1508,17 +1508,33 @@ Node *SceneTree::get_current_scene() const {
15081508
}
15091509

15101510
void SceneTree::_flush_scene_change() {
1511-
if (prev_scene) {
1512-
memdelete(prev_scene);
1513-
prev_scene = nullptr;
1511+
if (prev_scene_id.is_valid()) {
1512+
// Might have already been freed externally.
1513+
Node *prev_scene = Object::cast_to<Node>(ObjectDB::get_instance(prev_scene_id));
1514+
if (prev_scene) {
1515+
memdelete(prev_scene);
1516+
}
1517+
prev_scene_id = ObjectID();
15141518
}
1515-
current_scene = pending_new_scene;
1516-
root->add_child(pending_new_scene);
1517-
pending_new_scene = nullptr;
1518-
// Update display for cursor instantly.
1519-
root->update_mouse_cursor_state();
15201519

1521-
emit_signal(SNAME("scene_changed"));
1520+
DEV_ASSERT(pending_new_scene_id.is_valid());
1521+
Node *pending_new_scene = Object::cast_to<Node>(ObjectDB::get_instance(pending_new_scene_id));
1522+
if (pending_new_scene) {
1523+
// Ensure correct state before `add_child` (might enqueue subsequent scene change).
1524+
current_scene = pending_new_scene;
1525+
pending_new_scene_id = ObjectID();
1526+
1527+
root->add_child(pending_new_scene);
1528+
// Update display for cursor instantly.
1529+
root->update_mouse_cursor_state();
1530+
1531+
// Only on successful scene change.
1532+
emit_signal(SNAME("scene_changed"));
1533+
} else {
1534+
current_scene = nullptr;
1535+
pending_new_scene_id = ObjectID();
1536+
ERR_PRINT("Scene instance has been freed before becoming the current scene. No current scene is set.");
1537+
}
15221538
}
15231539

15241540
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) {
15381554
ERR_FAIL_NULL_V(new_scene, ERR_CANT_CREATE);
15391555

15401556
// If called again while a change is pending.
1541-
if (pending_new_scene) {
1542-
queue_delete(pending_new_scene);
1543-
pending_new_scene = nullptr;
1557+
if (pending_new_scene_id.is_valid()) {
1558+
Node *pending_new_scene = Object::cast_to<Node>(ObjectDB::get_instance(pending_new_scene_id));
1559+
if (pending_new_scene) {
1560+
queue_delete(pending_new_scene);
1561+
}
1562+
pending_new_scene_id = ObjectID();
15441563
}
15451564

1546-
prev_scene = current_scene;
1547-
15481565
if (current_scene) {
1566+
prev_scene_id = current_scene->get_instance_id();
15491567
// Let as many side effects as possible happen or be queued now,
15501568
// so they are run before the scene is actually deleted.
15511569
root->remove_child(current_scene);
15521570
}
15531571
DEV_ASSERT(!current_scene);
15541572

1555-
pending_new_scene = new_scene;
1573+
pending_new_scene_id = new_scene->get_instance_id();
15561574
return OK;
15571575
}
15581576

@@ -2009,13 +2027,19 @@ SceneTree::SceneTree() {
20092027
}
20102028

20112029
SceneTree::~SceneTree() {
2012-
if (prev_scene) {
2013-
memdelete(prev_scene);
2014-
prev_scene = nullptr;
2030+
if (prev_scene_id.is_valid()) {
2031+
Node *prev_scene = Object::cast_to<Node>(ObjectDB::get_instance(prev_scene_id));
2032+
if (prev_scene) {
2033+
memdelete(prev_scene);
2034+
}
2035+
prev_scene_id = ObjectID();
20152036
}
2016-
if (pending_new_scene) {
2017-
memdelete(pending_new_scene);
2018-
pending_new_scene = nullptr;
2037+
if (pending_new_scene_id.is_valid()) {
2038+
Node *pending_new_scene = Object::cast_to<Node>(ObjectDB::get_instance(pending_new_scene_id));
2039+
if (pending_new_scene) {
2040+
memdelete(pending_new_scene);
2041+
}
2042+
pending_new_scene_id = ObjectID();
20192043
}
20202044
if (root) {
20212045
root->_set_tree(nullptr);

scene/main/scene_tree.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ class SceneTree : public MainLoop {
187187
TypedArray<Node> _get_nodes_in_group(const StringName &p_group);
188188

189189
Node *current_scene = nullptr;
190-
Node *prev_scene = nullptr;
191-
Node *pending_new_scene = nullptr;
190+
ObjectID prev_scene_id;
191+
ObjectID pending_new_scene_id;
192192

193193
Color debug_collisions_color;
194194
Color debug_collision_contact_color;

0 commit comments

Comments
 (0)