Implement basic virtual Room support based on enet (#2803)
* Added support for network with ENet lib, connecting is possible, but data can't be sent, yet. * fixup! Added support for network with ENet lib, * fixup! CLang * fixup! Added support for network with ENet lib, * fixup! Added support for network with ENet lib, * fixup! Clang format * More fixups! * Moved ENetHost* and ENetPeer* into pimpl classes * fixup! Moved ENetHost* and ENetPeer* into pimpl classes * fixup! Clang again * fixup! Moved ENetHost* and ENetPeer* into pimpl classes * fixup! Moved ENetHost* and ENetPeer* into pimpl classes * fixup! Moved ENetHost* and ENetPeer* into pimpl classes
This commit is contained in:
		
							
								
								
									
										16
									
								
								src/network/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/network/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| set(SRCS | ||||
|             network.cpp | ||||
|             room.cpp | ||||
|             room_member.cpp | ||||
|             ) | ||||
|  | ||||
| set(HEADERS | ||||
|             network.h | ||||
|             room.h | ||||
|             room_member.h | ||||
|             ) | ||||
|  | ||||
| create_directory_groups(${SRCS} ${HEADERS}) | ||||
|  | ||||
| add_library(network STATIC ${SRCS} ${HEADERS}) | ||||
| target_link_libraries(network PRIVATE common enet) | ||||
							
								
								
									
										50
									
								
								src/network/network.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/network/network.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| // Copyright 2017 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "enet/enet.h" | ||||
| #include "network/network.h" | ||||
|  | ||||
| namespace Network { | ||||
|  | ||||
| static std::shared_ptr<RoomMember> g_room_member; ///< RoomMember (Client) for network games | ||||
| static std::shared_ptr<Room> g_room;              ///< Room (Server) for network games | ||||
| // TODO(B3N30): Put these globals into a networking class | ||||
|  | ||||
| bool Init() { | ||||
|     if (enet_initialize() != 0) { | ||||
|         LOG_ERROR(Network, "Error initalizing ENet"); | ||||
|         return false; | ||||
|     } | ||||
|     g_room = std::make_shared<Room>(); | ||||
|     g_room_member = std::make_shared<RoomMember>(); | ||||
|     LOG_DEBUG(Network, "initialized OK"); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| std::weak_ptr<Room> GetRoom() { | ||||
|     return g_room; | ||||
| } | ||||
|  | ||||
| std::weak_ptr<RoomMember> GetRoomMember() { | ||||
|     return g_room_member; | ||||
| } | ||||
|  | ||||
| void Shutdown() { | ||||
|     if (g_room_member) { | ||||
|         if (g_room_member->IsConnected()) | ||||
|             g_room_member->Leave(); | ||||
|         g_room_member.reset(); | ||||
|     } | ||||
|     if (g_room) { | ||||
|         if (g_room->GetState() == Room::State::Open) | ||||
|             g_room->Destroy(); | ||||
|         g_room.reset(); | ||||
|     } | ||||
|     enet_deinitialize(); | ||||
|     LOG_DEBUG(Network, "shutdown OK"); | ||||
| } | ||||
|  | ||||
| } // namespace Network | ||||
							
								
								
									
										25
									
								
								src/network/network.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/network/network.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| // Copyright 2017 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
| #include "network/room.h" | ||||
| #include "network/room_member.h" | ||||
|  | ||||
| namespace Network { | ||||
|  | ||||
| /// Initializes and registers the network device, the room, and the room member. | ||||
| bool Init(); | ||||
|  | ||||
| /// Returns a pointer to the room handle | ||||
| std::weak_ptr<Room> GetRoom(); | ||||
|  | ||||
| /// Returns a pointer to the room member handle | ||||
| std::weak_ptr<RoomMember> GetRoomMember(); | ||||
|  | ||||
| /// Unregisters the network device, the room, and the room member and shut them down. | ||||
| void Shutdown(); | ||||
|  | ||||
| } // namespace Network | ||||
							
								
								
									
										60
									
								
								src/network/room.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/network/room.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| // Copyright 2017 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "enet/enet.h" | ||||
| #include "network/room.h" | ||||
|  | ||||
| namespace Network { | ||||
|  | ||||
| /// Maximum number of concurrent connections allowed to this room. | ||||
| static constexpr u32 MaxConcurrentConnections = 10; | ||||
|  | ||||
| class Room::RoomImpl { | ||||
| public: | ||||
|     ENetHost* server = nullptr; ///< Network interface. | ||||
|  | ||||
|     std::atomic<State> state{State::Closed}; ///< Current state of the room. | ||||
|     RoomInformation room_information;        ///< Information about this room. | ||||
| }; | ||||
|  | ||||
| Room::Room() : room_impl{std::make_unique<RoomImpl>()} {} | ||||
|  | ||||
| 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; | ||||
|     enet_address_set_host(&address, server_address.c_str()); | ||||
|     address.port = server_port; | ||||
|  | ||||
|     room_impl->server = enet_host_create(&address, MaxConcurrentConnections, NumChannels, 0, 0); | ||||
|     // TODO(B3N30): Allow specifying the maximum number of concurrent connections. | ||||
|     room_impl->state = State::Open; | ||||
|  | ||||
|     room_impl->room_information.name = name; | ||||
|     room_impl->room_information.member_slots = MaxConcurrentConnections; | ||||
|  | ||||
|     // TODO(B3N30): Start the receiving thread | ||||
| } | ||||
|  | ||||
| Room::State Room::GetState() const { | ||||
|     return room_impl->state; | ||||
| } | ||||
|  | ||||
| const RoomInformation& Room::GetRoomInformation() const { | ||||
|     return room_impl->room_information; | ||||
| } | ||||
|  | ||||
| void Room::Destroy() { | ||||
|     room_impl->state = State::Closed; | ||||
|     // TODO(B3n30): Join the receiving thread | ||||
|  | ||||
|     if (room_impl->server) { | ||||
|         enet_host_destroy(room_impl->server); | ||||
|     } | ||||
|     room_impl->room_information = {}; | ||||
|     room_impl->server = nullptr; | ||||
| } | ||||
|  | ||||
| } // namespace Network | ||||
							
								
								
									
										60
									
								
								src/network/room.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/network/room.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| // Copyright 2017 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <atomic> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace Network { | ||||
|  | ||||
| constexpr u16 DefaultRoomPort = 1234; | ||||
| constexpr size_t NumChannels = 1; // Number of channels used for the connection | ||||
|  | ||||
| struct RoomInformation { | ||||
|     std::string name; ///< Name of the server | ||||
|     u32 member_slots; ///< Maximum number of members in this room | ||||
| }; | ||||
|  | ||||
| /// This is what a server [person creating a server] would use. | ||||
| class Room final { | ||||
| public: | ||||
|     enum class State : u8 { | ||||
|         Open,   ///< The room is open and ready to accept connections. | ||||
|         Closed, ///< The room is not opened and can not accept connections. | ||||
|     }; | ||||
|  | ||||
|     Room(); | ||||
|     ~Room(); | ||||
|  | ||||
|     /** | ||||
|      * Gets the current state of the room. | ||||
|      */ | ||||
|     State GetState() const; | ||||
|  | ||||
|     /** | ||||
|      * Gets the room information of the room. | ||||
|      */ | ||||
|     const RoomInformation& GetRoomInformation() const; | ||||
|  | ||||
|     /** | ||||
|      * Creates the socket for this room. Will bind to default address if | ||||
|      * server is empty string. | ||||
|      */ | ||||
|     void Create(const std::string& name, const std::string& server = "", | ||||
|                 u16 server_port = DefaultRoomPort); | ||||
|  | ||||
|     /** | ||||
|      * Destroys the socket | ||||
|      */ | ||||
|     void Destroy(); | ||||
|  | ||||
| private: | ||||
|     class RoomImpl; | ||||
|     std::unique_ptr<RoomImpl> room_impl; | ||||
| }; | ||||
|  | ||||
| } // namespace Network | ||||
							
								
								
									
										74
									
								
								src/network/room_member.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/network/room_member.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| // Copyright 2017 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "enet/enet.h" | ||||
