|
| 1 | +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project |
| 2 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | + |
| 4 | +#include <chrono> |
| 5 | +#include <future> |
| 6 | +#include <vector> |
| 7 | +#include "announce_multiplayer_session.h" |
| 8 | +#include "yuzu_common/announce_multiplayer_room.h" |
| 9 | +#include "yuzu_common/yuzu_assert.h" |
| 10 | +#include "yuzu_common/settings.h" |
| 11 | +#include "network/network.h" |
| 12 | + |
| 13 | +#ifdef ENABLE_WEB_SERVICE |
| 14 | +#include "web_service/announce_room_json.h" |
| 15 | +#endif |
| 16 | + |
| 17 | +namespace Core { |
| 18 | + |
| 19 | +// Time between room is announced to web_service |
| 20 | +static constexpr std::chrono::seconds announce_time_interval(15); |
| 21 | + |
| 22 | +AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_) |
| 23 | + : room_network{room_network_} { |
| 24 | +#ifdef ENABLE_WEB_SERVICE |
| 25 | + backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(), |
| 26 | + Settings::values.yuzu_username.GetValue(), |
| 27 | + Settings::values.yuzu_token.GetValue()); |
| 28 | +#else |
| 29 | + backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>(); |
| 30 | +#endif |
| 31 | +} |
| 32 | + |
| 33 | +WebService::WebResult AnnounceMultiplayerSession::Register() { |
| 34 | + auto room = room_network.GetRoom().lock(); |
| 35 | + if (!room) { |
| 36 | + return WebService::WebResult{WebService::WebResult::Code::LibError, |
| 37 | + "Network is not initialized", ""}; |
| 38 | + } |
| 39 | + if (room->GetState() != Network::Room::State::Open) { |
| 40 | + return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""}; |
| 41 | + } |
| 42 | + UpdateBackendData(room); |
| 43 | + WebService::WebResult result = backend->Register(); |
| 44 | + if (result.result_code != WebService::WebResult::Code::Success) { |
| 45 | + return result; |
| 46 | + } |
| 47 | + LOG_INFO(WebService, "Room has been registered"); |
| 48 | + room->SetVerifyUID(result.returned_data); |
| 49 | + registered = true; |
| 50 | + return WebService::WebResult{WebService::WebResult::Code::Success, "", ""}; |
| 51 | +} |
| 52 | + |
| 53 | +void AnnounceMultiplayerSession::Start() { |
| 54 | + if (announce_multiplayer_thread) { |
| 55 | + Stop(); |
| 56 | + } |
| 57 | + shutdown_event.Reset(); |
| 58 | + announce_multiplayer_thread = |
| 59 | + std::make_unique<std::thread>(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this); |
| 60 | +} |
| 61 | + |
| 62 | +void AnnounceMultiplayerSession::Stop() { |
| 63 | + if (announce_multiplayer_thread) { |
| 64 | + shutdown_event.Set(); |
| 65 | + announce_multiplayer_thread->join(); |
| 66 | + announce_multiplayer_thread.reset(); |
| 67 | + backend->Delete(); |
| 68 | + registered = false; |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback( |
| 73 | + std::function<void(const WebService::WebResult&)> function) { |
| 74 | + std::lock_guard lock(callback_mutex); |
| 75 | + auto handle = std::make_shared<std::function<void(const WebService::WebResult&)>>(function); |
| 76 | + error_callbacks.insert(handle); |
| 77 | + return handle; |
| 78 | +} |
| 79 | + |
| 80 | +void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) { |
| 81 | + std::lock_guard lock(callback_mutex); |
| 82 | + error_callbacks.erase(handle); |
| 83 | +} |
| 84 | + |
| 85 | +AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { |
| 86 | + Stop(); |
| 87 | +} |
| 88 | + |
| 89 | +void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room> room) { |
| 90 | + Network::RoomInformation room_information = room->GetRoomInformation(); |
| 91 | + std::vector<AnnounceMultiplayerRoom::Member> memberlist = room->GetRoomMemberList(); |
| 92 | + backend->SetRoomInformation(room_information.name, room_information.description, |
| 93 | + room_information.port, room_information.member_slots, |
| 94 | + Network::network_version, room->HasPassword(), |
| 95 | + room_information.preferred_game); |
| 96 | + backend->ClearPlayers(); |
| 97 | + for (const auto& member : memberlist) { |
| 98 | + backend->AddPlayer(member); |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { |
| 103 | + // Invokes all current bound error callbacks. |
| 104 | + const auto ErrorCallback = [this](WebService::WebResult result) { |
| 105 | + std::lock_guard lock(callback_mutex); |
| 106 | + for (auto callback : error_callbacks) { |
| 107 | + (*callback)(result); |
| 108 | + } |
| 109 | + }; |
| 110 | + |
| 111 | + if (!registered) { |
| 112 | + WebService::WebResult result = Register(); |
| 113 | + if (result.result_code != WebService::WebResult::Code::Success) { |
| 114 | + ErrorCallback(result); |
| 115 | + return; |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + auto update_time = std::chrono::steady_clock::now(); |
| 120 | + std::future<WebService::WebResult> future; |
| 121 | + while (!shutdown_event.WaitUntil(update_time)) { |
| 122 | + update_time += announce_time_interval; |
| 123 | + auto room = room_network.GetRoom().lock(); |
| 124 | + if (!room) { |
| 125 | + break; |
| 126 | + } |
| 127 | + if (room->GetState() != Network::Room::State::Open) { |
| 128 | + break; |
| 129 | + } |
| 130 | + UpdateBackendData(room); |
| 131 | + WebService::WebResult result = backend->Update(); |
| 132 | + if (result.result_code != WebService::WebResult::Code::Success) { |
| 133 | + ErrorCallback(result); |
| 134 | + } |
| 135 | + if (result.result_string == "404") { |
| 136 | + registered = false; |
| 137 | + // Needs to register the room again |
| 138 | + WebService::WebResult register_result = Register(); |
| 139 | + if (register_result.result_code != WebService::WebResult::Code::Success) { |
| 140 | + ErrorCallback(register_result); |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() { |
| 147 | + return backend->GetRoomList(); |
| 148 | +} |
| 149 | + |
| 150 | +bool AnnounceMultiplayerSession::IsRunning() const { |
| 151 | + return announce_multiplayer_thread != nullptr; |
| 152 | +} |
| 153 | + |
| 154 | +void AnnounceMultiplayerSession::UpdateCredentials() { |
| 155 | + ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running"); |
| 156 | + |
| 157 | +#ifdef ENABLE_WEB_SERVICE |
| 158 | + backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(), |
| 159 | + Settings::values.yuzu_username.GetValue(), |
| 160 | + Settings::values.yuzu_token.GetValue()); |
| 161 | +#endif |
| 162 | +} |
| 163 | + |
| 164 | +} // namespace Core |
0 commit comments