PageRenderTime 49ms CodeModel.GetById 12ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llagentwearablesfetch.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 599 lines | 455 code | 82 blank | 62 comment | 58 complexity | fab568b2876c497daaf58d9a396495e8 MD5 | raw file
  1/** 
  2 * @file llagentwearablesfetch.cpp
  3 * @brief LLAgentWearblesFetch class implementation
  4 *
  5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28#include "llagentwearablesfetch.h"
 29
 30#include "llagent.h"
 31#include "llagentwearables.h"
 32#include "llappearancemgr.h"
 33#include "llinventoryfunctions.h"
 34#include "llstartup.h"
 35#include "llvoavatarself.h"
 36
 37
 38class LLOrderMyOutfitsOnDestroy: public LLInventoryCallback
 39{
 40public:
 41	LLOrderMyOutfitsOnDestroy() {};
 42
 43	virtual ~LLOrderMyOutfitsOnDestroy()
 44	{
 45		if (!LLApp::isRunning())
 46		{
 47			llwarns << "called during shutdown, skipping" << llendl;
 48			return;
 49		}
 50		
 51		const LLUUID& my_outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
 52		if (my_outfits_id.isNull()) return;
 53
 54		LLInventoryModel::cat_array_t* cats;
 55		LLInventoryModel::item_array_t* items;
 56		gInventory.getDirectDescendentsOf(my_outfits_id, cats, items);
 57		if (!cats) return;
 58
 59		//My Outfits should at least contain saved initial outfit and one another outfit
 60		if (cats->size() < 2)
 61		{
 62			llwarning("My Outfits category was not populated properly", 0);
 63			return;
 64		}
 65
 66		llinfos << "Starting updating My Outfits with wearables ordering information" << llendl;
 67
 68		for (LLInventoryModel::cat_array_t::iterator outfit_iter = cats->begin();
 69			outfit_iter != cats->end(); ++outfit_iter)
 70		{
 71			const LLUUID& cat_id = (*outfit_iter)->getUUID();
 72			if (cat_id.isNull()) continue;
 73
 74			// saved initial outfit already contains wearables ordering information
 75			if (cat_id == LLAppearanceMgr::getInstance()->getBaseOutfitUUID()) continue;
 76
 77			LLAppearanceMgr::getInstance()->updateClothingOrderingInfo(cat_id);
 78		}
 79
 80		llinfos << "Finished updating My Outfits with wearables ordering information" << llendl;
 81	}
 82
 83	/* virtual */ void fire(const LLUUID& inv_item) {};
 84};
 85
 86
 87LLInitialWearablesFetch::LLInitialWearablesFetch(const LLUUID& cof_id) :
 88	LLInventoryFetchDescendentsObserver(cof_id)
 89{
 90}
 91
 92LLInitialWearablesFetch::~LLInitialWearablesFetch()
 93{
 94}
 95
 96// virtual
 97void LLInitialWearablesFetch::done()
 98{
 99	// Delay processing the actual results of this so it's not handled within
100	// gInventory.notifyObservers.  The results will be handled in the next
101	// idle tick instead.
102	gInventory.removeObserver(this);
103	doOnIdleOneTime(boost::bind(&LLInitialWearablesFetch::processContents,this));
104}
105
106void LLInitialWearablesFetch::add(InitialWearableData &data)
107
108{
109	mAgentInitialWearables.push_back(data);
110}
111
112void LLInitialWearablesFetch::processContents()
113{
114	if(!gAgentAvatarp) //no need to process wearables if the agent avatar is deleted.
115	{
116		delete this;
117		return ;
118	}
119
120	// Fetch the wearable items from the Current Outfit Folder
121	LLInventoryModel::cat_array_t cat_array;
122	LLInventoryModel::item_array_t wearable_array;
123	LLFindWearables is_wearable;
124	llassert_always(mComplete.size() != 0);
125	gInventory.collectDescendentsIf(mComplete.front(), cat_array, wearable_array, 
126									LLInventoryModel::EXCLUDE_TRASH, is_wearable);
127
128	LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
129	if (wearable_array.count() > 0)
130	{
131		gAgentWearables.notifyLoadingStarted();
132		LLAppearanceMgr::instance().updateAppearanceFromCOF();
133	}
134	else
135	{
136		// if we're constructing the COF from the wearables message, we don't have a proper outfit link
137		LLAppearanceMgr::instance().setOutfitDirty(true);
138		processWearablesMessage();
139	}
140	delete this;
141}
142
143class LLFetchAndLinkObserver: public LLInventoryFetchItemsObserver
144{
145public:
146	LLFetchAndLinkObserver(uuid_vec_t& ids):
147		LLInventoryFetchItemsObserver(ids)
148	{
149	}
150	~LLFetchAndLinkObserver()
151	{
152	}
153	virtual void done()
154	{
155		gInventory.removeObserver(this);
156
157		// Link to all fetched items in COF.
158		LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy;
159		for (uuid_vec_t::iterator it = mIDs.begin();
160			 it != mIDs.end();
161			 ++it)
162		{
163			LLUUID id = *it;
164			LLViewerInventoryItem *item = gInventory.getItem(*it);
165			if (!item)
166			{
167				llwarns << "fetch failed!" << llendl;
168				continue;
169			}
170
171			link_inventory_item(gAgent.getID(),
172								item->getLinkedUUID(),
173								LLAppearanceMgr::instance().getCOF(),
174								item->getName(),
175								item->getDescription(),
176								LLAssetType::AT_LINK,
177								link_waiter);
178		}
179	}
180};
181
182void LLInitialWearablesFetch::processWearablesMessage()
183{
184	if (!mAgentInitialWearables.empty()) // We have an empty current outfit folder, use the message data instead.
185	{
186		const LLUUID current_outfit_id = LLAppearanceMgr::instance().getCOF();
187		uuid_vec_t ids;
188		for (U8 i = 0; i < mAgentInitialWearables.size(); ++i)
189		{
190			// Populate the current outfit folder with links to the wearables passed in the message
191			InitialWearableData *wearable_data = new InitialWearableData(mAgentInitialWearables[i]); // This will be deleted in the callback.
192			
193			if (wearable_data->mAssetID.notNull())
194			{
195				ids.push_back(wearable_data->mItemID);
196			}
197			else
198			{
199				llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID "
200				<< wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl;
201				delete wearable_data;
202			}
203		}
204
205		// Add all current attachments to the requested items as well.
206		if (isAgentAvatarValid())
207		{
208			for (LLVOAvatar::attachment_map_t::const_iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); 
209				 iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter)
210			{
211				LLViewerJointAttachment* attachment = iter->second;
212				if (!attachment) continue;
213				for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
214					 attachment_iter != attachment->mAttachedObjects.end();
215					 ++attachment_iter)
216				{
217					LLViewerObject* attached_object = (*attachment_iter);
218					if (!attached_object) continue;
219					const LLUUID& item_id = attached_object->getAttachmentItemID();
220					if (item_id.isNull()) continue;
221					ids.push_back(item_id);
222				}
223			}
224		}
225
226		// Need to fetch the inventory items for ids, then create links to them after they arrive.
227		LLFetchAndLinkObserver *fetcher = new LLFetchAndLinkObserver(ids);
228		fetcher->startFetch();
229		// If no items to be fetched, done will never be triggered.
230		// TODO: Change LLInventoryFetchItemsObserver::fetchItems to trigger done() on this condition.
231		if (fetcher->isFinished())
232		{
233			fetcher->done();
234		}
235		else
236		{
237			gInventory.addObserver(fetcher);
238		}
239	}
240	else
241	{
242		LL_WARNS("Wearables") << "No current outfit folder items found and no initial wearables fallback message received." << LL_ENDL;
243	}
244}
245
246LLLibraryOutfitsFetch::LLLibraryOutfitsFetch(const LLUUID& my_outfits_id) : 
247	LLInventoryFetchDescendentsObserver(my_outfits_id),
248	mCurrFetchStep(LOFS_FOLDER), 
249	mOutfitsPopulated(false) 
250{
251	llinfos << "created" << llendl;
252
253	mMyOutfitsID = LLUUID::null;
254	mClothingID = LLUUID::null;
255	mLibraryClothingID = LLUUID::null;
256	mImportedClothingID = LLUUID::null;
257	mImportedClothingName = "Imported Library Clothing";
258}
259
260LLLibraryOutfitsFetch::~LLLibraryOutfitsFetch()
261{
262	llinfos << "destroyed" << llendl;
263}
264
265void LLLibraryOutfitsFetch::done()
266{
267	llinfos << "start" << llendl;
268
269	// Delay this until idle() routine, since it's a heavy operation and
270	// we also can't have it run within notifyObservers.
271	doOnIdleOneTime(boost::bind(&LLLibraryOutfitsFetch::doneIdle,this));
272	gInventory.removeObserver(this); // Prevent doOnIdleOneTime from being added twice.
273}
274
275void LLLibraryOutfitsFetch::doneIdle()
276{
277	llinfos << "start" << llendl;
278
279	gInventory.addObserver(this); // Add this back in since it was taken out during ::done()
280	
281	switch (mCurrFetchStep)
282	{
283		case LOFS_FOLDER:
284			folderDone();
285			mCurrFetchStep = LOFS_OUTFITS;
286			break;
287		case LOFS_OUTFITS:
288			outfitsDone();
289			mCurrFetchStep = LOFS_LIBRARY;
290			break;
291		case LOFS_LIBRARY:
292			libraryDone();
293			mCurrFetchStep = LOFS_IMPORTED;
294			break;
295		case LOFS_IMPORTED:
296			importedFolderDone();
297			mCurrFetchStep = LOFS_CONTENTS;
298			break;
299		case LOFS_CONTENTS:
300			contentsDone();
301			break;
302		default:
303			llwarns << "Got invalid state for outfit fetch: " << mCurrFetchStep << llendl;
304			mOutfitsPopulated = TRUE;
305			break;
306	}
307
308	// We're completely done.  Cleanup.
309	if (mOutfitsPopulated)
310	{
311		gInventory.removeObserver(this);
312		delete this;
313		return;
314	}
315}
316
317void LLLibraryOutfitsFetch::folderDone()
318{
319	llinfos << "start" << llendl;
320
321	LLInventoryModel::cat_array_t cat_array;
322	LLInventoryModel::item_array_t wearable_array;
323	gInventory.collectDescendents(mMyOutfitsID, cat_array, wearable_array, 
324								  LLInventoryModel::EXCLUDE_TRASH);
325	
326	// Early out if we already have items in My Outfits
327	// except the case when My Outfits contains just initial outfit
328	if (cat_array.count() > 1)
329	{
330		mOutfitsPopulated = true;
331		return;
332	}
333
334	mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING);
335	mLibraryClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true);
336
337	// If Library->Clothing->Initial Outfits exists, use that.
338	LLNameCategoryCollector matchFolderFunctor("Initial Outfits");
339	cat_array.clear();
340	gInventory.collectDescendentsIf(mLibraryClothingID,
341									cat_array, wearable_array, 
342									LLInventoryModel::EXCLUDE_TRASH,
343									matchFolderFunctor);
344	if (cat_array.count() > 0)
345	{
346		const LLViewerInventoryCategory *cat = cat_array.get(0);
347		mLibraryClothingID = cat->getUUID();
348	}
349
350	mComplete.clear();
351	
352	// Get the complete information on the items in the inventory.
353	uuid_vec_t folders;
354	folders.push_back(mClothingID);
355	folders.push_back(mLibraryClothingID);
356	setFetchIDs(folders);
357	startFetch();
358	if (isFinished())
359	{
360		done();
361	}
362}
363
364void LLLibraryOutfitsFetch::outfitsDone()
365{
366	llinfos << "start" << llendl;
367
368	LLInventoryModel::cat_array_t cat_array;
369	LLInventoryModel::item_array_t wearable_array;
370	uuid_vec_t folders;
371	
372	// Collect the contents of the Library's Clothing folder
373	gInventory.collectDescendents(mLibraryClothingID, cat_array, wearable_array, 
374								  LLInventoryModel::EXCLUDE_TRASH);
375	
376	llassert(cat_array.count() > 0);
377	for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin();
378		 iter != cat_array.end();
379		 ++iter)
380	{
381		const LLViewerInventoryCategory *cat = iter->get();
382		
383		// Get the names and id's of every outfit in the library, skip "Ruth"
384		// because it's a low quality legacy outfit
385		if (cat->getName() != "Ruth")
386		{
387			// Get the name of every outfit in the library 
388			folders.push_back(cat->getUUID());
389			mLibraryClothingFolders.push_back(cat->getUUID());
390		}
391	}
392	cat_array.clear();
393	wearable_array.clear();
394
395	// Check if you already have an "Imported Library Clothing" folder
396	LLNameCategoryCollector matchFolderFunctor(mImportedClothingName);
397	gInventory.collectDescendentsIf(mClothingID, 
398									cat_array, wearable_array, 
399									LLInventoryModel::EXCLUDE_TRASH,
400									matchFolderFunctor);
401	if (cat_array.size() > 0)
402	{
403		const LLViewerInventoryCategory *cat = cat_array.get(0);
404		mImportedClothingID = cat->getUUID();
405	}
406	
407	mComplete.clear();
408	setFetchIDs(folders);
409	startFetch();
410	if (isFinished())
411	{
412		done();
413	}
414}
415
416class LLLibraryOutfitsCopyDone: public LLInventoryCallback
417{
418public:
419	LLLibraryOutfitsCopyDone(LLLibraryOutfitsFetch * fetcher):
420	mFireCount(0), mLibraryOutfitsFetcher(fetcher)
421	{
422	}
423	
424	virtual ~LLLibraryOutfitsCopyDone()
425	{
426		if (!LLApp::isExiting() && mLibraryOutfitsFetcher)
427		{
428			gInventory.addObserver(mLibraryOutfitsFetcher);
429			mLibraryOutfitsFetcher->done();
430		}
431	}
432	
433	/* virtual */ void fire(const LLUUID& inv_item)
434	{
435		mFireCount++;
436	}
437private:
438	U32 mFireCount;
439	LLLibraryOutfitsFetch * mLibraryOutfitsFetcher;
440};
441
442// Copy the clothing folders from the library into the imported clothing folder
443void LLLibraryOutfitsFetch::libraryDone()
444{
445	llinfos << "start" << llendl;
446
447	if (mImportedClothingID != LLUUID::null)
448	{
449		// Skip straight to fetching the contents of the imported folder
450		importedFolderFetch();
451		return;
452	}
453
454	// Remove observer; next autopopulation step will be triggered externally by LLLibraryOutfitsCopyDone.
455	gInventory.removeObserver(this);
456	
457	LLPointer<LLInventoryCallback> copy_waiter = new LLLibraryOutfitsCopyDone(this);
458	mImportedClothingID = gInventory.createNewCategory(mClothingID,
459													   LLFolderType::FT_NONE,
460													   mImportedClothingName);
461	// Copy each folder from library into clothing unless it already exists.
462	for (uuid_vec_t::const_iterator iter = mLibraryClothingFolders.begin();
463		 iter != mLibraryClothingFolders.end();
464		 ++iter)
465	{
466		const LLUUID& src_folder_id = (*iter); // Library clothing folder ID
467		const LLViewerInventoryCategory *cat = gInventory.getCategory(src_folder_id);
468		if (!cat)
469		{
470			llwarns << "Library folder import for uuid:" << src_folder_id << " failed to find folder." << llendl;
471			continue;
472		}
473		
474		if (!LLAppearanceMgr::getInstance()->getCanMakeFolderIntoOutfit(src_folder_id))
475		{
476			llinfos << "Skipping non-outfit folder name:" << cat->getName() << llendl;
477			continue;
478		}
479		
480		// Don't copy the category if it already exists.
481		LLNameCategoryCollector matchFolderFunctor(cat->getName());
482		LLInventoryModel::cat_array_t cat_array;
483		LLInventoryModel::item_array_t wearable_array;
484		gInventory.collectDescendentsIf(mImportedClothingID, 
485										cat_array, wearable_array, 
486										LLInventoryModel::EXCLUDE_TRASH,
487										matchFolderFunctor);
488		if (cat_array.size() > 0)
489		{
490			continue;
491		}
492
493		LLUUID dst_folder_id = gInventory.createNewCategory(mImportedClothingID,
494															LLFolderType::FT_NONE,
495															cat->getName());
496		LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_folder_id, dst_folder_id, copy_waiter);
497	}
498}
499
500void LLLibraryOutfitsFetch::importedFolderFetch()
501{
502	llinfos << "start" << llendl;
503
504	// Fetch the contents of the Imported Clothing Folder
505	uuid_vec_t folders;
506	folders.push_back(mImportedClothingID);
507	
508	mComplete.clear();
509	setFetchIDs(folders);
510	startFetch();
511	if (isFinished())
512	{
513		done();
514	}
515}
516
517void LLLibraryOutfitsFetch::importedFolderDone()
518{
519	llinfos << "start" << llendl;
520
521	LLInventoryModel::cat_array_t cat_array;
522	LLInventoryModel::item_array_t wearable_array;
523	uuid_vec_t folders;
524	
525	// Collect the contents of the Imported Clothing folder
526	gInventory.collectDescendents(mImportedClothingID, cat_array, wearable_array, 
527								  LLInventoryModel::EXCLUDE_TRASH);
528	
529	for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin();
530		 iter != cat_array.end();
531		 ++iter)
532	{
533		const LLViewerInventoryCategory *cat = iter->get();
534		
535		// Get the name of every imported outfit
536		folders.push_back(cat->getUUID());
537		mImportedClothingFolders.push_back(cat->getUUID());
538	}
539	
540	mComplete.clear();
541	setFetchIDs(folders);
542	startFetch();
543	if (isFinished())
544	{
545		done();
546	}
547}
548
549void LLLibraryOutfitsFetch::contentsDone()
550{		
551	llinfos << "start" << llendl;
552
553	LLInventoryModel::cat_array_t cat_array;
554	LLInventoryModel::item_array_t wearable_array;
555	
556	LLPointer<LLOrderMyOutfitsOnDestroy> order_myoutfits_on_destroy = new LLOrderMyOutfitsOnDestroy;
557
558	for (uuid_vec_t::const_iterator folder_iter = mImportedClothingFolders.begin();
559		 folder_iter != mImportedClothingFolders.end();
560		 ++folder_iter)
561	{
562		const LLUUID &folder_id = (*folder_iter);
563		const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id);
564		if (!cat)
565		{
566			llwarns << "Library folder import for uuid:" << folder_id << " failed to find folder." << llendl;
567			continue;
568		}
569
570		//initial outfit should be already in My Outfits
571		if (cat->getName() == LLStartUp::getInitialOutfitName()) continue;
572		
573		// First, make a folder in the My Outfits directory.
574		LLUUID new_outfit_folder_id = gInventory.createNewCategory(mMyOutfitsID, LLFolderType::FT_OUTFIT, cat->getName());
575		
576		cat_array.clear();
577		wearable_array.clear();
578		// Collect the contents of each imported clothing folder, so we can create new outfit links for it
579		gInventory.collectDescendents(folder_id, cat_array, wearable_array, 
580									  LLInventoryModel::EXCLUDE_TRASH);
581		
582		for (LLInventoryModel::item_array_t::const_iterator wearable_iter = wearable_array.begin();
583			 wearable_iter != wearable_array.end();
584			 ++wearable_iter)
585		{
586			const LLViewerInventoryItem *item = wearable_iter->get();
587			link_inventory_item(gAgent.getID(),
588								item->getLinkedUUID(),
589								new_outfit_folder_id,
590								item->getName(),
591								item->getDescription(),
592								LLAssetType::AT_LINK,
593								order_myoutfits_on_destroy);
594		}
595	}
596
597	mOutfitsPopulated = true;
598}
599