SignalBus Architecture
Review draft. This source page has not completed website editorial and release-specific verification.
Publisher-Subscriber Model
Section titled “Publisher-Subscriber Model”The publisher-subscriber model is a communication pattern where one system announces that something happened and any interested systems may react.
Publisher -> Message Bus -> SubscribersThe publisher does not need to know:
- How many subscribers exist
- Which objects are listening
- Where those objects are located
- What each subscriber does with the message
Subscribers register interest in a message contract and receive it when a publisher sends that message through the bus.
SignalBus follows this model by routing messages through an owned World or Game Instance subsystem. Publishers and subscribers remain independent, and delivery is selected by:
ScopeMessage IDExact payload struct typeExample:
Damage system -> publishes Combat.DamageApplied -> quest system reacts -> scoring system reacts -> audio system reacts -> UI system reactsThe damage system does not store references to those systems or call them directly. Adding or removing a subscriber does not require changing the publisher.
Advantages
Section titled “Advantages”The publisher-subscriber model provides:
- Lower coupling: publishers and subscribers do not directly depend on each other.
- Easier extension: new listeners can be added without modifying existing publishers.
- Cleaner ownership: each system manages its own behavior and subscription lifetime.
- One-to-many delivery: one event can notify several independent systems.
- Better testability: publishers and subscribers can be tested separately.
- Clearer debugging: SignalBus diagnostics and the Broadcast Graph show observed message flow.
- Scope isolation: World and Game Instance buses prevent unrelated contexts from sharing messages.
- C++ and Blueprint interoperability: both use the same runtime routing path.
When to Use SignalBus
Section titled “When to Use SignalBus”SignalBus is a good fit when several systems need to react to the same event but should not hold direct references to one another.
Typical uses include:
- Gameplay events such as damage, death, spawning, objectives, and interactions
- UI reactions to gameplay or session state
- Audio, camera, analytics, tutorial, and quest reactions
- Communication between Actors, Components, and Subsystems
- Cross-level local session notifications through the Game Instance bus
- Deferred FIFO event delivery through queued messages
Example:
Door opens -> World.DoorOpened -> quest system updates -> audio system plays a sound -> map system removes a markerWhen Another Unreal System Is Better
Section titled “When Another Unreal System Is Better”SignalBus should not replace communication that has a clear direct owner or requires a different Unreal feature.
Use a direct function call when:
- One object owns another object
- The caller expects a return value
- The interaction is a clear one-to-one command
- The dependency is intentional and stable
Use an Unreal Event Dispatcher when:
- A specific object exposes an event
- Listeners already hold a reference to that object
- The relationship is local and object-centered
Use a Blueprint Interface when:
- The caller knows the target object
- Several target classes should implement the same command or query
- A direct request-response relationship is intended
Use Gameplay Tags or the Gameplay Ability System when:
- You need ability activation, effects, attribute changes, prediction, or tag-driven gameplay rules
Use replication and RPCs when:
- Information must move between server and clients
SignalBus is most valuable when the publisher should announce an event without discovering or controlling every listener.
Benefit Compared with Direct UE Communication
Section titled “Benefit Compared with Direct UE Communication”A direct-reference design can grow into this:
Damage system -> HUD -> quest manager -> audio manager -> scoring manager -> tutorial managerThe publisher must then know every receiver and be updated whenever another reaction is added.
SignalBus changes that relationship to:
Damage system -> Combat.DamageAppliedEach receiving system independently subscribes:
HUD -> subscribesQuest manager -> subscribesAudio manager -> subscribesScoring manager -> subscribesTutorial manager -> subscribesThis reduces reference chains and makes systems easier to add, remove, replace, or test. It also provides one consistent place to inspect subscriptions, dispatches, queued messages, payloads, warnings, and runtime topology.
SignalBus is a local, context-owned publisher-subscriber system for Unreal Engine.
Publisher -> SignalBus subsystem -> SubscriberPublishers never store, search for, or directly call subscribers. Subscribers never store, search for, or directly call publishers.
Routing is determined by:
- SignalBus scope
- Message ID
- Exact reflected payload type (
UScriptStruct*)
A message is delivered only to subscriptions registered in the same SignalBus subsystem with the same message ID and exact payload type.
Core Model
Section titled “Core Model”SignalBus separates message producers from message consumers.
Example:
Damage system -> publishes Combat.DamageApplied -> quest system reacts -> scoring system reacts -> AI director reacts -> analytics system reactsThe damage system does not need references to any of those listeners. New listeners can be added without modifying the publisher.
This makes SignalBus useful for:
- Gameplay event coordination
- UI notifications
- System-to-system communication
- Decoupling actors, components, and subsystems
- Runtime diagnostics and message-flow inspection
SignalBus is not a replacement for:
- Unreal replication
- Server or client RPCs
- Authority checks
- Save-game serialization
- Long-term state storage
- Cross-process or cross-machine messaging
Scopes
Section titled “Scopes”SignalBus provides two separate routing scopes:
WorldGame InstanceThe selected subsystem defines the routing boundary.
The two scopes never share:
- Subscriptions
- Queues
- Diagnostics
- Warnings
- Broadcast Graph state
- Message delivery
Using the same message ID and payload type in both scopes does not connect them.
World Scope
Section titled “World Scope”World messages route only through the owning:
USignalBusWorldSubsystemUse the World bus for events that belong to one specific gameplay world, map, or match.
Typical examples:
Combat.DamageAppliedActor.DestroyedEnemy.SpawnedDoor.OpenedObjective.ProgressChangedProjectile.ImpactMatch.ScoreChangedAI.AlertChangedConceptually:
UWorld└── USignalBusWorldSubsystem ├── subscriptions ├── queued messages ├── diagnostics └── warningsWhen the World is destroyed:
- World subscriptions are removed
- Pending World messages are discarded
- World diagnostics and warnings are destroyed
- A later World receives a new World SignalBus instance
Use World scope when the event only makes sense inside the current gameplay environment.
World-scope example
Section titled “World-scope example”Enemy takes damage -> server or client gameplay code updates local state -> publishes Combat.DamageApplied on that World bus -> local quest, UI, audio, or AI systems reactThe message remains inside that specific World.
Game Instance Scope
Section titled “Game Instance Scope”Game Instance messages route only through the owning:
USignalBusGameInstanceSubsystemUse the Game Instance bus for local session-level events that may outlive a World or remain relevant across map transitions.
Typical examples:
Profile.LoadedSave.CompletedLogin.StatusChangedMatchmaking.SearchStartedMatchmaking.SearchStoppedTravel.StartedTravel.CompletedFrontend.ScreenChangedPersistentInventory.RefreshedSession.ErrorConceptually:
UGameInstance└── USignalBusGameInstanceSubsystem ├── subscriptions ├── queued messages ├── diagnostics └── warningsThe Game Instance normally survives ordinary map travel, so its SignalBus can also survive while one World is destroyed and another is created.
Game Instance-scope example
Section titled “Game Instance-scope example”Current map begins travel -> publishes Travel.Started on the Game Instance bus -> loading screen listens -> frontend flow listens -> save coordinator listens
New map finishes loading -> publishes Travel.Completed on the same Game Instance busDo not place a message on the old World queue and expect it to arrive after travel. Use Game Instance scope for travel coordination.
Simple Scope Decision
Section titled “Simple Scope Decision”Use this rule:
Does the message belong only to the current level, match, or gameplay world? Yes -> World bus
Should it survive travel or represent the local game session? Yes -> Game Instance busAnother useful distinction:
World bus = gameplay-space communication
Game Instance bus = local session-level communicationExamples:
| Use World | Use Game Instance |
|---|---|
| Actor damaged | Profile loaded |
| Enemy spawned | Matchmaking state changed |
| Door opened | Save completed |
| Objective updated | Travel started |
| Projectile impacted | Login status changed |
| Match score changed | Frontend screen changed |
Dispatch Modes
Section titled “Dispatch Modes”SignalBus supports two delivery modes.
Immediate Dispatch
Section titled “Immediate Dispatch”Immediate dispatch is synchronous.
PublishImmediate -> validates the message and payload -> invokes matching subscribers -> returns after callbacks completeA subscriber callback runs before PublishImmediate returns.
Example:
Before PublishSubscriber CallbackAfter PublishImmediate dispatch is appropriate when the publisher expects all local reactions to complete during the current call stack.
Be aware that subscribers may:
- Unsubscribe themselves
- Unsubscribe another subscriber
- Add a new subscription
- Publish another immediate message
- Queue another message
SignalBus preserves deterministic insertion ordering and safe mutation behavior during dispatch.
Queued Dispatch
Section titled “Queued Dispatch”Queued dispatch copies the payload at enqueue time and returns without invoking subscribers.
PublishQueued -> validates the message and payload -> copies the payload -> adds it to the local subsystem queue -> returns
Later queue-processing pass -> finds current matching subscribers -> delivers in FIFO orderExpected order:
Before QueueAfter QueueSubscriber CallbackQueued dispatch is appropriate when work should occur later rather than inside the publisher’s current call stack.
Important behavior:
- Payload data is copied at enqueue time
- Subscribers are matched at delivery time
- A subscriber added before delivery can receive the message
- A subscriber removed before delivery does not receive it
- A destroyed publisher does not cancel an already queued message
- Messages queued during queue processing wait for a later pass
- Queues are isolated per World or Game Instance subsystem
Server, Client, and Multiplayer Architecture
Section titled “Server, Client, and Multiplayer Architecture”SignalBus is local to the Unreal context that owns it.
It is not a network transport.
A typical multiplayer layout looks like this:
Server process├── Server UWorld│ └── Server World SignalBus└── Server UGameInstance └── Server Game Instance SignalBus
Client 1 process├── Client 1 UWorld│ └── Client 1 World SignalBus└── Client 1 UGameInstance └── Client 1 Game Instance SignalBus
Client 2 process├── Client 2 UWorld│ └── Client 2 World SignalBus└── Client 2 UGameInstance └── Client 2 Game Instance SignalBusEach bus is separate.
A publish on the server’s World bus reaches only subscribers registered in that server World bus.
It does not automatically reach:
- Client 1
- Client 2
- Client World buses
- Client Game Instance buses
Likewise, a client publish remains local to that client unless Unreal networking separately sends information to the server or another client.
Multiplayer Safety
Section titled “Multiplayer Safety”SignalBus is safe for multiplayer when used as a local communication system.
The correct division is:
SignalBus = local coordination inside one World or Game Instance
Unreal networking = communication between server and clientsSignalBus does not bypass:
- Server authority
- Replication rules
- RPC ownership rules
- Relevancy
- Dormancy
- Network serialization
- Validation
A client cannot create authoritative server state merely by publishing a local SignalBus message.
Recommended Server-to-Client Pattern
Section titled “Recommended Server-to-Client Pattern”Use Unreal networking to cross the network, then use SignalBus locally on each machine.
Example:
Client input -> Server RPC -> server validates the request -> server changes authoritative state -> server publishes a local World SignalBus message -> server-side systems react -> replicated property or RPC reaches clients -> client OnRep or Client RPC publishes a local client SignalBus message -> client UI, audio, camera, and effects reactThis keeps responsibilities clear:
RPC or replication = moves commands or state between machines
SignalBus = fans that event out to local systemsServer Example
Section titled “Server Example”Authoritative damage processing:
Client requests attack -> Server RPC -> server validates attack -> server applies damage -> server publishes Combat.DamageApplied on the server World bus -> server quest system reacts -> server scoring system reacts -> server AI director reacts -> health value replicates to clientsThe SignalBus publish itself does not travel to clients.
Client Example
Section titled “Client Example”Reacting to replicated health:
Replicated Health changes on client -> OnRep_Health executes -> client publishes Combat.HealthChanged on the client World bus -> HUD updates -> hit audio plays -> camera effect reacts -> local accessibility feedback reactsThe client-side SignalBus message is a local reaction to networked state.
RPC Limitations and SignalBus
Section titled “RPC Limitations and SignalBus”SignalBus does not replace server-to-client or client-to-server RPC architecture.
An RPC has network semantics that SignalBus does not provide.
RPCs can:
- Cross process and machine boundaries
- Follow Unreal ownership rules
- Target the server, owning client, or multiple clients
- Serialize supported parameters
- Participate in Unreal’s network channel and connection model
SignalBus messages cannot do those things by themselves.
What SignalBus Cannot Do
Section titled “What SignalBus Cannot Do”This will not reach clients:
Server -> Publish Combat.DamageApplied on server World bus -> Client subscriberThe client subscriber is registered in a different subsystem on another machine or network context.
This also does not send a command to the server:
Client -> Publish Inventory.AddItem on client World bus -> Server inventory systemThe server inventory system cannot receive that local client publish.
Use a Server RPC or another Unreal networking mechanism.
RPC Delivery and SignalBus Delivery Are Different
Section titled “RPC Delivery and SignalBus Delivery Are Different”RPC delivery is directed by Unreal networking.
SignalBus delivery is selected by:
Local subsystem+ Message ID+ Exact payload typeAn RPC usually targets a specific network endpoint or group of endpoints.
SignalBus broadcasts locally to every matching subscriber in the selected subsystem.
Example:
Server RPC -> one authoritative server function
Server SignalBus message -> every matching local server subscriberThese mechanisms complement each other.
Recommended RPC Bridge
Section titled “Recommended RPC Bridge”A thin networking layer can translate between RPCs and local SignalBus messages.
Example:
Server gameplay system -> changes authoritative objective state -> replicated objective data reaches client
Client objective component -> OnRep_ObjectiveState -> publishes Objective.StateChanged on client World bus
Local listeners -> HUD -> map marker system -> announcer audio -> tutorial promptsAnother example:
Client UI -> requests matchmaking cancel -> Server RPC or online-session API -> authoritative response returns -> local Game Instance bus publishes Matchmaking.Cancelled -> frontend widgets and status systems reactDo not create an automatic generic “replicate every SignalBus message” bridge without a deliberate authority, security, bandwidth, and serialization design.
Important Multiplayer Limitations
Section titled “Important Multiplayer Limitations”No automatic replication
Section titled “No automatic replication”SignalBus messages do not replicate.
A server publish does not automatically produce a client publish.
No authority enforcement
Section titled “No authority enforcement”SignalBus does not know whether a publisher has server authority.
The caller must use Unreal authority checks where required.
Example:
if (HasAuthority()){ // Apply authoritative state and publish a server-local message.}No RPC validation
Section titled “No RPC validation”SignalBus does not validate client requests.
Server RPC handlers must still validate:
- Permissions
- Ownership
- Input ranges
- Gameplay rules
- Anti-cheat concerns
No network serialization
Section titled “No network serialization”SignalBus payloads use reflected structs for local routing, but they are not automatically network-serialized.
A payload being valid for SignalBus does not guarantee it is valid as an RPC parameter or replicated property.
No delivery guarantee across machines
Section titled “No delivery guarantee across machines”SignalBus has no concept of:
- Packet loss
- Network reliability
- Connections
- Relevancy
- Bandwidth
- Replication frequency
- Dormancy
Those remain Unreal networking concerns.
Separate server and client Game Instances
Section titled “Separate server and client Game Instances”A server and client generally own separate Game Instances.
A Game Instance SignalBus message remains local to its own Game Instance.
Game Instance scope means “across local Worlds owned by this Game Instance,” not “across every machine in the multiplayer session.”
Listen Server and Multi-PIE
Section titled “Listen Server and Multi-PIE”In Multi-PIE, several Worlds and Game Instances may exist in one executable.
SignalBus still isolates them by subsystem instance.
Example:
World - PIE 0Game Instance - PIE 0
World - PIE 1Game Instance - PIE 1Even though they share a process:
- World messages do not cross PIE Worlds
- Game Instance messages do not cross PIE Game Instances
- Queues remain separate
- Diagnostics remain separate
- Warnings remain separate
- Broadcast Graph state remains separate
This is why the SignalBus Runtime Inspector shows multiple selectable contexts.
Choosing Scope in Multiplayer
Section titled “Choosing Scope in Multiplayer”Use World scope for
Section titled “Use World scope for”- Server-authoritative match events
- Client-local reactions to replicated gameplay state
- Actor and component events
- Combat events
- Objective events
- Spawn and destruction notifications
- Map-local UI
- Local match systems
Use Game Instance scope for
Section titled “Use Game Instance scope for”- Login and account flow
- Matchmaking
- Frontend state
- Session errors
- Save or profile completion
- Travel coordination
- Persistent local UI state
- Local services that survive map transitions
Do not use Game Instance scope merely because an event should reach every client. It still remains local to one Game Instance.
Safe Multiplayer Patterns
Section titled “Safe Multiplayer Patterns”Pattern 1: Server authority, local server reactions
Section titled “Pattern 1: Server authority, local server reactions”Server changes authoritative state -> server World SignalBus message -> local server systems react -> state replicates normallyPattern 2: Client reacts to replication
Section titled “Pattern 2: Client reacts to replication”Replicated property changes -> OnRep -> client World SignalBus message -> client presentation systems reactPattern 3: Client request
Section titled “Pattern 3: Client request”Client local input -> Server RPC -> server validates -> server changes state -> optional server-local SignalBus publishPattern 4: Client RPC fan-out
Section titled “Pattern 4: Client RPC fan-out”Server sends Client RPC -> receiving client publishes local SignalBus message -> multiple local client systems reactPattern 5: NetMulticast fan-out
Section titled “Pattern 5: NetMulticast fan-out”Server executes NetMulticast RPC -> RPC runs on relevant machines -> each machine publishes its own local SignalBus message -> each machine's local listeners reactUse NetMulticast carefully according to Unreal’s authority, relevancy, and reliability rules.
Unsafe Multiplayer Patterns
Section titled “Unsafe Multiplayer Patterns”Do not treat this as authoritative:
Client publishes Player.CurrencyAdded locally -> client updates authoritative currencyDo not expect this to cross the network:
Server publishes Match.Ended -> all clients automatically receive itDo not create a generic payload-forwarding RPC that accepts arbitrary client SignalBus messages without validation.
That can introduce:
- Authority bypass
- Security problems
- Excess bandwidth
- Unbounded message types
- Unsafe payload handling
- Difficult debugging
- Accidental client control over server systems
Network-facing messages should use explicit, reviewed RPCs or replicated state.
Actor and Component Lifetime
Section titled “Actor and Component Lifetime”Actors and Actor Components may subscribe to either scope.
Typical guidance:
Gameplay Actor or Component -> usually World scope
Persistent manager or session coordinator -> usually Game Instance scopeA World Actor may subscribe to the Game Instance bus, but Game Instance scope does not make the Actor persistent.
When the Actor or listener component is destroyed:
- Its subscription is removed or pruned
- It no longer receives messages
- The Game Instance subsystem may continue to exist
The lifetime owner remains separate from the routing scope.
Blueprint Boundary
Section titled “Blueprint Boundary”Blueprint APIs call the same runtime subsystems used by C++.
There is no separate Blueprint-only bus.
Blueprint listener callbacks receive an owned payload wrapper copy before the Blueprint event is invoked. This keeps Blueprint delivery safe without changing native C++ immediate callback semantics.
Immediate Blueprint order:
Before PublishReceive CallbackAfter PublishQueued Blueprint order:
Before QueueAfter QueueReceive CallbackBlueprint and C++ subscribers can communicate through the same SignalBus subsystem when they use the same:
- Scope
- Message ID
- Exact payload struct type
Observability
Section titled “Observability”Diagnostics, warnings, the SignalBus Runtime Inspector, the Broadcast Graph, and Unreal Trace observe runtime behavior.
They do not:
- Control routing
- Mutate subscriptions
- Repair warnings
- Edit payloads
- Replay messages
- Change delivery order
- Replicate messages
The Inspector can show separate World and Game Instance contexts for server and client PIE instances.
The Broadcast Graph represents observed local topology:
Publisher -> Message -> SubscriberPublisher nodes are based on retained observed history. Publishers are not permanently registered.
Threading
Section titled “Threading”SignalBus is game-thread-only.
Unsupported operations include:
- Cross-thread publishing
- Cross-thread subscription
- Worker-thread callbacks
- Lock-free producer queues
Callers must return to the game thread before using SignalBus.
SignalBus does not add locks or partial cross-thread behavior.
Summary
Section titled “Summary”World SignalBus- One bus per UWorld- Current map, match, and gameplay- Destroyed with the World- Isolated per server/client World
Game Instance SignalBus- One bus per UGameInstance- Local session and cross-level coordination- Survives ordinary World travel- Isolated per server/client Game Instance
Unreal Networking- Moves commands and state between server and clients- Enforces authority, ownership, and replication- Separate from SignalBusThe safest multiplayer architecture is:
Use Unreal networking to cross machines.Use SignalBus to coordinate local systems after the network event arrives.