summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yaml64
-rw-r--r--indra/llcommon/llstring.h62
-rw-r--r--indra/llcommon/threadpool.cpp19
-rw-r--r--indra/llui/llchat.h19
-rw-r--r--indra/newview/llchathistory.cpp38
-rw-r--r--indra/newview/llchathistory.h3
-rw-r--r--indra/newview/llchatitemscontainerctrl.cpp5
-rw-r--r--indra/newview/llchatitemscontainerctrl.h6
-rw-r--r--indra/newview/llfloaterimnearbychat.cpp3
-rw-r--r--indra/newview/llfloaterimnearbychathandler.cpp13
-rw-r--r--indra/newview/llfloaterimnearbychatlistener.cpp35
-rw-r--r--indra/newview/llfloaterimnearbychatlistener.h6
-rw-r--r--indra/newview/llviewerchat.cpp9
-rw-r--r--indra/newview/llviewermessage.cpp5
-rw-r--r--indra/newview/scripts/lua/LLChat.lua17
-rw-r--r--indra/newview/scripts/lua/test_LLChat.lua18
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml4
17 files changed, 261 insertions, 65 deletions
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 4785273b78..8d1c6b63e6 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -2,20 +2,52 @@ name: Build
on:
workflow_dispatch:
+ inputs:
+ release_run:
+ type: boolean
+ description: Do a release of this build
+ default: false
pull_request:
push:
branches: ["main", "release/*", "project/*"]
tags: ["Second_Life*"]
jobs:
+ # The whole point of the setvar job is that we want to set a variable once
+ # that will be consumed by multiple subsequent jobs. We tried setting it in
+ # the global env, but a job.env can't directly reference the global env
+ # context.
+ setvar:
+ runs-on: ubuntu-latest
+ outputs:
+ release_run: ${{ steps.setvar.outputs.release_run }}
+ env:
+ # Build with a tag like "Second_Life#abcdef0" to generate a release page
+ # (used for builds we are planning to deploy).
+ # When you want to use a string variable as a workflow YAML boolean, it's
+ # important to ensure it's the empty string when false. If you omit || '',
+ # its value when false is "false", which is interpreted as true.
+ RELEASE_RUN: ${{ (github.event.inputs.release_run || github.ref_type == 'tag' && startsWith(github.ref_name, 'Second_Life')) && 'Y' || '' }}
+ steps:
+ - name: Set Variable
+ id: setvar
+ shell: bash
+ run: |
+ echo "release_run=$RELEASE_RUN" >> "$GITHUB_OUTPUT"
+
build:
+ needs: setvar
strategy:
matrix:
runner: [windows-large, macos-12-xl]
configuration: [Release]
+ Linden: [true]
include:
- runner: macos-12-xl
developer_dir: "/Applications/Xcode_14.0.1.app/Contents/Developer"
+ - runner: windows-large
+ configuration: ReleaseOS
+ Linden: false
runs-on: ${{ matrix.runner }}
outputs:
viewer_channel: ${{ steps.build.outputs.viewer_channel }}
@@ -37,7 +69,10 @@ jobs:
AUTOBUILD_VSVER: "170"
DEVELOPER_DIR: ${{ matrix.developer_dir }}
# Ensure that Linden viewer builds engage Bugsplat.
- BUGSPLAT_DB: ${{ matrix.configuration != 'ReleaseOS' && 'SecondLife_Viewer_2018' || '' }}
+ BUGSPLAT_DB: ${{ matrix.Linden && 'SecondLife_Viewer_2018' || '' }}
+ # Run BUILD steps for Release configuration.
+ # Run BUILD steps for ReleaseOS configuration only for release runs.
+ BUILD: ${{ (matrix.Linden || needs.setvar.outputs.release_run) && 'Y' || '' }}
build_coverity: false
build_log_dir: ${{ github.workspace }}/.logs
build_viewer: true
@@ -56,16 +91,19 @@ jobs:
variants: ${{ matrix.configuration }}
steps:
- name: Checkout code
+ if: env.BUILD
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup python
+ if: env.BUILD
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Checkout build variables
+ if: env.BUILD
uses: actions/checkout@v4
with:
repository: secondlife/build-variables
@@ -73,17 +111,20 @@ jobs:
path: .build-variables
- name: Checkout master-message-template
+ if: env.BUILD
uses: actions/checkout@v4
with:
repository: secondlife/master-message-template
path: .master-message-template
- name: Install autobuild and python dependencies
+ if: env.BUILD
run: pip3 install autobuild llsd
- name: Cache autobuild packages
- uses: actions/cache@v4
id: cache-installables
+ if: env.BUILD
+ uses: actions/cache@v4
with:
path: .autobuild-installables
key: ${{ runner.os }}-64-${{ matrix.configuration }}-${{ hashFiles('autobuild.xml') }}
@@ -92,17 +133,19 @@ jobs:
${{ runner.os }}-64-
- name: Install windows dependencies
- if: runner.os == 'Windows'
+ if: env.BUILD && runner.os == 'Windows'
run: choco install nsis-unicode
- name: Determine source branch
id: which-branch
+ if: env.BUILD
uses: secondlife/viewer-build-util/which-branch@v2
with:
token: ${{ github.token }}
- name: Build
id: build
+ if: env.BUILD
shell: bash
env:
AUTOBUILD_VCS_BRANCH: ${{ steps.which-branch.outputs.branch }}
@@ -179,7 +222,7 @@ jobs:
# determine the viewer channel from the branch name
branch=$AUTOBUILD_VCS_BRANCH
- IFS='/' read -ra ba <<< $branch
+ IFS='/' read -ra ba <<< "$branch"
prefix=${ba[0]}
if [ "$prefix" == "project" ]; then
IFS='_' read -ra prj <<< "${ba[1]}"
@@ -225,7 +268,7 @@ jobs:
echo "artifact=$RUNNER_OS$cfg_suffix" >> $GITHUB_OUTPUT
- name: Upload executable
- if: matrix.configuration != 'ReleaseOS' && steps.build.outputs.viewer_app
+ if: matrix.Linden && steps.build.outputs.viewer_app
uses: actions/upload-artifact@v4
with:
name: "${{ steps.build.outputs.artifact }}-app"
@@ -235,7 +278,7 @@ jobs:
# The other upload of nontrivial size is the symbol file. Use a distinct
# artifact for that too.
- name: Upload symbol file
- if: matrix.configuration != 'ReleaseOS'
+ if: matrix.Linden
uses: actions/upload-artifact@v4
with:
name: "${{ steps.build.outputs.artifact }}-symbols"
@@ -243,7 +286,7 @@ jobs:
${{ steps.build.outputs.symbolfile }}
- name: Upload metadata
- if: matrix.configuration != 'ReleaseOS'
+ if: matrix.Linden
uses: actions/upload-artifact@v4
with:
name: "${{ steps.build.outputs.artifact }}-metadata"
@@ -254,7 +297,7 @@ jobs:
- name: Upload physics package
uses: actions/upload-artifact@v4
# should only be set for viewer-private
- if: matrix.configuration != 'ReleaseOS' && steps.build.outputs.physicstpv
+ if: matrix.Linden && steps.build.outputs.physicstpv
with:
name: "${{ steps.build.outputs.artifact }}-physics"
# emitted by build.sh, zero or one lines
@@ -358,10 +401,9 @@ jobs:
version: ${{ needs.build.outputs.viewer_version }}
release:
- needs: [build, sign-and-package-windows, sign-and-package-mac]
+ needs: [setvar, build, sign-and-package-windows, sign-and-package-mac]
runs-on: ubuntu-latest
- # Build with a tag like "Second_Life#abcdef0" to generate a release page (used for builds we are planning to deploy).
- if: github.ref_type == 'tag' && startsWith(github.ref_name, 'Second_Life')
+ if: needs.setvar.outputs.release_run
steps:
- uses: actions/download-artifact@v4
with:
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 21b0da4822..cd8b2a2dcd 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -315,6 +315,14 @@ public:
static void trim(string_type& string) { trimHead(string); trimTail(string); }
static void truncate(string_type& string, size_type count);
+ // if string startsWith prefix, remove it and return true
+ static bool removePrefix(string_type& string, const string_type& prefix);
+ // if string startsWith prefix, return (string without prefix, true), else (string, false)
+ static std::pair<string_type, bool> withoutPrefix(const string_type& string, const string_type& prefix);
+ // like removePrefix()
+ static bool removeSuffix(string_type& string, const string_type& suffix);
+ static std::pair<string_type, bool> withoutSuffix(const string_type& string, const string_type& suffix);
+
static void toUpper(string_type& string);
static void toLower(string_type& string);
@@ -1479,6 +1487,60 @@ void LLStringUtilBase<T>::trimTail(string_type& string)
}
}
+// if string startsWith prefix, remove it and return true
+template<class T>
+bool LLStringUtilBase<T>::removePrefix(string_type& string, const string_type& prefix)
+{
+ bool found{ startsWith(string, prefix) };
+ if (found)
+ {
+ string.erase(0, prefix.length());
+ }
+ return found;
+}
+
+// if string startsWith prefix, return (string without prefix, true), else (string, false)
+template<class T>
+std::pair<typename LLStringUtilBase<T>::string_type, bool>
+LLStringUtilBase<T>::withoutPrefix(const string_type& string, const string_type& prefix)
+{
+ bool found{ startsWith(string, prefix) };
+ if (! found)
+ {
+ return { string, false };
+ }
+ else
+ {
+ return { string.substr(prefix.length()), true };
+ }
+}
+
+// like removePrefix()
+template<class T>
+bool LLStringUtilBase<T>::removeSuffix(string_type& string, const string_type& suffix)
+{
+ bool found{ endsWith(string, suffix) };
+ if (found)
+ {
+ string.erase(string.length() - suffix.length());
+ }
+ return found;
+}
+
+template<class T>
+std::pair<typename LLStringUtilBase<T>::string_type, bool>
+LLStringUtilBase<T>::withoutSuffix(const string_type& string, const string_type& suffix)
+{
+ bool found{ endsWith(string, suffix) };
+ if (! found)
+ {
+ return { string, false };
+ }
+ else
+ {
+ return { string.substr(0, string.length() - suffix.length()), true };
+ }
+}
// Replace line feeds with carriage return-line feed pairs.
//static
diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp
index edccdb097b..0f445b84fb 100644
--- a/indra/llcommon/threadpool.cpp
+++ b/indra/llcommon/threadpool.cpp
@@ -109,20 +109,19 @@ LL::ThreadPoolBase::~ThreadPoolBase()
void LL::ThreadPoolBase::close()
{
- if (! mQueue->isClosed())
+ // mQueue might have been closed already, but in any case we must join or
+ // detach each of our threads before destroying the mThreads vector.
+ LL_DEBUGS("ThreadPool") << mName << " closing queue and joining threads" << LL_ENDL;
+ mQueue->close();
+ for (auto& pair: mThreads)
{
- LL_DEBUGS("ThreadPool") << mName << " closing queue and joining threads" << LL_ENDL;
- mQueue->close();
- for (auto& pair: mThreads)
+ if (pair.second.joinable())
{
- if (pair.second.joinable())
- {
- LL_DEBUGS("ThreadPool") << mName << " waiting on thread " << pair.first << LL_ENDL;
- pair.second.join();
- }
+ LL_DEBUGS("ThreadPool") << mName << " waiting on thread " << pair.first << LL_ENDL;
+ pair.second.join();
}
- LL_DEBUGS("ThreadPool") << mName << " shutdown complete" << LL_ENDL;
}
+ LL_DEBUGS("ThreadPool") << mName << " shutdown complete" << LL_ENDL;
}
void LL::ThreadPoolBase::run(const std::string& name)
diff --git a/indra/llui/llchat.h b/indra/llui/llchat.h
index 56105add7e..70d7e82970 100644
--- a/indra/llui/llchat.h
+++ b/indra/llui/llchat.h
@@ -89,7 +89,8 @@ public:
mPosAgent(),
mURL(),
mChatStyle(CHAT_STYLE_NORMAL),
- mSessionID()
+ mSessionID(),
+ mIsScript(false)
{ }
std::string mText; // UTF-8 line of text
@@ -107,6 +108,22 @@ public:
std::string mURL;
EChatStyle mChatStyle;
LLUUID mSessionID;
+
+ bool mIsScript;
};
+static const std::string LUA_PREFIX("[LUA]");
+
+inline
+std::string without_LUA_PREFIX(const std::string& string, bool is_lua)
+{
+ if (is_lua)
+ {
+ return string.substr(LUA_PREFIX.size());
+ }
+ else
+ {
+ return string;
+ }
+}
#endif
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 4a08eace62..d549f372e3 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -125,6 +125,7 @@ public:
mUserNameTextBox(NULL),
mTimeBoxTextBox(NULL),
mNeedsTimeBox(true),
+ mIsFromScript(false),
mAvatarNameCacheConnection()
{}
@@ -658,11 +659,13 @@ public:
const LLUUID& getAvatarId () const { return mAvatarID;}
- void setup(const LLChat& chat, const LLStyle::Params& style_params, const LLSD& args)
+ void setup(const LLChat& chat, const LLStyle::Params& style_params, const LLSD& args, bool is_script)
{
mAvatarID = chat.mFromID;
mSessionID = chat.mSessionID;
mSourceType = chat.mSourceType;
+ mIsFromScript = is_script;
+ mPrefix = mIsFromScript ? LLTrans::getString("ScriptBy") : "";
// To be able to report a message, we need a copy of it's text
// and it's easier to store text directly than trying to get
@@ -732,7 +735,7 @@ public:
username_end == (chat.mFromName.length() - 1))
{
mFrom = chat.mFromName.substr(0, username_start);
- user_name->setValue(mFrom);
+ user_name->setValue(mPrefix + mFrom);
if (gSavedSettings.getBOOL("NameTagShowUsernames"))
{
@@ -774,7 +777,7 @@ public:
switch (mSourceType)
{
case CHAT_SOURCE_AGENT:
- icon->setValue(chat.mFromID);
+ icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD(chat.mFromID));
break;
case CHAT_SOURCE_OBJECT:
icon->setValue(LLSD("OBJECT_Icon"));
@@ -787,7 +790,7 @@ public:
icon->setValue(LLSD("Command_Destinations_Icon"));
break;
case CHAT_SOURCE_UNKNOWN:
- icon->setValue(LLSD("Unknown_Icon"));
+ icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD("Unknown_Icon"));
}
// In case the message came from an object, save the object info
@@ -1029,7 +1032,7 @@ private:
mFrom = av_name.getDisplayName();
LLTextBox* user_name = getChild<LLTextBox>("user_name");
- user_name->setValue( LLSD(av_name.getDisplayName() ) );
+ user_name->setValue(LLSD(mPrefix + av_name.getDisplayName()));
user_name->setToolTip( av_name.getUserName() );
if (gSavedSettings.getBOOL("NameTagShowUsernames") &&
@@ -1071,6 +1074,9 @@ protected:
bool mNeedsTimeBox;
+ bool mIsFromScript;
+ std::string mPrefix;
+
private:
boost::signals2::connection mAvatarNameCacheConnection;
};
@@ -1088,6 +1094,7 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
mTopHeaderPad(p.top_header_pad),
mBottomHeaderPad(p.bottom_header_pad),
mIsLastMessageFromLog(false),
+ mIsLastFromScript(false),
mNotifyAboutUnreadMsg(p.notify_unread_msg)
{
LLTextEditor::Params editor_params(p);
@@ -1185,11 +1192,11 @@ LLView* LLChatHistory::getSeparator()
return separator;
}
-LLView* LLChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args)
+LLView* LLChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args, bool is_script)
{
LLChatHistoryHeader* header = LLChatHistoryHeader::createInstance(mMessageHeaderFilename);
if (header)
- header->setup(chat, style_params, args);
+ header->setup(chat, style_params, args, is_script);
return header;
}
@@ -1258,8 +1265,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
name_params.color(name_color);
name_params.readonly_color(name_color);
- std::string prefix = chat.mText.substr(0, 4);
-
+ auto [message, is_lua] = LLStringUtil::withoutPrefix(chat.mText, LUA_PREFIX);
+ std::string prefix = message.substr(0, 4);
//IRC styled /me messages.
bool irc_me = prefix == "/me " || prefix == "/me'";
@@ -1335,6 +1342,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
// names showing
if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size())
{
+ std::string script_prefix = is_lua ? LLTrans::getString("ScriptBy") : "";
// Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text.
if (chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull())
{
@@ -1359,7 +1367,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID));
// Add link to avatar's inspector and delimiter to message.
- mEditor->appendText(std::string(link_params.link_href) + delimiter,
+ mEditor->appendText(script_prefix + std::string(link_params.link_href) + delimiter,
prependNewLineState, link_params);
prependNewLineState = false;
}
@@ -1372,7 +1380,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
}
else
{
- mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter,
+ mEditor->appendText(script_prefix + "<nolink>" + chat.mFromName + "</nolink>" + delimiter,
prependNewLineState, body_message_params);
prependNewLineState = false;
}
@@ -1393,7 +1401,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
&& mLastFromID == chat.mFromID
&& mLastMessageTime.notNull()
&& (new_message_time.secondsSinceEpoch() - mLastMessageTime.secondsSinceEpoch()) < 60.0
- && mIsLastMessageFromLog == message_from_log) //distinguish between current and previous chat session's histories
+ && mIsLastMessageFromLog == message_from_log //distinguish between current and previous chat session's histories
+ && mIsLastFromScript == is_lua)
{
view = getSeparator();
if (!view)
@@ -1408,7 +1417,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
}
else
{
- view = getHeader(chat, name_params, args);
+ view = getHeader(chat, name_params, args, is_lua);
if (!view)
{
LL_WARNS() << "Failed to create header from " << mMessageHeaderFilename << ": can't append to history" << LL_ENDL;
@@ -1437,6 +1446,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
mLastFromID = chat.mFromID;
mLastMessageTime = new_message_time;
mIsLastMessageFromLog = message_from_log;
+ mIsLastFromScript = is_lua;
}
// body of the message processing
@@ -1491,7 +1501,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
// usual messages showing
else if (!teleport_separator)
{
- std::string message = irc_me ? chat.mText.substr(3) : chat.mText;
+ message = irc_me ? message.substr(3) : message;
//MESSAGE TEXT PROCESSING
//*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010)
diff --git a/indra/newview/llchathistory.h b/indra/newview/llchathistory.h
index b8364ff860..8acbea6ed5 100644
--- a/indra/newview/llchathistory.h
+++ b/indra/newview/llchathistory.h
@@ -101,7 +101,7 @@ class LLChatHistory : public LLUICtrl
* Builds a message header.
* @return pointer to LLView header object.
*/
- LLView* getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args);
+ LLView* getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args, bool is_script = false);
public:
~LLChatHistory();
LLSD getValue() const;
@@ -127,6 +127,7 @@ class LLChatHistory : public LLUICtrl
LLDate mLastMessageTime;
bool mIsLastMessageFromLog;
bool mNotifyAboutUnreadMsg;
+ bool mIsLastFromScript;
//std::string mLastMessageTimeStr;
std::string mMessageHeaderFilename;
diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp
index 2b875c708d..0561d56fe1 100644
--- a/indra/newview/llchatitemscontainerctrl.cpp
+++ b/indra/newview/llchatitemscontainerctrl.cpp
@@ -187,6 +187,7 @@ void LLFloaterIMNearbyChatToastPanel::init(LLSD& notification)
int sType = notification["source"].asInteger();
mSourceType = (EChatSourceType)sType;
+ mIsFromScript = notification["is_lua"].asBoolean();
std::string color_name = notification["text_color"].asString();
@@ -214,7 +215,7 @@ void LLFloaterIMNearbyChatToastPanel::init(LLSD& notification)
{
std::string str_sender;
- str_sender = fromName;
+ str_sender = mIsFromScript ? LLTrans::getString("ScriptBy") + fromName : fromName;
str_sender+=" ";
@@ -401,7 +402,7 @@ void LLFloaterIMNearbyChatToastPanel::draw()
else if(mSourceType == CHAT_SOURCE_SYSTEM)
icon->setValue(LLSD("SL_Logo"));
else if(mSourceType == CHAT_SOURCE_AGENT)
- icon->setValue(mFromID);
+ icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD(mFromID));
else if(!mFromID.isNull())
icon->setValue(mFromID);
}
diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h
index d041615060..7df00e8fd9 100644
--- a/indra/newview/llchatitemscontainerctrl.h
+++ b/indra/newview/llchatitemscontainerctrl.h
@@ -48,7 +48,8 @@ protected:
LLFloaterIMNearbyChatToastPanel()
:
mIsDirty(false),
- mSourceType(CHAT_SOURCE_OBJECT)
+ mSourceType(CHAT_SOURCE_OBJECT),
+ mIsFromScript(false)
{};
public:
~LLFloaterIMNearbyChatToastPanel(){}
@@ -58,6 +59,8 @@ public:
const LLUUID& getFromID() const { return mFromID;}
const std::string& getFromName() const { return mFromName; }
+ bool isFromScript() { return mIsFromScript; }
+
//void addText (const std::string& message , const LLStyle::Params& input_params = LLStyle::Params());
//void setMessage (const LLChat& msg);
void snapToMessageHeight ();
@@ -88,6 +91,7 @@ private:
std::string mFromName;
EChatSourceType mSourceType;
LLChatMsgBox* mMsgText;
+ bool mIsFromScript;
diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
index e64f468cbe..6b817c7cf1 100644
--- a/indra/newview/llfloaterimnearbychat.cpp
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -52,6 +52,7 @@
#include "llfirstuse.h"
#include "llfloaterimnearbychat.h"
+#include "llfloaterimnearbychatlistener.h"
#include "llagent.h" // gAgent
#include "llgesturemgr.h"
#include "llmultigesture.h"
@@ -71,6 +72,8 @@
S32 LLFloaterIMNearbyChat::sLastSpecialChatChannel = 0;
+static LLFloaterIMNearbyChatListener sChatListener;
+
const S32 EXPANDED_HEIGHT = 266;
const S32 COLLAPSED_HEIGHT = 60;
const S32 EXPANDED_MIN_HEIGHT = 150;
diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp
index 77ceea19af..cda71f97d4 100644
--- a/indra/newview/llfloaterimnearbychathandler.cpp
+++ b/indra/newview/llfloaterimnearbychathandler.cpp
@@ -301,12 +301,13 @@ void LLFloaterIMNearbyChatScreenChannel::addChat(LLSD& chat)
{
LLUUID fromID = chat["from_id"].asUUID(); // agent id or object id
std::string from = chat["from"].asString();
+ bool is_lua = chat["is_lua"].asBoolean();
LLToast* toast = m_active_toasts[0].get();
if (toast)
{
LLFloaterIMNearbyChatToastPanel* panel = dynamic_cast<LLFloaterIMNearbyChatToastPanel*>(toast->getPanel());
- if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText())
+ if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->isFromScript() == is_lua && panel->canAddText())
{
panel->addMessage(chat);
toast->reshapeToPanel();
@@ -598,17 +599,22 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
{
// Handle IRC styled messages.
std::string toast_msg;
+ std::string msg_text = without_LUA_PREFIX(chat_msg.mText, chat_msg.mIsScript);
if (chat_msg.mChatStyle == CHAT_STYLE_IRC)
{
+ if (chat_msg.mIsScript)
+ {
+ toast_msg += LLTrans::getString("ScriptStr");
+ }
if (!chat_msg.mFromName.empty())
{
toast_msg += chat_msg.mFromName;
}
- toast_msg += chat_msg.mText.substr(3);
+ toast_msg += msg_text.substr(3);
}
else
{
- toast_msg = chat_msg.mText;
+ toast_msg = msg_text;
}
bool chat_overlaps = false;
@@ -658,6 +664,7 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
chat["color_alpha"] = r_color_alpha;
chat["font_size"] = (S32)LLViewerChat::getChatFontSize() ;
chat["message"] = toast_msg;
+ chat["is_lua"] = chat_msg.mIsScript;
channel->addChat(chat);
}
diff --git a/indra/newview/llfloaterimnearbychatlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp
index 616acf0eae..0618741cc4 100644
--- a/indra/newview/llfloaterimnearbychatlistener.cpp
+++ b/indra/newview/llfloaterimnearbychatlistener.cpp
@@ -34,12 +34,14 @@
#include "llagent.h"
#include "llchat.h"
#include "llviewercontrol.h"
+#include "stringize.h"
+static const F32 CHAT_THROTTLE_PERIOD = 1.f;
-LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar)
+LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener()
: LLEventAPI("LLChatBar",
"LLChatBar listener to (e.g.) sendChat, etc."),
- mChatbar(chatbar)
+ mLastThrottleTime(0)
{
add("sendChat",
"Send chat to the simulator:\n"
@@ -51,10 +53,19 @@ LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyCh
// "sendChat" command
-void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
+void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data)
{
+ F64 cur_time = LLTimer::getElapsedSeconds();
+
+ if (cur_time < mLastThrottleTime + CHAT_THROTTLE_PERIOD)
+ {
+ LL_DEBUGS("LLFloaterIMNearbyChatListener") << "'sendChat' was throttled" << LL_ENDL;
+ return;
+ }
+ mLastThrottleTime = cur_time;
+
// Extract the data
- std::string chat_text = chat_data["message"].asString();
+ std::string chat_text = LUA_PREFIX + chat_data["message"].asString();
S32 channel = 0;
if (chat_data.has("channel"))
@@ -81,20 +92,14 @@ void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
}
// Have to prepend /42 style channel numbers
- std::string chat_to_send;
- if (channel == 0)
- {
- chat_to_send = chat_text;
- }
- else
+ if (channel)
{
- chat_to_send += "/";
- chat_to_send += chat_data["channel"].asString();
- chat_to_send += " ";
- chat_to_send += chat_text;
+ chat_text = stringize("/", chat_data["channel"].asString(), " ", chat_text);
}
// Send it as if it was typed in
- mChatbar.sendChatFromViewer(chat_to_send, type_o_chat, ((BOOL)(channel == 0)) && gSavedSettings.getBOOL("PlayChatAnim"));
+ LLFloaterIMNearbyChat::sendChatFromViewer(chat_text, type_o_chat,
+ (channel == 0) &&
+ gSavedSettings.getBOOL("PlayChatAnim"));
}
diff --git a/indra/newview/llfloaterimnearbychatlistener.h b/indra/newview/llfloaterimnearbychatlistener.h
index 96184d95b3..18a8bacfaa 100644
--- a/indra/newview/llfloaterimnearbychatlistener.h
+++ b/indra/newview/llfloaterimnearbychatlistener.h
@@ -38,12 +38,12 @@ class LLFloaterIMNearbyChat;
class LLFloaterIMNearbyChatListener : public LLEventAPI
{
public:
- LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar);
+ LLFloaterIMNearbyChatListener();
private:
- void sendChat(LLSD const & chat_data) const;
+ void sendChat(LLSD const & chat_data);
- LLFloaterIMNearbyChat & mChatbar;
+ F64 mLastThrottleTime{ 0.0 };
};
#endif // LL_LLFLOATERIMNEARBYCHATLISTENER_H
diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp
index 597cf3c98c..00520f100e 100644
--- a/indra/newview/llviewerchat.cpp
+++ b/indra/newview/llviewerchat.cpp
@@ -30,6 +30,7 @@
// newview includes
#include "llagent.h" // gAgent
#include "llslurl.h"
+#include "lltrans.h"
#include "lluicolor.h"
#include "lluicolortable.h"
#include "llviewercontrol.h" // gSavedSettings
@@ -216,8 +217,7 @@ S32 LLViewerChat::getChatFontSize()
//static
void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
{
- std::string tmpmsg = chat.mText;
-
+ std::string tmpmsg = without_LUA_PREFIX(chat.mText, chat.mIsScript);
if(chat.mChatStyle == CHAT_STYLE_IRC)
{
formated_msg = chat.mFromName + tmpmsg.substr(3);
@@ -227,6 +227,11 @@ void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
formated_msg = tmpmsg;
}
+ if (chat.mIsScript)
+ {
+ formated_msg = LLTrans::getString("ScriptStr") + formated_msg;
+ }
+
}
//static
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 52bc2d9654..d1c773171b 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2600,8 +2600,11 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
BOOL ircstyle = FALSE;
+ auto [message, is_script] = LLStringUtil::withoutPrefix(mesg, LUA_PREFIX);
+ chat.mIsScript = is_script;
+
// Look for IRC-style emotes here so chatbubbles work
- std::string prefix = mesg.substr(0, 4);
+ std::string prefix = message.substr(0, 4);
if (prefix == "/me " || prefix == "/me'")
{
ircstyle = TRUE;
diff --git a/indra/newview/scripts/lua/LLChat.lua b/indra/newview/scripts/lua/LLChat.lua
new file mode 100644
index 0000000000..7db538e837
--- /dev/null
+++ b/indra/newview/scripts/lua/LLChat.lua
@@ -0,0 +1,17 @@
+leap = require 'leap'
+
+local LLChat = {}
+
+function LLChat.sendNearby(msg)
+ leap.send('LLChatBar', {op='sendChat', message=msg})
+end
+
+function LLChat.sendWhisper(msg)
+ leap.send('LLChatBar', {op='sendChat', type='whisper', message=msg})
+end
+
+function LLChat.sendShout(msg)
+ leap.send('LLChatBar', {op='sendChat', type='shout', message=msg})
+end
+
+return LLChat
diff --git a/indra/newview/scripts/lua/test_LLChat.lua b/indra/newview/scripts/lua/test_LLChat.lua
new file mode 100644
index 0000000000..3abaf28e42
--- /dev/null
+++ b/indra/newview/scripts/lua/test_LLChat.lua
@@ -0,0 +1,18 @@
+LLChat = require 'LLChat'
+
+function generateRandomWord(length)
+ local alphabet = "abcdefghijklmnopqrstuvwxyz"
+ local wordTable = {}
+ for i = 1, length do
+ local randomIndex = math.random(1, #alphabet)
+ table.insert(wordTable, alphabet:sub(randomIndex, randomIndex))
+ end
+ return table.concat(wordTable)
+end
+
+local msg = {'AI says:'}
+math.randomseed(os.time())
+for i = 1, math.random(1, 10) do
+ table.insert(msg, generateRandomWord(math.random(1, 8)))
+end
+LLChat.sendNearby(table.concat(msg, ' '))
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index bee58da6b0..76a2660dbb 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -534,7 +534,9 @@ http://secondlife.com/support for help fixing this problem.
<string name="ChangeYourDefaultAnimations">Change your default animations</string>
<string name="ForceSitAvatar">Force your avatar to sit</string>
<string name="ChangeEnvSettings">Change your environment settings</string>
-
+ <string name="ScriptBy" value="Script by "/>
+ <string name="ScriptStr" value="Script: "/>
+
<string name="NotConnected">Not Connected</string>
<string name="AgentNameSubst">(You)</string> <!-- Substitution for agent name -->
<string name="JoinAnExperience"/><!-- intentionally blank -->