From 7f090f7bec5264ca9e203c27dfb6b2992bb2bcbd Mon Sep 17 00:00:00 2001 From: McCabe Maxsted Date: Mon, 14 Sep 2009 17:52:41 -0700 Subject: Merged in jacek/next --- linden/indra/newview/rlvhandler.cpp | 2549 +++++++++++++++++++++++++++++++++++ 1 file changed, 2549 insertions(+) create mode 100644 linden/indra/newview/rlvhandler.cpp (limited to 'linden/indra/newview/rlvhandler.cpp') diff --git a/linden/indra/newview/rlvhandler.cpp b/linden/indra/newview/rlvhandler.cpp new file mode 100644 index 0000000..2915002 --- /dev/null +++ b/linden/indra/newview/rlvhandler.cpp @@ -0,0 +1,2549 @@ +#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[: