---
title: "Bus plugin"
---

> Documentation Index
> Fetch the complete documentation index at: https://openarkstudio.github.io/ark/llms.txt
> Use this file to discover all available pages before exploring further.

# Bus plugin

Bus gives processes logical addresses and loads application types, process endpoints and connection relationships. `bus_module` exposes configuration queries. `msg_module` sends messages by application type, Bus ID or session, while the Net plugin manages the underlying connections.

## Addresses and responsibilities

A Bus address has four components, `channel.zone.app.instance`, such as `1.1.31.1`. Each occupies 8 bits in the combined `bus_id_t`. It identifies a process, rather than an IP address or listening port.

| Component | Meaning | Example |
| --- | --- | --- |
| `channel` | Application-defined channel | `1` |
| `zone` | Application-defined region | `1` |
| `app` | Numeric `app_type` value | `31` is `ark_app_game` |
| `instance` | Instance number within that process type | `1` |

| Interface | Responsibility |
| --- | --- |
| `bus_module` | Local process information, application names, target relationships and other process configurations |
| `msg_module` | Serialize messages, select an existing connection, fill headers and request sending |
| `net_service_manager_module` | Listening services, client connections and Bus-to-session mappings |
| `net_client_service` / `net_server_service` | Receive callbacks, connection events and connection state handling |

## Configure processes and relationships

`bus_module::init()` reads four sections from `bus.plugin.conf`:

| Section | Contents |
| --- | --- |
| `applications` | Application names mapped to numeric `app_type` values |
| `relations` | Directed targets: which `target` types a `source` should connect to |
| `processes` | Listening endpoints, connection limits and thread settings for each Bus ID |
| `center` | Registry parameters exposed by `get_reg_center()` |

The following example describes Game and World processes. External and intranet endpoints use different ports to avoid conflicts when both listeners start.

```xml
<xml>
    <applications>
        <application name="world" type="30" />
        <application name="game" type="31" />
    </applications>
    <relations>
        <relation source="game" target="world" />
    </relations>
    <processes>
        <proc busid="1.1.30.1" endpoint_server="tcp://127.0.0.1:9101"
            endpoint_intranet="tcp://127.0.0.1:9111" max_connection="100" thread_num="1" />
        <proc busid="1.1.31.1" endpoint_server="tcp://127.0.0.1:9102"
            endpoint_intranet="tcp://127.0.0.1:9112" max_connection="100" thread_num="1" />
    </processes>
    <center ip="127.0.0.1" port="8500" service_name="ark-service"
        check_interval="10s" check_timeout="3s" />
</xml>
```

This is a Bus configuration example, not a complete two-process launch configuration. Applications must also load Bus, Net and their dependencies, set each process's Bus ID, start listeners and initiate connections. `relations` supplies target types; reading a relationship does not automatically create a connection.

Configuring `center` does not enable dynamic registration or discovery. The Consul registration and health-based discovery paths in the current network service manager remain commented out. This configuration alone cannot discover target processes.

## Query local and target processes

After module initialization, obtain the Bus interface from the plugin manager. This function collects statically configured processes for the current application's target types.

```cpp
#include <vector>

#include "ark/bus/bus_module.hpp"
#include "ark/plugin_manager.hpp"

std::vector<ark::proc_config> configured_targets(ark::plugin_manager& manager) {
    auto* bus = manager.find_module<ark::bus_module>();
    if (bus == nullptr)
    {
        return {};
    }

    std::vector<ark::app_type> types;
    if (!bus->get_target_bus_relations(types))
    {
        return {};
    }

    std::vector<ark::proc_config> result;
    for (auto type : types)
    {
        std::vector<const ark::proc_config*> processes;
        bus->get_other_proc(type, processes);
        for (const auto* process : processes)
        {
            result.push_back(*process);
        }
    }
    return result;
}
```

`get_other_proc(type, list)` appends pointers to its output list and returns the resulting total list length. The example starts with an empty list each time and copies the records, avoiding externally retained pointers into module state. This query filters by application type without also restricting channel or zone; applications can apply those filters when needed.

| Query | Result and limits |
| --- | --- |
| `get_self_bus_id()` / `get_self_bus_name()` | Current process integer ID / four-component address |
| `get_self_proc()` | Local `proc_config`; static configuration does not mean a listener is running |
| `get_app_type(name)` | Returns `ark_app_default` when absent |
| `get_app_name(type)` | Returns an empty string when absent |
| `get_other_proc(id, proc)` | Copies the configuration and returns `true` when found; otherwise returns `false` |
| `combine_bus_id(type, instance)` | Combines the local channel and zone with the supplied type and instance |

