diff --git a/indra/llmath/v4color.cpp b/indra/llmath/v4color.cpp index a8768bda35..1f3d9b7d21 100644 --- a/indra/llmath/v4color.cpp +++ b/indra/llmath/v4color.cpp @@ -59,6 +59,9 @@ LLColor4 LLColor4::grey1(0.8f, 0.8f, 0.8f, 1.0f); LLColor4 LLColor4::grey2(0.6f, 0.6f, 0.6f, 1.0f); LLColor4 LLColor4::grey3(0.4f, 0.4f, 0.4f, 1.0f); LLColor4 LLColor4::grey4(0.3f, 0.3f, 0.3f, 1.0f); +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) +LLColor4 LLColor4::silhouette(0.05f, 0.05f, 0.05f, 1.0f); +// [/RLVa:KB] LLColor4 LLColor4::red1(1.0f, 0.0f, 0.0f, 1.0f); LLColor4 LLColor4::red2(0.6f, 0.0f, 0.0f, 1.0f); diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h index 8f353ead5a..32e82a743e 100644 --- a/indra/llmath/v4color.h +++ b/indra/llmath/v4color.h @@ -154,6 +154,9 @@ class LLColor4 static LLColor4 grey2; static LLColor4 grey3; static LLColor4 grey4; +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + static LLColor4 silhouette; +// [/RLVa:KB] static LLColor4 red1; static LLColor4 red2; diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index 7bb67594af..5676001fdf 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -317,7 +317,7 @@ BOOL LLGLTexture::getIsAlphaMask() const } //BOOL LLGLTexture::getMask(const LLVector2 &tc) -// [RLVa:KB] - Checked: RLVa-2.3 (@setoverlay) +// [RLVa:KB] - Checked: RLVa-2.2 (@setoverlay) bool LLGLTexture::getMask(const LLVector2 &tc) const // [/RLVa:KB] { diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index a283f35ade..9c271e7885 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -143,7 +143,7 @@ public: LLGLenum getPrimaryFormat() const; BOOL getIsAlphaMask() const ; LLTexUnit::eTextureType getTarget(void) const ; -// [RLVa:KB] - Checked: RLVa-2.3 (@setoverlay) +// [RLVa:KB] - Checked: RLVa-2.2 (@setoverlay) bool getMask(const LLVector2 &tc) const; // [/RLVa:KB] // BOOL getMask(const LLVector2 &tc); diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 2f4a505235..753ab70cfb 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -2002,7 +2002,7 @@ void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) } //BOOL LLImageGL::getMask(const LLVector2 &tc) -// [RLVa:KB] - Checked: RLVa-2.3 (@setoverlay) +// [RLVa:KB] - Checked: RLVa-2.2 (@setoverlay) BOOL LLImageGL::getMask(const LLVector2 &tc) const // [/RLVa:KB] { diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 255a64c3a2..3e35367f2a 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -153,7 +153,7 @@ public: void setUseMipMaps(BOOL usemips) { mUseMipMaps = usemips; } void updatePickMask(S32 width, S32 height, const U8* data_in); -// [RLVa:KB] - Checked: RLVa-2.3 (@setoverlay) +// [RLVa:KB] - Checked: RLVa-2.2 (@setoverlay) BOOL getMask(const LLVector2 &tc) const; // [/RLVa:KB] // BOOL getMask(const LLVector2 &tc); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index ce46f79261..19a6327d38 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1229,6 +1229,17 @@ Value + + RLVaBlockedExperiences + + Comment + List of experiences blocked from interacting with RLVa + Persist + 1 + Type + String + Value + bfe25fb4-222c-11e5-85a2-fa4c4ccaa202 + RLVaCompatibilityModeList Comment @@ -1328,6 +1339,17 @@ Value 1 + RLVaExperienceMaturityThreshold + + Comment + Specifies the minimum maturity an experience has to be before it can interact with RLVa (0: never; 1: PG; 2: Mature; 3: Adult) + Persist + 1 + Type + S32 + Value + 2 + RLVaHideLockedLayers Comment diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index b8a2d52f2a..9eab3a6ab0 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -742,6 +742,13 @@ void LLAgent::moveLeftNudge(S32 direction) //----------------------------------------------------------------------------- void LLAgent::moveUp(S32 direction) { +// [RLVa:KB] - Checked: RLVa-2.2 (@jump) + if ( (!RlvActions::canJump()) && (direction > 0) && (!getFlying()) ) + { + return; + } +// [/Sl:KB] + mMoveTimer.reset(); LLFirstUse::notMoving(false); @@ -819,8 +826,11 @@ void LLAgent::movePitch(F32 mag) // Does this parcel allow you to fly? BOOL LLAgent::canFly() { -// [RLVa:KB] - Checked: 2010-03-02 (RLVa-1.2.0d) | Modified: RLVa-1.0.0c - if (gRlvHandler.hasBehaviour(RLV_BHVR_FLY)) return FALSE; +// [RLVa:KB] - Checked: RLVa-1.0 + if (!RlvActions::canFly()) + { + return FALSE; + } // [/RLVa:KB] if (isGodlike()) return TRUE; // Always fly @@ -876,8 +886,8 @@ void LLAgent::setFlying(BOOL fly) if (fly) { -// [RLVa:KB] - Checked: 2010-03-02 (RLVa-1.2.0d) | Modified: RLVa-1.0.0c - if (gRlvHandler.hasBehaviour(RLV_BHVR_FLY)) +// [RLVa:KB] - Checked: RLVa-1.0 + if (!RlvActions::canFly()) { return; } diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index 3171941407..fcfcfcd12b 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -1534,7 +1534,7 @@ void LLGroupMgr::notifyObservers(LLGroupChange gc) return; // observer_set_t& obs = obs_it->second; -// [RLVa:KB] - Checked: RLVa-2.3 (General bugfix) +// [RLVa:KB] - Checked: RLVa-2.2 (General bugfix) // Iterate over a *copy* of the observer list observer_set_t obs = obs_it->second; // [/RLVa:KB] diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 1662a7fd14..399559ca0b 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -5721,7 +5721,7 @@ bool LLTextureBridge::canSaveTexture(void) return false; } -// [RLVa:KB] - Checked: RLVa-2.2 +// [RLVa:KB] - Checked: RLVa-2.2 (@viewtexture) if (!RlvActions::canPreviewTextures()) { return false; diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 9ea275d5b6..39e9c4c328 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1428,7 +1428,7 @@ void render_ui(F32 zoom_factor, int subfield) } render_hud_elements(); -// [RLVa:KB] - Checked: RLVa-2.3 (@setoverlay) +// [RLVa:KB] - Checked: RLVa-2.2 (@setoverlay) if (gRlvHandler.isEnabled()) { gRlvHandler.renderOverlay(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index fcb6ac3d54..3c62af414a 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -140,6 +140,7 @@ #include "boost/unordered_map.hpp" #include "llcleanup.h" // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) +#include "fsavatarrenderpersistence.h" #include "rlvactions.h" #include "rlvhandler.h" #include "rlvlocks.h" @@ -3741,11 +3742,20 @@ bool check_avatar_render_mode(U32 mode) switch (mode) { case 0: - return (avatar->getVisualMuteSettings() == LLVOAvatar::AV_RENDER_NORMALLY); +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + return FSAvatarRenderPersistence::instance().getAvatarRenderSettings(avatar->getID()) == LLVOAvatar::AV_RENDER_NORMALLY; +// [/RLVa:KB] +// return (avatar->getVisualMuteSettings() == LLVOAvatar::AV_RENDER_NORMALLY); case 1: - return (avatar->getVisualMuteSettings() == LLVOAvatar::AV_DO_NOT_RENDER); +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + return FSAvatarRenderPersistence::instance().getAvatarRenderSettings(avatar->getID()) == LLVOAvatar::AV_DO_NOT_RENDER; +// [/RLVa:KB] +// return (avatar->getVisualMuteSettings() == LLVOAvatar::AV_DO_NOT_RENDER); case 2: - return (avatar->getVisualMuteSettings() == LLVOAvatar::AV_ALWAYS_RENDER); +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + return FSAvatarRenderPersistence::instance().getAvatarRenderSettings(avatar->getID()) == LLVOAvatar::AV_ALWAYS_RENDER; +// [/RLVa:KB] +// return (avatar->getVisualMuteSettings() == LLVOAvatar::AV_ALWAYS_RENDER); default: return false; } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 319fd77d7e..9108408e92 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -6941,7 +6941,7 @@ void LLPickInfo::fetchResults() mPickPt = mMousePt; -// [RLVa:KB] - Checked: RLVa-2.3 (@setoverlay) +// [RLVa:KB] - Checked: RLVa-2.2 (@setoverlay) if ( (gRlvHandler.isEnabled()) && (hit_object) && (!hit_object->isHUDAttachment()) ) { if (gRlvHandler.hitTestOverlay(mMousePt)) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 1ea72f71a3..d9e2d01ea3 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -101,6 +101,7 @@ // [RLVa:KB] - Checked: RLVa-2.0.1 #include "rlvactions.h" #include "rlvhandler.h" +#include "rlvmodifiers.h" // [/RLVa:KB] #include "llgesturemgr.h" //needed to trigger the voice gesticulations @@ -3845,6 +3846,12 @@ bool LLVOAvatar::isVisuallyMuted() // muted = true; //} // +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + else if (isRlvSilhouette()) + { + muted = true; + } +// [/RLVa:KB] else { muted = isTooComplex(); @@ -3873,6 +3880,30 @@ bool LLVOAvatar::isInMuteList() return muted; } +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) +bool LLVOAvatar::isRlvSilhouette() +{ + if (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_AVDIST)) + return false; + + static RlvCachedBehaviourModifier s_nSetCamAvDist(RLV_MODIFIER_SETCAM_AVDIST); + + const F64 now = LLFrameTimer::getTotalSeconds(); + if (now >= mCachedRlvSilhouetteUpdateTime) + { + const F64 SECONDS_BETWEEN_NEARBY_UPDATES = .5f; + bool fIsRlvSilhouette = dist_vec_squared(gAgent.getPositionGlobal(), getPositionGlobal()) > s_nSetCamAvDist() * s_nSetCamAvDist(); + if (fIsRlvSilhouette != mCachedIsRlvSilhouette) + { + mCachedIsRlvSilhouette = fIsRlvSilhouette; + mNeedsImpostorUpdate = TRUE; + } + mCachedRlvSilhouetteUpdateTime = now + SECONDS_BETWEEN_NEARBY_UPDATES; + } + return mCachedIsRlvSilhouette; +} +// [/RLVa:KB] + void LLVOAvatar::updateDebugText() { // clear debug text @@ -10283,9 +10314,21 @@ void LLVOAvatar::calcMutedAVColor() if (getVisualMuteSettings() == AV_DO_NOT_RENDER) { - // explicitly not-rendered avatars are light grey - new_color = LLColor4::grey3; - change_msg = " not rendered: color is grey3"; +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + if (isRlvSilhouette()) + { + new_color = LLColor4::silhouette; + change_msg = " not rendered: color is silhouette"; + } + else + { +// [/RLVa:KB] + // explicitly not-rendered avatars are light grey + new_color = LLColor4::grey3; + change_msg = " not rendered: color is grey3"; +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + } +// [/RLVa:KB] } else if (LLMuteList::getInstance()->isMuted(av_id)) // the user blocked them { @@ -10293,8 +10336,11 @@ void LLVOAvatar::calcMutedAVColor() new_color = LLColor4::grey4; change_msg = " blocked: color is grey4"; } - else if ( mMutedAVColor == LLColor4::white || mMutedAVColor == LLColor4::grey3 || mMutedAVColor == LLColor4::grey4 ) - { +// else if ( mMutedAVColor == LLColor4::white || mMutedAVColor == LLColor4::grey3 || mMutedAVColor == LLColor4::grey4 ) +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + else if ( mMutedAVColor == LLColor4::white || mMutedAVColor == LLColor4::grey3 || mMutedAVColor == LLColor4::grey4 || mMutedAVColor == LLColor4::silhouette) +// [/RLVa:KB] + { // select a color based on the first byte of the agents uuid so any muted agent is always the same color F32 color_value = (F32) (av_id.mData[0]); F32 spectrum = (color_value / 256.0); // spectrum is between 0 and 1.f diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index e5405fb6ea..4fb1623197 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -409,6 +409,9 @@ public: U32 renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0); bool isVisuallyMuted(); bool isInMuteList(); +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + bool isRlvSilhouette(); +// [/RLVa:KB] void forceUpdateVisualMuteSettings(); enum VisualMuteSettings @@ -418,7 +421,10 @@ public: AV_ALWAYS_RENDER = 2 }; void setVisualMuteSettings(VisualMuteSettings set); - VisualMuteSettings getVisualMuteSettings() { return mVisuallyMuteSetting; }; +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + VisualMuteSettings getVisualMuteSettings() { return (!isRlvSilhouette()) ? mVisuallyMuteSetting : AV_DO_NOT_RENDER; }; +// [/RLVa:KB] +// VisualMuteSettings getVisualMuteSettings() { return mVisuallyMuteSetting; }; U32 renderRigid(); U32 renderSkinned(); @@ -449,6 +455,10 @@ public: bool mCachedInMuteList; F64 mCachedMuteListUpdateTime; +// [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist) + mutable bool mCachedIsRlvSilhouette = false; + mutable F64 mCachedRlvSilhouetteUpdateTime = 0.f; +// [/RLVa:KB] VisualMuteSettings mVisuallyMuteSetting; // Always or never visually mute this AV diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp index 16a9c4c380..50e37d4455 100644 --- a/indra/newview/rlvactions.cpp +++ b/indra/newview/rlvactions.cpp @@ -297,6 +297,21 @@ bool RlvActions::autoAcceptTeleportRequest(const LLUUID& idRequester) return ((idRequester.notNull()) && (gRlvHandler.isException(RLV_BHVR_ACCEPTTPREQUEST, idRequester))) || (gRlvHandler.hasBehaviour(RLV_BHVR_ACCEPTTPREQUEST)); } +bool RlvActions::canFly() +{ + return (!gRlvHandler.getCurrentCommand()) ? !gRlvHandler.hasBehaviour(RLV_BHVR_FLY) : !gRlvHandler.hasBehaviourExcept(RLV_BHVR_FLY, gRlvHandler.getCurrentObject()); +} + +bool RlvActions::canFly(const LLUUID& idRlvObjExcept) +{ + return !gRlvHandler.hasBehaviourExcept(RLV_BHVR_FLY, idRlvObjExcept); +} + +bool RlvActions::canJump() +{ + return !gRlvHandler.hasBehaviour(RLV_BHVR_JUMP); +} + // ============================================================================ // Teleporting // diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h index afafa726b8..90b1069f6b 100644 --- a/indra/newview/rlvactions.h +++ b/indra/newview/rlvactions.h @@ -176,6 +176,18 @@ public: */ static bool autoAcceptTeleportRequest(const LLUUID& idRequester); + /* + * Returns true if the user can fly + * (NOTE: the parameter-less overload takes the currently executing command into account) + */ + static bool canFly(); + static bool canFly(const LLUUID& idRlvObjExcept); + + /* + * Returns true if the user can jump + */ + static bool canJump(); + // =========== // Teleporting // =========== diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index 13787a01fd..a57588ab50 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -86,9 +86,11 @@ void RlvNotifications::onGiveToRLVConfirmation(const LLSD& notification, const L bool RlvSettings::s_fCompositeFolders = false; #endif // RLV_EXPERIMENTAL_COMPOSITEFOLDERS bool RlvSettings::s_fCanOOC = true; +U8 RlvSettings::s_nExperienceMinMaturity = 0; bool RlvSettings::s_fLegacyNaming = true; bool RlvSettings::s_fNoSetEnv = false; bool RlvSettings::s_fTempAttach = true; +std::list RlvSettings::s_BlockedExperiences; std::list RlvSettings::s_CompatItemCreators; std::list RlvSettings::s_CompatItemNames; @@ -124,6 +126,10 @@ void RlvSettings::initClass() if (gSavedSettings.controlExists(RLV_SETTING_TOPLEVELMENU)) gSavedSettings.getControl(RLV_SETTING_TOPLEVELMENU)->getSignal()->connect(boost::bind(&onChangedMenuLevel)); + int nMinMaturity = gSavedSettings.getS32("RLVaExperienceMaturityThreshold"); + s_nExperienceMinMaturity = (nMinMaturity == 0) ? 0 : ((nMinMaturity == 1) ? SIM_ACCESS_PG : ((nMinMaturity == 2) ? SIM_ACCESS_MATURE : SIM_ACCESS_ADULT)); + boost::split(s_BlockedExperiences, gSavedSettings.getString("RLVaBlockedExperiences"), boost::is_any_of(";")); + fInitialized = true; } } @@ -226,6 +232,18 @@ bool RlvSettings::isCompatibilityModeObject(const LLUUID& idRlvObject) return fCompatMode; } +bool RlvSettings::isAllowedExperience(const LLUUID& idExperience, U8 nMaturity) +{ + // An experience is allowed to interact with RLVa if: + // - temporary attachments can interact with RLVa + // - the user set a minimum maturity and the specified maturity is equal or higher + // - the experience isn't explicitly blocked (NOTE: case-sensitive string comparison) + return + (getEnableTemporaryAttachments()) && + (s_nExperienceMinMaturity) && (s_nExperienceMinMaturity <= nMaturity) && + (s_BlockedExperiences.end() == std::find(s_BlockedExperiences.begin(), s_BlockedExperiences.end(), idExperience.asString())); +} + // ============================================================================ // RlvStrings // @@ -376,6 +394,8 @@ const char* RlvStrings::getStringFromReturnCode(ERlvCmdRet eRet) return "deprecated and disabled"; case RLV_RET_FAILED_NOBEHAVIOUR: return "no active behaviours"; + case RLV_RET_FAILED_BLOCKED: + return "blocked object"; // The following are identified by the chat verb case RLV_RET_RETAINED: case RLV_RET_SUCCESS: diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index ce87046da3..b0c77f513c 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -107,6 +107,8 @@ public: static void initCompatibilityMode(std::string strCompatList); static bool isCompatibilityModeObject(const LLUUID& idRlvObject); + static bool isAllowedExperience(const LLUUID& idExperience, U8 nMaturity); + static void initClass(); static void onChangedSettingMain(const LLSD& sdValue); protected: @@ -122,9 +124,11 @@ protected: */ protected: static bool s_fCanOOC; + static U8 s_nExperienceMinMaturity; static bool s_fLegacyNaming; static bool s_fNoSetEnv; static bool s_fTempAttach; + static std::list s_BlockedExperiences; static std::list s_CompatItemCreators; static std::list s_CompatItemNames; }; diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index a84fb8b8bb..4e322b8737 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -163,6 +163,7 @@ enum ERlvBehaviour { RLV_BHVR_TOUCHALL, // "touchall" RLV_BHVR_TOUCHME, // "touchme" RLV_BHVR_FLY, // "fly" + RLV_BHVR_JUMP, // "jump" RLV_BHVR_SETGROUP, // "setgroup" RLV_BHVR_UNSIT, // "unsit" RLV_BHVR_SIT, // "sit" @@ -205,6 +206,7 @@ enum ERlvBehaviour { // Camera (behaviours) RLV_BHVR_SETCAM, // Gives an object exclusive control of the user's camera + RLV_BHVR_SETCAM_AVDIST, // Distance at which nearby avatars turn into a silhouette RLV_BHVR_SETCAM_AVDISTMIN, // Enforces a minimum distance from the avatar (in m) RLV_BHVR_SETCAM_AVDISTMAX, // Enforces a maximum distance from the avatar (in m) RLV_BHVR_SETCAM_ORIGINDISTMIN, // Enforces a minimum distance from the camera origin (in m) @@ -257,6 +259,7 @@ enum ERlvBehaviourModifier RLV_MODIFIER_SENDIMDISTMAX, // Maximum distance to send an IM to an otherwise restricted recipient (squared value) RLV_MODIFIER_STARTIMDISTMIN, // Minimum distance to start an IM to an otherwise restricted recipient (squared value) RLV_MODIFIER_STARTIMDISTMAX, // Maximum distance to start an IM to an otherwise restricted recipient (squared value) + RLV_MODIFIER_SETCAM_AVDIST, // Distance at which nearby avatars turn into a silhouette (normal value) RLV_MODIFIER_SETCAM_AVDISTMIN, // Minimum distance between the camera position and the user's avatar (normal value) RLV_MODIFIER_SETCAM_AVDISTMAX, // Maximum distance between the camera position and the user's avatar (normal value) RLV_MODIFIER_SETCAM_ORIGINDISTMIN, // Minimum distance between the camera position and the origin point (normal value) @@ -310,6 +313,7 @@ enum ERlvCmdRet { RLV_RET_FAILED_NOSHAREDROOT, // Command failed (missing #RLV) RLV_RET_FAILED_DEPRECATED, // Command failed (deprecated and no longer supported) RLV_RET_FAILED_NOBEHAVIOUR, // Command failed (force modifier on an object with no active restrictions) + RLV_RET_FAILED_BLOCKED, // Command failed (object is blocked) RLV_RET_NO_PROCESSOR // Command doesn't have a template processor define (legacy code) }; #define RLV_RET_SUCCEEDED(eCmdRet) (((eCmdRet) & RLV_RET_SUCCESS) == RLV_RET_SUCCESS) diff --git a/indra/newview/rlvfloaters.cpp b/indra/newview/rlvfloaters.cpp index ed531eb93b..5f90fa1259 100644 --- a/indra/newview/rlvfloaters.cpp +++ b/indra/newview/rlvfloaters.cpp @@ -230,7 +230,7 @@ void RlvFloaterBehaviours::onAvatarNameLookup(const LLUUID& idAgent, const LLAva } // static -const std::string RlvFloaterBehaviours::getFormattedBehaviourString() +std::string RlvFloaterBehaviours::getFormattedBehaviourString(ERlvBehaviourFilter eFilter) { std::ostringstream strRestrictions; @@ -241,6 +241,13 @@ const std::string RlvFloaterBehaviours::getFormattedBehaviourString() strRestrictions << "\n" << rlvGetItemNameFromObjID(rlvObjectEntry.first) << ":\n"; for (const RlvCommand& rlvCmd : rlvObjectEntry.second.getCommandList()) { + bool fIsException = (rlvCmd.hasOption()) && (rlvGetShowException(rlvCmd.getBehaviourType())); + if ( ((ERlvBehaviourFilter::BEHAVIOURS_ONLY == eFilter) && (fIsException)) || + ((ERlvBehaviourFilter::EXCEPTIONS_ONLY == eFilter) && (!fIsException)) ) + { + continue; + } + std::string strOption; LLUUID idOption; if ( (rlvCmd.hasOption()) && (idOption.set(rlvCmd.getOption(), FALSE)) && (idOption.notNull()) ) { @@ -266,7 +273,7 @@ const std::string RlvFloaterBehaviours::getFormattedBehaviourString() // Checked: 2011-05-26 (RLVa-1.3.1c) | Added: RLVa-1.3.1c void RlvFloaterBehaviours::onBtnCopyToClipboard() { - LLWString wstrRestrictions = utf8str_to_wstring(getFormattedBehaviourString()); + LLWString wstrRestrictions = utf8str_to_wstring(getFormattedBehaviourString(ERlvBehaviourFilter::ALL)); LLClipboard::instance().copyToClipboard(wstrRestrictions, 0, wstrRestrictions.length()); } diff --git a/indra/newview/rlvfloaters.h b/indra/newview/rlvfloaters.h index 17903dd903..f29fa92483 100644 --- a/indra/newview/rlvfloaters.h +++ b/indra/newview/rlvfloaters.h @@ -32,6 +32,12 @@ class LLTextEditor; // RlvFloaterLocks class declaration // +enum class ERlvBehaviourFilter { + BEHAVIOURS_ONLY, + EXCEPTIONS_ONLY, + ALL +}; + class RlvFloaterBehaviours : public LLFloater { friend class LLFloaterReg; @@ -49,7 +55,7 @@ public: /* * Member functions */ - static const std::string getFormattedBehaviourString(); + static std::string getFormattedBehaviourString(ERlvBehaviourFilter eFilter); protected: void onAvatarNameLookup(const LLUUID& idAgent, const LLAvatarName& avName); void onBtnCopyToClipboard(); diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index 9069ed8fd8..874207885e 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -19,6 +19,8 @@ #include "llagent.h" #include "llappearancemgr.h" #include "llappviewer.h" +#include "llexperiencecache.h" +#include "llexperiencelog.h" #include "llgroupactions.h" #include "llhudtext.h" #include "llmoveview.h" @@ -265,6 +267,78 @@ void RlvHandler::removeException(const LLUUID& idObj, ERlvBehaviour eBhvr, const } } +// ============================================================================ +// Blocked object handling +// + +void RlvHandler::addBlockedObject(const LLUUID& idObj, const std::string& strName) +{ + m_BlockedObjects.push_back(std::make_tuple(idObj, strName, LLTimer::getTotalSeconds())); +} + +bool RlvHandler::hasUnresolvedBlockedObject() const +{ + return std::any_of(m_BlockedObjects.begin(), m_BlockedObjects.end(), [](const rlv_blocked_object_t& entry) { return std::get<0>(entry).isNull(); }); +} + +bool RlvHandler::isBlockedObject(const LLUUID& idObj) const +{ + return std::any_of(m_BlockedObjects.begin(), m_BlockedObjects.end(), [&idObj](const rlv_blocked_object_t& entry) { return std::get<0>(entry) == idObj; }); +} + +void RlvHandler::removeBlockedObject(const LLUUID& idObj) +{ + m_BlockedObjects.erase(std::remove_if(m_BlockedObjects.begin(), m_BlockedObjects.end(), + [&idObj](const rlv_blocked_object_t& entry) { + return (idObj.notNull()) ? std::get<0>(entry) == idObj : false; + }), m_BlockedObjects.end()); +} + +void RlvHandler::getAttachmentResourcesCoro(const std::string& strUrl) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("RlvHandler::getAttachmentResourcesCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + const LLSD sdResult = httpAdapter->getAndSuspend(httpRequest, strUrl); + + const LLCore::HttpStatus httpStatus = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(sdResult[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]); + if ( (httpStatus) && (sdResult.has("attachments")) ) + { + const LLSD& sdAttachments = sdResult["attachments"]; + for (auto& itAttach = sdAttachments.beginArray(), endAttach = sdAttachments.endArray(); itAttach != endAttach; ++itAttach) + { + if (!itAttach->has("objects")) + continue; + + const LLSD& sdAttachObjects = itAttach->get("objects"); + for (auto& itAttachObj = sdAttachObjects.beginArray(), endAttachObj = sdAttachObjects.endArray(); itAttachObj != endAttachObj; ++itAttachObj) + { + const LLUUID idObj = itAttachObj->get("id").asUUID(); + const std::string& strObjName = itAttachObj->get("name").asStringRef(); + + // If it's an attachment, it should be a temporary one (NOTE: we might catch it before it's had a chance to attach) + const LLViewerObject* pObj = gObjectList.findObject(idObj); + if ( (pObj) && ((!pObj->isAttachment()) || (!pObj->isTempAttachment()) || (isBlockedObject(idObj))) ) + continue; + + // Find it by object name + auto itBlockedObj = std::find_if(m_BlockedObjects.begin(), m_BlockedObjects.end(), + [&strObjName](const rlv_blocked_object_t& entry) { + return (std::get<0>(entry).isNull()) && (std::get<1>(entry) == strObjName); + }); + if (m_BlockedObjects.end() != itBlockedObj) + { + std::get<0>(*itBlockedObj) = idObj; + + RLV_INFOS << "Clearing restrictions from blocked object " << idObj.asString() << RLV_ENDL; + processCommand(idObj, "clear", true); + return; + } + } + } + } +} + // ============================================================================ // Command processing functions // @@ -315,6 +389,11 @@ ERlvCmdRet RlvHandler::processCommand(const RlvCommand& rlvCmd, bool fFromObj) { RLV_DEBUGS << "[" << rlvCmd.getObjectID() << "]: " << rlvCmd.asString() << RLV_ENDL; + if ( (isBlockedObject(rlvCmd.getObjectID())) && (RLV_TYPE_REMOVE != rlvCmd.getParamType()) && (RLV_TYPE_CLEAR != rlvCmd.getParamType()) ) + { + RLV_DEBUGS << "\t-> blocked object" << RLV_ENDL; + return RLV_RET_FAILED_BLOCKED; + } if (!rlvCmd.isValid()) { RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL; @@ -515,13 +594,13 @@ bool RlvHandler::processIMQuery(const LLUUID& idSender, const std::string& strMe RlvUtil::sendBusyMessage(idSender, RlvStrings::getVersion(LLUUID::null)); return true; } - else if ("@list" == strMessage) + else if ( ("@list" == strMessage) || ("@except" == strMessage) ) { LLNotification::Params params; params.name = "RLVaListRequested"; params.functor.function(boost::bind(&RlvHandler::onIMQueryListResponse, this, _1, _2)); params.substitutions = LLSD().with("NAME_LABEL", LLSLURL("agent", idSender, "completename").getSLURLString()).with("NAME_SLURL", LLSLURL("agent", idSender, "about").getSLURLString()); - params.payload = LLSD().with("from_id", idSender); + params.payload = LLSD().with("from_id", idSender).with("command", strMessage); class RlvPostponedOfferNotification : public LLPostponedNotification { @@ -543,9 +622,25 @@ bool RlvHandler::processIMQuery(const LLUUID& idSender, const std::string& strMe void RlvHandler::onIMQueryListResponse(const LLSD& sdNotification, const LLSD sdResponse) { const LLUUID idRequester = sdNotification["payload"]["from_id"].asUUID(); - if (LLNotificationsUtil::getSelectedOption(sdNotification, sdResponse) == 0) + + const int idxOption = LLNotificationsUtil::getSelectedOption(sdNotification, sdResponse); + if ( (idxOption == 0) || (idxOption == 1) ) { - RlvUtil::sendIMMessage(idRequester, RlvFloaterBehaviours::getFormattedBehaviourString(), '\n'); + if (idxOption == 1) + { + if (LLNotificationPtr pNotif = LLNotificationsUtil::find(sdNotification["id"])) + pNotif->setIgnored(true); + } + + const std::string& strCommand = sdNotification["payload"]["command"].asStringRef(); + if ("@list" == strCommand) + { + RlvUtil::sendIMMessage(idRequester, RlvFloaterBehaviours::getFormattedBehaviourString(ERlvBehaviourFilter::BEHAVIOURS_ONLY).append("\n").append(RlvStrings::getString("imquery_list_suffix")), '\n'); + } + else if ("@except" == strCommand) + { + RlvUtil::sendIMMessage(idRequester, RlvFloaterBehaviours::getFormattedBehaviourString(ERlvBehaviourFilter::EXCEPTIONS_ONLY), '\n'); + } } else { @@ -808,6 +903,42 @@ void RlvHandler::onDetach(const LLViewerObject* pAttachObj, const LLViewerJointA RLV_INFOS << "\t-> done" << RLV_ENDL; } } + + if (pAttachObj->isTempAttachment()) + { + removeBlockedObject(pAttachObj->getID()); + } + } +} + +void RlvHandler::onExperienceAttach(const LLSD& sdExperience, const std::string& strObjName) +{ + if (!RlvSettings::isAllowedExperience(sdExperience[LLExperienceCache::EXPERIENCE_ID].asUUID(), sdExperience[LLExperienceCache::MATURITY].asInteger())) + { + addBlockedObject(LLUUID::null, strObjName); + + const std::string strUrl = gAgent.getRegionCapability("AttachmentResources"); + if (!strUrl.empty()) + { + LLCoros::instance().launch("RlvHandler::getAttachmentResourcesCoro", boost::bind(&RlvHandler::getAttachmentResourcesCoro, this, strUrl)); + } + } +} + +void RlvHandler::onExperienceEvent(const LLSD& sdEvent) +{ + const int nPermission = sdEvent["Permission"].asInteger(); + switch (nPermission) + { + case 4: // Attach + { + const LLUUID& idExperience = sdEvent["public_id"].asUUID(); + const std::string strObjName = sdEvent["ObjectName"].asString(); + LLExperienceCache::instance().get(idExperience, boost::bind(&RlvHandler::onExperienceAttach, this, _1, strObjName)); + } + break; + default: + break; } } @@ -862,6 +993,23 @@ bool RlvHandler::onGC() RLV_ASSERT(gRlvAttachmentLocks.verifyAttachmentLocks()); // Verify that we haven't leaked any attachment locks somehow + // Clean up pending temp attachments that we were never able to resolve + rlv_blocked_object_list_t::const_iterator itBlocked = m_BlockedObjects.cbegin(), itCurBlocked; + while (itBlocked != m_BlockedObjects.end()) + { + itCurBlocked = itBlocked++; +#ifdef RLV_DEBUG + bool itBlocked = true; + RLV_ASSERT(itBlocked); +#endif // RLV_DEBUG + + const LLUUID& idObj = std::get<0>(*itCurBlocked); + if ( (idObj.notNull()) || (LLTimer::getTotalSeconds() - std::get<2>(*itCurBlocked) < 300.f) ) + continue; + + m_BlockedObjects.erase(itCurBlocked); + } + return (0 != m_Objects.size()); // GC will kill itself if it has nothing to do } @@ -895,8 +1043,9 @@ void RlvHandler::onLoginComplete() RlvInventory::instance().fetchSharedInventory(); RlvSettings::updateLoginLastLocation(); - LLViewerParcelMgr::getInstance()->setTeleportFailedCallback(boost::bind(&RlvHandler::onTeleportFailed, this)); - LLViewerParcelMgr::getInstance()->setTeleportFinishedCallback(boost::bind(&RlvHandler::onTeleportFinished, this, _1)); + m_ExperienceEventConn = LLExperienceLog::instance().addUpdateSignal(boost::bind(&RlvHandler::onExperienceEvent, this, _1)); + m_TeleportFailedConn = LLViewerParcelMgr::getInstance()->setTeleportFailedCallback(boost::bind(&RlvHandler::onTeleportFailed, this)); + m_TeleportFinishedConn = LLViewerParcelMgr::getInstance()->setTeleportFinishedCallback(boost::bind(&RlvHandler::onTeleportFinished, this, _1)); processRetainedCommands(); } @@ -2457,6 +2606,23 @@ ERlvCmdRet RlvForceHandler::onCommand(const RlvCommand& rlvCm return RLV_RET_SUCCESS; } +// Handles: @fly:[true|false]=force +template<> template<> +ERlvCmdRet RlvForceHandler::onCommand(const RlvCommand& rlvCmd) +{ + bool fForceFly = true; + if ( (rlvCmd.hasOption()) && (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), fForceFly)) ) + return RLV_RET_FAILED_OPTION; + + if ( (fForceFly) && (!RlvActions::canFly(rlvCmd.getObjectID())) ) + return RLV_RET_FAILED_LOCK; + + if (fForceFly != (bool)gAgent.getFlying()) + gAgent.setFlying(fForceFly); + + return RLV_RET_SUCCESS; +} + // Handles: @remattach[:]=force template<> template<> ERlvCmdRet RlvForceRemAttachHandler::onCommand(const RlvCommand& rlvCmd) @@ -3485,9 +3651,9 @@ void RlvHandler::renderOverlay() m_pOverlayImage->addTextureStats(nWidth * nHeight); m_pOverlayImage->setKnownDrawSize(nWidth, nHeight); + gGL.pushMatrix(); LLGLSUIDefault glsUI; gViewerWindow->setup2DRender(); - gGL.pushMatrix(); const LLVector2& displayScale = gViewerWindow->getDisplayScale(); gGL.scalef(displayScale.mV[VX], displayScale.mV[VY], 1.f); @@ -3516,6 +3682,7 @@ void RlvHandler::renderOverlay() gGL.popMatrix(); gGL.flush(); + gViewerWindow->setup3DRender(); if (LLGLSLShader::sNoFixedFunction) { diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index 7c48d0c276..0601cf1977 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -96,6 +96,16 @@ public: bool isHiddenCompositeItem(const LLUUID& idItem, const std::string& strItemType) const; #endif // RLV_EXPERIMENTAL_COMPOSITEFOLDERS +public: + // Adds a blocked object (= object that is blocked from issuing commands) by UUID (can be null) and/or name + void addBlockedObject(const LLUUID& idObj, const std::string& strObjName); + // Returns TRUE if there's an unresolved blocked object (known name but unknown UUID) + bool hasUnresolvedBlockedObject() const; + // Returns TRUE if the object with the specified UUID is blocked from issuing commands + bool isBlockedObject(const LLUUID& idObj) const; + // Removes a blocked object + void removeBlockedObject(const LLUUID& idObj); + // -------------------------------- /* @@ -163,6 +173,8 @@ public: void onActiveGroupChanged(); void onAttach(const LLViewerObject* pAttachObj, const LLViewerJointAttachment* pAttachPt); void onDetach(const LLViewerObject* pAttachObj, const LLViewerJointAttachment* pAttachPt); + void onExperienceAttach(const LLSD& sdExperience, const std::string& strObjName); + void onExperienceEvent(const LLSD& sdEvent); bool onGC(); void onLoginComplete(); void onSitOrStand(bool fSitting); @@ -170,6 +182,7 @@ public: void onTeleportFinished(const LLVector3d& posArrival); static void onIdleStartup(void* pParam); protected: + void getAttachmentResourcesCoro(const std::string& strUrl); void onTeleportCallback(U64 hRegion, const LLVector3& posRegion, const LLVector3& vecLookAt, const LLUUID& idRlvObj); /* @@ -214,9 +227,12 @@ protected: */ public: typedef std::map rlv_object_map_t; + typedef std::tuple rlv_blocked_object_t; + typedef std::list rlv_blocked_object_list_t; typedef std::multimap rlv_exception_map_t; protected: rlv_object_map_t m_Objects; // Map of objects that have active restrictions (idObj -> RlvObject) + rlv_blocked_object_list_t m_BlockedObjects; // List of (attached) objects that can't issue commands rlv_exception_map_t m_Exceptions; // Map of currently active restriction exceptions (ERlvBehaviour -> RlvException) S16 m_Behaviours[RLV_BHVR_COUNT]; @@ -230,6 +246,9 @@ protected: rlv_behaviour_signal_t m_OnBehaviourToggle; rlv_command_signal_t m_OnCommand; mutable std::list m_CommandHandlers; + boost::signals2::scoped_connection m_ExperienceEventConn; + boost::signals2::scoped_connection m_TeleportFailedConn; + boost::signals2::scoped_connection m_TeleportFinishedConn; static bool m_fEnabled; // Use setEnabled() to toggle this diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index ba50bac1e7..d509fae8ed 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -108,6 +108,7 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() addModifier(RLV_BHVR_FARTOUCH, RLV_MODIFIER_FARTOUCHDIST, new RlvBehaviourModifier("Fartouch Distance", RLV_MODIFIER_FARTOUCH_DEFAULT, true, new RlvBehaviourModifierCompMin)); addEntry(new RlvBehaviourGenericProcessor("fly", RLV_BHVR_FLY)); addEntry(new RlvBehaviourGenericProcessor("interact", RLV_BHVR_INTERACT, RlvBehaviourInfo::BHVR_EXTENDED)); + addEntry(new RlvBehaviourGenericProcessor("jump", RLV_BHVR_JUMP)); addEntry(new RlvBehaviourInfo("notify", RLV_BHVR_NOTIFY, RLV_TYPE_ADDREM)); addEntry(new RlvBehaviourGenericProcessor("permissive", RLV_BHVR_PERMISSIVE)); addEntry(new RlvBehaviourGenericProcessor("recvchat", RLV_BHVR_RECVCHAT, RlvBehaviourInfo::BHVR_STRICT)); @@ -182,6 +183,8 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() // Camera addEntry(new RlvBehaviourGenericToggleProcessor("setcam")); + addEntry(new RlvBehaviourGenericProcessor("setcam_avdist", RLV_BHVR_SETCAM_AVDIST)); + addModifier(RLV_BHVR_SETCAM_AVDIST, RLV_MODIFIER_SETCAM_AVDIST, new RlvBehaviourModifier("Camera - Silhouette Distance", 0.0f, false, new RlvBehaviourModifierCompMax())); addEntry(new RlvBehaviourGenericProcessor("setcam_avdistmin", RLV_BHVR_SETCAM_AVDISTMIN, RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addModifier(RLV_BHVR_SETCAM_AVDISTMIN, RLV_MODIFIER_SETCAM_AVDISTMIN, new RlvBehaviourModifierHandler("Camera - Avatar Distance (Min)", 0.0f, false, new RlvBehaviourModifierCompMax())); addEntry(new RlvBehaviourGenericProcessor("setcam_avdistmax", RLV_BHVR_SETCAM_AVDISTMAX, RlvBehaviourInfo::BHVR_EXPERIMENTAL)); @@ -203,6 +206,7 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() addModifier(RLV_BHVR_SETCAM_TEXTURES, RLV_MODIFIER_SETCAM_TEXTURE, new RlvBehaviourModifierHandler("Camera - Forced Texture", IMG_DEFAULT, true, nullptr)); addEntry(new RlvBehaviourGenericToggleProcessor("setcam_unlock")); // Camera (compatibility shim - to be deprecated) + addEntry(new RlvBehaviourGenericProcessor("camavdist", RLV_BHVR_SETCAM_AVDIST, RlvBehaviourInfo::BHVR_SYNONYM | RlvBehaviourInfo::BHVR_DEPRECATED)); addEntry(new RlvBehaviourGenericProcessor("camdistmin", RLV_BHVR_SETCAM_AVDISTMIN, RlvBehaviourInfo::BHVR_SYNONYM | RlvBehaviourInfo::BHVR_DEPRECATED)); addEntry(new RlvBehaviourGenericProcessor("camdistmax", RLV_BHVR_SETCAM_AVDISTMAX, RlvBehaviourInfo::BHVR_SYNONYM | RlvBehaviourInfo::BHVR_DEPRECATED)); addEntry(new RlvBehaviourGenericProcessor("camtextures", RLV_BHVR_SETCAM_TEXTURES, RlvBehaviourInfo::BHVR_SYNONYM | RlvBehaviourInfo::BHVR_DEPRECATED)); @@ -259,6 +263,7 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() // addEntry(new RlvBehaviourInfo("adjustheight", RLV_BHVR_ADJUSTHEIGHT, RLV_TYPE_FORCE)); addEntry(new RlvForceProcessor("detachme")); + addEntry(new RlvForceProcessor("fly")); addEntry(new RlvForceProcessor("setcam_focus", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addEntry(new RlvForceProcessor("setcam_eyeoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addEntry(new RlvForceProcessor("setcam_focusoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); @@ -703,6 +708,25 @@ bool RlvCommandOptionHelper::parseOption(const std::string& strOption, int& return true; } +template<> +bool RlvCommandOptionHelper::parseOption(const std::string& strOption, bool& fOption) +{ + try + { + // Try and parse it as a number first + int nOption = std::stoi(strOption); + fOption = (bool)nOption; + return (nOption == 0) || (nOption == 1); + } + catch (const std::invalid_argument&) + { + // Then try and parse it as true/false + std::istringstream ss(strOption); + ss >> std::boolalpha >> fOption; + return !ss.fail(); + } +} + template<> bool RlvCommandOptionHelper::parseOption(const std::string& strOption, float& nOption) { diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 0ca9d678f8..494f14792b 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -12249,12 +12249,18 @@ Changes won't take effect until after you restart [APP_NAME].