diff options
| author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
|---|---|---|
| committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
| commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
| tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llspeakers.cpp | |
| parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
| parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) | |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/newview/llspeakers.cpp')
| -rw-r--r-- | indra/newview/llspeakers.cpp | 2084 |
1 files changed, 1042 insertions, 1042 deletions
diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index e521378e70..7d71873b68 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -1,1042 +1,1042 @@ -/** - * @file llspeakers.cpp - * @brief Management interface for muting and controlling volume of residents currently speaking - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llspeakers.h" - -#include "llagent.h" -#include "llavatarnamecache.h" -#include "llappviewer.h" -#include "llimview.h" -#include "llgroupmgr.h" -#include "llsdutil.h" -#include "lluicolortable.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llvoavatar.h" -#include "llworld.h" -#include "llcorehttputil.h" - -extern LLControlGroup gSavedSettings; - -const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f); -const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); - -LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) : - mStatus(LLSpeaker::STATUS_TEXT_ONLY), - mLastSpokeTime(0.f), - mSpeechVolume(0.f), - mHasSpoken(false), - mHasLeftCurrentCall(false), - mDotColor(LLColor4::white), - mID(id), - mTyping(false), - mSortIndex(0), - mType(type), - mIsModerator(false), - mModeratorMutedVoice(false), - mModeratorMutedText(false) -{ - if (name.empty() && type == SPEAKER_AGENT) - { - lookupName(); - } - else - { - mDisplayName = name; - } -} - - -void LLSpeaker::lookupName() -{ - if (mDisplayName.empty()) - { - LLAvatarNameCache::get(mID, boost::bind(&LLSpeaker::onNameCache, this, _1, _2)); // todo: can be group??? - } -} - -void LLSpeaker::onNameCache(const LLUUID& id, const LLAvatarName& av_name) -{ - mDisplayName = av_name.getUserName(); -} - -bool LLSpeaker::isInVoiceChannel() -{ - return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED; -} - -LLSpeakerUpdateSpeakerEvent::LLSpeakerUpdateSpeakerEvent(LLSpeaker* source) -: LLEvent(source, "Speaker update speaker event"), - mSpeakerID (source->mID) -{ -} - -LLSD LLSpeakerUpdateSpeakerEvent::getValue() -{ - LLSD ret; - ret["id"] = mSpeakerID; - return ret; -} - -LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source) -: LLEvent(source, "Speaker add moderator event"), - mSpeakerID (source->mID), - mIsModerator (source->mIsModerator) -{ -} - -LLSD LLSpeakerUpdateModeratorEvent::getValue() -{ - LLSD ret; - ret["id"] = mSpeakerID; - ret["is_moderator"] = mIsModerator; - return ret; -} - -LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source) -: LLEvent(source, "Speaker text moderation event") -{ -} - -LLSD LLSpeakerTextModerationEvent::getValue() -{ - return std::string("text"); -} - - -LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source) -: LLEvent(source, "Speaker voice moderation event") -{ -} - -LLSD LLSpeakerVoiceModerationEvent::getValue() -{ - return std::string("voice"); -} - -LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id) -: LLEvent(source, "Speaker added/removed from speaker mgr"), - mSpeakerID(speaker_id) -{ -} - -LLSD LLSpeakerListChangeEvent::getValue() -{ - return mSpeakerID; -} - -// helper sort class -struct LLSortRecentSpeakers -{ - bool operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const; -}; - -bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const -{ - // Sort first on status - if (lhs->mStatus != rhs->mStatus) - { - return (lhs->mStatus < rhs->mStatus); - } - - // and then on last speaking time - if(lhs->mLastSpokeTime != rhs->mLastSpokeTime) - { - return (lhs->mLastSpokeTime > rhs->mLastSpokeTime); - } - - // and finally (only if those are both equal), on name. - return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 ); -} - -LLSpeakerActionTimer::LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id) -: LLEventTimer(action_period) -, mActionCallback(action_cb) -, mSpeakerId(speaker_id) -{ -} - -bool LLSpeakerActionTimer::tick() -{ - if (mActionCallback) - { - return (bool)mActionCallback(mSpeakerId); - } - return true; -} - -void LLSpeakerActionTimer::unset() -{ - mActionCallback = 0; -} - -LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay) -: mActionCallback(action_cb) -, mActionDelay(action_delay) -{ -} - -LLSpeakersDelayActionsStorage::~LLSpeakersDelayActionsStorage() -{ - removeAllTimers(); -} - -void LLSpeakersDelayActionsStorage::setActionTimer(const LLUUID& speaker_id) -{ - bool not_found = true; - if (mActionTimersMap.size() > 0) - { - not_found = mActionTimersMap.find(speaker_id) == mActionTimersMap.end(); - } - - // If there is already a started timer for the passed UUID don't do anything. - if (not_found) - { - // Starting a timer to remove an participant after delay is completed - mActionTimersMap.insert(LLSpeakerActionTimer::action_value_t(speaker_id, - new LLSpeakerActionTimer( - boost::bind(&LLSpeakersDelayActionsStorage::onTimerActionCallback, this, _1), - mActionDelay, speaker_id))); - } -} - -void LLSpeakersDelayActionsStorage::unsetActionTimer(const LLUUID& speaker_id) -{ - if (mActionTimersMap.size() == 0) return; - - LLSpeakerActionTimer::action_timer_iter_t it_speaker = mActionTimersMap.find(speaker_id); - - if (it_speaker != mActionTimersMap.end()) - { - it_speaker->second->unset(); - mActionTimersMap.erase(it_speaker); - } -} - -void LLSpeakersDelayActionsStorage::removeAllTimers() -{ - LLSpeakerActionTimer::action_timer_iter_t iter = mActionTimersMap.begin(); - for (; iter != mActionTimersMap.end(); ++iter) - { - delete iter->second; - } - mActionTimersMap.clear(); -} - -bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_id) -{ - unsetActionTimer(speaker_id); - - if (mActionCallback) - { - mActionCallback(speaker_id); - } - - return true; -} - -bool LLSpeakersDelayActionsStorage::isTimerStarted(const LLUUID& speaker_id) -{ - return (mActionTimersMap.size() > 0) && (mActionTimersMap.find(speaker_id) != mActionTimersMap.end()); -} - -// -// LLSpeakerMgr -// - -LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : - mVoiceChannel(channelp), - mVoiceModerated(false), - mModerateModeHandledFirstTime(false), - mSpeakerListUpdated(false) -{ - mGetListTime.reset(); - static LLUICachedControl<F32> remove_delay ("SpeakerParticipantRemoveDelay", 10.0); - - mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay); -} - -LLSpeakerMgr::~LLSpeakerMgr() -{ - delete mSpeakerDelayRemover; -} - -LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) -{ - LLUUID session_id = getSessionID(); - if (id.isNull() || (id == session_id)) - { - return NULL; - } - - LLPointer<LLSpeaker> speakerp; - if (mSpeakers.find(id) == mSpeakers.end()) - { - speakerp = new LLSpeaker(id, name, type); - speakerp->mStatus = status; - mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); - mSpeakersSorted.push_back(speakerp); - LL_DEBUGS("Speakers") << "Added speaker " << id << LL_ENDL; - fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add"); - } - else - { - speakerp = findSpeaker(id); - if (speakerp.notNull()) - { - // keep highest priority status (lowest value) instead of overriding current value - speakerp->mStatus = llmin(speakerp->mStatus, status); - // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id - // we need to override speakers that we think are objects when we find out they are really - // residents - if (type == LLSpeaker::SPEAKER_AGENT) - { - speakerp->mType = LLSpeaker::SPEAKER_AGENT; - speakerp->lookupName(); - } - } - else - { - LL_WARNS("Speakers") << "Speaker " << id << " not found" << LL_ENDL; - } - } - - mSpeakerDelayRemover->unsetActionTimer(speakerp->mID); - return speakerp; -} - -// *TODO: Once way to request the current voice channel moderation mode is implemented -// this method with related code should be removed. -/* - Initializes "moderate_mode" of voice session on first join. - - This is WORKAROUND because a way to request the current voice channel moderation mode exists - but is not implemented in viewer yet. See EXT-6937. -*/ -void LLSpeakerMgr::initVoiceModerateMode() -{ - if (!mModerateModeHandledFirstTime && (mVoiceChannel && mVoiceChannel->isActive())) - { - LLPointer<LLSpeaker> speakerp; - - if (mSpeakers.find(gAgentID) != mSpeakers.end()) - { - speakerp = mSpeakers[gAgentID]; - } - - if (speakerp.notNull()) - { - mVoiceModerated = speakerp->mModeratorMutedVoice; - mModerateModeHandledFirstTime = true; - } - } -} - -void LLSpeakerMgr::update(bool resort_ok) -{ - if (!LLVoiceClient::getInstance()) - { - return; - } - - LLColor4 speaking_color = LLUIColorTable::instance().getColor("SpeakingColor"); - LLColor4 overdriven_color = LLUIColorTable::instance().getColor("OverdrivenColor"); - - if(resort_ok) // only allow list changes when user is not interacting with it - { - updateSpeakerList(); - } - - // update status of all current speakers - bool voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); - for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); speaker_it++) - { - LLUUID speaker_id = speaker_it->first; - LLSpeaker* speakerp = speaker_it->second; - - if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id)) - { - speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id); - bool moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id); - if (moderator_muted_voice != speakerp->mModeratorMutedVoice) - { - speakerp->mModeratorMutedVoice = moderator_muted_voice; - LL_DEBUGS("Speakers") << (speakerp->mModeratorMutedVoice? "Muted" : "Umuted") << " speaker " << speaker_id<< LL_ENDL; - speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp)); - } - - if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) - { - speakerp->mStatus = LLSpeaker::STATUS_MUTED; - } - else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id)) - { - // reset inactivity expiration - if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) - { - speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); - speakerp->mHasSpoken = true; - fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); - } - speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; - // interpolate between active color and full speaking color based on power of speech output - speakerp->mDotColor = speaking_color; - if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL) - { - speakerp->mDotColor = overdriven_color; - } - } - else - { - speakerp->mSpeechVolume = 0.f; - speakerp->mDotColor = ACTIVE_COLOR; - - if (speakerp->mHasSpoken) - { - // have spoken once, not currently speaking - speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN; - } - else - { - // default state for being in voice channel - speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE; - } - } - } - // speaker no longer registered in voice channel, demote to text only - else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL) - { - if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL) - { - // external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice) - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - } - else - { - speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY; - speakerp->mSpeechVolume = 0.f; - speakerp->mDotColor = ACTIVE_COLOR; - } - } - } - - if(resort_ok) // only allow list changes when user is not interacting with it - { - // sort by status then time last spoken - std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers()); - } - - // for recent speakers who are not currently speaking, show "recent" color dot for most recent - // fading to "active" color - - S32 recent_speaker_count = 0; - S32 sort_index = 0; - speaker_list_t::iterator sorted_speaker_it; - for(sorted_speaker_it = mSpeakersSorted.begin(); - sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) - { - LLPointer<LLSpeaker> speakerp = *sorted_speaker_it; - - // color code recent speakers who are not currently speaking - if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN) - { - speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f)); - recent_speaker_count++; - } - - // stuff sort ordinal into speaker so the ui can sort by this value - speakerp->mSortIndex = sort_index++; - } -} - -void LLSpeakerMgr::updateSpeakerList() -{ - // Are we bound to the currently active voice channel? - if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) - { - std::set<LLUUID> participants; - LLVoiceClient::getInstance()->getParticipantList(participants); - // If we are, add all voice client participants to our list of known speakers - for (std::set<LLUUID>::iterator participant_it = participants.begin(); participant_it != participants.end(); ++participant_it) - { - setSpeaker(*participant_it, - LLVoiceClient::getInstance()->getDisplayName(*participant_it), - LLSpeaker::STATUS_VOICE_ACTIVE, - (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); - } - } - else - { - // If not, check if the list is empty, except if it's Nearby Chat (session_id NULL). - LLUUID session_id = getSessionID(); - if (!session_id.isNull() && !mSpeakerListUpdated) - { - // If the list is empty, we update it with whatever we have locally so that it doesn't stay empty too long. - // *TODO: Fix the server side code that sometimes forgets to send back the list of participants after a chat started. - // (IOW, fix why we get no ChatterBoxSessionAgentListUpdates message after the initial ChatterBoxSessionStartReply) - LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); - if (session->isGroupSessionType() && (mSpeakers.size() <= 1)) - { - // For groups, we need to hit the group manager. - // Note: The session uuid and the group uuid are actually one and the same. If that was to change, this will fail. - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(session_id); - - if (gdatap && gdatap->isMemberDataComplete() && !gdatap->mMembers.empty()) - { - // Add group members when we get the complete list (note: can take a while before we get that list) - LLGroupMgrGroupData::member_list_t::iterator member_it = gdatap->mMembers.begin(); - const S32 load_group_max_members = gSavedSettings.getS32("ChatLoadGroupMaxMembers"); - S32 updated = 0; - while (member_it != gdatap->mMembers.end()) - { - LLGroupMemberData* member = member_it->second; - LLUUID id = member_it->first; - // Add only members who are online and not already in the list - if ((member->getOnlineStatus() == "Online") && (mSpeakers.find(id) == mSpeakers.end())) - { - LLPointer<LLSpeaker> speakerp = setSpeaker(id, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); - speakerp->mIsModerator = ((member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR); - updated++; - } - ++member_it; - // Limit the number of "manually updated" participants to a reasonable number to avoid severe fps drop - // *TODO : solve the perf issue of having several hundreds of widgets in the conversation list - if (updated >= load_group_max_members) - break; - } - mSpeakerListUpdated = true; - } - } - else if (mSpeakers.size() == 0) - { - // For all other session type (ad-hoc, P2P), we use the initial participants targets list - for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();it!=session->mInitialTargetIDs.end();++it) - { - // Add buddies if they are on line, add any other avatar. - if (!LLAvatarTracker::instance().isBuddy(*it) || LLAvatarTracker::instance().isBuddyOnline(*it)) - { - setSpeaker(*it, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); - } - } - mSpeakerListUpdated = true; - } - else - { - // The list has been updated the normal way (i.e. by a ChatterBoxSessionAgentListUpdates received from the server) - mSpeakerListUpdated = true; - } - } - } - // Always add the current agent (it has to be there...). Will do nothing if already there. - setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); -} - -void LLSpeakerMgr::setSpeakerNotInChannel(LLPointer<LLSpeaker> speakerp) -{ - if (speakerp.notNull()) - { - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - speakerp->mDotColor = INACTIVE_COLOR; - mSpeakerDelayRemover->setActionTimer(speakerp->mID); - } -} - -bool LLSpeakerMgr::removeSpeaker(const LLUUID& speaker_id) -{ - mSpeakers.erase(speaker_id); - - speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin(); - - for(; sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) - { - if (speaker_id == (*sorted_speaker_it)->mID) - { - mSpeakersSorted.erase(sorted_speaker_it); - break; - } - } - - LL_DEBUGS("Speakers") << "Removed speaker " << speaker_id << LL_ENDL; - fireEvent(new LLSpeakerListChangeEvent(this, speaker_id), "remove"); - - update(true); - - return false; -} - -LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) -{ - //In some conditions map causes crash if it is empty(Windows only), adding check (EK) - if (mSpeakers.size() == 0) - return NULL; - speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); - if (found_it == mSpeakers.end()) - { - return NULL; - } - return found_it->second; -} - -void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, bool include_text) -{ - speaker_list->clear(); - for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) - { - LLPointer<LLSpeaker> speakerp = speaker_it->second; - // what about text only muted or inactive? - if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY) - { - speaker_list->push_back(speakerp); - } - } -} - -const LLUUID LLSpeakerMgr::getSessionID() -{ - return mVoiceChannel->getSessionID(); -} - -bool LLSpeakerMgr::isSpeakerToBeRemoved(const LLUUID& speaker_id) -{ - return mSpeakerDelayRemover && mSpeakerDelayRemover->isTimerStarted(speaker_id); -} - -void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, bool typing) -{ - LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); - if (speakerp.notNull()) - { - speakerp->mTyping = typing; - } -} - -// speaker has chatted via either text or voice -void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) -{ - LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); - if (speakerp.notNull()) - { - speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); - speakerp->mHasSpoken = true; - fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); - } -} - -bool LLSpeakerMgr::isVoiceActive() -{ - // mVoiceChannel = NULL means current voice channel, whatever it is - return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); -} - - -// -// LLIMSpeakerMgr -// -LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel) -{ -} - -void LLIMSpeakerMgr::updateSpeakerList() -{ - // don't do normal updates which are pulled from voice channel - // rely on user list reported by sim - - // We need to do this to allow PSTN callers into group chats to show in the list. - LLSpeakerMgr::updateSpeakerList(); - - return; -} - -void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) -{ - if ( !speakers.isMap() ) return; - - if ( speakers.has("agent_info") && speakers["agent_info"].isMap() ) - { - LLSD::map_const_iterator speaker_it; - for(speaker_it = speakers["agent_info"].beginMap(); - speaker_it != speakers["agent_info"].endMap(); - ++speaker_it) - { - LLUUID agent_id(speaker_it->first); - - LLPointer<LLSpeaker> speakerp = setSpeaker( - agent_id, - LLStringUtil::null, - LLSpeaker::STATUS_TEXT_ONLY); - - if ( speaker_it->second.isMap() ) - { - bool is_moderator = speakerp->mIsModerator; - speakerp->mIsModerator = speaker_it->second["is_moderator"]; - speakerp->mModeratorMutedText = - speaker_it->second["mutes"]["text"]; - // Fire event only if moderator changed - if ( is_moderator != speakerp->mIsModerator ) - { - LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL; - fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); - } - } - } - } - else if ( speakers.has("agents" ) && speakers["agents"].isArray() ) - { - //older, more decprecated way. Need here for - //using older version of servers - LLSD::array_const_iterator speaker_it; - for(speaker_it = speakers["agents"].beginArray(); - speaker_it != speakers["agents"].endArray(); - ++speaker_it) - { - const LLUUID agent_id = (*speaker_it).asUUID(); - - LLPointer<LLSpeaker> speakerp = setSpeaker( - agent_id, - LLStringUtil::null, - LLSpeaker::STATUS_TEXT_ONLY); - } - } -} - -void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) -{ - if ( !update.isMap() ) return; - - if ( update.has("agent_updates") && update["agent_updates"].isMap() ) - { - LLSD::map_const_iterator update_it; - for( - update_it = update["agent_updates"].beginMap(); - update_it != update["agent_updates"].endMap(); - ++update_it) - { - LLUUID agent_id(update_it->first); - LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id); - - LLSD agent_data = update_it->second; - - if (agent_data.isMap() && agent_data.has("transition")) - { - if (agent_data["transition"].asString() == "LEAVE") - { - setSpeakerNotInChannel(speakerp); - } - else if (agent_data["transition"].asString() == "ENTER") - { - // add or update speaker - speakerp = setSpeaker(agent_id); - } - else - { - LL_WARNS() << "bad membership list update from 'agent_updates' for agent " << agent_id << ", transition " << ll_print_sd(agent_data["transition"]) << LL_ENDL; - } - } - - if (speakerp.isNull()) continue; - - // should have a valid speaker from this point on - if (agent_data.isMap() && agent_data.has("info")) - { - LLSD agent_info = agent_data["info"]; - - if (agent_info.has("is_moderator")) - { - bool is_moderator = speakerp->mIsModerator; - speakerp->mIsModerator = agent_info["is_moderator"]; - // Fire event only if moderator changed - if ( is_moderator != speakerp->mIsModerator ) - { - LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL; - fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); - } - } - - if (agent_info.has("mutes")) - { - speakerp->mModeratorMutedText = agent_info["mutes"]["text"]; - } - } - } - } - else if ( update.has("updates") && update["updates"].isMap() ) - { - LLSD::map_const_iterator update_it; - for ( - update_it = update["updates"].beginMap(); - update_it != update["updates"].endMap(); - ++update_it) - { - LLUUID agent_id(update_it->first); - LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id); - - std::string agent_transition = update_it->second.asString(); - if (agent_transition == "LEAVE") - { - setSpeakerNotInChannel(speakerp); - } - else if ( agent_transition == "ENTER") - { - // add or update speaker - speakerp = setSpeaker(agent_id); - } - else - { - LL_WARNS() << "bad membership list update from 'updates' for agent " << agent_id << ", transition " << agent_transition << LL_ENDL; - } - } - } -} - -void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id) -{ - LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); - if (!speakerp) return; - - std::string url = gAgent.getRegionCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "mute update"; - data["session-id"] = getSessionID(); - data["params"] = LLSD::emptyMap(); - data["params"]["agent_id"] = speaker_id; - data["params"]["mute_info"] = LLSD::emptyMap(); - //current value represents ability to type, so invert - data["params"]["mute_info"]["text"] = !speakerp->mModeratorMutedText; - - LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", - boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); -} - -void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) -{ - LLPointer<LLSpeaker> speakerp = findSpeaker(avatar_id); - if (!speakerp) return; - - // *NOTE: mantipov: probably this condition will be incorrect when avatar will be blocked for - // text chat via moderation (LLSpeaker::mModeratorMutedText == true) - bool is_in_voice = speakerp->mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || speakerp->mStatus == LLSpeaker::STATUS_MUTED; - - // do not send voice moderation changes for avatars not in voice channel - if (!is_in_voice) return; - - std::string url = gAgent.getRegionCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "mute update"; - data["session-id"] = getSessionID(); - data["params"] = LLSD::emptyMap(); - data["params"]["agent_id"] = avatar_id; - data["params"]["mute_info"] = LLSD::emptyMap(); - data["params"]["mute_info"]["voice"] = !unmute; - - LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", - boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); -} - -void LLIMSpeakerMgr::moderationActionCoro(std::string url, LLSD action) -{ - LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); - LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t - httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("moderationActionCoro", httpPolicy)); - LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - - httpOpts->setWantHeaders(true); - - LLUUID sessionId = action["session-id"]; - - LLSD result = httpAdapter->postAndSuspend(httpRequest, url, action, httpOpts); - - LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; - LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - - if (!status) - { - if (gIMMgr) - { - //403 == you're not a mod - //should be disabled if you're not a moderator - if (status == LLCore::HttpStatus(HTTP_FORBIDDEN)) - { - gIMMgr->showSessionEventError( - "mute", - "not_a_mod_error", - sessionId); - } - else - { - gIMMgr->showSessionEventError( - "mute", - "generic_request_error", - sessionId); - } - } - return; - } -} - -void LLIMSpeakerMgr::moderateVoiceAllParticipants( bool unmute_everyone ) -{ - if (mVoiceModerated == !unmute_everyone) - { - // session already in requested state. Just force participants which do not match it. - forceVoiceModeratedMode(mVoiceModerated); - } - else - { - // otherwise set moderated mode for a whole session. - moderateVoiceSession(getSessionID(), !unmute_everyone); - } -} - -void LLIMSpeakerMgr::processSessionUpdate(const LLSD& session_update) -{ - if (session_update.has("moderated_mode") && - session_update["moderated_mode"].has("voice")) - { - mVoiceModerated = session_update["moderated_mode"]["voice"]; - } -} - -void LLIMSpeakerMgr::moderateVoiceSession(const LLUUID& session_id, bool disallow_voice) -{ - std::string url = gAgent.getRegionCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "session update"; - data["session-id"] = session_id; - data["params"] = LLSD::emptyMap(); - - data["params"]["update_info"] = LLSD::emptyMap(); - - data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap(); - data["params"]["update_info"]["moderated_mode"]["voice"] = disallow_voice; - - LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", - boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); -} - -void LLIMSpeakerMgr::forceVoiceModeratedMode(bool should_be_muted) -{ - for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) - { - LLUUID speaker_id = speaker_it->first; - LLSpeaker* speakerp = speaker_it->second; - - // participant does not match requested state - if (should_be_muted != (bool)speakerp->mModeratorMutedVoice) - { - moderateVoiceParticipant(speaker_id, !should_be_muted); - } - } -} - -// -// LLActiveSpeakerMgr -// - -LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL) -{ -} - -void LLActiveSpeakerMgr::updateSpeakerList() -{ - // point to whatever the current voice channel is - mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); - - // always populate from active voice channel - if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) //MA: seems this is always false - { - LL_DEBUGS("Speakers") << "Removed all speakers" << LL_ENDL; - fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear"); - mSpeakers.clear(); - mSpeakersSorted.clear(); - mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); - mSpeakerDelayRemover->removeAllTimers(); - } - LLSpeakerMgr::updateSpeakerList(); - - // clean up text only speakers - for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) - { - LLSpeaker* speakerp = speaker_it->second; - if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) - { - // automatically flag text only speakers for removal - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - } - } - -} - - - -// -// LLLocalSpeakerMgr -// - -LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance()) -{ -} - -LLLocalSpeakerMgr::~LLLocalSpeakerMgr () -{ -} - -void LLLocalSpeakerMgr::updateSpeakerList() -{ - // pull speakers from voice channel - LLSpeakerMgr::updateSpeakerList(); - - if (gDisconnected)//the world is cleared. - { - return ; - } - - // pick up non-voice speakers in chat range - uuid_vec_t avatar_ids; - std::vector<LLVector3d> positions; - LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS); - for(U32 i=0; i<avatar_ids.size(); i++) - { - setSpeaker(avatar_ids[i]); - } - - // check if text only speakers have moved out of chat range - for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) - { - LLUUID speaker_id = speaker_it->first; - LLPointer<LLSpeaker> speakerp = speaker_it->second; - if (speakerp.notNull() && speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) - { - LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id); - if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS * CHAT_NORMAL_RADIUS) - { - setSpeakerNotInChannel(speakerp); - } - } - } -} +/**
+ * @file llspeakers.cpp
+ * @brief Management interface for muting and controlling volume of residents currently speaking
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llspeakers.h"
+
+#include "llagent.h"
+#include "llavatarnamecache.h"
+#include "llappviewer.h"
+#include "llimview.h"
+#include "llgroupmgr.h"
+#include "llsdutil.h"
+#include "lluicolortable.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "llcorehttputil.h"
+
+extern LLControlGroup gSavedSettings;
+
+const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f);
+const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f);
+
+LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) :
+ mStatus(LLSpeaker::STATUS_TEXT_ONLY),
+ mLastSpokeTime(0.f),
+ mSpeechVolume(0.f),
+ mHasSpoken(false),
+ mHasLeftCurrentCall(false),
+ mDotColor(LLColor4::white),
+ mID(id),
+ mTyping(false),
+ mSortIndex(0),
+ mType(type),
+ mIsModerator(false),
+ mModeratorMutedVoice(false),
+ mModeratorMutedText(false)
+{
+ if (name.empty() && type == SPEAKER_AGENT)
+ {
+ lookupName();
+ }
+ else
+ {
+ mDisplayName = name;
+ }
+}
+
+
+void LLSpeaker::lookupName()
+{
+ if (mDisplayName.empty())
+ {
+ LLAvatarNameCache::get(mID, boost::bind(&LLSpeaker::onNameCache, this, _1, _2)); // todo: can be group???
+ }
+}
+
+void LLSpeaker::onNameCache(const LLUUID& id, const LLAvatarName& av_name)
+{
+ mDisplayName = av_name.getUserName();
+}
+
+bool LLSpeaker::isInVoiceChannel()
+{
+ return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED;
+}
+
+LLSpeakerUpdateSpeakerEvent::LLSpeakerUpdateSpeakerEvent(LLSpeaker* source)
+: LLEvent(source, "Speaker update speaker event"),
+ mSpeakerID (source->mID)
+{
+}
+
+LLSD LLSpeakerUpdateSpeakerEvent::getValue()
+{
+ LLSD ret;
+ ret["id"] = mSpeakerID;
+ return ret;
+}
+
+LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source)
+: LLEvent(source, "Speaker add moderator event"),
+ mSpeakerID (source->mID),
+ mIsModerator (source->mIsModerator)
+{
+}
+
+LLSD LLSpeakerUpdateModeratorEvent::getValue()
+{
+ LLSD ret;
+ ret["id"] = mSpeakerID;
+ ret["is_moderator"] = mIsModerator;
+ return ret;
+}
+
+LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source)
+: LLEvent(source, "Speaker text moderation event")
+{
+}
+
+LLSD LLSpeakerTextModerationEvent::getValue()
+{
+ return std::string("text");
+}
+
+
+LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source)
+: LLEvent(source, "Speaker voice moderation event")
+{
+}
+
+LLSD LLSpeakerVoiceModerationEvent::getValue()
+{
+ return std::string("voice");
+}
+
+LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id)
+: LLEvent(source, "Speaker added/removed from speaker mgr"),
+ mSpeakerID(speaker_id)
+{
+}
+
+LLSD LLSpeakerListChangeEvent::getValue()
+{
+ return mSpeakerID;
+}
+
+// helper sort class
+struct LLSortRecentSpeakers
+{
+ bool operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const;
+};
+
+bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const
+{
+ // Sort first on status
+ if (lhs->mStatus != rhs->mStatus)
+ {
+ return (lhs->mStatus < rhs->mStatus);
+ }
+
+ // and then on last speaking time
+ if(lhs->mLastSpokeTime != rhs->mLastSpokeTime)
+ {
+ return (lhs->mLastSpokeTime > rhs->mLastSpokeTime);
+ }
+
+ // and finally (only if those are both equal), on name.
+ return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 );
+}
+
+LLSpeakerActionTimer::LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id)
+: LLEventTimer(action_period)
+, mActionCallback(action_cb)
+, mSpeakerId(speaker_id)
+{
+}
+
+bool LLSpeakerActionTimer::tick()
+{
+ if (mActionCallback)
+ {
+ return (bool)mActionCallback(mSpeakerId);
+ }
+ return true;
+}
+
+void LLSpeakerActionTimer::unset()
+{
+ mActionCallback = 0;
+}
+
+LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay)
+: mActionCallback(action_cb)
+, mActionDelay(action_delay)
+{
+}
+
+LLSpeakersDelayActionsStorage::~LLSpeakersDelayActionsStorage()
+{
+ removeAllTimers();
+}
+
+void LLSpeakersDelayActionsStorage::setActionTimer(const LLUUID& speaker_id)
+{
+ bool not_found = true;
+ if (mActionTimersMap.size() > 0)
+ {
+ not_found = mActionTimersMap.find(speaker_id) == mActionTimersMap.end();
+ }
+
+ // If there is already a started timer for the passed UUID don't do anything.
+ if (not_found)
+ {
+ // Starting a timer to remove an participant after delay is completed
+ mActionTimersMap.insert(LLSpeakerActionTimer::action_value_t(speaker_id,
+ new LLSpeakerActionTimer(
+ boost::bind(&LLSpeakersDelayActionsStorage::onTimerActionCallback, this, _1),
+ mActionDelay, speaker_id)));
+ }
+}
+
+void LLSpeakersDelayActionsStorage::unsetActionTimer(const LLUUID& speaker_id)
+{
+ if (mActionTimersMap.size() == 0) return;
+
+ LLSpeakerActionTimer::action_timer_iter_t it_speaker = mActionTimersMap.find(speaker_id);
+
+ if (it_speaker != mActionTimersMap.end())
+ {
+ it_speaker->second->unset();
+ mActionTimersMap.erase(it_speaker);
+ }
+}
+
+void LLSpeakersDelayActionsStorage::removeAllTimers()
+{
+ LLSpeakerActionTimer::action_timer_iter_t iter = mActionTimersMap.begin();
+ for (; iter != mActionTimersMap.end(); ++iter)
+ {
+ delete iter->second;
+ }
+ mActionTimersMap.clear();
+}
+
+bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_id)
+{
+ unsetActionTimer(speaker_id);
+
+ if (mActionCallback)
+ {
+ mActionCallback(speaker_id);
+ }
+
+ return true;
+}
+
+bool LLSpeakersDelayActionsStorage::isTimerStarted(const LLUUID& speaker_id)
+{
+ return (mActionTimersMap.size() > 0) && (mActionTimersMap.find(speaker_id) != mActionTimersMap.end());
+}
+
+//
+// LLSpeakerMgr
+//
+
+LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) :
+ mVoiceChannel(channelp),
+ mVoiceModerated(false),
+ mModerateModeHandledFirstTime(false),
+ mSpeakerListUpdated(false)
+{
+ mGetListTime.reset();
+ static LLUICachedControl<F32> remove_delay ("SpeakerParticipantRemoveDelay", 10.0);
+
+ mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay);
+}
+
+LLSpeakerMgr::~LLSpeakerMgr()
+{
+ delete mSpeakerDelayRemover;
+}
+
+LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type)
+{
+ LLUUID session_id = getSessionID();
+ if (id.isNull() || (id == session_id))
+ {
+ return NULL;
+ }
+
+ LLPointer<LLSpeaker> speakerp;
+ if (mSpeakers.find(id) == mSpeakers.end())
+ {
+ speakerp = new LLSpeaker(id, name, type);
+ speakerp->mStatus = status;
+ mSpeakers.insert(std::make_pair(speakerp->mID, speakerp));
+ mSpeakersSorted.push_back(speakerp);
+ LL_DEBUGS("Speakers") << "Added speaker " << id << LL_ENDL;
+ fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add");
+ }
+ else
+ {
+ speakerp = findSpeaker(id);
+ if (speakerp.notNull())
+ {
+ // keep highest priority status (lowest value) instead of overriding current value
+ speakerp->mStatus = llmin(speakerp->mStatus, status);
+ // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id
+ // we need to override speakers that we think are objects when we find out they are really
+ // residents
+ if (type == LLSpeaker::SPEAKER_AGENT)
+ {
+ speakerp->mType = LLSpeaker::SPEAKER_AGENT;
+ speakerp->lookupName();
+ }
+ }
+ else
+ {
+ LL_WARNS("Speakers") << "Speaker " << id << " not found" << LL_ENDL;
+ }
+ }
+
+ mSpeakerDelayRemover->unsetActionTimer(speakerp->mID);
+ return speakerp;
+}
+
+// *TODO: Once way to request the current voice channel moderation mode is implemented
+// this method with related code should be removed.
+/*
+ Initializes "moderate_mode" of voice session on first join.
+
+ This is WORKAROUND because a way to request the current voice channel moderation mode exists
+ but is not implemented in viewer yet. See EXT-6937.
+*/
+void LLSpeakerMgr::initVoiceModerateMode()
+{
+ if (!mModerateModeHandledFirstTime && (mVoiceChannel && mVoiceChannel->isActive()))
+ {
+ LLPointer<LLSpeaker> speakerp;
+
+ if (mSpeakers.find(gAgentID) != mSpeakers.end())
+ {
+ speakerp = mSpeakers[gAgentID];
+ }
+
+ if (speakerp.notNull())
+ {
+ mVoiceModerated = speakerp->mModeratorMutedVoice;
+ mModerateModeHandledFirstTime = true;
+ }
+ }
+}
+
+void LLSpeakerMgr::update(bool resort_ok)
+{
+ if (!LLVoiceClient::getInstance())
+ {
+ return;
+ }
+
+ LLColor4 speaking_color = LLUIColorTable::instance().getColor("SpeakingColor");
+ LLColor4 overdriven_color = LLUIColorTable::instance().getColor("OverdrivenColor");
+
+ if(resort_ok) // only allow list changes when user is not interacting with it
+ {
+ updateSpeakerList();
+ }
+
+ // update status of all current speakers
+ bool voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());
+ for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); speaker_it++)
+ {
+ LLUUID speaker_id = speaker_it->first;
+ LLSpeaker* speakerp = speaker_it->second;
+
+ if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id))
+ {
+ speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id);
+ bool moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id);
+ if (moderator_muted_voice != speakerp->mModeratorMutedVoice)
+ {
+ speakerp->mModeratorMutedVoice = moderator_muted_voice;
+ LL_DEBUGS("Speakers") << (speakerp->mModeratorMutedVoice? "Muted" : "Umuted") << " speaker " << speaker_id<< LL_ENDL;
+ speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp));
+ }
+
+ if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice)
+ {
+ speakerp->mStatus = LLSpeaker::STATUS_MUTED;
+ }
+ else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id))
+ {
+ // reset inactivity expiration
+ if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING)
+ {
+ speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
+ speakerp->mHasSpoken = true;
+ fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker");
+ }
+ speakerp->mStatus = LLSpeaker::STATUS_SPEAKING;
+ // interpolate between active color and full speaking color based on power of speech output
+ speakerp->mDotColor = speaking_color;
+ if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL)
+ {
+ speakerp->mDotColor = overdriven_color;
+ }
+ }
+ else
+ {
+ speakerp->mSpeechVolume = 0.f;
+ speakerp->mDotColor = ACTIVE_COLOR;
+
+ if (speakerp->mHasSpoken)
+ {
+ // have spoken once, not currently speaking
+ speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN;
+ }
+ else
+ {
+ // default state for being in voice channel
+ speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE;
+ }
+ }
+ }
+ // speaker no longer registered in voice channel, demote to text only
+ else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL)
+ {
+ if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL)
+ {
+ // external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice)
+ speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
+ }
+ else
+ {
+ speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY;
+ speakerp->mSpeechVolume = 0.f;
+ speakerp->mDotColor = ACTIVE_COLOR;
+ }
+ }
+ }
+
+ if(resort_ok) // only allow list changes when user is not interacting with it
+ {
+ // sort by status then time last spoken
+ std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers());
+ }
+
+ // for recent speakers who are not currently speaking, show "recent" color dot for most recent
+ // fading to "active" color
+
+ S32 recent_speaker_count = 0;
+ S32 sort_index = 0;
+ speaker_list_t::iterator sorted_speaker_it;
+ for(sorted_speaker_it = mSpeakersSorted.begin();
+ sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it)
+ {
+ LLPointer<LLSpeaker> speakerp = *sorted_speaker_it;
+
+ // color code recent speakers who are not currently speaking
+ if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN)
+ {
+ speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f));
+ recent_speaker_count++;
+ }
+
+ // stuff sort ordinal into speaker so the ui can sort by this value
+ speakerp->mSortIndex = sort_index++;
+ }
+}
+
+void LLSpeakerMgr::updateSpeakerList()
+{
+ // Are we bound to the currently active voice channel?
+ if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
+ {
+ std::set<LLUUID> participants;
+ LLVoiceClient::getInstance()->getParticipantList(participants);
+ // If we are, add all voice client participants to our list of known speakers
+ for (std::set<LLUUID>::iterator participant_it = participants.begin(); participant_it != participants.end(); ++participant_it)
+ {
+ setSpeaker(*participant_it,
+ LLVoiceClient::getInstance()->getDisplayName(*participant_it),
+ LLSpeaker::STATUS_VOICE_ACTIVE,
+ (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL));
+ }
+ }
+ else
+ {
+ // If not, check if the list is empty, except if it's Nearby Chat (session_id NULL).
+ LLUUID session_id = getSessionID();
+ if (!session_id.isNull() && !mSpeakerListUpdated)
+ {
+ // If the list is empty, we update it with whatever we have locally so that it doesn't stay empty too long.
+ // *TODO: Fix the server side code that sometimes forgets to send back the list of participants after a chat started.
+ // (IOW, fix why we get no ChatterBoxSessionAgentListUpdates message after the initial ChatterBoxSessionStartReply)
+ LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (session->isGroupSessionType() && (mSpeakers.size() <= 1))
+ {
+ // For groups, we need to hit the group manager.
+ // Note: The session uuid and the group uuid are actually one and the same. If that was to change, this will fail.
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(session_id);
+
+ if (gdatap && gdatap->isMemberDataComplete() && !gdatap->mMembers.empty())
+ {
+ // Add group members when we get the complete list (note: can take a while before we get that list)
+ LLGroupMgrGroupData::member_list_t::iterator member_it = gdatap->mMembers.begin();
+ const S32 load_group_max_members = gSavedSettings.getS32("ChatLoadGroupMaxMembers");
+ S32 updated = 0;
+ while (member_it != gdatap->mMembers.end())
+ {
+ LLGroupMemberData* member = member_it->second;
+ LLUUID id = member_it->first;
+ // Add only members who are online and not already in the list
+ if ((member->getOnlineStatus() == "Online") && (mSpeakers.find(id) == mSpeakers.end()))
+ {
+ LLPointer<LLSpeaker> speakerp = setSpeaker(id, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
+ speakerp->mIsModerator = ((member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR);
+ updated++;
+ }
+ ++member_it;
+ // Limit the number of "manually updated" participants to a reasonable number to avoid severe fps drop
+ // *TODO : solve the perf issue of having several hundreds of widgets in the conversation list
+ if (updated >= load_group_max_members)
+ break;
+ }
+ mSpeakerListUpdated = true;
+ }
+ }
+ else if (mSpeakers.size() == 0)
+ {
+ // For all other session type (ad-hoc, P2P), we use the initial participants targets list
+ for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();it!=session->mInitialTargetIDs.end();++it)
+ {
+ // Add buddies if they are on line, add any other avatar.
+ if (!LLAvatarTracker::instance().isBuddy(*it) || LLAvatarTracker::instance().isBuddyOnline(*it))
+ {
+ setSpeaker(*it, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
+ }
+ }
+ mSpeakerListUpdated = true;
+ }
+ else
+ {
+ // The list has been updated the normal way (i.e. by a ChatterBoxSessionAgentListUpdates received from the server)
+ mSpeakerListUpdated = true;
+ }
+ }
+ }
+ // Always add the current agent (it has to be there...). Will do nothing if already there.
+ setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
+}
+
+void LLSpeakerMgr::setSpeakerNotInChannel(LLPointer<LLSpeaker> speakerp)
+{
+ if (speakerp.notNull())
+ {
+ speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
+ speakerp->mDotColor = INACTIVE_COLOR;
+ mSpeakerDelayRemover->setActionTimer(speakerp->mID);
+ }
+}
+
+bool LLSpeakerMgr::removeSpeaker(const LLUUID& speaker_id)
+{
+ mSpeakers.erase(speaker_id);
+
+ speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin();
+
+ for(; sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it)
+ {
+ if (speaker_id == (*sorted_speaker_it)->mID)
+ {
+ mSpeakersSorted.erase(sorted_speaker_it);
+ break;
+ }
+ }
+
+ LL_DEBUGS("Speakers") << "Removed speaker " << speaker_id << LL_ENDL;
+ fireEvent(new LLSpeakerListChangeEvent(this, speaker_id), "remove");
+
+ update(true);
+
+ return false;
+}
+
+LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id)
+{
+ //In some conditions map causes crash if it is empty(Windows only), adding check (EK)
+ if (mSpeakers.size() == 0)
+ return NULL;
+ speaker_map_t::iterator found_it = mSpeakers.find(speaker_id);
+ if (found_it == mSpeakers.end())
+ {
+ return NULL;
+ }
+ return found_it->second;
+}
+
+void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, bool include_text)
+{
+ speaker_list->clear();
+ for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
+ {
+ LLPointer<LLSpeaker> speakerp = speaker_it->second;
+ // what about text only muted or inactive?
+ if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY)
+ {
+ speaker_list->push_back(speakerp);
+ }
+ }
+}
+
+const LLUUID LLSpeakerMgr::getSessionID()
+{
+ return mVoiceChannel->getSessionID();
+}
+
+bool LLSpeakerMgr::isSpeakerToBeRemoved(const LLUUID& speaker_id)
+{
+ return mSpeakerDelayRemover && mSpeakerDelayRemover->isTimerStarted(speaker_id);
+}
+
+void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, bool typing)
+{
+ LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
+ if (speakerp.notNull())
+ {
+ speakerp->mTyping = typing;
+ }
+}
+
+// speaker has chatted via either text or voice
+void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id)
+{
+ LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
+ if (speakerp.notNull())
+ {
+ speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
+ speakerp->mHasSpoken = true;
+ fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker");
+ }
+}
+
+bool LLSpeakerMgr::isVoiceActive()
+{
+ // mVoiceChannel = NULL means current voice channel, whatever it is
+ return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive();
+}
+
+
+//
+// LLIMSpeakerMgr
+//
+LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel)
+{
+}
+
+void LLIMSpeakerMgr::updateSpeakerList()
+{
+ // don't do normal updates which are pulled from voice channel
+ // rely on user list reported by sim
+
+ // We need to do this to allow PSTN callers into group chats to show in the list.
+ LLSpeakerMgr::updateSpeakerList();
+
+ return;
+}
+
+void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers)
+{
+ if ( !speakers.isMap() ) return;
+
+ if ( speakers.has("agent_info") && speakers["agent_info"].isMap() )
+ {
+ LLSD::map_const_iterator speaker_it;
+ for(speaker_it = speakers["agent_info"].beginMap();
+ speaker_it != speakers["agent_info"].endMap();
+ ++speaker_it)
+ {
+ LLUUID agent_id(speaker_it->first);
+
+ LLPointer<LLSpeaker> speakerp = setSpeaker(
+ agent_id,
+ LLStringUtil::null,
+ LLSpeaker::STATUS_TEXT_ONLY);
+
+ if ( speaker_it->second.isMap() )
+ {
+ bool is_moderator = speakerp->mIsModerator;
+ speakerp->mIsModerator = speaker_it->second["is_moderator"];
+ speakerp->mModeratorMutedText =
+ speaker_it->second["mutes"]["text"];
+ // Fire event only if moderator changed
+ if ( is_moderator != speakerp->mIsModerator )
+ {
+ LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL;
+ fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator");
+ }
+ }
+ }
+ }
+ else if ( speakers.has("agents" ) && speakers["agents"].isArray() )
+ {
+ //older, more decprecated way. Need here for
+ //using older version of servers
+ LLSD::array_const_iterator speaker_it;
+ for(speaker_it = speakers["agents"].beginArray();
+ speaker_it != speakers["agents"].endArray();
+ ++speaker_it)
+ {
+ const LLUUID agent_id = (*speaker_it).asUUID();
+
+ LLPointer<LLSpeaker> speakerp = setSpeaker(
+ agent_id,
+ LLStringUtil::null,
+ LLSpeaker::STATUS_TEXT_ONLY);
+ }
+ }
+}
+
+void LLIMSpeakerMgr::updateSpeakers(const LLSD& update)
+{
+ if ( !update.isMap() ) return;
+
+ if ( update.has("agent_updates") && update["agent_updates"].isMap() )
+ {
+ LLSD::map_const_iterator update_it;
+ for(
+ update_it = update["agent_updates"].beginMap();
+ update_it != update["agent_updates"].endMap();
+ ++update_it)
+ {
+ LLUUID agent_id(update_it->first);
+ LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id);
+
+ LLSD agent_data = update_it->second;
+
+ if (agent_data.isMap() && agent_data.has("transition"))
+ {
+ if (agent_data["transition"].asString() == "LEAVE")
+ {
+ setSpeakerNotInChannel(speakerp);
+ }
+ else if (agent_data["transition"].asString() == "ENTER")
+ {
+ // add or update speaker
+ speakerp = setSpeaker(agent_id);
+ }
+ else
+ {
+ LL_WARNS() << "bad membership list update from 'agent_updates' for agent " << agent_id << ", transition " << ll_print_sd(agent_data["transition"]) << LL_ENDL;
+ }
+ }
+
+ if (speakerp.isNull()) continue;
+
+ // should have a valid speaker from this point on
+ if (agent_data.isMap() && agent_data.has("info"))
+ {
+ LLSD agent_info = agent_data["info"];
+
+ if (agent_info.has("is_moderator"))
+ {
+ bool is_moderator = speakerp->mIsModerator;
+ speakerp->mIsModerator = agent_info["is_moderator"];
+ // Fire event only if moderator changed
+ if ( is_moderator != speakerp->mIsModerator )
+ {
+ LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL;
+ fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator");
+ }
+ }
+
+ if (agent_info.has("mutes"))
+ {
+ speakerp->mModeratorMutedText = agent_info["mutes"]["text"];
+ }
+ }
+ }
+ }
+ else if ( update.has("updates") && update["updates"].isMap() )
+ {
+ LLSD::map_const_iterator update_it;
+ for (
+ update_it = update["updates"].beginMap();
+ update_it != update["updates"].endMap();
+ ++update_it)
+ {
+ LLUUID agent_id(update_it->first);
+ LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id);
+
+ std::string agent_transition = update_it->second.asString();
+ if (agent_transition == "LEAVE")
+ {
+ setSpeakerNotInChannel(speakerp);
+ }
+ else if ( agent_transition == "ENTER")
+ {
+ // add or update speaker
+ speakerp = setSpeaker(agent_id);
+ }
+ else
+ {
+ LL_WARNS() << "bad membership list update from 'updates' for agent " << agent_id << ", transition " << agent_transition << LL_ENDL;
+ }
+ }
+ }
+}
+
+void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id)
+{
+ LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
+ if (!speakerp) return;
+
+ std::string url = gAgent.getRegionCapability("ChatSessionRequest");
+ LLSD data;
+ data["method"] = "mute update";
+ data["session-id"] = getSessionID();
+ data["params"] = LLSD::emptyMap();
+ data["params"]["agent_id"] = speaker_id;
+ data["params"]["mute_info"] = LLSD::emptyMap();
+ //current value represents ability to type, so invert
+ data["params"]["mute_info"]["text"] = !speakerp->mModeratorMutedText;
+
+ LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro",
+ boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data));
+}
+
+void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute)
+{
+ LLPointer<LLSpeaker> speakerp = findSpeaker(avatar_id);
+ if (!speakerp) return;
+
+ // *NOTE: mantipov: probably this condition will be incorrect when avatar will be blocked for
+ // text chat via moderation (LLSpeaker::mModeratorMutedText == true)
+ bool is_in_voice = speakerp->mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || speakerp->mStatus == LLSpeaker::STATUS_MUTED;
+
+ // do not send voice moderation changes for avatars not in voice channel
+ if (!is_in_voice) return;
+
+ std::string url = gAgent.getRegionCapability("ChatSessionRequest");
+ LLSD data;
+ data["method"] = "mute update";
+ data["session-id"] = getSessionID();
+ data["params"] = LLSD::emptyMap();
+ data["params"]["agent_id"] = avatar_id;
+ data["params"]["mute_info"] = LLSD::emptyMap();
+ data["params"]["mute_info"]["voice"] = !unmute;
+
+ LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro",
+ boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data));
+}
+
+void LLIMSpeakerMgr::moderationActionCoro(std::string url, LLSD action)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("moderationActionCoro", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+
+ httpOpts->setWantHeaders(true);
+
+ LLUUID sessionId = action["session-id"];
+
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, url, action, httpOpts);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (!status)
+ {
+ if (gIMMgr)
+ {
+ //403 == you're not a mod
+ //should be disabled if you're not a moderator
+ if (status == LLCore::HttpStatus(HTTP_FORBIDDEN))
+ {
+ gIMMgr->showSessionEventError(
+ "mute",
+ "not_a_mod_error",
+ sessionId);
+ }
+ else
+ {
+ gIMMgr->showSessionEventError(
+ "mute",
+ "generic_request_error",
+ sessionId);
+ }
+ }
+ return;
+ }
+}
+
+void LLIMSpeakerMgr::moderateVoiceAllParticipants( bool unmute_everyone )
+{
+ if (mVoiceModerated == !unmute_everyone)
+ {
+ // session already in requested state. Just force participants which do not match it.
+ forceVoiceModeratedMode(mVoiceModerated);
+ }
+ else
+ {
+ // otherwise set moderated mode for a whole session.
+ moderateVoiceSession(getSessionID(), !unmute_everyone);
+ }
+}
+
+void LLIMSpeakerMgr::processSessionUpdate(const LLSD& session_update)
+{
+ if (session_update.has("moderated_mode") &&
+ session_update["moderated_mode"].has("voice"))
+ {
+ mVoiceModerated = session_update["moderated_mode"]["voice"];
+ }
+}
+
+void LLIMSpeakerMgr::moderateVoiceSession(const LLUUID& session_id, bool disallow_voice)
+{
+ std::string url = gAgent.getRegionCapability("ChatSessionRequest");
+ LLSD data;
+ data["method"] = "session update";
+ data["session-id"] = session_id;
+ data["params"] = LLSD::emptyMap();
+
+ data["params"]["update_info"] = LLSD::emptyMap();
+
+ data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap();
+ data["params"]["update_info"]["moderated_mode"]["voice"] = disallow_voice;
+
+ LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro",
+ boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data));
+}
+
+void LLIMSpeakerMgr::forceVoiceModeratedMode(bool should_be_muted)
+{
+ for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
+ {
+ LLUUID speaker_id = speaker_it->first;
+ LLSpeaker* speakerp = speaker_it->second;
+
+ // participant does not match requested state
+ if (should_be_muted != (bool)speakerp->mModeratorMutedVoice)
+ {
+ moderateVoiceParticipant(speaker_id, !should_be_muted);
+ }
+ }
+}
+
+//
+// LLActiveSpeakerMgr
+//
+
+LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL)
+{
+}
+
+void LLActiveSpeakerMgr::updateSpeakerList()
+{
+ // point to whatever the current voice channel is
+ mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
+
+ // always populate from active voice channel
+ if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) //MA: seems this is always false
+ {
+ LL_DEBUGS("Speakers") << "Removed all speakers" << LL_ENDL;
+ fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear");
+ mSpeakers.clear();
+ mSpeakersSorted.clear();
+ mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
+ mSpeakerDelayRemover->removeAllTimers();
+ }
+ LLSpeakerMgr::updateSpeakerList();
+
+ // clean up text only speakers
+ for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
+ {
+ LLSpeaker* speakerp = speaker_it->second;
+ if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
+ {
+ // automatically flag text only speakers for removal
+ speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
+ }
+ }
+
+}
+
+
+
+//
+// LLLocalSpeakerMgr
+//
+
+LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance())
+{
+}
+
+LLLocalSpeakerMgr::~LLLocalSpeakerMgr ()
+{
+}
+
+void LLLocalSpeakerMgr::updateSpeakerList()
+{
+ // pull speakers from voice channel
+ LLSpeakerMgr::updateSpeakerList();
+
+ if (gDisconnected)//the world is cleared.
+ {
+ return ;
+ }
+
+ // pick up non-voice speakers in chat range
+ uuid_vec_t avatar_ids;
+ std::vector<LLVector3d> positions;
+ LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS);
+ for(U32 i=0; i<avatar_ids.size(); i++)
+ {
+ setSpeaker(avatar_ids[i]);
+ }
+
+ // check if text only speakers have moved out of chat range
+ for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
+ {
+ LLUUID speaker_id = speaker_it->first;
+ LLPointer<LLSpeaker> speakerp = speaker_it->second;
+ if (speakerp.notNull() && speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
+ {
+ LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id);
+ if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS * CHAT_NORMAL_RADIUS)
+ {
+ setSpeakerNotInChannel(speakerp);
+ }
+ }
+ }
+}
|
