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