Browse Source
Implement basic virtual Room support based on enet (#2803)
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 classesnce_cpp
committed by
bunnei
15 changed files with 365 additions and 1 deletions
-
3.gitmodules
-
4externals/CMakeLists.txt
-
1externals/enet
-
1src/CMakeLists.txt
-
2src/citra_qt/CMakeLists.txt
-
3src/citra_qt/bootmanager.cpp
-
1src/common/logging/backend.cpp
-
1src/common/logging/log.h
-
16src/network/CMakeLists.txt
-
50src/network/network.cpp
-
25src/network/network.h
-
60src/network/room.cpp
-
60src/network/room.h
-
74src/network/room_member.cpp
-
65src/network/room_member.h
@ -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) |
|||
@ -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
|
|||
@ -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 |
|||
@ -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
|
|||
@ -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 |
|||
@ -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
|
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue