summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xindra/newview/app_settings/logcontrol.xml1
-rwxr-xr-xindra/newview/llagentwearables.cpp57
-rwxr-xr-xindra/newview/llappearancemgr.cpp105
-rwxr-xr-xindra/newview/llappearancemgr.h14
-rwxr-xr-xindra/newview/llattachmentsmgr.cpp245
-rwxr-xr-xindra/newview/llattachmentsmgr.h99
-rwxr-xr-xindra/newview/llinventorybridge.cpp32
-rwxr-xr-xindra/newview/llinventorybridge.h6
-rwxr-xr-xindra/newview/llinventoryfunctions.cpp60
-rwxr-xr-xindra/newview/llvoavatar.cpp18
-rwxr-xr-xindra/newview/llvoavatarself.cpp41
-rwxr-xr-xindra/newview/llvoavatarself.h7
-rwxr-xr-xindra/newview/llwearableitemslist.cpp2
13 files changed, 475 insertions, 212 deletions
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml
index de3732f339..6a798e1d42 100755
--- a/indra/newview/app_settings/logcontrol.xml
+++ b/indra/newview/app_settings/logcontrol.xml
@@ -42,6 +42,7 @@
</array>
<key>tags</key>
<array>
+ <string>Avatar</string>
<!-- sample entry for debugging specific items
<string>Avatar</string>
<string>Inventory</string>
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index f06ffb4fb3..d772e1c7f8 100755
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h"
#include "llagentwearables.h"
+#include "llattachmentsmgr.h"
#include "llaccordionctrltab.h"
#include "llagent.h"
#include "llagentcamera.h"
@@ -1344,6 +1345,7 @@ void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remo
if (objects_to_remove.empty())
return;
+ LL_DEBUGS("Avatar") << "ATT [ObjectDetach] removing " << objects_to_remove.size() << " objects" << LL_ENDL;
gMessageSystem->newMessage("ObjectDetach");
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
@@ -1357,6 +1359,10 @@ void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remo
//gAgentAvatarp->resetJointPositionsOnDetach(objectp);
gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID());
+ const LLUUID& item_id = objectp->getAttachmentItemID();
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_DEBUGS("Avatar") << "ATT removing object, item is " << (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+ LLAttachmentsMgr::instance().onDetachRequested(item_id);
}
gMessageSystem->sendReliable(gAgent.getRegionHost());
}
@@ -1365,51 +1371,18 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra
{
// Build a compound message to send all the objects that need to be rezzed.
S32 obj_count = obj_item_array.size();
-
- // Limit number of packets to send
- const S32 MAX_PACKETS_TO_SEND = 10;
- const S32 OBJECTS_PER_PACKET = 4;
- const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
- if( obj_count > MAX_OBJECTS_TO_SEND )
+ if (obj_count > 0)
{
- obj_count = MAX_OBJECTS_TO_SEND;
+ LL_DEBUGS("Avatar") << "ATT attaching multiple, total obj_count " << obj_count << LL_ENDL;
}
-
- // Create an id to keep the parts of the compound message together
- LLUUID compound_msg_id;
- compound_msg_id.generate();
- LLMessageSystem* msg = gMessageSystem;
-
- for(S32 i = 0; i < obj_count; ++i)
- {
- if( 0 == (i % OBJECTS_PER_PACKET) )
- {
- // Start a new message chunk
- msg->newMessageFast(_PREHASH_RezMultipleAttachmentsFromInv);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_HeaderData);
- msg->addUUIDFast(_PREHASH_CompoundMsgID, compound_msg_id );
- msg->addU8Fast(_PREHASH_TotalObjects, obj_count );
- msg->addBOOLFast(_PREHASH_FirstDetachAll, false );
- }
- const LLInventoryItem* item = obj_item_array.at(i).get();
- msg->nextBlockFast(_PREHASH_ObjectData );
- msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
- msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
- msg->addU8Fast(_PREHASH_AttachmentPt, 0 | ATTACHMENT_ADD); // Wear at the previous or default attachment point
- pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
- msg->addStringFast(_PREHASH_Name, item->getName());
- msg->addStringFast(_PREHASH_Description, item->getDescription());
-
- if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) )
- {
- // End of message chunk
- msg->sendReliable( gAgent.getRegion()->getHost() );
- }
- }
+ for(LLInventoryModel::item_array_t::const_iterator it = obj_item_array.begin();
+ it != obj_item_array.end();
+ ++it)
+ {
+ const LLInventoryItem* item = *it;
+ LLAttachmentsMgr::instance().addAttachmentRequest(item->getLinkedUUID(), 0, TRUE);
+ }
}
// Returns false if the given wearable is already topmost/bottommost
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index d7ef5fcba7..a94aa37ab7 100755
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -525,6 +525,12 @@ LLUpdateAppearanceAndEditWearableOnDestroy::LLUpdateAppearanceAndEditWearableOnD
{
}
+LLRequestServerAppearanceUpdateOnDestroy::~LLRequestServerAppearanceUpdateOnDestroy()
+{
+ LL_DEBUGS("Avatar") << "ATT requesting server appearance update" << LL_ENDL;
+ LLAppearanceMgr::instance().requestServerAppearanceUpdate();
+}
+
void edit_wearable_and_customize_avatar(LLUUID item_id)
{
// Start editing the item if previously requested.
@@ -828,6 +834,12 @@ void LLWearableHoldingPattern::onAllComplete()
// pre-attachment states.
gAgentAvatarp->clearAttachmentPosOverrides();
+ if (objects_to_remove.size() || items_to_add.size())
+ {
+ LL_DEBUGS("Avatar") << "ATT will remove " << objects_to_remove.size()
+ << " and add " << items_to_add.size() << " items" << LL_ENDL;
+ }
+
// Take off the attachments that will no longer be in the outfit.
LLAgentWearables::userRemoveMultipleAttachments(objects_to_remove);
@@ -1402,20 +1414,26 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear,
break;
case LLAssetType::AT_BODYPART:
- // TODO: investigate wearables may not be loaded at this point EXT-8231
-
- // Remove the existing wearables of the same type.
- // Remove existing body parts anyway because we must not be able to wear e.g. two skins.
- removeCOFLinksOfType(item_to_wear->getWearableType());
- if (!cb && do_update)
{
- cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
+ // TODO: investigate wearables may not be loaded at this point EXT-8231
+
+ // Remove the existing wearables of the same type.
+ // Remove existing body parts anyway because we must not be able to wear e.g. two skins.
+ removeCOFLinksOfType(item_to_wear->getWearableType());
+ if (!cb && do_update)
+ {
+ cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
+ }
+ addCOFItemLink(item_to_wear, cb);
}
- addCOFItemLink(item_to_wear, cb);
break;
case LLAssetType::AT_OBJECT:
- rez_attachment(item_to_wear, NULL, replace);
+ {
+ LL_DEBUGS("Avatar") << "ATT wearing object. calling rez_attachment, item " << item_to_wear->getName()
+ << " id " << item_to_wear->getLinkedUUID() << LL_ENDL;
+ rez_attachment(item_to_wear, NULL, replace);
+ }
break;
default: return false;;
@@ -3230,7 +3248,7 @@ void RequestAgentUpdateAppearanceResponder::onRequestRequested()
}
// Actually send the request.
- LL_DEBUGS("Avatar") << "Will send request for cof_version " << cof_version << LL_ENDL;
+ LL_DEBUGS("Avatar") << "ATT sending bake request for cof_version " << cof_version << LL_ENDL;
mRetryPolicy->reset();
sendRequest();
}
@@ -3703,6 +3721,11 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove)
{
const LLUUID& id_to_remove = *it;
const LLUUID& linked_item_id = gInventory.getLinkedItemID(id_to_remove);
+ LLViewerInventoryItem *item = gInventory.getItem(linked_item_id);
+ if (item && item->getType() == LLAssetType::AT_OBJECT)
+ {
+ LL_DEBUGS("Avatar") << "ATT removing attachment " << item->getName() << " id " << item->getUUID() << LL_ENDL;
+ }
removeCOFItemLinks(linked_item_id, cb);
addDoomedTempAttachment(linked_item_id);
}
@@ -3710,10 +3733,9 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove)
void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
{
- LLUUID linked_item_id = gInventory.getLinkedItemID(id_to_remove);
- LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
- removeCOFItemLinks(linked_item_id, cb);
- addDoomedTempAttachment(linked_item_id);
+ uuid_vec_t ids_to_remove;
+ ids_to_remove.push_back(id_to_remove);
+ removeItemsFromAvatar(ids_to_remove);
}
@@ -3898,37 +3920,32 @@ void dumpAttachmentSet(const std::set<LLUUID>& atts, const std::string& msg)
void LLAppearanceMgr::registerAttachment(const LLUUID& item_id)
{
- gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
-
- if (mAttachmentInvLinkEnabled)
- {
- // we have to pass do_update = true to call LLAppearanceMgr::updateAppearanceFromCOF.
- // it will trigger gAgentWariables.notifyLoadingFinished()
- // But it is not acceptable solution. See EXT-7777
- if (!isLinkedInCOF(item_id))
- {
- LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy();
- LLAppearanceMgr::addCOFItemLink(item_id, cb); // Add COF link for item.
- }
- }
- else
- {
- //LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
- }
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_DEBUGS("Avatar") << "ATT registering attachment "
+ << (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+
+ LLAttachmentsMgr::instance().onAttachmentArrived(item_id);
}
void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id)
{
- gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_DEBUGS("Avatar") << "ATT unregistering attachment "
+ << (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
- if (mAttachmentInvLinkEnabled)
- {
- LLAppearanceMgr::removeCOFItemLinks(item_id);
- }
- else
- {
- //LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
- }
+ LLAttachmentsMgr::instance().onDetachCompleted(item_id);
+ if (mAttachmentInvLinkEnabled && isLinkedInCOF(item_id))
+ {
+ LL_DEBUGS("Avatar") << "ATT removing COF link for attachment "
+ << (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+ LLAppearanceMgr::removeCOFItemLinks(item_id);
+ }
+ else
+ {
+ //LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
+ }
}
BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const
@@ -3942,14 +3959,6 @@ BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const
return FALSE;
}
-// static
-bool LLAppearanceMgr::isLinkInCOF(const LLUUID& obj_id)
-{
- const LLUUID& target_id = gInventory.getLinkedItemID(obj_id);
- LLLinkedItemIDMatches find_links(target_id);
- return gInventory.hasMatchingDirectDescendent(LLAppearanceMgr::instance().getCOF(), find_links);
-}
-
BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const
{
if (!getIsInCOF(obj_id)) return FALSE;
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index 7742a19c07..dfe392e3b7 100755
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -275,11 +275,6 @@ public:
BOOL getIsInCOF(const LLUUID& obj_id) const;
// Is this in the COF and can the user delete it from the COF?
BOOL getIsProtectedCOFItem(const LLUUID& obj_id) const;
-
- /**
- * Checks if COF contains link to specified object.
- */
- static bool isLinkInCOF(const LLUUID& obj_id);
};
class LLUpdateAppearanceOnDestroy: public LLInventoryCallback
@@ -311,6 +306,15 @@ private:
LLUUID mItemID;
};
+class LLRequestServerAppearanceUpdateOnDestroy: public LLInventoryCallback
+{
+public:
+ LLRequestServerAppearanceUpdateOnDestroy() {}
+ ~LLRequestServerAppearanceUpdateOnDestroy();
+
+ /* virtual */ void fire(const LLUUID& item_id) {}
+};
+
LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name);
// Invoke a given callable after category contents are fully fetched.
diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp
index 256980eb04..fa2d95a1e6 100755
--- a/indra/newview/llattachmentsmgr.cpp
+++ b/indra/newview/llattachmentsmgr.cpp
@@ -27,15 +27,22 @@
#include "llviewerprecompiledheaders.h"
#include "llattachmentsmgr.h"
+#include "llvoavatarself.h"
#include "llagent.h"
+#include "llappearancemgr.h"
#include "llinventorymodel.h"
#include "lltooldraganddrop.h" // pack_permissions_slam
#include "llviewerinventory.h"
#include "llviewerregion.h"
#include "message.h"
+const F32 COF_LINK_BATCH_TIME = 5.0F;
+const F32 MAX_ATTACHMENT_REQUEST_LIFETIME = 30.0F;
+const F32 MIN_RETRY_REQUEST_TIME = 5.0F;
-LLAttachmentsMgr::LLAttachmentsMgr()
+LLAttachmentsMgr::LLAttachmentsMgr():
+ mAttachmentRequests("attach",MIN_RETRY_REQUEST_TIME),
+ mDetachRequests("detach",MIN_RETRY_REQUEST_TIME)
{
}
@@ -43,15 +50,29 @@ LLAttachmentsMgr::~LLAttachmentsMgr()
{
}
-void LLAttachmentsMgr::addAttachment(const LLUUID& item_id,
- const U8 attachment_pt,
- const BOOL add)
+void LLAttachmentsMgr::addAttachmentRequest(const LLUUID& item_id,
+ const U8 attachment_pt,
+ const BOOL add)
{
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+
+ if (mAttachmentRequests.wasRequestedRecently(item_id))
+ {
+ LL_DEBUGS("Avatar") << "ATT not adding attachment to mPendingAttachments, recent request is already pending: "
+ << (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+ return;
+ }
+
+ LL_DEBUGS("Avatar") << "ATT adding attachment to mPendingAttachments "
+ << (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+
AttachmentsInfo attachment;
attachment.mItemID = item_id;
attachment.mAttachmentPt = attachment_pt;
attachment.mAdd = add;
mPendingAttachments.push_back(attachment);
+
+ mAttachmentRequests.addTime(item_id);
}
// static
@@ -68,29 +89,64 @@ void LLAttachmentsMgr::onIdle()
return;
}
- S32 obj_count = mPendingAttachments.size();
+ requestPendingAttachments();
+
+ linkRecentlyArrivedAttachments();
+
+ expireOldAttachmentRequests();
+}
+
+void LLAttachmentsMgr::requestPendingAttachments()
+{
+ if (mPendingAttachments.size())
+ {
+ requestAttachments(mPendingAttachments);
+ mPendingAttachments.clear();
+ }
+}
+
+// Send request(s) for a group of attachments. As coded, this can
+// request at most 40 attachments and the rest will be
+// ignored. Currently the max attachments per avatar is 38, so the 40
+// limit should not be hit in practice.
+void LLAttachmentsMgr::requestAttachments(const attachments_vec_t& attachment_requests)
+{
+ // Make sure we got a region before trying anything else
+ if( !gAgent.getRegion() )
+ {
+ return;
+ }
+
+ S32 obj_count = attachment_requests.size();
if (obj_count == 0)
{
return;
}
-
+
// Limit number of packets to send
const S32 MAX_PACKETS_TO_SEND = 10;
const S32 OBJECTS_PER_PACKET = 4;
const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
if( obj_count > MAX_OBJECTS_TO_SEND )
{
+ LL_WARNS() << "ATT Too many attachments requested: " << attachment_requests.size()
+ << " exceeds limit of " << MAX_OBJECTS_TO_SEND << LL_ENDL;
+ LL_WARNS() << "ATT Excess requests will be ignored" << LL_ENDL;
+
obj_count = MAX_OBJECTS_TO_SEND;
}
+ LL_DEBUGS("Avatar") << "ATT [RezMultipleAttachmentsFromInv] attaching multiple from attachment_requests,"
+ " total obj_count " << obj_count << LL_ENDL;
+
LLUUID compound_msg_id;
compound_msg_id.generate();
LLMessageSystem* msg = gMessageSystem;
S32 i = 0;
- for (attachments_vec_t::const_iterator iter = mPendingAttachments.begin();
- iter != mPendingAttachments.end();
+ for (attachments_vec_t::const_iterator iter = attachment_requests.begin();
+ iter != attachment_requests.end();
++iter)
{
if( 0 == (i % OBJECTS_PER_PACKET) )
@@ -110,9 +166,11 @@ void LLAttachmentsMgr::onIdle()
LLViewerInventoryItem* item = gInventory.getItem(attachment.mItemID);
if (!item)
{
- LL_INFOS() << "Attempted to add non-existant item ID:" << attachment.mItemID << LL_ENDL;
+ LL_INFOS() << "Attempted to add non-existent item ID:" << attachment.mItemID << LL_ENDL;
continue;
}
+ LL_DEBUGS("Avatar") << "ATT requesting from attachment_requests " << item->getName()
+ << " " << item->getLinkedUUID() << LL_ENDL;
S32 attachment_pt = attachment.mAttachmentPt;
if (attachment.mAdd)
attachment_pt |= ATTACHMENT_ADD;
@@ -132,6 +190,173 @@ void LLAttachmentsMgr::onIdle()
}
i++;
}
+}
+
+void LLAttachmentsMgr::linkRecentlyArrivedAttachments()
+{
+ if (mRecentlyArrivedAttachments.size())
+ {
+ // One or more attachments have arrived but have not yet been
+ // processed for COF links
+ if (mAttachmentRequests.empty())
+ {
+ // Not waiting for any more.
+ LL_DEBUGS("Avatar") << "ATT all pending attachments have arrived after "
+ << mCOFLinkBatchTimer.getElapsedTimeF32() << " seconds" << LL_ENDL;
+ }
+ else if (mCOFLinkBatchTimer.getElapsedTimeF32() > COF_LINK_BATCH_TIME)
+ {
+ LL_DEBUGS("Avatar") << "ATT " << mAttachmentRequests.size()
+ << " pending attachments have not arrived, but wait time exceeded" << LL_ENDL;
+ }
+ else
+ {
+ return;
+ }
+
+ LL_DEBUGS("Avatar") << "ATT checking COF linkability for " << mRecentlyArrivedAttachments.size()
+ << " recently arrived items" << LL_ENDL;
+ LLInventoryObject::const_object_list_t inv_items_to_link;
+ for (std::set<LLUUID>::iterator it = mRecentlyArrivedAttachments.begin();
+ it != mRecentlyArrivedAttachments.end(); ++it)
+ {
+ if (gAgentAvatarp->isWearingAttachment(*it) &&
+ !LLAppearanceMgr::instance().isLinkedInCOF(*it))
+ {
+ LLUUID item_id = *it;
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_DEBUGS("Avatar") << "ATT adding COF link for attachment "
+ << (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+ inv_items_to_link.push_back(item);
+ }
+ }
+ if (inv_items_to_link.size())
+ {
+ LLPointer<LLInventoryCallback> cb = new LLRequestServerAppearanceUpdateOnDestroy();
+ link_inventory_array(LLAppearanceMgr::instance().getCOF(), inv_items_to_link, cb);
+ }
+ mRecentlyArrivedAttachments.clear();
+ }
+}
+
+LLAttachmentsMgr::LLItemRequestTimes::LLItemRequestTimes(const std::string& op_name, F32 timeout):
+ mOpName(op_name),
+ mTimeout(timeout)
+{
+}
+
+void LLAttachmentsMgr::LLItemRequestTimes::addTime(const LLUUID& inv_item_id)
+{
+ LLInventoryItem *item = gInventory.getItem(inv_item_id);
+ LL_DEBUGS("Avatar") << "ATT " << mOpName << " adding request time " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+ LLTimer current_time;
+ (*this)[inv_item_id] = current_time;
+}
+
+void LLAttachmentsMgr::LLItemRequestTimes::removeTime(const LLUUID& inv_item_id)
+{
+ LLInventoryItem *item = gInventory.getItem(inv_item_id);
+ LL_DEBUGS("Avatar") << "ATT " << mOpName << " removing request time "
+ << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+ (*this).erase(inv_item_id);
+}
+
+BOOL LLAttachmentsMgr::LLItemRequestTimes::getTime(const LLUUID& inv_item_id, LLTimer& timer) const
+{
+ std::map<LLUUID,LLTimer>::const_iterator it = (*this).find(inv_item_id);
+ if (it != (*this).end())
+ {
+ timer = it->second;
+ return TRUE;
+ }
+ return FALSE;
+}
- mPendingAttachments.clear();
+BOOL LLAttachmentsMgr::LLItemRequestTimes::wasRequestedRecently(const LLUUID& inv_item_id) const
+{
+ LLTimer request_time;
+ if (getTime(inv_item_id, request_time))
+ {
+ F32 request_time_elapsed = request_time.getElapsedTimeF32();
+ if (request_time_elapsed >= mTimeout)
+ {
+ LLInventoryItem *item = gInventory.getItem(inv_item_id);
+ LL_DEBUGS("Avatar") << "ATT " << mOpName << " request time ignored, exceeded " << mTimeout
+ << " " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+ }
+ return request_time_elapsed < mTimeout;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// If we've been waiting for an attachment a long time, we want to
+// forget the request, because if the request is invalid (say the
+// object does not exist), the existence of a request that never goes
+// away will gum up the COF batch logic, causing it to always wait for
+// the timeout. Expiring a request means if the item does show up
+// late, the COF link request may not get properly batched up, but
+// behavior will be no worse than before we had the batching mechanism
+// in place; the COF link will still be created, but extra
+// requestServerAppearanceUpdate() calls may occur.
+void LLAttachmentsMgr::expireOldAttachmentRequests()
+{
+ for (std::map<LLUUID,LLTimer>::iterator it = mAttachmentRequests.begin();
+ it != mAttachmentRequests.end(); )
+ {
+ std::map<LLUUID,LLTimer>::iterator curr_it = it;
+ ++it;
+ if (it->second.getElapsedTimeF32() > MAX_ATTACHMENT_REQUEST_LIFETIME)
+ {
+ LLInventoryItem *item = gInventory.getItem(curr_it->first);
+ LL_DEBUGS("Avatar") << "ATT expiring request for attachment "
+ << (item ? item->getName() : "UNKNOWN") << "item_id " << curr_it->first
+ << " after " << MAX_ATTACHMENT_REQUEST_LIFETIME << " seconds" << LL_ENDL;
+ mAttachmentRequests.erase(curr_it);
+ }
+ }
+}
+
+// When an attachment arrives, we want to stop waiting for it, and add
+// it to the set of recently arrived items.
+void LLAttachmentsMgr::onAttachmentArrived(const LLUUID& inv_item_id)
+{
+ LLTimer timer;
+ if (!mAttachmentRequests.getTime(inv_item_id, timer))
+ {
+ LLInventoryItem *item = gInventory.getItem(inv_item_id);
+ LL_WARNS() << "ATT Attachment was unexpected or arrived after " << MAX_ATTACHMENT_REQUEST_LIFETIME << " seconds: "
+ << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL;
+ }
+ mAttachmentRequests.removeTime(inv_item_id);
+ if (mRecentlyArrivedAttachments.empty())
+ {
+ // Start the timer for sending off a COF link batch.
+ mCOFLinkBatchTimer.reset();
+ }
+ mRecentlyArrivedAttachments.insert(inv_item_id);
+}
+
+void LLAttachmentsMgr::onDetachRequested(const LLUUID& inv_item_id)
+{
+ mDetachRequests.addTime(inv_item_id);
+}
+
+void LLAttachmentsMgr::onDetachCompleted(const LLUUID& inv_item_id)
+{
+ LLTimer timer;
+ LLInventoryItem *item = gInventory.getItem(inv_item_id);
+ if (mDetachRequests.getTime(inv_item_id, timer))
+ {
+ LL_DEBUGS("Avatar") << "ATT detach completed after " << timer.getElapsedTimeF32()
+ << " seconds for " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+ mDetachRequests.removeTime(inv_item_id);
+ }
+ else
+ {
+ LL_WARNS() << "ATT unexpected detach for "
+ << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL;
+ }
}
diff --git a/indra/newview/llattachmentsmgr.h b/indra/newview/llattachmentsmgr.h
index 1d8ab74dfd..2834c10a2f 100755
--- a/indra/newview/llattachmentsmgr.h
+++ b/indra/newview/llattachmentsmgr.h
@@ -32,42 +32,95 @@
class LLViewerInventoryItem;
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//--------------------------------------------------------------------------------
// LLAttachmentsMgr
//
-// The sole purpose of this class is to take attachment
-// requests, queue them up, and send them all at once.
-// This handles situations where the viewer may request
-// a bunch of attachments at once in a short period of
-// time, where each of the requests would normally be
-// sent as a separate message versus being batched into
-// one single message.
-//
-// The intent of this batching is to reduce viewer->server
-// traffic.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This class manages batching up of requests at two stages of
+// attachment rezzing.
+//
+// First, attachments requested to rez get saved in
+// mPendingAttachments and sent as a single
+// RezMultipleAttachmentsFromInv request. This batching is needed
+// mainly because of weaknessing the UI element->inventory item
+// handling, such that we don't always know when we are requesting
+// multiple items. Now they just pile up and get swept into a single
+// request during the idle loop.
+//
+// Second, after attachments arrive, we need to generate COF links for
+// them. There are both efficiency and UI correctness reasons why it
+// is better to request all the COF links at once and run a single
+// callback after they all complete. Given the vagaries of the
+// attachment system, there is no guarantee that we will get all the
+// attachments we ask for, but we frequently do. So in the common case
+// that all the desired attachments arrive fairly quickly, we generate
+// a single batched request for COF links. If attachments arrive late
+// or not at all, we will still issue COF link requests once a timeout
+// value has been exceeded.
+//
+// To handle attachments that never arrive, we forget about requests
+// that exceed a timeout value.
+//--------------------------------------------------------------------------------
class LLAttachmentsMgr: public LLSingleton<LLAttachmentsMgr>
{
public:
- LLAttachmentsMgr();
- virtual ~LLAttachmentsMgr();
-
- void addAttachment(const LLUUID& item_id,
- const U8 attachment_pt,
- const BOOL add);
- static void onIdle(void *);
-protected:
- void onIdle();
-private:
+ // Stores info for attachments that will be requested during idle.
struct AttachmentsInfo
{
LLUUID mItemID;
U8 mAttachmentPt;
BOOL mAdd;
};
-
typedef std::vector<AttachmentsInfo> attachments_vec_t;
+
+ LLAttachmentsMgr();
+ virtual ~LLAttachmentsMgr();
+
+ void addAttachmentRequest(const LLUUID& item_id,
+ const U8 attachment_pt,
+ const BOOL add);
+ void requestAttachments(const attachments_vec_t& attachment_requests);
+ static void onIdle(void *);
+
+ void onAttachmentArrived(const LLUUID& inv_item_id);
+
+ void onDetachRequested(const LLUUID& inv_item_id);
+ void onDetachCompleted(const LLUUID& inv_item_id);
+
+private:
+
+ class LLItemRequestTimes: public std::map<LLUUID,LLTimer>
+ {
+ public:
+ LLItemRequestTimes(const std::string& op_name, F32 timeout);
+ void addTime(const LLUUID& inv_item_id);
+ void removeTime(const LLUUID& inv_item_id);
+ BOOL wasRequestedRecently(const LLUUID& item_id) const;
+ BOOL getTime(const LLUUID& inv_item_id, LLTimer& timer) const;
+
+ private:
+ F32 mTimeout;
+ std::string mOpName;
+ };
+
+ void removeAttachmentRequestTime(const LLUUID& inv_item_id);
+ void onIdle();
+ void requestPendingAttachments();
+ void linkRecentlyArrivedAttachments();
+ void expireOldAttachmentRequests();
+
+ // Attachments that we are planning to rez but haven't requested from the server yet.
attachments_vec_t mPendingAttachments;
+
+ // Attachments that have been requested from server but have not arrived yet.
+ LLItemRequestTimes mAttachmentRequests;
+
+ // Attachments that have been requested to detach but have not gone away yet.
+ LLItemRequestTimes mDetachRequests;
+
+ // Attachments that have arrived but have not been linked in the COF yet.
+ std::set<LLUUID> mRecentlyArrivedAttachments;
+ LLTimer mCOFLinkBatchTimer;
+
};
#endif
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 1910656066..bab86bc2e5 100755
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -5368,13 +5368,11 @@ void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attach
// Check for duplicate request.
if (isAgentAvatarValid() &&
- (gAgentAvatarp->attachmentWasRequested(item_id) ||
- gAgentAvatarp->isWearingAttachment(item_id)))
+ gAgentAvatarp->isWearingAttachment(item_id))
{
- LL_WARNS() << "duplicate attachment request, ignoring" << LL_ENDL;
+ LL_WARNS() << "ATT duplicate attachment request, ignoring" << LL_ENDL;
return;
}
- gAgentAvatarp->addAttachmentRequest(item_id);
S32 attach_pt = 0;
if (isAgentAvatarValid() && attachment)
@@ -5424,36 +5422,14 @@ bool confirm_attachment_rez(const LLSD& notification, const LLSD& response)
if (itemp)
{
- /*
- {
- U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
-
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_ObjectData);
- msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID());
- msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner());
- msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt);
- pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions());
- msg->addStringFast(_PREHASH_Name, itemp->getName());
- msg->addStringFast(_PREHASH_Description, itemp->getDescription());
- msg->sendReliable(gAgent.getRegion()->getHost());
- return false;
- }
- */
-
// Queue up attachments to be sent in next idle tick, this way the
// attachments are batched up all into one message versus each attachment
// being sent in its own separate attachments message.
U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
BOOL is_add = notification["payload"]["is_add"].asBoolean();
- LLAttachmentsMgr::instance().addAttachment(item_id,
- attachment_pt,
- is_add);
+ LL_DEBUGS("Avatar") << "ATT calling addAttachmentRequest " << (itemp ? itemp->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+ LLAttachmentsMgr::instance().addAttachmentRequest(item_id, attachment_pt, is_add);
}
}
return false;
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index e8d5db4437..8dde2c33b7 100755
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -687,4 +687,10 @@ void hide_context_entries(LLMenuGL& menu,
const menuentry_vec_t &entries_to_show,
const menuentry_vec_t &disabled_entries);
+// Helper functions to classify actions.
+bool isAddAction(const std::string& action);
+bool isRemoveAction(const std::string& action);
+bool isMarketplaceCopyAction(const std::string& action);
+bool isMarketplaceSendAction(const std::string& action);
+
#endif // LL_LLINVENTORYBRIDGE_H
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 1abc09bf3b..98c636c217 100755
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -230,7 +230,7 @@ BOOL get_is_item_worn(const LLUUID& id)
return FALSE;
// Consider the item as worn if it has links in COF.
- if (LLAppearanceMgr::instance().isLinkInCOF(id))
+ if (LLAppearanceMgr::instance().isLinkedInCOF(id))
{
return TRUE;
}
@@ -264,7 +264,7 @@ BOOL get_can_item_be_worn(const LLUUID& id)
if (!item)
return FALSE;
- if (LLAppearanceMgr::isLinkInCOF(item->getLinkedUUID()))
+ if (LLAppearanceMgr::instance().isLinkedInCOF(item->getLinkedUUID()))
{
// an item having links in COF (i.e. a worn item)
return FALSE;
@@ -1060,6 +1060,34 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder)
}
}
+// Succeeds iff all selected items are bridges to objects, in which
+// case returns their corresponding uuids.
+bool get_selection_object_uuids(LLFolderView *root, uuid_vec_t& ids)
+{
+ uuid_vec_t results;
+ S32 non_object = 0;
+ LLFolderView::selected_items_t selectedItems = root->getSelectedItems();
+ for(LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it)
+ {
+ LLObjectBridge *view_model = dynamic_cast<LLObjectBridge *>((*it)->getViewModelItem());
+
+ if(view_model && view_model->getUUID().notNull())
+ {
+ results.push_back(view_model->getUUID());
+ }
+ else
+ {
+ non_object++;
+ }
+ }
+ if (non_object == 0)
+ {
+ ids = results;
+ return true;
+ }
+ return false;
+}
+
void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action)
{
if ("rename" == action)
@@ -1116,13 +1144,29 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
std::set<LLFolderViewItem*>::iterator set_iter;
- for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter)
+
+ // This rather warty piece of code is to allow items to be removed
+ // from the avatar in a batch, eliminating redundant
+ // updateAppearanceFromCOF() requests further down the line. (MAINT-4918)
+ //
+ // There are probably other cases where similar batching would be
+ // desirable, but the current item-by-item performAction()
+ // approach would need to be reworked.
+ uuid_vec_t object_uuids_to_remove;
+ if (isRemoveAction(action) && get_selection_object_uuids(root, object_uuids_to_remove))
{
- LLFolderViewItem* folder_item = *set_iter;
- if(!folder_item) continue;
- LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem();
- if(!bridge) continue;
- bridge->performAction(model, action);
+ LLAppearanceMgr::instance().removeItemsFromAvatar(object_uuids_to_remove);
+ }
+ else
+ {
+ for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter)
+ {
+ LLFolderViewItem* folder_item = *set_iter;
+ if(!folder_item) continue;
+ LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem();
+ if(!bridge) continue;
+ bridge->performAction(model, action);
+ }
}
LLFloater::setFloaterHost(NULL);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index a90fade3c9..bc76dec587 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -5800,10 +5800,21 @@ LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* vi
//-----------------------------------------------------------------------------
const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_object)
{
+ if (isSelf())
+ {
+ const LLUUID& item_id = viewer_object->getAttachmentItemID();
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_DEBUGS("Avatar") << "ATT attaching object "
+ << (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+ }
LLViewerJointAttachment* attachment = getTargetAttachmentPoint(viewer_object);
if (!attachment || !attachment->addObject(viewer_object))
{
+ const LLUUID& item_id = viewer_object->getAttachmentItemID();
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_DEBUGS("Avatar") << "ATT attach failed "
+ << (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
return 0;
}
@@ -5863,6 +5874,13 @@ void LLVOAvatar::lazyAttach()
LLPointer<LLViewerObject> cur_attachment = mPendingAttachment[i];
if (cur_attachment->mDrawable)
{
+ if (isSelf())
+ {
+ const LLUUID& item_id = cur_attachment->getAttachmentItemID();
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_DEBUGS("Avatar") << "ATT attaching object "
+ << (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+ }
if (!attachObject(cur_attachment))
{ // Drop it
LL_WARNS() << "attachObject() failed for "
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index ae7a233876..bb838977aa 100755
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -38,6 +38,7 @@
#include "pipeline.h"
#include "llagent.h" // Get state values from here
+#include "llattachmentsmgr.h"
#include "llagentcamera.h"
#include "llagentwearables.h"
#include "llhudeffecttrail.h"
@@ -1119,44 +1120,6 @@ BOOL LLVOAvatarSelf::isWearingAttachment(const LLUUID& inv_item_id) const
}
//-----------------------------------------------------------------------------
-BOOL LLVOAvatarSelf::attachmentWasRequested(const LLUUID& inv_item_id) const
-{
- const F32 REQUEST_EXPIRATION_SECONDS = 5.0; // any request older than this is ignored/removed.
- std::map<LLUUID,LLTimer>::iterator it = mAttachmentRequests.find(inv_item_id);
- if (it != mAttachmentRequests.end())
- {
- const LLTimer& request_time = it->second;
- F32 request_time_elapsed = request_time.getElapsedTimeF32();
- if (request_time_elapsed > REQUEST_EXPIRATION_SECONDS)
- {
- mAttachmentRequests.erase(it);
- return FALSE;
- }
- else
- {
- return TRUE;
- }
- }
- else
- {
- return FALSE;
- }
-}
-
-//-----------------------------------------------------------------------------
-void LLVOAvatarSelf::addAttachmentRequest(const LLUUID& inv_item_id)
-{
- LLTimer current_time;
- mAttachmentRequests[inv_item_id] = current_time;
-}
-
-//-----------------------------------------------------------------------------
-void LLVOAvatarSelf::removeAttachmentRequest(const LLUUID& inv_item_id)
-{
- mAttachmentRequests.erase(inv_item_id);
-}
-
-//-----------------------------------------------------------------------------
// getWornAttachment()
//-----------------------------------------------------------------------------
LLViewerObject* LLVOAvatarSelf::getWornAttachment(const LLUUID& inv_item_id)
@@ -1222,8 +1185,6 @@ const LLViewerJointAttachment *LLVOAvatarSelf::attachObject(LLViewerObject *view
{
const LLUUID& attachment_id = viewer_object->getAttachmentItemID();
LLAppearanceMgr::instance().registerAttachment(attachment_id);
- // Clear any pending requests once the attachment arrives.
- removeAttachmentRequest(attachment_id);
updateLODRiggedAttachments();
}
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index b8e9bbb77a..fab500c397 100755
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -292,19 +292,12 @@ protected:
public:
void updateAttachmentVisibility(U32 camera_mode);
BOOL isWearingAttachment(const LLUUID& inv_item_id) const;
- BOOL attachmentWasRequested(const LLUUID& inv_item_id) const;
- void addAttachmentRequest(const LLUUID& inv_item_id);
- void removeAttachmentRequest(const LLUUID& inv_item_id);
LLViewerObject* getWornAttachment(const LLUUID& inv_item_id);
bool getAttachedPointName(const LLUUID& inv_item_id, std::string& name) const;
/*virtual*/ const LLViewerJointAttachment *attachObject(LLViewerObject *viewer_object);
/*virtual*/ BOOL detachObject(LLViewerObject *viewer_object);
static BOOL detachAttachmentIntoInventory(const LLUUID& item_id);
-private:
- // Track attachments that have been requested but have not arrived yet.
- mutable std::map<LLUUID,LLTimer> mAttachmentRequests;
-
//--------------------------------------------------------------------
// HUDs
//--------------------------------------------------------------------
diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp
index fac0fd63ee..d9d43a1653 100755
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -124,7 +124,7 @@ void LLPanelWearableOutfitItem::updateItem(const std::string& name,
// We don't use get_is_item_worn() here because this update is triggered by
// an inventory observer upon link in COF beind added or removed so actual
// worn status of a linked item may still remain unchanged.
- if (mWornIndicationEnabled && LLAppearanceMgr::instance().isLinkInCOF(mInventoryItemUUID))
+ if (mWornIndicationEnabled && LLAppearanceMgr::instance().isLinkedInCOF(mInventoryItemUUID))
{
search_label += LLTrans::getString("worn");
item_state = IS_WORN;