summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorFearlessTobi <thm.frey@gmail.com>2022-08-16 23:13:05 +0200
committerFearlessTobi <thm.frey@gmail.com>2022-08-27 03:02:21 +0200
commitb904652d69fb3d3bf1918a7dd7f04bc049c9f460 (patch)
tree4a252395a2a51d8998ef088e446c1f9a7f238aa3 /src/network
parentMerge pull request #8566 from german77/galaxy (diff)
downloadyuzu-b904652d69fb3d3bf1918a7dd7f04bc049c9f460.tar
yuzu-b904652d69fb3d3bf1918a7dd7f04bc049c9f460.tar.gz
yuzu-b904652d69fb3d3bf1918a7dd7f04bc049c9f460.tar.bz2
yuzu-b904652d69fb3d3bf1918a7dd7f04bc049c9f460.tar.lz
yuzu-b904652d69fb3d3bf1918a7dd7f04bc049c9f460.tar.xz
yuzu-b904652d69fb3d3bf1918a7dd7f04bc049c9f460.tar.zst
yuzu-b904652d69fb3d3bf1918a7dd7f04bc049c9f460.zip
Diffstat (limited to 'src/network')
-rw-r--r--src/network/CMakeLists.txt6
-rw-r--r--src/network/announce_multiplayer_session.cpp164
-rw-r--r--src/network/announce_multiplayer_session.h98
3 files changed, 268 insertions, 0 deletions
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 <chrono>
+#include <future>
+#include <vector>
+#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<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
+ Settings::values.yuzu_username.GetValue(),
+ Settings::values.yuzu_token.GetValue());
+#else
+ backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>();
+#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<std::thread>(&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<void(const WebService::WebResult&)> function) {
+ std::lock_guard lock(callback_mutex);
+ auto handle = std::make_shared<std::function<void(const WebService::WebResult&)>>(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<Network::Room> room) {
+ Network::RoomInformation room_information = room->GetRoomInformation();
+ std::vector<AnnounceMultiplayerRoom::Member> 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<WebService::WebResult> 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<WebService::RoomJson>(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 <atomic>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <thread>
+#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<std::function<void(const WebService::WebResult&)>>;
+ 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<void(const WebService::WebResult&)> 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<Network::Room> room);
+ void AnnounceMultiplayerLoop();
+
+ Common::Event shutdown_event;
+ std::mutex callback_mutex;
+ std::set<CallbackHandle> error_callbacks;
+ std::unique_ptr<std::thread> announce_multiplayer_thread;
+
+ /// Backend interface that logs fields
+ std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend;
+
+ std::atomic_bool registered = false; ///< Whether the room has been registered
+
+ Network::RoomNetwork& room_network;
+};
+
+} // namespace Core