From 090d3097d569922b7c3b681cb77ff14a29a60b07 Mon Sep 17 00:00:00 2001
From: "Brad Payne (Vir Linden)" <vir@lindenlab.com>
Date: Wed, 18 Mar 2015 21:41:47 -0400
Subject: [PATCH] MAINT-4917 WIP - Attachment deferred bulk COF linking WIP

---
 indra/newview/llagentwearables.cpp  |  56 ++----------
 indra/newview/llappearancemgr.cpp   |  21 +----
 indra/newview/llappearancemgr.h     |   6 +-
 indra/newview/llattachmentsmgr.cpp  | 133 +++++++++++++++++++++-------
 indra/newview/llattachmentsmgr.h    |  25 ++++--
 indra/newview/llinventorybridge.cpp |  12 +--
 indra/newview/llvoavatarself.cpp    |  41 +--------
 indra/newview/llvoavatarself.h      |   7 --
 8 files changed, 141 insertions(+), 160 deletions(-)

diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 69eb478cd85..4a395f547ae 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"
@@ -1373,55 +1374,16 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra
 	S32 obj_count = obj_item_array.size();
 	if (obj_count > 0)
 	{
-		LL_DEBUGS("Avatar") << "ATT [RezMultipleAttachmentsFromInv] attaching multiple, total obj_count " << obj_count << LL_ENDL;
+		LL_DEBUGS("Avatar") << "ATT attaching multiple, total obj_count " << obj_count << LL_ENDL;
 	}
 
-	// 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 )
-	{
-		obj_count = MAX_OBJECTS_TO_SEND;
-	}
-				
-	// 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();
-		LL_DEBUGS("Avatar") << "ATT requesting " << item->getName()
-							<< " " << item->getLinkedUUID() << LL_ENDL;
-		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 24169c152bb..258d69d546c 100755
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -525,7 +525,7 @@ LLUpdateAppearanceAndEditWearableOnDestroy::LLUpdateAppearanceAndEditWearableOnD
 {
 }
 
-LLRequestAppearanceUpdateOnDestroy::~LLRequestAppearanceUpdateOnDestroy()
+LLRequestServerAppearanceUpdateOnDestroy::~LLRequestServerAppearanceUpdateOnDestroy()
 {
 	LL_DEBUGS("Avatar") << "ATT requesting server appearance update" << LL_ENDL;
 	LLAppearanceMgr::instance().requestServerAppearanceUpdate();
@@ -3925,24 +3925,7 @@ void LLAppearanceMgr::registerAttachment(const LLUUID& item_id)
 						<< (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
 	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))
-		{
-			LL_DEBUGS("Avatar") << "ATT adding COF link for attachment "
-								<< (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
-			// FIXME replace with just a call to request bake update?
-			LLPointer<LLInventoryCallback> cb = new LLRequestAppearanceUpdateOnDestroy();
-			LLAppearanceMgr::addCOFItemLink(item_id, cb);  // Add COF link for item.
-		}
-	}
-	else
-	{
-		//LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
-	}
+	LLAttachmentsMgr::instance().onAttachmentArrived(item_id);
 }
 
 void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id)
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index e6094d4b536..dfe392e3b75 100755
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -306,11 +306,11 @@ class LLUpdateAppearanceAndEditWearableOnDestroy: public LLInventoryCallback
 	LLUUID mItemID;
 };
 
-class LLRequestAppearanceUpdateOnDestroy: public LLInventoryCallback
+class LLRequestServerAppearanceUpdateOnDestroy: public LLInventoryCallback
 {
 public:
-	LLRequestAppearanceUpdateOnDestroy() {}
-	~LLRequestAppearanceUpdateOnDestroy();
+	LLRequestServerAppearanceUpdateOnDestroy() {}
+	~LLRequestServerAppearanceUpdateOnDestroy();
 
 	/* virtual */ void fire(const LLUUID& item_id) {}
 };
diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp
index 64dcaab6c01..cc83500491a 100755
--- a/indra/newview/llattachmentsmgr.cpp
+++ b/indra/newview/llattachmentsmgr.cpp
@@ -44,9 +44,9 @@ 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);
 	LL_DEBUGS("Avatar") << "ATT adding attachment to mPendingAttachments "
