From 626049851d444de5c46f10d30ed476ffed74f4c1 Mon Sep 17 00:00:00 2001
From: "Brad Payne (Vir Linden)" <vir@lindenlab.com>
Date: Fri, 19 Feb 2010 17:39:03 -0500
Subject: Work in progress on EXT-5333: Bodyparts missing from appearance and
 COF

---
 indra/newview/llappearancemgr.cpp | 373 +++++++++++++++++++++++++++++++-------
 1 file changed, 309 insertions(+), 64 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 74c45a9467..ae1a7a0cbe 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -324,21 +324,29 @@ void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
 
 struct LLFoundData
 {
-	LLFoundData() : mAssetType(LLAssetType::AT_NONE), mWearable(NULL) {}
+	LLFoundData() :
+		mAssetType(LLAssetType::AT_NONE),
+		mWearableType(WT_INVALID),
+		mWearable(NULL) {}
+
 	LLFoundData(const LLUUID& item_id,
-		    const LLUUID& asset_id,
-		    const std::string& name,
-		    LLAssetType::EType asset_type) :
+				const LLUUID& asset_id,
+				const std::string& name,
+				const LLAssetType::EType& asset_type,
+				const EWearableType& wearable_type
+		) :
 		mItemID(item_id),
 		mAssetID(asset_id),
 		mName(name),
 		mAssetType(asset_type),
+		mWearableType(wearable_type),
 		mWearable( NULL ) {}
 	
 	LLUUID mItemID;
 	LLUUID mAssetID;
 	std::string mName;
 	LLAssetType::EType mAssetType;
+	EWearableType mWearableType;
 	LLWearable* mWearable;
 };
 
@@ -349,14 +357,26 @@ public:
 	LLWearableHoldingPattern();
 	~LLWearableHoldingPattern();
 
-	bool pollCompletion();
+	bool pollFetchCompletion();
+	void onFetchCompletion();
 	bool isFetchCompleted();
 	bool isTimedOut();
+
+	void checkMissingWearables();
+	bool pollMissingWearables();
+	bool isMissingCompleted();
+	void recoverMissingWearable(EWearableType type);
+	void clearCOFLinksForMissingWearables();
+	
+	void onAllComplete();
 	
 	typedef std::list<LLFoundData> found_list_t;
 	found_list_t mFoundList;
 	LLInventoryModel::item_array_t mObjItems;
 	LLInventoryModel::item_array_t mGestItems;
+	typedef std::set<S32> type_set_t;
+	type_set_t mTypesToRecover;
+	type_set_t mTypesToLink;
 	S32 mResolved;
 	LLTimer mWaitTime;
 	bool mFired;
@@ -383,13 +403,99 @@ bool LLWearableHoldingPattern::isTimedOut()
 	return mWaitTime.getElapsedTimeF32() > max_wait_time; 
 }
 
