network: play together in different room
This commit is contained in:
@@ -61,7 +61,10 @@ static void PrintHelp(const char* argv0) {
|
||||
"--log-file The file for storing the room log\n"
|
||||
"--enable-yuzu-mods Allow yuzu Community Moderators to moderate on your room\n"
|
||||
"-h, --help Display this help and exit\n"
|
||||
"-v, --version Output version information and exit\n",
|
||||
"-v, --version Output version information and exit\n"
|
||||
"--room-post-office Open server as a room post office\n"
|
||||
"--room-post-host Room post office host\n"
|
||||
"--room-post-port Room post office port\n",
|
||||
argv0);
|
||||
}
|
||||
|
||||
@@ -180,6 +183,35 @@ static void InitializeLogging(const std::string& log_file) {
|
||||
Common::Log::Start();
|
||||
}
|
||||
|
||||
static int RunRoomPostOffice(Common::DetachedTasks& detached_tasks,
|
||||
u32 port) {
|
||||
if (port > UINT16_MAX) {
|
||||
LOG_ERROR(Network, "Port needs to be in the range 0 - 65535!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Network::RoomNetwork network{};
|
||||
network.Init();
|
||||
if (auto post_office = network.GetRoomPostOffice().lock()) {
|
||||
if (!post_office->Create("", port)) {
|
||||
LOG_INFO(Network, "Failed to create room post office: ");
|
||||
return -1;
|
||||
}
|
||||
LOG_INFO(Network, "Room post office is open. Close with Q+Enter...");
|
||||
while (true) {
|
||||
std::string in;
|
||||
std::cin >> in;
|
||||
if (in.size() > 0) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
network.Shutdown();
|
||||
detached_tasks.WaitForAllTasks();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Application entry point
|
||||
int main(int argc, char** argv) {
|
||||
Common::DetachedTasks detached_tasks;
|
||||
@@ -195,10 +227,14 @@ int main(int argc, char** argv) {
|
||||
std::string web_api_url;
|
||||
std::string ban_list_file;
|
||||
std::string log_file = "yuzu-room.log";
|
||||
std::string room_post_host = "127.0.0.1";
|
||||
u64 preferred_game_id = 0;
|
||||
u32 port = Network::DefaultRoomPort;
|
||||
u32 room_post_port = Network::DefaultRoomPort;
|
||||
u32 max_members = 16;
|
||||
bool enable_yuzu_mods = false;
|
||||
bool room_post_office = false;
|
||||
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"room-name", required_argument, 0, 'n'},
|
||||
@@ -216,6 +252,9 @@ int main(int argc, char** argv) {
|
||||
{"enable-yuzu-mods", no_argument, 0, 'e'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"room-post-office", no_argument, 0, 'c'},
|
||||
{"room-post-host", required_argument, 0, 'f'},
|
||||
{"room-post-port", required_argument, 0, 'j'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
@@ -270,10 +309,23 @@ int main(int argc, char** argv) {
|
||||
case 'v':
|
||||
PrintVersion();
|
||||
return 0;
|
||||
case 'c':
|
||||
room_post_office = true;
|
||||
break;
|
||||
case 'f':
|
||||
room_post_host.assign(optarg);
|
||||
break;
|
||||
case 'j':
|
||||
room_post_port = strtoul(optarg, &endarg, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (room_post_office) {
|
||||
return RunRoomPostOffice(detached_tasks, room_post_port);
|
||||
}
|
||||
|
||||
if (room_name.empty()) {
|
||||
LOG_ERROR(Network, "Room name is empty!");
|
||||
PrintHelp(argv[0]);
|
||||
@@ -353,6 +405,10 @@ int main(int argc, char** argv) {
|
||||
verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
|
||||
}
|
||||
|
||||
if (!room_post_host.empty()) {
|
||||
room_post_office = true;
|
||||
}
|
||||
|
||||
Network::RoomNetwork network{};
|
||||
network.Init();
|
||||
if (auto room = network.GetRoom().lock()) {
|
||||
@@ -369,6 +425,9 @@ int main(int argc, char** argv) {
|
||||
if (announce) {
|
||||
announce_session->Start();
|
||||
}
|
||||
if (room_post_office && !room->HasMailBox()) {
|
||||
room->SetupMailBox(room_post_host, room_post_port);
|
||||
}
|
||||
while (room->GetState() == Network::Room::State::Open) {
|
||||
std::string in;
|
||||
std::cin >> in;
|
||||
|
||||
@@ -14,6 +14,8 @@ add_library(network STATIC
|
||||
room_member.h
|
||||
verify_user.cpp
|
||||
verify_user.h
|
||||
room_post_office.cpp
|
||||
room_post_office.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(network)
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Network {
|
||||
RoomNetwork::RoomNetwork() {
|
||||
m_room = std::make_shared<Room>();
|
||||
m_room_member = std::make_shared<RoomMember>();
|
||||
m_room_post_office = std::make_shared<RoomPostOffice>();
|
||||
}
|
||||
|
||||
bool RoomNetwork::Init() {
|
||||
@@ -20,10 +21,15 @@ bool RoomNetwork::Init() {
|
||||
}
|
||||
m_room = std::make_shared<Room>();
|
||||
m_room_member = std::make_shared<RoomMember>();
|
||||
m_room_post_office = std::make_shared<RoomPostOffice>();
|
||||
LOG_DEBUG(Network, "initialized OK");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::weak_ptr<RoomPostOffice> RoomNetwork::GetRoomPostOffice() {
|
||||
return m_room_post_office;
|
||||
}
|
||||
|
||||
std::weak_ptr<Room> RoomNetwork::GetRoom() {
|
||||
return m_room;
|
||||
}
|
||||
@@ -43,6 +49,11 @@ void RoomNetwork::Shutdown() {
|
||||
m_room->Destroy();
|
||||
m_room.reset();
|
||||
}
|
||||
if (m_room_post_office) {
|
||||
if (m_room_post_office->GetState() == RoomPostOffice::State::Open)
|
||||
m_room_post_office->Destroy();
|
||||
m_room_post_office.reset();
|
||||
}
|
||||
enet_deinitialize();
|
||||
LOG_DEBUG(Network, "shutdown OK");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include "network/room.h"
|
||||
#include "network/room_member.h"
|
||||
#include "network/room_post_office.h"
|
||||
|
||||
namespace Network {
|
||||
|
||||
@@ -16,6 +17,9 @@ public:
|
||||
/// Initializes and registers the network device, the room, and the room member.
|
||||
bool Init();
|
||||
|
||||
/// Returns a pointer to the room post office handle
|
||||
std::weak_ptr<RoomPostOffice> GetRoomPostOffice();
|
||||
|
||||
/// Returns a pointer to the room handle
|
||||
std::weak_ptr<Room> GetRoom();
|
||||
|
||||
@@ -28,6 +32,7 @@ public:
|
||||
private:
|
||||
std::shared_ptr<RoomMember> m_room_member; ///< RoomMember (Client) for network games
|
||||
std::shared_ptr<Room> m_room; ///< Room (Server) for network games
|
||||
std::shared_ptr<RoomPostOffice> m_room_post_office; ///< Room (Relay Server) for rooms
|
||||
};
|
||||
|
||||
} // namespace Network
|
||||
|
||||
@@ -48,6 +48,16 @@ public:
|
||||
IPBanList ip_ban_list; ///< List of banned IP addresses
|
||||
mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists
|
||||
|
||||
struct PostOffice {
|
||||
const u8 WaitPacket = 10; ///< Wait post office's reply max times.
|
||||
ENetPeer* peer = nullptr; ///< post office's peer
|
||||
std::string address; ///< post office's address. Will skip if this is empty.
|
||||
u16 port; ///< post office's port
|
||||
u8 wait = 0; ///< Wait post office's reply times.
|
||||
};
|
||||
PostOffice post_office;
|
||||
|
||||
|
||||
RoomImpl() : random_gen(std::random_device()()) {}
|
||||
|
||||
/// Thread that receives and dispatches network packets
|
||||
@@ -234,6 +244,26 @@ public:
|
||||
* to all other clients.
|
||||
*/
|
||||
void HandleClientDisconnection(ENetPeer* client);
|
||||
|
||||
/**
|
||||
* Post recived packet to room post office.
|
||||
*/
|
||||
void HandlePostMail(const ENetEvent* event);
|
||||
|
||||
/**
|
||||
* Check connection between this and room post office.
|
||||
*/
|
||||
void HandlePostOfficeConnection(const ENetEvent* event);
|
||||
|
||||
/**
|
||||
* Connect to room post office.
|
||||
*/
|
||||
bool ConnectPostOffice();
|
||||
|
||||
/**
|
||||
* Disconnect room post office
|
||||
*/
|
||||
void DisconnectPostOffice();
|
||||
};
|
||||
|
||||
// RoomImpl
|
||||
@@ -251,9 +281,11 @@ void Room::RoomImpl::ServerLoop() {
|
||||
HandleGameInfoPacket(&event);
|
||||
break;
|
||||
case IdProxyPacket:
|
||||
HandlePostMail(&event);
|
||||
HandleProxyPacket(&event);
|
||||
break;
|
||||
case IdLdnPacket:
|
||||
HandlePostMail(&event);
|
||||
HandleLdnPacket(&event);
|
||||
break;
|
||||
case IdChatMessage:
|
||||
@@ -283,9 +315,11 @@ void Room::RoomImpl::ServerLoop() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
HandlePostOfficeConnection(&event);
|
||||
}
|
||||
// Close the connection to all members:
|
||||
SendCloseMessage();
|
||||
DisconnectPostOffice();
|
||||
}
|
||||
|
||||
void Room::RoomImpl::StartLoop() {
|
||||
@@ -858,6 +892,8 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
|
||||
});
|
||||
if (member != members.end()) {
|
||||
enet_peer_send(member->peer, 0, enet_packet);
|
||||
} else if (event->peer == post_office.peer) {
|
||||
enet_packet_destroy(enet_packet);
|
||||
} else {
|
||||
LOG_ERROR(Network,
|
||||
"Attempting to send to unknown IP address: "
|
||||
@@ -912,7 +948,9 @@ void Room::RoomImpl::HandleLdnPacket(const ENetEvent* event) {
|
||||
});
|
||||
if (member != members.end()) {
|
||||
enet_peer_send(member->peer, 0, enet_packet);
|
||||
} else {
|
||||
} else if (event->peer == post_office.peer) {
|
||||
enet_packet_destroy(enet_packet);
|
||||
} else {
|
||||
LOG_ERROR(Network,
|
||||
"Attempting to send to unknown IP address: "
|
||||
"{}.{}.{}.{}",
|
||||
@@ -1037,6 +1075,76 @@ void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
|
||||
BroadcastRoomInformation();
|
||||
}
|
||||
|
||||
void Room::RoomImpl::HandlePostMail(const ENetEvent* event) {
|
||||
if (!post_office.peer) {
|
||||
ConnectPostOffice();
|
||||
}
|
||||
if (post_office.peer && event->peer != post_office.peer) {
|
||||
enet_peer_send(post_office.peer, 0, event->packet);
|
||||
}
|
||||
}
|
||||
|
||||
bool Room::RoomImpl::ConnectPostOffice() {
|
||||
|
||||
if (post_office.address.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(Network,
|
||||
"Try to Connecting room post office {}:{} ...",
|
||||
post_office.address, post_office.port);
|
||||
|
||||
ENetAddress address{};
|
||||
enet_address_set_host(&address, post_office.address.c_str());
|
||||
address.port = post_office.port;
|
||||
post_office.peer =
|
||||
enet_host_connect(server, &address, NumChannels, 0);
|
||||
if (!post_office.peer) {
|
||||
LOG_ERROR(Network,
|
||||
"Connect to room post office {}:{} failed!",
|
||||
post_office.address, post_office.port);
|
||||
return false;
|
||||
}
|
||||
post_office.wait = post_office.WaitPacket - 1;
|
||||
|
||||
// LOG_INFO(Network,
|
||||
// "Connect to room post office {}:{} success!",
|
||||
// post_office.address, post_office.port);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline
|
||||
void Room::RoomImpl::DisconnectPostOffice() {
|
||||
post_office.address = "";
|
||||
if (post_office.peer) {
|
||||
enet_peer_disconnect(post_office.peer, 0);
|
||||
post_office.peer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void Room::RoomImpl::HandlePostOfficeConnection(const ENetEvent* event) {
|
||||
if (post_office.address.empty() || post_office.wait == post_office.WaitPacket) {
|
||||
return;
|
||||
} else if (event->peer == post_office.peer) {
|
||||
post_office.wait = post_office.WaitPacket;
|
||||
LOG_INFO(Network,
|
||||
"Connect to room post office {}:{} success!",
|
||||
post_office.address, post_office.port);
|
||||
return;
|
||||
}
|
||||
--post_office.wait;
|
||||
if (post_office.wait == 0) {
|
||||
post_office.wait = 0xFF;
|
||||
LOG_ERROR(Network,
|
||||
"Room post office {}:{} have not response!",
|
||||
post_office.address, post_office.port);
|
||||
} else if (post_office.wait == post_office.WaitPacket + 1) {
|
||||
ConnectPostOffice();
|
||||
}
|
||||
}
|
||||
|
||||
// Room
|
||||
Room::Room() : room_impl{std::make_unique<RoomImpl>()} {}
|
||||
|
||||
@@ -1140,4 +1248,15 @@ void Room::Destroy() {
|
||||
room_impl->room_information.name.clear();
|
||||
}
|
||||
|
||||
bool Room::SetupMailBox(const std::string& server_address, u16 server_port) {
|
||||
room_impl->post_office.address = server_address;
|
||||
room_impl->post_office.port = server_port;
|
||||
|
||||
return room_impl->ConnectPostOffice();
|
||||
}
|
||||
|
||||
bool Room::HasMailBox() {
|
||||
return room_impl->post_office.peer;
|
||||
}
|
||||
|
||||
} // namespace Network
|
||||
|
||||
@@ -140,6 +140,13 @@ public:
|
||||
*/
|
||||
void Destroy();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool SetupMailBox(const std::string& server_address, u16 server_port);
|
||||
|
||||
bool HasMailBox();
|
||||
|
||||
private:
|
||||
class RoomImpl;
|
||||
std::unique_ptr<RoomImpl> room_impl;
|
||||
|
||||
165
src/network/room_post_office.cpp
Normal file
165
src/network/room_post_office.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
#include <shared_mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include "common/logging/log.h"
|
||||
#include "enet/enet.h"
|
||||
#include "network/packet.h"
|
||||
#include "network/room.h"
|
||||
#include "network/room_post_office.h"
|
||||
|
||||
namespace Network {
|
||||
class RoomPostOffice::RoomPostOfficeImpl {
|
||||
public:
|
||||
ENetHost* server = nullptr;
|
||||
/// Thread that receives and dispatches network packets
|
||||
std::unique_ptr<std::thread> room_center_thread;
|
||||
|
||||
std::atomic<State> state{State::Closed};
|
||||
|
||||
class Mailbox {
|
||||
public:
|
||||
ENetPeer* peer; ///< the remote room's peer
|
||||
};
|
||||
using MailboxList = std::vector<Mailbox>;
|
||||
/// remote rooms
|
||||
MailboxList mailboxes;
|
||||
|
||||
RoomPostOfficeImpl() {}
|
||||
|
||||
/// Thread function that will receive and dispatch messages until the room is destroyed.
|
||||
void ServerLoop();
|
||||
void StartLoop();
|
||||
|
||||
/**
|
||||
* Handle client disconnection.
|
||||
*/
|
||||
void HandleClientDisconnection(const ENetPeer* peer);
|
||||
/**
|
||||
* Handle mail.
|
||||
*/
|
||||
void HandleMail(const ENetEvent* event);
|
||||
/**
|
||||
* Handle the room joined room post office.
|
||||
*/
|
||||
void HandleJoinMailbox(const ENetEvent* event);
|
||||
};
|
||||
|
||||
RoomPostOffice::RoomPostOffice() : room_post_office_impl{std::make_unique<RoomPostOfficeImpl>()} {}
|
||||
RoomPostOffice::~RoomPostOffice() = default;
|
||||
|
||||
void RoomPostOffice::RoomPostOfficeImpl::ServerLoop() {
|
||||
while (state != State::Closed) {
|
||||
ENetEvent event;
|
||||
if (enet_host_service(server, &event, 5) > 0) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
HandleMail(&event);
|
||||
enet_packet_destroy(event.packet);
|
||||
break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
HandleClientDisconnection(event.peer);
|
||||
break;
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
break;
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
HandleJoinMailbox(&event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const Mailbox& mailbox : mailboxes) {
|
||||
enet_peer_disconnect(mailbox.peer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool RoomPostOffice::Create(const std::string& server_address, u16 server_port) {
|
||||
ENetAddress address;
|
||||
address.host = ENET_HOST_ANY;
|
||||
if (!server_address.empty()) {
|
||||
enet_address_set_host(&address, server_address.c_str());
|
||||
}
|
||||
address.port = server_port;
|
||||
room_post_office_impl->server = enet_host_create(&address, 16, NumChannels, 0, 0);
|
||||
if (!room_post_office_impl->server) {
|
||||
return false;
|
||||
}
|
||||
room_post_office_impl->state = State::Open;
|
||||
room_post_office_impl->StartLoop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RoomPostOffice::Destroy() {
|
||||
room_post_office_impl->state = State::Closed;
|
||||
room_post_office_impl->room_center_thread->join();
|
||||
room_post_office_impl->room_center_thread.reset();
|
||||
if (room_post_office_impl->server) {
|
||||
enet_host_destroy(room_post_office_impl->server);
|
||||
}
|
||||
room_post_office_impl->server = nullptr;
|
||||
room_post_office_impl->mailboxes.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RoomPostOffice::RoomPostOfficeImpl::StartLoop() {
|
||||
room_center_thread = std::make_unique<std::thread>(&RoomPostOffice::RoomPostOfficeImpl::ServerLoop, this);
|
||||
}
|
||||
|
||||
void RoomPostOffice::RoomPostOfficeImpl::HandleClientDisconnection(const ENetPeer* peer) {
|
||||
if (!peer) {
|
||||
return;
|
||||
}
|
||||
const auto& mailbox =
|
||||
std::find_if(mailboxes.begin(), mailboxes.end(), [peer](const Mailbox& mailbox_entry) {
|
||||
return mailbox_entry.peer == peer;
|
||||
});
|
||||
if (mailbox != mailboxes.end()) {
|
||||
mailboxes.erase(mailbox);
|
||||
LOG_INFO(Network, "some room leave to this room center");
|
||||
}
|
||||
}
|
||||
|
||||
void RoomPostOffice::RoomPostOfficeImpl::HandleMail(const ENetEvent* event) {
|
||||
for (const auto& mailbox : mailboxes) {
|
||||
if (mailbox.peer != event->peer) {
|
||||
enet_peer_send(mailbox.peer, 0, event->packet);
|
||||
}
|
||||
}
|
||||
enet_host_flush(server);
|
||||
}
|
||||
|
||||
void RoomPostOffice::RoomPostOfficeImpl::HandleJoinMailbox(const ENetEvent* event) {
|
||||
if (!event || !event->peer) {
|
||||
return;
|
||||
}
|
||||
Mailbox mailbox{};
|
||||
mailbox.peer = event->peer;
|
||||
|
||||
const auto& result =
|
||||
std::find_if(mailboxes.begin(), mailboxes.end(), [mailbox] (const Mailbox& mailbox_entry) {
|
||||
return mailbox_entry.peer == mailbox.peer;
|
||||
});
|
||||
if (result == mailboxes.end()) {
|
||||
mailboxes.push_back(mailbox);
|
||||
ENetAddress address = event->peer->address;
|
||||
LOG_INFO(Network,
|
||||
"{}.{}.{}.{}:{} Join to this room post office",
|
||||
(address.host ) & 0xFF,(address.host >> 8) & 0xFF,
|
||||
(address.host >> 16) & 0xFF,(address.host >> 24) & 0xFF,address.port);
|
||||
}
|
||||
}
|
||||
|
||||
RoomPostOffice::State RoomPostOffice::GetState() const {
|
||||
return room_post_office_impl->state;
|
||||
}
|
||||
|
||||
}
|
||||
47
src/network/room_post_office.h
Normal file
47
src/network/room_post_office.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "network/verify_user.h"
|
||||
|
||||
namespace Network {
|
||||
class RoomPostOffice final {
|
||||
public:
|
||||
enum class State : u8 {
|
||||
Open, ///< The room post office is open and ready to accept connections.
|
||||
Closed, ///< The room post office is not opened and can not accept connections.
|
||||
};
|
||||
|
||||
RoomPostOffice();
|
||||
~RoomPostOffice();
|
||||
|
||||
/**
|
||||
* Gets the current state of the room post office.
|
||||
*/
|
||||
State GetState() const;
|
||||
|
||||
/**
|
||||
* Creates the socket for this room post office.
|
||||
* Will bind to default address if server is empty string.
|
||||
*/
|
||||
bool Create(const std::string& server_addr, u16 server_port);
|
||||
|
||||
/**
|
||||
* Destorys the socket.
|
||||
*/
|
||||
bool Destroy();
|
||||
|
||||
private:
|
||||
class RoomPostOfficeImpl;
|
||||
std::unique_ptr<RoomPostOfficeImpl> room_post_office_impl;
|
||||
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user