From 705f7db84dd85555a6aef1e136cf251725cef293 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Sat, 25 Dec 2021 20:27:52 +0100 Subject: yuzu: Add ui files for multiplayer rooms --- src/core/CMakeLists.txt | 14 +- src/core/announce_multiplayer_session.cpp | 165 ++++++ src/core/announce_multiplayer_session.h | 96 ++++ src/core/core.cpp | 17 +- src/core/hle/service/nifm/nifm.cpp | 4 +- src/core/hle/service/sockets/bsd.cpp | 4 +- src/core/hle/service/sockets/bsd.h | 2 +- src/core/hle/service/sockets/sockets_translate.cpp | 2 +- src/core/hle/service/sockets/sockets_translate.h | 2 +- src/core/internal_network/network.cpp | 637 +++++++++++++++++++++ src/core/internal_network/network.h | 117 ++++ src/core/internal_network/network_interface.cpp | 209 +++++++ src/core/internal_network/network_interface.h | 28 + src/core/internal_network/sockets.h | 95 +++ src/core/network/network.cpp | 637 --------------------- src/core/network/network.h | 117 ---- src/core/network/network_interface.cpp | 209 ------- src/core/network/network_interface.h | 28 - src/core/network/sockets.h | 94 --- 19 files changed, 1378 insertions(+), 1099 deletions(-) create mode 100644 src/core/announce_multiplayer_session.cpp create mode 100644 src/core/announce_multiplayer_session.h create mode 100644 src/core/internal_network/network.cpp create mode 100644 src/core/internal_network/network.h create mode 100644 src/core/internal_network/network_interface.cpp create mode 100644 src/core/internal_network/network_interface.h create mode 100644 src/core/internal_network/sockets.h delete mode 100644 src/core/network/network.cpp delete mode 100644 src/core/network/network.h delete mode 100644 src/core/network/network_interface.cpp delete mode 100644 src/core/network/network_interface.h delete mode 100644 src/core/network/sockets.h (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 32cc2f392..48f5c1ee0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,4 +1,6 @@ add_library(core STATIC + announce_multiplayer_session.cpp + announce_multiplayer_session.h arm/arm_interface.h arm/arm_interface.cpp arm/cpu_interrupt_handler.cpp @@ -741,11 +743,11 @@ add_library(core STATIC memory/dmnt_cheat_vm.h memory.cpp memory.h - network/network.cpp - network/network.h - network/network_interface.cpp - network/network_interface.h - network/sockets.h + internal_network/network.cpp + internal_network/network.h + internal_network/network_interface.cpp + internal_network/network_interface.h + internal_network/sockets.h perf_stats.cpp perf_stats.h reporter.cpp @@ -780,7 +782,7 @@ endif() create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp new file mode 100644 index 000000000..a680ad202 --- /dev/null +++ b/src/core/announce_multiplayer_session.cpp @@ -0,0 +1,165 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "announce_multiplayer_session.h" +#include "common/announce_multiplayer_room.h" +#include "common/assert.h" +#include "common/settings.h" +#include "network/network.h" + +#ifdef ENABLE_WEB_SERVICE +#include "web_service/announce_room_json.h" +#endif + +namespace Core { + +// Time between room is announced to web_service +static constexpr std::chrono::seconds announce_time_interval(15); + +AnnounceMultiplayerSession::AnnounceMultiplayerSession() { +#ifdef ENABLE_WEB_SERVICE + backend = std::make_unique(Settings::values.web_api_url.GetValue(), + Settings::values.yuzu_username.GetValue(), + Settings::values.yuzu_token.GetValue()); +#else + backend = std::make_unique(); +#endif +} + +WebService::WebResult AnnounceMultiplayerSession::Register() { + std::shared_ptr room = Network::GetRoom().lock(); + if (!room) { + return WebService::WebResult{WebService::WebResult::Code::LibError, + "Network is not initialized"}; + } + if (room->GetState() != Network::Room::State::Open) { + return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open"}; + } + UpdateBackendData(room); + WebService::WebResult result = backend->Register(); + if (result.result_code != WebService::WebResult::Code::Success) { + return result; + } + LOG_INFO(WebService, "Room has been registered"); + room->SetVerifyUID(result.returned_data); + registered = true; + return WebService::WebResult{WebService::WebResult::Code::Success}; +} + +void AnnounceMultiplayerSession::Start() { + if (announce_multiplayer_thread) { + Stop(); + } + shutdown_event.Reset(); + announce_multiplayer_thread = + std::make_unique(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this); +} + +void AnnounceMultiplayerSession::Stop() { + if (announce_multiplayer_thread) { + shutdown_event.Set(); + announce_multiplayer_thread->join(); + announce_multiplayer_thread.reset(); + backend->Delete(); + registered = false; + } +} + +AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback( + std::function function) { + std::lock_guard lock(callback_mutex); + auto handle = std::make_shared>(function); + error_callbacks.insert(handle); + return handle; +} + +void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) { + std::lock_guard lock(callback_mutex); + error_callbacks.erase(handle); +} + +AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { + Stop(); +} + +void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr room) { + Network::RoomInformation room_information = room->GetRoomInformation(); + std::vector memberlist = room->GetRoomMemberList(); + backend->SetRoomInformation( + room_information.name, room_information.description, room_information.port, + room_information.member_slots, Network::network_version, room->HasPassword(), + room_information.preferred_game, room_information.preferred_game_id); + backend->ClearPlayers(); + for (const auto& member : memberlist) { + backend->AddPlayer(member.username, member.nickname, member.avatar_url, member.mac_address, + member.game_info.id, member.game_info.name); + } +} + +void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { + // Invokes all current bound error callbacks. + const auto ErrorCallback = [this](WebService::WebResult result) { + std::lock_guard lock(callback_mutex); + for (auto callback : error_callbacks) { + (*callback)(result); + } + }; + + if (!registered) { + WebService::WebResult result = Register(); + if (result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(result); + return; + } + } + + auto update_time = std::chrono::steady_clock::now(); + std::future future; + while (!shutdown_event.WaitUntil(update_time)) { + update_time += announce_time_interval; + std::shared_ptr room = Network::GetRoom().lock(); + if (!room) { + break; + } + if (room->GetState() != Network::Room::State::Open) { + break; + } + UpdateBackendData(room); + WebService::WebResult result = backend->Update(); + if (result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(result); + } + if (result.result_string == "404") { + registered = false; + // Needs to register the room again + WebService::WebResult register_result = Register(); + if (register_result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(register_result); + } + } + } +} + +AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() { + return backend->GetRoomList(); +} + +bool AnnounceMultiplayerSession::IsRunning() const { + return announce_multiplayer_thread != nullptr; +} + +void AnnounceMultiplayerSession::UpdateCredentials() { + ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running"); + +#ifdef ENABLE_WEB_SERVICE + backend = std::make_unique(Settings::values.web_api_url.GetValue(), + Settings::values.yuzu_username.GetValue(), + Settings::values.yuzu_token.GetValue()); +#endif +} + +} // namespace Core diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h new file mode 100644 index 000000000..2aaf55017 --- /dev/null +++ b/src/core/announce_multiplayer_session.h @@ -0,0 +1,96 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "common/announce_multiplayer_room.h" +#include "common/common_types.h" +#include "common/thread.h" + +namespace Network { +class Room; +} + +namespace Core { + +/** + * Instruments AnnounceMultiplayerRoom::Backend. + * Creates a thread that regularly updates the room information and submits them + * An async get of room information is also possible + */ +class AnnounceMultiplayerSession { +public: + using CallbackHandle = std::shared_ptr>; + AnnounceMultiplayerSession(); + ~AnnounceMultiplayerSession(); + + /** + * Allows to bind a function that will get called if the announce encounters an error + * @param function The function that gets called + * @return A handle that can be used the unbind the function + */ + CallbackHandle BindErrorCallback(std::function function); + + /** + * Unbind a function from the error callbacks + * @param handle The handle for the function that should get unbind + */ + void UnbindErrorCallback(CallbackHandle handle); + + /** + * Registers a room to web services + * @return The result of the registration attempt. + */ + WebService::WebResult Register(); + + /** + * Starts the announce of a room to web services + */ + void Start(); + + /** + * Stops the announce to web services + */ + void Stop(); + + /** + * Returns a list of all room information the backend got + * @param func A function that gets executed when the async get finished, e.g. a signal + * @return a list of rooms received from the web service + */ + AnnounceMultiplayerRoom::RoomList GetRoomList(); + + /** + * Whether the announce session is still running + */ + bool IsRunning() const; + + /** + * Recreates the backend, updating the credentials. + * This can only be used when the announce session is not running. + */ + void UpdateCredentials(); + +private: + Common::Event shutdown_event; + std::mutex callback_mutex; + std::set error_callbacks; + std::unique_ptr announce_multiplayer_thread; + + /// Backend interface that logs fields + std::unique_ptr backend; + + std::atomic_bool registered = false; ///< Whether the room has been registered + + void UpdateBackendData(std::shared_ptr room); + void AnnounceMultiplayerLoop(); +}; + +} // namespace Core diff --git a/src/core/core.cpp b/src/core/core.cpp index 0ede0d85c..5df32c1e7 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -43,14 +43,15 @@ #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/time/time_manager.h" +#include "core/internal_network/network.h" #include "core/loader/loader.h" #include "core/memory.h" #include "core/memory/cheat_engine.h" -#include "core/network/network.h" #include "core/perf_stats.h" #include "core/reporter.h" #include "core/telemetry_session.h" #include "core/tools/freezer.h" +#include "network/network.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -315,6 +316,15 @@ struct System::Impl { GetAndResetPerfStats(); perf_stats->BeginSystemFrame(); + std::string name = "Unknown Game"; + const Loader::ResultStatus res{app_loader->ReadTitle(name)}; + if (auto room_member = Network::GetRoomMember().lock()) { + Network::GameInfo game_info; + game_info.name = name; + game_info.id = program_id; + room_member->SendGameInfo(game_info); + } + status = SystemResultStatus::Success; return status; } @@ -362,6 +372,11 @@ struct System::Impl { memory.Reset(); applet_manager.ClearAll(); + if (auto room_member = Network::GetRoomMember().lock()) { + Network::GameInfo game_info{}; + room_member->SendGameInfo(game_info); + } + LOG_DEBUG(Core, "Shutdown OK"); } diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 7055ea93e..2889973e4 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -18,8 +18,8 @@ namespace { } // Anonymous namespace -#include "core/network/network.h" -#include "core/network/network_interface.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" namespace Service::NIFM { diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 3e9dc4a13..c7194731e 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -13,8 +13,8 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/service/sockets/bsd.h" #include "core/hle/service/sockets/sockets_translate.h" -#include "core/network/network.h" -#include "core/network/sockets.h" +#include "core/internal_network/network.h" +#include "core/internal_network/sockets.h" namespace Service::Sockets { diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index fed740d87..9ea36428d 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -16,7 +16,7 @@ class System; namespace Network { class Socket; -} +} // namespace Network namespace Service::Sockets { diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index 9c0936d97..2db10ec81 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -7,7 +7,7 @@ #include "common/common_types.h" #include "core/hle/service/sockets/sockets.h" #include "core/hle/service/sockets/sockets_translate.h" -#include "core/network/network.h" +#include "core/internal_network/network.h" namespace Service::Sockets { diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h index 5e9809add..c93291d3e 100644 --- a/src/core/hle/service/sockets/sockets_translate.h +++ b/src/core/hle/service/sockets/sockets_translate.h @@ -7,7 +7,7 @@ #include "common/common_types.h" #include "core/hle/service/sockets/sockets.h" -#include "core/network/network.h" +#include "core/internal_network/network.h" namespace Service::Sockets { diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp new file mode 100644 index 000000000..36c43cc8f --- /dev/null +++ b/src/core/internal_network/network.cpp @@ -0,0 +1,637 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include + +#include "common/error.h" + +#ifdef _WIN32 +#include +#include +#elif YUZU_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#else +#error "Unimplemented platform" +#endif + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" +#include "core/internal_network/sockets.h" + +namespace Network { + +namespace { + +#ifdef _WIN32 + +using socklen_t = int; + +void Initialize() { + WSADATA wsa_data; + (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); +} + +void Finalize() { + WSACleanup(); +} + +sockaddr TranslateFromSockAddrIn(SockAddrIn input) { + sockaddr_in result; + +#if YUZU_UNIX + result.sin_len = sizeof(result); +#endif + + switch (static_cast(input.family)) { + case Domain::INET: + result.sin_family = AF_INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); + result.sin_family = AF_INET; + break; + } + + result.sin_port = htons(input.portno); + + auto& ip = result.sin_addr.S_un.S_un_b; + ip.s_b1 = input.ip[0]; + ip.s_b2 = input.ip[1]; + ip.s_b3 = input.ip[2]; + ip.s_b4 = input.ip[3]; + + sockaddr addr; + std::memcpy(&addr, &result, sizeof(addr)); + return addr; +} + +LINGER MakeLinger(bool enable, u32 linger_value) { + ASSERT(linger_value <= std::numeric_limits::max()); + + LINGER value; + value.l_onoff = enable ? 1 : 0; + value.l_linger = static_cast(linger_value); + return value; +} + +bool EnableNonBlock(SOCKET fd, bool enable) { + u_long value = enable ? 1 : 0; + return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; +} + +Errno TranslateNativeError(int e) { + switch (e) { + case WSAEBADF: + return Errno::BADF; + case WSAEINVAL: + return Errno::INVAL; + case WSAEMFILE: + return Errno::MFILE; + case WSAENOTCONN: + return Errno::NOTCONN; + case WSAEWOULDBLOCK: + return Errno::AGAIN; + case WSAECONNREFUSED: + return Errno::CONNREFUSED; + case WSAEHOSTUNREACH: + return Errno::HOSTUNREACH; + case WSAENETDOWN: + return Errno::NETDOWN; + case WSAENETUNREACH: + return Errno::NETUNREACH; + default: + return Errno::OTHER; + } +} + +#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX + +using SOCKET = int; +using WSAPOLLFD = pollfd; +using ULONG = u64; + +constexpr SOCKET INVALID_SOCKET = -1; +constexpr SOCKET SOCKET_ERROR = -1; + +constexpr int SD_RECEIVE = SHUT_RD; +constexpr int SD_SEND = SHUT_WR; +constexpr int SD_BOTH = SHUT_RDWR; + +void Initialize() {} + +void Finalize() {} + +sockaddr TranslateFromSockAddrIn(SockAddrIn input) { + sockaddr_in result; + + switch (static_cast(input.family)) { + case Domain::INET: + result.sin_family = AF_INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); + result.sin_family = AF_INET; + break; + } + + result.sin_port = htons(input.portno); + + result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; + + sockaddr addr; + std::memcpy(&addr, &result, sizeof(addr)); + return addr; +} + +int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { + return poll(fds, static_cast(nfds), timeout); +} + +int closesocket(SOCKET fd) { + return close(fd); +} + +linger MakeLinger(bool enable, u32 linger_value) { + linger value; + value.l_onoff = enable ? 1 : 0; + value.l_linger = linger_value; + return value; +} + +bool EnableNonBlock(int fd, bool enable) { + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + return false; + } + if (enable) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + return fcntl(fd, F_SETFL, flags) == 0; +} + +Errno TranslateNativeError(int e) { + switch (e) { + case EBADF: + return Errno::BADF; + case EINVAL: + return Errno::INVAL; + case EMFILE: + return Errno::MFILE; + case ENOTCONN: + return Errno::NOTCONN; + case EAGAIN: + return Errno::AGAIN; + case ECONNREFUSED: + return Errno::CONNREFUSED; + case EHOSTUNREACH: + return Errno::HOSTUNREACH; + case ENETDOWN: + return Errno::NETDOWN; + case ENETUNREACH: + return Errno::NETUNREACH; + default: + return Errno::OTHER; + } +} + +#endif + +Errno GetAndLogLastError() { +#ifdef _WIN32 + int e = WSAGetLastError(); +#else + int e = errno; +#endif + const Errno err = TranslateNativeError(e); + if (err == Errno::AGAIN) { + return err; + } + LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); + return err; +} + +int TranslateDomain(Domain domain) { + switch (domain) { + case Domain::INET: + return AF_INET; + default: + UNIMPLEMENTED_MSG("Unimplemented domain={}", domain); + return 0; + } +} + +int TranslateType(Type type) { + switch (type) { + case Type::STREAM: + return SOCK_STREAM; + case Type::DGRAM: + return SOCK_DGRAM; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", type); + return 0; + } +} + +int TranslateProtocol(Protocol protocol) { + switch (protocol) { + case Protocol::TCP: + return IPPROTO_TCP; + case Protocol::UDP: + return IPPROTO_UDP; + default: + UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); + return 0; + } +} + +SockAddrIn TranslateToSockAddrIn(sockaddr input_) { + sockaddr_in input; + std::memcpy(&input, &input_, sizeof(input)); + + SockAddrIn result; + + switch (input.sin_family) { + case AF_INET: + result.family = Domain::INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); + result.family = Domain::INET; + break; + } + + result.portno = ntohs(input.sin_port); + + result.ip = TranslateIPv4(input.sin_addr); + + return result; +} + +short TranslatePollEvents(PollEvents events) { + short result = 0; + + if (True(events & PollEvents::In)) { + events &= ~PollEvents::In; + result |= POLLIN; + } + if (True(events & PollEvents::Pri)) { + events &= ~PollEvents::Pri; +#ifdef _WIN32 + LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); +#else + result |= POLLPRI; +#endif + } + if (True(events & PollEvents::Out)) { + events &= ~PollEvents::Out; + result |= POLLOUT; + } + + UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events); + + return result; +} + +PollEvents TranslatePollRevents(short revents) { + PollEvents result{}; + const auto translate = [&result, &revents](short host, PollEvents guest) { + if ((revents & host) != 0) { + revents &= static_cast(~host); + result |= guest; + } + }; + + translate(POLLIN, PollEvents::In); + translate(POLLPRI, PollEvents::Pri); + translate(POLLOUT, PollEvents::Out); + translate(POLLERR, PollEvents::Err); + translate(POLLHUP, PollEvents::Hup); + + UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); + + return result; +} + +template +Errno SetSockOpt(SOCKET fd, int option, T value) { + const int result = + setsockopt(fd, SOL_SOCKET, option, reinterpret_cast(&value), sizeof(value)); + if (result != SOCKET_ERROR) { + return Errno::SUCCESS; + } + return GetAndLogLastError(); +} + +} // Anonymous namespace + +NetworkInstance::NetworkInstance() { + Initialize(); +} + +NetworkInstance::~NetworkInstance() { + Finalize(); +} + +std::optional GetHostIPv4Address() { + const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return {}; + } + + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res != network_interfaces.end()) { + char ip_addr[16] = {}; + ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); + return TranslateIPv4(res->ip_address); + } else { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return {}; + } +} + +std::pair Poll(std::vector& pollfds, s32 timeout) { + const size_t num = pollfds.size(); + + std::vector host_pollfds(pollfds.size()); + std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { + WSAPOLLFD result; + result.fd = fd.socket->fd; + result.events = TranslatePollEvents(fd.events); + result.revents = 0; + return result; + }); + + const int result = WSAPoll(host_pollfds.data(), static_cast(num), timeout); + if (result == 0) { + ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), + [](WSAPOLLFD fd) { return fd.revents == 0; })); + return {0, Errno::SUCCESS}; + } + + for (size_t i = 0; i < num; ++i) { + pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); + } + + if (result > 0) { + return {result, Errno::SUCCESS}; + } + + ASSERT(result == SOCKET_ERROR); + + return {-1, GetAndLogLastError()}; +} + +Socket::~Socket() { + if (fd == INVALID_SOCKET) { + return; + } + (void)closesocket(fd); + fd = INVALID_SOCKET; +} + +Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} + +Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { + fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); + if (fd != INVALID_SOCKET) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair Socket::Accept() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + const SOCKET new_socket = accept(fd, &addr, &addrlen); + + if (new_socket == INVALID_SOCKET) { + return {AcceptResult{}, GetAndLogLastError()}; + } + + AcceptResult result; + result.socket = std::make_unique(); + result.socket->fd = new_socket; + + ASSERT(addrlen == sizeof(sockaddr_in)); + result.sockaddr_in = TranslateToSockAddrIn(addr); + + return {std::move(result), Errno::SUCCESS}; +} + +Errno Socket::Connect(SockAddrIn addr_in) { + const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); + if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair Socket::GetPeerName() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { + return {SockAddrIn{}, GetAndLogLastError()}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; +} + +std::pair Socket::GetSockName() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { + return {SockAddrIn{}, GetAndLogLastError()}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; +} + +Errno Socket::Bind(SockAddrIn addr) { + const sockaddr addr_in = TranslateFromSockAddrIn(addr); + if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +Errno Socket::Listen(s32 backlog) { + if (listen(fd, backlog) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +Errno Socket::Shutdown(ShutdownHow how) { + int host_how = 0; + switch (how) { + case ShutdownHow::RD: + host_how = SD_RECEIVE; + break; + case ShutdownHow::WR: + host_how = SD_SEND; + break; + case ShutdownHow::RDWR: + host_how = SD_BOTH; + break; + default: + UNIMPLEMENTED_MSG("Unimplemented flag how={}", how); + return Errno::SUCCESS; + } + if (shutdown(fd, host_how) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair Socket::Recv(int flags, std::vector& message) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast(std::numeric_limits::max())); + + const auto result = + recv(fd, reinterpret_cast(message.data()), static_cast(message.size()), 0); + if (result != SOCKET_ERROR) { + return {static_cast(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair Socket::RecvFrom(int flags, std::vector& message, SockAddrIn* addr) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast(std::numeric_limits::max())); + + sockaddr addr_in{}; + socklen_t addrlen = sizeof(addr_in); + socklen_t* const p_addrlen = addr ? &addrlen : nullptr; + sockaddr* const p_addr_in = addr ? &addr_in : nullptr; + + const auto result = recvfrom(fd, reinterpret_cast(message.data()), + static_cast(message.size()), 0, p_addr_in, p_addrlen); + if (result != SOCKET_ERROR) { + if (addr) { + ASSERT(addrlen == sizeof(addr_in)); + *addr = TranslateToSockAddrIn(addr_in); + } + return {static_cast(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair Socket::Send(const std::vector& message, int flags) { + ASSERT(message.size() < static_cast(std::numeric_limits::max())); + ASSERT(flags == 0); + + const auto result = send(fd, reinterpret_cast(message.data()), + static_cast(message.size()), 0); + if (result != SOCKET_ERROR) { + return {static_cast(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair Socket::SendTo(u32 flags, const std::vector& message, + const SockAddrIn* addr) { + ASSERT(flags == 0); + + const sockaddr* to = nullptr; + const int tolen = addr ? sizeof(sockaddr) : 0; + sockaddr host_addr_in; + + if (addr) { + host_addr_in = TranslateFromSockAddrIn(*addr); + to = &host_addr_in; + } + + const auto result = sendto(fd, reinterpret_cast(message.data()), + static_cast(message.size()), 0, to, tolen); + if (result != SOCKET_ERROR) { + return {static_cast(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +Errno Socket::Close() { + [[maybe_unused]] const int result = closesocket(fd); + ASSERT(result == 0); + fd = INVALID_SOCKET; + + return Errno::SUCCESS; +} + +Errno Socket::SetLinger(bool enable, u32 linger) { + return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); +} + +Errno Socket::SetReuseAddr(bool enable) { + return SetSockOpt(fd, SO_REUSEADDR, enable ? 1 : 0); +} + +Errno Socket::SetKeepAlive(bool enable) { + return SetSockOpt(fd, SO_KEEPALIVE, enable ? 1 : 0); +} + +Errno Socket::SetBroadcast(bool enable) { + return SetSockOpt(fd, SO_BROADCAST, enable ? 1 : 0); +} + +Errno Socket::SetSndBuf(u32 value) { + return SetSockOpt(fd, SO_SNDBUF, value); +} + +Errno Socket::SetRcvBuf(u32 value) { + return SetSockOpt(fd, SO_RCVBUF, value); +} + +Errno Socket::SetSndTimeo(u32 value) { + return SetSockOpt(fd, SO_SNDTIMEO, value); +} + +Errno Socket::SetRcvTimeo(u32 value) { + return SetSockOpt(fd, SO_RCVTIMEO, value); +} + +Errno Socket::SetNonBlock(bool enable) { + if (EnableNonBlock(fd, enable)) { + return Errno::SUCCESS; + } + return GetAndLogLastError(); +} + +bool Socket::IsOpened() const { + return fd != INVALID_SOCKET; +} + +} // namespace Network diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h new file mode 100644 index 000000000..10e5ef10d --- /dev/null +++ b/src/core/internal_network/network.h @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" + +#ifdef _WIN32 +#include +#elif YUZU_UNIX +#include +#endif + +namespace Network { + +class Socket; + +/// Error code for network functions +enum class Errno { + SUCCESS, + BADF, + INVAL, + MFILE, + NOTCONN, + AGAIN, + CONNREFUSED, + HOSTUNREACH, + NETDOWN, + NETUNREACH, + OTHER, +}; + +/// Address families +enum class Domain { + INET, ///< Address family for IPv4 +}; + +/// Socket types +enum class Type { + STREAM, + DGRAM, + RAW, + SEQPACKET, +}; + +/// Protocol values for sockets +enum class Protocol { + ICMP, + TCP, + UDP, +}; + +/// Shutdown mode +enum class ShutdownHow { + RD, + WR, + RDWR, +}; + +/// Array of IPv4 address +using IPv4Address = std::array; + +/// Cross-platform sockaddr structure +struct SockAddrIn { + Domain family; + IPv4Address ip; + u16 portno; +}; + +/// Cross-platform poll fd structure + +enum class PollEvents : u16 { + // Using Pascal case because IN is a macro on Windows. + In = 1 << 0, + Pri = 1 << 1, + Out = 1 << 2, + Err = 1 << 3, + Hup = 1 << 4, + Nval = 1 << 5, +}; + +DECLARE_ENUM_FLAG_OPERATORS(PollEvents); + +struct PollFD { + Socket* socket; + PollEvents events; + PollEvents revents; +}; + +class NetworkInstance { +public: + explicit NetworkInstance(); + ~NetworkInstance(); +}; + +#ifdef _WIN32 +constexpr IPv4Address TranslateIPv4(in_addr addr) { + auto& bytes = addr.S_un.S_un_b; + return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; +} +#elif YUZU_UNIX +constexpr IPv4Address TranslateIPv4(in_addr addr) { + const u32 bytes = addr.s_addr; + return IPv4Address{static_cast(bytes), static_cast(bytes >> 8), + static_cast(bytes >> 16), static_cast(bytes >> 24)}; +} +#endif + +/// @brief Returns host's IPv4 address +/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array +std::optional GetHostIPv4Address(); + +} // namespace Network diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp new file mode 100644 index 000000000..0f0a66160 --- /dev/null +++ b/src/core/internal_network/network_interface.cpp @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "common/bit_cast.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/internal_network/network_interface.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +namespace Network { + +#ifdef _WIN32 + +std::vector GetAvailableNetworkInterfaces() { + std::vector adapter_addresses; + DWORD ret = ERROR_BUFFER_OVERFLOW; + DWORD buf_size = 0; + + // retry up to 5 times + for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { + ret = GetAdaptersAddresses( + AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, + nullptr, adapter_addresses.data(), &buf_size); + + if (ret != ERROR_BUFFER_OVERFLOW) { + break; + } + + adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); + } + + if (ret != NO_ERROR) { + LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); + return {}; + } + + std::vector result; + + for (auto current_address = adapter_addresses.data(); current_address != nullptr; + current_address = current_address->Next) { + if (current_address->FirstUnicastAddress == nullptr || + current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { + continue; + } + + if (current_address->OperStatus != IfOperStatusUp) { + continue; + } + + const auto ip_addr = Common::BitCast( + *current_address->FirstUnicastAddress->Address.lpSockaddr) + .sin_addr; + + ULONG mask = 0; + if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, + &mask) != NO_ERROR) { + LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); + continue; + } + + struct in_addr gateway = {.S_un{.S_addr{0}}}; + if (current_address->FirstGatewayAddress != nullptr && + current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { + gateway = Common::BitCast( + *current_address->FirstGatewayAddress->Address.lpSockaddr) + .sin_addr; + } + + result.emplace_back(NetworkInterface{ + .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, + .ip_address{ip_addr}, + .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, + .gateway = gateway}); + } + + return result; +} + +#else + +std::vector GetAvailableNetworkInterfaces() { + struct ifaddrs* ifaddr = nullptr; + + if (getifaddrs(&ifaddr) != 0) { + LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", + std::strerror(errno)); + return {}; + } + + std::vector result; + + for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { + continue; + } + + if (ifa->ifa_addr->sa_family != AF_INET) { + continue; + } + + if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { + continue; + } + + u32 gateway{}; + + std::ifstream file{"/proc/net/route"}; + if (!file.is_open()) { + LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); + + result.emplace_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + continue; + } + + // ignore header + file.ignore(std::numeric_limits::max(), '\n'); + + bool gateway_found = false; + + for (std::string line; std::getline(file, line);) { + std::istringstream iss{line}; + + std::string iface_name; + iss >> iface_name; + if (iface_name != ifa->ifa_name) { + continue; + } + + iss >> std::hex; + + u32 dest{}; + iss >> dest; + if (dest != 0) { + // not the default route + continue; + } + + iss >> gateway; + + u16 flags{}; + iss >> flags; + + // flag RTF_GATEWAY (defined in ) + if ((flags & 0x2) == 0) { + continue; + } + + gateway_found = true; + break; + } + + if (!gateway_found) { + gateway = 0; + } + + result.emplace_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + } + + freeifaddrs(ifaddr); + + return result; +} + +#endif + +std::optional GetSelectedNetworkInterface() { + const auto& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return std::nullopt; + } + + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res == network_interfaces.end()) { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return std::nullopt; + } + + return *res; +} + +} // namespace Network diff --git a/src/core/internal_network/network_interface.h b/src/core/internal_network/network_interface.h new file mode 100644 index 000000000..9b98b6b42 --- /dev/null +++ b/src/core/internal_network/network_interface.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace Network { + +struct NetworkInterface { + std::string name; + struct in_addr ip_address; + struct in_addr subnet_mask; + struct in_addr gateway; +}; + +std::vector GetAvailableNetworkInterfaces(); +std::optional GetSelectedNetworkInterface(); + +} // namespace Network diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h new file mode 100644 index 000000000..77e27e928 --- /dev/null +++ b/src/core/internal_network/sockets.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#if defined(_WIN32) +#elif !YUZU_UNIX +#error "Platform not implemented" +#endif + +#include "common/common_types.h" +#include "core/internal_network/network.h" + +// TODO: C++20 Replace std::vector usages with std::span + +namespace Network { + +class Socket { +public: + struct AcceptResult { + std::unique_ptr socket; + SockAddrIn sockaddr_in; + }; + + explicit Socket() = default; + ~Socket(); + + Socket(const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + Socket(Socket&& rhs) noexcept; + + // Avoid closing sockets implicitly + Socket& operator=(Socket&&) noexcept = delete; + + Errno Initialize(Domain domain, Type type, Protocol protocol); + + Errno Close(); + + std::pair Accept(); + + Errno Connect(SockAddrIn addr_in); + + std::pair GetPeerName(); + + std::pair GetSockName(); + + Errno Bind(SockAddrIn addr); + + Errno Listen(s32 backlog); + + Errno Shutdown(ShutdownHow how); + + std::pair Recv(int flags, std::vector& message); + + std::pair RecvFrom(int flags, std::vector& message, SockAddrIn* addr); + + std::pair Send(const std::vector& message, int flags); + + std::pair SendTo(u32 flags, const std::vector& message, const SockAddrIn* addr); + + Errno SetLinger(bool enable, u32 linger); + + Errno SetReuseAddr(bool enable); + + Errno SetKeepAlive(bool enable); + + Errno SetBroadcast(bool enable); + + Errno SetSndBuf(u32 value); + + Errno SetRcvBuf(u32 value); + + Errno SetSndTimeo(u32 value); + + Errno SetRcvTimeo(u32 value); + + Errno SetNonBlock(bool enable); + + bool IsOpened() const; + +#if defined(_WIN32) + SOCKET fd = INVALID_SOCKET; +#elif YUZU_UNIX + int fd = -1; +#endif +}; + +std::pair Poll(std::vector& poll_fds, s32 timeout); + +} // namespace Network diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp deleted file mode 100644 index fdafbea92..000000000 --- a/src/core/network/network.cpp +++ /dev/null @@ -1,637 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include - -#include "common/error.h" - -#ifdef _WIN32 -#include -#include -#elif YUZU_UNIX -#include -#include -#include -#include -#include -#include -#include -#include -#else -#error "Unimplemented platform" -#endif - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/network/network.h" -#include "core/network/network_interface.h" -#include "core/network/sockets.h" - -namespace Network { - -namespace { - -#ifdef _WIN32 - -using socklen_t = int; - -void Initialize() { - WSADATA wsa_data; - (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); -} - -void Finalize() { - WSACleanup(); -} - -sockaddr TranslateFromSockAddrIn(SockAddrIn input) { - sockaddr_in result; - -#if YUZU_UNIX - result.sin_len = sizeof(result); -#endif - - switch (static_cast(input.family)) { - case Domain::INET: - result.sin_family = AF_INET; - break; - default: - UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); - result.sin_family = AF_INET; - break; - } - - result.sin_port = htons(input.portno); - - auto& ip = result.sin_addr.S_un.S_un_b; - ip.s_b1 = input.ip[0]; - ip.s_b2 = input.ip[1]; - ip.s_b3 = input.ip[2]; - ip.s_b4 = input.ip[3]; - - sockaddr addr; - std::memcpy(&addr, &result, sizeof(addr)); - return addr; -} - -LINGER MakeLinger(bool enable, u32 linger_value) { - ASSERT(linger_value <= std::numeric_limits::max()); - - LINGER value; - value.l_onoff = enable ? 1 : 0; - value.l_linger = static_cast(linger_value); - return value; -} - -bool EnableNonBlock(SOCKET fd, bool enable) { - u_long value = enable ? 1 : 0; - return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; -} - -Errno TranslateNativeError(int e) { - switch (e) { - case WSAEBADF: - return Errno::BADF; - case WSAEINVAL: - return Errno::INVAL; - case WSAEMFILE: - return Errno::MFILE; - case WSAENOTCONN: - return Errno::NOTCONN; - case WSAEWOULDBLOCK: - return Errno::AGAIN; - case WSAECONNREFUSED: - return Errno::CONNREFUSED; - case WSAEHOSTUNREACH: - return Errno::HOSTUNREACH; - case WSAENETDOWN: - return Errno::NETDOWN; - case WSAENETUNREACH: - return Errno::NETUNREACH; - default: - return Errno::OTHER; - } -} - -#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX - -using SOCKET = int; -using WSAPOLLFD = pollfd; -using ULONG = u64; - -constexpr SOCKET INVALID_SOCKET = -1; -constexpr SOCKET SOCKET_ERROR = -1; - -constexpr int SD_RECEIVE = SHUT_RD; -constexpr int SD_SEND = SHUT_WR; -constexpr int SD_BOTH = SHUT_RDWR; - -void Initialize() {} - -void Finalize() {} - -sockaddr TranslateFromSockAddrIn(SockAddrIn input) { - sockaddr_in result; - - switch (static_cast(input.family)) { - case Domain::INET: - result.sin_family = AF_INET; - break; - default: - UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); - result.sin_family = AF_INET; - break; - } - - result.sin_port = htons(input.portno); - - result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; - - sockaddr addr; - std::memcpy(&addr, &result, sizeof(addr)); - return addr; -} - -int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { - return poll(fds, static_cast(nfds), timeout); -} - -int closesocket(SOCKET fd) { - return close(fd); -} - -linger MakeLinger(bool enable, u32 linger_value) { - linger value; - value.l_onoff = enable ? 1 : 0; - value.l_linger = linger_value; - return value; -} - -bool EnableNonBlock(int fd, bool enable) { - int flags = fcntl(fd, F_GETFL); - if (flags == -1) { - return false; - } - if (enable) { - flags |= O_NONBLOCK; - } else { - flags &= ~O_NONBLOCK; - } - return fcntl(fd, F_SETFL, flags) == 0; -} - -Errno TranslateNativeError(int e) { - switch (e) { - case EBADF: - return Errno::BADF; - case EINVAL: - return Errno::INVAL; - case EMFILE: - return Errno::MFILE; - case ENOTCONN: - return Errno::NOTCONN; - case EAGAIN: - return Errno::AGAIN; - case ECONNREFUSED: - return Errno::CONNREFUSED; - case EHOSTUNREACH: - return Errno::HOSTUNREACH; - case ENETDOWN: - return Errno::NETDOWN; - case ENETUNREACH: - return Errno::NETUNREACH; - default: - return Errno::OTHER; - } -} - -#endif - -Errno GetAndLogLastError() { -#ifdef _WIN32 - int e = WSAGetLastError(); -#else - int e = errno; -#endif - const Errno err = TranslateNativeError(e); - if (err == Errno::AGAIN) { - return err; - } - LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); - return err; -} - -int TranslateDomain(Domain domain) { - switch (domain) { - case Domain::INET: - return AF_INET; - default: - UNIMPLEMENTED_MSG("Unimplemented domain={}", domain); - return 0; - } -} - -int TranslateType(Type type) { - switch (type) { - case Type::STREAM: - return SOCK_STREAM; - case Type::DGRAM: - return SOCK_DGRAM; - default: - UNIMPLEMENTED_MSG("Unimplemented type={}", type); - return 0; - } -} - -int TranslateProtocol(Protocol protocol) { - switch (protocol) { - case Protocol::TCP: - return IPPROTO_TCP; - case Protocol::UDP: - return IPPROTO_UDP; - default: - UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); - return 0; - } -} - -SockAddrIn TranslateToSockAddrIn(sockaddr input_) { - sockaddr_in input; - std::memcpy(&input, &input_, sizeof(input)); - - SockAddrIn result; - - switch (input.sin_family) { - case AF_INET: - result.family = Domain::INET; - break; - default: - UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); - result.family = Domain::INET; - break; - } - - result.portno = ntohs(input.sin_port); - - result.ip = TranslateIPv4(input.sin_addr); - - return result; -} - -short TranslatePollEvents(PollEvents events) { - short result = 0; - - if (True(events & PollEvents::In)) { - events &= ~PollEvents::In; - result |= POLLIN; - } - if (True(events & PollEvents::Pri)) { - events &= ~PollEvents::Pri; -#ifdef _WIN32 - LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); -#else - result |= POLLPRI; -#endif - } - if (True(events & PollEvents::Out)) { - events &= ~PollEvents::Out; - result |= POLLOUT; - } - - UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events); - - return result; -} - -PollEvents TranslatePollRevents(short revents) { - PollEvents result{}; - const auto translate = [&result, &revents](short host, PollEvents guest) { - if ((revents & host) != 0) { - revents &= static_cast(~host); - result |= guest; - } - }; - - translate(POLLIN, PollEvents::In); - translate(POLLPRI, PollEvents::Pri); - translate(POLLOUT, PollEvents::Out); - translate(POLLERR, PollEvents::Err); - translate(POLLHUP, PollEvents::Hup); - - UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); - - return result; -} - -template -Errno SetSockOpt(SOCKET fd, int option, T value) { - const int result = - setsockopt(fd, SOL_SOCKET, option, reinterpret_cast(&value), sizeof(value)); - if (result != SOCKET_ERROR) { - return Errno::SUCCESS; - } - return GetAndLogLastError(); -} - -} // Anonymous namespace - -NetworkInstance::NetworkInstance() { - Initialize(); -} - -NetworkInstance::~NetworkInstance() { - Finalize(); -} - -std::optional GetHostIPv4Address() { - const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); - const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - if (network_interfaces.size() == 0) { - LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); - return {}; - } - - const auto res = - std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { - return iface.name == selected_network_interface; - }); - - if (res != network_interfaces.end()) { - char ip_addr[16] = {}; - ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); - return TranslateIPv4(res->ip_address); - } else { - LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); - return {}; - } -} - -std::pair Poll(std::vector& pollfds, s32 timeout) { - const size_t num = pollfds.size(); - - std::vector host_pollfds(pollfds.size()); - std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { - WSAPOLLFD result; - result.fd = fd.socket->fd; - result.events = TranslatePollEvents(fd.events); - result.revents = 0; - return result; - }); - - const int result = WSAPoll(host_pollfds.data(), static_cast(num), timeout); - if (result == 0) { - ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), - [](WSAPOLLFD fd) { return fd.revents == 0; })); - return {0, Errno::SUCCESS}; - } - - for (size_t i = 0; i < num; ++i) { - pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); - } - - if (result > 0) { - return {result, Errno::SUCCESS}; - } - - ASSERT(result == SOCKET_ERROR); - - return {-1, GetAndLogLastError()}; -} - -Socket::~Socket() { - if (fd == INVALID_SOCKET) { - return; - } - (void)closesocket(fd); - fd = INVALID_SOCKET; -} - -Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} - -Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { - fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); - if (fd != INVALID_SOCKET) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -std::pair Socket::Accept() { - sockaddr addr; - socklen_t addrlen = sizeof(addr); - const SOCKET new_socket = accept(fd, &addr, &addrlen); - - if (new_socket == INVALID_SOCKET) { - return {AcceptResult{}, GetAndLogLastError()}; - } - - AcceptResult result; - result.socket = std::make_unique(); - result.socket->fd = new_socket; - - ASSERT(addrlen == sizeof(sockaddr_in)); - result.sockaddr_in = TranslateToSockAddrIn(addr); - - return {std::move(result), Errno::SUCCESS}; -} - -Errno Socket::Connect(SockAddrIn addr_in) { - const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); - if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -std::pair Socket::GetPeerName() { - sockaddr addr; - socklen_t addrlen = sizeof(addr); - if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { - return {SockAddrIn{}, GetAndLogLastError()}; - } - - ASSERT(addrlen == sizeof(sockaddr_in)); - return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; -} - -std::pair Socket::GetSockName() { - sockaddr addr; - socklen_t addrlen = sizeof(addr); - if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { - return {SockAddrIn{}, GetAndLogLastError()}; - } - - ASSERT(addrlen == sizeof(sockaddr_in)); - return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; -} - -Errno Socket::Bind(SockAddrIn addr) { - const sockaddr addr_in = TranslateFromSockAddrIn(addr); - if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -Errno Socket::Listen(s32 backlog) { - if (listen(fd, backlog) != SOCKET_ERROR) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -Errno Socket::Shutdown(ShutdownHow how) { - int host_how = 0; - switch (how) { - case ShutdownHow::RD: - host_how = SD_RECEIVE; - break; - case ShutdownHow::WR: - host_how = SD_SEND; - break; - case ShutdownHow::RDWR: - host_how = SD_BOTH; - break; - default: - UNIMPLEMENTED_MSG("Unimplemented flag how={}", how); - return Errno::SUCCESS; - } - if (shutdown(fd, host_how) != SOCKET_ERROR) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -std::pair Socket::Recv(int flags, std::vector& message) { - ASSERT(flags == 0); - ASSERT(message.size() < static_cast(std::numeric_limits::max())); - - const auto result = - recv(fd, reinterpret_cast(message.data()), static_cast(message.size()), 0); - if (result != SOCKET_ERROR) { - return {static_cast(result), Errno::SUCCESS}; - } - - return {-1, GetAndLogLastError()}; -} - -std::pair Socket::RecvFrom(int flags, std::vector& message, SockAddrIn* addr) { - ASSERT(flags == 0); - ASSERT(message.size() < static_cast(std::numeric_limits::max())); - - sockaddr addr_in{}; - socklen_t addrlen = sizeof(addr_in); - socklen_t* const p_addrlen = addr ? &addrlen : nullptr; - sockaddr* const p_addr_in = addr ? &addr_in : nullptr; - - const auto result = recvfrom(fd, reinterpret_cast(message.data()), - static_cast(message.size()), 0, p_addr_in, p_addrlen); - if (result != SOCKET_ERROR) { - if (addr) { - ASSERT(addrlen == sizeof(addr_in)); - *addr = TranslateToSockAddrIn(addr_in); - } - return {static_cast(result), Errno::SUCCESS}; - } - - return {-1, GetAndLogLastError()}; -} - -std::pair Socket::Send(const std::vector& message, int flags) { - ASSERT(message.size() < static_cast(std::numeric_limits::max())); - ASSERT(flags == 0); - - const auto result = send(fd, reinterpret_cast(message.data()), - static_cast(message.size()), 0); - if (result != SOCKET_ERROR) { - return {static_cast(result), Errno::SUCCESS}; - } - - return {-1, GetAndLogLastError()}; -} - -std::pair Socket::SendTo(u32 flags, const std::vector& message, - const SockAddrIn* addr) { - ASSERT(flags == 0); - - const sockaddr* to = nullptr; - const int tolen = addr ? sizeof(sockaddr) : 0; - sockaddr host_addr_in; - - if (addr) { - host_addr_in = TranslateFromSockAddrIn(*addr); - to = &host_addr_in; - } - - const auto result = sendto(fd, reinterpret_cast(message.data()), - static_cast(message.size()), 0, to, tolen); - if (result != SOCKET_ERROR) { - return {static_cast(result), Errno::SUCCESS}; - } - - return {-1, GetAndLogLastError()}; -} - -Errno Socket::Close() { - [[maybe_unused]] const int result = closesocket(fd); - ASSERT(result == 0); - fd = INVALID_SOCKET; - - return Errno::SUCCESS; -} - -Errno Socket::SetLinger(bool enable, u32 linger) { - return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); -} - -Errno Socket::SetReuseAddr(bool enable) { - return SetSockOpt(fd, SO_REUSEADDR, enable ? 1 : 0); -} - -Errno Socket::SetKeepAlive(bool enable) { - return SetSockOpt(fd, SO_KEEPALIVE, enable ? 1 : 0); -} - -Errno Socket::SetBroadcast(bool enable) { - return SetSockOpt(fd, SO_BROADCAST, enable ? 1 : 0); -} - -Errno Socket::SetSndBuf(u32 value) { - return SetSockOpt(fd, SO_SNDBUF, value); -} - -Errno Socket::SetRcvBuf(u32 value) { - return SetSockOpt(fd, SO_RCVBUF, value); -} - -Errno Socket::SetSndTimeo(u32 value) { - return SetSockOpt(fd, SO_SNDTIMEO, value); -} - -Errno Socket::SetRcvTimeo(u32 value) { - return SetSockOpt(fd, SO_RCVTIMEO, value); -} - -Errno Socket::SetNonBlock(bool enable) { - if (EnableNonBlock(fd, enable)) { - return Errno::SUCCESS; - } - return GetAndLogLastError(); -} - -bool Socket::IsOpened() const { - return fd != INVALID_SOCKET; -} - -} // namespace Network diff --git a/src/core/network/network.h b/src/core/network/network.h deleted file mode 100644 index 10e5ef10d..000000000 --- a/src/core/network/network.h +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "common/common_funcs.h" -#include "common/common_types.h" - -#ifdef _WIN32 -#include -#elif YUZU_UNIX -#include -#endif - -namespace Network { - -class Socket; - -/// Error code for network functions -enum class Errno { - SUCCESS, - BADF, - INVAL, - MFILE, - NOTCONN, - AGAIN, - CONNREFUSED, - HOSTUNREACH, - NETDOWN, - NETUNREACH, - OTHER, -}; - -/// Address families -enum class Domain { - INET, ///< Address family for IPv4 -}; - -/// Socket types -enum class Type { - STREAM, - DGRAM, - RAW, - SEQPACKET, -}; - -/// Protocol values for sockets -enum class Protocol { - ICMP, - TCP, - UDP, -}; - -/// Shutdown mode -enum class ShutdownHow { - RD, - WR, - RDWR, -}; - -/// Array of IPv4 address -using IPv4Address = std::array; - -/// Cross-platform sockaddr structure -struct SockAddrIn { - Domain family; - IPv4Address ip; - u16 portno; -}; - -/// Cross-platform poll fd structure - -enum class PollEvents : u16 { - // Using Pascal case because IN is a macro on Windows. - In = 1 << 0, - Pri = 1 << 1, - Out = 1 << 2, - Err = 1 << 3, - Hup = 1 << 4, - Nval = 1 << 5, -}; - -DECLARE_ENUM_FLAG_OPERATORS(PollEvents); - -struct PollFD { - Socket* socket; - PollEvents events; - PollEvents revents; -}; - -class NetworkInstance { -public: - explicit NetworkInstance(); - ~NetworkInstance(); -}; - -#ifdef _WIN32 -constexpr IPv4Address TranslateIPv4(in_addr addr) { - auto& bytes = addr.S_un.S_un_b; - return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; -} -#elif YUZU_UNIX -constexpr IPv4Address TranslateIPv4(in_addr addr) { - const u32 bytes = addr.s_addr; - return IPv4Address{static_cast(bytes), static_cast(bytes >> 8), - static_cast(bytes >> 16), static_cast(bytes >> 24)}; -} -#endif - -/// @brief Returns host's IPv4 address -/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array -std::optional GetHostIPv4Address(); - -} // namespace Network diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp deleted file mode 100644 index 15ecc6abf..000000000 --- a/src/core/network/network_interface.cpp +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include - -#include "common/bit_cast.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/string_util.h" -#include "core/network/network_interface.h" - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -namespace Network { - -#ifdef _WIN32 - -std::vector GetAvailableNetworkInterfaces() { - std::vector adapter_addresses; - DWORD ret = ERROR_BUFFER_OVERFLOW; - DWORD buf_size = 0; - - // retry up to 5 times - for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { - ret = GetAdaptersAddresses( - AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, - nullptr, adapter_addresses.data(), &buf_size); - - if (ret != ERROR_BUFFER_OVERFLOW) { - break; - } - - adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); - } - - if (ret != NO_ERROR) { - LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); - return {}; - } - - std::vector result; - - for (auto current_address = adapter_addresses.data(); current_address != nullptr; - current_address = current_address->Next) { - if (current_address->FirstUnicastAddress == nullptr || - current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { - continue; - } - - if (current_address->OperStatus != IfOperStatusUp) { - continue; - } - - const auto ip_addr = Common::BitCast( - *current_address->FirstUnicastAddress->Address.lpSockaddr) - .sin_addr; - - ULONG mask = 0; - if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, - &mask) != NO_ERROR) { - LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); - continue; - } - - struct in_addr gateway = {.S_un{.S_addr{0}}}; - if (current_address->FirstGatewayAddress != nullptr && - current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { - gateway = Common::BitCast( - *current_address->FirstGatewayAddress->Address.lpSockaddr) - .sin_addr; - } - - result.emplace_back(NetworkInterface{ - .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, - .ip_address{ip_addr}, - .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, - .gateway = gateway}); - } - - return result; -} - -#else - -std::vector GetAvailableNetworkInterfaces() { - struct ifaddrs* ifaddr = nullptr; - - if (getifaddrs(&ifaddr) != 0) { - LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", - std::strerror(errno)); - return {}; - } - - std::vector result; - - for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { - continue; - } - - if (ifa->ifa_addr->sa_family != AF_INET) { - continue; - } - - if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { - continue; - } - - u32 gateway{}; - - std::ifstream file{"/proc/net/route"}; - if (!file.is_open()) { - LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); - - result.emplace_back(NetworkInterface{ - .name{ifa->ifa_name}, - .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, - .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, - .gateway{in_addr{.s_addr = gateway}}}); - continue; - } - - // ignore header - file.ignore(std::numeric_limits::max(), '\n'); - - bool gateway_found = false; - - for (std::string line; std::getline(file, line);) { - std::istringstream iss{line}; - - std::string iface_name; - iss >> iface_name; - if (iface_name != ifa->ifa_name) { - continue; - } - - iss >> std::hex; - - u32 dest{}; - iss >> dest; - if (dest != 0) { - // not the default route - continue; - } - - iss >> gateway; - - u16 flags{}; - iss >> flags; - - // flag RTF_GATEWAY (defined in ) - if ((flags & 0x2) == 0) { - continue; - } - - gateway_found = true; - break; - } - - if (!gateway_found) { - gateway = 0; - } - - result.emplace_back(NetworkInterface{ - .name{ifa->ifa_name}, - .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, - .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, - .gateway{in_addr{.s_addr = gateway}}}); - } - - freeifaddrs(ifaddr); - - return result; -} - -#endif - -std::optional GetSelectedNetworkInterface() { - const auto& selected_network_interface = Settings::values.network_interface.GetValue(); - const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - if (network_interfaces.size() == 0) { - LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); - return std::nullopt; - } - - const auto res = - std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { - return iface.name == selected_network_interface; - }); - - if (res == network_interfaces.end()) { - LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); - return std::nullopt; - } - - return *res; -} - -} // namespace Network diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h deleted file mode 100644 index 9b98b6b42..000000000 --- a/src/core/network/network_interface.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#endif - -namespace Network { - -struct NetworkInterface { - std::string name; - struct in_addr ip_address; - struct in_addr subnet_mask; - struct in_addr gateway; -}; - -std::vector GetAvailableNetworkInterfaces(); -std::optional GetSelectedNetworkInterface(); - -} // namespace Network diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h deleted file mode 100644 index f889159f5..000000000 --- a/src/core/network/sockets.h +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#if defined(_WIN32) -#elif !YUZU_UNIX -#error "Platform not implemented" -#endif - -#include "common/common_types.h" -#include "core/network/network.h" - -// TODO: C++20 Replace std::vector usages with std::span - -namespace Network { - -class Socket { -public: - struct AcceptResult { - std::unique_ptr socket; - SockAddrIn sockaddr_in; - }; - - explicit Socket() = default; - ~Socket(); - - Socket(const Socket&) = delete; - Socket& operator=(const Socket&) = delete; - - Socket(Socket&& rhs) noexcept; - - // Avoid closing sockets implicitly - Socket& operator=(Socket&&) noexcept = delete; - - Errno Initialize(Domain domain, Type type, Protocol protocol); - - Errno Close(); - - std::pair Accept(); - - Errno Connect(SockAddrIn addr_in); - - std::pair GetPeerName(); - - std::pair GetSockName(); - - Errno Bind(SockAddrIn addr); - - Errno Listen(s32 backlog); - - Errno Shutdown(ShutdownHow how); - - std::pair Recv(int flags, std::vector& message); - - std::pair RecvFrom(int flags, std::vector& message, SockAddrIn* addr); - - std::pair Send(const std::vector& message, int flags); - - std::pair SendTo(u32 flags, const std::vector& message, const SockAddrIn* addr); - - Errno SetLinger(bool enable, u32 linger); - - Errno SetReuseAddr(bool enable); - - Errno SetKeepAlive(bool enable); - - Errno SetBroadcast(bool enable); - - Errno SetSndBuf(u32 value); - - Errno SetRcvBuf(u32 value); - - Errno SetSndTimeo(u32 value); - - Errno SetRcvTimeo(u32 value); - - Errno SetNonBlock(bool enable); - - bool IsOpened() const; - -#if defined(_WIN32) - SOCKET fd = INVALID_SOCKET; -#elif YUZU_UNIX - int fd = -1; -#endif -}; - -std::pair Poll(std::vector& poll_fds, s32 timeout); - -} // namespace Network -- cgit v1.2.3 From 7c3d241f0d03304df2c4d4449c2c8f1f9c7a16d3 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Thu, 7 Jul 2022 04:12:12 +0200 Subject: common, core: fix -Wmissing-field-initializers --- src/core/announce_multiplayer_session.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/core') diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index a680ad202..aeca87aac 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp @@ -34,10 +34,10 @@ WebService::WebResult AnnounceMultiplayerSession::Register() { std::shared_ptr room = Network::GetRoom().lock(); if (!room) { return WebService::WebResult{WebService::WebResult::Code::LibError, - "Network is not initialized"}; + "Network is not initialized", ""}; } if (room->GetState() != Network::Room::State::Open) { - return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open"}; + return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""}; } UpdateBackendData(room); WebService::WebResult result = backend->Register(); @@ -47,7 +47,7 @@ WebService::WebResult AnnounceMultiplayerSession::Register() { LOG_INFO(WebService, "Room has been registered"); room->SetVerifyUID(result.returned_data); registered = true; - return WebService::WebResult{WebService::WebResult::Code::Success}; + return WebService::WebResult{WebService::WebResult::Code::Success, "", ""}; } void AnnounceMultiplayerSession::Start() { -- cgit v1.2.3 From 7fbd2916a17763b93a8401ba0d560000d56b48cf Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Thu, 7 Jul 2022 16:54:20 +0200 Subject: core: Fix -Wunused-variable --- src/core/core.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/core.cpp b/src/core/core.cpp index 5df32c1e7..98fe6d39c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -317,7 +317,9 @@ struct System::Impl { perf_stats->BeginSystemFrame(); std::string name = "Unknown Game"; - const Loader::ResultStatus res{app_loader->ReadTitle(name)}; + if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { + LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); + } if (auto room_member = Network::GetRoomMember().lock()) { Network::GameInfo game_info; game_info.name = name; -- cgit v1.2.3 From 6c8e45618578de1406c0bf4a7f976bd4903c339c Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Fri, 15 Jul 2022 19:45:35 +0200 Subject: Address first part of review comments --- src/core/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 48f5c1ee0..c1cc62a45 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -716,6 +716,11 @@ add_library(core STATIC hle/service/vi/vi_u.h hle/service/wlan/wlan.cpp hle/service/wlan/wlan.h + internal_network/network.cpp + internal_network/network.h + internal_network/network_interface.cpp + internal_network/network_interface.h + internal_network/sockets.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/elf.cpp @@ -743,11 +748,6 @@ add_library(core STATIC memory/dmnt_cheat_vm.h memory.cpp memory.h - internal_network/network.cpp - internal_network/network.h - internal_network/network_interface.cpp - internal_network/network_interface.h - internal_network/sockets.h perf_stats.cpp perf_stats.h reporter.cpp -- cgit v1.2.3 From 4b404191cf054ec3206676f1fccc452bc0a0e223 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Fri, 15 Jul 2022 21:11:09 +0200 Subject: Address second part of review comments --- src/core/announce_multiplayer_session.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/core') diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index aeca87aac..f8aa9bb0b 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp @@ -88,15 +88,14 @@ AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr room) { Network::RoomInformation room_information = room->GetRoomInformation(); - std::vector memberlist = room->GetRoomMemberList(); + std::vector memberlist = room->GetRoomMemberList(); backend->SetRoomInformation( room_information.name, room_information.description, room_information.port, room_information.member_slots, Network::network_version, room->HasPassword(), room_information.preferred_game, room_information.preferred_game_id); backend->ClearPlayers(); for (const auto& member : memberlist) { - backend->AddPlayer(member.username, member.nickname, member.avatar_url, member.mac_address, - member.game_info.id, member.game_info.name); + backend->AddPlayer(member); } } -- cgit v1.2.3 From 899c8bb33094f43fbd8df9afb4ca84718ebac87e Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 17 Jul 2022 22:53:44 -0500 Subject: common: multiplayer: Use GameInfo type --- src/core/announce_multiplayer_session.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index f8aa9bb0b..db9eaeac8 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp @@ -89,10 +89,10 @@ AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr room) { Network::RoomInformation room_information = room->GetRoomInformation(); std::vector memberlist = room->GetRoomMemberList(); - backend->SetRoomInformation( - room_information.name, room_information.description, room_information.port, - room_information.member_slots, Network::network_version, room->HasPassword(), - room_information.preferred_game, room_information.preferred_game_id); + backend->SetRoomInformation(room_information.name, room_information.description, + room_information.port, room_information.member_slots, + Network::network_version, room->HasPassword(), + room_information.preferred_game); backend->ClearPlayers(); for (const auto& member : memberlist) { backend->AddPlayer(member); -- cgit v1.2.3 From 7d82e57b91dee30e0fe6fed36550ea7cc9eb778e Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Fri, 22 Jul 2022 16:31:13 +0200 Subject: network: Move global state into a seperate class Co-Authored-By: Narr the Reg <5944268+german77@users.noreply.github.com> --- src/core/announce_multiplayer_session.cpp | 7 ++++--- src/core/announce_multiplayer_session.h | 11 +++++++---- src/core/core.cpp | 16 +++++++++++++--- src/core/core.h | 10 ++++++++++ 4 files changed, 34 insertions(+), 10 deletions(-) (limited to 'src/core') diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index db9eaeac8..8f96b4ee8 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp @@ -20,7 +20,8 @@ namespace Core { // Time between room is announced to web_service static constexpr std::chrono::seconds announce_time_interval(15); -AnnounceMultiplayerSession::AnnounceMultiplayerSession() { +AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_) + : room_network{room_network_} { #ifdef ENABLE_WEB_SERVICE backend = std::make_unique(Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(), @@ -31,7 +32,7 @@ AnnounceMultiplayerSession::AnnounceMultiplayerSession() { } WebService::WebResult AnnounceMultiplayerSession::Register() { - std::shared_ptr room = Network::GetRoom().lock(); + std::shared_ptr room = room_network.GetRoom().lock(); if (!room) { return WebService::WebResult{WebService::WebResult::Code::LibError, "Network is not initialized", ""}; @@ -120,7 +121,7 @@ void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { std::future future; while (!shutdown_event.WaitUntil(update_time)) { update_time += announce_time_interval; - std::shared_ptr room = Network::GetRoom().lock(); + std::shared_ptr room = room_network.GetRoom().lock(); if (!room) { break; } diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h index 2aaf55017..5da3c1f8d 100644 --- a/src/core/announce_multiplayer_session.h +++ b/src/core/announce_multiplayer_session.h @@ -16,7 +16,8 @@ namespace Network { class Room; -} +class RoomNetwork; +} // namespace Network namespace Core { @@ -28,7 +29,7 @@ namespace Core { class AnnounceMultiplayerSession { public: using CallbackHandle = std::shared_ptr>; - AnnounceMultiplayerSession(); + AnnounceMultiplayerSession(Network::RoomNetwork& room_network_); ~AnnounceMultiplayerSession(); /** @@ -79,6 +80,9 @@ public: void UpdateCredentials(); private: + void UpdateBackendData(std::shared_ptr room); + void AnnounceMultiplayerLoop(); + Common::Event shutdown_event; std::mutex callback_mutex; std::set error_callbacks; @@ -89,8 +93,7 @@ private: std::atomic_bool registered = false; ///< Whether the room has been registered - void UpdateBackendData(std::shared_ptr room); - void AnnounceMultiplayerLoop(); + Network::RoomNetwork& room_network; }; } // namespace Core diff --git a/src/core/core.cpp b/src/core/core.cpp index 98fe6d39c..95791a07f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -131,7 +131,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, + : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} SystemResultStatus Run() { @@ -320,7 +320,7 @@ struct System::Impl { if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); } - if (auto room_member = Network::GetRoomMember().lock()) { + if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info; game_info.name = name; game_info.id = program_id; @@ -374,7 +374,7 @@ struct System::Impl { memory.Reset(); applet_manager.ClearAll(); - if (auto room_member = Network::GetRoomMember().lock()) { + if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info{}; room_member->SendGameInfo(game_info); } @@ -451,6 +451,8 @@ struct System::Impl { std::unique_ptr audio_core; Core::Memory::Memory memory; Core::HID::HIDCore hid_core; + Network::RoomNetwork room_network; + CpuManager cpu_manager; std::atomic_bool is_powered_on{}; bool exit_lock = false; @@ -896,6 +898,14 @@ const Core::Debugger& System::GetDebugger() const { return *impl->debugger; } +Network::RoomNetwork& System::GetRoomNetwork() { + return impl->room_network; +} + +const Network::RoomNetwork& System::GetRoomNetwork() const { + return impl->room_network; +} + void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) { impl->execute_program_callback = std::move(callback); } diff --git a/src/core/core.h b/src/core/core.h index a49d1214b..13122dd61 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -97,6 +97,10 @@ namespace Core::HID { class HIDCore; } +namespace Network { +class RoomNetwork; +} + namespace Core { class ARM_Interface; @@ -379,6 +383,12 @@ public: [[nodiscard]] Core::Debugger& GetDebugger(); [[nodiscard]] const Core::Debugger& GetDebugger() const; + /// Gets a mutable reference to the Room Network. + [[nodiscard]] Network::RoomNetwork& GetRoomNetwork(); + + /// Gets an immutable reference to the Room Network. + [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; + void SetExitLock(bool locked); [[nodiscard]] bool GetExitLock() const; -- cgit v1.2.3 From 61ce57b5242984c297283de5868ea4938391a911 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Mon, 25 Jul 2022 17:18:30 +0200 Subject: network, yuzu: Make copyright headers SPDX-compliant --- src/core/announce_multiplayer_session.cpp | 5 ++--- src/core/announce_multiplayer_session.h | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/core') diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index 8f96b4ee8..d73a488cf 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include #include diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h index 5da3c1f8d..db790f7d2 100644 --- a/src/core/announce_multiplayer_session.h +++ b/src/core/announce_multiplayer_session.h @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -- cgit v1.2.3