From dccf087d652ecf98d24927a397c38cc0c9db3ab4 Mon Sep 17 00:00:00 2001 From: Erik Kundiman Date: Sun, 27 Jul 2025 14:26:47 +0800 Subject: Move Discord-related code to llappviewer.cpp The doFrame is the one called over and over again, so running the Discord callbacks from there shouldn't have one extra function overhead, while running the Discord initialisation is only once so it's much more okay to have the extra function overhead there. --- indra/newview/llappviewer.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 419d2cb842..d70287ac83 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -278,6 +278,12 @@ using namespace LL; #pragma warning (disable:4702) #endif +#ifdef LL_DISCORD +#define DISCORDPP_IMPLEMENTATION +#include +static std::shared_ptr gDiscordClient; +#endif + static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); ////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor @@ -1349,7 +1355,7 @@ bool LLAppViewer::doFrame() TimePoint fpsLimitFrameStartTime = std::chrono::steady_clock::now(); #ifdef LL_DISCORD - LLStartUp::runDiscordCallbacks(); + discordpp::RunCallbacks(); #endif LL_RECORD_BLOCK_TIME(FTM_FRAME); @@ -5911,3 +5917,39 @@ void LLAppViewer::metricsSend(bool enable_reporting) // resolution in time. gViewerAssetStats->restart(); } + +#ifdef LL_DISCORD + +void LLAppViewer::initDiscordSocial() +{ + gDiscordClient = std::make_shared(); + gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) { + if (status == discordpp::Client::Status::Ready) { + discordpp::Activity activity; + activity.SetType(discordpp::ActivityTypes::Playing); + gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); + } + }); +} + +void LLAppViewer::handleDiscordSocial() +{ + static const uint64_t DISCORD_APPLICATION_ID = 1393451183741599796; + discordpp::AuthorizationArgs discordAuthArgs{}; + discordAuthArgs.SetClientId(DISCORD_APPLICATION_ID); + discordAuthArgs.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + auto discordCodeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier(); + discordAuthArgs.SetCodeChallenge(discordCodeVerifier.Challenge()); + gDiscordClient->Authorize(discordAuthArgs, [discordCodeVerifier](auto result, auto code, auto redirectUri) { + if (result.Successful()) { + gDiscordClient->GetToken(DISCORD_APPLICATION_ID, code, discordCodeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) { + gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [](discordpp::ClientResult result) { + if (result.Successful()) + gDiscordClient->Connect(); + }); + }); + } + }); +} + +#endif -- cgit v1.2.3 From 01dfd0f960b1cd500fbbe9b544a7c22cd0ab548a Mon Sep 17 00:00:00 2001 From: Erik Kundiman Date: Sun, 27 Jul 2025 19:32:42 +0800 Subject: Shorten Discord-related local variable names and minimise difference from SL main. --- indra/newview/llappviewer.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index d70287ac83..17f8c3e367 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5934,15 +5934,15 @@ void LLAppViewer::initDiscordSocial() void LLAppViewer::handleDiscordSocial() { - static const uint64_t DISCORD_APPLICATION_ID = 1393451183741599796; - discordpp::AuthorizationArgs discordAuthArgs{}; - discordAuthArgs.SetClientId(DISCORD_APPLICATION_ID); - discordAuthArgs.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); - auto discordCodeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier(); - discordAuthArgs.SetCodeChallenge(discordCodeVerifier.Challenge()); - gDiscordClient->Authorize(discordAuthArgs, [discordCodeVerifier](auto result, auto code, auto redirectUri) { + static const uint64_t APPLICATION_ID = 1393451183741599796; + discordpp::AuthorizationArgs args{}; + args.SetClientId(APPLICATION_ID); + args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + auto codeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier(); + args.SetCodeChallenge(codeVerifier.Challenge()); + gDiscordClient->Authorize(args, [codeVerifier](auto result, auto code, auto redirectUri) { if (result.Successful()) { - gDiscordClient->GetToken(DISCORD_APPLICATION_ID, code, discordCodeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) { + gDiscordClient->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) { gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [](discordpp::ClientResult result) { if (result.Successful()) gDiscordClient->Connect(); -- cgit v1.2.3 From ec86ee28171428824cde4afe88d1165e80633233 Mon Sep 17 00:00:00 2001 From: Erik Kundiman Date: Sun, 27 Jul 2025 22:38:14 +0800 Subject: Connect to Discord now through privacy tab Now the access token is saved the way passwords are saved, but without a username, so we can have some persistence without having to implement an OAuth2 backend server cause we would have to store those tokens there anyway still and that would even require more disclosure that the user token gets saved on a server, and it's just simpler to not go that way. Discord Social SDK doesn't have to have a helper for sending code to a custom server anyway, that we would have to have some asynchronous HTTP requestor ready. Show location check button gets enabled only when Discord integration is enabled, though it's not functioning yet. --- indra/newview/llappviewer.cpp | 72 ++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 22 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 17f8c3e367..030c778990 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5922,34 +5922,62 @@ void LLAppViewer::metricsSend(bool enable_reporting) void LLAppViewer::initDiscordSocial() { - gDiscordClient = std::make_shared(); - gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) { - if (status == discordpp::Client::Status::Ready) { - discordpp::Activity activity; - activity.SetType(discordpp::ActivityTypes::Playing); - gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); - } + gDiscordClient = std::make_shared(); + gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) { + if (status == discordpp::Client::Status::Ready) + { + discordpp::Activity activity; + activity.SetType(discordpp::ActivityTypes::Playing); + gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); + } + }); + if (gSavedSettings.getBOOL("EnableDiscord")) + { + gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, gSecAPIHandler->loadCredential("Discord")->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) { + if (result.Successful()) + gDiscordClient->Connect(); }); + } } -void LLAppViewer::handleDiscordSocial() +void LLAppViewer::handleDiscordSocial(bool enable) { static const uint64_t APPLICATION_ID = 1393451183741599796; - discordpp::AuthorizationArgs args{}; - args.SetClientId(APPLICATION_ID); - args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); - auto codeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier(); - args.SetCodeChallenge(codeVerifier.Challenge()); - gDiscordClient->Authorize(args, [codeVerifier](auto result, auto code, auto redirectUri) { - if (result.Successful()) { - gDiscordClient->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) { - gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [](discordpp::ClientResult result) { - if (result.Successful()) - gDiscordClient->Connect(); + if (enable) + { + discordpp::AuthorizationArgs args{}; + args.SetClientId(APPLICATION_ID); + args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + auto codeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier(); + args.SetCodeChallenge(codeVerifier.Challenge()); + gDiscordClient->Authorize(args, [codeVerifier](auto result, auto code, auto redirectUri) { + if (result.Successful()) + { + gDiscordClient->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) { + gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [](discordpp::ClientResult result) { + if (result.Successful()) + gDiscordClient->Connect(); }); - }); - } - }); + LLSD authenticator = LLSD::emptyMap(); + authenticator["token"] = accessToken; + gSecAPIHandler->saveCredential(gSecAPIHandler->createCredential("Discord", LLSD::emptyMap(), authenticator), true); + }); + } + else + { + gSavedSettings.setBOOL("EnableDiscord", false); + } + }); + } + else + { + gDiscordClient->RevokeToken(APPLICATION_ID, gSecAPIHandler->loadCredential("Discord")->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) { + if (result.Successful()) + gDiscordClient->Disconnect(); + auto cred = new LLCredential("Discord"); + gSecAPIHandler->deleteCredential(cred); + }); + } } #endif -- cgit v1.2.3 From 306eb28d78c9aa83669307dc49daacb94f8c8742 Mon Sep 17 00:00:00 2001 From: Erik Kundiman Date: Mon, 28 Jul 2025 15:13:36 +0800 Subject: Location for Discord Rich Presence Activity State I was going to use LLAgentUI::buildLocationString but there's no location format that shows only region and coords without having to have the parcel name empty, so I copied buildLocationString implementation in the case of LOCATION_FORMAT_NO_MATURITY but when the parcel name is empty. I had to make updateDiscordActivity check agent's ID and the existence of agent avatar pointer first before trying to set Activity Details or State, cause I like the "Show location" button be checkable not only after online when both the ID & pointer will have existed. I think this way is simpler than programmatically enabling the "Show location" button after the user is logged in. I put a trigger to Activity update somewhere after the user is logged in for now, not yet after a TP. The elapsed time gets reset whenever Activity is updated for now, but I'll try to make elapsed time extended instead. No Party for now, because I couldn't find a way to make a Party shown without showing its CurrentSize (I could still get away not showing its MaxSize by setting it to 0), so the State (location) is shown above the elapsed time, not on the right of it. I'll try to figure out to get some representative numbers for its CurrentSize & MaxSize next. Also no privacy on hiding the username for now, until the UI is ready. --- indra/newview/llappviewer.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 030c778990..3d8dfd39e6 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5980,4 +5980,36 @@ void LLAppViewer::handleDiscordSocial(bool enable) } } +void LLAppViewer::updateDiscordActivity() +{ + discordpp::Activity activity; + activity.SetType(discordpp::ActivityTypes::Playing); + if (gAgentAvatarp) + activity.SetDetails(gAgentAvatarp->getFullname()); + if (gAgent.getID() != LLUUID::null && gSavedSettings.getBOOL("ShowDiscordActivityState")) + { + auto agent_pos_region = gAgent.getPositionAgent(); + S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f); + S32 pos_y = S32(agent_pos_region.mV[VY] + 0.5f); + S32 pos_z = S32(agent_pos_region.mV[VZ] + 0.5f); + F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared(); + const F32 FLY_CUTOFF = 6.f; + const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; + const F32 WALK_CUTOFF = 1.5f; + const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; + if (velocity_mag_sq > FLY_CUTOFF_SQ) + { + pos_x -= pos_x % 4; + pos_y -= pos_y % 4; + } + else if (velocity_mag_sq > WALK_CUTOFF_SQ) + { + pos_x -= pos_x % 2; + pos_y -= pos_y % 2; + } + activity.SetState(llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z)); + } + gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); +} + #endif -- cgit v1.2.3 From 94f51d5a4b16d76e46e53802cc188da8c9eb795b Mon Sep 17 00:00:00 2001 From: Erik Kundiman Date: Mon, 28 Jul 2025 16:33:28 +0800 Subject: Show display name too on Discord Rich Presence Avatar name cache can be used right away upon login now after I moved the update call to the end of PRECACHE section in llstartup. --- indra/newview/llappviewer.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 3d8dfd39e6..9124651e84 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5982,11 +5982,21 @@ void LLAppViewer::handleDiscordSocial(bool enable) void LLAppViewer::updateDiscordActivity() { + if (gAgent.getID() == LLUUID::null) + return; + discordpp::Activity activity; activity.SetType(discordpp::ActivityTypes::Playing); - if (gAgentAvatarp) - activity.SetDetails(gAgentAvatarp->getFullname()); - if (gAgent.getID() != LLUUID::null && gSavedSettings.getBOOL("ShowDiscordActivityState")) + + LLAvatarName av_name; + LLAvatarNameCache::get(gAgent.getID(), &av_name); + auto name = av_name.getUserName(); + auto displayName = av_name.getDisplayName(); + if (name != displayName) + name = displayName + " (" + name + ")"; + activity.SetDetails(name); + + if (gSavedSettings.getBOOL("ShowDiscordActivityState")) { auto agent_pos_region = gAgent.getPositionAgent(); S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f); @@ -6009,6 +6019,7 @@ void LLAppViewer::updateDiscordActivity() } activity.SetState(llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z)); } + gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); } -- cgit v1.2.3 From 705cd43ae88b5d095e7953ef8ddf670f3bbc9e1a Mon Sep 17 00:00:00 2001 From: Erik Kundiman Date: Mon, 28 Jul 2025 17:01:34 +0800 Subject: Show Discord Rich Presence Activity Party By setting CurrentSize to the number of people within chat radius, and MaxSize to the number of people within (MP's) near range. --- indra/newview/llappviewer.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9124651e84..bc97e5b852 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -6017,7 +6017,19 @@ void LLAppViewer::updateDiscordActivity() pos_x -= pos_x % 2; pos_y -= pos_y % 2; } - activity.SetState(llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z)); + auto location = llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z); + activity.SetState(location); + + auto world = LLWorld::getInstance(); + uuid_vec_t chat_radius_uuids, near_me_uuids; + auto position = gAgent.getPositionGlobal(); + world->getAvatars(&chat_radius_uuids, NULL, position, CHAT_NORMAL_RADIUS); + world->getAvatars(&near_me_uuids, NULL, position, gSavedSettings.getF32("MPVNearMeRange")); + discordpp::ActivityParty party; + party.SetId(location); + party.SetCurrentSize(chat_radius_uuids.size()); + party.SetMaxSize(near_me_uuids.size()); + activity.SetParty(party); } gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); -- cgit v1.2.3 From dbb0336d4a4739759371f52ab305fa2bd9237f3b Mon Sep 17 00:00:00 2001 From: Erik Kundiman Date: Mon, 28 Jul 2025 17:49:15 +0800 Subject: Call updateDiscordActivity too in Discord init so when the user enables the integration after being logged in, the init can show the name and location right away. --- indra/newview/llappviewer.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index bc97e5b852..caf56d5c79 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5926,9 +5926,7 @@ void LLAppViewer::initDiscordSocial() gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) { if (status == discordpp::Client::Status::Ready) { - discordpp::Activity activity; - activity.SetType(discordpp::ActivityTypes::Playing); - gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); + updateDiscordActivity(); } }); if (gSavedSettings.getBOOL("EnableDiscord")) -- cgit v1.2.3 From c1adf320af2ca2afd9ed05cccc9031b99fc7ee60 Mon Sep 17 00:00:00 2001 From: Secret Foxtail Date: Mon, 28 Jul 2025 05:23:18 -0600 Subject: Discord Rich Presence: Hide name & connect to llappviewer.cpp Add option to show/hide avatar name in privacy panel & connect rich presense directly to llappviewer.cpp --- indra/newview/llappviewer.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index caf56d5c79..2ea8b65ee1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5938,10 +5938,10 @@ void LLAppViewer::initDiscordSocial() } } -void LLAppViewer::handleDiscordSocial(bool enable) +void LLAppViewer::handleDiscordSocial(const LLSD& value) { static const uint64_t APPLICATION_ID = 1393451183741599796; - if (enable) + if (value.asBoolean()) { discordpp::AuthorizationArgs args{}; args.SetClientId(APPLICATION_ID); @@ -5986,13 +5986,16 @@ void LLAppViewer::updateDiscordActivity() discordpp::Activity activity; activity.SetType(discordpp::ActivityTypes::Playing); - LLAvatarName av_name; - LLAvatarNameCache::get(gAgent.getID(), &av_name); - auto name = av_name.getUserName(); - auto displayName = av_name.getDisplayName(); - if (name != displayName) - name = displayName + " (" + name + ")"; - activity.SetDetails(name); + if (gSavedSettings.getBOOL("ShowDiscordActivityDetails")) + { + LLAvatarName av_name; + LLAvatarNameCache::get(gAgent.getID(), &av_name); + auto name = av_name.getUserName(); + auto displayName = av_name.getDisplayName(); + if (name != displayName) + name = displayName + " (" + name + ")"; + activity.SetDetails(name); + } if (gSavedSettings.getBOOL("ShowDiscordActivityState")) { -- cgit v1.2.3 From b7d33485406150bf466f61477cfd33d9bdacb705 Mon Sep 17 00:00:00 2001 From: Erik Kundiman Date: Mon, 28 Jul 2025 20:36:54 +0800 Subject: Discord time elapsed not reset on region change Time elapses right after viewer launch even before login. Plus parameter name change in header to make it the same as in implementation. --- indra/newview/llappviewer.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 2ea8b65ee1..b5175fbf71 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -282,6 +282,7 @@ using namespace LL; #define DISCORDPP_IMPLEMENTATION #include static std::shared_ptr gDiscordClient; +static uint64_t gDiscordTimestampsStart; #endif static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); @@ -5922,6 +5923,7 @@ void LLAppViewer::metricsSend(bool enable_reporting) void LLAppViewer::initDiscordSocial() { + gDiscordTimestampsStart = time(nullptr); gDiscordClient = std::make_shared(); gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) { if (status == discordpp::Client::Status::Ready) @@ -5980,11 +5982,17 @@ void LLAppViewer::handleDiscordSocial(const LLSD& value) void LLAppViewer::updateDiscordActivity() { - if (gAgent.getID() == LLUUID::null) - return; - discordpp::Activity activity; activity.SetType(discordpp::ActivityTypes::Playing); + discordpp::ActivityTimestamps timestamps; + timestamps.SetStart(gDiscordTimestampsStart); + activity.SetTimestamps(timestamps); + + if (gAgent.getID() == LLUUID::null) + { + gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); + return; + } if (gSavedSettings.getBOOL("ShowDiscordActivityDetails")) { -- cgit v1.2.3