Skip to main content
Docs: template-unreal

Message Bus

The msgbus feature provides client and adapter implementations using Unreal Engine's built-in Message Bus system. This enables:

  • Inter-process communication: Connect Unreal applications running on the same machine
  • Editor-to-game communication: Share data between editor tools and PIE sessions
  • Distributed Unreal systems: Connect multiple Unreal instances in a local network

Unlike OLink, which uses WebSockets, Message Bus uses Unreal's native UDP-based messaging system optimized for Unreal-to-Unreal communication.

FeatureMessage BusOLink
ProtocolUnreal's UDP Message BusWebSocket
Best forUnreal-to-Unreal communicationCross-platform, simulation
SetupZero configuration on localhostRequires URL configuration
NetworkLocal network / same machineAny network
ApiGear SimulationNoYes
Non-Unreal clientsNoYes
tip

Use Message Bus when communicating between Unreal applications. Use OLink when connecting to non-Unreal services or ApiGear simulation tools.

File overview for module

With our example API definition:

Hello World API (click to expand)
schema: apigear.module/1.0
name: io.world
version: "1.0.0"

interfaces:
- name: Hello
properties:
- { name: last, type: Message }
operations:
- name: say
params:
- { name: msg, type: Message }
- { name: when, type: When }
return:
type: int
signals:
- name: justSaid
params:
- { name: msg, type: Message }
enums:
- name: When
members:
- { name: Now, value: 0 }
- { name: Soon, value: 1 }
- { name: Never, value: 2 }
structs:
- name: Message
fields:
- { name: content, type: string }

The following file structure is generated in the IoWorldMsgBus module:

📂IoWorld/Source/IoWorldMsgBus
┣ 📂Private
┃ ┣ 📂Generated
┃ ┃ ┣ 📜IoWorldMsgBus.cpp
┃ ┃ ┗ 📂MsgBus
┃ ┃ ┣ 📜IoWorldHelloMsgBusClient.cpp
┃ ┃ ┗ 📜IoWorldHelloMsgBusAdapter.cpp
┃ ┗ 📂Tests
┃ ┣ 📜IoWorldHelloMsgBus.spec.cpp
┃ ┣ 📜IoWorldHelloMsgBusFixture.h
┃ ┗ 📜IoWorldHelloMsgBusFixture.cpp
┣ 📂Public
┃ ┗ 📂IoWorld
┃ ┣ 📜IoWorldMsgBus.h
┃ ┗ 📂Generated/MsgBus
┃ ┣ 📜IoWorldHelloMsgBusClient.h
┃ ┣ 📜IoWorldHelloMsgBusAdapter.h
┃ ┗ 📜IoWorldHelloMsgBusMessages.h
┗ 📜IoWorldMsgBus.Build.cs

Message Bus Client

The UIoWorldHelloMsgBusClient class implements IIoWorldHelloInterface and communicates with a remote adapter via Message Bus.

How it works

  1. Discovers service: Publishes a discovery message to locate the adapter on the network
  2. Subscribes to messages: Listens for property changes, signals, and operation replies from the adapter
  3. Publishes requests: Sends property change requests and operation calls
  4. Caches state: Maintains local copies of properties synchronized from the adapter
  5. Monitors health: Tracks connection status with heartbeat ping/pong

The client acts as a Remote Proxy - it implements the same interface as the backend service but forwards calls over Message Bus. Properties are cached locally for fast reads.

Connection Management

The client provides methods to manage the connection lifecycle:

UFUNCTION(BlueprintCallable, Category = "ApiGear|IoWorld|Hello|Remote")
void _Connect();

UFUNCTION(BlueprintCallable, Category = "ApiGear|IoWorld|Hello|Remote")
void _Disconnect();

UFUNCTION(BlueprintCallable, Category = "ApiGear|IoWorld|Hello|Remote")
bool _IsConnected() const;

The client automatically discovers the adapter service using Unreal's Message Bus. The endpoint is registered with the address "ApiGear/IoWorld/Hello/Client".

Connection Status

The client broadcasts connection status changes through delegates:

UPROPERTY(BlueprintAssignable, Category = "ApiGear|IoWorld|Hello|Remote", DisplayName = "Connection Status Changed")
FIoWorldHelloConnectionStatusChangedDelegateBP _ConnectionStatusChangedBP;
FIoWorldHelloConnectionStatusChangedDelegate _ConnectionStatusChanged;

Statistics and Health Monitoring

The client tracks round-trip time statistics (current, average, min, max RTT in milliseconds) through FIoWorldHelloStats. Access statistics and subscribe to updates:

const FIoWorldHelloStats& Stats = HelloClient->_GetStats();

HelloClient->_StatsUpdated.AddDynamic(this, &UMyClass::OnStatsUpdated);

API Implementation

The client implements the standard IIoWorldHelloInterface (see API documentation):

  • Properties: Getters return locally cached values synchronized from the adapter. Setters send change requests to the adapter. The implementation optimizes network load by only sending when values actually change.
  • Operations: Calls are sent to the adapter and block until a response is received. Returns a default value if not connected.
  • Signals: Subscribe to signals through the Publisher, which broadcasts events received from the adapter.

Using the Client

#include "IoWorld/Generated/MsgBus/IoWorldHelloMsgBusClient.h"