@@ -57,6 +57,8 @@ void LLAttachmentsMgr::addAttachment(const LLUUID& item_id,
 	attachment.mAttachmentPt = attachment_pt;
 	attachment.mAdd = add;
 	mPendingAttachments.push_back(attachment);
+
+    addAttachmentRequestTime(item_id);
 }
 
 // static
@@ -73,7 +75,9 @@ void LLAttachmentsMgr::onIdle()
 		return;
 	}
 
-	linkPendingAttachments();
+	requestPendingAttachments();
+
+    linkRecentlyArrivedAttachments();
 }
 
 class LLAttachAfterLinkCallback: public LLInventoryCallback
@@ -111,39 +115,58 @@ class LLAttachAfterLinkCallback: public LLInventoryCallback
 	LLAttachmentsMgr::attachments_vec_t mToLinkAndAttach;
 };
 
-//#define COF_LINK_FIRST
-
-void LLAttachmentsMgr::linkPendingAttachments()
+void LLAttachmentsMgr::requestPendingAttachments()
 {
 	if (mPendingAttachments.size())
 	{
-#ifdef COF_LINK_FIRST
-		LLPointer<LLInventoryCallback> cb = new LLAttachAfterLinkCallback(mPendingAttachments);
-		LLInventoryObject::const_object_list_t inv_items_to_link;
-		LL_DEBUGS("Avatar") << "ATT requesting COF links for " << mPendingAttachments.size() << " object(s):" << LL_ENDL;
-		for (attachments_vec_t::const_iterator it = mPendingAttachments.begin();
-			 it != mPendingAttachments.end(); ++it)
-		{
-			const AttachmentsInfo& att_info = *it;
-			LLViewerInventoryItem *item = gInventory.getItem(att_info.mItemID);
-			if (item)
-			{
-				LL_DEBUGS("Avatar") << "ATT - requesting COF link for " << item->getName() << LL_ENDL;
-				inv_items_to_link.push_back(item);
-			}
-			else
-			{
-				LL_WARNS() << "ATT unable to link requested attachment " << att_info.mItemID
-						   << ", item not found in inventory" << LL_ENDL;
-			}
-		}
-		link_inventory_array(LLAppearanceMgr::instance().getCOF(), inv_items_to_link, cb);
-#else
 		requestAttachments(mPendingAttachments);
-#endif
 		mPendingAttachments.clear();
 	}
+}
+
+void LLAttachmentsMgr::linkRecentlyArrivedAttachments()
+{
+    const F32 COF_LINK_BATCH_TIME = 5.0F;
+    
+    // One or more attachments have arrived but not been processed for COF links
+    if (mRecentlyArrivedAttachments.size())
+    {
+        if (mAttachmentRequests.empty())
+        {
+            // Not waiting for more
+            LL_DEBUGS("Avatar") << "ATT all pending attachments have arrived" << LL_ENDL;
+        }
+        else if (mCOFLinkBatchTimer.getElapsedTimeF32() > COF_LINK_BATCH_TIME)
+        {
+            LL_DEBUGS("Avatar") << mAttachmentRequests.size()
+                                << "ATT pending attachments have not arrived but wait time exceeded" << LL_ENDL;
+        }
+        else
+        {
+            return;
+        }
 
+        LL_DEBUGS("Avatar") << "ATT requesting COF links for " << mRecentlyArrivedAttachments.size() << LL_ENDL;
+		LLInventoryObject::const_object_list_t inv_items_to_link;
+        for (std::set<LLUUID>::iterator it = mRecentlyArrivedAttachments.begin();
+             it != mRecentlyArrivedAttachments.end(); ++it)
+        {
+            if (!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();
+    }
 }
 
 // FIXME this is basically the same code as LLAgentWearables::userAttachMultipleAttachments(),
@@ -225,3 +248,53 @@ void LLAttachmentsMgr::requestAttachments(const attachments_vec_t& attachment_re
 		i++;
 	}
 }
+
+void LLAttachmentsMgr::addAttachmentRequestTime(const LLUUID& inv_item_id)
+{
+    LLInventoryItem *item = gInventory.getItem(inv_item_id);
+    LL_DEBUGS("Avatar") << "ATT add request time " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+	LLTimer current_time;
+	mAttachmentRequests[inv_item_id] = current_time;
+}
+
+void LLAttachmentsMgr::removeAttachmentRequestTime(const LLUUID& inv_item_id)
+{
+    LLInventoryItem *item = gInventory.getItem(inv_item_id);
+    LL_DEBUGS("Avatar") << "ATT remove request time " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+	mAttachmentRequests.erase(inv_item_id);
+}
+
+BOOL LLAttachmentsMgr::attachmentWasRequestedRecently(const LLUUID& inv_item_id, F32 seconds) const
+{
+	std::map<LLUUID,LLTimer>::const_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 > seconds)
+		{
+            LLInventoryItem *item = gInventory.getItem(inv_item_id);
+            LL_DEBUGS("Avatar") << "ATT time ignored, exceeded " << seconds
+                                << " " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+			return FALSE;
+		}
+		else
+		{
+			return TRUE;
+		}
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+void LLAttachmentsMgr::onAttachmentArrived(const LLUUID& inv_item_id)
+{
+    removeAttachmentRequestTime(inv_item_id);
+    if (mRecentlyArrivedAttachments.empty())
+    {
+        mCOFLinkBatchTimer.reset();
+    }
+    mRecentlyArrivedAttachments.insert(inv_item_id);
+}
diff --git a/indra/newview/llattachmentsmgr.h b/indra/newview/llattachmentsmgr.h
index c145fa032b4..c84229475a8 100755
--- a/indra/newview/llattachmentsmgr.h
+++ b/indra/newview/llattachmentsmgr.h
@@ -60,18 +60,31 @@ class LLAttachmentsMgr: public LLSingleton<LLAttachmentsMgr>
 	LLAttachmentsMgr();
 	virtual ~LLAttachmentsMgr();
 
-	void addAttachment(const LLUUID& item_id,
-					   const U8 attachment_pt,
-					   const BOOL add);
+	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 *);
 
