Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,827 changes: 1,827 additions & 0 deletions 3rdParty/discord_social_sdk/cdiscord.h

Large diffs are not rendered by default.

12,001 changes: 12,001 additions & 0 deletions 3rdParty/discord_social_sdk/discordpp.h

Large diffs are not rendered by default.

Binary file added 3rdPartyBinaries/discord_social_sdk_linux.zip
Binary file not shown.
Binary file added 3rdPartyBinaries/discord_social_sdk_mac.zip
Binary file not shown.
Binary file added 3rdPartyBinaries/discord_social_sdk_win.zip
Binary file not shown.
42 changes: 39 additions & 3 deletions SerialPrograms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

# Set root dir for convenience
set(PROJECT_ROOT_DIR "${CMAKE_SOURCE_DIR}/..")
get_filename_component(PROJECT_ROOT_DIR "${CMAKE_SOURCE_DIR}/.." ABSOLUTE)

add_custom_target(build-time-make-directory ALL
COMMAND ${CMAKE_COMMAND} -E make_directory Assembly/)

Expand Down Expand Up @@ -732,6 +736,8 @@ file(GLOB MAIN_SOURCES
Source/Integrations/DiscordWebhookSettings.h
Source/Integrations/DppIntegration/DppClient.cpp
Source/Integrations/DppIntegration/DppClient.h
Source/Integrations/DiscordSocial/DiscordSocial.cpp
Source/Integrations/DiscordSocial/DiscordSocial.h
Source/Integrations/DppIntegration/DppCommandHandler.cpp
Source/Integrations/DppIntegration/DppCommandHandler.h
Source/Integrations/DppIntegration/DppUtility.cpp
Expand Down Expand Up @@ -2450,12 +2456,17 @@ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../Internal/SerialPrograms/Internal0.
target_sources(SerialPrograms PRIVATE ../../Internal/SerialPrograms/Internal1.cpp)
endif()

#extract opencv_world4110d.dll from archive on Windows Debug builds
#extract opencv_world4110d.dll from archive on Windows Debug build, extract Discord Social SDK
if (WIN32)
file(ARCHIVE_EXTRACT
INPUT ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/opencv_world4110d.zip
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/
)

file(ARCHIVE_EXTRACT
INPUT ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/discord_social_sdk_win.zip
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/
)
endif()

#add include directory
Expand Down Expand Up @@ -2504,29 +2515,44 @@ if (WIN32)

add_library(dpp_lib IMPORTED UNKNOWN)
set_target_properties(dpp_lib PROPERTIES
IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dpp.lib
IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dppd.lib)
IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dpp.lib
IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dppd.lib)
set_target_properties(dpp_lib PROPERTIES
MAP_IMPORTED_CONFIG_DEBUG DEBUG
MAP_IMPORTED_CONFIG_RELEASE RELEASE
MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE
MAP_IMPORTED_CONFIG_MINSIZEREL RELEASE
)

add_library(discord_lib IMPORTED UNKNOWN)
target_include_directories(SerialPrograms SYSTEM PRIVATE ../3rdParty/discord_social_sdk_win/)
set_target_properties(discord_lib PROPERTIES
IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/discord_social_sdk_win/lib/release/discord_partner_sdk.lib
IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/discord_social_sdk_win/lib/debug/discord_partner_sdk.lib)
set_target_properties(discord_lib PROPERTIES
MAP_IMPORTED_CONFIG_DEBUG DEBUG
MAP_IMPORTED_CONFIG_RELEASE RELEASE
MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE
MAP_IMPORTED_CONFIG_MINSIZEREL RELEASE
)

target_link_libraries(
SerialPrograms PRIVATE
tesseractPA.lib
OpenCV_lib
ONNX_lib
ONNX_Providers_lib
dpp_lib
discord_lib
)

target_compile_definitions(
SerialPrograms PRIVATE
_CRT_SECURE_NO_WARNINGS
_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
PA_TESSERACT
PA_DPP
PA_SOCIAL_SDK
)