| #include "network/room_member.h" | ||||
|  | ||||
| namespace Network { | ||||
|  | ||||
| constexpr u32 ConnectionTimeoutMs = 5000; | ||||
|  | ||||
| class RoomMember::RoomMemberImpl { | ||||
| public: | ||||
|     ENetHost* client = nullptr; ///< ENet network interface. | ||||
|     ENetPeer* server = nullptr; ///< The server peer the client is connected to | ||||
|  | ||||
|     std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember. | ||||
|  | ||||
|     std::string nickname; ///< The nickname of this member. | ||||
| }; | ||||
|  | ||||
| RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} { | ||||
|     room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); | ||||
|     ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client"); | ||||
| } | ||||
|  | ||||
| RoomMember::~RoomMember() { | ||||
|     ASSERT_MSG(!IsConnected(), "RoomMember is being destroyed while connected"); | ||||
|     enet_host_destroy(room_member_impl->client); | ||||
| } | ||||
|  | ||||
| RoomMember::State RoomMember::GetState() const { | ||||
|     return room_member_impl->state; | ||||
| } | ||||
|  | ||||
| void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port, | ||||
|                       u16 client_port) { | ||||
|     ENetAddress address{}; | ||||
|     enet_address_set_host(&address, server_addr); | ||||
|     address.port = server_port; | ||||
|  | ||||
|     room_member_impl->server = | ||||
|         enet_host_connect(room_member_impl->client, &address, NumChannels, 0); | ||||
|  | ||||
|     if (!room_member_impl->server) { | ||||
|         room_member_impl->state = State::Error; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     ENetEvent event{}; | ||||
|     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; | ||||
|         // 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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool RoomMember::IsConnected() const { | ||||
|     return room_member_impl->state == State::Joining || room_member_impl->state == State::Joined; | ||||
| } | ||||
|  | ||||
| void RoomMember::Leave() { | ||||
|     enet_peer_disconnect(room_member_impl->server, 0); | ||||
|     room_member_impl->state = State::Idle; | ||||
|     // TODO(B3N30): Close the receive thread | ||||
|     enet_peer_reset(room_member_impl->server); | ||||
| } | ||||
|  | ||||
| } // namespace Network | ||||
							
								
								
									
										65
									
								
								src/network/room_member.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/network/room_member.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| // Copyright 2017 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <atomic> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "common/common_types.h" | ||||
