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

Document the exact order of all lifetime signals/callbacks/notifications #5492

Open
KoBeWi opened this issue Jan 2, 2022 · 10 comments
Open
Labels
area:manual Issues and PRs related to the Manual/Tutorials section of the documentation content:new page Issues and PRs related to creation of new documentation pages for new or undocumented features enhancement

Comments

@KoBeWi
Copy link
Member

KoBeWi commented Jan 2, 2022

Your Godot version:
3.4.1

Issue description:
From time to time, I keep seeing people confused about order of lifetime methods. Recently there was a bug report, where it turned out that the user tried to access an onready variable inside a callback from signal emitted in _ready() of a child node. It's convoluted, but the onready variable wasn't initialized in parent, because child nodes will call their _ready() first.

We do have some docs about order of some methods, but there isn't any comprehensive guide that lists all callbacks/notifications etc and order of variable initialization.

I made a small test project. For a single node the order is this:

  • variables are initialized
  • _init() callback
  • exported variables are initialized
  • NOTIFICATION_ENTER_TREE
  • _enter_tree() callback
  • tree_entered signal
  • NOTIFICATION_POST_ENTER_TREE
  • onready variables are initialized
  • _ready() callback
  • NOTIFICATION_READY
  • ready signal

It gets super-complicated when node has children. My test scene was like this:
image
Tested with this script, that prints about everything for when node is created and destroyed:

extends Node

func _init() -> void:
	prints(self, "_init() callback")
	
	connect("tree_entered", self, "on_tree_entered")
	connect("ready", self, "on_ready")
	connect("tree_exiting", self, "on_tree_exiting")
	connect("tree_exited", self, "on_tree_exited")

func _enter_tree() -> void:
	prints(name, "_enter_tree() callback")

func _ready() -> void:
	prints(name, "_ready() callback")
	
	if get_parent() == get_tree().root:
		queue_free()

func _exit_tree() -> void:
	prints(name, "_exit_tree() callback")

func _notification(what: int) -> void:
	match what:
		NOTIFICATION_ENTER_TREE:
			prints(name, "NOTIFICATION_ENTER_TREE")
		NOTIFICATION_EXIT_TREE:
			prints(name, "NOTIFICATION_EXIT_TREE")
		NOTIFICATION_READY:
			prints(name, "NOTIFICATION_READY")
		NOTIFICATION_PREDELETE:
			prints(name, "NOTIFICATION_PREDELETE")
		NOTIFICATION_POSTINITIALIZE:
			prints(name, "NOTIFICATION_POSTINITIALIZE")
		NOTIFICATION_POST_ENTER_TREE:
			prints(name, "NOTIFICATION_POST_ENTER_TREE")

func on_tree_entered():
	prints(name, "tree_entered signal")

func on_ready():
	prints(name, "ready signal")

func on_tree_exiting():
	prints(name, "tree_exiting signal")

func on_tree_exited():
	prints(name, "tree_exited signal")

And the result:

--- entering

[Node:1254] _init() callback
[Spatial:1255] _init() callback
[Control:1256] _init() callback
[Node2D:1257] _init() callback
[CanvasLayer:1258] _init() callback
[HTTPRequest:1259] _init() callback
[ResourcePreloader:1264] _init() callback
A-- NOTIFICATION_ENTER_TREE
A-- _enter_tree() callback
A-- tree_entered signal
AA- NOTIFICATION_ENTER_TREE
AA- _enter_tree() callback
AA- tree_entered signal
AAA NOTIFICATION_ENTER_TREE
AAA _enter_tree() callback
AAA tree_entered signal
AAB NOTIFICATION_ENTER_TREE
AAB _enter_tree() callback
AAB tree_entered signal
AB- NOTIFICATION_ENTER_TREE
AB- _enter_tree() callback
AB- tree_entered signal
ABA NOTIFICATION_ENTER_TREE
ABA _enter_tree() callback
ABA tree_entered signal
ABB NOTIFICATION_ENTER_TREE
ABB _enter_tree() callback
ABB tree_entered signal
AAA NOTIFICATION_POST_ENTER_TREE
AAA _ready() callback
AAA NOTIFICATION_READY
AAA ready signal
AAB NOTIFICATION_POST_ENTER_TREE
AAB _ready() callback
AAB NOTIFICATION_READY
AAB ready signal
AA- NOTIFICATION_POST_ENTER_TREE
AA- _ready() callback
AA- NOTIFICATION_READY
AA- ready signal
ABA NOTIFICATION_POST_ENTER_TREE
ABA _ready() callback
ABA NOTIFICATION_READY
ABA ready signal
ABB NOTIFICATION_POST_ENTER_TREE
ABB _ready() callback
ABB NOTIFICATION_READY
ABB ready signal
AB- NOTIFICATION_POST_ENTER_TREE
AB- _ready() callback
AB- NOTIFICATION_READY
AB- ready signal
A-- NOTIFICATION_POST_ENTER_TREE
A-- _ready() callback
A-- NOTIFICATION_READY
A-- ready signal

