https://github.com/atomicptr/dove
A tiny, single file, header only messaging system for games written in C++23
https://github.com/atomicptr/dove
cpp cpp23 game-development header-only header-only-library message-broker message-passing
Last synced: 11 months ago
JSON representation
A tiny, single file, header only messaging system for games written in C++23
- Host: GitHub
- URL: https://github.com/atomicptr/dove
- Owner: atomicptr
- License: 0bsd
- Created: 2024-09-03T09:16:44.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2025-02-23T08:28:36.000Z (11 months ago)
- Last Synced: 2025-03-07T00:37:43.944Z (11 months ago)
- Topics: cpp, cpp23, game-development, header-only, header-only-library, message-broker, message-passing
- Language: C++
- Homepage:
- Size: 26.4 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# dove
A tiny, single file, header only messaging system for games written in C++23
Inspired by [pidgeon](https://github.com/atomicptr/pidgeon).
## Usage
Dove utilizes a broker to whom you send pre defined messages with data, which you put into a queue and then process them all at once.
First we need to define out message and data types:
```cpp
#pragma once
// This file could be src/game/broker.hpp or something
// Generally we want this at a place where it can actually be
// included and reused
#include
// In this example we will update our UI via messages, so when these values
// changei we will send out a message
enum class Message {
PlayerHPUpdated,
PlayerManaUpdated,
};
// for this example we will only pass new values so a number
// is sufficient, normally you'd probably use a union or a std::variant here
using MessageData = unsigned int;
// its highly recommended to define a Broker type like this:
using MyBroker = dove::Broker;
// also we only want one global broker in this case:
MyBroker* broker = nullptr;
````
Next we need to initialize the broker somewhere, for this example we just do it in main:
```cpp
int main() {
broker = new MyBroker;
// do stuff
delete broker;
}
```
Now at various locations in the code we can post messages:
```cpp
#include "broker.hpp"
// ...
void game::Player::cast_spell(const Spell& spell) {
mana_amount -= spell.cost;
broker->post(Message::PlayerManaUpdated, mana_amount);
spawn_spell(spell, position, direction);
}
```
And lastly we need to register to receive messages, which we can do like this:
```cpp
#include "broker.hpp"
game::UI::UI() {
// since we use a class here we need to wrap the on message handler so that we can just handle
// events in a class context
auto fn = [this](MyBroker::WhoPtr _receiver, Message message_type, MessageData message_data) {
return this->on_message(message_type, message_data);
};
// now we register the listeners
broker->add_listener(Message::PlayerHPUpdated, this, fn);
broker->add_listener(Message::PlayerManaUpdated, this, fn);
}
// we need to return if we handled the message or not, this helps figuring out
// if you registered to something without handling it
bool game::UI::on_message(Message message_type, MessageData message_data) {
switch (message_type) {
case Message::PlayerHPUpdated:
set_hp(message_data);
return true;
case Message::PlayerManaUpdated:
set_mana(message_data);
return true;
}
// in thsi case we haven't handled anything so we return false
return false;
}
```
And thats how you use dove.
## Configuration
Dove has a few configuration options exposed via defines.
Since C++ still does not offer proper reflection (and we can't print arbitrary things), you will probably have to implement custom formatters for your Message and MessageData types
### DOVE_DEBUG
Enables dove to print debugging information like messages registered, messages posted, etc.
```cpp
// enable like this before you import dove
#define DOVE_DEBUG
```
### DOVE_DEBUG_PRINT_FUNC
The function called to be when debug prints happen, defaults to std::println
```cpp
#define DOVE_DEBUG_PRINT_FUNC std::println
````
### DOVE_WARN_PRINT_FUNC
The function called to be when warning prints happen, defaults to std::println
```cpp
#define DOVE_WARN_PRINT_FUNC std::println
````
### DOVE_ERR_PRINT_FUNC
The function called to be when error prints happen, defaults to std::println (to stderr)
```cpp
#define DOVE_ERR_PRINT_FUNC std::println
````
### DOVE_ASSERT_FUNC
The function/macro called for asserts, defaults to slightly modified [rapture](https://github.com/atomicptr/rapture) assert
```cpp
#define DOVE_ASSERT(condition, ...) \
if (!(condition)) { \
auto rapture_loc = std::source_location::current(); \
DOVE_ERR_PRINT_FUNC(""); \
DOVE_ERR_PRINT_FUNC( \
"========= ASSERTATION FAILED {}:{}:{} =========", \
rapture_loc.file_name(), \
rapture_loc.line(), \
rapture_loc.column() \
); \
DOVE_ERR_PRINT_FUNC("\tAssert :\t{}", (#condition)); \
DOVE_ERR_PRINT_FUNC("\tMessage:\t{}", std::format(__VA_ARGS__)); \
DOVE_ERR_PRINT_FUNC(""); \
std::abort(); \
}
```
### DOVE_DISABLE_SOURCE_LOCATION_FORMATTER
By default dove registers a std::formatter for std::source_location, but you might not want this (or a custom one) so define this to disable it
### License
BSD 0-Clause