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