-bool LLWearableHoldingPattern::pollCompletion()
+void LLWearableHoldingPattern::checkMissingWearables()
+{
+	std::vector<S32> found_by_type(WT_COUNT,0);
+	std::vector<S32> requested_by_type(WT_COUNT,0);
+	for (found_list_t::iterator it = mFoundList.begin(); it != mFoundList.end(); ++it)
+	{
+		LLFoundData &data = *it;
+		if (data.mWearableType < WT_COUNT)
+			requested_by_type[data.mWearableType]++;
+		if (data.mWearable)
+			found_by_type[data.mWearableType]++;
+	}
+
+	for (S32 type = 0; type < WT_COUNT; ++type)
+	{
+		llinfos << "type " << type << " requested " << requested_by_type[type] << " found " << found_by_type[type] << llendl;
+		if (found_by_type[type] > 0)
+			continue;
+		if (
+			// Need to recover if at least one wearable of that type
+			// was requested but none was found (prevent missing
+			// pants)
+			(requested_by_type[type] > 0) ||  
+			// or if type is a body part and no wearables were found.
+			((type == WT_SHAPE) || (type == WT_SKIN) || (type == WT_HAIR) || (type == WT_EYES)))
+		{
+			mTypesToRecover.insert(type);
+			mTypesToLink.insert(type);
+			recoverMissingWearable((EWearableType)type);
+			llwarns << "need to replace " << type << llendl; 
+		}
+	}
+
+	if (!pollMissingWearables())
+	{
+		mWaitTime.reset();
+		doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this));
+	}
+}
+
+void LLWearableHoldingPattern::onAllComplete()
+{
+	// Activate all gestures in this folder
+	if (mGestItems.count() > 0)
+	{
+		llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
+		
+		LLGestureManager::instance().activateGestures(mGestItems);
+		
+		// Update the inventory item labels to reflect the fact
+		// they are active.
+		LLViewerInventoryCategory* catp =
+			gInventory.getCategory(LLAppearanceManager::instance().getCOF());
+		
+		if (catp)
+		{
+			gInventory.updateCategory(catp);
+			gInventory.notifyObservers();
+		}
+	}
+
+	// Update wearables.
+	llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
+	LLAppearanceManager::instance().updateAgentWearables(this, false);
+	
+	// Update attachments to match those requested.
+	LLVOAvatar* avatar = gAgent.getAvatarObject();
+	if( avatar )
+	{
+		llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
+		LLAgentWearables::userUpdateAttachments(mObjItems);
+	}
+
+	if (isFetchCompleted() && isMissingCompleted())
+	{
+		// Only safe to delete if all wearable callbacks and all missing wearables completed.
+		delete this;
+	}
+}
+
+void LLWearableHoldingPattern::onFetchCompletion()
+{
+	checkMissingWearables();
+}
+
+// Runs as an idle callback until all wearables are fetched (or we time out).
+bool LLWearableHoldingPattern::pollFetchCompletion()
 {
 	bool completed = isFetchCompleted();
 	bool timed_out = isTimedOut();
 	bool done = completed || timed_out;
 	
-	llinfos << "polling, done status: " << completed << " timed out? " << timed_out << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
+	llinfos << "polling, done status: " << completed << " timed out " << timed_out << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
 
 	if (done)
 	{
@@ -400,46 +506,193 @@ bool LLWearableHoldingPattern::pollCompletion()
 			llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
 		}
 
-		// Activate all gestures in this folder
-		if (mGestItems.count() > 0)
-		{
-			llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
-			
-			LLGestureManager::instance().activateGestures(mGestItems);
-			
-			// Update the inventory item labels to reflect the fact
-			// they are active.
-			LLViewerInventoryCategory* catp =
-				gInventory.getCategory(LLAppearanceManager::instance().getCOF());
+		onFetchCompletion();
+	}
+	return done;
+}
 
-			if (catp)
-			{
-				gInventory.updateCategory(catp);
-				gInventory.notifyObservers();
-			}
-		}
+class RecoveredItemLinkCB: public LLInventoryCallback
+{
+public:
+	RecoveredItemLinkCB(EWearableType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
+		mHolder(holder),
+		mWearable(wearable),
+		mType(type)
+	{
+	}
+	void fire(const LLUUID& item_id)
+	{
+		llinfos << "Recovered item link for type " << mType << llendl;
+		mHolder->mTypesToLink.erase(mType);
+		// Add wearable to FoundData for actual wearing
+		LLViewerInventoryItem *item = gInventory.getItem(item_id);
+		LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
 
-		// Update wearables.
-		llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
-		LLAppearanceManager::instance().updateAgentWearables(this, false);
-		
-		// Update attachments to match those requested.
-		LLVOAvatar* avatar = gAgent.getAvatarObject();
-		if( avatar )
+		gInventory.addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
+			
+		if (item && linked_item)
 		{
-			llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
-			LLAgentWearables::userUpdateAttachments(mObjItems);
+			LLFoundData found(linked_item->getUUID(),
+							  linked_item->getAssetUUID(),
+							  linked_item->getName(),
+							  linked_item->getType(),
+							  linked_item->isWearableType() ? linked_item->getWearableType() : WT_INVALID
+				);
+			found.mWearable = mWearable;
+			mHolder->mFoundList.push_front(found);
 		}
+		else
+		{
+			llwarns << "inventory item or link not found for recovered wearable" << llendl;
+		}
+	}
+private:
+	LLWearableHoldingPattern* mHolder;
+	LLWearable *mWearable;
+	EWearableType mType;
+};
+
+class RecoveredItemCB: public LLInventoryCallback
+{
+public:
+	RecoveredItemCB(EWearableType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
+		mHolder(holder),
+		mWearable(wearable),
+		mType(type)
+	{
+	}
+	void fire(const LLUUID& item_id)
+	{
+		llinfos << "Recovered item for type " << mType << llendl;
+		LLViewerInventoryItem *itemp = gInventory.getItem(item_id);
+		LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder);
+		mHolder->mTypesToRecover.erase(mType);
+		link_inventory_item( gAgent.getID(),
+							 item_id,
+							 LLAppearanceManager::instance().getCOF(),
+							 itemp->getName(),
+							 LLAssetType::AT_LINK,
+							 cb);
+	}
+private:
+	LLWearableHoldingPattern* mHolder;
+	LLWearable *mWearable;
+	EWearableType mType;
+};
 
-		if (completed)
+void LLWearableHoldingPattern::recoverMissingWearable(EWearableType type)
+{
+		// Try to recover by replacing missing wearable with a new one.
+	LLNotificationsUtil::add("ReplacedMissingWearable");
+	lldebugs << "Wearable " << LLWearableDictionary::getTypeLabel(type)
+			 << " could not be downloaded.  Replaced inventory item with default wearable." << llendl;
+	LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
+
+	// Add a new one in the lost and found folder.
+	const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
+	LLPointer<LLInventoryCallback> cb = new RecoveredItemCB(type,wearable,this);
+
+	create_inventory_item(gAgent.getID(),
+						  gAgent.getSessionID(),
+						  lost_and_found_id,
+						  wearable->getTransactionID(),
+						  wearable->getName(),
+						  wearable->getDescription(),
+						  wearable->getAssetType(),
+						  LLInventoryType::IT_WEARABLE,
+						  wearable->getType(),
+						  wearable->getPermissions().getMaskNextOwner(),
+						  cb);
+}
+
+bool LLWearableHoldingPattern::isMissingCompleted()
+{
+	return mTypesToLink.size()==0 && mTypesToRecover.size()==0;
+}
+
+void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
+{
+	for (found_list_t::iterator it = mFoundList.begin(); it != mFoundList.end(); ++it)
+	{
+		LLFoundData &data = *it;
+		if ((data.mWearableType < WT_COUNT) && (!data.mWearable))
 		{
-			// Only safe to delete if all wearable callbacks completed.
-			delete this;
+			// Wearable link that was never resolved; remove links to it from COF
+			llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl;
+			LLAppearanceManager::instance().removeCOFItemLinks(data.mItemID,false);
 		}
 	}
+}
+
+bool LLWearableHoldingPattern::pollMissingWearables()
+{
+	bool timed_out = isTimedOut();
+	bool missing_completed = isMissingCompleted();
+	bool done = timed_out || missing_completed;
+	
+	llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size()
+			<< " links " << mTypesToLink.size()
+			<< " wearables, timed out " << timed_out
+			<< " elapsed " << mWaitTime.getElapsedTimeF32()
+			<< " done " << done << llendl;
+
+	if (done)
+	{
+		clearCOFLinksForMissingWearables();
+		onAllComplete();
+	}
 	return done;
 }
 
+static void onWearableAssetFetch(LLWearable* wearable, void* data)
+{
+	LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
+	holder->mResolved += 1;  // just counting callbacks, not successes.
+	llinfos << "onWearableAssetFetch, resolved " << holder->mResolved << " requested " << holder->mFoundList.size() << llendl;
+	if (wearable)
+	{
+		llinfos << "wearable type " << wearable->getType() << llendl;
+	}
+	else
+	{
+		llinfos << "wearable: NONE" << llendl;
+	}
+
+	if (holder->mFired)
+	{
+		llwarns << "called after holder fired" << llendl;
+		return;
+	}
+
+	if (!wearable)
+	{
+		llwarns << "no wearable found" << llendl;
+		return;
+	}
+
+#if 0
+	if ((wearable->getType() == WT_SHAPE) || (wearable->getType() == WT_JACKET))
+	{
+		llwarns << "Forcing failure for type " << wearable->getType() << llendl;
+		return;
+	}
+#endif
+
+	for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
+		 iter != holder->mFoundList.end(); ++iter)
+	{
+		LLFoundData& data = *iter;
+		if(wearable->getAssetID() == data.mAssetID)
+		{
+			data.mWearable = wearable;
+			// Failing this means inventory or asset server are corrupted in a way we don't handle.
+			llassert((data.mWearableType < WT_COUNT) && (wearable->getType() == data.mWearableType));
+			break;
+		}
+	}
+}
+
+
 static void removeDuplicateItems(LLInventoryModel::item_array_t& items)
 {
 	LLInventoryModel::item_array_t new_items;
@@ -467,30 +720,6 @@ static void removeDuplicateItems(LLInventoryModel::item_array_t& items)
 	items = new_items;
 }
 