-protected:
-	void onIdle();
-	void linkPendingAttachments();
+	BOOL attachmentWasRequestedRecently(const LLUUID& inv_item_id, F32 seconds) const;
+	void addAttachmentRequestTime(const LLUUID& inv_item_id);
+    void onAttachmentArrived(const LLUUID& inv_item_id);
 
 private:
+	void removeAttachmentRequestTime(const LLUUID& inv_item_id);
+	void onIdle();
+	void requestPendingAttachments();
+	void linkRecentlyArrivedAttachments();
+
+    // 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.
+	std::map<LLUUID,LLTimer> mAttachmentRequests;
+
+    // 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 32d1da434ed..e44de6f91ea 100755
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -5367,17 +5367,15 @@ void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attach
 	const LLUUID& item_id = item->getLinkedUUID();
 
 	// Check for duplicate request.
+	const F32 MAX_TIME_SINCE_REQUEST = 5.0F;
 	if (isAgentAvatarValid() &&
-		(gAgentAvatarp->attachmentWasRequested(item_id) ||
+		(LLAttachmentsMgr::instance().attachmentWasRequestedRecently(item_id, MAX_TIME_SINCE_REQUEST) ||
 		 gAgentAvatarp->isWearingAttachment(item_id)))
 	{
 		LL_WARNS() << "ATT duplicate attachment request, ignoring" << LL_ENDL;
 		return;
 	}
 
-	LL_DEBUGS("Avatar") << "ATT add rez request for " << item->getName() << " id " << item_id << LL_ENDL;
-	gAgentAvatarp->addAttachmentRequest(item_id);
-
 	S32 attach_pt = 0;
 	if (isAgentAvatarValid() && attachment)
 	{
@@ -5432,10 +5430,8 @@ bool confirm_attachment_rez(const LLSD& notification, const LLSD& response)
 			U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
 			BOOL is_add = notification["payload"]["is_add"].asBoolean();
 
-			LL_DEBUGS("Avatar") << "ATT calling addAttachment " << (itemp ? itemp->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
-			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/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 3f3cd25f956..0859355b187 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"
@@ -1050,44 +1051,6 @@ BOOL LLVOAvatarSelf::isWearingAttachment(const LLUUID& inv_item_id) const
 	return FALSE;
 }
 
-//-----------------------------------------------------------------------------
-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()
 //-----------------------------------------------------------------------------
@@ -1154,8 +1117,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 5f36872575c..c6a038f5585 100755
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -289,19 +289,12 @@ class LLVOAvatarSelf :
 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
 	//--------------------------------------------------------------------
-- 
GitLab