diff options
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 11 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/rlvcommon.cpp | 37 | ||||
| -rw-r--r-- | indra/newview/rlvcommon.h | 49 | ||||
| -rw-r--r-- | indra/newview/rlvdefines.h | 142 | ||||
| -rw-r--r-- | indra/newview/rlvhandler.cpp | 93 | ||||
| -rw-r--r-- | indra/newview/rlvhandler.h | 8 | ||||
| -rw-r--r-- | indra/newview/rlvhelper.cpp | 231 | ||||
| -rw-r--r-- | indra/newview/rlvhelper.h | 265 | 
9 files changed, 757 insertions, 81 deletions
| diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 29954d8c28..366afda019 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10118,6 +10118,17 @@        <key>Value</key>        <boolean>1</boolean>      </map> +    <key>RLVaExperimentalCommands</key> +    <map> +      <key>Comment</key> +      <string>Enables the experimental command set</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <boolean>1</boolean> +    </map>  	<key>RevokePermsOnStopAnimation</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index bbba964604..1c0ad25a9e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3377,7 +3377,7 @@ LLSD LLAppViewer::getViewerInfo() const      }  #endif -    info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? RlvStrings::getVersionAbout() : "(disabled)"; +    info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? Rlv::Strings::getVersionAbout() : "(disabled)";      info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION)));      // Settings diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index f641d56a85..898df3af42 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -1,5 +1,10 @@  #include "llviewerprecompiledheaders.h" +#include "llagent.h" +#include "llchat.h" +#include "lldbstrings.h"  #include "llversioninfo.h" +#include "llviewerstats.h" +#include "message.h"  #include "rlvdefines.h"  #include "rlvcommon.h" @@ -10,7 +15,7 @@ using namespace Rlv;  // RlvStrings  // -std::string RlvStrings::getVersion(bool wants_legacy) +std::string Strings::getVersion(bool wants_legacy)  {      return llformat("%s viewer v%d.%d.%d (RLVa %d.%d.%d)",          !wants_legacy ? "RestrainedLove" : "RestrainedLife", @@ -18,23 +23,47 @@ std::string RlvStrings::getVersion(bool wants_legacy)          ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch);  } -std::string RlvStrings::getVersionAbout() +std::string Strings::getVersionAbout()  {      return llformat("RLV v%d.%d.%d / RLVa v%d.%d.%d.%d",          SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch,          ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, LLVersionInfo::instance().getBuild());  } -std::string RlvStrings::getVersionNum() +std::string Strings::getVersionNum()  {      return llformat("%d%02d%02d%02d",          SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, SpecVersion::Build);  } -std::string RlvStrings::getVersionImplNum() +std::string Strings::getVersionImplNum()  {      return llformat("%d%02d%02d%02d",          ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, ImplVersion::ImplId);  }  // ============================================================================ +// RlvUtil +// + +bool Util::sendChatReply(S32 nChannel, const std::string& strUTF8Text) +{ +    if (!isValidReplyChannel(nChannel)) +        return false; + +    // Copy/paste from send_chat_from_viewer() +    gMessageSystem->newMessageFast(_PREHASH_ChatFromViewer); +    gMessageSystem->nextBlockFast(_PREHASH_AgentData); +    gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); +    gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); +    gMessageSystem->nextBlockFast(_PREHASH_ChatData); +    gMessageSystem->addStringFast(_PREHASH_Message, utf8str_truncate(strUTF8Text, MAX_MSG_STR_LEN)); +    gMessageSystem->addU8Fast(_PREHASH_Type, CHAT_TYPE_SHOUT); +    gMessageSystem->addS32("Channel", nChannel); +    gAgent.sendReliableMessage(); +    add(LLStatViewer::CHAT_COUNT, 1); + +    return true; +} + +// ============================================================================ diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index 288d48e373..79ac6e1704 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -1,16 +1,41 @@  #pragma once -// ============================================================================ -// RlvStrings -// - -class RlvStrings +namespace Rlv  { -public: -    static std::string        getVersion(bool wants_legacy); -    static std::string        getVersionAbout(); -    static std::string        getVersionImplNum(); -    static std::string        getVersionNum(); -}; +    // ============================================================================ +    // RlvStrings +    // + +    class Strings +    { +    public: +        static std::string        getVersion(bool wants_legacy); +        static std::string        getVersionAbout(); +        static std::string        getVersionImplNum(); +        static std::string        getVersionNum(); +    }; + +    // ============================================================================ +    // RlvUtil +    // + +    namespace Util +    { +        bool isValidReplyChannel(S32 nChannel, bool isLoopback = false); +        bool sendChatReply(S32 nChannel, const std::string& strUTF8Text); +        bool sendChatReply(const std::string& strChannel, const std::string& strUTF8Text); +    }; + +    inline bool Util::isValidReplyChannel(S32 nChannel, bool isLoopback) +    { +        return (nChannel > (!isLoopback ? 0 : -1)) && (CHAT_CHANNEL_DEBUG != nChannel); +    } + +    inline bool Util::sendChatReply(const std::string& strChannel, const std::string& strUTF8Text) +    { +        S32 nChannel; +        return LLStringUtil::convertToS32(strChannel, nChannel) ? sendChatReply(nChannel, strUTF8Text) : false; +    } -// ============================================================================ +    // ============================================================================ +} diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index 6ba2afbc69..0459b59483 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -4,32 +4,14 @@  // Defines  // -namespace Rlv -{ -    // Version of the specification we report -    struct SpecVersion { -        static constexpr S32 Major = 4; -        static constexpr S32 Minor = 0; -        static constexpr S32 Patch = 0; -        static constexpr S32 Build = 0; -    }; - -    // RLVa implementation version -    struct ImplVersion { -        static constexpr S32 Major = 3; -        static constexpr S32 Minor = 0; -        static constexpr S32 Patch = 0; -        static constexpr S32 ImplId = 2; /* Official viewer */ -    }; -} -  // Defining these makes it easier if we ever need to change our tag  #define RLV_WARNS		LL_WARNS("RLV")  #define RLV_INFOS		LL_INFOS("RLV")  #define RLV_DEBUGS		LL_DEBUGS("RLV")  #define RLV_ENDL		LL_ENDL +#define RLV_VERIFY(f)	(f) -#define RLV_RELEASE +#define RLV_DEBUG  #if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG      // Make sure we halt execution on errors      #define RLV_ERRS				LL_ERRS("RLV") @@ -37,18 +19,36 @@ namespace Rlv      #define RLV_ASSERT(f)			if (!(f)) { RLV_ERRS << "ASSERT (" << #f << ")" << RLV_ENDL; }      #define RLV_ASSERT_DBG(f)		RLV_ASSERT(f)  #else +    // Don't halt execution on errors in release +    #define RLV_ERRS				LL_WARNS("RLV")      // We don't want to check assertions in release builds -    #ifndef RLV_RELEASE +    #ifdef RLV_DEBUG          #define RLV_ASSERT(f)		RLV_VERIFY(f)          #define RLV_ASSERT_DBG(f)      #else          #define RLV_ASSERT(f)          #define RLV_ASSERT_DBG(f) -    #endif // RLV_RELEASE +    #endif // RLV_DEBUG  #endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG  namespace Rlv  { +    // Version of the specification we report +    namespace SpecVersion { +        constexpr S32 Major = 4; +        constexpr S32 Minor = 0; +        constexpr S32 Patch = 0; +        constexpr S32 Build = 0; +    }; + +    // RLVa implementation version +    namespace ImplVersion { +        constexpr S32 Major = 3; +        constexpr S32 Minor = 0; +        constexpr S32 Patch = 0; +        constexpr S32 ImplId = 13; +    }; +      namespace Constants      {          constexpr char CmdPrefix = '@'; @@ -61,33 +61,84 @@ namespace Rlv  namespace Rlv  { -    enum class ECmdRet{ -        Unknown           = 0x0000,            // Unknown error (should only be used internally) -        Retained,                              // Command was retained -        Success           = 0x0100,            // Command executed successfully -        SuccessUnset,                          // Command executed successfully (RLV_TYPE_REMOVE for an unrestricted behaviour) -        SuccessDuplicate,                      // Command executed successfully (RLV_TYPE_ADD for an already restricted behaviour) -        SuccessDeprecated,                     // Command executed successfully but has been marked as deprecated -        SuccessDelayed,                        // Command parsed valid but will execute at a later time -        Failed            = 0x0200,            // Command failed (general failure) -        FailedSyntax,                          // Command failed (syntax error) -        FailedOption,                          // Command failed (invalid option) -        FailedParam,                           // Command failed (invalid param) -        FailedLock,                            // Command failed (command is locked by another object) -        FailedDisabled,                        // Command failed (command disabled by user) -        FailedUnknown,                         // Command failed (unknown command) -        FailedNoSharedRoot,                    // Command failed (missing #RLV) -        FailedDeprecated,                      // Command failed (deprecated and no longer supported) -        FailedNoBehaviour,                     // Command failed (force modifier on an object with no active restrictions) -        FailedUnheldBehaviour,                 // Command failed (local modifier on an object that doesn't hold the base behaviour) -        FailedBlocked,                         // Command failed (object is blocked) -        FailedThrottled,                       // Command failed (throttled) -        FailedNoProcessor                      // Command doesn't have a template processor define (legacy code) +    enum class EBehaviour { +        Version = 0, +        VersionNew, +        VersionNum, + +        Count, +        Unknown,      }; +    enum class EBehaviourOptionType +    { +        None,                               // Behaviour takes no parameters +        Exception,                          // Behaviour requires an exception as a parameter +        NoneOrException,                    // Behaviour takes either no parameters or an exception +    }; + +    enum class EParamType { +        Unknown = 0x00, +        Add     = 0x01,                 // <param> == "n"|"add" +        Remove  = 0x02,                 // <param> == "y"|"rem" +        Force   = 0x04,                 // <param> == "force" +        Reply   = 0x08,                 // <param> == <number> +        Clear   = 0x10, +        AddRem  = Add | Remove +    }; + +    enum class ECmdRet { +        Unknown = 0x0000,               // Unknown error (should only be used internally) +        Retained,                       // Command was retained +        Success = 0x0100,               // Command executed successfully +        SuccessUnset,                   // Command executed successfully (RLV_TYPE_REMOVE for an unrestricted behaviour) +        SuccessDuplicate,               // Command executed successfully (RLV_TYPE_ADD for an already restricted behaviour) +        SuccessDeprecated,              // Command executed successfully but has been marked as deprecated +        SuccessDelayed,                 // Command parsed valid but will execute at a later time +        Failed = 0x0200,                // Command failed (general failure) +        FailedSyntax,                   // Command failed (syntax error) +        FailedOption,                   // Command failed (invalid option) +        FailedParam,                    // Command failed (invalid param) +        FailedLock,                     // Command failed (command is locked by another object) +        FailedDisabled,                 // Command failed (command disabled by user) +        FailedUnknown,                  // Command failed (unknown command) +        FailedNoSharedRoot,             // Command failed (missing #RLV) +        FailedDeprecated,               // Command failed (deprecated and no longer supported) +        FailedNoBehaviour,              // Command failed (force modifier on an object with no active restrictions) +        FailedUnheldBehaviour,          // Command failed (local modifier on an object that doesn't hold the base behaviour) +        FailedBlocked,                  // Command failed (object is blocked) +        FailedThrottled,                // Command failed (throttled) +        FailedNoProcessor               // Command doesn't have a template processor define (legacy code) +    }; + +    enum class EExceptionCheck +    { +        Permissive,						// Exception can be set by any object +        Strict,							// Exception must be set by all objects holding the restriction +        Default,						// Permissive or strict will be determined by currently enforced restrictions +    }; + +    // Replace&remove in c++23 +    template <typename E> +    constexpr std::enable_if_t<std::is_enum_v<E> && !std::is_convertible_v<E, int>, std::underlying_type_t<E>> to_underlying(E e) noexcept +    { +        return static_cast<std::underlying_type_t<E>>(e); +    } + +    template <typename E> +    constexpr std::enable_if_t<std::is_enum_v<E> && !std::is_convertible_v<E, int>, bool> has_flag(E value, E flag) noexcept +    { +        return (to_underlying(value) & to_underlying(flag)) != 0; +    } +      constexpr bool isReturnCodeSuccess(ECmdRet eRet)      { -        return (static_cast<uint16_t>(eRet) & static_cast<uint16_t>(ECmdRet::Success)) == static_cast<uint16_t>(ECmdRet::Success); +        return (to_underlying(eRet) & to_underlying(ECmdRet::Success)) == to_underlying(ECmdRet::Success); +    } + +    constexpr bool isReturnCodeFailed(ECmdRet eRet) +    { +        return (to_underlying(eRet) & to_underlying(ECmdRet::Failed)) == to_underlying(ECmdRet::Failed);      }  } @@ -103,6 +154,7 @@ namespace Rlv          constexpr char Debug[] = "RestrainedLoveDebug";          constexpr char DebugHideUnsetDup[] = "RLVaDebugHideUnsetDuplicate"; +        constexpr char EnableExperimentalCommands[] = "RLVaExperimentalCommands";          constexpr char EnableIMQuery[] = "RLVaEnableIMQuery";          constexpr char EnableTempAttach[] = "RLVaEnableTemporaryAttachments";          constexpr char TopLevelMenu[] = "RLVaTopLevelMenu"; diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index bf78a0a38a..3d7f73937f 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -1,4 +1,5 @@  #include "llviewerprecompiledheaders.h" +#include "llagent.h"  #include "llappviewer.h"  #include "llstartup.h"  #include "llviewercontrol.h" @@ -22,11 +23,12 @@ bool RlvHandler::mIsEnabled = false;  bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj)  { +    // *TODO: There's an edge case for temporary attachments when going from enabled -> disabled with restrictions already in place      static LLCachedControl<bool> enable_temp_attach(gSavedSettings, Settings::EnableTempAttach);      static LLCachedControl<bool> show_debug_output(gSavedSettings, Settings::Debug);      static LLCachedControl<bool> hide_unset_dupes(gSavedSettings, Settings::DebugHideUnsetDup); -    if ( message.length() <= 3 || Rlv::Constants::CmdPrefix != message[0] || CHAT_TYPE_OWNER != chat.mChatType || +    if ( message.length() <= 3 || Constants::CmdPrefix != message[0] || CHAT_TYPE_OWNER != chat.mChatType ||           (chatObj && chatObj->isTempAttachment() && !enable_temp_attach()) )      {          return false; @@ -39,7 +41,7 @@ bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, c      boost_tokenizer tokens(message, boost::char_separator<char>(",", "", boost::drop_empty_tokens));      for (const std::string& strCmd : tokens)      { -        ECmdRet eRet = (ECmdRet)processCommand(chat.mFromID, strCmd, true); +        ECmdRet eRet = processCommand(chat.mFromID, strCmd, true);          if ( show_debug_output() &&               (!hide_unset_dupes() || (ECmdRet::SuccessUnset != eRet && ECmdRet::SuccessDuplicate != eRet)) )          { @@ -53,7 +55,44 @@ bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, c  ECmdRet RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCmd, bool fromObj)  { -    return ECmdRet::FailedNoProcessor; +    const RlvCommand rlvCmd(idObj, strCmd); +    return processCommand(std::ref(rlvCmd), fromObj); +} + +ECmdRet RlvHandler::processCommand(std::reference_wrapper<const RlvCommand> rlvCmd, bool fromObj) +{ +    { +        const RlvCommand& rlvCmdTmp = rlvCmd; // Reference to the temporary with limited variable scope since we don't want it to leak below + +        RLV_DEBUGS << "[" << rlvCmdTmp.getObjectID() << "]: " << rlvCmdTmp.asString() << RLV_ENDL; +        if (!rlvCmdTmp.isValid()) +        { +            RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL; +            return ECmdRet::FailedSyntax; +        } +        if (rlvCmdTmp.isBlocked()) +        { +            RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL; +            return ECmdRet::FailedDisabled; +        } +    } + +    ECmdRet eRet = ECmdRet::Unknown; +    switch (rlvCmd.get().getParamType()) +    { +        case EParamType::Reply: +            eRet = rlvCmd.get().processCommand(); +            break; +        case EParamType::Unknown: +        default: +            eRet = ECmdRet::FailedParam; +            break; +    } +    RLV_ASSERT(ECmdRet::Unknown != eRet); + +    RLV_DEBUGS << "\t--> command " << (isReturnCodeSuccess(eRet) ? "succeeded" : "failed") << RLV_ENDL; + +    return eRet;  }  // ============================================================================ @@ -72,7 +111,7 @@ bool RlvHandler::setEnabled(bool enable)      if (enable && canEnable())      { -        RLV_INFOS << "Enabling Restrained Love API support - " << RlvStrings::getVersionAbout() << RLV_ENDL; +        RLV_INFOS << "Enabling Restrained Love API support - " << Strings::getVersionAbout() << RLV_ENDL;          mIsEnabled = true;      } @@ -80,3 +119,49 @@ bool RlvHandler::setEnabled(bool enable)  }  // ============================================================================ +// Command handlers (RLV_TYPE_REPLY) +// + +ECmdRet CommandHandlerBaseImpl<EParamType::Reply>::processCommand(const RlvCommand& rlvCmd, ReplyHandlerFunc* pHandler) +{ +    // Sanity check - <param> should specify a - valid - reply channel +    S32 nChannel; +    if (!LLStringUtil::convertToS32(rlvCmd.getParam(), nChannel) || !Util::isValidReplyChannel(nChannel, rlvCmd.getObjectID() == gAgent.getID())) +        return ECmdRet::FailedParam; + +    std::string strReply; +    ECmdRet eRet = (*pHandler)(rlvCmd, strReply); + +    // If we made it this far then: +    //   - the command was handled successfully so we send off the response +    //   - the command failed but we still send off an - empty - response to keep the issuing script from blocking +    if (nChannel != 0) +    { +        Util::sendChatReply(nChannel, strReply); +    } + +    return eRet; +} + +// Handles: @version=<chnannel> and @versionnew=<channel> +template<> template<> +ECmdRet VersionReplyHandler::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ +    strReply = Strings::getVersion(EBehaviour::Version == rlvCmd.getBehaviourType()); +    return ECmdRet::Success; +} + +// Handles: @versionnum[:impl]=<channel> +template<> template<> +ECmdRet ReplyHandler<EBehaviour::VersionNum>::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ +    if (!rlvCmd.hasOption()) +        strReply = Strings::getVersionNum(); +    else if ("impl" == rlvCmd.getOption()) +        strReply = Strings::getVersionImplNum(); +    else +        return ECmdRet::FailedOption; +    return ECmdRet::Success; +} + +// ============================================================================ diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index 8cf054e98e..a5e91548ef 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -3,7 +3,7 @@  #include "llchat.h"  #include "llsingleton.h" -#include "rlvdefines.h" +#include "rlvhelper.h"  class LLViewerObject; @@ -15,15 +15,17 @@ class RlvHandler : public LLSingleton<RlvHandler>  {      LLSINGLETON_EMPTY_CTOR(RlvHandler); -public:      /* -     * Helper functions +     * Command processing       */  public:      // Command processing helper functions      bool         handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj);      Rlv::ECmdRet processCommand(const LLUUID& idObj, const std::string& stCmd, bool fromObj); +protected: +    Rlv::ECmdRet processCommand(std::reference_wrapper<const RlvCommand> rlvCmdRef, bool fromObj); +public:      // Initialization (deliberately static so they can safely be called in tight loops)      static bool canEnable();      static bool isEnabled() { return mIsEnabled; } diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index ade2b83dd7..c3f9e6f756 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -1,11 +1,242 @@  #include "llviewerprecompiledheaders.h"  #include "lltrans.h" +#include "llviewercontrol.h"  #include "rlvhelper.h" +#include <boost/algorithm/string.hpp> +  using namespace Rlv; +// ============================================================================ +// BehaviourDictionary +// + +BehaviourDictionary::BehaviourDictionary() +{ +    // +    // Restrictions +    // + +    // +    // Reply-only +    // +    addEntry(new ReplyProcessor<EBehaviour::Version, VersionReplyHandler>("version")); +    addEntry(new ReplyProcessor<EBehaviour::VersionNew, VersionReplyHandler>("versionnew")); +    addEntry(new ReplyProcessor<EBehaviour::VersionNum>("versionnum")); + +    // Populate mString2InfoMap (the tuple <behaviour, type> should be unique) +    for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) +    { +        RLV_VERIFY(mString2InfoMap.insert(std::make_pair(std::make_pair(bhvr_info_p->getBehaviour(), static_cast<EParamType>(bhvr_info_p->getParamTypeMask())), bhvr_info_p)).second); +    } + +    // Populate m_Bhvr2InfoMap (there can be multiple entries per ERlvBehaviour) +    for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) +    { +        if ((bhvr_info_p->getParamTypeMask() & to_underlying(EParamType::AddRem)) && !bhvr_info_p->isSynonym()) +        { +#ifdef RLV_DEBUG +            for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(bhvr_info_p->getBehaviourType()), mBhvr2InfoMap.upper_bound(bhvr_info_p->getBehaviourType()))) +            { +                RLV_ASSERT((itBhvr.first != bhvr_info_p->getBehaviourType()) || (itBhvr.second->getBehaviourFlags() != bhvr_info_p->getBehaviourFlags())); +            } +#endif // RLV_DEBUG +            mBhvr2InfoMap.insert(std::pair(bhvr_info_p->getBehaviourType(), bhvr_info_p)); +        } +    } +} + +BehaviourDictionary::~BehaviourDictionary() +{ +    for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) +    { +        delete bhvr_info_p; +    } +    mBhvrInfoList.clear(); +} + +void BehaviourDictionary::addEntry(const BehaviourInfo* entry_p) +{ +    // Filter experimental commands (if disabled) +    static LLCachedControl<bool> sEnableExperimental(gSavedSettings, Settings::EnableExperimentalCommands); +    if (!entry_p || (!sEnableExperimental && entry_p->isExperimental())) +    { +        return; +    } + +    // Sanity check for duplicate entries +#ifndef LL_RELEASE_FOR_DOWNLOAD +    std::for_each(mBhvrInfoList.begin(), mBhvrInfoList.end(), +        [&entry_p](const BehaviourInfo* bhvr_info_p) { +            RLV_ASSERT_DBG((bhvr_info_p->getBehaviour() != entry_p->getBehaviour()) || ((bhvr_info_p->getParamTypeMask() & entry_p->getParamTypeMask()) == 0)); +        }); +#endif // LL_RELEASE_FOR_DOWNLOAD + +    mBhvrInfoList.push_back(entry_p); +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const +{ +    const BehaviourInfo* bhvr_info_p = nullptr; +    for (auto itBhvrLower = mBhvr2InfoMap.lower_bound(eBhvr), itBhvrUpper = mBhvr2InfoMap.upper_bound(eBhvr); +        std::find_if(itBhvrLower, itBhvrUpper, [eParamType](const auto& bhvrEntry) { return bhvrEntry.second->getParamTypeMask() == to_underlying(eParamType); }) != itBhvrUpper; +        ++itBhvrLower) +    { +        if (bhvr_info_p) +            return nullptr; +        bhvr_info_p = itBhvrLower->second; +    } +    return bhvr_info_p; +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p) const +{ +    size_t idxBhvrLastPart = strBhvr.find_last_of('_'); +    std::string strBhvrLastPart((std::string::npos != idxBhvrLastPart) && (idxBhvrLastPart < strBhvr.size()) ? strBhvr.substr(idxBhvrLastPart + 1) : LLStringUtil::null); + +    bool isStrict = (strBhvrLastPart.compare("sec") == 0); +    if (is_strict_p) +        *is_strict_p = isStrict; + +    auto itBhvr = mString2InfoMap.find(std::make_pair((!isStrict) ? strBhvr : strBhvr.substr(0, strBhvr.size() - 4), (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); +    if ((mString2InfoMap.end() == itBhvr) && (!isStrict) && (!strBhvrLastPart.empty()) && (EParamType::Force == eParamType)) +    { +        // No match found but it could still be a local scope modifier +        auto itBhvrMod = mString2InfoMap.find(std::make_pair(strBhvr.substr(0, idxBhvrLastPart), EParamType::AddRem)); +    } + +    return ((itBhvr != mString2InfoMap.end()) && ((!isStrict) || (itBhvr->second->hasStrict()))) ? itBhvr->second : nullptr; +} + +EBehaviour BehaviourDictionary::getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* pisStrict) const +{ +    const BehaviourInfo* bhvr_info_p = getBehaviourInfo(strBhvr, eParamType, pisStrict); +    // Filter out locally scoped modifier commands since they don't actually have a unique behaviour value of their own +    return bhvr_info_p->getBehaviourType(); +} + +bool BehaviourDictionary::getCommands(const std::string& strMatch, EParamType eParamType, std::list<std::string>& cmdList) const +{ +    cmdList.clear(); +    for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) +    { +        if ((bhvr_info_p->getParamTypeMask() & to_underlying(eParamType)) || (EParamType::Unknown == eParamType)) +        { +            std::string strCmd = bhvr_info_p->getBehaviour(); +            if ((std::string::npos != strCmd.find(strMatch)) || (strMatch.empty())) +                cmdList.push_back(strCmd); +            if ((bhvr_info_p->hasStrict()) && ((std::string::npos != strCmd.append("_sec").find(strMatch)) || (strMatch.empty()))) +                cmdList.push_back(strCmd); +        } +    } +    return !cmdList.empty(); +} + +bool BehaviourDictionary::getHasStrict(EBehaviour eBhvr) const +{ +    for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(eBhvr), mBhvr2InfoMap.upper_bound(eBhvr))) +    { +        // Only restrictions can be strict +        if (to_underlying(EParamType::AddRem) != itBhvr.second->getParamTypeMask()) +            continue; +        return itBhvr.second->hasStrict(); +    } +    RLV_ASSERT(false); +    return false; +} + +void BehaviourDictionary::toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBhvrFlag, bool fEnable) +{ +    auto itBhvr = mString2InfoMap.find(std::make_pair(strBhvr, (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); +    if (mString2InfoMap.end() != itBhvr) +    { +        const_cast<BehaviourInfo*>(itBhvr->second)->toggleBehaviourFlag(eBhvrFlag, fEnable); +    } +} + +// ============================================================================ +// RlvCommmand +// + +RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCmd) +    : mObjId(idObj) +{ +    if (parseCommand(strCmd, mBehaviour, mOption, mParam)) +    { +         if ("n" == mParam || "add" == mParam) +             mParamType = EParamType::Add; +         else if ("y" == mParam || "rem" == mParam) +             mParamType = EParamType::Remove; +         else if ("clear" == mBehaviour)                                     // clear is the odd one out so just make it its own type +             mParamType = EParamType::Clear; +         else if ("force" == mParam) +             mParamType = EParamType::Force; +         else if (S32 nTemp; LLStringUtil::convertToS32(mParam, nTemp))  // Assume it's a reply command if we can convert <param> to an S32 +            mParamType = EParamType::Reply; +    } + +    mIsValid = mParamType != EParamType::Unknown; +    if (!mIsValid) +    { +        mOption.clear(); +        mParam.clear(); +        return; +    } + +    mBhvrInfo = BehaviourDictionary::instance().getBehaviourInfo(mBehaviour, mParamType, &mIsStrict); +} + +RlvCommand::RlvCommand(const RlvCommand& rlvCmd, EParamType eParamType) +    : mIsValid(rlvCmd.mIsValid), mObjId(rlvCmd.mObjId), mBehaviour(rlvCmd.mBehaviour), mBhvrInfo(rlvCmd.mBhvrInfo) +    , mParamType( (EParamType::Unknown == eParamType) ? rlvCmd.mParamType : eParamType) +    , mIsStrict(rlvCmd.mIsStrict), mOption(rlvCmd.mOption), mParam(rlvCmd.mParam), mIsRefCounted(rlvCmd.mIsRefCounted) +{ +} + +bool RlvCommand::parseCommand(const std::string& strCmd, std::string& strBhvr, std::string& strOption, std::string& strParam) +{ +    // Format: <behaviour>[:<option>]=<param> +    const size_t idxOption = strCmd.find(':'); +    const size_t idxParam  = strCmd.find('='); + +    // If <behaviour> is missing it's always an improperly formatted command +    // If there's an option, but it comes after <param> it's also invalid +    if ( (idxOption == 0 || idxParam == 0) || +         (idxOption != std::string::npos && idxOption >= idxParam) ) +    { +        return false; +    } + +    strBhvr = strCmd.substr(0, std::string::npos != idxOption ? idxOption : idxParam); +    strOption = strParam = ""; + +    // If <param> is missing it's an improperly formatted command +    if (idxParam == std::string::npos || idxParam + 1 == strCmd.length()) +    { +        // Unless "<behaviour> == "clear" AND (idxOption == 0)" +        // OR <behaviour> == "clear" AND (idxParam != 0) +        if (strBhvr == "clear" && (!idxOption || idxParam)) +            return true; +        return false; +    } + +    if (idxOption != std::string::npos && idxOption + 1 != idxParam) +        strOption = strCmd.substr(idxOption + 1, idxParam - idxOption - 1); +    strParam = strCmd.substr(idxParam + 1); + +    return true; +} + +std::string RlvCommand::asString() const +{ +    // NOTE: @clear=<param> should be represented as clear:<param> +    return mParamType != EParamType::Clear +        ? getBehaviour() + (!mOption.empty() ? ":" + mOption : "") +        : getBehaviour() + (!mParam.empty() ? ":" + mParam : ""); +} +  // =========================================================================  // Various helper classes/timers/functors  // diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h index 89bdc709d5..802b1cbadd 100644 --- a/indra/newview/rlvhelper.h +++ b/indra/newview/rlvhelper.h @@ -3,22 +3,263 @@  #include "rlvdefines.h"  // ============================================================================ -// Various helper classes/timers/functors +// Forward declarations  // +class RlvCommand; + +// ============================================================================ +  namespace Rlv  { -	struct CommandDbgOut -	{ -		CommandDbgOut(const std::string& orig_cmd) : mOrigCmd(orig_cmd) {} -		void add(std::string strCmd, ECmdRet eRet); -		std::string get() const; -		static std::string getDebugVerbFromReturnCode(ECmdRet eRet); -		static std::string getReturnCodeString(ECmdRet eRet); -	private: -		std::string mOrigCmd; -		std::map<ECmdRet, std::string> mCommandResults; -	}; +    // ============================================================================ +    // BehaviourInfo class - Generic behaviour descriptor (used by restrictions, reply and force commands) +    // + +    class BehaviourInfo +    { +    public: +        enum EBehaviourFlags : uint32_t +        { +            // General behaviour flags +            Strict = 0x0001,				// Behaviour has a "_sec" version +            Synonym = 0x0002,				// Behaviour is a synonym of another +            Extended = 0x0004,				// Behaviour is part of the RLVa extended command set +            Experimental = 0x0008,			// Behaviour is part of the RLVa experimental command set +            Blocked = 0x0010,				// Behaviour is blocked +            Deprecated = 0x0020,			// Behaviour is deprecated +            MaskGeneral = 0x0FFF, + +            // Force-wear specific flags +            ForceWear_WearReplace = 0x0001 << 16, +            ForceWear_WearAdd = 0x0002 << 16, +            ForceWear_WearRemove = 0x0004 << 16, +            ForceWear_Node = 0x0010 << 16, +            ForceWear_Subtree = 0x0020 << 16, +            ForceWear_ContextNone = 0x0100 << 16, +            ForceWear_ContextObject = 0x0200 << 16, +            MaskForceWear = 0xFFFFu << 16 +        }; + +        BehaviourInfo(const std::string& strBhvr, EBehaviour eBhvr, EParamType maskParamType, std::underlying_type_t<EBehaviourFlags> nBhvrFlags = 0) +            : mBhvr(strBhvr), mBhvrType(eBhvr), mBhvrFlags(nBhvrFlags), mMaskParamType(to_underlying(maskParamType)) {} +        virtual ~BehaviourInfo() {} + +        const std::string&    getBehaviour() const { return mBhvr; } +        EBehaviour            getBehaviourType() const { return mBhvrType; } +        std::underlying_type_t<EBehaviourFlags> getBehaviourFlags() const { return mBhvrFlags; } +        std::underlying_type_t<EParamType> getParamTypeMask() const { return mMaskParamType; } +        bool                  hasStrict() const { return mBhvrFlags & Strict; } +        bool                  isBlocked() const { return mBhvrFlags & Blocked; } +        bool                  isExperimental() const { return mBhvrFlags & Experimental; } +        bool                  isExtended() const { return mBhvrFlags & Extended; } +        bool                  isSynonym() const { return mBhvrFlags & Synonym; } +        void                  toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable); + +        virtual ECmdRet  processCommand(const RlvCommand& rlvCmd) const { return ECmdRet::FailedNoProcessor; } + +    protected: +        std::string mBhvr; +        EBehaviour mBhvrType; +        std::underlying_type_t<EBehaviourFlags> mBhvrFlags; +        std::underlying_type_t<EParamType> mMaskParamType; +    }; + +    inline void BehaviourInfo::toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable) +    { +        if (fEnable) +            mBhvrFlags |= eBhvrFlag; +        else +            mBhvrFlags &= ~eBhvrFlag; +    } + +    // ============================================================================ +    // BehaviourDictionary and related classes +    // + +    class BehaviourDictionary : public LLSingleton<BehaviourDictionary> +    { +        LLSINGLETON(BehaviourDictionary); +    protected: +        ~BehaviourDictionary() override; +    public: +        void addEntry(const BehaviourInfo* entry_p); + +        /* +         * General helper functions +         */ +    public: +        EBehaviour           getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p = nullptr) const; +        const BehaviourInfo* getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const; +        const BehaviourInfo* getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p = nullptr) const; +        bool                 getCommands(const std::string& strMatch, EParamType eParamType, std::list<std::string>& cmdList) const; +        bool                 getHasStrict(EBehaviour eBhvr) const; +        void                 toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBvhrFlag, bool fEnable); + +        /* +         * Member variables +         */ +    protected: +        std::list<const BehaviourInfo*> mBhvrInfoList; +        std::map<std::pair<std::string, EParamType>, const BehaviourInfo*> mString2InfoMap; +        std::multimap<EBehaviour, const BehaviourInfo*> mBhvr2InfoMap; +    }; + +    // ============================================================================ +    // CommandHandler and related classes +    // + +    typedef ECmdRet(BhvrHandlerFunc)(const RlvCommand&, bool&); +    typedef void(BhvrToggleHandlerFunc)(EBehaviour, bool); +    typedef ECmdRet(ForceHandlerFunc)(const RlvCommand&); +    typedef ECmdRet(ReplyHandlerFunc)(const RlvCommand&, std::string&); + +    // +    // CommandHandlerBaseImpl - Base implementation for each command type (the old process(AddRem|Force|Reply)Command functions) +    // +    template<EParamType paramType> struct CommandHandlerBaseImpl; +    template<> struct CommandHandlerBaseImpl<EParamType::AddRem> { static ECmdRet processCommand(const RlvCommand&, BhvrHandlerFunc*, BhvrToggleHandlerFunc* = nullptr); }; +    template<> struct CommandHandlerBaseImpl<EParamType::Force>  { static ECmdRet processCommand(const RlvCommand&, ForceHandlerFunc*); }; +    template<> struct CommandHandlerBaseImpl<EParamType::Reply>  { static ECmdRet processCommand(const RlvCommand&, ReplyHandlerFunc*); }; + +    // +    // CommandHandler - The actual command handler (Note that a handler is more general than a processor; a handler can - for instance - be used by multiple processors) +    // + +    template <EParamType templParamType, EBehaviour templBhvr> +    struct CommandHandler +    { +        template<typename = typename std::enable_if<templParamType == EParamType::AddRem>::type> static ECmdRet onCommand(const RlvCommand&, bool&); +        template<typename = typename std::enable_if<templParamType == EParamType::AddRem>::type> static void onCommandToggle(EBehaviour, bool); +        template<typename = typename std::enable_if<templParamType == EParamType::Force>::type>  static ECmdRet onCommand(const RlvCommand&); +        template<typename = typename std::enable_if<templParamType == EParamType::Reply>::type>  static ECmdRet onCommand(const RlvCommand&, std::string&); +    }; + +    // Aliases to improve readability in definitions +    template<EBehaviour templBhvr> using BehaviourHandler = CommandHandler<EParamType::AddRem, templBhvr>; +    template<EBehaviour templBhvr> using BehaviourToggleHandler = BehaviourHandler<templBhvr>; +    template<EBehaviour templBhvr> using ForceHandler = CommandHandler<EParamType::Force, templBhvr>; +    template<EBehaviour templBhvr> using ReplyHandler = CommandHandler<EParamType::Reply, templBhvr>; + +	// List of shared handlers +    using VersionReplyHandler = ReplyHandler<EBehaviour::Version>;				// Shared between @version and @versionnew + +    // +    // CommandProcessor - Templated glue class that brings BehaviourInfo, CommandHandlerBaseImpl and CommandHandler together +    // +    template <EParamType templParamType, EBehaviour templBhvr, typename handlerImpl = CommandHandler<templParamType, templBhvr>, typename baseImpl = CommandHandlerBaseImpl<templParamType>> +    class CommandProcessor : public BehaviourInfo +    { +    public: +        // Default constructor used by behaviour specializations +        template<typename = typename std::enable_if<templBhvr != EBehaviour::Unknown>::type> +        CommandProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, templBhvr, templParamType, nBhvrFlags) {} + +        // Constructor used when we don't want to specialize on behaviour (see BehaviourGenericProcessor) +        template<typename = typename std::enable_if<templBhvr == EBehaviour::Unknown>::type> +        CommandProcessor(const std::string& strBhvr, EBehaviour eBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, templParamType, nBhvrFlags) {} + +        ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return baseImpl::processCommand(rlvCmd, &handlerImpl::onCommand); } +    }; + +    // Aliases to improve readability in definitions +    template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::AddRem, templBhvr>> using BehaviourProcessor = CommandProcessor<EParamType::AddRem, templBhvr, handlerImpl>; +    template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::Force, templBhvr>> using ForceProcessor = CommandProcessor<EParamType::Force, templBhvr, handlerImpl>; +    template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::Reply, templBhvr>> using ReplyProcessor = CommandProcessor<EParamType::Reply, templBhvr, handlerImpl>; + +    // Provides pre-defined generic implementations of basic behaviours (template voodoo - see original commit for something that still made sense) +    template<EBehaviourOptionType templOptionType> struct BehaviourGenericHandler { static ECmdRet onCommand(const RlvCommand& rlvCmd, bool& fRefCount); }; +    template<EBehaviourOptionType templOptionType> using BehaviourGenericProcessor = BehaviourProcessor<EBehaviour::Unknown, BehaviourGenericHandler<templOptionType>>; +    template<EBehaviourOptionType templOptionType> struct ForceGenericHandler { static ECmdRet onCommand(const RlvCommand& rlvCmd); }; +    template<EBehaviourOptionType templOptionType> using ForceGenericProcessor = ForceProcessor<EBehaviour::Unknown, ForceGenericHandler<templOptionType>>; + +    // ============================================================================ +    // BehaviourProcessor and related classes - Handles add/rem comamnds aka "restrictions) +    // + +    template <EBehaviour eBhvr, typename handlerImpl = BehaviourHandler<eBhvr>, typename toggleHandlerImpl = BehaviourToggleHandler<eBhvr>> +    class BehaviourToggleProcessor : public BehaviourInfo +    { +    public: +        BehaviourToggleProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, EParamType::AddRem, nBhvrFlags) {} +        ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return CommandHandlerBaseImpl<EParamType::AddRem>::processCommand(rlvCmd, &handlerImpl::onCommand, &toggleHandlerImpl::onCommandToggle); } +    }; +    template <EBehaviour eBhvr, EBehaviourOptionType optionType, typename toggleHandlerImpl = BehaviourToggleHandler<eBhvr>> using RlvBehaviourGenericToggleProcessor = BehaviourToggleProcessor<eBhvr, BehaviourGenericHandler<optionType>, toggleHandlerImpl>; + +    // ============================================================================ +    // Various helper classes/timers/functors +    // + +    struct CommandDbgOut +    { +        CommandDbgOut(const std::string& orig_cmd) : mOrigCmd(orig_cmd) {} +        void add(std::string strCmd, ECmdRet eRet); +        std::string get() const; +        static std::string getDebugVerbFromReturnCode(ECmdRet eRet); +        static std::string getReturnCodeString(ECmdRet eRet); +    private: +        std::string mOrigCmd; +        std::map<ECmdRet, std::string> mCommandResults; +    };  }  // ============================================================================ +// RlvCommand +// + +class RlvCommand +{ +public: +    explicit RlvCommand(const LLUUID& idObj, const std::string& strCmd); +    RlvCommand(const RlvCommand& rlvCmd, Rlv::EParamType eParamType = Rlv::EParamType::Unknown); + +    /* +     * Member functions +     */ +public: +    std::string        asString() const; +    const std::string& getBehaviour() const { return mBehaviour; } +    const Rlv::BehaviourInfo* getBehaviourInfo() const { return mBhvrInfo; } +    Rlv::EBehaviour    getBehaviourType() const { return (mBhvrInfo) ? mBhvrInfo->getBehaviourType() : Rlv::EBehaviour::Unknown; } +    U32                getBehaviourFlags() const { return (mBhvrInfo) ? mBhvrInfo->getBehaviourFlags() : 0; } +    const LLUUID&      getObjectID() const { return mObjId; } +    const std::string& getOption() const { return mOption; } +    const std::string& getParam() const { return mParam; } +    Rlv::EParamType    getParamType() const { return mParamType; } +    bool               hasOption() const { return !mOption.empty(); } +    bool               isBlocked() const { return (mBhvrInfo) ? mBhvrInfo->isBlocked() : false; } +    bool               isRefCounted() const { return mIsRefCounted; } +    bool               isStrict() const { return mIsStrict; } +    bool               isValid() const { return mIsValid; } +    Rlv::ECmdRet       processCommand() const { return (mBhvrInfo) ? mBhvrInfo->processCommand(*this) : Rlv::ECmdRet::FailedNoProcessor; } + +protected: +    static bool parseCommand(const std::string& strCommand, std::string& strBehaviour, std::string& strOption, std::string& strParam); +    bool               markRefCounted() const { return mIsRefCounted = true; } + +    /* +     * Operators +     */ +public: +    bool operator ==(const RlvCommand&) const; + +    /* +     * Member variables +     */ +protected: +    bool                    mIsValid = false; +    LLUUID                  mObjId; +    std::string             mBehaviour; +    const Rlv::BehaviourInfo* mBhvrInfo = nullptr; +    Rlv::EParamType         mParamType = Rlv::EParamType::Unknown; +    bool                    mIsStrict = false; +    std::string             mOption; +    std::string             mParam; +    mutable bool            mIsRefCounted = false; + +    friend class RlvHandler; +    friend class RlvObject; +    template<Rlv::EParamType> friend struct Rlv::CommandHandlerBaseImpl; +}; + +// ============================================================================ | 
