From 1c8be4e323b2e1b049b47a1aaeeb7cc15f782fb6 Mon Sep 17 00:00:00 2001 From: Liny Date: Tue, 10 Sep 2019 04:24:02 -0700 Subject: [PATCH] Discord rich presence support --- autobuild.xml | 52 +++ indra/cmake/CMakeLists.txt | 1 + indra/cmake/Discord.cmake | 18 + indra/newview/CMakeLists.txt | 7 + indra/newview/app_settings/commands.xml | 10 + .../app_settings/settings_per_account.xml | 33 ++ indra/newview/fsdiscordconnect.cpp | 332 ++++++++++++++++++ indra/newview/fsdiscordconnect.h | 98 ++++++ indra/newview/fsfloaterdiscord.cpp | 323 +++++++++++++++++ indra/newview/fsfloaterdiscord.h | 78 ++++ indra/newview/llviewerfloaterreg.cpp | 2 + indra/newview/llvoavatar.cpp | 4 + .../skins/default/textures/textures.xml | 2 + .../textures/toolbar_icons/discord.png | Bin 0 -> 501 bytes .../default/xui/en/floater_fs_discord.xml | 189 ++++++++++ .../skins/default/xui/en/menu_viewer.xml | 7 + .../newview/skins/default/xui/en/strings.xml | 8 + 17 files changed, 1164 insertions(+) create mode 100644 indra/cmake/Discord.cmake create mode 100644 indra/newview/fsdiscordconnect.cpp create mode 100644 indra/newview/fsdiscordconnect.h create mode 100644 indra/newview/fsfloaterdiscord.cpp create mode 100644 indra/newview/fsfloaterdiscord.h create mode 100644 indra/newview/skins/default/textures/toolbar_icons/discord.png create mode 100644 indra/newview/skins/default/xui/en/floater_fs_discord.xml diff --git a/autobuild.xml b/autobuild.xml index e00c65c215..fa5d034546 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -237,6 +237,58 @@ + discord-rpc + + copyright + Copyright 2019 Discord, Inc. + description + C++ library to interface with Discord's API + license + MIT + license_file + LICENSES/discord-rpc.txt + name + discord-rpc + platforms + + windows + + archive + + hash + b760b4f3ab7794a897cf7f464d92e587 + url + http://downloads.phoenixviewer.com/discord_rpc-3.4.0-windows-192510505.tar.bz2 + + name + windows + + linux64 + + archive + + hash + 756c1dd3dddf218352b83b54db400056 + url + http://downloads.phoenixviewer.com/discord_rpc-3.4.0-darwin64-192522358.tar.bz2 + + name + linux64 + + darwin64 + + archive + + hash + 3bc297a0fa47094bb52d361f80186387 + url + http://downloads.phoenixviewer.com/discord_rpc-3.4.0-linux64-192531056.tar.bz2 + + name + darwin64 + + + SDL copyright diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 2939f6b90b..7065aa044c 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -22,6 +22,7 @@ set(cmake_SOURCE_FILES Copy3rdPartyLibs.cmake DBusGlib.cmake DeploySharedLibs.cmake + Discord.cmake # Discord rich presence DirectX.cmake DragDrop.cmake EXPAT.cmake diff --git a/indra/cmake/Discord.cmake b/indra/cmake/Discord.cmake new file mode 100644 index 0000000000..c535da0713 --- /dev/null +++ b/indra/cmake/Discord.cmake @@ -0,0 +1,18 @@ +# -*- cmake -*- + + + + +include(Prebuilt) +use_prebuilt_binary(discord-rpc) +set(DISCORD_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/discord-rpc) +if (ADDRESS_SIZE EQUAL 32 AND WINDOWS) + set(DISCORD_LIBRARY discord-rpc) +elseif (WINDOWS) + set(DISCORD_LIBRARY discord-rpc_x64) +elseif (LINUX || DARWIN) + set(DISCORD_LIBRARY libdiscord-rpc) +endif (ADDRESS_SIZE EQUAL 32 AND WINDOWS) +if (DISCORD_API_KEY) + add_definitions( -DDISCORD_API_KEY=\"${DISCORD_API_KEY}\") +endif (DISCORD_API_KEY) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 64889e0c38..c6ecca3e00 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -60,6 +60,7 @@ include(URIPARSER) include(Growl) include(ColladaDom) include(jemalloc) +include(Discord) # if using ndPhysicsstub this variable will be unset, we don't need to build any stub code viewer side in that case if( LLPHYSICSEXTENSIONS_SRC_DIR ) @@ -1792,6 +1793,11 @@ if (WINDOWS) # [FS] ) +# Discord rich presence + LIST(APPEND viewer_HEADER_FILES fsfloaterdiscord.h fsdiscordconnect.h) + LIST(APPEND viewer_SOURCE_FILES fsfloaterdiscord.cpp fsdiscordconnect.cpp) +# + # precompiled header configuration # llviewerprecompiledheaders.cpp generates # the .pch file. @@ -2432,6 +2438,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLPHYSICSEXTENSIONS_LIBRARIES} ${LLAPPEARANCE_LIBRARIES} ${GROWL_LIBRARY} + ${DISCORD_LIBRARY} ) if (BUGSPLAT_DB) diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 1c8a4fe389..0dc159b8e7 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -633,4 +633,14 @@ execute_function="Tools.StopAllAnimations" execute_parameters="stop" /> + diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index ebc6d1d2ac..738920cb8b 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -1224,5 +1224,38 @@ Value 1 + FSEnableDiscordIntegration + + Comment + If enabled, well allow Firestorm to connect to discord if open and share location data with. Default (false) + Persist + 1 + Type + Boolean + Value + 0 + + FSMaxSharedMaturity + + Comment + Max maturity rating to share with Discord + Persist + 1 + Type + U32 + Value + 13 + + FSBlacklistedRegionNames + + Comment + List of region names to never share with Discord + Persist + 1 + Type + LLSD + Value + + diff --git a/indra/newview/fsdiscordconnect.cpp b/indra/newview/fsdiscordconnect.cpp new file mode 100644 index 0000000000..a5e507c43c --- /dev/null +++ b/indra/newview/fsdiscordconnect.cpp @@ -0,0 +1,332 @@ +/** + * @file fsdiscordconnect.cpp + * @brief Connection to Discord + * @author liny@pinkfox.xyz + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2019 Liny Odell @ Second Life + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + */ + +#include "llviewerprecompiledheaders.h" + +#include "fsdiscordconnect.h" + +#include "llagent.h" +#include "llcallingcard.h" // for LLAvatarTracker +#include "llcommandhandler.h" +#include "llnotificationsutil.h" +#include "lltrans.h" +#include "llevents.h" +#include "llviewerregion.h" +#include "llavatarnamecache.h" +#include "llregioninfomodel.h" + +#include "rlvactions.h" +#include "rlvhandler.h" + +#include "llfloaterreg.h" + +#include "discord-rpc\discord_rpc.h" + +#include "boost/algorithm/string/case_conv.hpp" + +#ifndef DISCORD_API_KEY +#define DISCORD_API_KEY "" +#endif + +boost::scoped_ptr FSDiscordConnect::sStateWatcher(new LLEventStream("DiscordConnectState")); +boost::scoped_ptr FSDiscordConnect::sInfoWatcher(new LLEventStream("DiscordConnectInfo")); + + +// Returns false when the file exists and has not our UUID +// Or, put simply, returns true if someone else is using it +bool FSDiscordConnect::checkMarkerFile() +{ + if (!LLFile::isfile(mMarkerFilename)) + { + return false; + } + LLUUID fileID; + llifstream file(mMarkerFilename); + file >> fileID; + if ((fileID == gAgentID) || fileID.isNull()) + { + return false; + } + return true; +} + +void FSDiscordConnect::setMarkerFile() +{ + //LLFile file = LLFile::fopen(mMarkerFilename, "w"); + if (!checkMarkerFile()) + { + return; // dont over-write another instances file + } + llofstream file = llofstream(mMarkerFilename.c_str()); + file << gAgentID << std::endl; + file.close(); +} + +void FSDiscordConnect::clearMarkerFile() +{ + if (!checkMarkerFile()) + { + return; // dont remove another instances file + } + LLFile::remove(mMarkerFilename); +} + +void handleDiscordReady(const DiscordUser *request) +{ + LLSD info; + info["name"] = request->username; + FSDiscordConnect::getInstance()->storeInfo(info); + FSDiscordConnect::getInstance()->setConnectionState(FSDiscordConnect::DISCORD_CONNECTED); +} + +void handleDiscordError(int errorCode, const char* message) +{ + LL_WARNS("DiscordConnect") << "Discord error, errorCode: \"" << errorCode << "\", message: \"" << message << "\"" << LL_ENDL; +} + +void handleDiscordDisconnected(int errorCode, const char* message) +{ + LL_INFOS("DiscordConnect") << "Discord disconnected, errorCode: \"" << errorCode << "\", message: \"" << message << "\"" << LL_ENDL; + FSDiscordConnect::getInstance()->setConnectionState(FSDiscordConnect::DISCORD_NOT_CONNECTED); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void FSDiscordConnect::discordConnectCoro() +{ + DiscordEventHandlers handlers; + memset(&handlers, 0, sizeof(handlers)); + handlers.ready = handleDiscordReady; + handlers.errored = handleDiscordError; + handlers.disconnected = handleDiscordDisconnected; + //TODO: Get this working and sending TPs + /*handlers.joinGame = handleDiscordJoinGame; + handlers.spectateGame = handleDiscordSpectateGame; + handlers.joinRequest = handleDiscordJoinRequest;*/ + + Discord_Initialize(DISCORD_API_KEY, &handlers, 1, ""); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void FSDiscordConnect::discordDisconnectCoro() +{ + Discord_Shutdown(); + setConnectionState(FSDiscordConnect::DISCORD_NOT_CONNECTED); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void FSDiscordConnect::discordConnectedCoro(bool autoConnect) +{ + if (autoConnect) + { + setConnectionState(FSDiscordConnect::DISCORD_CONNECTION_IN_PROGRESS); + if (!checkMarkerFile()) + { + connectToDiscord(); + } + else + { + setConnectionState(FSDiscordConnect::DISCORD_CONNECTION_FAILED); + } + } + +} + +bool isRegionVisible(LLViewerRegion* region) +{ + U8 rating = region->getSimAccess(); + bool visible = true; + if (!(rating <= gSavedPerAccountSettings.getU32("FSMaxSharedMaturity"))) + { + visible = false; + } + else + { + std::string name = region->getName(); + LLSD list = gSavedPerAccountSettings.getLLSD("FSBlacklistedRegionNames"); + for (LLSD::array_const_iterator iter = list.beginArray(); + iter != list.endArray(); + iter++) + { + if (boost::algorithm::to_lower_copy((*iter).asString()) == boost::algorithm::to_lower_copy(name)) + { + visible = false; + break; + } + } + } + return visible; +} + +void FSDiscordConnect::updateRichPresence() +{ + LLViewerRegion * region = gAgent.getRegion(); + if (!isConnected() || !region) + { + return; + } + std::string region_name; + if (RlvActions::canShowLocation() && isRegionVisible(region)) + { + region_name = gAgent.getRegion()->getName(); + region_name += " "; + LLVector3 pos = gAgent.getPositionAgent(); + + region_name += llformat("(%.0f, %.0f, %.0f)", pos.mV[0], pos.mV[1], pos.mV[2]); + } + else + { + region_name = RlvStrings::getString(RLV_STRING_HIDDEN_REGION); + } + + DiscordRichPresence discordPresence; + memset(&discordPresence, 0, sizeof(discordPresence)); + discordPresence.state = region_name.c_str(); + LLAvatarName av_name; + std::string name; + if (RlvActions::canShowName(RlvActions::SNC_DEFAULT, gAgentID)) + { + if (LLAvatarNameCache::get(gAgentID, &av_name)) + { + name = av_name.getCompleteName(true, true); + } + else + { + name = gAgentUsername; + } + } + else + { + name = RlvStrings::getAnonym(av_name); + } + discordPresence.details = name.c_str(); + + discordPresence.largeImageKey = "secondlife_512"; + discordPresence.largeImageText = "Second Life"; + discordPresence.smallImageKey = "firestorm_512"; + //const char* appname = std::string("via " + APP_NAME).c_str(); // No idea why this doesnt work, but discord receives random data from somewhere in the programs address space and not the text that it should + discordPresence.smallImageText = "via Firestorm"; // I hate to hardcode the word "Firestorm" cause of the above global, but this way works + + discordPresence.partyId = gAgent.getRegion()->getRegionID().asString().c_str(); + discordPresence.partySize = gAgent.getRegion()->mMapAvatars.size(); + discordPresence.partyMax = LLRegionInfoModel::instance().mAgentLimit; + Discord_UpdatePresence(&discordPresence); +} + +/////////////////////////////////////////////////////////////////////////////// +// +FSDiscordConnect::FSDiscordConnect() +: mConnectionState(DISCORD_NOT_CONNECTED), + mConnected(false), + mInfo(), + mRefreshInfo(false) +{ + mMarkerFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "discord_in_use_marker"); + LLEventPumps::instance().obtain("mainloop").listen("FSDiscordConnect", boost::bind(&FSDiscordConnect::Tick, this, _1)); +} + +FSDiscordConnect::~FSDiscordConnect() +{ + disconnectFromDiscord(); +} + +void FSDiscordConnect::connectToDiscord() +{ + LLCoros::instance().launch("FSDiscordConnect::discordConnectCoro", + boost::bind(&FSDiscordConnect::discordConnectCoro, this)); +} + +void FSDiscordConnect::disconnectFromDiscord() +{ + setConnectionState(FSDiscordConnect::DISCORD_DISCONNECTING); + + LLCoros::instance().launch("FSDiscordConnect::discordDisconnectCoro", + boost::bind(&FSDiscordConnect::discordDisconnectCoro, this)); +} + +void FSDiscordConnect::checkConnectionToDiscord(bool auto_connect) +{ + LLCoros::instance().launch("FSDiscordConnect::discordConnectedCoro", + boost::bind(&FSDiscordConnect::discordConnectedCoro, this, auto_connect)); +} + +bool FSDiscordConnect::Tick(const LLSD&) +{ + Discord_RunCallbacks(); + updateRichPresence(); + + return false; +} + +void FSDiscordConnect::storeInfo(const LLSD& info) +{ + mInfo = info; + mRefreshInfo = false; + + sInfoWatcher->post(info); +} + +const LLSD& FSDiscordConnect::getInfo() const +{ + return mInfo; +} + +void FSDiscordConnect::clearInfo() +{ + mInfo = LLSD(); +} + +void FSDiscordConnect::setConnectionState(FSDiscordConnect::EConnectionState connection_state) +{ + if(connection_state == DISCORD_CONNECTED) + { + setMarkerFile(); + setConnected(true); + } + else if(connection_state == DISCORD_NOT_CONNECTED) + { + clearMarkerFile(); + setConnected(false); + } + + if (mConnectionState != connection_state) + { + // set the connection state before notifying watchers + mConnectionState = connection_state; + + LLSD state_info; + state_info["enum"] = connection_state; + sStateWatcher->post(state_info); + } +} + +void FSDiscordConnect::setConnected(bool connected) +{ + mConnected = connected; +} diff --git a/indra/newview/fsdiscordconnect.h b/indra/newview/fsdiscordconnect.h new file mode 100644 index 0000000000..3000ae2c88 --- /dev/null +++ b/indra/newview/fsdiscordconnect.h @@ -0,0 +1,98 @@ +/** + * @file fsdiscordconnect.h + * @brief Connection to Discord + * @author liny@pinkfox.xyz + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2019 Liny Odell @ Second Life + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + */ + +#ifndef FS_FSDISCORDCONNECT_H +#define FS_FSDISCORDCONNECT_H + +#include "llsingleton.h" +#include "llcoros.h" +#include "lleventcoro.h" + +class LLEventPump; + +/** + * @class LLTwitterConnect + * + * Manages authentication to, and interaction with, a web service allowing the + * the viewer to post status updates and upload photos to Twitter. + */ +class FSDiscordConnect : public LLSingleton +{ + LLSINGLETON(FSDiscordConnect); + LOG_CLASS(FSDiscordConnect); +public: + enum EConnectionState + { + DISCORD_NOT_CONNECTED = 0, + DISCORD_CONNECTION_IN_PROGRESS = 1, + DISCORD_CONNECTED = 2, + DISCORD_CONNECTION_FAILED = 3, + DISCORD_DISCONNECTING = 4 + }; + + ~FSDiscordConnect(); + + void connectToDiscord(); // Initiate the complete Discord connection. Please use checkConnectionToDiscord() in normal use. + void disconnectFromDiscord(); // Disconnect from the Discord service. + void checkConnectionToDiscord(bool auto_connect = false); // Check if connected to the Discord service. If not, call connectToDiscord(). + + void storeInfo(const LLSD& info); + const LLSD& getInfo() const; + void clearInfo(); + + void setConnectionState(EConnectionState connection_state); + void setConnected(bool connected); + bool isConnected() { return mConnected; } + EConnectionState getConnectionState() { return mConnectionState; } + + void updateRichPresence(); + + bool Tick(const LLSD&); + +private: + + EConnectionState mConnectionState; + BOOL mConnected; + LLSD mInfo; + bool mRefreshInfo; + + static boost::scoped_ptr sStateWatcher; + static boost::scoped_ptr sInfoWatcher; + static boost::scoped_ptr sContentWatcher; + + void discordConnectCoro(); + void discordDisconnectCoro(); + void discordConnectedCoro(bool autoConnect); + + bool checkMarkerFile(); + void setMarkerFile(); + void clearMarkerFile(); + + std::string mMarkerFilename; +}; + +#endif // FS_FSDISCORDCONNECT_H diff --git a/indra/newview/fsfloaterdiscord.cpp b/indra/newview/fsfloaterdiscord.cpp new file mode 100644 index 0000000000..061fcc19e4 --- /dev/null +++ b/indra/newview/fsfloaterdiscord.cpp @@ -0,0 +1,323 @@ +/** +* @file fsfloaterdiscord.cpp +* @brief Implementation of fsfloaterdiscord.cpp +* @author liny@pinkfox.xyz +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Phoenix Firestorm Viewer Source Code +* Copyright (C) 2019 Liny Odell @ Second Life +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA +* http://www.firestormviewer.org +*/ + +#include "llviewerprecompiledheaders.h" + +#include "fsfloaterdiscord.h" + +#include "llagent.h" +#include "llagentui.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "fsdiscordconnect.h" +#include "llfloaterreg.h" +#include "lltrans.h" +#include "llviewerregion.h" +#include "llviewercontrol.h" +#include "lltabcontainer.h" + +#include "boost/algorithm/string/case_conv.hpp" + +//////////////////////// +//FSFloaterDiscord/////// +//////////////////////// + +void FSFloaterDiscord::onVisibilityChange(BOOL visible) +{ + if(visible) + { + LLEventPumps::instance().obtain("DiscordConnectState").stopListening("FSDiscordAccountPanel"); + LLEventPumps::instance().obtain("DiscordConnectState").listen("FSDiscordAccountPanel", boost::bind(&FSFloaterDiscord::onDiscordConnectStateChange, this, _1)); + + LLEventPumps::instance().obtain("DiscordConnectInfo").stopListening("FSDiscordAccountPanel"); + LLEventPumps::instance().obtain("DiscordConnectInfo").listen("FSDiscordAccountPanel", boost::bind(&FSFloaterDiscord::onDiscordConnectInfoChange, this)); + + LLSD info = FSDiscordConnect::instance().getInfo(); + + if (info.has("name")) + { + mAccountNameLabel->setText(info["name"].asString()); + } + + //Connected + if(FSDiscordConnect::instance().isConnected()) + { + showConnectedLayout(); + } + //Check if connected (show disconnected layout in meantime) + else + { + showDisconnectedLayout(); + } + if ((FSDiscordConnect::instance().getConnectionState() == FSDiscordConnect::DISCORD_NOT_CONNECTED) || + (FSDiscordConnect::instance().getConnectionState() == FSDiscordConnect::DISCORD_CONNECTION_FAILED)) + { + FSDiscordConnect::instance().checkConnectionToDiscord(); + } + } + else + { + LLEventPumps::instance().obtain("DiscordConnectState").stopListening("FSDiscordAccountPanel"); + LLEventPumps::instance().obtain("DiscordConnectInfo").stopListening("FSDiscordAccountPanel"); + } +} + +void FSFloaterDiscord::onAllow() +{ + gSavedPerAccountSettings.setBOOL("FSEnableDiscordIntegration", mAllowCheckbox->get()); +} + +bool FSFloaterDiscord::onDiscordConnectStateChange(const LLSD& data) +{ + if(FSDiscordConnect::instance().isConnected()) + { + mAccountCaptionLabel->setText(getString("discord_connected")); + showConnectedLayout(); + } + else + { + mAccountCaptionLabel->setText(getString("discord_disconnected")); + showDisconnectedLayout(); + } + + return false; +} + +bool FSFloaterDiscord::onDiscordConnectInfoChange() +{ + LLSD info = FSDiscordConnect::instance().getInfo(); + + if(info.has("name")) + { + mAccountNameLabel->setText(info["name"].asString()); + } + + return false; +} + +void FSFloaterDiscord::showConnectButton() +{ + if(!mConnectButton->getVisible()) + { + mConnectButton->setVisible(TRUE); + mDisconnectButton->setVisible(FALSE); + } +} + +void FSFloaterDiscord::hideConnectButton() +{ + if(mConnectButton->getVisible()) + { + mConnectButton->setVisible(FALSE); + mDisconnectButton->setVisible(TRUE); + } +} + +void FSFloaterDiscord::showDisconnectedLayout() +{ + mAccountCaptionLabel->setText(getString("discord_disconnected")); + mAccountNameLabel->setText(std::string("")); + showConnectButton(); +} + +void FSFloaterDiscord::showConnectedLayout() +{ + mAccountCaptionLabel->setText(getString("discord_connected")); + hideConnectButton(); +} + +void FSFloaterDiscord::onConnect() +{ + FSDiscordConnect::instance().checkConnectionToDiscord(true); +} + +void FSFloaterDiscord::onDisconnect() +{ + FSDiscordConnect::instance().disconnectFromDiscord(); +} + +FSFloaterDiscord::FSFloaterDiscord(const LLSD& key) : LLFloater(key), +mStatusText(NULL) +{ + mCommitCallbackRegistrar.add("FSDiscord.Connect", boost::bind(&FSFloaterDiscord::onConnect, this)); + mCommitCallbackRegistrar.add("FSDiscord.Disconnect", boost::bind(&FSFloaterDiscord::onDisconnect, this)); + mCommitCallbackRegistrar.add("FSDiscord.Allow", boost::bind(&FSFloaterDiscord::onAllow, this)); + mCommitCallbackRegistrar.add("FSDiscord.Combo", boost::bind(&FSFloaterDiscord::onCombo, this)); + mCommitCallbackRegistrar.add("FSDiscord.Add", boost::bind(&FSFloaterDiscord::onAdd, this)); + mCommitCallbackRegistrar.add("FSDiscord.Rem", boost::bind(&FSFloaterDiscord::onRemove, this)); + + setVisibleCallback(boost::bind(&FSFloaterDiscord::onVisibilityChange, this, _2)); +} + +void FSFloaterDiscord::onCombo() +{ + gSavedPerAccountSettings.setU32("FSMaxSharedMaturity", mMaturityCombo->getSelectedValue().asInteger()); +} + +void FSFloaterDiscord::onAdd() +{ + std::string name = mBlacklistEntry->getText(); + LLStringUtil::trim(name); + if (name == "") + { + return; + } + std::string name_lower = boost::algorithm::to_lower_copy(name); + std::vector items = mBlacklistedNames->getAllData(); + std::vector::iterator itor; + for (itor = items.begin(); itor != items.end(); ++itor) + { + std::string tmp = (*itor)->getValue().asString(); + boost::algorithm::to_lower(tmp); + if (tmp == name_lower) + { + return; + } + } + mBlacklistedNames->addSimpleElement(name); + LLSD save; + for (itor = items.begin(); itor != items.end(); ++itor) + { + save.append((*itor)->getValue()); + } + save.append(name); + gSavedPerAccountSettings.setLLSD("FSBlacklistedRegionNames", save); +} + +void FSFloaterDiscord::onRemove() +{ + std::vector items = mBlacklistedNames->getAllData(); + std::vector::iterator itor; + LLSD save = LLSD::emptyArray(); + for (itor = items.begin(); itor != items.end(); ++itor) + { + if ((*itor)->getSelected()) + { + continue; + } + save.append((*itor)->getValue()); + } + mBlacklistedNames->deleteAllItems(); + for (LLSD::array_const_iterator iter = save.beginArray(); + iter != save.endArray(); + iter++) + { + mBlacklistedNames->addSimpleElement(iter->asString()); + } + gSavedPerAccountSettings.setLLSD("FSBlacklistedRegionNames", save); +} + +void FSFloaterDiscord::onClose(bool app_quitting) +{ + if (app_quitting) + { + std::vector items = mBlacklistedNames->getAllData(); + std::vector::iterator itor; + LLSD save = LLSD::emptyArray(); + for (itor = items.begin(); itor != items.end(); ++itor) + { + if ((*itor)->getSelected()) + { + continue; + } + save.append((*itor)->getValue()); + } + gSavedPerAccountSettings.setLLSD("FSBlacklistedRegionNames", save); + } + LLFloater::onClose(app_quitting); +} + +BOOL FSFloaterDiscord::postBuild() +{ + mAccountCaptionLabel = getChild("account_caption_label"); + mAccountNameLabel = getChild("account_name_label"); + mDisconnectButton = getChild("disconnect_btn"); + mConnectButton = getChild("connect_btn"); + mAllowCheckbox = getChild("startup_check"); + mMaturityCombo = getChild("maturity_desired_combobox"); + mBlacklistedNames = getChild("blacklisted_names"); + mBlacklistEntry = getChild("blacklist_entry"); + mAddBlacklist = getChild("blacklist_entry_add"); + mRemBlacklist = getChild("blacklist_entry_rem"); + + LLSD list = gSavedPerAccountSettings.getLLSD("FSBlacklistedRegionNames"); + for (LLSD::array_const_iterator iter = list.beginArray(); + iter != list.endArray(); + iter++) + { + mBlacklistedNames->addSimpleElement(iter->asString()); + } + + mMaturityCombo->selectByValue((LLSD::Integer)gSavedPerAccountSettings.getU32("FSMaxSharedMaturity")); + + mAllowCheckbox->set(gSavedPerAccountSettings.getBOOL("FSEnableDiscordIntegration")); + + // Connection status widgets + mStatusText = getChild("connection_status_text"); + + return LLFloater::postBuild(); +} + +void FSFloaterDiscord::draw() +{ + if (mStatusText) + { + mStatusText->setVisible(false); + FSDiscordConnect::EConnectionState connection_state = FSDiscordConnect::instance().getConnectionState(); + std::string status_text; + + switch (connection_state) + { + case FSDiscordConnect::DISCORD_NOT_CONNECTED: + // No status displayed when first opening the panel and no connection done + break; + case FSDiscordConnect::DISCORD_CONNECTION_IN_PROGRESS: + // Connection loading indicator + mStatusText->setVisible(true); + status_text = LLTrans::getString("SocialDiscordConnecting"); + mStatusText->setValue(status_text); + break; + case FSDiscordConnect::DISCORD_CONNECTED: + // When successfully connected, no message is displayed + break; + case FSDiscordConnect::DISCORD_CONNECTION_FAILED: + // Error connecting to the service + mStatusText->setVisible(true); + status_text = LLTrans::getString("SocialDiscordErrorConnecting"); + mStatusText->setValue(status_text); + break; + case FSDiscordConnect::DISCORD_DISCONNECTING: + // Disconnecting loading indicator + mStatusText->setVisible(true); + status_text = LLTrans::getString("SocialDiscordDisconnecting"); + mStatusText->setValue(status_text); + break; + } + } + LLFloater::draw(); +} + diff --git a/indra/newview/fsfloaterdiscord.h b/indra/newview/fsfloaterdiscord.h new file mode 100644 index 0000000000..f3c04cb2a9 --- /dev/null +++ b/indra/newview/fsfloaterdiscord.h @@ -0,0 +1,78 @@ +/** +* @file llfloatertwitter.h +* @brief Header file for fsfloaterdiscord +* @author liny@pinkfox.xyz +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Phoenix Firestorm Viewer Source Code +* Copyright (C) 2019 Liny Odell @ Second Life +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA +* http://www.firestormviewer.org +*/ +#ifndef FS_FSFLOATERDISCORD_H +#define FS_FSFLOATERDISCORD_H + +#include "llfloater.h" +#include "lltextbox.h" + +class LLCheckBoxCtrl; +class LLComboBox; +class LLScrollListCtrl; +class LLLineEditor; + +class FSFloaterDiscord : public LLFloater +{ +public: + FSFloaterDiscord(const LLSD& key); + BOOL postBuild(); + void draw(); + void onClose(bool app_quitting); + +private: + void onVisibilityChange(BOOL visible); + bool onDiscordConnectStateChange(const LLSD& data); + bool onDiscordConnectInfoChange(); + void onConnect(); + void onUseAnotherAccount(); + void onDisconnect(); + void onAllow(); + void onCombo(); + void onAdd(); + void onRemove(); + + void showConnectButton(); + void hideConnectButton(); + void showDisconnectedLayout(); + void showConnectedLayout(); + + LLTextBox * mAccountCaptionLabel; + LLTextBox * mAccountNameLabel; + LLButton * mConnectButton; + LLButton * mDisconnectButton; + LLCheckBoxCtrl * mAllowCheckbox; + LLComboBox * mMaturityCombo; + LLScrollListCtrl * mBlacklistedNames; + LLLineEditor * mBlacklistEntry; + LLButton * mAddBlacklist; + LLButton * mRemBlacklist; + + LLTextBox* mStatusText; +}; + +#endif // FS_FSFLOATERDISCORD_H + diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index f645afdef8..ef3b8da9a4 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -180,6 +180,7 @@ #include "fsfloateravatarrendersettings.h" #include "fsfloatercontacts.h" #include "fsfloatercontactsetconfiguration.h" +#include "fsfloaterdiscord.h" #include "fsfloaterexport.h" #include "fsfloaterblocklist.h" #include "fsfloatergroup.h" @@ -465,6 +466,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("fs_blocklist", "floater_fs_blocklist.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_add_contact", "floater_fs_contact_add.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_contact_set_config", "floater_fs_contact_set_configuration.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("fs_discord", "floater_fs_discord.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_group", "floater_fs_group.xml",&LLFloaterReg::build); LLFloaterReg::add("fs_group_titles", "floater_fs_group_titles.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_export", "floater_fs_export.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 93fbfbb0ef..f9aa45158e 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -130,6 +130,8 @@ #include "llsidepanelappearance.h" #include "fsavatarrenderpersistence.h" +#include "fsdiscordconnect.h" // tapping a place that happens on landing in world to start up discord + extern F32 SPEED_ADJUST_MAX; extern F32 SPEED_ADJUST_MAX_SEC; extern F32 ANIM_SPEED_MAX; @@ -3078,6 +3080,8 @@ void LLVOAvatar::idleUpdateLoadingEffect() // Animation Overrider AOEngine::instance().onLoginComplete(); + // tapping a place that happens on landing in world to start up discord + FSDiscordConnect::instance().checkConnectionToDiscord(gSavedPerAccountSettings.getBOOL("FSEnableDiscordIntegration")); } else { diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 3cb7dae535..d8e9cd6edb 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -1031,6 +1031,8 @@ with the same filename but different name + + diff --git a/indra/newview/skins/default/textures/toolbar_icons/discord.png b/indra/newview/skins/default/textures/toolbar_icons/discord.png new file mode 100644 index 0000000000000000000000000000000000000000..07a738904db665b08ca7102d215118d374b1a341 GIT binary patch literal 501 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+uenMVO6iP5s=4T z;_2(k{+N@4QH8DdBrV)Ym2I*N@3YY=3fMO|^M*p=hXQwO(lM>R(?BG@j~c zc}E6JpET7_T>b6tpb5fzb{ixsz1zL!s-L-X*Wa&O`RiX#6Teup{A=;$ow2Qc&Vk=m zU9 + + + + Connecting to Discord + Error connecting to Discord + Disconnecting from Discord + + Not connected to Discord. + + + + + + + + + + Dont share region names to Discord if the maturity is higher than: + + + + + + + + + Dont share region names to Discord if they are listed below: + + + + + + + + + Loading... + + diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 7ecb1e51eb..faa1f2f48b 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -604,6 +604,13 @@ + + + Problem connecting to Twitter Problem posting to Twitter Problem disconnecting from Twitter + + + Connecting to Discord + Error connecting to Discord + Disconnecting from Discord Black & White @@ -2712,6 +2717,9 @@ Try enclosing path to the editor with double quotes. Toggle flying mode on/off (HOME) Stop Animations Stop Animating my Avatar + + Discord + Discord Retain%