diff options
-rw-r--r-- | .github/workflows/build.yaml | 64 | ||||
-rw-r--r-- | indra/llcommon/llstring.h | 62 | ||||
-rw-r--r-- | indra/llcommon/threadpool.cpp | 19 | ||||
-rw-r--r-- | indra/llui/llchat.h | 19 | ||||
-rw-r--r-- | indra/newview/llchathistory.cpp | 38 | ||||
-rw-r--r-- | indra/newview/llchathistory.h | 3 | ||||
-rw-r--r-- | indra/newview/llchatitemscontainerctrl.cpp | 5 | ||||
-rw-r--r-- | indra/newview/llchatitemscontainerctrl.h | 6 | ||||
-rw-r--r-- | indra/newview/llfloaterimnearbychat.cpp | 3 | ||||
-rw-r--r-- | indra/newview/llfloaterimnearbychathandler.cpp | 13 | ||||
-rw-r--r-- | indra/newview/llfloaterimnearbychatlistener.cpp | 35 | ||||
-rw-r--r-- | indra/newview/llfloaterimnearbychatlistener.h | 6 | ||||
-rw-r--r-- | indra/newview/llviewerchat.cpp | 9 | ||||
-rw-r--r-- | indra/newview/llviewermessage.cpp | 5 | ||||
-rw-r--r-- | indra/newview/scripts/lua/LLChat.lua | 17 | ||||
-rw-r--r-- | indra/newview/scripts/lua/test_LLChat.lua | 18 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/strings.xml | 4 |
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 --> |