PageRenderTime 33ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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