PageRenderTime 111ms CodeModel.GetById 19ms app.highlight 82ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llgesturemgr.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1407 lines | 1026 code | 222 blank | 159 comment | 198 complexity | 5d12172915e1433c48742c946d68092b MD5 | raw file
   1/** 
   2 * @file llgesturemgr.cpp
   3 * @brief Manager for playing gestures on the viewer
   4 *
   5 * $LicenseInfo:firstyear=2004&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
  29#include "llgesturemgr.h"
  30
  31// system
  32#include <functional>
  33#include <algorithm>
  34
  35// library
  36#include "llaudioengine.h"
  37#include "lldatapacker.h"
  38#include "llinventory.h"
  39#include "llkeyframemotion.h"
  40#include "llmultigesture.h"
  41#include "llnotificationsutil.h"
  42#include "llstl.h"
  43#include "llstring.h"	// todo: remove
  44#include "llvfile.h"
  45#include "message.h"
  46
  47// newview
  48#include "llagent.h"
  49#include "lldelayedgestureerror.h"
  50#include "llinventorymodel.h"
  51#include "llviewermessage.h"
  52#include "llvoavatarself.h"
  53#include "llviewerstats.h"
  54#include "llnearbychatbar.h"
  55#include "llappearancemgr.h"
  56#include "llgesturelistener.h"
  57
  58// Longest time, in seconds, to wait for all animations to stop playing
  59const F32 MAX_WAIT_ANIM_SECS = 30.f;
  60
  61// If this gesture is a link, get the base gesture that this link points to,
  62// otherwise just return this id.
  63static const LLUUID& get_linked_uuid(const LLUUID& item_id);
  64
  65// Lightweight constructor.
  66// init() does the heavy lifting.
  67LLGestureMgr::LLGestureMgr()
  68:	mValid(FALSE),
  69	mPlaying(),
  70	mActive(),
  71	mLoadingCount(0)
  72{
  73	gInventory.addObserver(this);
  74	mListener.reset(new LLGestureListener());
  75}
  76
  77
  78// We own the data for gestures, so clean them up.
  79LLGestureMgr::~LLGestureMgr()
  80{
  81	item_map_t::iterator it;
  82	for (it = mActive.begin(); it != mActive.end(); ++it)
  83	{
  84		LLMultiGesture* gesture = (*it).second;
  85
  86		delete gesture;
  87		gesture = NULL;
  88	}
  89	gInventory.removeObserver(this);
  90}
  91
  92
  93void LLGestureMgr::init()
  94{
  95	// TODO
  96}
  97
  98void LLGestureMgr::changed(U32 mask) 
  99{ 
 100	LLInventoryFetchItemsObserver::changed(mask);
 101
 102	if (mask & LLInventoryObserver::GESTURE)
 103	{
 104		// If there was a gesture label changed, update all the names in the 
 105		// active gestures and then notify observers
 106		if (mask & LLInventoryObserver::LABEL)
 107		{
 108			for(item_map_t::iterator it = mActive.begin(); it != mActive.end(); ++it)
 109			{
 110				if(it->second)
 111				{
 112					LLViewerInventoryItem* item = gInventory.getItem(it->first);
 113					if(item)
 114					{
 115						it->second->mName = item->getName();
 116					}
 117				}
 118			}
 119			notifyObservers();
 120		}
 121		// If there was a gesture added or removed notify observers
 122		// STRUCTURE denotes that the inventory item has been moved
 123		// In the case of deleting gesture, it is moved to the trash
 124		else if(mask & LLInventoryObserver::ADD ||
 125				mask & LLInventoryObserver::REMOVE ||
 126				mask & LLInventoryObserver::STRUCTURE)
 127		{
 128			notifyObservers();
 129		}
 130	}
 131}
 132
 133
 134// Use this version when you have the item_id but not the asset_id,
 135// and you KNOW the inventory is loaded.
 136void LLGestureMgr::activateGesture(const LLUUID& item_id)
 137{
 138	LLViewerInventoryItem* item = gInventory.getItem(item_id);
 139	if (!item) return;
 140	if (item->getType() != LLAssetType::AT_GESTURE)
 141		return;
 142
 143	LLUUID asset_id = item->getAssetUUID();
 144
 145	mLoadingCount = 1;
 146	mDeactivateSimilarNames.clear();
 147
 148	const BOOL inform_server = TRUE;
 149	const BOOL deactivate_similar = FALSE; 
 150	activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar);
 151}
 152
 153
 154void LLGestureMgr::activateGestures(LLViewerInventoryItem::item_array_t& items)
 155{
 156	// Load up the assets
 157	S32 count = 0;
 158	LLViewerInventoryItem::item_array_t::const_iterator it;
 159	for (it = items.begin(); it != items.end(); ++it)
 160	{
 161		LLViewerInventoryItem* item = *it;
 162
 163		if (isGestureActive(item->getUUID()))
 164		{
 165			continue;
 166		}
 167		else 
 168		{ // Make gesture active and persistent through login sessions.  -spatters 07-12-06
 169			activateGesture(item->getUUID());
 170		}
 171
 172		count++;
 173	}
 174
 175	mLoadingCount = count;
 176	mDeactivateSimilarNames.clear();
 177
 178	for (it = items.begin(); it != items.end(); ++it)
 179	{
 180		LLViewerInventoryItem* item = *it;
 181
 182		if (isGestureActive(item->getUUID()))
 183		{
 184			continue;
 185		}
 186
 187		// Don't inform server, we'll do that in bulk
 188		const BOOL no_inform_server = FALSE;
 189		const BOOL deactivate_similar = TRUE;
 190		activateGestureWithAsset(item->getUUID(), item->getAssetUUID(),
 191								 no_inform_server,
 192								 deactivate_similar);
 193	}
 194
 195	// Inform the database of this change
 196	LLMessageSystem* msg = gMessageSystem;
 197
 198	BOOL start_message = TRUE;
 199
 200	for (it = items.begin(); it != items.end(); ++it)
 201	{
 202		LLViewerInventoryItem* item = *it;
 203
 204		if (isGestureActive(item->getUUID()))
 205		{
 206			continue;
 207		}
 208
 209		if (start_message)
 210		{
 211			msg->newMessage("ActivateGestures");
 212			msg->nextBlock("AgentData");
 213			msg->addUUID("AgentID", gAgent.getID());
 214			msg->addUUID("SessionID", gAgent.getSessionID());
 215			msg->addU32("Flags", 0x0);
 216			start_message = FALSE;
 217		}
 218		
 219		msg->nextBlock("Data");
 220		msg->addUUID("ItemID", item->getUUID());
 221		msg->addUUID("AssetID", item->getAssetUUID());
 222		msg->addU32("GestureFlags", 0x0);
 223
 224		if (msg->getCurrentSendTotal() > MTUBYTES)
 225		{
 226			gAgent.sendReliableMessage();
 227			start_message = TRUE;
 228		}
 229	}
 230
 231	if (!start_message)
 232	{
 233		gAgent.sendReliableMessage();
 234	}
 235}
 236
 237
 238struct LLLoadInfo
 239{
 240	LLUUID mItemID;
 241	BOOL mInformServer;
 242	BOOL mDeactivateSimilar;
 243};
 244
 245// If inform_server is true, will send a message upstream to update
 246// the user_gesture_active table.
 247/**
 248 * It will load a gesture from remote storage
 249 */
 250void LLGestureMgr::activateGestureWithAsset(const LLUUID& item_id,
 251												const LLUUID& asset_id,
 252												BOOL inform_server,
 253												BOOL deactivate_similar)
 254{
 255	const LLUUID& base_item_id = get_linked_uuid(item_id);
 256
 257	if( !gAssetStorage )
 258	{
 259		llwarns << "LLGestureMgr::activateGestureWithAsset without valid gAssetStorage" << llendl;
 260		return;
 261	}
 262	// If gesture is already active, nothing to do.
 263	if (isGestureActive(item_id))
 264	{
 265		llwarns << "Tried to loadGesture twice " << item_id << llendl;
 266		return;
 267	}
 268
 269//	if (asset_id.isNull())
 270//	{
 271//		llwarns << "loadGesture() - gesture has no asset" << llendl;
 272//		return;
 273//	}
 274
 275	// For now, put NULL into the item map.  We'll build a gesture
 276	// class object when the asset data arrives.
 277	mActive[base_item_id] = NULL;
 278
 279	// Copy the UUID
 280	if (asset_id.notNull())
 281	{
 282		LLLoadInfo* info = new LLLoadInfo;
 283		info->mItemID = base_item_id;
 284		info->mInformServer = inform_server;
 285		info->mDeactivateSimilar = deactivate_similar;
 286
 287		const BOOL high_priority = TRUE;
 288		gAssetStorage->getAssetData(asset_id,
 289									LLAssetType::AT_GESTURE,
 290									onLoadComplete,
 291									(void*)info,
 292									high_priority);
 293	}
 294	else
 295	{
 296		notifyObservers();
 297	}
 298}
 299
 300
 301void LLGestureMgr::deactivateGesture(const LLUUID& item_id)
 302{
 303	const LLUUID& base_item_id = get_linked_uuid(item_id);
 304	item_map_t::iterator it = mActive.find(base_item_id);
 305	if (it == mActive.end())
 306	{
 307		llwarns << "deactivateGesture for inactive gesture " << item_id << llendl;
 308		return;
 309	}
 310
 311	// mActive owns this gesture pointer, so clean up memory.
 312	LLMultiGesture* gesture = (*it).second;
 313
 314	// Can be NULL gestures in the map
 315	if (gesture)
 316	{
 317		stopGesture(gesture);
 318
 319		delete gesture;
 320		gesture = NULL;
 321	}
 322
 323	mActive.erase(it);
 324	gInventory.addChangedMask(LLInventoryObserver::LABEL, base_item_id);
 325
 326	// Inform the database of this change
 327	LLMessageSystem* msg = gMessageSystem;
 328	msg->newMessage("DeactivateGestures");
 329	msg->nextBlock("AgentData");
 330	msg->addUUID("AgentID", gAgent.getID());
 331	msg->addUUID("SessionID", gAgent.getSessionID());
 332	msg->addU32("Flags", 0x0);
 333	
 334	msg->nextBlock("Data");
 335	msg->addUUID("ItemID", item_id);
 336	msg->addU32("GestureFlags", 0x0);
 337
 338	gAgent.sendReliableMessage();
 339
 340	LLAppearanceMgr::instance().removeCOFItemLinks(base_item_id, false);
 341
 342	notifyObservers();
 343}
 344
 345
 346void LLGestureMgr::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id)
 347{
 348	const LLUUID& base_in_item_id = get_linked_uuid(in_item_id);
 349	uuid_vec_t gest_item_ids;
 350
 351	// Deactivate all gestures that match
 352	item_map_t::iterator it;
 353	for (it = mActive.begin(); it != mActive.end(); )
 354	{
 355		const LLUUID& item_id = (*it).first;
 356		LLMultiGesture* gest = (*it).second;
 357
 358		// Don't deactivate the gesture we are looking for duplicates of
 359		// (for replaceGesture)
 360		if (!gest || item_id == base_in_item_id) 
 361		{
 362			// legal, can have null pointers in list
 363			++it;
 364		}
 365		else if ((!gest->mTrigger.empty() && gest->mTrigger == in->mTrigger)
 366				 || (gest->mKey != KEY_NONE && gest->mKey == in->mKey && gest->mMask == in->mMask))
 367		{
 368			gest_item_ids.push_back(item_id);
 369
 370			stopGesture(gest);
 371
 372			delete gest;
 373			gest = NULL;
 374
 375			mActive.erase(it++);
 376			gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
 377
 378		}
 379		else
 380		{
 381			++it;
 382		}
 383	}
 384
 385	// Inform database of the change
 386	LLMessageSystem* msg = gMessageSystem;
 387	BOOL start_message = TRUE;
 388	uuid_vec_t::const_iterator vit = gest_item_ids.begin();
 389	while (vit != gest_item_ids.end())
 390	{
 391		if (start_message)
 392		{
 393			msg->newMessage("DeactivateGestures");
 394			msg->nextBlock("AgentData");
 395			msg->addUUID("AgentID", gAgent.getID());
 396			msg->addUUID("SessionID", gAgent.getSessionID());
 397			msg->addU32("Flags", 0x0);
 398			start_message = FALSE;
 399		}
 400	
 401		msg->nextBlock("Data");
 402		msg->addUUID("ItemID", *vit);
 403		msg->addU32("GestureFlags", 0x0);
 404
 405		if (msg->getCurrentSendTotal() > MTUBYTES)
 406		{
 407			gAgent.sendReliableMessage();
 408			start_message = TRUE;
 409		}
 410
 411		++vit;
 412	}
 413
 414	if (!start_message)
 415	{
 416		gAgent.sendReliableMessage();
 417	}
 418
 419	// Add to the list of names for the user.
 420	for (vit = gest_item_ids.begin(); vit != gest_item_ids.end(); ++vit)
 421	{
 422		LLViewerInventoryItem* item = gInventory.getItem(*vit);
 423		if (!item) continue;
 424
 425		mDeactivateSimilarNames.append(item->getName());
 426		mDeactivateSimilarNames.append("\n");
 427	}
 428
 429	notifyObservers();
 430}
 431
 432
 433BOOL LLGestureMgr::isGestureActive(const LLUUID& item_id)
 434{
 435	const LLUUID& base_item_id = get_linked_uuid(item_id);
 436	item_map_t::iterator it = mActive.find(base_item_id);
 437	return (it != mActive.end());
 438}
 439
 440
 441BOOL LLGestureMgr::isGesturePlaying(const LLUUID& item_id)
 442{
 443	const LLUUID& base_item_id = get_linked_uuid(item_id);
 444
 445	item_map_t::iterator it = mActive.find(base_item_id);
 446	if (it == mActive.end()) return FALSE;
 447
 448	LLMultiGesture* gesture = (*it).second;
 449	if (!gesture) return FALSE;
 450
 451	return gesture->mPlaying;
 452}
 453
 454BOOL LLGestureMgr::isGesturePlaying(LLMultiGesture* gesture)
 455{
 456	if(!gesture)
 457	{
 458		return FALSE;
 459	}
 460
 461	return gesture->mPlaying;
 462}
 463
 464void LLGestureMgr::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id)
 465{
 466	const LLUUID& base_item_id = get_linked_uuid(item_id);
 467
 468	item_map_t::iterator it = mActive.find(base_item_id);
 469	if (it == mActive.end())
 470	{
 471		llwarns << "replaceGesture for inactive gesture " << base_item_id << llendl;
 472		return;
 473	}
 474
 475	LLMultiGesture* old_gesture = (*it).second;
 476	stopGesture(old_gesture);
 477
 478	mActive.erase(base_item_id);
 479
 480	mActive[base_item_id] = new_gesture;
 481
 482	delete old_gesture;
 483	old_gesture = NULL;
 484
 485	if (asset_id.notNull())
 486	{
 487		mLoadingCount = 1;
 488		mDeactivateSimilarNames.clear();
 489
 490		LLLoadInfo* info = new LLLoadInfo;
 491		info->mItemID = base_item_id;
 492		info->mInformServer = TRUE;
 493		info->mDeactivateSimilar = FALSE;
 494
 495		const BOOL high_priority = TRUE;
 496		gAssetStorage->getAssetData(asset_id,
 497									LLAssetType::AT_GESTURE,
 498									onLoadComplete,
 499									(void*)info,
 500									high_priority);
 501	}
 502
 503	notifyObservers();
 504}
 505
 506void LLGestureMgr::replaceGesture(const LLUUID& item_id, const LLUUID& new_asset_id)
 507{
 508	const LLUUID& base_item_id = get_linked_uuid(item_id);
 509
 510	item_map_t::iterator it = LLGestureMgr::instance().mActive.find(base_item_id);
 511	if (it == mActive.end())
 512	{
 513		llwarns << "replaceGesture for inactive gesture " << base_item_id << llendl;
 514		return;
 515	}
 516
 517	// mActive owns this gesture pointer, so clean up memory.
 518	LLMultiGesture* gesture = (*it).second;
 519	LLGestureMgr::instance().replaceGesture(base_item_id, gesture, new_asset_id);
 520}
 521
 522void LLGestureMgr::playGesture(LLMultiGesture* gesture)
 523{
 524	if (!gesture) return;
 525
 526	// Reset gesture to first step
 527	gesture->mCurrentStep = 0;
 528
 529	// Add to list of playing
 530	gesture->mPlaying = TRUE;
 531	mPlaying.push_back(gesture);
 532
 533	// Load all needed assets to minimize the delays
 534	// when gesture is playing.
 535	for (std::vector<LLGestureStep*>::iterator steps_it = gesture->mSteps.begin();
 536		 steps_it != gesture->mSteps.end();
 537		 ++steps_it)
 538	{
 539		LLGestureStep* step = *steps_it;
 540		switch(step->getType())
 541		{
 542		case STEP_ANIMATION:
 543			{
 544				LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
 545				const LLUUID& anim_id = anim_step->mAnimAssetID;
 546
 547				// Don't request the animation if this step stops it or if it is already in Static VFS
 548				if (!(anim_id.isNull()
 549					  || anim_step->mFlags & ANIM_FLAG_STOP
 550					  || gAssetStorage->hasLocalAsset(anim_id, LLAssetType::AT_ANIMATION)))
 551				{
 552					mLoadingAssets.insert(anim_id);
 553
 554					LLUUID* id = new LLUUID(gAgentID);
 555					gAssetStorage->getAssetData(anim_id,
 556									LLAssetType::AT_ANIMATION,
 557									onAssetLoadComplete,
 558									(void *)id,
 559									TRUE);
 560				}
 561				break;
 562			}
 563		case STEP_SOUND:
 564			{
 565				LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
 566				const LLUUID& sound_id = sound_step->mSoundAssetID;
 567				if (!(sound_id.isNull()
 568					  || gAssetStorage->hasLocalAsset(sound_id, LLAssetType::AT_SOUND)))
 569				{
 570					mLoadingAssets.insert(sound_id);
 571
 572					gAssetStorage->getAssetData(sound_id,
 573									LLAssetType::AT_SOUND,
 574									onAssetLoadComplete,
 575									NULL,
 576									TRUE);
 577				}
 578				break;
 579			}
 580		case STEP_CHAT:
 581		case STEP_WAIT:
 582		case STEP_EOF:
 583			{
 584				break;
 585			}
 586		default:
 587			{
 588				llwarns << "Unknown gesture step type: " << step->getType() << llendl;
 589			}
 590		}
 591	}
 592
 593	// And get it going
 594	stepGesture(gesture);
 595
 596	notifyObservers();
 597}
 598
 599
 600// Convenience function that looks up the item_id for you.
 601void LLGestureMgr::playGesture(const LLUUID& item_id)
 602{
 603	const LLUUID& base_item_id = get_linked_uuid(item_id);
 604
 605	item_map_t::iterator it = mActive.find(base_item_id);
 606	if (it == mActive.end()) return;
 607
 608	LLMultiGesture* gesture = (*it).second;
 609	if (!gesture) return;
 610
 611	playGesture(gesture);
 612}
 613
 614
 615// Iterates through space delimited tokens in string, triggering any gestures found.
 616// Generates a revised string that has the found tokens replaced by their replacement strings
 617// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
 618BOOL LLGestureMgr::triggerAndReviseString(const std::string &utf8str, std::string* revised_string)
 619{
 620	std::string tokenized = utf8str;
 621
 622	BOOL found_gestures = FALSE;
 623	BOOL first_token = TRUE;
 624
 625	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
 626	boost::char_separator<char> sep(" ");
 627	tokenizer tokens(tokenized, sep);
 628	tokenizer::iterator token_iter;
 629
 630	for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
 631	{
 632		const char* cur_token = token_iter->c_str();
 633		LLMultiGesture* gesture = NULL;
 634
 635		// Only pay attention to the first gesture in the string.
 636		if( !found_gestures )
 637		{
 638			// collect gestures that match
 639			std::vector <LLMultiGesture *> matching;
 640			item_map_t::iterator it;
 641			for (it = mActive.begin(); it != mActive.end(); ++it)
 642			{
 643				gesture = (*it).second;
 644
 645				// Gesture asset data might not have arrived yet
 646				if (!gesture) continue;
 647				
 648				if (LLStringUtil::compareInsensitive(gesture->mTrigger, cur_token) == 0)
 649				{
 650					matching.push_back(gesture);
 651				}
 652				
 653				gesture = NULL;
 654			}
 655
 656			
 657			if (matching.size() > 0)
 658			{
 659				// choose one at random
 660				{
 661					S32 random = ll_rand(matching.size());
 662
 663					gesture = matching[random];
 664					
 665					playGesture(gesture);
 666
 667					if (!gesture->mReplaceText.empty())
 668					{
 669						if( !first_token )
 670						{
 671							if (revised_string)
 672								revised_string->append( " " );
 673						}
 674
 675						// Don't muck with the user's capitalization if we don't have to.
 676						if( LLStringUtil::compareInsensitive(cur_token, gesture->mReplaceText) == 0)
 677						{
 678							if (revised_string)
 679								revised_string->append( cur_token );
 680						}
 681						else
 682						{
 683							if (revised_string)
 684								revised_string->append( gesture->mReplaceText );
 685						}
 686					}
 687					found_gestures = TRUE;
 688				}
 689			}
 690		}
 691		
 692		if(!gesture)
 693		{
 694			// This token doesn't match a gesture.  Pass it through to the output.
 695			if( !first_token )
 696			{
 697				if (revised_string)
 698					revised_string->append( " " );
 699			}
 700			if (revised_string)
 701				revised_string->append( cur_token );
 702		}
 703
 704		first_token = FALSE;
 705		gesture = NULL;
 706	}
 707	return found_gestures;
 708}
 709
 710
 711BOOL LLGestureMgr::triggerGesture(KEY key, MASK mask)
 712{
 713	std::vector <LLMultiGesture *> matching;
 714	item_map_t::iterator it;
 715
 716	// collect matching gestures
 717	for (it = mActive.begin(); it != mActive.end(); ++it)
 718	{
 719		LLMultiGesture* gesture = (*it).second;
 720
 721		// asset data might not have arrived yet
 722		if (!gesture) continue;
 723
 724		if (gesture->mKey == key
 725			&& gesture->mMask == mask)
 726		{
 727			matching.push_back(gesture);
 728		}
 729	}
 730
 731	// choose one and play it
 732	if (matching.size() > 0)
 733	{
 734		U32 random = ll_rand(matching.size());
 735		
 736		LLMultiGesture* gesture = matching[random];
 737			
 738		playGesture(gesture);
 739		return TRUE;
 740	}
 741	return FALSE;
 742}
 743
 744
 745S32 LLGestureMgr::getPlayingCount() const
 746{
 747	return mPlaying.size();
 748}
 749
 750
 751struct IsGesturePlaying : public std::unary_function<LLMultiGesture*, bool>
 752{
 753	bool operator()(const LLMultiGesture* gesture) const
 754	{
 755		return gesture->mPlaying ? true : false;
 756	}
 757};
 758
 759void LLGestureMgr::update()
 760{
 761	S32 i;
 762	for (i = 0; i < (S32)mPlaying.size(); ++i)
 763	{
 764		stepGesture(mPlaying[i]);
 765	}
 766
 767	// Clear out gestures that are done, by moving all the
 768	// ones that are still playing to the front.
 769	std::vector<LLMultiGesture*>::iterator new_end;
 770	new_end = std::partition(mPlaying.begin(),
 771							 mPlaying.end(),
 772							 IsGesturePlaying());
 773
 774	// Something finished playing
 775	if (new_end != mPlaying.end())
 776	{
 777		// Delete the completed gestures that want deletion
 778		std::vector<LLMultiGesture*>::iterator it;
 779		for (it = new_end; it != mPlaying.end(); ++it)
 780		{
 781			LLMultiGesture* gesture = *it;
 782
 783			if (gesture->mDoneCallback)
 784			{
 785				gesture->mDoneCallback(gesture, gesture->mCallbackData);
 786
 787				// callback might have deleted gesture, can't
 788				// rely on this pointer any more
 789				gesture = NULL;
 790			}
 791		}
 792
 793		// And take done gestures out of the playing list
 794		mPlaying.erase(new_end, mPlaying.end());
 795
 796		notifyObservers();
 797	}
 798}
 799
 800
 801// Run all steps until you're either done or hit a wait.
 802void LLGestureMgr::stepGesture(LLMultiGesture* gesture)
 803{
 804	if (!gesture)
 805	{
 806		return;
 807	}
 808	if (!isAgentAvatarValid() || hasLoadingAssets(gesture)) return;
 809
 810	// Of the ones that started playing, have any stopped?
 811
 812	std::set<LLUUID>::iterator gest_it;
 813	for (gest_it = gesture->mPlayingAnimIDs.begin(); 
 814		 gest_it != gesture->mPlayingAnimIDs.end(); 
 815		 )
 816	{
 817		// look in signaled animations (simulator's view of what is
 818		// currently playing.
 819		LLVOAvatar::AnimIterator play_it = gAgentAvatarp->mSignaledAnimations.find(*gest_it);
 820		if (play_it != gAgentAvatarp->mSignaledAnimations.end())
 821		{
 822			++gest_it;
 823		}
 824		else
 825		{
 826			// not found, so not currently playing or scheduled to play
 827			// delete from the triggered set
 828			gesture->mPlayingAnimIDs.erase(gest_it++);
 829		}
 830	}
 831
 832	// Of all the animations that we asked the sim to start for us,
 833	// pick up the ones that have actually started.
 834	for (gest_it = gesture->mRequestedAnimIDs.begin();
 835		 gest_it != gesture->mRequestedAnimIDs.end();
 836		 )
 837	{
 838	 LLVOAvatar::AnimIterator play_it = gAgentAvatarp->mSignaledAnimations.find(*gest_it);
 839		if (play_it != gAgentAvatarp->mSignaledAnimations.end())
 840		{
 841			// Hooray, this animation has started playing!
 842			// Copy into playing.
 843			gesture->mPlayingAnimIDs.insert(*gest_it);
 844			gesture->mRequestedAnimIDs.erase(gest_it++);
 845		}
 846		else
 847		{
 848			// nope, not playing yet
 849			++gest_it;
 850		}
 851	}
 852
 853	// Run the current steps
 854	BOOL waiting = FALSE;
 855	while (!waiting && gesture->mPlaying)
 856	{
 857		// Get the current step, if there is one.
 858		// Otherwise enter the waiting at end state.
 859		LLGestureStep* step = NULL;
 860		if (gesture->mCurrentStep < (S32)gesture->mSteps.size())
 861		{
 862			step = gesture->mSteps[gesture->mCurrentStep];
 863			llassert(step != NULL);
 864		}
 865		else
 866		{
 867			// step stays null, we're off the end
 868			gesture->mWaitingAtEnd = TRUE;
 869		}
 870
 871
 872		// If we're waiting at the end, wait for all gestures to stop
 873		// playing.
 874		// TODO: Wait for all sounds to complete as well.
 875		if (gesture->mWaitingAtEnd)
 876		{
 877			// Neither do we have any pending requests, nor are they
 878			// still playing.
 879			if ((gesture->mRequestedAnimIDs.empty()
 880				&& gesture->mPlayingAnimIDs.empty()))
 881			{
 882				// all animations are done playing
 883				gesture->mWaitingAtEnd = FALSE;
 884				gesture->mPlaying = FALSE;
 885			}
 886			else
 887			{
 888				waiting = TRUE;
 889			}
 890			continue;
 891		}
 892
 893		// If we're waiting on our animations to stop, poll for
 894		// completion.
 895		if (gesture->mWaitingAnimations)
 896		{
 897			// Neither do we have any pending requests, nor are they
 898			// still playing.
 899			if ((gesture->mRequestedAnimIDs.empty()
 900				&& gesture->mPlayingAnimIDs.empty()))
 901			{
 902				// all animations are done playing
 903				gesture->mWaitingAnimations = FALSE;
 904				gesture->mCurrentStep++;
 905			}
 906			else if (gesture->mWaitTimer.getElapsedTimeF32() > MAX_WAIT_ANIM_SECS)
 907			{
 908				// we've waited too long for an animation
 909				llinfos << "Waited too long for animations to stop, continuing gesture."
 910					<< llendl;
 911				gesture->mWaitingAnimations = FALSE;
 912				gesture->mCurrentStep++;
 913			}
 914			else
 915			{
 916				waiting = TRUE;
 917			}
 918			continue;
 919		}
 920
 921		// If we're waiting a fixed amount of time, check for timer
 922		// expiration.
 923		if (gesture->mWaitingTimer)
 924		{
 925			// We're waiting for a certain amount of time to pass
 926			LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
 927
 928			F32 elapsed = gesture->mWaitTimer.getElapsedTimeF32();
 929			if (elapsed > wait_step->mWaitSeconds)
 930			{
 931				// wait is done, continue execution
 932				gesture->mWaitingTimer = FALSE;
 933				gesture->mCurrentStep++;
 934			}
 935			else
 936			{
 937				// we're waiting, so execution is done for now
 938				waiting = TRUE;
 939			}
 940			continue;
 941		}
 942
 943		// Not waiting, do normal execution
 944		runStep(gesture, step);
 945	}
 946}
 947
 948
 949void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step)
 950{
 951	switch(step->getType())
 952	{
 953	case STEP_ANIMATION:
 954		{
 955			LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
 956			if (anim_step->mAnimAssetID.isNull())
 957			{
 958				gesture->mCurrentStep++;
 959			}
 960
 961			if (anim_step->mFlags & ANIM_FLAG_STOP)
 962			{
 963				gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_STOP);
 964				// remove it from our request set in case we just requested it
 965				std::set<LLUUID>::iterator set_it = gesture->mRequestedAnimIDs.find(anim_step->mAnimAssetID);
 966				if (set_it != gesture->mRequestedAnimIDs.end())
 967				{
 968					gesture->mRequestedAnimIDs.erase(set_it);
 969				}
 970			}
 971			else
 972			{
 973				gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_START);
 974				// Indicate that we've requested this animation to play as
 975				// part of this gesture (but it won't start playing for at
 976				// least one round-trip to simulator).
 977				gesture->mRequestedAnimIDs.insert(anim_step->mAnimAssetID);
 978			}
 979			gesture->mCurrentStep++;
 980			break;
 981		}
 982	case STEP_SOUND:
 983		{
 984			LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
 985			const LLUUID& sound_id = sound_step->mSoundAssetID;
 986			const F32 volume = 1.f;
 987			send_sound_trigger(sound_id, volume);
 988			gesture->mCurrentStep++;
 989			break;
 990		}
 991	case STEP_CHAT:
 992		{
 993			LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
 994			std::string chat_text = chat_step->mChatText;
 995			// Don't animate the nodding, as this might not blend with
 996			// other playing animations.
 997
 998			const BOOL animate = FALSE;
 999
1000			LLNearbyChatBar::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate);
1001
1002			gesture->mCurrentStep++;
1003			break;
1004		}
1005	case STEP_WAIT:
1006		{
1007			LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
1008			if (wait_step->mFlags & WAIT_FLAG_TIME)
1009			{
1010				gesture->mWaitingTimer = TRUE;
1011				gesture->mWaitTimer.reset();
1012			}
1013			else if (wait_step->mFlags & WAIT_FLAG_ALL_ANIM)
1014			{
1015				gesture->mWaitingAnimations = TRUE;
1016				// Use the wait timer as a deadlock breaker for animation
1017				// waits.
1018				gesture->mWaitTimer.reset();
1019			}
1020			else
1021			{
1022				gesture->mCurrentStep++;
1023			}
1024			// Don't increment instruction pointer until wait is complete.
1025			break;
1026		}
1027	default:
1028		{
1029			break;
1030		}
1031	}
1032}
1033
1034
1035// static
1036void LLGestureMgr::onLoadComplete(LLVFS *vfs,
1037									   const LLUUID& asset_uuid,
1038									   LLAssetType::EType type,
1039									   void* user_data, S32 status, LLExtStat ext_status)
1040{
1041	LLLoadInfo* info = (LLLoadInfo*)user_data;
1042
1043	LLUUID item_id = info->mItemID;
1044	BOOL inform_server = info->mInformServer;
1045	BOOL deactivate_similar = info->mDeactivateSimilar;
1046
1047	delete info;
1048	info = NULL;
1049	LLGestureMgr& self = LLGestureMgr::instance();
1050	self.mLoadingCount--;
1051
1052	if (0 == status)
1053	{
1054		LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
1055		S32 size = file.getSize();
1056
1057		std::vector<char> buffer(size+1);
1058
1059		file.read((U8*)&buffer[0], size);
1060		// ensure there's a trailing NULL so strlen will work.
1061		buffer[size] = '\0';
1062
1063		LLMultiGesture* gesture = new LLMultiGesture();
1064
1065		LLDataPackerAsciiBuffer dp(&buffer[0], size+1);
1066		BOOL ok = gesture->deserialize(dp);
1067
1068		if (ok)
1069		{
1070			if (deactivate_similar)
1071			{
1072				self.deactivateSimilarGestures(gesture, item_id);
1073
1074				// Display deactivation message if this was the last of the bunch.
1075				if (self.mLoadingCount == 0
1076					&& self.mDeactivateSimilarNames.length() > 0)
1077				{
1078					// we're done with this set of deactivations
1079					LLSD args;
1080					args["NAMES"] = self.mDeactivateSimilarNames;
1081					LLNotificationsUtil::add("DeactivatedGesturesTrigger", args);
1082				}
1083			}
1084
1085			LLViewerInventoryItem* item = gInventory.getItem(item_id);
1086			if(item)
1087			{
1088				gesture->mName = item->getName();
1089			}
1090			else
1091			{
1092				// Watch this item and set gesture name when item exists in inventory
1093				self.setFetchID(item_id);
1094				self.startFetch();
1095			}
1096			self.mActive[item_id] = gesture;
1097
1098			// Everything has been successful.  Add to the active list.
1099			gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
1100
1101			if (inform_server)
1102			{
1103				// Inform the database of this change
1104				LLMessageSystem* msg = gMessageSystem;
1105				msg->newMessage("ActivateGestures");
1106				msg->nextBlock("AgentData");
1107				msg->addUUID("AgentID", gAgent.getID());
1108				msg->addUUID("SessionID", gAgent.getSessionID());
1109				msg->addU32("Flags", 0x0);
1110				
1111				msg->nextBlock("Data");
1112				msg->addUUID("ItemID", item_id);
1113				msg->addUUID("AssetID", asset_uuid);
1114				msg->addU32("GestureFlags", 0x0);
1115
1116				gAgent.sendReliableMessage();
1117			}
1118			callback_map_t::iterator i_cb = self.mCallbackMap.find(item_id);
1119			
1120			if(i_cb != self.mCallbackMap.end())
1121			{
1122				i_cb->second(gesture);
1123				self.mCallbackMap.erase(i_cb);
1124			}
1125
1126			self.notifyObservers();
1127		}
1128		else
1129		{
1130			llwarns << "Unable to load gesture" << llendl;
1131
1132			self.mActive.erase(item_id);
1133			
1134			delete gesture;
1135			gesture = NULL;
1136		}
1137	}
1138	else
1139	{
1140		LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
1141
1142		if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
1143			LL_ERR_FILE_EMPTY == status)
1144		{
1145			LLDelayedGestureError::gestureMissing( item_id );
1146		}
1147		else
1148		{
1149			LLDelayedGestureError::gestureFailedToLoad( item_id );
1150		}
1151
1152		llwarns << "Problem loading gesture: " << status << llendl;
1153		
1154		LLGestureMgr::instance().mActive.erase(item_id);			
1155	}
1156}
1157
1158// static
1159void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs,
1160									   const LLUUID& asset_uuid,
1161									   LLAssetType::EType type,
1162									   void* user_data, S32 status, LLExtStat ext_status)
1163{
1164	LLGestureMgr& self = LLGestureMgr::instance();
1165
1166	// Complete the asset loading process depending on the type and
1167	// remove the asset id from pending downloads list.
1168	switch(type)
1169	{
1170	case LLAssetType::AT_ANIMATION:
1171		{
1172			LLKeyframeMotion::onLoadComplete(vfs, asset_uuid, type, user_data, status, ext_status);
1173
1174			self.mLoadingAssets.erase(asset_uuid);
1175
1176			break;
1177		}
1178	case LLAssetType::AT_SOUND:
1179		{
1180			LLAudioEngine::assetCallback(vfs, asset_uuid, type, user_data, status, ext_status);
1181
1182			self.mLoadingAssets.erase(asset_uuid);
1183
1184			break;
1185		}
1186	default:
1187		{
1188			llwarns << "Unexpected asset type: " << type << llendl;
1189
1190			// We don't want to return from this callback without
1191			// an animation or sound callback being fired
1192			// and *user_data handled to avoid memory leaks.
1193			llassert(type == LLAssetType::AT_ANIMATION || type == LLAssetType::AT_SOUND);
1194		}
1195	}
1196}
1197
1198// static
1199bool LLGestureMgr::hasLoadingAssets(LLMultiGesture* gesture)
1200{
1201	LLGestureMgr& self = LLGestureMgr::instance();
1202
1203	for (std::vector<LLGestureStep*>::iterator steps_it = gesture->mSteps.begin();
1204		 steps_it != gesture->mSteps.end();
1205		 ++steps_it)
1206	{
1207		LLGestureStep* step = *steps_it;
1208		switch(step->getType())
1209		{
1210		case STEP_ANIMATION:
1211			{
1212				LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
1213				const LLUUID& anim_id = anim_step->mAnimAssetID;
1214
1215				if (!(anim_id.isNull()
1216					  || anim_step->mFlags & ANIM_FLAG_STOP
1217					  || self.mLoadingAssets.find(anim_id) == self.mLoadingAssets.end()))
1218				{
1219					return true;
1220				}
1221				break;
1222			}
1223		case STEP_SOUND:
1224			{
1225				LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
1226				const LLUUID& sound_id = sound_step->mSoundAssetID;
1227
1228				if (!(sound_id.isNull()
1229					  || self.mLoadingAssets.find(sound_id) == self.mLoadingAssets.end()))
1230				{
1231					return true;
1232				}
1233				break;
1234			}
1235		case STEP_CHAT:
1236		case STEP_WAIT:
1237		case STEP_EOF:
1238			{
1239				break;
1240			}
1241		default:
1242			{
1243				llwarns << "Unknown gesture step type: " << step->getType() << llendl;
1244			}
1245		}
1246	}
1247
1248	return false;
1249}
1250
1251void LLGestureMgr::stopGesture(LLMultiGesture* gesture)
1252{
1253	if (!gesture) return;
1254
1255	// Stop any animations that this gesture is currently playing
1256	std::set<LLUUID>::const_iterator set_it;
1257	for (set_it = gesture->mRequestedAnimIDs.begin(); set_it != gesture->mRequestedAnimIDs.end(); ++set_it)
1258	{
1259		const LLUUID& anim_id = *set_it;
1260		gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
1261	}
1262	for (set_it = gesture->mPlayingAnimIDs.begin(); set_it != gesture->mPlayingAnimIDs.end(); ++set_it)
1263	{
1264		const LLUUID& anim_id = *set_it;
1265		gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
1266	}
1267
1268	std::vector<LLMultiGesture*>::iterator it;
1269	it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
1270	while (it != mPlaying.end())
1271	{
1272		mPlaying.erase(it);
1273		it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
1274	}
1275
1276	gesture->reset();
1277
1278	if (gesture->mDoneCallback)
1279	{
1280		gesture->mDoneCallback(gesture, gesture->mCallbackData);
1281
1282		// callback might have deleted gesture, can't
1283		// rely on this pointer any more
1284		gesture = NULL;
1285	}
1286
1287	notifyObservers();
1288}
1289
1290
1291void LLGestureMgr::stopGesture(const LLUUID& item_id)
1292{
1293	const LLUUID& base_item_id = get_linked_uuid(item_id);
1294
1295	item_map_t::iterator it = mActive.find(base_item_id);
1296	if (it == mActive.end()) return;
1297
1298	LLMultiGesture* gesture = (*it).second;
1299	if (!gesture) return;
1300
1301	stopGesture(gesture);
1302}
1303
1304
1305void LLGestureMgr::addObserver(LLGestureManagerObserver* observer)
1306{
1307	mObservers.push_back(observer);
1308}
1309
1310void LLGestureMgr::removeObserver(LLGestureManagerObserver* observer)
1311{
1312	std::vector<LLGestureManagerObserver*>::iterator it;
1313	it = std::find(mObservers.begin(), mObservers.end(), observer);
1314	if (it != mObservers.end())
1315	{
1316		mObservers.erase(it);
1317	}
1318}
1319
1320// Call this method when it's time to update everyone on a new state.
1321// Copy the list because an observer could respond by removing itself
1322// from the list.
1323void LLGestureMgr::notifyObservers()
1324{
1325	lldebugs << "LLGestureMgr::notifyObservers" << llendl;
1326
1327	for(std::vector<LLGestureManagerObserver*>::iterator iter = mObservers.begin(); 
1328		iter != mObservers.end(); 
1329		++iter)
1330	{
1331		LLGestureManagerObserver* observer = (*iter);
1332		observer->changed();
1333	}
1334}
1335
1336BOOL LLGestureMgr::matchPrefix(const std::string& in_str, std::string* out_str)
1337{
1338	S32 in_len = in_str.length();
1339
1340	item_map_t::iterator it;
1341	for (it = mActive.begin(); it != mActive.end(); ++it)
1342	{
1343		LLMultiGesture* gesture = (*it).second;
1344		if (gesture)
1345		{
1346			const std::string& trigger = gesture->getTrigger();
1347			
1348			if (in_len > (S32)trigger.length())
1349			{
1350				// too short, bail out
1351				continue;
1352			}
1353
1354			std::string trigger_trunc = trigger;
1355			LLStringUtil::truncate(trigger_trunc, in_len);
1356			if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc))
1357			{
1358				*out_str = trigger;
1359				return TRUE;
1360			}
1361		}
1362	}
1363	return FALSE;
1364}
1365
1366
1367void LLGestureMgr::getItemIDs(uuid_vec_t* ids)
1368{
1369	item_map_t::const_iterator it;
1370	for (it = mActive.begin(); it != mActive.end(); ++it)
1371	{
1372		ids->push_back(it->first);
1373	}
1374}
1375
1376void LLGestureMgr::done()
1377{
1378	bool notify = false;
1379	for(item_map_t::iterator it = mActive.begin(); it != mActive.end(); ++it)
1380	{
1381		if(it->second && it->second->mName.empty())
1382		{
1383			LLViewerInventoryItem* item = gInventory.getItem(it->first);
1384			if(item)
1385			{
1386				it->second->mName = item->getName();
1387				notify = true;
1388			}
1389		}
1390	}
1391	if(notify)
1392	{
1393		notifyObservers();
1394	}
1395}
1396
1397// static
1398const LLUUID& get_linked_uuid(const LLUUID &item_id)
1399{
1400	LLViewerInventoryItem* item = gInventory.getItem(item_id);
1401	if (item && item->getIsLinkType())
1402	{
1403		return item->getLinkedUUID();
1404	}
1405	return item_id;
1406}
1407