diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt
index eff3bdd840..b54d09ebf6 100644
--- a/Source/Core/DolphinQt/CMakeLists.txt
+++ b/Source/Core/DolphinQt/CMakeLists.txt
@@ -8,6 +8,7 @@ set(CMAKE_AUTOMOC ON)
add_executable(dolphin-emu
AboutDialog.cpp
CheatsManager.cpp
+ DiscordHandler.cpp
FIFO/FIFOPlayerWindow.cpp
FIFO/FIFOAnalyzer.cpp
HotkeyScheduler.cpp
diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj
index 0228011308..3e5ad13b14 100644
--- a/Source/Core/DolphinQt/DolphinQt.vcxproj
+++ b/Source/Core/DolphinQt/DolphinQt.vcxproj
@@ -107,6 +107,7 @@
+
@@ -175,6 +176,7 @@
+
@@ -320,6 +322,7 @@
+
diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp
index c77b08718f..4804b469bc 100644
--- a/Source/Core/DolphinQt/MainWindow.cpp
+++ b/Source/Core/DolphinQt/MainWindow.cpp
@@ -67,6 +67,7 @@
#include "DolphinQt/Debugger/MemoryWidget.h"
#include "DolphinQt/Debugger/RegisterWidget.h"
#include "DolphinQt/Debugger/WatchWidget.h"
+#include "DolphinQt/DiscordHandler.h"
#include "DolphinQt/FIFO/FIFOPlayerWindow.h"
#include "DolphinQt/GCMemcardManager.h"
#include "DolphinQt/GameList/GameList.h"
@@ -1043,7 +1044,12 @@ void MainWindow::BootWiiSystemMenu()
void MainWindow::NetPlayInit()
{
m_netplay_setup_dialog = new NetPlaySetupDialog(this);
+<<<<<<< HEAD:Source/Core/DolphinQt/MainWindow.cpp
m_netplay_dialog = new NetPlayDialog;
+=======
+ m_netplay_dialog = new NetPlayDialog(this);
+ m_netplay_discord = new DiscordHandler();
+>>>>>>> Add Discord Join Net Play functionally:Source/Core/DolphinQt2/MainWindow.cpp
connect(m_netplay_dialog, &NetPlayDialog::Boot, this,
[this](const QString& path) { StartGame(path); });
@@ -1051,6 +1057,10 @@ void MainWindow::NetPlayInit()
connect(m_netplay_dialog, &NetPlayDialog::rejected, this, &MainWindow::NetPlayQuit);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Join, this, &MainWindow::NetPlayJoin);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Host, this, &MainWindow::NetPlayHost);
+ connect(m_netplay_discord, &DiscordHandler::Join, this, &MainWindow::NetPlayJoin);
+
+ Discord::InitNetPlayFunctionality([this] { m_netplay_discord->DiscordJoin(); });
+ m_netplay_discord->Start();
}
bool MainWindow::NetPlayJoin()
@@ -1169,6 +1179,7 @@ void MainWindow::NetPlayQuit()
{
Settings::Instance().ResetNetPlayClient();
Settings::Instance().ResetNetPlayServer();
+ Discord::UpdateDiscordPresence();
}
void MainWindow::EnableScreenSaver(bool enable)
diff --git a/Source/Core/DolphinQt/MainWindow.h b/Source/Core/DolphinQt/MainWindow.h
index 8c387a5f20..360e7343de 100644
--- a/Source/Core/DolphinQt/MainWindow.h
+++ b/Source/Core/DolphinQt/MainWindow.h
@@ -19,6 +19,7 @@ struct BootParameters;
class CheatsManager;
class CodeWidget;
class ControllersWindow;
+class DiscordHandler;
class DragEnterEvent;
class FIFOPlayerWindow;
class GameList;
@@ -183,6 +184,7 @@ private:
ControllersWindow* m_controllers_window;
SettingsWindow* m_settings_window;
NetPlayDialog* m_netplay_dialog;
+ DiscordHandler* m_netplay_discord;
NetPlaySetupDialog* m_netplay_setup_dialog;
GraphicsWindow* m_graphics_window;
static constexpr int num_gc_controllers = 4;
diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp
index e5af25d6cf..5224fb4820 100644
--- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp
+++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp
@@ -29,6 +29,7 @@
#include "Common/CommonPaths.h"
#include "Common/Config/Config.h"
+#include "Common/HttpRequest.h"
#include "Common/TraversalClient.h"
#include "Core/Config/GraphicsSettings.h"
@@ -49,6 +50,7 @@
#include "DolphinQt/Resources.h"
#include "DolphinQt/Settings.h"
+#include "UICommon/DiscordPresence.h"
#include "UICommon/GameFile.h"
#include "VideoCommon/VideoConfig.h"
@@ -418,6 +420,7 @@ void NetPlayDialog::show(std::string nickname, bool use_traversal)
m_nickname = nickname;
m_use_traversal = use_traversal;
m_buffer_size = 0;
+ m_old_player_count = 0;
m_room_box->clear();
m_chat_edit->clear();
@@ -565,6 +568,41 @@ void NetPlayDialog::UpdateGUI()
m_hostcode_action_button->setText(tr("Copy"));
m_hostcode_action_button->setEnabled(true);
}
+
+ if (m_old_player_count != player_count)
+ {
+ if (m_use_traversal)
+ {
+ const auto host_id = g_TraversalClient->GetHostID();
+ Discord::UpdateDiscordPresence(player_count, Discord::SecretType::RoomID,
+ std::string(host_id.begin(), host_id.end()));
+ }
+ else
+ {
+ // Temporary soluation
+ // To Do: Don't rely on a service that Dolphin devs aren't in control of. Ask one of the
+ // project managers about this.
+
+ Common::HttpRequest request;
+ Common::HttpRequest::Response response = request.Get("https://www.myexternalip.com/raw");
+
+ if (!response.has_value())
+ return;
+
+ // The response ends with a /n and the - 1 removes that
+ std::string exernalIPAddress = std::string(response->begin(), response->end() - 1);
+ std::string port = std::to_string(Settings::Instance().GetNetPlayServer()->GetPort());
+ std::string secret;
+ secret.reserve(exernalIPAddress.length() + 1 + port.length());
+ secret += exernalIPAddress;
+ secret += ':';
+ secret += port;
+
+ Discord::UpdateDiscordPresence(player_count, Discord::SecretType::IPAddress, secret);
+ }
+
+ m_old_player_count = player_count;
+ }
}
// NetPlayUI methods
diff --git a/Source/Core/DolphinQt2/DiscordHandler.cpp b/Source/Core/DolphinQt2/DiscordHandler.cpp
new file mode 100644
index 0000000000..bad505f2de
--- /dev/null
+++ b/Source/Core/DolphinQt2/DiscordHandler.cpp
@@ -0,0 +1,45 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt2/DiscordHandler.h"
+
+#include "Common/Thread.h"
+
+#include "UICommon/DiscordPresence.h"
+
+DiscordHandler::DiscordHandler() = default;
+
+DiscordHandler::~DiscordHandler()
+{
+ Stop();
+}
+
+void DiscordHandler::Start()
+{
+ m_stop_requested.Set(false);
+ m_thread = std::thread(&DiscordHandler::Run, this);
+}
+
+void DiscordHandler::Stop()
+{
+ m_stop_requested.Set(true);
+
+ if (m_thread.joinable())
+ m_thread.join();
+}
+
+void DiscordHandler::DiscordJoin()
+{
+ emit DiscordHandler::Join();
+}
+
+void DiscordHandler::Run()
+{
+ while (!m_stop_requested.IsSet())
+ {
+ Common::SleepCurrentThread(1000 * 2);
+
+ Discord::CallPendingCallbacks();
+ }
+}
diff --git a/Source/Core/DolphinQt2/DiscordHandler.h b/Source/Core/DolphinQt2/DiscordHandler.h
new file mode 100644
index 0000000000..1cc6787b46
--- /dev/null
+++ b/Source/Core/DolphinQt2/DiscordHandler.h
@@ -0,0 +1,30 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+#include
+
+#include "Common/Flag.h"
+
+class DiscordHandler : public QObject
+{
+ Q_OBJECT
+public:
+ explicit DiscordHandler();
+ ~DiscordHandler();
+
+ void Start();
+ void Stop();
+ void DiscordJoin();
+signals:
+ void Join();
+
+private:
+ void Run();
+ Common::Flag m_stop_requested;
+ std::thread m_thread;
+};
diff --git a/Source/Core/UICommon/DiscordPresence.cpp b/Source/Core/UICommon/DiscordPresence.cpp
index dece60dcbc..afd57d87c7 100644
--- a/Source/Core/UICommon/DiscordPresence.cpp
+++ b/Source/Core/UICommon/DiscordPresence.cpp
@@ -2,10 +2,14 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "UICommon/DiscordPresence.h"
+#include "Common/Hash.h"
+
+#include "Core/Config/NetplaySettings.h"
#include "Core/Config/UISettings.h"
#include "Core/ConfigManager.h"
+#include "UICommon/DiscordPresence.h"
+
#ifdef USE_DISCORD_PRESENCE
#include
@@ -15,6 +19,61 @@
namespace Discord
{
+#ifdef USE_DISCORD_PRESENCE
+static JoinFunction join_function = nullptr;
+static const char* username = "";
+
+static void HandleDiscordReady(const DiscordUser* user)
+{
+ username = user->username;
+}
+
+static void HandleDiscordJoin(const char* join_secret)
+{
+ if (join_function == nullptr)
+ return;
+
+ if (Config::Get(Config::NETPLAY_NICKNAME) == Config::NETPLAY_NICKNAME.default_value)
+ Config::SetBaseOrCurrent(Config::NETPLAY_NICKNAME, username);
+
+ std::string secret(join_secret);
+
+ size_t offset = 0;
+ std::string type = secret.substr(offset, secret.find('\n'));
+ offset += type.length() + 1;
+
+ switch (static_cast(std::stol(type)))
+ {
+ default:
+ case SecretType::Empty:
+ return;
+
+ case SecretType::IPAddress:
+ {
+ Config::SetBaseOrCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, "direct");
+
+ std::string host = secret.substr(offset, secret.find_last_of(':') - offset);
+ Config::SetBaseOrCurrent(Config::NETPLAY_HOST_CODE, host);
+
+ offset += host.length();
+ if (secret[offset] == ':')
+ Config::SetBaseOrCurrent(Config::NETPLAY_CONNECT_PORT, std::stoul(secret.substr(offset + 1)));
+ }
+ break;
+
+ case SecretType::RoomID:
+ {
+ Config::SetBaseOrCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, "traversal");
+
+ Config::SetBaseOrCurrent(Config::NETPLAY_HOST_CODE, secret.substr(offset));
+ }
+ break;
+ }
+
+ join_function();
+}
+#endif
+
void Init()
{
#ifdef USE_DISCORD_PRESENCE
@@ -22,13 +81,34 @@ void Init()
return;
DiscordEventHandlers handlers = {};
+
+ handlers.ready = HandleDiscordReady;
+ handlers.joinGame = HandleDiscordJoin;
// The number is the client ID for Dolphin, it's used for images and the appication name
Discord_Initialize("455712169795780630", &handlers, 1, nullptr);
UpdateDiscordPresence();
#endif
}
-void UpdateDiscordPresence()
+void CallPendingCallbacks()
+{
+#ifdef USE_DISCORD_PRESENCE
+ if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE))
+ return;
+
+ Discord_RunCallbacks();
+
+#endif
+}
+
+void InitNetPlayFunctionality(const JoinFunction& join)
+{
+#ifdef USE_DISCORD_PRESENCE
+ join_function = std::move(join);
+#endif
+}
+
+void UpdateDiscordPresence(const int party_size, SecretType type, const std::string& secret)
{
#ifdef USE_DISCORD_PRESENCE
if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE))
@@ -41,6 +121,44 @@ void UpdateDiscordPresence()
discord_presence.largeImageText = "Dolphin is an emulator for the GameCube and the Wii.";
discord_presence.details = title.empty() ? "Not in-game" : title.c_str();
discord_presence.startTimestamp = std::time(nullptr);
+
+ if (0 < party_size)
+ {
+ if (party_size < 4)
+ {
+ discord_presence.state = "In a party";
+ discord_presence.partySize = party_size;
+ discord_presence.partyMax = 4;
+ }
+ else
+ {
+ // others can still join to spectate
+ discord_presence.state = "In a full party";
+ discord_presence.partySize = party_size;
+ // Note: joining still works without partyMax
+ }
+ }
+
+ std::string party_ID;
+ std::string secret_final;
+ if (type != SecretType::Empty)
+ {
+ // Declearing party_ID or secret_final here will deallocate the variable before passing the
+ // values over to Discord_UpdatePresence.
+
+ const size_t secret_length = secret.length();
+ party_ID = std::to_string(
+ Common::HashAdler32(reinterpret_cast(secret.c_str()), secret_length));
+
+ const std::string secret_type = std::to_string(static_cast(type));
+ secret_final.reserve(secret_type.length() + 1 + secret_length);
+ secret_final += secret_type;
+ secret_final += '\n';
+ secret_final += secret;
+ }
+ discord_presence.partyId = party_ID.c_str();
+ discord_presence.joinSecret = secret_final.c_str();
+
Discord_UpdatePresence(&discord_presence);
#endif
}
diff --git a/Source/Core/UICommon/DiscordPresence.h b/Source/Core/UICommon/DiscordPresence.h
index 9c4673f885..f2e40bb0b6 100644
--- a/Source/Core/UICommon/DiscordPresence.h
+++ b/Source/Core/UICommon/DiscordPresence.h
@@ -4,10 +4,24 @@
#pragma once
+#include
+
namespace Discord
{
+using JoinFunction = std::function;
+
+enum class SecretType : char
+{
+ Empty,
+ IPAddress,
+ RoomID,
+};
+
void Init();
-void UpdateDiscordPresence();
+void InitNetPlayFunctionality(const JoinFunction& join);
+void CallPendingCallbacks();
+void UpdateDiscordPresence(int party_size = 0, SecretType type = SecretType::Empty,
+ const std::string& secret = {});
void Shutdown();
void SetDiscordPresenceEnabled(bool enabled);
} // namespace Discord