From 589dc083a58425cadd8390ddd81854dcf054dd27 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sat, 8 Jul 2017 15:24:47 +0200 Subject: [PATCH] Network: Threads for Room and RoomMember --- src/network/room.cpp | 38 ++++++++++++++++-- src/network/room.h | 14 ++++++- src/network/room_member.cpp | 79 +++++++++++++++++++++++++++++++++---- src/network/room_member.h | 1 - 4 files changed, 119 insertions(+), 13 deletions(-) diff --git a/src/network/room.cpp b/src/network/room.cpp index 48de2f5cb..274cb4159 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include #include "enet/enet.h" #include "network/room.h" @@ -16,8 +18,38 @@ public: std::atomic state{State::Closed}; ///< Current state of the room. RoomInformation room_information; ///< Information about this room. + + /// Thread that receives and dispatches network packets + std::unique_ptr room_thread; + + /// Thread function that will receive and dispatch messages until the room is destroyed. + void ServerLoop(); + void StartLoop(); }; +// RoomImpl +void Room::RoomImpl::ServerLoop() { + while (state != State::Closed) { + ENetEvent event; + if (enet_host_service(server, &event, 1000) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + // TODO(B3N30): Select the type of message and handle it + enet_packet_destroy(event.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: + // TODO(B3N30): Handle the disconnect from a client + break; + } + } + } +} + +void Room::RoomImpl::StartLoop() { + room_thread = std::make_unique(&Room::RoomImpl::ServerLoop, this); +} + +// Room Room::Room() : room_impl{std::make_unique()} {} Room::~Room() = default; @@ -34,8 +66,7 @@ void Room::Create(const std::string& name, const std::string& server_address, u1 room_impl->room_information.name = name; room_impl->room_information.member_slots = MaxConcurrentConnections; - - // TODO(B3N30): Start the receiving thread + room_impl->StartLoop(); } Room::State Room::GetState() const { @@ -48,7 +79,8 @@ const RoomInformation& Room::GetRoomInformation() const { void Room::Destroy() { room_impl->state = State::Closed; - // TODO(B3n30): Join the receiving thread + room_impl->room_thread->join(); + room_impl->room_thread.reset(); if (room_impl->server) { enet_host_destroy(room_impl->server); diff --git a/src/network/room.h b/src/network/room.h index 70c64d5f1..0a6217c11 100644 --- a/src/network/room.h +++ b/src/network/room.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include #include "common/common_types.h" @@ -19,6 +18,19 @@ struct RoomInformation { u32 member_slots; ///< Maximum number of members in this room }; +// The different types of messages that can be sent. The first byte of each packet defines the type +typedef uint8_t MessageID; +enum RoomMessageTypes { + IdJoinRequest = 1, + IdJoinSuccess, + IdRoomInformation, + IdSetGameName, + IdWifiPacket, + IdChatMessage, + IdNameCollision, + IdMacCollision +}; + /// This is what a server [person creating a server] would use. class Room final { public: diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index c87f009f4..e1a0dfdab 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include #include "common/assert.h" #include "enet/enet.h" #include "network/room_member.h" @@ -16,10 +19,65 @@ public: ENetPeer* server = nullptr; ///< The server peer the client is connected to std::atomic state{State::Idle}; ///< Current state of the RoomMember. + void SetState(const State new_state); + bool IsConnected() const; std::string nickname; ///< The nickname of this member. + + std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. + /// Thread that receives and dispatches network packets + std::unique_ptr receive_thread; + void ReceiveLoop(); + void StartLoop(); }; +// RoomMemberImpl +void RoomMember::RoomMemberImpl::SetState(const State new_state) { + state = new_state; + // TODO(B3N30): Invoke the callback functions +} + +bool RoomMember::RoomMemberImpl::IsConnected() const { + return state == State::Joining || state == State::Joined; +} + +void RoomMember::RoomMemberImpl::ReceiveLoop() { + // Receive packets while the connection is open + while (IsConnected()) { + std::lock_guard lock(network_mutex); + ENetEvent event; + if (enet_host_service(client, &event, 1000) > 0) { + if (event.type == ENET_EVENT_TYPE_RECEIVE) { + switch (event.packet->data[0]) { + // TODO(B3N30): Handle the other message types + case IdNameCollision: + SetState(State::NameCollision); + enet_packet_destroy(event.packet); + enet_peer_disconnect(server, 0); + enet_peer_reset(server); + return; + break; + case IdMacCollision: + SetState(State::MacCollision); + enet_packet_destroy(event.packet); + enet_peer_disconnect(server, 0); + enet_peer_reset(server); + return; + break; + default: + break; + } + enet_packet_destroy(event.packet); + } + } + } +}; + +void RoomMember::RoomMemberImpl::StartLoop() { + receive_thread = std::make_unique(&RoomMember::RoomMemberImpl::ReceiveLoop, this); +} + +// RoomMember RoomMember::RoomMember() : room_member_impl{std::make_unique()} { room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client"); @@ -44,7 +102,7 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv enet_host_connect(room_member_impl->client, &address, NumChannels, 0); if (!room_member_impl->server) { - room_member_impl->state = State::Error; + room_member_impl->SetState(State::Error); return; } @@ -52,22 +110,27 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs); if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { room_member_impl->nickname = nick; - room_member_impl->state = State::Joining; + room_member_impl->SetState(State::Joining); + room_member_impl->StartLoop(); // TODO(B3N30): Send a join request with the nickname to the server - // TODO(B3N30): Start the receive thread } else { - room_member_impl->state = State::CouldNotConnect; + room_member_impl->SetState(State::CouldNotConnect); } } bool RoomMember::IsConnected() const { - return room_member_impl->state == State::Joining || room_member_impl->state == State::Joined; + return room_member_impl->IsConnected(); } void RoomMember::Leave() { - enet_peer_disconnect(room_member_impl->server, 0); - room_member_impl->state = State::Idle; - // TODO(B3N30): Close the receive thread + ASSERT_MSG(room_member_impl->receive_thread != nullptr, "Must be in a room to leave it."); + { + std::lock_guard lock(room_member_impl->network_mutex); + enet_peer_disconnect(room_member_impl->server, 0); + room_member_impl->SetState(State::Idle); + } + room_member_impl->receive_thread->join(); + room_member_impl->receive_thread.reset(); enet_peer_reset(room_member_impl->server); } diff --git a/src/network/room_member.h b/src/network/room_member.h index 177622b69..89ec6ae5a 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include #include "common/common_types.h"