diff --git a/autobuild.xml b/autobuild.xml
index e00c65c215..fa5d034546 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -237,6 +237,58 @@
+ discord-rpc
+
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 0000000000..07a738904d
Binary files /dev/null and b/indra/newview/skins/default/textures/toolbar_icons/discord.png differ
diff --git a/indra/newview/skins/default/xui/en/floater_fs_discord.xml b/indra/newview/skins/default/xui/en/floater_fs_discord.xml
new file mode 100644
index 0000000000..5cd62a8801
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_fs_discord.xml
@@ -0,0 +1,189 @@
+
+
+
+
+ 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 @@
+
+
+