--- exiting

ABB _exit_tree() callback
ABB tree_exiting signal
ABB NOTIFICATION_EXIT_TREE
ABA _exit_tree() callback
ABA tree_exiting signal
ABA NOTIFICATION_EXIT_TREE
AB- _exit_tree() callback
AB- tree_exiting signal
AB- NOTIFICATION_EXIT_TREE
AAB _exit_tree() callback
AAB tree_exiting signal
AAB NOTIFICATION_EXIT_TREE
AAA _exit_tree() callback
AAA tree_exiting signal
AAA NOTIFICATION_EXIT_TREE
AA- _exit_tree() callback
AA- tree_exiting signal
AA- NOTIFICATION_EXIT_TREE
A-- _exit_tree() callback
A-- tree_exiting signal
A-- NOTIFICATION_EXIT_TREE
AAA tree_exited signal
AAB tree_exited signal
AA- tree_exited signal
ABA tree_exited signal
ABB tree_exited signal
AB- tree_exited signal
A-- tree_exited signal
ABB NOTIFICATION_PREDELETE
ABA NOTIFICATION_PREDELETE
AB- NOTIFICATION_PREDELETE
AAB NOTIFICATION_PREDELETE
AAA NOTIFICATION_PREDELETE
AA- NOTIFICATION_PREDELETE
A-- NOTIFICATION_PREDELETE

Interesting/not expected stuff:

  • _init() is called for every node in scene before any other method and before any is added to tree
  • enter_tree is called in the same order as init, i.e. depth-first
  • NOTIFICATION_POST_ENTER_TREE happens right before _ready() callback in any node, but onready variables aren't initialized yet
  • which means that _ready() is the earliest place where you can use onready variables and initialization of onready variables has the same order as _ready() callback
  • ready and enter tree have reverse order of things. For enter tree, the notification comes first, while it's the opposite for ready
  • tree_exiting is emitted first in children, same as NOTIFICATION_PREDELETE, but tree_exited is the opposite; parents emit it first.
  • thus tree_exiting is "equivalent" of ready and tree_exited is "equivalent" of enter_tree, but they are reversed, i.e. when destroying nodes it's as if "ready" came first instead of last

That's lots of information, not sure how much of it is really useful, but it would be nice to have it documented somewhere. Knowing the exact order of things in tree might come in handy for some complicated scene logic and might save some confusion when things don't get called in the order you'd expect.

@Calinou
Copy link
Member

Calinou commented Jan 2, 2022

Related to #3475 (possible duplicate)? Either way, this issue has more information, so I'd vouch for keeping it open.

@skyace65
Copy link
Contributor

skyace65 commented Jan 7, 2022

What would be a good section of the documentation to put this in?

@Zireael07
Copy link

@skyace65: Scripting, core features, new subpage (like "Groups" is one, or "Nodes and scene instancing" is another).

@viksl
Copy link
Contributor

viksl commented Jul 10, 2022

but tree_exited is the opposite; parents emit it first.

From the list children exited first as well as exiting and then their parent, or am I missing something, it's late over here so I might be dumb.

@KoBeWi
Copy link
Member Author

KoBeWi commented Jul 10, 2022

Ah you seem to be right. The order is still different though.

@viksl
Copy link
Contributor

viksl commented Jul 11, 2022

Yeah, it seems _exit_tree(), tree_exiting, notification_exit_tree are "deep bottom up", tree_exited is "deep top to bottom", notification_predelete is "deep bottom up".

tree_exited has the same order as _ready() callback, notification_ready and ready and notification_post_enter_tree.

Well I guess I'd expect all the exit and predelete to follow the same order, it's interesting to see it's not, I have no idea what reason could be behind it. :)

@theraot
Copy link

theraot commented Dec 18, 2022

I did look up into this a while back (I just found this issue), I'll link it here in case it is useful:

@skyace65 skyace65 added content:new page Issues and PRs related to creation of new documentation pages for new or undocumented features area:manual Issues and PRs related to the Manual/Tutorials section of the documentation labels Dec 28, 2022
@ershn
Copy link
Contributor

ershn commented Dec 25, 2023

Executing the same script on 4.2.1 now yields different results.

  • tree_exited is now called on nodes in the same order as exit_tree and tree_exiting
  • the timing of NOTIFICATION_PREDELETE seems to have broken and probably needs a fix

