diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index 72dd6e4c7dd72dba8de21893556aa171f089f0e3..fd51bfac44f82d3ca8204dbc141a6505a5bb9ac6 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -1782,7 +1782,7 @@ ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_MODIFIER>::onCommand(const RlvC // There should be an option and it should specify a valid modifier (RlvBehaviourModifier performs the appropriate type checks) RlvBehaviourModifier* pBhvrModifier = RlvBehaviourDictionary::instance().getModifierFromBehaviour(rlvCmd.getBehaviourType()); RlvBehaviourModifierValue modValue; - if ( (!rlvCmd.hasOption()) || (!pBhvrModifier) || (!pBhvrModifier->convertOptionValue(rlvCmd.getOption(), modValue)) ) + if ( (!rlvCmd.hasOption()) || (!pBhvrModifier) || (!pBhvrModifier->convertOptionValue(rlvCmd.getOption(), pBhvrModifier->getType(), modValue)) ) return RLV_RET_FAILED_OPTION; // HACK-RLVa: reference counting doesn't happen until control returns to our caller but the modifier callbacks will happen now so we need to adjust the reference counts here @@ -1803,15 +1803,22 @@ ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_MODIFIER>::onCommand(const RlvC return RLV_RET_SUCCESS; } -// Handles: @bhvr[:<modifier>]=n|y +// Handles: @bhvr=n, @bhvr:<global modifier>=n|y and @bhvr:<local modifier>=force template<> ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_NONE_OR_MODIFIER>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount) { - // If there is an option then it should specify a valid modifier (and reference count) - if (rlvCmd.hasOption()) + if ( (rlvCmd.getParamType() & RLV_TYPE_ADDREM) && (rlvCmd.hasOption()) ) + { + // @bhvr:<global modifier>=n|y : if there is an option then it should specify a valid global modifier and if so we reference count return RlvBehaviourGenericHandler<RLV_OPTION_MODIFIER>::onCommand(rlvCmd, fRefCount); + } + else if (rlvCmd.getParamType() == RLV_TYPE_FORCE) + { + // @bhvr:<local modifier>=force : local modifiers hide behind their primary behaviour which knows how to handle them + return rlvCmd.getBehaviourInfo()->processModifier(rlvCmd); + } - // Add the default option on an empty modifier if needed + // @bhvr=n : add the default option on an empty modifier if needed RlvBehaviourModifier* pBhvrModifier = RlvBehaviourDictionary::instance().getModifierFromBehaviour(rlvCmd.getBehaviourType()); if ( (pBhvrModifier) && (pBhvrModifier->getAddDefault()) ) { @@ -2622,7 +2629,7 @@ ERlvCmdRet RlvForceGenericHandler<RLV_OPTION_MODIFIER>::onCommand(const RlvComma // There should be an option and it should specify a valid modifier (RlvBehaviourModifier performs the appropriate type checks) RlvBehaviourModifier* pBhvrModifier = RlvBehaviourDictionary::instance().getModifierFromBehaviour(rlvCmd.getBehaviourType()); RlvBehaviourModifierValue modValue; - if ( (!rlvCmd.hasOption()) || (!pBhvrModifier) || (!pBhvrModifier->convertOptionValue(rlvCmd.getOption(), modValue)) ) + if ( (!rlvCmd.hasOption()) || (!pBhvrModifier) || (!pBhvrModifier->convertOptionValue(rlvCmd.getOption(), pBhvrModifier->getType(), modValue)) ) return RLV_RET_FAILED_OPTION; pBhvrModifier->setValue(modValue, rlvCmd.getObjectID()); diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index b08875fca55423127ce76573f81cfe2d49e2e711..920bc31d222e523041855aa142c3b85d695666ea 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -394,20 +394,36 @@ void RlvBehaviourDictionary::clearModifiers(const LLUUID& idRlvObj) } } -const RlvBehaviourInfo* RlvBehaviourDictionary::getBehaviourInfo(const std::string& strBhvr, ERlvParamType eParamType, bool* pfStrict) const +const RlvBehaviourInfo* RlvBehaviourDictionary::getBehaviourInfo(const std::string& strBhvr, ERlvParamType eParamType, bool* pfStrict, ERlvBehaviourModifier* peBhvrModifier) const { - bool fStrict = boost::algorithm::ends_with(strBhvr, "_sec"); + size_t idxBhvrLastPart = strBhvr.find_last_of('_'); + std::string strBhvrLastPart((std::string::npos != idxBhvrLastPart) && (idxBhvrLastPart < strBhvr.size()) ? strBhvr.substr(idxBhvrLastPart + 1) : LLStringUtil::null); + + bool fStrict = (strBhvrLastPart.compare("sec") == 0); if (pfStrict) *pfStrict = fStrict; + ERlvBehaviourModifier eBhvrModifier = RLV_MODIFIER_UNKNOWN; rlv_string2info_map_t::const_iterator itBhvr = m_String2InfoMap.find(std::make_pair( (!fStrict) ? strBhvr : strBhvr.substr(0, strBhvr.size() - 4), (eParamType & RLV_TYPE_ADDREM) ? RLV_TYPE_ADDREM : eParamType)); - return ( (itBhvr != m_String2InfoMap.end()) && ((!fStrict) || (itBhvr->second->hasStrict())) ) ? itBhvr->second : NULL; + if ( (m_String2InfoMap.end() == itBhvr) && (!fStrict) && (!strBhvrLastPart.empty()) && (RLV_TYPE_FORCE == eParamType) ) + { + // No match found but it could still be a local scope modifier + auto itBhvrMod = m_String2InfoMap.find(std::make_pair(strBhvr.substr(0, idxBhvrLastPart), RLV_TYPE_ADDREM)); + if ( (m_String2InfoMap.end() != itBhvrMod) && (eBhvrModifier = itBhvrMod->second->lookupBehaviourModifier(strBhvrLastPart)) != RLV_MODIFIER_UNKNOWN) + itBhvr = itBhvrMod; + } + + if (peBhvrModifier) + *peBhvrModifier = eBhvrModifier; + return ( (itBhvr != m_String2InfoMap.end()) && ((!fStrict) || (itBhvr->second->hasStrict())) ) ? itBhvr->second : nullptr; } ERlvBehaviour RlvBehaviourDictionary::getBehaviourFromString(const std::string& strBhvr, ERlvParamType eParamType, bool* pfStrict) const { - const RlvBehaviourInfo* pBhvrInfo = getBehaviourInfo(strBhvr, eParamType, pfStrict); - return (pBhvrInfo) ? pBhvrInfo->getBehaviourType() : RLV_BHVR_UNKNOWN; + ERlvBehaviourModifier eBhvrModifier; + const RlvBehaviourInfo* pBhvrInfo = getBehaviourInfo(strBhvr, eParamType, pfStrict, &eBhvrModifier); + // Filter out locally scoped modifier commands since they don't actually have a unique behaviour value of their own + return (pBhvrInfo && RLV_MODIFIER_UNKNOWN != eBhvrModifier) ? pBhvrInfo->getBehaviourType() : RLV_BHVR_UNKNOWN; } bool RlvBehaviourDictionary::getCommands(const std::string& strMatch, ERlvParamType eParamType, std::list<std::string>& cmdList) const @@ -456,6 +472,42 @@ void RlvBehaviourDictionary::toggleBehaviourFlag(const std::string& strBhvr, ERl } } +// ============================================================================ +// RlvBehaviourInfo +// + +// virtual +ERlvCmdRet RlvBehaviourInfo::processModifier(const RlvCommand& rlvCmd) const +{ + // The object should be holding at least one active behaviour + if (!gRlvHandler.hasBehaviour(rlvCmd.getObjectID())) + return RLV_RET_FAILED_NOBEHAVIOUR; + + auto itBhvrModifier = std::find_if(m_BhvrModifiers.begin(), m_BhvrModifiers.end(), [&rlvCmd](const modifier_lookup_t::value_type& entry) { return std::get<0>(entry.second) == rlvCmd.getBehaviourModifier(); }); + if (m_BhvrModifiers.end() == itBhvrModifier) + return RLV_RET_FAILED_UNKNOWN; + + ERlvCmdRet eCmdRet; const modifier_handler_func_t& fnHandler = std::get<2>(itBhvrModifier->second); + if (rlvCmd.hasOption()) + { + // If there's an option parse it (and perform type checking) + RlvBehaviourModifierValue modValue; + if ( (rlvCmd.hasOption()) && (!RlvBehaviourModifier::convertOptionValue(rlvCmd.getOption(), std::get<1>(itBhvrModifier->second), modValue)) ) + return RLV_RET_FAILED_OPTION; + eCmdRet = (fnHandler) ? fnHandler(rlvCmd.getObjectID(), modValue) : RLV_RET_SUCCESS; + if (RLV_RET_SUCCESS == eCmdRet) + gRlvHandler.getObject(rlvCmd.getObjectID())->setModifierValue(rlvCmd.getBehaviourModifier(), modValue); + } + else + { + eCmdRet = (fnHandler) ? fnHandler(rlvCmd.getObjectID(), boost::none) : RLV_RET_SUCCESS; + if (RLV_RET_SUCCESS == eCmdRet) + gRlvHandler.getObject(rlvCmd.getObjectID())->clearModifierValue(rlvCmd.getBehaviourModifier()); + } + + return eCmdRet; +} + // ============================================================================ // RlvBehaviourModifier // @@ -570,21 +622,22 @@ void RlvBehaviourModifier::setValue(const RlvBehaviourModifierValue& modValue, c } } -bool RlvBehaviourModifier::convertOptionValue(const std::string& optionValue, RlvBehaviourModifierValue& modValue) const +// static +bool RlvBehaviourModifier::convertOptionValue(const std::string& optionValue, const std::type_index& modType, RlvBehaviourModifierValue& modValue) { try { - if (typeid(float) == m_DefaultValue.type()) + if (modType == typeid(float)) { modValue = std::stof(optionValue); return true; } - else if (typeid(int) == m_DefaultValue.type()) + else if (modType == typeid(int)) { modValue = std::stoi(optionValue); return true; } - else if (typeid(LLVector3) == m_DefaultValue.type()) + else if (modType == typeid(LLVector3)) { LLVector3 vecOption; if (3 == sscanf(optionValue.c_str(), "%f/%f/%f", vecOption.mV + 0, vecOption.mV + 1, vecOption.mV + 2)) @@ -593,7 +646,7 @@ bool RlvBehaviourModifier::convertOptionValue(const std::string& optionValue, Rl return true; } } - else if (typeid(LLUUID) == m_DefaultValue.type()) + else if (modType == typeid(LLUUID)) { LLUUID idOption; if (LLUUID::parseUUID(optionValue, &idOption)) @@ -615,7 +668,7 @@ bool RlvBehaviourModifier::convertOptionValue(const std::string& optionValue, Rl // RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCommand) - : m_fValid(false), m_idObj(idObj), m_pBhvrInfo(NULL), m_eParamType(RLV_TYPE_UNKNOWN), m_fStrict(false), m_fRefCounted(false) + : m_idObj(idObj) { if (m_fValid = parseCommand(strCommand, m_strBehaviour, m_strOption, m_strParam)) { @@ -643,7 +696,7 @@ RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCommand) return; } - m_pBhvrInfo = RlvBehaviourDictionary::instance().getBehaviourInfo(m_strBehaviour, m_eParamType, &m_fStrict); + m_pBhvrInfo = RlvBehaviourDictionary::instance().getBehaviourInfo(m_strBehaviour, m_eParamType, &m_fStrict, &m_eBhvrModifier); } RlvCommand::RlvCommand(const RlvCommand& rlvCmd, ERlvParamType eParamType) @@ -1074,6 +1127,20 @@ std::string RlvObject::getStatusString(const std::string& strFilter, const std:: return strStatus; } +void RlvObject::clearModifierValue(ERlvBehaviourModifier eBhvrModifier) +{ + m_Modifiers.erase(eBhvrModifier); +} + +void RlvObject::setModifierValue(ERlvBehaviourModifier eBhvrModifier, const RlvBehaviourModifierValue& newValue) +{ + auto itBhvrModifierValue = m_Modifiers.find(eBhvrModifier); + if (m_Modifiers.end() != itBhvrModifierValue) + itBhvrModifierValue->second = newValue; + else + m_Modifiers.insert(std::make_pair(eBhvrModifier, newValue)); +} + // ============================================================================ // RlvForceWear // diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h index cbe79ec57a0347790b4227ab014eee427933df1e..322fd7dcda6eabd9d220b624530f300cf99a7e63 100644 --- a/indra/newview/rlvhelper.h +++ b/indra/newview/rlvhelper.h @@ -38,6 +38,7 @@ struct RlvBehaviourModifierComp; class RlvBehaviourInfo { + typedef std::function<ERlvCmdRet(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue>)> modifier_handler_func_t; public: enum EBehaviourFlags { @@ -65,24 +66,29 @@ class RlvBehaviourInfo : m_strBhvr(strBhvr), m_eBhvr(eBhvr), m_maskParamType(maskParamType), m_nBhvrFlags(nBhvrFlags) {} virtual ~RlvBehaviourInfo() {} - const std::string& getBehaviour() const { return m_strBhvr; } - ERlvBehaviour getBehaviourType() const { return m_eBhvr; } - U32 getBehaviourFlags() const { return m_nBhvrFlags; } - U32 getParamTypeMask() const { return m_maskParamType; } - bool hasStrict() const { return m_nBhvrFlags & BHVR_STRICT; } - bool isBlocked() const { return m_nBhvrFlags & BHVR_BLOCKED; } - bool isExperimental() const { return m_nBhvrFlags & BHVR_EXPERIMENTAL; } - bool isExtended() const { return m_nBhvrFlags & BHVR_EXTENDED; } - bool isSynonym() const { return m_nBhvrFlags & BHVR_SYNONYM; } - void toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable); + void addModifier(ERlvBehaviourModifier eBhvrMod, const std::type_info& valueType, const std::string& strBhvrMod, modifier_handler_func_t fnHandler = nullptr); + const std::string& getBehaviour() const { return m_strBhvr; } + ERlvBehaviour getBehaviourType() const { return m_eBhvr; } + U32 getBehaviourFlags() const { return m_nBhvrFlags; } + U32 getParamTypeMask() const { return m_maskParamType; } + bool hasStrict() const { return m_nBhvrFlags & BHVR_STRICT; } + bool isBlocked() const { return m_nBhvrFlags & BHVR_BLOCKED; } + bool isExperimental() const { return m_nBhvrFlags & BHVR_EXPERIMENTAL; } + bool isExtended() const { return m_nBhvrFlags & BHVR_EXTENDED; } + bool isSynonym() const { return m_nBhvrFlags & BHVR_SYNONYM; } + ERlvBehaviourModifier lookupBehaviourModifier(const std::string& strBhvrMod) const; + void toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable); virtual ERlvCmdRet processCommand(const RlvCommand& rlvCmd) const { return RLV_RET_NO_PROCESSOR; } + virtual ERlvCmdRet processModifier(const RlvCommand& rlvCmd) const; protected: std::string m_strBhvr; ERlvBehaviour m_eBhvr; U32 m_nBhvrFlags; U32 m_maskParamType; + typedef std::map<std::string, std::tuple<ERlvBehaviourModifier, std::type_index, modifier_handler_func_t>> modifier_lookup_t; + modifier_lookup_t m_BhvrModifiers; }; // ============================================================================ @@ -106,7 +112,7 @@ class RlvBehaviourDictionary : public LLSingleton<RlvBehaviourDictionary> public: void clearModifiers(const LLUUID& idRlvObj); ERlvBehaviour getBehaviourFromString(const std::string& strBhvr, ERlvParamType eParamType, bool* pfStrict = NULL) const; - const RlvBehaviourInfo* getBehaviourInfo(const std::string& strBhvr, ERlvParamType eParamType, bool* pfStrict = NULL) const; + const RlvBehaviourInfo* getBehaviourInfo(const std::string& strBhvr, ERlvParamType eParamType, bool* pfStrict = nullptr, ERlvBehaviourModifier* eBhvrModifier = nullptr) const; bool getCommands(const std::string& strMatch, ERlvParamType eParamType, std::list<std::string>& cmdList) const; bool getHasStrict(ERlvBehaviour eBhvr) const; RlvBehaviourModifier* getModifier(ERlvBehaviourModifier eBhvrMod) const { return (eBhvrMod < RLV_MODIFIER_COUNT) ? m_BehaviourModifiers[eBhvrMod] : nullptr; } @@ -244,12 +250,13 @@ class RlvBehaviourModifier virtual void onValueChange() const {} public: bool addValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idRlvObj, ERlvBehaviour eBhvr = RLV_BHVR_UNKNOWN); - bool convertOptionValue(const std::string& optionValue, RlvBehaviourModifierValue& modValue) const; + static bool convertOptionValue(const std::string& optionValue, const std::type_index& modType, RlvBehaviourModifierValue& modValue); void clearValues(const LLUUID& idRlvObj); bool getAddDefault() const { return m_fAddDefaultOnEmpty; } const RlvBehaviourModifierValue& getDefaultValue() const { return m_DefaultValue; } const LLUUID& getPrimaryObject() const; const std::string& getName() const { return m_strName; } + const std::type_info& getType() const { return m_DefaultValue.type(); } const RlvBehaviourModifierValue& getValue() const { return (hasValue()) ? std::get<0>(m_Values.front()) : m_DefaultValue; } template<typename T> const T& getValue() const { return boost::get<T>(getValue()); } bool hasValue() const; @@ -289,14 +296,17 @@ class RlvCommand public: std::string asString() const; const std::string& getBehaviour() const { return m_strBehaviour; } + const RlvBehaviourInfo* getBehaviourInfo() const { return m_pBhvrInfo; } ERlvBehaviour getBehaviourType() const { return (m_pBhvrInfo) ? m_pBhvrInfo->getBehaviourType() : RLV_BHVR_UNKNOWN; } U32 getBehaviourFlags() const{ return (m_pBhvrInfo) ? m_pBhvrInfo->getBehaviourFlags() : 0; } + ERlvBehaviourModifier getBehaviourModifier() const { return m_eBhvrModifier; } const LLUUID& getObjectID() const { return m_idObj; } const std::string& getOption() const { return m_strOption; } const std::string& getParam() const { return m_strParam; } ERlvParamType getParamType() const { return m_eParamType; } bool hasOption() const { return !m_strOption.empty(); } bool isBlocked() const { return (m_pBhvrInfo) ? m_pBhvrInfo->isBlocked() : false; } + bool isModifier() const { return RLV_MODIFIER_UNKNOWN != m_eBhvrModifier; } bool isRefCounted() const { return m_fRefCounted; } bool isStrict() const { return m_fStrict; } bool isValid() const { return m_fValid; } @@ -316,15 +326,16 @@ class RlvCommand * Member variables */ protected: - bool m_fValid; + bool m_fValid = false; LLUUID m_idObj; std::string m_strBehaviour; - const RlvBehaviourInfo* m_pBhvrInfo; - ERlvParamType m_eParamType; - bool m_fStrict; + const RlvBehaviourInfo* m_pBhvrInfo = nullptr; + ERlvParamType m_eParamType = RLV_TYPE_UNKNOWN; + ERlvBehaviourModifier m_eBhvrModifier = RLV_MODIFIER_UNKNOWN; + bool m_fStrict = false; std::string m_strOption; std::string m_strParam; - mutable bool m_fRefCounted; + mutable bool m_fRefCounted = false; friend class RlvHandler; friend class RlvObject; @@ -452,6 +463,14 @@ class RlvObject bool hasLookup() const { return m_fLookup; } const rlv_command_list_t& getCommandList() const { return m_Commands; } + /* + * Local-scope modifiers + */ +public: + void clearModifierValue(ERlvBehaviourModifier eBhvrMod); + template<typename T> bool getModifierValue(ERlvBehaviourModifier eBhvrModifier, T& value) const; + void setModifierValue(ERlvBehaviourModifier eBhvrMod, const RlvBehaviourModifierValue& modValue); + /* * Member variables */ @@ -462,6 +481,8 @@ class RlvObject bool m_fLookup; // TRUE if the object existed in gObjectList at one point in time S16 m_nLookupMisses; // Count of unsuccessful lookups in gObjectList by the GC rlv_command_list_t m_Commands; // List of behaviours held by this object (in the order they were received) + typedef std::map<ERlvBehaviourModifier, RlvBehaviourModifierValue> bhvr_modifier_map_t; + bhvr_modifier_map_t m_Modifiers; // List of (local scope) modifiers set on this object friend class RlvHandler; }; @@ -686,6 +707,19 @@ std::string rlvGetLastParenthesisedText(const std::string& strText, std::string: // Inlined class member functions // +inline void RlvBehaviourInfo::addModifier(ERlvBehaviourModifier eBhvrMod, const std::type_info& valueType, const std::string& strBhvrMod, modifier_handler_func_t fnHandler) +{ + RLV_ASSERT_DBG(m_BhvrModifiers.find(strBhvrMod) == m_BhvrModifiers.end()); + + m_BhvrModifiers.insert(std::make_pair(strBhvrMod, std::make_tuple(eBhvrMod, std::type_index(valueType), fnHandler))); +} + +inline ERlvBehaviourModifier RlvBehaviourInfo::lookupBehaviourModifier(const std::string& strBhvrMod) const +{ + auto itBhvrModifier = m_BhvrModifiers.find(strBhvrMod); + return (m_BhvrModifiers.end() != itBhvrModifier) ? std::get<0>(itBhvrModifier->second) : RLV_MODIFIER_UNKNOWN; +} + inline void RlvBehaviourInfo::toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable) { if (fEnable) @@ -710,6 +744,18 @@ inline bool RlvCommand::operator ==(const RlvCommand& rhs) const ( (RLV_TYPE_UNKNOWN != m_eParamType) ? (m_eParamType == rhs.m_eParamType) : (m_strParam == rhs.m_strParam) ); } +template <typename T> +inline bool RlvObject::getModifierValue(ERlvBehaviourModifier eBhvrModifier, T& value) const +{ + auto itBhvrModifierValue = m_Modifiers.find(eBhvrModifier); + if (m_Modifiers.end() != itBhvrModifierValue) + { + value = boost::get<T>(itBhvrModifierValue->second); + return true; + } + return false; +} + // Checked: 2010-04-05 (RLVa-1.2.0d) | Modified: RLVa-1.2.0d inline bool RlvForceWear::isWearableItem(const LLInventoryItem* pItem) {