From a0626221a52056669c0b6d19b37d4189c1671fb7 Mon Sep 17 00:00:00 2001
From: B3n30 <benediktthomas@gmail.com>
Date: Fri, 14 Jul 2017 09:20:39 +0200
Subject: [PATCH] Network: Made send async in RoomMember

---
 src/network/room.cpp        | 35 +++++++++++++++++++++---
 src/network/room.h          |  6 ++++-
 src/network/room_member.cpp | 53 +++++++++++++++++++++++--------------
 src/network/room_member.h   |  1 +
 4 files changed, 70 insertions(+), 25 deletions(-)

diff --git a/src/network/room.cpp b/src/network/room.cpp
index 9af5b4ea07..da16793120 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -73,6 +73,11 @@ public:
      */
     void SendMacCollision(ENetPeer* client);
 
+    /**
+     * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the MAC is invalid.
+     */
+    void SendVersionMismatch(ENetPeer* client);
+
     /**
      * Notifies the member that its connection attempt was successful,
      * and it is now part of the room.
@@ -170,6 +175,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
     MacAddress preferred_mac;
     packet >> preferred_mac;
 
+    u32 client_version;
+    packet >> client_version;
+
     if (!IsValidNickname(nickname)) {
         SendNameCollision(event->peer);
         return;
@@ -186,6 +194,11 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
         preferred_mac = GenerateMacAddress();
     }
 
+    if (client_version != network_version) {
+        SendVersionMismatch(event->peer);
+        return;
+    }
+
     // At this point the client is ready to be added to the room.
     Member member{};
     member.mac_address = preferred_mac;
@@ -232,6 +245,17 @@ void Room::RoomImpl::SendMacCollision(ENetPeer* client) {
     enet_host_flush(server);
 }
 
+void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) {
+    Packet packet;
+    packet << static_cast<MessageID>(IdVersionMismatch);
+    packet << network_version;
+
+    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::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) {
     Packet packet;
     packet << static_cast<MessageID>(IdJoinSuccess);
@@ -291,7 +315,7 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
     if (destination_address == BroadcastMac) { // Send the data to everyone except the sender
         for (const auto& member : members) {
             if (member.peer != event->peer)
-                enet_peer_send(member.peer, 0, event->packet);
+                enet_peer_send(member.peer, 0, enet_packet);
         }
     } else { // Send the data only to the destination client
         auto member = std::find_if(members.begin(), members.end(),
@@ -327,9 +351,9 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
 
     ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
                                                  ENET_PACKET_FLAG_RELIABLE);
-    for (auto it = members.begin(); it != members.end(); ++it) {
-        if (it->peer != event->peer)
-            enet_peer_send(it->peer, 0, enet_packet);
+    for (const auto& member : members) {
+        if (member.peer != event->peer)
+            enet_peer_send(member.peer, 0, enet_packet);
     }
     enet_host_flush(server);
 }
@@ -369,6 +393,9 @@ Room::~Room() = default;
 void Room::Create(const std::string& name, const std::string& server_address, u16 server_port) {
     ENetAddress address;
     address.host = ENET_HOST_ANY;
+    if (!server_address.empty()) {
+        enet_address_set_host(&address, server_address.c_str());
+    }
     address.port = server_port;
 
     room_impl->server = enet_host_create(&address, MaxConcurrentConnections, NumChannels, 0, 0);
diff --git a/src/network/room.h b/src/network/room.h
index 82e3dc62ce..ffa17599d4 100644
--- a/src/network/room.h
+++ b/src/network/room.h
@@ -11,6 +11,8 @@
 
 namespace Network {
 
+constexpr u32 network_version = 1; ///< The version of this Room and RoomMember
+
 constexpr u16 DefaultRoomPort = 1234;
 constexpr size_t NumChannels = 1; // Number of channels used for the connection
 
@@ -37,7 +39,9 @@ enum RoomMessageTypes {
     IdWifiPacket,
     IdChatMessage,
     IdNameCollision,
-    IdMacCollision
+    IdMacCollision,
+    IdVersionMismatch,
+    IdCloseRoom
 };
 
 /// This is what a server [person creating a server] would use.
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index ec67aa5be7..f6f8b0475c 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #include <atomic>
+#include <list>
 #include <mutex>
 #include <thread>
 #include "common/assert.h"
@@ -33,8 +34,10 @@ public:
 
     std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
     /// Thread that receives and dispatches network packets
-    std::unique_ptr<std::thread> receive_thread;
-    void ReceiveLoop();
+    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
+    void MemberLoop();
     void StartLoop();
 
     /**
@@ -91,7 +94,7 @@ bool RoomMember::RoomMemberImpl::IsConnected() const {
     return state == State::Joining || state == State::Joined;
 }
 
-void RoomMember::RoomMemberImpl::ReceiveLoop() {
+void RoomMember::RoomMemberImpl::MemberLoop() {
     // Receive packets while the connection is open
     while (IsConnected()) {
         std::lock_guard<std::mutex> lock(network_mutex);
@@ -121,6 +124,9 @@ void RoomMember::RoomMemberImpl::ReceiveLoop() {
                 case IdMacCollision:
                     SetState(State::MacCollision);
                     break;
+                case IdVersionMismatch:
+                    SetState(State::WrongVersion);
+                    break;
                 default:
                     break;
                 }
@@ -128,22 +134,30 @@ void RoomMember::RoomMemberImpl::ReceiveLoop() {
                 break;
             case ENET_EVENT_TYPE_DISCONNECT:
                 SetState(State::LostConnection);
+                break;
             }
         }
+        {
+            std::lock_guard<std::mutex> lock(send_list_mutex);
+            for (const auto& packet : send_list) {
+                ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(),
+                                                            ENET_PACKET_FLAG_RELIABLE);
+                enet_peer_send(server, 0, enetPacket);
+            }
+            enet_host_flush(client);
+            send_list.clear();
+        }
     }
     Disconnect();
 };
 
 void RoomMember::RoomMemberImpl::StartLoop() {
-    receive_thread = std::make_unique<std::thread>(&RoomMember::RoomMemberImpl::ReceiveLoop, this);
+    loop_thread = std::make_unique<std::thread>(&RoomMember::RoomMemberImpl::MemberLoop, this);
 }
 
 void RoomMember::RoomMemberImpl::Send(Packet& packet) {
-    std::lock_guard<std::mutex> lock(network_mutex);
-    ENetPacket* enetPacket =
-        enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
-    enet_peer_send(server, 0, enetPacket);
-    enet_host_flush(client);
+    std::lock_guard<std::mutex> lock(send_list_mutex);
+    send_list.push_back(std::move(packet));
 }
 
 void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname,
@@ -152,6 +166,7 @@ void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname,
     packet << static_cast<MessageID>(IdJoinRequest);
     packet << nickname;
     packet << preferred_mac;
+    packet << network_version;
     Send(packet);
 }
 
@@ -238,11 +253,9 @@ void RoomMember::RoomMemberImpl::Disconnect() {
     room_information.member_slots = 0;
     room_information.name.clear();
 
-    if (server) {
-        enet_peer_disconnect(server, 0);
-    } else {
+    if (!server)
         return;
-    }
+    enet_peer_disconnect(server, 0);
 
     ENetEvent event;
     while (enet_host_service(client, &event, ConnectionTimeoutMs) > 0) {
@@ -296,14 +309,14 @@ RoomInformation RoomMember::GetRoomInformation() const {
 void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port,
                       u16 client_port) {
     // If the member is connected, kill the connection first
-    if (room_member_impl->receive_thread && room_member_impl->receive_thread->joinable()) {
+    if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) {
         room_member_impl->SetState(State::Error);
-        room_member_impl->receive_thread->join();
-        room_member_impl->receive_thread.reset();
+        room_member_impl->loop_thread->join();
+        room_member_impl->loop_thread.reset();
     }
     // If the thread isn't running but the ptr still exists, reset it
-    else if (room_member_impl->receive_thread) {
-        room_member_impl->receive_thread.reset();
+    else if (room_member_impl->loop_thread) {
+        room_member_impl->loop_thread.reset();
     }
 
     ENetAddress address{};
@@ -361,8 +374,8 @@ void RoomMember::SendGameName(const std::string& game_name) {
 
 void RoomMember::Leave() {
     room_member_impl->SetState(State::Idle);
-    room_member_impl->receive_thread->join();
-    room_member_impl->receive_thread.reset();
+    room_member_impl->loop_thread->join();
+    room_member_impl->loop_thread.reset();
 }
 
 } // namespace Network
diff --git a/src/network/room_member.h b/src/network/room_member.h
index d874cc5e4d..6522f053cf 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -47,6 +47,7 @@ public:
         // Reasons why connection was rejected
         NameCollision,  ///< Somebody is already using this name
         MacCollision,   ///< Somebody is already using that mac-address
+        WrongVersion,   ///< The room version is not the same as for this RoomMember
         CouldNotConnect ///< The room is not responding to a connection attempt
     };