## Establish a TCP connection

The target process first starts its listeners with `net_service_manager_module::create_server()`. The following function reads the target's configured intranet endpoint and queues a TCP connection through the network service manager.

```cpp
#include "ark/bus/bus_module.hpp"
#include "ark/net/net_service_manager_module.hpp"
#include "ark/plugin_manager.hpp"

bool queue_tcp_connection(ark::plugin_manager& manager, ark::bus_id_t target_id) {
    auto* bus = manager.find_module<ark::bus_module>();
    auto* network = manager.find_module<ark::net_service_manager_module>();
    if (bus == nullptr || network == nullptr)
    {
        return false;
    }

    ark::proc_config target;
    if (!bus->get_other_proc(target_id, target))
    {
        return false;
    }

    const auto& endpoint = target.intranet_ep;
    if (endpoint.proto() != ark::proto_type::tcp || endpoint.get_ip().empty() || endpoint.get_port() == 0)
    {
        return false;
    }

    return network->create_client_service(
        ark::bus_addr(target_id), ark::proto_type::tcp, endpoint.get_ip(), endpoint.get_port());
}
```

`true` means the connection request was queued in the client service and will be processed by subsequent `update()` calls. It does not mean the connection has succeeded. Observe connection events with the client service's `reg_net_event_callback()` and register message handlers with `reg_msg_callback()`. The receiver registers handlers on the corresponding server service.

The current client service handles reconnection through its connection state machine, with a 30-second retry interval in the implementation. This is not a configurable backoff policy in the XML file. Reconnection does not replay application messages whose earlier send failed.

## Send and receive messages

Once a connection is available, send application-defined Protobuf messages through `msg_module`. The following function accepts the caller's message ID and message type, without defining an application protocol.

```cpp
#include "ark/bus/msg_module.hpp"

bool send_to_process(ark::msg_module& messages, ark::bus_id_t target_id, ark::msg_id_t message_id,
    const google::protobuf::Message& body) {
    return messages.send_msg_by_bus_id(target_id, message_id, body);
}
```

| Send method | Connection selection |
| --- | --- |
| `send_msg_by_bus_id()` | Uses an existing connection for the source and target Bus IDs, with an inter-process message header |
| `send_def_ss_msg_by_app_type()` | Uses consistent hashing within the target type's client service; the current key is the local process Bus ID |
| `send_def_cs_msg_by_app_type()` | Uses the same selection method with a client message header |
| `send_to_client()` | Sends to a session through the external listening service |
| `send_to_intra()` | Sends to a session through the intranet listening service |

Inter-process headers carry the message ID, source / target Bus IDs and Actor ID. Sending returns `false` on failure, for example when a service or connection is unavailable or serialization fails. `true` does not mean the remote application has processed the message. Request/response handling, timeouts and idempotency belong to the application protocol.

The receive flow is: the network service extracts a `net_msg` → invokes the handler registered for its message ID → the handler parses its application message with `msg_module::decode_protobuf()`. The parser returns `false` on a Protobuf parse failure. `read_message_body()` copies the raw body and retrieves the Actor ID. A callback's `net_msg*` is borrowed data; copy the required contents before deferring work.

## Coverage and planned work

These examples cover configuration queries, an explicit TCP connection request and sending through an existing connection. They are not an accepted multi-process deployment example, and do not imply that every protocol is integrated with the service manager. See the [Net plugin](/ark/reference/network) for transport integration.

TODO: provide a standalone two-process example and tests covering connection setup, message round trips, process restarts, reconnection and cross-host routing. Implement and validate dynamic registration / discovery separately.

## API sources

- [Bus addresses and process configuration](https://github.com/OpenArkStudio/ark/blob/main/include/ark/bus.hpp)
- [Bus interface](https://github.com/OpenArkStudio/ark/blob/main/include/ark/bus/bus_module.hpp)
- [Message interface](https://github.com/OpenArkStudio/ark/blob/main/include/ark/bus/msg_module.hpp)
- [Network service management interface](https://github.com/OpenArkStudio/ark/blob/main/include/ark/net/net_service_manager_module.hpp)
- [Network build and tests](/ark/networking)

Source: https://openarkstudio.github.io/ark/reference/bus/index.mdx