| #include "network/room.h" | ||||
|  | ||||
| namespace Network { | ||||
|  | ||||
| /** | ||||
|  * This is what a client [person joining a server] would use. | ||||
|  * It also has to be used if you host a game yourself (You'd create both, a Room and a | ||||
|  * RoomMembership for yourself) | ||||
|  */ | ||||
| class RoomMember final { | ||||
| public: | ||||
|     enum class State : u8 { | ||||
|         Idle,    ///< Default state | ||||
|         Error,   ///< Some error [permissions to network device missing or something] | ||||
|         Joining, ///< The client is attempting to join a room. | ||||
|         Joined,  ///< The client is connected to the room and is ready to send/receive packets. | ||||
|         LostConnection, ///< Connection closed | ||||
|  | ||||
|         // Reasons why connection was rejected | ||||
|         NameCollision,  ///< Somebody is already using this name | ||||
|         MacCollision,   ///< Somebody is already using that mac-address | ||||
|         CouldNotConnect ///< The room is not responding to a connection attempt | ||||
|     }; | ||||
|  | ||||
|     RoomMember(); | ||||
|     ~RoomMember(); | ||||
|  | ||||
|     /** | ||||
|      * Returns the status of our connection to the room. | ||||
|      */ | ||||
|     State GetState() const; | ||||
|  | ||||
|     /** | ||||
|      * Returns whether we're connected to a server or not. | ||||
|      */ | ||||
|     bool IsConnected() const; | ||||
|  | ||||
|     /** | ||||
|      * Attempts to join a room at the specified address and port, using the specified nickname. | ||||
|      * This may fail if the username is already taken. | ||||
|      */ | ||||
|     void Join(const std::string& nickname, const char* server_addr = "127.0.0.1", | ||||
|               const u16 serverPort = DefaultRoomPort, const u16 clientPort = 0); | ||||
|  | ||||
|     /** | ||||
|      * Leaves the current room. | ||||
|      */ | ||||
|     void Leave(); | ||||
|  | ||||
| private: | ||||
|     class RoomMemberImpl; | ||||
|     std::unique_ptr<RoomMemberImpl> room_member_impl; | ||||
| }; | ||||
|  | ||||
| } // namespace Network | ||||
		Reference in New Issue
	
	Block a user