Message Bus (GDD#3)

By | 2013-04-02

There’s a common problem in software design that doesn’t only exist in game development called code dependencies. In a project, small or big, every developer comes to the point where several modules depend on each other: The GUI renderer depends on the health points and inventory, the inventory depends on the object pool and player state, the rendering engine depends on the object pool, textures, geometry, and much more.

One goal of object-oriented programming is to write modular (thus flexible) source code, and one of the biggest rules is to keep dependencies low. This article will talk about a system that implements a message bus with fire-and-forget behavior, with the main target being eliminating dependencies in the sources.

Let’s at first look at an example diagram of a game’s class design:

Code dependencies

Code dependencies

At the first sight it already looks terrible: It seems that all the classes/modules in the diagram are all dependent on each other, which means all the code we write is not modular and can’t be reused in other projects. Another disadvantage is compile times: TUs (translation units) have to include code that it’s dependent on. Whenever the included code changes (mostly header files), the TU needs to be recompiled (there are tricks like forward declaration though).

The real pain however arises when you start giving references/pointers to modules. For example let’s look at the the Script module in the diagram above, which is a module for working with scripts that control some aspects of the game: It’s dependent on Map, ComponentSystem, Renderer and Object, so the constructor might look similar to this:

Wherever you instantiate Script, you need to have references to all the dependencies. If one of the dependencies change, for example due to a refactoring, all dependencies have to be updated, too.

I’m sure a lot of you guys have already experienced such code design and always got to the point where you got annoyed by it. Let’s see how we can avoid it.

Replacing the dependencies with a message bus

We’ll now be talking about an approach that’s really not new in software design; some programming languages even have such concepts built-in, like Smalltalk.

message bus is a connection between one or more senders and/or receivers. Think of it like a connection between computers in a bus topology: Every node can send a message by passing it to the bus, and all connected nodes will receive that message. If the node is processed and if a reply is sent is completely up to each receiver itself.

Having modules connected to a message bus gives us some advantages:

  • Every module is isolated, it does not need to know of any others.
  • Every module can react to any message that’s being sent to the bus; that means you get extra flexibility for free, without increasing dependencies at all.
  • It’s much easier to follow the YAGNI workflow: For example you’re going to add weapons. At first you implement the physics, then you add visuals in the renderer, and then playing sounds. All of those features can be implemented independently at any time, without interrupting each other.
  • You save yourself from thinking a lot about how to connect certain modules to each other. Sometimes it takes a huge amount of time, including drawing diagrams/dependency graphs.

Messages

What’s a message? Quite simple:

Every message should have a unique message ID for identification purposes. This can be basically anything: An integer, a string, whatever. Personally I chose to use hashed strings for performance reasons while still keeping readability (take a look at std::hash).

Furthermore messages can (but don’t have to) include attributes/parameters. The attributes should be preferably be of any type.

Example message:

  • Message ID: “entity_created”
  • Attribute “entity_id”: 1337
  • Attribute “position”: 1.0f, 2.0f, 3.0f
  • Attribute “color”: “green”

Sending useful messages

We now have a basic understanding of how the message bus and messages looks like. However it’s very important to not send messages that control other modules in a strict way — i.e. sending messages the correct way.

For example there are three modules: Script, Network and Renderer. A script decides to create a new game object, and that object needs to be rendered and sent over the wire, so that others can see it too. This requires us to somehow inform Network and Renderer of the entity creation.

Some of you would probably come up with messages like this:

First message, to Renderer:

  • Message ID: “render_object”
  • Attribute “type”: “cube”
  • Attribute “color”: “blue”

First message, to Network:

  • Message ID: “send_object”
  • Attribute “type”: “cube”
  • Attribute “color”: “blue”

DO NOT DO THIS!

Sorry for the red color, but that’s really not any better than writing code that’s dependent on a lot of other code. Instead it will couple the messages to specific receivers. With the kind of messages above you try to control other modules, and in order to be able to control something you have to know that it exists (code-wise). That’s a classic dependency, just in a different form.

So instead of telling others what to do, you tell everybody what happened. If an entity got created, you send a “entity_created” message with all relevant information. The modules that receive that message can then decide if they want to take action on it, or not.

What to send to the bus

When you start using a message bus, you’ll quickly want to decouple everything. That’s a good thing in my opinion. From my experience I can tell that even if you tend to overuse this approach, it still leads to very good code design.

It’s even okay to feed the bus with high-frequency events like mouse moves or entity position changes. Just keep in mind to optimize your message bus system good enough to be able to handle a high amount of messages in (very) short times. (But please be aware that premature optimization is still the root of all evil! — Donald Knuth)

Implementation hints

Like mentioned before the most critical part is identifying messages, because that’s what every receiver has to do. Instead of sending messages to all receivers you might also think about adding a hooking mechanism where receivers can subscribe to specific message IDs only.

One thing that you can do to improve the identification process is using a datatype for the message ID that can be compared really fast, and that’s mostly numbers. Additionally, to maintain readability, you can use hashing algorithms like std::hash.

If you plan to store any datatype as attributes/parameters, make sure that type conversions do not take processing time. Instead of calling conversion functions like std::to_string or similar, you should store your values in a way so that they can be casted using static_cast (or dynamic_cast in debug builds).

One thing you have to think about is where you’re going to add the message receivers. What you really don’t want are blackboxes, which especially make it very hard to test your code. One idea is to write message receivers that just call functions that do the actual work.

My personal reference implementation of a message bus for the game FlexWorld only consists of a small number of classes: A router where you can register receivers (which are std::function objects) and that takes care of the message dispatching, and a message class containing an ID (hashed string) and any number of attributes of any datatype.

Conclusion

In this article we talked about a nice way of removing internal code dependencies by replacing them with a message bus. The bus gives great flexibility, reduces code compilation time and allows implementing features step by step and in an isolated way.

Feel free to leave a comment if you liked or disliked this article! Thanks for reading.

If you like the GDD articles and would like to support the author, consider buying the PDF/EPUB/MOBI version in a pay-what-you-want fashion:

12 thoughts on “Message Bus (GDD#3)

  1. owen

    And then all your code ends up depending on the message bus but its better than a web of class inheritance.  I found the bus system most useful when doing a achievement statistics.

    Reply
    1. Stefan Post author

      That’s correct but also required if things are supposed to work together. 😉

      Reply
  2. Iggy Zuk

    What’s you workaround the C++ member function pointer issue? FastDelegate seems to do the job, but I wonder if there is an alternative, specially with C++11? And this was a good read, many thanks.

    Reply
    1. Stefan Post author

      Member functions can be easily bound using std::bind, e.g. std::bind( &MyClass::function, this ). When it’s being used together with std::function, then storing pointers to any type of callable object is a piece of cake. Available in C++11 only though. If you can’t use C++11, you might want to use Boost’s implementation, which is basically the same.

      Reply
  3. Chris M

    How do you in C++ define a message class which contains “any number of attributes of any datatype”?

    Reply
    1. Stefan Post author

      Any number of attributes should be clear, you use a container. To be able to store any type in the same container, you have multiple options. One is using Boost.Any, another one is deriving from a base class to a template class (like: template class ConcreteProperty : public BaseProperty). There are other approaches too, e.g. some people store only strings and cast when needed. I myself prefer the BaseProperty/ConcreteProperty method.

      Reply
  4. Pingback: Messaging using variadic templates | Therocode

  5. google api code

    Hello! I know this is kinda off topic but I was wondering which blog platform
    are you using for this site? I’m getting tired of WordPress because I’ve had
    issues with hackers and I’m looking at options for another platform.
    I would be great if you could point me in the direction of a good platform.

    Reply
    1. Stefan Post author

      I use WordPress. 😉 Just make sure to update when there are updates available. AFAIK the really critical ones don’t happen that often anymore, like they did years ago. I’m happy with it.

      Reply
  6. Sean Francis N. Ballais

    “Instead of sending messages to all receivers you might also think about adding a hooking mechanism where receivers can subscribe to specific message IDs only.”

    Can you elaborate more on the hooking mechanism and how it can be implemented?

    Reply
    1. Stefan Post author

      Hello Sean. That can be, for example, a classic observer pattern. Something along the lines of: bus.subscribe("message_name", my_callback);

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *