diff options
Diffstat (limited to 'src/network')
| -rw-r--r-- | src/network/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | src/network/announce_multiplayer_session.cpp | 164 | ||||
| -rw-r--r-- | src/network/announce_multiplayer_session.h | 98 | ||||
| -rw-r--r-- | src/network/room.cpp | 182 | ||||
| -rw-r--r-- | src/network/room.h | 14 | ||||
| -rw-r--r-- | src/network/room_member.cpp | 127 | ||||
| -rw-r--r-- | src/network/room_member.h | 72 | 
7 files changed, 451 insertions, 215 deletions
| diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 382a69e2f..6f8ca4b90 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -1,4 +1,9 @@ +# SPDX-FileCopyrightText: 2022 yuzu Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later +  add_library(network STATIC +    announce_multiplayer_session.cpp +    announce_multiplayer_session.h      network.cpp      network.h      packet.cpp @@ -14,3 +19,7 @@ add_library(network STATIC  create_target_directory_groups(network)  target_link_libraries(network PRIVATE common enet Boost::boost) +if (ENABLE_WEB_SERVICE) +    target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE) +    target_link_libraries(network PRIVATE web_service) +endif() diff --git a/src/network/announce_multiplayer_session.cpp b/src/network/announce_multiplayer_session.cpp new file mode 100644 index 000000000..6737ce85a --- /dev/null +++ b/src/network/announce_multiplayer_session.cpp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> +#include <future> +#include <vector> +#include "announce_multiplayer_session.h" +#include "common/announce_multiplayer_room.h" +#include "common/assert.h" +#include "common/settings.h" +#include "network/network.h" + +#ifdef ENABLE_WEB_SERVICE +#include "web_service/announce_room_json.h" +#endif + +namespace Core { + +// Time between room is announced to web_service +static constexpr std::chrono::seconds announce_time_interval(15); + +AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_) +    : room_network{room_network_} { +#ifdef ENABLE_WEB_SERVICE +    backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(), +                                                     Settings::values.yuzu_username.GetValue(), +                                                     Settings::values.yuzu_token.GetValue()); +#else +    backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>(); +#endif +} + +WebService::WebResult AnnounceMultiplayerSession::Register() { +    auto room = room_network.GetRoom().lock(); +    if (!room) { +        return WebService::WebResult{WebService::WebResult::Code::LibError, +                                     "Network is not initialized", ""}; +    } +    if (room->GetState() != Network::Room::State::Open) { +        return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""}; +    } +    UpdateBackendData(room); +    WebService::WebResult result = backend->Register(); +    if (result.result_code != WebService::WebResult::Code::Success) { +        return result; +    } +    LOG_INFO(WebService, "Room has been registered"); +    room->SetVerifyUID(result.returned_data); +    registered = true; +    return WebService::WebResult{WebService::WebResult::Code::Success, "", ""}; +} + +void AnnounceMultiplayerSession::Start() { +    if (announce_multiplayer_thread) { +        Stop(); +    } +    shutdown_event.Reset(); +    announce_multiplayer_thread = +        std::make_unique<std::thread>(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this); +} + +void AnnounceMultiplayerSession::Stop() { +    if (announce_multiplayer_thread) { +        shutdown_event.Set(); +        announce_multiplayer_thread->join(); +        announce_multiplayer_thread.reset(); +        backend->Delete(); +        registered = false; +    } +} + +AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback( +    std::function<void(const WebService::WebResult&)> function) { +    std::lock_guard lock(callback_mutex); +    auto handle = std::make_shared<std::function<void(const WebService::WebResult&)>>(function); +    error_callbacks.insert(handle); +    return handle; +} + +void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) { +    std::lock_guard lock(callback_mutex); +    error_callbacks.erase(handle); +} + +AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { +    Stop(); +} + +void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room> room) { +    Network::RoomInformation room_information = room->GetRoomInformation(); +    std::vector<AnnounceMultiplayerRoom::Member> memberlist = room->GetRoomMemberList(); +    backend->SetRoomInformation(room_information.name, room_information.description, +                                room_information.port, room_information.member_slots, +                                Network::network_version, room->HasPassword(), +                                room_information.preferred_game); +    backend->ClearPlayers(); +    for (const auto& member : memberlist) { +        backend->AddPlayer(member); +    } +} + +void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { +    // Invokes all current bound error callbacks. +    const auto ErrorCallback = [this](WebService::WebResult result) { +        std::lock_guard lock(callback_mutex); +        for (auto callback : error_callbacks) { +            (*callback)(result); +        } +    }; + +    if (!registered) { +        WebService::WebResult result = Register(); +        if (result.result_code != WebService::WebResult::Code::Success) { +            ErrorCallback(result); +            return; +        } +    } + +    auto update_time = std::chrono::steady_clock::now(); +    std::future<WebService::WebResult> future; +    while (!shutdown_event.WaitUntil(update_time)) { +        update_time += announce_time_interval; +        auto room = room_network.GetRoom().lock(); +        if (!room) { +            break; +        } +        if (room->GetState() != Network::Room::State::Open) { +            break; +        } +        UpdateBackendData(room); +        WebService::WebResult result = backend->Update(); +        if (result.result_code != WebService::WebResult::Code::Success) { +            ErrorCallback(result); +        } +        if (result.result_string == "404") { +            registered = false; +            // Needs to register the room again +            WebService::WebResult register_result = Register(); +            if (register_result.result_code != WebService::WebResult::Code::Success) { +                ErrorCallback(register_result); +            } +        } +    } +} + +AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() { +    return backend->GetRoomList(); +} + +bool AnnounceMultiplayerSession::IsRunning() const { +    return announce_multiplayer_thread != nullptr; +} + +void AnnounceMultiplayerSession::UpdateCredentials() { +    ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running"); + +#ifdef ENABLE_WEB_SERVICE +    backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(), +                                                     Settings::values.yuzu_username.GetValue(), +                                                     Settings::values.yuzu_token.GetValue()); +#endif +} + +} // namespace Core diff --git a/src/network/announce_multiplayer_session.h b/src/network/announce_multiplayer_session.h new file mode 100644 index 000000000..db790f7d2 --- /dev/null +++ b/src/network/announce_multiplayer_session.h @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <atomic> +#include <functional> +#include <memory> +#include <mutex> +#include <set> +#include <thread> +#include "common/announce_multiplayer_room.h" +#include "common/common_types.h" +#include "common/thread.h" + +namespace Network { +class Room; +class RoomNetwork; +} // namespace Network + +namespace Core { + +/** + * Instruments AnnounceMultiplayerRoom::Backend. + * Creates a thread that regularly updates the room information and submits them + * An async get of room information is also possible + */ +class AnnounceMultiplayerSession { +public: +    using CallbackHandle = std::shared_ptr<std::function<void(const WebService::WebResult&)>>; +    AnnounceMultiplayerSession(Network::RoomNetwork& room_network_); +    ~AnnounceMultiplayerSession(); + +    /** +     * Allows to bind a function that will get called if the announce encounters an error +     * @param function The function that gets called +     * @return A handle that can be used the unbind the function +     */ +    CallbackHandle BindErrorCallback(std::function<void(const WebService::WebResult&)> function); + +    /** +     * Unbind a function from the error callbacks +     * @param handle The handle for the function that should get unbind +     */ +    void UnbindErrorCallback(CallbackHandle handle); + +    /** +     * Registers a room to web services +     * @return The result of the registration attempt. +     */ +    WebService::WebResult Register(); + +    /** +     * Starts the announce of a room to web services +     */ +    void Start(); + +    /** +     * Stops the announce to web services +     */ +    void Stop(); + +    /** +     *  Returns a list of all room information the backend got +     * @param func A function that gets executed when the async get finished, e.g. a signal +     * @return a list of rooms received from the web service +     */ +    AnnounceMultiplayerRoom::RoomList GetRoomList(); + +    /** +     * Whether the announce session is still running +     */ +    bool IsRunning() const; + +    /** +     * Recreates the backend, updating the credentials. +     * This can only be used when the announce session is not running. +     */ +    void UpdateCredentials(); + +private: +    void UpdateBackendData(std::shared_ptr<Network::Room> room); +    void AnnounceMultiplayerLoop(); + +    Common::Event shutdown_event; +    std::mutex callback_mutex; +    std::set<CallbackHandle> error_callbacks; +    std::unique_ptr<std::thread> announce_multiplayer_thread; + +    /// Backend interface that logs fields +    std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend; + +    std::atomic_bool registered = false; ///< Whether the room has been registered + +    Network::RoomNetwork& room_network; +}; + +} // namespace Core diff --git a/src/network/room.cpp b/src/network/room.cpp index 3fc3a0383..8c63b255b 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -20,9 +20,7 @@ namespace Network {  class Room::RoomImpl {  public: -    // This MAC address is used to generate a 'Nintendo' like Mac address. -    const MacAddress NintendoOUI; -    std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress +    std::mt19937 random_gen; ///< Random number generator. Used for GenerateFakeIPAddress      ENetHost* server = nullptr; ///< Network interface. @@ -35,10 +33,9 @@ public:      std::string password; ///< The password required to connect to this room.      struct Member { -        std::string nickname;        ///< The nickname of the member. -        std::string console_id_hash; ///< A hash of the console ID of the member. -        GameInfo game_info;          ///< The current game of the member -        MacAddress mac_address;      ///< The assigned mac address of the member. +        std::string nickname; ///< The nickname of the member. +        GameInfo game_info;   ///< The current game of the member +        IPv4Address fake_ip;  ///< The assigned fake ip address of the member.          /// Data of the user, often including authenticated forum username.          VerifyUser::UserData user_data;          ENetPeer* peer; ///< The remote peer. @@ -51,8 +48,7 @@ public:      IPBanList ip_ban_list;             ///< List of banned IP addresses      mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists -    RoomImpl() -        : NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00}, random_gen(std::random_device()()) {} +    RoomImpl() : random_gen(std::random_device()()) {}      /// Thread that receives and dispatches network packets      std::unique_ptr<std::thread> room_thread; @@ -101,16 +97,10 @@ public:      bool IsValidNickname(const std::string& nickname) const;      /** -     * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the +     * Returns whether the fake ip address is valid, ie. isn't already taken by someone else in the       * room.       */ -    bool IsValidMacAddress(const MacAddress& address) const; - -    /** -     * Returns whether the console ID (hash) is valid, ie. isn't already taken by someone else in -     * the room. -     */ -    bool IsValidConsoleId(const std::string& console_id_hash) const; +    bool IsValidFakeIPAddress(const IPv4Address& address) const;      /**       * Returns whether a user has mod permissions. @@ -128,15 +118,9 @@ public:      void SendNameCollision(ENetPeer* client);      /** -     * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid. +     * Sends a ID_ROOM_IP_COLLISION message telling the client that the IP is invalid.       */ -    void SendMacCollision(ENetPeer* client); - -    /** -     * Sends a IdConsoleIdCollison message telling the client that another member with the same -     * console ID exists. -     */ -    void SendConsoleIdCollision(ENetPeer* client); +    void SendIPCollision(ENetPeer* client);      /**       * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. @@ -152,13 +136,13 @@ public:       * Notifies the member that its connection attempt was successful,       * and it is now part of the room.       */ -    void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); +    void SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip);      /**       * Notifies the member that its connection attempt was successful,       * and it is now part of the room, and it has been granted mod permissions.       */ -    void SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address); +    void SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip);      /**       * Sends a IdHostKicked message telling the client that they have been kicked. @@ -210,7 +194,7 @@ public:       * <u32> num_members: the number of currently joined clients       * This is followed by the following three values for each member:       * <String> nickname of that member -     * <MacAddress> mac_address of that member +     * <IPv4Address> fake_ip of that member       * <String> game_name of that member       */      void BroadcastRoomInformation(); @@ -219,13 +203,13 @@ public:       * Generates a free MAC address to assign to a new client.       * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32       */ -    MacAddress GenerateMacAddress(); +    IPv4Address GenerateFakeIPAddress();      /**       * Broadcasts this packet to all members except the sender.       * @param event The ENet event containing the data       */ -    void HandleWifiPacket(const ENetEvent* event); +    void HandleProxyPacket(const ENetEvent* event);      /**       * Extracts a chat entry from a received ENet packet and adds it to the chat queue. @@ -237,7 +221,7 @@ public:       * Extracts the game name from a received ENet packet and broadcasts it.       * @param event The ENet event that was received.       */ -    void HandleGameNamePacket(const ENetEvent* event); +    void HandleGameInfoPacket(const ENetEvent* event);      /**       * Removes the client from the members list if it was in it and announces the change @@ -250,7 +234,7 @@ public:  void Room::RoomImpl::ServerLoop() {      while (state != State::Closed) {          ENetEvent event; -        if (enet_host_service(server, &event, 16) > 0) { +        if (enet_host_service(server, &event, 5) > 0) {              switch (event.type) {              case ENET_EVENT_TYPE_RECEIVE:                  switch (event.packet->data[0]) { @@ -258,10 +242,10 @@ void Room::RoomImpl::ServerLoop() {                      HandleJoinRequest(&event);                      break;                  case IdSetGameInfo: -                    HandleGameNamePacket(&event); +                    HandleGameInfoPacket(&event);                      break; -                case IdWifiPacket: -                    HandleWifiPacket(&event); +                case IdProxyPacket: +                    HandleProxyPacket(&event);                      break;                  case IdChatMessage:                      HandleChatPacket(&event); @@ -313,11 +297,8 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {      std::string nickname;      packet.Read(nickname); -    std::string console_id_hash; -    packet.Read(console_id_hash); - -    MacAddress preferred_mac; -    packet.Read(preferred_mac); +    IPv4Address preferred_fake_ip; +    packet.Read(preferred_fake_ip);      u32 client_version;      packet.Read(client_version); @@ -338,20 +319,15 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {          return;      } -    if (preferred_mac != NoPreferredMac) { -        // Verify if the preferred mac is available -        if (!IsValidMacAddress(preferred_mac)) { -            SendMacCollision(event->peer); +    if (preferred_fake_ip != NoPreferredIP) { +        // Verify if the preferred fake ip is available +        if (!IsValidFakeIPAddress(preferred_fake_ip)) { +            SendIPCollision(event->peer);              return;          }      } else { -        // Assign a MAC address of this client automatically -        preferred_mac = GenerateMacAddress(); -    } - -    if (!IsValidConsoleId(console_id_hash)) { -        SendConsoleIdCollision(event->peer); -        return; +        // Assign a fake ip address of this client automatically +        preferred_fake_ip = GenerateFakeIPAddress();      }      if (client_version != network_version) { @@ -361,8 +337,7 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {      // At this point the client is ready to be added to the room.      Member member{}; -    member.mac_address = preferred_mac; -    member.console_id_hash = console_id_hash; +    member.fake_ip = preferred_fake_ip;      member.nickname = nickname;      member.peer = event->peer; @@ -408,9 +383,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {      // Notify everyone that the room information has changed.      BroadcastRoomInformation();      if (HasModPermission(event->peer)) { -        SendJoinSuccessAsMod(event->peer, preferred_mac); +        SendJoinSuccessAsMod(event->peer, preferred_fake_ip);      } else { -        SendJoinSuccess(event->peer, preferred_mac); +        SendJoinSuccess(event->peer, preferred_fake_ip);      }  } @@ -575,19 +550,11 @@ bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {                         [&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. +bool Room::RoomImpl::IsValidFakeIPAddress(const IPv4Address& address) const { +    // An IP address is valid if it is not already taken by anybody else in the room.      std::lock_guard lock(member_mutex);      return std::all_of(members.begin(), members.end(), -                       [&address](const auto& member) { return member.mac_address != address; }); -} - -bool Room::RoomImpl::IsValidConsoleId(const std::string& console_id_hash) const { -    // A Console ID is valid if it is not already taken by anybody else in the room. -    std::lock_guard lock(member_mutex); -    return std::all_of(members.begin(), members.end(), [&console_id_hash](const auto& member) { -        return member.console_id_hash != console_id_hash; -    }); +                       [&address](const auto& member) { return member.fake_ip != address; });  }  bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { @@ -621,19 +588,9 @@ void Room::RoomImpl::SendNameCollision(ENetPeer* client) {      enet_host_flush(server);  } -void Room::RoomImpl::SendMacCollision(ENetPeer* client) { +void Room::RoomImpl::SendIPCollision(ENetPeer* client) {      Packet packet; -    packet.Write(static_cast<u8>(IdMacCollision)); - -    ENetPacket* enet_packet = -        enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); -    enet_peer_send(client, 0, enet_packet); -    enet_host_flush(server); -} - -void Room::RoomImpl::SendConsoleIdCollision(ENetPeer* client) { -    Packet packet; -    packet.Write(static_cast<u8>(IdConsoleIdCollision)); +    packet.Write(static_cast<u8>(IdIpCollision));      ENetPacket* enet_packet =          enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); @@ -672,20 +629,20 @@ void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) {      enet_host_flush(server);  } -void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { +void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip) {      Packet packet;      packet.Write(static_cast<u8>(IdJoinSuccess)); -    packet.Write(mac_address); +    packet.Write(fake_ip);      ENetPacket* enet_packet =          enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);      enet_peer_send(client, 0, enet_packet);      enet_host_flush(server);  } -void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address) { +void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip) {      Packet packet;      packet.Write(static_cast<u8>(IdJoinSuccessAsMod)); -    packet.Write(mac_address); +    packet.Write(fake_ip);      ENetPacket* enet_packet =          enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);      enet_peer_send(client, 0, enet_packet); @@ -818,9 +775,10 @@ void Room::RoomImpl::BroadcastRoomInformation() {          std::lock_guard lock(member_mutex);          for (const auto& member : members) {              packet.Write(member.nickname); -            packet.Write(member.mac_address); +            packet.Write(member.fake_ip);              packet.Write(member.game_info.name);              packet.Write(member.game_info.id); +            packet.Write(member.game_info.version);              packet.Write(member.user_data.username);              packet.Write(member.user_data.display_name);              packet.Write(member.user_data.avatar_url); @@ -833,34 +791,44 @@ void Room::RoomImpl::BroadcastRoomInformation() {      enet_host_flush(server);  } -MacAddress Room::RoomImpl::GenerateMacAddress() { -    MacAddress result_mac = -        NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI -    std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF +IPv4Address Room::RoomImpl::GenerateFakeIPAddress() { +    IPv4Address result_ip{192, 168, 0, 0}; +    std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE      do { -        for (std::size_t i = 3; i < result_mac.size(); ++i) { -            result_mac[i] = dis(random_gen); +        for (std::size_t i = 2; i < result_ip.size(); ++i) { +            result_ip[i] = dis(random_gen);          } -    } while (!IsValidMacAddress(result_mac)); -    return result_mac; +    } while (!IsValidFakeIPAddress(result_ip)); + +    return result_ip;  } -void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { +void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {      Packet in_packet;      in_packet.Append(event->packet->data, event->packet->dataLength); -    in_packet.IgnoreBytes(sizeof(u8));         // Message type -    in_packet.IgnoreBytes(sizeof(u8));         // WifiPacket Type -    in_packet.IgnoreBytes(sizeof(u8));         // WifiPacket Channel -    in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address -    MacAddress destination_address; -    in_packet.Read(destination_address); +    in_packet.IgnoreBytes(sizeof(u8)); // Message type + +    in_packet.IgnoreBytes(sizeof(u8));          // Domain +    in_packet.IgnoreBytes(sizeof(IPv4Address)); // IP +    in_packet.IgnoreBytes(sizeof(u16));         // Port + +    in_packet.IgnoreBytes(sizeof(u8)); // Domain +    IPv4Address remote_ip; +    in_packet.Read(remote_ip);          // IP +    in_packet.IgnoreBytes(sizeof(u16)); // Port + +    in_packet.IgnoreBytes(sizeof(u8)); // Protocol + +    bool broadcast; +    in_packet.Read(broadcast); // Broadcast      Packet out_packet;      out_packet.Append(event->packet->data, event->packet->dataLength);      ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),                                                   ENET_PACKET_FLAG_RELIABLE); -    if (destination_address == BroadcastMac) { // Send the data to everyone except the sender +    const auto& destination_address = remote_ip; +    if (broadcast) { // Send the data to everyone except the sender          std::lock_guard lock(member_mutex);          bool sent_packet = false;          for (const auto& member : members) { @@ -877,16 +845,16 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {          std::lock_guard lock(member_mutex);          auto member = std::find_if(members.begin(), members.end(),                                     [destination_address](const Member& member_entry) -> bool { -                                       return member_entry.mac_address == destination_address; +                                       return member_entry.fake_ip == destination_address;                                     });          if (member != members.end()) {              enet_peer_send(member->peer, 0, enet_packet);          } else {              LOG_ERROR(Network, -                      "Attempting to send to unknown MAC address: " -                      "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", +                      "Attempting to send to unknown IP address: " +                      "{}.{}.{}.{}",                        destination_address[0], destination_address[1], destination_address[2], -                      destination_address[3], destination_address[4], destination_address[5]); +                      destination_address[3]);              enet_packet_destroy(enet_packet);          }      } @@ -943,7 +911,7 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {      }  } -void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { +void Room::RoomImpl::HandleGameInfoPacket(const ENetEvent* event) {      Packet in_packet;      in_packet.Append(event->packet->data, event->packet->dataLength); @@ -951,6 +919,7 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {      GameInfo game_info;      in_packet.Read(game_info.name);      in_packet.Read(game_info.id); +    in_packet.Read(game_info.version);      {          std::lock_guard lock(member_mutex); @@ -969,7 +938,8 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {              if (game_info.name.empty()) {                  LOG_INFO(Network, "{} is not playing", display_name);              } else { -                LOG_INFO(Network, "{} is playing {}", display_name, game_info.name); +                LOG_INFO(Network, "{} is playing {} ({})", display_name, game_info.name, +                         game_info.version);              }          }      } @@ -1073,7 +1043,7 @@ std::vector<Member> Room::GetRoomMemberList() const {          member.username = member_impl.user_data.username;          member.display_name = member_impl.user_data.display_name;          member.avatar_url = member_impl.user_data.avatar_url; -        member.mac_address = member_impl.mac_address; +        member.fake_ip = member_impl.fake_ip;          member.game = member_impl.game_info;          member_list.push_back(member);      } diff --git a/src/network/room.h b/src/network/room.h index 6f7e3b5b5..c2a4b1a70 100644 --- a/src/network/room.h +++ b/src/network/room.h @@ -9,12 +9,12 @@  #include <vector>  #include "common/announce_multiplayer_room.h"  #include "common/common_types.h" +#include "common/socket_types.h"  #include "network/verify_user.h"  namespace Network {  using AnnounceMultiplayerRoom::GameInfo; -using AnnounceMultiplayerRoom::MacAddress;  using AnnounceMultiplayerRoom::Member;  using AnnounceMultiplayerRoom::RoomInformation; @@ -29,12 +29,9 @@ static constexpr u32 MaxConcurrentConnections = 254;  constexpr std::size_t NumChannels = 1; // Number of channels used for the connection -/// A special MAC address that tells the room we're joining to assign us a MAC address +/// A special IP address that tells the room we're joining to assign us a IP address  /// automatically. -constexpr MacAddress NoPreferredMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - -// 802.11 broadcast MAC address -constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +constexpr IPv4Address NoPreferredIP = {0xFF, 0xFF, 0xFF, 0xFF};  // The different types of messages that can be sent. The first byte of each packet defines the type  enum RoomMessageTypes : u8 { @@ -42,15 +39,14 @@ enum RoomMessageTypes : u8 {      IdJoinSuccess,      IdRoomInformation,      IdSetGameInfo, -    IdWifiPacket, +    IdProxyPacket,      IdChatMessage,      IdNameCollision, -    IdMacCollision, +    IdIpCollision,      IdVersionMismatch,      IdWrongPassword,      IdCloseRoom,      IdRoomIsFull, -    IdConsoleIdCollision,      IdStatusMessage,      IdHostKicked,      IdHostBanned, diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index e4f823e98..06818af78 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -7,6 +7,7 @@  #include <set>  #include <thread>  #include "common/assert.h" +#include "common/socket_types.h"  #include "enet/enet.h"  #include "network/packet.h"  #include "network/room_member.h" @@ -38,7 +39,7 @@ public:      std::string username;              ///< The username of this member.      mutable std::mutex username_mutex; ///< Mutex for locking username. -    MacAddress mac_address; ///< The mac_address of this member. +    IPv4Address fake_ip; ///< The fake ip of this member.      std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.      /// Thread that receives and dispatches network packets @@ -56,7 +57,7 @@ public:          CallbackSet<T>& Get();      private: -        CallbackSet<WifiPacket> callback_set_wifi_packet; +        CallbackSet<ProxyPacket> callback_set_proxy_packet;          CallbackSet<ChatEntry> callback_set_chat_messages;          CallbackSet<StatusMessageEntry> callback_set_status_messages;          CallbackSet<RoomInformation> callback_set_room_information; @@ -78,15 +79,15 @@ public:      /**       * Sends a request to the server, asking for permission to join a room with the specified -     * nickname and preferred mac. +     * nickname and preferred fake ip.       * @params nickname The desired nickname. -     * @params console_id_hash A hash of the Console ID. -     * @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells +     * @params preferred_fake_ip The preferred IP address to use in the room, the NoPreferredIP +     * tells       * @params password The password for the room       * the server to assign one for us.       */ -    void SendJoinRequest(const std::string& nickname_, const std::string& console_id_hash, -                         const MacAddress& preferred_mac = NoPreferredMac, +    void SendJoinRequest(const std::string& nickname_, +                         const IPv4Address& preferred_fake_ip = NoPreferredIP,                           const std::string& password = "", const std::string& token = "");      /** @@ -101,10 +102,10 @@ public:      void HandleRoomInformationPacket(const ENetEvent* event);      /** -     * Extracts a WifiPacket from a received ENet packet. -     * @param event The  ENet event that was received. +     * Extracts a ProxyPacket from a received ENet packet. +     * @param event The ENet event that was received.       */ -    void HandleWifiPackets(const ENetEvent* event); +    void HandleProxyPackets(const ENetEvent* event);      /**       * Extracts a chat entry from a received ENet packet and adds it to the chat queue. @@ -158,12 +159,12 @@ void RoomMember::RoomMemberImpl::MemberLoop() {      while (IsConnected()) {          std::lock_guard lock(network_mutex);          ENetEvent event; -        if (enet_host_service(client, &event, 16) > 0) { +        if (enet_host_service(client, &event, 5) > 0) {              switch (event.type) {              case ENET_EVENT_TYPE_RECEIVE:                  switch (event.packet->data[0]) { -                case IdWifiPacket: -                    HandleWifiPackets(&event); +                case IdProxyPacket: +                    HandleProxyPackets(&event);                      break;                  case IdChatMessage:                      HandleChatPacket(&event); @@ -198,13 +199,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() {                      SetState(State::Idle);                      SetError(Error::NameCollision);                      break; -                case IdMacCollision: -                    SetState(State::Idle); -                    SetError(Error::MacCollision); -                    break; -                case IdConsoleIdCollision: +                case IdIpCollision:                      SetState(State::Idle); -                    SetError(Error::ConsoleIdCollision); +                    SetError(Error::IpCollision);                      break;                  case IdVersionMismatch:                      SetState(State::Idle); @@ -275,15 +272,13 @@ void RoomMember::RoomMemberImpl::Send(Packet&& packet) {  }  void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_, -                                                 const std::string& console_id_hash, -                                                 const MacAddress& preferred_mac, +                                                 const IPv4Address& preferred_fake_ip,                                                   const std::string& password,                                                   const std::string& token) {      Packet packet;      packet.Write(static_cast<u8>(IdJoinRequest));      packet.Write(nickname_); -    packet.Write(console_id_hash); -    packet.Write(preferred_mac); +    packet.Write(preferred_fake_ip);      packet.Write(network_version);      packet.Write(password);      packet.Write(token); @@ -317,9 +312,10 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev      for (auto& member : member_information) {          packet.Read(member.nickname); -        packet.Read(member.mac_address); +        packet.Read(member.fake_ip);          packet.Read(member.game_info.name);          packet.Read(member.game_info.id); +        packet.Read(member.game_info.version);          packet.Read(member.username);          packet.Read(member.display_name);          packet.Read(member.avatar_url); @@ -342,29 +338,38 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {      packet.IgnoreBytes(sizeof(u8)); // Ignore the message type      // Parse the MAC Address from the packet -    packet.Read(mac_address); +    packet.Read(fake_ip);  } -void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { -    WifiPacket wifi_packet{}; +void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) { +    ProxyPacket proxy_packet{};      Packet packet;      packet.Append(event->packet->data, event->packet->dataLength);      // Ignore the first byte, which is the message id.      packet.IgnoreBytes(sizeof(u8)); // Ignore the message type -    // Parse the WifiPacket from the packet -    u8 frame_type; -    packet.Read(frame_type); -    WifiPacket::PacketType type = static_cast<WifiPacket::PacketType>(frame_type); +    // Parse the ProxyPacket from the packet +    u8 local_family; +    packet.Read(local_family); +    proxy_packet.local_endpoint.family = static_cast<Domain>(local_family); +    packet.Read(proxy_packet.local_endpoint.ip); +    packet.Read(proxy_packet.local_endpoint.portno); + +    u8 remote_family; +    packet.Read(remote_family); +    proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family); +    packet.Read(proxy_packet.remote_endpoint.ip); +    packet.Read(proxy_packet.remote_endpoint.portno); + +    u8 protocol_type; +    packet.Read(protocol_type); +    proxy_packet.protocol = static_cast<Protocol>(protocol_type); -    wifi_packet.type = type; -    packet.Read(wifi_packet.channel); -    packet.Read(wifi_packet.transmitter_address); -    packet.Read(wifi_packet.destination_address); -    packet.Read(wifi_packet.data); +    packet.Read(proxy_packet.broadcast); +    packet.Read(proxy_packet.data); -    Invoke<WifiPacket>(wifi_packet); +    Invoke<ProxyPacket>(proxy_packet);  }  void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { @@ -440,8 +445,8 @@ void RoomMember::RoomMemberImpl::Disconnect() {  }  template <> -RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { -    return callback_set_wifi_packet; +RoomMember::RoomMemberImpl::CallbackSet<ProxyPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_proxy_packet;  }  template <> @@ -525,19 +530,18 @@ const std::string& RoomMember::GetUsername() const {      return room_member_impl->username;  } -const MacAddress& RoomMember::GetMacAddress() const { -    ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected"); -    return room_member_impl->mac_address; +const IPv4Address& RoomMember::GetFakeIpAddress() const { +    ASSERT_MSG(IsConnected(), "Tried to get fake ip address while not connected"); +    return room_member_impl->fake_ip;  }  RoomInformation RoomMember::GetRoomInformation() const {      return room_member_impl->room_information;  } -void RoomMember::Join(const std::string& nick, const std::string& console_id_hash, -                      const char* server_addr, u16 server_port, u16 client_port, -                      const MacAddress& preferred_mac, const std::string& password, -                      const std::string& token) { +void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port, +                      u16 client_port, const IPv4Address& preferred_fake_ip, +                      const std::string& password, const std::string& token) {      // If the member is connected, kill the connection first      if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) {          Leave(); @@ -571,7 +575,7 @@ void RoomMember::Join(const std::string& nick, const std::string& console_id_has      if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {          room_member_impl->nickname = nick;          room_member_impl->StartLoop(); -        room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password, token); +        room_member_impl->SendJoinRequest(nick, preferred_fake_ip, password, token);          SendGameInfo(room_member_impl->current_game_info);      } else {          enet_peer_disconnect(room_member_impl->server, 0); @@ -584,14 +588,22 @@ bool RoomMember::IsConnected() const {      return room_member_impl->IsConnected();  } -void RoomMember::SendWifiPacket(const WifiPacket& wifi_packet) { +void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) {      Packet packet; -    packet.Write(static_cast<u8>(IdWifiPacket)); -    packet.Write(static_cast<u8>(wifi_packet.type)); -    packet.Write(wifi_packet.channel); -    packet.Write(wifi_packet.transmitter_address); -    packet.Write(wifi_packet.destination_address); -    packet.Write(wifi_packet.data); +    packet.Write(static_cast<u8>(IdProxyPacket)); + +    packet.Write(static_cast<u8>(proxy_packet.local_endpoint.family)); +    packet.Write(proxy_packet.local_endpoint.ip); +    packet.Write(proxy_packet.local_endpoint.portno); + +    packet.Write(static_cast<u8>(proxy_packet.remote_endpoint.family)); +    packet.Write(proxy_packet.remote_endpoint.ip); +    packet.Write(proxy_packet.remote_endpoint.portno); + +    packet.Write(static_cast<u8>(proxy_packet.protocol)); +    packet.Write(proxy_packet.broadcast); +    packet.Write(proxy_packet.data); +      room_member_impl->Send(std::move(packet));  } @@ -611,6 +623,7 @@ void RoomMember::SendGameInfo(const GameInfo& game_info) {      packet.Write(static_cast<u8>(IdSetGameInfo));      packet.Write(game_info.name);      packet.Write(game_info.id); +    packet.Write(game_info.version);      room_member_impl->Send(std::move(packet));  } @@ -645,8 +658,8 @@ RoomMember::CallbackHandle<RoomMember::Error> RoomMember::BindOnError(      return room_member_impl->Bind(callback);  } -RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived( -    std::function<void(const WifiPacket&)> callback) { +RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived( +    std::function<void(const ProxyPacket&)> callback) {      return room_member_impl->Bind(callback);  } @@ -685,7 +698,7 @@ void RoomMember::Leave() {      room_member_impl->client = nullptr;  } -template void RoomMember::Unbind(CallbackHandle<WifiPacket>); +template void RoomMember::Unbind(CallbackHandle<ProxyPacket>);  template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);  template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>);  template void RoomMember::Unbind(CallbackHandle<RoomInformation>); diff --git a/src/network/room_member.h b/src/network/room_member.h index bbb7d13d4..f578f7f6a 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h @@ -9,6 +9,7 @@  #include <vector>  #include "common/announce_multiplayer_room.h"  #include "common/common_types.h" +#include "common/socket_types.h"  #include "network/room.h"  namespace Network { @@ -17,22 +18,12 @@ using AnnounceMultiplayerRoom::GameInfo;  using AnnounceMultiplayerRoom::RoomInformation;  /// Information about the received WiFi packets. -/// Acts as our own 802.11 header. -struct WifiPacket { -    enum class PacketType : u8 { -        Beacon, -        Data, -        Authentication, -        AssociationResponse, -        Deauthentication, -        NodeMap -    }; -    PacketType type;      ///< The type of 802.11 frame. -    std::vector<u8> data; ///< Raw 802.11 frame data, starting at the management frame header -                          /// for management frames. -    MacAddress transmitter_address; ///< Mac address of the transmitter. -    MacAddress destination_address; ///< Mac address of the receiver. -    u8 channel;                     ///< WiFi channel where this frame was transmitted. +struct ProxyPacket { +    SockAddrIn local_endpoint; +    SockAddrIn remote_endpoint; +    Protocol protocol; +    bool broadcast; +    std::vector<u8> data;  };  /// Represents a chat message. @@ -72,15 +63,14 @@ public:          HostKicked,     ///< Kicked by the host          // Reasons why connection was rejected -        UnknownError,       ///< Some error [permissions to network device missing or something] -        NameCollision,      ///< Somebody is already using this name -        MacCollision,       ///< Somebody is already using that mac-address -        ConsoleIdCollision, ///< Somebody in the room has the same Console ID -        WrongVersion,       ///< The room version is not the same as for this RoomMember -        WrongPassword,      ///< The password doesn't match the one from the Room -        CouldNotConnect,    ///< The room is not responding to a connection attempt -        RoomIsFull,         ///< Room is already at the maximum number of players -        HostBanned,         ///< The user is banned by the host +        UnknownError,    ///< Some error [permissions to network device missing or something] +        NameCollision,   ///< Somebody is already using this name +        IpCollision,     ///< Somebody is already using that fake-ip-address +        WrongVersion,    ///< The room version is not the same as for this RoomMember +        WrongPassword,   ///< The password doesn't match the one from the Room +        CouldNotConnect, ///< The room is not responding to a connection attempt +        RoomIsFull,      ///< Room is already at the maximum number of players +        HostBanned,      ///< The user is banned by the host          // Reasons why moderation request failed          PermissionDenied, ///< The user does not have mod permissions @@ -92,9 +82,9 @@ public:          std::string username;     ///< The web services username of the member. Can be empty.          std::string display_name; ///< The web services display name of the member. Can be empty.          std::string avatar_url;   ///< Url to the member's avatar. Can be empty. -        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. +        GameInfo game_info;  ///< Name of the game they're currently playing, or empty if they're +                             /// not playing anything. +        IPv4Address fake_ip; ///< Fake Ip address associated with this member.      };      using MemberList = std::vector<MemberInformation>; @@ -135,7 +125,7 @@ public:      /**       * Returns the MAC address of the RoomMember.       */ -    const MacAddress& GetMacAddress() const; +    const IPv4Address& GetFakeIpAddress() const;      /**       * Returns information about the room we're currently connected to. @@ -149,19 +139,17 @@ public:      /**       * Attempts to join a room at the specified address and port, using the specified nickname. -     * A console ID hash is passed in to check console ID conflicts. -     * This may fail if the username or console ID is already taken.       */ -    void Join(const std::string& nickname, const std::string& console_id_hash, -              const char* server_addr = "127.0.0.1", u16 server_port = DefaultRoomPort, -              u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac, +    void Join(const std::string& nickname, const char* server_addr = "127.0.0.1", +              u16 server_port = DefaultRoomPort, u16 client_port = 0, +              const IPv4Address& preferred_fake_ip = NoPreferredIP,                const std::string& password = "", const std::string& token = "");      /** -     * Sends a WiFi packet to the room. +     * Sends a Proxy packet to the room.       * @param packet The WiFi packet to send.       */ -    void SendWifiPacket(const WifiPacket& packet); +    void SendProxyPacket(const ProxyPacket& packet);      /**       * Sends a chat message to the room. @@ -207,14 +195,14 @@ public:      CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback);      /** -     * Binds a function to an event that will be triggered every time a WifiPacket is received. +     * Binds a function to an event that will be triggered every time a ProxyPacket 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); +    CallbackHandle<ProxyPacket> BindOnProxyPacketReceived( +        std::function<void(const ProxyPacket&)> callback);      /**       * Binds a function to an event that will be triggered every time the RoomInformation changes. @@ -292,10 +280,8 @@ inline const char* GetErrorStr(const RoomMember::Error& e) {          return "UnknownError";      case RoomMember::Error::NameCollision:          return "NameCollision"; -    case RoomMember::Error::MacCollision: -        return "MaxCollision"; -    case RoomMember::Error::ConsoleIdCollision: -        return "ConsoleIdCollision"; +    case RoomMember::Error::IpCollision: +        return "IpCollision";      case RoomMember::Error::WrongVersion:          return "WrongVersion";      case RoomMember::Error::WrongPassword: | 
