7.2.5. FlatBuffers: Monster#
FlatBuffers “is an efficient cross platform serialization library for C++, C#, C, Go, Java, Kotlin, JavaScript, Lobster, Lua, TypeScript, PHP, Python, Rust and Swift. It was originally created at Google for game development and other performance-critical applications.” eCAL has implemented flatbuffer serialization on top of the binary publisher and subscriber API, such that users can conveniently send flatbuffer objects. Currently, the eCAL FlatBuffers Message API is only available for C++.
Let’s start with a simple example of sending a monster object using FlatBuffers.
7.2.5.1. Monster FlatBuffers#
As the sender and receiver need the same .fbs
files, we place them in a separate directory next to the source directories for the sender and the receiver.
Let’s start with the monster/monster.fbs
file!
1namespace Game.Sample;
2
3enum Color:byte { Red = 0, Green, Blue = 2 }
4
5union Any { Monster } // add more elements..
6
7struct Vec3
8{
9 x:float;
10 y:float;
11 z:float;
12}
13
14table Monster
15{
16 pos:Vec3;
17 mana:short = 150;
18 hp:short = 100;
19 name:string;
20 friendly:bool = false (deprecated);
21 inventory:[ubyte];
22 color:Color = Blue;
23}
24
25root_type Monster;
Monster FlatBuffers File
└─ monster.fbs
7.2.5.2. Monster Publisher#
1#include <ecal/ecal.h>
2#include <ecal/msg/flatbuffers/publisher.h>
3
4#include <iostream>
5#include <chrono>
6#include <thread>
7
8// flatbuffers includes
9#include <flatbuffers/flatbuffers.h>
10
11// flatbuffers generated includes
12#include <monster/monster_generated.h>
13
14
15int main(int /*argc*/, char **/*argv*/)
16{
17 // initialize eCAL API
18 eCAL::Initialize("monster_send");
19
20 // set process state
21 eCAL::Process::SetState(eCAL::Process::eSeverity::healthy, eCAL::Process::eSeverityLevel::level1, "I feel good !");
22
23 // create a publisher (topic name "monster")
24 eCAL::flatbuffers::CObjectPublisher<Game::Sample::MonsterT> pub("monster");
25
26 Game::Sample::MonsterT my_monster;
27 my_monster.name = "Monster";
28 my_monster.pos = std::make_unique<Game::Sample::Vec3>( 1.0f, 2.0f, 3.0f );
29
30 // enter main loop
31 uint8_t cnt = 0;
32 while(eCAL::Ok())
33 {
34 if (cnt == 0)
35 {
36 my_monster.inventory.clear();
37 }
38 my_monster.inventory.push_back(cnt);
39 ++cnt;
40
41 // send the monster object
42 pub.Send(my_monster);
43
44 // print content
45 std::cout << "monster pos x : " << my_monster.pos->x() << std::endl;
46 std::cout << "monster pos y : " << my_monster.pos->y() << std::endl;
47 std::cout << "monster pos z : " << my_monster.pos->z() << std::endl;
48 std::cout << "monster mana : " << my_monster.mana << std::endl;
49 std::cout << "monster hp : " << my_monster.hp << std::endl;
50 std::cout << "monster name : " << my_monster.name << std::endl;
51
52 std::cout << "monster inventory : ";
53 for(auto inventory : my_monster.inventory)
54 {
55 std::cout << (int)inventory << " ";
56 }
57 std::cout << std::endl;
58
59 std::cout << "monster color : ";
60 switch (my_monster.color)
61 {
62 case Game::Sample::Color_Red:
63 std::cout << "Red";
64 break;
65 case Game::Sample::Color_Green:
66 std::cout << "Green";
67 break;
68 case Game::Sample::Color_Blue:
69 std::cout << "Blue";
70 break;
71 }
72 std::cout << std::endl;
73
74 std::cout << std::endl;
75
76 // sleep 500 ms
77 std::this_thread::sleep_for(std::chrono::milliseconds(500));
78 }
79
80 // finalize eCAL API
81 eCAL::Finalize();
82}
└─ C++
└─ monster_send.cpp
7.2.5.3. Monster Subscriber#
1#include <ecal/ecal.h>
2#include <ecal/msg/flatbuffers/subscriber.h>
3
4#include <iostream>
5#include <chrono>
6#include <thread>
7#include <mutex>
8
9// flatbuffers includes
10#include <flatbuffers/flatbuffers.h>
11
12// flatbuffers generated includes
13#include <monster/monster_generated.h>
14
15
16void OnFlatMonster(const eCAL::STopicId& topic_id_, const Game::Sample::Monster* const & monster_, long long time_, long long /*clock_*/)
17{
18 // print content
19 std::cout << "topic name : " << topic_id_.topic_name << std::endl;
20 std::cout << "time : " << time_ << std::endl;
21 std::cout << std::endl;
22 std::cout << "monster pos x : " << monster_->pos()->x() << std::endl;
23 std::cout << "monster pos y : " << monster_->pos()->y() << std::endl;
24 std::cout << "monster pos z : " << monster_->pos()->z() << std::endl;
25 std::cout << "monster mana : " << monster_->mana() << std::endl;
26 std::cout << "monster hp : " << monster_->hp() << std::endl;
27 std::cout << "monster name : " << monster_->name()->c_str() << std::endl;
28
29 std::cout << "monster inventory : ";
30 for(auto iter = monster_->inventory()->begin(); iter != monster_->inventory()->end(); ++iter)
31 {
32 std::cout << static_cast<int>(*iter) << " ";
33 }
34 std::cout << std::endl;
35
36 std::cout << "monster color : ";
37 switch (monster_->color())
38 {
39 case Game::Sample::Color_Red:
40 std::cout << "Red";
41 break;
42 case Game::Sample::Color_Green:
43 std::cout << "Green";
44 break;
45 case Game::Sample::Color_Blue:
46 std::cout << "Blue";
47 break;
48 }
49 std::cout << std::endl;
50
51 std::cout << std::endl;
52}
53
54void OnObjectMonster(const eCAL::STopicId& topic_id_, const Game::Sample::MonsterT* const& monster_, long long time_, long long /*clock_*/)
55{
56 // print content
57 std::cout << "topic name : " << topic_id_.topic_name << std::endl;
58 std::cout << "time : " << time_ << std::endl;
59 std::cout << std::endl;
60 std::cout << "monster pos x : " << monster_->pos->x() << std::endl;
61 std::cout << "monster pos y : " << monster_->pos->y() << std::endl;
62 std::cout << "monster pos z : " << monster_->pos->z() << std::endl;
63 std::cout << "monster mana : " << monster_->mana << std::endl;
64 std::cout << "monster hp : " << monster_->hp << std::endl;
65 std::cout << "monster name : " << monster_->name << std::endl;
66
67 std::cout << "monster inventory : ";
68 for (const auto& inventory : monster_->inventory)
69 {
70 std::cout << (int)inventory << " ";
71 }
72 std::cout << std::endl;
73
74 std::cout << "monster color : ";
75 switch (monster_->color)
76 {
77 case Game::Sample::Color_Red:
78 std::cout << "Red";
79 break;
80 case Game::Sample::Color_Green:
81 std::cout << "Green";
82 break;
83 case Game::Sample::Color_Blue:
84 std::cout << "Blue";
85 break;
86 }
87 std::cout << std::endl;
88
89 std::cout << std::endl;
90}
91
92
93int main(int /*argc*/, char** /*argv*/)
94{
95 // initialize eCAL API
96 eCAL::Initialize("monster_receive");
97
98 // set process state
99 eCAL::Process::SetState(eCAL::Process::eSeverity::healthy, eCAL::Process::eSeverityLevel::level1, "I feel good !");
100
101 // There are two different types of Flatbuffer subscribers
102 // One type is a "flat" type, e.g. it uses the API which is backed by raw memory
103 // The other type is an "object" type, where the type is directly mapped to e.g. std::string / std::vector ... types.
104
105 eCAL::flatbuffers::CFlatSubscriber<Game::Sample::Monster> flat_subscriber("monster");
106 flat_subscriber.SetReceiveCallback(OnFlatMonster);
107
108 eCAL::flatbuffers::CObjectSubscriber<Game::Sample::MonsterT> object_subscriber("monster");
109 object_subscriber.SetReceiveCallback(OnObjectMonster);
110
111 while(eCAL::Ok())
112 {
113 // sleep 100 ms
114 std::this_thread::sleep_for(std::chrono::milliseconds(100));
115 }
116
117 // finalize eCAL API
118 eCAL::Finalize();
119}
└─ C++
└─ monster_receive.cpp