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