diff options
| -rw-r--r-- | indra/newview/llappearancemgr.cpp | 373 | 
1 files changed, 309 insertions, 64 deletions
| 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));  	}  } | 