-static void onWearableAssetFetch(LLWearable* wearable, void* data)
-{
-	LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
-	if (holder->mFired)
-	{
-		llwarns << "called after holder fired" << llendl;
-	}
-
-	if(wearable)
-	{
-		for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
-			 iter != holder->mFoundList.end(); ++iter)
-		{
-			LLFoundData& data = *iter;
-			if(wearable->getAssetID() == data.mAssetID)
-			{
-				data.mWearable = wearable;
-				break;
-			}
-		}
-	}
-	holder->mResolved += 1;
-}
-
 const LLUUID LLAppearanceManager::getCOF() const
 {
 	return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
@@ -888,6 +1117,8 @@ void LLAppearanceManager::updateAppearanceFromCOF()
 	// wearables can be resolved immediately, then the
 	// callback will be called (and this object deleted)
 	// before the final getNextData().
+
+	// BAP cleanup - no point having found_container when mFoundList already has all the info.
 	LLDynamicArray<LLFoundData> found_container;
 	for(S32 i = 0; i  < wear_items.count(); ++i)
 	{
@@ -898,7 +1129,19 @@ void LLAppearanceManager::updateAppearanceFromCOF()
 			LLFoundData found(linked_item->getUUID(),
 							  linked_item->getAssetUUID(),
 							  linked_item->getName(),
-							  linked_item->getType());
+							  linked_item->getType(),
+							  linked_item->isWearableType() ? linked_item->getWearableType() : WT_INVALID
+				);
+
+#if 1
+			// BAP REMOVEME Force failure to test handling
+			if (found.mWearableType == WT_SHAPE || found.mWearableType == WT_JACKET)
+			{
+				found.mAssetID.generate(); // Replace with new UUID, guaranteed not to exist in DB
+				
+			}
+#endif
+
 			holder->mFoundList.push_front(found);
 			found_container.put(found);
 		}
@@ -918,7 +1161,9 @@ void LLAppearanceManager::updateAppearanceFromCOF()
 	for(S32 i = 0; i < found_container.count(); ++i)
 	{
 		LLFoundData& found = found_container.get(i);
-				
+
+		llinfos << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl;
+
 		// Fetch the wearables about to be worn.
 		LLWearableList::instance().getAsset(found.mAssetID,
 											found.mName,
@@ -928,9 +1173,9 @@ void LLAppearanceManager::updateAppearanceFromCOF()
 
 	}
 
-	if (!holder->pollCompletion())
+	if (!holder->pollFetchCompletion())
 	{
-		doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollCompletion,holder));
+		doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollFetchCompletion,holder));
 	}
 
 }
-- 
cgit v1.2.3