Advertisement
lolo4202137

Untitled

May 26th, 2022
170
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.27 KB | None | 0 0
  1. #ifndef ROBOTS_SERIALIZE_HPP
  2. #define ROBOTS_SERIALIZE_HPP
  3.  
  4. #include <boost/endian/conversion.hpp>
  5. #include <concepts>
  6. #include <cstddef>
  7. #include <iostream>
  8. #include <map>
  9. #include <ranges>
  10. #include <set>
  11. #include <string>
  12. #include <type_traits>
  13. #include <utility>
  14. #include <variant>
  15. #include <vector>
  16.  
  17. #include "robots-reader.hpp"
  18.  
  19. // Integral types used in messages.
  20. using message_id_t = uint8_t;
  21. using player_id_t = uint8_t;
  22. using bomb_id_t = uint32_t;
  23. using score_t = uint32_t;
  24.  
  25. // Position on the board - pair of coordinates.
  26. using Position = std::pair<uint16_t, uint16_t>;
  27. // Bomb description - position and timer.
  28. using Bomb = std::pair<Position, uint16_t>;
  29. // Player description - name and address.
  30. using Player = std::pair<std::string, std::string>;
  31.  
  32. // Game event types:
  33. // BombPlaced event: <id, position>
  34. using BombPlaced = std::tuple<bomb_id_t, Position>;
  35. // BombExploded event: <id, robots_destroyed, blocks_destroyed>
  36. using BombExploded = std::tuple<bomb_id_t, std::vector<player_id_t>, std::vector<Position>>;
  37. // PlayerMoved event: <id, position>
  38. using PlayerMoved = std::tuple<player_id_t, Position>;
  39. // BlockPlaced event: <position>
  40. using BlockPlaced = Position;
  41.  
  42. // Game event type.
  43. using Event = std::variant<BombPlaced, BombExploded, PlayerMoved, BlockPlaced>;
  44.  
  45. // Server information.
  46. struct ServerInfo {
  47.     std::string server_name;
  48.     uint16_t size_x;
  49.     uint16_t size_y;
  50.     uint16_t game_length;
  51. };
  52.  
  53. // Players information.
  54. struct PlayersInfo {
  55.     std::map<player_id_t, Player> players;
  56. };
  57.  
  58. // Lobby specific information.
  59. struct LobbyInfo {
  60.     uint8_t players_count;
  61.     uint16_t explosion_radius;
  62.     uint16_t bomb_timer;
  63. };
  64.  
  65. // In-game specific information.
  66. struct GameInfo {
  67.     uint16_t turn;
  68.     std::map<player_id_t, Position> player_positions;
  69.     std::set<Position> blocks;
  70.     std::vector<Bomb> bombs;
  71.     std::set<Position> explosions;
  72.     std::map<player_id_t, score_t> scores;
  73. };
  74.  
  75. // Server to client message types:
  76. // Hello message:
  77. // <server name, players count, size x, size y, game length, explosion radius, bomb timer>.
  78. struct Hello : ServerInfo, LobbyInfo {};
  79. // AcceptedPlayer message: <id, player>.
  80. using AcceptedPlayer = std::tuple<player_id_t, Player>;
  81. // GameStarted message: <players>.
  82. using GameStarted = std::map<player_id_t, Player>;
  83. // Turn message: <turn, events>.
  84. using Turn = std::tuple<uint16_t, std::vector<Event>>;
  85. // GameEnded message: <scores>.
  86. using GameEnded = std::map<player_id_t, score_t>;
  87.  
  88. // Server to client message type.
  89. using ServerMessage = std::variant<Hello, AcceptedPlayer, GameStarted, Turn, GameEnded>;
  90.  
  91. // Direction message types.
  92. using Up = struct {};
  93. using Right = struct {};
  94. using Down = struct {};
  95. using Left = struct {};
  96. using Direction = std::variant<Up, Right, Down, Left>;
  97.  
  98. // Movement message types.
  99. using Join = std::string;
  100. using PlaceBomb = struct {};
  101. using PlaceBlock = struct {};
  102. using Move = Direction;
  103.  
  104. // Client to server message type.
  105. using ClientMessage = std::variant<Join, PlaceBomb, PlaceBlock, Move>;
  106.  
  107. // GUI to client message type.
  108. using InputMessage = std::variant<PlaceBomb, PlaceBlock, Move>;
  109.  
  110. // Game state used by client to keep track on the game.
  111. struct GameState : ServerInfo, PlayersInfo, LobbyInfo, GameInfo {
  112.     bool in_game = false;
  113.     std::map<bomb_id_t, Bomb> bomb_map;
  114.     std::set<player_id_t> robots_killed;
  115.     std::set<Position> previous_blocks;
  116. };
  117.  
  118. // Client to GUI message types:
  119. // Client to GUI message for lobby state.
  120. struct Lobby : ServerInfo, PlayersInfo, LobbyInfo {
  121.     Lobby() = default;
  122.     explicit Lobby(const GameState &g) : ServerInfo(g), PlayersInfo(g), LobbyInfo(g) {}
  123. };
  124. // Client to GUI message for game state.
  125. struct Game : ServerInfo, PlayersInfo, GameInfo {
  126.     Game() = default;
  127.     explicit Game(const GameState &g) : ServerInfo(g), PlayersInfo(g), GameInfo(g) {}
  128. };
  129.  
  130. //Client to GUI message type.
  131. using DrawMessage = std::variant<Lobby, Game>;
  132.  
  133. ClientMessage client_from_gui_message(const InputMessage &m) {
  134.     return std::visit([]<typename T>(const T &a) { return ClientMessage{a}; }, m);
  135. }
  136.  
  137. template<typename T>
  138. using range_size_t =
  139.         typename std::conditional<std::is_same_v<T, std::string>, uint8_t, uint32_t>::type;
  140.  
  141. // Check if a type is same as std::pair of any types.
  142. template<typename T>
  143. concept is_pair = requires(T a) {
  144.     { a.first } -> std::same_as<typename T::first_type &>;
  145.     { a.second } -> std::same_as<typename T::second_type &>;
  146. };
  147.  
  148. // Check if type has all fields of Game structure.
  149. template<typename T>
  150. concept is_lobby_transformable = requires(T a) {
  151.     a.server_name;
  152.     a.players_count;
  153.     a.size_x;
  154.     a.size_y;
  155.     a.game_length;
  156.     a.explosion_radius;
  157.     a.bomb_timer;
  158.     a.players;
  159. };
  160.  
  161. // Check if type has all fields of Game structure.
  162. template<typename T>
  163. concept is_game_transformable = requires(T a) {
  164.     a.server_name;
  165.     a.size_x;
  166.     a.size_y;
  167.     a.game_length;
  168.     a.turn;
  169.     a.players;
  170.     a.player_positions;
  171.     a.blocks;
  172.     a.bombs;
  173.     a.explosions;
  174.     a.scores;
  175. };
  176.  
  177.  
  178. // Serializer class.
  179. // Implements host to network serialization for a number of data types which may be members of
  180. // client messages.
  181. class Serializer {
  182.     // data always saved in serialized way
  183.     std::vector<uint8_t> data;
  184.  
  185. public:
  186.     // Util function.
  187.     void clear() { data = std::vector<uint8_t>{}; }
  188.  
  189.     // Util function.
  190.     void print() const {
  191.         for (auto byte: data) printf("%u,", byte);
  192.         std::cout << std::endl;
  193.     }
  194.  
  195.     // Extract data from serializer. Clear.
  196.     Serializer &operator>>(std::vector<uint8_t> &v) {
  197.         v = std::move(data);
  198.         clear();
  199.  
  200.         return *this;
  201.     }
  202.  
  203.     std::vector<uint8_t> get() {
  204.         std::vector<uint8_t> result{std::move(data)};
  205.         clear();
  206.  
  207.         return result;
  208.     }
  209.  
  210.     // Serialize an integral type.
  211.     // Convert the argument to network order if needed, append it to the data vector byte by byte.
  212.     template<std::integral T>
  213.     void serialize(T i) requires(!std::is_enum_v<T>) {
  214.         boost::endian::native_to_big_inplace(i);
  215.  
  216.         uint8_t bytes[sizeof(T)];
  217.         *reinterpret_cast<T *>(bytes) = i;
  218.  
  219.         for (auto byte: bytes) data.push_back(byte);
  220.     }
  221.  
  222.     // Serialize a sized range.
  223.     // Serialize its size as 4 byte unsigned (1 byte for string) and then serialize all its
  224.     // elements one after another.
  225.     template<std::ranges::sized_range T>
  226.     void serialize(const T &r) {
  227.         serialize(static_cast<range_size_t<T>>(std::ranges::size(r)));
  228.  
  229.         for (const auto &e: r) *this << e;
  230.     }
  231.  
  232.     // Serialize a pair.
  233.     // Serialize it as a tuple.
  234.     template<typename... Ts>
  235.     void serialize(const std::pair<Ts...> &p) {
  236.         *this << p.first << p.second;
  237.     }
  238.  
  239.     // Serialize a tuple.
  240.     // Serialize its elements one after another.
  241.     template<typename... Ts>
  242.     void serialize(const std::tuple<Ts...> &t) {
  243.         std::apply([this](const auto &...i) { ((*this << i), ...); }, t);
  244.     }
  245.  
  246.     // Serialize a variant.
  247.     // Serialize the held alternative's index as 1 byte unsigned and then serialize the underlying
  248.     // data type.
  249.     template<typename... Ts>
  250.     void serialize(const std::variant<Ts...> &v) {
  251.         auto index = static_cast<message_id_t>(v.index());
  252.         serialize(index);
  253.  
  254.         std::visit([this](const auto &i) { *this << i; }, v);
  255.     }
  256.  
  257.     // Serialize a Lobby structure.
  258.     // Serialize it in order given by GUI interface.
  259.     void serialize(const Lobby &l) {
  260.         *this << l.server_name << l.players_count << l.size_x << l.size_y << l.game_length
  261.               << l.explosion_radius << l.bomb_timer << l.players;
  262.     }
  263.  
  264.     // Serialize a Game structure.
  265.     // Serialize it in order given by GUI interface.
  266.     void serialize(const Game &g) {
  267.         *this << g.server_name << g.size_x << g.size_y << g.game_length << g.turn << g.players
  268.               << g.player_positions << g.blocks << g.bombs << g.explosions << g.scores;
  269.     }
  270.  
  271.     // Serialize a GameState as DrawMessage of Game or Lobby type according to in_game value.
  272.     void serialize(const GameState &g) {
  273.         if (g.in_game) *this << DrawMessage{Game{g}};
  274.         else
  275.             *this << DrawMessage{Lobby{g}};
  276.     }
  277.  
  278.     // Serialize an empty structure.
  279.     // Empty structures are used to automate the serialization by using std::variants, they are not
  280.     // converted to any bytes.
  281.     template<typename T>
  282.     requires(std::is_empty_v<T>) void serialize(const T &) {}
  283.  
  284.     // Insertion operator.
  285.     // Serialize the argument, return this.
  286.     template<typename T>
  287.     Serializer &operator<<(T i) {
  288.         serialize(i);
  289.  
  290.         return *this;
  291.     }
  292. };
  293.  
  294.  
  295. class DeserializationError : public std::runtime_error {
  296.     static const char *deserialization_error_message;
  297.  
  298. public:
  299.     explicit DeserializationError(const std::string &message)
  300.         : std::runtime_error(deserialization_error_message + message) {}
  301. };
  302.  
  303. const char *DeserializationError::deserialization_error_message{"Deserialization error: "};
  304.  
  305.  
  306. template<Reader R>
  307. class Deserializer {
  308.     R &reader;
  309.  
  310.     // Utility template used for generalizing std::ranges::range deserialization.
  311.     // If T is same type as std::pair, remove const qualifier form types inside this pair.
  312.     template<bool is_pair, typename T>
  313.     struct remove_cv_pair {
  314.         using type = std::pair<std::remove_cv_t<typename T::first_type>,
  315.                                std::remove_cv_t<typename T::second_type>>;
  316.     };
  317.     // If T is not std::pair type, don't change anything.
  318.     template<typename T>
  319.     struct remove_cv_pair<false, T> {
  320.         using type = T;
  321.     };
  322.  
  323.     // Construct any variant from given index.
  324.     // Idea for function from:
  325.     // https://stackoverflow.com/questions/60564132/default-constructing-an-stdvariant-from-index
  326.     template<typename T, std::size_t I = 0>
  327.     T variant_from_index(std::size_t index) {
  328.         if constexpr (I >= std::variant_size_v<T>)
  329.             throw ::std::runtime_error{"Variant index out of bounds"};
  330.         else
  331.             return index == 0 ? T{std::in_place_index<I>} : variant_from_index<T, I + 1>(index - 1);
  332.     }
  333.  
  334. public:
  335.     explicit Deserializer(R &r) : reader{r} {}
  336.  
  337.     // Deserialize an integral type.
  338.     // Convert the argument from network order to host order if needed, append it to the data
  339.     // vector byte by byte.
  340.     template<std::integral T>
  341.     void deserialize(T &i) requires(!std::is_enum_v<T>) {
  342.         try {
  343.             std::vector<uint8_t> bytes = reader.read(sizeof(T));
  344.  
  345.             i = boost::endian::big_to_native(*reinterpret_cast<T *>(bytes.data()));
  346.         } catch (std::exception &e) { throw DeserializationError{e.what()}; }
  347.     }
  348.  
  349.     // Deserialize a pair.
  350.     // Deserialize it as a tuple.
  351.     template<typename T1, typename T2>
  352.     void deserialize(std::pair<T1, T2> &p) {
  353.         *this >> p.first >> p.second;
  354.     }
  355.  
  356.     // Deserialize a tuple.
  357.     // Deserialize its elements one after another.
  358.     template<typename... Ts>
  359.     void deserialize(std::tuple<Ts...> &t) {
  360.         std::apply([this](auto &...i) { ((*this >> i), ...); }, t);
  361.     }
  362.  
  363.     // Deserialize a sized range.
  364.     // Deserialize its size as 4 byte unsigned (1 byte for string) and then deserialize all its
  365.     // elements one after another.
  366.     template<std::ranges::range T>
  367.     void deserialize(T &r) {
  368.         range_size_t<T> size;
  369.         using value_t = std::remove_cvref_t<typename T::iterator::value_type>;
  370.         using E = typename remove_cv_pair<is_pair<value_t>, value_t>::type;
  371.  
  372.         deserialize(size);
  373.         for (; size > 0; size--) {
  374.             E element;
  375.             *this >> element;
  376.             r.insert(r.end(), element);
  377.         }
  378.     }
  379.  
  380.     // Deserialize a variant.
  381.     // Deserialize the held alternative's index as 1 byte unsigned and then deserialize the
  382.     // underlying data type.
  383.     template<typename... Ts>
  384.     void deserialize(std::variant<Ts...> &v) {
  385.         message_id_t index;
  386.  
  387.         std::cout << "Trying to deserialize variant: \n";
  388.  
  389.         deserialize(index);
  390.  
  391.         std::cout << "Deserializing variant of ID: " << int(index) << std::endl;
  392.  
  393.         try {
  394.             v = variant_from_index<std::variant<Ts...>>(index);
  395.         } catch (std::exception &e) { throw DeserializationError("Unknown message id."); }
  396.  
  397.         std::visit([this](auto &i) { *this >> i; }, v);
  398.  
  399.         if constexpr (std::is_same_v<std::variant<Ts...>, InputMessage>) {
  400.             if (!reader.empty()) { throw DeserializationError("Trailing bytes in UDP message!"); }
  401.         }
  402.     }
  403.  
  404.     // Deserialize an empty structure.
  405.     // Empty structures are used to automate the serialization by using variants, they are not
  406.     // converted to any bytes.
  407.     template<typename T>
  408.     void deserialize(T &) requires(std::is_empty_v<T>) {}
  409.  
  410.     // Deserialize Hello structure.
  411.     void deserialize(Hello &h) {
  412.         *this >> h.server_name >> h.players_count >> h.size_x >> h.size_y >> h.game_length >>
  413.                 h.explosion_radius >> h.bomb_timer;
  414.     }
  415.  
  416.  
  417.     // Extraction operator.
  418.     // Deserialize the argument, return this.
  419.     template<typename T>
  420.     Deserializer &operator>>(T &i) {
  421.         deserialize(i);
  422.  
  423.         return *this;
  424.     }
  425. };
  426.  
  427. #endif//ROBOTS_SERIALIZE_HPP
  428.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement