announce_multiplayer_session.cpp
1 // Copyright 2017 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #include <chrono> 6 #include <future> 7 #include <vector> 8 #include "announce_multiplayer_session.h" 9 #include "common/announce_multiplayer_room.h" 10 #include "common/assert.h" 11 #include "network/network.h" 12 #include "network/network_settings.h" 13 14 #ifdef ENABLE_WEB_SERVICE 15 #include "web_service/announce_room_json.h" 16 #endif 17 18 namespace Network { 19 20 // Time between room is announced to web_service 21 static constexpr std::chrono::seconds announce_time_interval(15); 22 23 AnnounceMultiplayerSession::AnnounceMultiplayerSession() { 24 #ifdef ENABLE_WEB_SERVICE 25 backend = std::make_unique<WebService::RoomJson>(NetSettings::values.web_api_url, 26 NetSettings::values.citra_username, 27 NetSettings::values.citra_token); 28 #else 29 backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>(); 30 #endif 31 } 32 33 Common::WebResult AnnounceMultiplayerSession::Register() { 34 std::shared_ptr<Network::Room> room = Network::GetRoom().lock(); 35 if (!room) { 36 return Common::WebResult{Common::WebResult::Code::LibError, "Network is not initialized"}; 37 } 38 if (room->GetState() != Network::Room::State::Open) { 39 return Common::WebResult{Common::WebResult::Code::LibError, "Room is not open"}; 40 } 41 UpdateBackendData(room); 42 Common::WebResult result = backend->Register(); 43 if (result.result_code != Common::WebResult::Code::Success) { 44 return result; 45 } 46 LOG_INFO(WebService, "Room has been registered"); 47 room->SetVerifyUID(result.returned_data); 48 registered = true; 49 return Common::WebResult{Common::WebResult::Code::Success}; 50 } 51 52 void AnnounceMultiplayerSession::Start() { 53 if (announce_multiplayer_thread) { 54 Stop(); 55 } 56 shutdown_event.Reset(); 57 announce_multiplayer_thread = 58 std::make_unique<std::thread>(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this); 59 } 60 61 void AnnounceMultiplayerSession::Stop() { 62 if (announce_multiplayer_thread) { 63 shutdown_event.Set(); 64 announce_multiplayer_thread->join(); 65 announce_multiplayer_thread.reset(); 66 backend->Delete(); 67 registered = false; 68 } 69 } 70 71 AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback( 72 std::function<void(const Common::WebResult&)> function) { 73 std::lock_guard lock(callback_mutex); 74 auto handle = std::make_shared<std::function<void(const Common::WebResult&)>>(function); 75 error_callbacks.insert(handle); 76 return handle; 77 } 78 79 void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) { 80 std::lock_guard lock(callback_mutex); 81 error_callbacks.erase(handle); 82 } 83 84 AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { 85 Stop(); 86 } 87 88 void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room> room) { 89 Network::RoomInformation room_information = room->GetRoomInformation(); 90 std::vector<Network::Room::Member> memberlist = room->GetRoomMemberList(); 91 backend->SetRoomInformation( 92 room_information.name, room_information.description, room_information.port, 93 room_information.member_slots, Network::network_version, room->HasPassword(), 94 room_information.preferred_game, room_information.preferred_game_id); 95 backend->ClearPlayers(); 96 for (const auto& member : memberlist) { 97 backend->AddPlayer(member.username, member.nickname, member.avatar_url, member.mac_address, 98 member.game_info.id, member.game_info.name); 99 } 100 } 101 102 void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { 103 // Invokes all current bound error callbacks. 104 const auto ErrorCallback = [this](Common::WebResult result) { 105 std::lock_guard<std::mutex> lock(callback_mutex); 106 for (auto callback : error_callbacks) { 107 (*callback)(result); 108 } 109 }; 110 111 if (!registered) { 112 Common::WebResult result = Register(); 113 if (result.result_code != Common::WebResult::Code::Success) { 114 ErrorCallback(result); 115 return; 116 } 117 } 118 119 auto update_time = std::chrono::steady_clock::now(); 120 std::future<Common::WebResult> future; 121 while (!shutdown_event.WaitUntil(update_time)) { 122 update_time += announce_time_interval; 123 std::shared_ptr<Network::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 Common::WebResult result = backend->Update(); 132 if (result.result_code != Common::WebResult::Code::Success) { 133 ErrorCallback(result); 134 } 135 if (result.result_string == "404") { 136 registered = false; 137 // Needs to register the room again 138 Common::WebResult new_result = Register(); 139 if (new_result.result_code != Common::WebResult::Code::Success) { 140 ErrorCallback(new_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>(NetSettings::values.web_api_url, 159 NetSettings::values.citra_username, 160 NetSettings::values.citra_token); 161 #endif 162 } 163 164 } // namespace Network