aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/rlvhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/rlvhandler.cpp2549
1 files changed, 2549 insertions, 0 deletions
diff --git a/linden/indra/newview/rlvhandler.cpp b/linden/indra/newview/rlvhandler.cpp
new file mode 100644
index 0000000..2915002
--- /dev/null
+++ b/linden/indra/newview/rlvhandler.cpp
@@ -0,0 +1,2549 @@
1#include "llviewerprecompiledheaders.h"
2#include "llagent.h"
3#include "lldrawpoolalpha.h"
4#include "llfloaterbeacons.h"
5#include "llfloaterchat.h"
6#include "llfloaterdaycycle.h"
7#include "llfloaterenvsettings.h"
8#include "llfloatergodtools.h"
9#include "llfloaterland.h"
10#include "llfloatermap.h"
11#include "llfloaterregioninfo.h"
12#include "llfloatertools.h"
13#include "llfloaterwater.h"
14#include "llfloaterwindlight.h"
15#include "llfloaterworldmap.h"
16#include "llgesturemgr.h"
17#include "llinventoryview.h"
18#include "llstartup.h"
19#include "llviewermenu.h"
20#include "llviewermessage.h"
21#include "llviewerparcelmgr.h"
22#include "llviewerregion.h"
23#include "llviewerwindow.h"
24#include "llvoavatar.h"
25#include "llworld.h"
26#include "pipeline.h"
27
28#include "rlvhelper.h"
29#include "rlvevent.h"
30#include "rlvextensions.h"
31#include "rlvhandler.h"
32
33// Only defined in llinventorybridge.cpp
34#if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
35 #include "llinventorybridge.h"
36 void confirm_replace_attachment_rez(S32 option, void* user_data);
37#endif
38// Only defined in llinventorymodel.cpp
39extern const char* NEW_CATEGORY_NAME;
40
41// ============================================================================
42// Static variable initialization
43//
44
45BOOL RlvHandler::m_fEnabled = FALSE;
46BOOL RlvHandler::fNoSetEnv = FALSE;
47BOOL RlvHandler::fLegacyNaming = FALSE;
48BOOL RlvHandler::m_fFetchStarted = FALSE;
49BOOL RlvHandler::m_fFetchComplete = FALSE;
50RlvMultiStringSearch RlvHandler::m_AttachLookup;
51
52const std::string RlvHandler::cstrSharedRoot = RLV_ROOT_FOLDER;
53
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 human being", "This human being",
69 "That human 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;
74
75// ============================================================================
76// Helper functions
77//
78
79// Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.0e
80inline bool rlvIsWearingItem(const LLInventoryItem* pItem)
81{
82 return
83 ((LLAssetType::AT_OBJECT == pItem->getType()) && (gAgent.getAvatarObject()->isWearingAttachment(pItem->getUUID()))) ||
84 ((LLAssetType::AT_GESTURE == pItem->getType()) && (gGestureManager.isGestureActive(pItem->getUUID()))) ||
85 (gAgent.isWearingItem(pItem->getUUID()));
86}
87
88// ============================================================================
89// Command specific helper functions
90//
91
92// Checked: 2009-08-04 (RLVa-1.0.1d) | Added: RLVa-1.0.1d
93static bool rlvParseNotifyOption(const std::string& strOption, S32& nChannel, std::string& strFilter)
94{
95 boost_tokenizer tokens(strOption, boost::char_separator<char>(";", "", boost::keep_empty_tokens));
96 boost_tokenizer::iterator itTok = tokens.begin();
97
98 // Extract and sanity check the first token (required) which is the channel
99 if ( (itTok == tokens.end()) || (!LLStringUtil::convertToS32(*itTok, nChannel)) || (!rlvIsValidChannel(nChannel)) )
100 return false;
101
102 // Second token (optional) is the filter
103 strFilter.clear();
104 if (++itTok != tokens.end())
105 {
106 strFilter = *itTok;
107 ++itTok;
108 }
109
110 return (itTok == tokens.end());
111}
112
113// ============================================================================
114// Constructor/destructor
115//
116
117// Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
118RlvHandler::RlvHandler()
119 : m_fCanCancelTp(false), m_idCurObject(LLUUID::null), m_pCurCommand(NULL), m_pGCTimer(NULL), m_pWLSnapshot(NULL), m_pBhvrNotify(NULL)
120{
121 // Array auto-initialization to 0 is non-standard? (Compiler warning in VC-8.0)
122 memset(m_LayersAdd, 0, sizeof(S16) * WT_COUNT);
123 memset(m_LayersRem, 0, sizeof(S16) * WT_COUNT);
124 memset(m_Behaviours, 0, sizeof(S16) * RLV_BHVR_COUNT);
125}
126
127// Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
128RlvHandler::~RlvHandler()
129{
130 //delete m_pGCTimer; // <- deletes itself
131 delete m_pWLSnapshot; // <- delete on NULL is harmless
132 delete m_pBhvrNotify;
133}
134
135// ============================================================================
136// Attachment related functions
137//
138
139// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
140inline LLViewerJointAttachment* RlvHandler::getAttachPoint(const std::string& strText, bool fExact) const
141{
142 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
143 return (pAvatar) ? get_if_there(pAvatar->mAttachmentPoints, getAttachPointIndex(strText, fExact), (LLViewerJointAttachment*)NULL)
144 : NULL;
145}
146
147// Checked: 2009-07-29 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b
148LLViewerJointAttachment* RlvHandler::getAttachPoint(const LLInventoryCategory* pFolder, bool /*fStrict*/) const
149{
150 if (!pFolder)
151 return NULL;
152
153 // RLVa-1.0.1 added support for legacy matching (See http://rlva.catznip.com/blog/2009/07/attachment-point-naming-convention/)
154 if (fLegacyNaming)
155 return getAttachPointLegacy(pFolder);
156
157 // Otherwise the only valid way to specify an attachment point in a folder name is: ^\.\(\s+attachpt\s+\)
158 std::string::size_type idxMatch;
159 std::string strAttachPt = rlvGetFirstParenthesisedText(pFolder->getName(), &idxMatch);
160 LLStringUtil::trim(strAttachPt);
161
162 return ( (1 == idxMatch) && (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName().at(0)) ) ? getAttachPoint(strAttachPt, true) : NULL;
163}
164
165// Checked: 2009-07-29 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b
166LLViewerJointAttachment* RlvHandler::getAttachPoint(const LLInventoryItem* pItem, bool fStrict) const
167{
168 // Sanity check - if it's not an object then it can't have an attachment point
169 if ( (!pItem) || (LLAssetType::AT_OBJECT != pItem->getType()) )
170 return NULL;
171
172 // The attachment point should be placed at the end of the item's name, surrounded by parenthesis
173 // (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)
174 std::string strAttachPt = rlvGetLastParenthesisedText(pItem->getName());
175 LLStringUtil::trim(strAttachPt);
176
177 // If the item is modify : we look at the item's name first and only then at the containing folder
178 // If the item is no modify: we look at the containing folder's name first and only then at the item itself
179 LLViewerJointAttachment* pAttachPt;
180 if (pItem->getPermissions().allowModifyBy(gAgent.getID()))
181 {
182 pAttachPt = (!strAttachPt.empty()) ? getAttachPoint(strAttachPt, true) : NULL;
183 if (!pAttachPt)
184 pAttachPt = getAttachPoint(gInventory.getCategory(pItem->getParentUUID()), fStrict);
185 }
186 else
187 {
188 pAttachPt = getAttachPoint(gInventory.getCategory(pItem->getParentUUID()), fStrict);
189 if ( (!pAttachPt) && (!strAttachPt.empty()) )
190 pAttachPt = getAttachPoint(strAttachPt, true);
191 }
192 return pAttachPt;
193}
194
195// Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.2a
196S32 RlvHandler::getAttachPointIndex(const LLViewerJointAttachment* pAttachPt) const
197{
198 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
199 if (pAvatar)
200 {
201 for (LLVOAvatar::attachment_map_t::const_iterator itAttach = pAvatar->mAttachmentPoints.begin();
202 itAttach != pAvatar->mAttachmentPoints.end(); ++itAttach)
203 {
204 if (itAttach->second == pAttachPt)
205 return itAttach->first;
206 }
207 }
208 return 0;
209}
210
211// Checked: 2009-07-29 (RLVa-1.0.1b) | Added: RLVa-1.0.1b
212LLViewerJointAttachment* RlvHandler::getAttachPointLegacy(const LLInventoryCategory* pFolder) const
213{
214 // Hopefully some day this can just be deprecated (see http://rlva.catznip.com/blog/2009/07/attachment-point-naming-convention/)
215 if ( (!pFolder) || (pFolder->getName().empty()) )
216 return NULL;
217
218 // Check for a (...) block *somewhere* in the name
219 std::string::size_type idxMatch;
220 std::string strAttachPt = rlvGetFirstParenthesisedText(pFolder->getName(), &idxMatch);
221 if (!strAttachPt.empty())
222 {
223 // Could be "(attachpt)", ".(attachpt)" or "Folder name (attachpt)"
224 if ( (0 != idxMatch) && ((1 != idxMatch) || (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName().at(0)) ) && // No '(' or '.(' start
225 (idxMatch + strAttachPt.length() + 1 != pFolder->getName().length()) ) // or there's extra text
226 {
227 // It's definitely not one of the first two so assume it's the last form (in which case we need the last paranthesised block)
228 strAttachPt = rlvGetLastParenthesisedText(pFolder->getName());
229 }
230 }
231 else
232 {
233 // There's no paranthesised block, but it could still be "attachpt" or ".attachpt" (just strip away the '.' from the last one)
234 strAttachPt = pFolder->getName();
235 if (RLV_FOLDER_PREFIX_HIDDEN == strAttachPt[0])
236 strAttachPt.erase(0, 1);
237 }
238 return getAttachPoint(strAttachPt, true);
239}
240
241bool RlvHandler::hasLockedHUD() const
242{
243 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
244 if (!pAvatar)
245 return false;
246
247 LLViewerJointAttachment* pAttachPt;
248 for (rlv_detach_map_t::const_iterator itAttachPt = m_Attachments.begin(); itAttachPt != m_Attachments.end(); ++itAttachPt)
249 {
250 pAttachPt = get_if_there(pAvatar->mAttachmentPoints, (S32)itAttachPt->first, (LLViewerJointAttachment*)NULL);
251 if ( (pAttachPt) && (pAttachPt->getIsHUDAttachment()) )
252 return true; // At least one of our locked attachments is a HUD
253 }
254 return false; // None of our locked attachments is a HUD
255}
256
257bool RlvHandler::isDetachable(const LLInventoryItem* pItem) const
258{
259 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
260 return ( (pItem) && (pAvatar) ) ? isDetachable(pAvatar->getWornAttachment(pItem->getUUID())) : true;
261}
262
263// Checked: 2009-08-11 (RLVa-1.0.1h) | Added: RLVa-1.0.1h
264bool RlvHandler::isDetachableExcept(S32 idxAttachPt, LLViewerObject *pObj) const
265{
266 // Loop over every object that marked the specific attachment point undetachable (but ignore pObj and any of its children)
267 for (rlv_detach_map_t::const_iterator itAttach = m_Attachments.lower_bound(idxAttachPt),
268 endAttach = m_Attachments.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach)
269 {
270 LLViewerObject* pTempObj = gObjectList.findObject(itAttach->second);
271 if ( (!pTempObj) || (pTempObj->getRootEdit()->getID() != pObj->getID()) )
272 return false;
273 }
274 return true;
275}
276
277// Checked: 2009-05-31 (RLVa-0.2.0e) | Modified: RLVa-0.2.0e
278bool RlvHandler::setDetachable(S32 idxAttachPt, const LLUUID& idRlvObj, bool fDetachable)
279{
280 // Sanity check - make sure it's an object we know about
281 rlv_object_map_t::const_iterator itObj = m_Objects.find(idRlvObj);
282 if ( (itObj == m_Objects.end()) || (!idxAttachPt) )
283 return false; // If (idxAttachPt) == 0 then: (pObj == NULL) || (pObj->isAttachment() == FALSE)
284
285 if (!fDetachable)
286 {
287 // Sanity check - make sure it's not already marked undetachable by this object (NOTE: m_Attachments is a *multimap*, not a map)
288 for (rlv_detach_map_t::const_iterator itAttach = m_Attachments.lower_bound(idxAttachPt),
289 endAttach = m_Attachments.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach)
290 {
291 if (itObj->second.m_UUID == itAttach->second)
292 return false;
293 }
294
295 m_Attachments.insert(std::pair<S32, LLUUID>(idxAttachPt, itObj->second.m_UUID));
296 return true;
297 }
298 else
299 {
300 // NOTE: m_Attachments is a *multimap*, not a map
301 for (rlv_detach_map_t::iterator itAttach = m_Attachments.lower_bound(idxAttachPt),
302 endAttach = m_Attachments.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach)
303 {
304 if (itObj->second.m_UUID == itAttach->second)
305 {
306 m_Attachments.erase(itAttach);
307 return true;
308 }
309 }
310 }
311 return false; // Fall-through for (fDetachable == TRUE) - if the object wasn't undetachable then we consider it a failure
312}
313
314
315
316#ifdef RLV_EXTENSION_FLAG_NOSTRIP
317 // Checked: 2009-05-26 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
318 bool RlvHandler::isStrippable(const LLUUID& idItem) const
319 {
320 // An item is exempt from @detach or @remoutfit if:
321 // - its name contains "nostrip" (anywhere in the name)
322 // - its parent folder contains "nostrip" (anywhere in the name)
323 if (idItem.notNull())
324 {
325 LLViewerInventoryItem* pItem = gInventory.getItem(idItem);
326 if (pItem)
327 {
328 if (-1 != pItem->getName().find(RLV_FOLDER_FLAG_NOSTRIP))
329 return false;
330
331 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
332 if ( (pFolder) && (-1 != pFolder->getName().find(RLV_FOLDER_FLAG_NOSTRIP)) )
333 return false;
334 }
335 }
336 return true;
337 }
338#endif // RLV_EXTENSION_FLAG_NOSTRIP
339
340// ============================================================================
341// Behaviour related functions
342//
343
344bool RlvHandler::hasBehaviourExcept(const std::string& strBehaviour, const LLUUID& idObj) const
345{
346 for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
347 if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(strBehaviour)) )
348 return true;
349 return false;
350}
351
352bool RlvHandler::hasBehaviourExcept(ERlvBehaviour eBehaviour, const std::string& strOption, const LLUUID& idObj) const
353{
354 for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
355 if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(eBehaviour, strOption)) )
356 return true;
357 return false;
358}
359
360bool RlvHandler::hasBehaviourExcept(const std::string& strBehaviour, const std::string& strOption, const LLUUID& idObj) const
361{
362 for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
363 if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(strBehaviour, strOption)) )
364 return true;
365 return false;
366}
367
368// ============================================================================
369// Command processing functions
370//
371
372// Checked: 2009-06-03 (RLVa-0.2.0h)
373void RlvHandler::addBehaviourObserver(RlvBehaviourObserver* pBhvrObserver)
374{
375 std::list<RlvBehaviourObserver*>::iterator itBhvrObserver = std::find(m_BhvrObservers.begin(), m_BhvrObservers.end(), pBhvrObserver);
376 if (itBhvrObserver == m_BhvrObservers.end())
377 m_BhvrObservers.push_back(pBhvrObserver);
378}
379
380// Checked: 2009-06-03 (RLVa-0.2.0h)
381void RlvHandler::removeBehaviourObserver(RlvBehaviourObserver* pBhvrObserver)
382{
383 std::list<RlvBehaviourObserver*>::iterator itBhvrObserver = std::find(m_BhvrObservers.begin(), m_BhvrObservers.end(), pBhvrObserver);
384 if (itBhvrObserver != m_BhvrObservers.end())
385 m_BhvrObservers.erase(itBhvrObserver);
386}
387
388// Checked: 2009-06-03 (RLVa-0.2.0h)
389void RlvHandler::notifyBehaviourObservers(const RlvCommand& rlvCmd, bool fInternal)
390{
391 for (std::list<RlvBehaviourObserver*>::const_iterator itBhvrObserver = m_BhvrObservers.begin();
392 itBhvrObserver != m_BhvrObservers.end(); ++itBhvrObserver)
393 {
394 (*itBhvrObserver)->changed(rlvCmd, fInternal);
395 }
396}
397
398// Checked:
399BOOL RlvHandler::processCommand(const LLUUID& uuid, const std::string& strCmd, bool fFromObj)
400{
401 #ifdef RLV_DEBUG
402 RLV_INFOS << "[" << uuid << "]: " << strCmd << LL_ENDL;
403 #endif // RLV_DEBUG
404
405 RlvCommand rlvCmd(strCmd);
406 if (!rlvCmd.isValid())
407 {
408 #ifdef RLV_DEBUG
409 RLV_INFOS << "\t-> invalid command: " << strCmd << LL_ENDL;
410 #endif // RLV_DEBUG
411 return FALSE;
412 }
413 m_pCurCommand = &rlvCmd; m_idCurObject = uuid;
414
415 BOOL fRet = FALSE;
416 switch (rlvCmd.getParamType())
417 {
418 case RLV_TYPE_ADD: // Checked: 2009-06-03 (RLVa-0.2.0h) | Modified: RLVa-0.2.0h
419 {
420 if ( (m_Behaviours[rlvCmd.getBehaviourType()]) &&
421 ( (RLV_BHVR_SETDEBUG == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETENV == rlvCmd.getBehaviourType()) ) )
422 {
423 // Some restrictions can only be held by one single object to avoid deadlocks
424 #ifdef RLV_DEBUG
425 RLV_INFOS << "\t- " << rlvCmd.getBehaviour() << " is already set by another object => discarding" << LL_ENDL;
426 #endif // RLV_DEBUG
427 break;
428 }
429
430 rlv_object_map_t::iterator itObj = m_Objects.find(uuid);
431 if (itObj != m_Objects.end())
432 {
433 RlvObject& rlvObj = itObj->second;
434 fRet = rlvObj.addCommand(rlvCmd);
435 }
436 else
437 {
438 RlvObject rlvObj(uuid);
439 fRet = rlvObj.addCommand(rlvCmd);
440 m_Objects.insert(std::pair<LLUUID, RlvObject>(uuid, rlvObj));
441 }
442
443 #ifdef RLV_DEBUG
444 RLV_INFOS << "\t- " << ( (fRet) ? "adding behaviour" : "skipping duplicate") << LL_ENDL;
445 #endif // RLV_DEBUG
446
447 if (fRet) { // If FALSE then this was a duplicate, there's no need to handle those
448 if (!m_pGCTimer)
449 m_pGCTimer = new RlvGCTimer();
450 processAddCommand(uuid, rlvCmd);
451 notifyBehaviourObservers(rlvCmd, !fFromObj);
452 }
453 }
454 break;
455 case RLV_TYPE_REMOVE: // Checked:
456 {
457 rlv_object_map_t::iterator itObj = m_Objects.find(uuid);
458 if (itObj != m_Objects.end())
459 fRet = itObj->second.removeCommand(rlvCmd);
460
461 #ifdef RLV_DEBUG
462 RLV_INFOS << "\t- " << ( (fRet) ? "removing behaviour"
463 : "skipping remove (unset behaviour or unknown object)") << LL_ENDL;
464 #endif // RLV_DEBUG
465
466 if (fRet) { // Don't handle non-sensical removes
467 processRemoveCommand(uuid, rlvCmd);
468 notifyBehaviourObservers(rlvCmd, !fFromObj);
469
470 if (0 == itObj->second.m_Commands.size())
471 {
472 #ifdef RLV_DEBUG
473 RLV_INFOS << "\t- command list empty => removing " << uuid << LL_ENDL;
474 #endif // RLV_DEBUG
475 m_Objects.erase(itObj);
476 }
477 }
478 }
479 break;
480 case RLV_TYPE_FORCE: // Checked:
481 fRet = processForceCommand(uuid, rlvCmd);
482 break;
483 case RLV_TYPE_REPLY: // Checked:
484 fRet = processReplyCommand(uuid, rlvCmd);
485 break;
486 case RLV_TYPE_UNKNOWN: // Checked:
487 {
488 if ("clear" == rlvCmd.getBehaviour())
489 {
490 const std::string& strFilter = rlvCmd.getParam(); std::string strCmdRem;
491
492 rlv_object_map_t::const_iterator itObj = m_Objects.find(uuid);
493 if (itObj != m_Objects.end()) // No sense in @clear'ing if we don't have any commands for this object
494 {
495 const RlvObject& rlvObj = itObj->second; bool fContinue = true;
496 for (rlv_command_list_t::const_iterator itCmd = rlvObj.m_Commands.begin(), itCurCmd;
497 ((fContinue) && (itCmd != rlvObj.m_Commands.end())); )
498 {
499 itCurCmd = itCmd++; // Point itCmd ahead so it won't get invalidated if/when we erase a command
500
501 const RlvCommand& rlvCmdRem = *itCurCmd;
502 if ( (strFilter.empty()) || (-1 != rlvCmdRem.asString().find(strFilter)) )
503 {
504 fContinue = (rlvObj.m_Commands.size() > 1); // rlvObj will become invalid once we remove the last command
505 strCmdRem = rlvCmdRem.getBehaviour() + ":" + rlvCmdRem.getOption() + "=y";
506 processCommand(uuid, strCmdRem, false);
507 }
508 }
509 fRet = TRUE;
510 }
511 }
512 }
513 break;
514 #ifdef LL_GNUC
515 default:
516 break;
517 #endif // LL_GNUC
518 }
519
520 #ifdef RLV_DEBUG
521 RLV_INFOS << "\t--> command " << ((fRet) ? "succeeded" : "failed") << LL_ENDL;
522 #endif // RLV_DEBUG
523
524 m_pCurCommand = NULL; m_idCurObject.setNull();
525 return fRet;
526}
527
528BOOL RlvHandler::processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd)
529{
530 // NOTE: - at this point the command has already been added to the corresponding RlvObject instance
531 // - the object's UUID may or may not exist in gObjectList (see handling of @detach=n)
532
533 ERlvBehaviour eBehaviour = rlvCmd.getBehaviourType();
534 const std::string& strOption = rlvCmd.getOption();
535
536 if ( (RLV_BHVR_UNKNOWN != eBehaviour) && (strOption.empty()) )
537 m_Behaviours[eBehaviour]++;
538
539 switch (eBehaviour)
540 {
541 case RLV_BHVR_DETACH: // @detach[:<option>]=n - Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
542 {
543 LLViewerObject* pObj = NULL; S32 idxAttachPt = 0;
544 if (strOption.empty()) // @detach=n
545 {
546 // If the object rezzed before we received @detach=n from it then we can just do our thing here
547 // If the object hasn't rezzed yet then we need to wait until RlvHandler::onAttach()
548 // If @detach=n were possible for non-attachments another copy/paste would be needed in RlvHandler::onGC()
549 if ((pObj = gObjectList.findObject(uuid)) != NULL)
550 setDetachable(pObj, uuid, false);
551 }
552 else if ((idxAttachPt = getAttachPointIndex(strOption, true)) != 0) // @detach:<attachpt>=n
553 {
554 setDetachable(idxAttachPt, uuid, false);
555
556 // (See below)
557 LLViewerJointAttachment* pAttachPt = getAttachPoint(strOption, true);
558 if (pAttachPt)
559 pObj = pAttachPt->getObject();
560 }
561
562 // When at least one HUD attachment is locked we want to make sure they're all visible (ie prevent hiding a blindfold HUD)
563 // However, since @detach:<attachpt>=n might lock a HUD attachment point that doesn't currently have an object we
564 // have to do this here *and* in RlvHandler::onAttach()
565 if ( (pObj) && (pObj->isHUDAttachment()) )
566 LLPipeline::sShowHUDAttachments = TRUE;
567 }
568 break;
569 case RLV_BHVR_REDIRCHAT: // @redirchat:<option>=n - Checked: 2009-07-07 (RLVa-1.0.0d)
570 case RLV_BHVR_REDIREMOTE: // @rediremote:<option>=n - Checked: 2009-07-07 (RLVa-1.0.0d) | Added: RLVa-0.2.2a
571 {
572 if (!strOption.empty())
573 m_Behaviours[eBehaviour]++; // @redirchat and @rediremote don't have an optionless version so keep track of it here
574 else
575 m_Behaviours[eBehaviour]--; // @redirchat=n and @rediremote=n are undefined, don't keep track of them
576 }
577 break;
578 case RLV_BHVR_SHOWWORLDMAP: // @showworldmap=n - Checked: 2009-07-05 (RLVa-1.0.0c)
579 {
580 // Simulate clicking the Map button [see LLToolBar::onClickMap()]
581 if (gFloaterWorldMap->getVisible())
582 LLFloaterWorldMap::toggle(NULL);
583 }
584 break;
585 case RLV_BHVR_SHOWMINIMAP: // @showminimap=n - Checked: 2009-07-05 (RLVa-1.0.0c)
586 {
587 // Simulate clicking the Minimap button [see LLToolBar::onClickRadar()]
588 if (LLFloaterMap::instanceVisible())
589 LLFloaterMap::hideInstance();
590 }
591 break;
592 #ifdef RLV_EXTENSION_STARTLOCATION
593 case RLV_BHVR_TPLOC: // @tploc=n - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.1d
594 case RLV_BHVR_UNSIT: // @unsit=n - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.1d
595 {
596 if (strOption.empty())
597 RlvSettings::updateLoginLastLocation();
598 }
599 break;
600 #endif // RLV_EXTENSION_STARTLOCATION
601 case RLV_BHVR_EDIT: // @edit=n - Checked: 2009-07-04 (RLVa-1.0.0b)
602 {
603 // Turn off "View / Highlight Transparent"
604 LLDrawPoolAlpha::sShowDebugAlpha = FALSE;
605
606 // Close the Beacons floater if it's open
607 if (LLFloaterBeacons::instanceVisible())
608 LLFloaterBeacons::toggleInstance();
609
610 // Get rid of the build floater if it's open [copy/paste from toggle_build_mode()]
611 if (gFloaterTools->getVisible())
612 {
613 gAgent.resetView(FALSE);
614 gFloaterTools->close();
615 gViewerWindow->showCursor();
616 }
617 }
618 break;
619 case RLV_BHVR_ADDOUTFIT: // @addoutfit[:<layer>]=n - Checked: 2009-07-07 (RLVa-1.0.0d)
620 case RLV_BHVR_REMOUTFIT: // @remoutfit[:<layer>]=n - Checked: 2009-07-07 (RLVa-1.0.0d)
621 {
622 S16* pLayers = (eBehaviour == RLV_BHVR_ADDOUTFIT) ? m_LayersAdd : m_LayersRem;
623
624 if (strOption.empty())
625 {
626 for (int idx = 0; idx < WT_COUNT; idx++)
627 pLayers[idx]++;
628 }
629 else
630 {
631 EWearableType type = LLWearable::typeNameToType(strOption);
632 if (WT_INVALID != type)
633 {
634 pLayers[type]++;
635 m_Behaviours[eBehaviour]++;
636 }
637 }
638 }
639 break;
640 case RLV_BHVR_SHOWINV: // @showinv=n - Checked: 2009-07-10 (RLVa-1.0.0g) | Modified: RLVa-1.0.0g
641 {
642 // Close all open inventory windows
643 LLInventoryView::closeAll();
644 }
645 break;
646 case RLV_BHVR_SHOWLOC: // @showloc=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
647 {
648 // If we're the first @showloc=n restriction refresh all object text so we can filter it if necessary
649 if (1 == m_Behaviours[RLV_BHVR_SHOWLOC])
650 LLHUDText::refreshAllObjectText();
651
652 // Close the "About Land" floater if it's currently visible
653 if (LLFloaterLand::instanceVisible())
654 LLFloaterLand::hideInstance();
655
656 // Close the "Estate Tools" floater is it's currently visible
657 if (LLFloaterRegionInfo::instanceVisible())
658 LLFloaterRegionInfo::hideInstance();
659
660 // NOTE: we should close the "God Tools" floater as well, but since calling LLFloaterGodTools::instance() always
661 // creates a new instance of the floater and since it's very unlikely to be open it's just better not to
662 }
663 break;
664 case RLV_BHVR_SHOWNAMES: // @shownames=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
665 {
666 // If we're the first @shownames=n restriction refresh all object text so we can filter it if necessary
667 if (1 == m_Behaviours[RLV_BHVR_SHOWNAMES])
668 LLHUDText::refreshAllObjectText();
669
670 // Close the "Active Speakers" panel if it's currently visible
671 LLFloaterChat::getInstance()->childSetVisible("active_speakers_panel", false);
672 }
673 break;
674 case RLV_BHVR_FLY: // @fly=n - Checked: 2009-07-05 (RLVa-1.0.0c)
675 {
676 // If currently flying, simulate clicking the Fly button [see LLToolBar::onClickFly()]
677 if (gAgent.getFlying())
678 gAgent.toggleFlying();
679 }
680 break;
681 case RLV_BHVR_SETENV: // @setenv=n - Checked: 2009-07-10 (RLVa-1.0.0g) | Modified: RLVa-0.2.0a
682 {
683 if (!fNoSetEnv)
684 {
685 // Only close the floaters if their instance exists and they're actually visible
686 if ( (LLFloaterEnvSettings::isOpen()) && (LLFloaterEnvSettings::instance()->getVisible()) )
687 LLFloaterEnvSettings::instance()->close();
688 if ( (LLFloaterWindLight::isOpen()) && (LLFloaterWindLight::instance()->getVisible()) )
689 LLFloaterWindLight::instance()->close();
690 if ( (LLFloaterWater::isOpen()) && (LLFloaterWater::instance()->getVisible()) )
691 LLFloaterWater::instance()->close();
692 if ( (LLFloaterDayCycle::isOpen()) && (LLFloaterDayCycle::instance()->getVisible()) )
693 LLFloaterDayCycle::instance()->close();
694
695 // Save the current WindLight params so we can restore them on @setenv=y
696 if (m_pWLSnapshot)
697 {
698 RLV_ERRS << "m_pWLSnapshot != NULL" << LL_ENDL; // Safety net in case we set @setenv=n for more than 1 object
699 delete m_pWLSnapshot;
700 }
701 m_pWLSnapshot = RlvWLSnapshot::takeSnapshot();
702 }
703 }
704 break;
705 case RLV_BHVR_SHOWHOVERTEXTALL: // @showhovertextal=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
706 case RLV_BHVR_SHOWHOVERTEXTWORLD: // @showhovertextworld=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
707 case RLV_BHVR_SHOWHOVERTEXTHUD: // @showhovertexthud=n - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
708 {
709 // Refresh all hover text (LLHUDText::setStringUTF8() will decide what needs clearing and what doesn't)
710 LLHUDText::refreshAllObjectText();
711 }
712 break;
713 case RLV_BHVR_SHOWHOVERTEXT: // @showhovertext:<uuid>=n - Checked: 2009-07-09 (RLVa-0.2.2a) | Modified: RLVa-1.0.0f
714 {
715 LLUUID idException(strOption);
716 if (!idException.isNull()) // If there's an option it should be a valid UUID
717 {
718 addException(eBehaviour, LLUUID(strOption));
719
720 // Clear the object's hover text
721 LLViewerObject* pObj = gObjectList.findObject(idException);
722 if ( (pObj) && (pObj->mText.notNull()) && (!pObj->mText->getObjectText().empty()) )
723 pObj->mText->setStringUTF8("");
724 }
725 }
726 break;
727 case RLV_BHVR_NOTIFY: // @notify:<option>=add - Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
728 {
729 S32 nChannel; std::string strFilter;
730 if ( (!strOption.empty()) && (rlvParseNotifyOption(strOption, nChannel, strFilter)) )
731 {
732 if (!m_pBhvrNotify)
733 addBehaviourObserver(m_pBhvrNotify = new RlvBehaviourNotifyObserver());
734 m_pBhvrNotify->addNotify(uuid, nChannel, strFilter);
735 }
736 }
737 break;
738 case RLV_BHVR_RECVCHAT: // @recvchat:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
739 case RLV_BHVR_RECVEMOTE: // @recvemote:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
740 case RLV_BHVR_RECVIM: // @recvim:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
741 case RLV_BHVR_SENDIM: // @sendim:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
742 case RLV_BHVR_TPLURE: // @tplure:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
743 case RLV_BHVR_ACCEPTTP: // @accepttp:<uuid>=add - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
744 {
745 addException(eBehaviour, LLUUID(strOption));
746 }
747 break;
748 default:
749 {
750 // Give our observers a chance to handle any command we don't
751 RlvEvent rlvEvent(uuid, rlvCmd);
752 m_Emitter.update(&RlvObserver::onAddCommand, rlvEvent);
753 }
754 break;
755 }
756 return TRUE; // Add command success/failure is decided by RlvObject::addCommand()
757}
758
759// Checked: 2009-08-05 (RLVa-1.0.1e) | Added: RLVa-1.0.1e
760void RlvHandler::processRetainedCommands()
761{
762 for (rlv_retained_list_t::const_iterator itCmd = m_Retained.begin(); itCmd != m_Retained.end(); ++itCmd)
763 {
764 const RlvRetainedCommand& cmd = *itCmd;
765 processCommand(cmd.idObject, cmd.strCmd, true);
766 }
767 m_Retained.clear();
768}
769
770BOOL RlvHandler::processRemoveCommand(const LLUUID& uuid, const RlvCommand& rlvCmd)
771{
772 // NOTE: - the RlvObject instance still exists at this point, but the viewer might already have removed it from its object list
773 ERlvBehaviour eBehaviour = rlvCmd.getBehaviourType();
774 const std::string& strOption = rlvCmd.getOption();
775
776 if ( (RLV_BHVR_UNKNOWN != eBehaviour) && (strOption.empty()) )
777 m_Behaviours[eBehaviour]--;
778
779 switch (eBehaviour)
780 {
781 case RLV_BHVR_DETACH: // @detach[:<option>]=y - Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
782 {
783 S32 idxAttachPt;
784 if (strOption.empty()) // @detach=y
785 {
786 // The object may or may not (if it got detached) still exist so clean up the hard way
787 if (m_Objects.find(uuid) != m_Objects.end())
788 {
789 for (rlv_detach_map_t::const_iterator itAttach = m_Attachments.begin(), endAttach = m_Attachments.end();
790 itAttach != endAttach; ++itAttach)
791 {
792 if (itAttach->second == uuid)
793 {
794 setDetachable(itAttach->first, uuid, true); // <- invalidates our iterators on return
795 break;
796 }
797 }
798 }
799 }
800 else if ((idxAttachPt = getAttachPointIndex(strOption, true))) // @detach:<attachpt>=y
801 {
802 setDetachable(idxAttachPt, uuid, true);
803 }
804 }
805 break;
806 case RLV_BHVR_REDIRCHAT: // @redirchat:<option>=y - Checked: 2009-07-07 (RLVa-1.0.0d)
807 case RLV_BHVR_REDIREMOTE: // @rediremote:<option>=y - Checked: 2009-07-07 (RLVa-1.0.0d) | Added: RLVa-0.2.2a
808 {
809 if (!strOption.empty())
810 m_Behaviours[eBehaviour]--; // @redirchat and @rediremote don't have an optionless version so keep track of it here
811 else
812 m_Behaviours[eBehaviour]++; // @redirchat=n and @rediremote=n are undefined, don't keep track of them
813 }
814 break;
815 #ifdef RLV_EXTENSION_STARTLOCATION
816 case RLV_BHVR_TPLOC: // @tploc=y - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.1d
817 case RLV_BHVR_UNSIT: // @unsit=y - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.1d
818 {
819 if (strOption.empty())
820 RlvSettings::updateLoginLastLocation();
821 }
822 break;
823 #endif // RLV_EXTENSION_STARTLOCATION
824 case RLV_BHVR_ADDOUTFIT: // @addoutfit[:<layer>]=y - Checked: 2009-07-07 (RLVa-1.0.0d)
825 case RLV_BHVR_REMOUTFIT: // @remoutfit[:<layer>]=y - Checked: 2009-07-07 (RLVa-1.0.0d)
826 {
827 S16* pLayers = (eBehaviour == RLV_BHVR_ADDOUTFIT) ? m_LayersAdd : m_LayersRem;
828
829 if (strOption.empty())
830 {
831 for (int idx = 0; idx < WT_COUNT; idx++)
832 pLayers[idx]--;
833 }
834 else
835 {
836 EWearableType type = LLWearable::typeNameToType(strOption);
837 if (WT_INVALID != type)
838 {
839 pLayers[type]--;
840 m_Behaviours[eBehaviour]--;
841 }
842 }
843 }
844 break;
845 case RLV_BHVR_SETENV: // @setenv=y - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-0.2.0h
846 {
847 if (!fNoSetEnv)
848 {
849 // Restore WindLight parameters to what they were before @setenv=n was issued
850 RlvWLSnapshot::restoreSnapshot(m_pWLSnapshot);
851 delete m_pWLSnapshot;
852 m_pWLSnapshot = NULL;
853 }
854 }
855 break;
856 case RLV_BHVR_SHOWLOC: // @showloc=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
857 case RLV_BHVR_SHOWNAMES: // @shownames=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
858 case RLV_BHVR_SHOWHOVERTEXTALL: // @showhovertextal=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
859 case RLV_BHVR_SHOWHOVERTEXTWORLD: // @showhovertextworld=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
860 case RLV_BHVR_SHOWHOVERTEXTHUD: // @showhovertexthud=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f
861 {
862 // If this was the last of any of the five restrictions we should refresh all hover text in case anything needs restoring
863 if (!m_Behaviours[eBehaviour])
864 LLHUDText::refreshAllObjectText();
865 }
866 break;
867 case RLV_BHVR_SHOWHOVERTEXT: // @showhovertext:<uuid>=y - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
868 {
869 LLUUID idException(strOption);
870 if (!idException.isNull()) // If there's an option it should be a valid UUID
871 {
872 removeException(eBehaviour, LLUUID(strOption));
873
874 // Restore the object's hover text
875 LLViewerObject* pObj = gObjectList.findObject(idException);
876 if ( (pObj) && (pObj->mText.notNull()) && (!pObj->mText->getObjectText().empty()) )
877 pObj->mText->setStringUTF8(pObj->mText->getObjectText());
878 }
879 }
880 break;
881 case RLV_BHVR_NOTIFY: // @notify:<option>=rem - Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
882 {
883 S32 nChannel; std::string strFilter;
884 if ( (m_pBhvrNotify) && (!strOption.empty()) && (rlvParseNotifyOption(strOption, nChannel, strFilter)) )
885 {
886 m_pBhvrNotify->removeNotify(uuid, nChannel, strFilter);
887
888 if (!m_pBhvrNotify->hasNotify())
889 {
890 removeBehaviourObserver(m_pBhvrNotify);
891 delete m_pBhvrNotify;
892 m_pBhvrNotify = NULL;
893 }
894 }
895 }
896 break;
897 case RLV_BHVR_RECVCHAT: // @recvchat:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
898 case RLV_BHVR_RECVEMOTE: // @recvemote:<uui>=red - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
899 case RLV_BHVR_RECVIM: // @recvim:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
900 case RLV_BHVR_SENDIM: // @sendim:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
901 case RLV_BHVR_TPLURE: // @recvim:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
902 case RLV_BHVR_ACCEPTTP: // @accepttp:<uuid>=rem - Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
903 {
904 removeException(eBehaviour, LLUUID(strOption));
905 }
906 break;
907 default:
908 {
909 // Give our observers a chance to handle any command we don't
910 RlvEvent rlvEvent(uuid, rlvCmd);
911 m_Emitter.update(&RlvObserver::onRemoveCommand, rlvEvent);
912 }
913 break;
914 }
915 return TRUE; // Remove commands don't fail, doesn't matter what we return here
916}
917
918BOOL RlvHandler::processForceCommand(const LLUUID& idObj, const RlvCommand& rlvCmd) const
919{
920 const std::string& strOption = rlvCmd.getOption();
921 BOOL fHandled = TRUE;
922
923 switch (rlvCmd.getBehaviourType())
924 {
925 case RLV_BHVR_DETACH: // @detach[:<option>]=force - Checked:
926 onForceDetach(idObj, strOption);
927 break;
928 case RLV_BHVR_REMOUTFIT: // @remoutfit:<option>=force - Checked:
929 onForceRemOutfit(idObj, strOption);
930 break;
931 case RLV_BHVR_UNSIT: // @unsit=force - Checked: 2009-06-02 (RLVa-0.2.0g)
932 {
933 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
934 if ( (pAvatar) && (pAvatar->mIsSitting) && (!hasBehaviourExcept(RLV_BHVR_UNSIT, idObj)) )
935 {
936 // See behaviour notes on why we have to force an agent update here
937 gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
938 send_agent_update(TRUE, TRUE);
939 }
940 }
941 break;
942 case RLV_BHVR_TPTO: // @tpto:<option>=force - Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-1.0.0h
943 {
944 fHandled = FALSE;
945 if ( (!strOption.empty()) && (-1 == strOption.find_first_not_of("0123456789/.")) )
946 {
947 LLVector3d posGlobal;
948
949 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
950 boost::char_separator<char> sep("/", "", boost::keep_empty_tokens);
951 tokenizer tokens(strOption, sep); int idx = 0;
952 for (tokenizer::iterator itToken = tokens.begin(); itToken != tokens.end(); ++itToken)
953 {
954 if (idx < 3)
955 LLStringUtil::convertToF64(*itToken, posGlobal[idx++]);
956 }
957
958 if (idx == 3)
959 {
960 gAgent.teleportViaLocation(posGlobal);
961 fHandled = TRUE;
962 }
963 }
964 }
965 break;
966 case RLV_BHVR_SIT: // @sit:<option>=force - Checked: 2009-06-02 (RLVa-0.2.0g)
967 fHandled = onForceSit(idObj, rlvCmd.getOption());
968 break;
969 case RLV_BHVR_ADDOUTFIT: // @addoutfit:<option>=force <- synonym of @attach:<option>=force
970 case RLV_BHVR_ATTACH: // @attach:<option>=force - Checked:
971 onForceWear(rlvCmd.getOption(), true, false); // Force attach single folder
972 break;
973 case RLV_BHVR_ATTACHALL: // @attachall:<option>=force - Checked:
974 onForceWear(rlvCmd.getOption(), true, true); // Force attach nested folders
975 break;
976 case RLV_BHVR_DETACHALL: // @detachall:<option>=force - Checked:
977 onForceWear(rlvCmd.getOption(), false, true); // Force detach nested folders
978 break;
979 case RLV_BHVR_ATTACHTHIS:
980 case RLV_BHVR_ATTACHALLTHIS:
981 case RLV_BHVR_DETACHTHIS:
982 case RLV_BHVR_DETACHALLTHIS:
983 {
984 ERlvBehaviour eBehaviour = rlvCmd.getBehaviourType();
985 std::string strReply;
986 if (onGetPath(idObj, strOption, strReply))
987 {
988 LLStringUtil::toLower(strReply);
989 onForceWear(strReply,
990 (RLV_BHVR_ATTACHTHIS == eBehaviour) || (RLV_BHVR_ATTACHALLTHIS == eBehaviour),
991 (RLV_BHVR_ATTACHALLTHIS == eBehaviour) || (RLV_BHVR_DETACHALLTHIS == eBehaviour));
992 }
993 }
994 break;
995 case RLV_BHVR_DETACHME: // @detachme=force - Checked: 2009-06-07 (RLVa-0.2.1c)
996 {
997 // NOTE: @detachme=force could be seen as a @detach:<attachpt>=force but RLV implements it as a "detach by UUID"
998 LLViewerObject* pObj; LLVOAvatar* pAvatar; LLViewerJointAttachment* pAttachPt;
999 if ( ((pObj = gObjectList.findObject(idObj)) != NULL) && (pObj->isAttachment()) &&
1000 ((pAvatar = gAgent.getAvatarObject()) != NULL) &&
1001 ((pAttachPt = pAvatar->getTargetAttachmentPoint(pObj->getRootEdit())) != NULL) )
1002 {
1003 handle_detach_from_avatar(pAttachPt);
1004 }
1005 }
1006 break;
1007 default:
1008 {
1009 // Give our observers a chance to handle any command we don't
1010 RlvEvent rlvEvent(idObj, rlvCmd);
1011 fHandled = m_Emitter.update(&RlvObserver::onForceCommand, rlvEvent);
1012 }
1013 break;
1014 }
1015 return fHandled; // If we handled it then it'll still be TRUE; if an observer doesn't handle it'll be FALSE
1016}
1017
1018// Checked: 2009-07-12 (RLVa-1.0.0h)
1019BOOL RlvHandler::processReplyCommand(const LLUUID& uuid, const RlvCommand& rlvCmd) const
1020{
1021 const std::string& strOption = rlvCmd.getOption();
1022 const std::string& strChannel = rlvCmd.getParam();
1023 std::string strReply;
1024
1025 BOOL fHandled = TRUE;
1026 switch (rlvCmd.getBehaviourType())
1027 {
1028 case RLV_BHVR_VERSION: // @version=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1029 strReply = getVersionString();
1030 break;
1031 case RLV_BHVR_GETOUTFIT: // @getoufit[:<layer>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
1032 {
1033 // (Quirk: RLV 1.16.1 will execute @getoutfit=<channel> if <layer> is invalid, so we need to as well)
1034 EWearableType layerType = LLWearable::typeNameToType(strOption);
1035
1036 const EWearableType layerTypes[] =
1037 {
1038 WT_GLOVES, WT_JACKET, WT_PANTS, WT_SHIRT, WT_SHOES, WT_SKIRT, WT_SOCKS,
1039 WT_UNDERPANTS, WT_UNDERSHIRT, WT_SKIN, WT_EYES, WT_HAIR, WT_SHAPE
1040 };
1041
1042 #ifdef RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1043 for (int idx = 0, cnt = sizeof(layerTypes) / sizeof(EWearableType); idx < cnt; idx++)
1044 {
1045 if ( (WT_INVALID == layerType) || (layerTypes[idx] == layerType) )
1046 {
1047 // TODO-RLVa: add support for 'fHideLockedLayers'
1048 bool fWorn = (gAgent.getWearable(layerTypes[idx])) &&
1049 (!isHiddenCompositeItem(gAgent.getWearableItem(layerTypes[idx]),
1050 LLWearable::typeToTypeName(layerTypes[idx])));
1051 strReply.push_back( (fWorn) ? '1' : '0' );
1052 }
1053 }
1054 #else
1055 for (int idx = 0, cnt = sizeof(layerTypes) / sizeof(EWearableType); idx < cnt; idx++)
1056 if ( (WT_INVALID == layerType) || (layerTypes[idx] == layerType) )
1057 {
1058 // We never hide body parts, even if they're "locked" and we're hiding locked layers
1059 // (nor do we hide a layer if the issuing object is the only one that has this layer locked)
1060 bool fWorn = (gAgent.getWearable(layerTypes[idx])) &&
1061 ( (!RlvSettings::getHideLockedLayers()) ||
1062 (LLAssetType::AT_BODYPART == LLWearable::typeToAssetType(layerTypes[idx])) ||
1063 ( (isRemovableExcept(layerTypes[idx], uuid)) &&
1064 (isStrippable(gAgent.getWearableItem(layerTypes[idx]))) ) );
1065 strReply.push_back( (fWorn) ? '1' : '0' );
1066 //strReply.push_back( (gAgent.getWearable(layerTypes[idx])) ? '1' : '0' );
1067 }
1068 #endif // RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1069 }
1070 break;
1071 case RLV_BHVR_GETATTACH: // @getattach[:<layer>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
1072 {
1073 // If we're fetching all worn attachments then the reply should start with 0
1074 if (strOption.empty())
1075 strReply.push_back('0');
1076
1077 LLVOAvatar* pAvatar = gAgent.getAvatarObject(); std::string strAttachName;
1078 for (LLVOAvatar::attachment_map_t::const_iterator itAttach = pAvatar->mAttachmentPoints.begin();
1079 itAttach != pAvatar->mAttachmentPoints.end(); ++itAttach)
1080 {
1081 LLViewerJointAttachment* pAttachment = itAttach->second;
1082 if (!pAttachment)
1083 continue;
1084
1085 strAttachName = pAttachment->getName(); // Capitalized (see avatar_lad.xml)
1086 LLStringUtil::toLower(strAttachName);
1087
1088 #ifdef RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1089 if ( (strOption.empty()) || (strOption == strAttachName) )
1090 {
1091 // TODO-RLVa: add support for 'fHideLockedAttach'
1092 bool fWorn = (pAttachment->getItemID().notNull()) &&
1093 (!isHiddenCompositeItem(pAttachment->getItemID(), strAttachName));
1094 strReply.push_back( (fWorn) ? '1' : '0' );
1095 }
1096 #else
1097 if ( (strOption.empty()) || (strOption == strAttachName) )
1098 {
1099 bool fWorn = (pAttachment->getItemID().notNull()) &&
1100 ( (!RlvSettings::getHideLockedAttach()) ||
1101 ( (isDetachable(itAttach->first)) && (isStrippable(pAttachment->getItemID())) ) );
1102 strReply.push_back( (fWorn) ? '1' : '0' );
1103 //strReply.push_back( (pAttachment->getItemID().notNull()) ? '1' : '0' );
1104 }
1105 #endif // RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1106 }
1107 }
1108 break;
1109 case RLV_BHVR_GETSTATUS: // @getstatus[:<option>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1110 {
1111 // NOTE: specification says response should start with '/' but RLV-1.16.1 returns an empty string when no rules are set
1112 rlv_object_map_t::const_iterator itObj = m_Objects.find(uuid);
1113 if (itObj != m_Objects.end())
1114 {
1115 std::string strObjStatus = itObj->second.getStatusString(strOption);
1116 if (!strObjStatus.empty())
1117 {
1118 strReply.push_back('/');
1119 strReply += strObjStatus;
1120 }
1121 }
1122 }
1123 break;
1124 case RLV_BHVR_GETSTATUSALL: // @getstatusall[:<option>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1125 {
1126 // NOTE: specification says response should start with '/' but RLV-1.16.1 returns an empty string when no rules are set
1127 std::string strObjStatus;
1128 for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
1129 {
1130 strObjStatus = itObj->second.getStatusString(strOption);
1131 if (!strObjStatus.empty())
1132 {
1133 strReply.push_back('/');
1134 strReply += strObjStatus;
1135 }
1136 }
1137 }
1138 break;
1139 case RLV_BHVR_GETINV: // @getinv[:<path>]=<channel> - Checked: 2009-07-28 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b
1140 {
1141 LLViewerInventoryCategory* pFolder = getSharedFolder(strOption);
1142 if (pFolder)
1143 {
1144 LLInventoryModel::cat_array_t* pFolders;
1145 LLInventoryModel::item_array_t* pItems;
1146 gInventory.getDirectDescendentsOf(pFolder->getUUID(), pFolders, pItems);
1147
1148 if (pFolders)
1149 {
1150 for (S32 idxFolder = 0, cntFolder = pFolders->count(); idxFolder < cntFolder; idxFolder++)
1151 {
1152 const std::string& strFolder = pFolders->get(idxFolder)->getName();
1153 if ( (!strFolder.empty()) && (RLV_FOLDER_PREFIX_HIDDEN != strFolder[0]) &&
1154 (!isFoldedFolder(pFolders->get(idxFolder).get(), true)) )
1155 {
1156 if (!strReply.empty())
1157 strReply.push_back(',');
1158 strReply += strFolder;
1159 }
1160 }
1161 }
1162 }
1163 }
1164 break;
1165 case RLV_BHVR_GETINVWORN: // @getinvworn[:path]=<channel> - Checked:
1166 onGetInvWorn(rlvCmd.getOption(), strReply);
1167 break;
1168 case RLV_BHVR_FINDFOLDER: // @findfolder:<criteria>=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1169 {
1170 // COMPAT-RLV: RLV 1.16.1 returns the first random folder it finds (probably tries to match "" to a folder name?)
1171 // (just going to stick with what's there for now... no option => no folder)
1172 LLInventoryModel::cat_array_t folders;
1173 if ( (!strOption.empty()) && (findSharedFolders(strOption, folders)) )
1174 {
1175 // We need to return an "in depth" result so whoever has the most '/' is our lucky winner
1176 int maxSlashes = 0, curSlashes; std::string strFolderName;
1177 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
1178 {
1179 strFolderName = getSharedPath(folders.get(idxFolder));
1180
1181 curSlashes = std::count(strFolderName.begin(), strFolderName.end(), '/');
1182 if (curSlashes > maxSlashes)
1183 {
1184 maxSlashes = curSlashes;
1185 strReply = strFolderName;
1186 }
1187 }
1188 }
1189 }
1190 break;
1191 #ifdef RLV_EXTENSION_CMD_FINDFOLDERS
1192 case RLV_BHVR_FINDFOLDERS: // @findfolders:<criteria>=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.0b
1193 {
1194 LLInventoryModel::cat_array_t folders;
1195 if ( (!strOption.empty()) && (findSharedFolders(strOption, folders)) )
1196 {
1197 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
1198 {
1199 if (!strReply.empty())
1200 strReply.push_back(',');
1201 strReply += getSharedPath(folders.get(idxFolder));
1202 }
1203 }
1204 }
1205 break;
1206 #endif // RLV_EXTENSION_CMD_FINDFOLDERS
1207 case RLV_BHVR_GETPATH: // @getpath[:<option>]=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1208 onGetPath(uuid, rlvCmd.getOption(), strReply);
1209 break;
1210 case RLV_BHVR_GETSITID: // @getsitid=<channel> - Checked: 2009-07-12 (RLVa-1.0.0h)
1211 {
1212 // (Quirk: RLV 1.16.1 returns a NULL uuid if we're not sitting)
1213 LLVOAvatar* pAvatarObj = gAgent.getAvatarObject(); LLUUID uuid;
1214 if ( (pAvatarObj) && (pAvatarObj->mIsSitting) )
1215 {
1216 // LLVOAvatar inherits from 2 classes so make sure we get the right vfptr
1217 LLViewerObject* pAvatar = dynamic_cast<LLViewerObject*>(pAvatarObj), *pParent;
1218 // (If there is a parent, we need to upcast it from LLXform to LLViewerObject to get its UUID)
1219 if ( (pAvatar) && ((pParent = static_cast<LLViewerObject*>(pAvatar->getRoot())) != pAvatar) )
1220 uuid = pParent->getID();
1221 }
1222 strReply = uuid.asString();
1223 }
1224 break;
1225 default:
1226 {
1227 // Give our observers a chance to handle any command we don't
1228 RlvEvent rlvEvent(uuid, rlvCmd);
1229 return m_Emitter.update(&RlvObserver::onReplyCommand, rlvEvent);
1230 }
1231 break;
1232 }
1233
1234 if (fHandled)
1235 rlvSendChatReply(strChannel, strReply);
1236 return fHandled;
1237}
1238
1239// ============================================================================
1240// House keeping (see debug notes for test methodology, test script and last run)
1241//
1242
1243void RlvHandler::initLookupTables()
1244{
1245 static bool fInitialized = false;
1246 if (!fInitialized)
1247 {
1248 // Initialize the attachment name lookup table
1249 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
1250 if (pAvatar)
1251 {
1252 std::string strAttachPtName;
1253 for (LLVOAvatar::attachment_map_t::const_iterator itAttach = pAvatar->mAttachmentPoints.begin();
1254 itAttach != pAvatar->mAttachmentPoints.end(); ++itAttach)
1255 {
1256 LLViewerJointAttachment* pAttachPt = itAttach->second;
1257 if (pAttachPt)
1258 {
1259 strAttachPtName = pAttachPt->getName();
1260 LLStringUtil::toLower(strAttachPtName);
1261 m_AttachLookup.addKeyword(strAttachPtName, itAttach->first);
1262 }
1263 }
1264 fInitialized = true;
1265 }
1266 }
1267}
1268
1269// Checked: 2009-08-11 (RLVa-1.0.1h) | Modified: RLVa-1.0.1h
1270void RlvHandler::onAttach(LLViewerJointAttachment* pAttachPt, bool fFullyLoaded)
1271{
1272 // Sanity check - LLVOAvatar::attachObject() should call us *after* calling LLViewerJointAttachment::addObject()
1273 LLViewerObject* pObj = pAttachPt->getObject();
1274 S32 idxAttachPt = getAttachPointIndex(pObj); // getAttachPointIndex() has a NULL pointer check so this is safe
1275 if ( (!pObj) || (!idxAttachPt) )
1276 {
1277 RLV_ERRS << "pAttachPt->getObject() == NULL" << LL_ENDL;
1278 return;
1279 }
1280
1281 // Check if this attachment point has a pending "reattach-on-detach"
1282 rlv_reattach_map_t::iterator itReattach = m_AttachPending.find(idxAttachPt);
1283 if (itReattach != m_AttachPending.end())
1284 {
1285 if (itReattach->second == pAttachPt->getItemID())
1286 {
1287 RLV_INFOS << "Reattached " << pAttachPt->getItemID().asString() << " to " << idxAttachPt << LL_ENDL;
1288 m_AttachPending.erase(itReattach);
1289 }
1290 }
1291 else if ( (fFullyLoaded) && (!isDetachableExcept(idxAttachPt, pObj)) )
1292 {
1293 // We're fully loaded with no pending reattach on this attach point but it's "undetachable" -> force detach the new attachment
1294
1295 // Assertion: the only way the attachment point could be locked at this point is if some object locked it with @detach:attachpt=n
1296 // - previous attachments on this attachment point might have issued @detach=n but those were all cleaned up at detach
1297 // - the new attachment might have issued @detach=n but that won't actually lock down the attachment point until further down
1298 // NOTE 1: "some object" may no longer exist if it was not an attachment and the GC hasn't cleaned it up yet (informative)
1299 // NOTE 2: "some object" may refer to the new attachment - ie @detach:spine=n from object on spine (problematic, causes reattach)
1300 // -> solved by using isDetachableExcept(idxAttachPt, pObj) instead of isDetachable(idxAttachPt)
1301
1302 m_DetachPending.insert(std::pair<S32, LLUUID>(idxAttachPt, pObj->getID()));
1303 rlvForceDetach(pAttachPt);
1304 }
1305
1306 // Check if we already have an RlvObject instance for this object (rezzed prim attached from in-world, or an attachment that rezzed in)
1307 rlv_object_map_t::iterator itObj = m_Objects.find(pObj->getID());
1308 if (itObj != m_Objects.end())
1309 {
1310 // If it's an attachment we processed commands for but that only just rezzed in we need to mark it as existing in gObjectList
1311 if (!itObj->second.m_fLookup)
1312 itObj->second.m_fLookup = true;
1313
1314 // In both cases we should check for "@detach=n" and actually lock down the attachment point it got attached to
1315 if (itObj->second.hasBehaviour(RLV_BHVR_DETACH))
1316 {
1317 // (Copy/paste from processAddCommand)
1318 setDetachable(pObj, pObj->getID(), false);
1319
1320 if (pObj->isHUDAttachment())
1321 LLPipeline::sShowHUDAttachments = TRUE; // Prevents hiding of locked HUD attachments
1322 }
1323 }
1324
1325 // Fetch the inventory item if we don't currently have it since we might need it for reattach-on-detach
1326 const LLUUID& idItem = pAttachPt->getItemID();
1327 LLViewerInventoryItem* pItem = ( (idItem.notNull()) && (gInventory.isInventoryUsable()) ) ? gInventory.getItem(idItem) : NULL;
1328 if ( (STATE_STARTED == LLStartUp::getStartupState()) && (pItem != NULL) )
1329 {
1330 RlvCurrentlyWorn f;
1331 f.fetchItem(idItem);
1332 }
1333
1334 // If what we're wearing is located under the shared root then append the attachment point name (if needed)
1335 LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
1336 if ( (STATE_STARTED == LLStartUp::getStartupState()) && (pRlvRoot) && (pItem) && (pItem->isComplete()) &&
1337 (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) )
1338 {
1339 std::string strAttachPt = pAttachPt->getName();
1340 LLStringUtil::toLower(strAttachPt);
1341
1342 // If we can modify the item then it should contain the attach point name itself, otherwise its parent should
1343 if (pItem->getPermissions().allowModifyBy(gAgent.getID()))
1344 {
1345 if (!getAttachPoint(pItem, true))
1346 {
1347 // It doesn't specify an attach point and we can rename it [see LLItemBridge::renameItem()]
1348 std::string strName = pItem->getName();
1349 LLStringUtil::truncate(strName, DB_INV_ITEM_NAME_STR_LEN - strAttachPt.length() - 3);
1350
1351 strName += " (" + strAttachPt + ")";
1352
1353 pItem->rename(strName);
1354 pItem->updateServer(FALSE);
1355 gInventory.updateItem(pItem);
1356 //gInventory.notifyObservers(); <- done further down in LLVOAvatar::attachObject()
1357 }
1358 }
1359 else
1360 {
1361 // Folder can't be the shared root, or be its direct descendant (= nested at least 2 levels deep)
1362 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
1363 if ( (pFolder) &&
1364 (pFolder->getUUID() != pRlvRoot->getUUID()) && (pFolder->getParentUUID() != pRlvRoot->getUUID()) &&
1365 (!getAttachPoint(pFolder, true)) )
1366 {
1367 // It's no mod and its parent folder doesn't contain an attach point
1368 LLInventoryModel::cat_array_t* pFolders;
1369 LLInventoryModel::item_array_t* pItems;
1370 gInventory.getDirectDescendentsOf(pFolder->getUUID(), pFolders, pItems);
1371
1372 if (pItems)
1373 {
1374 int cntObjects = 0;
1375 for (S32 idxItem = 0, cntItem = pItems->size(); idxItem < cntItem; idxItem++)
1376 {
1377 if (LLAssetType::AT_OBJECT == pItems->get(idxItem)->getType())
1378 cntObjects++;
1379 }
1380
1381 // Only rename if there's exactly 1 object/attachment inside of it [see LLFolderBridge::renameItem()]
1382 if ( (1 == cntObjects) && (NEW_CATEGORY_NAME == pFolder->getName()) )
1383 {
1384 std::string strName = ".(" + strAttachPt + ")";
1385
1386 pFolder->rename(strName);
1387 pFolder->updateServer(FALSE);
1388 gInventory.updateCategory(pFolder);
1389 //gInventory.notifyObservers(); <- done further down in LLVOAvatar::attachObject()
1390 }
1391 }
1392 }
1393 }
1394 }
1395}
1396
1397// Checked: 2009-05-31 (RLVa-0.2.0e) | Modified: RLVa-0.2.0e
1398void RlvHandler::onDetach(LLViewerJointAttachment* pAttachPt)
1399{
1400 LLViewerObject* pObj = pAttachPt->getObject();
1401 if (!pObj)
1402 {
1403 // LLVOAvatar::detachObject() should call us *before* calling LLViewerJointAttachment::removeObject()
1404 RLV_ERRS << "pAttachPt->getObject() == NULL" << LL_ENDL;
1405 return;
1406 }
1407 S32 idxAttachPt = getAttachPointIndex(pObj);
1408 if (0 == idxAttachPt)
1409 {
1410 // If we ended up here then the user "Drop"'ed this attachment (which we can't recover from)
1411 return;
1412 }
1413
1414 #ifdef RLV_DEBUG
1415 // TODO-RLV: when we're exiting (for whatever reason) app state won't always reflect it but
1416 // gAgent.getAvatarObject()->mAttachmentPoints will be NULL so anywhere we use
1417 // "get_if_there" will call through a NULL pointer. One case is "idling out" -> test the rest
1418 //LLViewerJointAttachment* pDbgAttachmentPt =
1419 // get_if_there(gAgent.getAvatarObject()->mAttachmentPoints, (S32)idxAttachPt, (LLViewerJointAttachment*)NULL);
1420 //RLV_INFOS << "Clean up for '" << pDbgAttachmentPt->getName() << "'" << LL_ENDL;
1421 #endif // RLV_DEBUG
1422
1423 // If the attachment was locked then we should reattach it (unless we're already trying to reattach to this attachment point)
1424 // (unless we forcefully detached it else in which case we do not want to reattach it)
1425 rlv_reattach_map_t::iterator itDetach = m_DetachPending.find(idxAttachPt);
1426 if (itDetach != m_DetachPending.end())
1427 {
1428 // RLVa-TODO: we should really be comparing item UUIDs but is it even possible to end up here and not have them match?
1429 m_DetachPending.erase(itDetach);
1430 }
1431 else if ( (!isDetachable(idxAttachPt)) && (m_AttachPending.find(idxAttachPt) == m_AttachPending.end()) )
1432 {
1433 // In an ideal world we would simply set up an LLInventoryObserver but there's no specific "asset updated" changed flag *sighs*
1434 // NOTE: attachments *always* know their "inventory item UUID" so we don't have to worry about fetched vs unfetched inventory
1435 m_AttachPending.insert(std::pair<S32, LLUUID>(idxAttachPt, pAttachPt->getItemID()));
1436 }
1437
1438 // We can't - easily - clean up child prims that never issued @detach=n but the GC will get those eventually
1439 rlv_detach_map_t::iterator itAttach = m_Attachments.find(idxAttachPt);
1440 while ( (itAttach != m_Attachments.upper_bound(idxAttachPt)) && (itAttach != m_Attachments.end()) )
1441 {
1442 LLViewerObject* pTempObj = gObjectList.findObject(itAttach->second);
1443 if ( (pTempObj) && (pTempObj->getRootEdit()->getID() == pObj->getID()) )
1444 {
1445 // Iterator points to the object (or to a child prim) so issue a clear on behalf of the object (there's the
1446 // possibility of going into an eternal loop, but that's ok since it indicates a bug in @clear that needs fixing)
1447 processCommand(itAttach->second, "clear", true);
1448
1449 itAttach = m_Attachments.find(idxAttachPt); // @clear will invalidate all iterators so we have to start anew
1450 }
1451 else
1452 {
1453 itAttach++;
1454 }
1455 }
1456
1457 // Clean up in case there was never a @detach=n (only works for the root prim - see above)
1458 rlv_object_map_t::iterator itObj = m_Objects.find(pObj->getID());
1459 if (itObj != m_Objects.end())
1460 processCommand(itObj->second.m_UUID, "clear", true);
1461}
1462
1463// Checked: 2009-07-30 (RLVa-1.0.1c) | Modified: RLVa-1.0.1c
1464bool RlvHandler::onGC()
1465{
1466 // We can't issue @clear on an object while we're in the loop below since that would invalidate our iterator
1467 // (and starting over would mean that some objects might get their "lookup misses" counter updated more than once per GC run)
1468 std::list<LLUUID> ExpiredObjects;
1469
1470 for (rlv_object_map_t::iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
1471 {
1472 LLViewerObject* pObj = gObjectList.findObject(itObj->second.m_UUID);
1473 if (!pObj)
1474 {
1475 // If the RlvObject once existed in the gObjectList and now doesn't then expire it right now
1476 // If the RlvObject never existed in the gObjectList and still doesn't then increase its "lookup misses" counter
1477 // but if that reaches 20 (we run every 30 seconds so that's about 10 minutes) then we'll expire it too
1478 if ( (itObj->second.m_fLookup) || (++itObj->second.m_nLookupMisses > 20) )
1479 ExpiredObjects.push_back(itObj->first);
1480 }
1481 else
1482 {
1483 // Check if this is an RlvObject instance who's object never existed in gObjectList before (rezzed prim in-world)
1484 // (it could also be an attachment that only just rezzed in but RlvHandler::onAttach() should be handling those)
1485 if ( (!itObj->second.m_fLookup) && (!pObj->isAttachment()) )
1486 itObj->second.m_fLookup = true;
1487 }
1488 }
1489
1490 for (std::list<LLUUID>::const_iterator itExpired = ExpiredObjects.begin(); itExpired != ExpiredObjects.end(); ++itExpired)
1491 {
1492 #ifdef RLV_DEBUG
1493 RLV_INFOS << "Garbage collecting " << *itExpired << LL_ENDL;
1494 #endif // RLV_DEBUG
1495
1496 processCommand(*itExpired, "clear", true);
1497 }
1498
1499 return (0 != m_Objects.size()); // GC will kill itself if it has nothing to do
1500}
1501
1502// Checked: 2009-08-08 (RLVa-1.0.1g) | Modified: RLVa-1.0.1g
1503void RlvHandler::onSavedAssetIntoInventory(const LLUUID& idItem)
1504{
1505 for (rlv_reattach_map_t::iterator itAttach = m_AttachPending.begin(); itAttach != m_AttachPending.end(); ++itAttach)
1506 {
1507 if (idItem == itAttach->second)
1508 {
1509 RLV_INFOS << "Reattaching " << idItem.asString() << " to " << itAttach->first << LL_ENDL;
1510
1511 #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
1512 LLAttachmentRezAction* rez_action = new LLAttachmentRezAction;
1513 rez_action->mItemID = itAttach->second;
1514 rez_action->mAttachPt = itAttach->first;
1515
1516 confirm_replace_attachment_rez(0/*YES*/, (void*)rez_action); // (Will call delete on rez_action)
1517 #else // Version: 1.23.4
1518 LLSD payload;
1519 payload["item_id"] = itAttach->second;
1520 payload["attachment_point"] = itAttach->first;
1521
1522 LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/);
1523 #endif
1524 }
1525 }
1526}
1527
1528// ============================================================================
1529// String/chat censoring functions
1530//
1531
1532// LL must have included an strlen for UTF8 *somewhere* but I can't seem to find it so this one is home grown
1533size_t utf8str_strlen(const std::string& utf8)
1534{
1535 const char* pUTF8 = utf8.c_str(); size_t length = 0;
1536 for (int idx = 0, cnt = utf8.length(); idx < cnt ;idx++)
1537 {
1538 // We're looking for characters that don't start with 10 as their high bits
1539 if ((pUTF8[idx] & 0xC0) != 0x80)
1540 length++;
1541 }
1542 return length;
1543}
1544
1545// TODO-RLV: works, but more testing won't hurt
1546std::string utf8str_chtruncate(const std::string& utf8, size_t length)
1547{
1548 if (0 == length)
1549 return std::string();
1550 if (utf8.length() <= length)
1551 return utf8;
1552
1553 const char* pUTF8 = utf8.c_str(); int idx = 0;
1554 while ( (pUTF8[idx]) && (length > 0) )
1555 {
1556 // We're looking for characters that don't start with 10 as their high bits
1557 if ((pUTF8[idx] & 0xC0) != 0x80)
1558 length--;
1559 idx++;
1560 }
1561
1562 return utf8.substr(0, idx);
1563}
1564
1565// Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
1566void RlvHandler::filterChat(std::string& strUTF8Text, bool fFilterEmote) const
1567{
1568 if (strUTF8Text.empty())
1569 return;
1570
1571 if (rlvIsEmote(strUTF8Text)) // Check if it's an emote
1572 {
1573 if (fFilterEmote) // Emote filtering depends on fFilterEmote
1574 {
1575 if ( (strUTF8Text.find_first_of("\"()*=^_?~") != -1) ||
1576 (strUTF8Text.find(" -") != -1) || (strUTF8Text.find("- ") != -1) || (strUTF8Text.find("''") != -1) )
1577 {
1578 strUTF8Text = "..."; // Emote contains illegal character (or character sequence)
1579 }
1580 else if (!hasBehaviour("emote"))
1581 {
1582 int idx = strUTF8Text.find('.'); // Truncate at 20 characters or at the dot (whichever is shorter)
1583 strUTF8Text = utf8str_chtruncate(strUTF8Text, ( (idx > 0) && (idx < 20) ) ? idx + 1 : 20);
1584 }
1585 }
1586 }
1587 else if (strUTF8Text[0] == '/') // Not an emote, but starts with a '/'
1588 {
1589 if (utf8str_strlen(strUTF8Text) > 7) // Allow as long if it's 6 characters or less
1590 strUTF8Text = "...";
1591 }
1592 else if ((strUTF8Text.length() < 4) || (strUTF8Text.compare(0, 2, "((")) || (strUTF8Text.compare(strUTF8Text.length() - 2, 2, "))")))
1593 {
1594 strUTF8Text = "..."; // Regular chat (not OOC)
1595 }
1596}
1597
1598// Checked: 2009-07-04 (RLVa-1.0.0a) | Modified: RLVa-1.0.0a
1599void RlvHandler::filterLocation(std::string& strUTF8Text) const
1600{
1601 // TODO-RLVa: if either the region or parcel name is a simple word such as "a" or "the" then confusion will ensue?
1602 // -> not sure how you would go about preventing this though :|...
1603
1604 // Filter any mention of the surrounding region names
1605 LLWorld::region_list_t regions = LLWorld::getInstance()->getRegionList();
1606 for (LLWorld::region_list_t::const_iterator itRegion = regions.begin(); itRegion != regions.end(); ++itRegion)
1607 rlvStringReplace(strUTF8Text, (*itRegion)->getName(), rlv_handler_t::cstrHiddenRegion);
1608
1609 // Filter any mention of the parcel name
1610 LLViewerParcelMgr* pParcelMgr = LLViewerParcelMgr::getInstance();
1611 if (pParcelMgr)
1612 rlvStringReplace(strUTF8Text, pParcelMgr->getAgentParcelName(), rlv_handler_t::cstrHiddenParcel);
1613}
1614
1615void RlvHandler::filterNames(std::string& strUTF8Text) const
1616{
1617 std::string strFirstName, strLastName, strName;
1618
1619 // TODO-RLV: make this a bit more efficient (ie people with a large draw distance will have a load of active regions)
1620 // -> the cost of multi string matching them all at once seems to be about the same as calling rlvStringReplace
1621 // twice so that would be a tremendous gain (and we'd get first name and word matching for free)
1622 #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
1623 for (LLWorld::region_list_t::const_iterator itRegion = LLWorld::getInstance()->getRegionList().begin();
1624 itRegion != LLWorld::getInstance()->getRegionList().end(); ++itRegion)
1625 {
1626 LLViewerRegion* pRegion = *itRegion;
1627
1628 for (S32 idxAgent = 0, cntAgent = pRegion->mMapAvatars.count(); idxAgent < cntAgent; idxAgent++)
1629 {
1630 // LLCacheName::getName() will add the UUID to the lookup queue if we don't know it yet
1631 if (gCacheName->getName(pRegion->mMapAvatarIDs.get(idxAgent), strFirstName, strLastName))
1632 {
1633 strName = strFirstName + " " + strLastName;
1634
1635 rlvStringReplace(strUTF8Text, strName, getAnonym(strName));
1636 }
1637 }
1638 }
1639 #else // Version: trunk
1640 // TODO-RLV: should restrict this to a certain radius (probably 1-2 sim range?)
1641 std::vector<LLUUID> idAgents;
1642 LLWorld::getInstance()->getAvatars(&idAgents, NULL);
1643
1644 for (int idxAgent = 0, cntAgent = idAgents.size(); idxAgent < cntAgent; idxAgent++)
1645 {
1646 // LLCacheName::getName() will add the UUID to the lookup queue if we don't know it yet
1647 if (gCacheName->getName(idAgents[idxAgent], strFirstName, strLastName))
1648 {
1649 strName = strFirstName + " " + strLastName;
1650
1651 rlvStringReplace(strUTF8Text, strName, getAnonym(strName));
1652 }
1653 }
1654 #endif
1655}
1656
1657const std::string& RlvHandler::getAnonym(const std::string& strName) const
1658{
1659 const char* pszName = strName.c_str();
1660 U32 nHash = 0;
1661
1662 // Test with 11,264 SL names showed a 3.33% - 3.82% occurance for each so we *should* get a very even spread
1663 for (int idx = 0, cnt = strName.length(); idx < cnt; idx++)
1664 nHash += pszName[idx];
1665
1666 return cstrAnonyms[nHash % 28];
1667}
1668
1669// Checked: 2009-07-07 (RLVa-1.0.0d) | Modified: RLVa-0.2.2a
1670bool RlvHandler::redirectChatOrEmote(const std::string& strUTF8Text) const
1671{
1672 // Sanity check - @redirchat only for chat and @rediremote only for emotes
1673 bool fIsEmote = rlvIsEmote(strUTF8Text);
1674 if ( ((!fIsEmote) && (!hasBehaviour(RLV_BHVR_REDIRCHAT))) || ((fIsEmote) && (!hasBehaviour(RLV_BHVR_REDIREMOTE))) )
1675 return false;
1676
1677 if (!fIsEmote)
1678 {
1679 std::string strText = strUTF8Text;
1680 filterChat(strText, true);
1681 if (strText != "...")
1682 return false; // @sendchat wouldn't filter it so @redirchat won't redirect it either
1683 }
1684
1685 bool fSendChannel = hasBehaviour("sendchannel");
1686 for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
1687 {
1688 for (rlv_command_list_t::const_iterator itCmd = itObj->second.m_Commands.begin(),
1689 endCmd = itObj->second.m_Commands.end(); itCmd != endCmd; ++itCmd)
1690 {
1691 if ( ( ((!fIsEmote) && (RLV_BHVR_REDIRCHAT == itCmd->getBehaviourType())) ||
1692 ((fIsEmote) && (RLV_BHVR_REDIREMOTE == itCmd->getBehaviourType())) ) &&
1693 ( (!fSendChannel) || (hasBehaviour("sendchannel", itCmd->getOption())) ) )
1694 {
1695 rlvSendChatReply(itCmd->getOption(), strUTF8Text);
1696 }
1697 }
1698 }
1699 return true;
1700}
1701
1702// ============================================================================
1703// Public service functions (called by the outside world or by extension handlers)
1704//
1705
1706BOOL RlvHandler::isAgentNearby(const LLUUID& uuid) const
1707{
1708 #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
1709 for (LLWorld::region_list_t::const_iterator itRegion = LLWorld::getInstance()->getRegionList().begin();
1710 itRegion != LLWorld::getInstance()->getRegionList().end(); ++itRegion)
1711 {
1712 LLViewerRegion* pRegion = *itRegion;
1713
1714 for (S32 idxAgent = 0, cntAgent = pRegion->mMapAvatars.count(); idxAgent < cntAgent; idxAgent++)
1715 if (pRegion->mMapAvatarIDs.get(idxAgent) == uuid)
1716 return TRUE;
1717 }
1718 #else // Version: trunk
1719 // TODO-RLV: rewrite this to fit trunk, but still need the radius limited to a sane range
1720 std::vector<LLUUID> idAgents;
1721 LLWorld::getInstance()->getAvatars(&idAgents, NULL);
1722
1723 for (int idxAgent = 0, cntAgent = idAgents.size(); idxAgent < cntAgent; idxAgent++)
1724 {
1725 if (idAgents[idxAgent] == uuid)
1726 return TRUE;
1727 }
1728 #endif
1729 return FALSE;
1730}
1731
1732// ============================================================================
1733// General purpose inventory functions
1734//
1735
1736// Checked: 2009-07-12 (RLVa-1.0.0h)
1737class RlvSharedRootFetcher : public LLInventoryFetchDescendentsObserver
1738{
1739public:
1740 RlvSharedRootFetcher() {}
1741
1742 virtual void done()
1743 {
1744 RLV_INFOS << "Shared folders fetch completed" << LL_ENDL;
1745 RlvHandler::m_fFetchComplete = TRUE;
1746
1747 gInventory.removeObserver(this);
1748 delete this;
1749 }
1750};
1751
1752// Checked: 2009-07-12 (RLVa-1.0.0h)
1753void RlvHandler::fetchSharedInventory()
1754{
1755 // Sanity check - don't fetch if we're already fetching, or if we don't have a shared root
1756 LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
1757 if ( (m_fFetchStarted) || (!pRlvRoot) )
1758 return;
1759
1760 // Grab all the folders under the shared root
1761 LLInventoryModel::cat_array_t folders;
1762 LLInventoryModel::item_array_t items;
1763 gInventory.collectDescendents(pRlvRoot->getUUID(), folders, items, FALSE);
1764
1765 /*
1766 * Add them to the "to fetch" list
1767 */
1768 LLInventoryFetchDescendentsObserver::folder_ref_t fetchFolders;
1769
1770 fetchFolders.push_back(pRlvRoot->getUUID());
1771 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
1772 fetchFolders.push_back(folders.get(idxFolder)->getUUID());
1773
1774 /*
1775 * Now fetch them all in one go
1776 */
1777 RlvSharedRootFetcher* fetcher = new RlvSharedRootFetcher;
1778
1779 RLV_INFOS << "Starting fetch of " << fetchFolders.size() << " shared folders" << LL_ENDL;
1780 fetcher->fetchDescendents(fetchFolders);
1781
1782 if (fetcher->isEverythingComplete())
1783 fetcher->done();
1784 else
1785 gInventory.addObserver(fetcher);
1786}
1787
1788bool RlvHandler::findSharedFolders(const std::string& strCriteria, LLInventoryModel::cat_array_t& folders) const
1789{
1790 // Sanity check - can't do anything without a shared root
1791 LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
1792 if (!pRlvRoot)
1793 return false;
1794
1795 folders.clear();
1796 LLInventoryModel::item_array_t items;
1797 RlvCriteriaCategoryCollector functor(strCriteria);
1798 gInventory.collectDescendentsIf(pRlvRoot->getUUID(), folders, items, FALSE, functor);
1799
1800 return (folders.count() != 0);
1801}
1802
1803// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0e
1804LLViewerInventoryCategory* RlvHandler::getSharedRoot()
1805{
1806 if (gInventory.isInventoryUsable())
1807 {
1808 LLInventoryModel::cat_array_t* pFolders;
1809 LLInventoryModel::item_array_t* pItems;
1810 gInventory.getDirectDescendentsOf(gAgent.getInventoryRootID(), pFolders, pItems);
1811 if (pFolders)
1812 {
1813 // NOTE: we might have multiple #RLV folders so we'll just go with the first one we come across
1814 LLViewerInventoryCategory* pFolder;
1815 for (S32 idxFolder = 0, cntFolder = pFolders->count(); idxFolder < cntFolder; idxFolder++)
1816 {
1817 if ( ((pFolder = pFolders->get(idxFolder)) != NULL) && (RlvHandler::cstrSharedRoot == pFolder->getName()) )
1818 return pFolder;
1819 }
1820 }
1821 }
1822 return NULL;
1823}
1824
1825// Checked: 2009-07-28 (RLVa-1.0.1a) | Modified: RLVa-1.0.1a
1826LLViewerInventoryCategory* RlvHandler::getSharedFolder(const LLUUID& idParent, const std::string& strFolderName) const
1827{
1828 LLInventoryModel::cat_array_t* pFolders;
1829 LLInventoryModel::item_array_t* pItems;
1830 gInventory.getDirectDescendentsOf(idParent, pFolders, pItems);
1831 if ( (!pFolders) || (strFolderName.empty()) )
1832 return NULL;
1833
1834 // If we can't find an exact match then we'll settle for a "contains" match
1835 LLViewerInventoryCategory* pPartial = NULL;
1836
1837 //LLStringUtil::toLower(strFolderName); <- everything was already converted to lower case before
1838
1839 std::string strName;
1840 for (S32 idxFolder = 0, cntFolder = pFolders->count(); idxFolder < cntFolder; idxFolder++)
1841 {
1842 LLViewerInventoryCategory* pFolder = pFolders->get(idxFolder);
1843
1844 strName = pFolder->getName();
1845 if (strName.empty())
1846 continue;
1847 LLStringUtil::toLower(strName);
1848
1849 if (strFolderName == strName)
1850 return pFolder; // Found an exact match, no need to keep on going
1851 else if ( (!pPartial) && (RLV_FOLDER_PREFIX_HIDDEN != strName[0]) && (strName.find(strFolderName) != std::string::npos) )
1852 pPartial = pFolder; // Found a partial (non-hidden) match, but we might still find an exact one (first partial match wins)
1853 }
1854
1855 return pPartial;
1856}
1857
1858// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0e
1859LLViewerInventoryCategory* RlvHandler::getSharedFolder(const std::string& strPath) const
1860{
1861 // Sanity check - no shared root => no shared folder
1862 LLViewerInventoryCategory* pRlvRoot = getSharedRoot(), *pFolder = pRlvRoot;
1863 if (!pRlvRoot)
1864 return NULL;
1865
1866 // Walk the path (starting at the root)
1867 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
1868 boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
1869 tokenizer tokens(strPath, sep);
1870 for (tokenizer::iterator itToken = tokens.begin(); itToken != tokens.end(); ++itToken)
1871 {
1872 pFolder = getSharedFolder(pFolder->getUUID(), *itToken);
1873 if (!pFolder)
1874 return NULL; // No such folder
1875 }
1876
1877 return pFolder; // If strPath was empty or just a bunch of //// then: pFolder == pRlvRoot
1878}
1879
1880// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0g
1881std::string RlvHandler::getSharedPath(const LLViewerInventoryCategory* pFolder) const
1882{
1883 LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
1884 // Sanity check - no shared root or no folder => no path
1885 if ( (!pRlvRoot) || (!pFolder) || (pRlvRoot->getUUID() == pFolder->getUUID()) )
1886 return std::string();
1887
1888 const LLUUID& idRLV = pRlvRoot->getUUID();
1889 const LLUUID& idRoot = gAgent.getInventoryRootID();
1890 std::string strPath;
1891
1892 // Walk up the tree until we reach the top
1893 while (pFolder)
1894 {
1895 strPath = "/" + pFolder->getName() + strPath;
1896
1897 const LLUUID& idParent = pFolder->getParentUUID();
1898 if (idRLV == idParent) // Reached the shared root, we're done
1899 break;
1900 else if (idRoot == idParent) // We reached the agent's inventory root (indicative of a logic error elsewhere)
1901 {
1902 RLV_ERRS << "Reached agent's inventory root while building path for shared folder" << LL_ENDL;
1903 return std::string();
1904 }
1905 else
1906 pFolder = gInventory.getCategory(idParent);
1907 }
1908
1909 return strPath.erase(0, 1);
1910}
1911
1912// ============================================================================
1913// Composite folders
1914//
1915
1916#ifdef RLV_EXPERIMENTAL_COMPOSITES
1917 // Checked:
1918 bool RlvHandler::getCompositeInfo(const LLInventoryCategory* pFolder, std::string* pstrName) const
1919 {
1920 if (pFolder)
1921 {
1922 // Composite folder naming: ^\.?[Folder]
1923 const std::string& cstrFolder = pFolder->getName();
1924 int idxStart = cstrFolder.find('['), idxEnd = cstrFolder.find(']', idxStart);
1925 if ( ((0 == idxStart) || (1 == idxStart)) && (idxEnd - idxStart > 1) )
1926 {
1927 if (pstrName)
1928 pstrName->assign(cstrFolder.substr(idxStart + 1, idxEnd - idxStart - 1));
1929 return true;
1930 }
1931 }
1932 return false;
1933 }
1934
1935 // Checked:
1936 bool RlvHandler::getCompositeInfo(const LLUUID& idItem, std::string* pstrName, LLViewerInventoryCategory** ppFolder) const
1937 {
1938 LLViewerInventoryCategory* pRlvRoot; LLViewerInventoryItem* pItem;
1939
1940 if ( (idItem.notNull()) && ((pRlvRoot = getSharedRoot()) != NULL) &&
1941 (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) && ((pItem = gInventory.getItem(idItem)) != NULL) )
1942 {
1943 // We know it's an item in a folder under the shared root...
1944 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
1945 if (getAttachPoint(pFolder, true))
1946 {
1947 // ... but it could be named ".(attachpt)" in which case we need its parent
1948 pFolder = gInventory.getCategory(pFolder->getParentUUID());
1949 }
1950
1951 if ( (pFolder) && (getCompositeInfo(pFolder, pstrName)) )
1952 {
1953 if (ppFolder)
1954 *ppFolder = pFolder;
1955 return true;
1956 }
1957 }
1958 return false;
1959 }
1960#endif // RLV_EXPERIMENTAL_COMPOSITES
1961
1962#ifdef RLV_EXPERIMENTAL_COMPOSITE_FOLDING
1963 // Checked:
1964 inline bool RlvHandler::isHiddenCompositeItem(const LLUUID& idItem, const std::string& cstrItemType) const
1965 {
1966 // An item that's part of a composite folder will be hidden from @getoutfit and @getattach if:
1967 // (1) the composite name specifies either a wearable layer or an attachment point
1968 // (2) the specified wearable layer or attachment point is worn and resides in the folder
1969 // (3) cstrItemType isn't the specified wearable layer or attach point
1970 //
1971 // Example: #RLV/Separates/Shoes/ChiChi Pumps/.[shoes] with items: "Shoe Base", "Shoe (left foot)" and "Shoe (right foot)"
1972 // -> as long as "Shoe Base" is worn, @getattach should not reflect "left foot", nor "right foot"
1973 std::string strComposite; LLViewerInventoryCategory* pFolder;
1974 EWearableType type; S32 idxAttachPt;
1975 if ( (getCompositeInfo(idItem, &strComposite, &pFolder)) && (cstrItemType != strComposite) )
1976 {
1977 LLUUID idCompositeItem;
1978 if ((type = LLWearable::typeNameToType(strComposite)) != WT_INVALID)
1979 {
1980 idCompositeItem = gAgent.getWearableItem(type);
1981 }
1982 else if ((idxAttachPt = getAttachPointIndex(strComposite, true)) != 0)
1983 {
1984 LLVOAvatar* pAvatar; LLViewerJointAttachment* pAttachmentPt;
1985 if ( ((pAvatar = gAgent.getAvatarObject()) != NULL) &&
1986 ((pAttachmentPt = get_if_there(pAvatar->mAttachmentPoints, idxAttachPt, (LLViewerJointAttachment*)NULL)) != NULL) )
1987 {
1988 idCompositeItem = pAttachmentPt->getItemID();
1989 }
1990 }
1991
1992 if ( (idCompositeItem.notNull()) && (gInventory.isObjectDescendentOf(idCompositeItem, pFolder->getUUID())) )
1993 return true;
1994 }
1995 return false;
1996 }
1997#endif // RLV_EXPERIMENTAL_COMPOSITEFOLDING
1998
1999#ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2000 // Checked:
2001 bool RlvHandler::canTakeOffComposite(const LLInventoryCategory* pFolder) const
2002 {
2003 if (!pFolder) // If there's no folder then there is nothing to take off
2004 return false;
2005
2006 LLInventoryModel::cat_array_t folders;
2007 LLInventoryModel::item_array_t items;
2008 RlvWearableItemCollector functor(pFolder->getUUID(), true, false);
2009
2010 // Grab a list of all the items @detachthis would be detaching/unwearing
2011 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor);
2012 if (!items.count())
2013 return false; // There are no wearable items in the folder so there is nothing to take off
2014
2015 LLViewerInventoryItem* pItem;
2016 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
2017 {
2018 pItem = items.get(idxItem);
2019
2020 switch (pItem->getType())
2021 {
2022 case LLAssetType::AT_CLOTHING:
2023 {
2024 LLWearable* pWearable = gAgent.getWearableFromWearableItem(pItem->getUUID());
2025 if ( (pWearable) && (!isRemovable(pWearable->getType())) )
2026 return false; // If one clothing layer in the composite folder is unremoveable then the entire folder is
2027 }
2028 break;
2029 case LLAssetType::AT_OBJECT:
2030 {
2031 LLVOAvatar* pAvatar; LLViewerObject* pObj;
2032 if ( ((pAvatar = gAgent.getAvatarObject()) != NULL) &&
2033 ((pObj = pAvatar->getWornAttachment(pItem->getUUID())) != NULL) && (!isDetachable(pObj)) )
2034 {
2035 return false; // If one attachment in the composite folder is undetachable then the entire folder is
2036 }
2037 }
2038 break;
2039 #ifdef LL_GNUC
2040 default:
2041 break;
2042 #endif // LL_GNUC
2043 }
2044 }
2045 return true;
2046 }
2047#endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2048
2049// ============================================================================
2050// Event handlers
2051//
2052
2053// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
2054void RlvHandler::onForceDetach(const LLUUID& idObj, const std::string& strOption) const
2055{
2056 U16 nParam;
2057 if (strOption.empty())
2058 {
2059 // Simulate right-click / Take Off > Detach All
2060 LLAgent::userRemoveAllAttachments(NULL);
2061 }
2062 else if (m_AttachLookup.getExactMatchParam(strOption, nParam))
2063 {
2064 // Simulate right-click / Take Off > Detach > ...
2065 LLVOAvatar* pAvatar; LLViewerJointAttachment* pAttachmentPt;
2066 if ( ((pAvatar = gAgent.getAvatarObject()) != NULL) && // Make sure we're actually wearing something on the attachment point
2067 ((pAttachmentPt = get_if_there(pAvatar->mAttachmentPoints, (S32)nParam, (LLViewerJointAttachment*)NULL)) != NULL) &&
2068 (isStrippable(pAttachmentPt->getItemID())) ) // ... and that it's not marked as "nostrip"
2069 {
2070 #ifdef RLV_EXPERIMENTAL_COMPOSITES
2071 // If we're stripping something that's part of a composite folder then we should @detachthis instead
2072 if (isCompositeDescendent(pAttachmentPt->getItemID()))
2073 {
2074 std::string strCmd = "detachthis:" + strOption + "=force";
2075 #ifdef RLV_DEBUG
2076 RLV_INFOS << "\t- '" << strOption << "' belongs to composite folder: @" << strCmd << LL_ENDL;
2077 #endif // RLV_DEBUG
2078 processForceCommand(idObj, RlvCommand(strCmd));
2079 }
2080 else
2081 #endif // RLV_EXPERIMENTAL_COMPOSITES
2082 {
2083 handle_detach_from_avatar(pAttachmentPt);
2084 }
2085 }
2086 }
2087 else
2088 {
2089 // Force detach single folder
2090 onForceWear(strOption, false, false);
2091 }
2092}
2093
2094// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
2095void RlvHandler::onForceRemOutfit(const LLUUID& idObj, const std::string& strOption) const
2096{
2097 EWearableType typeOption = LLWearable::typeNameToType(strOption), type;
2098 if ( (WT_INVALID == typeOption) && (!strOption.empty()) )
2099 return;
2100
2101 // Before we had an option and optionless branch, but with the addition of composites and nostrip there's less duplication this way
2102 for (int idxType = 0; idxType < WT_COUNT; idxType++)
2103 {
2104 type = (EWearableType)idxType;
2105
2106 // Only strip clothing (that's currently worn and not marked "nostrip")
2107 if ( (LLAssetType::AT_CLOTHING != LLWearable::typeToAssetType(type)) ||
2108 (!gAgent.getWearable(type)) || (!isStrippable(gAgent.getWearableItem(type))) )
2109 {
2110 continue;
2111 }
2112
2113 if ( (typeOption == type) || (strOption.empty()) )
2114 {
2115 #ifdef RLV_EXPERIMENTAL_COMPOSITES
2116 // If we're stripping something that's part of a composite folder then we should @detachthis instead
2117 if (isCompositeDescendent(gAgent.getWearableItem(type)))
2118 {
2119 std::string strCmd = "detachthis:" + LLWearable::typeToTypeName(type) + "=force";
2120 #ifdef RLV_DEBUG
2121 RLV_INFOS << "\t- '" << LLWearable::typeToTypeName(type) << "' is composite descendent: @" << strCmd << LL_ENDL;
2122 #endif // RLV_DEBUG
2123 processForceCommand(idObj, RlvCommand(strCmd));
2124 }
2125 else
2126 #endif // RLV_EXPERIMENTAL_COMPOSITES
2127 {
2128 gAgent.removeWearable(type);
2129 }
2130 }
2131 }
2132}
2133
2134// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0g
2135bool RlvHandler::onForceSit(const LLUUID& idObj, const std::string& strOption) const
2136{
2137 LLViewerObject* pObject = NULL; LLUUID idTarget(strOption);
2138 // Sanity checking - we need to know about the object and it should identify a prim/linkset
2139 if ( (idTarget.isNull()) || ((pObject = gObjectList.findObject(idTarget)) == NULL) || (LL_PCODE_VOLUME != pObject->getPCode()) )
2140 return false;
2141
2142 // Don't force sit if:
2143 // 1) currently sitting and prevented from standing up
2144 // 2) prevented from sitting
2145 // 3) @sittp=n restricted (except if @sittp=n was issued by the same prim that's currently force sitting the avie)
2146 if ( ( (hasBehaviour(RLV_BHVR_UNSIT)) && (gAgent.getAvatarObject()) && (gAgent.getAvatarObject()->mIsSitting) ) ||
2147 ( (hasBehaviour(RLV_BHVR_SIT)) ) ||
2148 ( (hasBehaviourExcept(RLV_BHVR_SITTP, idObj)) &&
2149 (dist_vec_squared(gAgent.getPositionGlobal(), pObject->getPositionGlobal()) > 1.5f * 1.5f) ))
2150 {
2151 return false;
2152 }
2153
2154 // Copy/paste from handle_sit_or_stand() [see http://wiki.secondlife.com/wiki/AgentRequestSit]
2155 gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit);
2156 gMessageSystem->nextBlockFast(_PREHASH_AgentData);
2157 gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
2158 gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
2159 gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
2160 gMessageSystem->addUUIDFast(_PREHASH_TargetID, pObject->mID);
2161 // Offset: "a rough position in local coordinates for the edge to sit on"
2162 // (we might not even be looking at the object so I don't think we can supply the offset to an edge)
2163 gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3::zero);
2164 pObject->getRegion()->sendReliableMessage();
2165
2166 return true;
2167}
2168
2169void RlvHandler::onForceWear(const std::string& strPath, bool fAttach, bool fMatchAll) const
2170{
2171 // See LLWearableBridge::wearOnAvatar(): don't wear anything until initial wearables are loaded, can destroy clothing items
2172 if (!gAgent.areWearablesLoaded())
2173 return;
2174
2175 LLViewerInventoryCategory* pFolder = getSharedFolder(strPath);
2176 if (!pFolder) // Folder not found = nothing to attach
2177 return;
2178
2179 LLInventoryModel::cat_array_t folders;
2180 LLInventoryModel::item_array_t items;
2181 RlvWearableItemCollector functor(pFolder->getUUID(), fAttach, fMatchAll);
2182
2183 // Grab a list of all the items we'll be wearing/attaching
2184 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor);
2185
2186 LLViewerInventoryItem* pItem;
2187 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
2188 {
2189 pItem = items.get(idxItem);
2190
2191 switch (pItem->getType())
2192 {
2193 case LLAssetType::AT_CLOTHING:
2194 case LLAssetType::AT_BODYPART:
2195 {
2196 LLWearable* pWearable = gAgent.getWearableFromWearableItem(pItem->getUUID());
2197
2198 #ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2199 // If we're already wearing something on this layer then we have to check if it isn't part of a composite
2200 // folder that has at least one unremovable item (in which case we can't wear or remove this item)
2201 LLViewerInventoryCategory* pCompositeFolder;
2202 if ( (!pWearable) || (!getCompositeInfo(pItem->getUUID(), NULL, &pCompositeFolder)) ||
2203 (canTakeOffComposite(pFolder)))
2204 {
2205 #endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2206 if (fAttach)
2207 {
2208 // Simulate wearing a clothing item from inventory (right click / "Wear")
2209 // LLWearableBridge::performAction() => LLWearableBridge::wearOnAvatar() => wear_inventory_item_on_avatar()
2210 wear_inventory_item_on_avatar(pItem);
2211 }
2212 else
2213 {
2214 if ( (pWearable) && (LLAssetType::AT_CLOTHING == pItem->getType()) )
2215 gAgent.removeWearable(pWearable->getType());
2216 }
2217 #ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2218 }
2219 #endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2220 }
2221 break;
2222 case LLAssetType::AT_OBJECT:
2223 {
2224 LLVOAvatar* pAvatar = gAgent.getAvatarObject();
2225 LLViewerObject* pObj;
2226
2227 #ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2228 // If we're already wearing something on this attach point then we have to check if it isn't part of a composite
2229 // folder that has at least one unremovable item (in which case we can't attach or detach this item)
2230 LLViewerInventoryCategory* pCompositeFolder;
2231 if ( (pAvatar) &&
2232 ( ((pObj = pAvatar->getWornAttachment(pItem->getUUID())) == NULL) ||
2233 (!getCompositeInfo(pItem->getUUID(), NULL, &pCompositeFolder)) || (canTakeOffComposite(pFolder)) ) )
2234 {
2235 #endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2236 if (fAttach)
2237 {
2238 // Simulate wearing an object to a specific attachment point (copy/paste to suppress replacement dialog)
2239 // LLAttachObject::handleEvent() => rez_attachment()
2240 LLViewerJointAttachment* pAttachPt = getAttachPoint(pItem, true);
2241 if ( (pAttachPt) && (isDetachable(pAttachPt->getObject())) )
2242 {
2243 #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11
2244 LLAttachmentRezAction* rez_action = new LLAttachmentRezAction;
2245 rez_action->mItemID = pItem->getUUID();
2246 rez_action->mAttachPt = getAttachPointIndex(pAttachPt->getName(), true);
2247
2248 confirm_replace_attachment_rez(0/*YES*/, (void*)rez_action); // (Will call delete on rez_action)
2249 #else // Version: 1.23.4
2250 LLSD payload;
2251 payload["item_id"] = pItem->getUUID();
2252 payload["attachment_point"] = getAttachPointIndex(pAttachPt->getName(), true);
2253
2254 LLNotifications::instance().forceResponse(
2255 LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/);
2256 #endif
2257 }
2258 }
2259 else
2260 {
2261 if ( (pAvatar) && ((pObj = pAvatar->getWornAttachment(pItem->getUUID())) != NULL) )
2262 {
2263 LLViewerJointAttachment* pAttachment = pAvatar->getTargetAttachmentPoint(pObj);
2264 if (pAttachment)
2265 handle_detach_from_avatar(pAttachment);
2266 }
2267 }
2268 #ifdef RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2269 }
2270 #endif // RLV_EXPERIMENTAL_COMPOSITE_LOCKING
2271 }
2272 break;
2273 #ifdef LL_GNUC
2274 default:
2275 break;
2276 #endif // LL_GNUC
2277 }
2278 }
2279}
2280
2281// Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0g
2282bool RlvHandler::onGetPath(const LLUUID &uuid, const std::string& strOption, std::string& strReply) const
2283{
2284 // Sanity check - no need to go through all this trouble if we don't have a shared root
2285 LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
2286 if (!pRlvRoot)
2287 return false;
2288
2289 LLUUID idItem;
2290
2291 // <option> can be a clothing layer
2292 EWearableType layerType = LLWearable::typeNameToType(strOption);
2293 if (WT_INVALID != layerType)
2294 {
2295 idItem = gAgent.getWearableItem(layerType);
2296 }
2297 else
2298 {
2299 LLViewerJointAttachment* pAttachPt = NULL;
2300
2301 // ... or it can be empty
2302 if (strOption.empty())
2303 {
2304 // (in which case we act on the object that issued the command)
2305 LLViewerObject* pObj = gObjectList.findObject(uuid);
2306 if ( (pObj) && (pObj->isAttachment()) && (gAgent.getAvatarObject()) )
2307 pAttachPt = gAgent.getAvatarObject()->getTargetAttachmentPoint(pObj);
2308 }
2309 else
2310 {
2311 // ... or it can specify an attach point
2312 pAttachPt = getAttachPoint(strOption, true);
2313 }
2314
2315 // If we found something, get its inventory item UUID
2316 if (pAttachPt)
2317 idItem = pAttachPt->getItemID();
2318 }
2319
2320 // If we found something and it's under the shared root, then get its path
2321 if ( (!idItem.isNull()) && (gInventory.isObjectDescendentOf(idItem, pRlvRoot->getUUID())) )
2322 {
2323 LLInventoryItem* pItem = gInventory.getItem(idItem);
2324 if (pItem)
2325 {
2326 // ... unless the containing folder's name specifies an attach point (or nostrip) in which case we need its parent
2327 LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
2328 #ifdef RLV_EXTENSION_FLAG_NOSTRIP
2329 if ( (getAttachPoint(pFolder, true)) || (pFolder->getName() == ".("RLV_FOLDER_FLAG_NOSTRIP")") )
2330 #else
2331 if (getAttachPoint(pFolder, true))
2332 #endif // RLV_EXTENSION_FLAG_NOSTRIP
2333 strReply = getSharedPath(pFolder->getParentUUID());
2334 else
2335 strReply = getSharedPath(pFolder);
2336 }
2337 }
2338 return !strReply.empty();
2339}
2340
2341struct rlv_wear_info { U32 cntWorn, cntTotal, cntChildWorn, cntChildTotal; };
2342
2343// Checked: 2009-05-30 (RLVa-0.2.0e) | Modified: RLVa-0.2.0e
2344void RlvHandler::onGetInvWorn(const std::string& strPath, std::string& strReply) const
2345{
2346 // Sanity check - getAvatarObject() can't be NULL [see rlvIsWearingItem()] and the folder should exist and not be hidden
2347 LLViewerInventoryCategory* pFolder = getSharedFolder(strPath);
2348 if ((!gAgent.getAvatarObject()) || (!pFolder) || (pFolder->getName().empty()) || (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName()[0]))
2349 return;
2350
2351 // Collect everything @attachall would be attaching
2352 LLInventoryModel::cat_array_t folders;
2353 LLInventoryModel::item_array_t items;
2354 RlvWearableItemCollector functor(pFolder->getUUID(), true, true);
2355 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor);
2356
2357 rlv_wear_info wi = {0};
2358
2359 // Add all the folders to a lookup map
2360 std::map<LLUUID, rlv_wear_info> mapFolders;
2361 mapFolders.insert(std::pair<LLUUID, rlv_wear_info>(pFolder->getUUID(), wi));
2362 for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
2363 mapFolders.insert(std::pair<LLUUID, rlv_wear_info>(folders.get(idxFolder)->getUUID(), wi));
2364
2365 // Iterate over all the found items
2366 LLViewerInventoryItem* pItem; std::map<LLUUID, rlv_wear_info>::iterator itFolder;
2367 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
2368 {
2369 pItem = items.get(idxItem);
2370
2371 // The "folded parent" is the folder this item should be considered a direct descendent of (may or may not match actual parent)
2372 const LLUUID& idParent = functor.getFoldedParent(pItem->getParentUUID());
2373
2374 // Walk up the tree: sooner or later one of the parents will be a folder in the map
2375 LLViewerInventoryCategory* pParent = gInventory.getCategory(idParent);
2376 while ( (itFolder = mapFolders.find(pParent->getUUID())) == mapFolders.end() )
2377 pParent = gInventory.getCategory(pParent->getParentUUID());
2378
2379 U32 &cntWorn = (idParent == pParent->getUUID()) ? itFolder->second.cntWorn : itFolder->second.cntChildWorn,
2380 &cntTotal = (idParent == pParent->getUUID()) ? itFolder->second.cntTotal : itFolder->second.cntChildTotal;
2381
2382 if (rlvIsWearingItem(pItem))
2383 cntWorn++;
2384 cntTotal++;
2385 }
2386
2387 // Extract the result for the main folder
2388 itFolder = mapFolders.find(pFolder->getUUID());
2389 wi.cntWorn = itFolder->second.cntWorn;
2390 wi.cntTotal = itFolder->second.cntTotal;
2391 mapFolders.erase(itFolder);
2392
2393 // Build the result for each child folder
2394 for (itFolder = mapFolders.begin(); itFolder != mapFolders.end(); ++itFolder)
2395 {
2396 rlv_wear_info& wiFolder = itFolder->second;
2397
2398 wi.cntChildWorn += wiFolder.cntWorn + wiFolder.cntChildWorn;
2399 wi.cntChildTotal += wiFolder.cntTotal + wiFolder.cntChildTotal;
2400
2401 strReply += llformat(",%s|%d%d", gInventory.getCategory(itFolder->first)->getName().c_str(),
2402 (0 == wiFolder.cntTotal) ? 0 : (0 == wiFolder.cntWorn) ? 1 : (wiFolder.cntWorn != wiFolder.cntTotal) ? 2 : 3,
2403 (0 == wiFolder.cntChildTotal) ? 0 : (0 == wiFolder.cntChildWorn) ? 1 : (wiFolder.cntChildWorn != wiFolder.cntChildTotal) ? 2 : 3
2404 );
2405 }
2406
2407 // Now just prepend the root and done
2408 strReply = llformat("|%d%d", (0 == wi.cntTotal) ? 0 : (0 == wi.cntWorn) ? 1 : (wi.cntWorn != wi.cntTotal) ? 2 : 3,
2409 (0 == wi.cntChildTotal) ? 0 : (0 == wi.cntChildWorn) ? 1 : (wi.cntChildWorn != wi.cntChildTotal) ? 2: 3) + strReply;
2410}
2411
2412// (In case anyone cares: this isn't used in public builds)
2413bool RlvHandler::getWornInfo(const LLInventoryCategory* pFolder, U8& wiFolder, U8& wiChildren) const
2414{
2415 // Sanity check - getAvatarObject() can't be NULL [see rlvIsWearingItem()] and the folder should exist and not be hidden
2416 if ((!gAgent.getAvatarObject()) || (!pFolder) || (pFolder->getName().empty()) || (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName()[0]))
2417 return false;
2418
2419 LLInventoryModel::cat_array_t folders;
2420 LLInventoryModel::item_array_t items;
2421 RlvWearableItemCollector functor(pFolder->getUUID(), true, true);
2422 gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, functor);
2423
2424 LLViewerInventoryItem* pItem;
2425 U32 cntWorn = 0, cntTotal = 0, cntChildWorn = 0, cntChildTotal = 0;
2426 for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
2427 {
2428 pItem = items.get(idxItem);
2429
2430 bool fDirectDescendent = (pFolder->getUUID() == functor.getFoldedParent(pItem->getParentUUID()));
2431 U32 &refWorn = (fDirectDescendent) ? cntWorn : cntChildWorn, &refTotal = (fDirectDescendent) ? cntTotal : cntChildTotal;
2432
2433 if (rlvIsWearingItem(pItem))
2434 refWorn++;
2435 refTotal++;
2436 }
2437
2438 wiFolder = (0 == cntTotal + cntChildTotal) ? 0 : (0 == cntWorn + cntChildWorn) ? 1 :
2439 (cntWorn + cntChildWorn != cntTotal + cntChildTotal) ? 2 : 3;
2440 wiChildren = (0 == cntChildTotal) ? 0 : (0 == cntChildWorn) ? 1 : (cntChildWorn != cntChildTotal) ? 2 : 3;
2441
2442 return true;
2443}
2444
2445// ============================================================================
2446// Initialization helper functions
2447//
2448
2449BOOL RlvHandler::setEnabled(BOOL fEnable)
2450{
2451 if (m_fEnabled == fEnable)
2452 return fEnable;
2453
2454 if (fEnable)
2455 {
2456 if (gSavedSettings.controlExists(RLV_SETTING_NOSETENV))
2457 fNoSetEnv = gSavedSettings.getBOOL(RLV_SETTING_NOSETENV);
2458 if (gSavedSettings.controlExists(RLV_SETTING_ENABLELEGACYNAMING))
2459 fLegacyNaming = gSavedSettings.getBOOL(RLV_SETTING_ENABLELEGACYNAMING);
2460 if (gSavedSettings.controlExists(RLV_SETTING_SHOWNAMETAGS))
2461 RlvSettings::fShowNameTags = gSavedSettings.getBOOL(RLV_SETTING_SHOWNAMETAGS);
2462
2463 RlvCommand::initLookupTable();
2464 gRlvHandler.addObserver(new RlvExtGetSet());
2465
2466 if (LLStartUp::getStartupState() >= STATE_CLEANUP)
2467 fetchSharedInventory();
2468
2469 m_fEnabled = TRUE;
2470 }
2471 else if (canDisable())
2472 {
2473 #ifdef RLV_DEBUG
2474 RLV_INFOS << "Disabling RLV:" << LL_ENDL;
2475 #endif // RLV_DEBUG
2476
2477 gRlvHandler.clearState();
2478
2479 #ifdef RLV_DEBUG
2480 RLV_INFOS << "\t--> RLV disabled" << LL_ENDL;
2481 #endif // RLV_DEBUG
2482
2483 m_fEnabled = FALSE;
2484 }
2485
2486 #ifdef RLV_ADVANCED_MENU
2487 // RELEASE-RLVa: LL defines CLIENT_MENU_NAME but we can't get to it from here so we need to keep those two in sync manually
2488 LLMenuGL* pClientMenu = NULL;
2489 if ( (gMenuBarView) && ((pClientMenu = gMenuBarView->getChildMenuByName("Advanced", FALSE)) != NULL) )
2490 {
2491 pClientMenu->setItemVisible("RLVa", m_fEnabled);
2492 pClientMenu->setItemEnabled("RLVa", m_fEnabled);
2493 }
2494 #endif // RLV_ADVANCED_MENU
2495
2496 return m_fEnabled; // Return enabled/disabled state
2497}
2498
2499BOOL RlvHandler::canDisable()
2500{
2501 return TRUE;
2502}
2503
2504void RlvHandler::clearState()
2505{
2506 // TODO-RLVa: should restore all RLV controlled debug variables to their defaults
2507
2508 // Issue @clear on behalf of every object that has a currently active RLV restriction (even if it's just an exception)
2509 LLUUID idObj; LLViewerObject* pObj; bool fDetachable;
2510 while (m_Objects.size())
2511 {
2512 idObj = m_Objects.begin()->first; // Need a copy since after @clear the data it points to will no longer exist
2513 fDetachable = ((pObj = gObjectList.findObject(idObj)) != NULL) ? isDetachable(pObj) : true;
2514
2515 processCommand(idObj, "clear", false);
2516 if (!fDetachable)
2517 processCommand(idObj, "detachme=force", false);
2518 }
2519
2520 // Sanity check - these should all be empty after we issue @clear on the last object
2521 if ( (!m_Objects.empty()) || !(m_Exceptions.empty()) || (!m_Attachments.empty()) )
2522 {
2523 RLV_ERRS << "Object, exception or attachment map not empty after clearing state!" << LL_ENDL;
2524 m_Objects.clear();
2525 m_Exceptions.clear();
2526 m_Attachments.clear();
2527 }
2528
2529 // These all need manual clearing
2530 memset(m_LayersAdd, 0, sizeof(S16) * WT_COUNT);
2531 memset(m_LayersRem, 0, sizeof(S16) * WT_COUNT);
2532 memset(m_Behaviours, 0, sizeof(S16) * RLV_BHVR_COUNT);
2533 m_AttachPending.clear();
2534 m_Emitter.clearObservers(); // <- calls delete on all active observers
2535
2536 // Clear dynamically allocated memory
2537 if (m_pGCTimer)
2538 {
2539 delete m_pGCTimer;
2540 m_pGCTimer = NULL;
2541 }
2542 if (m_pWLSnapshot)
2543 {
2544 delete m_pWLSnapshot;
2545 m_pWLSnapshot = NULL;
2546 }
2547}
2548
2549// ============================================================================