summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
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
-rw-r--r--src/network/room.cpp14
-rw-r--r--src/network/room_member.cpp6
-rw-r--r--src/network/room_member.h2
6 files changed, 282 insertions, 8 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
diff --git a/src/network/room.cpp b/src/network/room.cpp
index b06797bf1..8c63b255b 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -221,7 +221,7 @@ public:
* Extracts the game name from a received ENet packet and broadcasts it.
* @param event The ENet event that was received.
*/
- void HandleGameNamePacket(const ENetEvent* event);
+ void HandleGameInfoPacket(const ENetEvent* event);
/**
* Removes the client from the members list if it was in it and announces the change
@@ -234,7 +234,7 @@ public:
void Room::RoomImpl::ServerLoop() {
while (state != State::Closed) {
ENetEvent event;
- if (enet_host_service(server, &event, 50) > 0) {
+ if (enet_host_service(server, &event, 5) > 0) {
switch (event.type) {
case ENET_EVENT_TYPE_RECEIVE:
switch (event.packet->data[0]) {
@@ -242,7 +242,7 @@ void Room::RoomImpl::ServerLoop() {
HandleJoinRequest(&event);
break;
case IdSetGameInfo:
- HandleGameNamePacket(&event);
+ HandleGameInfoPacket(&event);
break;
case IdProxyPacket:
HandleProxyPacket(&event);
@@ -778,6 +778,7 @@ void Room::RoomImpl::BroadcastRoomInformation() {
packet.Write(member.fake_ip);
packet.Write(member.game_info.name);
packet.Write(member.game_info.id);
+ packet.Write(member.game_info.version);
packet.Write(member.user_data.username);
packet.Write(member.user_data.display_name);
packet.Write(member.user_data.avatar_url);
@@ -817,6 +818,7 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
in_packet.IgnoreBytes(sizeof(u16)); // Port
in_packet.IgnoreBytes(sizeof(u8)); // Protocol
+
bool broadcast;
in_packet.Read(broadcast); // Broadcast
@@ -909,7 +911,7 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
}
}
-void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
+void Room::RoomImpl::HandleGameInfoPacket(const ENetEvent* event) {
Packet in_packet;
in_packet.Append(event->packet->data, event->packet->dataLength);
@@ -917,6 +919,7 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
GameInfo game_info;
in_packet.Read(game_info.name);
in_packet.Read(game_info.id);
+ in_packet.Read(game_info.version);
{
std::lock_guard lock(member_mutex);
@@ -935,7 +938,8 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
if (game_info.name.empty()) {
LOG_INFO(Network, "{} is not playing", display_name);
} else {
- LOG_INFO(Network, "{} is playing {}", display_name, game_info.name);
+ LOG_INFO(Network, "{} is playing {} ({})", display_name, game_info.name,
+ game_info.version);
}
}
}
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index 9f08bf611..06818af78 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -103,7 +103,7 @@ public:
/**
* Extracts a ProxyPacket from a received ENet packet.
- * @param event The ENet event that was received.
+ * @param event The ENet event that was received.
*/
void HandleProxyPackets(const ENetEvent* event);
@@ -159,7 +159,7 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
while (IsConnected()) {
std::lock_guard lock(network_mutex);
ENetEvent event;
- if (enet_host_service(client, &event, 100) > 0) {
+ if (enet_host_service(client, &event, 5) > 0) {
switch (event.type) {
case ENET_EVENT_TYPE_RECEIVE:
switch (event.packet->data[0]) {
@@ -315,6 +315,7 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev
packet.Read(member.fake_ip);
packet.Read(member.game_info.name);
packet.Read(member.game_info.id);
+ packet.Read(member.game_info.version);
packet.Read(member.username);
packet.Read(member.display_name);
packet.Read(member.avatar_url);
@@ -622,6 +623,7 @@ void RoomMember::SendGameInfo(const GameInfo& game_info) {
packet.Write(static_cast<u8>(IdSetGameInfo));
packet.Write(game_info.name);
packet.Write(game_info.id);
+ packet.Write(game_info.version);
room_member_impl->Send(std::move(packet));
}
diff --git a/src/network/room_member.h b/src/network/room_member.h
index 4252b7146..f578f7f6a 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -146,7 +146,7 @@ public:
const std::string& password = "", const std::string& token = "");
/**
- * Sends a WiFi packet to the room.
+ * Sends a Proxy packet to the room.
* @param packet The WiFi packet to send.
*/
void SendProxyPacket(const ProxyPacket& packet);