Mass Signal vs. Observer processor vs. OnWorldBeginPlay

Hello,

I have a following setup in Mass:

A signal processor listening to a signal

An observer processor, listening to entity creation, and firing the above signal

A subsystem that spawns some entities in OnWorldBeginPlay

What happens is that observer processor detects the entity being created, fires the signal, however the signal processor doesn’t exist at the time and the signal is ignored. The signal processor is created in FMassProcessingPhaseManager::OnPhaseStart.

I have tried waiting for MassSimulation start - however that doesn’t help, OnPhaseStart is called much later.

Directly registering the processor with ProcessingPhaseManager doesn’t work either, as initialization of these processors is deferred to the same OnPhaseStart.

What is the correct solution here? I could duplicate the same operations from the signal processor to the observer (effectively having two different processors do the same operation, just under different circumstances), is that an intended way to use these two processors?

[Attachment Removed]

I have found a possible solution:

I have a subsystem that has MassSimulation as dependency,. In its Initialize() it creates all the signal processors and calls CallInitialize() on them at that moment, which registers them to Signal Subsystem. So far it seems that it works.

[Attachment Removed]

Hi Martin,

Thanks for the updated information.

This is indeed a Mass lifecycle edge case, and your diagnosis is correct. The issue comes from a timing mismatch between structural events and phase-bound processors. Observer processors are structural and exist before phases begin, while signal processors are phase-driven and only exist once the simulation phases start. As a result, if a subsystem spawns Mass entities in OnWorldBeginPlay and an observer sends a signal at that time, the signal being dropped could be expected.

It appears you’ve found a workaround, and if it meets your needs, that is perfectly fine. It is up to you to make a signal processor with observer-timing, or have an observer processor that could receive signals, or have two processors for the same timing with different purposes. However, since there is limited context regarding the motivation behind this setup, I’d like to suggest a few Epic-style approaches for handling this scenario more robustly.

Rather than relying on signals from observers, it helps to first consider:

What the signal semantically represents (e.g., initialization, registration, or gameplay logic).

Which phase of the Mass simulation is intended to run in?

Since Mass is a data-oriented framework, the most “Mass-native” solution in this case would be to use a tag-based approach—for example, adding an FNeedsInitTag and handling it with a processor. This follows typical data-oriented design by separating structural setup from runtime behavior and handing off work via data rather than events.

Signals are best reserved for one-shot, runtime gameplay events. They are not intended to be a general notification mechanism and should not be relied upon for structural or initialization logic.

Additional options you may consider include:

Delaying entity spawning until the Mass simulation has started. If spawning in OnWorldBeginPlay is not strictly required, entities could be queued and spawned after the first phase begins.

Manually buffering signals by storing entities in a subsystem and flushing those signals once phases are active. This is rare, but it may be good for certain requirements.

If you can provide more details about the specific use case and the intent behind this flow, a clearer understanding of the use case would assist us in evaluating the best approach.

Best regards,

Henry Liu

[Attachment Removed]

I have entities which perform a loop of processes - finding target location, pathfinding, movement, waiting at location (very rough simulation of NPCs at long distance). I am using MassRepresentation, so anywhere in this loop they can become hydrated, which pauses the loop and hands over control to the high-resolution actor. Upon dehydration, the loop resumes. I handle most transitions with signals, as many of the phases are one-shot changes followed by some wait (with the exception of movement, which does update every frame - for that I use a Tag).

As a result, when the entity is spawned, no processors run on it, because it has not started any phase of the loop. In my current implementation, I fire a signal that starts the FindingTarget phase. I use signal there, because the phase also starts at the end of WaitingAtLocation, which is handled by signalling the entity from one waiting queue.

I have consired delaying spawning of entities, but ultimately decided against that, as we have multiple different sources of entities, which would all need to be handled differently, and also we are trying to develop some save/load system, which is already complicated, and I don’t want to add further limitations by spawning the entities later - they will be needed asap to load data into them (this is just speculation on my part, but I want to avoid adding technical limitations where we don’t even have a initial solution yet).

[Attachment Removed]

Thank you for the detailed information.

What you’re describing is a valid and fairly common pattern in Mass, especially for NPC simulation. Using signals to drive phase transitions is absolutely reasonable.

From an architectural standpoint, the main missing piece is how the loop is entered the first time. If “not yet started” is treated as an explicit state, such as an Idle or NeedsInitialization tag/fragment, a processor can consume that state once and transition the entity into FindingTarget or WaitingAtLocation. This avoids introducing a special-case signal just for spawning, while keeping the system fully data-driven.

You can also use tags to explicitly mark entities as hydrated or dehydrated, allowing certain processors to run or be skipped, based on that state. This aligns well with Mass data-oriented approach.

Delaying entity spawning isn’t necessary, and your reasoning for avoiding it is sound. AMassSpawner/UMassAgentComponent could be good references for handling different entity sources. And there are no inherent limitations to delayed spawning entities. It works well for batch generation.

Hope this helps clarify things. Let me know if you have further questions.

[Attachment Removed]