diff options
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/core.cpp | 5 | ||||
| -rw-r--r-- | src/core/loader/ncch.cpp | 8 | ||||
| -rw-r--r-- | src/network/packet.cpp | 38 | ||||
| -rw-r--r-- | src/network/packet.h | 4 | ||||
| -rw-r--r-- | src/network/room.cpp | 84 | ||||
| -rw-r--r-- | src/network/room.h | 19 | ||||
| -rw-r--r-- | src/network/room_member.cpp | 128 | ||||
| -rw-r--r-- | src/network/room_member.h | 59 | 
9 files changed, 310 insertions, 37 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 360f407f3..0a6f97e4b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -388,7 +388,7 @@ set(HEADERS  create_directory_groups(${SRCS} ${HEADERS})  add_library(core STATIC ${SRCS} ${HEADERS}) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)  target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)  if (ENABLE_WEB_SERVICE)      target_link_libraries(core PUBLIC json-headers web_service) diff --git a/src/core/core.cpp b/src/core/core.cpp index d08f18623..5332318cf 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -19,6 +19,7 @@  #include "core/loader/loader.h"  #include "core/memory_setup.h"  #include "core/settings.h" +#include "network/network.h"  #include "video_core/video_core.h"  namespace Core { @@ -188,6 +189,10 @@ void System::Shutdown() {      cpu_core = nullptr;      app_loader = nullptr;      telemetry_session = nullptr; +    if (auto room_member = Network::GetRoomMember().lock()) { +        Network::GameInfo game_info{}; +        room_member->SendGameInfo(game_info); +    }      LOG_DEBUG(Core, "Shutdown OK");  } diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index c007069a9..7aff7f29b 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -20,6 +20,7 @@  #include "core/loader/ncch.h"  #include "core/loader/smdh.h"  #include "core/memory.h" +#include "network/network.h"  ////////////////////////////////////////////////////////////////////////////////////////////////////  // Loader namespace @@ -350,6 +351,13 @@ ResultStatus AppLoader_NCCH::Load() {      Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); +    if (auto room_member = Network::GetRoomMember().lock()) { +        Network::GameInfo game_info; +        ReadTitle(game_info.name); +        game_info.id = ncch_header.program_id; +        room_member->SendGameInfo(game_info); +    } +      is_loaded = true; // Set state to loaded      result = LoadExec(); // Load the executable into memory for booting diff --git a/src/network/packet.cpp b/src/network/packet.cpp index 660e92c0d..cc60f2fbc 100644 --- a/src/network/packet.cpp +++ b/src/network/packet.cpp @@ -13,6 +13,18 @@  namespace Network { +#ifndef htonll +u64 htonll(u64 x) { +    return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32)); +} +#endif + +#ifndef ntohll +u64 ntohll(u64 x) { +    return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32)); +} +#endif +  void Packet::Append(const void* in_data, std::size_t size_in_bytes) {      if (in_data && (size_in_bytes > 0)) {          std::size_t start = data.size(); @@ -100,6 +112,20 @@ Packet& Packet::operator>>(u32& out_data) {      return *this;  } +Packet& Packet::operator>>(s64& out_data) { +    s64 value; +    Read(&value, sizeof(value)); +    out_data = ntohll(value); +    return *this; +} + +Packet& Packet::operator>>(u64& out_data) { +    u64 value; +    Read(&value, sizeof(value)); +    out_data = ntohll(value); +    return *this; +} +  Packet& Packet::operator>>(float& out_data) {      Read(&out_data, sizeof(out_data));      return *this; @@ -183,6 +209,18 @@ Packet& Packet::operator<<(u32 in_data) {      return *this;  } +Packet& Packet::operator<<(s64 in_data) { +    s64 toWrite = htonll(in_data); +    Append(&toWrite, sizeof(toWrite)); +    return *this; +} + +Packet& Packet::operator<<(u64 in_data) { +    u64 toWrite = htonll(in_data); +    Append(&toWrite, sizeof(toWrite)); +    return *this; +} +  Packet& Packet::operator<<(float in_data) {      Append(&in_data, sizeof(in_data));      return *this; diff --git a/src/network/packet.h b/src/network/packet.h index 94b351ab1..5a2e58dc2 100644 --- a/src/network/packet.h +++ b/src/network/packet.h @@ -72,6 +72,8 @@ public:      Packet& operator>>(u16& out_data);      Packet& operator>>(s32& out_data);      Packet& operator>>(u32& out_data); +    Packet& operator>>(s64& out_data); +    Packet& operator>>(u64& out_data);      Packet& operator>>(float& out_data);      Packet& operator>>(double& out_data);      Packet& operator>>(char* out_data); @@ -89,6 +91,8 @@ public:      Packet& operator<<(u16 in_data);      Packet& operator<<(s32 in_data);      Packet& operator<<(u32 in_data); +    Packet& operator<<(s64 in_data); +    Packet& operator<<(u64 in_data);      Packet& operator<<(float in_data);      Packet& operator<<(double in_data);      Packet& operator<<(const char* in_data); diff --git a/src/network/room.cpp b/src/network/room.cpp index fbbaf8b93..261049ab0 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -4,9 +4,9 @@  #include <algorithm>  #include <atomic> +#include <mutex>  #include <random>  #include <thread> -#include <vector>  #include "enet/enet.h"  #include "network/packet.h"  #include "network/room.h" @@ -29,12 +29,14 @@ public:      struct Member {          std::string nickname;   ///< The nickname of the member. -        std::string game_name;  ///< The current game of the member +        GameInfo game_info;     ///< The current game of the member          MacAddress mac_address; ///< The assigned mac address of the member.          ENetPeer* peer;         ///< The remote peer.      };      using MemberList = std::vector<Member>; -    MemberList members; ///< Information about the members of this room. +    MemberList members;              ///< Information about the members of this room +    mutable std::mutex member_mutex; ///< Mutex for locking the members list +    /// This should be a std::shared_mutex as soon as C++17 is supported      RoomImpl()          : random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {} @@ -147,7 +149,7 @@ void Room::RoomImpl::ServerLoop() {                  case IdJoinRequest:                      HandleJoinRequest(&event);                      break; -                case IdSetGameName: +                case IdSetGameInfo:                      HandleGameNamePacket(&event);                      break;                  case IdWifiPacket: @@ -213,7 +215,10 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {      member.nickname = nickname;      member.peer = event->peer; -    members.push_back(std::move(member)); +    { +        std::lock_guard<std::mutex> lock(member_mutex); +        members.push_back(std::move(member)); +    }      // Notify everyone that the room information has changed.      BroadcastRoomInformation(); @@ -223,12 +228,14 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {  bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {      // A nickname is valid if it is not already taken by anybody else in the room.      // TODO(B3N30): Check for empty names, spaces, etc. +    std::lock_guard<std::mutex> lock(member_mutex);      return std::all_of(members.begin(), members.end(),                         [&nickname](const auto& member) { return member.nickname != nickname; });  }  bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const {      // A MAC address is valid if it is not already taken by anybody else in the room. +    std::lock_guard<std::mutex> lock(member_mutex);      return std::all_of(members.begin(), members.end(),                         [&address](const auto& member) { return member.mac_address != address; });  } @@ -279,6 +286,7 @@ void Room::RoomImpl::SendCloseMessage() {      packet << static_cast<u8>(IdCloseRoom);      ENetPacket* enet_packet =          enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); +    std::lock_guard<std::mutex> lock(member_mutex);      for (auto& member : members) {          enet_peer_send(member.peer, 0, enet_packet);      } @@ -295,10 +303,14 @@ void Room::RoomImpl::BroadcastRoomInformation() {      packet << room_information.member_slots;      packet << static_cast<u32>(members.size()); -    for (const auto& member : members) { -        packet << member.nickname; -        packet << member.mac_address; -        packet << member.game_name; +    { +        std::lock_guard<std::mutex> lock(member_mutex); +        for (const auto& member : members) { +            packet << member.nickname; +            packet << member.mac_address; +            packet << member.game_info.name; +            packet << member.game_info.id; +        }      }      ENetPacket* enet_packet = @@ -335,11 +347,13 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {                                                   ENET_PACKET_FLAG_RELIABLE);      if (destination_address == BroadcastMac) { // Send the data to everyone except the sender +        std::lock_guard<std::mutex> lock(member_mutex);          for (const auto& member : members) {              if (member.peer != event->peer)                  enet_peer_send(member.peer, 0, enet_packet);          }      } else { // Send the data only to the destination client +        std::lock_guard<std::mutex> lock(member_mutex);          auto member = std::find_if(members.begin(), members.end(),                                     [destination_address](const Member& member) -> bool {                                         return member.mac_address == destination_address; @@ -361,6 +375,8 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {      auto CompareNetworkAddress = [event](const Member member) -> bool {          return member.peer == event->peer;      }; + +    std::lock_guard<std::mutex> lock(member_mutex);      const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress);      if (sending_member == members.end()) {          return; // Received a chat message from a unknown sender @@ -385,22 +401,32 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {      in_packet.Append(event->packet->data, event->packet->dataLength);      in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type -    std::string game_name; -    in_packet >> game_name; -    auto member = -        std::find_if(members.begin(), members.end(), -                     [event](const Member& member) -> bool { return member.peer == event->peer; }); -    if (member != members.end()) { -        member->game_name = game_name; -        BroadcastRoomInformation(); +    GameInfo game_info; +    in_packet >> game_info.name; +    in_packet >> game_info.id; + +    { +        std::lock_guard<std::mutex> lock(member_mutex); +        auto member = +            std::find_if(members.begin(), members.end(), [event](const Member& member) -> bool { +                return member.peer == event->peer; +            }); +        if (member != members.end()) { +            member->game_info = game_info; +        }      } +    BroadcastRoomInformation();  }  void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {      // Remove the client from the members list. -    members.erase(std::remove_if(members.begin(), members.end(), -                                 [client](const Member& member) { return member.peer == client; }), -                  members.end()); +    { +        std::lock_guard<std::mutex> lock(member_mutex); +        members.erase( +            std::remove_if(members.begin(), members.end(), +                           [client](const Member& member) { return member.peer == client; }), +            members.end()); +    }      // Announce the change to all clients.      enet_peer_disconnect(client, 0); @@ -437,6 +463,19 @@ const RoomInformation& Room::GetRoomInformation() const {      return room_impl->room_information;  } +std::vector<Room::Member> Room::GetRoomMemberList() const { +    std::vector<Room::Member> member_list; +    std::lock_guard<std::mutex> lock(room_impl->member_mutex); +    for (const auto& member_impl : room_impl->members) { +        Member member; +        member.nickname = member_impl.nickname; +        member.mac_address = member_impl.mac_address; +        member.game_info = member_impl.game_info; +        member_list.push_back(member); +    } +    return member_list; +}; +  void Room::Destroy() {      room_impl->state = State::Closed;      room_impl->room_thread->join(); @@ -447,7 +486,10 @@ void Room::Destroy() {      }      room_impl->room_information = {};      room_impl->server = nullptr; -    room_impl->members.clear(); +    { +        std::lock_guard<std::mutex> lock(room_impl->member_mutex); +        room_impl->members.clear(); +    }      room_impl->room_information.member_slots = 0;      room_impl->room_information.name.clear();  } diff --git a/src/network/room.h b/src/network/room.h index 65b0d008a..8285a4d0c 100644 --- a/src/network/room.h +++ b/src/network/room.h @@ -7,6 +7,7 @@  #include <array>  #include <memory>  #include <string> +#include <vector>  #include "common/common_types.h"  namespace Network { @@ -21,6 +22,11 @@ struct RoomInformation {      u32 member_slots; ///< Maximum number of members in this room  }; +struct GameInfo { +    std::string name{""}; +    u64 id{0}; +}; +  using MacAddress = std::array<u8, 6>;  /// A special MAC address that tells the room we're joining to assign us a MAC address  /// automatically. @@ -34,7 +40,7 @@ enum RoomMessageTypes : u8 {      IdJoinRequest = 1,      IdJoinSuccess,      IdRoomInformation, -    IdSetGameName, +    IdSetGameInfo,      IdWifiPacket,      IdChatMessage,      IdNameCollision, @@ -51,6 +57,12 @@ public:          Closed, ///< The room is not opened and can not accept connections.      }; +    struct Member { +        std::string nickname;   ///< The nickname of the member. +        GameInfo game_info;     ///< The current game of the member +        MacAddress mac_address; ///< The assigned mac address of the member. +    }; +      Room();      ~Room(); @@ -65,6 +77,11 @@ public:      const RoomInformation& GetRoomInformation() const;      /** +     * Gets a list of the mbmers connected to the room. +     */ +    std::vector<Member> GetRoomMemberList() const; + +    /**       * Creates the socket for this room. Will bind to default address if       * server is empty string.       */ diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index dac9bacae..f229ec6fd 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -5,6 +5,7 @@  #include <atomic>  #include <list>  #include <mutex> +#include <set>  #include <thread>  #include "common/assert.h"  #include "enet/enet.h" @@ -25,6 +26,9 @@ public:      /// Information about the room we're connected to.      RoomInformation room_information; +    /// The current game name, id and version +    GameInfo current_game_info; +      std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.      void SetState(const State new_state);      bool IsConnected() const; @@ -37,6 +41,24 @@ public:      std::unique_ptr<std::thread> loop_thread;      std::mutex send_list_mutex;  ///< Mutex that controls access to the `send_list` variable.      std::list<Packet> send_list; ///< A list that stores all packets to send the async + +    template <typename T> +    using CallbackSet = std::set<CallbackHandle<T>>; +    std::mutex callback_mutex; ///< The mutex used for handling callbacks + +    class Callbacks { +    public: +        template <typename T> +        CallbackSet<T>& Get(); + +    private: +        CallbackSet<WifiPacket> callback_set_wifi_packet; +        CallbackSet<ChatEntry> callback_set_chat_messages; +        CallbackSet<RoomInformation> callback_set_room_information; +        CallbackSet<State> callback_set_state; +    }; +    Callbacks callbacks; ///< All CallbackSets to all events +      void MemberLoop();      void StartLoop(); @@ -84,12 +106,20 @@ public:       * Disconnects the RoomMember from the Room       */      void Disconnect(); + +    template <typename T> +    void Invoke(const T& data); + +    template <typename T> +    CallbackHandle<T> Bind(std::function<void(const T&)> callback);  };  // RoomMemberImpl  void RoomMember::RoomMemberImpl::SetState(const State new_state) { -    state = new_state; -    // TODO(B3N30): Invoke the callback functions +    if (state != new_state) { +        state = new_state; +        Invoke<State>(state); +    }  }  bool RoomMember::RoomMemberImpl::IsConnected() const { @@ -195,9 +225,10 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev      for (auto& member : member_information) {          packet >> member.nickname;          packet >> member.mac_address; -        packet >> member.game_name; +        packet >> member.game_info.name; +        packet >> member.game_info.id;      } -    // TODO(B3N30): Invoke callbacks +    Invoke(room_information);  }  void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { @@ -209,7 +240,7 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {      // Parse the MAC Address from the packet      packet >> mac_address; -    // TODO(B3N30): Invoke callbacks +    SetState(State::Joined);  }  void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { @@ -235,7 +266,7 @@ void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {      packet >> wifi_packet.data; -    // TODO(B3N30): Invoke callbacks +    Invoke<WifiPacket>(wifi_packet);  }  void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { @@ -248,7 +279,7 @@ void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {      ChatEntry chat_entry{};      packet >> chat_entry.nickname;      packet >> chat_entry.message; -    // TODO(B3N30): Invoke callbacks +    Invoke<ChatEntry>(chat_entry);  }  void RoomMember::RoomMemberImpl::Disconnect() { @@ -276,6 +307,46 @@ void RoomMember::RoomMemberImpl::Disconnect() {      server = nullptr;  } +template <> +RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_wifi_packet; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>& +RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_state; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<RoomInformation>& +RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_room_information; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<ChatEntry>& RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_chat_messages; +} + +template <typename T> +void RoomMember::RoomMemberImpl::Invoke(const T& data) { +    std::lock_guard<std::mutex> lock(callback_mutex); +    CallbackSet<T> callback_set = callbacks.Get<T>(); +    for (auto const& callback : callback_set) +        (*callback)(data); +} + +template <typename T> +RoomMember::CallbackHandle<T> RoomMember::RoomMemberImpl::Bind( +    std::function<void(const T&)> callback) { +    std::lock_guard<std::mutex> lock(callback_mutex); +    CallbackHandle<T> handle; +    handle = std::make_shared<std::function<void(const T&)>>(callback); +    callbacks.Get<T>().insert(handle); +    return handle; +} +  // RoomMember  RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {      room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); @@ -339,6 +410,7 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv          room_member_impl->SetState(State::Joining);          room_member_impl->StartLoop();          room_member_impl->SendJoinRequest(nick, preferred_mac); +        SendGameInfo(room_member_impl->current_game_info);      } else {          room_member_impl->SetState(State::CouldNotConnect);      } @@ -366,17 +438,53 @@ void RoomMember::SendChatMessage(const std::string& message) {      room_member_impl->Send(std::move(packet));  } -void RoomMember::SendGameName(const std::string& game_name) { +void RoomMember::SendGameInfo(const GameInfo& game_info) { +    room_member_impl->current_game_info = game_info; +    if (!IsConnected()) +        return; +      Packet packet; -    packet << static_cast<u8>(IdSetGameName); -    packet << game_name; +    packet << static_cast<u8>(IdSetGameInfo); +    packet << game_info.name; +    packet << game_info.id;      room_member_impl->Send(std::move(packet));  } +RoomMember::CallbackHandle<RoomMember::State> RoomMember::BindOnStateChanged( +    std::function<void(const RoomMember::State&)> callback) { +    return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived( +    std::function<void(const WifiPacket&)> callback) { +    return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged( +    std::function<void(const RoomInformation&)> callback) { +    return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved( +    std::function<void(const ChatEntry&)> callback) { +    return room_member_impl->Bind(callback); +} + +template <typename T> +void RoomMember::Unbind(CallbackHandle<T> handle) { +    std::lock_guard<std::mutex> lock(room_member_impl->callback_mutex); +    room_member_impl->callbacks.Get<T>().erase(handle); +} +  void RoomMember::Leave() {      room_member_impl->SetState(State::Idle);      room_member_impl->loop_thread->join();      room_member_impl->loop_thread.reset();  } +template void RoomMember::Unbind(CallbackHandle<WifiPacket>); +template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); +template void RoomMember::Unbind(CallbackHandle<RoomInformation>); +template void RoomMember::Unbind(CallbackHandle<ChatEntry>); +  } // namespace Network diff --git a/src/network/room_member.h b/src/network/room_member.h index bc1af3a7e..98770a234 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h @@ -4,6 +4,7 @@  #pragma once +#include <functional>  #include <memory>  #include <string>  #include <vector> @@ -53,12 +54,23 @@ public:      struct MemberInformation {          std::string nickname;   ///< Nickname of the member. -        std::string game_name;  ///< Name of the game they're currently playing, or empty if they're +        GameInfo game_info;     ///< Name of the game they're currently playing, or empty if they're                                  /// not playing anything.          MacAddress mac_address; ///< MAC address associated with this member.      };      using MemberList = std::vector<MemberInformation>; +    // The handle for the callback functions +    template <typename T> +    using CallbackHandle = std::shared_ptr<std::function<void(const T&)>>; + +    /** +     * Unbinds a callback function from the events. +     * @param handle The connection handle to disconnect +     */ +    template <typename T> +    void Unbind(CallbackHandle<T> handle); +      RoomMember();      ~RoomMember(); @@ -113,10 +125,49 @@ public:      void SendChatMessage(const std::string& message);      /** -     * Sends the current game name  to the room. -     * @param game_name The game name. +     * Sends the current game info to the room. +     * @param game_info The game information. +     */ +    void SendGameInfo(const GameInfo& game_info); + +    /** +     * Binds a function to an event that will be triggered every time the State of the member +     * changed. The function wil be called every time the event is triggered. The callback function +     * must not bind or unbind a function. Doing so will cause a deadlock +     * @param callback The function to call +     * @return A handle used for removing the function from the registered list +     */ +    CallbackHandle<State> BindOnStateChanged(std::function<void(const State&)> callback); + +    /** +     * Binds a function to an event that will be triggered every time a WifiPacket is received. +     * The function wil be called everytime the event is triggered. +     * The callback function must not bind or unbind a function. Doing so will cause a deadlock +     * @param callback The function to call +     * @return A handle used for removing the function from the registered list +     */ +    CallbackHandle<WifiPacket> BindOnWifiPacketReceived( +        std::function<void(const WifiPacket&)> callback); + +    /** +     * Binds a function to an event that will be triggered every time the RoomInformation changes. +     * The function wil be called every time the event is triggered. +     * The callback function must not bind or unbind a function. Doing so will cause a deadlock +     * @param callback The function to call +     * @return A handle used for removing the function from the registered list +     */ +    CallbackHandle<RoomInformation> BindOnRoomInformationChanged( +        std::function<void(const RoomInformation&)> callback); + +    /** +     * Binds a function to an event that will be triggered every time a ChatMessage is received. +     * The function wil be called every time the event is triggered. +     * The callback function must not bind or unbind a function. Doing so will cause a deadlock +     * @param callback The function to call +     * @return A handle used for removing the function from the registered list       */ -    void SendGameName(const std::string& game_name); +    CallbackHandle<ChatEntry> BindOnChatMessageRecieved( +        std::function<void(const ChatEntry&)> callback);      /**       * Leaves the current room.  | 
