From b904652d69fb3d3bf1918a7dd7f04bc049c9f460 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Tue, 16 Aug 2022 23:13:05 +0200 Subject: yuzu_room: Remove dependency on core --- src/network/CMakeLists.txt | 6 + src/network/announce_multiplayer_session.cpp | 164 +++++++++++++++++++++++++++ src/network/announce_multiplayer_session.h | 98 ++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 src/network/announce_multiplayer_session.cpp create mode 100644 src/network/announce_multiplayer_session.h (limited to 'src/network') diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 312f79b68..6f8ca4b90 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -2,6 +2,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later add_library(network STATIC + announce_multiplayer_session.cpp + announce_multiplayer_session.h network.cpp network.h packet.cpp @@ -17,3 +19,7 @@ add_library(network STATIC create_target_directory_groups(network) target_link_libraries(network PRIVATE common enet Boost::boost) +if (ENABLE_WEB_SERVICE) + target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE) + target_link_libraries(network PRIVATE web_service) +endif() diff --git a/src/network/announce_multiplayer_session.cpp b/src/network/announce_multiplayer_session.cpp new file mode 100644 index 000000000..6737ce85a --- /dev/null +++ b/src/network/announce_multiplayer_session.cpp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#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(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(), + Settings::values.yuzu_token.GetValue()); +#else + backend = std::make_unique(); +#endif +} + +WebService::WebResult AnnounceMultiplayerSession::Register() { + auto room = 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); + backend->ClearPlayers(); + for (const auto& member : memberlist) { + backend->AddPlayer(member); + } +} + +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; + auto room = 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/network/announce_multiplayer_session.h b/src/network/announce_multiplayer_session.h new file mode 100644 index 000000000..db790f7d2 --- /dev/null +++ b/src/network/announce_multiplayer_session.h @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#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; +class RoomNetwork; +} // namespace Network + +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(Network::RoomNetwork& room_network_); + ~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: + void UpdateBackendData(std::shared_ptr room); + void AnnounceMultiplayerLoop(); + + 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 + + Network::RoomNetwork& room_network; +}; + +} // namespace Core -- cgit v1.2.3