From 158c0d54b1b4f332ab8fca897a8396bde5686e74 Mon Sep 17 00:00:00 2001 From: Sleepy Flower Girl Date: Mon, 6 Aug 2018 17:56:40 -0400 Subject: [PATCH] Force IPv4 on external IP addresses --- Source/Core/Common/HttpRequest.cpp | 11 ++++ Source/Core/Common/HttpRequest.h | 1 + Source/Core/DolphinQt/DiscordHandler.cpp | 43 +++++++++----- Source/Core/DolphinQt/DiscordHandler.h | 18 ++++-- .../DolphinQt/DiscordJoinRequestDialog.cpp | 39 +++++++------ .../Core/DolphinQt/DiscordJoinRequestDialog.h | 8 +-- Source/Core/DolphinQt/MainWindow.cpp | 9 ++- .../Core/DolphinQt/NetPlay/NetPlayDialog.cpp | 58 ++++++++++--------- Source/Core/DolphinQt/NetPlay/NetPlayDialog.h | 2 +- Source/Core/UICommon/DiscordPresence.cpp | 21 +++---- Source/Core/UICommon/DiscordPresence.h | 5 +- 11 files changed, 129 insertions(+), 86 deletions(-) diff --git a/Source/Core/Common/HttpRequest.cpp b/Source/Core/Common/HttpRequest.cpp index af8eb5f5c5..63e4e85878 100644 --- a/Source/Core/Common/HttpRequest.cpp +++ b/Source/Core/Common/HttpRequest.cpp @@ -29,6 +29,7 @@ public: bool IsValid() const; void SetCookies(const std::string& cookies); + void UseIPv4(); Response Fetch(const std::string& url, Method method, const Headers& headers, const u8* payload, size_t size); @@ -62,6 +63,11 @@ void HttpRequest::SetCookies(const std::string& cookies) m_impl->SetCookies(cookies); } +void HttpRequest::UseIPv4() +{ + m_impl->UseIPv4(); +} + HttpRequest::Response HttpRequest::Get(const std::string& url, const Headers& headers) { return m_impl->Fetch(url, Impl::Method::GET, headers, nullptr, 0); @@ -136,6 +142,11 @@ void HttpRequest::Impl::SetCookies(const std::string& cookies) curl_easy_setopt(m_curl.get(), CURLOPT_COOKIE, cookies.c_str()); } +void HttpRequest::Impl::UseIPv4() +{ + curl_easy_setopt(m_curl.get(), CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); +} + static size_t CurlWriteCallback(char* data, size_t size, size_t nmemb, void* userdata) { auto* buffer = static_cast*>(userdata); diff --git a/Source/Core/Common/HttpRequest.h b/Source/Core/Common/HttpRequest.h index fefb54c784..94916f5c3c 100644 --- a/Source/Core/Common/HttpRequest.h +++ b/Source/Core/Common/HttpRequest.h @@ -32,6 +32,7 @@ public: using Headers = std::map>; void SetCookies(const std::string& cookies); + void UseIPv4(); Response Get(const std::string& url, const Headers& headers = {}); Response Post(const std::string& url, const std::vector& payload, const Headers& headers = {}); diff --git a/Source/Core/DolphinQt/DiscordHandler.cpp b/Source/Core/DolphinQt/DiscordHandler.cpp index fa3f39a8ad..8bfadd21dd 100644 --- a/Source/Core/DolphinQt/DiscordHandler.cpp +++ b/Source/Core/DolphinQt/DiscordHandler.cpp @@ -4,16 +4,19 @@ #ifdef USE_DISCORD_PRESENCE +#include "DolphinQt/DiscordHandler.h" + #include #include -#include "DolphinQt/DiscordHandler.h" - #include "Common/Thread.h" #include "UICommon/DiscordPresence.h" +#include "DolphinQt/DiscordJoinRequestDialog.h" +#include "DolphinQt/QtUtils/RunOnObject.h" + DiscordHandler::DiscordHandler(QWidget* parent) : QObject{parent}, m_parent{parent} { connect(this, &DiscordHandler::JoinRequest, this, &DiscordHandler::ShowNewJoinRequest); @@ -41,8 +44,7 @@ void DiscordHandler::Stop() void DiscordHandler::DiscordJoinRequest(const char* id, const std::string& discord_tag, const char* avatar) { - m_request_dialogs.emplace_front(m_parent, id, discord_tag, avatar); - emit DiscordHandler::JoinRequest(); + emit DiscordHandler::JoinRequest(id, discord_tag, avatar); } void DiscordHandler::DiscordJoin() @@ -50,9 +52,15 @@ void DiscordHandler::DiscordJoin() emit DiscordHandler::Join(); } -void DiscordHandler::ShowNewJoinRequest() +void DiscordHandler::ShowNewJoinRequest(const std::string& id, const std::string& discord_tag, + const std::string& avatar) { - m_request_dialogs.front().show(); + std::lock_guard lock(m_request_dialogs_mutex); + m_request_dialogs.emplace_front(m_parent, id, discord_tag, avatar); + DiscordJoinRequestDialog& request_dialog = m_request_dialogs.front(); + request_dialog.show(); + request_dialog.raise(); + request_dialog.activateWindow(); QApplication::alert(nullptr, DiscordJoinRequestDialog::s_max_lifetime_seconds * 1000); } @@ -64,14 +72,23 @@ void DiscordHandler::Run() Discord::CallPendingCallbacks(); // close and remove dead requests - for (auto request_dialog = m_request_dialogs.rbegin(); - request_dialog != m_request_dialogs.rend(); ++request_dialog) { - if (std::time(nullptr) < request_dialog->GetCloseTimestamp()) - continue; - request_dialog->close(); - std::advance(request_dialog, 1); - m_request_dialogs.erase(request_dialog.base()); + std::lock_guard lock(m_request_dialogs_mutex); + for (auto request_dialog = m_request_dialogs.begin(); + request_dialog != m_request_dialogs.end();) + { + if (std::time(nullptr) < request_dialog->GetCloseTimestamp()) + { + ++request_dialog; + continue; + } + + RunOnObject(m_parent, [this, &request_dialog] { + request_dialog->close(); + request_dialog = m_request_dialogs.erase(request_dialog); + return nullptr; + }); + } } Common::SleepCurrentThread(1000 * 2); diff --git a/Source/Core/DolphinQt/DiscordHandler.h b/Source/Core/DolphinQt/DiscordHandler.h index 4f951ceadd..d1be3d01c0 100644 --- a/Source/Core/DolphinQt/DiscordHandler.h +++ b/Source/Core/DolphinQt/DiscordHandler.h @@ -4,22 +4,22 @@ #pragma once -// Note using a ifdef around this class causes link issues with qt - #include +#include #include #include #include "Common/Flag.h" -#include "DolphinQt/DiscordJoinRequestDialog.h" - #include "UICommon/DiscordPresence.h" +class DiscordJoinRequestDialog; + class DiscordHandler : public QObject, public Discord::Handler { Q_OBJECT +#ifdef USE_DISCORD_PRESENCE public: explicit DiscordHandler(QWidget* parent); ~DiscordHandler(); @@ -29,15 +29,21 @@ public: void DiscordJoin() override; void DiscordJoinRequest(const char* id, const std::string& discord_tag, const char* avatar) override; - void ShowNewJoinRequest(); + void ShowNewJoinRequest(const std::string& id, const std::string& discord_tag, + const std::string& avatar); +#endif + signals: void Join(); - void JoinRequest(); + void JoinRequest(const std::string id, const std::string discord_tag, const std::string avatar); +#ifdef USE_DISCORD_PRESENCE private: void Run(); QWidget* m_parent; Common::Flag m_stop_requested; std::thread m_thread; std::list m_request_dialogs; + std::mutex m_request_dialogs_mutex; +#endif }; diff --git a/Source/Core/DolphinQt/DiscordJoinRequestDialog.cpp b/Source/Core/DolphinQt/DiscordJoinRequestDialog.cpp index c7177a4a03..dd0a6eb64b 100644 --- a/Source/Core/DolphinQt/DiscordJoinRequestDialog.cpp +++ b/Source/Core/DolphinQt/DiscordJoinRequestDialog.cpp @@ -4,6 +4,8 @@ #ifdef USE_DISCORD_PRESENCE +#include "DolphinQt/DiscordJoinRequestDialog.h" + #include #include #include @@ -14,11 +16,9 @@ #include "Common/HttpRequest.h" #include "Common/StringUtil.h" -#include "DolphinQt/DiscordJoinRequestDialog.h" - -DiscordJoinRequestDialog::DiscordJoinRequestDialog(QWidget* parent, const char* id, +DiscordJoinRequestDialog::DiscordJoinRequestDialog(QWidget* parent, const std::string& id, const std::string& discord_tag, - const char* avatar) + const std::string& avatar) : QDialog(parent), m_user_id(id), m_close_timestamp(std::time(nullptr) + s_max_lifetime_seconds) { setWindowTitle(tr("Request to Join Your Party")); @@ -26,10 +26,10 @@ DiscordJoinRequestDialog::DiscordJoinRequestDialog(QWidget* parent, const char* QPixmap avatar_pixmap; - if (avatar[0] != '\0') + if (!avatar.empty()) { - const std::string avatar_endpoint = - StringFromFormat("https://cdn.discordapp.com/avatars/%s/%s.png", id, avatar); + const std::string avatar_endpoint = StringFromFormat( + "https://cdn.discordapp.com/avatars/%s/%s.png", id.c_str(), avatar.c_str()); Common::HttpRequest request; Common::HttpRequest::Response response = request.Get(avatar_endpoint); @@ -38,7 +38,7 @@ DiscordJoinRequestDialog::DiscordJoinRequestDialog(QWidget* parent, const char* avatar_pixmap.loadFromData(response->data(), static_cast(response->size()), "png"); } - CreateMainLayout(discord_tag, avatar_pixmap); + CreateLayout(discord_tag, avatar_pixmap); ConnectWidgets(); } @@ -47,15 +47,18 @@ std::time_t DiscordJoinRequestDialog::GetCloseTimestamp() const return m_close_timestamp; } -void DiscordJoinRequestDialog::CreateMainLayout(const std::string& discord_tag, - const QPixmap& avatar) +void DiscordJoinRequestDialog::CreateLayout(const std::string& discord_tag, const QPixmap& avatar) { m_main_layout = new QGridLayout; - m_invite_button = new QPushButton(QString::fromWCharArray(L"\u2714 Invite")); - m_decline_button = new QPushButton(QString::fromWCharArray(L"\u2716 Decline")); + m_invite_button = new QPushButton(tr("\u2714 Invite")); + m_decline_button = new QPushButton(tr("\u2716 Decline")); m_ignore_button = new QPushButton(tr("Ignore")); + QLabel* text = + new QLabel(tr("%1\nwants to join your party.").arg(QString::fromStdString(discord_tag))); + text->setAlignment(Qt::AlignCenter); + if (!avatar.isNull()) { QLabel* picture = new QLabel(); @@ -63,9 +66,7 @@ void DiscordJoinRequestDialog::CreateMainLayout(const std::string& discord_tag, m_main_layout->addWidget(picture, 1, 0, 1, 3, Qt::AlignHCenter); } - m_main_layout->addWidget(new QLabel(tr(discord_tag.c_str())), 2, 0, 3, 3, Qt::AlignHCenter); - m_main_layout->addWidget(new QLabel(tr("wants to join your party.")), 4, 0, 4, 3, - Qt::AlignHCenter); + m_main_layout->addWidget(text, 2, 0, 3, 3, Qt::AlignHCenter); m_main_layout->addWidget(m_invite_button, 8, 0); m_main_layout->addWidget(m_decline_button, 8, 1); m_main_layout->addWidget(m_ignore_button, 8, 2); @@ -75,15 +76,15 @@ void DiscordJoinRequestDialog::CreateMainLayout(const std::string& discord_tag, void DiscordJoinRequestDialog::ConnectWidgets() { - connect(m_invite_button, &QPushButton::clicked, [this] { Reply(DISCORD_REPLY_YES); }); - connect(m_decline_button, &QPushButton::clicked, [this] { Reply(DISCORD_REPLY_NO); }); - connect(m_ignore_button, &QPushButton::clicked, [this] { Reply(DISCORD_REPLY_IGNORE); }); + connect(m_invite_button, &QPushButton::pressed, [this] { Reply(DISCORD_REPLY_YES); }); + connect(m_decline_button, &QPushButton::pressed, [this] { Reply(DISCORD_REPLY_NO); }); + connect(m_ignore_button, &QPushButton::pressed, [this] { Reply(DISCORD_REPLY_IGNORE); }); connect(this, &QDialog::rejected, this, [this] { Reply(DISCORD_REPLY_IGNORE); }); } void DiscordJoinRequestDialog::Reply(int reply) { - Discord_Respond(m_user_id, reply); + Discord_Respond(m_user_id.c_str(), reply); close(); } diff --git a/Source/Core/DolphinQt/DiscordJoinRequestDialog.h b/Source/Core/DolphinQt/DiscordJoinRequestDialog.h index d2345cf407..d9a7a8c9dd 100644 --- a/Source/Core/DolphinQt/DiscordJoinRequestDialog.h +++ b/Source/Core/DolphinQt/DiscordJoinRequestDialog.h @@ -14,14 +14,14 @@ class DiscordJoinRequestDialog : public QDialog { Q_OBJECT public: - explicit DiscordJoinRequestDialog(QWidget* parent, const char* id, const std::string& discord_tag, - const char* avatar); + explicit DiscordJoinRequestDialog(QWidget* parent, const std::string& id, + const std::string& discord_tag, const std::string& avatar); std::time_t GetCloseTimestamp() const; static constexpr std::time_t s_max_lifetime_seconds = 30; private: - void CreateMainLayout(const std::string& discord_tag, const QPixmap& avatar); + void CreateLayout(const std::string& discord_tag, const QPixmap& avatar); void ConnectWidgets(); void Reply(int reply); @@ -30,6 +30,6 @@ private: QPushButton* m_decline_button; QPushButton* m_ignore_button; - const char* const m_user_id; + const std::string m_user_id; const std::time_t m_close_timestamp; }; diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 7f016e5993..514308e548 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -640,7 +640,8 @@ void MainWindow::OnStopComplete() HideRenderWidget(); EnableScreenSaver(true); #ifdef USE_DISCORD_PRESENCE - Discord::UpdateDiscordPresence(); + if (!m_netplay_dialog->isVisible()) + Discord::UpdateDiscordPresence(); #endif SetFullScreenResolution(false); @@ -794,7 +795,8 @@ void MainWindow::StartGame(std::unique_ptr&& parameters) ShowRenderWidget(); #ifdef USE_DISCORD_PRESENCE - Discord::UpdateDiscordPresence(); + if (!NetPlay::IsNetPlayRunning()) + Discord::UpdateDiscordPresence(); #endif if (SConfig::GetInstance().bFullscreen) @@ -1045,7 +1047,6 @@ void MainWindow::NetPlayInit() { m_netplay_setup_dialog = new NetPlaySetupDialog(this); m_netplay_dialog = new NetPlayDialog; - m_netplay_dialog = new NetPlayDialog(this); #ifdef USE_DISCORD_PRESENCE m_netplay_discord = new DiscordHandler(this); #endif @@ -1180,7 +1181,9 @@ void MainWindow::NetPlayQuit() { Settings::Instance().ResetNetPlayClient(); Settings::Instance().ResetNetPlayServer(); +#ifdef USE_DISCORD_PRESENCE Discord::UpdateDiscordPresence(); +#endif } void MainWindow::EnableScreenSaver(bool enable) diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp index 1e18eaea87..a6775180de 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -473,40 +473,44 @@ void NetPlayDialog::UpdateDiscordPresence() Discord::UpdateDiscordPresence(m_player_count, Discord::SecretType::Empty, "", m_current_game); }; - if (g_TraversalClient) - { - const auto host_id = g_TraversalClient->GetHostID(); - if (host_id == decltype(host_id)()) - return use_default(); + if (Core::IsRunning()) + return use_default(); - Discord::UpdateDiscordPresence(m_player_count, Discord::SecretType::RoomID, - std::string(host_id.begin(), host_id.end()), m_current_game); - } - else if (IsHosting()) + if (IsHosting()) { - if (m_exernal_ip_address.empty()) + if (g_TraversalClient) { - Common::HttpRequest request; - Common::HttpRequest::Response response = - request.Get("https://ip.dolphin-emu.org/", {{"X-Is-Dolphin", "1"}}); - - if (!response.has_value()) + const auto host_id = g_TraversalClient->GetHostID(); + if (host_id[0] == '\0') return use_default(); - m_exernal_ip_address = std::string(response->begin(), response->end()); - } - const int port = Settings::Instance().GetNetPlayServer()->GetPort(); - Discord::UpdateDiscordPresence(m_player_count, Discord::SecretType::IPAddress, - Discord::CreateSecretFromIPAddress(m_exernal_ip_address, port), - m_current_game); + Discord::UpdateDiscordPresence(m_player_count, Discord::SecretType::RoomID, + std::string(host_id.begin(), host_id.end()), m_current_game); + } + else + { + if (m_external_ip_address.empty()) + { + Common::HttpRequest request; + // ENet does not support IPv6, so IPv4 has to be used + request.UseIPv4(); + Common::HttpRequest::Response response = + request.Get("https://ip.dolphin-emu.org/", {{"X-Is-Dolphin", "1"}}); + + if (!response.has_value()) + return use_default(); + m_external_ip_address = std::string(response->begin(), response->end()); + } + const int port = Settings::Instance().GetNetPlayServer()->GetPort(); + + Discord::UpdateDiscordPresence( + m_player_count, Discord::SecretType::IPAddress, + Discord::CreateSecretFromIPAddress(m_external_ip_address, port), m_current_game); + } } else { - Discord::UpdateDiscordPresence( - m_player_count, Discord::SecretType::IPAddress, - Discord::CreateSecretFromIPAddress(Config::Get(Config::NETPLAY_HOST_CODE), - Config::Get(Config::NETPLAY_HOST_PORT)), - m_current_game); + use_default(); } #endif } @@ -729,11 +733,13 @@ void NetPlayDialog::OnMsgStartGame() auto client = Settings::Instance().GetNetPlayClient(); if (client) client->StartGame(FindGame(m_current_game)); + UpdateDiscordPresence(); }); } void NetPlayDialog::OnMsgStopGame() { + QueueOnObject(this, [this] { UpdateDiscordPresence(); }); } void NetPlayDialog::OnPadBufferChanged(u32 buffer) diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h index acb9d27596..a9d835baf7 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h @@ -115,7 +115,7 @@ private: MD5Dialog* m_md5_dialog; PadMappingDialog* m_pad_mapping; std::string m_current_game; - std::string m_exernal_ip_address; + std::string m_external_ip_address; std::string m_nickname; GameListModel* m_game_list_model = nullptr; bool m_use_traversal = false; diff --git a/Source/Core/UICommon/DiscordPresence.cpp b/Source/Core/UICommon/DiscordPresence.cpp index 6f742308c0..5fb7ee676b 100644 --- a/Source/Core/UICommon/DiscordPresence.cpp +++ b/Source/Core/UICommon/DiscordPresence.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "UICommon/DiscordPresence.h" + #include "Common/Hash.h" #include "Common/StringUtil.h" @@ -9,8 +11,6 @@ #include "Core/Config/UISettings.h" #include "Core/ConfigManager.h" -#include "UICommon/DiscordPresence.h" - #ifdef USE_DISCORD_PRESENCE #include @@ -44,7 +44,7 @@ static void HandleDiscordJoin(const char* join_secret) return; if (Config::Get(Config::NETPLAY_NICKNAME) == Config::NETPLAY_NICKNAME.default_value) - Config::SetBaseOrCurrent(Config::NETPLAY_NICKNAME, username); + Config::SetCurrent(Config::NETPLAY_NICKNAME, username); std::string secret(join_secret); @@ -59,22 +59,23 @@ static void HandleDiscordJoin(const char* join_secret) case SecretType::IPAddress: { - Config::SetBaseOrCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, "direct"); + // SetBaseOrCurrent will save the ip address, which isn't what's wanted in this situation + Config::SetCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, "direct"); std::string host = secret.substr(offset, secret.find_last_of(':') - offset); - Config::SetBaseOrCurrent(Config::NETPLAY_HOST_CODE, host); + Config::SetCurrent(Config::NETPLAY_ADDRESS, host); offset += host.length(); if (secret[offset] == ':') - Config::SetBaseOrCurrent(Config::NETPLAY_CONNECT_PORT, std::stoul(secret.substr(offset + 1))); + Config::SetCurrent(Config::NETPLAY_CONNECT_PORT, std::stoul(secret.substr(offset + 1))); } break; case SecretType::RoomID: { - Config::SetBaseOrCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, "traversal"); + Config::SetCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, "traversal"); - Config::SetBaseOrCurrent(Config::NETPLAY_HOST_CODE, secret.substr(offset)); + Config::SetCurrent(Config::NETPLAY_HOST_CODE, secret.substr(offset)); } break; } @@ -120,7 +121,7 @@ void InitNetPlayFunctionality(Handler& handler) #endif } -void UpdateDiscordPresence(const int party_size, SecretType type, const std::string& secret, +void UpdateDiscordPresence(int party_size, SecretType type, const std::string& secret, const std::string& current_game) { #ifdef USE_DISCORD_PRESENCE @@ -136,7 +137,7 @@ void UpdateDiscordPresence(const int party_size, SecretType type, const std::str discord_presence.details = title.empty() ? "Not in-game" : title.c_str(); discord_presence.startTimestamp = std::time(nullptr); - if (0 < party_size) + if (party_size > 0) { if (party_size < 4) { diff --git a/Source/Core/UICommon/DiscordPresence.h b/Source/Core/UICommon/DiscordPresence.h index c7e921e804..70ff1e46fb 100644 --- a/Source/Core/UICommon/DiscordPresence.h +++ b/Source/Core/UICommon/DiscordPresence.h @@ -5,13 +5,10 @@ #pragma once #include +#include namespace Discord { -using JoinFunction = std::function; -using JoinRequestFunction = - std::function; - class Handler { public: