dolphin/Source/Core/UICommon/DiscordPresence.cpp

253 lines
6.5 KiB
C++
Raw Normal View History

2018-05-27 04:24:13 +00:00
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
2018-08-06 21:56:40 +00:00
#include "UICommon/DiscordPresence.h"
2018-07-03 21:50:08 +00:00
#include "Common/Hash.h"
#include "Common/StringUtil.h"
2018-07-03 21:50:08 +00:00
#include "Core/Config/NetplaySettings.h"
#include "Core/Config/UISettings.h"
#include "Core/ConfigManager.h"
#ifdef USE_DISCORD_PRESENCE
2018-05-27 04:24:13 +00:00
#include <algorithm>
#include <cctype>
2018-05-27 04:24:13 +00:00
#include <ctime>
#include <discord-rpc/include/discord_rpc.h>
#include <string>
2018-05-27 04:24:13 +00:00
#endif
namespace Discord
{
2018-07-03 21:50:08 +00:00
#ifdef USE_DISCORD_PRESENCE
namespace
{
Handler* event_handler = nullptr;
const char* username = "";
2018-07-03 21:50:08 +00:00
void HandleDiscordReady(const DiscordUser* user)
2018-07-03 21:50:08 +00:00
{
username = user->username;
}
void HandleDiscordJoinRequest(const DiscordUser* user)
{
if (event_handler == nullptr)
return;
const std::string discord_tag = StringFromFormat("%s#%s", user->username, user->discriminator);
event_handler->DiscordJoinRequest(user->userId, discord_tag, user->avatar);
}
void HandleDiscordJoin(const char* join_secret)
2018-07-03 21:50:08 +00:00
{
if (event_handler == nullptr)
2018-07-03 21:50:08 +00:00
return;
if (Config::Get(Config::NETPLAY_NICKNAME) == Config::NETPLAY_NICKNAME.default_value)
2018-08-06 21:56:40 +00:00
Config::SetCurrent(Config::NETPLAY_NICKNAME, username);
2018-07-03 21:50:08 +00:00
std::string secret(join_secret);
std::string type = secret.substr(0, secret.find('\n'));
size_t offset = type.length() + 1;
2018-07-03 21:50:08 +00:00
switch (static_cast<SecretType>(std::stol(type)))
{
default:
case SecretType::Empty:
return;
case SecretType::IPAddress:
{
2018-08-06 21:56:40 +00:00
// SetBaseOrCurrent will save the ip address, which isn't what's wanted in this situation
Config::SetCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, "direct");
2018-07-03 21:50:08 +00:00
std::string host = secret.substr(offset, secret.find_last_of(':') - offset);
2018-08-06 21:56:40 +00:00
Config::SetCurrent(Config::NETPLAY_ADDRESS, host);
2018-07-03 21:50:08 +00:00
offset += host.length();
if (secret[offset] == ':')
2018-08-06 21:56:40 +00:00
Config::SetCurrent(Config::NETPLAY_CONNECT_PORT, std::stoul(secret.substr(offset + 1)));
2018-07-03 21:50:08 +00:00
}
break;
case SecretType::RoomID:
{
2018-08-06 21:56:40 +00:00
Config::SetCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, "traversal");
2018-07-03 21:50:08 +00:00
2018-08-06 21:56:40 +00:00
Config::SetCurrent(Config::NETPLAY_HOST_CODE, secret.substr(offset));
2018-07-03 21:50:08 +00:00
}
break;
}
event_handler->DiscordJoin();
2018-07-03 21:50:08 +00:00
}
std::string ArtworkForGameId(const std::string& gameid)
{
static const std::set<std::string> REGISTERED_GAMES{
"GAL", // Super Smash Bros. Melee
};
std::string region_neutral_gameid = gameid.substr(0, 3);
if (REGISTERED_GAMES.count(region_neutral_gameid) != 0)
{
// Discord asset keys can only be lowercase.
std::transform(region_neutral_gameid.begin(), region_neutral_gameid.end(),
region_neutral_gameid.begin(), tolower);
return "game_" + region_neutral_gameid;
}
return "";
}
} // namespace
2018-07-03 21:50:08 +00:00
#endif
Discord::Handler::~Handler() = default;
2018-05-27 04:24:13 +00:00
void Init()
{
#ifdef USE_DISCORD_PRESENCE
if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE))
return;
2018-05-27 04:24:13 +00:00
DiscordEventHandlers handlers = {};
2018-07-03 21:50:08 +00:00
handlers.ready = HandleDiscordReady;
handlers.joinRequest = HandleDiscordJoinRequest;
2018-07-03 21:50:08 +00:00
handlers.joinGame = HandleDiscordJoin;
2018-05-27 04:24:13 +00:00
// The number is the client ID for Dolphin, it's used for images and the appication name
Discord_Initialize("455712169795780630", &handlers, 1, nullptr);
2018-05-27 04:24:13 +00:00
UpdateDiscordPresence();
#endif
}
2018-07-03 21:50:08 +00:00
void CallPendingCallbacks()
{
#ifdef USE_DISCORD_PRESENCE
if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE))
return;
Discord_RunCallbacks();
#endif
}
void InitNetPlayFunctionality(Handler& handler)
2018-07-03 21:50:08 +00:00
{
#ifdef USE_DISCORD_PRESENCE
event_handler = &handler;
2018-07-03 21:50:08 +00:00
#endif
}
2018-08-06 21:56:40 +00:00
void UpdateDiscordPresence(int party_size, SecretType type, const std::string& secret,
const std::string& current_game)
2018-05-27 04:24:13 +00:00
{
#ifdef USE_DISCORD_PRESENCE
if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE))
return;
const std::string& title =
current_game.empty() ? SConfig::GetInstance().GetTitleDescription() : current_game;
std::string game_artwork = ArtworkForGameId(SConfig::GetInstance().GetGameID());
2018-05-27 04:24:13 +00:00
DiscordRichPresence discord_presence = {};
if (game_artwork.empty())
{
discord_presence.largeImageKey = "dolphin_logo";
discord_presence.largeImageText = "Dolphin is an emulator for the GameCube and the Wii.";
}
else
{
discord_presence.largeImageKey = game_artwork.c_str();
discord_presence.largeImageText = title.c_str();
discord_presence.smallImageKey = "dolphin_logo";
discord_presence.smallImageText = "Dolphin is an emulator for the GameCube and the Wii.";
}
2018-05-27 04:24:13 +00:00
discord_presence.details = title.empty() ? "Not in-game" : title.c_str();
discord_presence.startTimestamp = std::time(nullptr);
2018-07-03 21:50:08 +00:00
2018-08-06 21:56:40 +00:00
if (party_size > 0)
2018-07-03 21:50:08 +00:00
{
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;
2018-07-03 21:50:08 +00:00
std::string secret_final;
if (type != SecretType::Empty)
{
// Declearing party_id or secret_final here will deallocate the variable before passing the
2018-07-03 21:50:08 +00:00
// values over to Discord_UpdatePresence.
const size_t secret_length = secret.length();
party_id = std::to_string(
2018-07-03 21:50:08 +00:00
Common::HashAdler32(reinterpret_cast<const u8*>(secret.c_str()), secret_length));
const std::string secret_type = std::to_string(static_cast<int>(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();
2018-07-03 21:50:08 +00:00
discord_presence.joinSecret = secret_final.c_str();
2018-05-27 04:24:13 +00:00
Discord_UpdatePresence(&discord_presence);
#endif
}
std::string CreateSecretFromIPAddress(const std::string& ip_address, int port)
{
const std::string port_string = std::to_string(port);
std::string secret;
secret.reserve(ip_address.length() + 1 + port_string.length());
secret += ip_address;
secret += ':';
secret += port_string;
return secret;
}
2018-05-27 04:24:13 +00:00
void Shutdown()
{
#ifdef USE_DISCORD_PRESENCE
if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE))
return;
Discord_ClearPresence();
2018-05-27 04:24:13 +00:00
Discord_Shutdown();
#endif
}
void SetDiscordPresenceEnabled(bool enabled)
{
if (Config::Get(Config::MAIN_USE_DISCORD_PRESENCE) == enabled)
return;
if (Config::Get(Config::MAIN_USE_DISCORD_PRESENCE))
Discord::Shutdown();
Config::SetBase(Config::MAIN_USE_DISCORD_PRESENCE, enabled);
if (Config::Get(Config::MAIN_USE_DISCORD_PRESENCE))
Discord::Init();
}
2018-05-27 04:24:13 +00:00
} // namespace Discord