UIoWorldHelloMsgBusClient* HelloClient = GetGameInstance()->GetSubsystem<UIoWorldHelloMsgBusClient>();

HelloClient->_Connect();

HelloClient->_ConnectionStatusChangedBP.AddDynamic(this, &UMyClass::OnConnectionStatusChanged);

TScriptInterface<IIoWorldHelloInterface> Hello = HelloClient;
Hello->Say(Msg, EIoWorldWhen::IWW_Now);

Hello->_GetPublisher()->OnLastChangedBP.AddDynamic(this, &UMyClass::OnLastChanged);

Message Bus Adapter

The UIoWorldHelloMsgBusAdapter wraps a local IIoWorldHelloInterface implementation and exposes it via Message Bus.

How it works

  1. Wraps local implementation: Uses an existing implementation (e.g., your stub)
  2. Handles discovery: Responds to client discovery messages and initializes connections
  3. Handles requests: Processes property change requests and operation calls from clients
  4. Broadcasts updates: Publishes property changes and signals to all subscribed clients
  5. Tracks clients: Monitors connected clients and handles timeouts

The adapter uses the Adapter Pattern - it wraps any IIoWorldHelloInterface implementation and exposes it over Message Bus. Multiple clients can connect simultaneously.

Connection Management

The adapter provides methods to control when it accepts client connections:

UFUNCTION(BlueprintCallable, Category = "ApiGear|IoWorld|Hello|Remote")
void _StartListening();

UFUNCTION(BlueprintCallable, Category = "ApiGear|IoWorld|Hello|Remote")
void _StopListening();

UFUNCTION(BlueprintCallable, Category = "ApiGear|IoWorld|Hello|Remote")
bool _IsListening() const;

Client Tracking

The adapter provides delegates to monitor client connections:

UPROPERTY(BlueprintAssignable, Category = "ApiGear|IoWorld|Hello|Remote", DisplayName = "New client connected")
FIoWorldHelloClientConnectedDelegate _OnClientConnected;

UPROPERTY(BlueprintAssignable, Category = "ApiGear|IoWorld|Hello|Remote", DisplayName = "Client disconnected")
FIoWorldHelloClientDisconnectedDelegate _OnClientDisconnected;

UPROPERTY(BlueprintAssignable, Category = "ApiGear|IoWorld|Hello|Remote", DisplayName = "Client timed out")
FIoWorldHelloClientTimeoutDelegate _OnClientTimeout;

UPROPERTY(BlueprintAssignable, Category = "ApiGear|IoWorld|Hello|Remote", DisplayName = "Clients connected count changed")
FIoWorldHelloClientCountDelegate _OnClientsConnectedCountChanged;

UFUNCTION(BlueprintCallable, Category = "ApiGear|IoWorld|Hello|Remote")
const int32 _GetClientsConnectedCount() const;

Backend Service

The adapter wraps an existing implementation that handles the actual business logic:

UFUNCTION(BlueprintCallable, Category = "ApiGear|IoWorld|Hello")
void _setBackendService(TScriptInterface<IIoWorldHelloInterface> InService);

Using the Adapter

#include "IoWorld/Generated/MsgBus/IoWorldHelloMsgBusAdapter.h"
#include "IoWorld/Implementation/IoWorldHello.h"

UIoWorldHelloImplementation* LocalHello = GetGameInstance()->GetSubsystem<UIoWorldHelloImplementation>();

UIoWorldHelloMsgBusAdapter* Adapter = GetGameInstance()->GetSubsystem<UIoWorldHelloMsgBusAdapter>();
Adapter->_setBackendService(LocalHello);
Adapter->_StartListening();

Adapter->_OnClientConnected.AddDynamic(this, &UMyClass::OnClientConnected);
Adapter->_OnClientDisconnected.AddDynamic(this, &UMyClass::OnClientDisconnected);

Testing with Message Bus

The msgbus_tests feature generates test fixtures:

IoWorld/Source/IoWorldMsgBus/Private/Tests
├── IoWorldHelloMsgBus.spec.cpp
├── IoWorldHelloMsgBusFixture.h
└── IoWorldHelloMsgBusFixture.cpp

Running Tests

UnrealEditor-Cmd.exe YourProject.uproject -ExecCmds="Automation RunTests IoWorld.MsgBus"

Best Practices

Lifecycle Management

  • Initialize adapters before clients start connecting
  • Clean up adapters when no longer needed to stop message handling
  • Clients should handle adapter unavailability gracefully
  • Monitor connection status changes to react to disconnections

Performance

  • Message Bus is efficient for moderate message rates
  • For high-frequency updates, consider using direct connections
  • Property changes are only broadcast when values actually change
  • The client tracks sent values to avoid redundant network requests

Message Protocol

The Message Bus feature uses generated message structures (IoWorldHelloMsgBusMessages.h) for communication between client and adapter. These messages handle:

  • Connection lifecycle - service discovery, initialization, and disconnect
  • Heartbeat monitoring - ping/pong messages to detect connection health
  • Property synchronization - change requests and notifications
  • Operation request/response - calls with correlation IDs for matching replies
  • Signal broadcasting - events sent to all connected clients
note

You don't need to interact with these message types directly - they are used internally by the client and adapter classes.

Communication Flow

The following diagram shows how messages flow between client and adapter for each type of interaction: