PageRenderTime 81ms CodeModel.GetById 19ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llspeakers.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 929 lines | 699 code | 131 blank | 99 comment | 158 complexity | d62d9255718394d9ec3fc41090cf17b7 MD5 | raw file
  1/** 
  2 * @file llspeakers.cpp
  3 * @brief Management interface for muting and controlling volume of residents currently speaking
  4 *
  5 * $LicenseInfo:firstyear=2005&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 "llspeakers.h"
 30
 31#include "llagent.h"
 32#include "llappviewer.h"
 33#include "llimview.h"
 34#include "llsdutil.h"
 35#include "lluicolortable.h"
 36#include "llviewerobjectlist.h"
 37#include "llvoavatar.h"
 38#include "llworld.h"
 39
 40const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f);
 41const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f);
 42
 43LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) : 
 44	mStatus(LLSpeaker::STATUS_TEXT_ONLY),
 45	mLastSpokeTime(0.f), 
 46	mSpeechVolume(0.f), 
 47	mHasSpoken(FALSE),
 48	mHasLeftCurrentCall(FALSE),
 49	mDotColor(LLColor4::white),
 50	mID(id),
 51	mTyping(FALSE),
 52	mSortIndex(0),
 53	mType(type),
 54	mIsModerator(FALSE),
 55	mModeratorMutedVoice(FALSE),
 56	mModeratorMutedText(FALSE)
 57{
 58	if (name.empty() && type == SPEAKER_AGENT)
 59	{
 60		lookupName();
 61	}
 62	else
 63	{
 64		mDisplayName = name;
 65	}
 66}
 67
 68
 69void LLSpeaker::lookupName()
 70{
 71	if (mDisplayName.empty())
 72	{
 73		gCacheName->get(mID, false, boost::bind(&LLSpeaker::onNameCache, this, _1, _2, _3));
 74	}
 75}
 76
 77void LLSpeaker::onNameCache(const LLUUID& id, const std::string& full_name, bool is_group)
 78{
 79	mDisplayName = full_name;
 80}
 81
 82bool LLSpeaker::isInVoiceChannel()
 83{
 84	return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED;
 85}
 86
 87LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source)
 88: LLEvent(source, "Speaker add moderator event"),
 89  mSpeakerID (source->mID),
 90  mIsModerator (source->mIsModerator)
 91{
 92}
 93
 94LLSD LLSpeakerUpdateModeratorEvent::getValue()
 95{
 96	LLSD ret;
 97	ret["id"] = mSpeakerID;
 98	ret["is_moderator"] = mIsModerator;
 99	return ret;
100}
101
102LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source)
103: LLEvent(source, "Speaker text moderation event")
104{
105}
106
107LLSD LLSpeakerTextModerationEvent::getValue()
108{
109	return std::string("text");
110}
111
112
113LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source)
114: LLEvent(source, "Speaker voice moderation event")
115{
116}
117
118LLSD LLSpeakerVoiceModerationEvent::getValue()
119{
120	return std::string("voice");
121}
122
123LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id)
124: LLEvent(source, "Speaker added/removed from speaker mgr"),
125  mSpeakerID(speaker_id)
126{
127}
128
129LLSD LLSpeakerListChangeEvent::getValue()
130{
131	return mSpeakerID;
132}
133
134// helper sort class
135struct LLSortRecentSpeakers
136{
137	bool operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const;
138};
139
140bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const
141{
142	// Sort first on status
143	if (lhs->mStatus != rhs->mStatus) 
144	{
145		return (lhs->mStatus < rhs->mStatus);
146	}
147
148	// and then on last speaking time
149	if(lhs->mLastSpokeTime != rhs->mLastSpokeTime)
150	{
151		return (lhs->mLastSpokeTime > rhs->mLastSpokeTime);
152	}
153	
154	// and finally (only if those are both equal), on name.
155	return(	lhs->mDisplayName.compare(rhs->mDisplayName) < 0 );
156}
157
158LLSpeakerActionTimer::LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id)
159: LLEventTimer(action_period)
160, mActionCallback(action_cb)
161, mSpeakerId(speaker_id)
162{
163}
164
165BOOL LLSpeakerActionTimer::tick()
166{
167	if (mActionCallback)
168	{
169		return (BOOL)mActionCallback(mSpeakerId);
170	}
171	return TRUE;
172}
173
174void LLSpeakerActionTimer::unset()
175{
176	mActionCallback = 0;
177}
178
179LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay)
180: mActionCallback(action_cb)
181, mActionDelay(action_delay)
182{
183}
184
185LLSpeakersDelayActionsStorage::~LLSpeakersDelayActionsStorage()
186{
187	removeAllTimers();
188}
189
190void LLSpeakersDelayActionsStorage::setActionTimer(const LLUUID& speaker_id)
191{
192	bool not_found = true;
193	if (mActionTimersMap.size() > 0)
194	{
195		not_found = mActionTimersMap.find(speaker_id) == mActionTimersMap.end();
196	}
197
198	// If there is already a started timer for the passed UUID don't do anything.
199	if (not_found)
200	{
201		// Starting a timer to remove an participant after delay is completed
202		mActionTimersMap.insert(LLSpeakerActionTimer::action_value_t(speaker_id,
203			new LLSpeakerActionTimer(
204				boost::bind(&LLSpeakersDelayActionsStorage::onTimerActionCallback, this, _1),
205				mActionDelay, speaker_id)));
206	}
207}
208
209void LLSpeakersDelayActionsStorage::unsetActionTimer(const LLUUID& speaker_id)
210{
211	if (mActionTimersMap.size() == 0) return;
212
213	LLSpeakerActionTimer::action_timer_iter_t it_speaker = mActionTimersMap.find(speaker_id);
214
215	if (it_speaker != mActionTimersMap.end())
216	{
217		it_speaker->second->unset();
218		mActionTimersMap.erase(it_speaker);
219	}
220}
221
222void LLSpeakersDelayActionsStorage::removeAllTimers()
223{
224	LLSpeakerActionTimer::action_timer_iter_t iter = mActionTimersMap.begin();
225	for (; iter != mActionTimersMap.end(); ++iter)
226	{
227		delete iter->second;
228	}
229	mActionTimersMap.clear();
230}
231
232bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_id)
233{
234	unsetActionTimer(speaker_id);
235
236	if (mActionCallback)
237	{
238		mActionCallback(speaker_id);
239	}
240
241	return true;
242}
243
244
245//
246// LLSpeakerMgr
247//
248
249LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : 
250	mVoiceChannel(channelp)
251, mVoiceModerated(false)
252, mModerateModeHandledFirstTime(false)
253{
254	static LLUICachedControl<F32> remove_delay ("SpeakerParticipantRemoveDelay", 10.0);
255
256	mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay);
257}
258
259LLSpeakerMgr::~LLSpeakerMgr()
260{
261	delete mSpeakerDelayRemover;
262}
263
264LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type)
265{
266	if (id.isNull()) return NULL;
267
268	LLPointer<LLSpeaker> speakerp;
269	if (mSpeakers.find(id) == mSpeakers.end())
270	{
271		speakerp = new LLSpeaker(id, name, type);
272		speakerp->mStatus = status;
273		mSpeakers.insert(std::make_pair(speakerp->mID, speakerp));
274		mSpeakersSorted.push_back(speakerp);
275		fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add");
276	}
277	else
278	{
279		speakerp = findSpeaker(id);
280		if (speakerp.notNull())
281		{
282			// keep highest priority status (lowest value) instead of overriding current value
283			speakerp->mStatus = llmin(speakerp->mStatus, status);
284			// RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id
285			// we need to override speakers that we think are objects when we find out they are really
286			// residents
287			if (type == LLSpeaker::SPEAKER_AGENT)
288			{
289				speakerp->mType = LLSpeaker::SPEAKER_AGENT;
290				speakerp->lookupName();
291			}
292		}
293	}
294
295	mSpeakerDelayRemover->unsetActionTimer(speakerp->mID);
296	return speakerp;
297}
298
299// *TODO: Once way to request the current voice channel moderation mode is implemented
300// this method with related code should be removed.
301/*
302 Initializes "moderate_mode" of voice session on first join.
303 
304 This is WORKAROUND because a way to request the current voice channel moderation mode exists
305 but is not implemented in viewer yet. See EXT-6937.
306*/
307void LLSpeakerMgr::initVoiceModerateMode()
308{
309	if (!mModerateModeHandledFirstTime && (mVoiceChannel && mVoiceChannel->isActive()))
310	{
311		LLPointer<LLSpeaker> speakerp;
312
313		if (mSpeakers.find(gAgentID) != mSpeakers.end())
314		{
315			speakerp = mSpeakers[gAgentID];
316		}
317
318		if (speakerp.notNull())
319		{
320			mVoiceModerated = speakerp->mModeratorMutedVoice;
321			mModerateModeHandledFirstTime = true;
322		}
323	}
324}
325
326void LLSpeakerMgr::update(BOOL resort_ok)
327{
328	if (!LLVoiceClient::getInstance())
329	{
330		return;
331	}
332	
333	LLColor4 speaking_color = LLUIColorTable::instance().getColor("SpeakingColor");
334	LLColor4 overdriven_color = LLUIColorTable::instance().getColor("OverdrivenColor");
335
336	if(resort_ok) // only allow list changes when user is not interacting with it
337	{
338		updateSpeakerList();
339	}
340
341	// update status of all current speakers
342	BOOL voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());
343	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();)
344	{
345		LLUUID speaker_id = speaker_it->first;
346		LLSpeaker* speakerp = speaker_it->second;
347		
348		speaker_map_t::iterator  cur_speaker_it = speaker_it++;
349
350		if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id))
351		{
352			speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id);
353			BOOL moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id);
354			if (moderator_muted_voice != speakerp->mModeratorMutedVoice)
355			{
356				speakerp->mModeratorMutedVoice = moderator_muted_voice;
357				speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp));
358			}
359
360			if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice)
361			{
362				speakerp->mStatus = LLSpeaker::STATUS_MUTED;
363			}
364			else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id))
365			{
366				// reset inactivity expiration
367				if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING)
368				{
369					speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
370					speakerp->mHasSpoken = TRUE;
371				}
372				speakerp->mStatus = LLSpeaker::STATUS_SPEAKING;
373				// interpolate between active color and full speaking color based on power of speech output
374				speakerp->mDotColor = speaking_color;
375				if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL)
376				{
377					speakerp->mDotColor = overdriven_color;
378				}
379			}
380			else
381			{
382				speakerp->mSpeechVolume = 0.f;
383				speakerp->mDotColor = ACTIVE_COLOR;
384
385				if (speakerp->mHasSpoken)
386				{
387					// have spoken once, not currently speaking
388					speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN;
389				}
390				else
391				{
392					// default state for being in voice channel
393					speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE;
394				}
395			}
396		}
397		// speaker no longer registered in voice channel, demote to text only
398		else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL)
399		{
400			if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL)
401			{
402				// external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice)
403				speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
404			}
405			else
406			{
407				speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY;
408				speakerp->mSpeechVolume = 0.f;
409				speakerp->mDotColor = ACTIVE_COLOR;
410			}
411		}
412	}
413
414	if(resort_ok)  // only allow list changes when user is not interacting with it
415	{
416		// sort by status then time last spoken
417		std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers());
418	}
419
420	// for recent speakers who are not currently speaking, show "recent" color dot for most recent
421	// fading to "active" color
422
423	S32 recent_speaker_count = 0;
424	S32 sort_index = 0;
425	speaker_list_t::iterator sorted_speaker_it;
426	for(sorted_speaker_it = mSpeakersSorted.begin(); 
427		sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it)
428	{
429		LLPointer<LLSpeaker> speakerp = *sorted_speaker_it;
430		
431		// color code recent speakers who are not currently speaking
432		if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN)
433		{
434			speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f));
435			recent_speaker_count++;
436		}
437
438		// stuff sort ordinal into speaker so the ui can sort by this value
439		speakerp->mSortIndex = sort_index++;
440	}
441}
442
443void LLSpeakerMgr::updateSpeakerList()
444{
445	// are we bound to the currently active voice channel?
446	if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
447	{
448	        std::set<LLUUID> participants;
449	        LLVoiceClient::getInstance()->getParticipantList(participants);
450		// add new participants to our list of known speakers
451		for (std::set<LLUUID>::iterator participant_it = participants.begin();
452			 participant_it != participants.end(); 
453			 ++participant_it)
454		{
455				setSpeaker(*participant_it, 
456						   LLVoiceClient::getInstance()->getDisplayName(*participant_it),
457						   LLSpeaker::STATUS_VOICE_ACTIVE, 
458						   (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL));
459
460
461		}
462	}
463}
464
465void LLSpeakerMgr::setSpeakerNotInChannel(LLSpeaker* speakerp)
466{
467	speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
468	speakerp->mDotColor = INACTIVE_COLOR;
469	mSpeakerDelayRemover->setActionTimer(speakerp->mID);
470}
471
472bool LLSpeakerMgr::removeSpeaker(const LLUUID& speaker_id)
473{
474	mSpeakers.erase(speaker_id);
475
476	speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin();
477	
478	for(; sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it)
479	{
480		if (speaker_id == (*sorted_speaker_it)->mID)
481		{
482			mSpeakersSorted.erase(sorted_speaker_it);
483			break;
484		}
485	}
486
487	fireEvent(new LLSpeakerListChangeEvent(this, speaker_id), "remove");
488
489	update(TRUE);
490
491	return false;
492}
493
494LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id)
495{
496	//In some conditions map causes crash if it is empty(Windows only), adding check (EK)
497	if (mSpeakers.size() == 0)
498		return NULL;
499	speaker_map_t::iterator found_it = mSpeakers.find(speaker_id);
500	if (found_it == mSpeakers.end())
501	{
502		return NULL;
503	}
504	return found_it->second;
505}
506
507void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, BOOL include_text)
508{
509	speaker_list->clear();
510	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
511	{
512		LLPointer<LLSpeaker> speakerp = speaker_it->second;
513		// what about text only muted or inactive?
514		if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY)
515		{
516			speaker_list->push_back(speakerp);
517		}
518	}
519}
520
521const LLUUID LLSpeakerMgr::getSessionID() 
522{ 
523	return mVoiceChannel->getSessionID(); 
524}
525
526
527void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, BOOL typing)
528{
529	LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
530	if (speakerp.notNull())
531	{
532		speakerp->mTyping = typing;
533	}
534}
535
536// speaker has chatted via either text or voice
537void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id)
538{
539	LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
540	if (speakerp.notNull())
541	{
542		speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
543		speakerp->mHasSpoken = TRUE;
544	}
545}
546
547BOOL LLSpeakerMgr::isVoiceActive()
548{
549	// mVoiceChannel = NULL means current voice channel, whatever it is
550	return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive();
551}
552
553
554//
555// LLIMSpeakerMgr
556//
557LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel)
558{
559}
560
561void LLIMSpeakerMgr::updateSpeakerList()
562{
563	// don't do normal updates which are pulled from voice channel
564	// rely on user list reported by sim
565	
566	// We need to do this to allow PSTN callers into group chats to show in the list.
567	LLSpeakerMgr::updateSpeakerList();
568	
569	return;
570}
571
572void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers)
573{
574	if ( !speakers.isMap() ) return;
575
576	if ( speakers.has("agent_info") && speakers["agent_info"].isMap() )
577	{
578		LLSD::map_const_iterator speaker_it;
579		for(speaker_it = speakers["agent_info"].beginMap();
580			speaker_it != speakers["agent_info"].endMap();
581			++speaker_it)
582		{
583			LLUUID agent_id(speaker_it->first);
584
585			LLPointer<LLSpeaker> speakerp = setSpeaker(
586				agent_id,
587				LLStringUtil::null,
588				LLSpeaker::STATUS_TEXT_ONLY);
589
590			if ( speaker_it->second.isMap() )
591			{
592				BOOL is_moderator = speakerp->mIsModerator;
593				speakerp->mIsModerator = speaker_it->second["is_moderator"];
594				speakerp->mModeratorMutedText =
595					speaker_it->second["mutes"]["text"];
596				// Fire event only if moderator changed
597				if ( is_moderator != speakerp->mIsModerator )
598					fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator");
599			}
600		}
601	}
602	else if ( speakers.has("agents" ) && speakers["agents"].isArray() )
603	{
604		//older, more decprecated way.  Need here for
605		//using older version of servers
606		LLSD::array_const_iterator speaker_it;
607		for(speaker_it = speakers["agents"].beginArray();
608			speaker_it != speakers["agents"].endArray();
609			++speaker_it)
610		{
611			const LLUUID agent_id = (*speaker_it).asUUID();
612
613			LLPointer<LLSpeaker> speakerp = setSpeaker(
614				agent_id,
615				LLStringUtil::null,
616				LLSpeaker::STATUS_TEXT_ONLY);
617		}
618	}
619}
620
621void LLIMSpeakerMgr::updateSpeakers(const LLSD& update)
622{
623	if ( !update.isMap() ) return;
624
625	if ( update.has("agent_updates") && update["agent_updates"].isMap() )
626	{
627		LLSD::map_const_iterator update_it;
628		for(
629			update_it = update["agent_updates"].beginMap();
630			update_it != update["agent_updates"].endMap();
631			++update_it)
632		{
633			LLUUID agent_id(update_it->first);
634			LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id);
635
636			LLSD agent_data = update_it->second;
637
638			if (agent_data.isMap() && agent_data.has("transition"))
639			{
640				if (agent_data["transition"].asString() == "LEAVE" && speakerp.notNull())
641				{
642					setSpeakerNotInChannel(speakerp);
643				}
644				else if (agent_data["transition"].asString() == "ENTER")
645				{
646					// add or update speaker
647					speakerp = setSpeaker(agent_id);
648				}
649				else
650				{
651					llwarns << "bad membership list update " << ll_print_sd(agent_data["transition"]) << llendl;
652				}
653			}
654
655			if (speakerp.isNull()) continue;
656
657			// should have a valid speaker from this point on
658			if (agent_data.isMap() && agent_data.has("info"))
659			{
660				LLSD agent_info = agent_data["info"];
661
662				if (agent_info.has("is_moderator"))
663				{
664					BOOL is_moderator = speakerp->mIsModerator;
665					speakerp->mIsModerator = agent_info["is_moderator"];
666					// Fire event only if moderator changed
667					if ( is_moderator != speakerp->mIsModerator )
668						fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator");
669				}
670
671				if (agent_info.has("mutes"))
672				{
673					speakerp->mModeratorMutedText = agent_info["mutes"]["text"];
674				}
675			}
676		}
677	}
678	else if ( update.has("updates") && update["updates"].isMap() )
679	{
680		LLSD::map_const_iterator update_it;
681		for (
682			update_it = update["updates"].beginMap();
683			update_it != update["updates"].endMap();
684			++update_it)
685		{
686			LLUUID agent_id(update_it->first);
687			LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id);
688
689			std::string agent_transition = update_it->second.asString();
690			if (agent_transition == "LEAVE" && speakerp.notNull())
691			{
692				setSpeakerNotInChannel(speakerp);
693			}
694			else if ( agent_transition == "ENTER")
695			{
696				// add or update speaker
697				speakerp = setSpeaker(agent_id);
698			}
699			else
700			{
701				llwarns << "bad membership list update "
702						<< agent_transition << llendl;
703			}
704		}
705	}
706}
707
708class ModerationResponder : public LLHTTPClient::Responder
709{
710public:
711	ModerationResponder(const LLUUID& session_id)
712	{
713		mSessionID = session_id;
714	}
715
716	virtual void error(U32 status, const std::string& reason)
717	{
718		llwarns << status << ": " << reason << llendl;
719
720		if ( gIMMgr )
721		{
722			//403 == you're not a mod
723			//should be disabled if you're not a moderator
724			if ( 403 == status )
725			{
726				gIMMgr->showSessionEventError(
727					"mute",
728					"not_a_mod_error",
729					mSessionID);
730			}
731			else
732			{
733				gIMMgr->showSessionEventError(
734					"mute",
735					"generic_request_error",
736					mSessionID);
737			}
738		}
739	}
740
741private:
742	LLUUID mSessionID;
743};
744
745void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id)
746{
747	LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
748	if (!speakerp) return;
749
750	std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest");
751	LLSD data;
752	data["method"] = "mute update";
753	data["session-id"] = getSessionID();
754	data["params"] = LLSD::emptyMap();
755	data["params"]["agent_id"] = speaker_id;
756	data["params"]["mute_info"] = LLSD::emptyMap();
757	//current value represents ability to type, so invert
758	data["params"]["mute_info"]["text"] = !speakerp->mModeratorMutedText;
759
760	LLHTTPClient::post(url, data, new ModerationResponder(getSessionID()));
761}
762
763void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute)
764{
765	LLPointer<LLSpeaker> speakerp = findSpeaker(avatar_id);
766	if (!speakerp) return;
767
768	// *NOTE: mantipov: probably this condition will be incorrect when avatar will be blocked for
769	// text chat via moderation (LLSpeaker::mModeratorMutedText == TRUE)
770	bool is_in_voice = speakerp->mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || speakerp->mStatus == LLSpeaker::STATUS_MUTED;
771
772	// do not send voice moderation changes for avatars not in voice channel
773	if (!is_in_voice) return;
774
775	std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest");
776	LLSD data;
777	data["method"] = "mute update";
778	data["session-id"] = getSessionID();
779	data["params"] = LLSD::emptyMap();
780	data["params"]["agent_id"] = avatar_id;
781	data["params"]["mute_info"] = LLSD::emptyMap();
782	data["params"]["mute_info"]["voice"] = !unmute;
783
784	LLHTTPClient::post(
785		url,
786		data,
787		new ModerationResponder(getSessionID()));
788}
789
790void LLIMSpeakerMgr::moderateVoiceAllParticipants( bool unmute_everyone )
791{
792	if (mVoiceModerated == !unmute_everyone)
793	{
794		// session already in requested state. Just force participants which do not match it.
795		forceVoiceModeratedMode(mVoiceModerated);
796	}
797	else
798	{
799		// otherwise set moderated mode for a whole session.
800		moderateVoiceSession(getSessionID(), !unmute_everyone);
801	}
802}
803
804void LLIMSpeakerMgr::processSessionUpdate(const LLSD& session_update)
805{
806	if (session_update.has("moderated_mode") &&
807		session_update["moderated_mode"].has("voice"))
808	{
809		mVoiceModerated = session_update["moderated_mode"]["voice"];
810	}
811}
812
813void LLIMSpeakerMgr::moderateVoiceSession(const LLUUID& session_id, bool disallow_voice)
814{
815	std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest");
816	LLSD data;
817	data["method"] = "session update";
818	data["session-id"] = session_id;
819	data["params"] = LLSD::emptyMap();
820
821	data["params"]["update_info"] = LLSD::emptyMap();
822
823	data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap();
824	data["params"]["update_info"]["moderated_mode"]["voice"] = disallow_voice;
825
826	LLHTTPClient::post(url, data, new ModerationResponder(session_id));
827}
828
829void LLIMSpeakerMgr::forceVoiceModeratedMode(bool should_be_muted)
830{
831	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
832	{
833		LLUUID speaker_id = speaker_it->first;
834		LLSpeaker* speakerp = speaker_it->second;
835
836		// participant does not match requested state
837		if (should_be_muted != (bool)speakerp->mModeratorMutedVoice)
838		{
839			moderateVoiceParticipant(speaker_id, !should_be_muted);
840		}
841	}
842}
843
844//
845// LLActiveSpeakerMgr
846//
847
848LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL)
849{
850}
851
852void LLActiveSpeakerMgr::updateSpeakerList()
853{
854	// point to whatever the current voice channel is
855	mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
856
857	// always populate from active voice channel
858	if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) //MA: seems this is always false
859	{
860		fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear");
861		mSpeakers.clear();
862		mSpeakersSorted.clear();
863		mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
864		mSpeakerDelayRemover->removeAllTimers();
865	}
866	LLSpeakerMgr::updateSpeakerList();
867
868	// clean up text only speakers
869	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
870	{
871		LLUUID speaker_id = speaker_it->first;
872		LLSpeaker* speakerp = speaker_it->second;
873		if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
874		{
875			// automatically flag text only speakers for removal
876			speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
877		}
878	}
879
880}
881
882
883
884//
885// LLLocalSpeakerMgr
886//
887
888LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance())
889{
890}
891
892LLLocalSpeakerMgr::~LLLocalSpeakerMgr ()
893{
894}
895
896void LLLocalSpeakerMgr::updateSpeakerList()
897{
898	// pull speakers from voice channel
899	LLSpeakerMgr::updateSpeakerList();
900
901	if (gDisconnected)//the world is cleared.
902	{
903		return ;
904	}
905
906	// pick up non-voice speakers in chat range
907	uuid_vec_t avatar_ids;
908	std::vector<LLVector3d> positions;
909	LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS);
910	for(U32 i=0; i<avatar_ids.size(); i++)
911	{
912		setSpeaker(avatar_ids[i]);
913	}
914
915	// check if text only speakers have moved out of chat range
916	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
917	{
918		LLUUID speaker_id = speaker_it->first;
919		LLSpeaker* speakerp = speaker_it->second;
920		if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
921		{
922			LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id);
923			if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS_SQUARED)
924			{
925				setSpeakerNotInChannel(speakerp);
926			}
927		}
928	}
929}