Concerning the inconsistency? between enter_tree and ready when it comes to the notification/callback/signal trigger order, it seems to still be there.

Given the behavior seems to have changed over time, I'm not sure how much should be left out as "implementation details" when creating the documentation.
IMO we should be as specific as possible and try to keep the implementation in sync with the documentation, because some people will invariably end up relying on the order without necessarily being aware of it.

--- entering

<Node#27917288653> _init() callback
<Node3D#27934065870> _init() callback
<Control#27950843087> _init() callback
<Node2D#27984397524> _init() callback
<CanvasLayer#28001174741> _init() callback
<HTTPRequest#28017951958> _init() callback
<ResourcePreloader#28135392477> _init() callback
A-- NOTIFICATION_ENTER_TREE
A-- _enter_tree() callback
A-- tree_entered signal
AA- NOTIFICATION_ENTER_TREE
AA- _enter_tree() callback
AA- tree_entered signal
AAA NOTIFICATION_ENTER_TREE
AAA _enter_tree() callback
AAA tree_entered signal
AAB NOTIFICATION_ENTER_TREE
AAB _enter_tree() callback
AAB tree_entered signal
AB- NOTIFICATION_ENTER_TREE
AB- _enter_tree() callback
AB- tree_entered signal
ABA NOTIFICATION_ENTER_TREE
ABA _enter_tree() callback
ABA tree_entered signal
ABB NOTIFICATION_ENTER_TREE
ABB _enter_tree() callback
ABB tree_entered signal
AAA NOTIFICATION_POST_ENTER_TREE
AAA _ready() callback
AAA NOTIFICATION_READY
AAA ready signal
AAB NOTIFICATION_POST_ENTER_TREE
AAB _ready() callback
AAB NOTIFICATION_READY
AAB ready signal
AA- NOTIFICATION_POST_ENTER_TREE
AA- _ready() callback
AA- NOTIFICATION_READY
AA- ready signal
ABA NOTIFICATION_POST_ENTER_TREE
ABA _ready() callback
ABA NOTIFICATION_READY
ABA ready signal
ABB NOTIFICATION_POST_ENTER_TREE
ABB _ready() callback
ABB NOTIFICATION_READY
ABB ready signal
AB- NOTIFICATION_POST_ENTER_TREE
AB- _ready() callback
AB- NOTIFICATION_READY
AB- ready signal
A-- NOTIFICATION_POST_ENTER_TREE
A-- _ready() callback
A-- NOTIFICATION_READY
A-- ready signal

--- exiting

A-- NOTIFICATION_PREDELETE
ABB _exit_tree() callback
ABB tree_exiting signal
ABB NOTIFICATION_EXIT_TREE
ABA _exit_tree() callback
ABA tree_exiting signal
ABA NOTIFICATION_EXIT_TREE
AB- _exit_tree() callback
AB- tree_exiting signal
AB- NOTIFICATION_EXIT_TREE
AAB _exit_tree() callback
AAB tree_exiting signal
AAB NOTIFICATION_EXIT_TREE
AAA _exit_tree() callback
AAA tree_exiting signal
AAA NOTIFICATION_EXIT_TREE
AA- _exit_tree() callback
AA- tree_exiting signal
AA- NOTIFICATION_EXIT_TREE
A-- _exit_tree() callback
A-- tree_exiting signal
A-- NOTIFICATION_EXIT_TREE
ABB tree_exited signal
ABA tree_exited signal
AB- tree_exited signal
AAB tree_exited signal
AAA tree_exited signal
AA- tree_exited signal
A-- tree_exited signal
AB- NOTIFICATION_PREDELETE
ABB NOTIFICATION_PREDELETE
ABA NOTIFICATION_PREDELETE
AA- NOTIFICATION_PREDELETE
AAB NOTIFICATION_PREDELETE
AAA NOTIFICATION_PREDELETE

@KoBeWi
Copy link
Member Author

KoBeWi commented Dec 25, 2023

Here's the test scene with up-to-date script:
OrderOfNodes.tscn.txt
Indeed the predelete thing looks like a bug. Other changes might've been caused by Godot 3 to Godot 4 transition.

@allenwp
Copy link
Contributor

allenwp commented Apr 16, 2024

Neat. One of the first things I did when getting to know the engine was make a test protect something like this one. Mine, and my takeaways, are here: https://github.com/allenwp/godot-script-execution-order

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:manual Issues and PRs related to the Manual/Tutorials section of the documentation content:new page Issues and PRs related to creation of new documentation pages for new or undocumented features enhancement
Projects
None yet
Development

No branches or pull requests

8 participants