diff options
Diffstat (limited to '')
-rw-r--r-- | linden/indra/newview/rlvhandler.cpp | 2549 |
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 | ||
39 | extern const char* NEW_CATEGORY_NAME; | ||
40 | |||
41 | // ============================================================================ | ||
42 | // Static variable initialization | ||
43 | // | ||
44 | |||
45 | BOOL RlvHandler::m_fEnabled = FALSE; | ||
46 | BOOL RlvHandler::fNoSetEnv = FALSE; | ||
47 | BOOL RlvHandler::fLegacyNaming = FALSE; | ||
48 | BOOL RlvHandler::m_fFetchStarted = FALSE; | ||
49 | BOOL RlvHandler::m_fFetchComplete = FALSE; | ||
50 | RlvMultiStringSearch RlvHandler::m_AttachLookup; | ||
51 | |||
52 | const std::string RlvHandler::cstrSharedRoot = RLV_ROOT_FOLDER; | ||
53 | |||
54 | // Keep these consistent with regular RLV | ||
55 | const std::string RlvHandler::cstrBlockedRecvIM = "*** IM blocked by your viewer"; | ||
56 | const std::string RlvHandler::cstrBlockedSendIM = "*** IM blocked by sender's viewer"; | ||
57 | const std::string RlvHandler::cstrHidden = "(Hidden)"; | ||
58 | const std::string RlvHandler::cstrHiddenParcel = "(Hidden parcel)"; | ||
59 | const std::string RlvHandler::cstrHiddenRegion = "(Hidden region)"; | ||
60 | const std::string RlvHandler::cstrMsgRecvIM = | ||
61 | "The Resident you messaged is prevented from reading your instant messages at the moment, please try again later."; | ||
62 | const std::string RlvHandler::cstrMsgTpLure = | ||
63 | "The Resident you invited is prevented from accepting teleport offers. Please try again later."; | ||
64 | |||
65 | const 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 | |||
73 | rlv_handler_t gRlvHandler; | ||
74 | |||
75 | // ============================================================================ | ||
76 | // Helper functions | ||
77 | // | ||
78 | |||
79 | // Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.0e | ||
80 | inline 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 | ||
93 | static 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 | ||
118 | RlvHandler::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 | ||
128 | RlvHandler::~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 | ||
140 | inline 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 | ||
148 | LLViewerJointAttachment* 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 | ||
166 | LLViewerJointAttachment* 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 | ||
196 | S32 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 | ||
212 | LLViewerJointAttachment* 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 | |||
241 | bool 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 | |||
257 | bool 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 | ||
264 | bool 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 | ||
278 | bool 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 | |||
344 | bool 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 | |||
352 | bool 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 | |||
360 | bool 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) | ||
373 | void 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) | ||
381 | void 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) | ||
389 | void 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: | ||
399 | BOOL 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 | |||
528 | BOOL 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 | ||
760 | void 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 | |||
770 | BOOL 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 | |||
918 | BOOL 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) | ||
1019 | BOOL 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 | |||
1243 | void 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 | ||
1270 | void 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 | ||
1398 | void 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 | ||
1464 | bool 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 | ||
1503 | void 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 | ||
1533 | size_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 | ||
1546 | std::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 | ||
1566 | void 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 | ||
1599 | void 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 | |||
1615 | void 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 | |||
1657 | const 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 | ||
1670 | bool 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 | |||
1706 | BOOL 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) | ||
1737 | class RlvSharedRootFetcher : public LLInventoryFetchDescendentsObserver | ||
1738 | { | ||
1739 | public: | ||
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) | ||
1753 | void 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 | |||
1788 | bool 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 | ||
1804 | LLViewerInventoryCategory* 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 | ||
1826 | LLViewerInventoryCategory* 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 | ||
1859 | LLViewerInventoryCategory* 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 | ||
1881 | std::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 | ||
2054 | void 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 | ||
2095 | void 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 | ||
2135 | bool 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 | |||
2169 | void 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 | ||
2282 | bool 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 | |||
2341 | struct rlv_wear_info { U32 cntWorn, cntTotal, cntChildWorn, cntChildTotal; }; | ||
2342 | |||
2343 | // Checked: 2009-05-30 (RLVa-0.2.0e) | Modified: RLVa-0.2.0e | ||
2344 | void 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) | ||
2413 | bool 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 | |||
2449 | BOOL 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 | |||
2499 | BOOL RlvHandler::canDisable() | ||
2500 | { | ||
2501 | return TRUE; | ||
2502 | } | ||
2503 | |||
2504 | void 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 | // ============================================================================ | ||