aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/rlvhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/newview/rlvhandler.cpp')
-rw-r--r--linden/indra/newview/rlvhandler.cpp2809
1 files changed, 1478 insertions, 1331 deletions
diff --git a/linden/indra/newview/rlvhandler.cpp b/linden/indra/newview/rlvhandler.cpp
index 18c0836..43a5ba3 100644
--- a/linden/indra/newview/rlvhandler.cpp
+++ b/linden/indra/newview/rlvhandler.cpp
@@ -1,12 +1,26 @@
1/**
2 *
3 * Copyright (c) 2009-2010, Kitty Barnett
4 *
5 * The source code in this file is provided to you under the terms of the
6 * GNU General Public License, version 2.0, but WITHOUT ANY WARRANTY;
7 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8 * PARTICULAR PURPOSE. Terms of the GPL can be found in doc/GPL-license.txt
9 * in this distribution, or online at http://www.gnu.org/licenses/gpl-2.0.txt
10 *
11 * By copying, modifying or distributing this software, you acknowledge that
12 * you have read and understood your obligations described above, and agree to
13 * abide by those obligations.
14 *
15 */
16
1#include "llviewerprecompiledheaders.h" 17#include "llviewerprecompiledheaders.h"
2#include "llagent.h" 18#include "llcallbacklist.h"
3#include "lldrawpoolalpha.h" 19#include "lldrawpoolalpha.h"
4#include "llfirstuse.h"
5#include "llfloaterbeacons.h" 20#include "llfloaterbeacons.h"
6#include "llfloaterchat.h" 21#include "llfloaterchat.h"
7#include "llfloaterdaycycle.h" 22#include "llfloaterdaycycle.h"
8#include "llfloaterenvsettings.h" 23#include "llfloaterenvsettings.h"
9#include "llfloatergodtools.h"
10#include "llfloaterland.h" 24#include "llfloaterland.h"
11#include "llfloatermap.h" 25#include "llfloatermap.h"
12#include "llfloaterregioninfo.h" 26#include "llfloaterregioninfo.h"
@@ -14,22 +28,19 @@
14#include "llfloaterwater.h" 28#include "llfloaterwater.h"
15#include "llfloaterwindlight.h" 29#include "llfloaterwindlight.h"
16#include "llfloaterworldmap.h" 30#include "llfloaterworldmap.h"
17#include "llgesturemgr.h"
18#include "llinventoryview.h" 31#include "llinventoryview.h"
19#include "llstartup.h" 32#include "llstartup.h"
20#include "llviewermenu.h" 33#include "llviewermenu.h"
21#include "llviewermessage.h" 34#include "llviewermessage.h"
35#include "llviewerobjectlist.h"
22#include "llviewerparcelmgr.h" 36#include "llviewerparcelmgr.h"
23#include "llviewerregion.h" 37#include "llviewerregion.h"
24#include "llviewerwindow.h" 38#include "llviewerwindow.h"
25#include "llvoavatar.h"
26#include "llworld.h" 39#include "llworld.h"
27#include "pipeline.h" 40#include "pipeline.h"
28 41
29#include "rlvhelper.h"
30#include "rlvevent.h"
31#include "rlvextensions.h"
32#include "rlvhandler.h" 42#include "rlvhandler.h"
43#include "rlvextensions.h"
33 44
34// Only defined in llinventorybridge.cpp 45// Only defined in llinventorybridge.cpp
35#if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11 46#if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
@@ -43,46 +54,51 @@ extern const char* NEW_CATEGORY_NAME;
43// 54//
44 55
45BOOL RlvHandler::m_fEnabled = FALSE; 56BOOL RlvHandler::m_fEnabled = FALSE;
46BOOL RlvHandler::fNoSetEnv = FALSE; 57bool RlvHandler::m_fFetchStarted = false;
47BOOL RlvHandler::fLegacyNaming = FALSE; 58bool RlvHandler::m_fFetchComplete = false;
48BOOL RlvHandler::m_fFetchStarted = FALSE;
49BOOL RlvHandler::m_fFetchComplete = FALSE;
50RlvMultiStringSearch RlvHandler::m_AttachLookup; 59RlvMultiStringSearch RlvHandler::m_AttachLookup;
51
52const std::string RlvHandler::cstrSharedRoot = RLV_ROOT_FOLDER; 60const std::string RlvHandler::cstrSharedRoot = RLV_ROOT_FOLDER;
53 61
54// Keep these consistent with regular RLV
55const std::string RlvHandler::cstrBlockedRecvIM = "*** IM blocked by your viewer";
56const std::string RlvHandler::cstrBlockedSendIM = "*** IM blocked by sender's viewer";
57const std::string RlvHandler::cstrHidden = "(Hidden)";
58const std::string RlvHandler::cstrHiddenParcel = "(Hidden parcel)";
59const std::string RlvHandler::cstrHiddenRegion = "(Hidden region)";
60const std::string RlvHandler::cstrMsgRecvIM =
61 "The Resident you messaged is prevented from reading your instant messages at the moment, please try again later.";
62const std::string RlvHandler::cstrMsgTpLure =
63 "The Resident you invited is prevented from accepting teleport offers. Please try again later.";
64
65const std::string RlvHandler::cstrAnonyms[] =
66{
67 "A resident", "This resident", "That resident", "An individual", "This individual", "That individual", "A person",
68 "This person", "That person", "A stranger", "This stranger", "That stranger", "A being", "This being",
69 "That being", "An agent", "This agent", "That agent", "A soul", "This soul", "That soul", "Somebody",
70 "Some people", "Someone", "Mysterious one", "An unknown being", "Unidentified one", "An unknown person"
71};
72
73rlv_handler_t gRlvHandler; 62rlv_handler_t gRlvHandler;
74 63
75// ============================================================================ 64// ============================================================================
76// Helper functions 65// Attachment group helper functions
77// 66//
78 67
79// Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.0e 68// Has to match the order of ERlvAttachGroupType
80inline bool rlvIsWearingItem(const LLInventoryItem* pItem) 69const std::string cstrAttachGroups[RLV_ATTACHGROUP_COUNT] = { "head", "torso", "arms", "legs", "hud" };
70
71// Checked: 2009-10-19 (RLVa-1.1.0e) | Added: RLVa-1.1.0e
72inline ERlvAttachGroupType rlvGetAttachGroupTypeFromIndex(S32 idxGroup)
81{ 73{
82 return 74 switch (idxGroup)
83 ((LLAssetType::AT_OBJECT == pItem->getType()) && (gAgent.getAvatarObject()->isWearingAttachment(pItem->getUUID()))) || 75 {
84 ((LLAssetType::AT_GESTURE == pItem->getType()) && (gGestureManager.isGestureActive(pItem->getUUID()))) || 76 case 0: // Right Hand
85 (gAgent.isWearingItem(pItem->getUUID())); 77 case 1: // Right Arm
78 case 3: // Left Arm
79 case 4: // Left Hand
80 return RLV_ATTACHGROUP_ARMS;
81 case 2: // Head
82 return RLV_ATTACHGROUP_HEAD;
83 case 5: // Left Leg
84 case 7: // Right Leg
85 return RLV_ATTACHGROUP_LEGS;
86 case 6: // Torso
87 return RLV_ATTACHGROUP_TORSO;
88 case 8: // HUD
89 return RLV_ATTACHGROUP_HUD;
90 default:
91 return RLV_ATTACHGROUP_INVALID;
92 }
93}
94
95// Checked: 2009-10-19 (RLVa-1.1.0e) | Added: RLVa-1.1.0e
96inline ERlvAttachGroupType rlvGetAttachGroupTypeFromString(const std::string& strGroup)
97{
98 for (int idx = 0; idx < RLV_ATTACHGROUP_COUNT; idx++)
99 if (cstrAttachGroups[idx] == strGroup)
100 return (ERlvAttachGroupType)idx;
101 return RLV_ATTACHGROUP_INVALID;
86} 102}
87 103
88// ============================================================================ 104// ============================================================================
@@ -116,7 +132,7 @@ static bool rlvParseNotifyOption(const std::string& strOption, S32& nChannel, st
116 132
117// Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d 133// Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
118RlvHandler::RlvHandler() 134RlvHandler::RlvHandler()
119 : m_fCanCancelTp(true), m_idCurObject(LLUUID::null), m_pCurCommand(NULL), m_pGCTimer(NULL), m_pWLSnapshot(NULL), m_pBhvrNotify(NULL) 135 : m_fCanCancelTp(true), m_pGCTimer(NULL), m_pWLSnapshot(NULL), m_pBhvrNotify(NULL)
120{ 136{
121 // Array auto-initialization to 0 is non-standard? (Compiler warning in VC-8.0) 137 // Array auto-initialization to 0 is non-standard? (Compiler warning in VC-8.0)
122 memset(m_LayersAdd, 0, sizeof(S16) * WT_COUNT); 138 memset(m_LayersAdd, 0, sizeof(S16) * WT_COUNT);
@@ -137,38 +153,30 @@ RlvHandler::~RlvHandler()
137// Attachment related functions 153// Attachment related functions
138// 154//
139 155
140// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
141inline LLViewerJointAttachment* RlvHandler::getAttachPoint(const std::string& strText, bool fExact) const
142{
143 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
144 return (pAvatar) ? get_if_there(pAvatar->mAttachmentPoints, getAttachPointIndex(strText, fExact), (LLViewerJointAttachment*)NULL)
145 : NULL;
146}
147
148// Checked: 2009-07-29 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b 156// Checked: 2009-07-29 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b
149LLViewerJointAttachment* RlvHandler::getAttachPoint(const LLInventoryCategory* pFolder, bool /*fStrict*/) const 157S32 RlvHandler::getAttachPointIndex(const LLInventoryCategory* pFolder, bool /*fStrict*/) const
150{ 158{
151 if (!pFolder) 159 if (!pFolder)
152 return NULL; 160 return 0;
153 161
154 // RLVa-1.0.1 added support for legacy matching (See http://rlva.catznip.com/blog/2009/07/attachment-point-naming-convention/) 162 // RLVa-1.0.1 added support for legacy matching (See http://rlva.catznip.com/blog/2009/07/attachment-point-naming-convention/)
155 if (fLegacyNaming) 163 if (RlvSettings::getEnableLegacyNaming())
156 return getAttachPointLegacy(pFolder); 164 return getAttachPointIndexLegacy(pFolder);
157 165
158 // Otherwise the only valid way to specify an attachment point in a folder name is: ^\.\(\s+attachpt\s+\) 166 // Otherwise the only valid way to specify an attachment point in a folder name is: ^\.\(\s+attachpt\s+\)
159 std::string::size_type idxMatch; 167 std::string::size_type idxMatch;
160 std::string strAttachPt = rlvGetFirstParenthesisedText(pFolder->getName(), &idxMatch); 168 std::string strAttachPt = rlvGetFirstParenthesisedText(pFolder->getName(), &idxMatch);
161 LLStringUtil::trim(strAttachPt); 169 LLStringUtil::trim(strAttachPt);
162 170
163 return ( (1 == idxMatch) && (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName().at(0)) ) ? getAttachPoint(strAttachPt, true) : NULL; 171 return ( (1 == idxMatch) && (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName().at(0)) ) ? getAttachPointIndex(strAttachPt, true) : 0;
164} 172}
165 173
166// Checked: 2009-07-29 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b 174// Checked: 2009-07-29 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b
167LLViewerJointAttachment* RlvHandler::getAttachPoint(const LLInventoryItem* pItem, bool fStrict) const 175S32 RlvHandler::getAttachPointIndex(const LLInventoryItem* pItem, bool fStrict) const
168{ 176{
169 // Sanity check - if it's not an object then it can't have an attachment point 177 // Sanity check - if it's not an object then it can't have an attachment point
170 if ( (!pItem) || (LLAssetType::AT_OBJECT != pItem->getType()) ) 178 if ( (!pItem) || (LLAssetType::AT_OBJECT != pItem->getType()) )
171 return NULL; 179 return 0;
172 180
173 // The attachment point should be placed at the end of the item's name, surrounded by parenthesis 181 // The attachment point should be placed at the end of the item's name, surrounded by parenthesis
174 // (if there is no such text then strAttachPt will be an empty string which is fine since it means we'll look at the item's parent) 182 // (if there is no such text then strAttachPt will be an empty string which is fine since it means we'll look at the item's parent)
@@ -177,20 +185,20 @@ LLViewerJointAttachment* RlvHandler::getAttachPoint(const LLInventoryItem* pItem
177 185
178 // If the item is modify : we look at the item's name first and only then at the containing folder 186 // If the item is modify : we look at the item's name first and only then at the containing folder
179 // If the item is no modify: we look at the containing folder's name first and only then at the item itself 187 // If the item is no modify: we look at the containing folder's name first and only then at the item itself
180 LLViewerJointAttachment* pAttachPt; 188 S32 idxAttachPt = 0;
181 if (pItem->getPermissions().allowModifyBy(gAgent.getID())) 189 if (pItem->getPermissions().allowModifyBy(gAgent.getID()))
182 { 190 {
183 pAttachPt = (!strAttachPt.empty()) ? getAttachPoint(strAttachPt, true) : NULL; 191 idxAttachPt = (!strAttachPt.empty()) ? getAttachPointIndex(strAttachPt, true) : 0;
184 if (!pAttachPt) 192 if (!idxAttachPt)
185 pAttachPt = getAttachPoint(gInventory.getCategory(pItem->getParentUUID()), fStrict); 193 idxAttachPt = getAttachPointIndex(gInventory.getCategory(pItem->getParentUUID()), fStrict);
186 } 194 }
187 else 195 else
188 { 196 {
189 pAttachPt = getAttachPoint(gInventory.getCategory(pItem->getParentUUID()), fStrict); 197 idxAttachPt = getAttachPointIndex(gInventory.getCategory(pItem->getParentUUID()), fStrict);
190 if ( (!pAttachPt) && (!strAttachPt.empty()) ) 198 if ( (!idxAttachPt) && (!strAttachPt.empty()) )
191 pAttachPt = getAttachPoint(strAttachPt, true); 199 idxAttachPt = getAttachPointIndex(strAttachPt, true);
192 } 200 }
193 return pAttachPt; 201 return idxAttachPt;
194} 202}
195 203
196// Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.2a 204// Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.2a
@@ -210,11 +218,11 @@ S32 RlvHandler::getAttachPointIndex(const LLViewerJointAttachment* pAttachPt) co
210} 218}
211 219
212// Checked: 2009-07-29 (RLVa-1.0.1b) | Added: RLVa-1.0.1b 220// Checked: 2009-07-29 (RLVa-1.0.1b) | Added: RLVa-1.0.1b
213LLViewerJointAttachment* RlvHandler::getAttachPointLegacy(const LLInventoryCategory* pFolder) const 221S32 RlvHandler::getAttachPointIndexLegacy(const LLInventoryCategory* pFolder) const
214{ 222{
215 // Hopefully some day this can just be deprecated (see http://rlva.catznip.com/blog/2009/07/attachment-point-naming-convention/) 223 // Hopefully some day this can just be deprecated (see http://rlva.catznip.com/blog/2009/07/attachment-point-naming-convention/)
216 if ( (!pFolder) || (pFolder->getName().empty()) ) 224 if ( (!pFolder) || (pFolder->getName().empty()) )
217 return NULL; 225 return 0;
218 226
219 // Check for a (...) block *somewhere* in the name 227 // Check for a (...) block *somewhere* in the name
220 std::string::size_type idxMatch; 228 std::string::size_type idxMatch;
@@ -236,7 +244,7 @@ LLViewerJointAttachment* RlvHandler::getAttachPointLegacy(const LLInventoryCateg
236 if (RLV_FOLDER_PREFIX_HIDDEN == strAttachPt[0]) 244 if (RLV_FOLDER_PREFIX_HIDDEN == strAttachPt[0])
237 strAttachPt.erase(0, 1); 245 strAttachPt.erase(0, 1);
238 } 246 }
239 return getAttachPoint(strAttachPt, true); 247 return getAttachPointIndex(strAttachPt, true);
240} 248}
241 249
242bool RlvHandler::hasLockedHUD() const 250bool RlvHandler::hasLockedHUD() const
@@ -255,16 +263,12 @@ bool RlvHandler::hasLockedHUD() const
255 return false; // None of our locked attachments is a HUD 263 return false; // None of our locked attachments is a HUD
256} 264}
257 265
258// Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a
259bool RlvHandler::isLockedAttachment(const LLInventoryItem* pItem, ERlvLockMask eLock) const
260{
261 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
262 return (pItem) && (pAvatar) && (isLockedAttachment(pAvatar->getWornAttachment(pItem->getUUID()), eLock));
263}
264
265// Checked: 2009-10-13 (RLVa-1.0.5b) | Added: RLVa-1.0.5b 266// Checked: 2009-10-13 (RLVa-1.0.5b) | Added: RLVa-1.0.5b
266bool RlvHandler::isLockedAttachmentExcept(S32 idxAttachPt, ERlvLockMask eLock, LLViewerObject *pObj) const 267bool RlvHandler::isLockedAttachmentExcept(S32 idxAttachPt, ERlvLockMask eLock, LLViewerObject *pExceptObj) const
267{ 268{
269 if (!pExceptObj)
270 return isLockedAttachment(idxAttachPt, eLock);
271
268 // Loop over every object that marked the specific attachment point eLock type locked (but ignore pObj and any of its children) 272 // Loop over every object that marked the specific attachment point eLock type locked (but ignore pObj and any of its children)
269 LLViewerObject* pTempObj; 273 LLViewerObject* pTempObj;
270 if (eLock & RLV_LOCK_REMOVE) 274 if (eLock & RLV_LOCK_REMOVE)
@@ -272,7 +276,7 @@ bool RlvHandler::isLockedAttachmentExcept(S32 idxAttachPt, ERlvLockMask eLock, L
272 for (rlv_attachlock_map_t::const_iterator itAttach = m_AttachRem.lower_bound(idxAttachPt), 276 for (rlv_attachlock_map_t::const_iterator itAttach = m_AttachRem.lower_bound(idxAttachPt),
273 endAttach = m_AttachRem.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach) 277 endAttach = m_AttachRem.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach)
274 { 278 {
275 if ( ((pTempObj = gObjectList.findObject(itAttach->second)) == NULL) || (pTempObj->getRootEdit()->getID() != pObj->getID()) ) 279 if ( ((pTempObj = gObjectList.findObject(itAttach->second)) == NULL) || (pTempObj->getRootEdit()->getID() != pExceptObj->getID()) )
276 return true; 280 return true;
277 } 281 }
278 } 282 }
@@ -281,7 +285,7 @@ bool RlvHandler::isLockedAttachmentExcept(S32 idxAttachPt, ERlvLockMask eLock, L
281 for (rlv_attachlock_map_t::const_iterator itAttach = m_AttachAdd.lower_bound(idxAttachPt), 285 for (rlv_attachlock_map_t::const_iterator itAttach = m_AttachAdd.lower_bound(idxAttachPt),
282 endAttach = m_AttachAdd.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach) 286 endAttach = m_AttachAdd.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach)
283 { 287 {
284 if ( ((pTempObj = gObjectList.findObject(itAttach->second)) == NULL) || (pTempObj->getRootEdit()->getID() != pObj->getID()) ) 288 if ( ((pTempObj = gObjectList.findObject(itAttach->second)) == NULL) || (pTempObj->getRootEdit()->getID() != pExceptObj->getID()) )
285 return true; 289 return true;
286 } 290 }
287 } 291 }
@@ -310,6 +314,40 @@ void RlvHandler::addAttachmentLock(S32 idxAttachPt, const LLUUID &idRlvObj, ERlv
310 } 314 }
311} 315}
312 316
317// Checked: 2010-07-18 (RLVa-1.1.2b) | Added: RLVa-1.1.2a
318void RlvHandler::dumpAttachmentLocks(void*)
319{
320 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
321 if (!pAvatar)
322 {
323 RLV_INFOS << "No avatar object to dump attachments for" << RLV_ENDL;
324 return;
325 }
326
327 RLV_INFOS << "Dumping 'remove' locks:" << RLV_ENDL;
328 for (rlv_attachlock_map_t::iterator itAttachPt = gRlvHandler.m_AttachRem.begin();
329 itAttachPt != gRlvHandler.m_AttachRem.end(); ++itAttachPt)
330 {
331 // Grab the attachment on the attachment point that's locked (if there is one)
332 /*const*/ LLViewerJointAttachment* pAttachPt =
333 get_if_there(pAvatar->mAttachmentPoints, (S32)itAttachPt->first, (LLViewerJointAttachment*)NULL);
334 /*const*/ LLViewerObject* pAttachObj = (pAttachPt) ? pAttachPt->getObject() : NULL;
335 const LLViewerInventoryItem* pAttachItem = (pAttachPt) ? gInventory.getItem(pAttachPt->getItemID()) : NULL;
336
337 // Grab the locking attachment (if we can)
338 /*const*/ LLViewerObject* pRlvObj = gObjectList.findObject(itAttachPt->second);
339 /*const*/ LLViewerJointAttachment* pRlvAttachPt = (pRlvObj) ? pAvatar->getTargetAttachmentPoint(pRlvObj) : NULL;
340 const LLViewerInventoryItem* pRlvItem = (pRlvAttachPt) ? gInventory.getItem(pRlvAttachPt->getItemID()) : NULL;
341
342 std::string strMsg = llformat("'%s' on %s held by '%s' on %s",
343 ((pAttachItem) ? pAttachItem->getName().c_str() : ((pAttachObj) ? pAttachObj->getID().asString().c_str() : "(empty)")),
344 (pAttachPt) ? pAttachPt->getName().c_str() : "(unknown)",
345 ((pRlvItem) ? pRlvItem->getName().c_str() : ((pRlvObj) ? pRlvObj->getID().asString().c_str() : "(empty)")),
346 (pRlvAttachPt) ? pRlvAttachPt->getName().c_str() : "(unknown)");
347 RLV_INFOS << strMsg << RLV_ENDL;
348 }
349}
350
313// Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a 351// Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a
314void RlvHandler::removeAttachmentLock(S32 idxAttachPt, const LLUUID &idRlvObj, ERlvLockMask eLock) 352void RlvHandler::removeAttachmentLock(S32 idxAttachPt, const LLUUID &idRlvObj, ERlvLockMask eLock)
315{ 353{
@@ -344,7 +382,7 @@ void RlvHandler::removeAttachmentLock(S32 idxAttachPt, const LLUUID &idRlvObj, E
344} 382}
345 383
346#ifdef RLV_EXTENSION_FLAG_NOSTRIP 384#ifdef RLV_EXTENSION_FLAG_NOSTRIP
347 // Checked: 2009-05-26 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d 385 // Checked: 2009-12-25 (RLVa-1.1.0k) | Modified: RLVa-1.1.0k
348 bool RlvHandler::isStrippable(const LLUUID& idItem) const 386 bool RlvHandler::isStrippable(const LLUUID& idItem) const
349 { 387 {
350 // An item is exempt from @detach or @remoutfit if: 388 // An item is exempt from @detach or @remoutfit if:
@@ -359,8 +397,13 @@ void RlvHandler::removeAttachmentLock(S32 idxAttachPt, const LLUUID &idRlvObj, E
359 return false; 397 return false;
360 398
361 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID()); 399 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
362 if ( (pFolder) && (std::string::npos != pFolder->getName().find(RLV_FOLDER_FLAG_NOSTRIP)) ) 400 while ( (pFolder) && gAgent.getInventoryRootID() != (pFolder->getUUID()) )
363 return false; 401 {
402 if (std::string::npos != pFolder->getName().find(RLV_FOLDER_FLAG_NOSTRIP))
403 return false;
404 // If the item's parent is a folded folder then we need to check its parent as well
405 pFolder = (isFoldedFolder(pFolder, false, true)) ? gInventory.getCategory(pFolder->getParentUUID()) : NULL;
406 }
364 } 407 }
365 } 408 }
366 return true; 409 return true;
@@ -419,539 +462,210 @@ bool RlvHandler::isException(ERlvBehaviour eBhvr, const RlvExceptionOption& varO
419// Command processing functions 462// Command processing functions
420// 463//
421 464
422// Checked: 2009-06-03 (RLVa-0.2.0h) 465// Checked: 2009-11-27 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
423void RlvHandler::addBehaviourObserver(RlvBehaviourObserver* pBhvrObserver) 466void RlvHandler::addBehaviourObserver(RlvBehaviourObserver* pBhvrObserver)
424{ 467{
425 std::list<RlvBehaviourObserver*>::iterator itBhvrObserver = std::find(m_BhvrObservers.begin(), m_BhvrObservers.end(), pBhvrObserver); 468 if ( (pBhvrObserver) && (std::find(m_BhvrObservers.begin(), m_BhvrObservers.end(), pBhvrObserver) == m_BhvrObservers.end()) )
426 if (itBhvrObserver == m_BhvrObservers.end())
427 m_BhvrObservers.push_back(pBhvrObserver); 469 m_BhvrObservers.push_back(pBhvrObserver);
428} 470}
429 471
430// Checked: 2009-06-03 (RLVa-0.2.0h) 472// Checked: 2009-11-27 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
473void RlvHandler::addCommandHandler(RlvCommandHandler* pCmdHandler)
474{
475 if ( (pCmdHandler) && (std::find(m_CommandHandlers.begin(), m_CommandHandlers.end(), pCmdHandler) == m_CommandHandlers.end()) )
476 m_CommandHandlers.push_back(pCmdHandler);
477}
478
479// Checked: 2009-11-27 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
431void RlvHandler::removeBehaviourObserver(RlvBehaviourObserver* pBhvrObserver) 480void RlvHandler::removeBehaviourObserver(RlvBehaviourObserver* pBhvrObserver)
432{ 481{
433 std::list<RlvBehaviourObserver*>::iterator itBhvrObserver = std::find(m_BhvrObservers.begin(), m_BhvrObservers.end(), pBhvrObserver); 482 if (pBhvrObserver)
434 if (itBhvrObserver != m_BhvrObservers.end()) 483 m_BhvrObservers.remove(pBhvrObserver);
435 m_BhvrObservers.erase(itBhvrObserver); 484}
485
486// Checked: 2009-11-27 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
487void RlvHandler::removeCommandHandler(RlvCommandHandler* pCmdHandler)
488{
489 if (pCmdHandler)
490 m_CommandHandlers.remove(pCmdHandler);
491}
492
493// Checked: 2009-10-26 (RLVa-1.1.0a) | Modified: RLVa-1.1.0a
494void RlvHandler::clearCommandHandlers()
495{
496 std::list<RlvCommandHandler*>::const_iterator itHandler = m_CommandHandlers.begin();
497 while (itHandler != m_CommandHandlers.end())
498 {
499 delete *itHandler;
500 ++itHandler;
501 }
502 m_CommandHandlers.clear();
436} 503}
437 504
438// Checked: 2009-06-03 (RLVa-0.2.0h) 505// Checked: 2009-06-03 (RLVa-0.2.0h)
439void RlvHandler::notifyBehaviourObservers(const RlvCommand& rlvCmd, bool fInternal) 506void RlvHandler::notifyBehaviourObservers(const RlvCommand& rlvCmd, bool fInternal)
440{ 507{
441 for (std::list<RlvBehaviourObserver*>::const_iterator itBhvrObserver = m_BhvrObservers.begin(); 508 for (std::list<RlvBehaviourObserver*>::const_iterator itBhvrObserver = m_BhvrObservers.begin();
442 itBhvrObserver != m_BhvrObservers.end(); ++itBhvrObserver) 509 itBhvrObserver != m_BhvrObservers.end(); ++itBhvrObserver)
443 { 510 {
444 (*itBhvrObserver)->changed(rlvCmd, fInternal); 511 (*itBhvrObserver)->changed(rlvCmd, fInternal);
445 } 512 }
446} 513}
447 514
448// Checked: 515// Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
449BOOL RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCmd, bool fFromObj) 516bool RlvHandler::notifyCommandHandlers(rlvCommandHandler f, const LLUUID& idObj, const RlvCommand& rlvCmd, ERlvCmdRet& eRet, bool fNotifyAll) const
517{
518 std::list<RlvCommandHandler*>::const_iterator itHandler = m_CommandHandlers.begin(); bool fContinue = true; eRet = RLV_RET_UNKNOWN;
519 while ( (itHandler != m_CommandHandlers.end()) && ((fContinue) || (fNotifyAll)) )
520 {
521 ERlvCmdRet eCmdRet = RLV_RET_UNKNOWN;
522 if ((fContinue = !((*itHandler)->*f)(idObj, rlvCmd, eCmdRet)) == false)
523 eRet = eCmdRet;
524 ++itHandler;
525 }
526 RLV_ASSERT( (fContinue) || (eRet != RLV_RET_UNKNOWN) );
527 return !fContinue;
528}
529
530// Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
531ERlvCmdRet RlvHandler::processCommand(const LLUUID& idObj, const RlvCommand& rlvCmd, bool fFromObj)
450{ 532{
451 #ifdef RLV_DEBUG 533 #ifdef RLV_DEBUG
452 RLV_INFOS << "[" << idObj << "]: " << strCmd << LL_ENDL; 534 RLV_INFOS << "[" << idObj << "]: " << rlvCmd.asString() << RLV_ENDL;
453 #endif // RLV_DEBUG 535 #endif // RLV_DEBUG
454 536
455 RlvCommand rlvCmd(strCmd);
456 if (!rlvCmd.isValid()) 537 if (!rlvCmd.isValid())
457 { 538 {
458 #ifdef RLV_DEBUG 539 #ifdef RLV_DEBUG
459 RLV_INFOS << "\t-> invalid command: " << strCmd << LL_ENDL; 540 RLV_INFOS << "\t-> invalid syntax" << RLV_ENDL;
460 #endif // RLV_DEBUG 541 #endif // RLV_DEBUG
461 return FALSE; 542 return RLV_RET_FAILED_SYNTAX;
462 } 543 }
463 544
464 // NOTE: if we pass RlvObject::m_UUID for idObj somewhere and process a @clear then it will point to invalid/cleared memory at the end 545 // Using a stack for executing commands solves a few problems:
465 // so make sure to *always* pass our private copy to other functions 546 // - if we passed RlvObject::m_UUID for idObj somewhere and process a @clear then idObj points to invalid/cleared memory at the end
466 m_pCurCommand = &rlvCmd; m_idCurObject = idObj; 547 // - if command X triggers command Y along the way then getCurrentCommand()/getCurrentObject() still return Y even when finished
548 m_CurCommandStack.push(&rlvCmd); m_CurObjectStack.push(idObj);
549 const LLUUID& idCurObj = m_CurObjectStack.top();
467 550
468 BOOL fRet = FALSE; 551 ERlvCmdRet eRet = RLV_RET_UNKNOWN;
469 switch (rlvCmd.getParamType()) 552 switch (rlvCmd.getParamType())
470 { 553 {
471 case RLV_TYPE_ADD: // Checked: 2009-06-03 (RLVa-0.2.0h) | Modified: RLVa-0.2.0h 554 case RLV_TYPE_ADD: // Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
472 { 555 {
473 if ( (m_Behaviours[rlvCmd.getBehaviourType()]) && 556 if ( (m_Behaviours[rlvCmd.getBehaviourType()]) &&
474 ( (RLV_BHVR_SETDEBUG == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETENV == rlvCmd.getBehaviourType()) ) ) 557 ( (RLV_BHVR_SETDEBUG == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETENV == rlvCmd.getBehaviourType()) ) )
475 { 558 {
476 // Some restrictions can only be held by one single object to avoid deadlocks 559 // Some restrictions can only be held by one single object to avoid deadlocks
477 #ifdef RLV_DEBUG 560 #ifdef RLV_DEBUG
478 RLV_INFOS << "\t- " << rlvCmd.getBehaviour() << " is already set by another object => discarding" << LL_ENDL; 561 RLV_INFOS << "\t- " << rlvCmd.getBehaviour() << " is already set by another object => discarding" << RLV_ENDL;
479 #endif // RLV_DEBUG 562 #endif // RLV_DEBUG
563 eRet = RLV_RET_FAILED_LOCK;
480 break; 564 break;
481 } 565 }
482 566
483 rlv_object_map_t::iterator itObj = m_Objects.find(m_idCurObject); 567 rlv_object_map_t::iterator itObj = m_Objects.find(idCurObj); bool fAdded = false;
484 if (itObj != m_Objects.end()) 568 if (itObj != m_Objects.end())
485 { 569 {
486 RlvObject& rlvObj = itObj->second; 570 RlvObject& rlvObj = itObj->second;
487 fRet = rlvObj.addCommand(rlvCmd); 571 fAdded = rlvObj.addCommand(rlvCmd);
488 } 572 }
489 else 573 else
490 { 574 {
491 RlvObject rlvObj(m_idCurObject); 575 RlvObject rlvObj(idCurObj);
492 fRet = rlvObj.addCommand(rlvCmd); 576 fAdded = rlvObj.addCommand(rlvCmd);
493 m_Objects.insert(std::pair<LLUUID, RlvObject>(m_idCurObject, rlvObj)); 577 m_Objects.insert(std::pair<LLUUID, RlvObject>(idCurObj, rlvObj));
494 } 578 }
495 579
496 #ifdef RLV_DEBUG 580 #ifdef RLV_DEBUG
497 RLV_INFOS << "\t- " << ( (fRet) ? "adding behaviour" : "skipping duplicate") << LL_ENDL; 581 RLV_INFOS << "\t- " << ( (fAdded) ? "adding behaviour" : "skipping duplicate" ) << RLV_ENDL;
498 #endif // RLV_DEBUG 582 #endif // RLV_DEBUG
499 583
500 if (fRet) { // If FALSE then this was a duplicate, there's no need to handle those 584 if (fAdded) { // If FALSE then this was a duplicate, there's no need to handle those
501 if (!m_pGCTimer) 585 if (!m_pGCTimer)
502 m_pGCTimer = new RlvGCTimer(); 586 m_pGCTimer = new RlvGCTimer();
503 processAddCommand(m_idCurObject, rlvCmd); 587 eRet = processAddRemCommand(idCurObj, rlvCmd);
504 notifyBehaviourObservers(rlvCmd, !fFromObj); 588 notifyBehaviourObservers(rlvCmd, !fFromObj);
505 } 589 }
590 else
591 {
592 eRet = RLV_RET_SUCCESS_DUPLICATE;
593 }
506 } 594 }
507 break; 595 break;
508 case RLV_TYPE_REMOVE: // Checked: 596 case RLV_TYPE_REMOVE: // Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
509 { 597 {
510 rlv_object_map_t::iterator itObj = m_Objects.find(m_idCurObject); 598 rlv_object_map_t::iterator itObj = m_Objects.find(idCurObj); bool fRemoved = false;
511 if (itObj != m_Objects.end()) 599 if (itObj != m_Objects.end())
512 fRet = itObj->second.removeCommand(rlvCmd); 600 fRemoved = itObj->second.removeCommand(rlvCmd);
513 601
514 #ifdef RLV_DEBUG 602 #ifdef RLV_DEBUG
515 RLV_INFOS << "\t- " << ( (fRet) ? "removing behaviour" 603 RLV_INFOS << "\t- " << ( (fRemoved) ? "removing behaviour"
516 : "skipping remove (unset behaviour or unknown object)") << LL_ENDL; 604 : "skipping remove (unset behaviour or unknown object)") << RLV_ENDL;
517 #endif // RLV_DEBUG 605 #endif // RLV_DEBUG
518 606
519 if (fRet) { // Don't handle non-sensical removes 607 if (fRemoved) { // Don't handle non-sensical removes
520 processRemoveCommand(m_idCurObject, rlvCmd); 608 eRet = processAddRemCommand(idCurObj, rlvCmd);
521 notifyBehaviourObservers(rlvCmd, !fFromObj); 609 notifyBehaviourObservers(rlvCmd, !fFromObj);
522 610
523 if (0 == itObj->second.m_Commands.size()) 611 if (0 == itObj->second.m_Commands.size())
524 { 612 {
525 #ifdef RLV_DEBUG 613 #ifdef RLV_DEBUG
526 RLV_INFOS << "\t- command list empty => removing " << m_idCurObject << LL_ENDL; 614 RLV_INFOS << "\t- command list empty => removing " << idCurObj << RLV_ENDL;
527 #endif // RLV_DEBUG 615 #endif // RLV_DEBUG
528 m_Objects.erase(itObj); 616 m_Objects.erase(itObj);
529 } 617 }
530 } 618 }
619 else
620 {
621 eRet = RLV_RET_SUCCESS_UNSET;
622 }
531 } 623 }
532 break; 624 break;
533 case RLV_TYPE_CLEAR: 625 case RLV_TYPE_CLEAR: // Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
534 fRet = processClearCommand(m_idCurObject, rlvCmd); 626 eRet = processClearCommand(idCurObj, rlvCmd);
535 notifyBehaviourObservers(rlvCmd, !fFromObj); 627 notifyBehaviourObservers(rlvCmd, !fFromObj);
536 break; 628 break;
537 case RLV_TYPE_FORCE: // Checked: 629 case RLV_TYPE_FORCE: // Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
538 fRet = processForceCommand(m_idCurObject, rlvCmd); 630 eRet = processForceCommand(idCurObj, rlvCmd);
539 break;
540 case RLV_TYPE_REPLY: // Checked:
541 fRet = processReplyCommand(m_idCurObject, rlvCmd);
542 break; 631 break;
543 case RLV_TYPE_UNKNOWN: // Checked: 632 case RLV_TYPE_REPLY: // Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
633 eRet = processReplyCommand(idCurObj, rlvCmd);
544 break; 634 break;
545 #ifdef LL_GNUC 635 case RLV_TYPE_UNKNOWN: // Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
546 default: 636 default:
637 eRet = RLV_RET_FAILED_PARAM;
547 break; 638 break;
548 #endif // LL_GNUC
549 } 639 }
640 RLV_ASSERT(RLV_RET_UNKNOWN != eRet);
550 641
551 #ifdef RLV_DEBUG 642 #ifdef RLV_DEBUG
552 RLV_INFOS << "\t--> command " << ((fRet) ? "succeeded" : "failed") << LL_ENDL; 643 RLV_INFOS << "\t--> command " << ((eRet & RLV_RET_SUCCESS) ? "succeeded" : "failed") << RLV_ENDL;
553 #endif // RLV_DEBUG 644 #endif // RLV_DEBUG
554 645
555 m_pCurCommand = NULL; m_idCurObject.setNull(); 646 m_CurCommandStack.pop(); m_CurObjectStack.pop();
556 return fRet; 647 return eRet;
557} 648}
558 649
559BOOL RlvHandler::processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd) 650// Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
651void RlvHandler::processRetainedCommands(ERlvBehaviour eBhvrFilter /*=RLV_BHVR_UNKNOWN*/, ERlvParamType eTypeFilter /*=RLV_TYPE_UNKNOWN*/)
560{ 652{
561 // NOTE: - at this point the command has already been added to the corresponding RlvObject instance 653 rlv_retained_list_t::iterator itCmd = m_Retained.begin(), itCurCmd;
562 // - the object's UUID may or may not exist in gObjectList (see handling of @detach=n) 654 while (itCmd != m_Retained.end())
563
564 ERlvBehaviour eBehaviour = rlvCmd.getBehaviourType();
565 const std::string& strOption = rlvCmd.getOption();
566
567 if ( (RLV_BHVR_UNKNOWN != eBehaviour) && (strOption.empty()) )
568 { 655 {
569 if (rlvCmd.isStrict()) 656 itCurCmd = itCmd++; // Point the loop iterator ahead
570 addException(uuid, RLV_BHVR_PERMISSIVE, eBehaviour);
571 m_Behaviours[eBehaviour]++;
572 }
573 657
574 bool fRefCount = false; // Unused for the moment 658 const RlvRetainedCommand& cmd = *itCurCmd;
575 switch (eBehaviour) 659 if ( ((RLV_BHVR_UNKNOWN == eBhvrFilter) || (cmd.rlvCmd.getBehaviourType() == eBhvrFilter)) &&
576 { 660 ((RLV_TYPE_UNKNOWN == eTypeFilter) || (cmd.rlvCmd.getParamType() == eTypeFilter)) )
577 case RLV_BHVR_DETACH: // @detach[:<option>]=n - Checked: 2009-10-10 (RLVa-1.0.5a) | Modified: RLVa-1.0.5a 661 {
578 onAddRemDetach(uuid, rlvCmd, fRefCount); 662 processCommand(cmd.idObject, cmd.rlvCmd, true);
579 break; 663 m_Retained.erase(itCurCmd);
580 case RLV_BHVR_ADDATTACH: // @addattach[:<option>]=n - Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a 664 }
581 case RLV_BHVR_REMATTACH: // @addattach[:<option>]=n - Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a
582 onAddRemAttach(uuid, rlvCmd, fRefCount);
583 break;
584 case RLV_BHVR_REDIRCHAT: // @redirchat:<option>=n - Checked: 2009-07-07 (RLVa-1.0.0d)
585 case RLV_BHVR_REDIREMOTE: // @rediremote:<option>=n - Checked: 2009-07-07 (RLVa-1.0.0d) | Added: RLVa-0.2.2a
586 {
587 if (!strOption.empty())
588 m_Behaviours[eBehaviour]++; // @redirchat and @rediremote don't have an optionless version so keep track of it here
589 else
590 m_Behaviours[eBehaviour]--; // @redirchat=n and @rediremote=n are undefined, don't keep track of them
591 }
592 break;
593 case RLV_BHVR_SHOWWORLDMAP: // @showworldmap=n - Checked: 2009-07-05 (RLVa-1.0.0c)
594 {
595 // Simulate clicking the Map button [see LLToolBar::onClickMap()]
596 if (gFloaterWorldMap->getVisible())
597 LLFloaterWorldMap::toggle(NULL);
598 }
599 break;
600 case RLV_BHVR_SHOWMINIMAP: // @showminimap=n - Checked: 2009-07-05 (RLVa-1.0.0c)
601 {
602 // Simulate clicking the Minimap button [see LLToolBar::onClickRadar()]
603 #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
604 if (gFloaterMap->getVisible())
605 LLFloaterMap::toggle(NULL);
606 #else // Version: 1.23.4
607 if (LLFloaterMap::instanceVisible())
608 LLFloaterMap::hideInstance();
609 #endif
610 }
611 break;
612 #ifdef RLV_EXTENSION_STARTLOCATION
613 case RLV_BHVR_TPLOC: // @tploc=n - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.1d
614 case RLV_BHVR_UNSIT: // @unsit=n - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.1d
615 {
616 if (strOption.empty())
617 RlvSettings::updateLoginLastLocation();
618 }
619 break;
620 #endif // RLV_EXTENSION_STARTLOCATION
621 case RLV_BHVR_EDIT: // @edit=n - Checked: 2009-07-04 (RLVa-1.0.0b)
622 {
623 // Turn off "View / Highlight Transparent"
624 LLDrawPoolAlpha::sShowDebugAlpha = FALSE;
625
626 // Close the Beacons floater if it's open
627 if (LLFloaterBeacons::instanceVisible())
628 LLFloaterBeacons::toggleInstance();
629
630 // Get rid of the build floater if it's open [copy/paste from toggle_build_mode()]
631 if (gFloaterTools->getVisible())
632 {
633 gAgent.resetView(FALSE);
634 gFloaterTools->close();
635 gViewerWindow->showCursor();
636 }
637 }
638 break;
639 case RLV_BHVR_ADDOUTFIT: // @addoutfit[:<layer>]=n - Checked: 2009-07-07 (RLVa-1.0.0d)
640 case RLV_BHVR_REMOUTFIT: // @remoutfit[:<layer>]=n - Checked: 2009-07-07 (RLVa-1.0.0d)
641 {
642 S16* pLayers = (eBehaviour == RLV_BHVR_ADDOUTFIT) ? m_LayersAdd : m_LayersRem;
643
644 if (strOption.empty())
645 {
646 for (int idx = 0; idx < WT_COUNT; idx++)
647 pLayers[idx]++;
648 }
649 else
650 {
651 EWearableType type = LLWearable::typeNameToType(strOption);
652 if (WT_INVALID != type)
653 {
654 pLayers[type]++;
655 m_Behaviours[eBehaviour]++;
656 }
657 }
658 }
659 break;
660 case RLV_BHVR_SHOWINV: // @showinv=n - Checked: 2009-07-10 (RLVa-1.0.0g) | Modified: RLVa-1.0.0g
661 {
662 // Close all open inventory windows
663 LLInventoryView::closeAll();
664 }
665 break;
666 case RLV_BHVR_SHOWLOC: // @showloc=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
667 {
668 // If we're the first @showloc=n restriction refresh all object text so we can filter it if necessary
669 if (1 == m_Behaviours[RLV_BHVR_SHOWLOC])
670 LLHUDText::refreshAllObjectText();
671
672 // Close the "About Land" floater if it's currently visible
673 if (LLFloaterLand::instanceVisible())
674 LLFloaterLand::hideInstance();
675
676 // Close the "Estate Tools" floater is it's currently visible
677 if (LLFloaterRegionInfo::instanceVisible())
678 LLFloaterRegionInfo::hideInstance();
679
680 // NOTE: we should close the "God Tools" floater as well, but since calling LLFloaterGodTools::instance() always
681 // creates a new instance of the floater and since it's very unlikely to be open it's just better not to
682 }
683 break;
684 case RLV_BHVR_SHOWNAMES: // @shownames=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
685 {
686 // If we're the first @shownames=n restriction refresh all object text so we can filter it if necessary
687 if (1 == m_Behaviours[RLV_BHVR_SHOWNAMES])
688 LLHUDText::refreshAllObjectText();
689
690 // Close the "Active Speakers" panel if it's currently visible
691 LLFloaterChat::getInstance()->childSetVisible("active_speakers_panel", false);
692 }
693 break;
694 case RLV_BHVR_FARTOUCH:
695 {
696 #ifdef RLV_EXPERIMENTAL_FIRSTUSE
697 //LLFirstUse::useRlvFartouch();
698 #endif // RLV_EXPERIMENTAL_FIRSTUSE
699 }
700 break;
701 case RLV_BHVR_FLY: // @fly=n - Checked: 2009-07-05 (RLVa-1.0.0c)
702 {
703 // If currently flying, simulate clicking the Fly button [see LLToolBar::onClickFly()]
704 if (gAgent.getFlying())
705 gAgent.toggleFlying();
706 }
707 break;
708 case RLV_BHVR_SETENV: // @setenv=n - Checked: 2009-07-10 (RLVa-1.0.0g) | Modified: RLVa-0.2.0a
709 {
710 if (!fNoSetEnv)
711 {
712 // Only close the floaters if their instance exists and they're actually visible
713 if ( (LLFloaterEnvSettings::isOpen()) && (LLFloaterEnvSettings::instance()->getVisible()) )
714 LLFloaterEnvSettings::instance()->close();
715 if ( (LLFloaterWindLight::isOpen()) && (LLFloaterWindLight::instance()->getVisible()) )
716 LLFloaterWindLight::instance()->close();
717 if ( (LLFloaterWater::isOpen()) && (LLFloaterWater::instance()->getVisible()) )
718 LLFloaterWater::instance()->close();
719 if ( (LLFloaterDayCycle::isOpen()) && (LLFloaterDayCycle::instance()->getVisible()) )
720 LLFloaterDayCycle::instance()->close();
721
722 // Save the current WindLight params so we can restore them on @setenv=y
723 if (m_pWLSnapshot)
724 {
725 RLV_ERRS << "m_pWLSnapshot != NULL" << LL_ENDL; // Safety net in case we set @setenv=n for more than 1 object
726 delete m_pWLSnapshot;
727 }
728 m_pWLSnapshot = RlvWLSnapshot::takeSnapshot();
729 }
730 }
731 break;
732 case RLV_BHVR_SHOWHOVERTEXTALL: // @showhovertextal=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
733 case RLV_BHVR_SHOWHOVERTEXTWORLD: // @showhovertextworld=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
734 case RLV_BHVR_SHOWHOVERTEXTHUD: // @showhovertexthud=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
735 {
736 // Refresh all hover text (LLHUDText::setStringUTF8() will decide what needs clearing and what doesn't)
737 LLHUDText::refreshAllObjectText();
738 }
739 break;
740 case RLV_BHVR_SHOWHOVERTEXT: // @showhovertext:<uuid>=n - Checked: 2009-07-09 (RLVa-0.2.2a) | Modified: RLVa-1.0.0f
741 {
742 LLUUID idException(strOption);
743 if (idException.notNull()) // If there's an option it should be a valid UUID
744 {
745 addException(uuid, eBehaviour, idException);
746
747 // Clear the object's hover text
748 LLViewerObject* pObj = gObjectList.findObject(idException);
749 if ( (pObj) && (pObj->mText.notNull()) && (!pObj->mText->getObjectText().empty()) )
750 pObj->mText->setStringUTF8("");
751 }
752 }
753 break;
754 case RLV_BHVR_NOTIFY: // @notify:<option>=add - Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
755 {
756 S32 nChannel; std::string strFilter;
757 if ( (!strOption.empty()) && (rlvParseNotifyOption(strOption, nChannel, strFilter)) )
758 {
759 if (!m_pBhvrNotify)
760 addBehaviourObserver(m_pBhvrNotify = new RlvBehaviourNotifyObserver());
761 m_pBhvrNotify->addNotify(uuid, nChannel, strFilter);
762 }
763 }
764 break;
765 case RLV_BHVR_SENDCHANNEL: // @sendchannel:<uuid>=add - Checked: 2009-10-05 (RLVa-1.0.4a) | Modified: RLVa-1.0.4a
766 {
767 S32 nChannel; // If there's an option it should be a valid (=positive and non-zero) chat channel
768 if ( (!strOption.empty()) && (LLStringUtil::convertToS32(strOption, nChannel)) && (nChannel > 0) )
769 addException(uuid, eBehaviour, nChannel);
770 }
771 break;
772 case RLV_BHVR_RECVCHAT: // @recvchat:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
773 case RLV_BHVR_RECVEMOTE: // @recvemote:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
774 case RLV_BHVR_RECVIM: // @recvim:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
775 case RLV_BHVR_SENDIM: // @sendim:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
776 case RLV_BHVR_TPLURE: // @tplure:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
777 case RLV_BHVR_ACCEPTTP: // @accepttp:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
778 {
779 LLUUID idException(strOption);
780 if (idException.notNull()) // If there's an option it should be a valid UUID
781 addException(uuid, eBehaviour, LLUUID(strOption));
782 }
783 break;
784 case RLV_BHVR_UNKNOWN:
785 {
786 // Give our observers a chance to handle any command we don't
787 RlvEvent rlvEvent(uuid, rlvCmd);
788 m_Emitter.update(&RlvObserver::onAddCommand, rlvEvent);
789 }
790 break;
791 default:
792 break;
793 }
794 return TRUE; // Add command success/failure is decided by RlvObject::addCommand()
795}
796
797// Checked: 2009-08-05 (RLVa-1.0.1e) | Added: RLVa-1.0.1e
798void RlvHandler::processRetainedCommands()
799{
800 for (rlv_retained_list_t::const_iterator itCmd = m_Retained.begin(); itCmd != m_Retained.end(); ++itCmd)
801 {
802 const RlvRetainedCommand& cmd = *itCmd;
803 processCommand(cmd.idObject, cmd.strCmd, true);
804 }
805 m_Retained.clear();
806}
807
808BOOL RlvHandler::processRemoveCommand(const LLUUID& uuid, const RlvCommand& rlvCmd)
809{
810 // NOTE: - the RlvObject instance still exists at this point, but the viewer might already have removed it from its object list
811 ERlvBehaviour eBehaviour = rlvCmd.getBehaviourType();
812 const std::string& strOption = rlvCmd.getOption();
813
814 if ( (RLV_BHVR_UNKNOWN != eBehaviour) && (strOption.empty()) )
815 {
816 if (rlvCmd.isStrict())
817 removeException(uuid, RLV_BHVR_PERMISSIVE, eBehaviour);
818 m_Behaviours[eBehaviour]--;
819 }
820
821 bool fRefCount = false; // Unused for the moment
822 switch (eBehaviour)
823 {
824 case RLV_BHVR_DETACH: // @detach[:<option>]=y - Checked: 2009-10-10 (RLVa-1.0.5a) | Modified: RLVa-1.0.5a
825 onAddRemDetach(uuid, rlvCmd, fRefCount);
826 break;
827 case RLV_BHVR_ADDATTACH: // @addattach[:<option>]=y - Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a
828 case RLV_BHVR_REMATTACH: // @addattach[:<option>]=y - Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a
829 onAddRemAttach(uuid, rlvCmd, fRefCount);
830 break;
831 case RLV_BHVR_REDIRCHAT: // @redirchat:<option>=y - Checked: 2009-07-07 (RLVa-1.0.0d)
832 case RLV_BHVR_REDIREMOTE: // @rediremote:<option>=y - Checked: 2009-07-07 (RLVa-1.0.0d) | Added: RLVa-0.2.2a
833 {
834 if (!strOption.empty())
835 m_Behaviours[eBehaviour]--; // @redirchat and @rediremote don't have an optionless version so keep track of it here
836 else
837 m_Behaviours[eBehaviour]++; // @redirchat=n and @rediremote=n are undefined, don't keep track of them
838 }
839 break;
840 #ifdef RLV_EXTENSION_STARTLOCATION
841 case RLV_BHVR_TPLOC: // @tploc=y - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.1d
842 case RLV_BHVR_UNSIT: // @unsit=y - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.1d
843 {
844 if (strOption.empty())
845 RlvSettings::updateLoginLastLocation();
846 }
847 break;
848 #endif // RLV_EXTENSION_STARTLOCATION
849 case RLV_BHVR_ADDOUTFIT: // @addoutfit[:<layer>]=y - Checked: 2009-07-07 (RLVa-1.0.0d)
850 case RLV_BHVR_REMOUTFIT: // @remoutfit[:<layer>]=y - Checked: 2009-07-07 (RLVa-1.0.0d)
851 {
852 S16* pLayers = (eBehaviour == RLV_BHVR_ADDOUTFIT) ? m_LayersAdd : m_LayersRem;
853
854 if (strOption.empty())
855 {
856 for (int idx = 0; idx < WT_COUNT; idx++)
857 pLayers[idx]--;
858 }
859 else
860 {
861 EWearableType type = LLWearable::typeNameToType(strOption);
862 if (WT_INVALID != type)
863 {
864 pLayers[type]--;
865 m_Behaviours[eBehaviour]--;
866 }
867 }
868 }
869 break;
870 case RLV_BHVR_SETENV: // @setenv=y - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-0.2.0h
871 {
872 if (!fNoSetEnv)
873 {
874 // Restore WindLight parameters to what they were before @setenv=n was issued
875 RlvWLSnapshot::restoreSnapshot(m_pWLSnapshot);
876 delete m_pWLSnapshot;
877 m_pWLSnapshot = NULL;
878 }
879 }
880 break;
881 case RLV_BHVR_SHOWLOC: // @showloc=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
882 case RLV_BHVR_SHOWNAMES: // @shownames=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
883 case RLV_BHVR_SHOWHOVERTEXTALL: // @showhovertextal=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
884 case RLV_BHVR_SHOWHOVERTEXTWORLD: // @showhovertextworld=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
885 case RLV_BHVR_SHOWHOVERTEXTHUD: // @showhovertexthud=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
886 {
887 // If this was the last of any of the five restrictions we should refresh all hover text in case anything needs restoring
888 if (!m_Behaviours[eBehaviour])
889 LLHUDText::refreshAllObjectText();
890 }
891 break;
892 case RLV_BHVR_SHOWHOVERTEXT: // @showhovertext:<uuid>=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
893 {
894 LLUUID idException(strOption);
895 if (idException.notNull()) // If there's an option it should be a valid UUID
896 {
897 removeException(uuid, eBehaviour, idException);
898
899 // Restore the object's hover text
900 LLViewerObject* pObj = gObjectList.findObject(idException);
901 if ( (pObj) && (pObj->mText.notNull()) && (!pObj->mText->getObjectText().empty()) )
902 pObj->mText->setStringUTF8(pObj->mText->getObjectText());
903 }
904 }
905 break;
906 case RLV_BHVR_NOTIFY: // @notify:<option>=rem - Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
907 {
908 S32 nChannel; std::string strFilter;
909 if ( (m_pBhvrNotify) && (!strOption.empty()) && (rlvParseNotifyOption(strOption, nChannel, strFilter)) )
910 {
911 m_pBhvrNotify->removeNotify(uuid, nChannel, strFilter);
912
913 if (!m_pBhvrNotify->hasNotify())
914 {
915 removeBehaviourObserver(m_pBhvrNotify);
916 delete m_pBhvrNotify;
917 m_pBhvrNotify = NULL;
918 }
919 }
920 }
921 break;
922 case RLV_BHVR_SENDCHANNEL: // @sendchannel:<uuid>=rem - Checked: 2009-10-05 (RLVa-1.0.4a) | Modified: RLVa-1.0.4a
923 {
924 S32 nChannel; // If there's an option it should be a valid (=positive and non-zero) chat channel
925 if ( (!strOption.empty()) && (LLStringUtil::convertToS32(strOption, nChannel)) && (nChannel > 0) )
926 removeException(uuid, eBehaviour, nChannel);
927 }
928 break;
929 case RLV_BHVR_RECVCHAT: // @recvchat:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
930 case RLV_BHVR_RECVEMOTE: // @recvemote:<uui>=red - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
931 case RLV_BHVR_RECVIM: // @recvim:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
932 case RLV_BHVR_SENDIM: // @sendim:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
933 case RLV_BHVR_TPLURE: // @recvim:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
934 case RLV_BHVR_ACCEPTTP: // @accepttp:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
935 {
936 LLUUID idException(strOption);
937 if (idException.notNull()) // If there's an option it should be a valid UUID
938 removeException(uuid, eBehaviour, LLUUID(strOption));
939 }
940 break;
941 case RLV_BHVR_UNKNOWN:
942 {
943 // Give our observers a chance to handle any command we don't
944 RlvEvent rlvEvent(uuid, rlvCmd);
945 m_Emitter.update(&RlvObserver::onRemoveCommand, rlvEvent);
946 }
947 break;
948 default:
949 break;
950 } 665 }
951 return TRUE; // Remove commands don't fail, doesn't matter what we return here
952} 666}
953 667
954BOOL RlvHandler::processClearCommand(const LLUUID idObj, const RlvCommand& rlvCmd) 668ERlvCmdRet RlvHandler::processClearCommand(const LLUUID& idObj, const RlvCommand& rlvCmd)
955{ 669{
956 const std::string& strFilter = rlvCmd.getParam(); std::string strCmdRem; 670 const std::string& strFilter = rlvCmd.getParam(); std::string strCmdRem;
957 671
@@ -974,340 +688,10 @@ BOOL RlvHandler::processClearCommand(const LLUUID idObj, const RlvCommand& rlvCm
974 } 688 }
975 689
976 // Let our observers know about clear commands 690 // Let our observers know about clear commands
977 RlvEvent rlvEvent(idObj, rlvCmd); 691 ERlvCmdRet eRet = RLV_RET_SUCCESS;
978 m_Emitter.update(&RlvObserver::onClearCommand, rlvEvent); 692 notifyCommandHandlers(&RlvCommandHandler::onClearCommand, idObj, rlvCmd, eRet, true);
979 693
980 return TRUE; // Don't fail clear commands even if the object didn't exist since it confuses people 694 return RLV_RET_SUCCESS; // Don't fail clear commands even if the object didn't exist since it confuses people
981}
982
983BOOL RlvHandler::processForceCommand(const LLUUID& idObj, const RlvCommand& rlvCmd) const
984{
985 const std::string& strOption = rlvCmd.getOption();
986 BOOL fHandled = TRUE;
987
988 switch (rlvCmd.getBehaviourType())
989 {
990 case RLV_BHVR_DETACH: // @detach[:<option>]=force - Checked: 2009-10-12 (RLVa-1.0.5a) | Modified: RLVa-1.0.5a
991 onForceDetach(idObj, rlvCmd);
992 break;
993 case RLV_BHVR_REMATTACH: // @remattach[:<option>]=force - Checked: 2009-10-12 (RLVa-1.0.5a) | Modified: RLVa-1.0.5a
994 onForceRemAttach(idObj, rlvCmd);
995 break;
996 case RLV_BHVR_REMOUTFIT: // @remoutfit:<option>=force - Checked:
997 onForceRemOutfit(idObj, strOption);
998 break;
999 case RLV_BHVR_UNSIT: // @unsit=force - Checked: 2009-06-02 (RLVa-0.2.0g)
1000 {
1001 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
1002 if ( (pAvatar) && (pAvatar->mIsSitting) && (!hasBehaviourExcept(RLV_BHVR_UNSIT, idObj)) )
1003 {
1004 // See behaviour notes on why we have to force an agent update here
1005 gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
1006 send_agent_update(TRUE, TRUE);
1007 }
1008 }
1009 break;
1010 case RLV_BHVR_TPTO: // @tpto:<option>=force - Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-1.0.0h
1011 {
1012 fHandled = FALSE;
1013 if ( (!strOption.empty()) && (-1 == strOption.find_first_not_of("0123456789/.")) )
1014 {
1015 LLVector3d posGlobal;
1016
1017 boost_tokenizer tokens(strOption, boost::char_separator<char>("/", "", boost::keep_empty_tokens)); int idx = 0;
1018 for (boost_tokenizer::const_iterator itToken = tokens.begin(); itToken != tokens.end(); ++itToken)
1019 {
1020 if (idx < 3)
1021 LLStringUtil::convertToF64(*itToken, posGlobal[idx++]);
1022 }
1023
1024 if (idx == 3)
1025 {
1026 gAgent.teleportViaLocation(posGlobal);
1027 fHandled = TRUE;
1028 }
1029 }
1030 }
1031 break;
1032 case RLV_BHVR_SIT: // @sit:<option>=force - Checked: 2009-06-02 (RLVa-0.2.0g)
1033 fHandled = onForceSit(idObj, rlvCmd.getOption());
1034 break;
1035 case RLV_BHVR_ADDOUTFIT: // @addoutfit:<option>=force <- synonym of @attach:<option>=force
1036 case RLV_BHVR_ATTACH: // @attach:<option>=force - Checked:
1037 onForceWear(rlvCmd.getOption(), true, false); // Force attach single folder
1038 break;
1039 case RLV_BHVR_ATTACHALL: // @attachall:<option>=force - Checked:
1040 onForceWear(rlvCmd.getOption(), true, true); // Force attach nested folders
1041 break;
1042 case RLV_BHVR_DETACHALL: // @detachall:<option>=force - Checked:
1043 onForceWear(rlvCmd.getOption(), false, true); // Force detach nested folders
1044 break;
1045 case RLV_BHVR_ATTACHTHIS:
1046 case RLV_BHVR_ATTACHALLTHIS:
1047 case RLV_BHVR_DETACHTHIS:
1048 case RLV_BHVR_DETACHALLTHIS:
1049 {
1050 ERlvBehaviour eBehaviour = rlvCmd.getBehaviourType();
1051 std::string strReply;
1052 if (onGetPath(idObj, strOption, strReply))
1053 {
1054 LLStringUtil::toLower(strReply);
1055 onForceWear(strReply,
1056 (RLV_BHVR_ATTACHTHIS == eBehaviour) || (RLV_BHVR_ATTACHALLTHIS == eBehaviour),
1057 (RLV_BHVR_ATTACHALLTHIS == eBehaviour) || (RLV_BHVR_DETACHALLTHIS == eBehaviour));
1058 }
1059 }
1060 break;
1061 case RLV_BHVR_DETACHME: // @detachme=force - Checked: 2009-06-07 (RLVa-0.2.1c)
1062 {
1063 // NOTE: @detachme=force could be seen as a @detach:<attachpt>=force but RLV implements it as a "detach by UUID"
1064 LLViewerObject* pObj; LLVOAvatar* pAvatar; LLViewerJointAttachment* pAttachPt;
1065 if ( ((pObj = gObjectList.findObject(idObj)) != NULL) && (pObj->isAttachment()) &&
1066 ((pAvatar = gAgent.getAvatarObject()) != NULL) &&
1067 ((pAttachPt = pAvatar->getTargetAttachmentPoint(pObj->getRootEdit())) != NULL) )
1068 {
1069 handle_detach_from_avatar(pAttachPt);
1070 }
1071 }
1072 break;
1073 case RLV_BHVR_UNKNOWN:
1074 {
1075 // Give our observers a chance to handle any command we don't
1076 RlvEvent rlvEvent(idObj, rlvCmd);
1077 fHandled = m_Emitter.update(&RlvObserver::onForceCommand, rlvEvent);
1078 }
1079 break;
1080 default:
1081 break;
1082 }
1083 return fHandled; // If we handled it then it'll still be TRUE; if an observer doesn't handle it'll be FALSE
1084}
1085
1086// Checked: 2009-07-12 (RLVa-1.0.0h)
1087BOOL RlvHandler::processReplyCommand(const LLUUID& uuid, const RlvCommand& rlvCmd) const
1088{
1089 const std::string& strOption = rlvCmd.getOption();
1090 const std::string& strChannel = rlvCmd.getParam();
1091 std::string strReply;
1092
1093 BOOL fHandled = TRUE;
1094 switch (rlvCmd.getBehaviourType())
1095 {
1096 case RLV_BHVR_VERSION: // @version=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1097 strReply = getVersionString();
1098 break;
1099 case RLV_BHVR_VERSIONNUM: // @versionnum=<channel> - Checked: 2009-10-04 (RLVa-1.0.4b) | Added: RLVa-1.0.4b
1100 strReply = getVersionNumString();
1101 break;
1102 case RLV_BHVR_GETOUTFIT: // @getoufit[:<layer>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
1103 {
1104 // (Quirk: RLV 1.16.1 will execute @getoutfit=<channel> if <layer> is invalid, so we need to as well)
1105 EWearableType layerType = LLWearable::typeNameToType(strOption);
1106
1107 const EWearableType layerTypes[] =
1108 {
1109 WT_GLOVES, WT_JACKET, WT_PANTS, WT_SHIRT, WT_SHOES, WT_SKIRT, WT_SOCKS,
1110 WT_UNDERPANTS, WT_UNDERSHIRT, WT_SKIN, WT_EYES, WT_HAIR, WT_SHAPE, WT_ALPHA, WT_TATTOO
1111 };
1112
1113 #ifdef RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1114 for (int idx = 0, cnt = sizeof(layerTypes) / sizeof(EWearableType); idx < cnt; idx++)
1115 {
1116 if ( (WT_INVALID == layerType) || (layerTypes[idx] == layerType) )
1117 {
1118 // TODO-RLVa: add support for 'fHideLockedLayers'
1119 bool fWorn = (gAgent.getWearable(layerTypes[idx])) &&
1120 (!isHiddenCompositeItem(gAgent.getWearableItem(layerTypes[idx]),
1121 LLWearable::typeToTypeName(layerTypes[idx])));
1122 strReply.push_back( (fWorn) ? '1' : '0' );
1123 }
1124 }
1125 #else
1126 for (int idx = 0, cnt = sizeof(layerTypes) / sizeof(EWearableType); idx < cnt; idx++)
1127 if ( (WT_INVALID == layerType) || (layerTypes[idx] == layerType) )
1128 {
1129 // We never hide body parts, even if they're "locked" and we're hiding locked layers
1130 // (nor do we hide a layer if the issuing object is the only one that has this layer locked)
1131 bool fWorn = (gAgent.getWearable(layerTypes[idx])) &&
1132 ( (!RlvSettings::getHideLockedLayers()) ||
1133 (LLAssetType::AT_BODYPART == LLWearable::typeToAssetType(layerTypes[idx])) ||
1134 ( (isRemovableExcept(layerTypes[idx], uuid)) &&
1135 (isStrippable(gAgent.getWearableItem(layerTypes[idx]))) ) );
1136 strReply.push_back( (fWorn) ? '1' : '0' );
1137 //strReply.push_back( (gAgent.getWearable(layerTypes[idx])) ? '1' : '0' );
1138 }
1139 #endif // RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1140 }
1141 break;
1142 case RLV_BHVR_GETATTACH: // @getattach[:<layer>]=<channel> - Checked: 2009-10-10 (RLVa-1.0.5a) | Modified: RLVa-1.0.5a
1143 {
1144 // If we're fetching all worn attachments then the reply should start with 0
1145 if (strOption.empty())
1146 strReply.push_back('0');
1147
1148 LLVOAvatar* pAvatar = gAgent.getAvatarObject(); std::string strAttachName;
1149 for (LLVOAvatar::attachment_map_t::const_iterator itAttach = pAvatar->mAttachmentPoints.begin();
1150 itAttach != pAvatar->mAttachmentPoints.end(); ++itAttach)
1151 {
1152 LLViewerJointAttachment* pAttachment = itAttach->second;
1153 if (!pAttachment)
1154 continue;
1155
1156 strAttachName = pAttachment->getName(); // Capitalized (see avatar_lad.xml)
1157 LLStringUtil::toLower(strAttachName);
1158
1159 #ifdef RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1160 if ( (strOption.empty()) || (strOption == strAttachName) )
1161 {
1162 // TODO-RLVa: add support for 'fHideLockedAttach'
1163 bool fWorn = (pAttachment->getItemID().notNull()) &&
1164 (!isHiddenCompositeItem(pAttachment->getItemID(), strAttachName));
1165 strReply.push_back( (fWorn) ? '1' : '0' );
1166 }
1167 #else
1168 if ( (strOption.empty()) || (strOption == strAttachName) )
1169 {
1170 bool fWorn = (pAttachment->getItemID().notNull()) &&
1171 ( (!RlvSettings::getHideLockedAttach()) ||
1172 ( (!isLockedAttachmentExcept(itAttach->first, RLV_LOCK_REMOVE, gObjectList.findObject(uuid))) &&
1173 (isStrippable(pAttachment->getItemID())) ) );
1174 strReply.push_back( (fWorn) ? '1' : '0' );
1175 }
1176 #endif // RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1177 }
1178 }
1179 break;
1180 case RLV_BHVR_GETSTATUS: // @getstatus[:<option>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1181 {
1182 // NOTE: specification says response should start with '/' but RLV-1.16.1 returns an empty string when no rules are set
1183 rlv_object_map_t::const_iterator itObj = m_Objects.find(uuid);
1184 if (itObj != m_Objects.end())
1185 {
1186 std::string strObjStatus = itObj->second.getStatusString(strOption);
1187 if (!strObjStatus.empty())
1188 {
1189 strReply.push_back('/');
1190 strReply += strObjStatus;
1191 }
1192 }
1193 }
1194 break;
1195 case RLV_BHVR_GETSTATUSALL: // @getstatusall[:<option>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1196 {
1197 // NOTE: specification says response should start with '/' but RLV-1.16.1 returns an empty string when no rules are set
1198 std::string strObjStatus;
1199 for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
1200 {
1201 strObjStatus = itObj->second.getStatusString(strOption);
1202 if (!strObjStatus.empty())
1203 {
1204 strReply.push_back('/');
1205 strReply += strObjStatus;
1206 }
1207 }
1208 }
1209 break;
1210 case RLV_BHVR_GETINV: // @getinv[:<path>]=<channel> - Checked: 2009-07-28 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b
1211 {
1212 LLViewerInventoryCategory* pFolder = getSharedFolder(strOption);
1213 if (pFolder)
1214 {
1215 LLInventoryModel::cat_array_t* pFolders;
1216 LLInventoryModel::item_array_t* pItems;
1217 gInventory.getDirectDescendentsOf(pFolder->getUUID(), pFolders, pItems);
1218
1219 if (pFolders)
1220 {
1221 for (S32 idxFolder = 0, cntFolder = pFolders->count(); idxFolder < cntFolder; idxFolder++)
1222 {
1223 const std::string& strFolder = pFolders->get(idxFolder)->getName();
1224 if ( (!strFolder.empty()) && (RLV_FOLDER_PREFIX_HIDDEN != strFolder[0]) &&
1225 (!isFoldedFolder(pFolders->get(idxFolder).get(), true)) )
1226 {
1227 if (!strReply.empty())
1228 strReply.push_back(',');
1229 strReply += strFolder;
1230 }
1231 }
1232 }
1233 }
1234 }
1235 break;
1236 case RLV_BHVR_GETINVWORN: // @getinvworn[:path]=<channel> - Checked:
1237 onGetInvWorn(rlvCmd.getOption(), strReply);
1238 break;
1239 case RLV_BHVR_FINDFOLDER: // @findfolder:<criteria>=<channel> - Checked: 2009-08-26 (RLVa-1.0.2a) | Modified: RLVa-1.0.2a
1240 {
1241 // COMPAT-RLV: RLV 1.16.1 returns the first random folder it finds (probably tries to match "" to a folder name?)
1242 // (just going to stick with what's there for now... no option => no folder)
1243 LLInventoryModel::cat_array_t folders;
1244 if ( (!strOption.empty()) && (findSharedFolders(strOption, folders)) )
1245 {
1246 // We need to return an "in depth" result so whoever has the most '/' is our lucky winner
1247 // (maxSlashes needs to be initialized to -1 since children of the #RLV folder won't have '/' in their shared path)
1248 int maxSlashes = -1, curSlashes; std::string strFolderName;
1249 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
1250 {
1251 strFolderName = getSharedPath(folders.get(idxFolder));
1252
1253 curSlashes = std::count(strFolderName.begin(), strFolderName.end(), '/');
1254 if (curSlashes > maxSlashes)
1255 {
1256 maxSlashes = curSlashes;
1257 strReply = strFolderName;
1258 }
1259 }
1260 }
1261 }
1262 break;
1263 #ifdef RLV_EXTENSION_CMD_FINDFOLDERS
1264 case RLV_BHVR_FINDFOLDERS: // @findfolders:<criteria>=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.0b
1265 {
1266 LLInventoryModel::cat_array_t folders;
1267 if ( (!strOption.empty()) && (findSharedFolders(strOption, folders)) )
1268 {
1269 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
1270 {
1271 if (!strReply.empty())
1272 strReply.push_back(',');
1273 strReply += getSharedPath(folders.get(idxFolder));
1274 }
1275 }
1276 }
1277 break;
1278 #endif // RLV_EXTENSION_CMD_FINDFOLDERS
1279 case RLV_BHVR_GETPATH: // @getpath[:<option>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1280 onGetPath(uuid, rlvCmd.getOption(), strReply);
1281 break;
1282 case RLV_BHVR_GETSITID: // @getsitid=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1283 {
1284 // (Quirk: RLV 1.16.1 returns a NULL uuid if we're not sitting)
1285 LLVOAvatar* pAvatarObj = gAgent.getAvatarObject(); LLUUID uuid;
1286 if ( (pAvatarObj) && (pAvatarObj->mIsSitting) )
1287 {
1288 // LLVOAvatar inherits from 2 classes so make sure we get the right vfptr
1289 LLViewerObject* pAvatar = dynamic_cast<LLViewerObject*>(pAvatarObj), *pParent;
1290 // (If there is a parent, we need to upcast it from LLXform to LLViewerObject to get its UUID)
1291 if ( (pAvatar) && ((pParent = static_cast<LLViewerObject*>(pAvatar->getRoot())) != pAvatar) )
1292 uuid = pParent->getID();
1293 }
1294 strReply = uuid.asString();
1295 }
1296 break;
1297 case RLV_BHVR_UNKNOWN:
1298 {
1299 // Give our observers a chance to handle any command we don't
1300 RlvEvent rlvEvent(uuid, rlvCmd);
1301 return m_Emitter.update(&RlvObserver::onReplyCommand, rlvEvent);
1302 }
1303 break;
1304 default:
1305 break;
1306 }
1307
1308 if (fHandled)
1309 rlvSendChatReply(strChannel, strReply);
1310 return fHandled;
1311} 695}
1312 696
1313// ============================================================================ 697// ============================================================================
@@ -1340,7 +724,7 @@ void RlvHandler::initLookupTables()
1340 } 724 }
1341} 725}
1342 726
1343// Checked: 2009-08-11 (RLVa-1.0.1h) | Modified: RLVa-1.0.1h 727// Checked: 2010-07-18 (RLVa-1.1.2b) | Modified: RLVa-1.1.2a
1344void RlvHandler::onAttach(LLViewerJointAttachment* pAttachPt) 728void RlvHandler::onAttach(LLViewerJointAttachment* pAttachPt)
1345{ 729{
1346 // Sanity check - LLVOAvatar::attachObject() should call us *after* calling LLViewerJointAttachment::addObject() 730 // Sanity check - LLVOAvatar::attachObject() should call us *after* calling LLViewerJointAttachment::addObject()
@@ -1359,25 +743,25 @@ void RlvHandler::onAttach(LLViewerJointAttachment* pAttachPt)
1359 rlv_object_map_t::iterator itObj = m_Objects.find(pObj->getID()); 743 rlv_object_map_t::iterator itObj = m_Objects.find(pObj->getID());
1360 if (itObj != m_Objects.end()) 744 if (itObj != m_Objects.end())
1361 { 745 {
1362 // Save the attachment point index 746 // Only if we haven't been able to find this object (= attachment that rezzed in) or if it's a rezzed prim attached from in-world
1363 itObj->second.m_idxAttachPt = idxAttachPt; 747 if ( (!itObj->second.m_fLookup) || (!itObj->second.m_idxAttachPt) )
1364
1365 // If it's an attachment we processed commands for but that only just rezzed in we need to mark it as existing in gObjectList
1366 if (!itObj->second.m_fLookup)
1367 itObj->second.m_fLookup = true;
1368
1369 // In both cases we should check for "@detach=n" and actually lock down the attachment point it got attached to
1370 if (itObj->second.hasBehaviour(RLV_BHVR_DETACH, false))
1371 { 748 {
1372 // (Copy/paste from processAddCommand) 749 // Reset any lookup information we might have for this object
1373 addAttachmentLock(idxAttachPt, itObj->second.m_UUID, RLV_LOCK_REMOVE); 750 itObj->second.m_idxAttachPt = idxAttachPt;
751 itObj->second.m_fLookup = true;
1374 752
1375 if (pObj->isHUDAttachment()) 753 // In both cases we should check for "@detach=n" and actually lock down the attachment point it got attached to
1376 LLPipeline::sShowHUDAttachments = TRUE; // Prevents hiding of locked HUD attachments 754 if (itObj->second.hasBehaviour(RLV_BHVR_DETACH, false))
755 {
756 // (Copy/paste from processAddCommand)
757 addAttachmentLock(idxAttachPt, itObj->second.m_UUID, RLV_LOCK_REMOVE);
758 if (pObj->isHUDAttachment())
759 LLPipeline::sShowHUDAttachments = TRUE; // Prevents hiding of locked HUD attachments
760 }
1377 } 761 }
1378 } 762 }
1379 763
1380 // Fetch the inventory item if we don't currently have it since we might need it for reattach-on-detach 764 // Fetch the inventory item if it isn't already (we need it for a potential reattach-on-detach)
1381 const LLUUID& idItem = pAttachPt->getItemID(); 765 const LLUUID& idItem = pAttachPt->getItemID();
1382 LLViewerInventoryItem* pItem = ( (idItem.notNull()) && (gInventory.isInventoryUsable()) ) ? gInventory.getItem(idItem) : NULL; 766 LLViewerInventoryItem* pItem = ( (idItem.notNull()) && (gInventory.isInventoryUsable()) ) ? gInventory.getItem(idItem) : NULL;
1383 if ( (STATE_STARTED == LLStartUp::getStartupState()) && (pItem != NULL) ) 767 if ( (STATE_STARTED == LLStartUp::getStartupState()) && (pItem != NULL) )
@@ -1386,20 +770,22 @@ void RlvHandler::onAttach(LLViewerJointAttachment* pAttachPt)
1386 f.fetchItem(idItem); 770 f.fetchItem(idItem);
1387 } 771 }
1388 772
1389 // If what we're wearing is located under the shared root then append the attachment point name (if needed) 773 // If what we're wearing is located under the shared root then append the attachment point name as needed
1390 LLViewerInventoryCategory* pRlvRoot = getSharedRoot(); 774 LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
1391 if ( (STATE_STARTED == LLStartUp::getStartupState()) && (pRlvRoot) && (pItem) && (pItem->isComplete()) && 775 if ( (!RlvSettings::getEnableSharedWear()) && (RlvSettings::getSharedInvAutoRename()) &&
776 (STATE_STARTED == LLStartUp::getStartupState()) && (pRlvRoot) && (pItem) && (pItem->isComplete()) &&
1392 (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) ) 777 (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) )
1393 { 778 {
1394 std::string strAttachPt = pAttachPt->getName(); 779 // TODO: find a not too convoluted way to rename the attachment in case it specifies a name different than the current attach point
1395 LLStringUtil::toLower(strAttachPt); 780 S32 idxAttachPtItem = getAttachPointIndex(pItem, true);
1396 781 if ( (idxAttachPt != idxAttachPtItem) && (!idxAttachPtItem) )
1397 // If we can modify the item then it should contain the attach point name itself, otherwise its parent should
1398 if (pItem->getPermissions().allowModifyBy(gAgent.getID()))
1399 { 782 {
1400 if (!getAttachPoint(pItem, true)) 783 std::string strAttachPt = pAttachPt->getName();
784 LLStringUtil::toLower(strAttachPt);
785
786 // If we can modify the item then we rename it directly, otherwise we create a new folder and move it
787 if (pItem->getPermissions().allowModifyBy(gAgent.getID()))
1401 { 788 {
1402 // It doesn't specify an attach point and we can rename it [see LLItemBridge::renameItem()]
1403 std::string strName = pItem->getName(); 789 std::string strName = pItem->getName();
1404 LLStringUtil::truncate(strName, DB_INV_ITEM_NAME_STR_LEN - strAttachPt.length() - 3); 790 LLStringUtil::truncate(strName, DB_INV_ITEM_NAME_STR_LEN - strAttachPt.length() - 3);
1405 791
@@ -1410,25 +796,30 @@ void RlvHandler::onAttach(LLViewerJointAttachment* pAttachPt)
1410 gInventory.updateItem(pItem); 796 gInventory.updateItem(pItem);
1411 //gInventory.notifyObservers(); <- done further down in LLVOAvatar::attachObject() 797 //gInventory.notifyObservers(); <- done further down in LLVOAvatar::attachObject()
1412 } 798 }
1413 } 799 else
1414 else
1415 {
1416 // Folder can't be the shared root, or be its direct descendant (= nested at least 2 levels deep)
1417 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
1418 if ( (pFolder) &&
1419 (pFolder->getUUID() != pRlvRoot->getUUID()) && (pFolder->getParentUUID() != pRlvRoot->getUUID()) &&
1420 (!getAttachPoint(pFolder, true)) )
1421 { 800 {
1422 // It's no mod and its parent folder doesn't contain an attach point 801 // Don't do anything if the item is a direct descendant of the shared root, or a folded folder
1423 if ( (1 == rlvGetDirectDescendentsCount(pFolder, LLAssetType::AT_OBJECT)) && (NEW_CATEGORY_NAME == pFolder->getName()) ) 802 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
803 if ( (pFolder) && (pFolder->getUUID() != pRlvRoot->getUUID()) && (!isFoldedFolder(pFolder, true, false)) )
1424 { 804 {
1425 // Only rename if there's exactly 1 object/attachment inside of it [see LLFolderBridge::renameItem()] 805 std::string strFolderName = ".(" + strAttachPt + ")";
1426 std::string strName = ".(" + strAttachPt + ")";
1427 806
1428 pFolder->rename(strName); 807 // Rename the item's parent folder if it's called "New Folder", isn't directly under #RLV and contains exactly 1 object
1429 pFolder->updateServer(FALSE); 808 if ( (NEW_CATEGORY_NAME == pFolder->getName()) && (pFolder->getParentUUID() != pRlvRoot->getUUID()) &&
1430 gInventory.updateCategory(pFolder); 809 (1 == rlvGetDirectDescendentsCount(pFolder, LLAssetType::AT_OBJECT)) )
1431 //gInventory.notifyObservers(); <- done further down in LLVOAvatar::attachObject() 810 {
811 pFolder->rename(strFolderName);
812 pFolder->updateServer(FALSE);
813 gInventory.updateCategory(pFolder);
814 //gInventory.notifyObservers(); <- done further down in LLVOAvatar::attachObject()
815 }
816 else
817 {
818 // "No modify" item with a non-renameable parent: create a new folder named and move the item into it
819 LLUUID idAttachFolder = gInventory.createNewCategory(pFolder->getUUID(), LLAssetType::AT_NONE, strFolderName);
820 move_inventory_item(gAgent.getID(), gAgent.getSessionID(), pItem->getUUID(), idAttachFolder, std::string(), NULL);
821 //gInventory.notifyObservers(); <- done further down in LLVOAvatar::attachObject()
822 }
1432 } 823 }
1433 } 824 }
1434 } 825 }
@@ -1515,6 +906,28 @@ bool RlvHandler::onGC()
1515 return (0 != m_Objects.size()); // GC will kill itself if it has nothing to do 906 return (0 != m_Objects.size()); // GC will kill itself if it has nothing to do
1516} 907}
1517 908
909// Checked: 2009-11-26 (RLVa-1.1.0f) | Added: RLVa-1.1.0f
910void RlvHandler::onIdleStartup(void* pParam)
911{
912 LLTimer* pTimer = (LLTimer*)pParam;
913 if (LLStartUp::getStartupState() < STATE_STARTED)
914 {
915 // We don't want to run this *too* often
916 if ( (LLStartUp::getStartupState() >= STATE_MISC) && (pTimer->getElapsedTimeF32() >= 2.0) )
917 {
918 gRlvHandler.processRetainedCommands(RLV_BHVR_VERSION, RLV_TYPE_REPLY);
919 gRlvHandler.processRetainedCommands(RLV_BHVR_VERSIONNUM, RLV_TYPE_REPLY);
920 pTimer->reset();
921 }
922 }
923 else
924 {
925 // Clean-up
926 gIdleCallbacks.deleteFunction(onIdleStartup, pParam);
927 delete pTimer;
928 }
929}
930
1518// ============================================================================ 931// ============================================================================
1519// String/chat censoring functions 932// String/chat censoring functions
1520// 933//
@@ -1593,13 +1006,14 @@ void RlvHandler::filterLocation(std::string& strUTF8Text) const
1593 1006
1594 // Filter any mention of the surrounding region names 1007 // Filter any mention of the surrounding region names
1595 LLWorld::region_list_t regions = LLWorld::getInstance()->getRegionList(); 1008 LLWorld::region_list_t regions = LLWorld::getInstance()->getRegionList();
1009 const std::string& strHiddenRegion = RlvStrings::getString(RLV_STRING_HIDDEN_REGION);
1596 for (LLWorld::region_list_t::const_iterator itRegion = regions.begin(); itRegion != regions.end(); ++itRegion) 1010 for (LLWorld::region_list_t::const_iterator itRegion = regions.begin(); itRegion != regions.end(); ++itRegion)
1597 rlvStringReplace(strUTF8Text, (*itRegion)->getName(), rlv_handler_t::cstrHiddenRegion); 1011 rlvStringReplace(strUTF8Text, (*itRegion)->getName(), strHiddenRegion);
1598 1012
1599 // Filter any mention of the parcel name 1013 // Filter any mention of the parcel name
1600 LLViewerParcelMgr* pParcelMgr = LLViewerParcelMgr::getInstance(); 1014 LLViewerParcelMgr* pParcelMgr = LLViewerParcelMgr::getInstance();
1601 if (pParcelMgr) 1015 if (pParcelMgr)
1602 rlvStringReplace(strUTF8Text, pParcelMgr->getAgentParcelName(), rlv_handler_t::cstrHiddenParcel); 1016 rlvStringReplace(strUTF8Text, pParcelMgr->getAgentParcelName(), RlvStrings::getString(RLV_STRING_HIDDEN_PARCEL));
1603} 1017}
1604 1018
1605void RlvHandler::filterNames(std::string& strUTF8Text) const 1019void RlvHandler::filterNames(std::string& strUTF8Text) const
@@ -1638,55 +1052,36 @@ void RlvHandler::filterNames(std::string& strUTF8Text) const
1638 { 1052 {
1639 strName = strFirstName + " " + strLastName; 1053 strName = strFirstName + " " + strLastName;
1640 1054
1641 rlvStringReplace(strUTF8Text, strName, getAnonym(strName)); 1055 rlvStringReplace(strUTF8Text, strName, RlvStrings::getAnonym(strName));
1642 } 1056 }
1643 } 1057 }
1644 #endif 1058 #endif
1645} 1059}
1646 1060
1647const std::string& RlvHandler::getAnonym(const std::string& strName) const 1061// Checked: 2010-02-27 (RLVa-1.1.1a) | Modified: RLVa-1.2.0a
1648{
1649 const char* pszName = strName.c_str();
1650 U32 nHash = 0;
1651
1652 // Test with 11,264 SL names showed a 3.33% - 3.82% occurance for each so we *should* get a very even spread
1653 for (int idx = 0, cnt = strName.length(); idx < cnt; idx++)
1654 nHash += pszName[idx];
1655
1656 return cstrAnonyms[nHash % 28];
1657}
1658
1659// Checked: 2009-07-07 (RLVa-1.0.0d) | Modified: RLVa-0.2.2a
1660bool RlvHandler::redirectChatOrEmote(const std::string& strUTF8Text) const 1062bool RlvHandler::redirectChatOrEmote(const std::string& strUTF8Text) const
1661{ 1063{
1662 // Sanity check - @redirchat only for chat and @rediremote only for emotes 1064 // Sanity check - @redirchat only for chat and @rediremote only for emotes
1663 bool fIsEmote = rlvIsEmote(strUTF8Text); 1065 ERlvBehaviour eBhvr = (!rlvIsEmote(strUTF8Text)) ? RLV_BHVR_REDIRCHAT : RLV_BHVR_REDIREMOTE;
1664 if ( ((!fIsEmote) && (!hasBehaviour(RLV_BHVR_REDIRCHAT))) || ((fIsEmote) && (!hasBehaviour(RLV_BHVR_REDIREMOTE))) ) 1066 if (!hasBehaviour(eBhvr))
1665 return false; 1067 return false;
1666 1068
1667 if (!fIsEmote) 1069 if (RLV_BHVR_REDIRCHAT == eBhvr)
1668 { 1070 {
1669 std::string strText = strUTF8Text; 1071 std::string strText = strUTF8Text;
1670 filterChat(strText, true); 1072 filterChat(strText, false);
1671 if (strText != "...") 1073 if (strText != "...")
1672 return false; // @sendchat wouldn't filter it so @redirchat won't redirect it either 1074 return false; // @sendchat wouldn't filter it so @redirchat won't redirect it either
1673 } 1075 }
1674 1076
1675 bool fSendChannel = hasBehaviour(RLV_BHVR_SENDCHANNEL); S32 nChannel = 0; 1077 for (rlv_exception_map_t::const_iterator itRedir = m_Exceptions.lower_bound(eBhvr),
1676 for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj) 1078 endRedir = m_Exceptions.upper_bound(eBhvr); itRedir != endRedir; ++itRedir)
1677 { 1079 {
1678 for (rlv_command_list_t::const_iterator itCmd = itObj->second.m_Commands.begin(), 1080 S32 nChannel = boost::get<S32>(itRedir->second.varOption);
1679 endCmd = itObj->second.m_Commands.end(); itCmd != endCmd; ++itCmd) 1081 if ( (!hasBehaviour(RLV_BHVR_SENDCHANNEL)) || (isException(RLV_BHVR_SENDCHANNEL, nChannel)) )
1680 { 1082 rlvSendChatReply(nChannel, strUTF8Text);
1681 if ( ( ((!fIsEmote) && (RLV_BHVR_REDIRCHAT == itCmd->getBehaviourType())) || // Redirect if: (not an emote and @redirchat
1682 ((fIsEmote) && (RLV_BHVR_REDIREMOTE == itCmd->getBehaviourType())) ) && // OR an emote and @rediremote)
1683 (LLStringUtil::convertToS32(itCmd->getOption(), nChannel)) && // AND the channel is a number
1684 ( (!fSendChannel) || (isException(RLV_BHVR_SENDCHANNEL, nChannel)) ) ) // AND we're allowed to send to that channel
1685 {
1686 rlvSendChatReply(nChannel, strUTF8Text);
1687 }
1688 }
1689 } 1083 }
1084
1690 return true; 1085 return true;
1691} 1086}
1692 1087
@@ -1694,7 +1089,8 @@ bool RlvHandler::redirectChatOrEmote(const std::string& strUTF8Text) const
1694// Public service functions (called by the outside world or by extension handlers) 1089// Public service functions (called by the outside world or by extension handlers)
1695// 1090//
1696 1091
1697BOOL RlvHandler::isAgentNearby(const LLUUID& uuid) const 1092// Checked: 2009-11-24 (RLVa-1.1.0e)
1093bool RlvHandler::isAgentNearby(const LLUUID& idAgent) const
1698{ 1094{
1699 #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11 1095 #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
1700 for (LLWorld::region_list_t::const_iterator itRegion = LLWorld::getInstance()->mActiveRegionList.begin(); 1096 for (LLWorld::region_list_t::const_iterator itRegion = LLWorld::getInstance()->mActiveRegionList.begin();
@@ -1704,20 +1100,19 @@ BOOL RlvHandler::isAgentNearby(const LLUUID& uuid) const
1704 1100
1705 for (S32 idxAgent = 0, cntAgent = pRegion->mMapAvatars.count(); idxAgent < cntAgent; idxAgent++) 1101 for (S32 idxAgent = 0, cntAgent = pRegion->mMapAvatars.count(); idxAgent < cntAgent; idxAgent++)
1706 if (pRegion->mMapAvatarIDs.get(idxAgent) == uuid) 1102 if (pRegion->mMapAvatarIDs.get(idxAgent) == uuid)
1707 return TRUE; 1103 return true;
1708 } 1104 }
1709 #else // Version: trunk 1105 #else // Version: 1.23.4
1710 // TODO-RLV: rewrite this to fit trunk, but still need the radius limited to a sane range
1711 std::vector<LLUUID> idAgents; 1106 std::vector<LLUUID> idAgents;
1712 LLWorld::getInstance()->getAvatars(&idAgents, NULL); 1107 LLWorld::getInstance()->getAvatars(&idAgents, NULL);
1713 1108
1714 for (int idxAgent = 0, cntAgent = idAgents.size(); idxAgent < cntAgent; idxAgent++) 1109 for (int idxAgent = 0, cntAgent = idAgents.size(); idxAgent < cntAgent; idxAgent++)
1715 { 1110 {
1716 if (idAgents[idxAgent] == uuid) 1111 if (idAgents[idxAgent] == idAgent)
1717 return TRUE; 1112 return true;
1718 } 1113 }
1719 #endif 1114 #endif
1720 return FALSE; 1115 return false;
1721} 1116}
1722 1117
1723// ============================================================================ 1118// ============================================================================
@@ -1733,7 +1128,7 @@ public:
1733 virtual void done() 1128 virtual void done()
1734 { 1129 {
1735 RLV_INFOS << "Shared folders fetch completed" << LL_ENDL; 1130 RLV_INFOS << "Shared folders fetch completed" << LL_ENDL;
1736 RlvHandler::m_fFetchComplete = TRUE; 1131 RlvHandler::m_fFetchComplete = true;
1737 1132
1738 gInventory.removeObserver(this); 1133 gInventory.removeObserver(this);
1739 delete this; 1134 delete this;
@@ -1749,26 +1144,22 @@ void RlvHandler::fetchSharedInventory()
1749 return; 1144 return;
1750 1145
1751 // Grab all the folders under the shared root 1146 // Grab all the folders under the shared root
1752 LLInventoryModel::cat_array_t folders; 1147 LLInventoryModel::cat_array_t folders;
1753 LLInventoryModel::item_array_t items; 1148 LLInventoryModel::item_array_t items;
1754 gInventory.collectDescendents(pRlvRoot->getUUID(), folders, items, FALSE); 1149 gInventory.collectDescendents(pRlvRoot->getUUID(), folders, items, FALSE);
1755 1150
1756 /* 1151 // Add them to the "to fetch" list
1757 * Add them to the "to fetch" list
1758 */
1759 LLInventoryFetchDescendentsObserver::folder_ref_t fetchFolders; 1152 LLInventoryFetchDescendentsObserver::folder_ref_t fetchFolders;
1760
1761 fetchFolders.push_back(pRlvRoot->getUUID()); 1153 fetchFolders.push_back(pRlvRoot->getUUID());
1762 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++) 1154 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
1763 fetchFolders.push_back(folders.get(idxFolder)->getUUID()); 1155 fetchFolders.push_back(folders.get(idxFolder)->getUUID());
1764 1156
1765 /* 1157 // Now fetch them all in one go
1766 * Now fetch them all in one go
1767 */
1768 RlvSharedRootFetcher* fetcher = new RlvSharedRootFetcher; 1158 RlvSharedRootFetcher* fetcher = new RlvSharedRootFetcher;
1769 1159
1770 RLV_INFOS << "Starting fetch of " << fetchFolders.size() << " shared folders" << LL_ENDL; 1160 RLV_INFOS << "Starting fetch of " << fetchFolders.size() << " shared folders" << RLV_ENDL;
1771 fetcher->fetchDescendents(fetchFolders); 1161 fetcher->fetchDescendents(fetchFolders);
1162 m_fFetchStarted = true;
1772 1163
1773 if (fetcher->isEverythingComplete()) 1164 if (fetcher->isEverythingComplete())
1774 fetcher->done(); 1165 fetcher->done();
@@ -1796,7 +1187,7 @@ LLViewerInventoryCategory* RlvHandler::getSharedRoot()
1796{ 1187{
1797 if (gInventory.isInventoryUsable()) 1188 if (gInventory.isInventoryUsable())
1798 { 1189 {
1799 LLInventoryModel::cat_array_t* pFolders; 1190 LLInventoryModel::cat_array_t* pFolders;
1800 LLInventoryModel::item_array_t* pItems; 1191 LLInventoryModel::item_array_t* pItems;
1801 gInventory.getDirectDescendentsOf(gAgent.getInventoryRootID(), pFolders, pItems); 1192 gInventory.getDirectDescendentsOf(gAgent.getInventoryRootID(), pFolders, pItems);
1802 if (pFolders) 1193 if (pFolders)
@@ -1902,15 +1293,15 @@ std::string RlvHandler::getSharedPath(const LLViewerInventoryCategory* pFolder)
1902// Composite folders 1293// Composite folders
1903// 1294//
1904 1295
1905#ifdef RLV_EXPERIMENTAL_COMPOSITES 1296#ifdef RLV_EXPERIMENTAL_COMPOSITEFOLDERS
1906 // Checked: 1297 // Checked: 2009-12-18 (RLVa-1.1.0k) | Modified: RLVa-1.1.0i
1907 bool RlvHandler::getCompositeInfo(const LLInventoryCategory* pFolder, std::string* pstrName) const 1298 bool RlvHandler::getCompositeInfo(const LLInventoryCategory* pFolder, std::string* pstrName) const
1908 { 1299 {
1909 if (pFolder) 1300 if (pFolder)
1910 { 1301 {
1911 // Composite folder naming: ^\.?[Folder] 1302 // Composite folder naming: ^\.?[Folder]
1912 const std::string& cstrFolder = pFolder->getName(); 1303 const std::string& cstrFolder = pFolder->getName();
1913 int idxStart = cstrFolder.find('['), idxEnd = cstrFolder.find(']', idxStart); 1304 std::string::size_type idxStart = cstrFolder.find('['), idxEnd = cstrFolder.find(']', idxStart);
1914 if ( ((0 == idxStart) || (1 == idxStart)) && (idxEnd - idxStart > 1) ) 1305 if ( ((0 == idxStart) || (1 == idxStart)) && (idxEnd - idxStart > 1) )
1915 { 1306 {
1916 if (pstrName) 1307 if (pstrName)
@@ -1921,21 +1312,17 @@ std::string RlvHandler::getSharedPath(const LLViewerInventoryCategory* pFolder)
1921 return false; 1312 return false;
1922 } 1313 }
1923 1314
1924 // Checked: 1315 // Checked: 2009-12-18 (RLVa-1.1.0k) | Modified: RLVa-1.1.0i
1925 bool RlvHandler::getCompositeInfo(const LLUUID& idItem, std::string* pstrName, LLViewerInventoryCategory** ppFolder) const 1316 bool RlvHandler::getCompositeInfo(const LLUUID& idItem, std::string* pstrName, LLViewerInventoryCategory** ppFolder) const
1926 { 1317 {
1927 LLViewerInventoryCategory* pRlvRoot; LLViewerInventoryItem* pItem; 1318 LLViewerInventoryCategory* pRlvRoot; LLViewerInventoryItem* pItem;
1928
1929 if ( (idItem.notNull()) && ((pRlvRoot = getSharedRoot()) != NULL) && 1319 if ( (idItem.notNull()) && ((pRlvRoot = getSharedRoot()) != NULL) &&
1930 (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) && ((pItem = gInventory.getItem(idItem)) != NULL) ) 1320 (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) && ((pItem = gInventory.getItem(idItem)) != NULL) )
1931 { 1321 {
1932 // We know it's an item in a folder under the shared root... 1322 // We know it's an item in a folder under the shared root (we need its parent if it's a folded folder)
1933 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID()); 1323 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
1934 if (getAttachPoint(pFolder, true)) 1324 if (isFoldedFolder(pFolder, true, false)) // Don't check if the folder is a composite folder
1935 {
1936 // ... but it could be named ".(attachpt)" in which case we need its parent
1937 pFolder = gInventory.getCategory(pFolder->getParentUUID()); 1325 pFolder = gInventory.getCategory(pFolder->getParentUUID());
1938 }
1939 1326
1940 if ( (pFolder) && (getCompositeInfo(pFolder, pstrName)) ) 1327 if ( (pFolder) && (getCompositeInfo(pFolder, pstrName)) )
1941 { 1328 {
@@ -1946,7 +1333,7 @@ std::string RlvHandler::getSharedPath(const LLViewerInventoryCategory* pFolder)
1946 } 1333 }
1947 return false; 1334 return false;
1948 } 1335 }
1949#endif // RLV_EXPERIMENTAL_COMPOSITES 1336#endif // RLV_EXPERIMENTAL_COMPOSITEFOLDERS
1950 1337
1951#ifdef RLV_EXPERIMENTAL_COMPOSITE_FOLDING 1338#ifdef RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1952 // Checked: 1339 // Checked:
@@ -1985,367 +1372,124 @@ std::string RlvHandler::getSharedPath(const LLViewerInventoryCategory* pFolder)
1985 } 1372 }
1986#endif // RLV_EXPERIMENTAL_COMPOSITEFOLDING 1373#endif // RLV_EXPERIMENTAL_COMPOSITEFOLDING
1987 1374
1988#ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING 1375#ifdef RLV_EXPERIMENTAL_COMPOSITEFOLDERS
1989 // Checked: 1376 // Checked: 2009-12-18 (RLVa-1.1.0k) | Modified: RLVa-1.1.0i
1990 bool RlvHandler::canTakeOffComposite(const LLInventoryCategory* pFolder) const 1377 bool RlvHandler::canTakeOffComposite(const LLInventoryCategory* pFolder) const
1991 { 1378 {
1992 if (!pFolder) // If there's no folder then there is nothing to take off 1379 // Sanity check - if there's no folder or no avatar then there is nothing to take off
1380 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
1381 if ( (!pFolder) || (!pAvatar) )
1993 return false; 1382 return false;
1383 // Sanity check - if nothing is locked then we can definitely take it off
1384 if ( (!hasBehaviour(RLV_BHVR_REMOUTFIT)) && (!hasLockedAttachment(RLV_LOCK_REMOVE)) )
1385 return true;
1994 1386
1995 LLInventoryModel::cat_array_t folders; 1387 LLInventoryModel::cat_array_t folders;
1996 LLInventoryModel::item_array_t items; 1388 LLInventoryModel::item_array_t items;
1997 RlvWearableItemCollector functor(pFolder->getUUID(), true, false); 1389 RlvWearableItemCollector functor(pFolder->getUUID(), true, false);
1998
1999 // Grab a list of all the items @detachthis would be detaching/unwearing
2000 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor); 1390 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor);
2001 if (!items.count())
2002 return false; // There are no wearable items in the folder so there is nothing to take off
2003 1391
2004 LLViewerInventoryItem* pItem;
2005 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++) 1392 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
2006 { 1393 {
2007 pItem = items.get(idxItem); 1394 const LLViewerInventoryItem* pItem = items.get(idxItem);
2008
2009 switch (pItem->getType()) 1395 switch (pItem->getType())
2010 { 1396 {
1397 case LLAssetType::AT_BODYPART:
2011 case LLAssetType::AT_CLOTHING: 1398 case LLAssetType::AT_CLOTHING:
2012 { 1399 {
2013 LLWearable* pWearable = gAgent.getWearableFromWearableItem(pItem->getUUID()); 1400 LLWearable* pWearable = gAgent.getWearableFromWearableItem(pItem->getUUID());
2014 if ( (pWearable) && (!isRemovable(pWearable->getType())) ) 1401 if ( (pWearable) && (!isRemovable(pWearable->getType())) )
2015 return false; // If one clothing layer in the composite folder is unremoveable then the entire folder is 1402 return false; // If one wearable in the folder is non-removeable then the entire folder should be
2016 } 1403 }
2017 break; 1404 break;
2018 case LLAssetType::AT_OBJECT: 1405 case LLAssetType::AT_OBJECT:
2019 { 1406 {
2020 LLVOAvatar* pAvatar; LLViewerObject* pObj; 1407 LLViewerObject* pObj = pAvatar->getWornAttachment(pItem->getUUID());
2021 if ( ((pAvatar = gAgent.getAvatarObject()) != NULL) && 1408 if ( (pObj != NULL) && (isLockedAttachment(pObj, RLV_LOCK_REMOVE)) )
2022 ((pObj = pAvatar->getWornAttachment(pItem->getUUID())) != NULL) && (!isDetachable(pObj)) ) 1409 return false; // If one attachment in the folder is non-detachable then the entire folder should be
2023 {
2024 return false; // If one attachment in the composite folder is undetachable then the entire folder is
2025 }
2026 } 1410 }
2027 break; 1411 break;
2028 #ifdef LL_GNUC
2029 default: 1412 default:
2030 break; 1413 break;
2031 #endif // LL_GNUC
2032 } 1414 }
2033 } 1415 }
2034 return true; 1416 return true;
2035 } 1417 }
2036#endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2037
2038// ============================================================================
2039// Event handlers
2040//
2041
2042// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
2043void RlvHandler::onForceRemOutfit(const LLUUID& idObj, const std::string& strOption) const
2044{
2045 EWearableType typeOption = LLWearable::typeNameToType(strOption), type;
2046 if ( (WT_INVALID == typeOption) && (!strOption.empty()) )
2047 return;
2048
2049 // Before we had an option and optionless branch, but with the addition of composites and nostrip there's less duplication this way
2050 for (int idxType = 0; idxType < WT_COUNT; idxType++)
2051 {
2052 type = (EWearableType)idxType;
2053 if (LLAssetType::AT_CLOTHING != LLWearable::typeToAssetType(type))
2054 continue; // Only strip clothing, not bodyparts
2055
2056 if ( ((typeOption == type) || (strOption.empty())) && (gAgent.getWearable(type)) && (isStrippable(gAgent.getWearableItem(type))) )
2057 {
2058 #ifdef RLV_EXPERIMENTAL_COMPOSITES
2059 // If we're stripping something that's part of a composite folder then we should @detachthis instead
2060 if (isCompositeDescendent(gAgent.getWearableItem(type)))
2061 {
2062 std::string strCmd = "detachthis:" + LLWearable::typeToTypeName(type) + "=force";
2063 #ifdef RLV_DEBUG
2064 RLV_INFOS << "\t- '" << LLWearable::typeToTypeName(type) << "' is composite descendent: @" << strCmd << LL_ENDL;
2065 #endif // RLV_DEBUG
2066 processForceCommand(idObj, RlvCommand(strCmd));
2067 }
2068 else
2069 #endif // RLV_EXPERIMENTAL_COMPOSITES
2070 {
2071 gAgent.removeWearable(type);
2072 }
2073 }
2074 }
2075}
2076 1418
2077// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0g 1419 // Checked: 2009-12-18 (RLVa-1.1.0k) | Modified: RLVa-1.1.0i
2078bool RlvHandler::onForceSit(const LLUUID& idObj, const std::string& strOption) const 1420 bool RlvHandler::canWearComposite(const LLInventoryCategory* pFolder) const
2079{
2080 LLViewerObject* pObject = NULL; LLUUID idTarget(strOption);
2081 // Sanity checking - we need to know about the object and it should identify a prim/linkset
2082 if ( (idTarget.isNull()) || ((pObject = gObjectList.findObject(idTarget)) == NULL) || (LL_PCODE_VOLUME != pObject->getPCode()) )
2083 return false;
2084
2085 // Don't force sit if:
2086 // 1) currently sitting and prevented from standing up
2087 // 2) prevented from sitting
2088 // 3) @sittp=n restricted (except if @sittp=n was issued by the same prim that's currently force sitting the avie)
2089 if ( ( (hasBehaviour(RLV_BHVR_UNSIT)) && (gAgent.getAvatarObject()) && (gAgent.getAvatarObject()->mIsSitting) ) ||
2090 ( (hasBehaviour(RLV_BHVR_SIT)) ) ||
2091 ( (hasBehaviourExcept(RLV_BHVR_SITTP, idObj)) &&
2092 (dist_vec_squared(gAgent.getPositionGlobal(), pObject->getPositionGlobal()) > 1.5f * 1.5f) ))
2093 { 1421 {
2094 return false; 1422 // Sanity check - if there's no folder or no avatar then there is nothing to wear
2095 } 1423 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
2096 1424 if ( (!pFolder) || (!pAvatar) )
2097 // Copy/paste from handle_sit_or_stand() [see http://wiki.secondlife.com/wiki/AgentRequestSit] 1425 return false;
2098 gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit); 1426 // Sanity check - if nothing is locked then we can definitely wear it
2099 gMessageSystem->nextBlockFast(_PREHASH_AgentData); 1427 if ( (!hasBehaviour(RLV_BHVR_ADDOUTFIT)) && (!hasBehaviour(RLV_BHVR_REMOUTFIT)) && (!hasLockedAttachment(RLV_LOCK_ANY)) )
2100 gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); 1428 return true;
2101 gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
2102 gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
2103 gMessageSystem->addUUIDFast(_PREHASH_TargetID, pObject->mID);
2104 // Offset: "a rough position in local coordinates for the edge to sit on"
2105 // (we might not even be looking at the object so I don't think we can supply the offset to an edge)
2106 gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3::zero);
2107 pObject->getRegion()->sendReliableMessage();
2108
2109 return true;
2110}
2111
2112// Checked: 2009-10-10 (RLVa-1.0.5a) | Modified: RLVa-1.0.5a
2113void RlvHandler::onForceWear(const std::string& strPath, bool fAttach, bool fMatchAll) const
2114{
2115 // See LLWearableBridge::wearOnAvatar(): don't wear anything until initial wearables are loaded, can destroy clothing items
2116 if (!gAgent.areWearablesLoaded())
2117 return;
2118
2119 LLViewerInventoryCategory* pFolder = getSharedFolder(strPath);
2120 if (!pFolder) // Folder not found = nothing to attach
2121 return;
2122 1429
2123 LLInventoryModel::cat_array_t folders; 1430 LLInventoryModel::cat_array_t folders;
2124 LLInventoryModel::item_array_t items; 1431 LLInventoryModel::item_array_t items;
2125 RlvWearableItemCollector functor(pFolder->getUUID(), fAttach, fMatchAll); 1432 RlvWearableItemCollector functor(pFolder->getUUID(), true, false);
1433 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor);
2126 1434
2127 // Grab a list of all the items we'll be wearing/attaching 1435 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
2128 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor); 1436 {
1437 LLViewerInventoryItem* pItem = items.get(idxItem);
2129 1438
2130 LLViewerInventoryItem* pItem; 1439 if (RlvForceWear::isWearingItem(pItem))
2131 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++) 1440 continue; // Don't examine any items we're already wearing
2132 {
2133 pItem = items.get(idxItem);
2134 1441
2135 switch (pItem->getType()) 1442 // A wearable layer or attachment point:
2136 { 1443 // - can't be "add locked"
2137 case LLAssetType::AT_CLOTHING: 1444 // - can't be worn and "remove locked"
2138 case LLAssetType::AT_BODYPART: 1445 // - can't be worn and have its item belong to a *different* composite folder that we can't take off
2139 { 1446 switch (pItem->getType())
2140 LLWearable* pWearable = gAgent.getWearableFromWearableItem(pItem->getUUID()); 1447 {
2141 1448 case LLAssetType::AT_BODYPART:
2142 #ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING 1449 case LLAssetType::AT_CLOTHING:
2143 // If we're already wearing something on this layer then we have to check if it isn't part of a composite 1450 {
2144 // folder that has at least one unremovable item (in which case we can't wear or remove this item) 1451 // NOTE: without its asset we don't know what type the wearable is so we need to look at the item's flags instead
2145 LLViewerInventoryCategory* pCompositeFolder; 1452 EWearableType wtType = (EWearableType)(pItem->getFlags() & LLInventoryItem::II_FLAGS_WEARABLES_MASK);
2146 if ( (!pWearable) || (!getCompositeInfo(pItem->getUUID(), NULL, &pCompositeFolder)) || 1453 LLViewerInventoryCategory* pFolder;
2147 (canTakeOffComposite(pFolder))) 1454 if ( (!isWearable(wtType)) ||
1455 ( (gAgent.getWearable(wtType)) && (!isRemovable(wtType)) ) ||
1456 ( (gRlvHandler.getCompositeInfo(gAgent.getWearableItem(wtType), NULL, &pFolder)) &&
1457 (pFolder->getUUID() != pItem->getParentUUID()) && (!gRlvHandler.canTakeOffComposite(pFolder)) ) )
2148 { 1458 {
2149 #endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING 1459 return false;
2150 if (fAttach)
2151 {
2152 // Simulate wearing a clothing item from inventory (right click / "Wear")
2153 // LLWearableBridge::performAction() => LLWearableBridge::wearOnAvatar() => wear_inventory_item_on_avatar()
2154 wear_inventory_item_on_avatar(pItem);
2155 }
2156 else
2157 {
2158 if ( (pWearable) && (LLAssetType::AT_CLOTHING == pItem->getType()) )
2159 gAgent.removeWearable(pWearable->getType());
2160 }
2161 #ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2162 } 1460 }
2163 #endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING 1461 }
2164 } 1462 break;
2165 break; 1463 case LLAssetType::AT_OBJECT:
2166 case LLAssetType::AT_OBJECT: 1464 {
2167 { 1465 // If we made it here then *something* is add/remove locked so we absolutely need to know its attachment point
2168 LLVOAvatar* pAvatar = gAgent.getAvatarObject(); 1466 LLViewerJointAttachment* pAttachPt = getAttachPoint(pItem, true);
2169 LLViewerObject* pObj; 1467 LLViewerInventoryCategory* pFolder;
2170 1468 if ( (!pAttachPt) || (isLockedAttachment(pAttachPt, RLV_LOCK_ADD)) ||
2171 #ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING 1469 ( (pAttachPt->getObject()) && (isLockedAttachment(pAttachPt, RLV_LOCK_REMOVE)) ) ||
2172 // If we're already wearing something on this attach point then we have to check if it isn't part of a composite 1470 ( (gRlvHandler.getCompositeInfo(pAttachPt->getItemID(), NULL, &pFolder)) &&
2173 // folder that has at least one unremovable item (in which case we can't attach or detach this item) 1471 (pFolder->getUUID() != pItem->getParentUUID()) && (!gRlvHandler.canTakeOffComposite(pFolder)) ) )
2174 LLViewerInventoryCategory* pCompositeFolder;
2175 if ( (pAvatar) &&
2176 ( ((pObj = pAvatar->getWornAttachment(pItem->getUUID())) == NULL) ||
2177 (!getCompositeInfo(pItem->getUUID(), NULL, &pCompositeFolder)) || (canTakeOffComposite(pFolder)) ) )
2178 { 1472 {
2179 #endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING 1473 return false;
2180 if (fAttach)
2181 {
2182 // Simulate wearing an object to a specific attachment point (copy/paste to suppress replacement dialog)
2183 // LLAttachObject::handleEvent() => rez_attachment()
2184 LLViewerJointAttachment* pAttachPt = getAttachPoint(pItem, true);
2185 if ( (pAttachPt) && // Need a specific attach pt that
2186 ( (!isLockedAttachment(pAttachPt->getObject(), RLV_LOCK_REMOVE)) && // doesn't have locked object
2187 (!isLockedAttachment(pAttachPt, RLV_LOCK_ADD)) ) ) // and that can be attached to
2188 {
2189 RlvAttachmentManager::forceAttach(pItem->getUUID(), getAttachPointIndex(pAttachPt->getName(), true));
2190 }
2191 }
2192 else
2193 {
2194 if ( (pAvatar) && ((pObj = pAvatar->getWornAttachment(pItem->getUUID())) != NULL) )
2195 {
2196 LLViewerJointAttachment* pAttachment = pAvatar->getTargetAttachmentPoint(pObj);
2197 if (pAttachment)
2198 handle_detach_from_avatar(pAttachment);
2199 }
2200 }
2201 #ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2202 } 1474 }
2203 #endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING 1475 }
2204 } 1476 break;
2205 break; 1477 default:
2206 #ifdef LL_GNUC 1478 break;
2207 default: 1479 }
2208 break;
2209 #endif // LL_GNUC
2210 }
2211 }
2212}
2213
2214// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0g
2215bool RlvHandler::onGetPath(const LLUUID &uuid, const std::string& strOption, std::string& strReply) const
2216{
2217 // Sanity check - no need to go through all this trouble if we don't have a shared root
2218 LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
2219 if (!pRlvRoot)
2220 return false;
2221
2222 LLUUID idItem;
2223
2224 // <option> can be a clothing layer
2225 EWearableType layerType = LLWearable::typeNameToType(strOption);
2226 if (WT_INVALID != layerType)
2227 {
2228 idItem = gAgent.getWearableItem(layerType);
2229 }
2230 else
2231 {
2232 LLViewerJointAttachment* pAttachPt = NULL;
2233
2234 // ... or it can be empty
2235 if (strOption.empty())
2236 {
2237 // (in which case we act on the object that issued the command)
2238 LLViewerObject* pObj = gObjectList.findObject(uuid);
2239 if ( (pObj) && (pObj->isAttachment()) && (gAgent.getAvatarObject()) )
2240 pAttachPt = gAgent.getAvatarObject()->getTargetAttachmentPoint(pObj);
2241 }
2242 else
2243 {
2244 // ... or it can specify an attach point
2245 pAttachPt = getAttachPoint(strOption, true);
2246 }
2247
2248 // If we found something, get its inventory item UUID
2249 if (pAttachPt)
2250 idItem = pAttachPt->getItemID();
2251 }
2252
2253 // If we found something and it's under the shared root, then get its path
2254 if ( (!idItem.isNull()) && (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) )
2255 {
2256 LLInventoryItem* pItem = gInventory.getItem(idItem);
2257 if (pItem)
2258 {
2259 // ... unless the containing folder's name specifies an attach point (or nostrip) in which case we need its parent
2260 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
2261 #ifdef RLV_EXTENSION_FLAG_NOSTRIP
2262 if ( (getAttachPoint(pFolder, true)) || (pFolder->getName() == ".("RLV_FOLDER_FLAG_NOSTRIP")") )
2263 #else
2264 if (getAttachPoint(pFolder, true))
2265 #endif // RLV_EXTENSION_FLAG_NOSTRIP
2266 strReply = getSharedPath(pFolder->getParentUUID());
2267 else
2268 strReply = getSharedPath(pFolder);
2269 } 1480 }
1481 return true;
2270 } 1482 }
2271 return !strReply.empty(); 1483#endif // RLV_EXPERIMENTAL_COMPOSITEFOLDERS
2272}
2273
2274struct rlv_wear_info { U32 cntWorn, cntTotal, cntChildWorn, cntChildTotal; };
2275
2276// Checked: 2009-05-30 (RLVa-0.2.0e) | Modified: RLVa-0.2.0e
2277void RlvHandler::onGetInvWorn(const std::string& strPath, std::string& strReply) const
2278{
2279 // Sanity check - getAvatarObject() can't be NULL [see rlvIsWearingItem()] and the folder should exist and not be hidden
2280 LLViewerInventoryCategory* pFolder = getSharedFolder(strPath);
2281 if ((!gAgent.getAvatarObject()) || (!pFolder) || (pFolder->getName().empty()) || (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName()[0]))
2282 return;
2283
2284 // Collect everything @attachall would be attaching
2285 LLInventoryModel::cat_array_t folders;
2286 LLInventoryModel::item_array_t items;
2287 RlvWearableItemCollector functor(pFolder->getUUID(), true, true);
2288 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor);
2289
2290 rlv_wear_info wi = {0};
2291
2292 // Add all the folders to a lookup map
2293 std::map<LLUUID, rlv_wear_info> mapFolders;
2294 mapFolders.insert(std::pair<LLUUID, rlv_wear_info>(pFolder->getUUID(), wi));
2295 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
2296 mapFolders.insert(std::pair<LLUUID, rlv_wear_info>(folders.get(idxFolder)->getUUID(), wi));
2297
2298 // Iterate over all the found items
2299 LLViewerInventoryItem* pItem; std::map<LLUUID, rlv_wear_info>::iterator itFolder;
2300 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
2301 {
2302 pItem = items.get(idxItem);
2303
2304 // The "folded parent" is the folder this item should be considered a direct descendent of (may or may not match actual parent)
2305 const LLUUID& idParent = functor.getFoldedParent(pItem->getParentUUID());
2306
2307 // Walk up the tree: sooner or later one of the parents will be a folder in the map
2308 LLViewerInventoryCategory* pParent = gInventory.getCategory(idParent);
2309 while ( (itFolder = mapFolders.find(pParent->getUUID())) == mapFolders.end() )
2310 pParent = gInventory.getCategory(pParent->getParentUUID());
2311
2312 U32 &cntWorn = (idParent == pParent->getUUID()) ? itFolder->second.cntWorn : itFolder->second.cntChildWorn,
2313 &cntTotal = (idParent == pParent->getUUID()) ? itFolder->second.cntTotal : itFolder->second.cntChildTotal;
2314
2315 if (rlvIsWearingItem(pItem))
2316 cntWorn++;
2317 cntTotal++;
2318 }
2319
2320 // Extract the result for the main folder
2321 itFolder = mapFolders.find(pFolder->getUUID());
2322 wi.cntWorn = itFolder->second.cntWorn;
2323 wi.cntTotal = itFolder->second.cntTotal;
2324 mapFolders.erase(itFolder);
2325
2326 // Build the result for each child folder
2327 for (itFolder = mapFolders.begin(); itFolder != mapFolders.end(); ++itFolder)
2328 {
2329 rlv_wear_info& wiFolder = itFolder->second;
2330
2331 wi.cntChildWorn += wiFolder.cntWorn + wiFolder.cntChildWorn;
2332 wi.cntChildTotal += wiFolder.cntTotal + wiFolder.cntChildTotal;
2333
2334 strReply += llformat(",%s|%d%d", gInventory.getCategory(itFolder->first)->getName().c_str(),
2335 (0 == wiFolder.cntTotal) ? 0 : (0 == wiFolder.cntWorn) ? 1 : (wiFolder.cntWorn != wiFolder.cntTotal) ? 2 : 3,
2336 (0 == wiFolder.cntChildTotal) ? 0 : (0 == wiFolder.cntChildWorn) ? 1 : (wiFolder.cntChildWorn != wiFolder.cntChildTotal) ? 2 : 3
2337 );
2338 }
2339 1484
2340 // Now just prepend the root and done 1485// ============================================================================
2341 strReply = llformat("|%d%d", (0 == wi.cntTotal) ? 0 : (0 == wi.cntWorn) ? 1 : (wi.cntWorn != wi.cntTotal) ? 2 : 3, 1486// Event handlers
2342 (0 == wi.cntChildTotal) ? 0 : (0 == wi.cntChildWorn) ? 1 : (wi.cntChildWorn != wi.cntChildTotal) ? 2: 3) + strReply; 1487//
2343}
2344 1488
2345// (In case anyone cares: this isn't used in public builds) 1489// (In case anyone cares: this isn't used in public builds)
2346bool RlvHandler::getWornInfo(const LLInventoryCategory* pFolder, U8& wiFolder, U8& wiChildren) const 1490bool RlvHandler::getWornInfo(const LLInventoryCategory* pFolder, U8& wiFolder, U8& wiChildren) const
2347{ 1491{
2348 // Sanity check - getAvatarObject() can't be NULL [see rlvIsWearingItem()] and the folder should exist and not be hidden 1492 // Sanity check - getAvatarObject() can't be NULL [see RlvForceWear::isWearingItem()] and the folder should exist and not be hidden
2349 if ((!gAgent.getAvatarObject()) || (!pFolder) || (pFolder->getName().empty()) || (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName()[0])) 1493 if ((!gAgent.getAvatarObject()) || (!pFolder) || (pFolder->getName().empty()) || (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName()[0]))
2350 return false; 1494 return false;
2351 1495
@@ -2363,7 +1507,7 @@ bool RlvHandler::getWornInfo(const LLInventoryCategory* pFolder, U8& wiFolder, U
2363 bool fDirectDescendent = (pFolder->getUUID() == functor.getFoldedParent(pItem->getParentUUID())); 1507 bool fDirectDescendent = (pFolder->getUUID() == functor.getFoldedParent(pItem->getParentUUID()));
2364 U32 &refWorn = (fDirectDescendent) ? cntWorn : cntChildWorn, &refTotal = (fDirectDescendent) ? cntTotal : cntChildTotal; 1508 U32 &refWorn = (fDirectDescendent) ? cntWorn : cntChildWorn, &refTotal = (fDirectDescendent) ? cntTotal : cntChildTotal;
2365 1509
2366 if (rlvIsWearingItem(pItem)) 1510 if (RlvForceWear::isWearingItem(pItem))
2367 refWorn++; 1511 refWorn++;
2368 refTotal++; 1512 refTotal++;
2369 } 1513 }
@@ -2379,6 +1523,7 @@ bool RlvHandler::getWornInfo(const LLInventoryCategory* pFolder, U8& wiFolder, U
2379// Initialization helper functions 1523// Initialization helper functions
2380// 1524//
2381 1525
1526// Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
2382BOOL RlvHandler::setEnabled(BOOL fEnable) 1527BOOL RlvHandler::setEnabled(BOOL fEnable)
2383{ 1528{
2384 if (m_fEnabled == fEnable) 1529 if (m_fEnabled == fEnable)
@@ -2386,17 +1531,17 @@ BOOL RlvHandler::setEnabled(BOOL fEnable)
2386 1531
2387 if (fEnable) 1532 if (fEnable)
2388 { 1533 {
2389 if (gSavedSettings.controlExists(RLV_SETTING_NOSETENV)) 1534 // Initialize the command lookup table
2390 fNoSetEnv = gSavedSettings.getBOOL(RLV_SETTING_NOSETENV);
2391 if (gSavedSettings.controlExists(RLV_SETTING_ENABLELEGACYNAMING))
2392 fLegacyNaming = gSavedSettings.getBOOL(RLV_SETTING_ENABLELEGACYNAMING);
2393 if (gSavedSettings.controlExists(RLV_SETTING_SHOWNAMETAGS))
2394 RlvSettings::fShowNameTags = gSavedSettings.getBOOL(RLV_SETTING_SHOWNAMETAGS);
2395
2396 RlvCommand::initLookupTable(); 1535 RlvCommand::initLookupTable();
1536
1537 // Initialize static classes
1538 RlvSettings::initClass();
1539 RlvStrings::initClass();
1540
2397 gRlvHandler.m_pAttachMgr = new RlvAttachmentManager(); 1541 gRlvHandler.m_pAttachMgr = new RlvAttachmentManager();
2398 gRlvHandler.addObserver(new RlvExtGetSet()); 1542 gRlvHandler.addCommandHandler(new RlvExtGetSet());
2399 1543
1544 // Fetch shared inventory if we're enabled after logon
2400 if (LLStartUp::getStartupState() >= STATE_CLEANUP) 1545 if (LLStartUp::getStartupState() >= STATE_CLEANUP)
2401 fetchSharedInventory(); 1546 fetchSharedInventory();
2402 1547
@@ -2404,17 +1549,7 @@ BOOL RlvHandler::setEnabled(BOOL fEnable)
2404 } 1549 }
2405 else if (canDisable()) 1550 else if (canDisable())
2406 { 1551 {
2407 #ifdef RLV_DEBUG
2408 RLV_INFOS << "Disabling RLV:" << LL_ENDL;
2409 #endif // RLV_DEBUG
2410
2411 gRlvHandler.clearState(); 1552 gRlvHandler.clearState();
2412
2413 #ifdef RLV_DEBUG
2414 RLV_INFOS << "\t--> RLV disabled" << LL_ENDL;
2415 #endif // RLV_DEBUG
2416
2417 m_fEnabled = FALSE;
2418 } 1553 }
2419 1554
2420 #ifdef RLV_ADVANCED_MENU 1555 #ifdef RLV_ADVANCED_MENU
@@ -2466,7 +1601,7 @@ void RlvHandler::clearState()
2466 memset(m_LayersRem, 0, sizeof(S16) * WT_COUNT); 1601 memset(m_LayersRem, 0, sizeof(S16) * WT_COUNT);
2467 memset(m_Behaviours, 0, sizeof(S16) * RLV_BHVR_COUNT); 1602 memset(m_Behaviours, 0, sizeof(S16) * RLV_BHVR_COUNT);
2468 m_Retained.clear(); 1603 m_Retained.clear();
2469 m_Emitter.clearObservers(); // <- calls delete on all active observers 1604 clearCommandHandlers(); // <- calls delete on all registered command handlers
2470 1605
2471 // Clear dynamically allocated memory 1606 // Clear dynamically allocated memory
2472 delete m_pGCTimer; 1607 delete m_pGCTimer;
@@ -2481,6 +1616,359 @@ void RlvHandler::clearState()
2481// Command handlers (RLV_TYPE_ADD and RLV_TYPE_REMOVE) 1616// Command handlers (RLV_TYPE_ADD and RLV_TYPE_REMOVE)
2482// 1617//
2483 1618
1619#define VERIFY_OPTION(x) { if (!(x)) { eRet = RLV_RET_FAILED_OPTION; break; } }
1620#define VERIFY_OPTION_REF(x) { if (!(x)) { eRet = RLV_RET_FAILED_OPTION; break; } fRefCount = true; }
1621
1622// Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1623ERlvCmdRet RlvHandler::processAddRemCommand(const LLUUID& idObj, const RlvCommand& rlvCmd)
1624{
1625 // NOTE: - at this point the command has already been:
1626 // * added to the RlvObject
1627 // * removed from the RlvObject (which still exists at this point even if this is the last restriction)
1628 // - the object's UUID may or may not exist in gObjectList (see handling of @detach=n|y)
1629 ERlvBehaviour eBhvr = rlvCmd.getBehaviourType(); ERlvParamType eType = rlvCmd.getParamType();
1630
1631 ERlvCmdRet eRet = RLV_RET_SUCCESS; bool fRefCount = false, fRefreshHover = false; const std::string& strOption = rlvCmd.getOption();
1632 switch (eBhvr)
1633 {
1634 case RLV_BHVR_DETACH: // @detach[:<option>]=n|y
1635 eRet = onAddRemDetach(idObj, rlvCmd, fRefCount);
1636 break;
1637 case RLV_BHVR_ADDATTACH: // @addattach[:<option>]=n|y
1638 case RLV_BHVR_REMATTACH: // @addattach[:<option>]=n|y
1639 eRet = onAddRemAttach(idObj, rlvCmd, fRefCount);
1640 break;
1641 case RLV_BHVR_ADDOUTFIT: // @addoutfit[:<layer>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1642 case RLV_BHVR_REMOUTFIT: // @remoutfit[:<layer>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1643 {
1644 // If there's an option it should specify a wearable type name (reference count on no option *and* a valid option)
1645 EWearableType wtType = LLWearable::typeNameToType(strOption);
1646 VERIFY_OPTION_REF( (strOption.empty()) || (WT_INVALID != wtType) );
1647
1648 S16* pLayers = (RLV_BHVR_ADDOUTFIT == eBhvr) ? m_LayersAdd : m_LayersRem;
1649 for (int idxType = 0; idxType < WT_COUNT; idxType++)
1650 {
1651 if ( ((EWearableType)idxType == wtType) || (WT_INVALID == wtType) )
1652 {
1653 if (RLV_TYPE_ADD == eType)
1654 pLayers[idxType]++;
1655 else
1656 pLayers[idxType]--;
1657 }
1658 }
1659 }
1660 break;
1661 case RLV_BHVR_REDIRCHAT: // @redirchat:<channel>=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1662 case RLV_BHVR_REDIREMOTE: // @rediremote:<channel>=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1663 {
1664 // There should be an option and it should specify a valid reply channel (if there's an empty option the command is invalid)
1665 S32 nChannel = 0;
1666 VERIFY_OPTION_REF( (LLStringUtil::convertToS32(strOption, nChannel)) && (rlvIsValidReplyChannel(nChannel)) );
1667
1668 if (RLV_TYPE_ADD == eType)
1669 addException(idObj, eBhvr, nChannel);
1670 else
1671 removeException(idObj, eBhvr, nChannel);
1672 }
1673 break;
1674 case RLV_BHVR_SENDCHANNEL: // @sendchannel[:<channel>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1675 {
1676 // If there's an option then it should be a valid (= positive and non-zero) chat channel (only reference count empty option)
1677 S32 nChannel = 0;
1678 if ( (LLStringUtil::convertToS32(strOption, nChannel)) && (nChannel > 0) )
1679 {
1680 if (RLV_TYPE_ADD == eType)
1681 addException(idObj, eBhvr, nChannel);
1682 else
1683 removeException(idObj, eBhvr, nChannel);
1684 break;
1685 }
1686 VERIFY_OPTION_REF(strOption.empty());
1687 }
1688 break;
1689 case RLV_BHVR_NOTIFY: // @notify:<params>=add|rem - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1690 {
1691 // There should be an option that we can successfully parse (if there's an empty option the command is invalid)
1692 S32 nChannel; std::string strFilter;
1693 VERIFY_OPTION_REF( (!strOption.empty()) && (rlvParseNotifyOption(strOption, nChannel, strFilter)) );
1694
1695 if (RLV_TYPE_ADD == eType)
1696 {
1697 if (!m_pBhvrNotify)
1698 addBehaviourObserver(m_pBhvrNotify = new RlvBehaviourNotifyObserver());
1699 m_pBhvrNotify->addNotify(idObj, nChannel, strFilter);
1700 }
1701 else if (m_pBhvrNotify)
1702 {
1703 m_pBhvrNotify->removeNotify(idObj, nChannel, strFilter);
1704 if (!m_pBhvrNotify->hasNotify())
1705 {
1706 removeBehaviourObserver(m_pBhvrNotify);
1707 delete m_pBhvrNotify;
1708 m_pBhvrNotify = NULL;
1709 }
1710 }
1711 }
1712 break;
1713 case RLV_BHVR_SHOWINV: // @showinv=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1714 {
1715 VERIFY_OPTION_REF(strOption.empty());
1716
1717 if (RLV_TYPE_ADD == eType)
1718 {
1719 // Close all open inventory windows
1720 LLInventoryView::closeAll();
1721 }
1722 }
1723 break;
1724 case RLV_BHVR_SHOWMINIMAP: // @showminimap=n|y - Checked: 2009-12-05 (RLVa-1.1.0g) | Modified: RLVa-1.1.0g
1725 {
1726 VERIFY_OPTION_REF(strOption.empty());
1727
1728 if (RLV_TYPE_ADD == eType)
1729 {
1730 // Simulate clicking the Minimap button [see LLToolBar::onClickRadar()]
1731 #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
1732 if (gFloaterMap->getVisible())
1733 LLFloaterMap::toggle(NULL);
1734 #else // Version: 1.23.4
1735 if (LLFloaterMap::instanceVisible())
1736 LLFloaterMap::hideInstance();
1737 #endif
1738 }
1739 }
1740 break;
1741 case RLV_BHVR_SHOWWORLDMAP: // @showworldmap=n|y - Checked: 2009-12-05 (RLVa-1.1.0g) | Modified: RLVa-1.1.0g
1742 {
1743 VERIFY_OPTION_REF(strOption.empty());
1744
1745 if (RLV_TYPE_ADD == eType)
1746 {
1747 // Simulate clicking the Map button [see LLToolBar::onClickMap()]
1748 if (gFloaterWorldMap->getVisible())
1749 LLFloaterWorldMap::toggle(NULL);
1750 }
1751 }
1752 break;
1753 case RLV_BHVR_SHOWLOC: // @showloc=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1754 {
1755 VERIFY_OPTION_REF(strOption.empty());
1756
1757 if (RLV_TYPE_ADD == eType)
1758 {
1759 // If this is the first @showloc=n restriction refresh all object text so we can filter it if necessary
1760 fRefreshHover = (0 == m_Behaviours[RLV_BHVR_SHOWLOC]);
1761
1762 // Close the "About Land" floater if it's currently visible
1763 if (LLFloaterLand::instanceVisible())
1764 LLFloaterLand::hideInstance();
1765
1766 // Close the "Estate Tools" floater is it's currently visible
1767 if (LLFloaterRegionInfo::instanceVisible())
1768 LLFloaterRegionInfo::hideInstance();
1769
1770 // NOTE: we should close the "God Tools" floater as well, but since calling LLFloaterGodTools::instance() always
1771 // creates a new instance of the floater and since it's very unlikely to be open it's just better not to
1772 }
1773 else
1774 {
1775 // If this is the last @showloc=n restriction refresh all object text in case anything needs restoring
1776 fRefreshHover = (1 == m_Behaviours[RLV_BHVR_SHOWLOC]);
1777 }
1778 }
1779 break;
1780 case RLV_BHVR_SHOWNAMES: // @shownames=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1781 {
1782 VERIFY_OPTION_REF(strOption.empty());
1783
1784 if (RLV_TYPE_ADD == eType)
1785 {
1786 // If this is the first @shownames=n restriction refresh all object text so we can filter it if necessary
1787 fRefreshHover = (0 == m_Behaviours[RLV_BHVR_SHOWNAMES]);
1788
1789 // Close the "Active Speakers" panel if it's currently visible
1790 LLFloaterChat::getInstance()->childSetVisible("active_speakers_panel", false);
1791 }
1792 else
1793 {
1794 // If this is the last @shownames=n restriction refresh all object text in case anything needs restoring
1795 fRefreshHover = (1 == m_Behaviours[RLV_BHVR_SHOWNAMES]);
1796 }
1797 }
1798 break;
1799 case RLV_BHVR_SHOWHOVERTEXTALL: // @showhovertextall=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1800 case RLV_BHVR_SHOWHOVERTEXTWORLD: // @showhovertextworld=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1801 case RLV_BHVR_SHOWHOVERTEXTHUD: // @showhovertexthud=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1802 {
1803 VERIFY_OPTION_REF(strOption.empty());
1804
1805 // Refresh all object text on the first/last add/rem(LLHUDText::setStringUTF8() decides what needs clearing and what doesn't)
1806 fRefreshHover = ((RLV_TYPE_ADD == eType) && (0 == m_Behaviours[eBhvr])) ||
1807 ((RLV_TYPE_REMOVE == eType) && (1 == m_Behaviours[eBhvr]));
1808 }
1809 break;
1810 case RLV_BHVR_SHOWHOVERTEXT: // @showhovertext:<uuid>=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1811 {
1812 // There should be an option and it should specify a valid UUID
1813 LLUUID idException(strOption);
1814 VERIFY_OPTION_REF(idException.notNull());
1815
1816 if (RLV_TYPE_ADD == eType)
1817 addException(idObj, eBhvr, idException);
1818 else
1819 removeException(idObj, eBhvr, idException);
1820
1821 // Clear/restore the object's hover text as needed
1822 LLViewerObject* pObj = gObjectList.findObject(idException);
1823 if ( (pObj) && (pObj->mText.notNull()) && (!pObj->mText->getObjectText().empty()) )
1824 pObj->mText->setStringUTF8( (RLV_TYPE_ADD == eType) ? "" : pObj->mText->getObjectText());
1825 }
1826 break;
1827 case RLV_BHVR_EDIT: // @edit=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1828 {
1829 VERIFY_OPTION_REF(strOption.empty());
1830
1831 if (RLV_TYPE_ADD == eType)
1832 {
1833 // Turn off "View / Highlight Transparent"
1834 LLDrawPoolAlpha::sShowDebugAlpha = FALSE;
1835
1836 // Close the Beacons floater if it's open
1837 if (LLFloaterBeacons::instanceVisible())
1838 LLFloaterBeacons::toggleInstance();
1839
1840 // Get rid of the build floater if it's open [copy/paste from toggle_build_mode()]
1841 if (gFloaterTools->getVisible())
1842 {
1843 gAgent.resetView(FALSE);
1844 gFloaterTools->close();
1845 gViewerWindow->showCursor();
1846 }
1847 }
1848 }
1849 break;
1850#ifdef RLV_EXTENSION_CMD_TOUCHXXX
1851 case RLV_BHVR_TOUCH: // @touch:<uuid>=n - Checked: 2010-01-01 (RLVa-1.1.0l) | Added: RLVa-1.1.0l
1852 {
1853 // There should be an option and it should specify a valid UUID
1854 LLUUID idException(strOption);
1855 VERIFY_OPTION_REF(idException.notNull());
1856
1857 if (RLV_TYPE_ADD == eType)
1858 addException(idObj, eBhvr, idException);
1859 else
1860 removeException(idObj, eBhvr, idException);
1861 }
1862 break;
1863#endif // RLV_EXTENSION_CMD_TOUCHXXX
1864 case RLV_BHVR_FLY: // @fly=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1865 {
1866 VERIFY_OPTION_REF(strOption.empty());
1867
1868 if (RLV_TYPE_ADD == eType)
1869 gAgent.setFlying(FALSE);
1870 }
1871 break;
1872 case RLV_BHVR_SETENV: // @setenv=n|y
1873 eRet = onAddRemSetEnv(idObj, rlvCmd, fRefCount);
1874 break;
1875 // The following block is only valid if there's no option
1876 case RLV_BHVR_EMOTE: // @emote=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1877 case RLV_BHVR_SENDCHAT: // @sendchat=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1878 case RLV_BHVR_CHATWHISPER: // @chatwhisper=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1879 case RLV_BHVR_CHATNORMAL: // @chatnormal=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1880 case RLV_BHVR_CHATSHOUT: // @chatshout=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1881 case RLV_BHVR_PERMISSIVE: // @permissive=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1882 case RLV_BHVR_TPLM: // @tplm=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1883 case RLV_BHVR_TPLOC: // @tploc=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1884 case RLV_BHVR_VIEWNOTE: // @viewnote=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1885 case RLV_BHVR_VIEWSCRIPT: // @viewscript=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1886 case RLV_BHVR_VIEWTEXTURE: // @viewtexture=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1887 case RLV_BHVR_ACCEPTPERMISSION: // @acceptpermission=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1888 case RLV_BHVR_DEFAULTWEAR: // @defaultwear=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1889#ifdef RLV_EXTENSION_CMD_ALLOWIDLE
1890 case RLV_BHVR_ALLOWIDLE: // @allowidle=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1891#endif // RLV_EXTENSION_CMD_ALLOWIDLE
1892 case RLV_BHVR_REZ: // @rez=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1893 case RLV_BHVR_FARTOUCH: // @fartouch=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1894#ifdef RLV_EXTENSION_CMD_INTERACT
1895 case RLV_BHVR_INTERACT: // @interact=n|y - Checked: 2010-01-01 (RLVa-1.1.0l) | Added: RLVa-1.1.0l
1896#endif // RLV_EXTENSION_CMD_INTERACT
1897 case RLV_BHVR_UNSIT: // @unsit=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1898 case RLV_BHVR_SIT: // @sit=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1899 case RLV_BHVR_SITTP: // @sittp=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1900 case RLV_BHVR_SETDEBUG: // @setdebug=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1901 VERIFY_OPTION_REF(strOption.empty());
1902 break;
1903 // The following block is only valid if there's no option (= restriction) or if it specifies a valid UUID (= behaviour exception)
1904 case RLV_BHVR_RECVCHAT: // @recvchat[:<uuid>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1905 case RLV_BHVR_RECVEMOTE: // @recvemote[:<uuid>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1906 case RLV_BHVR_SENDIM: // @sendim[:<uuid>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1907 case RLV_BHVR_RECVIM: // @recvim[:<uuid>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1908 case RLV_BHVR_TPLURE: // @tplure[:<uuid>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1909 case RLV_BHVR_ACCEPTTP: // @accepttp[:<uuid>]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h
1910#ifdef RLV_EXTENSION_CMD_TOUCHXXX
1911 case RLV_BHVR_TOUCHWORLD: // @touchworld[:<uuid>=n|y - Checked: 2010-01-01 (RLVa-1.1.0l) | Added: RLVa-1.1.0l
1912 case RLV_BHVR_TOUCHATTACH: // @touchattach[:<uuid>=n|y - Checked: 2010-01-01 (RLVa-1.1.0l) | Added: RLVa-1.1.0l
1913 case RLV_BHVR_TOUCHHUD: // @touchhud[:<uuid>=n|y - Checked: 2010-01-01 (RLVa-1.1.0l) | Added: RLVa-1.1.0l
1914#endif // RLV_EXTENSION_CMD_TOUCHXXX
1915 {
1916 LLUUID idException(strOption);
1917 if (idException.notNull()) // If there's an option then it should specify a valid UUID
1918 {
1919 if (RLV_TYPE_ADD == eType)
1920 addException(idObj, eBhvr, idException);
1921 else
1922 removeException(idObj, eBhvr, idException);
1923 break;
1924 }
1925 VERIFY_OPTION_REF(strOption.empty());
1926 }
1927 break;
1928 case RLV_BHVR_UNKNOWN:
1929 // Pass unknown commands on to registered command handlers
1930 return (notifyCommandHandlers(&RlvCommandHandler::onAddRemCommand, idObj, rlvCmd, eRet, false)) ? eRet : RLV_RET_FAILED_UNKNOWN;
1931 default:
1932 // Fail with "Invalid param" if none of the above handled it
1933 eRet = RLV_RET_FAILED_PARAM;
1934 break;
1935 }
1936
1937 // If this command represents a behaviour restriction that's been added/removed then we need to do some additional processing
1938 if ( (RLV_RET_SUCCESS == eRet) && (fRefCount) )
1939 {
1940 if (RLV_TYPE_ADD == eType)
1941 {
1942 if (rlvCmd.isStrict())
1943 addException(idObj, RLV_BHVR_PERMISSIVE, eBhvr);
1944 m_Behaviours[eBhvr]++;
1945 }
1946 else
1947 {
1948 if (rlvCmd.isStrict())
1949 removeException(idObj, RLV_BHVR_PERMISSIVE, eBhvr);
1950 m_Behaviours[eBhvr]--;
1951 }
1952
1953 // Since canShowHoverText() uses hasBehaviour() refreshing object text needs to wait until after we've reference counted
1954 if (fRefreshHover)
1955 LLHUDText::refreshAllObjectText();
1956
1957 // Since RlvSettings::updateLoginLastLocation() uses hasBehaviour() it needs to be called after we've reference counted
1958 #ifdef RLV_EXTENSION_STARTLOCATION
1959 RlvSettings::updateLoginLastLocation();
1960 #endif // RLV_EXTENSION_STARTLOCATION
1961
1962 // Show an - optional - notification on every global behaviour change
1963 #ifdef RLV_EXTENSION_NOTIFY_BEHAVIOUR
1964 if ( ((RLV_TYPE_ADD == eType) && (1 == m_Behaviours[eBhvr])) || (0 == m_Behaviours[eBhvr]) )
1965 RlvNotifications::notifyBehaviour(eBhvr, eType);
1966 #endif // RLV_EXTENSION_NOTIFY_BEHAVIOUR
1967 }
1968
1969 return eRet;
1970}
1971
2484// Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a 1972// Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a
2485ERlvCmdRet RlvHandler::onAddRemAttach(const LLUUID& idObj, const RlvCommand& rlvCmd, bool& fRefCount) 1973ERlvCmdRet RlvHandler::onAddRemAttach(const LLUUID& idObj, const RlvCommand& rlvCmd, bool& fRefCount)
2486{ 1974{
@@ -2511,10 +1999,10 @@ ERlvCmdRet RlvHandler::onAddRemAttach(const LLUUID& idObj, const RlvCommand& rlv
2511 LLPipeline::sShowHUDAttachments = TRUE; 1999 LLPipeline::sShowHUDAttachments = TRUE;
2512 2000
2513 fRefCount = rlvCmd.getOption().empty(); // Only reference count global locks 2001 fRefCount = rlvCmd.getOption().empty(); // Only reference count global locks
2514 return RLV_RET_NOERROR; 2002 return RLV_RET_SUCCESS;
2515} 2003}
2516 2004
2517// Checked: 2009-10-10 (RLVa-1.0.5a) | Added: RLVa-1.0.5a 2005// Checked: 2010-07-18 (RLVa-1.1.2b) | Modified: RLVa-1.1.2a
2518ERlvCmdRet RlvHandler::onAddRemDetach(const LLUUID& idObj, const RlvCommand& rlvCmd, bool& fRefCount) 2006ERlvCmdRet RlvHandler::onAddRemDetach(const LLUUID& idObj, const RlvCommand& rlvCmd, bool& fRefCount)
2519{ 2007{
2520 S32 idxAttachPt = 0; 2008 S32 idxAttachPt = 0;
@@ -2526,7 +2014,7 @@ ERlvCmdRet RlvHandler::onAddRemDetach(const LLUUID& idObj, const RlvCommand& rlv
2526 // * @detach=y: - if it ever rezzed as an attachment we'll have cached its attach point 2014 // * @detach=y: - if it ever rezzed as an attachment we'll have cached its attach point
2527 // - if it never rezzed as an attachment there won't be a lock to remove 2015 // - if it never rezzed as an attachment there won't be a lock to remove
2528 rlv_object_map_t::const_iterator itObj = m_Objects.find(idObj); 2016 rlv_object_map_t::const_iterator itObj = m_Objects.find(idObj);
2529 if (itObj != m_Objects.end()) 2017 if ( (itObj != m_Objects.end()) && (itObj->second.m_fLookup) && (itObj->second.m_idxAttachPt) )
2530 idxAttachPt = itObj->second.m_idxAttachPt; 2018 idxAttachPt = itObj->second.m_idxAttachPt;
2531 } 2019 }
2532 else // @detach:<attachpt>=n|y 2020 else // @detach:<attachpt>=n|y
@@ -2536,7 +2024,7 @@ ERlvCmdRet RlvHandler::onAddRemDetach(const LLUUID& idObj, const RlvCommand& rlv
2536 2024
2537 // The attach point can be zero for @detach=n|y (i.e. non-attachment) but should always be non-zero for @detach:<attachpt>=n|y 2025 // The attach point can be zero for @detach=n|y (i.e. non-attachment) but should always be non-zero for @detach:<attachpt>=n|y
2538 if (0 == idxAttachPt) 2026 if (0 == idxAttachPt)
2539 return (rlvCmd.getOption().empty()) ? RLV_RET_NOERROR : RLV_RET_FAILED_OPTION; 2027 return (rlvCmd.getOption().empty()) ? RLV_RET_SUCCESS : RLV_RET_FAILED_OPTION;
2540 2028
2541 // Actually lock the attachment point (@detach=n locks remove only; @detach:<attachpt>=n locks both remove and add) 2029 // Actually lock the attachment point (@detach=n locks remove only; @detach:<attachpt>=n locks both remove and add)
2542 ERlvLockMask eLock = (rlvCmd.getOption().empty()) ? RLV_LOCK_REMOVE : (ERlvLockMask)(RLV_LOCK_ADD | RLV_LOCK_REMOVE); 2030 ERlvLockMask eLock = (rlvCmd.getOption().empty()) ? RLV_LOCK_REMOVE : (ERlvLockMask)(RLV_LOCK_ADD | RLV_LOCK_REMOVE);
@@ -2550,67 +2038,726 @@ ERlvCmdRet RlvHandler::onAddRemDetach(const LLUUID& idObj, const RlvCommand& rlv
2550 LLPipeline::sShowHUDAttachments = TRUE; 2038 LLPipeline::sShowHUDAttachments = TRUE;
2551 2039
2552 fRefCount = false; // Don't reference count @detach[:<option>]=n 2040 fRefCount = false; // Don't reference count @detach[:<option>]=n
2553 return RLV_RET_NOERROR; 2041 return RLV_RET_SUCCESS;
2554} 2042}
2555 2043
2556// ============================================================================ 2044// Checked: 2009-12-05 (RLVa-1.1.0h) | Added: RLVa-1.1.0h
2557// Command handlers (RLV_TYPE_FORCE) 2045ERlvCmdRet RlvHandler::onAddRemSetEnv(const LLUUID& idObj, const RlvCommand& rlvCmd, bool& fRefCount)
2558//
2559
2560// Checked: 2009-10-12 (RLVa-1.0.5b) | Modified: RLVa-1.0.5b
2561ERlvCmdRet RlvHandler::onForceDetach(const LLUUID& idObj, const RlvCommand& rlvCmd) const
2562{ 2046{
2563 // TODO-RLVA: this still needs a rewrite to conform to the new event handler system 2047 // Sanity check - there shouldn't be an option
2564 if ( (rlvCmd.getOption().empty()) || (getAttachPointIndex(rlvCmd.getOption(), true)) ) 2048 if (!rlvCmd.getOption().empty())
2049 return RLV_RET_FAILED_OPTION;
2050 if (RlvSettings::getNoSetEnv())
2051 return RLV_RET_FAILED_DISABLED;
2052
2053 if (RLV_TYPE_ADD == rlvCmd.getParamType())
2565 { 2054 {
2566 onForceRemAttach(idObj, rlvCmd); 2055 // Only close the floaters if their instance exists and they're actually visible
2056 if ( (LLFloaterEnvSettings::isOpen()) && (LLFloaterEnvSettings::instance()->getVisible()) )
2057 LLFloaterEnvSettings::instance()->close();
2058 if ( (LLFloaterWindLight::isOpen()) && (LLFloaterWindLight::instance()->getVisible()) )
2059 LLFloaterWindLight::instance()->close();
2060 if ( (LLFloaterWater::isOpen()) && (LLFloaterWater::instance()->getVisible()) )
2061 LLFloaterWater::instance()->close();
2062 if ( (LLFloaterDayCycle::isOpen()) && (LLFloaterDayCycle::instance()->getVisible()) )
2063 LLFloaterDayCycle::instance()->close();
2064
2065 // Save the current WindLight params so we can restore them on @setenv=y
2066 if (m_pWLSnapshot)
2067 {
2068 RLV_ERRS << "m_pWLSnapshot != NULL" << RLV_ENDL; // Safety net in case we set @setenv=n for more than 1 object
2069 delete m_pWLSnapshot;
2070 }
2071 m_pWLSnapshot = RlvWLSnapshot::takeSnapshot();
2567 } 2072 }
2568 else 2073 else
2569 { 2074 {
2570 // Force detach single folder 2075 // Restore WindLight parameters to what they were before @setenv=n was issued
2571 onForceWear(rlvCmd.getOption(), false, false); 2076 RlvWLSnapshot::restoreSnapshot(m_pWLSnapshot);
2077 delete m_pWLSnapshot;
2078 m_pWLSnapshot = NULL;
2572 } 2079 }
2080 fRefCount = true;
2081 return RLV_RET_SUCCESS;
2082}
2083
2084// ============================================================================
2085// Command handlers (RLV_TYPE_FORCE)
2086//
2087
2088// Checked: 2009-12-21 (RLVa-1.1.0j) | Modified: RLVa-1.1.0j
2089ERlvCmdRet RlvHandler::processForceCommand(const LLUUID& idObj, const RlvCommand& rlvCmd) const
2090{
2091 RLV_ASSERT(RLV_TYPE_FORCE == rlvCmd.getParamType());
2092
2093 ERlvCmdRet eRet = RLV_RET_SUCCESS;
2094 switch (rlvCmd.getBehaviourType())
2095 {
2096 case RLV_BHVR_DETACH: // @detach[:<option>]=force - Checked: 2009-12-21 (RLVa-1.1.0k) | Modified: RLVa-1.1.0j
2097 eRet = onForceRemAttach(idObj, rlvCmd);
2098 if (RLV_RET_SUCCESS != eRet)
2099 eRet = onForceWear(rlvCmd.getOption(), false, false);
2100 break;
2101 case RLV_BHVR_REMATTACH: // @remattach[:<option>]=force
2102 eRet = onForceRemAttach(idObj, rlvCmd);
2103 break;
2104 case RLV_BHVR_REMOUTFIT: // @remoutfit[:<option>]=force
2105 eRet = onForceRemOutfit(idObj, rlvCmd);
2106 break;
2107 case RLV_BHVR_UNSIT: // @unsit=force - Checked: 2009-12-21 (RLVa-1.1.0k) | Modified: RLVa-0.2.0g
2108 {
2109 VERIFY_OPTION(rlvCmd.getOption().empty());
2110 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
2111 if ( (pAvatar) && (pAvatar->mIsSitting) && (!hasBehaviourExcept(RLV_BHVR_UNSIT, idObj)) )
2112 {
2113 gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
2114 send_agent_update(TRUE, TRUE); // See behaviour notes on why we have to force an agent update here
2115 }
2116 }
2117 break;
2118 case RLV_BHVR_SIT: // @sit:<option>=force
2119 eRet = onForceSit(idObj, rlvCmd);
2120 break;
2121 case RLV_BHVR_TPTO: // @tpto:<option>=force - Checked: 2009-07-12 (RLVa-1.0.0k) | Modified: RLVa-1.0.0h
2122 {
2123 eRet = RLV_RET_FAILED_OPTION;
2124 if ( (!rlvCmd.getOption().empty()) && (std::string::npos == rlvCmd.getOption().find_first_not_of("0123456789/.")) )
2125 {
2126 LLVector3d posGlobal;
2573 2127
2574 return RLV_RET_NOERROR; 2128 boost_tokenizer tokens(rlvCmd.getOption(), boost::char_separator<char>("/", "", boost::keep_empty_tokens)); int idx = 0;
2129 for (boost_tokenizer::const_iterator itToken = tokens.begin(); itToken != tokens.end(); ++itToken)
2130 {
2131 if (idx < 3)
2132 LLStringUtil::convertToF64(*itToken, posGlobal[idx++]);
2133 }
2134
2135 if (idx == 3)
2136 {
2137 gAgent.teleportViaLocation(posGlobal);
2138 eRet = RLV_RET_SUCCESS;
2139 }
2140 }
2141 }
2142 break;
2143 case RLV_BHVR_ADDOUTFIT: // @addoutfit:<option>=force <- synonym of @attach:<option>=force
2144 case RLV_BHVR_ATTACH: // @attach:<option>=force
2145 eRet = onForceWear(rlvCmd.getOption(), true, false); // Force attach single folder
2146 break;
2147 case RLV_BHVR_ATTACHALL: // @attachall:<option>=force
2148 eRet = onForceWear(rlvCmd.getOption(), true, true); // Force attach nested folders
2149 break;
2150 case RLV_BHVR_DETACHALL: // @detachall:<option>=force
2151 eRet = onForceWear(rlvCmd.getOption(), false, true); // Force detach nested folders
2152 break;
2153 case RLV_BHVR_ATTACHTHIS: // @attachthis[:<option>]=force
2154 case RLV_BHVR_ATTACHALLTHIS:// @attachallthis[:<option>]=force
2155 case RLV_BHVR_DETACHTHIS: // @detachthis[:<option>]=force
2156 case RLV_BHVR_DETACHALLTHIS:// @detachallthis[:<option>]=force
2157 {
2158 ERlvBehaviour eBehaviour = rlvCmd.getBehaviourType(); std::string strReply;
2159 if ( ((eRet = onGetPath(idObj, rlvCmd, strReply)) == RLV_RET_SUCCESS) && (!strReply.empty()) )
2160 {
2161 LLStringUtil::toLower(strReply);
2162 eRet = onForceWear(strReply,
2163 (RLV_BHVR_ATTACHTHIS == eBehaviour) || (RLV_BHVR_ATTACHALLTHIS == eBehaviour),
2164 (RLV_BHVR_ATTACHALLTHIS == eBehaviour) || (RLV_BHVR_DETACHALLTHIS == eBehaviour));
2165 }
2166 }
2167 break;
2168 case RLV_BHVR_DETACHME: // @detachme=force - Checked:
2169 {
2170 // NOTE: @detachme=force could be seen as a @detach:<attachpt>=force but RLV implements it as a "detach by UUID"
2171 VERIFY_OPTION(rlvCmd.getOption().empty());
2172 LLViewerObject* pObj = NULL; LLVOAvatar* pAvatar = NULL; LLViewerJointAttachment* pAttachPt = NULL;
2173 if ( ((pObj = gObjectList.findObject(idObj)) != NULL) && (pObj->isAttachment()) &&
2174 ((pAvatar = gAgent.getAvatarObject()) != NULL) &&
2175 ((pAttachPt = pAvatar->getTargetAttachmentPoint(pObj->getRootEdit())) != NULL) )
2176 {
2177 // @detachme should respect locks but shouldn't respect things like nostrip so handle it like a manual user detach
2178 handle_detach_from_avatar(pAttachPt);
2179 }
2180 }
2181 break;
2182 case RLV_BHVR_UNKNOWN:
2183 // Pass unknown commands on to registered command handlers
2184 return (notifyCommandHandlers(&RlvCommandHandler::onForceCommand, idObj, rlvCmd, eRet, false)) ? eRet : RLV_RET_FAILED_UNKNOWN;
2185 default:
2186 // Fail with "Invalid param" if none of the above handled it
2187 eRet = RLV_RET_FAILED_PARAM;
2188 break;
2189 }
2190 return eRet;
2575} 2191}
2576 2192
2577// Checked: 2009-10-12 (RLVa-1.0.5b) | Added: RLVa-1.0.5b 2193// Checked: 2009-11-24 (RLVa-1.1.0k) | Modified: RLVa-1.1.0e
2578ERlvCmdRet RlvHandler::onForceRemAttach(const LLUUID& idObj, const RlvCommand& rlvCmd) const 2194ERlvCmdRet RlvHandler::onForceRemAttach(const LLUUID& idObj, const RlvCommand& rlvCmd) const
2579{ 2195{
2580 S32 idxAttachPt = 0; 2196 RLV_ASSERT(RLV_TYPE_FORCE == rlvCmd.getParamType());
2581 if (rlvCmd.getOption().empty()) 2197 RLV_ASSERT( (RLV_BHVR_REMATTACH == rlvCmd.getBehaviourType()) || (RLV_BHVR_DETACH == rlvCmd.getBehaviourType()) );
2198
2199 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
2200 if (!pAvatar)
2201 return RLV_RET_FAILED;
2202
2203 S32 idxAttachPt = 0; ERlvAttachGroupType eAttachGroup = RLV_ATTACHGROUP_INVALID;
2204 // @remattach:<attachpt>=force - force detach single worn attachment
2205 if ((idxAttachPt = getAttachPointIndex(rlvCmd.getOption(), true)) != 0)
2582 { 2206 {
2583 // Simulate right-click / Take Off > Detach All 2207 LLViewerJointAttachment* pAttachPt = get_if_there(pAvatar->mAttachmentPoints, (S32)idxAttachPt, (LLViewerJointAttachment*)NULL);
2584 LLAgent::userRemoveAllAttachments(NULL); 2208 if (pAttachPt)
2585 return RLV_RET_NOERROR; 2209 {
2210 RlvForceWear rlvWear;
2211 rlvWear.forceDetach(pAttachPt);
2212 rlvWear.done();
2213 }
2214 return RLV_RET_SUCCESS;
2586 } 2215 }
2587 else if ((idxAttachPt = getAttachPointIndex(rlvCmd.getOption(), true)) != 0) 2216 // @remattach:<group>=force - force detach worn attachments belonging to <group>
2217 // @remattach=force - force detach all worn attachments
2218 else if ( ((eAttachGroup = rlvGetAttachGroupTypeFromString(rlvCmd.getOption())) != RLV_ATTACHGROUP_INVALID) ||
2219 (rlvCmd.getOption().empty()) )
2588 { 2220 {
2589 // Simulate right-click / Take Off > Detach > ... 2221 RlvForceWear rlvWear;
2590 LLVOAvatar* pAvatar; LLViewerJointAttachment* pAttachmentPt; 2222 for (LLVOAvatar::attachment_map_t::const_iterator itAttach = pAvatar->mAttachmentPoints.begin();
2591 if ( ((pAvatar = gAgent.getAvatarObject()) != NULL) && // Make sure we're actually wearing something on the attachment point 2223 itAttach != pAvatar->mAttachmentPoints.end(); ++itAttach)
2592 ((pAttachmentPt = get_if_there(pAvatar->mAttachmentPoints, (S32)idxAttachPt, (LLViewerJointAttachment*)NULL)) != NULL) &&
2593 (isStrippable(pAttachmentPt->getItemID())) ) // ... and that it's not marked as "nostrip"
2594 { 2224 {
2595 #ifdef RLV_EXPERIMENTAL_COMPOSITES 2225 LLViewerJointAttachment* pAttachPt = itAttach->second;
2596 // If we're stripping something that's part of a composite folder then we should @detachthis instead 2226 if ( (pAttachPt) && (pAttachPt->getObject()) &&
2597 if (isCompositeDescendent(pAttachmentPt->getItemID())) 2227 ((RLV_ATTACHGROUP_INVALID == eAttachGroup) || (rlvGetAttachGroupTypeFromIndex(pAttachPt->getGroup()) == eAttachGroup)) )
2228 {
2229 rlvWear.forceDetach(pAttachPt);
2230 }
2231 }
2232 rlvWear.done();
2233 return RLV_RET_SUCCESS;
2234 }
2235 return RLV_RET_FAILED_OPTION;
2236}
2237
2238// Checked: 2009-12-18 (RLVa-1.1.0k) | Modified: RLVa-1.1.0i
2239ERlvCmdRet RlvHandler::onForceRemOutfit(const LLUUID& idObj, const RlvCommand& rlvCmd) const
2240{
2241 EWearableType wtOption = LLWearable::typeNameToType(rlvCmd.getOption()), wtType;
2242 if ( (WT_INVALID == wtOption) && (!rlvCmd.getOption().empty()) )
2243 return RLV_RET_FAILED_OPTION;
2244
2245 RlvForceWear rlvWear;
2246 for (int idxType = 0; idxType < WT_COUNT; idxType++)
2247 {
2248 wtType = (EWearableType)idxType;
2249 if ( (wtType == wtOption) || (WT_INVALID == wtOption) )
2250 rlvWear.forceRemove(wtType);
2251 }
2252 rlvWear.done();
2253 return RLV_RET_SUCCESS;
2254}
2255
2256// Checked: 2009-12-21 (RLVa-1.1.0j) | Modified: RLVa-1.1.0j
2257ERlvCmdRet RlvHandler::onForceSit(const LLUUID& idObj, const RlvCommand& rlvCmd) const
2258{
2259 LLViewerObject* pObj = NULL; LLUUID idTarget(rlvCmd.getOption());
2260 // Sanity checking - we need to know about the object and it should identify a prim/linkset
2261 if ( (idTarget.isNull()) || ((pObj = gObjectList.findObject(idTarget)) == NULL) || (LL_PCODE_VOLUME != pObj->getPCode()) )
2262 return RLV_RET_FAILED_OPTION;
2263
2264 if (!canSit(pObj))
2265 return RLV_RET_FAILED_LOCK;
2266
2267 // Copy/paste from handle_sit_or_stand() [see http://wiki.secondlife.com/wiki/AgentRequestSit]
2268 gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit);
2269 gMessageSystem->nextBlockFast(_PREHASH_AgentData);
2270 gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
2271 gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
2272 gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
2273 gMessageSystem->addUUIDFast(_PREHASH_TargetID, pObj->mID);
2274 // Offset: "a rough position in local coordinates for the edge to sit on"
2275 // (we might not even be looking at the object so I don't think we can supply the offset to an edge)
2276 gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3::zero);
2277 pObj->getRegion()->sendReliableMessage();
2278
2279 return RLV_RET_SUCCESS;
2280}
2281
2282// Checked: 2009-12-18 (RLVa-1.1.0k) | Modified: RLVa-1.1.0i
2283ERlvCmdRet RlvHandler::onForceWear(const std::string& strPath, bool fAttach, bool fMatchAll) const
2284{
2285 LLViewerInventoryCategory* pFolder = getSharedFolder(strPath), *pRlvRoot = getSharedRoot();
2286 if ( (!pFolder) || (pFolder->getUUID() == pRlvRoot->getUUID()) )
2287 return (pRlvRoot != NULL) ? RLV_RET_FAILED_OPTION : RLV_RET_FAILED_NOSHAREDROOT;
2288
2289 RlvForceWear rlvWear;
2290 rlvWear.forceFolder(pFolder,
2291 (fAttach) ? RlvForceWear::ACTION_ATTACH : RlvForceWear::ACTION_DETACH,
2292 (fMatchAll) ? (RlvForceWear::eWearFlags)(RlvForceWear::FLAG_DEFAULT | RlvForceWear::FLAG_MATCHALL) : (RlvForceWear::FLAG_DEFAULT));
2293 rlvWear.done();
2294
2295 return RLV_RET_SUCCESS;
2296}
2297
2298// ============================================================================
2299// Command handlers (RLV_TYPE_REPLY)
2300//
2301
2302// Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
2303ERlvCmdRet RlvHandler::processReplyCommand(const LLUUID& idObj, const RlvCommand& rlvCmd) const
2304{
2305 RLV_ASSERT(RLV_TYPE_REPLY == rlvCmd.getParamType());
2306
2307 // Sanity check - <param> should specify a - valid - reply channel
2308 S32 nChannel;
2309 if ( (!LLStringUtil::convertToS32(rlvCmd.getParam(), nChannel)) || (!rlvIsValidReplyChannel(nChannel)) )
2310 return RLV_RET_FAILED_PARAM;
2311
2312 ERlvCmdRet eRet = RLV_RET_SUCCESS; std::string strReply;
2313 switch (rlvCmd.getBehaviourType())
2314 {
2315 case RLV_BHVR_VERSION: // @version=<channel> - Checked: 2010-03-27 (RLVa-1.2.0b)
2316 case RLV_BHVR_VERSIONNEW: // @versionnew=<channel> - Checked: 2010-03-27 (RLVa-1.2.0b) | Added: RLVa-1.2.0b
2317 // NOTE: RLV will respond even if there's an option
2318 strReply = RlvStrings::getVersion(RLV_BHVR_VERSION == rlvCmd.getBehaviourType());
2319 break;
2320 case RLV_BHVR_VERSIONNUM: // @versionnum=<channel> - Checked: 2009-11-26 (RLVa-1.1.0f) | Added: RLVa-1.0.4b
2321 // NOTE: RLV will respond even if there's an option
2322 strReply = RlvStrings::getVersionNum();
2323 break;
2324 case RLV_BHVR_GETATTACH: // @getattach[:<layer>]=<channel>
2325 eRet = onGetAttach(idObj, rlvCmd, strReply);
2326 break;
2327#ifdef RLV_EXTENSION_CMD_GETXXXNAMES
2328 case RLV_BHVR_GETATTACHNAMES: // @getattachnames[:<grp>]=<channel>
2329 case RLV_BHVR_GETADDATTACHNAMES:// @getaddattachnames[:<grp>]=<channel>
2330 case RLV_BHVR_GETREMATTACHNAMES:// @getremattachnames[:<grp>]=<channel>
2331 eRet = onGetAttachNames(idObj, rlvCmd, strReply);
2332 break;
2333#endif // RLV_EXTENSION_CMD_GETXXXNAMES
2334 case RLV_BHVR_GETOUTFIT: // @getoutfit[:<layer>]=<channel>
2335 eRet = onGetOutfit(idObj, rlvCmd, strReply);
2336 break;
2337#ifdef RLV_EXTENSION_CMD_GETXXXNAMES
2338 case RLV_BHVR_GETOUTFITNAMES: // @getoutfitnames=<channel>
2339 case RLV_BHVR_GETADDOUTFITNAMES:// @getaddoutfitnames=<channel>
2340 case RLV_BHVR_GETREMOUTFITNAMES:// @getremoutfitnames=<channel>
2341 eRet = onGetOutfitNames(idObj, rlvCmd, strReply);
2342 break;
2343#endif // RLV_EXTENSION_CMD_GETXXXNAMES
2344 case RLV_BHVR_FINDFOLDER: // @findfolder:<criteria>=<channel>
2345#ifdef RLV_EXTENSION_CMD_FINDFOLDERS
2346 case RLV_BHVR_FINDFOLDERS: // @findfolders:<criteria>=<channel>
2347#endif // RLV_EXTENSION_CMD_FINDFOLDERS
2348 eRet = onFindFolder(idObj, rlvCmd, strReply);
2349 break;
2350 case RLV_BHVR_GETPATH: // @getpath[:<option>]=<channel>
2351 eRet = onGetPath(idObj, rlvCmd, strReply);
2352 break;
2353 case RLV_BHVR_GETINV: // @getinv[:<path>]=<channel>
2354 eRet = onGetInv(idObj, rlvCmd, strReply);
2355 break;
2356 case RLV_BHVR_GETINVWORN: // @getinvworn[:path]=<channel>
2357 eRet = onGetInvWorn(idObj, rlvCmd, strReply);
2358 break;
2359 case RLV_BHVR_GETSITID: // @getsitid=<channel> - Checked: 2009-11-26 (RLVa-1.1.0f)
2360 {
2361 // NOTE: RLV 1.16.1 returns a NULL UUID if we're not sitting
2362 LLVOAvatar* pAvatar = gAgent.getAvatarObject(); LLUUID idSitObj;
2363 if ( (pAvatar) && (pAvatar->mIsSitting) )
2598 { 2364 {
2599 std::string strCmd = "detachthis:" + strOption + "=force"; 2365 // LLVOAvatar inherits from 2 classes so make sure we get the right vfptr
2600 #ifdef RLV_DEBUG 2366 LLViewerObject* pAvatarObj = dynamic_cast<LLViewerObject*>(pAvatar), *pParent;
2601 RLV_INFOS << "\t- '" << strOption << "' belongs to composite folder: @" << strCmd << LL_ENDL; 2367 // (If there is a parent, we need to upcast it from LLXform to LLViewerObject to get its UUID)
2602 #endif // RLV_DEBUG 2368 if ( (pAvatarObj) && ((pParent = static_cast<LLViewerObject*>(pAvatarObj->getRoot())) != pAvatarObj) )
2603 processForceCommand(idObj, RlvCommand(strCmd)); 2369 idSitObj = pParent->getID();
2604 } 2370 }
2605 else 2371 strReply = idSitObj.asString();
2606 #endif // RLV_EXPERIMENTAL_COMPOSITES 2372 }
2373 break;
2374 case RLV_BHVR_GETSTATUS: // @getstatus[:<option>]=<channel> - Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
2375 {
2376 // NOTE: specification says response should start with '/' but RLV-1.16.1 returns an empty string when no rules are set
2377 rlv_object_map_t::const_iterator itObj = m_Objects.find(idObj);
2378 if (itObj != m_Objects.end())
2379 strReply = itObj->second.getStatusString(rlvCmd.getOption());
2380 }
2381 break;
2382 case RLV_BHVR_GETSTATUSALL: // @getstatusall[:<option>]=<channel> - Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
2383 {
2384 // NOTE: specification says response should start with '/' but RLV-1.16.1 returns an empty string when no rules are set
2385 for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
2386 strReply += itObj->second.getStatusString(rlvCmd.getOption());
2387 }
2388 break;
2389 case RLV_BHVR_UNKNOWN:
2390 // Pass unknown commands on to registered command handlers
2391 return (notifyCommandHandlers(&RlvCommandHandler::onReplyCommand, idObj, rlvCmd, eRet, false)) ? eRet : RLV_RET_FAILED_UNKNOWN;
2392 default:
2393 // Fail with "Invalid param" if none of the above handled it
2394 return RLV_RET_FAILED_PARAM;
2395 }
2396
2397 // If we made it this far then:
2398 // - the command was handled successfully so we send off the response
2399 // - the command failed but we still send off an (empty) response to keep the issuing script from blocking
2400 rlvSendChatReply(nChannel, strReply);
2401
2402 return eRet;
2403}
2404
2405// Checked: 2009-11-24 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
2406ERlvCmdRet RlvHandler::onFindFolder(const LLUUID& idObj, const RlvCommand& rlvCmd, std::string& strReply) const
2407{
2408 RLV_ASSERT(RLV_TYPE_REPLY == rlvCmd.getParamType());
2409 RLV_ASSERT( (RLV_BHVR_FINDFOLDER == rlvCmd.getBehaviourType()) || (RLV_BHVR_FINDFOLDERS == rlvCmd.getBehaviourType()) );
2410
2411 // (Compatibility: RLV 1.16.1 returns the first random folder it finds while we return a blank on no option)
2412 if (rlvCmd.getOption().empty())
2413 return RLV_RET_FAILED_OPTION;
2414
2415 LLInventoryModel::cat_array_t folders;
2416 if (findSharedFolders(rlvCmd.getOption(), folders))
2417 {
2418 if (RLV_BHVR_FINDFOLDER == rlvCmd.getBehaviourType())
2419 {
2420 // We need to return an "in depth" result so whoever has the most '/' is our lucky winner
2421 // (maxSlashes needs to be initialized to -1 since children of the #RLV folder won't have '/' in their shared path)
2422 int maxSlashes = -1, curSlashes; std::string strFolderName;
2423 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
2424 {
2425 strFolderName = getSharedPath(folders.get(idxFolder));
2426
2427 curSlashes = std::count(strFolderName.begin(), strFolderName.end(), '/');
2428 if (curSlashes > maxSlashes)
2607 { 2429 {
2608 handle_detach_from_avatar(pAttachmentPt); 2430 maxSlashes = curSlashes;
2431 strReply = strFolderName;
2609 } 2432 }
2433 }
2434 }
2435 else if (RLV_BHVR_FINDFOLDERS == rlvCmd.getBehaviourType())
2436 {
2437 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
2438 {
2439 if (!strReply.empty())
2440 strReply.push_back(',');
2441 strReply += getSharedPath(folders.get(idxFolder));
2442 }
2610 } 2443 }
2611 return RLV_RET_NOERROR;
2612 } 2444 }
2613 return RLV_RET_FAILED_OPTION; 2445 return RLV_RET_SUCCESS;
2446}
2447
2448// Checked: 2009-11-24 (RLVa-1.1.0f) | Modified: RLVa-1.1.0e
2449ERlvCmdRet RlvHandler::onGetAttach(const LLUUID& idObj, const RlvCommand& rlvCmd, std::string& strReply) const
2450{
2451 RLV_ASSERT(RLV_TYPE_REPLY == rlvCmd.getParamType());
2452 RLV_ASSERT(RLV_BHVR_GETATTACH == rlvCmd.getBehaviourType());
2453
2454 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
2455 if (!pAvatar)
2456 return RLV_RET_FAILED;
2457
2458 // Sanity check - <option> should specify an attachment point or be empty
2459 S32 idxAttachPt = getAttachPointIndex(rlvCmd.getOption(), true);
2460 if ( (idxAttachPt == 0) && (!rlvCmd.getOption().empty()) )
2461 return RLV_RET_FAILED_OPTION;
2462
2463 // If we're fetching all worn attachments then the reply should start with 0
2464 if (0 == idxAttachPt)
2465 strReply.push_back('0');
2466
2467 for (LLVOAvatar::attachment_map_t::const_iterator itAttach = pAvatar->mAttachmentPoints.begin();
2468 itAttach != pAvatar->mAttachmentPoints.end(); ++itAttach)
2469 {
2470 LLViewerJointAttachment* pAttachPt = itAttach->second;
2471 if ( (0 == idxAttachPt) || (itAttach->first == idxAttachPt) )
2472 {
2473 bool fWorn = (pAttachPt->getObject() != NULL) &&
2474 ( (!RlvSettings::getHideLockedAttach()) ||
2475 (RlvForceWear::isForceDetachable(pAttachPt, true, gObjectList.findObject(idObj))) );
2476 strReply.push_back( (fWorn) ? '1' : '0' );
2477 }
2478 }
2479 return RLV_RET_SUCCESS;
2480}
2481
2482// Checked: 2009-10-19 (RLVa-1.1.0f) | Added: RLVa-1.1.0e
2483ERlvCmdRet RlvHandler::onGetAttachNames(const LLUUID& idObj, const RlvCommand& rlvCmd, std::string& strReply) const
2484{
2485 RLV_ASSERT(RLV_TYPE_REPLY == rlvCmd.getParamType());
2486 RLV_ASSERT( (RLV_BHVR_GETATTACHNAMES == rlvCmd.getBehaviourType()) || (RLV_BHVR_GETADDATTACHNAMES == rlvCmd.getBehaviourType()) ||
2487 (RLV_BHVR_GETREMATTACHNAMES == rlvCmd.getBehaviourType()) );
2488
2489 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
2490 if (!pAvatar)
2491 return RLV_RET_FAILED;
2492
2493 ERlvAttachGroupType eAttachGroup = rlvGetAttachGroupTypeFromString(rlvCmd.getOption());
2494 for (LLVOAvatar::attachment_map_t::const_iterator itAttach = pAvatar->mAttachmentPoints.begin();
2495 itAttach != pAvatar->mAttachmentPoints.end(); ++itAttach)
2496 {
2497 LLViewerJointAttachment* pAttachPt = itAttach->second;
2498 if ( (RLV_ATTACHGROUP_INVALID == eAttachGroup) || (rlvGetAttachGroupTypeFromIndex(pAttachPt->getGroup()) == eAttachGroup) )
2499 {
2500 bool fAdd = false;
2501 switch (rlvCmd.getBehaviourType())
2502 {
2503 case RLV_BHVR_GETATTACHNAMES: // Every attachment point that has an attached object (locked or unlocked)
2504 fAdd = (pAttachPt->getObject() != NULL);
2505 break;
2506 case RLV_BHVR_GETADDATTACHNAMES: // Every attachment point that can be worn on (but ignore any locks set by the issuer)
2507 fAdd = (!isLockedAttachmentExcept(itAttach->first, RLV_LOCK_ADD, gObjectList.findObject(idObj))) &&
2508 ( (pAttachPt->getObject() == NULL) ||
2509 (!isLockedAttachmentExcept(itAttach->first, RLV_LOCK_REMOVE, gObjectList.findObject(idObj))) );
2510 break;
2511 case RLV_BHVR_GETREMATTACHNAMES: // Every attachment point that can be detached (but ignore any locks set by the issuer)
2512 fAdd = RlvForceWear::isForceDetachable(pAttachPt, true, gObjectList.findObject(idObj));
2513 break;
2514 default:
2515 break;
2516 }
2517
2518 if (fAdd)
2519 {
2520 if (!strReply.empty())
2521 strReply.push_back(',');
2522 strReply.append(pAttachPt->getName());
2523 }
2524 }
2525 }
2526 return RLV_RET_SUCCESS;
2527}
2528
2529// Checked: 2009-11-21 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
2530ERlvCmdRet RlvHandler::onGetInv(const LLUUID& idObj, const RlvCommand& rlvCmd, std::string& strReply) const
2531{
2532 RLV_ASSERT(RLV_TYPE_REPLY == rlvCmd.getParamType());
2533 RLV_ASSERT(RLV_BHVR_GETINV == rlvCmd.getBehaviourType());
2534
2535 LLViewerInventoryCategory* pFolder = getSharedFolder(rlvCmd.getOption());
2536 if (!pFolder)
2537 return (getSharedRoot() != NULL) ? RLV_RET_FAILED_OPTION : RLV_RET_FAILED_NOSHAREDROOT;
2538
2539 LLInventoryModel::cat_array_t* pFolders;
2540 LLInventoryModel::item_array_t* pItems;
2541 gInventory.getDirectDescendentsOf(pFolder->getUUID(), pFolders, pItems);
2542 if (!pFolders)
2543 return RLV_RET_FAILED;
2544
2545 for (S32 idxFolder = 0, cntFolder = pFolders->count(); idxFolder < cntFolder; idxFolder++)
2546 {
2547 // Return all folders that:
2548 // - aren't hidden
2549 // - aren't a folded folder (only really matters when "Enable Legacy Naming" is enabled - see related blog post)
2550 // (we can skip checking for .<composite> folders since the ones we'll want to hide start with '.' anyway)
2551 const std::string& strFolder = pFolders->get(idxFolder)->getName();
2552 if ( (!strFolder.empty()) && (RLV_FOLDER_PREFIX_HIDDEN != strFolder[0]) &&
2553 (!isFoldedFolder(pFolders->get(idxFolder).get(), true, false)) )
2554 {
2555 if (!strReply.empty())
2556 strReply.push_back(',');
2557 strReply += strFolder;
2558 }
2559 }
2560 return RLV_RET_SUCCESS;
2561}
2562
2563struct rlv_wear_info { U32 cntWorn, cntTotal, cntChildWorn, cntChildTotal; };
2564
2565// Checked: 2009-11-27 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
2566ERlvCmdRet RlvHandler::onGetInvWorn(const LLUUID& idObj, const RlvCommand& rlvCmd, std::string& strReply) const
2567{
2568 // Sanity check - getAvatarObject() can't be NULL [see RlvForceWear::isWearingItem()]
2569 if (!gAgent.getAvatarObject())
2570 return RLV_RET_FAILED;
2571 // Sanity check - folder should exist and not be hidden
2572 LLViewerInventoryCategory* pFolder = getSharedFolder(rlvCmd.getOption());
2573 if ( (!pFolder) || (pFolder->getName().empty()) || (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName()[0]) )
2574 return (getSharedRoot() != NULL) ? RLV_RET_FAILED_OPTION : RLV_RET_FAILED_NOSHAREDROOT;
2575
2576 // Collect everything @attachall would be attaching
2577 LLInventoryModel::cat_array_t folders;
2578 LLInventoryModel::item_array_t items;
2579 RlvWearableItemCollector functor(pFolder->getUUID(), true, true);
2580 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor);
2581
2582 rlv_wear_info wi = {0};
2583
2584 // Add all the folders to a lookup map
2585 std::map<LLUUID, rlv_wear_info> mapFolders;
2586 mapFolders.insert(std::pair<LLUUID, rlv_wear_info>(pFolder->getUUID(), wi));
2587 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
2588 mapFolders.insert(std::pair<LLUUID, rlv_wear_info>(folders.get(idxFolder)->getUUID(), wi));
2589
2590 // Iterate over all the found items
2591 LLViewerInventoryItem* pItem; std::map<LLUUID, rlv_wear_info>::iterator itFolder;
2592 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
2593 {
2594 pItem = items.get(idxItem);
2595
2596 // The "folded parent" is the folder this item should be considered a direct descendent of (may or may not match actual parent)
2597 const LLUUID& idParent = functor.getFoldedParent(pItem->getParentUUID());
2598
2599 // Walk up the tree: sooner or later one of the parents will be a folder in the map
2600 LLViewerInventoryCategory* pParent = gInventory.getCategory(idParent);
2601 while ( (itFolder = mapFolders.find(pParent->getUUID())) == mapFolders.end() )
2602 pParent = gInventory.getCategory(pParent->getParentUUID());
2603
2604 U32 &cntWorn = (idParent == pParent->getUUID()) ? itFolder->second.cntWorn : itFolder->second.cntChildWorn,
2605 &cntTotal = (idParent == pParent->getUUID()) ? itFolder->second.cntTotal : itFolder->second.cntChildTotal;
2606
2607 if (RlvForceWear::isWearingItem(pItem))
2608 cntWorn++;
2609 cntTotal++;
2610 }
2611
2612 // Extract the result for the main folder
2613 itFolder = mapFolders.find(pFolder->getUUID());
2614 wi.cntWorn = itFolder->second.cntWorn;
2615 wi.cntTotal = itFolder->second.cntTotal;
2616 mapFolders.erase(itFolder);
2617
2618 // Build the result for each child folder
2619 for (itFolder = mapFolders.begin(); itFolder != mapFolders.end(); ++itFolder)
2620 {
2621 rlv_wear_info& wiFolder = itFolder->second;
2622
2623 wi.cntChildWorn += wiFolder.cntWorn + wiFolder.cntChildWorn;
2624 wi.cntChildTotal += wiFolder.cntTotal + wiFolder.cntChildTotal;
2625
2626 strReply += llformat(",%s|%d%d", gInventory.getCategory(itFolder->first)->getName().c_str(),
2627 (0 == wiFolder.cntTotal) ? 0 : (0 == wiFolder.cntWorn) ? 1 : (wiFolder.cntWorn != wiFolder.cntTotal) ? 2 : 3,
2628 (0 == wiFolder.cntChildTotal) ? 0 : (0 == wiFolder.cntChildWorn) ? 1 : (wiFolder.cntChildWorn != wiFolder.cntChildTotal) ? 2 : 3
2629 );
2630 }
2631
2632 // Now just prepend the root and done
2633 strReply = llformat("|%d%d", (0 == wi.cntTotal) ? 0 : (0 == wi.cntWorn) ? 1 : (wi.cntWorn != wi.cntTotal) ? 2 : 3,
2634 (0 == wi.cntChildTotal) ? 0 : (0 == wi.cntChildWorn) ? 1 : (wi.cntChildWorn != wi.cntChildTotal) ? 2: 3) + strReply;
2635
2636 return RLV_RET_SUCCESS;
2637}
2638
2639// Checked: 2009-11-24 (RLVa-1.1.0f) | Modified: RLVa-1.1.0e
2640ERlvCmdRet RlvHandler::onGetOutfit(const LLUUID& idObj, const RlvCommand& rlvCmd, std::string& strReply) const
2641{
2642 RLV_ASSERT(RLV_TYPE_REPLY == rlvCmd.getParamType());
2643 RLV_ASSERT(RLV_BHVR_GETOUTFIT == rlvCmd.getBehaviourType());
2644
2645 // (Compatibility: RLV 1.16.1 will execute @getoutfit=<channel> if <layer> is invalid while we just return failure)
2646 EWearableType layerType = LLWearable::typeNameToType(rlvCmd.getOption());
2647 if ( (WT_INVALID == layerType) && (!rlvCmd.getOption().empty()) )
2648 return RLV_RET_FAILED_OPTION;
2649
2650 const EWearableType layerTypes[] =
2651 {
2652 WT_GLOVES, WT_JACKET, WT_PANTS, WT_SHIRT, WT_SHOES, WT_SKIRT, WT_SOCKS,
2653 WT_UNDERPANTS, WT_UNDERSHIRT, WT_SKIN, WT_EYES, WT_HAIR, WT_SHAPE, WT_ALPHA, WT_TATTOO
2654 };
2655
2656 for (int idx = 0, cnt = sizeof(layerTypes) / sizeof(EWearableType); idx < cnt; idx++)
2657 if ( (WT_INVALID == layerType) || (layerTypes[idx] == layerType) )
2658 {
2659 // We never hide body parts, even if they're "locked" and we're hiding locked layers
2660 // (nor do we hide a layer if the issuing object is the only one that has this layer locked)
2661 bool fWorn = (gAgent.getWearable(layerTypes[idx])) &&
2662 ( (!RlvSettings::getHideLockedLayers()) ||
2663 (LLAssetType::AT_BODYPART == LLWearable::typeToAssetType(layerTypes[idx])) ||
2664 ( (isRemovableExcept(layerTypes[idx], idObj)) &&
2665 (isStrippable(gAgent.getWearableItem(layerTypes[idx]))) ) );
2666 strReply.push_back( (fWorn) ? '1' : '0' );
2667 }
2668
2669 return RLV_RET_SUCCESS;
2670}
2671
2672// Checked: 2009-11-21 (RLVa-1.1.0f) | Added: RLVa-1.1.0e
2673ERlvCmdRet RlvHandler::onGetOutfitNames(const LLUUID& idObj, const RlvCommand& rlvCmd, std::string& strReply) const
2674{
2675 RLV_ASSERT(RLV_TYPE_REPLY == rlvCmd.getParamType());
2676 RLV_ASSERT( (RLV_BHVR_GETOUTFITNAMES == rlvCmd.getBehaviourType()) || (RLV_BHVR_GETADDOUTFITNAMES == rlvCmd.getBehaviourType()) ||
2677 (RLV_BHVR_GETREMOUTFITNAMES == rlvCmd.getBehaviourType()) );
2678
2679 // Sanity check - all these commands are optionless
2680 if (!rlvCmd.getOption().empty())
2681 return RLV_RET_FAILED_OPTION;
2682
2683 for (int idxType = 0; idxType < WT_COUNT; idxType++)
2684 {
2685 bool fAdd = false; EWearableType wtType = (EWearableType)idxType;
2686 switch (rlvCmd.getBehaviourType())
2687 {
2688 case RLV_BHVR_GETOUTFITNAMES: // Every layer that's worn
2689 fAdd = (gAgent.getWearable(wtType) != NULL);
2690 break;
2691 case RLV_BHVR_GETADDOUTFITNAMES: // Every layer that can be worn on (but ignore any locks set by the issuer)
2692 fAdd = (isWearable(wtType)) && ( (gAgent.getWearable(wtType) == NULL) || (isRemovableExcept(wtType, idObj)) );
2693 break;
2694 case RLV_BHVR_GETREMOUTFITNAMES: // Every layer that can be removed (but ignore any locks set by the issuer)
2695 fAdd = (gAgent.getWearable(wtType) != NULL) &&
2696 (LLAssetType::AT_BODYPART != LLWearable::typeToAssetType(wtType)) &&
2697 (isRemovableExcept(wtType, idObj)) && (isStrippable(gAgent.getWearableItem(wtType)));
2698 break;
2699 default:
2700 break;
2701 }
2702
2703 if (fAdd)
2704 {
2705 if (!strReply.empty())
2706 strReply.push_back(',');
2707 strReply.append(LLWearable::typeToTypeName((EWearableType)idxType));
2708 }
2709 }
2710 return RLV_RET_SUCCESS;
2711}
2712
2713// Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
2714ERlvCmdRet RlvHandler::onGetPath(const LLUUID& idObj, const RlvCommand& rlvCmd, std::string& strReply) const
2715{
2716 // NOTE: @attachthis/attachallthis/detachthis/detachallthis call us directly to simulate @attach:getpath[:<option>]=force
2717
2718 // Sanity check - no need to go through all this trouble if we don't have a shared root
2719 LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
2720 if (!pRlvRoot)
2721 return RLV_RET_FAILED_NOSHAREDROOT;
2722
2723 EWearableType wtType = LLWearable::typeNameToType(rlvCmd.getOption()); LLUUID idItem;
2724 if (WT_INVALID != wtType) // <option> can be a clothing layer
2725 {
2726 idItem = gAgent.getWearableItem(wtType);
2727 }
2728 else
2729 {
2730 LLViewerJointAttachment* pAttachPt = NULL;
2731
2732 if (rlvCmd.getOption().empty()) // ... or it can be empty (in which case we act on the object that issued the command)
2733 {
2734 LLViewerObject* pObj = gObjectList.findObject(idObj);
2735 if ( (pObj) && (pObj->isAttachment()) && (gAgent.getAvatarObject()) )
2736 pAttachPt = gAgent.getAvatarObject()->getTargetAttachmentPoint(pObj);
2737 }
2738 else // ... or it can specify an attachment point
2739 {
2740 pAttachPt = getAttachPoint(rlvCmd.getOption(), true);
2741 }
2742
2743 // If we found something get its inventory item UUID, otherwise return failure
2744 if (!pAttachPt)
2745 return RLV_RET_FAILED_OPTION;
2746 idItem = pAttachPt->getItemID();
2747 }
2748
2749 // If what we found is under the shared root then get its path
2750 if ( (!idItem.isNull()) && (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) )
2751 {
2752 LLInventoryItem* pItem = gInventory.getItem(idItem);
2753 if (pItem)
2754 {
2755 // (unless the containing folder is a folded folder in which case we need its parent)
2756 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
2757 strReply = getSharedPath( (!isFoldedFolder(pFolder, true, true)) ? pFolder : gInventory.getCategory(pFolder->getParentUUID()) );
2758 }
2759 }
2760 return RLV_RET_SUCCESS;
2614} 2761}
2615 2762
2616// ============================================================================ 2763// ============================================================================