/**
*
* Copyright (c) 2009-2020, Kitty Barnett
*
* The source code in this file is provided to you under the terms of the
* GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt
* in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt
*
* By copying, modifying or distributing this software, you acknowledge that
* you have read and understood your obligations described above, and agree to
* abide by those obligations.
*
*/
#include "llviewerprecompiledheaders.h"
#include "llappearancemgr.h"
#include "llattachmentsmgr.h"
#include "llfeaturemanager.h"
#include "llgesturemgr.h"
#include "llnotificationsutil.h"
#include "llviewerobjectlist.h"
#include "pipeline.h"
// FIRE-4453 bridge detached by the RLV command @remattach=force
#include "fslslbridge.h"
// FIRE-4453
#include "rlvcommon.h"
#include "rlveffects.h"
#include "rlvhelper.h"
#include "rlvhandler.h"
#include "rlvinventory.h"
#include "rlvmodifiers.h"
#include
// ============================================================================
// RlvBehaviourDictionary
//
/*
* Processing of RLVa commands used to be a big switch/case loop with one function for each command type(addrem, reply
* and force). This is slowly being replaced with templated command handling which might be more confusing intially
* (also due to my poor naming schemes) but is actually far simpler and less error-prone than the old way.
*
* In the general case you just add a definition for the command below and then write the function body in rlvhandler.cpp
* and you're done! Told you this was easy.
*
* Reply command
* =============
* Definition: RlvReplyProcessor("commandname"[, ]));
* Implement : ERlvCmdRet RlvReplyHandler::onCommand(const RlvCommand& rlvCmd, std::string& strReply)
*
* Force command
* =============
* Definition: new RlvForceProcessor("commandname"[, ]));
* Implement : ERlvCmdRet RlvForceProcessor::onCommand(const RlvCommand& rlvCmd)
*
* Behaviours
* ==========
* Behaviours come in many forms but the added complexity is only in the variety of choices. The implementation is as
* easy as reply or force commands.
*
* For simple behaviours that only require recordkeeping and don't run code when set/unset (see ERlvBehaviourOptionType):
* Definition: RlvBehaviourGenericProcessor("commandname", RLV_BHVR_COMMANDNAME)
* Implement : nothing! (it automagically works)
* For simple behaviours that only require recordkeeping and only run code when they toggle:
* Definition: RlvBehaviourGenericToggleProcessor("commandname"))
* Implement : void RlvBehaviourToggleHandler::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr)
* For behaviours that require manual processing:
* Definition: RlvBehaviourProcessor("commandname"))
* Implement : ERlvCmdRet RlvBehaviourHandler::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
* For behaviours that run code when their modifier changes:
* Definition: addModifier(RLV_BHVR_COMMANDNAME, RLV_MODIFIER_COMMANDNAME, new RlvBehaviourModifierHandler(, , ));
* Implement : void RlvBehaviourModifierHandler::onValueChanged()
*
*/
RlvBehaviourDictionary::RlvBehaviourDictionary()
{
// Array auto-initialization to 0 is still not supported in VS2013
memset(m_BehaviourModifiers, 0, sizeof(RlvBehaviourModifier*) * RLV_MODIFIER_COUNT);
//
// Restrictions
//
addEntry(new RlvBehaviourGenericProcessor("acceptpermission", RLV_BHVR_ACCEPTPERMISSION));
addEntry(new RlvBehaviourGenericProcessor("accepttp", RLV_BHVR_ACCEPTTP, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourGenericProcessor("accepttprequest", RLV_BHVR_ACCEPTTPREQUEST, RlvBehaviourInfo::BHVR_STRICT | RlvBehaviourInfo::BHVR_EXTENDED));
addEntry(new RlvBehaviourProcessor("addattach"));
addEntry(new RlvBehaviourInfo("addoutfit", RLV_BHVR_ADDOUTFIT, RLV_TYPE_ADDREM));
addEntry(new RlvBehaviourGenericProcessor("allowidle", RLV_BHVR_ALLOWIDLE, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourGenericProcessor("alwaysrun", RLV_BHVR_ALWAYSRUN));
addEntry(new RlvBehaviourInfo("attachthis", RLV_BHVR_ATTACHTHIS, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_NODE));
addEntry(new RlvBehaviourInfo("attachallthis", RLV_BHVR_ATTACHTHIS, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_SUBTREE));
addEntry(new RlvBehaviourInfo("attachthis_except", RLV_BHVR_ATTACHTHISEXCEPT, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_NODE));
addEntry(new RlvBehaviourInfo("attachallthis_except", RLV_BHVR_ATTACHTHISEXCEPT, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_SUBTREE));
addEntry(new RlvBehaviourGenericToggleProcessor("buy"));
addEntry(new RlvBehaviourGenericProcessor("chatwhisper", RLV_BHVR_CHATWHISPER));
addEntry(new RlvBehaviourGenericProcessor("chatnormal", RLV_BHVR_CHATNORMAL));
addEntry(new RlvBehaviourGenericProcessor("chatshout", RLV_BHVR_CHATSHOUT));
addEntry(new RlvBehaviourProcessor("detach"));
addEntry(new RlvBehaviourInfo("detachthis", RLV_BHVR_DETACHTHIS, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_NODE));
addEntry(new RlvBehaviourInfo("detachallthis", RLV_BHVR_DETACHTHIS, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_SUBTREE));
addEntry(new RlvBehaviourInfo("detachthis_except", RLV_BHVR_DETACHTHISEXCEPT, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_NODE));
addEntry(new RlvBehaviourInfo("detachallthis_except", RLV_BHVR_DETACHTHISEXCEPT, RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_SUBTREE));
addEntry(new RlvBehaviourGenericToggleProcessor("edit"));
addEntry(new RlvBehaviourGenericProcessor("editattach", RLV_BHVR_EDITATTACH));
addEntry(new RlvBehaviourGenericProcessor("editobj", RLV_BHVR_EDITOBJ));
addEntry(new RlvBehaviourGenericProcessor("editworld", RLV_BHVR_EDITWORLD));
addEntry(new RlvBehaviourGenericToggleProcessor("viewtransparent", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourGenericToggleProcessor("viewwireframe", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourGenericProcessor("emote", RLV_BHVR_EMOTE));
addEntry(new RlvBehaviourGenericProcessor("fartouch", RLV_BHVR_FARTOUCH));
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 RlvBehaviourGenericToggleProcessor("pay"));
addEntry(new RlvBehaviourGenericProcessor("permissive", RLV_BHVR_PERMISSIVE));
addEntry(new RlvBehaviourGenericProcessor("recvchat", RLV_BHVR_RECVCHAT, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourGenericProcessor("recvchatfrom", RLV_BHVR_RECVCHATFROM, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourGenericProcessor("recvemote", RLV_BHVR_RECVEMOTE, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourGenericProcessor("recvemotefrom", RLV_BHVR_RECVEMOTEFROM, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourProcessor("recvim", RlvBehaviourInfo::BHVR_STRICT));
addModifier(RLV_BHVR_RECVIM, RLV_MODIFIER_RECVIMDISTMIN, new RlvBehaviourModifier("RecvIM Distance (Min)", F32_MAX, true, new RlvBehaviourModifierCompMax));
addModifier(RLV_BHVR_RECVIM, RLV_MODIFIER_RECVIMDISTMAX, new RlvBehaviourModifier("RecvIM Distance (Max)", F32_MAX, true, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericProcessor("recvimfrom", RLV_BHVR_RECVIMFROM, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourInfo("redirchat", RLV_BHVR_REDIRCHAT, RLV_TYPE_ADDREM));
addEntry(new RlvBehaviourInfo("rediremote", RLV_BHVR_REDIREMOTE, RLV_TYPE_ADDREM));
addEntry(new RlvBehaviourProcessor("remattach"));
addEntry(new RlvBehaviourInfo("remoutfit", RLV_BHVR_REMOUTFIT, RLV_TYPE_ADDREM));
addEntry(new RlvBehaviourGenericProcessor("rez", RLV_BHVR_REZ));
addEntry(new RlvBehaviourProcessor("sendchannel", RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourProcessor("sendchannel_except", RlvBehaviourInfo::BHVR_STRICT | RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourGenericProcessor("sendchat", RLV_BHVR_SENDCHAT));
addEntry(new RlvBehaviourToggleProcessor("sendim", RlvBehaviourInfo::BHVR_STRICT));
addModifier(RLV_BHVR_SENDIM, RLV_MODIFIER_SENDIMDISTMIN, new RlvBehaviourModifier("SendIM Distance (Min)", F32_MAX, true, new RlvBehaviourModifierCompMax));
addModifier(RLV_BHVR_SENDIM, RLV_MODIFIER_SENDIMDISTMAX, new RlvBehaviourModifier("SendIM Distance (Max)", F32_MAX, true, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericProcessor("sendimto", RLV_BHVR_SENDIMTO, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourGenericProcessor("sendgesture", RLV_BHVR_SENDGESTURE, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourGenericToggleProcessor("setdebug"));
addEntry(new RlvBehaviourGenericToggleProcessor("setenv"));
addEntry(new RlvBehaviourGenericProcessor("setgroup", RLV_BHVR_SETGROUP));
addEntry(new RlvBehaviourGenericProcessor("share", RLV_BHVR_SHARE, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourInfo("sharedunwear", RLV_BHVR_SHAREDUNWEAR, RLV_TYPE_ADDREM, RlvBehaviourInfo::BHVR_EXTENDED));
addEntry(new RlvBehaviourInfo("sharedwear", RLV_BHVR_SHAREDWEAR, RLV_TYPE_ADDREM, RlvBehaviourInfo::BHVR_EXTENDED));
addEntry(new RlvBehaviourProcessor("showhovertext"));
addEntry(new RlvBehaviourGenericProcessor("showhovertextall", RLV_BHVR_SHOWHOVERTEXTALL));
addEntry(new RlvBehaviourGenericProcessor("showhovertexthud", RLV_BHVR_SHOWHOVERTEXTHUD));
addEntry(new RlvBehaviourGenericProcessor("showhovertextworld", RLV_BHVR_SHOWHOVERTEXTWORLD));
addEntry(new RlvBehaviourGenericToggleProcessor("showinv"));
addEntry(new RlvBehaviourGenericProcessor("showloc", RLV_BHVR_SHOWLOC));
addEntry(new RlvBehaviourGenericProcessor("showminimap", RLV_BHVR_SHOWMINIMAP));
addEntry(new RlvBehaviourToggleProcessor("shownames", RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourProcessor("shownametags", RlvBehaviourInfo::BHVR_STRICT));
addModifier(RLV_BHVR_SHOWNAMETAGS, RLV_MODIFIER_SHOWNAMETAGSDIST, new RlvBehaviourModifierHandler("Name Tags - Visible Distance", 0.0f, true, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericToggleProcessor("shownearby", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourGenericToggleProcessor("showself", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourGenericToggleProcessor("showselfhead", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourGenericProcessor("showworldmap", RLV_BHVR_SHOWWORLDMAP));
addEntry(new RlvBehaviourGenericProcessor("sit", RLV_BHVR_SIT));
addEntry(new RlvBehaviourGenericProcessor("sittp", RLV_BHVR_SITTP));
addModifier(RLV_BHVR_SITTP, RLV_MODIFIER_SITTPDIST, new RlvBehaviourModifier("SitTp Distance", RLV_MODIFIER_SITTP_DEFAULT, true, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericProcessor("standtp", RLV_BHVR_STANDTP));
addEntry(new RlvBehaviourProcessor("startim", RlvBehaviourInfo::BHVR_STRICT));
addModifier(RLV_BHVR_STARTIM, RLV_MODIFIER_STARTIMDISTMIN, new RlvBehaviourModifier("StartIM Distance (Min)", F32_MAX, true, new RlvBehaviourModifierCompMax));
addModifier(RLV_BHVR_STARTIM, RLV_MODIFIER_STARTIMDISTMAX, new RlvBehaviourModifier("StartIM Distance (Max)", F32_MAX, true, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericProcessor("startimto", RLV_BHVR_STARTIMTO, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourGenericProcessor("temprun", RLV_BHVR_TEMPRUN));
addEntry(new RlvBehaviourGenericProcessor("touchall", RLV_BHVR_TOUCHALL));
addEntry(new RlvBehaviourGenericProcessor("touchattach", RLV_BHVR_TOUCHATTACH));
addEntry(new RlvBehaviourGenericProcessor("touchattachother", RLV_BHVR_TOUCHATTACHOTHER));
addEntry(new RlvBehaviourGenericProcessor("touchattachself", RLV_BHVR_TOUCHATTACHSELF));
addEntry(new RlvBehaviourGenericProcessor("touchfar", RLV_BHVR_FARTOUCH, RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourGenericProcessor("touchhud", RLV_BHVR_TOUCHHUD, RlvBehaviourInfo::BHVR_EXTENDED));
addEntry(new RlvBehaviourGenericProcessor("touchme", RLV_BHVR_TOUCHME));
addEntry(new RlvBehaviourGenericProcessor("touchthis", RLV_BHVR_TOUCHTHIS));
addEntry(new RlvBehaviourGenericProcessor("touchworld", RLV_BHVR_TOUCHWORLD));
addEntry(new RlvBehaviourGenericProcessor("tplm", RLV_BHVR_TPLM));
addEntry(new RlvBehaviourGenericProcessor("tploc", RLV_BHVR_TPLOC));
addEntry(new RlvBehaviourGenericProcessor("tplocal", RLV_BHVR_TPLOCAL, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addModifier(RLV_BHVR_TPLOCAL, RLV_MODIFIER_TPLOCALDIST, new RlvBehaviourModifier("Local Teleport Distance", RLV_MODIFIER_TPLOCAL_DEFAULT, true, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericProcessor("tplure", RLV_BHVR_TPLURE, RlvBehaviourInfo::BHVR_STRICT));
addEntry(new RlvBehaviourGenericProcessor("tprequest", RLV_BHVR_TPREQUEST, RlvBehaviourInfo::BHVR_STRICT | RlvBehaviourInfo::BHVR_EXTENDED));
addEntry(new RlvBehaviourInfo("unsharedunwear", RLV_BHVR_UNSHAREDUNWEAR, RLV_TYPE_ADDREM));
addEntry(new RlvBehaviourInfo("unsharedwear", RLV_BHVR_UNSHAREDWEAR, RLV_TYPE_ADDREM));
addEntry(new RlvBehaviourGenericProcessor("unsit", RLV_BHVR_UNSIT));
addEntry(new RlvBehaviourGenericProcessor("viewnote", RLV_BHVR_VIEWNOTE));
addEntry(new RlvBehaviourGenericProcessor("viewscript", RLV_BHVR_VIEWSCRIPT));
addEntry(new RlvBehaviourGenericProcessor("viewtexture", RLV_BHVR_VIEWTEXTURE));
// 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));
addModifier(RLV_BHVR_SETCAM_AVDISTMAX, RLV_MODIFIER_SETCAM_AVDISTMAX, new RlvBehaviourModifier("Camera - Avatar Distance (Max)", F32_MAX, false, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericProcessor("setcam_origindistmin", RLV_BHVR_SETCAM_ORIGINDISTMIN, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addModifier(RLV_BHVR_SETCAM_ORIGINDISTMIN, RLV_MODIFIER_SETCAM_ORIGINDISTMIN, new RlvBehaviourModifier("Camera - Focus Distance (Min)", 0.0f, true, new RlvBehaviourModifierCompMax));
addEntry(new RlvBehaviourGenericProcessor("setcam_origindistmax", RLV_BHVR_SETCAM_ORIGINDISTMAX, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addModifier(RLV_BHVR_SETCAM_ORIGINDISTMAX, RLV_MODIFIER_SETCAM_ORIGINDISTMAX, new RlvBehaviourModifier("Camera - Focus Distance (Max)", F32_MAX, true, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericToggleProcessor("setcam_eyeoffset"));
addModifier(RLV_BHVR_SETCAM_EYEOFFSET, RLV_MODIFIER_SETCAM_EYEOFFSET, new RlvBehaviourModifierHandler("Camera - Eye Offset", LLVector3::zero, true, nullptr));
addEntry(new RlvBehaviourGenericToggleProcessor("setcam_eyeoffsetscale"));
addModifier(RLV_BHVR_SETCAM_EYEOFFSETSCALE, RLV_MODIFIER_SETCAM_EYEOFFSETSCALE, new RlvBehaviourModifierHandler("Camera - Eye Offset Scale", 0.0f, true, nullptr));
addEntry(new RlvBehaviourGenericToggleProcessor("setcam_focusoffset"));
addModifier(RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_MODIFIER_SETCAM_FOCUSOFFSET, new RlvBehaviourModifierHandler("Camera - Focus Offset", LLVector3d::zero, true, nullptr));
addEntry(new RlvBehaviourProcessor("setcam_fovmin"));
addModifier(RLV_BHVR_SETCAM_FOVMIN, RLV_MODIFIER_SETCAM_FOVMIN, new RlvBehaviourModifierHandler("Camera - FOV (Min)", DEFAULT_FIELD_OF_VIEW, true, new RlvBehaviourModifierCompMax));
addEntry(new RlvBehaviourProcessor("setcam_fovmax"));
addModifier(RLV_BHVR_SETCAM_FOVMAX, RLV_MODIFIER_SETCAM_FOVMAX, new RlvBehaviourModifierHandler("Camera - FOV (Max)", DEFAULT_FIELD_OF_VIEW, true, new RlvBehaviourModifierCompMin));
addEntry(new RlvBehaviourGenericToggleProcessor("setcam_mouselook"));
addEntry(new RlvBehaviourGenericProcessor("setcam_textures", RLV_BHVR_SETCAM_TEXTURES));
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));
addEntry(new RlvBehaviourProcessor("camzoommin", RlvBehaviourInfo::BHVR_DEPRECATED));
addEntry(new RlvBehaviourProcessor("camzoommax", RlvBehaviourInfo::BHVR_DEPRECATED));
addEntry(new RlvBehaviourGenericToggleProcessor("camunlock", RlvBehaviourInfo::BHVR_SYNONYM | RlvBehaviourInfo::BHVR_DEPRECATED));
// Overlay
RlvBehaviourInfo* pSetOverlayBhvr = new RlvBehaviourProcessor("setoverlay");
pSetOverlayBhvr->addModifier(ERlvLocalBhvrModifier::OverlayAlpha, typeid(float), "alpha", &RlvOverlayEffect::onAlphaValueChanged);
pSetOverlayBhvr->addModifier(ERlvLocalBhvrModifier::OverlayTexture, typeid(LLUUID), "texture", &RlvOverlayEffect::onTextureChanged);
pSetOverlayBhvr->addModifier(ERlvLocalBhvrModifier::OverlayTint, typeid(LLVector3), "tint", &RlvOverlayEffect::onColorValueChanged);
addEntry(pSetOverlayBhvr);
addEntry(new RlvBehaviourProcessor("setoverlay_touch"));
addEntry(new RlvForceProcessor("setoverlay_tween"));
// Sphere
RlvBehaviourInfo* pSetSphereBhvr = new RlvBehaviourProcessor("setsphere");
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereMode, typeid(int), "mode", &RlvSphereEffect::onModeChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereOrigin, typeid(int), "origin", &RlvSphereEffect::onOriginChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereColor, typeid(LLVector3), "color", &RlvSphereEffect::onColorChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereDistMin, typeid(float), "distmin", &RlvSphereEffect::onDistMinChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereDistMax, typeid(float), "distmax", &RlvSphereEffect::onDistMaxChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereDistExtend, typeid(int), "distextend", &RlvSphereEffect::onDistExtendChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereParams, typeid(LLVector4), "param", &RlvSphereEffect::onParamsChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereTween, typeid(float), "tween", &RlvSphereEffect::onTweenDurationChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereValueMin, typeid(float), "valuemin", &RlvSphereEffect::onValueMinChanged);
pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereValueMax, typeid(float), "valuemax", &RlvSphereEffect::onValueMaxChanged);
addEntry(pSetSphereBhvr);
//
// Force-wear
//
addEntry(new RlvBehaviourInfo("attach", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE));
addEntry(new RlvBehaviourInfo("attachall", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE));
addEntry(new RlvBehaviourInfo("attachover", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_ADD | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE));
addEntry(new RlvBehaviourInfo("attachallover", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_ADD | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE));
addEntry(new RlvBehaviourInfo("attachthis", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT));
addEntry(new RlvBehaviourInfo("attachallthis", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT));
addEntry(new RlvBehaviourInfo("attachthisover", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_ADD | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT));
addEntry(new RlvBehaviourInfo("attachallthisover", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_ADD | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT));
addEntry(new RlvForceProcessor("detach", RlvBehaviourInfo::FORCEWEAR_WEAR_REMOVE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE));
addEntry(new RlvBehaviourInfo("detachall", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REMOVE | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE));
addEntry(new RlvBehaviourInfo("detachthis", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REMOVE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT));
addEntry(new RlvBehaviourInfo("detachallthis", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REMOVE | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT));
addEntry(new RlvForceProcessor("remattach", RlvBehaviourInfo::FORCEWEAR_WEAR_REMOVE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE));
addEntry(new RlvForceProcessor("remoutfit", RlvBehaviourInfo::FORCEWEAR_WEAR_REMOVE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE));
// Synonyms (addoutfit* -> attach*)
addEntry(new RlvBehaviourInfo("addoutfit", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("addoutfitall", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("addoutfitover", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_ADD | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("addoutfitallover", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_ADD | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("addoutfitthis", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("addoutfitallthis", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("addoutfitthisover", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_ADD | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("addoutfitallthisover", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_ADD | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT | RlvBehaviourInfo::BHVR_SYNONYM));
// Synonyms (attach*overorreplace -> attach*)
addEntry(new RlvBehaviourInfo("attachoverorreplace", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("attachalloverorreplace", RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_NONE | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("attachthisoverorreplace",RLV_CMD_FORCEWEAR, RLV_TYPE_FORCE, RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_NODE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT | RlvBehaviourInfo::BHVR_SYNONYM));
addEntry(new RlvBehaviourInfo("attachallthisoverorreplace",RLV_CMD_FORCEWEAR,RLV_TYPE_FORCE,RlvBehaviourInfo::FORCEWEAR_WEAR_REPLACE | RlvBehaviourInfo::FORCEWEAR_SUBTREE | RlvBehaviourInfo::FORCEWEAR_CONTEXT_OBJECT | RlvBehaviourInfo::BHVR_SYNONYM));
//
// Force-only
//
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"));
addEntry(new RlvForceProcessor("setcam_eyeoffsetscale"));
addEntry(new RlvForceProcessor("setcam_focusoffset"));
addEntry(new RlvForceProcessor("setcam_fov", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvForceProcessor("setcam_mode", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvForceProcessor("setgroup"));
addEntry(new RlvForceProcessor("sit"));
addEntry(new RlvForceProcessor("sitground"));
addEntry(new RlvForceProcessor("tpto"));
addEntry(new RlvBehaviourInfo("unsit", RLV_BHVR_UNSIT, RLV_TYPE_FORCE));
//
// Reply-only
//
addEntry(new RlvBehaviourInfo("findfolder", RLV_BHVR_FINDFOLDER, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("findfolders", RLV_BHVR_FINDFOLDERS, RLV_TYPE_REPLY, RlvBehaviourInfo::BHVR_EXTENDED));
addEntry(new RlvBehaviourInfo("getaddattachnames", RLV_BHVR_GETADDATTACHNAMES, RLV_TYPE_REPLY, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourInfo("getaddoutfitnames", RLV_BHVR_GETADDOUTFITNAMES, RLV_TYPE_REPLY, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourInfo("getattach", RLV_BHVR_GETATTACH, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("getattachnames", RLV_BHVR_GETATTACHNAMES, RLV_TYPE_REPLY, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvReplyProcessor("getcam_avdist", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvReplyProcessor("getcam_avdistmin", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvReplyProcessor("getcam_avdistmax", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvReplyProcessor("getcam_fov", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvReplyProcessor("getcam_fovmin", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvReplyProcessor("getcam_fovmax", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvReplyProcessor("getcam_textures", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvReplyProcessor("getcommand", RlvBehaviourInfo::BHVR_EXTENDED));
addEntry(new RlvBehaviourInfo("getgroup", RLV_BHVR_GETGROUP, RLV_TYPE_REPLY));
addEntry(new RlvReplyProcessor("getheightoffset", RlvBehaviourInfo::BHVR_EXTENDED));
addEntry(new RlvBehaviourInfo("getinv", RLV_BHVR_GETINV, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("getinvworn", RLV_BHVR_GETINVWORN, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("getoutfit", RLV_BHVR_GETOUTFIT, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("getoutfitnames", RLV_BHVR_GETOUTFITNAMES, RLV_TYPE_REPLY, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourInfo("getpath", RLV_BHVR_GETPATH, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("getpathnew", RLV_BHVR_GETPATHNEW, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("getremattachnames", RLV_BHVR_GETREMATTACHNAMES, RLV_TYPE_REPLY, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourInfo("getremoutfitnames", RLV_BHVR_GETREMOUTFITNAMES, RLV_TYPE_REPLY, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
addEntry(new RlvBehaviourInfo("getsitid", RLV_BHVR_GETSITID, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("getstatus", RLV_BHVR_GETSTATUS, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("getstatusall", RLV_BHVR_GETSTATUSALL, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("version", RLV_BHVR_VERSION, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("versionnew", RLV_BHVR_VERSIONNEW, RLV_TYPE_REPLY));
addEntry(new RlvBehaviourInfo("versionnum", RLV_BHVR_VERSIONNUM, RLV_TYPE_REPLY));
// Populate m_String2InfoMap (the tuple should be unique)
for (const RlvBehaviourInfo* pBhvrInfo : m_BhvrInfoList)
{
RLV_VERIFY(m_String2InfoMap.insert(std::make_pair(std::make_pair(pBhvrInfo->getBehaviour(), (ERlvParamType)pBhvrInfo->getParamTypeMask()), pBhvrInfo)).second == true);
}
// Populate m_Bhvr2InfoMap (there can be multiple entries per ERlvBehaviour)
for (const RlvBehaviourInfo* pBhvrInfo : m_BhvrInfoList)
{
if ( (pBhvrInfo->getParamTypeMask() & RLV_TYPE_ADDREM) && (!pBhvrInfo->isSynonym()) )
{
#ifdef RLV_DEBUG
for (const rlv_bhvr2info_map_t::value_type& itBhvr : boost::make_iterator_range(m_Bhvr2InfoMap.lower_bound(pBhvrInfo->getBehaviourType()), m_Bhvr2InfoMap.upper_bound(pBhvrInfo->getBehaviourType())))
{
RLV_ASSERT( (itBhvr.first != pBhvrInfo->getBehaviourType()) || (itBhvr.second->getBehaviourFlags() != pBhvrInfo->getBehaviourFlags()) );
}
#endif // RLV_DEBUG
m_Bhvr2InfoMap.insert(std::pair(pBhvrInfo->getBehaviourType(), pBhvrInfo));
}
}
// Process blocked commands
if (RlvSettings::getNoSetEnv())
toggleBehaviourFlag("setenv", RLV_TYPE_ADDREM, RlvBehaviourInfo::BHVR_BLOCKED, true);
}
RlvBehaviourDictionary::~RlvBehaviourDictionary()
{
for (const RlvBehaviourInfo* pBhvrInfo : m_BhvrInfoList)
delete pBhvrInfo;
m_BhvrInfoList.clear();
for (int idxBhvrMod = 0; idxBhvrMod < RLV_MODIFIER_COUNT; idxBhvrMod++)
{
delete m_BehaviourModifiers[idxBhvrMod];
m_BehaviourModifiers[idxBhvrMod] = nullptr;
}
}
void RlvBehaviourDictionary::addEntry(const RlvBehaviourInfo* pEntry)
{
// Filter experimental commands (if disabled)
static LLCachedControl sEnableExperimental(gSavedSettings, "RLVaExperimentalCommands");
if ( (!pEntry) || ((!sEnableExperimental) && (pEntry->isExperimental())) )
{
return;
}
// Sanity check for duplicate entries
#ifndef LL_RELEASE_FOR_DOWNLOAD
std::for_each(m_BhvrInfoList.begin(), m_BhvrInfoList.end(),
[&pEntry](const RlvBehaviourInfo* pBhvrInfo) {
RLV_ASSERT_DBG( ((pBhvrInfo->getBehaviour()) != (pEntry->getBehaviour())) || ((pBhvrInfo->getParamTypeMask() & pEntry->getParamTypeMask()) == 0) );
});
#endif // LL_RELEASE_FOR_DOWNLOAD
m_BhvrInfoList.push_back(pEntry);
}
void RlvBehaviourDictionary::addModifier(ERlvBehaviour eBhvr, ERlvBehaviourModifier eModifier, RlvBehaviourModifier* pModifierEntry)
{
if (eModifier < RLV_MODIFIER_COUNT)
{
m_BehaviourModifiers[eModifier] = pModifierEntry;
m_Bhvr2ModifierMap.insert(std::make_pair(eBhvr, eModifier));
}
}
// Convenience function to add both the behaviour entry as well as the corresponding modifier entry
void RlvBehaviourDictionary::addModifier(const RlvBehaviourInfo* pBhvrEntry, ERlvBehaviourModifier eModifier, RlvBehaviourModifier* pModifierEntry)
{
addEntry(pBhvrEntry);
addModifier(pBhvrEntry->getBehaviourType(), eModifier, pModifierEntry);
}
// TODO: this shouldn't be in this class - find a better spot for it
void RlvBehaviourDictionary::clearModifiers(const LLUUID& idRlvObj)
{
for (int idxModifier = 0; idxModifier < RLV_MODIFIER_COUNT; idxModifier++)
{
if (m_BehaviourModifiers[idxModifier])
m_BehaviourModifiers[idxModifier]->clearValues(idRlvObj);
}
}
const RlvBehaviourInfo* RlvBehaviourDictionary::getBehaviourInfo(ERlvBehaviour eBhvr, ERlvParamType eParamType) const
{
const RlvBehaviourInfo* pBhvrInfo = nullptr;
for (auto itBhvrLower = m_Bhvr2InfoMap.lower_bound(eBhvr), itBhvrUpper = m_Bhvr2InfoMap.upper_bound(eBhvr);
std::find_if(itBhvrLower, itBhvrUpper, [eParamType](const rlv_bhvr2info_map_t::value_type& bhvrEntry) { return bhvrEntry.second->getParamTypeMask() == eParamType; }) != itBhvrUpper;
++itBhvrLower)
{
if (pBhvrInfo)
return nullptr;
pBhvrInfo = itBhvrLower->second;
}
return pBhvrInfo;
}
const RlvBehaviourInfo* RlvBehaviourDictionary::getBehaviourInfo(const std::string& strBhvr, ERlvParamType eParamType, bool* pfStrict, ERlvLocalBhvrModifier* peBhvrModifier) 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 fStrict = (strBhvrLastPart.compare("sec") == 0);
if (pfStrict)
*pfStrict = fStrict;
ERlvLocalBhvrModifier eBhvrModifier = ERlvLocalBhvrModifier::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));
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)) != ERlvLocalBhvrModifier::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
{
ERlvLocalBhvrModifier 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 && ERlvLocalBhvrModifier::Unknown == eBhvrModifier) ? pBhvrInfo->getBehaviourType() : RLV_BHVR_UNKNOWN;
}
bool RlvBehaviourDictionary::getCommands(const std::string& strMatch, ERlvParamType eParamType, std::list& cmdList) const
{
cmdList.clear();
for (const RlvBehaviourInfo* pBhvrInfo : m_BhvrInfoList)
{
if ( (pBhvrInfo->getParamTypeMask() & eParamType) || (RLV_TYPE_UNKNOWN == eParamType) )
{
std::string strCmd = pBhvrInfo->getBehaviour();
if ( (std::string::npos != strCmd.find(strMatch)) || (strMatch.empty()) )
cmdList.push_back(strCmd);
if ( (pBhvrInfo->hasStrict()) && ((std::string::npos != strCmd.append("_sec").find(strMatch)) || (strMatch.empty())) )
cmdList.push_back(strCmd);
}
}
return !cmdList.empty();
}
bool RlvBehaviourDictionary::getHasStrict(ERlvBehaviour eBhvr) const
{
for (const rlv_bhvr2info_map_t::value_type& itBhvr : boost::make_iterator_range(m_Bhvr2InfoMap.lower_bound(eBhvr), m_Bhvr2InfoMap.upper_bound(eBhvr)))
{
// Only restrictions can be strict
if (RLV_TYPE_ADDREM != itBhvr.second->getParamTypeMask())
continue;
return itBhvr.second->hasStrict();
}
RLV_ASSERT(false);
return false;
}
RlvBehaviourModifier* RlvBehaviourDictionary::getModifierFromBehaviour(ERlvBehaviour eBhvr) const
{
rlv_bhvr2mod_map_t::const_iterator itMod = m_Bhvr2ModifierMap.find(eBhvr);
ERlvBehaviourModifier eBhvrMod = (m_Bhvr2ModifierMap.end() != itMod) ? itMod->second : RLV_MODIFIER_UNKNOWN;
return (eBhvrMod < RLV_MODIFIER_UNKNOWN) ? m_BehaviourModifiers[eBhvrMod] : nullptr;
}
void RlvBehaviourDictionary::toggleBehaviourFlag(const std::string& strBhvr, ERlvParamType eParamType, RlvBehaviourInfo::EBehaviourFlags eBhvrFlag, bool fEnable)
{
rlv_string2info_map_t::const_iterator itBhvr = m_String2InfoMap.find(std::make_pair(strBhvr, (eParamType & RLV_TYPE_ADDREM) ? RLV_TYPE_ADDREM : eParamType));
if (m_String2InfoMap.end() != itBhvr)
{
const_cast(itBhvr->second)->toggleBehaviourFlag(eBhvrFlag, fEnable);
}
}
// ============================================================================
// RlvBehaviourInfo
//
// virtual
ERlvCmdRet RlvBehaviourInfo::processModifier(const RlvCommand& rlvCmd) const
{
// The object should have the base behaviour set (or else there's nothing to modify)
if (!gRlvHandler.hasBehaviour(rlvCmd.getObjectID(), rlvCmd.getBehaviourType()))
return RLV_RET_FAILED_UNHELDBEHAVIOUR;
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
//
RlvBehaviourModifier::RlvBehaviourModifier(std::string strName, const RlvBehaviourModifierValue& defaultValue, bool fAddDefaultOnEmpty, RlvBehaviourModifierComp* pValueComparator)
: m_strName(strName), m_DefaultValue(defaultValue), m_fAddDefaultOnEmpty(fAddDefaultOnEmpty), m_pValueComparator(pValueComparator)
{
m_pValueComparator = (pValueComparator) ? pValueComparator : new RlvBehaviourModifierComp();
}
RlvBehaviourModifier::~RlvBehaviourModifier()
{
if (m_pValueComparator)
{
delete m_pValueComparator;
m_pValueComparator = NULL;
}
}
bool RlvBehaviourModifier::addValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idRlvObj, ERlvBehaviour eBhvr)
{
if (modValue.which() == m_DefaultValue.which())
{
m_Values.insert((m_pValueComparator) ? std::lower_bound(m_Values.begin(), m_Values.end(), std::make_tuple(modValue, idRlvObj, eBhvr), boost::bind(&RlvBehaviourModifierComp::operator(), m_pValueComparator, _1, _2)) : m_Values.end(), std::make_tuple(modValue, idRlvObj, eBhvr));
// NOTE: change signal needs to trigger before modifier handlers so cached values have a chance to update properly
m_ChangeSignal(getValue());
onValueChange();
return true;
}
return false;
}
void RlvBehaviourModifier::clearValues(const LLUUID& idRlvObj)
{
size_t origCount = m_Values.size();
m_Values.erase(std::remove_if(m_Values.begin(), m_Values.end(),
[&idRlvObj](const RlvBehaviourModifierValueTuple& modValue) {
return (std::get<1>(modValue) == idRlvObj) && (std::get<2>(modValue) == RLV_BHVR_UNKNOWN);
}), m_Values.end());
if (origCount != m_Values.size())
{
onValueChange();
m_ChangeSignal(getValue());
}
}
const LLUUID& RlvBehaviourModifier::getPrimaryObject() const
{
return (m_pValueComparator) ? m_pValueComparator->m_idPrimaryObject : LLUUID::null;
}
bool RlvBehaviourModifier::hasValue() const {
// If no primary object is set this returns "any value set"; otherwise it returns "any value set by the primary object"
if ( (!m_pValueComparator) || (m_pValueComparator->m_idPrimaryObject.isNull()) )
return !m_Values.empty();
return (!m_Values.empty()) ? std::get<1>(m_Values.front()) == m_pValueComparator->m_idPrimaryObject : false;
}
bool RlvBehaviourModifier::hasValue(const LLUUID& idRlvObj) const
{
return m_Values.end() != std::find_if(m_Values.begin(), m_Values.end(), [&idRlvObj](const RlvBehaviourModifierValueTuple& cmpValue) { return std::get<1>(cmpValue) == idRlvObj; });
}
void RlvBehaviourModifier::removeValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idRlvObj, ERlvBehaviour eBhvr)
{
if ( (modValue.which() == m_DefaultValue.which()) )
{
auto itValue = std::find(m_Values.begin(), m_Values.end(), std::make_tuple(modValue, idRlvObj, eBhvr));
if (m_Values.end() != itValue)
{
m_Values.erase(itValue);
onValueChange();
m_ChangeSignal(getValue());
}
}
}
void RlvBehaviourModifier::setPrimaryObject(const LLUUID& idPrimaryObject)
{
if (m_pValueComparator)
{
m_pValueComparator->m_idPrimaryObject = idPrimaryObject;
m_Values.sort(boost::bind(&RlvBehaviourModifierComp::operator(), m_pValueComparator, _1, _2));
onValueChange();
m_ChangeSignal(getValue());
}
}
void RlvBehaviourModifier::setValue(const RlvBehaviourModifierValue& modValue, const LLUUID& idRlvObj)
{
if ( (modValue.which() == m_DefaultValue.which()) )
{
auto itValue = std::find_if(m_Values.begin(), m_Values.end(),
[&idRlvObj](const RlvBehaviourModifierValueTuple& cmpValue) {
return (std::get<1>(cmpValue) == idRlvObj) && (std::get<2>(cmpValue) == RLV_BHVR_UNKNOWN);
});
if (m_Values.end() != itValue)
{
// The object already has a modifier value set => change it
std::get<0>(*itValue) = modValue;
if (m_pValueComparator)
m_Values.sort(boost::bind(&RlvBehaviourModifierComp::operator(), m_pValueComparator, _1, _2));
onValueChange();
m_ChangeSignal(getValue());
}
else
{
// The command doesn't have a modifier value yet => add it
addValue(modValue, idRlvObj, RLV_BHVR_UNKNOWN);
}
}
}
// static
bool RlvBehaviourModifier::convertOptionValue(const std::string& optionValue, const std::type_index& modType, RlvBehaviourModifierValue& modValue)
{
try
{
if (modType == typeid(float))
{
modValue = std::stof(optionValue);
return true;
}
else if (modType == typeid(int))
{
modValue = std::stoi(optionValue);
return true;
}
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))
{
modValue = vecOption;
return true;
}
}
else if (modType == typeid(LLVector4))
{
LLVector4 vecOption;
if (4 == sscanf(optionValue.c_str(), "%f/%f/%f/%f", vecOption.mV + 0, vecOption.mV + 1, vecOption.mV + 2, vecOption.mV + 3))
{
modValue = vecOption;
return true;
}
}
else if (modType == typeid(LLUUID))
{
LLUUID idOption;
if (LLUUID::parseUUID(optionValue, &idOption))
{
modValue = idOption;
return true;
}
}
return false;
}
catch (const std::invalid_argument&)
{
return false;
}
catch (const std::out_of_range&)
{
return false;
}
}
// ============================================================================
// RlvCommmand
//
RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCommand)
: m_idObj(idObj)
{
if ((m_fValid = parseCommand(strCommand, m_strBehaviour, m_strOption, m_strParam)))
{
S32 nTemp = 0;
if ( ("n" == m_strParam) || ("add" == m_strParam) )
m_eParamType = RLV_TYPE_ADD;
else if ( ("y" == m_strParam) || ("rem" == m_strParam) )
m_eParamType = RLV_TYPE_REMOVE;
else if (m_strBehaviour == "clear") // clear is the odd one out so just make it its own type
m_eParamType = RLV_TYPE_CLEAR;
else if ("force" == m_strParam)
m_eParamType = RLV_TYPE_FORCE;
else if (LLStringUtil::convertToS32(m_strParam, nTemp)) // Assume it's a reply command if we can convert to an S32
m_eParamType = RLV_TYPE_REPLY;
else
{
m_eParamType = RLV_TYPE_UNKNOWN;
m_fValid = false;
}
}
if (!m_fValid)
{
m_strOption = m_strParam = "";
return;
}
m_pBhvrInfo = RlvBehaviourDictionary::instance().getBehaviourInfo(m_strBehaviour, m_eParamType, &m_fStrict, &m_eBhvrModifier);
}
RlvCommand::RlvCommand(const RlvCommand& rlvCmd, ERlvParamType eParamType)
: m_fValid(rlvCmd.m_fValid), m_idObj(rlvCmd.m_idObj), m_strBehaviour(rlvCmd.m_strBehaviour), m_pBhvrInfo(rlvCmd.m_pBhvrInfo)
, m_eParamType( (RLV_TYPE_UNKNOWN == eParamType) ? rlvCmd.m_eParamType : eParamType), m_eBhvrModifier(rlvCmd.m_eBhvrModifier)
, m_fStrict(rlvCmd.m_fStrict), m_strOption(rlvCmd.m_strOption), m_strParam(rlvCmd.m_strParam), m_fRefCounted(rlvCmd.m_fRefCounted)
{
}
bool RlvCommand::parseCommand(const std::string& strCommand, std::string& strBehaviour, std::string& strOption, std::string& strParam)
{
// (See behaviour notes for the command parsing truth table)
// Format: [: