Compare commits

..

1 Commits

Author SHA1 Message Date
Smile Yik
568f54ec73 network: play together in different room 2022-11-08 14:03:25 +08:00
10 changed files with 420 additions and 11 deletions

View File

@@ -384,8 +384,7 @@ void KScheduler::SwitchThread(KThread* next_thread) {
void KScheduler::ScheduleImpl() {
// First, clear the needs scheduling bool.
m_state.needs_scheduling.store(false, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
// Load the appropriate thread pointers for scheduling.
KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
@@ -401,8 +400,7 @@ void KScheduler::ScheduleImpl() {
// If there aren't, we want to check if the highest priority thread is the same as the current
// thread.
if (highest_priority_thread == cur_thread) {
// If they're the same, then we can just issue a memory barrier and return.
std::atomic_thread_fence(std::memory_order_seq_cst);
// If they're the same, then we can just return.
return;
}
@@ -478,8 +476,7 @@ void KScheduler::ScheduleImplFiber() {
// We failed to successfully do the context switch, and need to retry.
// Clear needs_scheduling.
m_state.needs_scheduling.store(false, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
// Refresh the highest priority thread.
highest_priority_thread = m_state.highest_priority_thread;

View File

@@ -60,9 +60,6 @@ public:
// Release an instance of the lock.
if ((--lock_count) == 0) {
// Perform a memory barrier here.
std::atomic_thread_fence(std::memory_order_seq_cst);
// We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling =
SchedulerType::UpdateHighestPriorityThreads(kernel);

View File

@@ -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;

View File

@@ -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)

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View 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;
}
}

View 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;
};
}