target_compile_options(SerialPrograms PRIVATE /FAs /FaAssembly/ /MP /W4 /WX /external:anglebrackets /external:W0 /utf-8)
Expand Down Expand Up @@ -2771,6 +2797,16 @@ file(GLOB MY_DLLS
"../3rdPartyBinaries/*.dll"
)
file(COPY ${MY_DLLS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

# Copy discord_social_sdk DLL based on build type
add_custom_command(
TARGET SerialPrograms
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<IF:$<CONFIG:Debug>,"${PROJECT_ROOT_DIR}/3rdPartyBinaries/discord_social_sdk_win/bin/debug/discord_partner_sdk.dll","${PROJECT_ROOT_DIR}/3rdPartyBinaries/discord_social_sdk_win/bin/release/discord_partner_sdk.dll">
$<TARGET_FILE_DIR:SerialPrograms>/Binaries64/discord_partner_sdk.dll
COMMENT "Copying Discord SDK DLL to Binaries64"
)
endif()

if (QT_MAJOR GREATER_EQUAL 6)
Expand Down
6 changes: 6 additions & 0 deletions SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ GlobalSettings::GlobalSettings()
"online documentation"
) + ")</font>"
)
, RICH_PRESENCE(
"<b>Enable Rich Presence:</b><br>Will display program activity and status under your Discord user.",
LockMode::UNLOCK_WHILE_RUNNING,
true
)
, ALL_STATS(
"<b>All Stats:</b><br>Include all-time stats for notifications.",
LockMode::UNLOCK_WHILE_RUNNING,
Expand Down Expand Up @@ -223,6 +228,7 @@ GlobalSettings::GlobalSettings()
#endif

PA_ADD_STATIC(m_discord_settings);
PA_ADD_OPTION(RICH_PRESENCE);
PA_ADD_OPTION(ALL_STATS);
PA_ADD_OPTION(DISCORD);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class GlobalSettings : public BatchOption, private ConfigOption::Listener{
Pimpl<SleepSuppressOptions> SLEEP_SUPPRESS;

SectionDividerOption m_discord_settings;
BooleanCheckBoxOption RICH_PRESENCE;
BooleanCheckBoxOption ALL_STATS;
Pimpl<Integration::DiscordSettingsOption> DISCORD;

Expand Down
3 changes: 3 additions & 0 deletions SerialPrograms/Source/CommonFramework/Globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ const std::string DISCORD_LINK_URL_PROGRAM = "https://discord.gg/BSjDp27";
// URL to use in the Discord notifications/embeds.
const std::string DISCORD_LINK_URL_EMBED = "https://discord.gg/xMJcveK";

// URL to use in the DiscordSocialSDK integration.
const std::string DISCORD_LINK_URL_SDK = "https://discord.gg/gn9YEyjjAV";



#if 0
Expand Down
1 change: 1 addition & 0 deletions SerialPrograms/Source/CommonFramework/Globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ extern const std::string GITHUB_LINK_URL;
extern const std::string DISCORD_LINK_TEXT;
extern const std::string DISCORD_LINK_URL_PROGRAM;
extern const std::string DISCORD_LINK_URL_EMBED;
extern const std::string DISCORD_LINK_URL_SDK;

extern const std::string COMPILER_VERSION;

Expand Down
7 changes: 7 additions & 0 deletions SerialPrograms/Source/CommonFramework/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//#include <QTextStream>
#include <QMessageBox>
#include <Integrations/DppIntegration/DppClient.h>
#include <Integrations/DiscordSocial/DiscordSocial.h>
#include "Common/Cpp/Exceptions.h"
#include "Common/Cpp/ImageResolution.h"
#include "PersistentSettings.h"
Expand Down Expand Up @@ -115,6 +116,12 @@ int main(int argc, char *argv[]){
discord_settings.on_config_value_changed(nullptr);
}

if (GlobalSettings::instance().RICH_PRESENCE){
#ifdef PA_SOCIAL_SDK
Integration::DiscordSocialSDK::DiscordSocial::instance().run();
#endif
}

set_working_directory();

// Run this asynchronously to we don't block startup.
Expand Down
177 changes: 177 additions & 0 deletions SerialPrograms/Source/Integrations/DiscordSocial/DiscordSocial.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#ifdef PA_SOCIAL_SDK
#define DISCORDPP_IMPLEMENTATION

#include <thread>
#include "DiscordSocial.h"
#include "CommonFramework/Logging/Logger.h"
#include "Integrations/ProgramTracker.h"
#include "../../CommonFramework/GlobalSettingsPanel.h"
#include "../../CommonFramework/Globals.h"

using namespace discordpp;
namespace PokemonAutomation{
namespace Integration{
namespace DiscordSocialSDK{

DiscordSocial& DiscordSocial::instance(){
static DiscordSocial instance;
return instance;
}

void DiscordSocial::run(){
auto client = std::make_shared<Client>();
if (!client){
log("Failed to initialize DiscordSocialSDK.", "run()", LoggingSeverity::Error);
return;
}

m_client = std::move(client);
try{
m_client->SetApplicationId(m_app_id);
m_client->AddLogCallback([&](auto message, auto severity){
log(message, "Internal", severity);
}, m_log_level);

m_thread = std::thread(&DiscordSocial::thread_loop, this);
}catch (...){
m_client.reset();
log("Failed to start DiscordSocialSDK.", "run()", LoggingSeverity::Error);
throw;
}
}

void DiscordSocial::thread_loop(){
log("Starting Discord Rich Presence update thread...", "thread_loop()", LoggingSeverity::Info);
m_running.store(true, std::memory_order_release);
m_activity = create_activity();
m_client->UpdateRichPresence(m_activity, [&](ClientResult result){
if (!result.Successful()){
log("Rich Presence initial update failed.", "thread_loop()", LoggingSeverity::Warning);
}
});

while (m_running.load(std::memory_order_relaxed)){
try{
if (!m_running.load(std::memory_order_relaxed) || !GlobalSettings::instance().RICH_PRESENCE){
break;
}

RunCallbacks();
update_rich_presence();
std::this_thread::sleep_for(std::chrono::seconds(1));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do this every second? Is it possible to diff to see if anything changed. And only update if something changed?

Ideally CC itself should push these changes out, but now is not the time to re-architect that part so this is fine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DiscordSDK requires some sort of a main loop that calls RunCallbacks() regularly for internal logging and other set callbacks. 1 second with logging only set to errors won't spam, but a loop running on status change (which is possible) would be more invasive (likely require OAuth2 or additional scopes: currently there are none).

If we want CC to dictate rich presence update, that would require a direct program state reference (or something thereof). Not sure how feasible when initialized from Main.cpp.

}catch (const std::exception& e){
log("Exception: " + std::string(e.what()), "thread_loop()", LoggingSeverity::Error);
break;
}
}

m_running.store(false, std::memory_order_release);
m_client.reset();
log("Discord Rich Presence update thread exiting...", "thread_loop()", LoggingSeverity::Info);
}

void DiscordSocial::update_rich_presence(){
try{
std::string details = m_activity.Details().value();
std::string state = m_activity.State().value();

discordpp::ActivityAssets assets{};
assets.SetLargeImage("rotom");
assets.SetLargeText(PROGRAM_NAME);

for (const auto& item : ProgramTracker::instance().all_programs()){
switch (item.second.state){
case ProgramState::RUNNING:{
assets.SetSmallImage("running");
assets.SetSmallText("Running");
state = "Running";
m_activity.SetAssets(assets); break;
}
case ProgramState::STOPPED:{
assets.SetSmallImage("stopped");
assets.SetSmallText("Stopped");
state = "Stopped";
m_activity.SetAssets(assets); break;
}
default:{
assets.SetSmallImage("idle");
assets.SetSmallText("Idle");
state = "Idle";
m_activity.SetAssets(assets); break;
}
}

if (item.second.program_name != details){
details = item.second.program_name;
}

m_timestamps.SetStart(std::chrono::duration_cast<std::chrono::seconds>(item.second.start_time.time_since_epoch()).count());
}

m_activity.SetTimestamps(m_timestamps);
m_activity.SetDetails(details);
m_activity.SetState(state);
m_client->UpdateRichPresence(m_activity, [&](ClientResult result){
if (!result.Successful()){
log("Rich Presence update failed.", "update_rp()", LoggingSeverity::Warning);
}
});
}catch (const std::exception& e){
m_running.store(false, std::memory_order_release);
log("Exception: " + std::string(e.what()), "update_rp()", LoggingSeverity::Error);
}
}

Logger& DiscordSocial::logger(){
static TaggedLogger logger(global_logger_raw(), "DiscordSocialSDK");
return logger;
}

void DiscordSocial::log(const std::string& message, const std::string& identity, const LoggingSeverity& severity){
if (severity < m_log_level){
return;
}

std::string log = identity + ": " + message;
Color color;
switch (severity){
case LoggingSeverity::Verbose: color = COLOR_GRAY; break;
case LoggingSeverity::Info: color = COLOR_CYAN; break;
case LoggingSeverity::Warning: color = COLOR_ORANGE; break;
case LoggingSeverity::Error: color = COLOR_MAGENTA; break;
default: color = COLOR_PURPLE; break;
};

logger().log(log, color);
}

Activity DiscordSocial::create_activity(){
Activity activity{};
activity.SetType(ActivityTypes::Playing);
activity.SetDetails("ComputerControl: Preparing to automate...");
activity.SetState("Idle");

ActivityButton button1{};
button1.SetLabel("Download from GitHub");
button1.SetUrl(m_github_release_latest);
activity.AddButton(button1);

ActivityButton button2{};
button2.SetLabel("Join our Discord");
button2.SetUrl(DISCORD_LINK_URL_SDK);
activity.AddButton(button2);

discordpp::ActivityAssets assets{};
assets.SetLargeImage("rotom");
assets.SetLargeText(PROGRAM_NAME);
assets.SetSmallImage("idle");
assets.SetSmallText("Idle");
activity.Assets().emplace(assets);
return activity;
}


}
}
}
#endif
45 changes: 45 additions & 0 deletions SerialPrograms/Source/Integrations/DiscordSocial/DiscordSocial.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once
#ifndef DISCORD_SOCIAL_H
#define DISCORD_SOCIAL_H

#include <thread>
#include <atomic>
#include <discord_social_sdk/discordpp.h>
#include "Common/Cpp/AbstractLogger.h"

namespace PokemonAutomation{
namespace Integration{
namespace DiscordSocialSDK{
class DiscordSocial{
public:
DiscordSocial() : m_running(false) {}
~DiscordSocial(){
m_running.store(false, std::memory_order_release);
m_thread.join();
if (m_client) m_client.reset();
}

static DiscordSocial& instance();
void run();

private:
Logger& logger();
void log(const std::string& message, const std::string& identity, const discordpp::LoggingSeverity& severity);
discordpp::Activity create_activity();
void update_rich_presence();
void thread_loop();

private:
std::thread m_thread;
std::shared_ptr<discordpp::Client> m_client = nullptr;
std::atomic<bool> m_running;
const uint64_t m_app_id = 1406867596585865326;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentionally hard-coded? Where does it come from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_app_id is intentionally hardcoded. It's the private Discord application the SDK gets art assets from.

discordpp::Activity m_activity;
discordpp::ActivityTimestamps m_timestamps{};
discordpp::LoggingSeverity m_log_level = discordpp::LoggingSeverity::Error;
const std::string m_github_release_latest = "https://github.com/PokemonAutomation/ComputerControl/releases/latest";
};
}
}
}
#endif