How exactly are signals processed?

Godot Version

4.2.2

Question

How exactly are signals processed? Are they processed immediately as they are emitted, or are they processed on the next tick?

Reading through the forum, I’m getting the feeling that they are processed immediately, which is something I don’t quite like for my particular use case, so I’m debating if I should roll out my own signal system or use Godot’s.

I like the FRP approach, where signals are emitted at the “end of a tick” and applied at the “beginning of the next tick”. Its much clearer what’s happening and when, especially if there’s a feedback loop between e.g. two objects that are listening to each other’s signals.

While I don’t know for sure, I imagine they do get processed immediately, but that said, as far as I can tell after reading the docs, there is an option to have the signal be processed with the “deferred” flag, which might be more what you’re looking for. Unfortunately I’m not familiar enough with GDScript, but I hope this helps!

Signals are processed immediately by default, but you can also process them in idle time by using the appropriate flag.

Signal.connect(some_callable, CONNECT_DEFERRED)
1 Like

Signals are processed immediately. They are processed in the order of the event processing, so higher in the tree nodes first, then lower ones. However there are some complexities around this, where the engine deals with process events compared to where the signal is emitted, but generally, yes, callbacks are immediately called when a signal is emitted.

You can delay it. With a flag or a timer for instance.

var delay_signal = false

func _on_signal_emitted():
    delay_signal = true

func _process(delta):
    if delay_signal:
        # Process the signal here
        delay_signal = false
        # Your code to handle the signal

Or something like this (I have not tested these btw so consider these pseudo code):

var timer = null

func _ready():
    timer = Timer.new()
    add_child(timer)
    timer.set_one_shot(true)

func _on_signal_emitted():
    timer.start(0) 

func _on_Timer_timeout():
    # Process the signal here

I also forgot call_deferred (grulps mentioned it previously though) which will emit the signal during the next idle time. I think usually at the end of the current frame.

	call_deferred("emit_signal", "my_signal")

Yeah. I’m aware of deferred, but that’s not really what I’m looking for a consistent, well defined and deterministic signal processing, so I guess I’ll implement that myself.

In my multiplayer game I need to keep my clients time in sync with the server, and I want signals that fired on the server side at specific “in-game time” to also fire on the client side at the exact “in-game” time.

I don’t think I can achieve those guarantees with Godot’s signals it would seem, especially since I have some feedback loops between various objects.

1 Like

Wow, that sounds very complex. Sorry if my previous answer was a bit basic. That is something I would simply not be able to do. I am really looking forward to doing a multiplayer but at the mo I would not even know where to start. I will now slowly swim back to the shallow end of the pool with the other amateurs :slight_smile:

Well, I’m a game dev amateur as well. My day job is a different story though :slight_smile:

1 Like

Try looking at Time.get_ticks_msec(), you might have an easier time synchronizing based on this output.

What are you making that requires perfect synchronization of multiplayer clients? FRP does not guarentee this, it’s closer to CONNECT_DEFERRED as others have brought up, which isn’t a time/synchronization tool.

I’m already using get_ticks_msec. Every object and move command is expressed relative to in-game time. Now I want to do the same for signals and express them as “timed” items.

I haven’t said that FRP guarantees perfect synchronization. It has well defined signal behavior, and it can’t happen that e.g. in a single “tick” I get 3 backs and forths between two objects via signals.

Such behavior makes it hard to express signals as a function of time (and user inputs, as well as the game state :).

Ideally, Godot signals would be tied to the _process step or something, and as the _process step finishes, all of the signal messages are collected, then, on the beginning of the next _process step, all of the messages sent on the previous step are received by the subscribers, all new messages are collected and the cycle repeats.