From db8916454457e3e4856fddcbbafe66b3766e4ffa Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Tue, 3 Sep 2024 18:14:06 +0200 Subject: Add the RLVa console --- indra/newview/CMakeLists.txt | 2 + indra/newview/llviewerfloaterreg.cpp | 2 + indra/newview/rlvdefines.h | 1 + indra/newview/rlvfloaters.cpp | 95 ++++++++++++++++++++++ indra/newview/rlvfloaters.h | 40 +++++++++ indra/newview/rlvhandler.cpp | 6 +- indra/newview/rlvhandler.h | 15 ++++ indra/newview/rlvhelper.cpp | 38 ++++----- indra/newview/rlvhelper.h | 5 +- .../skins/default/xui/en/floater_rlv_console.xml | 73 +++++++++++++++++ indra/newview/skins/default/xui/en/strings.xml | 5 ++ 11 files changed, 255 insertions(+), 27 deletions(-) create mode 100644 indra/newview/rlvfloaters.cpp create mode 100644 indra/newview/rlvfloaters.h create mode 100644 indra/newview/skins/default/xui/en/floater_rlv_console.xml diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a7bbadfd86..e3f675cb8e 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -728,6 +728,7 @@ set(viewer_SOURCE_FILES pipeline.cpp rlvactions.cpp rlvcommon.cpp + rlvfloaters.cpp rlvhelper.cpp rlvhandler.cpp ) @@ -1393,6 +1394,7 @@ set(viewer_HEADER_FILES rlvdefines.h rlvactions.h rlvcommon.h + rlvfloaters.h rlvhelper.h rlvhandler.h roles_constants.h diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 9bdd246129..e41d8c44fb 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -175,6 +175,7 @@ #include "llpreviewtexture.h" #include "llscriptfloater.h" #include "llsyswellwindow.h" +#include "rlvfloaters.h" // *NOTE: Please add files in alphabetical order to keep merges easy. @@ -480,6 +481,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("region_debug_console", "floater_region_debug_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("region_info", "floater_region_info.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("region_restarting", "floater_region_restarting.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("rlv_console", "floater_rlv_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("script_debug", "floater_script_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("script_debug_output", "floater_script_debug_panel.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index c36512007b..88dffa1127 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -52,6 +52,7 @@ namespace Rlv namespace Constants { constexpr char CmdPrefix = '@'; + constexpr char ConsolePrompt[] = "> "; constexpr std::string_view OptionSeparator = ";"; } } diff --git a/indra/newview/rlvfloaters.cpp b/indra/newview/rlvfloaters.cpp new file mode 100644 index 0000000000..8d107b2540 --- /dev/null +++ b/indra/newview/rlvfloaters.cpp @@ -0,0 +1,95 @@ +#include "llviewerprecompiledheaders.h" + +#include "llagentdata.h" +#include "llchatentry.h" +#include "lltexteditor.h" +#include "lltrans.h" +#include "llvoavatarself.h" + +#include "rlvfloaters.h" +#include "rlvhandler.h" + +using namespace Rlv; + +// ============================================================================ +// FloaterConsole +// + +bool FloaterConsole::postBuild() +{ + mInputEdit = getChild("console_input"); + mInputEdit->setCommitCallback(std::bind(&FloaterConsole::onInput, this)); + mInputEdit->setTextExpandedCallback(std::bind(&FloaterConsole::reshapeLayoutPanel, this)); + mInputEdit->setFocus(true); + mInputEdit->setCommitOnFocusLost(false); + + mInputPanel = getChild("input_panel"); + mInputEditPad = mInputPanel->getRect().getHeight() - mInputEdit->getRect().getHeight(); + + mOutputText = getChild("console_output"); + mOutputText->appendText(Constants::ConsolePrompt, false); + + if (RlvHandler::isEnabled()) + { + mCommandOutputConn = RlvHandler::instance().setCommandOutputCallback([this](const RlvCommand& rlvCmd, S32, const std::string strText) + { + if (rlvCmd.getObjectID() == gAgentID) + { + mOutputText->appendText(rlvCmd.getBehaviour() + ": ", true); + mOutputText->appendText(strText, false); + } + }); + } + + return true; +} + +void FloaterConsole::onClose(bool fQuitting) +{ + if (RlvHandler::isEnabled()) + { + RlvHandler::instance().processCommand(gAgentID, "clear", true); + } +} + +void FloaterConsole::onInput() +{ + if (!isAgentAvatarValid()) + { + return; + } + + std::string strText = mInputEdit->getText(); + LLStringUtil::trim(strText); + + mOutputText->appendText(strText, false); + mInputEdit->setText(LLStringUtil::null); + + if (!RlvHandler::isEnabled()) + { + mOutputText->appendText(LLTrans::getString("RlvConsoleDisable"), true); + } + else if (strText.length() <= 3 || Constants::CmdPrefix != strText[0]) + { + mOutputText->appendText(LLTrans::getString("RlvConsoleInvalidCmd"), true); + } + else + { + LLChat chat; + chat.mFromID = gAgentID; + chat.mChatType = CHAT_TYPE_OWNER; + + RlvHandler::instance().handleSimulatorChat(strText, chat, gAgentAvatarp); + + mOutputText->appendText(strText, true); + } + + mOutputText->appendText(Constants::ConsolePrompt, true); +} + +void FloaterConsole::reshapeLayoutPanel() +{ + mInputPanel->reshape(mInputPanel->getRect().getWidth(), mInputEdit->getRect().getHeight() + mInputEditPad, false); +} + +// ============================================================================ diff --git a/indra/newview/rlvfloaters.h b/indra/newview/rlvfloaters.h new file mode 100644 index 0000000000..a599bb051a --- /dev/null +++ b/indra/newview/rlvfloaters.h @@ -0,0 +1,40 @@ +#pragma once + +#include "llfloater.h" + +#include "rlvdefines.h" + +class LLChatEntry; +class LLLayoutPanel; +class LLTextEditor; +class RlvCommand; +class RlvHandler; + +namespace Rlv +{ + // ============================================================================ + // FloaterConsole - debug console to allow command execution without the need for a script + // + + class FloaterConsole : public LLFloater + { + friend class LLFloaterReg; + FloaterConsole(const LLSD& sdKey) : LLFloater(sdKey) {} + + public: + bool postBuild() override; + void onClose(bool fQuitting) override; + protected: + void onInput(); + void reshapeLayoutPanel(); + + private: + boost::signals2::scoped_connection mCommandOutputConn; + int mInputEditPad = 0; + LLLayoutPanel* mInputPanel = nullptr; + LLChatEntry* mInputEdit = nullptr; + LLTextEditor* mOutputText = nullptr; + }; + + // ============================================================================ +}; diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index 8b2620cf48..0a0da0e25d 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -1,6 +1,5 @@ #include "llviewerprecompiledheaders.h" #include "llagent.h" -#include "llappviewer.h" #include "llstartup.h" #include "llviewercontrol.h" #include "llviewerobject.h" @@ -38,7 +37,7 @@ bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, c message.erase(0, 1); LLStringUtil::toLower(message); - CommandDbgOut cmdDbgOut(message); + CommandDbgOut cmdDbgOut(message, chatObj->getID() == gAgentID); boost_tokenizer tokens(message, boost::char_separator(",", "", boost::drop_empty_tokens)); for (const std::string& strCmd : tokens) @@ -128,7 +127,7 @@ ECmdRet CommandHandlerBaseImpl::processCommand(const RlvComma { // Sanity check - should specify a - valid - reply channel S32 nChannel; - if (!LLStringUtil::convertToS32(rlvCmd.getParam(), nChannel) || !Util::isValidReplyChannel(nChannel, rlvCmd.getObjectID() == gAgent.getID())) + if (!LLStringUtil::convertToS32(rlvCmd.getParam(), nChannel) || !Util::isValidReplyChannel(nChannel, rlvCmd.getObjectID() == gAgentID)) return ECmdRet::FailedParam; std::string strReply; @@ -141,6 +140,7 @@ ECmdRet CommandHandlerBaseImpl::processCommand(const RlvComma { Util::sendChatReply(nChannel, strReply); } + RlvHandler::instance().mOnCommandOutput(rlvCmd, nChannel, strReply); return eRet; } diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index a5e91548ef..7fa4da767a 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -13,6 +13,8 @@ class LLViewerObject; class RlvHandler : public LLSingleton { + template friend struct Rlv::CommandHandlerBaseImpl; + LLSINGLETON_EMPTY_CTOR(RlvHandler); /* @@ -25,12 +27,25 @@ public: protected: Rlv::ECmdRet processCommand(std::reference_wrapper rlvCmdRef, bool fromObj); + /* + * Helper functions + */ public: // Initialization (deliberately static so they can safely be called in tight loops) static bool canEnable(); static bool isEnabled() { return mIsEnabled; } static bool setEnabled(bool enable); + /* + * Event handling + */ +public: + // The command output signal is triggered whenever a command produces channel or debug output + using command_output_signal_t = boost::signals2::signal; + boost::signals2::connection setCommandOutputCallback(const command_output_signal_t::slot_type& cb) { return mOnCommandOutput.connect(cb); } + +protected: + command_output_signal_t mOnCommandOutput; private: static bool mIsEnabled; }; diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index aad615d813..ecb76a1db3 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -250,21 +250,13 @@ namespace Rlv void CommandDbgOut::add(std::string strCmd, ECmdRet eRet) { - ECmdRet resultBucket; - - // Successful and retained commands are added as-is - if (isReturnCodeSuccess(eRet)) - resultBucket = ECmdRet::Success; - else if (ECmdRet::Retained == eRet) - resultBucket = ECmdRet::Retained; - else - { - // Failed commands get the failure reason appended to help troubleshooting - resultBucket = ECmdRet::Failed; - strCmd.append(llformat(" (%s)", getReturnCodeString(eRet).c_str())); - } + const std::string strSuffix = getReturnCodeString(eRet); + if (!strSuffix.empty()) + strCmd.append(llformat(" (%s)", strSuffix.c_str())); + else if (mForConsole) + return; // Only show console feedback on successful commands when there's an informational notice - std::string& strResult = mCommandResults[resultBucket]; + std::string& strResult = mCommandResults[isReturnCodeSuccess(eRet) ? ECmdRet::Success : (ECmdRet::Retained == eRet ? ECmdRet::Retained : ECmdRet::Failed)]; if (!strResult.empty()) strResult.append(", "); strResult.append(strCmd); @@ -273,23 +265,25 @@ namespace Rlv std::string CommandDbgOut::get() const { std::ostringstream result; - if (1 == mCommandResults.size()) + if (1 == mCommandResults.size() && !mForConsole) { auto it = mCommandResults.begin(); - result << " " << getDebugVerbFromReturnCode(it->first) << ": @" << it->second;; + result << " " << getDebugVerbFromReturnCode(it->first) << ": @" << it->second; } - else if (mCommandResults.size() > 1) + else if (!mCommandResults.empty()) { auto appendResult = [&](ECmdRet eRet, const std::string& name) { auto it = mCommandResults.find(eRet); if (it == mCommandResults.end()) return; - result << "\n - " << LLTrans::getString(name) << ": @" << it->second; + if (!mForConsole) result << "\n - "; + result << LLTrans::getString(name) << ": @" << it->second; }; - result << ": @" << mOrigCmd; - appendResult(ECmdRet::Success, "RlvDebugExecuted"); - appendResult(ECmdRet::Failed, "RlvDebugFailed"); - appendResult(ECmdRet::Retained, "RlvDebugRetained"); + if (!mForConsole) + result << ": @" << mOrigCmd; + appendResult(ECmdRet::Success, !mForConsole ? "RlvDebugExecuted" : "RlvConsoleExecuted"); + appendResult(ECmdRet::Failed, !mForConsole ? "RlvDebugFailed" : "RlvConsoleFailed"); + appendResult(ECmdRet::Retained, !mForConsole ? "RlvDebugRetained" : "RlvConsoleRetained"); } return result.str(); diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h index 802b1cbadd..644cd0ee22 100644 --- a/indra/newview/rlvhelper.h +++ b/indra/newview/rlvhelper.h @@ -141,7 +141,7 @@ namespace Rlv template using ForceHandler = CommandHandler; template using ReplyHandler = CommandHandler; - // List of shared handlers + // List of shared handlers using VersionReplyHandler = ReplyHandler; // Shared between @version and @versionnew // @@ -192,7 +192,7 @@ namespace Rlv struct CommandDbgOut { - CommandDbgOut(const std::string& orig_cmd) : mOrigCmd(orig_cmd) {} + CommandDbgOut(const std::string& orig_cmd, bool for_console) : mOrigCmd(orig_cmd), mForConsole(for_console) {} void add(std::string strCmd, ECmdRet eRet); std::string get() const; static std::string getDebugVerbFromReturnCode(ECmdRet eRet); @@ -200,6 +200,7 @@ namespace Rlv private: std::string mOrigCmd; std::map mCommandResults; + bool mForConsole = false; }; } diff --git a/indra/newview/skins/default/xui/en/floater_rlv_console.xml b/indra/newview/skins/default/xui/en/floater_rlv_console.xml new file mode 100644 index 0000000000..928d50cb41 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_rlv_console.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 39a663298a..cebe1ff6c3 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -4377,6 +4377,11 @@ and report the problem. + RLVa is disabled + Invalid command + INFO + ERR + RET executed failed retained -- cgit v1.2.3