aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/rlvhandler.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/rlvhandler.h445
1 files changed, 445 insertions, 0 deletions
diff --git a/linden/indra/newview/rlvhandler.h b/linden/indra/newview/rlvhandler.h
new file mode 100644
index 0000000..5295a72
--- /dev/null
+++ b/linden/indra/newview/rlvhandler.h
@@ -0,0 +1,445 @@
1#ifndef RLV_HANDLER_H
2#define RLV_HANDLER_H
3
4#include "llagentconstants.h"
5#include "llappviewer.h"
6#include "llformat.h"
7#include "llversionviewer.h"
8#include "llviewerjointattachment.h"
9#include "llviewerobject.h"
10#include "llwearable.h"
11
12#include "rlvhelper.h"
13#include "rlvevent.h"
14#include "rlvmultistringsearch.h"
15
16// ============================================================================
17
18typedef std::map<LLUUID, RlvObject> rlv_object_map_t;
19typedef std::multimap<S32, LLUUID> rlv_detach_map_t;
20typedef std::map<S32, LLUUID> rlv_reattach_map_t;
21typedef std::multimap<ERlvBehaviour, RlvException> rlv_exception_map_t;
22
23class RlvHandler
24{
25public:
26 RlvHandler();
27 ~RlvHandler();
28
29 // --------------------------------
30
31 /*
32 * Attachment point helper functions
33 */
34public:
35 // Returns a pointer to the attachment point for a supplied parameter
36 LLViewerJointAttachment* getAttachPoint(const std::string& strText, bool fExact) const;
37 LLViewerJointAttachment* getAttachPoint(const LLInventoryItem* pItem, bool fStrict) const;
38 LLViewerJointAttachment* getAttachPoint(const LLInventoryCategory* pFolder, bool fStrict) const;
39 LLViewerJointAttachment* getAttachPointLegacy(const LLInventoryCategory* pFolder) const;
40 S32 getAttachPointIndex(std::string strText, bool fExact) const;
41 S32 getAttachPointIndex(LLViewerObject* pObj) const;
42 S32 getAttachPointIndex(const LLViewerJointAttachment* pObj) const;
43 bool hasAttachPointName(const LLInventoryItem* pItem, bool fStrict) const;
44
45 // --------------------------------
46
47 /*
48 * Rule checking functions
49 */
50 // NOTE: - to check @detach=n -> hasLockedAttachment() / hasLockedHUD() / isDetachable()
51 // - to check exceptions -> isException()
52 // - to check @addoutfit=n -> isWearable()
53 // - to check @remoutfit=n -> isRemovable()
54public:
55 // Returns TRUE is at least one object contains the specified behaviour (and optional option)
56 bool hasBehaviour(ERlvBehaviour eBehaviour) const { return (eBehaviour < RLV_BHVR_COUNT) ? (0 != m_Behaviours[eBehaviour]) : false; }
57 bool hasBehaviour(ERlvBehaviour eBehaviour, const std::string& strOption) const;
58 // Returns TRUE if at least one object (except the specified one) contains the specified behaviour (and optional option)
59 bool hasBehaviourExcept(ERlvBehaviour eBehaviour, const LLUUID& idObj) const;
60 bool hasBehaviourExcept(ERlvBehaviour eBehaviour, const std::string& strOption, const LLUUID& idObj) const;
61
62 // Returns TRUE if there is at least 1 non-detachable attachment
63 bool hasLockedAttachment() const { return (0 != m_Attachments.size()); }
64 // Returns TRUE if there is at least 1 non-detachable HUD attachment
65 bool hasLockedHUD() const;
66 // Returns TRUE if the specified attachment point is detachable
67 bool isDetachable(S32 idxAttachPt) const { return (idxAttachPt) && (m_Attachments.find(idxAttachPt) == m_Attachments.end()); }
68 bool isDetachable(const LLInventoryItem* pItem) const;
69 bool isDetachable(LLViewerJointAttachment* pAttachPt) const;
70 bool isDetachable(LLViewerObject* pObj) const;
71 // Returns TRUE if the specified attachment point is set non-detachable by anything other than pObj (or one of its children)
72 bool isDetachableExcept(S32 idxAttachPt, LLViewerObject* pObj) const;
73 // Marks the specified attachment point as (non-)detachable (return value indicates success ; used by unit tests)
74 bool setDetachable(S32 idxAttachPt, const LLUUID& idRlvObj, bool fDetachable);
75 bool setDetachable(LLViewerObject* pObj, const LLUUID& idRlvObj, bool fDetachable);
76
77 // Adds or removes an exception for the specified behaviour
78 void addException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& varOption);
79 void removeException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& varOption);
80 // Returns TRUE if the specified option was added as an exception for the specified behaviour
81 bool isException(ERlvBehaviour eBhvr, const RlvExceptionOption& varOption, ERlvExceptionCheck typeCheck = RLV_CHECK_DEFAULT) const;
82 // Returns TRUE if the specified behaviour should behave "permissive" (rather than "strict"/"secure")
83 bool isPermissive(ERlvBehaviour eBhvr) const;
84
85 // Returns TRUE if the specified layer is removable (use hasBehaviour(RLV_BHVR_REMOUTFIT) for the general case)
86 bool isRemovable(EWearableType type) const { return (type < WT_COUNT) ? (0 == m_LayersRem[type]) : true; }
87 // Returns TRUE if the specified layer is not remoutfit blocked by any object (except the one specified by UUID)
88 bool isRemovableExcept(EWearableType type, const LLUUID& idObj) const;
89 // Returns TRUE if the inventory item is strippable by @detach or @remoutfit
90 bool isStrippable(const LLUUID& idItem) const;
91 // Returns TRUE if the specified layer is wearable (use hasBehaviour(RLV_BHVR_ADDOUTFIT) for the general case)
92 bool isWearable(EWearableType type) const { return (type < WT_COUNT) ? (0 == m_LayersAdd[type]) : true; }
93
94 // Returns TRUE if the composite folder doesn't contain any "locked" items
95 bool canTakeOffComposite(const LLInventoryCategory* pFolder) const;
96 // Returns TRUE if the folder is a composite folder and optionally returns the name
97 bool getCompositeInfo(const LLInventoryCategory* pFolder, std::string* pstrName) const;
98 // Returns TRUE if the inventory item belongs to a composite folder and optionally returns the name and composite folder
99 bool getCompositeInfo(const LLUUID& idItem, std::string* pstrName, LLViewerInventoryCategory** ppFolder) const;
100 // Returns TRUE if the folder is a composite folder
101 bool isCompositeFolder(const LLInventoryCategory* pFolder) const;
102 // Returns TRUE if the inventory item belongs to a composite folder
103 bool isCompositeDescendent(const LLUUID& idItem) const;
104 // Returns TRUE if the inventory item is part of a folded composite folder and should be hidden from @getoufit or @getattach
105 bool isHiddenCompositeItem(const LLUUID& idItem, const std::string& strItemType) const;
106
107 // --------------------------------
108
109 /*
110 * Helper functions
111 */
112public:
113 // Accessors
114 bool getCanCancelTp() const { return m_fCanCancelTp; } // @accepttp and @tpto
115 void setCanCancelTp(bool fAllow) { m_fCanCancelTp = fAllow; } // @accepttp and @tpto
116
117 // Command specific helper functions
118 bool canShowHoverText(LLViewerObject* pObj) const; // @showhovertext* command family
119 void filterChat(std::string& strUTF8Text, bool fFilterEmote) const; // @sendchat, @recvchat and @redirchat
120 void filterLocation(std::string& strUTF8Text) const; // @showloc
121 void filterNames(std::string& strUTF8Text) const; // @shownames
122 const std::string& getAnonym(const std::string& strName) const; // @shownames
123 std::string getVersionString() const; // @version
124 std::string getVersionNumString() const; // @versionnum
125 BOOL isAgentNearby(const LLUUID& uuid) const; // @shownames
126 bool redirectChatOrEmote(const std::string& strUTF8Test) const; // @redirchat and @rediremote
127
128 // Command processing helper functions
129 BOOL processCommand(const LLUUID& idObj, const std::string& strCommand, bool fFromObj);
130 void processRetainedCommands();
131 void retainCommand(const std::string& strObj, const LLUUID& idObj, const std::string& strCmd);
132
133 // Returns a pointer to the currently executing command (do *not* save this pointer)
134 const RlvCommand* getCurrentCommand() const { return m_pCurCommand; }
135 // Returns the UUID of the object we're currently executing a command for
136 const LLUUID& getCurrentObject() const { return m_idCurObject; }
137
138 // Initialization
139 static BOOL canDisable();
140 static BOOL isEnabled() { return m_fEnabled; }
141 static void initLookupTables();
142 static BOOL setEnabled(BOOL fEnable);
143protected:
144 void clearState();
145
146 // --------------------------------
147
148 /*
149 * Inventory related functions
150 */
151public:
152 // Starts a fetch of everything under the shared root (if there is one)
153 static void fetchSharedInventory();
154 // Returns the path of the supplied folder (relative to the shared root)
155 std::string getSharedPath(const LLViewerInventoryCategory* pFolder) const;
156 std::string getSharedPath(const LLUUID& idFolder) const;
157 // Returns a pointer to the shared root folder (if there is one)
158 static LLViewerInventoryCategory* getSharedRoot();
159 // A "folded folder" is a folder whose items logically belong to the grandparent rather than the parent
160 bool isFoldedFolder(const LLInventoryCategory* pFolder, bool fAttach) const;
161 bool isFoldedFolderLegacy(const LLInventoryCategory* pFolder, bool fAttach) const;
162protected:
163 // Find all folders that match a supplied criteria (clears the supplied array)
164 bool findSharedFolders(const std::string& strCriteria, LLInventoryModel::cat_array_t& folders) const;
165
166 // Returns a subfolder of idParent that starts with name (exact match > partial match)
167 LLViewerInventoryCategory* getSharedFolder(const LLUUID& idParent, const std::string& strName) const;
168 // Looks up a folder from a path (relative to the shared root)
169 LLViewerInventoryCategory* getSharedFolder(const std::string& strPath) const;
170
171 bool getWornInfo(const LLInventoryCategory* pFolder, U8& wiFolder, U8& wiChildren) const;
172
173 // --------------------------------
174
175 /*
176 * Event handling (forwards to registered observers if we don't handle the command)
177 */
178public:
179 BOOL addObserver(RlvObserver* pObserver) { return m_Emitter.addObserver(pObserver); }
180 BOOL removeObserver(RlvObserver* pObserver) { return m_Emitter.remObserver(pObserver); }
181 void addBehaviourObserver(RlvBehaviourObserver* pBhvrObserver);
182 void removeBehaviourObserver(RlvBehaviourObserver* pBhvrObserver);
183 void notifyBehaviourObservers(const RlvCommand& rlvCmd, bool fInternal);
184
185 // Externally invoked event handlers
186 void onAttach(LLViewerJointAttachment* pAttachPt, bool fFullyLoaded); // LLVOAvatar::attachObject()
187 void onDetach(LLViewerJointAttachment* pAttachPt); // LLVOAvatar::detachObject()
188 bool onGC(); // RlvGCTimer::tick()
189 void onSavedAssetIntoInventory(const LLUUID& idItem); // LLInventoryModel::processSaveAssetIntoInventory()
190protected:
191 BOOL processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd);
192 BOOL processRemoveCommand(const LLUUID& uuid, const RlvCommand& rlvCmd);
193 BOOL processClearCommand(const LLUUID& idObj, const RlvCommand& rlvCmd);
194 BOOL processReplyCommand(const LLUUID& uuid, const RlvCommand& rlvCmd) const;
195 BOOL processForceCommand(const LLUUID& uuid, const RlvCommand& rlvCmd) const;
196
197 // Command handlers (exist for no other reason than to keep the length of the processXXX functions down)
198 void onForceDetach(const LLUUID& idObj, const std::string& strOption) const;
199 void onForceRemOutfit(const LLUUID& idObj, const std::string& strOption) const;
200 bool onForceSit(const LLUUID& uuid, const std::string& strOption) const;
201 void onForceWear(const std::string& strPath, bool fAttach, bool fMatchAll) const;
202 bool onGetPath(const LLUUID& uuid, const std::string& strOption, std::string& strReply) const;
203 void onGetInvWorn(const std::string& strPath, std::string &strReply) const;
204
205 // --------------------------------
206
207 /*
208 * Member variables
209 */
210public:
211 static BOOL fNoSetEnv;
212 static BOOL fLegacyNaming;
213
214 static const std::string cstrSharedRoot; // Name of the shared root folder
215 static const std::string cstrBlockedRecvIM; // Stand-in text for incoming IM when recvim restricted
216 static const std::string cstrBlockedSendIM; // Stand-in text for outgoing IM when sendim restricted
217 static const std::string cstrHidden; // General purpose "this was censored" text
218 static const std::string cstrHiddenParcel;
219 static const std::string cstrHiddenRegion;
220 static const std::string cstrMsgRecvIM; // Message sent to IM sender when sendim restricted
221 static const std::string cstrMsgTpLure; // Message sent to tplure sender when tplure restricted
222 static const std::string cstrAnonyms[28];
223protected:
224 rlv_object_map_t m_Objects; // Map of objects that have active restrictions (by UUID)
225 rlv_exception_map_t m_Exceptions; // Map of UUIDs that are exempt from the associated ERlvBehaviour
226 rlv_detach_map_t m_Attachments; // Map of locked attachments (attachment point index -> object that issued @detach=n)
227 S16 m_LayersAdd[WT_COUNT]; // Array of locked layers (reference counted)
228 S16 m_LayersRem[WT_COUNT]; // Array of locked layers (reference counted)
229 S16 m_Behaviours[RLV_BHVR_COUNT];
230
231 rlv_retained_list_t m_Retained;
232 rlv_reattach_map_t m_AttachPending;
233 rlv_reattach_map_t m_DetachPending;
234 RlvGCTimer* m_pGCTimer;
235 RlvWLSnapshot* m_pWLSnapshot;
236
237 RlvCommand* m_pCurCommand; // Convenience (see @tpto)
238 LLUUID m_idCurObject; // Convenience (see @tpto)
239
240 mutable RlvEventEmitter<RlvObserver> m_Emitter;
241 mutable std::list<RlvBehaviourObserver*> m_BhvrObservers;
242 RlvBehaviourNotifyObserver* m_pBhvrNotify;
243
244 static BOOL m_fEnabled; // Use setEnabled() to toggle this
245 static BOOL m_fFetchStarted; // TRUE if we fired off an inventory fetch
246 static BOOL m_fFetchComplete; // TRUE if everything was fetched
247 static RlvMultiStringSearch m_AttachLookup; // Lookup table for attachment names (lower case)
248
249 bool m_fCanCancelTp;
250
251 friend class RlvSharedRootFetcher; // Fetcher needs access to m_fFetchComplete
252 friend class RlvGCTimer; // Timer clear its own point at destruction
253
254 // --------------------------------
255
256 /*
257 * Internal access functions used by unit tests
258 */
259public:
260 const rlv_object_map_t* getObjectMap() const { return &m_Objects; }
261 const rlv_exception_map_t* getExceptionMap() const { return &m_Exceptions; }
262 const rlv_detach_map_t* getDetachMap() const { return &m_Attachments; }
263 #ifdef RLV_DEBUG_TESTS
264 const S16* getAddLayers() const { return m_LayersAdd; }
265 const S16* getRemLayers() const { return m_LayersRem; }
266 const S16* getBehaviours() const { return m_Behaviours; }
267 const rlv_retained_list_t* getRetainedList() const { return &m_Retained; }
268 #endif // RLV_DEBUG_TESTS
269};
270
271typedef RlvHandler rlv_handler_t;
272extern rlv_handler_t gRlvHandler;
273
274// ============================================================================
275// Inlined member functions
276//
277
278// Checked: 2009-10-04 (RLVa-1.0.4a) | Modified: RLVa-1.0.4a
279inline void RlvHandler::addException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& varOption)
280{
281 m_Exceptions.insert(std::pair<ERlvBehaviour, RlvException>(eBhvr, RlvException(idObj, eBhvr, varOption)));
282}
283
284// Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
285inline bool RlvHandler::canShowHoverText(LLViewerObject *pObj) const
286{
287 return ( (!pObj) || (LL_PCODE_VOLUME != pObj->getPCode()) ||
288 !( (hasBehaviour(RLV_BHVR_SHOWHOVERTEXTALL)) ||
289 ( (hasBehaviour(RLV_BHVR_SHOWHOVERTEXTWORLD)) && (!pObj->isHUDAttachment()) ) ||
290 ( (hasBehaviour(RLV_BHVR_SHOWHOVERTEXTHUD)) && (pObj->isHUDAttachment()) ) ||
291 (isException(RLV_BHVR_SHOWHOVERTEXT, pObj->getID(), RLV_CHECK_PERMISSIVE)) ) );
292}
293
294// Checked: 2009-05-23 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
295inline S32 RlvHandler::getAttachPointIndex(std::string strText, bool fExact) const
296{
297 U16 nParam; RlvMultiStringSearchMatch match;
298 LLStringUtil::toLower(strText);
299 return (fExact) ? ((m_AttachLookup.getExactMatchParam(strText, nParam)) ? nParam : 0)
300 : ((m_AttachLookup.findLast(strText, match)) ? match.nParam : 0);
301}
302
303// Checked: 2009-05-23 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
304inline S32 RlvHandler::getAttachPointIndex(LLViewerObject* pObj) const
305{
306 return (pObj) ? ATTACHMENT_ID_FROM_STATE(pObj->getState()) : 0;
307}
308
309// Checked: 2009-06-02 (RLVa-0.2.0g)
310inline std::string RlvHandler::getSharedPath(const LLUUID& idFolder) const
311{
312 return getSharedPath(gInventory.getCategory(idFolder)); // getSharedPath() has a NULL pointer check so this is safe
313}
314
315// Checked: 2009-06-07 (RLVa-0.2.1c)
316inline std::string RlvHandler::getVersionString() const
317{
318 return llformat("RestrainedLife viewer v%d.%d.%d (%s %d.%d.%d.%d - RLVa %d.%d.%d)",
319 RLV_VERSION_MAJOR, RLV_VERSION_MINOR, RLV_VERSION_PATCH,
320 LLAppViewer::instance()->getSecondLifeTitle().c_str(), LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VERSION_BUILD,
321 RLVa_VERSION_MAJOR, RLVa_VERSION_MINOR, RLVa_VERSION_PATCH);
322}
323
324// Checked: 2009-10-04 (RLVa-1.0.4b) | Added: RLVa-1.0.4b
325inline std::string RlvHandler::getVersionNumString() const
326{
327 return llformat("%d%02d%02d%02d", RLV_VERSION_MAJOR, RLV_VERSION_MINOR, RLV_VERSION_PATCH, RLV_VERSION_BUILD);
328}
329
330// Checked: 2009-05-23 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
331inline bool RlvHandler::hasAttachPointName(const LLInventoryItem *pItem, bool fStrict) const
332{
333 return (getAttachPoint(pItem, fStrict) != NULL); // getAttachPoint() has a NULL pointer check so this is safe
334}
335
336// Checked:
337inline bool RlvHandler::hasBehaviour(ERlvBehaviour eBehaviour, const std::string& strOption) const
338{
339 return hasBehaviourExcept(eBehaviour, strOption, LLUUID::null);
340}
341
342// Checked:
343inline bool RlvHandler::hasBehaviourExcept(ERlvBehaviour eBehaviour, const LLUUID& idObj) const
344{
345 return hasBehaviourExcept(eBehaviour, std::string(), idObj);
346}
347
348#ifdef RLV_EXPERIMENTAL_COMPOSITES
349 // Checked:
350 inline bool RlvHandler::isCompositeFolder(const LLInventoryCategory* pFolder) const
351 {
352 return getCompositeInfo(pFolder, NULL);
353 }
354
355 // Checked:
356 inline bool RlvHandler::isCompositeDescendent(const LLUUID& idItem) const
357 {
358 return getCompositeInfo(idItem, NULL, NULL);
359 }
360#endif // RLV_EXPERIMENTAL_COMPOSITES
361
362// Checked: 2009-09-08 (RLVa-1.0.2c) | Added: RLVa-1.0.2c
363inline bool RlvHandler::isDetachable(LLViewerJointAttachment *pAttachPt) const
364{
365 // If there's an attached object it's faster to just use that; otherwise look up the attachment index because it might be locked empty
366 return (pAttachPt == NULL) ||
367 ( (pAttachPt->getObject() != NULL) && isDetachable(pAttachPt->getObject()) ) || (isDetachable(getAttachPointIndex(pAttachPt)));
368}
369
370// Checked: 2009-05-23 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
371inline bool RlvHandler::isDetachable(LLViewerObject* pObj) const
372{
373 return (pObj == NULL) || (!pObj->isAttachment()) || (isDetachable(getAttachPointIndex(pObj)));
374}
375
376inline bool RlvHandler::isPermissive(ERlvBehaviour eBhvr) const
377{
378 return (RlvCommand::hasStrictVariant(eBhvr))
379 ? !((hasBehaviour(RLV_BHVR_PERMISSIVE)) || (isException(RLV_BHVR_PERMISSIVE, eBhvr, RLV_CHECK_PERMISSIVE)))
380 : true;
381}
382
383// Checked: 2009-07-29 (RLVa-1.0.1b) | Added: RLVa-1.0.1b
384inline bool RlvHandler::isFoldedFolder(const LLInventoryCategory* pFolder, bool fAttach) const
385{
386 return
387 (
388 // .(<attachpt>) type folder (on detach we don't care about its children, but on attach there can only be 1 attachment)
389 ( (gRlvHandler.getAttachPoint(pFolder, true)) &&
390 ( (!fAttach) || (1 == rlvGetDirectDescendentsCount(pFolder, LLAssetType::AT_OBJECT))) )
391 #ifdef RLV_EXTENSION_FLAG_NOSTRIP
392 // .(nostrip) folder
393 || ( (pFolder) && (".("RLV_FOLDER_FLAG_NOSTRIP")" == pFolder->getName()) )
394 #endif // RLV_EXTENSION_FLAG_NOSTRIP
395 );
396}
397
398// Checked: 2009-05-23 (RLVa-0.2.0d) | Added: RLVa-0.2.0d
399inline bool RlvHandler::isRemovableExcept(EWearableType type, const LLUUID& idObj) const
400{
401 // NOTE: mind the bitwise OR rather than the logical OR!!
402 return (isRemovable(type)) || !( (hasBehaviourExcept(RLV_BHVR_REMOUTFIT, idObj)) |
403 (hasBehaviourExcept(RLV_BHVR_REMOUTFIT, LLWearable::typeToTypeName(type), idObj)) );
404}
405
406#ifndef RLV_EXTENSION_FLAG_NOSTRIP
407 // Checked: 2009-05-23 (RLVa-0.2.0d) | Added: RLVa-0.2.0d
408 bool RlvHandler::isStrippable(const LLUUID& idItem) const
409 {
410 return true;
411 }
412#endif // RLV_EXTENSION_FLAG_NOSTRIP
413
414// Checked: 2009-10-04 (RLVa-1.0.4a) | Modified: RLVa-1.0.4a
415inline void RlvHandler::removeException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& varOption)
416{
417 for (rlv_exception_map_t::iterator itException = m_Exceptions.lower_bound(eBhvr),
418 endException = m_Exceptions.upper_bound(eBhvr); itException != endException; ++itException)
419 {
420 if ( (itException->second.idObject == idObj) && (itException->second.varOption == varOption) )
421 {
422 m_Exceptions.erase(itException);
423 break;
424 }
425 }
426}
427
428// Checked: 2009-08-05 (RLVa-1.0.1e) | Modified: RLVa-1.0.1e
429inline void RlvHandler::retainCommand(const std::string& strObj, const LLUUID& idObj, const std::string& strCmd)
430{
431 #ifdef RLV_DEBUG
432 RLV_INFOS << "[" << idObj << "]: " << strCmd << " (retaining)" << LL_ENDL;
433 #endif // RLV_DEBUG
434 m_Retained.push_back(RlvRetainedCommand(strObj, idObj, strCmd));
435}
436
437// Checked: 2009-05-23 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
438inline bool RlvHandler::setDetachable(LLViewerObject* pObj, const LLUUID& idRlvObj, bool fDetachable)
439{
440 return setDetachable(getAttachPointIndex(pObj), idRlvObj, fDetachable); // getAttachPointIndex() has a NULL pointer check
441}
442
443// ============================================================================
444
445#endif // RLV_HANDLER_H