#include "llviewerprecompiledheaders.h" #include "llagent.h" #include "lldrawpoolalpha.h" #include "llfloaterbeacons.h" #include "llfloaterchat.h" #include "llfloaterdaycycle.h" #include "llfloaterenvsettings.h" #include "llfloatergodtools.h" #include "llfloaterland.h" #include "llfloatermap.h" #include "llfloaterregioninfo.h" #include "llfloatertools.h" #include "llfloaterwater.h" #include "llfloaterwindlight.h" #include "llfloaterworldmap.h" #include "llgesturemgr.h" #include "llinventoryview.h" #include "llstartup.h" #include "llviewermenu.h" #include "llviewermessage.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llviewerwindow.h" #include "llvoavatar.h" #include "llworld.h" #include "pipeline.h" #include "rlvhelper.h" #include "rlvevent.h" #include "rlvextensions.h" #include "rlvhandler.h" // Only defined in llinventorybridge.cpp #if RLV_TARGET < RLV_MAKE_TARGET(1, 23, 0) // Version: 1.22.11 #include "llinventorybridge.h" void confirm_replace_attachment_rez(S32 option, void* user_data); #endif // Only defined in llinventorymodel.cpp extern const char* NEW_CATEGORY_NAME; // ============================================================================ // Static variable initialization // BOOL RlvHandler::m_fEnabled = FALSE; BOOL RlvHandler::fNoSetEnv = FALSE; BOOL RlvHandler::fLegacyNaming = FALSE; BOOL RlvHandler::m_fFetchStarted = FALSE; BOOL RlvHandler::m_fFetchComplete = FALSE; RlvMultiStringSearch RlvHandler::m_AttachLookup; const std::string RlvHandler::cstrSharedRoot = RLV_ROOT_FOLDER; // Keep these consistent with regular RLV const std::string RlvHandler::cstrBlockedRecvIM = "*** IM blocked by your viewer"; const std::string RlvHandler::cstrBlockedSendIM = "*** IM blocked by sender's viewer"; const std::string RlvHandler::cstrHidden = "(Hidden)"; const std::string RlvHandler::cstrHiddenParcel = "(Hidden parcel)"; const std::string RlvHandler::cstrHiddenRegion = "(Hidden region)"; const std::string RlvHandler::cstrMsgRecvIM = "The Resident you messaged is prevented from reading your instant messages at the moment, please try again later."; const std::string RlvHandler::cstrMsgTpLure = "The Resident you invited is prevented from accepting teleport offers. Please try again later."; const std::string RlvHandler::cstrAnonyms[] = { "A resident", "This resident", "That resident", "An individual", "This individual", "That individual", "A person", "This person", "That person", "A stranger", "This stranger", "That stranger", "A human being", "This human being", "That human being", "An agent", "This agent", "That agent", "A soul", "This soul", "That soul", "Somebody", "Some people", "Someone", "Mysterious one", "An unknown being", "Unidentified one", "An unknown person" }; rlv_handler_t gRlvHandler; // ============================================================================ // Helper functions // // Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.0e inline bool rlvIsWearingItem(const LLInventoryItem* pItem) { return ((LLAssetType::AT_OBJECT == pItem->getType()) && (gAgent.getAvatarObject()->isWearingAttachment(pItem->getUUID()))) || ((LLAssetType::AT_GESTURE == pItem->getType()) && (gGestureManager.isGestureActive(pItem->getUUID()))) || (gAgent.isWearingItem(pItem->getUUID())); } // ============================================================================ // Command specific helper functions // // Checked: 2009-08-04 (RLVa-1.0.1d) | Added: RLVa-1.0.1d static bool rlvParseNotifyOption(const std::string& strOption, S32& nChannel, std::string& strFilter) { boost_tokenizer tokens(strOption, boost::char_separator(";", "", boost::keep_empty_tokens)); boost_tokenizer::iterator itTok = tokens.begin(); // Extract and sanity check the first token (required) which is the channel if ( (itTok == tokens.end()) || (!LLStringUtil::convertToS32(*itTok, nChannel)) || (!rlvIsValidChannel(nChannel)) ) return false; // Second token (optional) is the filter strFilter.clear(); if (++itTok != tokens.end()) { strFilter = *itTok; ++itTok; } return (itTok == tokens.end()); } // ============================================================================ // Constructor/destructor // // Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d RlvHandler::RlvHandler() : m_fCanCancelTp(false), m_idCurObject(LLUUID::null), m_pCurCommand(NULL), m_pGCTimer(NULL), m_pWLSnapshot(NULL), m_pBhvrNotify(NULL) { // Array auto-initialization to 0 is non-standard? (Compiler warning in VC-8.0) memset(m_LayersAdd, 0, sizeof(S16) * WT_COUNT); memset(m_LayersRem, 0, sizeof(S16) * WT_COUNT); memset(m_Behaviours, 0, sizeof(S16) * RLV_BHVR_COUNT); } // Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d RlvHandler::~RlvHandler() { //delete m_pGCTimer; // <- deletes itself delete m_pWLSnapshot; // <- delete on NULL is harmless delete m_pBhvrNotify; } // ============================================================================ // Attachment related functions // // Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d inline LLViewerJointAttachment* RlvHandler::getAttachPoint(const std::string& strText, bool fExact) const { LLVOAvatar* pAvatar = gAgent.getAvatarObject(); return (pAvatar) ? get_if_there(pAvatar->mAttachmentPoints, getAttachPointIndex(strText, fExact), (LLViewerJointAttachment*)NULL) : NULL; } // Checked: 2009-07-29 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b LLViewerJointAttachment* RlvHandler::getAttachPoint(const LLInventoryCategory* pFolder, bool /*fStrict*/) const { if (!pFolder) return NULL; // RLVa-1.0.1 added support for legacy matching (See http://rlva.catznip.com/blog/2009/07/attachment-point-naming-convention/) if (fLegacyNaming) return getAttachPointLegacy(pFolder); // Otherwise the only valid way to specify an attachment point in a folder name is: ^\.\(\s+attachpt\s+\) std::string::size_type idxMatch; std::string strAttachPt = rlvGetFirstParenthesisedText(pFolder->getName(), &idxMatch); LLStringUtil::trim(strAttachPt); return ( (1 == idxMatch) && (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName().at(0)) ) ? getAttachPoint(strAttachPt, true) : NULL; } // Checked: 2009-07-29 (RLVa-1.0.1b) | Modified: RLVa-1.0.1b LLViewerJointAttachment* RlvHandler::getAttachPoint(const LLInventoryItem* pItem, bool fStrict) const { // Sanity check - if it's not an object then it can't have an attachment point if ( (!pItem) || (LLAssetType::AT_OBJECT != pItem->getType()) ) return NULL; // The attachment point should be placed at the end of the item's name, surrounded by parenthesis // (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) std::string strAttachPt = rlvGetLastParenthesisedText(pItem->getName()); LLStringUtil::trim(strAttachPt); // If the item is modify : we look at the item's name first and only then at the containing folder // If the item is no modify: we look at the containing folder's name first and only then at the item itself LLViewerJointAttachment* pAttachPt; if (pItem->getPermissions().allowModifyBy(gAgent.getID())) { pAttachPt = (!strAttachPt.empty()) ? getAttachPoint(strAttachPt, true) : NULL; if (!pAttachPt) pAttachPt = getAttachPoint(gInventory.getCategory(pItem->getParentUUID()), fStrict); } else { pAttachPt = getAttachPoint(gInventory.getCategory(pItem->getParentUUID()), fStrict); if ( (!pAttachPt) && (!strAttachPt.empty()) ) pAttachPt = getAttachPoint(strAttachPt, true); } return pAttachPt; } // Checked: 2009-07-12 (RLVa-1.0.0h) | Added: RLVa-0.2.2a S32 RlvHandler::getAttachPointIndex(const LLViewerJointAttachment* pAttachPt) const { LLVOAvatar* pAvatar = gAgent.getAvatarObject(); if (pAvatar) { for (LLVOAvatar::attachment_map_t::const_iterator itAttach = pAvatar->mAttachmentPoints.begin(); itAttach != pAvatar->mAttachmentPoints.end(); ++itAttach) { if (itAttach->second == pAttachPt) return itAttach->first; } } return 0; } // Checked: 2009-07-29 (RLVa-1.0.1b) | Added: RLVa-1.0.1b LLViewerJointAttachment* RlvHandler::getAttachPointLegacy(const LLInventoryCategory* pFolder) const { // Hopefully some day this can just be deprecated (see http://rlva.catznip.com/blog/2009/07/attachment-point-naming-convention/) if ( (!pFolder) || (pFolder->getName().empty()) ) return NULL; // Check for a (...) block *somewhere* in the name std::string::size_type idxMatch; std::string strAttachPt = rlvGetFirstParenthesisedText(pFolder->getName(), &idxMatch); if (!strAttachPt.empty()) { // Could be "(attachpt)", ".(attachpt)" or "Folder name (attachpt)" if ( (0 != idxMatch) && ((1 != idxMatch) || (RLV_FOLDER_PREFIX_HIDDEN == pFolder->getName().at(0)) ) && // No '(' or '.(' start (idxMatch + strAttachPt.length() + 1 != pFolder->getName().length()) ) // or there's extra text { // It's definitely not one of the first two so assume it's the last form (in which case we need the last paranthesised block) strAttachPt = rlvGetLastParenthesisedText(pFolder->getName()); } } else { // There's no paranthesised block, but it could still be "attachpt" or ".attachpt" (just strip away the '.' from the last one) strAttachPt = pFolder->getName(); if (RLV_FOLDER_PREFIX_HIDDEN == strAttachPt[0]) strAttachPt.erase(0, 1); } return getAttachPoint(strAttachPt, true); } bool RlvHandler::hasLockedHUD() const { LLVOAvatar* pAvatar = gAgent.getAvatarObject(); if (!pAvatar) return false; LLViewerJointAttachment* pAttachPt; for (rlv_detach_map_t::const_iterator itAttachPt = m_Attachments.begin(); itAttachPt != m_Attachments.end(); ++itAttachPt) { pAttachPt = get_if_there(pAvatar->mAttachmentPoints, (S32)itAttachPt->first, (LLViewerJointAttachment*)NULL); if ( (pAttachPt) && (pAttachPt->getIsHUDAttachment()) ) return true; // At least one of our locked attachments is a HUD } return false; // None of our locked attachments is a HUD } bool RlvHandler::isDetachable(const LLInventoryItem* pItem) const { LLVOAvatar* pAvatar = gAgent.getAvatarObject(); return ( (pItem) && (pAvatar) ) ? isDetachable(pAvatar->getWornAttachment(pItem->getUUID())) : true; } // Checked: 2009-08-11 (RLVa-1.0.1h) | Added: RLVa-1.0.1h bool RlvHandler::isDetachableExcept(S32 idxAttachPt, LLViewerObject *pObj) const { // Loop over every object that marked the specific attachment point undetachable (but ignore pObj and any of its children) for (rlv_detach_map_t::const_iterator itAttach = m_Attachments.lower_bound(idxAttachPt), endAttach = m_Attachments.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach) { LLViewerObject* pTempObj = gObjectList.findObject(itAttach->second); if ( (!pTempObj) || (pTempObj->getRootEdit()->getID() != pObj->getID()) ) return false; } return true; } // Checked: 2009-05-31 (RLVa-0.2.0e) | Modified: RLVa-0.2.0e bool RlvHandler::setDetachable(S32 idxAttachPt, const LLUUID& idRlvObj, bool fDetachable) { // Sanity check - make sure it's an object we know about rlv_object_map_t::const_iterator itObj = m_Objects.find(idRlvObj); if ( (itObj == m_Objects.end()) || (!idxAttachPt) ) return false; // If (idxAttachPt) == 0 then: (pObj == NULL) || (pObj->isAttachment() == FALSE) if (!fDetachable) { // Sanity check - make sure it's not already marked undetachable by this object (NOTE: m_Attachments is a *multimap*, not a map) for (rlv_detach_map_t::const_iterator itAttach = m_Attachments.lower_bound(idxAttachPt), endAttach = m_Attachments.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach) { if (itObj->second.m_UUID == itAttach->second) return false; } m_Attachments.insert(std::pair(idxAttachPt, itObj->second.m_UUID)); return true; } else { // NOTE: m_Attachments is a *multimap*, not a map for (rlv_detach_map_t::iterator itAttach = m_Attachments.lower_bound(idxAttachPt), endAttach = m_Attachments.upper_bound(idxAttachPt); itAttach != endAttach; ++itAttach) { if (itObj->second.m_UUID == itAttach->second) { m_Attachments.erase(itAttach); return true; } } } return false; // Fall-through for (fDetachable == TRUE) - if the object wasn't undetachable then we consider it a failure } #ifdef RLV_EXTENSION_FLAG_NOSTRIP // Checked: 2009-05-26 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d bool RlvHandler::isStrippable(const LLUUID& idItem) const { // An item is exempt from @detach or @remoutfit if: // - its name contains "nostrip" (anywhere in the name) // - its parent folder contains "nostrip" (anywhere in the name) if (idItem.notNull()) { LLViewerInventoryItem* pItem = gInventory.getItem(idItem); if (pItem) { if (-1 != pItem->getName().find(RLV_FOLDER_FLAG_NOSTRIP)) return false; LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID()); if ( (pFolder) && (-1 != pFolder->getName().find(RLV_FOLDER_FLAG_NOSTRIP)) ) return false; } } return true; } #endif // RLV_EXTENSION_FLAG_NOSTRIP // ============================================================================ // Behaviour related functions // bool RlvHandler::hasBehaviourExcept(const std::string& strBehaviour, const LLUUID& idObj) const { for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj) if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(strBehaviour)) ) return true; return false; } bool RlvHandler::hasBehaviourExcept(ERlvBehaviour eBehaviour, const std::string& strOption, const LLUUID& idObj) const { for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj) if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(eBehaviour, strOption)) ) return true; return false; } bool RlvHandler::hasBehaviourExcept(const std::string& strBehaviour, const std::string& strOption, const LLUUID& idObj) const { for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj) if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(strBehaviour, strOption)) ) return true; return false; } // ============================================================================ // Command processing functions // // Checked: 2009-06-03 (RLVa-0.2.0h) void RlvHandler::addBehaviourObserver(RlvBehaviourObserver* pBhvrObserver) { std::list::iterator itBhvrObserver = std::find(m_BhvrObservers.begin(), m_BhvrObservers.end(), pBhvrObserver); if (itBhvrObserver == m_BhvrObservers.end()) m_BhvrObservers.push_back(pBhvrObserver); } // Checked: 2009-06-03 (RLVa-0.2.0h) void RlvHandler::removeBehaviourObserver(RlvBehaviourObserver* pBhvrObserver) { std::list::iterator itBhvrObserver = std::find(m_BhvrObservers.begin(), m_BhvrObservers.end(), pBhvrObserver); if (itBhvrObserver != m_BhvrObservers.end()) m_BhvrObservers.erase(itBhvrObserver); } // Checked: 2009-06-03 (RLVa-0.2.0h) void RlvHandler::notifyBehaviourObservers(const RlvCommand& rlvCmd, bool fInternal) { for (std::list::const_iterator itBhvrObserver = m_BhvrObservers.begin(); itBhvrObserver != m_BhvrObservers.end(); ++itBhvrObserver) { (*itBhvrObserver)->changed(rlvCmd, fInternal); } } // Checked: BOOL RlvHandler::processCommand(const LLUUID& uuid, const std::string& strCmd, bool fFromObj) { #ifdef RLV_DEBUG RLV_INFOS << "[" << uuid << "]: " << strCmd << LL_ENDL; #endif // RLV_DEBUG RlvCommand rlvCmd(strCmd); if (!rlvCmd.isValid()) { #ifdef RLV_DEBUG RLV_INFOS << "\t-> invalid command: " << strCmd << LL_ENDL; #endif // RLV_DEBUG return FALSE; } m_pCurCommand = &rlvCmd; m_idCurObject = uuid; BOOL fRet = FALSE; switch (rlvCmd.getParamType()) { case RLV_TYPE_ADD: // Checked: 2009-06-03 (RLVa-0.2.0h) | Modified: RLVa-0.2.0h { if ( (m_Behaviours[rlvCmd.getBehaviourType()]) && ( (RLV_BHVR_SETDEBUG == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETENV == rlvCmd.getBehaviourType()) ) ) { // Some restrictions can only be held by one single object to avoid deadlocks #ifdef RLV_DEBUG RLV_INFOS << "\t- " << rlvCmd.getBehaviour() << " is already set by another object => discarding" << LL_ENDL; #endif // RLV_DEBUG break; } rlv_object_map_t::iterator itObj = m_Objects.find(uuid); if (itObj != m_Objects.end()) { RlvObject& rlvObj = itObj->second; fRet = rlvObj.addCommand(rlvCmd); } else { RlvObject rlvObj(uuid); fRet = rlvObj.addCommand(rlvCmd); m_Objects.insert(std::pair(uuid, rlvObj)); } #ifdef RLV_DEBUG RLV_INFOS << "\t- " << ( (fRet) ? "adding behaviour" : "skipping duplicate") << LL_ENDL; #endif // RLV_DEBUG if (fRet) { // If FALSE then this was a duplicate, there's no need to handle those if (!m_pGCTimer) m_pGCTimer = new RlvGCTimer(); processAddCommand(uuid, rlvCmd); notifyBehaviourObservers(rlvCmd, !fFromObj); } } break; case RLV_TYPE_REMOVE: // Checked: { rlv_object_map_t::iterator itObj = m_Objects.find(uuid); if (itObj != m_Objects.end()) fRet = itObj->second.removeCommand(rlvCmd); #ifdef RLV_DEBUG RLV_INFOS << "\t- " << ( (fRet) ? "removing behaviour" : "skipping remove (unset behaviour or unknown object)") << LL_ENDL; #endif // RLV_DEBUG if (fRet) { // Don't handle non-sensical removes processRemoveCommand(uuid, rlvCmd); notifyBehaviourObservers(rlvCmd, !fFromObj); if (0 == itObj->second.m_Commands.size()) { #ifdef RLV_DEBUG RLV_INFOS << "\t- command list empty => removing " << uuid << LL_ENDL; #endif // RLV_DEBUG m_Objects.erase(itObj); } } } break; case RLV_TYPE_FORCE: // Checked: fRet = processForceCommand(uuid, rlvCmd); break; case RLV_TYPE_REPLY: // Checked: fRet = processReplyCommand(uuid, rlvCmd); break; case RLV_TYPE_UNKNOWN: // Checked: { if ("clear" == rlvCmd.getBehaviour()) { const std::string& strFilter = rlvCmd.getParam(); std::string strCmdRem; rlv_object_map_t::const_iterator itObj = m_Objects.find(uuid); if (itObj != m_Objects.end()) // No sense in @clear'ing if we don't have any commands for this object { const RlvObject& rlvObj = itObj->second; bool fContinue = true; for (rlv_command_list_t::const_iterator itCmd = rlvObj.m_Commands.begin(), itCurCmd; ((fContinue) && (itCmd != rlvObj.m_Commands.end())); ) { itCurCmd = itCmd++; // Point itCmd ahead so it won't get invalidated if/when we erase a command const RlvCommand& rlvCmdRem = *itCurCmd; if ( (strFilter.empty()) || (-1 != rlvCmdRem.asString().find(strFilter)) ) { fContinue = (rlvObj.m_Commands.size() > 1); // rlvObj will become invalid once we remove the last command strCmdRem = rlvCmdRem.getBehaviour() + ":" + rlvCmdRem.getOption() + "=y"; processCommand(uuid, strCmdRem, false); } } fRet = TRUE; } } } break; #ifdef LL_GNUC default: break; #endif // LL_GNUC } #ifdef RLV_DEBUG RLV_INFOS << "\t--> command " << ((fRet) ? "succeeded" : "failed") << LL_ENDL; #endif // RLV_DEBUG m_pCurCommand = NULL; m_idCurObject.setNull(); return fRet; } BOOL RlvHandler::processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd) { // NOTE: - at this point the command has already been added to the corresponding RlvObject instance // - the object's UUID may or may not exist in gObjectList (see handling of @detach=n) ERlvBehaviour eBehaviour = rlvCmd.getBehaviourType(); const std::string& strOption = rlvCmd.getOption(); if ( (RLV_BHVR_UNKNOWN != eBehaviour) && (strOption.empty()) ) m_Behaviours[eBehaviour]++; switch (eBehaviour) { case RLV_BHVR_DETACH: // @detach[: