mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Network: Threads for Room and RoomMember
This commit is contained in:
		
							parent
							
								
									5137a198f9
								
							
						
					
					
						commit
						589dc083a5
					
				
					 4 changed files with 119 additions and 13 deletions
				
			
		|  | @ -2,6 +2,8 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <atomic> | ||||||
|  | #include <thread> | ||||||
| #include "enet/enet.h" | #include "enet/enet.h" | ||||||
| #include "network/room.h" | #include "network/room.h" | ||||||
| 
 | 
 | ||||||
|  | @ -16,8 +18,38 @@ public: | ||||||
| 
 | 
 | ||||||
|     std::atomic<State> state{State::Closed}; ///< Current state of the room.
 |     std::atomic<State> state{State::Closed}; ///< Current state of the room.
 | ||||||
|     RoomInformation room_information;        ///< Information about this room.
 |     RoomInformation room_information;        ///< Information about this room.
 | ||||||
|  | 
 | ||||||
|  |     /// Thread that receives and dispatches network packets
 | ||||||
|  |     std::unique_ptr<std::thread> 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<std::thread>(&Room::RoomImpl::ServerLoop, this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Room
 | ||||||
| Room::Room() : room_impl{std::make_unique<RoomImpl>()} {} | Room::Room() : room_impl{std::make_unique<RoomImpl>()} {} | ||||||
| 
 | 
 | ||||||
| Room::~Room() = default; | 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.name = name; | ||||||
|     room_impl->room_information.member_slots = MaxConcurrentConnections; |     room_impl->room_information.member_slots = MaxConcurrentConnections; | ||||||
| 
 |     room_impl->StartLoop(); | ||||||
|     // TODO(B3N30): Start the receiving thread
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Room::State Room::GetState() const { | Room::State Room::GetState() const { | ||||||
|  | @ -48,7 +79,8 @@ const RoomInformation& Room::GetRoomInformation() const { | ||||||
| 
 | 
 | ||||||
| void Room::Destroy() { | void Room::Destroy() { | ||||||
|     room_impl->state = State::Closed; |     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) { |     if (room_impl->server) { | ||||||
|         enet_host_destroy(room_impl->server); |         enet_host_destroy(room_impl->server); | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <atomic> |  | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | @ -19,6 +18,19 @@ struct RoomInformation { | ||||||
|     u32 member_slots; ///< Maximum number of members in this room
 |     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.
 | /// This is what a server [person creating a server] would use.
 | ||||||
| class Room final { | class Room final { | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -2,6 +2,9 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <atomic> | ||||||
|  | #include <mutex> | ||||||
|  | #include <thread> | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "enet/enet.h" | #include "enet/enet.h" | ||||||
| #include "network/room_member.h" | #include "network/room_member.h" | ||||||
|  | @ -16,10 +19,65 @@ public: | ||||||
|     ENetPeer* server = nullptr; ///< The server peer the client is connected to
 |     ENetPeer* server = nullptr; ///< The server peer the client is connected to
 | ||||||
| 
 | 
 | ||||||
|     std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
 |     std::atomic<State> 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::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<std::thread> 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<std::mutex> 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<std::thread>(&RoomMember::RoomMemberImpl::ReceiveLoop, this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // RoomMember
 | ||||||
| RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} { | RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} { | ||||||
|     room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); |     room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); | ||||||
|     ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client"); |     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); |         enet_host_connect(room_member_impl->client, &address, NumChannels, 0); | ||||||
| 
 | 
 | ||||||
|     if (!room_member_impl->server) { |     if (!room_member_impl->server) { | ||||||
|         room_member_impl->state = State::Error; |         room_member_impl->SetState(State::Error); | ||||||
|         return; |         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); |     int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs); | ||||||
|     if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { |     if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { | ||||||
|         room_member_impl->nickname = nick; |         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): Send a join request with the nickname to the server
 | ||||||
|         // TODO(B3N30): Start the receive thread
 |  | ||||||
|     } else { |     } else { | ||||||
|         room_member_impl->state = State::CouldNotConnect; |         room_member_impl->SetState(State::CouldNotConnect); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RoomMember::IsConnected() const { | 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() { | void RoomMember::Leave() { | ||||||
|  |     ASSERT_MSG(room_member_impl->receive_thread != nullptr, "Must be in a room to leave it."); | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(room_member_impl->network_mutex); | ||||||
|         enet_peer_disconnect(room_member_impl->server, 0); |         enet_peer_disconnect(room_member_impl->server, 0); | ||||||
|     room_member_impl->state = State::Idle; |         room_member_impl->SetState(State::Idle); | ||||||
|     // TODO(B3N30): Close the receive thread
 |     } | ||||||
|  |     room_member_impl->receive_thread->join(); | ||||||
|  |     room_member_impl->receive_thread.reset(); | ||||||
|     enet_peer_reset(room_member_impl->server); |     enet_peer_reset(room_member_impl->server); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <atomic> |  | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue